#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 static CHECK_RESULT int parser_parse_rhs_start(Parser *self, Ast *rhs_expr); static CHECK_RESULT int parser_parse_body(Parser *self, Ast *ast); static CHECK_RESULT int parser_queue_file(Parser *self, BufferView path); int parser_thread_data_init(ParserThreadData *self) { am_memset(&self->allocator, 0, sizeof(self->allocator)); am_memset(&self->thread, 0, sizeof(self->thread)); self->status = PARSER_THREAD_STATUS_NEW; return scoped_allocator_init(&self->allocator); } int parser_thread_data_deinit(ParserThreadData *self) { scoped_allocator_deinit(&self->allocator); return amal_thread_deinit(&self->thread); } int parser_thread_data_start(ParserThreadData *self, AmalThreadCallbackFunc callback_func, void *userdata) { return_if_error(amal_thread_deinit(&self->thread)); return_if_error(amal_thread_create(&self->thread, AMAL_THREAD_JOINABLE, "Parser", callback_func, userdata)); self->status = PARSER_THREAD_STATUS_RUNNING; return 0; } int parser_thread_data_join(ParserThreadData *self, void **result) { if(self->status == PARSER_THREAD_STATUS_NEW) return 0; return amal_thread_join(&self->thread, result); } int parser_init(Parser *self, amal_compiler *compiler, ScopedAllocator *allocator) { self->allocator = allocator; self->compiler = compiler; self->started = bool_false; self->error.index = 0; self->error.str = NULL; return buffer_init(&self->ast_objects, self->allocator); } static bool parser_got_overwritable_error(Parser *self) { return self->error.str != NULL; } /* BODY_LOOP = BODY* @end_token */ static CHECK_RESULT int parser_parse_body_loop(Parser *self, Buffer *body_list, Token end_token) { for(;;) { Ast body_obj; bool is_end_token; return_if_error(tokenizer_consume_if(&self->tokenizer, end_token, &is_end_token)); if(is_end_token) break; return_if_error(parser_parse_body(self, &body_obj)); return_if_error(buffer_append(body_list, &body_obj, sizeof(body_obj))); } return PARSER_OK; } /* LHS = 'const'|'var' IDENTIFIER */ static CHECK_RESULT int parser_parse_lhs(Parser *self, LhsExpr **result) { bool isConst; BufferView var_name; *result = NULL; return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_CONST, &isConst)); if(!isConst) { bool isVar; return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_VAR, &isVar)); if(!isVar) return PARSER_OK; } return_if_error(tokenizer_accept(&self->tokenizer, TOK_IDENTIFIER)); var_name = self->tokenizer.value.identifier; amal_log_debug("var name: %.*s", (int)var_name.size, var_name.data); return_if_error(scoped_allocator_alloc(self->allocator, sizeof(LhsExpr), (void**)result)); lhsexpr_init(*result, isConst, var_name); return PARSER_OK; } /* CLOSURE = 'fn' ('(' PARAM* ')')? '{' BODY_LOOP '}' */ static CHECK_RESULT int parser_parse_function_decl(Parser *self, FunctionDecl **func_decl) { bool match; *func_decl = NULL; return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_FN, &match)); if(!match) return PARSER_OK; return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_OPEN_BRACE, &match)); if(!match) { return_if_error(tokenizer_accept(&self->tokenizer, TOK_OPEN_PAREN)); /* TODO: Parse parameters */ return_if_error(tokenizer_accept(&self->tokenizer, TOK_CLOSING_PAREN)); /* TODO: Parse return types */ return_if_error(tokenizer_accept(&self->tokenizer, TOK_OPEN_BRACE)); } return_if_error(scoped_allocator_alloc(self->allocator, sizeof(FunctionDecl), (void**)func_decl)); return_if_error(funcdecl_init(*func_decl, self->allocator)); return parser_parse_body_loop(self, &(*func_decl)->body, TOK_CLOSING_BRACE); } /* FUNC_ARGS = (RHS_START)? (',' RHS_START)* ')' */ static CHECK_RESULT int parser_parse_function_args(Parser *self, FunctionCall *func_call) { bool first_arg; first_arg = bool_true; for(;;) { Ast arg_expr; bool is_end_token; arg_expr = ast_none(); return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_CLOSING_PAREN, &is_end_token)); if(is_end_token) break; if(!first_arg) return_if_error(tokenizer_accept(&self->tokenizer, TOK_COMMA)); first_arg = bool_false; return_if_error(parser_parse_rhs_start(self, &arg_expr)); return_if_error(buffer_append(&func_call->args, &arg_expr, sizeof(arg_expr))); } return PARSER_OK; } /* VARIABLE = IDENTIFIER FUNC_CALL_OR_VARIABLE = VARIABLE '(' FUNC_ARGS ')' */ static CHECK_RESULT int parser_parse_function_call_or_variable(Parser *self, Ast *expr) { bool match; BufferView identifier; FunctionCall *func_call; return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IDENTIFIER, &match)); if(!match) return PARSER_OK; identifier = self->tokenizer.value.identifier; return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_OPEN_PAREN, &match)); if(!match) { Variable *variable; return_if_error(scoped_allocator_alloc(self->allocator, sizeof(Variable), (void**)&variable)); variable->name = identifier; expr->type = AST_VARIABLE; expr->value.variable = variable; return PARSER_OK; } return_if_error(scoped_allocator_alloc(self->allocator, sizeof(FunctionCall), (void**)&func_call)); return_if_error(funccall_init(func_call, self->tokenizer.value.identifier, self->allocator)); expr->type = AST_FUNCTION_CALL; expr->value.func_call = func_call; return parser_parse_function_args(self, func_call); } /* IMPORT = IMPORT_SYMBOL */ static CHECK_RESULT int parser_parse_import(Parser *self, Import **import) { bool match; *import = NULL; return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IMPORT, &match)); if(!match) return PARSER_OK; return_if_error(scoped_allocator_alloc(self->allocator, sizeof(Import), (void**)import)); import_init(*import, self->tokenizer.value.string); return PARSER_OK; } static CHECK_RESULT int parser_parse_number(Parser *self, Ast *rhs_expr) { bool match; Number *number; return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_NUMBER, &match)); if(!match) return PARSER_OK; return_if_error(scoped_allocator_alloc(self->allocator, sizeof(Number), (void**)&number)); number_init(number, self->tokenizer.value.integer, self->tokenizer.number_is_integer); rhs_expr->type = AST_NUMBER; rhs_expr->value.number = number; return PARSER_OK; } /* RHS = STRING | NUMBER | FUNC_CALL_OR_VARIABLE */ static CHECK_RESULT int parser_parse_rhs(Parser *self, Ast *rhs_expr) { bool match; return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_STRING, &match)); if(match) { String *string; return_if_error(scoped_allocator_alloc(self->allocator, sizeof(String), (void**)&string)); return_if_error(string_init(string, self->tokenizer.value.string)); rhs_expr->type = AST_STRING; rhs_expr->value.string = string; return PARSER_OK; } return_if_error(parser_parse_number(self, rhs_expr)); if(rhs_expr->type != AST_NONE) return PARSER_OK; return_if_error(parser_parse_function_call_or_variable(self, rhs_expr)); if(rhs_expr->type != AST_NONE) return PARSER_OK; self->error = tokenizer_create_error(&self->tokenizer, "Expected string, variable or function call"); return PARSER_UNEXPECTED_TOKEN; } /* RHS_START = CLOSURE | IMPORT | RHS */ int parser_parse_rhs_start(Parser *self, Ast *rhs_expr) { FunctionDecl *func_decl; Import *import; int result; return_if_error(parser_parse_function_decl(self, &func_decl)); if(func_decl) { rhs_expr->type = AST_FUNCTION_DECL; rhs_expr->value.func_decl = func_decl; return PARSER_OK; } return_if_error(parser_parse_import(self, &import)); if(import) { rhs_expr->type = AST_IMPORT; rhs_expr->value.import = import; return_if_error(parser_queue_file(self, import->path)); return PARSER_OK; } result = parser_parse_rhs(self, rhs_expr); if(result == PARSER_UNEXPECTED_TOKEN && parser_got_overwritable_error(self)) self->error = tokenizer_create_error(&self->tokenizer, "Expected string, variable, closure, function call or import"); return result; } /* BODY = (LHS '=' RHS_START) | RHS_START */ int parser_parse_body(Parser *self, Ast *ast) { LhsExpr *lhs_expr; Ast rhs_expr; rhs_expr = ast_none(); return_if_error(parser_parse_lhs(self, &lhs_expr)); if(lhs_expr) return_if_error(tokenizer_accept(&self->tokenizer, TOK_EQUALS)); return_if_error(parser_parse_rhs_start(self, &rhs_expr)); if(lhs_expr) { lhs_expr->rhs_expr = rhs_expr; ast->type = AST_LHS; ast->value.lhs_expr = lhs_expr; } else { *ast = rhs_expr; } return PARSER_OK; } /* ROOT = BODY_LOOP */ int parser_parse_buffer(Parser *self, BufferView code_buffer, BufferView buffer_name) { int result; return_if_error(tokenizer_init(&self->tokenizer, code_buffer, buffer_name)); result = parser_parse_body_loop(self, &self->ast_objects, TOK_END_OF_FILE); if(self->error.str != NULL) tokenizer_print_error_object(&self->tokenizer, &self->error); return result; } int parser_parse_file(Parser *self, BufferView filepath) { int result; char *file_data; usize file_size; char *filepath_tmp; amal_log_debug("Parsing %.*s", (int)filepath.size, filepath.data); assert(!self->started && "Parser can't be reused. Create a new parser."); self->started = bool_true; /* TODO: Somehow free this... */ /*return_if_error(scoped_allocator_alloc(self->allocator, filepath.size + 1, (void**)&filepath_tmp));*/ filepath_tmp = malloc(filepath.size + 1); am_memcpy(filepath_tmp, filepath.data, filepath.size); filepath_tmp[filepath.size] = '\0'; result = read_whole_file(filepath_tmp, &file_data, &file_size); if(result != 0) return result; result = parser_parse_buffer(self, create_buffer_view(file_data, file_size), create_buffer_view(filepath_tmp, filepath.size)); /* TODO: Somehow free this.. causes issue where filepath becomes corrupt */ /*am_free(file_data);*/ return result; } /* 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 '/' */ int parser_queue_file(Parser *self, BufferView path) { /* TODO: Do not load same path twice or the compile will fail (and it can have recursive import) also for performance reasons */ /* TODO: Parse special path (to include library path with dots) */ return_if_error(amal_compiler_load_file(self->compiler, path)); return PARSER_OK; }