typedef int token;
typedef int integer;

enum type_kind {undefined_type, integer_type, boolean_type};

typedef struct symbol_block* symbol;

struct symbol_block {
    char * name;
    type_kind type;
    integer value;

    symbol_block(char * n, type_kind t, integer v) :
        name(n), type(t), value(v)
    {
    }
};

typedef struct table_block* table;
struct table_block {
    symbol head;
    table tail;

    table_block(symbol h, table t) :
        head(h), tail(t)
    {
    }

};

// ***<<< LEFT FOR YOU TO DEFINE >>>***
// ***<<< type symtab is linked_stack of table >>>***

typedef struct eval_result_block* eval_result;
struct eval_result_block {
    type_kind type;
    integer value;

    eval_result_block(type_kind t, integer v) :
        type(t), value(v)
    {
    }

};

typedef struct node_block* node;

struct node_block {

    virtual eval_result eval() {
        cerr << "Error: You forgot to implement eval for this node type\n";
    }
    virtual void pprint(int indent = 0) {
        cerr << "Error: You forgot to implement pprint for this node type\n";
    }

};

typedef struct node_pair* node_list;

struct node_pair {

    node head;
    node_list tail;
    
    node_pair(node h, node_list t) :
        head(h), tail(t)
    {
    }

    eval_result eval()
    {
    }

};

struct binary_node : node_block {

    token bin_op;
    node left, right;

    binary_node(token b_op, node l, node r) :
        bin_op(b_op), left(l), right(r)
    {
    }

    virtual eval_result eval() 
    {
    }
    virtual void pprint(int indent) 
    {
    }
};

struct unary_node : node_block {

    token un_op;
    node operand;

    unary_node(token u_op, node op) :
        un_op(u_op), operand(op)
    {
    }

    virtual eval_result eval() 
    {
    }
    virtual void pprint(int indent) 
    {
    }

};

struct integer_node : node_block {
    
    int value;

    integer_node(int v) :
        value(v)
    {
    }

    virtual eval_result eval() 
    {
    }
    virtual void pprint(int indent) 
    {
    }

};

struct ident_node : node_block {

    char * id_name;

    ident_node(char * n) :
        id_name(n)
    {
    }

    virtual eval_result eval() 
    {
    }
    virtual void pprint(int indent) 
    {
    }

};

struct assign_node : node_block {

    char * dest;
    node src;

    assign_node(char * d, node s) :
        dest(d), src(s)
    {
    }

    virtual eval_result eval() 
    {
    }
    virtual void pprint(int indent) 
    {
    }

};

struct var_decl_node : node_block {
    char * name;
    char * type;

    var_decl_node(char * n, char * t) :
        name(n), type(t)
    {
    }

    virtual eval_result eval() 
    {
    }
    virtual void pprint(int indent) 
    {
    }
};

struct declare_node : node_block {
    node_list decls;
    node_list stmts;

    declare_node(node_list d, node_list s) :
        decls(d), stmts(s)
    {
    }

    virtual eval_result eval() 
    {
    }
    virtual void pprint(int indent) 
    {
    }
};

struct if_node : node_block {
    node if_cond;
    node_list if_stmts;
    node if_next;

    if_node(node cond, node_list s, node n):
        if_cond(cond), if_stmts(s), if_next(n)
    {
    }

    virtual eval_result eval() 
    {
    }
    virtual void pprint(int indent) 
    {
    }
};

struct while_node : node_block {
    node while_cond;
    node_list while_stmts;

    while_node(node cond, node_list s) :
        while_cond(cond), while_stmts(s)
    {
    }

    virtual eval_result eval() 
    {
    }
    virtual void pprint(int indent) 
    {
    }

};

struct output_node : node_block {
    char * function;
    node argument;

    output_node(char * f, node arg) :
        function(f), argument(arg)
    {
    }

    virtual eval_result eval() 
    {
    }
    virtual void pprint(int indent) 
    {
    }

};

