/****
 *
 * This file defines a tree-generating parser for EJay programming language
 * being studied in CSC 330.
 *
 */
import java_cup.runtime.*;

parser code {:

    public void syntax_error(Symbol cur_token) {
        report_error("Sytax error at line " + (cur_token.left+1) +
            ", column " + cur_token.right, null);
    }

    public void initSymbolTable(int size) {
        symtab = new SymbolTable(size);
    }

    public SymbolTable getSymbolTable() {
        return symtab;
    }

    /** Reference to current symbol table.  This value changes as the program
     * is parsed, such that symtab points to the symbol table for the scope in
     * which the parse is currently active. */
    public SymbolTable symtab;

    /** Reference to the current function entry.  It is assigned each time a
     * function entry is created. */
    public FunctionEntry entry;

    /** Reasonable size for a function symbol table. */
    public final int FUNC_SYMTAB_SIZE = 25;

    /** Reasonable size for a block symbol table. */
    public final int BLOCK_SYMTAB_SIZE = 25;

    /** Incrementing counter for declared structs.  This number is used to
     * create a unique name for each struct, so it can be entered and
     * subsequently retrieved from a symbol table. */
    public int structNum;

    /** Incrementing counter for nested blocks.  This number is used to create
     * a unique name for each block, so it can be entered and subsequently
     * retrieved from a symbol table. */
    public int blockNum;

    /** Enter the vars in the given tree node list in the current symtab.  The
     * type of each var entry is the given type. */
    public void enterVars(TypeNode type, TreeNodeList vars) {
        TreeNode node;
        TreeNodeList rest;
        boolean done  = false;
        for (node = vars.node, rest = vars.siblings; !done; ) {
            symtab.enter(new VariableEntry(
                (String) (((LeafNode) node).value), type, false, 0,
		    symtab.level));
            if (rest == null) {
                done = true;
            }
            else {
                node = rest.node;
                rest = rest.siblings;
            }
        }
    }

    /** Enter an inner block by allocating a symtab for it, entering a
     * unique-named BlockEntry for it in the current symtab, and descending
     * into the new block symtab.  This function is used both for inner
     * statement blocks and struct types; the String-valued blockType parameter
     * is "block" or "struct" depending on the caller.
     *
     * NOTE: An acceptable solution to CSC 330 Assignment 3 does not need to
     * generate the unique entry name. */
    public void enterBlock(String blockType) {
        SymbolTable blockTab = new SymbolTable(BLOCK_SYMTAB_SIZE);
        BlockEntry blockEntry = new BlockEntry(blockTab);
        blockEntry.name = blockType + Integer.toString(
            blockType.equals("block") ? blockNum++ : structNum++);
        symtab.enter(blockEntry);
        blockTab.parent = symtab;
        symtab = blockTab;
    }

:}

terminal BOOLEAN;
terminal ELSE;
terminal IF;
terminal INT;
terminal VOID;
terminal WHILE;
terminal TRUE;
terminal FALSE;
terminal FLOAT;
terminal STRING;
terminal STRUCT;
terminal RETURN;
terminal PRINT;
terminal REF;
terminal LEFT_PAREN; // "("
terminal RT_PAREN;   // ")"
terminal LEFT_BRACE; // "{"
terminal RT_BRACE;   // "}"
terminal SEMI;       // ";"
terminal COMMA;      // ","
terminal LEFT_BRKT;  // "["
terminal RT_BRKT;    // "]"
terminal EQ;         // "="
terminal PLUS;       // "+"
terminal MINUS;      // "-"
terminal TIMES;      // "*"
terminal DIVIDE;     // "/"
terminal LESS;       // "<"
terminal LESS_EQ;    // "<="
terminal GTR;        // ">"
terminal GTR_EQ;     // ">="
terminal EQ_EQ;      // "=="
terminal NOT_EQ;     // "!="
terminal AND;        // "&&"
terminal OR;         // "||"
terminal NOT;        // "!"
terminal DOT;        // "."
terminal UNY_PLUS;   // unary "+"
terminal UNY_MINUS;  // unary "-"
terminal IDENT;      // identifier
terminal INTEGER;    // decimal, hex, or octal integer
terminal FLOATING_PT;// floating point
terminal STRING_LIT; // floating point

