%code requires {
    #include "../src/ast.h"
}

%{
#include <stdio.h>
#include "../src/ast.h"

void yyerror(FILE *fp, Node **pRootNode, char *s)
{
    fprintf (stderr, "%s\n", s);
}

extern char *yytext;
extern int yylex (void);
extern FILE *yyin;
%}

%define api.value.type {struct Node*}

%token VOID
%token INT
%token UINT
%token FLOAT
%token DOUBLE
%token STRING
%token BOOL
%token STRUCT
%token RETURN
%token STATIC
%token REFERENCE
%token ALLOC
%token IF
%token ELSE
%token IN
%token FOR
%token NUMBER
%token ID
%token STRING_LITERAL
%token PLUS
%token MINUS
%token STAR
%token SLASH
%token PERCENT
%token EQUAL
%token LESS_THAN
%token GREATER_THAN
%token QUOTE
%token BANG
%token BAR
%token AMPERSAND
%token POINT
%token COMMA
%token SEMICOLON
%token COLON
%token QUESTION
%token LEFT_PAREN
%token RIGHT_PAREN
%token LEFT_BRACE
%token RIGHT_BRACE
%token LEFT_BRACKET
%token RIGHT_BRACKET
%token COMMENT
%token NEWLINE

%parse-param { FILE* fp } { Node **pRootNode }

%define parse.error verbose

%left GREATER_THAN LESS_THAN EQUAL
%left PLUS MINUS
%left STAR PERCENT
%left BANG BAR
%left LEFT_PAREN RIGHT_PAREN

%%
Program                 : TopLevelDeclarations
                        {
                            *pRootNode = $1;
                        }

BaseType                : VOID
                        {
                            $$ = MakePrimitiveTypeNode(Void);
                        }
                        | INT
                        {
                            $$ = MakePrimitiveTypeNode(Int);
                        }
                        | UINT
                        {
                            $$ = MakePrimitiveTypeNode(UInt);
                        }
                        | FLOAT
                        {
                            $$ = MakePrimitiveTypeNode(Float);
                        }
                        | DOUBLE
                        {
                            $$ = MakePrimitiveTypeNode(Double);
                        }
                        | STRING
                        {
                            $$ = MakePrimitiveTypeNode(String);
                        }
                        | BOOL
                        {
                            $$ = MakePrimitiveTypeNode(Bool);
                        }
                        | Identifier
                        {
                            $$ = MakeCustomTypeNode(yytext);
                        }
                        | REFERENCE LESS_THAN Type GREATER_THAN
                        {
                            $$ = MakeReferenceTypeNode($3);
                        }
                        ;

Type                    : BaseType
                        {
                            $$ = MakeTypeNode($1);
                        }

Identifier              : ID
                        {
                            $$ = MakeIdentifierNode(yytext);
                        }

HeapAllocation          : ALLOC Type
                        {
                            $$ = MakeAllocNode($2);
                        }

AccessExpression        : Identifier POINT AccessExpression
                        {
                            $$ = MakeAccessExpressionNode($1, $3);
                        }
                        | Identifier
                        {
                            $$ = $1;
                        }

Number                  : NUMBER
                        {
                            $$ = MakeNumberNode(yytext);
                        }

PrimaryExpression       : Number
                        | STRING
                        {
                            $$ = MakeStringNode(yytext);
                        }
                        | LEFT_PAREN Expression RIGHT_PAREN
                        {
                            $$ = $2;
                        }
                        | FunctionCallExpression
                        | AccessExpression
                        ;

UnaryExpression         : BANG Expression
                        {
                            $$ = MakeUnaryNode(Negate, $2);
                        }

BinaryExpression        : Expression PLUS Expression
                        {
                            $$ = MakeBinaryNode(Add, $1, $3);
                        }
                        | Expression MINUS Expression
                        {
                            $$ = MakeBinaryNode(Subtract, $1, $3);
                        }
                        | Expression STAR Expression
                        {
                            $$ = MakeBinaryNode(Multiply, $1, $3);
                        }
                        | Expression PERCENT Expression
                        {
                            $$ = MakeBinaryNode(Mod, $1, $3);
                        }
                        | Expression LESS_THAN Expression
                        {
                            $$ = MakeBinaryNode(LessThan, $1, $3);
                        }
                        | Expression GREATER_THAN Expression
                        {
                            $$ = MakeBinaryNode(GreaterThan, $1, $3);
                        }
                        | Expression EQUAL EQUAL Expression
                        {
                            $$ = MakeBinaryNode(Equal, $1, $4);
                        }
                        | Expression BAR BAR Expression
                        {
                            $$ = MakeBinaryNode(LogicalOr, $1, $4);
                        }

