/****************************************************/
/* File: tiny.y                                     */
/* The TINY Yacc/Bison specification file           */
/* Compiler Construction: Principles and Practice   */
/* Kenneth C. Louden                                */
/****************************************************/
%{
#define YYPARSER /* distinguishes Yacc output from other code files */

#include "globals.h"
#include "util.h"
#include "scan.h"
#include "parse.h"

#define YYSTYPE TreeNode *
static char * savedName; /* for use in assignments */
static int savedNumber;
static int savedLineNo;  /* ditto */
static TreeNode * savedTree; /* stores syntax tree for later return */
static int yylex(void); // added 11/2/11 to ensure no conflict with lex
int yyerror(char * message);
%}

%token IF ELSE WHILE RETURN INT VOID
%token EQ NE LT LE GT GE LPAREN RPAREN LBRACE LCURLY RBRACE RCURLY SEMI COMMA
%token ID NUM

%left PLUS MINUS
%left TIMES OVER
%right ASSIGN

%nonassoc THEN
%nonassoc ELSE

%token ERROR

%% /* Grammar for C-MINUS */

program : declaration_list
            {savedTree = $1;};

declaration_list : declaration_list declaration {
                      YYSTYPE t = $1;
                      if (t != NULL) {
                        while (t->sibling != NULL) {
                          t = t->sibling;
                        }
                        t->sibling = $2;
                        $$ = $1;
                      } else { $$ = $2; };
                    }
                    | declaration { $$ = $1; }
                    ;

declaration : var_declaration {$$ = $1; } | func_declaration { $$ = $1; };

name_specifier : ID {
  savedName = copyString(tokenString);
  savedLineNo = lineno;
};

number_specifier : NUM {
  savedNumber = atoi(tokenString);
  savedLineNo = lineno;
};

var_declaration : type_specifier name_specifier SEMI {
  $$ = newDeclNode(VarK);
  $$->lineno = savedLineNo;
  $$->attr.name = savedName;
  $$->type = $1->type;
  free($1);
} | type_specifier name_specifier LBRACE number_specifier RBRACE SEMI {
  $$ = newDeclNode(ArrVarK);
  $$->lineno = savedLineNo;
  if ($1->type == Integer)
    $$->type = IntegerArray;
  else
    $$->type = Void;
  $$->attr.name = savedName;
  free($1);
  $$->child[0] = newExpNode(ConstK);
  $$->child[0]->type = Integer;
  $$->child[0]->attr.val = savedNumber;
};

type_specifier : INT {
  $$ = newTypeNode(TypeNameK);
  $$->type = Integer;
}
| VOID {
  $$ = newTypeNode(TypeNameK);
  $$->type = Void;
};

func_declaration : type_specifier name_specifier {
  $$ = newDeclNode(FuncK);
  $$->lineno = savedLineNo;
  $$->attr.name = savedName;
  $$->type = $1->type;  /* 타입 바로 복사 */
} LPAREN params RPAREN compound_stmt {
  $$ = $3;
  $$->child[0] = $5;  /* params */
  $$->child[1] = $7;  /* compound_stmt */
};

params : param_list { $$ = $1; } | VOID {
  $$ = newDeclNode(NonArrParamK);
  $$->type = Void;
 };

param_list : param_list COMMA param {
  YYSTYPE t = $1;
  if (t != NULL) {
    while (t->sibling != NULL) {
      t = t->sibling;
    }  
    t->sibling = $3;
    $$ = $1;
  } else {
    $$ = $3;
    };
} | param {$$ = $1; };

param : type_specifier name_specifier {
  $$ = newDeclNode(NonArrParamK);
  $$->attr.name = savedName;
  $$->type = $1->type;
} | type_specifier name_specifier LBRACE RBRACE {
  $$ = newDeclNode(ArrParamK);
  $$->attr.name = savedName;
  if ($1->type == Integer)
    $$->type = IntegerArray;
  else
    $$->type = Void;
};

compound_stmt : LCURLY local_declarations statement_list RCURLY {
  $$ = newStmtNode(CompK);
  $$->lineno = lineno;
  $$->child[0] = $2;
  $$->child[1] = $3;
};

local_declarations : local_declarations var_declaration {
  YYSTYPE t = $1;
  if (t != NULL) {
    while (t->sibling != NULL)
      t = t->sibling;
    t->sibling = $2;
    $$ = $1;
  } else $$ = $2;
} | { $$ = NULL; };

statement_list : statement_list statement {
  YYSTYPE t = $1;
  if (t != NULL) {
    while (t->sibling != NULL)
      t = t->sibling;
    t->sibling = $2;
    $$ = $1;
  } else $$ = $2;
} | { $$ = NULL; };

statement : expression_stmt { $$ = $1; }
          | compound_stmt { $$ = $1; }
          | selection_stmt { $$ = $1; }
          | iteration_stmt { $$ = $1; }
          | return_stmt { $$ = $1; }
          ;