nonterminal TreeNodeList Program;
nonterminal TreeNodeList Declarations;
nonterminal TreeNode Declaration;
nonterminal TreeNode DataDeclaration;
nonterminal TreeNodeList DataDeclarations;
nonterminal TreeNode FunctionDeclaration;
nonterminal TreeNode4 FunctionHeader;
nonterminal TreeNode FunctionBlock;
nonterminal TypeNode Type;
nonterminal TreeNodeList Identifiers;
nonterminal TypeNode ArrayType;
nonterminal TypeNode StructType;
nonterminal TreeNode StructHeader;
nonterminal TreeNodeList Dimensions;
nonterminal LeafNode Identifier;
nonterminal TreeNode Integer;
nonterminal TreeNodeList Fields;
nonterminal TreeNode Field;
nonterminal TreeNodeList Formals;
nonterminal TreeNode Block;
nonterminal TreeNode BlockHeader;
nonterminal TreeNode FormalDeclaration;
nonterminal TreeNodeList Statements;
nonterminal TreeNode Statement;
nonterminal TreeNode Assignment;
nonterminal TreeNode IfStatement;
nonterminal TreeNode WhileStatement;
nonterminal TreeNode CallStatement;
nonterminal TreeNode ReturnStatement;
nonterminal TreeNode PrintStatement;
nonterminal TreeNode Designator;
nonterminal TreeNode Expression;
nonterminal TreeNode ArraySelection;
nonterminal TreeNode StructSelection;
nonterminal TreeNodeList Expressions;
nonterminal TreeNode2 RelationalOp;
nonterminal TreeNode2 AdditiveOp;
nonterminal TreeNode2 MultiplicativeOp;
nonterminal TreeNode1 UnaryOp;
nonterminal TreeNode Literal;
nonterminal TreeNode Float;
nonterminal TreeNode String;
nonterminal TreeNode Boolean;

precedence right RT_PAREN;
precedence right RT_BRKT;
precedence left OR;
precedence left AND;
precedence left LESS, LESS_EQ, GTR, GTR_EQ, EQ_EQ, NOT_EQ;
precedence left PLUS, MINUS;
precedence left TIMES, DIVIDE;
precedence left DOT;
precedence left LEFT_BRKT;
precedence left UNY_PLUS, UNY_MINUS;
precedence left LEFT_PAREN;
precedence left ELSE;

Program ::= Declarations:ds
                {: RESULT = ds; :}
        ;

Declarations ::= Declaration:d
                   {: RESULT = new TreeNodeList(d, null); :}
             | Declaration:d Declarations:ds
                   {: RESULT = new TreeNodeList(d, ds); :}
             ;

Declaration ::= DataDeclaration:dd SEMI
                  {: RESULT = dd; :}
            | FunctionDeclaration:fd
                  {: RESULT = fd; :}
            ;

DataDeclaration ::= Type:t Identifiers:is
                        {: RESULT = new TreeNode2(
			       sym.SEMI, t, is, tleft, tright);
                           parser.enterVars(t, is); :}
                ;

Type ::= INT:i
           {: RESULT = new TypeNode(sym.INT, ileft, iright); :}
     | FLOAT:f
           {: RESULT = new TypeNode(sym.FLOAT, fleft, fright); :}
     | STRING:s
           {: RESULT = new TypeNode(sym.STRING, sleft, sright); :}
     | BOOLEAN:b
           {: RESULT = new TypeNode(sym.BOOLEAN, bleft, bright); :}
     | VOID:v
           {: RESULT = new TypeNode(sym.VOID, vleft, vright); :}
     | ArrayType:at
           {: RESULT = at; :}
     | StructType:st
           {: RESULT = st; :}
     ;

ArrayType ::= Type:t LEFT_BRKT Dimensions:ds RT_BRKT
                  {: RESULT = new TypeNode(
		         sym.LEFT_BRKT, t, ds, tleft, tright); :}
          ;

