Calculator parser

Nov 18, 2009 at 2:09pm
I have just started C++ programming and was required to write a program that accepts as input math expressions; parses it to validate the expression and then calculate the value of the math expression. I have the parser figured out which I did by splitting the string into tokens, verifying each token and the checking the syntax of the expression. I am having trouble evaluating the expression. I found a java version which works great but I cannot convert it to C++.Does anyone have suggestions of a easy way to implement this before I post code?

Last edited on Nov 30, 2009 at 3:30pm
Nov 18, 2009 at 2:13pm
Nov 18, 2009 at 2:31pm
Thank you for your response; before I take a look at it do you know if it evaluates expression that include negative values as well? For example:
(3+2*-4)+1=
Nov 18, 2009 at 2:43pm
Yes, see in function prim:
1
2
case MINUS:		// unary minus
    return -prim(true);
Nov 18, 2009 at 4:10pm
Quick question:

How would I change the input source to that of from a file and the expression assigned to a string. The cin.get and cin.pushback are confusing in the get_token function.
Nov 18, 2009 at 4:40pm
You can create a stringstream
Nov 18, 2009 at 4:45pm
Would this be correct?
function that receives input string -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
void calcExpression(string &exp)
{
	 input = new istringstream(exp);		// read argument string
	      
      while (*input)
      {
	  getToken();
	  if (currToken == END) break;
	  if (currToken == PRINT) continue;
	  cout<< expr(false) <<endl;
      }
}
Token_value getToken()
{
      char ch = 0;
      do{ 
		  if(!cin.get(ch)) 
			  return currToken = END; 
	  }		// skip whitespace except '\n'
		while (ch != '\n' && isspace(ch));
      
      switch (ch)
      {
	  case 0 :
	        return currToken=END;
	  case ';' :
	  case '\n' :
	        return currToken = PRINT;
	  case '*' :
	  case '/' :
	  case '+' :
	  case '-' :
	  case '(' :
	  case ')' :
	  case '=' :
	        return currToken = Op(ch);
	  
	  case '0' :	case '1' :	case '2' :	case '3' : 	case '4' :
	  case '5' :	case '6' :	case '7' :	case '8' :	case '9' :
	  case '.' :
	        cin.putback(ch);
	        cin >> num;	        return currToken = NUMBER;
	  default:				// NAME, NAME =, or error
	        if (isalpha(ch))
	        {
		    line = ch;
		    while (cin.get(ch) && isalnum(ch)) cin.putback(ch);
		    cin.putback(ch);
		    return currToken = NAME;
	        }
	        cout<<("Bad Token");
	        return currToken = PRINT;
      }
}




how would i change the cin.get() to work
Nov 18, 2009 at 5:00pm
Replace cin with input.
If you allocate input with new, you should also delete it when you don't need it any more
This is from the link I gave you:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Token_value get_token()
{
	char ch;

	do {	// skip whitespace except '\en'
		if(!input->get(ch)) return curr_tok = END;
	} while (ch!='\n' && isspace(ch));

	switch (ch) {
	case ';':
	case '\n':
		return curr_tok=PRINT;
	case '*':
	case '/':
	case '+':
	case '-':
	case '(':
	case ')':
	case '=':
		return curr_tok=Token_value(ch);
	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
	case '.':
		input->putback(ch);
		*input >> number_value;
		return curr_tok=NUMBER;
	default:			// NAME, NAME=, or error
		if (isalpha(ch)) {
			string_value = ch;
			while (input->get(ch) && isalnum(ch))
				string_value += ch;	// string_value.push_back(ch);
							// to work around library bug
			input->putback(ch);
			return curr_tok=NAME;
		}
		error("bad token");
		return curr_tok=PRINT;
	}
}

Nov 19, 2009 at 2:43am
I have rewritten bjarne stroustrup's calculator program to accept input from a file and parse it and evaluate it. Everything works fine except for the instance where the plus sign acts as a signed value. For example 3*+2.5 equals 7.5 but the program expects a parenthesis. How can I implement the change?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
double expr(bool get)
{
	double left = value(get);
	for(;;)
	switch(currToken)
	{
		case PLUS:
			left += value(true);
			break;
		case MINUS:
			left -= value(true);
			break;
		default:
			return left;
	}
}



Nov 19, 2009 at 1:46pm
Add a case in prim:
1
2
case PLUS:
    return prim(true);
Nov 19, 2009 at 10:24pm
I did try this and it gives an error
error C2196: case value '43' already used

it works in the original code but not in my altered version.
Nov 19, 2009 at 10:30pm
43 is the ascii code for your PLUS.

You can't have two cases which test the same condition.
Nov 20, 2009 at 2:10pm
What do you have in the switch inside prim? Do you have two PLUS cases?
Last edited on Nov 20, 2009 at 2:10pm
Nov 25, 2009 at 7:53pm
Thanks for the responses, I have managed to resolve the issue. I had the plus case in another method associated with it.
I have another question, can this same program be applied to conditional statements like testing IF statements for example.
Nov 25, 2009 at 8:18pm
Yes, you need to modify it a bit. Here is a theoretical example:
The token is if

   evaluate next expression
   if that is non-zero ( or whatever you want to be true )
      execute next expression
   else
      read next expression and skip that

   if the next token is else
       if the conditional expression was true
           read next expression and skip that
       else
           execute next expression
You may think of implementing a way for reading blocks of code to allow multiple statements to be inside the if.


It would take a bit of work though
Nov 25, 2009 at 8:30pm
Thanks Bazzy, the if statements will be one line inputs from a file. I will take a crack at it and report back if necessary.
Nov 30, 2009 at 3:47am
I managed to code thus far the tokenizer which breaks the if statement into tokens and validates each one according to a set of grammar rules. I am having trouble validating the syntax of the statement and I realize it is probably easy but what I have implemented does not work.
I used boolean flags to confirm relevant tokens and used those to validate the statement but it does not work all the time.
eg.
1
2
if((isExp && isIdentifier && isRelop) ||(isExp && isStmnt && isElse))
			cout<<"  Statement Syntax Valid. " <<endl;

Another example since the code is long, is the function to test for a token.
1
2
3
4
5
6
7
8
9
10
11
12
bool LexicalTestAddop(string exp)
{
	if((exp == "+") || (exp == "-") || (exp == "OR"))
	{	isAddop = true;
		return true;
	}
	else
	{
		isValid = false;
		return false;
	}
}

Here are the rules of the grammar and all I need now is to validate the final syntax of the statement.

<if-stmt> ::= IF <expr> <stmt> | IF <expr> <stmt> ELSE <stmt>
<stmt> ::= <identifier> := <expr> ?
<expr> ::= <simple expr> | <simple expr> <relop> <simple expr>
<relop> ::= = | <> | < | <= | >= | >
<simple expr> ::= <term> | <simple expr> <addop> <term>
<sign> ::= + | -
<addop> ::= + | - | OR
<term> ::= <factor> | <term> <mulop> <factor>
<mulop> ::= * | / | DIV | MOD | AND
<factor> ::= <identifier> | <sign> <identifier> | <number> |
<sign> <number> | (<expr>) | <sign> (<expr>) | NOT <factor>
<identifier> ::= <letter> | <identifier> <letter> | <identifier> <digit>
<letter> ::= A | B | C | .... | Y | Z
<digit> ::= 0 | 1 | 2 | .... | 8 | 9
<natural> ::= <digit> | <natural> <digit>
<number> ::= <natural> | .<natural> | <natural>.<natural>


Thanks for an suggestions.

Topic archived. No new replies allowed.