%{ #include #include #include #include #include #include #define FOREACH_TOKEN(M) \ M(LPAREN) \ M(RPAREN) \ M(LSQUARE) \ M(RSQUARE) \ M(LCURLY) \ M(RCURLY) \ M(APOSTROPHE) \ M(AMPERSAND) \ M(COMMA) \ M(DOT) \ M(EQ) \ M(LT) \ M(PLUS) \ M(MINUS) \ M(MULT) \ M(DIV) \ M(VAR) \ M(IS) \ M(UNLESS) \ M(OTHERWISE) \ M(UNTIL) \ M(DO) \ M(DONE) \ M(PROCEDURE) \ M(FUNCTION) \ M(RETURN) \ M(PRINT) \ M(END) \ M(STRING) \ M(DATE_LITERAL) \ M(INT_LITERAL) \ M(IDENT) \ M(FUNC_IDENT) \ M(PROC_IDENT) %} %locations %define parse.trace %define parse.error verbose %define api.pure full %define lr.type ielr %lex-param {void *scanner} {struct parser *parser} %parse-param {void *scanner} {struct parser* parser} %union { struct ast_node *node; ph_date_t num; int64_t snum; char *str; }; %token STRING %token DATE_LITERAL %token INT_LITERAL %token IDENT; %token FUNC_IDENT; %token PROC_IDENT; %token LPAREN "(" %token RPAREN ")" %token LSQUARE "[" %token RSQUARE "]" %token LCURLY "{" %token RCURLY "}" %token APOSTROPHE "'" %token AMPERSAND "&" %token COMMA "," %token DOT "." %token EQ "=" %token LT "<" %token PLUS "+" %token MINUS "-" %token MULT "*" %token DIV "/" %token VAR "var" %token IS "is" %token UNLESS "unless" %token OTHERWISE "otherwise" %token UNTIL "until" %token DO "do" %token DONE "done" %token PROCEDURE "procedure" %token FUNCTION "function" %token RETURN "return" %token PRINT "print" %token END "end" %{ /** Modifies the signature of yylex to fit our parser better. */ #define YY_DECL int yylex(YYSTYPE *yylval, YYLTYPE *yylloc, \ void *yyscanner, struct parser *parser) /** * Declare yylex. * * @param yylval Bison current value. * @param yylloc Bison location info. * @param yyscanner Flex scanner. * @param parser Current parser state. * @return \c 0 when succesful, \c 1 otherwise. * More info on yylex() can be found in the flex manual. */ YY_DECL; /** * Convert bison location info to our own source location info. * * @param yylloc Bison location info. * @return Internal location info. */ static struct src_loc src_loc(YYLTYPE yylloc); /** * Print parsing error. * Automatically called by bison. * * @param yylloc Location of error. * @param lexer Lexer. * @param parser Parser state. * @param msg Message to print. */ static void yyerror(YYLTYPE *yylloc, void *lexer, struct parser *parser, const char *msg); %} %start program; %% statement_list : statement "," statement_list | statement definitions : definition definitions | definition definition : function_definition | procedure_definition | variable_definition variable_definition : "var" IDENT "=" expression variable_definitions : variable_definition variable_definitions | variable_definition opt_variable_definitions : variable_definitions | return : "return" IDENT opt_return : return | function_definition : "function" FUNC_IDENT "{" opt_formals "}" return opt_variable_definitions "is" rvalue "end" "function" procedure_definition : "procedure" PROC_IDENT "{" opt_formals "}" opt_return opt_variable_definitions "is" statement_list "end" "procedure" formals : formal_arg "," formal_arg | formal_arg opt_formals : formals | formal_arg : IDENT "[" IDENT "]" procedure_call : PROC_IDENT "(" opt_arguments ")" arguments : expression "," arguments | expression opt_arguments : arguments | assignment : lvalue "=" rvalue lvalue : IDENT | IDENT "." IDENT rvalue : expression | unless_expression print_statement : "print" print_items print_items : print_item "&" print_items | print_item print_item : STRING | expression statement : procedure_call | assignment | print_statement | until_statement | unless_statement | "return" expression until_statement : "do" statement_list "until" expression unless_statement : "do" statement_list "unless" expression "done" | "do" statement_list "unless" expression "otherwise" statement_list "done" expression : simple_expr | expression "=" simple_expr | expression "<" simple_expr simple_expr : term | simple_expr "+" term | simple_expr "-" term term : factor | term "*" factor | term "/" factor factor : "+" atom | "-" atom | atom atom : IDENT | IDENT "'" IDENT | INT_LITERAL | DATE_LITERAL | function_call | procedure_call | "(" expression ")" function_call : FUNC_IDENT "(" opt_arguments ")" unless_expression : "do" expression "unless" expression "otherwise" expression "done" opt_definitions : definitions | {} program : opt_definitions statement_list | error { /* make sure to catch parse errors */ parser->failed = true; } %% #include "gen_lexer.inc" static void dump_yychar(struct parser *p, int yychar, YYSTYPE yylval, YYLTYPE yylloc) { struct src_loc loc = src_loc(yylloc); printf("%s:%d:%d: ", p->fname, loc.first_line, loc.first_col); #define PRINT_NAME(token) case token: printf(#token " "); break; switch (yychar) { FOREACH_TOKEN(PRINT_NAME); default: printf("Unknown yychar\n"); return; } char date_str[11] = {0}; switch (yychar) { case INT_LITERAL: printf("(%lld)", (long long int)yylval.snum); break; case IDENT: printf("(%s)", yylval.str); break; case FUNC_IDENT: printf("(%s)", yylval.str); break; case PROC_IDENT: printf("(%s)", yylval.str); break; case DATE_LITERAL: date_to_string(date_str, yylval.num); printf("(%s)", date_str); break; } printf("\n"); } static void dump_lex(struct parser *p) { int yychar; YYSTYPE yylval; YYLTYPE yylloc = {1, 1, 1, 1}; /* run lexer until we reach the end of the file */ while ((yychar = yylex(&yylval, &yylloc, p->lexer, p)) != YYEOF) { dump_yychar(p, yychar, yylval, yylloc); } } static struct src_loc src_loc(YYLTYPE yylloc) { struct src_loc loc; loc.first_line = yylloc.first_line; loc.last_line = yylloc.last_line; loc.first_col = yylloc.first_column; loc.last_col = yylloc.last_column; return loc; } static void yyerror(YYLTYPE *yylloc, void *lexer, struct parser *parser, const char *msg) { (void)lexer; struct src_issue issue; issue.loc = src_loc(*yylloc); issue.fname = parser->fname; issue.buf = parser->buf; src_issue(issue, msg); } struct parser *create_parser() { return calloc(1, sizeof(struct parser)); } void destroy_parser(struct parser *p) { yylex_destroy(p->lexer); free(p); } void parse(struct parser *p, const char *fname, const char *buf) { p->fname = fname; p->buf = buf; p->comment_nesting = 0; p->failed = false; yylex_init(&p->lexer); yy_scan_string(buf, p->lexer); // debugging, remember to reset yy_scan_string once the actual parser // runs dump_lex(p); yy_scan_string(buf, p->lexer); yyparse(p->lexer, p); }