/* SI 413 Fall 2011
 * ast.hpp
 * This is a C++ header file for the AST class hierarchy.
 */

#ifndef AST_HPP
#define AST_HPP

#include <fstream>
#include <string>
#include <vector>
#include <stack>
#include <set>
#include <map>
#include <cstdlib>
using namespace std;

#include "value.hpp"
#include "st.hpp"

// This enum type gives codes to the different kinds of operators.
enum Oper {
  ADD, SUB,
  MUL, DIV,
  LT, GT, LE, GE, 
  EQ, NE,
  AND, OR, NOTOP
};

// Takes an Oper code and returns the corresponding operator string.
// This is used to generate the text in AST nodes, and for debugging.
const char* opstring(Oper op);

/* The AST class is the super-class for abstract syntax trees.
 * Every type of AST (or AST node) has its own subclass.
 */
class AST {
  private:
    /* Adds this node and all children to the output stream in DOT format. */
    int addToDot(ostream& out, int& nodes);

  protected:
    /* Writes the label for this AST node to the given output stream. */
    virtual void writeLabel(ostream& out) = 0;

  public:
    /* These two methods define the tree structure of the AST. */
    virtual int numChildren() = 0;
    virtual AST* getChild(int i) = 0;

    /* Writes this AST to a .dot file as named. */
    void writeDot(const char* fname);
};

/* A Stmt is anything that can be evaluated at the top level such
 * as I/O, assignments, and control structures.
 * The last child of any statement is the next statement in sequence.
 */
class Stmt :public AST {
  private:
    Stmt* next;

  protected:
    // From AST
    void writeLabel(ostream& out) {
      out << getType() << ":stmt" << flush;
    }

    /* Returns a string indicating the type of statement this is. */
    virtual const char* getType() = 0;

    /* The last child (next statement in sequence) is handled in this
     * superclass, Stmt. All the other children are called "parts" and
     * are indicated and returned with the following two functions that
     * subclasses must implement.
     */
    virtual int numParts() = 0;
    virtual AST* getPart(int i) = 0;

  public:
    /* This static method is for building sequences of statements by the
     * parser. It takes two statements, and appends one after the other.
     * The returned value is a pointer to the new statement representing
     * the sequence.
     */
    static Stmt* append(Stmt* a, Stmt* b);

    /* Default constructor. The next statement pointer will be set to an
     * object of the NullStmt class. */
    Stmt();

    /* Using this constructor manually specifies the next statement in
     * sequence. */
    Stmt (Stmt* nextStmt) :next(nextStmt) { }

    /* This is the command that must be implemented everywhere to
     * execute this Stmt - that is, do whatever it is that this statement
     * says to do. */
    virtual void exec()
    { cerr << "Exec not yet implemented in " << getType() << endl; }

    /* This method just executes the current statement, and then
     * executes all the following statements, in sequence.
     * It can be overridden in sub-classes but usually won't need to be. */
    virtual void execAll() {
      exec();
      next->execAll();
    }

    /* Gets and sets the next statement in the sequence. */
    Stmt* getNext() { return next; }
    void setNext(Stmt* n) { next = n; }

    /* From the AST class - doesn't need to be overridden by subclasses. */
    virtual int numChildren() { return numParts() + 1; }

    /* From the AST class - doesn't need to be overridden by subclasses. */
    virtual AST* getChild(int i)
    { if (i == numParts()) return next; else return getPart(i); }

    /* Just indicates whether this is an instance of the NullStmt class. */
    virtual bool isNull() { return false; }
};

/* Every AST node that is not a Stmt is an Exp.
 * These represent actual computations that return something
 * (in particular, a Value object).
 */
class Exp :public AST {
 public:
  /* This is the method that must be overridden by all subclasses.
   * It should perform the computation specified by this node, and
   * return the resulting value that gets computed. */
  virtual Value eval() {
    cerr << "Eval not yet implemented in ";
    writeLabel(cerr);
    cerr << endl;
    return 0;
  }
};

/* A leaf in the AST tree for literal types (id, num, bool). */
class Atom :public Exp {
  protected:
    void writeLabel(ostream& out);