Dimensions ::=
           | Integer:i
                 {: RESULT = new TreeNodeList(i, null); :}
           | Integer:i COMMA Dimensions:ds
                  {: RESULT = new TreeNodeList(i, ds); :}
           ;

StructType ::= StructHeader:s LEFT_BRACE Fields:fs RT_BRACE
                   {: RESULT = new TypeNode(sym.LEFT_BRACE, fs, sleft, sright);
		      RESULT.symtab = parser.symtab;
                      parser.symtab = parser.symtab.ascend(); :}
           ;

StructHeader ::= STRUCT
                   {: parser.enterBlock("struct"); :}
           ;

Fields ::= Field:f SEMI
             {: RESULT = new TreeNodeList(f, null); :}
       | Field:f SEMI Fields:fs
             {: RESULT = new TreeNodeList(f, fs); :}
       ;

Field ::= DataDeclaration:dd
              {: RESULT = dd; :}
      ;

FunctionDeclaration ::= FunctionHeader:fh LEFT_PAREN Formals:fs RT_PAREN FunctionBlock:b
                     {: RESULT = fh; fh.child3 = fs; fh.child4 = b;
                        parser.entry.formals = fs;
                        parser.entry.body = b;
                        parser.symtab = parser.symtab.ascend(); :}
                    ;

FunctionHeader ::= Type:t Identifier:i 
                     {: RESULT = new TreeNode4(sym.LEFT_PAREN,
			    t, i, null, null, tleft, tright);
                        parser.symtab = parser.symtab.newLevel(parser.entry =
                            new FunctionEntry((String) i.value, t, null,
                                null, null), parser.FUNC_SYMTAB_SIZE); :}
               ;

FunctionBlock ::= LEFT_BRACE:l RT_BRACE
                    {: RESULT = new TreeNode2(sym.LEFT_BRACE, null, null,
		           lleft, lright); :}
              | LEFT_BRACE:l Statements:ss RT_BRACE
                    {: RESULT = new TreeNode2(sym.LEFT_BRACE, null, ss,
		           lleft, lright); :}
              | LEFT_BRACE:l DataDeclarations:dds Statements:ss RT_BRACE
                    {: RESULT = new TreeNode2(sym.LEFT_BRACE, dds, ss,
		           lleft, lright); :}
              ;

Formals ::= 
	| FormalDeclaration:fd
              {: RESULT = new TreeNodeList(fd, null); :}
        | FormalDeclaration:fd COMMA Formals:fs
              {: RESULT = new TreeNodeList(fd, fs); :}
        ;

FormalDeclaration ::= Type:t Identifier:i
                          {: RESULT = new TreeNode3(sym.COMMA, t, i, null,
		                 tleft, tright);
                             parser.symtab.enter(new VariableEntry(
                                 (String) i.value, t, false, 0,
			             parser.symtab.level)); :}
                  | REF:r Type:t Identifier:i
                      {: RESULT = new TreeNode3(sym.COMMA, t, i,
                             new LeafNode(sym.REF, null, rleft, rright),
			         tleft, tright);
                         parser.symtab.enter(new VariableEntry(
                             (String) i.value, t, true, 0,
			         parser.symtab.level)); :}
                  ;

Block ::= LEFT_BRACE:l RT_BRACE
            {: RESULT = new TreeNode2(sym.LEFT_BRACE, null, null,
	           lleft, lright); :}
      | LEFT_BRACE:l Statements:ss RT_BRACE
            {: RESULT = new TreeNode2(sym.LEFT_BRACE, null, ss,
		           lleft, lright); :}
      | BlockHeader:b DataDeclarations:dds Statements:ss RT_BRACE
            {: RESULT = new TreeNode2(sym.LEFT_BRACE, dds, ss,
		           bleft, bright);
               parser.symtab = parser.symtab.ascend(); :}
      ;

BlockHeader ::= LEFT_BRACE
                 {: parser.enterBlock("block"); :}
           ;