expression_stmt : expression SEMI { $$ = $1; }
                  | SEMI { $$ = NULL; }
                  ;
selection_stmt : IF LPAREN expression RPAREN statement %prec THEN {
  $$ = newStmtNode(IfK);
  $$->lineno = lineno;
  $$->child[0] = $3;
  $$->child[1] = $5;
} | IF LPAREN expression RPAREN statement ELSE statement {
  $$ = newStmtNode(IfK);
  $$->lineno = lineno;
  $$->child[0] = $3;
  $$->child[1] = $5;
  $$->child[2] = $7;
};

iteration_stmt : WHILE LPAREN expression RPAREN statement {
  $$ = newStmtNode(IterK);
  $$->lineno = lineno;
  $$->child[0] = $3;
  $$->child[1] = $5;
};

return_stmt : RETURN SEMI {
  $$ = newStmtNode(ReturnK);
  $$->lineno = lineno;
} | RETURN expression SEMI {
  $$ = newStmtNode(ReturnK);
  $$->lineno = lineno;
  $$->child[0] = $2;
};

expression : var ASSIGN expression {
  $$ = newExpNode(AssignK);
  $$->lineno = lineno;
  $$->type = $3->type;
  $$->child[0] = $1;
  $$->child[1] = $3;
} | simple_expression { $$ = $1; };

var : name_specifier {
  $$ = newExpNode(IdK);
  $$->attr.name = savedName;
} | name_specifier {
  $$ = newExpNode(ArrIdK);
  $$->attr.name = savedName;
} LBRACE expression RBRACE {
  $$ = $2;
  $$->child[0] = $4;
};

simple_expression : additive_expression relop additive_expression {
  $$ = $2;
  $$->lineno = $2->lineno;
  $$->child[0] = $1;
  $$->child[1] = $3;
  $$->type = Integer;
} | additive_expression { $$ = $1; };

relop : LE {
  $$ = newExpNode(OpK);
  $$->lineno = lineno;
  $$->attr.op = LE;
} | LT {
  $$ = newExpNode(OpK);
  $$->lineno = lineno;
  $$->attr.op = LT;
} | GT {
  $$ = newExpNode(OpK);
  $$->lineno = lineno;
  $$->attr.op = GT;
} | GE {
  $$ = newExpNode(OpK);
  $$->lineno = lineno;
  $$->attr.op = GE;
} | EQ {
  $$ = newExpNode(OpK);
  $$->lineno = lineno;
  $$->attr.op = EQ;
} | NE {
  $$ = newExpNode(OpK);
  $$->lineno = lineno;
  $$->attr.op = NE;
};

additive_expression : additive_expression addop term {
  $$ = $2;
  $$->lineno = $2->lineno;
  $$->child[0] = $1;
  $$->child[1] = $3;
} | term { $$ = $1; };

addop : PLUS {
  $$ = newExpNode(OpK);
  $$->lineno = lineno;
  $$->attr.op = PLUS;
} | MINUS {
  $$ = newExpNode(OpK);
  $$->lineno = lineno;
  $$->attr.op = MINUS;
};

term : term mulop factor {
  $$ = $2;
  $$->lineno = $2->lineno;
  $$->child[0] = $1;
  $$->child[1] = $3;
} | factor { $$ = $1; };

mulop : TIMES {
  $$ = newExpNode(OpK);
  $$->lineno = lineno;
  $$->attr.op = TIMES;
} | OVER {
  $$ = newExpNode(OpK);
  $$->lineno = lineno;
  $$->attr.op = OVER;
};

factor : LPAREN expression RPAREN { $$ = $2; }
  | var { $$ = $1; }
  | call { $$ = $1; }
  | NUM {
  $$ = newExpNode(ConstK);
  $$->lineno = lineno;
  $$->type =  Integer;
  $$->attr.val = atoi(tokenString);
  }
  ;

call : name_specifier {
    $$ = newExpNode(CallK);
    $$->lineno = lineno;
    $$->attr.name = savedName;
  } LPAREN args RPAREN {
    $$ = $2;
    $$->child[0] = $4;
  };

args : arg_list { $$ = $1; } | { $$ = NULL; } ;

arg_list : arg_list COMMA expression {
  YYSTYPE t = $1;
  if (t != NULL) {
    while (t->sibling != NULL)
      t = t->sibling;
    t->sibling = $3;
    $$ = $1;
  } else $$ = $3;
} | expression { $$ = $1; } ;


%%

int yyerror(char * message)
{ fprintf(listing,"Syntax error at line %d: %s\n",lineno,message);
  fprintf(listing,"Current token: ");
  printToken(yychar,tokenString);
  Error = TRUE;
  return 0;
}

/* yylex calls getToken to make Yacc/Bison output
 * compatible with ealier versions of the TINY scanner
 */
static int yylex(void)
{ return getToken(); }

TreeNode * parse(void)
{ yyparse();
  return savedTree;
}