    /* Returns a string for the type of this Atom. */
    virtual const char* getType() = 0;

    /* Writes the value stored in this atom to the output stream. */
    virtual void writeValue(ostream& out) = 0;

  public:
    // Two methods from AST:
    int numChildren() { return 0; }
    AST* getChild(int i) { return NULL; }
};

/* An identifier, i.e. variable or function name. */
class Id :public Atom {
  private:
    string val;

  protected:
    const char* getType() { return "id"; }

    void writeValue(ostream& out) { out << val; }

  public:
    // Constructor from a C-style string
    Id(const char* v) :val(v) { }

    // Returns a reference to the stored string value.
    string& getVal() { return val; }
};

/* A literal number in the program. */
class Num :public Atom {
  private:
    int val;

  protected:
    const char* getType() { return "num"; }

    void writeValue(ostream& out) { out << val; }

  public:
    Num(int v) :val(v) { }

    // To evaluate, just return the number!
    Value eval() { return val; }
};

/* A literal boolean value like "true" or "false" */
class BoolExp :public Atom {
  private:
    bool val;

  protected:
    const char* getType() { return "bool"; }

    void writeValue(ostream& out) { out << (val ? "true" : "false"); }

  public:
    BoolExp(bool v) :val(v) { }
};

/* This class represents a binary operation. That is, an operation with
 * exactly two arguments, such as +, *, /, and, <=, etc. */
class BinOp :public Exp {
  protected:
    Exp* args[2];
    Oper op;

    void writeLabel(ostream& out) 
    { out << "binop:exp\\n" << opstring(op) << flush; }

  public:
    BinOp(Exp* l, Oper o, Exp* r) :op(o) 
    { args[0] = l; args[1] = r; }

    /* Two methods from the AST superclass will be taken care of here. */
    int numChildren() { return 2; }
    AST* getChild(int i) { return args[i]; }
};

/* A binary opration for arithmetic, like + or *. */
class ArithOp :public BinOp {
  public:
    ArithOp(Exp* l, Oper o, Exp* r) :BinOp(l,o,r) { }
};

/* A binary operation for comparison, like < or !=. */
class CompOp :public BinOp {
  public:
    CompOp(Exp* l, Oper o, Exp* r) :BinOp(l,o,r) { }
};

/* A binary operation for boolean logic, like "and". */
class BoolOp :public BinOp {
  public:
    BoolOp(Exp* l, Oper o, Exp* r) :BinOp(l,o,r) { }
};

/* This class represents a unary operation. That is, an operation with
 * just one argument, such as "not" and -. */
class UnarOp :public Exp {
  protected:
    Oper op;
    Exp* arg;

    void writeLabel(ostream& out) 
    { out << "unarop:exp\\n" << opstring(op) << flush; }

  public:
    UnarOp(Oper o, Exp* l) :op(o), arg(l) { }

    /* Two methods from AST superclass are taken care of here. */
    int numChildren() { return 1; }
    AST* getChild(int i) { return arg; }
};

/* This class represents a unary negation operation. */
class NegOp :public UnarOp {
  public:
    NegOp(Exp* l) :UnarOp(SUB,l) { }
};

/* This class represents a unary "not" operation. */
class NotOp :public UnarOp {
  public:
    NotOp(Exp* l) :UnarOp(NOTOP,l) { }
};

/* This is a statement for a block of code, i.e., code enclosed
 * in curly braces { and }.
 * Eventually, this is where scopes will begin and end.
 */
class Block :public Stmt {
  private:
    Stmt* body;

  protected:
    const char* getType() { return "block"; }

    int numParts() { return 1; }
    AST* getPart(int i) { return body; }

  public:
    Block(Stmt* b) :body(b) { }
};

/* This class is necessary to terminate a sequence of statements. */
class NullStmt :public Stmt {
  protected:
    const char* getType() { return "null"; }

    int numParts() { return 0; }
    AST* getPart(int i) { return NULL; }
   
  public:
    NullStmt() :Stmt(NULL) { }
    
