Commit e1a7bc3d authored by Peter Eastman's avatar Peter Eastman
Browse files

Added support for subexpressions

parent e09764b5
...@@ -63,9 +63,11 @@ public: ...@@ -63,9 +63,11 @@ public:
*/ */
static ParsedExpression parse(const std::string& expression, const std::map<std::string, CustomFunction*>& customFunctions); static ParsedExpression parse(const std::string& expression, const std::map<std::string, CustomFunction*>& customFunctions);
private: private:
static std::vector<ParseToken> tokenize(std::string expression); static std::string trim(const std::string& expression);
static ParseToken getNextToken(std::string expression, int start); static std::vector<ParseToken> tokenize(const std::string& expression);
static ExpressionTreeNode parsePrecedence(const std::vector<ParseToken>& tokens, int& pos, const std::map<std::string, CustomFunction*>& customFunctions, int precedence); static ParseToken getNextToken(const std::string& expression, int start);
static ExpressionTreeNode parsePrecedence(const std::vector<ParseToken>& tokens, int& pos, const std::map<std::string, CustomFunction*>& customFunctions,
const std::map<std::string, ExpressionTreeNode>& subexpressionDefs, int precedence);
static Operation* getOperatorOperation(const std::string& name); static Operation* getOperatorOperation(const std::string& name);
static Operation* getFunctionOperation(const std::string& name, const std::map<std::string, CustomFunction*>& customFunctions); static Operation* getFunctionOperation(const std::string& name, const std::map<std::string, CustomFunction*>& customFunctions);
}; };
......
...@@ -63,7 +63,20 @@ private: ...@@ -63,7 +63,20 @@ private:
Type type; Type type;
}; };
ParseToken Parser::getNextToken(string expression, int start) { string Parser::trim(const string& expression) {
// Remove leading and trailing spaces.
int start, end;
for (start = 0; start < (int) expression.size() && expression[start] == ' '; start++)
;
for (end = expression.size()-1; end > start && expression[end] == ' '; end--)
;
if (start == end && expression[end] == ' ')
return "";
return expression.substr(start, end-start+1);
}
ParseToken Parser::getNextToken(const string& expression, int start) {
char c = expression[start]; char c = expression[start];
if (c == '(') if (c == '(')
return ParseToken("(", ParseToken::LeftParen); return ParseToken("(", ParseToken::LeftParen);
...@@ -119,7 +132,7 @@ ParseToken Parser::getNextToken(string expression, int start) { ...@@ -119,7 +132,7 @@ ParseToken Parser::getNextToken(string expression, int start) {
return ParseToken(expression.substr(start, string::npos), ParseToken::Variable); return ParseToken(expression.substr(start, string::npos), ParseToken::Variable);
} }
vector<ParseToken> Parser::tokenize(string expression) { vector<ParseToken> Parser::tokenize(const string& expression) {
vector<ParseToken> tokens; vector<ParseToken> tokens;
int pos = 0; int pos = 0;
while (pos < (int) expression.size()) { while (pos < (int) expression.size()) {
...@@ -136,15 +149,49 @@ ParsedExpression Parser::parse(const string& expression) { ...@@ -136,15 +149,49 @@ ParsedExpression Parser::parse(const string& expression) {
} }
ParsedExpression Parser::parse(const string& expression, const map<string, CustomFunction*>& customFunctions) { ParsedExpression Parser::parse(const string& expression, const map<string, CustomFunction*>& customFunctions) {
vector<ParseToken> tokens = tokenize(expression); // First split the expression into subexpressions.
string primaryExpression = expression;
vector<string> subexpressions;
while (true) {
string::size_type pos = primaryExpression.find_last_of(';');
if (pos == string::npos)
break;
string sub = trim(primaryExpression.substr(pos+1));
if (sub.size() > 0)
subexpressions.push_back(sub);
primaryExpression = primaryExpression.substr(0, pos);
}
// Parse the subexpressions.
map<string, ExpressionTreeNode> subexpDefs;
for (int i = 0; i < (int) subexpressions.size(); i++) {
string::size_type equalsPos = subexpressions[i].find('=');
if (equalsPos == string::npos)
throw Exception("Parse error: subexpression does not specify a name");
string name = trim(subexpressions[i].substr(0, equalsPos));
if (name.size() == 0)
throw Exception("Parse error: subexpression does not specify a name");
vector<ParseToken> tokens = tokenize(subexpressions[i].substr(equalsPos+1));
int pos = 0;
subexpDefs[name] = parsePrecedence(tokens, pos, customFunctions, subexpDefs, 0);
if (pos != tokens.size())
throw Exception("Parse error: unexpected text at end of subexpression");
}
// Now parse the primary expression.
vector<ParseToken> tokens = tokenize(primaryExpression);
int pos = 0; int pos = 0;
ExpressionTreeNode result = parsePrecedence(tokens, pos, customFunctions, 0); ExpressionTreeNode result = parsePrecedence(tokens, pos, customFunctions, subexpDefs, 0);
if (pos != tokens.size()) if (pos != tokens.size())
throw Exception("Parse error: unexpected text at end of expression"); throw Exception("Parse error: unexpected text at end of expression");
return ParsedExpression(result); return ParsedExpression(result);
} }
ExpressionTreeNode Parser::parsePrecedence(const vector<ParseToken>& tokens, int& pos, const map<string, CustomFunction*>& customFunctions, int precedence) { ExpressionTreeNode Parser::parsePrecedence(const vector<ParseToken>& tokens, int& pos, const map<string, CustomFunction*>& customFunctions,
const map<string, ExpressionTreeNode>& subexpressionDefs, int precedence) {
if (pos == tokens.size()) if (pos == tokens.size())
throw Exception("Parse error: unexpected end of expression"); throw Exception("Parse error: unexpected end of expression");
...@@ -159,13 +206,18 @@ ExpressionTreeNode Parser::parsePrecedence(const vector<ParseToken>& tokens, int ...@@ -159,13 +206,18 @@ ExpressionTreeNode Parser::parsePrecedence(const vector<ParseToken>& tokens, int
pos++; pos++;
} }
else if (token.getType() == ParseToken::Variable) { else if (token.getType() == ParseToken::Variable) {
Operation* op = new Operation::Variable(token.getText()); map<string, ExpressionTreeNode>::const_iterator subexp = subexpressionDefs.find(token.getText());
result = ExpressionTreeNode(op); if (subexp == subexpressionDefs.end()) {
Operation* op = new Operation::Variable(token.getText());
result = ExpressionTreeNode(op);
}
else
result = subexp->second;
pos++; pos++;
} }
else if (token.getType() == ParseToken::LeftParen) { else if (token.getType() == ParseToken::LeftParen) {
pos++; pos++;
result = parsePrecedence(tokens, pos, customFunctions, 0); result = parsePrecedence(tokens, pos, customFunctions, subexpressionDefs, 0);
if (pos == tokens.size() || tokens[pos].getType() != ParseToken::RightParen) if (pos == tokens.size() || tokens[pos].getType() != ParseToken::RightParen)
throw Exception("Parse error: unbalanced parentheses"); throw Exception("Parse error: unbalanced parentheses");
pos++; pos++;
...@@ -175,7 +227,7 @@ ExpressionTreeNode Parser::parsePrecedence(const vector<ParseToken>& tokens, int ...@@ -175,7 +227,7 @@ ExpressionTreeNode Parser::parsePrecedence(const vector<ParseToken>& tokens, int
vector<ExpressionTreeNode> args; vector<ExpressionTreeNode> args;
bool moreArgs; bool moreArgs;
do { do {
args.push_back(parsePrecedence(tokens, pos, customFunctions, 0)); args.push_back(parsePrecedence(tokens, pos, customFunctions, subexpressionDefs, 0));
moreArgs = (pos < (int) tokens.size() && tokens[pos].getType() == ParseToken::Comma); moreArgs = (pos < (int) tokens.size() && tokens[pos].getType() == ParseToken::Comma);
if (moreArgs) if (moreArgs)
pos++; pos++;
...@@ -187,7 +239,7 @@ ExpressionTreeNode Parser::parsePrecedence(const vector<ParseToken>& tokens, int ...@@ -187,7 +239,7 @@ ExpressionTreeNode Parser::parsePrecedence(const vector<ParseToken>& tokens, int
} }
else if (token.getType() == ParseToken::Operator && token.getText() == "-") { else if (token.getType() == ParseToken::Operator && token.getText() == "-") {
pos++; pos++;
ExpressionTreeNode toNegate = parsePrecedence(tokens, pos, customFunctions, 2); ExpressionTreeNode toNegate = parsePrecedence(tokens, pos, customFunctions, subexpressionDefs, 2);
result = ExpressionTreeNode(new Operation::Negate(), toNegate); result = ExpressionTreeNode(new Operation::Negate(), toNegate);
} }
else else
...@@ -202,7 +254,7 @@ ExpressionTreeNode Parser::parsePrecedence(const vector<ParseToken>& tokens, int ...@@ -202,7 +254,7 @@ ExpressionTreeNode Parser::parsePrecedence(const vector<ParseToken>& tokens, int
if (opPrecedence < precedence) if (opPrecedence < precedence)
return result; return result;
pos++; pos++;
ExpressionTreeNode arg = parsePrecedence(tokens, pos, customFunctions, LeftAssociative[op] ? opPrecedence+1 : opPrecedence); ExpressionTreeNode arg = parsePrecedence(tokens, pos, customFunctions, subexpressionDefs, LeftAssociative[op] ? opPrecedence+1 : opPrecedence);
result = ExpressionTreeNode(getOperatorOperation(token.getText()), result, arg); result = ExpressionTreeNode(getOperatorOperation(token.getText()), result, arg);
} }
return result; return result;
......
...@@ -178,6 +178,8 @@ int main() { ...@@ -178,6 +178,8 @@ int main() {
verifyEvaluation("2*(3*x)", 4.0, 4.0, 24.0); verifyEvaluation("2*(3*x)", 4.0, 4.0, 24.0);
verifyEvaluation("2*x/3", 1.0, 4.0, 2.0/3.0); verifyEvaluation("2*x/3", 1.0, 4.0, 2.0/3.0);
verifyEvaluation("x*2/3", 1.0, 4.0, 2.0/3.0); verifyEvaluation("x*2/3", 1.0, 4.0, 2.0/3.0);
verifyEvaluation("x*w; w = 5", 3.0, 1.0, 15.0);
verifyEvaluation("a+b^2;a=x-b;b=3*y", 2.0, 3.0, 74.0);
verifyInvalidExpression("1..2"); verifyInvalidExpression("1..2");
verifyInvalidExpression("1*(2+3"); verifyInvalidExpression("1*(2+3");
verifyInvalidExpression("5++4"); verifyInvalidExpression("5++4");
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment