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