DataDeclarations ::= DataDeclaration:dd SEMI
                       {: RESULT = new TreeNodeList(dd, null); :}
                 | DataDeclaration:dd SEMI DataDeclarations:dds
                       {: RESULT = new TreeNodeList(dd, dds); :}
                 ;

Statements ::= Statement:s
                 {: RESULT = new TreeNodeList(s, null);:}
           | Statement:s Statements:ss
                 {: RESULT = new TreeNodeList(s, ss); :}
           ;

Statement ::= SEMI:s
                {: RESULT = new LeafNode(sym.SEMI, null, sleft, sright); :}
          | Block:b
                {: RESULT = b; :}
          | Assignment:a
                {: RESULT = a; :}
          | IfStatement:is
                {: RESULT = is; :}
          | WhileStatement:ws
                {: RESULT = ws; :}
          | CallStatement:cs
                {: RESULT = cs; :}
          | ReturnStatement:rs
                {: RESULT = rs; :}
          | PrintStatement:ps
                {: RESULT = ps; :}
          ;

Assignment ::= Designator:d EQ Expression:e SEMI
                   {: RESULT = new TreeNode2(sym.EQ, d, e, dleft, dright); :}
           ;

Designator ::= Identifier:i
                 {: RESULT = i; :}
           | ArraySelection:as
                 {: RESULT = as; :}
           | StructSelection:ss
                 {: RESULT = ss; :}
           ;

ArraySelection ::= Designator:d LEFT_BRKT Expressions:es RT_BRKT
                     {: RESULT = new TreeNode2(sym.LEFT_BRKT, d, es,
		           dleft, dright); :}
               ;

StructSelection ::= Designator:d DOT Identifier:i
                      {: RESULT = new TreeNode2(sym.DOT, d, i,
		           dleft, dright); :}
                ;

IfStatement ::= IF:i LEFT_PAREN Expression:e RT_PAREN Statement:s
                  {: RESULT = new TreeNode3(sym.IF, e, s, null,
		           ileft, iright); :}
            | IF:i LEFT_PAREN Expression:e RT_PAREN Statement:s1 ELSE Statement:s2
                  {: RESULT = new TreeNode3(sym.IF, e, s1, s2,
		           ileft, iright); :}
            ;

WhileStatement ::= WHILE:w LEFT_PAREN Expression:e RT_PAREN Statement:s
                     {: RESULT = new TreeNode2(sym.WHILE, e, s,
		           wleft, wright); :}
               ;

CallStatement ::= Identifier:i LEFT_PAREN RT_PAREN SEMI
                    {: RESULT = new TreeNode2(sym.LEFT_PAREN, i, null,
		           ileft, iright); :}
              | Identifier:i LEFT_PAREN Expressions:es RT_PAREN SEMI
                    {: RESULT = new TreeNode2(sym.LEFT_PAREN, i, es,
		           ileft, iright); :}
              ;

ReturnStatement ::= RETURN:r Expression:e SEMI
                        {: RESULT = new TreeNode1(sym.RETURN, e,
		           rleft, rright); :}
                ;

PrintStatement ::= PRINT:p Expressions:es SEMI
                     {: RESULT = new TreeNode1(sym.PRINT, es,
		           pleft, pright); :}
               ;

Expression ::= Expression:e1 OR:op Expression:e2
                 {: RESULT = new TreeNode2(sym.OR, e1, e2, opleft, opright); :}
           | Expression:e1 AND:op Expression:e2
                 {: RESULT = new TreeNode2(sym.AND, e1, e2,
		        opleft, opright); :}
           | Expression:e1 RelationalOp:op Expression:e2
                 {: RESULT = op; op.child1 = e1; op.child2 = e2; :}  %prec LESS
           | Expression:e1 AdditiveOp:op Expression:e2
                 {: RESULT = op; op.child1 = e1; op.child2 = e2; :}  %prec PLUS
           | Expression:e1 MultiplicativeOp:op Expression:e2
                 {: RESULT = op; op.child1 = e1; op.child2 = e2; :} %prec TIMES
           | UnaryOp:op Expression:e
                 {: RESULT = op; op.child = e; :}               %prec UNY_MINUS
           | Identifier:i LEFT_PAREN RT_PAREN
                 {: RESULT = new TreeNode2(sym.RT_PAREN, i, null,
		        ileft, iright); :}
           | Identifier:i LEFT_PAREN Expressions:es RT_PAREN
                 {: RESULT = new TreeNode2(sym.RT_PAREN, i, es,
		        ileft, iright); :}
           | Designator:d
                 {: RESULT = d; :}                                    %prec DOT
           | Literal:l
                 {: RESULT = l; :}
           | LEFT_PAREN Expression:e RT_PAREN
                 {: RESULT = e; :}
           ;

