%{
#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 arithmetic_expr term factor relational_expr
%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
%nonassoc EQ LT GT LE GE NEQ
%left ADD SUB
%left MUL DIV MOD
%left AND
%left OR
%nonassoc NOT
%nonassoc UNARY

%%

program:
    package_declaration import_declaration func_declaration statements
;

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:
    relational_expr
;

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

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

factor:
    NUMBER {
        $$ = $1;
    }
    | IDENTIFIER {
        printf("identifier: %s\n", $1);
    }
    | LPAREN expression RPAREN {
        $$ = $2;
    }
    | SUB factor UNARY {
        $$ = -$2;
    }
    | ADD factor UNARY {
        $$ = $2;
    }
    | NOT factor UNARY {
        $$ = !$2;
    }
    | 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);
    }
;

relational_expr:
    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;
    }
;

%%

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

int main() {
    yyparse();
    return 0;
}
gptparser.y: warning: 20 nonterminals useless in grammar [-Wother]
gptparser.y: warning: 47 rules useless in grammar [-Wother]
gptparser.y:30.1-7: error: start symbol program does not derive any sentence
   30 | program: