}END_TEST START_TEST(test_acc_ifexpr_ambiguous_less_than_or_generic) { struct ast_node *n; static const struct RFstring s = RF_STRING_STATIC_INIT( "if a < 42 {\n" "}" ); front_testdriver_new_main_source(&s); struct ast_node *id1 = testsupport_parser_identifier_create(0, 3, 0, 3); testsupport_parser_constant_create(cnum, 0, 7, 0, 8, integer, 42); testsupport_parser_node_create(cmp_exp, binaryop, 0, 3, 0, 8, BINARYOP_CMP_LT, id1, cnum); testsupport_parser_block_create(bnode, 0, 10, 1, 0); testsupport_parser_node_create(cond, condbranch, 0, 3, 1, 0, cmp_exp, bnode); testsupport_parser_node_create(ifx, ifexpr, 0, 0, 1, 0, cond, NULL); ck_test_parse_as(n, ifexpr, "if_expression", ifx, TOKEN_KW_IF); ast_node_destroy(n); ast_node_destroy(ifx); }END_TEST
struct ast_node *parser_acc_fnimpl(struct parser *p) { struct ast_node *n; struct ast_node *decl; struct ast_node *body; struct token *tok; decl = parser_acc_fndecl(p, FNDECL_PARTOF_IMPL); if (!decl) { return NULL; } tok = lexer_lookahead(p->lexer, 1); if (!tok) { goto fail_free_decl; } if (tok->type == TOKEN_SM_OCBRACE) { // normal function body body = parser_acc_block(p, true); if (!body) { parser_synerr(p, ast_node_endmark(decl), NULL, "Expected a body for \""RF_STR_PF_FMT"\" function " "implementation", RF_STR_PF_ARG(ast_fndecl_name_str(decl))); goto fail_free_decl; } } else { // attempt to find a headless match expression as the function body body = parser_acc_matchexpr(p, false, true); if (!body) { parser_synerr(p, ast_node_endmark(decl), NULL, "Expected a body for \""RF_STR_PF_FMT"\" function " "implementation", RF_STR_PF_ARG(ast_fndecl_name_str(decl))); goto fail_free_decl; } // now set the matchexpr's fn_args since this is a headless matchexpr ast_matchexpr_set_fnargs(body, ast_fndecl_args_get(decl)); } n = ast_fnimpl_create(ast_node_startmark(decl), ast_node_endmark(body), decl, body); if (!n) { RF_ERRNOMEM(); ast_node_destroy(body); ast_node_destroy(decl); return NULL; } return n; fail_free_decl: ast_node_destroy(decl); return NULL; }
}END_TEST START_TEST(test_acc_ifexpr_2) { struct ast_node *n; static const struct RFstring s = RF_STRING_STATIC_INIT( "if a == 42 {\n" " do_sth()\n" "} else { \n" " 55 + 2.31\n" "}" ); front_testdriver_new_main_source(&s); struct ast_node *id1 = testsupport_parser_identifier_create(0, 3, 0, 3); testsupport_parser_constant_create(cnum1, 0, 8, 0, 9, integer, 42); testsupport_parser_node_create(cmp_exp, binaryop, 0, 3, 0, 9, BINARYOP_CMP_EQ, id1, cnum1); testsupport_parser_block_create(bnode1, 0, 11, 2, 0); struct ast_node *fn_name = testsupport_parser_identifier_create( 1, 4, 1, 9); testsupport_parser_node_create(fc, fncall, 1, 4, 1, 11, fn_name, NULL, NULL); ast_node_add_child(bnode1, fc); testsupport_parser_node_create(cond1, condbranch, 0, 3, 2, 0, cmp_exp, bnode1); testsupport_parser_block_create(bnode2, 2, 7, 4, 0); testsupport_parser_constant_create(cnum2, 3, 4, 3, 5, integer, 55); testsupport_parser_constant_create(cnum3, 3, 9, 3, 12, float, 2.31); testsupport_parser_node_create(op1, binaryop, 3, 4, 3, 12, BINARYOP_ADD, cnum2, cnum3); ast_node_add_child(bnode2, op1); testsupport_parser_node_create(ifx, ifexpr, 0, 0, 4, 0, cond1, bnode2); ck_test_parse_as(n, ifexpr, "if_expression", ifx, TOKEN_KW_IF); ast_node_destroy(n); ast_node_destroy(ifx); }END_TEST
/* will always post a syntax error if it fails */ static struct ast_node *parser_acc_condbranch(struct parser *p, struct token *after_tok) { struct ast_node *n; struct ast_node *expr; struct ast_node *block; expr = parser_acc_expression(p); if (!expr) { parser_synerr(p, token_get_end(after_tok), NULL, "Expected an expression after '"RF_STR_PF_FMT"'", RF_STR_PF_ARG(tokentype_to_str(after_tok->type))); return NULL; } block = parser_acc_block(p, true); if (!block) { parser_synerr(p, ast_node_endmark(expr), NULL, "Expected a block after \""RF_STR_PF_FMT"\"'s " "conditional expression", RF_STR_PF_ARG(tokentype_to_str(after_tok->type))); ast_node_destroy(expr); return NULL; } n = ast_condbranch_create(ast_node_startmark(expr), ast_node_endmark(block), expr, block); if (!n) { RF_ERRNOMEM(); ast_node_destroy(expr); ast_node_destroy(block); return NULL; } return n; }
}END_TEST START_TEST(test_acc_ifexpr_4) { struct ast_node *n; static const struct RFstring s = RF_STRING_STATIC_INIT( "if a == 42 {\n" " do_sth()\n" "} elif (a == 50 && is_good()) {\n" " \"foo\"\n" "} elif (5 < something) {\n" " 283.23\n" "} else { \n" " 55 + 2.31\n" "}" ); front_testdriver_new_main_source(&s); struct ast_node *id1 = testsupport_parser_identifier_create(0, 3, 0, 3); testsupport_parser_constant_create(cnum1, 0, 8, 0, 9, integer, 42); testsupport_parser_node_create(cmp_exp, binaryop, 0, 3, 0, 9, BINARYOP_CMP_EQ, id1, cnum1); testsupport_parser_block_create(bnode1, 0, 11, 2, 0); struct ast_node *fn_name = testsupport_parser_identifier_create( 1, 4, 1, 9); testsupport_parser_node_create(fc, fncall, 1, 4, 1, 11, fn_name, NULL, NULL); ast_node_add_child(bnode1, fc); testsupport_parser_node_create(cond1, condbranch, 0, 3, 2, 0, cmp_exp, bnode1); struct ast_node *id2 = testsupport_parser_identifier_create(2, 8, 2, 8); testsupport_parser_constant_create(cnum2, 2, 13, 2, 14, integer, 50); testsupport_parser_node_create(op1, binaryop, 2, 8, 2, 14, BINARYOP_CMP_EQ, id2, cnum2); struct ast_node *fn_name2 = testsupport_parser_identifier_create( 2, 19, 2, 25); testsupport_parser_node_create(fc2, fncall, 2, 19, 2, 27, fn_name2, NULL, NULL); testsupport_parser_node_create(cmp_exp2, binaryop, 2, 8, 2, 27, BINARYOP_LOGIC_AND, op1, fc2); testsupport_parser_block_create(bnode2, 2, 30, 4, 0); testsupport_parser_string_literal_create(sliteral1, 3, 4, 3, 8); ast_node_add_child(bnode2, sliteral1); testsupport_parser_node_create(cond2, condbranch, 2, 8, 4, 0, cmp_exp2, bnode2); testsupport_parser_constant_create(cnum3, 4, 8, 4, 8, integer, 5); struct ast_node *id3 = testsupport_parser_identifier_create(4, 12, 4, 20); testsupport_parser_node_create(cmp_exp3, binaryop, 4, 8, 4, 20, BINARYOP_CMP_LT, cnum3, id3); testsupport_parser_block_create(bnode3, 4, 23, 6, 0); testsupport_parser_constant_create(cnum4, 5, 4, 5, 9, float, 283.23); ast_node_add_child(bnode3, cnum4); testsupport_parser_node_create(cond3, condbranch, 4, 8, 6, 0, cmp_exp3, bnode3); testsupport_parser_block_create(bnode4, 6, 7, 8, 0); testsupport_parser_constant_create(cnum5, 7, 4, 7, 5, integer, 55); testsupport_parser_constant_create(cnum6, 7, 9, 7, 12, float, 2.31); testsupport_parser_node_create(op3, binaryop, 7, 4, 7, 12, BINARYOP_ADD, cnum5, cnum6); ast_node_add_child(bnode4, op3); // the second elif testsupport_parser_node_create(if3, ifexpr, 4, 2, 8, 0, cond3, bnode4); // the first elif testsupport_parser_node_create(if2, ifexpr, 2, 2, 8, 0, cond2, if3); // the first if testsupport_parser_node_create(ifx, ifexpr, 0, 0, 8, 0, cond1, if2); ck_test_parse_as(n, ifexpr, "if_expression", ifx, TOKEN_KW_IF); ast_node_destroy(n); ast_node_destroy(ifx); }END_TEST
struct ast_node *ast_parser_acc_block(struct ast_parser *p, bool expect_braces) { struct ast_node *n; struct token *tok; struct ast_node *element; const struct inplocation_mark *start = NULL; const struct inplocation_mark *end = NULL; lexer_push(parser_lexer(p)); tok = lexer_lookahead(parser_lexer(p), 1); if (!tok) { goto err; } if (expect_braces) { if (tok->type != TOKEN_SM_OCBRACE) { goto err; } // consume '{' lexer_curr_token_advance(parser_lexer(p)); start = token_get_start(tok); } if (!(n = ast_block_create())) { goto err; } //try to parse the first element if (!(element = ast_parser_acc_block_element(p))) { if (!expect_braces) { goto err_free_block; } //else just find the terminating '}' and return tok = lexer_lookahead(parser_lexer(p), 1); if (!tok || tok->type != TOKEN_SM_CCBRACE) { parser_synerr( p, lexer_last_token_end(parser_lexer(p)), NULL, "Expected an expression or a '}' at block end" ); goto err_free_block; } //consume the '}' lexer_curr_token_advance(parser_lexer(p)); ast_node_set_start(n, start); ast_node_set_end(n, token_get_end(tok)); return n; //empty block } if (!start) { start = ast_node_startmark(element); } end = ast_node_endmark(element); ast_block_add_element(n, element); // now add elements to the block while ((element = ast_parser_acc_block_element(p))) { ast_block_add_element(n, element); end = ast_node_endmark(element); } if (expect_braces) { tok = lexer_lookahead(parser_lexer(p), 1); if (!tok || tok->type != TOKEN_SM_CCBRACE) { parser_synerr( p, lexer_last_token_end(parser_lexer(p)), NULL, "Expected an expression or a '}' at block end" ); goto err_free_block; } //consume the '}' lexer_curr_token_advance(parser_lexer(p)); end = token_get_end(tok); } ast_node_set_start(n, start); ast_node_set_end(n, end); lexer_pop(parser_lexer(p)); return n; err_free_block: ast_node_destroy(n); err: lexer_rollback(parser_lexer(p)); return NULL; }
struct ast_node *parser_acc_fncall(struct parser *p, bool expect_it) { struct ast_node *n; struct token *tok; struct ast_node *name; struct ast_node *genr = NULL; struct ast_node *args = NULL; lexer_push(p->lexer); name = parser_acc_identifier(p); if (!name) { goto err; } genr = parser_acc_genrattr(p, false); if (!genr && parser_has_syntax_error(p)) { // name is an identifier and even in failure does not need to get destroyed here goto err; } tok = lexer_lookahead(p->lexer, 1); if (!tok || tok->type != TOKEN_SM_OPAREN) { if (expect_it) { parser_synerr(p, lexer_last_token_start(p->lexer), NULL, "Expected '('"); } goto err_free_genr; } //consume '(' lexer_next_token(p->lexer); args = parser_acc_expression(p); if (!args) { if (parser_has_syntax_error(p)) { parser_synerr(p, lexer_last_token_start(p->lexer), NULL, "Expected argument expression for function call"); goto err_free_genr; } } tok = lexer_lookahead(p->lexer, 1); if (!tok || tok->type != TOKEN_SM_CPAREN) { if (expect_it) { parser_synerr(p, lexer_last_token_end(p->lexer), NULL, "Expected ')' at end of "RF_STR_PF_FMT" function call", RF_STR_PF_ARG(ast_identifier_str(name))); } goto err_free_args; } //consume ')' lexer_next_token(p->lexer); n = ast_fncall_create(ast_node_startmark(name), token_get_end(tok), name, args, genr); if (!n) { RF_ERRNOMEM(); goto err_free_args; } lexer_pop(p->lexer); return n; err_free_args: if (args) { ast_node_destroy(args); } err_free_genr: if (genr) { ast_node_destroy(genr); } err: lexer_rollback(p->lexer); return NULL; }
struct ast_node *parser_acc_fndecl(struct parser *p, int fndecl_position) { struct ast_node *n; struct token *tok; struct token *oparen_tok; struct ast_node *name; struct ast_node *genr = NULL; struct ast_node *args = NULL; struct ast_node *ret_type = NULL; const struct inplocation_mark *start; const struct inplocation_mark *end; lexer_push(p->lexer); tok = lexer_lookahead(p->lexer, 1); if (fndecl_position != FNDECL_PARTOF_FOREIGN_IMPORT) { if (!tok || tok->type != TOKEN_KW_FUNCTION) { goto err; } //consume function keyword lexer_next_token(p->lexer); } // start should be either start of fn, or start of next token (an identifier) start = token_get_start(tok); name = parser_acc_identifier(p); if (!name) { parser_synerr(p, lexer_last_token_start(p->lexer), NULL, "Expected an identifier for the function name after 'fn'"); goto err; } tok = lexer_lookahead(p->lexer, 1); if (GENRDECL_START_COND(tok)) { genr = parser_acc_genrdecl(p); if (!genr) { goto err; } } tok = lexer_next_token(p->lexer); if (!tok || tok->type != TOKEN_SM_OPAREN) { parser_synerr(p, lexer_last_token_start(p->lexer), NULL, "Expected '(' at function declaration"); goto err_free_genr; } oparen_tok = tok; args = parser_acc_typedesc(p); if (!args && parser_has_syntax_error_reset(p)) { parser_synerr(p, token_get_end(tok), NULL, "Expected either a type description for the function's " "arguments or ')' after '('"); goto err_free_genr; } tok = lexer_next_token(p->lexer); if (!tok || tok->type != TOKEN_SM_CPAREN) { if (args) { parser_synerr(p, lexer_last_token_end(p->lexer), NULL, "Expected ')' at function declaration after " "type description"); } else { parser_synerr(p, token_get_end(oparen_tok), NULL, "Expected ')' at function declaration after '('"); } goto err_free_args; } end = token_get_end(tok); tok = lexer_lookahead(p->lexer, 1); if (tok && tok->type == TOKEN_OP_IMPL) { //consume '->' lexer_next_token(p->lexer); ret_type = parser_acc_typedesc(p); if (!ret_type) { parser_synerr(p, token_get_end(tok), NULL, "Expected type description for the function's " "return type after '->'"); goto err_free_args; } end = ast_node_endmark(ret_type); } n = ast_fndecl_create(start, end, fndecl_position, name, genr, args, ret_type); if (!n) { RF_ERRNOMEM(); goto err_free_rettype; } lexer_pop(p->lexer); return n; err_free_rettype: if (ret_type) { ast_node_destroy(ret_type); } err_free_args: if (args) { ast_node_destroy(args); } err_free_genr: if (genr) { ast_node_destroy(genr); } err: lexer_rollback(p->lexer); return NULL; }
struct ast_node *parser_acc_typeinstance(struct parser *p) { struct ast_node *n = NULL; struct token *tok; struct ast_node *class_name; struct ast_node *type_name; struct ast_node *genr = NULL; const struct inplocation_mark *start; enum parser_fnimpl_list_err err; tok = lexer_lookahead(p->lexer, 1); if (!tok || tok->type != TOKEN_KW_TYPEINSTANCE) { return NULL; } start = token_get_start(tok); //consumer typeclass isntance keyword lexer_next_token(p->lexer); class_name = parser_acc_identifier(p); if (!class_name) { parser_synerr(p, lexer_last_token_start(p->lexer), NULL, "Expected an identifier for the typeclass instance " "name after 'instance'"); goto err; } type_name = parser_acc_identifier(p); if (!type_name) { parser_synerr(p, lexer_last_token_start(p->lexer), NULL, "Expected an identifier for the name of \""RF_STR_PF_FMT"\" " "typeclass instance", RF_STR_PF_ARG(ast_identifier_str(class_name))); goto err; } genr = parser_acc_genrdecl(p); if (!genr && parser_has_syntax_error_reset(p)) { parser_synerr(p, ast_node_endmark(type_name), NULL, "Expected a generic declaration for type instance \"" RF_STR_PF_FMT"\" after type name \""RF_STR_PF_FMT"\"", RF_STR_PF_ARG(ast_identifier_str(class_name)), RF_STR_PF_ARG(ast_identifier_str(type_name))); goto err; } tok = lexer_next_token(p->lexer); if (!tok || tok->type != TOKEN_SM_OCBRACE) { parser_synerr(p, ast_node_endmark(type_name), NULL, "Expected '{' at type instance \""RF_STR_PF_FMT"\" " "after \""RF_STR_PF_FMT"\"", RF_STR_PF_ARG(ast_identifier_str(class_name)), RF_STR_PF_ARG(ast_identifier_str(type_name))); goto err_free_genr; } n = ast_typeinstance_create(start, NULL, class_name, type_name, genr); if (!n) { RF_ERRNOMEM(); goto err_free_genr; } err = parser_acc_fnimpl_list(p, n); switch (err) { case PARSER_FNIMPL_LIST_EMPTY: parser_synerr(p, token_get_end(tok), NULL, "Expected at least one function implementation inside " "the body of typeinstace \""RF_STR_PF_FMT"\" for " "\""RF_STR_PF_FMT"\" after'{'", RF_STR_PF_ARG(ast_identifier_str(class_name)), RF_STR_PF_ARG(ast_identifier_str(type_name))); goto err_free_typeinstance; break; case PARSER_FNIMPL_LIST_FAILURE: parser_synerr(p, lexer_last_token_end(p->lexer), NULL, "Expected a proper function implementation inside " "the body of typeinstace \""RF_STR_PF_FMT"\" for " "\""RF_STR_PF_FMT"\" after'{'", RF_STR_PF_ARG(ast_identifier_str(class_name)), RF_STR_PF_ARG(ast_identifier_str(type_name))); goto err_free_typeinstance; break; default: // SUCCESS break; } tok = lexer_next_token(p->lexer); if (!tok || tok->type != TOKEN_SM_CCBRACE) { parser_synerr(p, lexer_last_token_end(p->lexer), NULL, "Expected '}' at the end of \""RF_STR_PF_FMT"\" " "typeinstance for \""RF_STR_PF_FMT"\"", RF_STR_PF_ARG(ast_identifier_str(class_name)), RF_STR_PF_ARG(ast_identifier_str(type_name))); goto err_free_typeinstance; } ast_node_set_end(n, token_get_end(tok)); return n; err_free_genr: if (genr) { ast_node_destroy(genr); } err_free_typeinstance: if (n) { ast_node_destroy(n); } err: return NULL; }
struct ast_node *parser_acc_typeclass(struct parser *p) { struct ast_node *n = NULL; struct token *tok; struct ast_node *name; struct ast_node *genr = NULL; const struct inplocation_mark *start; enum parser_fndecl_list_err err; tok = lexer_lookahead(p->lexer, 1); if (!tok || tok->type != TOKEN_KW_TYPECLASS) { return NULL; } start = token_get_start(tok); lexer_push(p->lexer); //consume typeclass keyword lexer_next_token(p->lexer); name = parser_acc_identifier(p); if (!name) { parser_synerr(p, lexer_last_token_start(p->lexer), NULL, "Expected an identifier for the typeclass " "name after 'class'"); goto err; } genr = parser_acc_genrdecl(p); if (!genr && parser_has_syntax_error_reset(p)) { parser_synerr(p, ast_node_endmark(name), NULL, "Expected a generic declaration for typeclass \"" RF_STR_PF_FMT"\" after identifier", RF_STR_PF_ARG(ast_identifier_str(name))); goto err_free_genr; } tok = lexer_next_token(p->lexer); if (!tok || tok->type != TOKEN_SM_OCBRACE) { parser_synerr(p, ast_node_endmark(name), NULL, "Expected '{' at \""RF_STR_PF_FMT"\" typeclass " "declaration after identifier", RF_STR_PF_ARG(ast_identifier_str(name))); goto err_free_genr; } n = ast_typeclass_create(start, NULL, name, genr); if (!n) { RF_ERRNOMEM(); goto err_free_genr; } err = parser_acc_fndecl_list(p, n, FNDECL_PARTOF_TYPECLASS); switch (err) { case PARSER_FNDECL_LIST_EMPTY: parser_synerr(p, token_get_end(tok), NULL, "Expected at least one function declaration inside " "the body of typeclass \""RF_STR_PF_FMT"\" after '{'", RF_STR_PF_ARG(ast_identifier_str(name))); goto err_free_typeclass; break; case PARSER_FNDECL_LIST_FAILURE: parser_synerr(p, lexer_last_token_end(p->lexer), NULL, "Expected a proper function declaration inside " "typeclass \""RF_STR_PF_FMT"\"", RF_STR_PF_ARG(ast_identifier_str(name))); goto err_free_typeclass; break; default: // SUCCESS break; } tok = lexer_next_token(p->lexer); if (!tok || tok->type != TOKEN_SM_CCBRACE) { parser_synerr(p, lexer_last_token_end(p->lexer), NULL, "Expected '}' at the end of \""RF_STR_PF_FMT"\" " "typeclass declaration", RF_STR_PF_ARG(ast_identifier_str(name))); goto err_free_typeclass; } ast_node_set_end(n, token_get_end(tok)); lexer_pop(p->lexer); return n; err_free_genr: if (genr) { ast_node_destroy(genr); } err_free_typeclass: if (n) { ast_node_destroy(n); } err: lexer_rollback(p->lexer); return NULL; }
struct ast_node *parser_acc_ifexpr(struct parser *p, enum token_type if_type) { struct ast_node *n; struct ast_node *branch; struct token *tok; const struct inplocation_mark *start; RF_ASSERT(if_type == TOKEN_KW_IF || if_type == TOKEN_KW_ELIF, "parse_ifexp called with invalid token type"); tok = lexer_lookahead(p->lexer, 1); if (!tok || tok->type != if_type) { return NULL; } start = token_get_start(tok); // consume 'if' or 'elif' lexer_next_token(p->lexer); lexer_push(p->lexer); // parse the taken branch branch = parser_acc_condbranch(p, tok); if (!branch) { goto err; } // create the if expression n = ast_ifexpr_create(start, ast_node_endmark(branch), branch, NULL); if (!n) { ast_node_destroy(branch); RF_ERRNOMEM(); goto err; } tok = lexer_lookahead(p->lexer, 1); while (tok && (tok->type == TOKEN_KW_ELIF || tok->type == TOKEN_KW_ELSE)) { if (tok->type == TOKEN_KW_ELIF) { branch = parser_acc_ifexpr(p, TOKEN_KW_ELIF); if (!branch) { // error reporting should already happen in parser_acc_ifexpr() goto err_free; } } else { //can only be an else lexer_next_token(p->lexer); // consume it branch = parser_acc_block(p, true); if (!branch) { parser_synerr(p, token_get_end(tok), NULL, "Expected a block after 'else'"); goto err_free; } } ast_ifexpr_add_fallthrough_branch(n, branch); ast_node_set_end(n, ast_node_endmark(branch)); tok = lexer_lookahead(p->lexer, 1); } lexer_pop(p->lexer); return n; err_free: ast_node_destroy(n); err: lexer_rollback(p->lexer); return NULL; }