#include "parser.h" #include "lexer.h" #include "number.h" #include #include #define DEBUG 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(); lex.setMode( Lexer::modeNormal ); break; case Token::tCommand: lex.setMode( Lexer::modeCommand ); 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::tString ) { int32_t i = strtol( t2.sVal->getStr(), 0, 10 ); 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::tString ) { int32_t i = strtol( t2.sVal->getStr(), 0, 10 ); 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 numeric command parameters (i.e. \\scale, \\radix) are in base 10 no\n" "matter what radix is currently set. These are also displayed in base 10\n" "at all times.\n" "\n" "Changing the radix always clears all variables.\n" ); } else { Bu::println( rOut, "ERROR: Unknown command '%1'"). arg( *t.sVal ); } lex.setMode( Lexer::modeNormal ); break; case Token::tNumber: case Token::tVariable: tsTerminal.push( t ); break; default: if( tsNonTerminal.getSize() == 0 || getPriority( tsNonTerminal.peek().eType ) <= getPriority( t.eType ) ) { #ifdef DEBUG Bu::println("Pushing non-terminal: %1").arg( t.eType ); #endif tsNonTerminal.push( t ); } else { #ifdef DEBUG Bu::println("Unwinding stack before pushing: %1").arg( t.eType ); #endif unwind(); tsNonTerminal.push( t ); } break; } } } void Parser::unwind() { for(;;) { #ifdef DEBUG for( TokenStack::iterator i = tsTerminal.begin(); i; i++ ) { if( (*i).eType == Token::tNumber ) Bu::print(" [%1]").arg( *(*i).nVal ); else Bu::print(" [%1]").arg( *(*i).sVal ); } Bu::println(""); for( TokenStack::iterator i = tsNonTerminal.begin(); i; i++ ) Bu::print(" <%1>").arg( (*i).eType ); Bu::println(""); #endif 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::tString: 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: 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: case Token::tEquals: 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."); } }