Parser Implementation
#include <iostream> #include <string> #include <memory> using namespace std; #include "parser.h" // *** These are the parsing functions. *** // Look for the variable assigned, the :=, the expression, and the ;. // Wrap 'em up in a nice little Asst-shaped box and return them. Asst * Parser::asst() { // First thing we should find is a variable. unique_ptr<Variable> v(var()); // The assigned variable is supposed to be followed by :=. if(scanner.curr_tok().code() != Scanner::tok_asst) throw ParseError(":=", scanner.curr_tok().text()); scanner.next_tok(); // Now recur to find the expression. unique_ptr<Expr> e(expr()); // And make sure the expression is followed by a semicolon. If so, // we've found all the stuff and we return the containing object. if(scanner.curr_tok().code() != Scanner::tok_semi) throw ParseError(";", scanner.curr_tok().text()); scanner.next_tok(); return new Asst(v, e); } // Look for the expr, based on its rule. If it's followed by + or -, consume // it and look for another term. Repeat until the following token is not a +. // or -. Expr *Parser::expr() { // The value of t will be our return value, but the loop will // change it if we enter. unique_ptr<Expr> t(term()); // Repeat while we see an appropriate op, which must be // followed by another term. while(scanner.curr_tok().code() == Scanner::tok_plus || scanner.curr_tok().code() == Scanner::tok_minus) { // Record the operation, and find the term on the other side of // it. Scanner::Token op = scanner.curr_tok(); // Known to be + or -. scanner.next_tok(); unique_ptr<Expr> t2(term()); // Combine the new term with the old one and the operation. t = unique_ptr<Expr>(new Binary(op, t, t2)); } return t.release(); } // This is almost exactly like Parser::expr(), and they could be combined // (though the parameterization would be a killer). Complicated enough // as it stands. Expr *Parser::term() { // The value of t will be our return value, but the loop will // change it if we enter. unique_ptr<Expr> t(fact()); // Repeat while we see an appropriate op, which must be // followed by another term. while(scanner.curr_tok().code() == Scanner::tok_splat || scanner.curr_tok().code() == Scanner::tok_slash) { // Record the operation, and find the factor on the other side // of it. Scanner::Token op = scanner.curr_tok(); // Known to be * or / scanner.next_tok(); unique_ptr<Expr> t2(fact()); // Combine the new factor with the old one and the operation. t = unique_ptr<Expr>(new Binary(op, t, t2)); } return t.release(); } // A factor is one of three things. Look at the terminals to see what. Expr *Parser::fact() { // Must be an identifier, a literal, or start with (. if(scanner.curr_tok().code() == Scanner::tok_id) // The variable is it. return var(); if(scanner.curr_tok().code() == Scanner::tok_lit) // The constant value is it. return val(); if(scanner.curr_tok().code() == Scanner::tok_left) { // Consume the (, match the contained expression, then match and // consume the closing ). If all that succeeds, return the // expression. scanner.next_tok(); unique_ptr<Expr> e(expr()); if(scanner.curr_tok().code() != Scanner::tok_right) throw ParseError(")", scanner.curr_tok().text()); scanner.next_tok(); return e.release(); } // That took care of all the legal possibilities. All that's left // is to barf. throw ParseError("id, literal, or (", scanner.curr_tok().text()); } // Just see if we have the appropriate token. If so, consume it and // place it in a wrapper object to return. Variable *Parser::var() { if(scanner.curr_tok().code() != Scanner::tok_id) throw ParseError("id", scanner.curr_tok().text()); unique_ptr<Variable> v(new Variable(scanner.curr_tok())); scanner.next_tok(); return v.release(); } Value *Parser::val() { if(scanner.curr_tok().code() != Scanner::tok_lit) throw ParseError("const", scanner.curr_tok().text()); unique_ptr<Value> v(new Value(scanner.curr_tok())); scanner.next_tok(); return v.release(); } // Print for the assignment. void Asst::pr(ostream &s, int indent) const { s << spaces(indent) << "[ Asst: " << endl; m_target->pro(s, indent+2) << endl; m_source->pro(s, indent+2) << endl; s << spaces(indent) << "]"; } // Print the binary object. Much the same. void Binary::pr(ostream &s, int indent) const { s << spaces(indent) << "[ Binary, op = " << m_oper << ":" << endl; m_term1->pro(s, indent+2) << endl; m_term2->pro(s, indent+2) << endl; s << spaces(indent) << "]"; } // Print from the parser prints the tree. void Parser::print(ostream &s) { if(root == NULL) s << "No parse tree produced." << endl; else { s << *root << endl; if(scanner.curr_tok().code() != Scanner::tok_EOF) cout << "Notice: Extra input after asst." << endl; } }