From 2909f50d008920568f0e50da760b266388ccc124 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Mon, 22 Apr 2013 13:05:22 -0600 Subject: There is now a parser & calculator interface. --- src/parser.cpp | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 src/parser.cpp (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp new file mode 100644 index 0000000..21ccc11 --- /dev/null +++ b/src/parser.cpp @@ -0,0 +1,266 @@ +#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( *tsTerminal.peek().nVal ); + } + 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() ); + } + } + 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() ); + } + } + else + { + Bu::println( rOut, "You must provide a number as " + "the parameter to radix."); + } + } + else + { + Bu::println( rOut, "ERROR: Unknown command '%1'"). + arg( *t.sVal ); + } + break; + + case Token::tNumber: + 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( *a.nVal + *b.nVal ) ) + ); + } + break; + + case Token::tMinus: + { + Token b = tsTerminal.peekPop(); + Token a = tsTerminal.peekPop(); + tsTerminal.push( + Token( Token::tNumber, new Number( *a.nVal - *b.nVal ) ) + ); + } + break; + + case Token::tMultiply: + { + Token b = tsTerminal.peekPop(); + Token a = tsTerminal.peekPop(); + tsTerminal.push( + Token( Token::tNumber, new Number( *a.nVal * *b.nVal ) ) + ); + } + break; + + case Token::tDivide: + { + Token b = tsTerminal.peekPop(); + Token a = tsTerminal.peekPop(); + tsTerminal.push( + Token( Token::tNumber, new Number( *a.nVal / *b.nVal ) ) + ); + } + 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::tNumber: + case Token::tString: + 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: + 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::tString: + case Token::tCommand: + return 0; + + case Token::tPlus: + case Token::tMinus: + return 1; + + case Token::tDivide: + case Token::tMultiply: + 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"); + } +} + -- cgit v1.2.3