From 2323ca6c9ec3c8ee76b9acf13745b80b92952a6a Mon Sep 17 00:00:00 2001 From: dec05eba Date: Mon, 18 Mar 2019 23:47:45 +0100 Subject: Add struct, import caching, binop ops etc --- src/parser.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 133 insertions(+), 34 deletions(-) (limited to 'src/parser.c') diff --git a/src/parser.c b/src/parser.c index 7c0280a..5396b72 100644 --- a/src/parser.c +++ b/src/parser.c @@ -11,6 +11,12 @@ #include #include +#ifdef AMAL_PEDANTIC + #define throw_debug_msg do {} while(0) +#else + #define throw_debug_msg do { amal_log_info("Throwing from %s:%d", __FUNCTION__, __LINE__); } while(0) +#endif + #define THROWABLE CHECK_RESULT int #define try(result) \ do { \ @@ -18,7 +24,7 @@ do { \ return_if_result = (result); \ (void)return_if_result; \ } while(0) -#define throw(result) do { longjmp(self->parse_env, (result)); } while(0) +#define throw(result) do { throw_debug_msg; longjmp(self->parse_env, (result)); } while(0) #define throw_if_error(result) \ do { \ int return_if_result; \ @@ -29,7 +35,8 @@ do { \ static THROWABLE parser_parse_rhs_start(Parser *self, Ast *rhs_expr); static THROWABLE parser_parse_body(Parser *self, Ast *ast); -static THROWABLE parser_queue_file(Parser *self, BufferView path); +static THROWABLE parser_parse_struct_body(Parser *self, Ast *ast); +static THROWABLE parser_queue_file(Parser *self, BufferView path, FileScopeReference **file_scope); int parser_thread_data_init(ParserThreadData *self) { am_memset(&self->allocator, 0, sizeof(self->allocator)); @@ -39,8 +46,9 @@ int parser_thread_data_init(ParserThreadData *self) { } int parser_thread_data_deinit(ParserThreadData *self) { + ignore_result_int(amal_thread_deinit(&self->thread)); scoped_allocator_deinit(&self->allocator); - return amal_thread_deinit(&self->thread); + return 0; } int parser_thread_data_start(ParserThreadData *self, AmalThreadCallbackFunc callback_func, void *userdata) { @@ -63,9 +71,8 @@ int parser_init(Parser *self, amal_compiler *compiler, ScopedAllocator *allocato self->error.index = 0; self->error.str = NULL; self->error_context = ERROR_CONTEXT_NONE; - /* TODO: When resolving ast uses mutex, add compiler->scope as the parent of the parser scope */ self->current_scope = &self->scope; - return scope_init(&self->scope, NULL, self->allocator); + return scope_init(&self->scope, &compiler->root_scope, self->allocator); } /* @@ -100,6 +107,39 @@ static THROWABLE parser_parse_body_loop(Parser *self, Scope *scope, Token end_to return PARSER_OK; } +/* +STRUCT_BODY_LOOP = '{' STRUCT_BODY* '}' +*/ +static THROWABLE 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; + + try(parser_parse_struct_body(self, &body_obj)); + 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); + } + } + return PARSER_OK; +} + /* VAR_TYPE_DEF = ':' TOK_IDENTIFIER */ @@ -137,37 +177,44 @@ static THROWABLE parser_parse_lhs(Parser *self, LhsExpr **result, bool *assignme throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(LhsExpr), (void**)result)); lhsexpr_init(*result, is_const, var_name); - try(parser_parse_var_type_def(self, &(*result)->type_name)); + try(parser_parse_var_type_def(self, &(*result)->type.name)); throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_EQUALS, &match)); if(match) { *assignment_or_rhs = bool_true; return PARSER_OK; } + + if(!(*result)->type.name.data) { + self->error = tokenizer_create_error(&self->tokenizer, + tokenizer_get_error_index(&self->tokenizer), + "Variable declaration requires type or right-hand side expression (Expected ':' or '=')"); + throw(PARSER_UNEXPECTED_TOKEN); + } *assignment_or_rhs = bool_false; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_SEMICOLON, &match)); if(match && is_const) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), - "const variable declaration requires assignment (expected '=', got ';')"); + "Const variable declaration requires assignment (expected '=', got ';')"); throw(PARSER_UNEXPECTED_TOKEN); } if(!match) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), - "Expected '=' or ';'"); + "Expected ';'"); throw(PARSER_UNEXPECTED_TOKEN); } return PARSER_OK; } /* +TODO: Implement params CLOSURE = 'fn' ('(' PARAM* ')')? '{' BODY_LOOP '}' */ static THROWABLE parser_parse_function_decl(Parser *self, FunctionDecl **func_decl) { bool match; - Scope *prev_scope; *func_decl = NULL; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_FN, &match)); @@ -186,11 +233,29 @@ static THROWABLE parser_parse_function_decl(Parser *self, FunctionDecl **func_de throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(FunctionDecl), (void**)func_decl)); throw_if_error(funcdecl_init(*func_decl, self->current_scope, self->allocator)); - prev_scope = self->current_scope; self->current_scope = &(*func_decl)->body; try(parser_parse_body_loop(self, self->current_scope, TOK_CLOSING_BRACE)); self->current_scope = (*func_decl)->body.parent; - assert(self->current_scope == prev_scope); + return PARSER_OK; +} + +/* +STRUCT = 'struct' '{' STRUCT_BODY_LOOP '}' +*/ +static THROWABLE parser_parse_struct_decl(Parser *self, StructDecl **struct_decl) { + bool match; + *struct_decl = NULL; + + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_STRUCT, &match)); + if(!match) + return PARSER_OK; + + throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(StructDecl), (void**)struct_decl)); + throw_if_error(structdecl_init(*struct_decl, self->current_scope, self->allocator)); + + self->current_scope = &(*struct_decl)->body; + try(parser_parse_struct_body_loop(self, self->current_scope)); + self->current_scope = (*struct_decl)->body.parent; return PARSER_OK; } @@ -381,6 +446,7 @@ RHS_START = CLOSURE | IMPORT | RHS */ int parser_parse_rhs_start(Parser *self, Ast *rhs_expr) { FunctionDecl *func_decl; + StructDecl *struct_decl; Import *import; try(parser_parse_function_decl(self, &func_decl)); @@ -389,10 +455,16 @@ int parser_parse_rhs_start(Parser *self, Ast *rhs_expr) { return PARSER_OK; } + try(parser_parse_struct_decl(self, &struct_decl)); + if(struct_decl) { + ast_init(rhs_expr, struct_decl, AST_STRUCT_DECL); + return PARSER_OK; + } + try(parser_parse_import(self, &import)); if(import) { + try(parser_queue_file(self, import->path, &import->file_scope)); ast_init(rhs_expr, import, AST_IMPORT); - try(parser_queue_file(self, import->path)); return PARSER_OK; } @@ -421,8 +493,8 @@ static THROWABLE parser_parse_body_semicolon(Parser *self, Ast *expr) { return PARSER_OK; } - /* TODO: Check for struct and tables */ - if(expr->type != AST_FUNCTION_DECL) + /* TODO: Check for tables */ + if(expr->type != AST_FUNCTION_DECL && expr->type != AST_STRUCT_DECL) throw_if_error(tokenizer_accept(&self->tokenizer, TOK_SEMICOLON)); return PARSER_OK; @@ -457,6 +529,26 @@ int parser_parse_body(Parser *self, Ast *ast) { return PARSER_OK; } +/* +STRUCT_BODY = LHS ';' +*/ +int parser_parse_struct_body(Parser *self, Ast *ast) { + BufferView var_name; + BufferView type_name; + StructField *struct_field; + + throw_if_error(tokenizer_accept(&self->tokenizer, TOK_IDENTIFIER)); + var_name = self->tokenizer.value.identifier; + throw_if_error(tokenizer_accept(&self->tokenizer, TOK_COLON)); + throw_if_error(tokenizer_accept(&self->tokenizer, TOK_IDENTIFIER)); + type_name = self->tokenizer.value.identifier; + throw_if_error(tokenizer_accept(&self->tokenizer, TOK_SEMICOLON)); + throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(LhsExpr), (void**)&struct_field)); + structfield_init(struct_field, var_name, type_name); + ast_init(ast, struct_field, AST_STRUCT_FIELD); + return PARSER_OK; +} + /* ROOT = BODY_LOOP */ @@ -472,13 +564,16 @@ int parser_parse_buffer(Parser *self, BufferView code_buffer, BufferView buffer_ tokenizer_print_error_object(&self->tokenizer, &self->error); break; case ERROR_CONTEXT_RHS_START: - tokenizer_print_error(&self->tokenizer, self->tokenizer.prev_index, "Expected string, variable, closure, function call or import"); + tokenizer_print_error(&self->tokenizer, self->tokenizer.prev_index, "Expected string, variable, closure, struct, function call or import"); break; default: assert(bool_false && "Error context handling not implemented"); break; } } + if(result != 0) { + amal_log_info("Failed, reason: %d", result); + } assert(self->current_scope == &self->scope); return result; } @@ -488,7 +583,6 @@ int parser_parse_file(Parser *self, BufferView filepath) { char *file_data; usize file_size; - 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; assert(filepath.size > 0 && filepath.data[filepath.size] == '\0'); @@ -498,13 +592,13 @@ int parser_parse_file(Parser *self, BufferView filepath) { return result; } -static CHECK_RESULT int file_path_join(BufferView directory, BufferView file, ScopedAllocator *allocator, Buffer *result) { - return_if_error(buffer_init(result, allocator)); - return_if_error(buffer_append(result, NULL, directory.size + 1 + file.size + 1)); - am_memcpy(result->data, directory.data, directory.size); - result->data[directory.size] = '/'; - am_memcpy(result->data + directory.size + 1, file.data, file.size); - result->data[directory.size + 1 + file.size] = '\0'; +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; } @@ -512,19 +606,24 @@ static CHECK_RESULT int file_path_join(BufferView directory, BufferView file, Sc 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 */ +int parser_queue_file(Parser *self, BufferView path, FileScopeReference **file_scope) { /* TODO: Parse special path (to include library path with dots) */ BufferView file_directory; - BufferView filename; - Buffer file_to_parse; + char *path_relative; + int result; + file_directory = file_get_parent_directory(self->tokenizer.code_name); - filename = file_get_name(path); - return_if_error(file_path_join(file_directory, filename, self->allocator, &file_to_parse)); - /* - We want buffer to be null terminated but null terminated character - should not be included for the length. - */ - throw_if_error(amal_compiler_load_file(self->compiler, create_buffer_view(file_to_parse.data, file_to_parse.size - 1))); + return_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_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); + self->error_context = ERROR_CONTEXT_NONE; + am_free(path_relative); + throw(result); + } + am_free(path_relative); return PARSER_OK; } -- cgit v1.2.3