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; }
static struct ast_node *ast_parser_acc_return_statement(struct ast_parser *p) { struct token *tok; struct ast_node *expr; struct ast_node *n = NULL; const struct inplocation_mark *start; tok = lexer_lookahead(parser_lexer(p), 1); if (!tok) { return NULL; } if (tok->type != TOKEN_KW_RETURN) { return NULL; } start = token_get_start(tok); //consume the return keyword lexer_curr_token_advance(parser_lexer(p)); expr = ast_parser_acc_expression(p); if (!expr) { parser_synerr(p, lexer_last_token_end(parser_lexer(p)), NULL, "Expected an expression for the return statement"); goto end; } n = ast_returnstmt_create(start, ast_node_endmark(expr), expr); if (!n) { RF_ERROR("Could not create a return statement during parsing"); } end: return n; }
static bool analyzer_populate_symbol_module(struct analyzer_traversal_ctx *ctx, struct ast_node *n) { const struct ast_node *search_node; bool symbol_found_at_first_st; const struct RFstring *name = ast_module_name(n); search_node = symbol_table_lookup_node(ctx->current_st, name, &symbol_found_at_first_st); if (search_node && symbol_found_at_first_st) { analyzer_err(ctx->m, ast_node_startmark(n), ast_node_endmark(n), "Identifier \""RF_STR_PF_FMT"\" was already used in scope " "at "INPLOCATION_FMT, RF_STR_PF_ARG(name), INPLOCATION_ARG(module_get_file(ctx->m), ast_node_location(search_node))); return false; } if (!symbol_table_add_node(ctx->current_st, ctx->m, name, n)) { RF_ERROR("Could not add a module to a symbol table"); return false; } return true; }
static bool analyzer_populate_symbol_table_typeleaf(struct analyzer_traversal_ctx *ctx, struct ast_node *n) { const struct ast_node *search_node; bool symbol_found_at_first_st; const struct RFstring *id_name; struct ast_node *left; AST_NODE_ASSERT_TYPE(n, AST_TYPE_LEAF); left = ast_typeleaf_left(n); id_name = ast_identifier_str(left); search_node = symbol_table_lookup_node(ctx->current_st, id_name, &symbol_found_at_first_st); if (search_node && symbol_found_at_first_st) { analyzer_err(ctx->m, ast_node_startmark(n), ast_node_endmark(n), "Identifier \""RF_STR_PF_FMT"\" was already used in scope " "at "INPLOCATION_FMT, RF_STR_PF_ARG(id_name), INPLOCATION_ARG(module_get_file(ctx->m), ast_node_location(search_node))); return false; } if (!symbol_table_add_node(ctx->current_st, ctx->m, id_name, n)) { RF_ERROR("Could not add a type leaf's node to a symbol table"); return false; } return true; }
static bool analyzer_populate_symbol_table_typedecl(struct analyzer_traversal_ctx *ctx, struct ast_node *n) { const struct ast_node *search_node; bool symbol_found_at_first_st; const struct RFstring *type_name; AST_NODE_ASSERT_TYPE(n, AST_TYPE_DECLARATION); type_name = ast_typedecl_name_str(n); search_node = symbol_table_lookup_node(ctx->current_st, type_name, &symbol_found_at_first_st); if (search_node && symbol_found_at_first_st) { analyzer_err(ctx->m, ast_node_startmark(n), ast_node_endmark(n), "Type \""RF_STR_PF_FMT"\" was already declared in scope " "at "INPLOCATION_FMT, RF_STR_PF_ARG(type_name), INPLOCATION_ARG(module_get_file(ctx->m), ast_node_location(search_node))); return false; } if (!symbol_table_add_node(ctx->current_st, ctx->m, type_name, n)) { if (!module_have_errors(ctx->m)) { RF_ERROR("Could not add a typedecl node to a symbol table"); } return false; } return true; }
static bool analyzer_symbol_table_add_fndecl(struct analyzer_traversal_ctx *ctx, struct ast_node *n) { struct symbol_table_record *rec; const struct RFstring *fn_name; fn_name = ast_fndecl_name_str(n); rec = symbol_table_lookup_record(ctx->current_st, fn_name, NULL); if (rec && type_is_function(rec->data)) { analyzer_err(ctx->m, ast_node_startmark(n), ast_node_endmark(n), "Function \""RF_STR_PF_FMT"\" was already declared " "at "INPLOCATION_FMT, RF_STR_PF_ARG(fn_name), INPLOCATION_ARG( module_get_file(ctx->m), ast_node_location(symbol_table_record_node(rec)))); return false; } if (!symbol_table_add_node(ctx->current_st, ctx->m, fn_name, n)) { RF_ERROR("Could not add a function node to a symbol table"); return false; } // function's arguments are added to the symbol table by type creation return true; }
/* 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; }
struct ast_node *ast_iterable_create_identifier(struct ast_node *identifier) { struct ast_node *ret = ast_node_create_marks( AST_ITERABLE, ast_node_startmark(identifier), ast_node_endmark(identifier) ); if (!ret) { return NULL; } ret->iterable.type = ITERABLE_COLLECTION; ast_node_register_child(ret, identifier, iterable.identifier); return ret; }
struct ast_node *ast_iterable_create_range( struct ast_node *start_node, struct ast_node *step_node, struct ast_node *end_node) { struct ast_node *ret = ast_node_create_marks( AST_ITERABLE, ast_node_startmark(start_node), ast_node_endmark(end_node) ); if (!ret) { return NULL; } ret->iterable.type = ITERABLE_RANGE; memset(&ret->iterable.range, 0 , sizeof(struct int_range)); ret->iterable.range.start = -1; ret->iterable.range.step = 1; // default step ret->iterable.range.end = -1; ast_node_register_child(ret, start_node, iterable.range.start_node); if (ast_node_type(start_node) == AST_CONSTANT) { if (!ast_constant_get_integer(&start_node->constant, &ret->iterable.range.start)) { return NULL; } } if (step_node) { ast_node_register_child(ret, step_node, iterable.range.step_node); if (ast_node_type(step_node) == AST_CONSTANT) { if (!ast_constant_get_integer(&step_node->constant, &ret->iterable.range.step)) { return NULL; } } } ast_node_register_child(ret, end_node, iterable.range.end_node); if (ast_node_type(end_node) == AST_CONSTANT) { if (!ast_constant_get_integer(&end_node->constant, &ret->iterable.range.end)) { return NULL; } } return ret; }
static bool analyzer_populate_symbol_table_vardecl(struct analyzer_traversal_ctx *ctx, struct ast_node *n) { struct ast_node *left; struct ast_node *desc; AST_NODE_ASSERT_TYPE(n, AST_VARIABLE_DECLARATION); desc = ast_vardecl_desc_get(n); left = ast_typeleaf_left(desc); if (left->type != AST_IDENTIFIER) { analyzer_err(ctx->m, ast_node_startmark(left), ast_node_endmark(left), "Expected an identifier in the left side of a variable's " "type description node but " "found a \""RF_STR_PF_FMT"\"", RF_STR_PF_ARG(ast_node_str(left))); return false; } return analyzer_populate_symbol_table_typeleaf(ctx, desc); }
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_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; }
enum traversal_cb_res typecheck_function_call( struct ast_node *n, struct analyzer_traversal_ctx *ctx) { const struct RFstring *fn_name; const struct type *fn_type; const struct type *fn_declared_args_type; const struct type *fn_found_args_type; struct ast_node *fn_call_args = ast_fncall_args(n); // check for existence of function fn_name = ast_fncall_name(n); fn_type = type_lookup_identifier_string(fn_name, ctx->current_st); if (!fn_type || !type_is_callable(fn_type)) { analyzer_err(ctx->m, ast_node_startmark(n), ast_node_endmark(n), "Undefined function call \""RFS_PF"\" detected", RFS_PA(fn_name)); goto fail; } // create the arguments ast node array ast_fncall_arguments(n); // also check the ast node of the function declaration to get more // information if it's not a conversion if (!type_is_explicitly_convertable_elementary(fn_type)) { const struct ast_node *fndecl = symbol_table_lookup_node( ctx->current_st, fn_name, NULL ); RF_ASSERT(fndecl, "Since fn_type was found so should fndecl be found here"); if (fndecl->fndecl.position == FNDECL_PARTOF_FOREIGN_IMPORT) { n->fncall.type = AST_FNCALL_FOREIGN; } } fn_found_args_type = (fn_call_args) ? ast_node_get_type(fn_call_args) : type_elementary_get_type(ELEMENTARY_TYPE_NIL); if (!fn_found_args_type) { // argument typechecking failed goto fail; } if (type_is_explicitly_convertable_elementary(fn_type)) { // silly way to check if it's only 1 argument. Maybe figure out safer way? if (!fn_call_args || fn_found_args_type->category == TYPE_CATEGORY_OPERATOR) { analyzer_err( ctx->m, ast_node_startmark(n), ast_node_endmark(n), "Invalid arguments for explicit conversion to \"" RFS_PF"\".", RFS_PA(fn_name) ); goto fail; } // check if the explicit conversion is valid if (!type_compare(fn_found_args_type, fn_type, TYPECMP_EXPLICIT_CONVERSION)) { analyzer_err(ctx->m, ast_node_startmark(n), ast_node_endmark(n), "Invalid explicit conversion. "RFS_PF".", RFS_PA(typecmp_ctx_get_error())); goto fail; } n->fncall.type = AST_FNCALL_EXPLICIT_CONVERSION; } else { //check that the types of its arguments do indeed match fn_declared_args_type = type_callable_get_argtype(fn_type); n->fncall.declared_type = fn_declared_args_type; if (type_is_sumop(fn_declared_args_type)) { n->fncall.type = AST_FNCALL_SUM; } typecmp_ctx_set_flags(TYPECMP_FLAG_FUNCTION_CALL); if (!type_compare( fn_found_args_type, fn_declared_args_type, n->fncall.type == AST_FNCALL_SUM ? TYPECMP_PATTERN_MATCHING : TYPECMP_IMPLICIT_CONVERSION)) { RFS_PUSH(); analyzer_err( ctx->m, ast_node_startmark(n), ast_node_endmark(n), RFS_PF" "RFS_PF"() is called with argument type of \""RFS_PF "\" which does not match the expected " "type of \""RFS_PF"\"%s"RFS_PF".", RFS_PA(type_callable_category_str(fn_type)), RFS_PA(fn_name), RFS_PA(type_str_or_die(fn_found_args_type, TSTR_DEFAULT)), RFS_PA(type_str_or_die(fn_declared_args_type, TSTR_DEFINED_ONLY_CONTENTS)), typecmp_ctx_have_error() ? ". " : "", RFS_PA(typecmp_ctx_get_error()) ); RFS_POP(); goto fail; } // the function call's matched type should be either a specific sum type // that may have matched or the entirety of the called arguments if (!(n->fncall.params_type = typemp_ctx_get_matched_type())) { n->fncall.params_type = fn_found_args_type; } } traversal_node_set_type(n, type_callable_get_rettype(fn_type), ctx); return TRAVERSAL_CB_OK; fail: traversal_node_set_type(n, NULL, ctx); return TRAVERSAL_CB_ERROR; }
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; }