#include "parser.h" #include "lexer.h" #include "number.h" #include Parser::Parser( Lexer &lex, Bu::Stream &rOut ) : lex( lex ), rOut( rOut ) { } Parser::~Parser() { } void Parser::parse() { for(;;) { Token t = lex.nextToken(); switch( t.eType ) { case Token::tEndOfInput: return; case Token::tEndOfLine: unwind(); if( !tsTerminal.isEmpty() ) { Bu::println( rOut, "%1").arg( deref( tsTerminal.peek() ) ); } tsTerminal.clear(); break; case Token::tCommand: if( *t.sVal == "exit" || *t.sVal == "quit" ) return; else if( *t.sVal == "scale" ) { Token t2 = lex.nextToken(); if( t2.eType == Token::tEndOfLine ) { Bu::println( rOut, "Current scale: %1"). arg( lex.getScale() ); } else if( t2.eType == Token::tNumber ) { int32_t i = t2.nVal->toInt32(); lex.setScale( i ); if( i < 0 ) { Bu::println( rOut, "ERROR: You must provide a " "positive integer or zero as the parameter " "to scale."); } else { Bu::println( rOut, "Scale changed to: %1"). arg( lex.getScale() ); for( VarHash::iterator i = hVars.begin(); i; i++ ) (*i).setScale( lex.getScale() ); nZero = Number( lex.getScale(), lex.getRadix() ); } } else { Bu::println( rOut, "ERROR: You must provide a number " "as the parameter to scale."); } } else if( *t.sVal == "radix" ) { Token t2 = lex.nextToken(); if( t2.eType == Token::tEndOfLine ) { Bu::println( rOut, "Current radix: %1"). arg( lex.getRadix() ); } else if( t2.eType == Token::tNumber ) { int32_t i = t2.nVal->toInt32(); if( i < 2 || i > 36 ) Bu::println( rOut, "ERROR: Radix must be between " "2 and 36 inclusive"); else { lex.setRadix( i ); Bu::println( rOut, "Radix changed to: %1"). arg( lex.getRadix() ); hVars.clear(); nZero = Number( lex.getScale(), lex.getRadix() ); } } else { Bu::println( rOut, "You must provide a number as " "the parameter to radix."); } } else if( *t.sVal == "vars" ) { Bu::println( rOut, "Declared variables:"); for( VarHash::iterator i = hVars.begin(); i; i++ ) { Bu::println( rOut, " - %1 = %2"). arg( i.getKey() ).arg( *i ); } Bu::println( rOut, ""); } else if( *t.sVal == "help" || *t.sVal == "h" || *t.sVal == "?" ) { Bu::println( rOut, "All commands start with a '\\'. The following commands are defined:\n" " \\help, \\h, \\? - This help\n" " \\exit, \\quit - Exit the program\n" " \\radix - Display the current radix (base)\n" " \\radix - Set the radix to anything between 2 and 36 (inclusive)\n" " \\scale - Display the current scale\n" " \\scale - Set the current scale\n" " \\vars - List all defined variables\n" "\n" "All variables must prefixed with a $ but otherwise can be used anywhere a\n" "number can be used. To assign a variable use =, i.e.:\n" " $amnt = $rate * 144000.09\n" "\n" "When using a radix greater than 10 all extended digits are lowercase letters\n" "starting with 'a'. Upper case is not currently supported.\n" "\n" "All numbers are interpreted in the current radix, this means that you need to\n" "set the radix using the current radix, even though the current value is aways\n" "displayed in decimal. That is, to go to base 8 and back to base 10 you would:\n" " \\radix 8\n" " Radix changed to: 8\n" " \\radix 12\n" " Radix changed to: 10\n" "\n" "Changing the radix always clears all variables.\n" ); } else { Bu::println( rOut, "ERROR: Unknown command '%1'"). arg( *t.sVal ); } break; case Token::tNumber: case Token::tVariable: tsTerminal.push( t ); break; default: if( tsNonTerminal.getSize() == 0 || getPriority( tsNonTerminal.peek().eType ) <= getPriority( t.eType ) ) { // Bu::println("Pushing non-terminal: %1").arg( t.eType ); tsNonTerminal.push( t ); // for( TokenStack::iterator i = tsTerminal.begin(); i; i++ ) Bu::print(" [%1]").arg( *(*i).nVal ); Bu::println(""); // for( TokenStack::iterator i = tsNonTerminal.begin(); i; i++ ) Bu::print(" <%1>").arg( (*i).eType ); Bu::println(""); } else { // Bu::println("Unwinding stack before pushing: %1").arg( t.eType ); unwind(); tsNonTerminal.push( t ); // for( TokenStack::iterator i = tsTerminal.begin(); i; i++ ) Bu::print(" [%1]").arg( *(*i).nVal ); Bu::println(""); // for( TokenStack::iterator i = tsNonTerminal.begin(); i; i++ ) Bu::print(" <%1>").arg( (*i).eType ); Bu::println(""); } break; } } } void Parser::unwind() { for(;;) { // for( TokenStack::iterator i = tsTerminal.begin(); i; i++ ) Bu::print(" [%1]").arg( *(*i).nVal ); Bu::println(""); // for( TokenStack::iterator i = tsNonTerminal.begin(); i; i++ ) Bu::print(" <%1>").arg( (*i).eType ); Bu::println(""); if( tsNonTerminal.isEmpty() ) return; if( tsTerminal.getSize() < reqTokens( tsNonTerminal.peek().eType ) ) { return; } Token t = tsNonTerminal.peekPop(); switch( t.eType ) { case Token::tPlus: { Token b = tsTerminal.peekPop(); Token a = tsTerminal.peekPop(); tsTerminal.push( Token( Token::tNumber, new Number( deref(a) + deref(b) ) ) ); } break; case Token::tMinus: { Token b = tsTerminal.peekPop(); Token a = tsTerminal.peekPop(); tsTerminal.push( Token( Token::tNumber, new Number( deref(a) - deref(b) ) ) ); } break; case Token::tMultiply: { Token b = tsTerminal.peekPop(); Token a = tsTerminal.peekPop(); tsTerminal.push( Token( Token::tNumber, new Number( deref(a) * deref(b) ) ) ); } break; case Token::tDivide: { Token b = tsTerminal.peekPop(); Token a = tsTerminal.peekPop(); tsTerminal.push( Token( Token::tNumber, new Number( deref(a) / deref(b) ) ) ); } break; case Token::tModulus: { Token b = tsTerminal.peekPop(); Token a = tsTerminal.peekPop(); tsTerminal.push( Token( Token::tNumber, new Number( deref(a) % deref(b) ) ) ); } break; case Token::tOpenParen: tsNonTerminal.push( t ); return; case Token::tCloseParen: unwind(); if( tsNonTerminal.peek().eType == Token::tOpenParen ) { tsNonTerminal.pop(); } else { throw Bu::ExceptionBase("Close paren found without open paren."); } break; case Token::tEquals: { Token b = tsTerminal.peekPop(); Token a = tsTerminal.peekPop(); if( a.eType != Token::tVariable ) { Bu::println("The left hand side of a = must be a variable."); tsTerminal.clear(); tsNonTerminal.clear(); return; } hVars.insert( *a.sVal, deref( b ) ); tsTerminal.push( Token( Token::tNumber, new Number( deref( b ) ) ) ); } break; case Token::tNumber: case Token::tVariable: case Token::tCommand: case Token::tEndOfLine: case Token::tEndOfInput: // These should never show up at all break; } } } int Parser::reqTokens( Token::Type eType ) { switch( eType ) { case Token::tPlus: case Token::tMinus: case Token::tDivide: case Token::tMultiply: case Token::tModulus: case Token::tEquals: return 2; case Token::tOpenParen: return 0; case Token::tCloseParen: return 1; default: return 0; } } int Parser::getPriority( Token::Type eType ) { switch( eType ) { case Token::tNumber: case Token::tVariable: case Token::tCommand: case Token::tEquals: return 0; case Token::tPlus: case Token::tMinus: return 1; case Token::tDivide: case Token::tMultiply: case Token::tModulus: return 2; case Token::tOpenParen: case Token::tCloseParen: return 3; case Token::tEndOfLine: case Token::tEndOfInput: return -1; default: throw Bu::ExceptionBase("Invalid type in getPriority"); } } Number &Parser::deref( Token &t ) { if( t.eType == Token::tNumber ) { return *t.nVal; } else if( t.eType == Token::tVariable ) { try { return hVars.get( *t.sVal ); } catch(...) { return nZero; } } else { throw Bu::ExceptionBase("Element was not a number or variable."); } }