    /* These AST methods are overridden because NullStmt is very special. */
    int numChildren() { return 0; }
    AST* getChild(int i) { return NULL; }

    bool isNull() { return true; }

    // Nothing to execute!
    void execAll() { }
};

/* This class is for plain "if" statements without an else clause. */
class IfStmt :public Stmt {
  private:
    Exp* clause;
    Stmt* ifblock;

  protected:
    const char* getType() { return "if"; }

    int numParts() { return 2; }
    AST* getPart(int i)
    { if (i == 0) return clause; else return ifblock; }
   
  public:
    IfStmt(Exp* e, Stmt* s) :clause(e), ifblock(s) { }
};

/* This class is for if-else statements. */
class IfElse :public Stmt {
  private:
    Exp* clause;
    Stmt* ifblock;
    Stmt* elseblock;
   
  protected:
    const char* getType() { return "ifelse"; }

    int numParts() { return 3; }
    AST* getPart(int i);

  public:
    IfElse(Exp* e, Stmt* ib, Stmt* eb)
      :clause(e), ifblock(ib), elseblock(eb)
    { }
};

/* Class for while(...) {...} statements. */
class WhileStmt :public Stmt {
  private:
    Exp* clause;
    Stmt* block;
   
  protected:
    const char* getType() { return "while"; }

    int numParts() { return 2; }
    AST* getPart(int i)
    { if (i == 0) return clause; else return block; }
   
  public:
    WhileStmt(Exp* c, Stmt* b) :clause(c), block(b) { }
};

/* An assignment statement. This represents a RE-binding in the symbol table. */
class Asn :public Stmt {
  protected:
    Id* lhs;
    Exp* rhs;
   
    /* Returns the Id that we are assigning to. */
    Id* getLhs() { return lhs; }
    
    virtual const char* getType() { return "asn"; }

    int numParts() { return 2; }
    AST* getPart(int i) { return (i == 0 ? lhs : rhs); }
   
  public:
    Asn(Id* l, Exp* r) :lhs(l), rhs(r) { }
};

/* A "new" statement is a subclass of assignment because it
 * has the same functionality as assignment, plus the additional
 * functionality that a new binding is created. */
class NewStmt :public Asn {
  protected:
    const char* getType() { return "new"; }

  public:
    NewStmt(Id* l, Exp* r) :Asn(l,r) { }
};

/* A write statement. */
class Write :public Stmt {
  private:
    Exp* val;

  protected:
    const char* getType() { return "write"; }

    virtual int numParts() { return 1; }
    virtual AST* getPart(int i) { return val; }

  public:
    Write(Exp* v) :val(v) { }

    // Executes this statement by writing the value of the expression to
    // standard out.
    void exec() { val->eval().writeTo(cout); cout << endl; }
};

/* A read statement. */
class Read :public Stmt {
  private:
    Id* var;
   
  protected:
    const char* getType() { return "read"; }

    virtual int numParts() { return 1; }
    virtual AST* getPart(int i) { return var; }

  public:
    Read(Id* v) :var(v) { }
};

/* A lambda expression consists of a parameter name and a body. */
class Lambda :public Exp {
  private:
    Id* var;
    Block* body;

  protected:
    void writeLabel(ostream& out) { out << "lambda:exp" << flush; }

  public:
    Lambda(Id* v, Block* b) :var(v), body(b) { }

    // These getter methods are necessary to support actually calling
    // the lambda sometime after it gets created.
    string& getVar() { return var->getVal(); }
    Block* getBody() { return body; }

    int numChildren() { return 2; }
    AST* getChild(int i) 
    { if (i == 0) return var; else return body; }
};

/* A function call consists of the function name, and the actual argument.
 * Note that all functions are unary. */
class Funcall :public Exp {
  private:
    Exp* funexp;
    Exp* arg;
  
  protected:
    void writeLabel(ostream& out) { out << "funcall:exp" << flush; }

  public:
    Funcall(Exp* f, Exp* a) :funexp(f), arg(a) { }

    int numChildren() { return 2; }
    AST* getChild(int i) { return (i==0 ? funexp : arg); }
};

#endif //AST_HPP