patterncppMinor
Monster token parser function
Viewed 0 times
monstertokenparserfunction
Problem
This function will read an expression (made up of tokens) from the vector of tokens passed into it. It will remove the tokens that make up the expression from the vector and return them.
A token is added to the expression if
Brackets are handled in parseBracketTokens (which calls this function)
```
RawExpression *parseExpressionTokens(
vector &tokens, bool brackets = false) {
// This is a vector of elements that make up an expression
vector elements;
while(tokens.size() > 0) {
// Operands must be at the start of an expression or after an operator
if(tokenIsOperand(tokens[0])) {
if(elements.size() == 0 ||
eElementIsRelationalOperator(elements.back())) {
elements.push_back(makeEElementFromToken(tokens[0]));
removeToken(tokens, 0);
continue;
}
// This token is part of the next expression, break from the loop
break;
}
if(tokenIsRelationalOperator(tokens[0])) {
// Make sure we have an operand left of it
if(tokens[0]->type == TOKEN_MINUS) {
if(elements.size() == 0 ||
!eElementIsOperand(elements.back())) {
// handle the minus number or symbol :O
}
}
if(elements.size() != 0 &&
eElementIsOperand(elements.back())) {
elements.push_back(makeEElementFromToken(tokens[0]));
removeToken(tokens, 0);
continue;
}
throw(string("Operator has bad lhand:") + tokenToString(tokens[0]));
}
// A closing bracket marks the end of an expression
if(tokens[0]->type == TOKEN_CLOSEBRACKET) {
if(!brackets)
throw(string("Unmatched closing bracket"));
break;
}
A token is added to the expression if
- it is the first token
- it has an operator before it
Brackets are handled in parseBracketTokens (which calls this function)
```
RawExpression *parseExpressionTokens(
vector &tokens, bool brackets = false) {
// This is a vector of elements that make up an expression
vector elements;
while(tokens.size() > 0) {
// Operands must be at the start of an expression or after an operator
if(tokenIsOperand(tokens[0])) {
if(elements.size() == 0 ||
eElementIsRelationalOperator(elements.back())) {
elements.push_back(makeEElementFromToken(tokens[0]));
removeToken(tokens, 0);
continue;
}
// This token is part of the next expression, break from the loop
break;
}
if(tokenIsRelationalOperator(tokens[0])) {
// Make sure we have an operand left of it
if(tokens[0]->type == TOKEN_MINUS) {
if(elements.size() == 0 ||
!eElementIsOperand(elements.back())) {
// handle the minus number or symbol :O
}
}
if(elements.size() != 0 &&
eElementIsOperand(elements.back())) {
elements.push_back(makeEElementFromToken(tokens[0]));
removeToken(tokens, 0);
continue;
}
throw(string("Operator has bad lhand:") + tokenToString(tokens[0]));
}
// A closing bracket marks the end of an expression
if(tokens[0]->type == TOKEN_CLOSEBRACKET) {
if(!brackets)
throw(string("Unmatched closing bracket"));
break;
}
Solution
My first thought is that parsers should be implemented as table driven state machines for exactly the reason you see in the code :-)
Actually, I think breaking this into a set of separate functions is one good approach, and adding a new enum to support it makes it very clear the state of the expression after each step.
Another option I would consider is determining the paths through the code and possibly doing some optimizations around them at the expense of clarity. There appear to be several places where once you first entered a conditional block, you have committed to not doing any other code in the loop. Re-arranging these may eliminate some of the separate continue/break statements and allow easier refactoring.
Actually, I think breaking this into a set of separate functions is one good approach, and adding a new enum to support it makes it very clear the state of the expression after each step.
Another option I would consider is determining the paths through the code and possibly doing some optimizations around them at the expense of clarity. There appear to be several places where once you first entered a conditional block, you have committed to not doing any other code in the loop. Re-arranging these may eliminate some of the separate continue/break statements and allow easier refactoring.
Context
StackExchange Code Review Q#1564, answer score: 8
Revisions (0)
No revisions yet.