From 72654b61cc5db718593f0bffe3149e09b63a9e5a Mon Sep 17 00:00:00 2001 From: Rene Gollent Date: Sat, 25 Oct 2014 16:51:09 -0400 Subject: [PATCH] Debugger: Add initial skeleton for a C-style expression evaluator. - Adds a stripped down version of ExpressionParser from libshared, which will eventually be modified to support various other capabilities needed for debug expressions. Not yet used or complete. --- src/apps/debugger/Jamfile | 5 + .../CLanguageExpressionEvaluator.cpp | 626 ++++++++++++++++++ .../CLanguageExpressionEvaluator.h | 71 ++ 3 files changed, 702 insertions(+) create mode 100644 src/apps/debugger/source_language/expression_evaluators/CLanguageExpressionEvaluator.cpp create mode 100644 src/apps/debugger/source_language/expression_evaluators/CLanguageExpressionEvaluator.h diff --git a/src/apps/debugger/Jamfile b/src/apps/debugger/Jamfile index e5d96e2fc3..300b372547 100644 --- a/src/apps/debugger/Jamfile +++ b/src/apps/debugger/Jamfile @@ -6,6 +6,7 @@ UseHeaders [ FDirName $(HAIKU_TOP) headers compatibility bsd ] : true ; UseHeaders [ FDirName $(TARGET_COMMON_DEBUG_OBJECT_DIR_$(TARGET_PACKAGING_ARCH)) system kernel ] ; +UseLibraryHeaders mapm ; UsePrivateHeaders app debug interface kernel package shared libroot ; UsePrivateSystemHeaders ; @@ -27,6 +28,7 @@ SEARCH_SOURCE += [ FDirName $(SUBDIR) model ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) settings ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) settings generic ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) source_language ] ; +SEARCH_SOURCE += [ FDirName $(SUBDIR) source_language expression_evaluators ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) types ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface cli ] ; @@ -212,6 +214,9 @@ Application Debugger : UnsupportedLanguage.cpp X86AssemblyLanguage.cpp + # source_language/expression_evaluators + CLanguageExpressionEvaluator.cpp + # types ArrayIndexPath.cpp TargetAddressRangeList.cpp diff --git a/src/apps/debugger/source_language/expression_evaluators/CLanguageExpressionEvaluator.cpp b/src/apps/debugger/source_language/expression_evaluators/CLanguageExpressionEvaluator.cpp new file mode 100644 index 0000000000..3632652d74 --- /dev/null +++ b/src/apps/debugger/source_language/expression_evaluators/CLanguageExpressionEvaluator.cpp @@ -0,0 +1,626 @@ +/* + * Copyright 2006-2014 Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + * Rene Gollent + * John Scipione + * Ingo Weinhold + */ + +#include "CLanguageExpressionEvaluator.h" + +#include +#include +#include +#include +#include + +#include + + +static const int32 kMaxDecimalPlaces = 32; + + +enum { + TOKEN_NONE = 0, + TOKEN_IDENTIFIER, + TOKEN_CONSTANT, + + TOKEN_END_OF_LINE = '\n', + + TOKEN_PLUS = '+', + TOKEN_MINUS = '-', + + TOKEN_STAR = '*', + TOKEN_SLASH = '/', + TOKEN_MODULO = '%', + + TOKEN_POWER = '^', + + TOKEN_OPENING_BRACKET = '(', + TOKEN_CLOSING_BRACKET = ')', + + TOKEN_AND = '&', + TOKEN_OR = '|', + TOKEN_NOT = '~', + TOKEN_EQ = '=', + TOKEN_GE = '>', + TOKEN_LE = '<' +}; + + +struct CLanguageExpressionEvaluator::Token { + Token() + : string(""), + type(TOKEN_NONE), + value(0), + 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), + value(0), + 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; + MAPM value; + + int32 position; +}; + + +class CLanguageExpressionEvaluator::Tokenizer { + public: + Tokenizer() + : fString(""), + fCurrentChar(NULL), + fCurrentToken(), + fReuseToken(false), + fHexSupport(false) + { + } + + void SetSupportHexInput(bool enabled) + { + fHexSupport = enabled; + } + + 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()); + return fCurrentToken; + } + + 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)) { + if (fHexSupport && *fCurrentChar == '0' && fCurrentChar[1] == 'x') + return _ParseHexNumber(); + + BString temp; + + const char* begin = fCurrentChar; + + // optional digits before the comma + while (isdigit(*fCurrentChar)) { + temp << *fCurrentChar; + fCurrentChar++; + } + + // optional post comma part + // (required if there are no digits before the comma) + if (*fCurrentChar == '.' || *fCurrentChar == ',') { + temp << '.'; + fCurrentChar++; + + // optional post comma digits + while (isdigit(*fCurrentChar)) { + temp << *fCurrentChar; + fCurrentChar++; + } + } + + // optional exponent part + if (*fCurrentChar == 'E') { + temp << *fCurrentChar; + fCurrentChar++; + + // optional exponent sign + if (*fCurrentChar == '+' || *fCurrentChar == '-') { + temp << *fCurrentChar; + fCurrentChar++; + } + + // required exponent digits + if (!isdigit(*fCurrentChar)) { + throw ParseException("missing exponent in constant", + fCurrentChar - begin); + } + + while (isdigit(*fCurrentChar)) { + temp << *fCurrentChar; + fCurrentChar++; + } + } + + int32 length = fCurrentChar - begin; + BString test = temp; + test << "&_"; + double value; + char t[2]; + int32 matches = sscanf(test.String(), "%lf&%s", &value, t); + if (matches != 2) { + throw ParseException("error in constant", + _CurrentPos() - length); + } + + fCurrentToken = Token(begin, length, _CurrentPos() - length, + TOKEN_CONSTANT); + fCurrentToken.value = temp.String(); + } else if (isalpha(*fCurrentChar) && *fCurrentChar != 'x') { + const char* begin = fCurrentChar; + while (*fCurrentChar != 0 && (isalpha(*fCurrentChar) + || isdigit(*fCurrentChar))) { + fCurrentChar++; + } + int32 length = fCurrentChar - begin; + fCurrentToken = Token(begin, length, _CurrentPos() - length, + TOKEN_IDENTIFIER); + } else if (strncmp(fCurrentChar, "π", 2) == 0) { + fCurrentToken = Token(fCurrentChar, 2, _CurrentPos() - 1, + TOKEN_IDENTIFIER); + fCurrentChar += 2; + } else { + int32 type = TOKEN_NONE; + + switch (*fCurrentChar) { + case TOKEN_PLUS: + case TOKEN_MINUS: + case TOKEN_STAR: + case TOKEN_SLASH: + case TOKEN_MODULO: + case TOKEN_POWER: + case TOKEN_OPENING_BRACKET: + case TOKEN_CLOSING_BRACKET: + case TOKEN_AND: + case TOKEN_OR: + case TOKEN_NOT: + case TOKEN_EQ: + case TOKEN_GE: + case TOKEN_LE: + case TOKEN_END_OF_LINE: + type = *fCurrentChar; + break; + + case '\\': + case ':': + type = TOKEN_SLASH; + break; + + case 'x': + if (!fHexSupport) { + type = TOKEN_STAR; + break; + } + // fall through + + default: + throw ParseException("unexpected character", _CurrentPos()); + } + fCurrentToken = Token(fCurrentChar, 1, _CurrentPos(), type); + fCurrentChar++; + } + +//printf("next token: '%s'\n", fCurrentToken.string.String()); + return fCurrentToken; + } + + void RewindToken() + { + fReuseToken = true; + } + + private: + 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; + } + + int32 _CurrentPos() const + { + return fCurrentChar - fString.String(); + } + + BString fString; + const char* fCurrentChar; + Token fCurrentToken; + bool fReuseToken; + bool fHexSupport; +}; + + +CLanguageExpressionEvaluator::CLanguageExpressionEvaluator() + : fTokenizer(new Tokenizer()) +{ +} + + +CLanguageExpressionEvaluator::~CLanguageExpressionEvaluator() +{ + delete fTokenizer; +} + + +void +CLanguageExpressionEvaluator::SetSupportHexInput(bool enabled) +{ + fTokenizer->SetSupportHexInput(enabled); +} + + +BString +CLanguageExpressionEvaluator::Evaluate(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); + + if (value == 0) + return BString("0"); + + char* buffer = value.toFixPtStringExp(kMaxDecimalPlaces, '.', 0, 0); + 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--; + } + + BString result(buffer, lastChar + 1); + free(buffer); + return result; +} + + +int64 +CLanguageExpressionEvaluator::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 +CLanguageExpressionEvaluator::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); +} + + +MAPM +CLanguageExpressionEvaluator::_ParseBinary() +{ + 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; +// } +// } +} + + +MAPM +CLanguageExpressionEvaluator::_ParseSum() +{ + // TODO: check isnan()... + MAPM value = _ParseProduct(); + + 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; + } + } +} + + +MAPM +CLanguageExpressionEvaluator::_ParseProduct() +{ + // TODO: check isnan()... + MAPM value = _ParsePower(); + + while (true) { + Token token = fTokenizer->NextToken(); + switch (token.type) { + case TOKEN_STAR: + value = value * _ParsePower(); + break; + case TOKEN_SLASH: { + MAPM rhs = _ParsePower(); + if (rhs == MAPM(0)) + throw ParseException("division by zero", token.position); + value = value / rhs; + break; + } + case TOKEN_MODULO: { + MAPM rhs = _ParsePower(); + if (rhs == MAPM(0)) + throw ParseException("modulo by zero", token.position); + value = value % rhs; + break; + } + + default: + fTokenizer->RewindToken(); + return value; + } + } +} + + +MAPM +CLanguageExpressionEvaluator::_ParsePower() +{ + MAPM value = _ParseUnary(); + + while (true) { + Token token = fTokenizer->NextToken(); + if (token.type != TOKEN_POWER) { + fTokenizer->RewindToken(); + return value; + } + value = value.pow(_ParseUnary()); + } +} + + +MAPM +CLanguageExpressionEvaluator::_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(); +// TODO: Implement ! +// case TOKEN_NOT: +// return ~(uint64)_ParseUnary(); + + case TOKEN_IDENTIFIER: + return _ParseIdentifier(); + + default: + fTokenizer->RewindToken(); + return _ParseAtom(); + } + + return MAPM(0); +} + + +struct Function { + const char* name; + int argumentCount; + void* function; + MAPM value; +}; + + +MAPM +CLanguageExpressionEvaluator::_ParseIdentifier() +{ + throw ParseException("Identifiers not implemented", 0); + + return MAPM(0); +} + + +void +CLanguageExpressionEvaluator::_InitArguments(MAPM values[], int32 argumentCount) +{ + _EatToken(TOKEN_OPENING_BRACKET); + + for (int32 i = 0; i < argumentCount; i++) + values[i] = _ParseBinary(); + + _EatToken(TOKEN_CLOSING_BRACKET); +} + + +MAPM +CLanguageExpressionEvaluator::_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); + + MAPM value = _ParseBinary(); + + _EatToken(TOKEN_CLOSING_BRACKET); + + return value; +} + + +void +CLanguageExpressionEvaluator::_EatToken(int32 type) +{ + Token token = fTokenizer->NextToken(); + if (token.type != type) { + BString expected; + switch (type) { + case TOKEN_IDENTIFIER: + expected = "an identifier"; + break; + + case TOKEN_CONSTANT: + expected = "a constant"; + break; + + case TOKEN_PLUS: + case TOKEN_MINUS: + case TOKEN_STAR: + case TOKEN_MODULO: + case TOKEN_POWER: + case TOKEN_OPENING_BRACKET: + case TOKEN_CLOSING_BRACKET: + case TOKEN_AND: + case TOKEN_OR: + case TOKEN_NOT: + case TOKEN_EQ: + case TOKEN_GE: + case TOKEN_LE: + expected << "'" << (char)type << "'"; + break; + + case TOKEN_SLASH: + expected = "'/', '\\', or ':'"; + break; + + case TOKEN_END_OF_LINE: + expected = "'\\n'"; + break; + } + BString temp; + temp << "Expected " << expected.String() << " got '" << token.string << "'"; + throw ParseException(temp.String(), token.position); + } +} diff --git a/src/apps/debugger/source_language/expression_evaluators/CLanguageExpressionEvaluator.h b/src/apps/debugger/source_language/expression_evaluators/CLanguageExpressionEvaluator.h new file mode 100644 index 0000000000..b87a887c4d --- /dev/null +++ b/src/apps/debugger/source_language/expression_evaluators/CLanguageExpressionEvaluator.h @@ -0,0 +1,71 @@ +/* + * Copyright 2006-2014 Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + * Rene Gollent + * John Scipione + * Ingo Weinhold + */ +#ifndef C_LANGUAGE_EXPRESSION_EVALUATOR_H +#define C_LANGUAGE_EXPRESSION_EVALUATOR_H + + +#include + + +class ParseException { + public: + ParseException(const char* message, int32 position) + : message(message), + position(position) + { + } + + ParseException(const ParseException& other) + : message(other.message), + position(other.position) + { + } + + BString message; + int32 position; +}; + +struct Function; +class MAPM; + +class CLanguageExpressionEvaluator { + + public: + CLanguageExpressionEvaluator(); + ~CLanguageExpressionEvaluator(); + + void SetSupportHexInput(bool enabled); + + BString Evaluate(const char* expressionString); + int64 EvaluateToInt64(const char* expressionString); + double EvaluateToDouble(const char* expressionString); + + private: + struct Token; + class Tokenizer; + + private: + MAPM _ParseBinary(); + MAPM _ParseSum(); + MAPM _ParseProduct(); + MAPM _ParsePower(); + MAPM _ParseUnary(); + MAPM _ParseIdentifier(); + void _InitArguments(MAPM values[], + int32 argumentCount); + MAPM _ParseAtom(); + + void _EatToken(int32 type); + + Tokenizer* fTokenizer; +}; + +#endif // C_LANGUAGE_EXPRESSION_EVALUATOR_H