2006-06-08 02:05:03 +04:00
|
|
|
/*
|
2009-06-28 21:10:40 +04:00
|
|
|
* Copyright 2006-2009 Haiku, Inc. All Rights Reserved.
|
2006-06-08 02:05:03 +04:00
|
|
|
* Distributed under the terms of the MIT License.
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Ingo Weinhold <bonefish@cs.tu-berlin.de>
|
|
|
|
* Stephan Aßmus <superstippi@gmx.de>
|
|
|
|
*/
|
|
|
|
|
2009-06-28 21:10:40 +04:00
|
|
|
#include <ExpressionParser.h>
|
|
|
|
|
2006-06-08 02:05:03 +04:00
|
|
|
#include <ctype.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <stdio.h>
|
2009-06-28 21:10:40 +04:00
|
|
|
#include <stdlib.h>
|
2006-06-08 02:05:03 +04:00
|
|
|
#include <string.h>
|
|
|
|
|
2009-06-28 21:10:40 +04:00
|
|
|
#include <m_apm.h>
|
2008-11-04 23:47:41 +03:00
|
|
|
|
2006-06-08 02:05:03 +04:00
|
|
|
|
2009-10-13 06:48:17 +04:00
|
|
|
static const int32 kMaxDecimalPlaces = 32;
|
2008-11-04 23:47:41 +03:00
|
|
|
|
2006-06-08 02:05:03 +04:00
|
|
|
enum {
|
2009-01-17 17:19:36 +03:00
|
|
|
TOKEN_IDENTIFIER = 0,
|
2006-06-08 02:05:03 +04:00
|
|
|
|
2009-01-17 17:19:36 +03:00
|
|
|
TOKEN_CONSTANT,
|
2006-06-08 02:05:03 +04:00
|
|
|
|
2009-01-17 17:19:36 +03:00
|
|
|
TOKEN_PLUS,
|
|
|
|
TOKEN_MINUS,
|
2006-06-08 02:05:03 +04:00
|
|
|
|
2009-01-17 17:19:36 +03:00
|
|
|
TOKEN_STAR,
|
|
|
|
TOKEN_SLASH,
|
|
|
|
TOKEN_MODULO,
|
2006-06-08 02:05:03 +04:00
|
|
|
|
2009-01-17 17:19:36 +03:00
|
|
|
TOKEN_POWER,
|
2006-06-08 02:05:03 +04:00
|
|
|
|
2009-01-17 17:19:36 +03:00
|
|
|
TOKEN_OPENING_BRACKET,
|
|
|
|
TOKEN_CLOSING_BRACKET,
|
2006-06-08 02:05:03 +04:00
|
|
|
|
2009-01-17 17:19:36 +03:00
|
|
|
TOKEN_AND,
|
|
|
|
TOKEN_OR,
|
|
|
|
TOKEN_NOT,
|
2006-06-08 02:05:03 +04:00
|
|
|
|
2009-01-17 17:19:36 +03:00
|
|
|
TOKEN_NONE,
|
|
|
|
TOKEN_END_OF_LINE
|
2006-06-08 02:05:03 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
struct Token {
|
|
|
|
Token()
|
|
|
|
: string(""),
|
|
|
|
type(TOKEN_NONE),
|
2008-11-04 23:47:41 +03:00
|
|
|
value(0),
|
2006-06-08 02:05:03 +04:00
|
|
|
position(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Token(const Token& other)
|
|
|
|
: string(other.string),
|
|
|
|
type(other.type),
|
|
|
|
value(other.value),
|
|
|
|
position(other.position)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Token(const char* string, int32 length, int32 position, int32 type)
|
|
|
|
: string(string, length),
|
|
|
|
type(type),
|
2008-11-04 23:47:41 +03:00
|
|
|
value(0),
|
2006-06-08 02:05:03 +04:00
|
|
|
position(position)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Token& operator=(const Token& other)
|
|
|
|
{
|
|
|
|
string = other.string;
|
|
|
|
type = other.type;
|
|
|
|
value = other.value;
|
|
|
|
position = other.position;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
BString string;
|
|
|
|
int32 type;
|
2008-11-04 23:47:41 +03:00
|
|
|
MAPM value;
|
2006-06-08 02:05:03 +04:00
|
|
|
|
|
|
|
int32 position;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class Tokenizer {
|
|
|
|
public:
|
|
|
|
Tokenizer()
|
|
|
|
: fString(""),
|
|
|
|
fCurrentChar(NULL),
|
|
|
|
fCurrentToken(),
|
2009-06-28 21:10:40 +04:00
|
|
|
fReuseToken(false),
|
|
|
|
fHexSupport(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetSupportHexInput(bool enabled)
|
2006-06-08 02:05:03 +04:00
|
|
|
{
|
2009-06-28 21:10:40 +04:00
|
|
|
fHexSupport = enabled;
|
2006-06-08 02:05:03 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void SetTo(const char* string)
|
|
|
|
{
|
|
|
|
fString = string;
|
|
|
|
fCurrentChar = fString.String();
|
|
|
|
fCurrentToken = Token();
|
|
|
|
fReuseToken = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Token& NextToken()
|
|
|
|
{
|
|
|
|
if (fCurrentToken.type == TOKEN_END_OF_LINE)
|
|
|
|
return fCurrentToken;
|
|
|
|
|
|
|
|
if (fReuseToken) {
|
|
|
|
fReuseToken = false;
|
|
|
|
//printf("next token (recycled): '%s'\n", fCurrentToken.string.String());
|
2008-01-27 16:05:19 +03:00
|
|
|
return fCurrentToken;
|
2006-06-08 02:05:03 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
while (*fCurrentChar != 0 && isspace(*fCurrentChar))
|
|
|
|
fCurrentChar++;
|
|
|
|
|
|
|
|
if (*fCurrentChar == 0)
|
|
|
|
return fCurrentToken = Token("", 0, _CurrentPos(), TOKEN_END_OF_LINE);
|
|
|
|
|
|
|
|
bool decimal = *fCurrentChar == '.' || *fCurrentChar == ',';
|
|
|
|
|
|
|
|
if (decimal || isdigit(*fCurrentChar)) {
|
2009-06-28 21:10:40 +04:00
|
|
|
if (fHexSupport && *fCurrentChar == '0' && fCurrentChar[1] == 'x')
|
|
|
|
return _ParseHexNumber();
|
2006-06-08 02:05:03 +04:00
|
|
|
|
|
|
|
BString temp;
|
|
|
|
|
|
|
|
const char* begin = fCurrentChar;
|
|
|
|
while (*fCurrentChar != 0) {
|
|
|
|
if (!isdigit(*fCurrentChar)) {
|
|
|
|
if (!(*fCurrentChar == '.' || *fCurrentChar == ','
|
|
|
|
|| *fCurrentChar == 'e' || *fCurrentChar == 'E'))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (*fCurrentChar == ',')
|
|
|
|
temp << '.';
|
|
|
|
else
|
|
|
|
temp << *fCurrentChar;
|
|
|
|
fCurrentChar++;
|
|
|
|
}
|
|
|
|
int32 length = fCurrentChar - begin;
|
2008-11-04 23:47:41 +03:00
|
|
|
BString test = temp;
|
|
|
|
test << "&_";
|
2006-06-08 02:05:03 +04:00
|
|
|
double value;
|
|
|
|
char t[2];
|
2008-11-04 23:47:41 +03:00
|
|
|
int32 matches = sscanf(test.String(), "%lf&%s", &value, t);
|
|
|
|
if (matches != 2) {
|
|
|
|
throw ParseException("error in constant",
|
|
|
|
_CurrentPos() - length);
|
|
|
|
}
|
2006-06-08 02:05:03 +04:00
|
|
|
|
2008-11-04 23:47:41 +03:00
|
|
|
fCurrentToken = Token(begin, length, _CurrentPos() - length,
|
|
|
|
TOKEN_CONSTANT);
|
|
|
|
fCurrentToken.value = temp.String();
|
2006-06-08 02:05:03 +04:00
|
|
|
|
2009-01-17 17:21:53 +03:00
|
|
|
} else if (isalpha(*fCurrentChar) && *fCurrentChar != 'x') {
|
2006-06-08 02:05:03 +04:00
|
|
|
const char* begin = fCurrentChar;
|
2008-11-04 23:47:41 +03:00
|
|
|
while (*fCurrentChar != 0 && (isalpha(*fCurrentChar)
|
|
|
|
|| isdigit(*fCurrentChar))) {
|
2006-06-08 02:05:03 +04:00
|
|
|
fCurrentChar++;
|
2008-11-04 23:47:41 +03:00
|
|
|
}
|
2006-06-08 02:05:03 +04:00
|
|
|
int32 length = fCurrentChar - begin;
|
2008-11-04 23:47:41 +03:00
|
|
|
fCurrentToken = Token(begin, length, _CurrentPos() - length,
|
|
|
|
TOKEN_IDENTIFIER);
|
2006-06-08 02:05:03 +04:00
|
|
|
|
|
|
|
} else {
|
2009-01-17 17:19:36 +03:00
|
|
|
int32 type = TOKEN_NONE;
|
|
|
|
|
2006-06-08 02:05:03 +04:00
|
|
|
switch (*fCurrentChar) {
|
|
|
|
case '+':
|
2009-01-17 17:19:36 +03:00
|
|
|
type = TOKEN_PLUS;
|
|
|
|
break;
|
2006-06-08 02:05:03 +04:00
|
|
|
case '-':
|
2009-01-17 17:19:36 +03:00
|
|
|
type = TOKEN_MINUS;
|
|
|
|
break;
|
2006-06-08 02:05:03 +04:00
|
|
|
case '*':
|
2009-01-17 17:19:36 +03:00
|
|
|
type = TOKEN_STAR;
|
|
|
|
break;
|
2006-06-08 02:05:03 +04:00
|
|
|
case '/':
|
2009-01-17 17:19:36 +03:00
|
|
|
case '\\':
|
|
|
|
case ':':
|
|
|
|
type = TOKEN_SLASH;
|
|
|
|
break;
|
2006-06-08 02:05:03 +04:00
|
|
|
|
|
|
|
case '%':
|
2009-01-17 17:19:36 +03:00
|
|
|
type = TOKEN_MODULO;
|
|
|
|
break;
|
|
|
|
case '^':
|
|
|
|
type = TOKEN_POWER;
|
|
|
|
break;
|
2006-06-08 02:05:03 +04:00
|
|
|
|
|
|
|
case '(':
|
2009-01-17 17:19:36 +03:00
|
|
|
type = TOKEN_OPENING_BRACKET;
|
|
|
|
break;
|
2006-06-08 02:05:03 +04:00
|
|
|
case ')':
|
2009-01-17 17:19:36 +03:00
|
|
|
type = TOKEN_CLOSING_BRACKET;
|
|
|
|
break;
|
2006-06-08 02:05:03 +04:00
|
|
|
|
|
|
|
case '&':
|
2009-01-17 17:19:36 +03:00
|
|
|
type = TOKEN_AND;
|
|
|
|
break;
|
2006-06-08 02:05:03 +04:00
|
|
|
case '|':
|
2009-01-17 17:19:36 +03:00
|
|
|
type = TOKEN_OR;
|
|
|
|
break;
|
2006-06-08 02:05:03 +04:00
|
|
|
case '~':
|
2009-01-17 17:19:36 +03:00
|
|
|
type = TOKEN_NOT;
|
2006-06-08 02:05:03 +04:00
|
|
|
break;
|
2009-01-17 17:19:36 +03:00
|
|
|
|
|
|
|
case '\n':
|
|
|
|
type = TOKEN_END_OF_LINE;
|
|
|
|
break;
|
|
|
|
|
2009-06-28 21:10:40 +04:00
|
|
|
case 'x':
|
|
|
|
if (!fHexSupport) {
|
|
|
|
type = TOKEN_STAR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// fall through
|
|
|
|
|
2006-06-08 02:05:03 +04:00
|
|
|
default:
|
|
|
|
throw ParseException("unexpected character", _CurrentPos());
|
|
|
|
}
|
2009-01-17 17:19:36 +03:00
|
|
|
fCurrentToken = Token(fCurrentChar, 1, _CurrentPos(), type);
|
|
|
|
fCurrentChar++;
|
2006-06-08 02:05:03 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
//printf("next token: '%s'\n", fCurrentToken.string.String());
|
|
|
|
return fCurrentToken;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RewindToken()
|
|
|
|
{
|
|
|
|
fReuseToken = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2009-06-28 21:10:40 +04:00
|
|
|
static bool _IsHexDigit(char c)
|
|
|
|
{
|
|
|
|
return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
|
|
|
}
|
|
|
|
|
|
|
|
Token& _ParseHexNumber()
|
|
|
|
{
|
|
|
|
const char* begin = fCurrentChar;
|
|
|
|
fCurrentChar += 2;
|
|
|
|
// skip "0x"
|
|
|
|
|
|
|
|
if (!_IsHexDigit(*fCurrentChar))
|
|
|
|
throw ParseException("expected hex digit", _CurrentPos());
|
|
|
|
|
|
|
|
fCurrentChar++;
|
|
|
|
while (_IsHexDigit(*fCurrentChar))
|
|
|
|
fCurrentChar++;
|
|
|
|
|
|
|
|
int32 length = fCurrentChar - begin;
|
|
|
|
fCurrentToken = Token(begin, length, _CurrentPos() - length,
|
|
|
|
TOKEN_CONSTANT);
|
|
|
|
|
|
|
|
// MAPM has no conversion from long long, so we need to improvise.
|
|
|
|
uint64 value = strtoll(fCurrentToken.string.String(), NULL, 0);
|
|
|
|
if (value <= 0x7fffffff) {
|
|
|
|
fCurrentToken.value = (long)value;
|
|
|
|
} else {
|
|
|
|
fCurrentToken.value = (int)(value >> 60);
|
|
|
|
fCurrentToken.value *= 1 << 30;
|
|
|
|
fCurrentToken.value += (int)((value >> 30) & 0x3fffffff);
|
|
|
|
fCurrentToken.value *= 1 << 30;
|
|
|
|
fCurrentToken.value += (int)(value& 0x3fffffff);
|
|
|
|
}
|
|
|
|
|
|
|
|
return fCurrentToken;
|
|
|
|
}
|
|
|
|
|
2006-06-08 02:05:03 +04:00
|
|
|
int32 _CurrentPos() const
|
|
|
|
{
|
|
|
|
return fCurrentChar - fString.String();
|
|
|
|
}
|
|
|
|
|
|
|
|
BString fString;
|
|
|
|
const char* fCurrentChar;
|
|
|
|
Token fCurrentToken;
|
|
|
|
bool fReuseToken;
|
2009-06-28 21:10:40 +04:00
|
|
|
bool fHexSupport;
|
2006-06-08 02:05:03 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
ExpressionParser::ExpressionParser()
|
|
|
|
: fTokenizer(new Tokenizer())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ExpressionParser::~ExpressionParser()
|
|
|
|
{
|
|
|
|
delete fTokenizer;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-06-28 21:10:40 +04:00
|
|
|
void
|
|
|
|
ExpressionParser::SetSupportHexInput(bool enabled)
|
|
|
|
{
|
|
|
|
fTokenizer->SetSupportHexInput(enabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-04 23:47:41 +03:00
|
|
|
BString
|
2006-06-08 02:05:03 +04:00
|
|
|
ExpressionParser::Evaluate(const char* expressionString)
|
|
|
|
{
|
|
|
|
fTokenizer->SetTo(expressionString);
|
|
|
|
|
2008-11-04 23:47:41 +03:00
|
|
|
MAPM value = _ParseBinary();
|
2006-06-08 02:05:03 +04:00
|
|
|
Token token = fTokenizer->NextToken();
|
|
|
|
if (token.type != TOKEN_END_OF_LINE)
|
|
|
|
throw ParseException("parse error", token.position);
|
|
|
|
|
2008-11-04 23:47:41 +03:00
|
|
|
if (value == 0)
|
|
|
|
return BString("0");
|
|
|
|
|
2009-10-13 06:48:17 +04:00
|
|
|
char* buffer = value.toFixPtStringExp(kMaxDecimalPlaces, '.', 0, 0);
|
2008-11-04 23:47:41 +03:00
|
|
|
if (buffer == NULL)
|
|
|
|
throw ParseException("out of memory", 0);
|
|
|
|
|
|
|
|
// remove surplus zeros
|
|
|
|
int32 lastChar = strlen(buffer) - 1;
|
|
|
|
if (strchr(buffer, '.')) {
|
|
|
|
while (buffer[lastChar] == '0')
|
|
|
|
lastChar--;
|
|
|
|
if (buffer[lastChar] == '.')
|
|
|
|
lastChar--;
|
|
|
|
}
|
|
|
|
|
2009-10-13 06:48:17 +04:00
|
|
|
BString result(buffer, lastChar + 1);
|
|
|
|
free(buffer);
|
2008-11-04 23:47:41 +03:00
|
|
|
return result;
|
2006-06-08 02:05:03 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-06-28 21:10:40 +04:00
|
|
|
int64
|
|
|
|
ExpressionParser::EvaluateToInt64(const char* expressionString)
|
|
|
|
{
|
|
|
|
fTokenizer->SetTo(expressionString);
|
|
|
|
|
|
|
|
MAPM value = _ParseBinary();
|
|
|
|
Token token = fTokenizer->NextToken();
|
|
|
|
if (token.type != TOKEN_END_OF_LINE)
|
|
|
|
throw ParseException("parse error", token.position);
|
|
|
|
|
|
|
|
char buffer[128];
|
|
|
|
value.toIntegerString(buffer);
|
|
|
|
|
|
|
|
return strtoll(buffer, NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double
|
|
|
|
ExpressionParser::EvaluateToDouble(const char* expressionString)
|
|
|
|
{
|
|
|
|
fTokenizer->SetTo(expressionString);
|
|
|
|
|
|
|
|
MAPM value = _ParseBinary();
|
|
|
|
Token token = fTokenizer->NextToken();
|
|
|
|
if (token.type != TOKEN_END_OF_LINE)
|
|
|
|
throw ParseException("parse error", token.position);
|
|
|
|
|
|
|
|
char buffer[1024];
|
|
|
|
value.toString(buffer, sizeof(buffer) - 4);
|
|
|
|
|
|
|
|
return strtod(buffer, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-04 23:47:41 +03:00
|
|
|
MAPM
|
2006-06-08 02:05:03 +04:00
|
|
|
ExpressionParser::_ParseBinary()
|
|
|
|
{
|
2008-11-04 23:47:41 +03:00
|
|
|
return _ParseSum();
|
|
|
|
// binary operation appearantly not supported by m_apm library,
|
|
|
|
// should not be too hard to implement though....
|
|
|
|
|
|
|
|
// double value = _ParseSum();
|
|
|
|
//
|
|
|
|
// while (true) {
|
|
|
|
// Token token = fTokenizer->NextToken();
|
|
|
|
// switch (token.type) {
|
|
|
|
// case TOKEN_AND:
|
|
|
|
// value = (uint64)value & (uint64)_ParseSum();
|
|
|
|
// break;
|
|
|
|
// case TOKEN_OR:
|
|
|
|
// value = (uint64)value | (uint64)_ParseSum();
|
|
|
|
// break;
|
|
|
|
//
|
|
|
|
// default:
|
|
|
|
// fTokenizer->RewindToken();
|
|
|
|
// return value;
|
|
|
|
// }
|
|
|
|
// }
|
2006-06-08 02:05:03 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-04 23:47:41 +03:00
|
|
|
MAPM
|
2006-06-08 02:05:03 +04:00
|
|
|
ExpressionParser::_ParseSum()
|
|
|
|
{
|
|
|
|
// TODO: check isnan()...
|
2008-11-04 23:47:41 +03:00
|
|
|
MAPM value = _ParseProduct();
|
2006-06-08 02:05:03 +04:00
|
|
|
|
|
|
|
while (true) {
|
|
|
|
Token token = fTokenizer->NextToken();
|
|
|
|
switch (token.type) {
|
|
|
|
case TOKEN_PLUS:
|
|
|
|
value = value + _ParseProduct();
|
|
|
|
break;
|
|
|
|
case TOKEN_MINUS:
|
|
|
|
value = value - _ParseProduct();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
fTokenizer->RewindToken();
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-04 23:47:41 +03:00
|
|
|
MAPM
|
2006-06-08 02:05:03 +04:00
|
|
|
ExpressionParser::_ParseProduct()
|
|
|
|
{
|
|
|
|
// TODO: check isnan()...
|
2008-11-04 23:47:41 +03:00
|
|
|
MAPM value = _ParsePower();
|
2006-06-08 02:05:03 +04:00
|
|
|
|
|
|
|
while (true) {
|
|
|
|
Token token = fTokenizer->NextToken();
|
|
|
|
switch (token.type) {
|
|
|
|
case TOKEN_STAR:
|
|
|
|
value = value * _ParsePower();
|
|
|
|
break;
|
|
|
|
case TOKEN_SLASH: {
|
2008-11-04 23:47:41 +03:00
|
|
|
MAPM rhs = _ParsePower();
|
|
|
|
if (rhs == MAPM(0))
|
2006-06-08 02:05:03 +04:00
|
|
|
throw ParseException("division by zero", token.position);
|
|
|
|
value = value / rhs;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TOKEN_MODULO: {
|
2008-11-04 23:47:41 +03:00
|
|
|
MAPM rhs = _ParsePower();
|
|
|
|
if (rhs == MAPM(0))
|
2006-06-08 02:05:03 +04:00
|
|
|
throw ParseException("modulo by zero", token.position);
|
2008-11-04 23:47:41 +03:00
|
|
|
value = value % rhs;
|
2006-06-08 02:05:03 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
fTokenizer->RewindToken();
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-04 23:47:41 +03:00
|
|
|
MAPM
|
2006-06-08 02:05:03 +04:00
|
|
|
ExpressionParser::_ParsePower()
|
|
|
|
{
|
2008-11-04 23:47:41 +03:00
|
|
|
MAPM value = _ParseUnary();
|
2006-06-08 02:05:03 +04:00
|
|
|
|
|
|
|
while (true) {
|
|
|
|
Token token = fTokenizer->NextToken();
|
|
|
|
if (token.type != TOKEN_POWER) {
|
|
|
|
fTokenizer->RewindToken();
|
|
|
|
return value;
|
|
|
|
}
|
2008-11-17 01:15:13 +03:00
|
|
|
value = value.pow(_ParseUnary());
|
2006-06-08 02:05:03 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-04 23:47:41 +03:00
|
|
|
MAPM
|
2006-06-08 02:05:03 +04:00
|
|
|
ExpressionParser::_ParseUnary()
|
|
|
|
{
|
|
|
|
Token token = fTokenizer->NextToken();
|
|
|
|
if (token.type == TOKEN_END_OF_LINE)
|
|
|
|
throw ParseException("unexpected end of expression", token.position);
|
|
|
|
|
|
|
|
switch (token.type) {
|
|
|
|
case TOKEN_PLUS:
|
|
|
|
return _ParseUnary();
|
|
|
|
case TOKEN_MINUS:
|
|
|
|
return -_ParseUnary();
|
2008-11-04 23:47:41 +03:00
|
|
|
// TODO: Implement !
|
|
|
|
// case TOKEN_NOT:
|
|
|
|
// return ~(uint64)_ParseUnary();
|
2006-06-08 02:05:03 +04:00
|
|
|
|
|
|
|
case TOKEN_IDENTIFIER:
|
|
|
|
return _ParseFunction(token);
|
|
|
|
|
|
|
|
default:
|
|
|
|
fTokenizer->RewindToken();
|
|
|
|
return _ParseAtom();
|
|
|
|
}
|
|
|
|
|
2008-11-04 23:47:41 +03:00
|
|
|
return MAPM(0);
|
2006-06-08 02:05:03 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct Function {
|
|
|
|
const char* name;
|
|
|
|
int argumentCount;
|
|
|
|
void* function;
|
2008-11-04 23:47:41 +03:00
|
|
|
MAPM value;
|
2006-06-08 02:05:03 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2008-11-04 23:47:41 +03:00
|
|
|
void
|
|
|
|
ExpressionParser::_InitArguments(MAPM values[], int32 argumentCount)
|
2006-06-08 02:05:03 +04:00
|
|
|
{
|
|
|
|
_EatToken(TOKEN_OPENING_BRACKET);
|
|
|
|
|
2008-11-04 23:47:41 +03:00
|
|
|
for (int32 i = 0; i < argumentCount; i++)
|
2006-06-08 02:05:03 +04:00
|
|
|
values[i] = _ParseBinary();
|
|
|
|
|
|
|
|
_EatToken(TOKEN_CLOSING_BRACKET);
|
2008-11-04 23:47:41 +03:00
|
|
|
}
|
2006-06-08 02:05:03 +04:00
|
|
|
|
|
|
|
|
2008-11-04 23:47:41 +03:00
|
|
|
MAPM
|
|
|
|
ExpressionParser::_ParseFunction(const Token& token)
|
|
|
|
{
|
|
|
|
if (strcasecmp("e", token.string.String()) == 0)
|
|
|
|
return MAPM(M_E);
|
|
|
|
else if (strcasecmp("pi", token.string.String()) == 0)
|
|
|
|
return MAPM(M_PI);
|
|
|
|
|
|
|
|
// hard coded cases for different count of arguments
|
|
|
|
// supports functions with 3 arguments at most
|
|
|
|
|
|
|
|
MAPM values[3];
|
|
|
|
|
|
|
|
if (strcasecmp("abs", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 1);
|
|
|
|
return values[0].abs();
|
|
|
|
} else if (strcasecmp("acos", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 1);
|
|
|
|
return values[0].acos();
|
|
|
|
} else if (strcasecmp("asin", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 1);
|
|
|
|
return values[0].asin();
|
|
|
|
} else if (strcasecmp("atan", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 1);
|
|
|
|
return values[0].atan();
|
|
|
|
} else if (strcasecmp("atan2", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 2);
|
|
|
|
return values[0].atan2(values[1]);
|
|
|
|
} else if (strcasecmp("ceil", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 1);
|
|
|
|
return values[0].ceil();
|
|
|
|
} else if (strcasecmp("cos", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 1);
|
|
|
|
return values[0].cos();
|
|
|
|
} else if (strcasecmp("cosh", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 1);
|
|
|
|
return values[0].cosh();
|
|
|
|
} else if (strcasecmp("exp", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 1);
|
|
|
|
return values[0].exp();
|
|
|
|
} else if (strcasecmp("floor", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 1);
|
|
|
|
return values[0].floor();
|
|
|
|
} else if (strcasecmp("log", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 1);
|
|
|
|
return values[0].log();
|
|
|
|
} else if (strcasecmp("log10", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 1);
|
|
|
|
return values[0].log10();
|
|
|
|
} else if (strcasecmp("pow", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 2);
|
|
|
|
return values[0].pow(values[1]);
|
|
|
|
} else if (strcasecmp("sin", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 1);
|
|
|
|
return values[0].sin();
|
|
|
|
} else if (strcasecmp("sinh", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 1);
|
|
|
|
return values[0].sinh();
|
|
|
|
} else if (strcasecmp("sqrt", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 1);
|
|
|
|
return values[0].sqrt();
|
|
|
|
} else if (strcasecmp("tan", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 1);
|
|
|
|
return values[0].tan();
|
|
|
|
} else if (strcasecmp("tanh", token.string.String()) == 0) {
|
|
|
|
_InitArguments(values, 1);
|
|
|
|
return values[0].tanh();
|
2006-06-08 02:05:03 +04:00
|
|
|
}
|
2008-11-04 23:47:41 +03:00
|
|
|
|
|
|
|
throw ParseException("unknown identifier", token.position);
|
2006-06-08 02:05:03 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-04 23:47:41 +03:00
|
|
|
MAPM
|
2006-06-08 02:05:03 +04:00
|
|
|
ExpressionParser::_ParseAtom()
|
|
|
|
{
|
|
|
|
Token token = fTokenizer->NextToken();
|
|
|
|
if (token.type == TOKEN_END_OF_LINE)
|
|
|
|
throw ParseException("unexpected end of expression", token.position);
|
|
|
|
|
|
|
|
if (token.type == TOKEN_CONSTANT)
|
|
|
|
return token.value;
|
|
|
|
|
|
|
|
fTokenizer->RewindToken();
|
|
|
|
|
|
|
|
_EatToken(TOKEN_OPENING_BRACKET);
|
|
|
|
|
2008-11-04 23:47:41 +03:00
|
|
|
MAPM value = _ParseBinary();
|
2006-06-08 02:05:03 +04:00
|
|
|
|
|
|
|
_EatToken(TOKEN_CLOSING_BRACKET);
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ExpressionParser::_EatToken(int32 type)
|
|
|
|
{
|
|
|
|
Token token = fTokenizer->NextToken();
|
|
|
|
if (token.type != type) {
|
|
|
|
BString temp("expected '");
|
|
|
|
temp << (char)type << "' got '" << token.string << "'";
|
|
|
|
throw ParseException(temp.String(), token.position);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|