%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int yylex();
void yyerror(const char* s);
%}

%union {
    int num;
    char* str;
}

%token <num> NUMBER
%token <str> IDENTIFIER STRING_LITERAL
%token IF ELSE FOR ADD SUB MUL DIV MOD ASSIGN EQ LT GT LE GE NEQ AND OR NOT SEMICOLON LPAREN RPAREN LBRACE RBRACE PKG MAIN IMPORT FUNC VAR PRINTLN PRINT INT_TYPE

%type <num> expression term factor
%type <str> program package_declaration import_declaration func_declaration statements statement var_declaration print_statement assignment_statement if_statement for_loop for_initializer for_update data_type
%left ADD SUB
%left MUL DIV MOD
%start program

%%

program:
    package_declaration import_declaration func_declaration
;

package_declaration:
    PKG MAIN SEMICOLON
;

import_declaration:
    IMPORT STRING_LITERAL SEMICOLON
;

func_declaration:
    FUNC MAIN LPAREN RPAREN LBRACE statements RBRACE
;

statements:
    statement
    | statements statement
;

statement:
    var_declaration SEMICOLON {
        printf("variable declaration and assignment\n");
    }
    | print_statement SEMICOLON
    | assignment_statement SEMICOLON
    | if_statement
    | for_loop
;

if_statement:
    IF LPAREN expression RPAREN LBRACE statements RBRACE {
        printf("if statement\n");
    }
    | IF LPAREN expression RPAREN LBRACE statements RBRACE ELSE LBRACE statements RBRACE {
        printf("if-else statement\n");
    }
;

for_loop:
    FOR LPAREN for_initializer SEMICOLON expression SEMICOLON for_update RPAREN LBRACE statements RBRACE
;

for_initializer:
    IDENTIFIER ASSIGN expression
;

for_update:
    IDENTIFIER ASSIGN expression
;

var_declaration:
    VAR IDENTIFIER data_type ASSIGN expression {
        printf("var declaration: %s\n", $2);
    }
;

print_statement:
    PRINTLN LPAREN expression RPAREN {
        printf("println statement\n");
    }
    | PRINT LPAREN expression RPAREN {
        printf("print statement\n");
    }
;

assignment_statement:
    IDENTIFIER ASSIGN expression {
        printf("%s = %d\n", $1, $3);
    }
;

data_type:
    INT_TYPE
;

expression:
    expression ADD term {
        $$ = $1 + $3;
    }
    | expression SUB term {
        $$ = $1 - $3;
    }
    | term {
        $$ = $1;
    }
;

term:
    term MUL factor {
        $$ = $1 * $3;
    }
    | term DIV factor {
        $$ = $1 / $3;
    }
    | factor {
        $$ = $1;
    }
;

factor:
    NUMBER {
        $$ = $1;
    }
    | IDENTIFIER {
        printf("identifier: %s\n", $1);
    }
    | LPAREN expression RPAREN {
        $$ = $2;
    }
    | SUB factor {
        $$ = -$2;
    }
    | ADD factor {
        $$ = $2;
    }
    | NOT factor {
        $$ = !$2;
    }
    | factor MOD factor {
        $$ = $1 % $3;
    }
    | expression EQ expression {
        $$ = $1 == $3;
    }
    | expression LT expression {
        $$ = $1 < $3;
    }
    | expression GT expression {
        $$ = $1 > $3;
    }
    | expression LE expression {
        $$ = $1 <= $3;
    }
    | expression GE expression {
        $$ = $1 >= $3;
    }
    | expression NEQ expression {
        $$ = $1 != $3;
    }
    | expression AND expression {
        $$ = $1 && $3;
    }
    | expression OR expression {
        $$ = $1 || $3;
    }
    | IDENTIFIER LPAREN RPAREN {
        printf("function call: %s\n", $1);
    }
    | IDENTIFIER LPAREN expression RPAREN {
        printf("function call with argument: %s\n", $1);
    }
    | IDENTIFIER LPAREN expression ',' expression RPAREN {
        printf("function call with multiple arguments: %s\n", $1);
    }
;

%%

void yyerror(const char* s) {
    fprintf(stderr, "%s\n", s);
}

int main() {
    yyparse();
    return 0;
}