/* Declare token types at the top of the bison file, causing them to be automatically generated in parser.tab.h for use by scanner.c. */ %debug %define parse.error detailed %token TOKEN_ASSIGNMENT %token TOKEN_COMMA %token TOKEN_DIV %token TOKEN_EQUAL_EQUAL %token TOKEN_GREATER_THAN %token TOKEN_GREATER_THAN_OR_EQUAL %token TOKEN_ID %token TOKEN_INT %token TOKEN_INTEGER %token TOKEN_KEYWORD_ENDIF %token TOKEN_KEYWORD_IF %token TOKEN_KEYWORD_PRINT %token TOKEN_KEYWORD_THEN %token TOKEN_LBRACKET %token TOKEN_LESS_THAN %token TOKEN_LESS_THAN_OR_EQUAL %token TOKEN_LPAREN %token TOKEN_MINUS %token TOKEN_MUL %token TOKEN_NOT_EQUAL %token TOKEN_PLUS %token TOKEN_RBRACKET %token TOKEN_RPAREN %token TOKEN_SEMI %union { struct stmt *stmt; struct expr *expr; struct decl *decl; }; %type program statement_list statement print_statement assignment_statement if_statement block %type expr term factor name sum array_subscript star_expr %type declaration %{ #define YYDEBUG 1 #include #include #include #include "expr.h" /* Clunky: Manually declare the interface to the scanner generated by flex. */ extern int yylineno; extern char *yytext; extern int yylex(); extern void yyerror(const char*); /* Clunky: Keep the final result of the parse in a global variable, so that it can be retrieved by main(). */ struct stmt * parser_result = 0; %} %% /* Here is the grammar: program is the start symbol. */ program : statement_list { parser_result = $1; return 0; } ; statement_list : statement { $$ = $1; } | statement statement_list { $$ = $1; $1->next = $2; } ; block : statement { $$ = $1; } | statement block { $$ = $1; $1->next = $2; } ; statement : assignment_statement TOKEN_SEMI { $$ = $1; } | print_statement TOKEN_SEMI { $$ = $1; } | if_statement { $$ = $1; } ; assignment_statement : declaration { $$ = stmt_create(STMT_DECL,$1,0,0,0,0,0,0); } ; print_statement : TOKEN_KEYWORD_PRINT expr { $$ = stmt_create(STMT_PRINT,0,0,$2,0,0,0,0); } ; if_statement : TOKEN_KEYWORD_IF expr TOKEN_KEYWORD_THEN block TOKEN_KEYWORD_ENDIF { $$ = stmt_create(STMT_IF,0,0,$2,0,$4,0,0); } ; declaration : name TOKEN_ASSIGNMENT expr { $$ = decl_create($1,0,$3,0,0); } | array_subscript TOKEN_ASSIGNMENT expr { $$ = decl_create($1,0,$3,0,0); } ; name : TOKEN_ID { $$ = expr_create_name(yytext); } ; array_subscript : name TOKEN_LBRACKET expr TOKEN_RBRACKET { $$ = expr_create(EXPR_SUBSCRIPT,$1,$3); } ; star_expr : expr { $$ = expr_create(EXPR_ARRAY,$1,0); } | expr TOKEN_COMMA star_expr { $$ = expr_create(EXPR_ARRAY,$1,$3); } ; expr : sum TOKEN_LESS_THAN sum { $$ = expr_create(EXPR_LESS_THAN,$1,$3); } | sum TOKEN_LESS_THAN_OR_EQUAL sum { $$ = expr_create(EXPR_LESS_THAN_OR_EQUAL,$1,$3); } | sum TOKEN_GREATER_THAN sum { $$ = expr_create(EXPR_GREATER_THAN,$1,$3); } | sum TOKEN_GREATER_THAN_OR_EQUAL sum { $$ = expr_create(EXPR_GREATER_THAN_OR_EQUAL,$1,$3); } | sum TOKEN_EQUAL_EQUAL sum { $$ = expr_create(EXPR_EQUAL_EQUAL,$1,$3); } | sum TOKEN_NOT_EQUAL sum { $$ = expr_create(EXPR_NOT_EQUAL,$1,$3); } | sum { $$ = $1; } ; sum : sum TOKEN_PLUS term { $$ = expr_create(EXPR_ADD,$1,$3); } | sum TOKEN_MINUS term { $$ = expr_create(EXPR_SUBTRACT,$1,$3); } | term { $$ = $1; } ; term : term TOKEN_MUL factor { $$ = expr_create(EXPR_MULTIPLY,$1,$3); } | term TOKEN_DIV factor { $$ = expr_create(EXPR_DIVIDE,$1,$3); } | factor { $$ = $1; } ; factor : TOKEN_LPAREN expr TOKEN_RPAREN { $$ = $2; } | TOKEN_MINUS factor { $$ = expr_create(EXPR_SUBTRACT,expr_create_integer_literal(0),$2); } | TOKEN_INT { $$ = expr_create_integer_literal(atoi(yytext)); } | TOKEN_LBRACKET star_expr TOKEN_RBRACKET { $$ = $2; } | array_subscript { $$ = $1; } | name { $$ = $1; } ; %% /* This function will be called by bison if the parse should encounter an error. In principle, "str" will contain something useful. In practice, it often does not. */ void yyerror(const char* msg) { fprintf(stderr, "Error | Line: %d\n%s\n", yylineno, msg); }