#include "config.h" #include "parser.h" #include "lexer.h" #include "number.h" #include "datafiles.h" #include #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: while( !tsNonTerminal.isEmpty() ) unwind(); return; case Token::tEndOfLine: while( !tsNonTerminal.isEmpty() ) unwind(); if( !tsTerminal.isEmpty() ) { Bu::println( rOut, "%1").arg( deref( tsTerminal.peek() ) ); hVars.insert("ans", 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, Datafiles::getString("parserhelp.txt") ); } 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 ) ) { DBS( PARSE, Bu::println("Pushing non-terminal: %1").arg( t.eType ) ); tsNonTerminal.push( t ); } else { DBS( PARSE, Bu::println("Unwinding stack before pushing: %1") .arg( t.eType ) ); unwind(); tsNonTerminal.push( t ); } break; } } } Number Parser::getVariable( const Bu::String &sName ) { return hVars.get( sName ); } void Parser::setVariable( const Bu::String &sName, const Number &rValue ) { hVars.insert( sName, rValue ); } void Parser::unwind() { for(;;) { DBS_START( PARSE ); 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(""); DBS_END(); 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(); Number *pProduct = new Number( deref(a) * deref(b) ); pProduct->setScale( lex.getScale() ); tsTerminal.push( Token( Token::tNumber, pProduct ) ); } 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(); unwind(); } 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; } if( !tsNonTerminal.isEmpty() && getPriority( tsNonTerminal.peek().eType ) < getPriority( t.eType ) ) return; } } 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."); } }