/* * 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 #include #include #include 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: // Note: Caller is giving up control of the pointers. Asst(unique_ptr &t, unique_ptr &e): m_target(t.release()), m_source(e.release()) { } void pr(ostream &s, int indent) const; private: unique_ptr m_target; unique_ptr m_source; }; // An expression is an abstract class that doesn't do anything else. // Its provides an alternative by being the base class of the alternatives. // In Java terms, it's an interface with several implementors. class Expr: public ParserObject { }; // A Binary holds two operands and an operator. class Binary: public Expr { public: // Note: Caller is giving up control of the expression pointers. Binary(const Scanner::Token &o, unique_ptr &t1, unique_ptr &t2): m_oper(o), m_term1(t1.release()), m_term2(t2.release()) { } void pr(ostream &s, int indent) const; Scanner::Token oper() const { return m_oper; } private: Scanner::Token m_oper; unique_ptr m_term1, m_term2; }; // Variable just wraps the token and inherits from Expr class Variable: public Expr { public: Variable(const 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(const 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) { } // Build the parse tree. void parse() { root = unique_ptr(asst()); } // Print the tree. void print(ostream &); private: Scanner scanner; // Scanner object. Tokenizes input. unique_ptr 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