/* calculator.cpp: a simple infix calculator with a recursive descent parser * * UMass Lowell Computer Science * * original program by Bill Moloney * C-style I/O converted to C++-style stream I/O by Jesse Heines * formatting and documentation added by Jesse Heines * * Note that a 'q' is used to cause the program to quit, so this * character cannot be used to start a token name. * * * Important Terms: * An EXPRESSION is made up of TERMS which are made up of FACTORS. * Plus and minus TOKENS separate TERMS in an EXPRESSION. * Multiply and divide tokens separate FACTORS in a term. */ #include // for cout, etc #include "nodes.h" // user-defined header file containing all class definitions using namespace std ; // for Standard Template Library static int token ; // token parsed from user input static char tokchars[81] ; // string for temporary storage of a multi-character token Nament* Id::symtab = NULL ; // initialization of static member variable // symtab is the head of the linked list used to implement the symbol table // token type identifiers enum { ID=257, INT, EOLN, BAD, QUIT } ; // the above technique is an alternative to setting each value indivdually with: // static const int ID = 257 ; // static const int INT = 258 ; // etc. // ID gets the value of 257, INT gets 258, EOLN gets 259, BAD gets 260, etc. // function prototypes Node *Expression(), *Term(), *Factor() ; // This function scans the user's inputs and parses it into tokens. int scan() { while( true ) // seemingly infinite loop, terminates via return statements { // embedded within case statements char c = (char) cin.get() ; // read a character from the input stream switch( c ) // process (interpret) the character just read { case '.' : // a '.', 'q', or 'Q' causes the program to quit case 'q' : case 'Q' : return QUIT ; // return the QUIT token case '+' : // operators detected case '-' : case '*' : case '/' : case '(' : case ')' : case '=' : return c ; // return the character read as a token case ' ' : // whitespace detected case '\t': continue ; // branches back to the while statement case '\n': // end-of-line detected return EOLN ; // return EOLN token default: // all other characters int k = 0 ; // counter initialization char* s = tokchars ; // character pointer initialization if ( isdigit( c ) ) // character read is a digit { do // read and append subsequent characters { // until the character read is not a digit s[k++] = c ; } while ( isdigit ( c = (char) cin.get() ) ) ; s[k] = '\0' ; // terminate the string read cin.putback( c ) ; // push the last character read back into the input buffer return INT ; // return that token read is an integer } else if ( isalpha( c ) ) // character read is a letter { do // read and append subsequent characters { // until the character read is not a letter or number s[k++] = c ; } while ( isalnum ( c = (char) cin.get() ) ) ; s[k] = '\0' ; // terminate the string read cin.putback( c ) ; // push the last character read back into the input buffer return ID ; // return that token read is an ID (a variable in the symbol table) } return BAD ; // return that the token read contains an error } // end switch } // end while } // end scan // This function displays a error message and terminates the program with an error flag. void error() { cout << "Program terminated due to an input error." << endl ; exit( 255 ) ; } // This function evaluates an entire expression (a collection of terms). Node* Expression() { // set a local root pointer to the first term Node* root = Term() ; // seemingly infinite loop, terminates via the return statement in the default case // when the token is neither a '+' nor a '-' while ( true ) switch( token ) { case '+' : // addition token = scan() ; // scan for the next token root = new Plus( root, Term() ) ; // add the root and the next term break ; case '-' : // subtraction token = scan() ; // scan for the next token root = new Minus( root, Term() ) ; // subtract the root and the next term break ; default : // exit point return root ; // return the pointer to the root of this subtree } } // This function evaluates a single term (two tokens separated by '+' or '-'). Node* Term() { // set a local root pointer to the first factor Node* root = Factor() ; // seemingly infinite loop, terminates via the return statement in the default case // when the token is neither a '*' nor a '/' while( true ) switch( token ) { case '*' : // multiplication token = scan() ; // scan for the next token root = new Times( root, Factor() ) ; // multiply the root and the next factor break ; case '/' : token = scan() ; // scan for the next token root = new Div( root, Factor() ) ; // divide the root and the next factor break ; default : // exit point return root ; // return the pointer to the root of this subtree } } // This function evaluates a single factor (two tokens separated by '*' or '/'). Node* Factor () { // declare a local root pointer Node* root ; switch( token ) { case ID : // token is an ID (variable name in the symbol table) root = new Id( tokchars ) ; // set the root to a new Id object with the appropriate name token = scan() ; // scan for the next token if ( token == '=' ) // token read is an assignment operator (=) { token = scan() ; // scan for the next token root = new Assign( (Id*) root, Expression() ) ; } // set the root to the result of assigning the expression // to the right of the = sign to the variable on the left return root ; // return the pointer to the root of this subtree case INT : // token is an integer root = new Int( atoi( tokchars ) ) ; // set the root to a new Id object with the value of the integer token = scan() ; // scan for the next token return root ; // return the pointer to the root of this subtree case '(' : // token is a left parenthesis token = scan() ; // scan for the next token root = Expression() ; // set the root to the result of calling Exression again if ( token != ')' ) // the last token read should be a right parenthesis error() ; // if it's not, display an error message token = scan() ; // scan for the next token return root ; // return the pointer to the root of this subtree case '-' : // token read is a unary minus sign token = scan() ; // scan for the next token return new Uminus( Factor() ) ; // return a new Uminus object whose value is the next factor default : // none of the above cases apply error() ; // display an error message return NULL ; // added by JMH to avoid compiler warning: } // "not all control paths return a value" } // application start point int main() { Node* root ; // the root of the parse tree while( true ) // loop until user enters a quit character { // prompt user for input and begin scanning the user's entry cout << "Enter expression > " << flush ; token = scan() ; // exit normally (no error flag) if user entered 'q' to quit if ( token == QUIT ) { cout << "Program terminated normally." << endl ; return 0 ; } // begin evaluating the user's entry if ( ( root = Expression() ) && token == EOLN ) { cout << " The result is > " << root->eval() << endl ; delete root ; // deallocate the evaluation tree } else error() ; // display an error message } }