RelationalOp ::= LESS:op
                   {: RESULT = new TreeNode2(sym.LESS, null, null,
			  opleft, opright); :}
             | LESS_EQ:op
                   {: RESULT = new TreeNode2(sym.LESS_EQ, null, null,
			  opleft, opright); :}
             | GTR:op
                   {: RESULT = new TreeNode2(sym.GTR, null, null,
			  opleft, opright); :}
             | GTR_EQ:op
                   {: RESULT = new TreeNode2(sym.GTR_EQ, null, null,
			  opleft, opright); :}
             | EQ_EQ:op
                   {: RESULT = new TreeNode2(sym.EQ_EQ, null, null,
			  opleft, opright); :}
             | NOT_EQ:op
                   {: RESULT = new TreeNode2(sym.NOT_EQ, null, null,
			  opleft, opright); :}
             ;

AdditiveOp ::= PLUS:op
                 {: RESULT = new TreeNode2(sym.PLUS, null, null,
		           opleft, opright);  :}
           | MINUS:op
                 {: RESULT = new TreeNode2(sym.MINUS, null, null,
		           opleft, opright); :}
           ;

MultiplicativeOp ::= TIMES:op
                     {: RESULT = new TreeNode2(sym.TIMES, null, null,
		           opleft, opright); :}
                 | DIVIDE:op
                     {: RESULT = new TreeNode2(sym.DIVIDE, null, null,
		           opleft, opright); :}
                 ;

UnaryOp ::= UNY_PLUS:op
              {: RESULT = new TreeNode1(sym.UNY_PLUS, null, opleft, opright); :}
        | UNY_MINUS:op
              {: RESULT = new TreeNode1(sym.UNY_MINUS, null, opleft, opright); :}
        | NOT:op
              {: RESULT = new TreeNode1(sym.NOT, null, opleft, opright); :}
        ;

Expressions ::= Expression:e
                  {: RESULT = new TreeNodeList(e, null); :}
            | Expression:e COMMA Expressions:es
                  {: RESULT = new TreeNodeList(e, es); :}
;

Identifiers ::= Identifier:i
                  {: RESULT = new TreeNodeList(i, null); :}
            | Identifier:i COMMA Identifiers:is
                  {: RESULT = new TreeNodeList(i, is); :}
            ;

Literal ::= Integer:i
              {: RESULT = i; :}
        | Float:f
              {: RESULT = f; :}
        | String:s
              {: RESULT = s; :}
        | Boolean:b
              {: RESULT = b; :}
        ;

Identifier ::= IDENT:i
                 {: RESULT = new LeafNode(sym.IDENT, i, ileft, iright); :}
           ;

Integer ::= INTEGER:i
              {: RESULT = new LeafNode(sym.INTEGER, i, ileft, iright); :}
        ;

Float ::= FLOATING_PT:fp
           {: RESULT = new LeafNode(sym.FLOATING_PT, fp, fpleft, fpright); :}
      ;

String ::= STRING_LIT:sl
             {: RESULT = new LeafNode(sym.STRING_LIT, sl, slleft, slright); :}
       ;

Boolean ::= TRUE:t
              {: RESULT = new LeafNode(sym.IDENT, null, tleft, tright); :}
        | FALSE:f
              {: RESULT = new LeafNode(sym.IDENT, null, fleft, fright); :}
        ;