------------------------------------------------------------------------------
MC logo
Parser Definitions
[^] Recursive Descent Parser
------------------------------------------------------------------------------
parser.h
/*
 * This file contains a recursive-descent parser for the following
 * expression grammar:
 *   Asst --> Id := Expr ;
 *   Expr --> Term | { [ + | - ] Term }*
 *   Term --> Fact | { [ * | / ] Fact }*
 *   Fact --> Id | Lit | ( Expr )
 */

#ifndef _parser_h_
#define _parser_h_

#include <iostream>
#include <stdexcept>
#include <string>

using namespace std;

#include "util.h"
#include "scan.h"

/* These classes represent the abstract syntax of the parsed assignment. */
class Asst;             // Holds the parts of an assignment statement.
class Expr;             // Base class for any type of expression.
class Binary;           // Expression which is the result of a binary op.
class Variable;         // Expression which is a variable.
class Value;            // Expression which is a constant.

// An assignment statement is a target and an expression.
class Asst: public ParserObject {
public:
        Asst(Variable *t, Expr *e): m_target(t), m_source(e) { }
        void pr(ostream &s, int indent) const;
private:
        Variable *m_target;
        Expr *m_source;
};

// An expression is an abstract class that doesn't do anything else.
class Expr: public ParserObject {
};

// A Binary holds two operands and an operator.
class Binary: public Expr {
public:
        Binary(Scanner::Token o, Expr *t1, Expr *t2): 
                m_oper(o), m_term1(t1), m_term2(t2) { }
        void pr(ostream &s, int indent) const;  
        Scanner::Token oper() const { return m_oper; }
private:
        Scanner::Token m_oper;
        Expr *m_term1, *m_term2;
};

// Variable just wraps the token and inherits from Expr
class Variable: public Expr {
public:
        Variable(Scanner::Token v): m_var(v) { }
        void pr(ostream &s, int indent) const {
                s << spaces(indent) << "[ Variable: " << m_var << "]";
        }
private:
        Scanner::Token m_var;
};

// Ditto for value (constant)
class Value: public Expr {
public:
        Value(Scanner::Token v): m_val(v) { }
        void pr(ostream &s, int indent) const {
                s << spaces(indent) << "[ Value: " << m_val << "]";
        }
private:
        Scanner::Token m_val;
};

// Exception for parsing errors. 
class ParseError: public runtime_error {
public:
        ParseError(string exp, string got): 
                runtime_error("Parse error: Expecting " + exp +
                              ", got " + got) { }
};

// Here is the parser class.  It takes an input stream in the constructor,
// which it turns into a tree.  The print method will output the parse
// tree.
class Parser {
public:
        // Construct the object.
        Parser(istream &strm): scanner(strm), root(NULL) { }

        // Build the parse tree.
        void parse() { root = asst(); }

        // Print the tree.
        void print(ostream &);
private:
        Scanner scanner;        // Scanner object.  Tokenizes input.
        Asst *root;             // Root of parse tree.

        // Functions to find each non-terminal in the input.  Note: Each
        // finding function expects curr_tok to contain the first token
        // of whatever they are to find, and must make sure it contains
        // the next token after when they return.
        Asst *asst();
        Expr *expr();
        Expr *term();
        Expr *fact();

        // These find a variable or value in the input.  Their main purpose
        // is just to wrap the token object from the scanner in a Variable
        // or Value object which is derived from Expr.  They have the same
        // behavior regarding next_tok.
        Variable *var();
        Value *val();
};

#endif