#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 #include static CHECK_RESULT int parser_queue_file(Parser *self, BufferView path); static CHECK_RESULT int parser_parse_body(Parser *self, Ast *ast); 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) { int retval; if(self->status == PARSER_THREAD_STATUS_NEW) return 0; retval = amal_thread_join(&self->thread, result); if(retval == 0 || retval == AMAL_THREAD_NOT_JOINABLE) self->status = PARSER_THREAD_STATUS_IDLE; return retval; } int parser_init(Parser *self, amal_compiler *compiler, ScopedAllocator *allocator) { self->allocator = allocator; self->compiler = compiler; self->started = bool_false; return buffer_init(&self->ast_objects, self->allocator); } /* 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; } /* FUNC_DECL = '(' PARAM* ')' '{' BODY* '}' */ static CHECK_RESULT int parser_parse_function_decl(Parser *self, FunctionDecl **func_decl) { bool result; *func_decl = NULL; return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_OPEN_PAREN, &result)); if(!result) return PARSER_OK; /* 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)); for(;;) { Ast body_obj; return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_CLOSING_BRACE, &result)); if(result) break; return_if_error(parser_parse_body(self, &body_obj)); return_if_error(funcdecl_add_to_body(*func_decl, body_obj)); } return PARSER_OK; } /* FUNC_CALL = IDENTIFIER '(' ARGS* ')' */ static CHECK_RESULT int parser_parse_function_call(Parser *self, FunctionCall **func_call) { bool result; BufferView func_name; *func_call = NULL; return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IDENTIFIER, &result)); if(!result) return PARSER_OK; func_name = self->tokenizer.value.identifier; return_if_error(tokenizer_accept(&self->tokenizer, TOK_OPEN_PAREN)); /* TODO: Parse arguments */ return_if_error(tokenizer_accept(&self->tokenizer, TOK_CLOSING_PAREN)); return_if_error(scoped_allocator_alloc(self->allocator, sizeof(FunctionCall), (void**)func_call)); funccall_init(*func_call, func_name); return PARSER_OK; } /* IMPORT = IMPORT_SYMBOL */ static CHECK_RESULT int parser_parse_import(Parser *self, Import **import) { bool result; return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IMPORT, &result)); if(!result) 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; } /* RHS = FUNC_DECL | FUNC_CALL | IMPORT */ static CHECK_RESULT int parser_parse_rhs(Parser *self, Ast *rhs_expr) { FunctionDecl *func_decl; FunctionCall *func_call; Import *import; 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_function_call(self, &func_call)); if(func_call) { rhs_expr->type = AST_FUNCTION_CALL; rhs_expr->value.func_call = func_call; 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; } /* TODO: Convert token to string */ tokenizer_print_error(&self->tokenizer, "Expected function declaration or function call"); return PARSER_UNEXPECTED_TOKEN; } /* BODY = (LHS '=' RHS) | RHS */ int parser_parse_body(Parser *self, Ast *ast) { LhsExpr *lhs_expr; Ast rhs_expr; 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(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* */ int parser_parse_buffer(Parser *self, BufferView code_buffer, BufferView buffer_name) { Ast ast; return_if_error(tokenizer_init(&self->tokenizer, code_buffer, buffer_name)); for(;;) { bool isEof; return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_END_OF_FILE, &isEof)); if(isEof) break; return_if_error(parser_parse_body(self, &ast)); return_if_error(buffer_append(&self->ast_objects, &ast, sizeof(ast))); } return PARSER_OK; } int parser_parse_file(Parser *self, BufferView filepath) { int result; int mapped_file_deinit_result; MappedFile mapped_file; 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 = mapped_file_init(&mapped_file, filepath_tmp, MAPPED_FILE_READ); if(result != 0) return result; result = parser_parse_buffer(self, create_buffer_view(mapped_file.file_data, mapped_file.file_size), filepath); mapped_file_deinit_result = mapped_file_deinit(&mapped_file); return result != 0 ? result : mapped_file_deinit_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; }