xref: /haiku/src/kits/shared/ExpressionParser.cpp (revision 52c4471a3024d2eb81fe88e2c3982b9f8daa5e56)
1 /*
2  * Copyright 2006-2012 Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus <superstippi@gmx.de>
7  *		John Scipione <jscipione@gmail.com>
8  *		Ingo Weinhold <bonefish@cs.tu-berlin.de>
9  */
10 
11 #include <ExpressionParser.h>
12 
13 #include <ctype.h>
14 #include <math.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <strings.h>
18 
19 #include <m_apm.h>
20 
21 
22 static const int32 kMaxDecimalPlaces = 32;
23 
24 
25 enum {
26 	TOKEN_NONE					= 0,
27 	TOKEN_IDENTIFIER,
28 	TOKEN_CONSTANT,
29 
30 	TOKEN_END_OF_LINE			= '\n',
31 
32 	TOKEN_PLUS					= '+',
33 	TOKEN_MINUS					= '-',
34 
35 	TOKEN_STAR					= '*',
36 	TOKEN_SLASH					= '/',
37 	TOKEN_MODULO				= '%',
38 
39 	TOKEN_POWER					= '^',
40 	TOKEN_FACTORIAL				= '!',
41 
42 	TOKEN_OPENING_BRACKET		= '(',
43 	TOKEN_CLOSING_BRACKET		= ')',
44 
45 	TOKEN_AND					= '&',
46 	TOKEN_OR					= '|',
47 	TOKEN_NOT					= '~'
48 };
49 
50 
51 struct ExpressionParser::Token {
52 	Token()
53 		: string(""),
54 		  type(TOKEN_NONE),
55 		  value(0),
56 		  position(0)
57 	{
58 	}
59 
60 	Token(const Token& other)
61 		: string(other.string),
62 		  type(other.type),
63 		  value(other.value),
64 		  position(other.position)
65 	{
66 	}
67 
68 	Token(const char* string, int32 length, int32 position, int32 type)
69 		: string(string, length),
70 		  type(type),
71 		  value(0),
72 		  position(position)
73 	{
74 	}
75 
76 	Token& operator=(const Token& other)
77 	{
78 		string = other.string;
79 		type = other.type;
80 		value = other.value;
81 		position = other.position;
82 		return *this;
83 	}
84 
85 	BString		string;
86 	int32		type;
87 	MAPM		value;
88 
89 	int32		position;
90 };
91 
92 
93 class ExpressionParser::Tokenizer {
94  public:
95 	Tokenizer()
96 		: fString(""),
97 		  fCurrentChar(NULL),
98 		  fCurrentToken(),
99 		  fReuseToken(false),
100 		  fHexSupport(false),
101 		  fDecimalSeparator("."),
102 		  fGroupSeparator(",")
103 	{
104 	}
105 
106 	void SetSupportHexInput(bool enabled)
107 	{
108 		fHexSupport = enabled;
109 	}
110 
111 	void SetTo(const char* string)
112 	{
113 		fString = string;
114 		fCurrentChar = fString.String();
115 		fCurrentToken = Token();
116 		fReuseToken = false;
117 	}
118 
119 	const Token& NextToken()
120 	{
121 		if (fCurrentToken.type == TOKEN_END_OF_LINE)
122 			return fCurrentToken;
123 
124 		if (fReuseToken) {
125 			fReuseToken = false;
126 //printf("next token (recycled): '%s'\n", fCurrentToken.string.String());
127 			return fCurrentToken;
128 		}
129 
130 		while (*fCurrentChar != 0 && isspace(*fCurrentChar))
131 			fCurrentChar++;
132 
133 		int32 decimalLen = fDecimalSeparator.Length();
134 		int32 groupLen = fGroupSeparator.Length();
135 
136 		if (*fCurrentChar == 0 || decimalLen == 0)
137 			return fCurrentToken = Token("", 0, _CurrentPos(), TOKEN_END_OF_LINE);
138 
139 		if (*fCurrentChar == fDecimalSeparator[0] || isdigit(*fCurrentChar)) {
140 			if (fHexSupport && *fCurrentChar == '0' && fCurrentChar[1] == 'x')
141 				return _ParseHexNumber();
142 
143 			BString temp;
144 
145 			const char* begin = fCurrentChar;
146 
147 			// optional digits before the comma
148 			while (isdigit(*fCurrentChar) ||
149 				(groupLen > 0 && *fCurrentChar == fGroupSeparator[0])) {
150 				if (groupLen > 0 && *fCurrentChar == fGroupSeparator[0]) {
151 					int i = 0;
152 					while (i < groupLen && *fCurrentChar == fGroupSeparator[i]) {
153 						fCurrentChar++;
154 						i++;
155 					}
156 				} else {
157 					temp << *fCurrentChar;
158 					fCurrentChar++;
159 				}
160 			}
161 
162 			// optional post comma part
163 			// (required if there are no digits before the comma)
164 			if (*fCurrentChar == fDecimalSeparator[0]) {
165 				int i = 0;
166 				while (i < decimalLen && *fCurrentChar == fDecimalSeparator[i]) {
167 					fCurrentChar++;
168 					i++;
169 				}
170 
171 				temp << '.';
172 
173 				// optional post comma digits
174 				while (isdigit(*fCurrentChar)) {
175 					temp << *fCurrentChar;
176 					fCurrentChar++;
177 				}
178 			}
179 
180 			// optional exponent part
181 			if (*fCurrentChar == 'E') {
182 				temp << *fCurrentChar;
183 				fCurrentChar++;
184 
185 				// optional exponent sign
186 				if (*fCurrentChar == '+' || *fCurrentChar == '-') {
187 					temp << *fCurrentChar;
188 					fCurrentChar++;
189 				}
190 
191 				// required exponent digits
192 				if (!isdigit(*fCurrentChar)) {
193 					throw ParseException("missing exponent in constant",
194 						fCurrentChar - begin);
195 				}
196 
197 				while (isdigit(*fCurrentChar)) {
198 					temp << *fCurrentChar;
199 					fCurrentChar++;
200 				}
201 			}
202 
203 			int32 length = fCurrentChar - begin;
204 			BString test = temp;
205 			test << "&_";
206 			double value;
207 			char t[2];
208 			int32 matches = sscanf(test.String(), "%lf&%s", &value, t);
209 			if (matches != 2) {
210 				throw ParseException("error in constant",
211 					_CurrentPos() - length);
212 			}
213 
214 			fCurrentToken = Token(begin, length, _CurrentPos() - length,
215 				TOKEN_CONSTANT);
216 			fCurrentToken.value = temp.String();
217 		} else if (isalpha(*fCurrentChar) && *fCurrentChar != 'x') {
218 			const char* begin = fCurrentChar;
219 			while (*fCurrentChar != 0 && (isalpha(*fCurrentChar)
220 				|| isdigit(*fCurrentChar))) {
221 				fCurrentChar++;
222 			}
223 			int32 length = fCurrentChar - begin;
224 			fCurrentToken = Token(begin, length, _CurrentPos() - length,
225 				TOKEN_IDENTIFIER);
226 		} else if (strncmp(fCurrentChar, "π", 2) == 0) {
227 			fCurrentToken = Token(fCurrentChar, 2, _CurrentPos() - 1,
228 				TOKEN_IDENTIFIER);
229 			fCurrentChar += 2;
230 		} else {
231 			int32 type = TOKEN_NONE;
232 
233 			switch (*fCurrentChar) {
234 				case TOKEN_PLUS:
235 				case TOKEN_MINUS:
236 				case TOKEN_STAR:
237 				case TOKEN_SLASH:
238 				case TOKEN_MODULO:
239 				case TOKEN_POWER:
240 				case TOKEN_FACTORIAL:
241 				case TOKEN_OPENING_BRACKET:
242 				case TOKEN_CLOSING_BRACKET:
243 				case TOKEN_AND:
244 				case TOKEN_OR:
245 				case TOKEN_NOT:
246 				case TOKEN_END_OF_LINE:
247 					type = *fCurrentChar;
248 					break;
249 
250 				case '\\':
251 				case ':':
252 				type = TOKEN_SLASH;
253 					break;
254 
255 				case 'x':
256 					if (!fHexSupport) {
257 						type = TOKEN_STAR;
258 						break;
259 					}
260 					// fall through
261 
262 				default:
263 					throw ParseException("unexpected character", _CurrentPos());
264 			}
265 			fCurrentToken = Token(fCurrentChar, 1, _CurrentPos(), type);
266 			fCurrentChar++;
267 		}
268 
269 //printf("next token: '%s'\n", fCurrentToken.string.String());
270 		return fCurrentToken;
271 	}
272 
273 	void RewindToken()
274 	{
275 		fReuseToken = true;
276 	}
277 
278 	BString DecimalSeparator()
279 	{
280 		return fDecimalSeparator;
281 	}
282 
283 	BString GroupSeparator()
284 	{
285 		return fGroupSeparator;
286 	}
287 
288 	void SetSeparators(BString decimal, BString group)
289 	{
290 		fDecimalSeparator = decimal;
291 		fGroupSeparator = group;
292 	}
293 
294  private:
295 	static bool _IsHexDigit(char c)
296 	{
297 		return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
298 	}
299 
300 	Token& _ParseHexNumber()
301 	{
302 		const char* begin = fCurrentChar;
303 		fCurrentChar += 2;
304 			// skip "0x"
305 
306 		if (!_IsHexDigit(*fCurrentChar))
307 			throw ParseException("expected hex digit", _CurrentPos());
308 
309 		fCurrentChar++;
310 		while (_IsHexDigit(*fCurrentChar))
311 			fCurrentChar++;
312 
313 		int32 length = fCurrentChar - begin;
314 		fCurrentToken = Token(begin, length, _CurrentPos() - length,
315 			TOKEN_CONSTANT);
316 
317 		// MAPM has no conversion from long long, so we need to improvise.
318 		uint64 value = strtoll(fCurrentToken.string.String(), NULL, 0);
319 		if (value <= 0x7fffffff) {
320 			fCurrentToken.value = (long)value;
321 		} else {
322 			fCurrentToken.value = (int)(value >> 60);
323 			fCurrentToken.value *= 1 << 30;
324 			fCurrentToken.value += (int)((value >> 30) & 0x3fffffff);
325 			fCurrentToken.value *= 1 << 30;
326 			fCurrentToken.value += (int)(value& 0x3fffffff);
327 		}
328 
329 		return fCurrentToken;
330 	}
331 
332 	int32 _CurrentPos() const
333 	{
334 		return fCurrentChar - fString.String();
335 	}
336 
337 	BString		fString;
338 	const char*	fCurrentChar;
339 	Token		fCurrentToken;
340 	bool		fReuseToken;
341 	bool		fHexSupport;
342 	BString		fDecimalSeparator;
343 	BString		fGroupSeparator;
344 };
345 
346 
347 ExpressionParser::ExpressionParser()
348 	:	fTokenizer(new Tokenizer()),
349 		fDegreeMode(false)
350 {
351 }
352 
353 
354 ExpressionParser::~ExpressionParser()
355 {
356 	delete fTokenizer;
357 }
358 
359 
360 bool
361 ExpressionParser::DegreeMode()
362 {
363 	return fDegreeMode;
364 }
365 
366 
367 void
368 ExpressionParser::SetDegreeMode(bool degrees)
369 {
370 	fDegreeMode = degrees;
371 }
372 
373 
374 void
375 ExpressionParser::SetSupportHexInput(bool enabled)
376 {
377 	fTokenizer->SetSupportHexInput(enabled);
378 }
379 
380 
381 BString
382 ExpressionParser::Evaluate(const char* expressionString)
383 {
384 	fTokenizer->SetTo(expressionString);
385 
386 	MAPM value = _ParseBinary();
387 	Token token = fTokenizer->NextToken();
388 	if (token.type != TOKEN_END_OF_LINE)
389 		throw ParseException("parse error", token.position);
390 
391 	if (value == 0)
392 		return BString("0");
393 
394 	char* buffer = value.toFixPtStringExp(kMaxDecimalPlaces,
395 						'.', 0, 0);
396 
397 	if (buffer == NULL)
398 		throw ParseException("out of memory", 0);
399 
400 	// remove surplus zeros
401 	int32 lastChar = strlen(buffer) - 1;
402 	if (strchr(buffer, '.')) {
403 		while (buffer[lastChar] == '0')
404 			lastChar--;
405 
406 		if (buffer[lastChar] == '.')
407 			lastChar--;
408 	}
409 
410 	BString result(buffer, lastChar + 1);
411 	result.Replace(".", fTokenizer->DecimalSeparator(), 1);
412 	free(buffer);
413 	return result;
414 }
415 
416 
417 int64
418 ExpressionParser::EvaluateToInt64(const char* expressionString)
419 {
420 	fTokenizer->SetTo(expressionString);
421 
422 	MAPM value = _ParseBinary();
423 	Token token = fTokenizer->NextToken();
424 	if (token.type != TOKEN_END_OF_LINE)
425 		throw ParseException("parse error", token.position);
426 
427 	char buffer[128];
428 	value.toIntegerString(buffer);
429 
430 	return strtoll(buffer, NULL, 0);
431 }
432 
433 
434 double
435 ExpressionParser::EvaluateToDouble(const char* expressionString)
436 {
437 	fTokenizer->SetTo(expressionString);
438 
439 	MAPM value = _ParseBinary();
440 	Token token = fTokenizer->NextToken();
441 	if (token.type != TOKEN_END_OF_LINE)
442 		throw ParseException("parse error", token.position);
443 
444 	char buffer[1024];
445 	value.toString(buffer, sizeof(buffer) - 4);
446 
447 	return strtod(buffer, NULL);
448 }
449 
450 
451 MAPM
452 ExpressionParser::_ParseBinary()
453 {
454 	return _ParseSum();
455 	// binary operation appearantly not supported by m_apm library,
456 	// should not be too hard to implement though....
457 
458 //	double value = _ParseSum();
459 //
460 //	while (true) {
461 //		Token token = fTokenizer->NextToken();
462 //		switch (token.type) {
463 //			case TOKEN_AND:
464 //				value = (uint64)value & (uint64)_ParseSum();
465 //				break;
466 //			case TOKEN_OR:
467 //				value = (uint64)value | (uint64)_ParseSum();
468 //				break;
469 //
470 //			default:
471 //				fTokenizer->RewindToken();
472 //				return value;
473 //		}
474 //	}
475 }
476 
477 
478 MAPM
479 ExpressionParser::_ParseSum()
480 {
481 	// TODO: check isnan()...
482 	MAPM value = _ParseProduct();
483 
484 	while (true) {
485 		Token token = fTokenizer->NextToken();
486 		switch (token.type) {
487 			case TOKEN_PLUS:
488 				value = value + _ParseProduct();
489 				break;
490 			case TOKEN_MINUS:
491 				value = value - _ParseProduct();
492 				break;
493 
494 			default:
495 				fTokenizer->RewindToken();
496 				return _ParseFactorial(value);
497 		}
498 	}
499 }
500 
501 
502 MAPM
503 ExpressionParser::_ParseProduct()
504 {
505 	// TODO: check isnan()...
506 	MAPM value = _ParsePower();
507 
508 	while (true) {
509 		Token token = fTokenizer->NextToken();
510 		switch (token.type) {
511 			case TOKEN_STAR:
512 				value = value * _ParsePower();
513 				break;
514 			case TOKEN_SLASH: {
515 				MAPM rhs = _ParsePower();
516 				if (rhs == MAPM(0))
517 					throw ParseException("division by zero", token.position);
518 				value = value / rhs;
519 				break;
520 			}
521 			case TOKEN_MODULO: {
522 				MAPM rhs = _ParsePower();
523 				if (rhs == MAPM(0))
524 					throw ParseException("modulo by zero", token.position);
525 				value = value % rhs;
526 				break;
527 			}
528 
529 			default:
530 				fTokenizer->RewindToken();
531 				return _ParseFactorial(value);
532 		}
533 	}
534 }
535 
536 
537 MAPM
538 ExpressionParser::_ParsePower()
539 {
540 	MAPM value = _ParseUnary();
541 
542 	while (true) {
543 		Token token = fTokenizer->NextToken();
544 		if (token.type != TOKEN_POWER) {
545 			fTokenizer->RewindToken();
546 			return _ParseFactorial(value);
547 		}
548 		value = value.pow(_ParseUnary());
549 	}
550 }
551 
552 
553 MAPM
554 ExpressionParser::_ParseUnary()
555 {
556 	Token token = fTokenizer->NextToken();
557 	if (token.type == TOKEN_END_OF_LINE)
558 		throw ParseException("unexpected end of expression", token.position);
559 
560 	switch (token.type) {
561 		case TOKEN_PLUS:
562 			return _ParseUnary();
563 		case TOKEN_MINUS:
564 			return -_ParseUnary();
565 // TODO: Implement !
566 //		case TOKEN_NOT:
567 //			return ~(uint64)_ParseUnary();
568 
569 		case TOKEN_IDENTIFIER:
570 			return _ParseFunction(token);
571 
572 		default:
573 			fTokenizer->RewindToken();
574 			return _ParseAtom();
575 	}
576 
577 	return MAPM(0);
578 }
579 
580 
581 struct Function {
582 	const char*	name;
583 	int			argumentCount;
584 	void*		function;
585 	MAPM		value;
586 };
587 
588 
589 void
590 ExpressionParser::_InitArguments(MAPM values[], int32 argumentCount)
591 {
592 	_EatToken(TOKEN_OPENING_BRACKET);
593 
594 	for (int32 i = 0; i < argumentCount; i++)
595 		values[i] = _ParseBinary();
596 
597 	_EatToken(TOKEN_CLOSING_BRACKET);
598 }
599 
600 
601 MAPM
602 ExpressionParser::_ParseFunction(const Token& token)
603 {
604 	if (token.string == "e")
605 		return _ParseFactorial(MAPM(MM_E));
606 	else if (token.string.ICompare("pi") == 0 || token.string == "π")
607 		return _ParseFactorial(MAPM(MM_PI));
608 
609 	// hard coded cases for different count of arguments
610 	// supports functions with 3 arguments at most
611 
612 	MAPM values[3];
613 
614 	if (strcasecmp("abs", token.string.String()) == 0) {
615 		_InitArguments(values, 1);
616 		return _ParseFactorial(values[0].abs());
617 	} else if (strcasecmp("acos", token.string.String()) == 0) {
618 		_InitArguments(values, 1);
619 		if (fDegreeMode)
620 			values[0] = values[0] * MM_PI / 180;
621 
622 		if (values[0] < -1 || values[0] > 1)
623 			throw ParseException("out of domain", token.position);
624 
625 		return _ParseFactorial(values[0].acos());
626 	} else if (strcasecmp("asin", token.string.String()) == 0) {
627 		_InitArguments(values, 1);
628 		if (fDegreeMode)
629 			values[0] = values[0] * MM_PI / 180;
630 
631 		if (values[0] < -1 || values[0] > 1)
632 			throw ParseException("out of domain", token.position);
633 
634 		return _ParseFactorial(values[0].asin());
635 	} else if (strcasecmp("atan", token.string.String()) == 0) {
636 		_InitArguments(values, 1);
637 		if (fDegreeMode)
638 			values[0] = values[0] * MM_PI / 180;
639 
640 		return _ParseFactorial(values[0].atan());
641 	} else if (strcasecmp("atan2", token.string.String()) == 0) {
642 		_InitArguments(values, 2);
643 
644 		if (fDegreeMode) {
645 			values[0] = values[0] * MM_PI / 180;
646 			values[1] = values[1] * MM_PI / 180;
647 		}
648 
649 		return _ParseFactorial(values[0].atan2(values[1]));
650 	} else if (strcasecmp("cbrt", token.string.String()) == 0) {
651 		_InitArguments(values, 1);
652 		return _ParseFactorial(values[0].cbrt());
653 	} else if (strcasecmp("ceil", token.string.String()) == 0) {
654 		_InitArguments(values, 1);
655 		return _ParseFactorial(values[0].ceil());
656 	} else if (strcasecmp("cos", token.string.String()) == 0) {
657 		_InitArguments(values, 1);
658 		if (fDegreeMode)
659 			values[0] = values[0] * MM_PI / 180;
660 
661 		return _ParseFactorial(values[0].cos());
662 	} else if (strcasecmp("cosh", token.string.String()) == 0) {
663 		_InitArguments(values, 1);
664 		// This function always uses radians
665 		return _ParseFactorial(values[0].cosh());
666 	} else if (strcasecmp("exp", token.string.String()) == 0) {
667 		_InitArguments(values, 1);
668 		return _ParseFactorial(values[0].exp());
669 	} else if (strcasecmp("floor", token.string.String()) == 0) {
670 		_InitArguments(values, 1);
671 		return _ParseFactorial(values[0].floor());
672 	} else if (strcasecmp("ln", token.string.String()) == 0) {
673 		_InitArguments(values, 1);
674 		if (values[0] <= 0)
675 			throw ParseException("out of domain", token.position);
676 
677 		return _ParseFactorial(values[0].log());
678 	} else if (strcasecmp("log", token.string.String()) == 0) {
679 		_InitArguments(values, 1);
680 		if (values[0] <= 0)
681 			throw ParseException("out of domain", token.position);
682 
683 		return _ParseFactorial(values[0].log10());
684 	} else if (strcasecmp("pow", token.string.String()) == 0) {
685 		_InitArguments(values, 2);
686 		return _ParseFactorial(values[0].pow(values[1]));
687 	} else if (strcasecmp("sin", token.string.String()) == 0) {
688 		_InitArguments(values, 1);
689 		if (fDegreeMode)
690 			values[0] = values[0] * MM_PI / 180;
691 
692 		return _ParseFactorial(values[0].sin());
693 	} else if (strcasecmp("sinh", token.string.String()) == 0) {
694 		_InitArguments(values, 1);
695 		// This function always uses radians
696 		return _ParseFactorial(values[0].sinh());
697 	} else if (strcasecmp("sqrt", token.string.String()) == 0) {
698 		_InitArguments(values, 1);
699 		if (values[0] < 0)
700 			throw ParseException("out of domain", token.position);
701 
702 		return _ParseFactorial(values[0].sqrt());
703 	} else if (strcasecmp("tan", token.string.String()) == 0) {
704 		_InitArguments(values, 1);
705 		if (fDegreeMode)
706 			values[0] = values[0] * MM_PI / 180;
707 
708 		MAPM divided_by_half_pi = values[0] / MM_HALF_PI;
709 		if (divided_by_half_pi.is_integer() && divided_by_half_pi.is_odd())
710 			throw ParseException("out of domain", token.position);
711 
712 		return _ParseFactorial(values[0].tan());
713 	} else if (strcasecmp("tanh", token.string.String()) == 0) {
714 		_InitArguments(values, 1);
715 		// This function always uses radians
716 		return _ParseFactorial(values[0].tanh());
717 	}
718 
719 	throw ParseException("unknown identifier", token.position);
720 }
721 
722 
723 MAPM
724 ExpressionParser::_ParseAtom()
725 {
726 	Token token = fTokenizer->NextToken();
727 	if (token.type == TOKEN_END_OF_LINE)
728 		throw ParseException("unexpected end of expression", token.position);
729 
730 	if (token.type == TOKEN_CONSTANT)
731 		return _ParseFactorial(token.value);
732 
733 	fTokenizer->RewindToken();
734 
735 	_EatToken(TOKEN_OPENING_BRACKET);
736 
737 	MAPM value = _ParseBinary();
738 
739 	_EatToken(TOKEN_CLOSING_BRACKET);
740 
741 	return _ParseFactorial(value);
742 }
743 
744 
745 MAPM
746 ExpressionParser::_ParseFactorial(MAPM value)
747 {
748 	if (fTokenizer->NextToken().type == TOKEN_FACTORIAL) {
749 		fTokenizer->RewindToken();
750 		_EatToken(TOKEN_FACTORIAL);
751 		if (value < 1000)
752 			return value.factorial();
753 		else {
754 			// Use Stirling's approximation (9 term expansion)
755 			// http://en.wikipedia.org/wiki/Stirling%27s_approximation
756 			// http://www.wolframalpha.com/input/?i=stirling%27s+series
757 			// all constants must fit in a signed long for MAPM
758 			// (LONG_MAX = 2147483647)
759 			return value.pow(value) / value.exp()
760 				* (MAPM(2) * MAPM(MM_PI) * value).sqrt()
761 				* (MAPM(1) + (MAPM(1) / (MAPM(12) * value))
762 					+ (MAPM(1) / (MAPM(288) * value.pow(2)))
763 					- (MAPM(139) / (MAPM(51840) * value.pow(3)))
764 					- (MAPM(571) / (MAPM(2488320) * value.pow(4)))
765 					+ (MAPM(163879) / (MAPM(209018880) * value.pow(5)))
766 						// 2147483647 * 35 + 84869155 = 75246796800
767 					+ (MAPM(5246819) / ((MAPM(2147483647) * MAPM(35)
768 						+ MAPM(84869155)) * value.pow(6)))
769 						// 2147483647 * 420 + 1018429860 = 902961561600
770 					- (MAPM(534703531) / ((MAPM(2147483647) * MAPM(420)
771 						+ MAPM(1018429860)) * value.pow(7)))
772 						// 2147483647 * 2 + 188163965 = 4483131259
773 						// 2147483647 * 40366 + 985018798 = 86686309913600
774 					- ((MAPM(2147483647) * MAPM(2) + MAPM(188163965))
775 						/ ((MAPM(2147483647) * MAPM(40366) + MAPM(985018798))
776 							* value.pow(8)))
777 						// 2147483647 * 201287 + 1380758682 = 432261921612371
778 						// 2147483647 * 239771232 + 1145740896
779 						// = 514904800886784000
780 					+ ((MAPM(2147483647) * MAPM(201287) + MAPM(1380758682))
781 						/ ((MAPM(2147483647) * MAPM(239771232)
782 								+ MAPM(1145740896))
783 							* value.pow(9))));
784 		}
785 	}
786 
787 	fTokenizer->RewindToken();
788 	return value;
789 }
790 
791 
792 void
793 ExpressionParser::_EatToken(int32 type)
794 {
795 	Token token = fTokenizer->NextToken();
796 	if (token.type != type) {
797 		BString expected;
798 		switch (type) {
799 			case TOKEN_IDENTIFIER:
800 				expected = "an identifier";
801 				break;
802 
803 			case TOKEN_CONSTANT:
804 				expected = "a constant";
805 				break;
806 
807 			case TOKEN_PLUS:
808 			case TOKEN_MINUS:
809 			case TOKEN_STAR:
810 			case TOKEN_MODULO:
811 			case TOKEN_POWER:
812 			case TOKEN_FACTORIAL:
813 			case TOKEN_OPENING_BRACKET:
814 			case TOKEN_CLOSING_BRACKET:
815 			case TOKEN_AND:
816 			case TOKEN_OR:
817 			case TOKEN_NOT:
818 				expected << "'" << (char)type << "'";
819 				break;
820 
821 			case TOKEN_SLASH:
822 				expected = "'/', '\\', or ':'";
823 				break;
824 
825 			case TOKEN_END_OF_LINE:
826 				expected = "'\\n'";
827 				break;
828 		}
829 		BString temp;
830 		temp << "Expected " << expected.String() << " got '" << token.string << "'";
831 		throw ParseException(temp.String(), token.position);
832 	}
833 }
834 
835 
836 status_t
837 ExpressionParser::SetSeparators(BString decimal, BString group)
838 {
839 	if (decimal == group)
840 		return B_ERROR;
841 
842 	fTokenizer->SetSeparators(decimal, group);
843 
844 	return B_OK;
845 }
846