------------------------------------------------------------------------------
MC logo
Parser Implementation
[^] Recursive Descent Parser
------------------------------------------------------------------------------
parser.cpp
#include <iostream>
#include <string>

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.
        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.
        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.
        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();
                Expr *t2 = term();

                // Combine the new term with the old one and the operation.
                t = new Binary(op, t, t2);
        }
        return t;
}

// 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.
        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();
                Expr *t2 = fact();

                // Combine the new factor with the old one and the operation.
                t = new Binary(op, t, t2);
        }
        return t;
}

// 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();
                Expr *e = expr();
                if(scanner.curr_tok().code() != Scanner::tok_right)
                        throw ParseError(")", scanner.curr_tok().text());
                scanner.next_tok();
                return e;
        }

        // 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());
        Variable *v = new Variable(scanner.curr_tok());
        scanner.next_tok();
        return v;
}
Value *Parser::val()
{
        if(scanner.curr_tok().code() != Scanner::tok_lit) 
                throw ParseError("const", scanner.curr_tok().text());
        Value *v = new Value(scanner.curr_tok());
        scanner.next_tok();
        return v;
}

// 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;
        }
}