aboutsummaryrefslogtreecommitdiff
path: root/src/parser.c
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2019-03-18 23:47:45 +0100
committerdec05eba <dec05eba@protonmail.com>2020-07-25 14:36:46 +0200
commit2323ca6c9ec3c8ee76b9acf13745b80b92952a6a (patch)
tree93013237dbcb0fa96ceb5f3c026fd040aff464cf /src/parser.c
parent5a93c32a59775cd1be4b4f450e8230016b434366 (diff)
Add struct, import caching, binop ops etc
Diffstat (limited to 'src/parser.c')
-rw-r--r--src/parser.c167
1 files changed, 133 insertions, 34 deletions
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 <stdlib.h>
#include <assert.h>
+#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);
}
/*
@@ -101,6 +108,39 @@ static THROWABLE parser_parse_body_loop(Parser *self, Scope *scope, Token end_to
}
/*
+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
*/
static THROWABLE parser_parse_var_type_def(Parser *self, BufferView *type_name) {
@@ -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;
@@ -458,6 +530,26 @@ int parser_parse_body(Parser *self, Ast *ast) {
}
/*
+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
*/
int parser_parse_buffer(Parser *self, BufferView code_buffer, BufferView buffer_name) {
@@ -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;
}