/*
* 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>
#include <memory>
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<Variable> &t, unique_ptr<Expr> &e):
m_target(t.release()), m_source(e.release()) { }
void pr(ostream &s, int indent) const;
private:
unique_ptr<Variable> m_target;
unique_ptr<Expr> 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<Expr> &t1, unique_ptr<Expr> &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<Expr> 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>(asst()); }
// Print the tree.
void print(ostream &);
private:
Scanner scanner; // Scanner object. Tokenizes input.
unique_ptr<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