From 9f90ff50cf4f93e6c6bb622bc2098dc7cea7f240 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 11 Jul 2016 00:10:15 +0100 Subject: [PATCH] Tags, enclosed proc results and better error handling --- build.bat | 1 - src/checker/checker.cpp | 64 +++++---- src/checker/expression.cpp | 160 +++++++++++++++-------- src/checker/statements.cpp | 45 +++++-- src/checker/type.cpp | 4 - src/main.cpp | 3 + src/parser.cpp | 258 +++++++++++++++++++++---------------- src/printer.cpp | 3 +- src/test.odin | 12 +- src/tokenizer.cpp | 11 -- 10 files changed, 326 insertions(+), 235 deletions(-) diff --git a/build.bat b/build.bat index 07eee7b82..015aa6026 100644 --- a/build.bat +++ b/build.bat @@ -43,7 +43,6 @@ set libs= kernel32.lib user32.lib gdi32.lib opengl32.lib ^ - set linker_flags= -incremental:no -opt:ref -subsystem:console rem Debug diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index db3c4ae16..87b370c4f 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -68,6 +68,7 @@ enum BuiltinProcedureId { BuiltinProcedure_len, BuiltinProcedure_cap, BuiltinProcedure_copy, + BuiltinProcedure_copy_bytes, BuiltinProcedure_print, BuiltinProcedure_println, @@ -91,6 +92,7 @@ gb_global BuiltinProcedure builtin_procedures[BuiltinProcedure_Count] = { {STR_LIT("len"), 1, false, Expression_Expression}, {STR_LIT("cap"), 1, false, Expression_Expression}, {STR_LIT("copy"), 2, false, Expression_Expression}, + {STR_LIT("copy_bytes"), 3, false, Expression_Statement}, {STR_LIT("print"), 1, true, Expression_Statement}, {STR_LIT("println"), 1, true, Expression_Statement}, }; @@ -112,7 +114,7 @@ struct Checker { Scope *curr_scope; gbArray(Type *) procedure_stack; - b32 in_defer; + b32 in_defer; // TODO(bill): Actually handle correctly #define MAX_CHECKER_ERROR_COUNT 10 isize error_prev_line; @@ -199,6 +201,14 @@ void add_global_entity(Entity *entity) { } } +void add_global_constant(gbAllocator a, String name, Type *type, Value value) { + Token token = {Token_Identifier}; + token.string = name; + Entity *entity = alloc_entity(a, Entity_Constant, NULL, token, type); + entity->constant.value = value; + add_global_entity(entity); +} + void init_global_scope(void) { // NOTE(bill): No need to free these gbAllocator a = gb_heap_allocator(); @@ -217,23 +227,9 @@ void init_global_scope(void) { } // Constants - Token true_token = {Token_Identifier}; - true_token.string = make_string("true"); - Entity *true_entity = alloc_entity(a, Entity_Constant, NULL, true_token, &basic_types[Basic_UntypedBool]); - true_entity->constant.value = make_value_bool(true); - add_global_entity(true_entity); - - Token false_token = {Token_Identifier}; - false_token.string = make_string("false"); - Entity *false_entity = alloc_entity(a, Entity_Constant, NULL, false_token, &basic_types[Basic_UntypedBool]); - false_entity->constant.value = make_value_bool(false); - add_global_entity(false_entity); - - Token null_token = {Token_Identifier}; - null_token.string = make_string("null"); - Entity *null_entity = alloc_entity(a, Entity_Constant, NULL, null_token, &basic_types[Basic_UntypedPointer]); - null_entity->constant.value = make_value_pointer(NULL); - add_global_entity(null_entity); + add_global_constant(a, make_string("true"), &basic_types[Basic_UntypedBool], make_value_bool(true)); + add_global_constant(a, make_string("false"), &basic_types[Basic_UntypedBool], make_value_bool(false)); + add_global_constant(a, make_string("null"), &basic_types[Basic_UntypedPointer], make_value_pointer(NULL)); // Builtin Procedures for (isize i = 0; i < gb_count_of(builtin_procedures); i++) { @@ -290,25 +286,25 @@ void destroy_checker(Checker *c) { #define print_checker_error(p, token, fmt, ...) print_checker_error_(p, __FUNCTION__, token, fmt, ##__VA_ARGS__) void print_checker_error_(Checker *c, char *function, Token token, char *fmt, ...) { - va_list va; + // NOTE(bill): Duplicate error, skip it - if (c->error_prev_line == token.line && c->error_prev_column == token.column) { - goto error; + if (!(c->error_prev_line == token.line && c->error_prev_column == token.column)) { + c->error_prev_line = token.line; + c->error_prev_column = token.column; + + #if 0 + gb_printf_err("%s()\n", function); + #endif + + va_list va; + va_start(va, fmt); + gb_printf_err("%s(%td:%td) %s\n", + c->parser->tokenizer.fullpath, token.line, token.column, + gb_bprintf_va(fmt, va)); + va_end(va); + } - c->error_prev_line = token.line; - c->error_prev_column = token.column; - -#if 0 - gb_printf_err("%s()\n", function); -#endif - va_start(va, fmt); - gb_printf_err("%s(%td:%td) %s\n", - c->parser->tokenizer.fullpath, token.line, token.column, - gb_bprintf_va(fmt, va)); - va_end(va); - -error: c->error_count++; // NOTE(bill): If there are too many errors, just quit if (c->error_count > MAX_CHECKER_ERROR_COUNT) { diff --git a/src/checker/expression.cpp b/src/checker/expression.cpp index cc098bf5b..072cec859 100644 --- a/src/checker/expression.cpp +++ b/src/checker/expression.cpp @@ -70,10 +70,14 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize fiel if (type_expression) { Type *type = check_type(c, type_expression); for (AstNode *name = field->field.name_list; name != NULL; name = name->next) { - GB_ASSERT(name->kind == AstNode_Identifier); - Entity *param = make_entity_param(c, scope, name->identifier.token, type); - add_entity(c, scope, name, param); - variables[variable_index++] = param; + if (name->kind == AstNode_Identifier) { + Entity *param = make_entity_param(c, scope, name->identifier.token, type); + add_entity(c, scope, name, param); + variables[variable_index++] = param; + } else { + print_checker_error(c, ast_node_token(name), + "Invalid parameter (invalid AST)"); + } } } } @@ -98,6 +102,10 @@ Type *check_get_results(Checker *c, Scope *scope, AstNode *list, isize list_coun Entity *param = make_entity_param(c, scope, token, type); // NOTE(bill): No need to record variables[variable_index++] = param; + + if (get_base_type(type)->kind == Type_Array) { + print_checker_error(c, token, "You cannot return an array from a procedure"); + } } tuple->tuple.variables = variables; tuple->tuple.variable_count = list_count; @@ -107,23 +115,12 @@ Type *check_get_results(Checker *c, Scope *scope, AstNode *list, isize list_coun void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) { - isize param_count = 0; - isize result_count = 0; + isize param_count = proc_type_node->procedure_type.param_count; + isize result_count = proc_type_node->procedure_type.result_count; - // NOTE(bill): Each field can store multiple items - for (AstNode *field = proc_type_node->procedure_type.param_list; - field != NULL; - field = field->next) { - param_count += field->field.name_list_count; - } + // gb_printf("%td -> %td\n", param_count, result_count); - for (AstNode *item = proc_type_node->procedure_type.results_list; - item != NULL; - item = item->next) { - result_count++; - } - - Type *params = check_get_params (c, c->curr_scope, proc_type_node->procedure_type.param_list, param_count); + Type *params = check_get_params(c, c->curr_scope, proc_type_node->procedure_type.param_list, param_count); Type *results = check_get_results(c, c->curr_scope, proc_type_node->procedure_type.results_list, result_count); type->procedure.scope = c->curr_scope; @@ -1227,6 +1224,52 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->mode = Addressing_Value; } break; + case BuiltinProcedure_copy_bytes: { + // copy_bytes :: proc(dest, source: rawptr, byte_count: int) + Type *dest_type = NULL, *src_type = NULL; + + Type *d = get_base_type(operand->type); + if (is_type_pointer(d)) + dest_type = d; + + Operand op = {}; + check_expression(c, &op, ce->arg_list->next); + if (op.mode == Addressing_Invalid) + return false; + Type *s = get_base_type(op.type); + if (is_type_pointer(s)) + src_type = s; + + if (dest_type == NULL || src_type == NULL) { + print_checker_error(c, ast_node_token(call), "`copy_bytes` only expects pointers for the destintation and source"); + return false; + } + + check_expression(c, &op, ce->arg_list->next->next); + if (op.mode == Addressing_Invalid) + return false; + + convert_to_typed(c, &op, &basic_types[Basic_int]); + if (op.mode == Addressing_Invalid || + op.type->kind != Type_Basic || + op.type->basic.kind != Basic_int) { + gbString str = type_to_string(op.type); + defer (gb_string_free(str)); + print_checker_error(c, ast_node_token(call), "`copy_bytes` 3rd argument must be of type `int`, a `%s` was given", str); + return false; + } + + if (op.mode == Addressing_Constant) { + if (value_to_integer(op.value).value_integer <= 0) { + print_checker_error(c, ast_node_token(call), "You cannot copy a zero or negative amount of bytes with `copy_bytes`"); + return false; + } + } + + operand->type = NULL; + operand->mode = Addressing_NoValue; + } break; + case BuiltinProcedure_print: case BuiltinProcedure_println: { @@ -1445,62 +1488,69 @@ void check_cast_expression(Checker *c, Operand *operand, Type *type) { -ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expression, Type *type_hint) { +ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *node, Type *type_hint) { ExpressionKind kind = Expression_Statement; operand->mode = Addressing_Invalid; operand->type = &basic_types[Basic_Invalid]; - switch (expression->kind) { + switch (node->kind) { case AstNode_BadExpression: goto error; case AstNode_Identifier: - check_identifier(c, operand, expression, type_hint); + check_identifier(c, operand, node, type_hint); break; case AstNode_BasicLiteral: { - BasicKind kind = Basic_Invalid; - Token lit = expression->basic_literal; + BasicKind basic_kind = Basic_Invalid; + Token lit = node->basic_literal; switch (lit.kind) { - case Token_Integer: kind = Basic_UntypedInteger; break; - case Token_Float: kind = Basic_UntypedFloat; break; - case Token_String: kind = Basic_UntypedString; break; - case Token_Rune: kind = Basic_UntypedRune; break; - default: GB_PANIC("Unknown literal"); break; + case Token_Integer: basic_kind = Basic_UntypedInteger; break; + case Token_Float: basic_kind = Basic_UntypedFloat; break; + case Token_String: basic_kind = Basic_UntypedString; break; + case Token_Rune: basic_kind = Basic_UntypedRune; break; + default: GB_PANIC("Unknown literal"); break; } operand->mode = Addressing_Constant; - operand->type = &basic_types[kind]; + operand->type = &basic_types[basic_kind]; operand->value = make_value_from_basic_literal(lit); } break; case AstNode_ParenExpression: - kind = check_expression_base(c, operand, expression->paren_expression.expression); - operand->expression = expression; + kind = check_expression_base(c, operand, node->paren_expression.expression, type_hint); + operand->expression = node; + break; + + case AstNode_TagExpression: + // TODO(bill): Tag expressions + print_checker_error(c, ast_node_token(node), "Tag expressions are not supported yet"); + kind = check_expression_base(c, operand, node->tag_expression.expression, type_hint); + operand->expression = node; break; case AstNode_UnaryExpression: - check_expression(c, operand, expression->unary_expression.operand); + check_expression(c, operand, node->unary_expression.operand); if (operand->mode == Addressing_Invalid) goto error; - check_unary_expression(c, operand, expression->unary_expression.op, expression); + check_unary_expression(c, operand, node->unary_expression.op, node); if (operand->mode == Addressing_Invalid) goto error; break; case AstNode_BinaryExpression: - check_binary_expression(c, operand, expression); + check_binary_expression(c, operand, node); if (operand->mode == Addressing_Invalid) goto error; break; case AstNode_SelectorExpression: - check_expression_base(c, operand, expression->selector_expression.operand); - check_selector(c, operand, expression); + check_expression_base(c, operand, node->selector_expression.operand); + check_selector(c, operand, node); break; case AstNode_IndexExpression: { - check_expression(c, operand, expression->index_expression.expression); + check_expression(c, operand, node->index_expression.expression); if (operand->mode == Addressing_Invalid) goto error; @@ -1548,7 +1598,7 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr goto error; } - if (expression->index_expression.value == NULL) { + if (node->index_expression.value == NULL) { gbString str = expression_to_string(operand->expression); print_checker_error(c, ast_node_token(operand->expression), "Missing index for `%s`", str); @@ -1556,12 +1606,12 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr goto error; } - check_index_value(c, expression->index_expression.value, max_count, NULL); + check_index_value(c, node->index_expression.value, max_count, NULL); } break; case AstNode_SliceExpression: { - auto *se = &expression->slice_expression; + auto *se = &node->slice_expression; check_expression(c, operand, se->expression); if (operand->mode == Addressing_Invalid) goto error; @@ -1584,8 +1634,8 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr valid = true; max_count = t->array.count; if (operand->mode != Addressing_Variable) { - gbString str = expression_to_string(expression); - print_checker_error(c, ast_node_token(expression), "Cannot slice array `%s`, value is not addressable", str); + gbString str = expression_to_string(node); + print_checker_error(c, ast_node_token(node), "Cannot slice array `%s`, value is not addressable", str); gb_string_free(str); goto error; } @@ -1645,18 +1695,18 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr } break; case AstNode_CastExpression: { - Type *cast_type = check_type(c, expression->cast_expression.type_expression); - check_expression_or_type(c, operand, expression->cast_expression.operand); + Type *cast_type = check_type(c, node->cast_expression.type_expression); + check_expression_or_type(c, operand, node->cast_expression.operand); if (operand->mode != Addressing_Invalid) check_cast_expression(c, operand, cast_type); } break; case AstNode_CallExpression: - return check_call_expression(c, operand, expression); + return check_call_expression(c, operand, node); case AstNode_DereferenceExpression: - check_expression_or_type(c, operand, expression->dereference_expression.operand); + check_expression_or_type(c, operand, node->dereference_expression.operand); if (operand->mode == Addressing_Invalid) { goto error; } else { @@ -1679,17 +1729,17 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr case AstNode_ArrayType: case AstNode_StructType: operand->mode = Addressing_Type; - operand->type = check_type(c, expression); + operand->type = check_type(c, node); break; } kind = Expression_Expression; - operand->expression = expression; + operand->expression = node; goto after_error; error: operand->mode = Addressing_Invalid; - operand->expression = expression; + operand->expression = node; goto after_error; after_error: @@ -1713,9 +1763,9 @@ after_error: if (type) { if (is_type_untyped(type)) { - add_untyped(c, expression, false, operand->mode, type, value); + add_untyped(c, node, false, operand->mode, type, value); } else { - add_type_and_value(c, expression, operand->mode, type, value); + add_type_and_value(c, node, operand->mode, type, value); } } return kind; @@ -1824,6 +1874,12 @@ gbString write_expression_to_string(gbString str, AstNode *node) { str = string_append_token(str, node->basic_literal); break; + case AstNode_TagExpression: + str = gb_string_appendc(str, "#"); + str = string_append_token(str, node->tag_expression.name); + str = write_expression_to_string(str, node->tag_expression.expression); + break; + case AstNode_UnaryExpression: str = string_append_token(str, node->unary_expression.op); str = write_expression_to_string(str, node->unary_expression.operand); diff --git a/src/checker/statements.cpp b/src/checker/statements.cpp index a7c72d7f0..a967ff742 100644 --- a/src/checker/statements.cpp +++ b/src/checker/statements.cpp @@ -385,6 +385,12 @@ void check_statement(Checker *c, AstNode *node) { } } break; + case AstNode_TagStatement: + // TODO(bill): Tag Statements + print_checker_error(c, ast_node_token(node), "Tag statements are not supported yet"); + check_statement(c, node->tag_statement.statement); + break; + case AstNode_IncDecStatement: { Token op = {}; auto *s = &node->inc_dec_statement; @@ -653,7 +659,7 @@ void check_statement(Checker *c, AstNode *node) { case AstNode_ProcedureDeclaration: { auto *pd = &node->procedure_declaration; - GB_ASSERT_MSG(pd->kind == Declaration_Immutable, "Mutable/temp procedures are not yet implemented"); + GB_ASSERT_MSG(pd->kind == Declaration_Immutable, "Mutable/temp/anonymous procedures are not yet implemented"); Entity *e = make_entity_procedure(c, c->curr_scope, pd->name->identifier.token, NULL); add_entity(c, c->curr_scope, pd->name, e); @@ -667,9 +673,38 @@ void check_statement(Checker *c, AstNode *node) { check_open_scope(c, pd->procedure_type); { check_procedure_type(c, proc_type, pd->procedure_type); + b32 is_foreign = false; + b32 is_inline = false; + b32 is_no_inline = false; + for (AstNode *tag = pd->tag_list; tag != NULL; tag = tag->next) { + GB_ASSERT(tag->kind == AstNode_TagExpression); + + String tag_name = tag->tag_expression.name.string; + if (are_strings_equal(tag_name, make_string("foreign"))) { + is_foreign = true; + } else if (are_strings_equal(tag_name, make_string("inline"))) { + is_inline = true; + } else if (are_strings_equal(tag_name, make_string("no_inline"))) { + is_no_inline = true; + } else { + print_checker_error(c, ast_node_token(tag), "Unknown procedure tag"); + } + // TODO(bill): Other tags + } + + if (is_inline && is_no_inline) { + print_checker_error(c, ast_node_token(pd->tag_list), + "You cannot apply both `inline` and `no_inline` to a procedure"); + } + if (pd->body) { GB_ASSERT(pd->body->kind == AstNode_BlockStatement); + if (is_foreign) { + print_checker_error(c, ast_node_token(pd->body), + "A procedure tagged as `#foreign` cannot have a body"); + } + push_procedure(c, proc_type); check_statement_list(c, pd->body->block_statement.list); if (pd->procedure_type->procedure_type.result_count > 0) { @@ -678,14 +713,6 @@ void check_statement(Checker *c, AstNode *node) { } } pop_procedure(c); - } else if (pd->tag) { - GB_ASSERT(pd->tag->kind == AstNode_TagExpression); - - String tag_name = pd->tag->tag_expression.name.string; - if (are_strings_equal(tag_name, make_string("foreign"))) { - // NOTE(bill): Foreign procedure (linking stage) - } - // TODO(bill): Other tags } } check_close_scope(c); diff --git a/src/checker/type.cpp b/src/checker/type.cpp index ca658c2e4..b7be9a3db 100644 --- a/src/checker/type.cpp +++ b/src/checker/type.cpp @@ -570,10 +570,6 @@ gbString write_type_to_string(gbString str, Type *type) { GB_ASSERT(var->kind == Entity_Variable); if (i > 0) str = gb_string_appendc(str, ", "); - if (var->token.string.len > 0) { - str = gb_string_append_length(str, var->token.string.text, var->token.string.len); - str = gb_string_appendc(str, ": "); - } str = write_type_to_string(str, var->type); } } diff --git a/src/main.cpp b/src/main.cpp index 55bb389ab..66e93fdfd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,7 @@ #include "checker.cpp" #include "generator.cpp" + int main(int argc, char **argv) { if (argc < 2) { gb_printf_err("Please specify a .odin file\n"); @@ -29,11 +30,13 @@ int main(int argc, char **argv) { check_statement_list(&checker, root_node); +#if 0 Generator generator = {}; if (init_generator(&generator, &checker)) { defer (destroy_generator(&generator)); generate_code(&generator, root_node); } +#endif } } diff --git a/src/parser.cpp b/src/parser.cpp index bd054cb98..d9f4928f1 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -55,6 +55,7 @@ AstNode__ExpressionEnd, AstNode__StatementBegin, AstNode_BadStatement, // NOTE(bill): Naughty statement AstNode_EmptyStatement, + AstNode_TagStatement, AstNode_ExpressionStatement, AstNode_IncDecStatement, AstNode_AssignStatement, @@ -111,6 +112,7 @@ struct AstNode { struct { Token token; Token name; + AstNode *expression; } tag_expression; struct { Token begin, end; } bad_expression; @@ -137,6 +139,11 @@ struct AstNode { struct { Token token; } empty_statement; struct { AstNode *expression; } expression_statement; struct { Token op; AstNode *expression; } inc_dec_statement; + struct { + Token token; + Token name; + AstNode *statement; + } tag_statement; struct { Token op; AstNode *lhs_list, *rhs_list; @@ -182,7 +189,7 @@ struct AstNode { } field; struct { Token token; - AstNode *param_list; // AstNode_Field + AstNode *param_list; // AstNode_Field list isize param_count; AstNode *results_list; // type expression list isize result_count; @@ -192,9 +199,8 @@ struct AstNode { AstNode *name; // AstNode_Identifier AstNode *procedure_type; // AstNode_ProcedureType AstNode *body; // AstNode_BlockStatement - AstNode *tag; // AstNode_TagExpression - // TODO(bill): Allow for multiple tags - // TODO(bill): Modifiers: inline, no_inline, etc. + AstNode *tag_list; // AstNode_TagExpression + isize tag_count; } procedure_declaration; struct { Token token; @@ -252,6 +258,8 @@ Token ast_node_token(AstNode *node) { return node->basic_literal; case AstNode_Identifier: return node->identifier.token; + case AstNode_TagExpression: + return node->tag_expression.token; case AstNode_BadExpression: return node->bad_expression.begin; case AstNode_UnaryExpression: @@ -278,6 +286,8 @@ Token ast_node_token(AstNode *node) { return node->empty_statement.token; case AstNode_ExpressionStatement: return ast_node_token(node->expression_statement.expression); + case AstNode_TagStatement: + return node->tag_statement.token; case AstNode_IncDecStatement: return node->inc_dec_statement.op; case AstNode_AssignStatement: @@ -366,8 +376,36 @@ AstEntity *ast_scope_insert(AstScope *scope, AstEntity entity) { return prev; } -// NOTE(bill): And this below is why is need a new language! Discriminated unions are a pain in C/C++ + +#define print_parse_error(p, token, fmt, ...) print_parse_error_(p, __FUNCTION__, token, fmt, ##__VA_ARGS__) +void print_parse_error_(Parser *p, char *function, Token token, char *fmt, ...) { + + // NOTE(bill): Duplicate error, skip it + if (p->error_prev_line != token.line || p->error_prev_column != token.column) { + va_list va; + + p->error_prev_line = token.line; + p->error_prev_column = token.column; + + #if 1 + gb_printf_err("%s()\n", function); + #endif + va_start(va, fmt); + gb_printf_err("%s(%td:%td) Syntax error: %s\n", + p->tokenizer.fullpath, token.line, token.column, + gb_bprintf_va(fmt, va)); + va_end(va); + } + p->error_count++; +} + + +// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++ gb_inline AstNode *make_node(Parser *p, AstNodeKind kind) { + if (gb_arena_size_remaining(&p->arena, GB_DEFAULT_MEMORY_ALIGNMENT) < gb_size_of(AstNode)) { + // NOTE(bill): If a syntax error is so bad, just quit! + gb_exit(1); + } AstNode *node = gb_alloc_item(gb_arena_allocator(&p->arena), AstNode); node->kind = kind; return node; @@ -380,10 +418,19 @@ gb_inline AstNode *make_bad_expression(Parser *p, Token begin, Token end) { return result; } -gb_inline AstNode *make_tag_expression(Parser *p, Token token, Token name) { +gb_inline AstNode *make_tag_expression(Parser *p, Token token, Token name, AstNode *expression) { AstNode *result = make_node(p, AstNode_TagExpression); result->tag_expression.token = token; result->tag_expression.name = name; + result->tag_expression.expression = expression; + return result; +} + +gb_inline AstNode *make_tag_statement(Parser *p, Token token, Token name, AstNode *statement) { + AstNode *result = make_node(p, AstNode_TagStatement); + result->tag_statement.token = token; + result->tag_statement.name = name; + result->tag_statement.statement = statement; return result; } @@ -593,13 +640,14 @@ gb_inline AstNode *make_procedure_type(Parser *p, Token token, AstNode *param_li return result; } -gb_inline AstNode *make_procedure_declaration(Parser *p, DeclarationKind kind, AstNode *name, AstNode *procedure_type, AstNode *body, AstNode *tag) { +gb_inline AstNode *make_procedure_declaration(Parser *p, DeclarationKind kind, AstNode *name, AstNode *procedure_type, AstNode *body, AstNode *tag_list, isize tag_count) { AstNode *result = make_node(p, AstNode_ProcedureDeclaration); result->procedure_declaration.kind = kind; result->procedure_declaration.name = name; result->procedure_declaration.procedure_type = procedure_type; result->procedure_declaration.body = body; - result->procedure_declaration.tag = tag; + result->procedure_declaration.tag_list = tag_list; + result->procedure_declaration.tag_count = tag_count; return result; } @@ -635,28 +683,6 @@ gb_inline AstNode *make_type_declaration(Parser *p, Token token, AstNode *name, } -#define print_parse_error(p, token, fmt, ...) print_parse_error_(p, __FUNCTION__, token, fmt, ##__VA_ARGS__) -void print_parse_error_(Parser *p, char *function, Token token, char *fmt, ...) { - - // NOTE(bill): Duplicate error, skip it - if (p->error_prev_line != token.line || p->error_prev_column != token.column) { - va_list va; - - p->error_prev_line = token.line; - p->error_prev_column = token.column; - - #if 0 - gb_printf_err("%s()\n", function); - #endif - va_start(va, fmt); - gb_printf_err("%s(%td:%td) %s\n", - p->tokenizer.fullpath, token.line, token.column, - gb_bprintf_va(fmt, va)); - va_end(va); - } - p->error_count++; -} - gb_inline b32 next_token(Parser *p) { if (p->cursor+1 < p->tokens + gb_array_count(p->tokens)) { @@ -724,7 +750,7 @@ b32 init_parser(Parser *p, char *filename) { // NOTE(bill): Is this big enough or too small? isize arena_size = gb_max(gb_size_of(AstNode), gb_size_of(AstScope)); - arena_size *= 1.25*gb_array_count(p->tokens); + arena_size *= 2*gb_array_count(p->tokens); gb_arena_init_from_allocator(&p->arena, gb_heap_allocator(), arena_size); open_ast_scope(p); @@ -746,7 +772,10 @@ void destroy_parser(Parser *p) { gb_internal void add_ast_entity(Parser *p, AstScope *scope, AstNode *declaration, AstNode *name_list) { for (AstNode *n = name_list; n != NULL; n = n->next) { - GB_ASSERT_MSG(n->kind == AstNode_Identifier, "Identifier is already declared or resolved"); + if (n->kind != AstNode_Identifier) { + print_parse_error(p, ast_node_token(declaration), "Identifier is already declared or resolved"); + continue; + } AstEntity *entity = make_ast_entity(p, n->identifier.token, declaration, scope); n->identifier.entity = entity; @@ -765,8 +794,26 @@ gb_internal void add_ast_entity(Parser *p, AstScope *scope, AstNode *declaration AstNode *parse_expression(Parser *p, b32 lhs); AstNode *parse_identifier(Parser *p) { - Token identifier = expect_token(p, Token_Identifier); - return make_identifier(p, identifier); + Token token = p->cursor[0]; + if (token.kind == Token_Identifier) { + next_token(p); + } else { + token.string = make_string("_"); + expect_token(p, Token_Identifier); + } + return make_identifier(p, token); +} + +AstNode *parse_tag_expression(Parser *p, AstNode *expression) { + Token token = expect_token(p, Token_Hash); + Token name = expect_token(p, Token_Identifier); + return make_tag_expression(p, token, name, expression); +} + +AstNode *parse_tag_statement(Parser *p, AstNode *statement) { + Token token = expect_token(p, Token_Hash); + Token name = expect_token(p, Token_Identifier); + return make_tag_statement(p, token, name, statement); } AstNode *unparen_expression(AstNode *node) { @@ -803,6 +850,11 @@ AstNode *parse_atom_expression(Parser *p, b32 lhs) { close = expect_token(p, Token_CloseParen); operand = make_paren_expression(p, operand, open, close); } break; + + case Token_Hash: { + operand = parse_tag_expression(p, NULL); + operand->tag_expression.expression = parse_expression(p, false); + } break; } b32 loop = true; @@ -852,10 +904,9 @@ AstNode *parse_atom_expression(Parser *p, b32 lhs) { operand = make_selector_expression(p, token, operand, parse_identifier(p)); break; default: { - Token token = p->cursor[0]; - print_parse_error(p, token, "Expected a selector"); + print_parse_error(p, p->cursor[0], "Expected a selector"); next_token(p); - operand = make_selector_expression(p, token, operand, NULL); + operand = make_selector_expression(p, p->cursor[0], operand, NULL); } break; } } break; @@ -1153,50 +1204,21 @@ AstNode *parse_field_declaration(Parser *p, AstScope *scope) { return field; } +Token parse_procedure_signature(Parser *p, AstScope *scope, + AstNode **param_list, isize *param_count, + AstNode **result_list, isize *result_count); + AstNode *parse_procedure_type(Parser *p, AstScope **scope_) { - Token token = expect_token(p, Token_proc); AstScope *scope = make_ast_scope(p, p->file_scope); // Procedure's scope AstNode *params = NULL; AstNode *results = NULL; isize param_count = 0; isize result_count = 0; - expect_token(p, Token_OpenParen); - if (p->cursor[0].kind != Token_CloseParen) { - // IMPORTANT TODO(bill): Allow for lhs-expression list style types - // proc(x, y: int, a, b: f32); - AstNode *params_curr = NULL; - do { - AstNode *type_node = parse_type(p); - DLIST_APPEND(params, params_curr, type_node); - param_count++; - if (p->cursor[0].kind != Token_Comma || - p->cursor[0].kind == Token_EOF) - break; - next_token(p); - } while (true); - } - expect_token(p, Token_CloseParen); - - // NOTE(bill): Has results - if (allow_token(p, Token_ArrowRight)) { - if (p->cursor[0].kind != Token_Semicolon) { - AstNode *results_curr = NULL; - do { - DLIST_APPEND(results, results_curr, parse_type(p)); - result_count++; - if (p->cursor[0].kind != Token_Comma || - p->cursor[0].kind == Token_EOF) - break; - next_token(p); - } while (true); - } else { - print_parse_error(p, p->cursor[0], "Expected at least one type after the `->`"); - } - } + Token proc_token = parse_procedure_signature(p, scope, ¶ms, ¶m_count, &results, &result_count); if (scope_) *scope_ = scope; - return make_procedure_type(p, token, params, param_count, results, result_count); + return make_procedure_type(p, proc_token, params, param_count, results, result_count); } @@ -1260,22 +1282,23 @@ AstNode *parse_identifier_or_type(Parser *p) { break; default: - print_parse_error(p, p->cursor[0], "Expected type after type separator `:`"); + print_parse_error(p, p->cursor[0], + "Expected a type after `%.*s`, got `%.*s`", LIT(p->cursor[-1].string), LIT(p->cursor[0].string)); break; } return NULL; } -// TODO(bill): Probably unify `parse_parameters` and `parse_results` AstNode *parse_parameters(Parser *p, AstScope *scope, isize *param_count_) { AstNode *param_list = NULL; AstNode *param_list_curr = NULL; isize param_count = 0; expect_token(p, Token_OpenParen); while (p->cursor[0].kind != Token_CloseParen) { - DLIST_APPEND(param_list, param_list_curr, parse_field_declaration(p, scope)); - param_count++; + AstNode *field = parse_field_declaration(p, scope); + DLIST_APPEND(param_list, param_list_curr, field); + param_count += field->field.name_list_count; if (p->cursor[0].kind != Token_Comma) break; next_token(p); @@ -1286,34 +1309,42 @@ AstNode *parse_parameters(Parser *p, AstScope *scope, isize *param_count_) { return param_list; } -AstNode *parse_results(Parser *p, AstScope *scope, isize *result_count_) { - AstNode *result_list = NULL; - AstNode *result_list_curr = NULL; - isize result_count = 0; - +AstNode *parse_results(Parser *p, AstScope *scope, isize *result_count) { if (allow_token(p, Token_ArrowRight)) { - while (p->cursor[0].kind != Token_OpenBrace && - p->cursor[0].kind != Token_Semicolon) { - DLIST_APPEND(result_list, result_list_curr, parse_type(p)); - result_count++; - if (p->cursor[0].kind != Token_Comma) - break; - next_token(p); + if (p->cursor[0].kind == Token_OpenParen) { + expect_token(p, Token_OpenParen); + AstNode *list = NULL; + AstNode *list_curr = NULL; + isize count = 0; + while (p->cursor[0].kind != Token_CloseParen && + p->cursor[0].kind != Token_EOF) { + DLIST_APPEND(list, list_curr, parse_type(p)); + count++; + if (p->cursor[0].kind != Token_Comma) + break; + next_token(p); + } + expect_token(p, Token_CloseParen); + + if (result_count) *result_count = count; + return list; } - if (result_count == 0) - print_parse_error(p, p->cursor[0], "Expected return types after `->`"); + AstNode *field = make_field(p, NULL, 0, parse_type(p)); + if (result_count) *result_count = 1; + return field; } - - if (result_count_) *result_count_ = result_count; - return result_list; + if (result_count) *result_count = 0; + return NULL; } -void parse_procedure_signature(Parser *p, AstScope *scope, +Token parse_procedure_signature(Parser *p, AstScope *scope, AstNode **param_list, isize *param_count, AstNode **result_list, isize *result_count) { + Token proc_token = expect_token(p, Token_proc); *param_list = parse_parameters(p, scope, param_count); *result_list = parse_results(p, scope, result_count); + return proc_token; } AstNode *parse_body(Parser *p, AstScope *scope) { @@ -1327,13 +1358,6 @@ AstNode *parse_body(Parser *p, AstScope *scope) { return make_block_statement(p, statement_list, statement_list_count, open, close); } - -AstNode *parse_tag_expression(Parser *p) { - Token token = expect_token(p, Token_Hash); - Token name = expect_token(p, Token_Identifier); - return make_tag_expression(p, token, name); -} - AstNode *parse_procedure_declaration(Parser *p, Token proc_token, AstNode *name, DeclarationKind kind) { AstNode *param_list = NULL; AstNode *result_list = NULL; @@ -1344,17 +1368,22 @@ AstNode *parse_procedure_declaration(Parser *p, Token proc_token, AstNode *name, parse_procedure_signature(p, scope, ¶m_list, ¶m_count, &result_list, &result_count); - AstNode *body = NULL, *tag = NULL; + AstNode *body = NULL; + AstNode *tag_list = NULL; + AstNode *tag_list_curr = NULL; + isize tag_count = 0; + while (p->cursor[0].kind == Token_Hash) { + DLIST_APPEND(tag_list, tag_list_curr, parse_tag_expression(p, NULL)); + tag_count++; + } if (p->cursor[0].kind == Token_OpenBrace) { body = parse_body(p, scope); - } else if (p->cursor[0].kind == Token_Hash) { - tag = parse_tag_expression(p); } close_ast_scope(p); AstNode *proc_type = make_procedure_type(p, proc_token, param_list, param_count, result_list, result_count); - return make_procedure_declaration(p, kind, name, proc_type, body, tag); + return make_procedure_declaration(p, kind, name, proc_type, body, tag_list, tag_count); } AstNode *parse_declaration(Parser *p, AstNode *name_list, isize name_list_count) { @@ -1379,16 +1408,15 @@ AstNode *parse_declaration(Parser *p, AstNode *name_list, isize name_list_count) Token proc_token = p->cursor[0]; AstNode *name = name_list; if (name_list_count != 1) { - print_parse_error(p, p->cursor[0], "You can only declare one procedure at a time (at the moment)"); - return make_bad_declaration(p, name->identifier.token, p->cursor[0]); + print_parse_error(p, proc_token, "You can only declare one procedure at a time (at the moment)"); + return make_bad_declaration(p, name->identifier.token, proc_token); } // TODO(bill): Allow for mutable procedures if (declaration_kind != Declaration_Immutable) { - print_parse_error(p, p->cursor[0], "Only immutable procedures are supported (at the moment)"); - return make_bad_declaration(p, name->identifier.token, p->cursor[0]); + print_parse_error(p, proc_token, "Only immutable procedures are supported (at the moment)"); + return make_bad_declaration(p, name->identifier.token, proc_token); } - next_token(p); // Skip `proc` token AstNode *procedure_declaration = parse_procedure_declaration(p, proc_token, name, declaration_kind); add_ast_entity(p, p->curr_scope, procedure_declaration, name_list); @@ -1549,7 +1577,8 @@ AstNode *parse_type_declaration(Parser *p) { AstNode *type_declaration = make_type_declaration(p, token, name, type_expression); - if (type_expression->kind != AstNode_StructType) + if (type_expression->kind != AstNode_StructType && + type_expression->kind != AstNode_ProcedureType) expect_token(p, Token_Semicolon); return type_declaration; @@ -1588,8 +1617,12 @@ AstNode *parse_statement(Parser *p) { // case Token_match: // case Token_case: + case Token_Hash: + s = parse_tag_statement(p, NULL); + s->tag_statement.statement = parse_statement(p); // TODO(bill): Find out why this doesn't work as an argument + return s; + case Token_OpenBrace: return parse_block_statement(p); - // case Token_CloseBrace: s = make_empty_statement(p, token); break; case Token_Semicolon: s = make_empty_statement(p, token); @@ -1607,7 +1640,6 @@ AstNode *parse_statement_list(Parser *p, isize *list_count_) { isize list_count = 0; while (p->cursor[0].kind != Token_case && - p->cursor[0].kind != Token_default && p->cursor[0].kind != Token_CloseBrace && p->cursor[0].kind != Token_EOF) { DLIST_APPEND(list_root, list_curr, parse_statement(p)); diff --git a/src/printer.cpp b/src/printer.cpp index 26b9e0c2c..f34dd4559 100644 --- a/src/printer.cpp +++ b/src/printer.cpp @@ -24,6 +24,7 @@ void print_ast(AstNode *node, isize indent) { gb_printf("(tag)\n"); print_indent(indent+1); print_token(node->tag_expression.name); + print_ast(node->tag_expression.expression, indent+1); break; case AstNode_UnaryExpression: @@ -137,7 +138,7 @@ void print_ast(AstNode *node, isize indent) { gb_printf("(decl:proc,immutable)\n"); print_ast(node->procedure_declaration.procedure_type, indent+1); print_ast(node->procedure_declaration.body, indent+1); - print_ast(node->procedure_declaration.tag, indent+1); + print_ast(node->procedure_declaration.tag_list, indent+1); break; case AstNode_TypeDeclaration: diff --git a/src/test.odin b/src/test.odin index 004fcc537..089da3f1e 100644 --- a/src/test.odin +++ b/src/test.odin @@ -1,15 +1,7 @@ -type Vec2: struct { - x, y: f32; -} - -print_string_array :: proc(args: []string) { - args[0] = ""; -} - main :: proc() { - x := 0; + x : u8 = 0; - thing :: proc(n: int) -> int, f32 { + thing :: proc(n: int) -> (int, f32) { return n*n, 13.37; } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 78a2c16fe..c3e952814 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -109,7 +109,6 @@ Token__KeywordBegin, Token_continue, Token_fallthrough, Token_case, - Token_default, Token_if, Token_else, @@ -122,9 +121,6 @@ Token__KeywordBegin, Token_struct, Token_union, Token_enum, - - Token_inline, - Token_no_inline, Token__KeywordEnd, Token_Count, @@ -200,7 +196,6 @@ char const *TOKEN_STRINGS[] = { "continue", "fallthrough", "case", - "default", "if", "else", "for", @@ -211,9 +206,6 @@ char const *TOKEN_STRINGS[] = { "struct", "union", "enum", - "inline", - "no_inline", - "import", "_KeywordEnd", }; @@ -621,7 +613,6 @@ Token tokenizer_get_token(Tokenizer *t) { KWT("continue", Token_continue); KWT("fallthrough", Token_fallthrough); KWT("case", Token_case); - KWT("default", Token_default); KWT("if", Token_if); KWT("else", Token_else); KWT("for", Token_for); @@ -632,8 +623,6 @@ Token tokenizer_get_token(Tokenizer *t) { KWT("struct", Token_struct); KWT("union", Token_union); KWT("enum", Token_enum); - KWT("inline", Token_inline); - KWT("no_inline", Token_no_inline); KWE #undef KWB