%{ #include #include #include #include #include #include #include %} %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; 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" %nterm program %nterm definition %nterm definitions %nterm opt_definitions %nterm variable_definition %nterm variable_definitions %nterm opt_variable_definitions %nterm procedure_definition %nterm function_definition %nterm formal_arg %nterm formals %nterm lvalue %nterm rvalue %nterm print_item %nterm print_items %nterm opt_formals %nterm statement %nterm statement_list %nterm opt_arguments %nterm arguments %nterm print_statement %nterm unless_statement %nterm unless_expression %nterm until_statement %nterm assignment %nterm procedure_call %nterm function_call %nterm simple_expr %nterm atom %nterm term %nterm factor %nterm expression %nterm ident /* special case */ %nterm return %nterm opt_return %{ /** 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 { $$ = $1; $$->n = $3; } | statement definitions : definition definitions { $$ = $1; $$->n = $2; } | definition definition : function_definition | procedure_definition | variable_definition variable_definition : "var" IDENT "=" expression { $$ = gen_var_def($[IDENT], $[expression], src_loc(@$)); } variable_definitions : variable_definition variable_definitions { $$ = $1; $$->n = $2; } | variable_definition opt_variable_definitions : variable_definitions | { $$ = NULL; } return : "return" IDENT { $$ = $2; } opt_return : return | { $$ = NULL; } function_definition : "function" FUNC_IDENT "{" opt_formals "}" return opt_variable_definitions "is" rvalue "end" "function" { struct ast *ret = gen_return($[rvalue], $[rvalue]->loc); $$ = gen_func_def($[FUNC_IDENT], $[return], $[opt_formals], $[opt_variable_definitions], ret, src_loc(@$)); } procedure_definition : "procedure" PROC_IDENT "{" opt_formals "}" opt_return opt_variable_definitions "is" statement_list "end" "procedure" { $$ = gen_proc_def($[PROC_IDENT], $[opt_return], $[opt_formals], $[opt_variable_definitions], $[statement_list], src_loc(@$)); } formals : formal_arg "," formals { $$ = $1; $$->n = $3; } | formal_arg opt_formals : formals | { $$ = NULL; } formal_arg : IDENT "[" IDENT "]" { $$ = gen_formal_def($1, $3, src_loc(@$)); } procedure_call : PROC_IDENT "(" opt_arguments ")" { $$ = gen_proc_call($[PROC_IDENT], $[opt_arguments], src_loc(@$)); } arguments : expression "," arguments { $$ = $1; $$->n = $3; } | expression opt_arguments : arguments | { $$ = NULL; } assignment : lvalue "=" rvalue { $$ = gen_assign($[lvalue], $[rvalue], src_loc(@$)); } ident : IDENT { $$ = gen_id($[IDENT], src_loc(@$)); } lvalue : ident | ident "." IDENT { $$ = gen_dot($1, $3, src_loc(@$)); } rvalue : expression | unless_expression print_statement : "print" print_items { $$ = gen_print($[print_items], src_loc(@$)); } print_items : print_item "&" print_items { $$ = $1; $$->n = $3; } | print_item print_item : STRING { $$ = gen_const_string($[STRING], src_loc(@$)); } | expression statement : procedure_call | assignment | print_statement | until_statement | unless_statement | "return" expression { $$ = gen_return($[expression], src_loc(@$)); } until_statement : "do" statement_list "until" expression { $$ = gen_until($[statement_list], $[expression], src_loc(@$)); } unless_statement : "do" statement_list "unless" expression "done" { $$ = gen_unless($[statement_list], $[expression], NULL, src_loc(@$)); } | "do" statement_list "unless" expression "otherwise" statement_list "done" { $$ = gen_unless($2, $[expression], $6, src_loc(@$)); } expression : simple_expr | expression "=" simple_expr { $$ = gen_eq($1, $[simple_expr], src_loc(@$)); } | expression "<" simple_expr { $$ = gen_lt($1, $[simple_expr], src_loc(@$)); } simple_expr : term | simple_expr "+" term { $$ = gen_add($1, $[term], src_loc(@$)); } | simple_expr "-" term { $$ = gen_sub($1, $[term], src_loc(@$)); } term : factor | term "*" factor { $$ = gen_mul($1, $[factor], src_loc(@$)); } | term "/" factor { $$ = gen_div($1, $[factor], src_loc(@$)); } factor : "+" atom { $$ = gen_pos($[atom], src_loc(@$)); } | "-" atom { $$ = gen_neg($[atom], src_loc(@$)); } | atom atom : ident | ident "'" IDENT { $$ = gen_attr($[ident], $[IDENT], src_loc(@$)); } | INT_LITERAL { $$ = gen_const_int($[INT_LITERAL], src_loc(@$)); } | DATE_LITERAL { $$ = gen_const_date($[DATE_LITERAL], src_loc(@$)); } | function_call | procedure_call | "(" expression ")" { $$ = $2; } function_call : FUNC_IDENT "(" opt_arguments ")" { $$ = gen_func_call($[FUNC_IDENT], $[opt_arguments], src_loc(@$)); } unless_expression : "do" expression "unless" expression "otherwise" expression "done" { $$ = gen_unless_expr($2, $4, $6, src_loc(@$)); } opt_definitions : definitions | { $$ = NULL; } program : opt_definitions statement_list { if ($1) { ast_last($1)->n = $2; $$ = $1; } else { $$ = $2; } parser->tree = $$; } | error { /* make sure to catch parse errors */ parser->failed = true; } %% #include "gen_lexer.inc" 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); yyparse(p->lexer, p); }