Debugger: Rework expression evaluator.

CLanguageExpressionEvaluator:
- Revise to make use of Number class rather than MAPM. In the process,
  implement support for bitwise operators.

SourceLanguage/CLanguageFamily/CLanguageExpressionEvaluator:
- Adjust interface for expression evaluation to allow specifying the
  type to evaluate the expression as. Adjust implementing classes
  accordingly.

ExpressionEvaluationWindow:
- Adjust to new expression interface. For now defaults to 64-bit integer
  type, UI for type selection to be added.
This commit is contained in:
Rene Gollent 2014-10-27 11:42:58 -04:00
parent 1c6c5f3b57
commit fc8713b02e
7 changed files with 139 additions and 166 deletions

View File

@ -11,7 +11,9 @@
#include <stdlib.h> #include <stdlib.h>
#include "CLanguageExpressionEvaluator.h" #include "CLanguageExpressionEvaluator.h"
#include "FloatValue.h"
#include "IntegerValue.h" #include "IntegerValue.h"
#include "Number.h"
#include "StringValue.h" #include "StringValue.h"
#include "TeamTypeInformation.h" #include "TeamTypeInformation.h"
#include "Type.h" #include "Type.h"
@ -165,16 +167,36 @@ CLanguageFamily::ParseTypeExpression(const BString& expression,
status_t status_t
CLanguageFamily::EvaluateExpression(const BString& expression, Value*& _output) CLanguageFamily::EvaluateExpression(const BString& expression,
type_code type, Value*& _output)
{ {
_output = NULL; _output = NULL;
CLanguageExpressionEvaluator evaluator; CLanguageExpressionEvaluator evaluator;
evaluator.SetSupportHexInput(true); Number result;
int64 resultValue;
try { try {
resultValue = evaluator.EvaluateToInt64(expression); result = evaluator.Evaluate(expression, type);
BVariant variantValue(resultValue); BVariant resultValue = result.GetValue();
_output = new(std::nothrow) IntegerValue(variantValue); switch (type) {
case B_INT8_TYPE:
case B_UINT8_TYPE:
case B_INT16_TYPE:
case B_UINT16_TYPE:
case B_INT32_TYPE:
case B_UINT32_TYPE:
case B_INT64_TYPE:
case B_UINT64_TYPE:
_output = new(std::nothrow) IntegerValue(resultValue);
break;
case B_FLOAT_TYPE:
_output = new(std::nothrow) FloatValue(resultValue.ToFloat());
break;
case B_DOUBLE_TYPE:
_output = new(std::nothrow) FloatValue(resultValue.ToDouble());
break;
}
if (_output == NULL) if (_output == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;

View File

@ -22,7 +22,7 @@ public:
Type*& _resultType) const; Type*& _resultType) const;
virtual status_t EvaluateExpression(const BString& expression, virtual status_t EvaluateExpression(const BString& expression,
Value*& _output); type_code type, Value*& _output);
protected: protected:
virtual bool IsModifierValid(char modifier) const = 0; virtual bool IsModifierValid(char modifier) const = 0;

View File

@ -30,7 +30,7 @@ SourceLanguage::ParseTypeExpression(const BString& expression,
status_t status_t
SourceLanguage::EvaluateExpression(const BString& expression, SourceLanguage::EvaluateExpression(const BString& expression,
Value*& _resultValue) type_code type, Value*& _resultValue)
{ {
return B_NOT_SUPPORTED; return B_NOT_SUPPORTED;
} }

View File

@ -32,7 +32,7 @@ public:
Type*& _resultType) const; Type*& _resultType) const;
virtual status_t EvaluateExpression(const BString& expression, virtual status_t EvaluateExpression(const BString& expression,
Value*& _output); type_code type, Value*& _output);
}; };

View File

@ -11,17 +11,13 @@
#include "CLanguageExpressionEvaluator.h" #include "CLanguageExpressionEvaluator.h"
#include "Number.h"
#include <ctype.h> #include <ctype.h>
#include <math.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <strings.h> #include <strings.h>
#include <m_apm.h>
static const int32 kMaxDecimalPlaces = 32;
enum { enum {
TOKEN_NONE = 0, TOKEN_NONE = 0,
@ -47,6 +43,7 @@ enum {
TOKEN_BITWISE_AND, TOKEN_BITWISE_AND,
TOKEN_BITWISE_OR, TOKEN_BITWISE_OR,
TOKEN_BITWISE_NOT, TOKEN_BITWISE_NOT,
TOKEN_BITWISE_XOR,
TOKEN_EQ, TOKEN_EQ,
TOKEN_NE, TOKEN_NE,
TOKEN_GT, TOKEN_GT,
@ -81,7 +78,7 @@ static BString TokenTypeToString(int32 type)
break; break;
case TOKEN_POWER: case TOKEN_POWER:
token = "^"; token = "**";
break; break;
case TOKEN_OPENING_BRACKET: case TOKEN_OPENING_BRACKET:
@ -116,6 +113,10 @@ static BString TokenTypeToString(int32 type)
token = "~"; token = "~";
break; break;
case TOKEN_BITWISE_XOR:
token = "^";
break;
case TOKEN_EQ: case TOKEN_EQ:
token = "=="; token = "==";
break; break;
@ -153,7 +154,7 @@ struct CLanguageExpressionEvaluator::Token {
Token() Token()
: string(""), : string(""),
type(TOKEN_NONE), type(TOKEN_NONE),
value(0), value(0L),
position(0) position(0)
{ {
} }
@ -169,7 +170,7 @@ struct CLanguageExpressionEvaluator::Token {
Token(const char* string, int32 length, int32 position, int32 type) Token(const char* string, int32 length, int32 position, int32 type)
: string(string, length), : string(string, length),
type(type), type(type),
value(0), value(),
position(position) position(position)
{ {
} }
@ -185,7 +186,7 @@ struct CLanguageExpressionEvaluator::Token {
BString string; BString string;
int32 type; int32 type;
MAPM value; Number value;
int32 position; int32 position;
}; };
@ -198,15 +199,10 @@ class CLanguageExpressionEvaluator::Tokenizer {
fCurrentChar(NULL), fCurrentChar(NULL),
fCurrentToken(), fCurrentToken(),
fReuseToken(false), fReuseToken(false),
fHexSupport(false) fType(B_INT32_TYPE)
{ {
} }
void SetSupportHexInput(bool enabled)
{
fHexSupport = enabled;
}
void SetTo(const char* string) void SetTo(const char* string)
{ {
fString = string; fString = string;
@ -215,6 +211,11 @@ class CLanguageExpressionEvaluator::Tokenizer {
fReuseToken = false; fReuseToken = false;
} }
void SetType(type_code type)
{
fType = type;
}
const Token& NextToken() const Token& NextToken()
{ {
if (fCurrentToken.type == TOKEN_END_OF_LINE) if (fCurrentToken.type == TOKEN_END_OF_LINE)
@ -235,7 +236,7 @@ class CLanguageExpressionEvaluator::Tokenizer {
bool decimal = *fCurrentChar == '.' || *fCurrentChar == ','; bool decimal = *fCurrentChar == '.' || *fCurrentChar == ',';
if (decimal || isdigit(*fCurrentChar)) { if (decimal || isdigit(*fCurrentChar)) {
if (fHexSupport && *fCurrentChar == '0' && fCurrentChar[1] == 'x') if (*fCurrentChar == '0' && fCurrentChar[1] == 'x')
return _ParseHexNumber(); return _ParseHexNumber();
BString temp; BString temp;
@ -297,7 +298,7 @@ class CLanguageExpressionEvaluator::Tokenizer {
fCurrentToken = Token(begin, length, _CurrentPos() - length, fCurrentToken = Token(begin, length, _CurrentPos() - length,
TOKEN_CONSTANT); TOKEN_CONSTANT);
fCurrentToken.value = temp.String(); fCurrentToken.value.SetTo(fType, temp.String());
} else if (isalpha(*fCurrentChar) && *fCurrentChar != 'x') { } else if (isalpha(*fCurrentChar) && *fCurrentChar != 'x') {
const char* begin = fCurrentChar; const char* begin = fCurrentChar;
while (*fCurrentChar != 0 && (isalpha(*fCurrentChar) while (*fCurrentChar != 0 && (isalpha(*fCurrentChar)
@ -331,13 +332,6 @@ class CLanguageExpressionEvaluator::Tokenizer {
type = TOKEN_SLASH; type = TOKEN_SLASH;
break; break;
case 'x':
if (!fHexSupport) {
type = TOKEN_STAR;
break;
}
// fall through
default: default:
throw ParseException("unexpected character", throw ParseException("unexpected character",
_CurrentPos()); _CurrentPos());
@ -367,8 +361,14 @@ class CLanguageExpressionEvaluator::Tokenizer {
break; break;
case '*': case '*':
type = TOKEN_STAR; if (Peek() == '*') {
length = 1; type = TOKEN_POWER;
length = 2;
}
else {
type = TOKEN_STAR;
length = 1;
}
break; break;
case '/': case '/':
@ -382,7 +382,7 @@ class CLanguageExpressionEvaluator::Tokenizer {
break; break;
case '^': case '^':
type = TOKEN_POWER; type = TOKEN_BITWISE_XOR;
length = 1; length = 1;
break; break;
@ -497,18 +497,7 @@ class CLanguageExpressionEvaluator::Tokenizer {
fCurrentToken = Token(begin, length, _CurrentPos() - length, fCurrentToken = Token(begin, length, _CurrentPos() - length,
TOKEN_CONSTANT); TOKEN_CONSTANT);
// MAPM has no conversion from long long, so we need to improvise. fCurrentToken.value.SetTo(fType, fCurrentToken.string.String(), 16);
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; return fCurrentToken;
} }
@ -521,7 +510,7 @@ class CLanguageExpressionEvaluator::Tokenizer {
const char* fCurrentChar; const char* fCurrentChar;
Token fCurrentToken; Token fCurrentToken;
bool fReuseToken; bool fReuseToken;
bool fHexSupport; type_code fType;
}; };
@ -537,80 +526,22 @@ CLanguageExpressionEvaluator::~CLanguageExpressionEvaluator()
} }
void Number
CLanguageExpressionEvaluator::SetSupportHexInput(bool enabled) CLanguageExpressionEvaluator::Evaluate(const char* expressionString, type_code type)
{
fTokenizer->SetSupportHexInput(enabled);
}
BString
CLanguageExpressionEvaluator::Evaluate(const char* expressionString)
{ {
fTokenizer->SetType(type);
fTokenizer->SetTo(expressionString); fTokenizer->SetTo(expressionString);
MAPM value = _ParseBinary(); Number value = _ParseBinary();
Token token = fTokenizer->NextToken(); Token token = fTokenizer->NextToken();
if (token.type != TOKEN_END_OF_LINE) if (token.type != TOKEN_END_OF_LINE)
throw ParseException("parse error", token.position); throw ParseException("parse error", token.position);
if (value == 0) return value;
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 Number
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() CLanguageExpressionEvaluator::_ParseBinary()
{ {
return _ParseSum(); return _ParseSum();
@ -637,20 +568,20 @@ CLanguageExpressionEvaluator::_ParseBinary()
} }
MAPM Number
CLanguageExpressionEvaluator::_ParseSum() CLanguageExpressionEvaluator::_ParseSum()
{ {
// TODO: check isnan()... // TODO: check isnan()...
MAPM value = _ParseProduct(); Number value = _ParseProduct();
while (true) { while (true) {
Token token = fTokenizer->NextToken(); Token token = fTokenizer->NextToken();
switch (token.type) { switch (token.type) {
case TOKEN_PLUS: case TOKEN_PLUS:
value = value + _ParseProduct(); value += _ParseProduct();
break; break;
case TOKEN_MINUS: case TOKEN_MINUS:
value = value - _ParseProduct(); value -= _ParseProduct();
break; break;
default: default:
@ -661,66 +592,86 @@ CLanguageExpressionEvaluator::_ParseSum()
} }
MAPM Number
CLanguageExpressionEvaluator::_ParseProduct() CLanguageExpressionEvaluator::_ParseProduct()
{ {
// TODO: check isnan()... Number value = _ParsePower();
MAPM value = _ParsePower();
while (true) { while (true) {
Token token = fTokenizer->NextToken(); Token token = fTokenizer->NextToken();
switch (token.type) { switch (token.type) {
case TOKEN_STAR: case TOKEN_STAR:
value = value * _ParsePower(); value *= _ParsePower();
break; break;
case TOKEN_SLASH: case TOKEN_SLASH:
{ {
MAPM rhs = _ParsePower(); Number rhs = _ParsePower();
if (rhs == MAPM(0)) if (rhs == Number(fCurrentType, 0))
throw ParseException("division by zero", token.position); throw ParseException("division by zero", token.position);
value = value / rhs; value /= rhs;
break; break;
} }
case TOKEN_MODULO: case TOKEN_MODULO:
{ {
MAPM rhs = _ParsePower(); Number rhs = _ParsePower();
if (rhs == MAPM(0)) if (rhs == Number())
throw ParseException("modulo by zero", token.position); throw ParseException("modulo by zero", token.position);
value = value % rhs; value %= rhs;
break; break;
} }
case TOKEN_LOGICAL_AND: case TOKEN_LOGICAL_AND:
value = (value != MAPM(0) && _ParsePower() != MAPM(0)); {
Number zero(BVariant(0L));
value.SetTo(BVariant((int32)((value != zero)
&& (_ParsePower() != zero))));
break; break;
}
case TOKEN_LOGICAL_OR: case TOKEN_LOGICAL_OR:
value = (value != MAPM(0) || _ParsePower() != MAPM(0)); {
Number zero(BVariant(0L));
value.SetTo(BVariant((int32)((value != zero)
|| (_ParsePower() != zero))));
break;
}
case TOKEN_BITWISE_AND:
value &= _ParsePower();
break;
case TOKEN_BITWISE_OR:
value |= _ParsePower();
break;
case TOKEN_BITWISE_XOR:
value ^= _ParsePower();
break; break;
case TOKEN_EQ: case TOKEN_EQ:
value = (value == _ParsePower()); value.SetTo(BVariant((int32)(value == _ParsePower())));
break; break;
case TOKEN_NE: case TOKEN_NE:
value = (value != _ParsePower()); value.SetTo(BVariant((int32)(value != _ParsePower())));
break; break;
case TOKEN_GT: case TOKEN_GT:
value = (value > _ParsePower()); value.SetTo(BVariant((int32)(value > _ParsePower())));
break; break;
case TOKEN_GE: case TOKEN_GE:
value = (value >= _ParsePower()); value.SetTo(BVariant((int32)(value >= _ParsePower())));
break; break;
case TOKEN_LT: case TOKEN_LT:
value = (value < _ParsePower()); value.SetTo(BVariant((int32)(value < _ParsePower())));
break; break;
case TOKEN_LE: case TOKEN_LE:
value = (value <= _ParsePower()); value.SetTo(BVariant((int32)(value <= _ParsePower())));
break; break;
default: default:
@ -731,10 +682,10 @@ CLanguageExpressionEvaluator::_ParseProduct()
} }
MAPM Number
CLanguageExpressionEvaluator::_ParsePower() CLanguageExpressionEvaluator::_ParsePower()
{ {
MAPM value = _ParseUnary(); Number value = _ParseUnary();
while (true) { while (true) {
Token token = fTokenizer->NextToken(); Token token = fTokenizer->NextToken();
@ -742,12 +693,16 @@ CLanguageExpressionEvaluator::_ParsePower()
fTokenizer->RewindToken(); fTokenizer->RewindToken();
return value; return value;
} }
value = value.pow(_ParseUnary());
Number power = _ParseUnary();
Number temp = value;
for (int32 powerValue = power.GetValue().ToInt32(); powerValue > 1; powerValue--)
value *= temp;
} }
} }
MAPM Number
CLanguageExpressionEvaluator::_ParseUnary() CLanguageExpressionEvaluator::_ParseUnary()
{ {
Token token = fTokenizer->NextToken(); Token token = fTokenizer->NextToken();
@ -761,13 +716,11 @@ CLanguageExpressionEvaluator::_ParseUnary()
case TOKEN_MINUS: case TOKEN_MINUS:
return -_ParseUnary(); return -_ParseUnary();
case TOKEN_BITWISE_AND:
case TOKEN_BITWISE_OR:
case TOKEN_BITWISE_NOT: case TOKEN_BITWISE_NOT:
throw ParseException("Unimplemented bitwise operator", token.position); return ~_ParseUnary();
case TOKEN_LOGICAL_NOT: case TOKEN_LOGICAL_NOT:
return MAPM(_ParseUnary() == 0); return Number((int32)(_ParseUnary() == Number(BVariant(0L))));
case TOKEN_IDENTIFIER: case TOKEN_IDENTIFIER:
return _ParseIdentifier(); return _ParseIdentifier();
@ -777,7 +730,7 @@ CLanguageExpressionEvaluator::_ParseUnary()
return _ParseAtom(); return _ParseAtom();
} }
return MAPM(0); return Number();
} }
@ -785,21 +738,21 @@ struct Function {
const char* name; const char* name;
int argumentCount; int argumentCount;
void* function; void* function;
MAPM value; Number value;
}; };
MAPM Number
CLanguageExpressionEvaluator::_ParseIdentifier() CLanguageExpressionEvaluator::_ParseIdentifier()
{ {
throw ParseException("Identifiers not implemented", 0); throw ParseException("Identifiers not implemented", 0);
return MAPM(0); return Number();
} }
void void
CLanguageExpressionEvaluator::_InitArguments(MAPM values[], int32 argumentCount) CLanguageExpressionEvaluator::_InitArguments(Number values[], int32 argumentCount)
{ {
_EatToken(TOKEN_OPENING_BRACKET); _EatToken(TOKEN_OPENING_BRACKET);
@ -810,7 +763,7 @@ CLanguageExpressionEvaluator::_InitArguments(MAPM values[], int32 argumentCount)
} }
MAPM Number
CLanguageExpressionEvaluator::_ParseAtom() CLanguageExpressionEvaluator::_ParseAtom()
{ {
Token token = fTokenizer->NextToken(); Token token = fTokenizer->NextToken();
@ -824,7 +777,7 @@ CLanguageExpressionEvaluator::_ParseAtom()
_EatToken(TOKEN_OPENING_BRACKET); _EatToken(TOKEN_OPENING_BRACKET);
MAPM value = _ParseBinary(); Number value = _ParseBinary();
_EatToken(TOKEN_CLOSING_BRACKET); _EatToken(TOKEN_CLOSING_BRACKET);

View File

@ -34,7 +34,7 @@ class ParseException {
}; };
struct Function; struct Function;
class MAPM; class Number;
class CLanguageExpressionEvaluator { class CLanguageExpressionEvaluator {
@ -42,30 +42,28 @@ class CLanguageExpressionEvaluator {
CLanguageExpressionEvaluator(); CLanguageExpressionEvaluator();
~CLanguageExpressionEvaluator(); ~CLanguageExpressionEvaluator();
void SetSupportHexInput(bool enabled); Number Evaluate(const char* expressionString,
type_code type);
BString Evaluate(const char* expressionString);
int64 EvaluateToInt64(const char* expressionString);
double EvaluateToDouble(const char* expressionString);
private: private:
struct Token; struct Token;
class Tokenizer; class Tokenizer;
private: private:
MAPM _ParseBinary(); Number _ParseBinary();
MAPM _ParseSum(); Number _ParseSum();
MAPM _ParseProduct(); Number _ParseProduct();
MAPM _ParsePower(); Number _ParsePower();
MAPM _ParseUnary(); Number _ParseUnary();
MAPM _ParseIdentifier(); Number _ParseIdentifier();
void _InitArguments(MAPM values[], void _InitArguments(Number values[],
int32 argumentCount); int32 argumentCount);
MAPM _ParseAtom(); Number _ParseAtom();
void _EatToken(int32 type); void _EatToken(int32 type);
Tokenizer* fTokenizer; Tokenizer* fTokenizer;
type_code fCurrentType;
}; };
#endif // C_LANGUAGE_EXPRESSION_EVALUATOR_H #endif // C_LANGUAGE_EXPRESSION_EVALUATOR_H

View File

@ -124,7 +124,7 @@ ExpressionEvaluationWindow::MessageReceived(BMessage* message)
Value* value = NULL; Value* value = NULL;
BString outputText; BString outputText;
status_t error = fLanguage->EvaluateExpression( status_t error = fLanguage->EvaluateExpression(
fExpressionInput->TextView()->Text(), value); fExpressionInput->TextView()->Text(), B_INT64_TYPE, value);
if (error != B_OK) { if (error != B_OK) {
if (value != NULL) if (value != NULL)
value->ToString(outputText); value->ToString(outputText);