#include "../include/parser.h" #include "../include/ast.h" #include "../include/compiler.h" #include "../include/std/misc.h" #include "../include/std/file.h" #include "../include/std/mem.h" #include "../include/std/log.h" #include "../include/std/alloc.h" #include #include #include #define throw(result) do { throw_debug_msg; longjmp(self->parse_env, (result)); } while(0) #define throw_if_error(result) \ do { \ int return_if_result; \ return_if_result = (result); \ if((return_if_result) != 0) \ throw(return_if_result); \ } while(0) #define VAR_MAX_LEN UINT8_MAX static CHECK_RESULT Ast* parser_parse_rhs(Parser *self); static CHECK_RESULT Ast* parser_parse_body(Parser *self); static CHECK_RESULT Ast* parser_parse_struct_body(Parser *self); static CHECK_RESULT Ast* parser_parse_rhs_binop(Parser *self); static void parser_parse_var_type(Parser *self, VariableType *result); static void parser_parse_var_type_def(Parser *self, VariableType *result); static void parser_queue_file(Parser *self, BufferView path, FileScopeReference **file_scope); int parser_init(Parser *self, amal_compiler *compiler, ArenaAllocator *allocator) { self->allocator = allocator; self->compiler = compiler; self->ssa = NULL; self->started = bool_false; self->error.index = 0; self->error.str = NULL; self->error_context = ERROR_CONTEXT_NONE; return_if_error(structdecl_init(&self->struct_decl, &compiler->root_scope, allocator)); self->struct_decl.body.parser = self; lhsexpr_init(&self->file_decl, DECL_FLAG_EXTERN | DECL_FLAG_PUB | DECL_FLAG_CONST, create_buffer_view_null()); return_if_error(ast_create(self->allocator, &self->struct_decl, AST_STRUCT_DECL, &self->file_decl.rhs_expr)); self->current_scope = &self->struct_decl.body; self->has_func_parent = bool_false; am_memset(&self->bytecode, 0, sizeof(self->bytecode)); return PARSER_OK; } static bool parser_is_current_scope_file_scope(Parser *self) { return self->current_scope == &self->struct_decl.body; } /* BODY_LOOP = BODY* @end_token */ static void parser_parse_body_loop(Parser *self, Scope *scope, Token end_token) { int result; for(;;) { Ast *body_obj; bool is_end_token; throw_if_error(tokenizer_consume_if(&self->tokenizer, end_token, &is_end_token)); if(is_end_token) break; body_obj = parser_parse_body(self); result = scope_add_child(scope, body_obj); if(result == 0) { continue; } else if(result == AST_ERR_DEF_DUP) { /* TODO: Convert ast type to string for error message */ BufferView obj_name; obj_name = ast_get_name(body_obj); self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_code_reference_index(&self->tokenizer, obj_name.data), "Variable with the name %.*s was declared twice in the same scope", obj_name.size, obj_name.data); self->error_context = ERROR_CONTEXT_NONE; throw(result); } else { throw(result); } } } /* STRUCT_BODY_LOOP = '{' STRUCT_BODY* '}' */ static void parser_parse_struct_body_loop(Parser *self, Scope *scope) { int result; throw_if_error(tokenizer_accept(&self->tokenizer, TOK_OPEN_BRACE)); for(;;) { Ast *body_obj; bool is_end_token; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_CLOSING_BRACE, &is_end_token)); if(is_end_token) break; body_obj = parser_parse_struct_body(self); result = scope_add_child(scope, body_obj); if(result == 0) { continue; } else if(result == AST_ERR_DEF_DUP) { /* TODO: Convert ast type to string for error message */ BufferView obj_name; obj_name = ast_get_name(body_obj); self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_code_reference_index(&self->tokenizer, obj_name.data), "Variable with the name %.*s was declared twice in the struct", obj_name.size, obj_name.data); self->error_context = ERROR_CONTEXT_NONE; throw(result); } else { throw(result); } } } /* FUNC_PARAM = TOK_IDENTIFIER VAR_TYPE_DEF */ static CHECK_RESULT bool parser_parse_function_parameter(Parser *self, FunctionParameter *result) { bool match; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IDENTIFIER, &match)); if(!match) return bool_false; result->name = self->tokenizer.value.identifier; parser_parse_var_type_def(self, &result->type); if(result->type.type == VARIABLE_TYPE_NONE) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), "Expected ':' after parameter name"); throw(PARSER_UNEXPECTED_TOKEN); } return bool_true; } /* FUNC_PARAMS = FUNC_PARAM (',' FUNC_PARAM)* */ static void parser_parse_function_parameters(Parser *self, FunctionSignature *func_sig) { for(;;) { FunctionParameter func_param; bool match; int result; function_parameter_init(&func_param); if(!parser_parse_function_parameter(self, &func_param)) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), "Expected closure parameter"); throw(PARSER_UNEXPECTED_TOKEN); } result = function_signature_add_parameter(func_sig, &func_param); if(result == AST_ERR_DEF_DUP) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_code_reference_index(&self->tokenizer, func_param.name.data), "Parameter with name %.*s was defined twice for the function", func_param.name.size, func_param.name.data); self->error_context = ERROR_CONTEXT_NONE; throw(result); } else if(result != 0) { throw(result); } throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_COMMA, &match)); if(!match) return; } } /* FUNC_RETURN_TYPES = VAR_TYPE (',' VAR_TYPE)* */ static void parser_parse_function_return_types(Parser *self, FunctionSignature *func_sig) { int return_type_index; return_type_index = 0; for(;;) { VariableType var_type; bool match; parser_parse_var_type(self, &var_type); if(var_type.type == VARIABLE_TYPE_NONE) { /* If function has no return types */ if(return_type_index == 0) return; self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), "Expected type or closure signature"); throw(PARSER_UNEXPECTED_TOKEN); } throw_if_error(function_signature_add_return_type(func_sig, &var_type)); throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_COMMA, &match)); if(!match) return; ++return_type_index; } } /* FUNC_SIGNATURE = 'fn' ('(' FUNC_PARAMS? ') FUNC_RETURN_TYPES? ')? */ static CHECK_RESULT FunctionSignature* parser_parse_function_signature(Parser *self) { FunctionSignature *signature; bool match; throw_if_error(arena_allocator_alloc(self->allocator, sizeof(FunctionSignature), (void**)&signature)); throw_if_error(function_signature_init(signature, self->allocator)); throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_FN, &match)); if(!match) return NULL; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_OPEN_PAREN, &match)); if(match) { bool has_closing_paren; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_CLOSING_PAREN, &has_closing_paren)); if(!has_closing_paren) { parser_parse_function_parameters(self, signature); throw_if_error(tokenizer_accept(&self->tokenizer, TOK_CLOSING_PAREN)); } parser_parse_function_return_types(self, signature); } return signature; } /* VAR_TYPE = TOK_IDENTIFIER|FUNC_SIGNATURE */ void parser_parse_var_type(Parser *self, VariableType *result) { bool match; result->type = VARIABLE_TYPE_NONE; result->value.variable = NULL; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IDENTIFIER, &match)); if(match) { result->type = VARIABLE_TYPE_VARIABLE; throw_if_error(arena_allocator_alloc(self->allocator, sizeof(Variable), (void**)&result->value.variable)); variable_init(result->value.variable, self->tokenizer.value.identifier); return; } result->value.signature = parser_parse_function_signature(self); if(result->value.signature) result->type = VARIABLE_TYPE_SIGNATURE; } /* VAR_TYPE_DEF = ':' VAR_TYPE */ void parser_parse_var_type_def(Parser *self, VariableType *result) { bool match; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_COLON, &match)); if(!match) { result->type = VARIABLE_TYPE_NONE; result->value.variable = NULL; return; } parser_parse_var_type(self, result); if(result->type == VARIABLE_TYPE_NONE) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), "Expected type or closure signature"); throw(PARSER_UNEXPECTED_TOKEN); } } /* LHS_DECLARATION = ('extern'|'export')? 'pub'? 'const'|'var' TOK_IDENTIFIER VAR_TYPE_DEF? */ static CHECK_RESULT LhsExpr* parser_parse_declaration_lhs(Parser *self) { LhsExpr *result; DeclFlag decl_flag; bool is_extern; bool is_export; bool is_pub; bool is_const; BufferView var_name; decl_flag = DECL_FLAG_NONE; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_EXTERN, &is_extern)); if(is_extern) { decl_flag |= DECL_FLAG_EXTERN; if(self->has_func_parent) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), "Only declarations in global structs can be extern"); throw(PARSER_UNEXPECTED_TOKEN); } } else { throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_EXPORT, &is_export)); if(is_export) { decl_flag |= DECL_FLAG_EXPORT; if(self->has_func_parent) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), "Only declarations in global structs can be exported"); throw(PARSER_UNEXPECTED_TOKEN); } } } throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_PUB, &is_pub)); if(is_pub) { decl_flag |= DECL_FLAG_PUB; if(self->has_func_parent) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), "Only declarations in global structs can be public"); throw(PARSER_UNEXPECTED_TOKEN); } } throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_CONST, &is_const)); if(is_const) { decl_flag |= DECL_FLAG_CONST; } else { bool isVar; if(is_extern || is_export) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), "Extern and exported variables have to be declared with \"const\""); throw(PARSER_UNEXPECTED_TOKEN); } throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_VAR, &isVar)); if(!isVar) return NULL; } throw_if_error(tokenizer_accept(&self->tokenizer, TOK_IDENTIFIER)); var_name = self->tokenizer.value.identifier; throw_if_error(arena_allocator_alloc(self->allocator, sizeof(LhsExpr), (void**)&result)); lhsexpr_init(result, decl_flag, var_name); if(var_name.size > VAR_MAX_LEN) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_code_reference_index(&self->tokenizer, var_name.data), "Variable name can't be longer than %d", VAR_MAX_LEN); throw(PARSER_ERR); } parser_parse_var_type_def(self, &result->type); return result; } /* CLOSURE = FUNC_SIGNATURE '{' BODY_LOOP '}' */ static CHECK_RESULT FunctionDecl* parser_parse_closure(Parser *self) { FunctionSignature *signature; FunctionDecl *result; bool prev_has_func_parent; signature = parser_parse_function_signature(self); if(!signature) return NULL; /* TODO: Implement function declaration inside other functions. Such functions should be moved to the file scope in the bytecode generation */ assert(parser_is_current_scope_file_scope(self)); throw_if_error(arena_allocator_alloc(self->allocator, sizeof(FunctionDecl), (void**)&result)); throw_if_error(funcdecl_init(result, signature, self->current_scope, self->allocator)); signature->func_decl = result; result->body.function_signature = signature; self->current_scope = &result->body; prev_has_func_parent = self->has_func_parent; self->has_func_parent = bool_true; throw_if_error(tokenizer_accept(&self->tokenizer, TOK_OPEN_BRACE)); parser_parse_body_loop(self, self->current_scope, TOK_CLOSING_BRACE); self->current_scope = result->body.parent; self->has_func_parent = prev_has_func_parent; return result; } /* STRUCT = 'struct' '{' STRUCT_BODY_LOOP '}' */ static CHECK_RESULT StructDecl* parser_parse_struct_decl(Parser *self) { StructDecl *result; bool match; result = NULL; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_STRUCT, &match)); if(!match) return result; throw_if_error(arena_allocator_alloc(self->allocator, sizeof(StructDecl), (void**)&result)); throw_if_error(structdecl_init(result, self->current_scope, self->allocator)); self->current_scope = &result->body; parser_parse_struct_body_loop(self, self->current_scope); self->current_scope = result->body.parent; return result; } /* FUNC_ARGS = (RHS_START)? (',' RHS_START)* ')' */ static void parser_parse_function_args(Parser *self, FunctionCall *func_call) { bool first_arg; first_arg = bool_true; for(;;) { Ast *arg_expr; bool is_end_token; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_CLOSING_PAREN, &is_end_token)); if(is_end_token) break; if(!first_arg) throw_if_error(tokenizer_accept(&self->tokenizer, TOK_COMMA)); first_arg = bool_false; arg_expr = parser_parse_rhs(self); throw_if_error(buffer_append(&func_call->args, &arg_expr, sizeof(arg_expr))); } } /* VARIABLE = TOK_IDENTIFIER FUNC_CALL_OR_VARIABLE = VARIABLE ('(' FUNC_ARGS ')')? */ static CHECK_RESULT Ast* parser_parse_function_call_or_variable(Parser *self) { Ast *result; bool match; BufferView identifier; FunctionCall *func_call; result = NULL; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IDENTIFIER, &match)); if(!match) return result; identifier = self->tokenizer.value.identifier; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_OPEN_PAREN, &match)); if(!match) { Variable *variable; throw_if_error(arena_allocator_alloc(self->allocator, sizeof(Variable), (void**)&variable)); variable_init(variable, identifier); throw_if_error(ast_create(self->allocator, variable, AST_VARIABLE, &result)); return result; } throw_if_error(arena_allocator_alloc(self->allocator, sizeof(FunctionCall), (void**)&func_call)); throw_if_error(funccall_init(func_call, self->tokenizer.value.identifier, self->allocator)); throw_if_error(ast_create(self->allocator, func_call, AST_FUNCTION_CALL, &result)); /* Ends after TOK_CLOSING_PAREN */ parser_parse_function_args(self, func_call); return result; } /* IMPORT = IMPORT_SYMBOL */ static CHECK_RESULT Import* parser_parse_import(Parser *self) { Import *result; bool match; result = NULL; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IMPORT, &match)); if(!match) return result; throw_if_error(arena_allocator_alloc(self->allocator, sizeof(Import), (void**)&result)); import_init(result, self->tokenizer.value.string); return result; } /* ELSE_IF_STATEMENT = 'else' ('if' RHS_BINOP)? ('{' BODY_LOOP '}')|BODY */ static CHECK_RESULT ElseIfStatement* parser_parse_else_if_statement(Parser *self) { ElseIfStatement *result; bool match; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_ELSE, &match)); if(!match) return NULL; throw_if_error(arena_allocator_alloc(self->allocator, sizeof(ElseIfStatement), (void**)&result)); throw_if_error(else_if_statement_init(result, self->current_scope, self->allocator)); throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IF, &match)); if(match) result->condition = parser_parse_rhs_binop(self); throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_OPEN_BRACE, &match)); if(match) { parser_parse_body_loop(self, &result->body, TOK_CLOSING_BRACE); } else { Ast *body_obj; body_obj = parser_parse_body(self); throw_if_error(scope_add_child(&result->body, body_obj)); } return result; } static void parser_parse_else_if_statement_loop(Parser *self, IfStatement *if_stmt) { ElseIfStatement *else_if_stmt = NULL; for(;;) { ElseIfStatement *next_else_if; next_else_if = parser_parse_else_if_statement(self); if(!next_else_if) break; if(!else_if_stmt) if_stmt->else_if_stmt = next_else_if; else else_if_stmt->next_else_if_stmt = next_else_if; /* else statement that has no condition can't be followed by another else statement */ if(!next_else_if->condition) break; else_if_stmt = next_else_if; } } /* IF_STATEMENT = 'if' RHS_BINOP ('{' BODY_LOOP '}')|BODY ELSE_IF_STATEMENT* */ static CHECK_RESULT IfStatement* parser_parse_if_statement(Parser *self) { IfStatement *result; bool match; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IF, &match)); if(!match) return NULL; throw_if_error(arena_allocator_alloc(self->allocator, sizeof(IfStatement), (void**)&result)); throw_if_error(if_statement_init(result, self->current_scope, self->allocator)); result->condition = parser_parse_rhs_binop(self); throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_OPEN_BRACE, &match)); if(match) { parser_parse_body_loop(self, &result->body, TOK_CLOSING_BRACE); } else { Ast *body_obj; body_obj = parser_parse_body(self); throw_if_error(scope_add_child(&result->body, body_obj)); } parser_parse_else_if_statement_loop(self, result); return result; } /* WHILE_STATEMENT = 'while' RHS_BINOP ('{' BODY_LOOP '}')|BODY */ static CHECK_RESULT WhileStatement* parser_parse_while_statement(Parser *self) { WhileStatement *result; bool match; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_WHILE, &match)); if(!match) return NULL; throw_if_error(arena_allocator_alloc(self->allocator, sizeof(WhileStatement), (void**)&result)); throw_if_error(while_statement_init(result, self->current_scope, self->allocator)); result->condition = parser_parse_rhs_binop(self); throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_OPEN_BRACE, &match)); if(match) { parser_parse_body_loop(self, &result->body, TOK_CLOSING_BRACE); } else { Ast *body_obj; body_obj = parser_parse_body(self); throw_if_error(scope_add_child(&result->body, body_obj)); } return result; } static CHECK_RESULT Ast* parser_parse_number(Parser *self) { Ast *result; bool match; Number *number; result = NULL; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_NUMBER, &match)); if(!match) return result; throw_if_error(arena_allocator_alloc(self->allocator, sizeof(Number), (void**)&number)); number_init(number, self->tokenizer.value.integer, self->tokenizer.number_is_integer, create_buffer_view(self->tokenizer.code.data + self->tokenizer.prev_index, self->tokenizer.index - self->tokenizer.prev_index)); throw_if_error(ast_create(self->allocator, number, AST_NUMBER, &result)); return result; } /* RHS_S = STRING | NUMBER | FUNC_CALL_OR_VARIABLE */ static CHECK_RESULT Ast* parser_parse_rhs_single_expr(Parser *self) { Ast *result; bool match; result = NULL; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_STRING, &match)); if(match) { String *string; throw_if_error(arena_allocator_alloc(self->allocator, sizeof(String), (void**)&string)); throw_if_error(string_init(string, self->tokenizer.value.string)); throw_if_error(ast_create(self->allocator, string, AST_STRING, &result)); return result; } result = parser_parse_number(self); if(result) return result; result = parser_parse_function_call_or_variable(self); if(result) return result; self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), "Expected string, variable or function call"); throw(PARSER_UNEXPECTED_TOKEN); } /* RHS_BINOP_OPT_PAREN = RHS_S | '(' RHS_BINOP ')' */ static CHECK_RESULT Ast* parser_parse_rhs_binop_opt_paren(Parser *self) { Ast *result; bool match; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_OPEN_PAREN, &match)); if(!match) return parser_parse_rhs_single_expr(self); result = parser_parse_rhs_binop(self); throw_if_error(tokenizer_accept(&self->tokenizer, TOK_CLOSING_PAREN)); if(result->type == AST_BINOP) result->value.binop->grouped = bool_true; return result; } /* RHS_BINOP = RHS_BINOP_OPT_PAREN (TOK_BINOP RHS_BINOP_OPT_PAREN)? Note: Parantheses count has to match for the beginning paranthesis and the ending parenthesis. */ Ast* parser_parse_rhs_binop(Parser *self) { bool match; Ast *result; Ast *lhs; Ast *rhs; BinopType binop_type; Binop *binop; lhs = parser_parse_rhs_binop_opt_paren(self); throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_BINOP, &match)); if(!match) return lhs; binop_type = self->tokenizer.value.binop_type; rhs = parser_parse_rhs_binop(self); throw_if_error(arena_allocator_alloc(self->allocator, sizeof(Binop), (void**)&binop)); binop_init(binop); binop->type = binop_type; binop->lhs = lhs; binop->rhs = rhs; throw_if_error(ast_create(self->allocator, binop, AST_BINOP, &result)); return result; } /* RHS = RHS_BINOP ';' Note: Parantheses count has to match for the beginning paranthesis and the ending parenthesis. */ Ast* parser_parse_rhs(Parser *self) { /* TODO: If binop only contains one expression, then use that directly for @rhs_expr */ Ast *result; result = parser_parse_rhs_binop(self); /* TODO: Implement this */ /*binop_reorder_by_precedence(binop);*/ return result; } static bool type_requires_semicolon_at_end(AstType type) { /* TODO: Check for tables */ return type != AST_FUNCTION_DECL && type != AST_STRUCT_DECL && type != AST_IF_STATEMENT; } /* BODY_SEMICOLON = ';' Note: Semicolon is not required for closures, structs and tables */ static void parser_parse_body_semicolon(Parser *self, Ast *expr) { if(expr->type == AST_BINOP) { bool match; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_SEMICOLON, &match)); if(!match) { /* TODO: Specify all the binop characters instead of "binop" which doesn't make sense for the user */ self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), "Expected ';' or binop"); throw(PARSER_UNEXPECTED_TOKEN); } return; } if(type_requires_semicolon_at_end(expr->type)) throw_if_error(tokenizer_accept(&self->tokenizer, TOK_SEMICOLON)); } /* CONDITIONAL = (IF_STATEMENT|WHILE_STATEMENT)? */ static CHECK_RESULT Ast* parser_parse_conditional(Parser *self) { Ast *result; IfStatement *if_stmt; WhileStatement *while_stmt; if_stmt = parser_parse_if_statement(self); if(if_stmt) { throw_if_error(ast_create(self->allocator, if_stmt, AST_IF_STATEMENT, &result)); self->error_context = ERROR_CONTEXT_NONE; return result; } while_stmt = parser_parse_while_statement(self); if(while_stmt) { throw_if_error(ast_create(self->allocator, while_stmt, AST_WHILE_STATEMENT, &result)); self->error_context = ERROR_CONTEXT_NONE; return result; } return NULL; } /* LHS_RHS = CLOSURE|STRUCT|IMPORT */ static Ast* parser_parse_lhs_rhs(Parser *self, LhsExpr *lhs_expr) { Ast *result; FunctionDecl *func_decl; StructDecl *struct_decl; Import *import; func_decl = parser_parse_closure(self); if(func_decl) { if(buffer_get_size(&func_decl->signature->return_types, FunctionReturnType) > 1 && (LHS_EXPR_IS_EXTERN(lhs_expr) || LHS_EXPR_IS_EXPORT(lhs_expr))) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_code_reference_index(&self->tokenizer, lhs_expr->var_name.data), "Extern and export closures can only have one return value"); throw(PARSER_ERR); } throw_if_error(ast_create(self->allocator, func_decl, AST_FUNCTION_DECL, &result)); func_decl->lhs_expr = lhs_expr; return result; } struct_decl = parser_parse_struct_decl(self); if(struct_decl) { throw_if_error(ast_create(self->allocator, struct_decl, AST_STRUCT_DECL, &result)); return result; } import = parser_parse_import(self); if(import) { parser_queue_file(self, import->path, &import->file_scope); throw_if_error(ast_create(self->allocator, import, AST_IMPORT, &result)); parser_parse_body_semicolon(self, result); return result; } return NULL; } /* RETURN = 'return' RHS */ static ReturnExpr* parser_parse_return(Parser *self) { bool match; ReturnExpr *result; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_RETURN, &match)); if(!match) return NULL; throw_if_error(arena_allocator_alloc(self->allocator, sizeof(ReturnExpr), (void**)&result)); return_expr_init(result, parser_parse_rhs(self)); return result; } /* BODY = (LHS_DECLARATION ';') | (LHS_DECLARATION '=' LHS_RHS|(RHS BODY_SEMICOLON)) | CONDITIONAL | (RHS ';'|('=' RHS BODY_SEMICOLON)) */ Ast* parser_parse_body(Parser *self) { Ast *result; LhsExpr *lhs_expr; ReturnExpr *return_expr; Ast *rhs_expr; lhs_expr = parser_parse_declaration_lhs(self); if(lhs_expr) { bool match; throw_if_error(ast_create(self->allocator, lhs_expr, AST_LHS, &result)); if(LHS_EXPR_IS_EXTERN(lhs_expr)) { throw_if_error(tokenizer_accept(&self->tokenizer, TOK_SEMICOLON)); if (lhs_expr->type.type == VARIABLE_TYPE_NONE) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_code_reference_index(&self->tokenizer, lhs_expr->var_name.data), "An extern variable can't be declared without a type"); throw(PARSER_UNEXPECTED_TOKEN); } return result; } else { throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_SEMICOLON, &match)); if(match) { if(lhs_expr->type.type == VARIABLE_TYPE_SIGNATURE) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_code_reference_index(&self->tokenizer, lhs_expr->var_name.data), "Expected function declaration. Only extern functions can have empty declarations."); throw(PARSER_UNEXPECTED_TOKEN); } else if (lhs_expr->type.type == VARIABLE_TYPE_NONE) { /* TODO: Allow this for non-const variables */ self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_code_reference_index(&self->tokenizer, lhs_expr->var_name.data), "A variable can't be declared without a type or assignment"); throw(PARSER_UNEXPECTED_TOKEN); } else if(LHS_EXPR_IS_CONST(lhs_expr)) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_code_reference_index(&self->tokenizer, lhs_expr->var_name.data), "A const variable can't be declared without assignment"); throw(PARSER_UNEXPECTED_TOKEN); } return result; } } throw_if_error(tokenizer_accept(&self->tokenizer, TOK_EQUALS)); lhs_expr->rhs_expr = parser_parse_lhs_rhs(self, lhs_expr); if(lhs_expr->rhs_expr) return result; } else { self->error_context = ERROR_CONTEXT_NO_LHS; result = parser_parse_conditional(self); if(result) return result; } if(self->has_func_parent) { return_expr = parser_parse_return(self); if(return_expr) { throw_if_error(tokenizer_accept(&self->tokenizer, TOK_SEMICOLON)); throw_if_error(ast_create(self->allocator, return_expr, AST_RETURN, &result)); return result; } } self->error_context = ERROR_CONTEXT_RHS_STANDALONE; rhs_expr = parser_parse_rhs(self); self->error_context = ERROR_CONTEXT_NONE; /* Variable declaration with lhs and rhs */ if(lhs_expr) { lhs_expr->rhs_expr = rhs_expr; } else { bool match; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_EQUALS, &match)); /* Variable assignment */ if(match) { AssignmentExpr *assign_expr; throw_if_error(arena_allocator_alloc(self->allocator, sizeof(AssignmentExpr), (void**)&assign_expr)); throw_if_error(ast_create(self->allocator, assign_expr, AST_ASSIGN, &result)); assign_expr->lhs_expr = rhs_expr; self->error_context = ERROR_CONTEXT_RHS_STANDALONE; assign_expr->rhs_expr = parser_parse_rhs(self); self->error_context = ERROR_CONTEXT_NONE; } else { /* Only rhs (for example function call, binop etc...) */ result = rhs_expr; } } parser_parse_body_semicolon(self, rhs_expr); return result; } /* STRUCT_BODY = TOK_IDENTIFIER VAR_TYPE_DEF ';' */ Ast* parser_parse_struct_body(Parser *self) { Ast *result; BufferView var_name; VariableType var_type; StructField *struct_field; throw_if_error(tokenizer_accept(&self->tokenizer, TOK_IDENTIFIER)); var_name = self->tokenizer.value.identifier; parser_parse_var_type_def(self, &var_type); if(var_type.type == VARIABLE_TYPE_NONE) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), "Expected ':' after struct field name"); throw(PARSER_UNEXPECTED_TOKEN); } throw_if_error(tokenizer_accept(&self->tokenizer, TOK_SEMICOLON)); throw_if_error(arena_allocator_alloc(self->allocator, sizeof(LhsExpr), (void**)&struct_field)); structfield_init(struct_field, var_name, &var_type); throw_if_error(ast_create(self->allocator, struct_field, AST_STRUCT_FIELD, &result)); return result; } /* ROOT = BODY_LOOP */ int parser_parse_buffer(Parser *self, BufferView code_buffer, BufferView buffer_name) { int result; self->file_decl.var_name = buffer_name; throw_if_error(tokenizer_init(&self->tokenizer, self->allocator, code_buffer, buffer_name, &self->compiler->options)); result = setjmp(self->parse_env); if(result == 0) parser_parse_body_loop(self, &self->struct_decl.body, TOK_END_OF_FILE); else if(self->error.str != NULL) { switch(self->error_context) { case ERROR_CONTEXT_NONE: tokenizer_print_error_object(&self->tokenizer, &self->error); break; case ERROR_CONTEXT_RHS_STANDALONE: /* TODO: Only show "return" in error message if @self->has_func_parent */ tokenizer_print_error(&self->tokenizer, self->tokenizer.prev_index, "Expected string, variable, closure, struct, function call, import or return"); break; case ERROR_CONTEXT_NO_LHS: tokenizer_print_error(&self->tokenizer, self->tokenizer.prev_index, "Expected variable declaration, string, variable or function call"); break; default: assert(bool_false && "Error context handling not implemented"); break; } } if(result != 0) amal_log_error("Failed, reason: %d", result); return result; } int parser_parse_file(Parser *self, BufferView filepath) { Buffer file_content; return_if_error(buffer_init(&file_content, self->allocator)); assert(!self->started && "Parser can't be reused. Create a new parser."); self->started = bool_true; assert(filepath.size > 0 && filepath.data[filepath.size] == '\0'); return_if_error(read_whole_file(filepath.data, &file_content)); return parser_parse_buffer(self, create_buffer_view(file_content.data, file_content.size), filepath); } static CHECK_RESULT int file_path_join(BufferView directory, BufferView file, char **result_path) { /* '/' '\0' */ return_if_error(am_malloc(directory.size + 1 + file.size + 1, (void**)result_path)); am_memcpy(*result_path, directory.data, directory.size); (*result_path)[directory.size] = '/'; am_memcpy(*result_path + directory.size + 1, file.data, file.size); (*result_path)[directory.size + 1 + file.size] = '\0'; return 0; } /* Path can be path to included library path (or system library path) in which case the path separator is a dot, otherwise the path separator is forward slash '/' */ void parser_queue_file(Parser *self, BufferView path, FileScopeReference **file_scope) { /* TODO: Parse special path (to include library path with dots) */ BufferView file_directory; char *path_relative; int result; file_directory = file_get_parent_directory(self->tokenizer.code_name); throw_if_error(file_path_join(file_directory, path, &path_relative)); /* We want buffer to be null-terminated but null character should not be included for the size */ result = amal_compiler_internal_load_file(self->compiler, path_relative, file_scope); if(result != 0) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_code_reference_index(&self->tokenizer, path.data), "Failed to while parsing path %s (invalid path?)", path_relative); am_free(path_relative); throw(result); } am_free(path_relative); }