From 1b430c1387f4281c02572c334084482e046aab7b Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Mon, 18 May 2020 22:28:31 -0500 Subject: [PATCH] Progress on the initial parser #1 --- Makefile | 1 + bh.h | 22 ++-- onyx | Bin 54120 -> 69296 bytes onyx.c | 30 ++++- onyxlex.c | 57 ++++++---- onyxlex.h | 35 ++++-- onyxmsgs.c | 44 ++++++++ onyxmsgs.h | 36 ++++++ onyxparser.c | 270 ++++++++++++++++++++++++++++++++++++++++++++- onyxparser.h | 107 ++++++++++-------- progs/minimal.onyx | 4 +- 11 files changed, 508 insertions(+), 98 deletions(-) create mode 100644 onyxmsgs.c create mode 100644 onyxmsgs.h diff --git a/Makefile b/Makefile index a3c6d82e..b204645f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ OBJ_FILES=\ onyxlex.o \ onyxparser.o \ + onyxmsgs.o \ onyx.o CC=gcc diff --git a/bh.h b/bh.h index b6af9362..6ee6f882 100644 --- a/bh.h +++ b/bh.h @@ -321,6 +321,7 @@ typedef enum bh_file_standard { typedef struct bh_file_contents { bh_allocator allocator; + const char *filename; isize length; void* data; } bh_file_contents; @@ -389,7 +390,7 @@ typedef struct bh__arr { #define bh_arr_last(arr) ((arr)[bh__arrhead(arr)->length - 1]) #define bh_arr_end(arr, i) ((i) >= &(arr)[bh_arr_length(arr)]) -#define bh_arr_new(allocator_, arr, cap) (bh__arr_grow((allocator_), (void**) &arr, sizeof(*(arr)), cap)) +#define bh_arr_new(allocator_, arr, cap) (bh__arr_grow((allocator_), (void**) &(arr), sizeof(*(arr)), cap)) #define bh_arr_free(arr) (bh__arr_free((void**) &(arr))) #define bh_arr_copy(allocator_, arr) (bh__arr_copy((allocator_), (arr), sizeof(*(arr)))) @@ -601,7 +602,7 @@ i64 chars_match(char* ptr1, char* ptr2) { //------------------------------------------------------------------------------------- -// CUSTOM ALLOCATORS IMPLEMENTATION +// CUSTOM ALLOCATORS IMPLEMENTATION //------------------------------------------------------------------------------------- @@ -716,7 +717,7 @@ BH_ALLOCATOR_PROC(bh_arena_allocator_proc) { // Size too large for the arena return NULL; } - + if (alloc_arena->size + size >= alloc_arena->arena_size) { alloc_arena->size = sizeof(ptr); bh__arena_internal* new_arena = (bh__arena_internal *) bh_alloc(alloc_arena->backing, alloc_arena->arena_size); @@ -790,7 +791,7 @@ BH_ALLOCATOR_PROC(bh_scratch_allocator_proc) { if (scratch->curr >= scratch->end) { scratch->curr = scratch->memory; - retval = scratch->curr; + retval = scratch->curr; } } break; @@ -1103,6 +1104,7 @@ i64 bh_file_size(bh_file* file) { bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file) { bh_file_contents fc = { .allocator = alloc, + .filename = file->filename, .length = 0, .data = NULL }; @@ -1225,7 +1227,7 @@ void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize) { } void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems) { - bh__arr* arrptr = bh__arrhead(*arr); + bh__arr* arrptr = bh__arrhead(*arr); if (index >= arrptr->length) return; // Can't delete past the end of the array if (numelems <= 0) return; // Can't delete nothing @@ -1328,13 +1330,13 @@ found_matching: } b32 bh__hash_has(bh__hash *table, i32 elemsize, char *key) { - u64 index = bh__hash_function(key, 0); + u64 index = bh__hash_function(key, 0); ptr arrptr = table->arrs[index]; if (arrptr == NULL) return 0; i32 len = bh_arr_length(arrptr); - i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE; + i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE; while (len--) { if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) return 1; @@ -1348,11 +1350,11 @@ ptr bh__hash_get(bh__hash *table, i32 elemsize, char *key) { u64 index = bh__hash_function(key, 0); ptr arrptr = table->arrs[index]; - i32 len = bh_arr_length(arrptr); - assert(arrptr != NULL); + if (arrptr == NULL) return NULL; i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE; + i32 len = bh_arr_length(arrptr); while (len--) { if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) { return bh_pointer_add(arrptr, BH__HASH_STORED_KEY_SIZE); @@ -1361,7 +1363,7 @@ ptr bh__hash_get(bh__hash *table, i32 elemsize, char *key) { return bh_pointer_add(arrptr, stride); } - return 0; + return NULL; } void bh__hash_delete(bh__hash *table, i32 elemsize, char *key) { diff --git a/onyx b/onyx index 40a17c46c671440e0ad8fe44d735b485029ac837..1cada87b18070428c1c4e5a7f8e4f39090250da9 100755 GIT binary patch literal 69296 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 literal 54120 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 Y%caUyAQCT8{)ee!;#y+MU`uNM4>uY>{Qv*} diff --git a/onyx.c b/onyx.c index 42fbb892..8406fc07 100644 --- a/onyx.c +++ b/onyx.c @@ -6,6 +6,7 @@ #include // TODO: Replace with custom lib #include "onyxlex.h" +#include "onyxmsgs.h" #include "onyxparser.h" int main(int argc, char *argv[]) { @@ -21,16 +22,39 @@ int main(int argc, char *argv[]) { bh_file_contents fc = bh_file_read_contents(alloc, &source_file); bh_file_close(&source_file); - bh_arr(OnyxToken) token_arr = onyx_parse_tokens(alloc, &fc); + OnyxTokenizer tokenizer = onyx_tokenizer_create(alloc, &fc); + onyx_parse_tokens(&tokenizer); + bh_arr(OnyxToken) token_arr = tokenizer.tokens; printf("There are %d tokens (Allocated space for %d tokens)\n", bh_arr_length(token_arr), bh_arr_capacity(token_arr)); for (OnyxToken* it = token_arr; !bh_arr_end(token_arr, it); it++) { - printf("%s '%c' (Line %ld, Col %ld)\n", onyx_get_token_type_name(*it), *(char *)it->token, it->line_number, it->line_column); + onyx_token_null_toggle(*it); + printf("%s '%s' (%s:%ld:%ld)\n", onyx_get_token_type_name(*it), it->token, it->pos.filename, it->pos.line, it->pos.column); + onyx_token_null_toggle(*it); } + bh_arena msg_arena; + bh_arena_init(&msg_arena, alloc, 4096); + bh_allocator msg_alloc = bh_arena_allocator(&msg_arena); + + OnyxMessages msgs; + onyx_message_create(msg_alloc, &msgs); + + bh_arena ast_arena; + bh_arena_init(&ast_arena, alloc, 16 * 1024 * 1024); // 16MB + bh_allocator ast_alloc = bh_arena_allocator(&ast_arena); + + OnyxParser parser = onyx_parser_create(ast_alloc, &tokenizer, &msgs); + OnyxAstNode* program = onyx_parse(&parser); + + onyx_message_print(&msgs); + bh_file_contents_delete(&fc); - bh_arr_free(token_arr); + onyx_tokenizer_free(&tokenizer); + bh_arena_free(&msg_arena); + bh_arena_free(&ast_arena); + return 0; } diff --git a/onyxlex.c b/onyxlex.c index b25256f0..2f790a1b 100644 --- a/onyxlex.c +++ b/onyxlex.c @@ -37,7 +37,6 @@ static const char* onyx_token_type_names[] = { "TOKEN_TYPE_SYM_DOT", "TOKEN_TYPE_SYM_FSLASH", "TOKEN_TYPE_SYM_BSLASH", - "TOKEN_TYPE_SYM_TYPE_SIGNATURE", "TOKEN_TYPE_SYM_COLON", "TOKEN_TYPE_SYM_SEMICOLON", "TOKEN_TYPE_SYM_COMMA", @@ -76,8 +75,8 @@ static b32 token_lit(OnyxTokenizer* tokenizer, OnyxToken* tk, char* lit, OnyxTok tk->type = type; tk->token = tokenizer->curr; tk->length = len; - tk->line_number = tokenizer->line_number; - tk->line_column = (i32)(tokenizer->curr - tokenizer->line_start) + 1; + tk->pos.line = tokenizer->line_number; + tk->pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1; tokenizer->curr += len; @@ -90,7 +89,14 @@ const char* onyx_get_token_type_name(OnyxToken tkn) { return onyx_token_type_names[tkn.type]; } -OnyxToken onyx_get_token(OnyxTokenizer* tokenizer) { +void onyx_token_null_toggle(OnyxToken tkn) { + static char backup = 0; + char tmp = tkn.token[tkn.length]; + tkn.token[tkn.length] = backup; + backup = tmp; +} + +OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer) { OnyxToken tk; // Skip whitespace @@ -100,8 +106,9 @@ OnyxToken onyx_get_token(OnyxTokenizer* tokenizer) { tk.type = TOKEN_TYPE_UNKNOWN; tk.token = tokenizer->curr; tk.length = 1; - tk.line_number = tokenizer->line_number; - tk.line_column = (i32)(tokenizer->curr - tokenizer->line_start) + 1; + tk.pos.filename = tokenizer->filename; + tk.pos.line = tokenizer->line_number; + tk.pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1; if (tokenizer->curr == tokenizer->end) { tk.type = TOKEN_TYPE_END_STREAM; @@ -110,7 +117,7 @@ OnyxToken onyx_get_token(OnyxTokenizer* tokenizer) { // Comments if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') { - tokenizer->curr += 2; + tokenizer->curr += 2; tk.type = TOKEN_TYPE_COMMENT; tk.token = tokenizer->curr; u16 layers = 1; @@ -122,7 +129,7 @@ OnyxToken onyx_get_token(OnyxTokenizer* tokenizer) { tk.type = TOKEN_TYPE_END_STREAM; break; } - + if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') { layers++; INCREMENT_CURR_TOKEN(tokenizer); @@ -132,7 +139,7 @@ OnyxToken onyx_get_token(OnyxTokenizer* tokenizer) { layers--; INCREMENT_CURR_TOKEN(tokenizer); } - } + } INCREMENT_CURR_TOKEN(tokenizer); @@ -168,7 +175,6 @@ OnyxToken onyx_get_token(OnyxTokenizer* tokenizer) { LITERAL_TOKEN("%", TOKEN_TYPE_SYM_PERCENT); LITERAL_TOKEN("/", TOKEN_TYPE_SYM_FSLASH); LITERAL_TOKEN("\\", TOKEN_TYPE_SYM_BSLASH); - LITERAL_TOKEN("::", TOKEN_TYPE_SYM_TYPE_SIGNATURE); LITERAL_TOKEN(":", TOKEN_TYPE_SYM_COLON); LITERAL_TOKEN(";", TOKEN_TYPE_SYM_SEMICOLON); LITERAL_TOKEN(",", TOKEN_TYPE_SYM_COMMA); @@ -235,26 +241,35 @@ OnyxToken onyx_get_token(OnyxTokenizer* tokenizer) { INCREMENT_CURR_TOKEN(tokenizer); token_parsed: - return tk; + bh_arr_push(tokenizer->tokens, tk); + + return &tokenizer->tokens[bh_arr_length(tokenizer->tokens) - 1]; } -bh_arr(OnyxToken) onyx_parse_tokens(bh_allocator tk_alloc, bh_file_contents *fc) { +OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc) { OnyxTokenizer tknizer = { .start = fc->data, .curr = fc->data, .end = fc->data + fc->length, + + .filename = fc->filename, + .line_number = 1, .line_start = fc->data, + .tokens = NULL, }; - bh_arr(OnyxToken) token_arr = NULL; - bh_arr_new(tk_alloc, token_arr, 512); + bh_arr_new(allocator, tknizer.tokens, 512); + return tknizer; +} - OnyxToken tk; - do { - tk = onyx_get_token(&tknizer); - bh_arr_push(token_arr, tk); - } while (tk.type != TOKEN_TYPE_END_STREAM); +void onyx_tokenizer_free(OnyxTokenizer* tokenizer) { + bh_arr_free(tokenizer->tokens); +} - return token_arr; -} \ No newline at end of file +void onyx_parse_tokens(OnyxTokenizer* tokenizer) { + OnyxToken* tk; + do { + tk = onyx_get_token(tokenizer); + } while (tk->type != TOKEN_TYPE_END_STREAM); +} diff --git a/onyxlex.h b/onyxlex.h index 15b19b40..9a3b3f12 100644 --- a/onyxlex.h +++ b/onyxlex.h @@ -3,13 +3,6 @@ #include "bh.h" -typedef struct OnyxTokenizer { - char *start, *curr, *end; - - char* line_start; - u64 line_number; -} OnyxTokenizer; - typedef enum OnyxTokenType { TOKEN_TYPE_UNKNOWN, TOKEN_TYPE_END_STREAM, @@ -46,7 +39,6 @@ typedef enum OnyxTokenType { TOKEN_TYPE_SYM_DOT, TOKEN_TYPE_SYM_FSLASH, TOKEN_TYPE_SYM_BSLASH, - TOKEN_TYPE_SYM_TYPE_SIGNATURE, TOKEN_TYPE_SYM_COLON, TOKEN_TYPE_SYM_SEMICOLON, TOKEN_TYPE_SYM_COMMA, @@ -64,15 +56,34 @@ typedef enum OnyxTokenType { TOKEN_TYPE_COUNT } OnyxTokenType; +typedef struct OnyxFilePos { + const char* filename; + u64 line, column; +} OnyxFilePos; + typedef struct OnyxToken { OnyxTokenType type; isize length; char* token; - u64 line_number, line_column; + OnyxFilePos pos; } OnyxToken; +typedef struct OnyxTokenizer { + char *start, *curr, *end; + + const char* filename; + + char* line_start; + u64 line_number; + + bh_arr(OnyxToken) tokens; +} OnyxTokenizer; + const char* onyx_get_token_type_name(OnyxToken tkn); -OnyxToken onyx_get_token(OnyxTokenizer* tokenizer); -bh_arr(OnyxToken) onyx_parse_tokens(bh_allocator tk_alloc, bh_file_contents *fc); +void onyx_token_null_toggle(OnyxToken tkn); +OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer); +OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc); +void onyx_tokenizer_free(OnyxTokenizer* tokenizer); +void onyx_parse_tokens(OnyxTokenizer* tokenizer); -#endif \ No newline at end of file +#endif diff --git a/onyxmsgs.c b/onyxmsgs.c new file mode 100644 index 00000000..9838fa02 --- /dev/null +++ b/onyxmsgs.c @@ -0,0 +1,44 @@ + +#include "onyxmsgs.h" + +static const char* msg_formats[] = { + "expected token '%s'", + "unexpected token '%s'", + "unknown type '%s'" +}; + +void onyx_message_add(OnyxMessages* msgs, OnyxMessageType type, OnyxFilePos pos, ...) { + OnyxMessage* msg = bh_alloc_item(msgs->allocator, OnyxMessage); + msg->type = type; + msg->pos = pos; + + va_list arg_list; + va_start(arg_list, pos); + vsnprintf(msg->text, ONYX_MSG_BUFFER_SIZE, msg_formats[type], arg_list); + va_end(arg_list); + + OnyxMessage** walker = &msgs->first; + while (*walker && (*walker)->pos.line < pos.line) walker = &(*walker)->next; + while (*walker && (*walker)->pos.line == pos.line && (*walker)->pos.column < pos.column) walker = &(*walker)->next; + + msg->next = *walker; + *walker = msg; +} + +void onyx_message_print(OnyxMessages* msgs) { + OnyxMessage* msg = msgs->first; + + while (msg) { + if (msg->pos.filename) { + printf("(%s:%ld:%ld) %s\n", msg->pos.filename, msg->pos.line, msg->pos.column, msg->text); + } else { + printf("(%ld:%ld) %s\n", msg->pos.line, msg->pos.column, msg->text); + } + msg = msg->next; + } +} + +void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs) { + msgs->allocator = allocator; + msgs->first = NULL; +} diff --git a/onyxmsgs.h b/onyxmsgs.h new file mode 100644 index 00000000..db3a22ce --- /dev/null +++ b/onyxmsgs.h @@ -0,0 +1,36 @@ +#ifndef ONYXMSGS_H +#define ONYXMSGS_H + +#include "bh.h" +#include "onyxlex.h" + +#include + +#define ONYX_MSG_BUFFER_SIZE 256 + +typedef enum OnyxMessageType { + ONYX_MESSAGE_TYPE_EXPECTED_TOKEN, + ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN, + ONYX_MESSAGE_TYPE_UNKNOWN_TYPE, + + ONYX_MESSAGE_TYPE_COUNT, +} OnyxMessageType; + +typedef struct OnyxMessage { + OnyxMessageType type; + OnyxFilePos pos; + struct OnyxMessage* next; + char text[ONYX_MSG_BUFFER_SIZE]; +} OnyxMessage; + +typedef struct OnyxMessages { + bh_allocator allocator; + + OnyxMessage* first; +} OnyxMessages; + +void onyx_message_add(OnyxMessages* msgs, OnyxMessageType type, OnyxFilePos pos, ...); +void onyx_message_print(OnyxMessages* msgs); +void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs); + +#endif \ No newline at end of file diff --git a/onyxparser.c b/onyxparser.c index 8db202c7..002dd6ce 100644 --- a/onyxparser.c +++ b/onyxparser.c @@ -1,4 +1,5 @@ +#include "onyxlex.h" #include "onyxparser.h" struct OnyxTypeInfo builtin_types[] = { @@ -20,4 +21,271 @@ struct OnyxTypeInfo builtin_types[] = { { ONYX_TYPE_INFO_KIND_FLOAT32, 4, "f32", 0, 0, 1, 0 }, { ONYX_TYPE_INFO_KIND_FLOAT64, 8, "f64", 0, 0, 1, 0 }, { ONYX_TYPE_INFO_KIND_SOFT_FLOAT, 8, "sf64", 0, 0, 1, 0 }, -}; \ No newline at end of file + + { 0xffffffff } +}; + +static OnyxAstNode error_node = { { ONYX_AST_NODE_KIND_ERROR, 0, NULL, &builtin_types[0], NULL, NULL, NULL } }; + +static void parser_next_token(OnyxParser* parser) { + parser->prev_token = parser->curr_token; + parser->curr_token++; +} + +static b32 is_terminating_token(OnyxTokenType token_type) { + switch (token_type) { + case TOKEN_TYPE_SYM_SEMICOLON: + case TOKEN_TYPE_CLOSE_BRACE: + case TOKEN_TYPE_OPEN_BRACE: + case TOKEN_TYPE_END_STREAM: + return 1; + default: + return 0; + } +} + +// Advances to next token no matter what +static OnyxToken* expect(OnyxParser* parser, OnyxTokenType token_type) { + OnyxToken* token = parser->curr_token; + if (token->type != token_type) { + onyx_message_add(parser->msgs, ONYX_MESSAGE_TYPE_EXPECTED_TOKEN, token->pos, onyx_get_token_type_name(*token)); + return NULL; + } + + parser_next_token(parser); + return token; +} + +static OnyxAstNode* parse_expression(OnyxParser* parser) { + return &error_node; +} + +static OnyxAstNode* parse_if_stmt(OnyxParser* parser) { + return &error_node; +} + +static OnyxAstNode* parse_block(OnyxParser* parser) { + assert(parser->curr_token->type == TOKEN_TYPE_OPEN_BRACE); + + return &error_node; +} + +static OnyxAstNode* parse_expression_statement(OnyxParser* parser) { + +} + +static OnyxAstNode* parse_return_statement(OnyxParser* parser) { + // Only should get here with a return as the current token + assert(parser->curr_token->type == TOKEN_TYPE_KEYWORD_RETURN); + + OnyxAstNode* expr = NULL; + + OnyxToken* return_token = parser->curr_token; + parser_next_token(parser); + if (parser->curr_token->type != TOKEN_TYPE_SYM_SEMICOLON) { + expr = parse_expression(parser); + + if (expr == &error_node) { + return &error_node; + } + } +} + +static OnyxAstNode* parse_statement(OnyxParser* parser, b32 is_top_level) { + switch (parser->curr_token->type) { + case TOKEN_TYPE_KEYWORD_RETURN: + return parse_return_statement(parser); + + case TOKEN_TYPE_OPEN_BRACE: + return (OnyxAstNode *) parse_block(parser); + + case TOKEN_TYPE_SYMBOL: + case TOKEN_TYPE_OPEN_PAREN: + case TOKEN_TYPE_SYM_PLUS: + case TOKEN_TYPE_SYM_MINUS: + case TOKEN_TYPE_SYM_BANG: + case TOKEN_TYPE_LITERAL_NUMERIC: + case TOKEN_TYPE_LITERAL_STRING: + return parse_expression_statement(parser); + + case TOKEN_TYPE_KEYWORD_IF: + return parse_if_stmt(parser); + + case TOKEN_TYPE_SYM_SEMICOLON: + return NULL; + + default: + printf("ERROR\n"); + parser_next_token(parser); + return NULL; + } +} + +static OnyxTypeInfo* parse_type(OnyxParser* parser) { + OnyxTypeInfo* type_info = &builtin_types[ONYX_TYPE_INFO_KIND_UNKNOWN]; + + OnyxToken* symbol = expect(parser, TOKEN_TYPE_SYMBOL); + if (symbol == NULL) return type_info; + + onyx_token_null_toggle(*symbol); + + if (!bh_hash_has(OnyxAstNode*, parser->identifiers, symbol->token)) { + onyx_message_add(parser->msgs, ONYX_MESSAGE_TYPE_UNKNOWN_TYPE, symbol->pos, symbol->token); + } else { + OnyxAstNode* type_info_node = bh_hash_get(OnyxAstNode*, parser->identifiers, symbol->token); + + if (type_info_node->kind == ONYX_AST_NODE_KIND_TYPE) { + type_info = type_info_node->type; + } + } + + onyx_token_null_toggle(*symbol); + return type_info; +} + +static OnyxAstNodeParam* parse_function_params(OnyxParser* parser) { + expect(parser, TOKEN_TYPE_OPEN_PAREN); + + if (parser->curr_token->type == TOKEN_TYPE_CLOSE_PAREN) { + parser_next_token(parser); + return NULL; + } + + OnyxAstNodeParam* first_param = NULL; + + OnyxAstNodeParam* curr_param = NULL; + OnyxAstNodeParam** walker = NULL; + + OnyxToken* symbol; + while (parser->curr_token->type != TOKEN_TYPE_CLOSE_PAREN) { + if (parser->curr_token->type == TOKEN_TYPE_SYM_COMMA) parser_next_token(parser); + + symbol = expect(parser, TOKEN_TYPE_SYMBOL); + curr_param = (OnyxAstNodeParam *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_PARAM); + curr_param->token = symbol; + curr_param->type = parse_type(parser); + + curr_param->next = NULL; + if (first_param == NULL) { + first_param = curr_param; + } else { + (*walker)->next = curr_param; + } + walker = &curr_param; + } + + parser_next_token(parser); // Skip the ) + return first_param; +} + +static OnyxAstNodeFuncDef* parse_function_definition(OnyxParser* parser) { + expect(parser, TOKEN_TYPE_KEYWORD_PROC); + + OnyxAstNodeFuncDef* func_def = (OnyxAstNodeFuncDef *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_FUNCDEF); + func_def->param_count = 0; + + OnyxAstNodeParam* params = parse_function_params(parser); + func_def->params = params; + + for (OnyxAstNode* walker = (OnyxAstNode *) params; walker != NULL; walker = walker->next) + func_def->param_count++; + + expect(parser, TOKEN_TYPE_RIGHT_ARROW); + + OnyxTypeInfo* return_type = parse_type(parser); + func_def->return_type = return_type; + + func_def->body = NULL; + return func_def; +} + + +static OnyxAstNode* parse_top_level_statement(OnyxParser* parser) { + switch (parser->curr_token->type) { + case TOKEN_TYPE_KEYWORD_USE: + assert(0); + break; + + case TOKEN_TYPE_KEYWORD_EXPORT: + assert(0); + break; + + case TOKEN_TYPE_SYMBOL: { + OnyxToken* symbol = parser->curr_token; + parser_next_token(parser); + + expect(parser, TOKEN_TYPE_SYM_COLON); + expect(parser, TOKEN_TYPE_SYM_COLON); + + if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_PROC) { + OnyxAstNodeFuncDef* func_def = parse_function_definition(parser); + func_def->token = symbol; + return (OnyxAstNode *) func_def; + + } else if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_STRUCT) { + // Handle struct case + assert(0); + } else { + onyx_message_add(parser->msgs, + ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN, + parser->curr_token->pos, + onyx_get_token_type_name(*parser->curr_token)); + } + } break; + } + parser_next_token(parser); + return NULL; +} + + + + + + + +OnyxAstNode* onyx_ast_node_new(bh_allocator alloc, OnyxAstNodeKind kind) {\ + OnyxAstNode* node = (OnyxAstNode *) bh_alloc(alloc, sizeof(OnyxAstNode)); + node->kind = kind; + + return node; +} + +OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer, OnyxMessages* msgs) { + OnyxParser parser; + + bh_hash_init(bh_heap_allocator(), parser.identifiers); + + OnyxTypeInfo* it = &builtin_types[0]; + while (it->kind != 0xffffffff) { + OnyxAstNode* tmp = onyx_ast_node_new(alloc, ONYX_AST_NODE_KIND_TYPE); + tmp->type = it; + bh_hash_put(OnyxAstNode*, parser.identifiers, (char *)it->name, tmp); + it++; + } + + parser.allocator = alloc; + parser.tokenizer = tokenizer; + parser.curr_token = tokenizer->tokens; + parser.prev_token = NULL; + parser.msgs = msgs; + + return parser; +} + +OnyxAstNode* onyx_parse(OnyxParser *parser) { + OnyxAstNode* program = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_PROGRAM); + + OnyxAstNode** prev_stmt = &program->next; + OnyxAstNode* curr_stmt = NULL; + while (parser->curr_token->type != TOKEN_TYPE_END_STREAM) { + curr_stmt = parse_top_level_statement(parser); + + // Building a linked list of statements down the "next" chain + if (curr_stmt != NULL && curr_stmt != &error_node) { + *prev_stmt = curr_stmt; + prev_stmt = &curr_stmt->next; + } + } + + return program; +} diff --git a/onyxparser.h b/onyxparser.h index c8b2c7e2..5b2731fd 100644 --- a/onyxparser.h +++ b/onyxparser.h @@ -1,50 +1,63 @@ +#ifndef ONYXPARSER_H +#define ONYXPARSER_H + #define BH_NO_STRING #include "bh.h" #include "onyxlex.h" +#include "onyxmsgs.h" + +typedef union OnyxAstNode OnyxAstNode; +typedef struct OnyxAstNodeBlock OnyxAstNodeBlock; +typedef struct OnyxAstNodeParam OnyxAstNodeParam; +typedef struct OnyxAstNodeFuncDef OnyxAstNodeFuncDef; typedef struct OnyxParser { - OnyxTokenizer tokenizer; - OnyxToken *prev; - OnyxToken *curr; + OnyxTokenizer *tokenizer; + OnyxToken *prev_token; + OnyxToken *curr_token; + + bh_hash(OnyxAstNode*) identifiers; + + OnyxMessages *msgs; bh_allocator allocator; } OnyxParser; typedef enum OnyxAstNodeKind { - ONYX_PARSE_NODE_KIND_ERROR, - ONYX_PARSE_NODE_KIND_PROGRAM, - - ONYX_PARSE_NODE_KIND_FUNCDEF, - ONYX_PARSE_NODE_KIND_BLOCK, - ONYX_PARSE_NODE_KIND_SCOPE, - - ONYX_PARSE_NODE_KIND_ADD, - ONYX_PARSE_NODE_KIND_SUB, - ONYX_PARSE_NODE_KIND_MUL, - ONYX_PARSE_NODE_KIND_DIVIDE, - ONYX_PARSE_NODE_KIND_MODULUS, - ONYX_PARSE_NODE_KIND_NEGATE, - - ONYX_PARSE_NODE_KIND_TYPE, - ONYX_PARSE_NODE_KIND_LITERAL, - ONYX_PARSE_NODE_KIND_CAST, - ONYX_PARSE_NODE_KIND_PARAM, - ONYX_PARSE_NODE_KIND_CALL, - ONYX_PARSE_NODE_KIND_RETURN, - - ONYX_PARSE_NODE_KIND_EQUAL, - ONYX_PARSE_NODE_KIND_NOT_EQUAL, - ONYX_PARSE_NODE_KIND_GREATER, - ONYX_PARSE_NODE_KIND_GREATER_EQUAL, - ONYX_PARSE_NODE_KIND_LESS, - ONYX_PARSE_NODE_KIND_LESS_EQUAL, - ONYX_PARSE_NODE_KIND_NOT, - - ONYX_PARSE_NODE_KIND_IF, - ONYX_PARSE_NODE_KIND_LOOP, - - ONYX_PARSE_NODE_KIND_COUNT + ONYX_AST_NODE_KIND_ERROR, + ONYX_AST_NODE_KIND_PROGRAM, + + ONYX_AST_NODE_KIND_FUNCDEF, + ONYX_AST_NODE_KIND_BLOCK, + ONYX_AST_NODE_KIND_SCOPE, + + ONYX_AST_NODE_KIND_ADD, + ONYX_AST_NODE_KIND_SUB, + ONYX_AST_NODE_KIND_MUL, + ONYX_AST_NODE_KIND_DIVIDE, + ONYX_AST_NODE_KIND_MODULUS, + ONYX_AST_NODE_KIND_NEGATE, + + ONYX_AST_NODE_KIND_TYPE, + ONYX_AST_NODE_KIND_LITERAL, + ONYX_AST_NODE_KIND_CAST, + ONYX_AST_NODE_KIND_PARAM, + ONYX_AST_NODE_KIND_CALL, + ONYX_AST_NODE_KIND_RETURN, + + ONYX_AST_NODE_KIND_EQUAL, + ONYX_AST_NODE_KIND_NOT_EQUAL, + ONYX_AST_NODE_KIND_GREATER, + ONYX_AST_NODE_KIND_GREATER_EQUAL, + ONYX_AST_NODE_KIND_LESS, + ONYX_AST_NODE_KIND_LESS_EQUAL, + ONYX_AST_NODE_KIND_NOT, + + ONYX_AST_NODE_KIND_IF, + ONYX_AST_NODE_KIND_LOOP, + + ONYX_AST_NODE_KIND_COUNT } OnyxAstNodeKind; typedef enum OnyxTypeInfoKind { @@ -79,11 +92,6 @@ typedef struct OnyxTypeInfo { extern OnyxTypeInfo builtin_types[]; -typedef union OnyxAstNode OnyxAstNode; -typedef struct OnyxAstNodeBlock OnyxAstNodeBlock; -typedef struct OnyxAstNodeParam OnyxAstNodeParam; -typedef struct OnyxAstNodeFuncDef OnyxAstNodeFuncDef; - typedef enum OnyxAstFlags { ONYX_AST_BLOCK_FLAG_HAS_RETURN = BH_BIT(1), ONYX_AST_BLOCK_FLAG_TOP_LEVEL = BH_BIT(2), @@ -103,9 +111,9 @@ struct OnyxAstNodeBlock { struct OnyxAstNodeParam { OnyxAstNodeKind kind; u32 flags; - OnyxToken *token; + OnyxToken *token; // Symbol name i.e. 'a', 'b' OnyxTypeInfo *type; - OnyxAstNode *next; + OnyxAstNodeParam *next; OnyxAstNode *left; OnyxAstNode *right; }; @@ -118,7 +126,6 @@ struct OnyxAstNodeFuncDef { OnyxAstNodeBlock *body; OnyxAstNodeParam *params; u64 param_count; // Same size as ptr - u64 unused1; }; union OnyxAstNode { @@ -128,15 +135,17 @@ union OnyxAstNode { OnyxAstNodeKind kind; u32 flags; OnyxToken *token; - OnyxTypeInfo* type; + OnyxTypeInfo *type; OnyxAstNode *next; OnyxAstNode *left; OnyxAstNode *right; - } as_node; + }; OnyxAstNodeBlock as_block; }; -ptr onyx_ast_node_new(bh_allocator alloc, OnyxAstNodeKind kind); -OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer tokenizer); -OnyxAstNode* onyx_parse(OnyxParser parser); \ No newline at end of file +OnyxAstNode* onyx_ast_node_new(bh_allocator alloc, OnyxAstNodeKind kind); +OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer, OnyxMessages* msgs); +OnyxAstNode* onyx_parse(OnyxParser *parser); + +#endif // #ifndef ONYXPARSER_H \ No newline at end of file diff --git a/progs/minimal.onyx b/progs/minimal.onyx index 7b9209ad..0d4bee4b 100644 --- a/progs/minimal.onyx +++ b/progs/minimal.onyx @@ -1,3 +1,3 @@ add :: proc(a i32, b i32) -> i32 { - return a + b -}; \ No newline at end of file + return a + b; +}; -- 2.25.1