Expression              : BinaryExpression
                        | UnaryExpression
                        | PrimaryExpression
                        | HeapAllocation
                        ;

VariableDeclaration     : Identifier COLON Type
                        {
                            $$ = MakeDeclarationNode($3, $1);
                        }

AssignmentStatement     : VariableDeclaration EQUAL Expression
                        {
                            $$ = MakeAssignmentNode($1, $3);
                        }
                        | AccessExpression EQUAL Expression
                        {
                            $$ = MakeAssignmentNode($1, $3);
                        }
                        | Identifier EQUAL Expression
                        {
                            $$ = MakeAssignmentNode($1, $3);
                        }

ReturnStatement         : RETURN Expression
                        {
                            $$ = MakeReturnStatementNode($2);
                        }
                        | RETURN
                        {
                            $$ = MakeReturnVoidStatementNode();
                        }

FunctionCallExpression  : AccessExpression LEFT_PAREN Arguments RIGHT_PAREN
                        {
                            $$ = MakeFunctionCallExpressionNode($1, $3);
                        }

PartialStatement        : FunctionCallExpression
                        | AssignmentStatement
                        | VariableDeclaration
                        | ReturnStatement
                        ;

IfStatement             : IF LEFT_PAREN Expression RIGHT_PAREN LEFT_BRACE Statements RIGHT_BRACE
                        {
                            $$ = MakeIfNode($3, $6);
                        }

Conditional             : IfStatement
                        | IfStatement ELSE LEFT_BRACE Statements RIGHT_BRACE
                        {
                            $$ = MakeIfElseNode($1, $4);
                        }
                        | IfStatement ELSE Conditional
                        {
                            $$ = MakeIfElseNode($1, $3);
                        }

ForStatement            : FOR LEFT_PAREN VariableDeclaration IN LEFT_BRACKET Number POINT POINT Number RIGHT_BRACKET RIGHT_PAREN LEFT_BRACE Statements RIGHT_BRACE
                        {
                            $$ = MakeForLoopNode($3, $6, $9, $13);
                        }

Statement               : PartialStatement SEMICOLON
                        | Conditional
                        | ForStatement
                        ;

Statements              : Statement
                        {
                            $$ = StartStatementSequenceNode($1);
                        }
                        | Statements Statement
                        {
                            $$ = AddStatement($1, $2);
                        }

Arguments               : PrimaryExpression
                        {
                            $$ = StartFunctionArgumentSequenceNode($1);
                        }
                        | Arguments COMMA PrimaryExpression
                        {
                            $$ = AddFunctionArgumentNode($1, $3);
                        }
                        |
                        {
                            $$ = MakeEmptyFunctionArgumentSequenceNode();
                        }

SignatureArguments      : VariableDeclaration
                        {
                            $$ = StartFunctionSignatureArgumentsNode($1);
                        }
                        | SignatureArguments COMMA VariableDeclaration
                        {
                            $$ = AddFunctionSignatureArgumentNode($1, $3);
                        }
                        |
                        {
                            $$ = MakeEmptyFunctionSignatureArgumentsNode();
                        }
                        ;

Body                    : LEFT_BRACE Statements RIGHT_BRACE
                        {
                            $$ = $2;
                        }

FunctionSignature       : Identifier LEFT_PAREN SignatureArguments RIGHT_PAREN COLON Type
                        {
                            $$ = MakeFunctionSignatureNode($1, $6, $3, MakeFunctionModifiersNode(NULL, 0));
                        }
                        | STATIC Identifier LEFT_PAREN SignatureArguments RIGHT_PAREN COLON Type
                        {
                            Node *modifier = MakeStaticNode();
                            $$ = MakeFunctionSignatureNode($2, $7, $4, MakeFunctionModifiersNode(&modifier, 1));
                        }

FunctionDeclaration     : FunctionSignature Body
                        {
                            $$ = MakeFunctionDeclarationNode($1, $2);
                        }

StructDeclaration       : STRUCT Identifier LEFT_BRACE Declarations RIGHT_BRACE
                        {
                            $$ = MakeStructDeclarationNode($2, $4);
                        }

Declaration             : FunctionDeclaration
                        | VariableDeclaration SEMICOLON
                        ;

Declarations            : Declaration
                        {
                            $$ = StartDeclarationSequenceNode($1);
                        }
                        | Declarations Declaration
                        {
                            $$ = AddDeclarationNode($1, $2);
                        }

TopLevelDeclaration     : StructDeclaration;

TopLevelDeclarations    : TopLevelDeclaration
                        {
                            $$ = StartDeclarationSequenceNode($1);
                        }
                        | TopLevelDeclarations TopLevelDeclaration
                        {
                            $$ = AddDeclarationNode($1, $2);
                        }
%%