diff --git a/.gitignore b/.gitignore index dd063b293..12920b056 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,256 @@ +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + + +# Project Specific bin/ misc/ *.sln -*.suo diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..6e53c9cb2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2016, Ginger Bill +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 000000000..c553d2296 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# The Odin Programming Language + +Odin in an open source programming language that is simple to read, comprehend and reason with. + +## Warnings + +* This is still highly in development and the language's design is quite volatile. +* Syntax is not fixed. +* The language's name, Odin, is not final. + +## Goals + +* Simple to read and write +* Easy to comprehend and reason with +* Fast +* Low-level +* Compiled, strongly-typed, static language +* Metaprogramming: + - Ability to run any code at compile time + - Unified syntax between main language and metaprogramming language + - Built-in introspection for all types +* Higher control of data layout and data access +* No need for external tools such as Make/IDE + diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index 4136be912..db3c4ae16 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -17,7 +17,7 @@ struct Operand { Value value; AstNode *expression; - i32 builtin_id; + BuiltinProcedureId builtin_id; }; struct TypeAndValue { @@ -44,8 +44,9 @@ ExpressionInfo make_expression_info(b32 is_lhs, AddressingMode mode, Type *type, struct Scope { Scope *parent; - gbArray(Scope *) children; // TODO(bill): Remove and make into a linked list - Map elements; // Key: String + Scope *prev, *next; + Scope *first_child, *last_child; + Map elements; // Key: String }; enum ExpressionKind { @@ -54,13 +55,6 @@ enum ExpressionKind { Expression_Statement, }; -struct BuiltinProcedure { - String name; - isize arg_count; - b32 variadic; - ExpressionKind kind; -}; - enum BuiltinProcedureId { BuiltinProcedure_Invalid, @@ -79,32 +73,12 @@ enum BuiltinProcedureId { BuiltinProcedure_Count, }; - -struct Checker { - Parser * parser; - Map types; // Key: AstNode * | Expression -> Type (and value) - Map definitions; // Key: AstNode * | Identifier -> Entity - Map uses; // Key: AstNode * | Identifier -> Entity (Anonymous field) - Map scopes; // Key: AstNode * | Node -> Scope - Map untyped; // Key: AstNode * | Expression -> ExpressionInfo - BaseTypeSizes sizes; - Scope * file_scope; - - gbArena entity_arena; - - Scope *curr_scope; - gbArray(Type *) procedure_stack; - b32 in_defer; - -#define MAX_CHECKER_ERROR_COUNT 10 - isize error_prev_line; - isize error_prev_column; - isize error_count; +struct BuiltinProcedure { + String name; + isize arg_count; + b32 variadic; + ExpressionKind kind; }; - - -gb_global Scope *global_scope = NULL; - gb_global BuiltinProcedure builtin_procedures[BuiltinProcedure_Count] = { {STR_LIT(""), 0, false, Expression_Statement}, {STR_LIT("size_of"), 1, false, Expression_Expression}, @@ -122,25 +96,50 @@ gb_global BuiltinProcedure builtin_procedures[BuiltinProcedure_Count] = { }; -// TODO(bill): Arena allocation -Scope *make_scope(Scope *parent) { - gbAllocator a = gb_heap_allocator(); - Scope *s = gb_alloc_item(a, Scope); + +struct Checker { + Parser * parser; + Map types; // Key: AstNode * | Expression -> Type (and value) + Map definitions; // Key: AstNode * | Identifier -> Entity + Map uses; // Key: AstNode * | Identifier -> Entity + Map scopes; // Key: AstNode * | Node -> Scope + Map untyped; // Key: AstNode * | Expression -> ExpressionInfo + BaseTypeSizes sizes; + Scope * file_scope; + + gbArena arena; + gbAllocator allocator; + + Scope *curr_scope; + gbArray(Type *) procedure_stack; + b32 in_defer; + +#define MAX_CHECKER_ERROR_COUNT 10 + isize error_prev_line; + isize error_prev_column; + isize error_count; +}; + + +gb_global Scope *global_scope = NULL; + + +Scope *make_scope(Scope *parent, gbAllocator allocator) { + Scope *s = gb_alloc_item(allocator, Scope); s->parent = parent; - gb_array_init(s->children, a); - map_init(&s->elements, a); - if (parent != NULL && parent != global_scope) - gb_array_append(parent->children, s); + map_init(&s->elements, gb_heap_allocator()); + if (parent != NULL && parent != global_scope) { + DLIST_APPEND(parent->first_child, parent->last_child, s); + } return s; } void destroy_scope(Scope *scope) { - for (isize i = 0; i < gb_array_count(scope->children); i++) { - destroy_scope(scope->children[i]); + for (Scope *child = scope->first_child; child != NULL; child = child->next) { + destroy_scope(child); } map_destroy(&scope->elements); - gb_array_free(scope->children); - gb_free(gb_heap_allocator(), scope); + // NOTE(bill): No need to free scope as it "should" be allocated in an arena (except for the global scope) } @@ -164,7 +163,7 @@ Entity *scope_lookup_entity(Scope *s, String name) { return entity; } -Entity *scope_lookup_entity_current(Scope *s, String name) { +Entity *current_scope_lookup_entity(Scope *s, String name) { u64 key = hash_string(name); Entity **found = map_get(&s->elements, key); if (found) @@ -201,8 +200,9 @@ void add_global_entity(Entity *entity) { } void init_global_scope(void) { - global_scope = make_scope(NULL); + // NOTE(bill): No need to free these gbAllocator a = gb_heap_allocator(); + global_scope = make_scope(NULL, a); // Types for (isize i = 0; i < gb_count_of(basic_types); i++) { @@ -237,7 +237,7 @@ void init_global_scope(void) { // Builtin Procedures for (isize i = 0; i < gb_count_of(builtin_procedures); i++) { - i32 id = cast(i32)i; + BuiltinProcedureId id = cast(BuiltinProcedureId)i; Token token = {Token_Identifier}; token.string = builtin_procedures[i].name; Entity *entity = alloc_entity(a, Entity_Builtin, NULL, token, &basic_types[Basic_Invalid]); @@ -265,14 +265,16 @@ void init_checker(Checker *c, Parser *parser) { map_init(&c->untyped, a); - c->file_scope = make_scope(global_scope); - c->curr_scope = c->file_scope; - gb_array_init(c->procedure_stack, a); // NOTE(bill): Is this big enough or too small? - isize entity_arena_size = 2 * gb_size_of(Entity) * gb_array_count(c->parser->tokens); - gb_arena_init_from_allocator(&c->entity_arena, a, entity_arena_size); + isize item_size = gb_max(gb_max(gb_size_of(Entity), gb_size_of(Type)), gb_size_of(Scope)); + isize arena_size = 2 * item_size * gb_array_count(c->parser->tokens); + gb_arena_init_from_allocator(&c->arena, a, arena_size); + c->allocator = gb_arena_allocator(&c->arena); + + c->file_scope = make_scope(global_scope, c->allocator); + c->curr_scope = c->file_scope; } void destroy_checker(Checker *c) { @@ -283,7 +285,7 @@ void destroy_checker(Checker *c) { map_destroy(&c->untyped); destroy_scope(c->file_scope); gb_array_free(c->procedure_stack); - gb_arena_free(&c->entity_arena); + gb_arena_free(&c->arena); } #define print_checker_error(p, token, fmt, ...) print_checker_error_(p, __FUNCTION__, token, fmt, ##__VA_ARGS__) @@ -397,7 +399,7 @@ void add_scope(Checker *c, AstNode *node, Scope *scope) { void check_open_scope(Checker *c, AstNode *statement) { - Scope *scope = make_scope(c->curr_scope); + Scope *scope = make_scope(c->curr_scope, c->allocator); add_scope(c, statement, scope); c->curr_scope = scope; } @@ -418,40 +420,40 @@ void pop_procedure(Checker *c) { Entity *make_entity_variable(Checker *c, Scope *parent, Token token, Type *type) { - Entity *entity = alloc_entity(gb_arena_allocator(&c->entity_arena), Entity_Variable, parent, token, type); + Entity *entity = alloc_entity(c->allocator, Entity_Variable, parent, token, type); return entity; } Entity *make_entity_constant(Checker *c, Scope *parent, Token token, Type *type, Value value) { - Entity *entity = alloc_entity(gb_arena_allocator(&c->entity_arena), Entity_Constant, parent, token, type); + Entity *entity = alloc_entity(c->allocator, Entity_Constant, parent, token, type); entity->constant.value = value; return entity; } Entity *make_entity_type_name(Checker *c, Scope *parent, Token token, Type *type) { - Entity *entity = alloc_entity(gb_arena_allocator(&c->entity_arena), Entity_TypeName, parent, token, type); + Entity *entity = alloc_entity(c->allocator, Entity_TypeName, parent, token, type); return entity; } Entity *make_entity_param(Checker *c, Scope *parent, Token token, Type *type) { - Entity *entity = alloc_entity(gb_arena_allocator(&c->entity_arena), Entity_Variable, parent, token, type); + Entity *entity = alloc_entity(c->allocator, Entity_Variable, parent, token, type); entity->variable.used = true; return entity; } Entity *make_entity_field(Checker *c, Scope *parent, Token token, Type *type) { - Entity *entity = alloc_entity(gb_arena_allocator(&c->entity_arena), Entity_Variable, parent, token, type); + Entity *entity = alloc_entity(c->allocator, Entity_Variable, parent, token, type); entity->variable.is_field = true; return entity; } Entity *make_entity_procedure(Checker *c, Scope *parent, Token token, Type *signature_type) { - Entity *entity = alloc_entity(gb_arena_allocator(&c->entity_arena), Entity_Procedure, parent, token, signature_type); + Entity *entity = alloc_entity(c->allocator, Entity_Procedure, parent, token, signature_type); return entity; } -Entity *make_entity_builtin(Checker *c, Scope *parent, Token token, Type *type, i32 id) { - Entity *entity = alloc_entity(gb_arena_allocator(&c->entity_arena), Entity_Builtin, parent, token, type); +Entity *make_entity_builtin(Checker *c, Scope *parent, Token token, Type *type, BuiltinProcedureId id) { + Entity *entity = alloc_entity(c->allocator, Entity_Builtin, parent, token, type); entity->builtin.id = id; return entity; } diff --git a/src/checker/entity.cpp b/src/checker/entity.cpp index 74ed6780f..e93a11822 100644 --- a/src/checker/entity.cpp +++ b/src/checker/entity.cpp @@ -1,5 +1,6 @@ struct Scope; struct Checker; +enum BuiltinProcedureId; enum EntityKind { Entity_Invalid, @@ -32,7 +33,7 @@ struct Entity { } variable; struct {} type_name; struct {} procedure; - struct { i32 id; } builtin; + struct { BuiltinProcedureId id; } builtin; }; }; @@ -54,7 +55,7 @@ Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *parent, Token token, - +// NOTE(bill): Defined in "checker.cpp" Entity *make_entity_variable (Checker *c, Scope *parent, Token token, Type *type); Entity *make_entity_constant (Checker *c, Scope *parent, Token token, Type *type, Value value); Entity *make_entity_type_name(Checker *c, Scope *parent, Token token, Type *type); @@ -62,5 +63,4 @@ Entity *make_entity_param (Checker *c, Scope *parent, Token token, Type *type Entity *make_entity_field (Checker *c, Scope *parent, Token token, Type *type); Entity *make_entity_procedure(Checker *c, Scope *parent, Token token, Type *signature_type); Entity *make_entity_builtin (Checker *c, Scope *parent, Token token, Type *type, i32 id); - Entity *make_entity_dummy_variable(Checker *c, Token token); diff --git a/src/checker/expression.cpp b/src/checker/expression.cpp index fbb213dfa..cc098bf5b 100644 --- a/src/checker/expression.cpp +++ b/src/checker/expression.cpp @@ -1,4 +1,5 @@ void check_assignment (Checker *c, Operand *operand, Type *type, String context_name); +b32 check_is_assignable_to (Checker *c, Operand *operand, Type *type); void check_expression (Checker *c, Operand *operand, AstNode *expression); void check_multi_expression (Checker *c, Operand *operand, AstNode *expression); void check_expression_or_type(Checker *c, Operand *operand, AstNode *expression); @@ -31,8 +32,7 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) { } } - Entity **fields = gb_alloc_array(gb_arena_allocator(&c->entity_arena), - Entity *, st->field_count); + Entity **fields = gb_alloc_array(c->allocator, Entity *, st->field_count); isize field_index = 0; for (AstNode *field = st->field_list; field != NULL; field = field->next) { Type *type = check_type(c, field->field.type_expression); @@ -60,10 +60,9 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize fiel if (field_list == NULL || field_count == 0) return NULL; - Type *tuple = make_type_tuple(); + Type *tuple = make_type_tuple(c->allocator); - Entity **variables = gb_alloc_array(gb_arena_allocator(&c->entity_arena), - Entity *, field_count); + Entity **variables = gb_alloc_array(c->allocator, Entity *, field_count); isize variable_index = 0; for (AstNode *field = field_list; field != NULL; field = field->next) { GB_ASSERT(field->kind == AstNode_Field); @@ -87,10 +86,9 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize fiel Type *check_get_results(Checker *c, Scope *scope, AstNode *list, isize list_count) { if (list == NULL) return NULL; - Type *tuple = make_type_tuple(); + Type *tuple = make_type_tuple(c->allocator); - Entity **variables = gb_alloc_array(gb_arena_allocator(&c->entity_arena), - Entity *, list_count); + Entity **variables = gb_alloc_array(c->allocator, Entity *, list_count); isize variable_index = 0; for (AstNode *item = list; item != NULL; item = item->next) { Type *type = check_type(c, item); @@ -143,7 +141,7 @@ void check_identifier(Checker *c, Operand *operand, AstNode *n, Type *named_type scope_lookup_parent_entity(c->curr_scope, n->identifier.token.string, NULL, &e); if (e == NULL) { print_checker_error(c, n->identifier.token, - "Undeclared type/identifier: %.*s", LIT(n->identifier.token.string)); + "Undeclared type/identifier `%.*s`", LIT(n->identifier.token.string)); return; } add_entity_use(c, n, e); @@ -247,32 +245,33 @@ Type *check_type_expression_extra(Checker *c, AstNode *expression, Type *named_t case AstNode_ArrayType: if (expression->array_type.count != NULL) { - Type *t = make_type_array(check_type(c, expression->array_type.element), + Type *t = make_type_array(c->allocator, + check_type(c, expression->array_type.element), check_array_count(c, expression->array_type.count)); set_base_type(named_type, t); return t; } else { - Type *t = make_type_slice(check_type(c, expression->array_type.element)); + Type *t = make_type_slice(c->allocator, check_type(c, expression->array_type.element)); set_base_type(named_type, t); return t; } break; case AstNode_StructType: { - Type *t = make_type_structure(); + Type *t = make_type_structure(c->allocator); set_base_type(named_type, t); check_struct_type(c, t, expression); return t; } break; case AstNode_PointerType: { - Type *t = make_type_pointer(check_type(c, expression->pointer_type.type_expression)); + Type *t = make_type_pointer(c->allocator, check_type(c, expression->pointer_type.type_expression)); set_base_type(named_type, t); return t; } break; case AstNode_ProcedureType: { - Type *t = alloc_type(Type_Procedure); + Type *t = alloc_type(c->allocator, Type_Procedure); set_base_type(named_type, t); check_open_scope(c, expression); check_procedure_type(c, t, expression); @@ -338,31 +337,32 @@ Type *check_type(Checker *c, AstNode *expression, Type *named_type) { case AstNode_ArrayType: { if (expression->array_type.count != NULL) { - type = make_type_array(check_type(c, expression->array_type.element), + type = make_type_array(c->allocator, + check_type(c, expression->array_type.element), check_array_count(c, expression->array_type.count)); set_base_type(named_type, type); } else { - type = make_type_slice(check_type(c, expression->array_type.element)); + type = make_type_slice(c->allocator, check_type(c, expression->array_type.element)); set_base_type(named_type, type); } goto end; } break; case AstNode_StructType: { - type = make_type_structure(); + type = make_type_structure(c->allocator); set_base_type(named_type, type); check_struct_type(c, type, expression); goto end; } break; case AstNode_PointerType: { - type = make_type_pointer(check_type(c, expression->pointer_type.type_expression)); + type = make_type_pointer(c->allocator, check_type(c, expression->pointer_type.type_expression)); set_base_type(named_type, type); goto end; } break; case AstNode_ProcedureType: { - type = alloc_type(Type_Procedure); + type = alloc_type(c->allocator, Type_Procedure); set_base_type(named_type, type); check_procedure_type(c, type, expression); goto end; @@ -482,7 +482,7 @@ b32 check_value_is_expressible(Checker *c, Value in_value, Type *type, Value *ou return false; if (out_value) *out_value = in_value; i64 i = in_value.value_integer; - i64 s = 8*type_size_of(c->sizes, gb_arena_allocator(&c->entity_arena), type); + i64 s = 8*type_size_of(c->sizes, c->allocator, type); u64 umax = ~0ull; if (s < 64) umax = (1ull << s) - 1ull; @@ -570,7 +570,7 @@ void check_unary_expression(Checker *c, Operand *operand, Token op, AstNode *nod return; } operand->mode = Addressing_Value; - operand->type = make_type_pointer(operand->type); + operand->type = make_type_pointer(c->allocator, operand->type); return; } @@ -584,7 +584,7 @@ void check_unary_expression(Checker *c, Operand *operand, Token op, AstNode *nod GB_ASSERT(type->kind == Type_Basic); i32 precision = 0; if (is_type_unsigned(type)) - precision = cast(i32)(8 * type_size_of(c->sizes, gb_arena_allocator(&c->entity_arena), type)); + precision = cast(i32)(8 * type_size_of(c->sizes, c->allocator, type)); operand->value = unary_operator_value(op, operand->value, precision); if (is_type_typed(type)) { @@ -598,14 +598,12 @@ void check_unary_expression(Checker *c, Operand *operand, Token op, AstNode *nod operand->mode = Addressing_Value; } -b32 check_assignable_to(Checker *c, Operand *operand, Type *type); - void check_comparison(Checker *c, Operand *x, Operand *y, Token op) { gbString err_str = NULL; defer (gb_string_free(err_str)); - if (check_assignable_to(c, x, y->type) || - check_assignable_to(c, y, x->type)) { + if (check_is_assignable_to(c, x, y->type) || + check_is_assignable_to(c, y, x->type)) { b32 defined = false; switch (op.kind) { case Token_CmpEq: @@ -787,7 +785,7 @@ void convert_untyped_error(Checker *c, Operand *operand, Type *target_type) { if (operand->mode == Addressing_Constant) { if (operand->value.value_integer == 0) { - // NOTE(bill): Doesn't matter what the type is as it's still zero + // NOTE(bill): Doesn't matter what the type is as it's still zero in the union extra_text = " - Did you want `null`?"; } } @@ -927,7 +925,8 @@ Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) { type = get_base_type(type->pointer.element); String field_str = field_node->identifier.token.string; - if (type->kind == Type_Structure) { + switch (type->kind) { + case Type_Structure: for (isize i = 0; i < type->structure.field_count; i++) { Entity *f = type->structure.fields[i]; GB_ASSERT(f->kind == Entity_Variable && f->variable.is_field); @@ -937,6 +936,10 @@ Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) { return f; } } + break; + // TODO(bill): Other types and extra "hidden" fields (e.g. introspection stuff) + // TODO(bill): Allow for access of field through index? e.g. `x.3` will get member of index 3 + // Or is this only suitable if tuples are first-class? } return NULL; @@ -995,15 +998,15 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) case BuiltinProcedure_size_of: case BuiltinProcedure_align_of: case BuiltinProcedure_offset_of: + // NOTE(bill): The first arg is a Type, this will be checked case by case break; default: check_multi_expression(c, operand, ce->arg_list); } - gbAllocator allocator = gb_arena_allocator(&c->entity_arena); - switch (id) { case BuiltinProcedure_size_of: { + // size_of :: proc(Type) Type *type = check_type(c, ce->arg_list); if (!type) { print_checker_error(c, ast_node_token(ce->arg_list), "Expected a type for `size_of`"); @@ -1011,43 +1014,47 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } operand->mode = Addressing_Constant; - operand->value = make_value_integer(type_size_of(c->sizes, allocator, type)); + operand->value = make_value_integer(type_size_of(c->sizes, c->allocator, type)); operand->type = &basic_types[Basic_int]; } break; case BuiltinProcedure_size_of_val: + // size_of_val :: proc(val) check_assignment(c, operand, NULL, make_string("argument of `size_of`")); if (operand->mode == Addressing_Invalid) return false; operand->mode = Addressing_Constant; - operand->value = make_value_integer(type_size_of(c->sizes, allocator, operand->type)); + operand->value = make_value_integer(type_size_of(c->sizes, c->allocator, operand->type)); operand->type = &basic_types[Basic_int]; break; case BuiltinProcedure_align_of: { + // align_of :: proc(Type) Type *type = check_type(c, ce->arg_list); if (!type) { print_checker_error(c, ast_node_token(ce->arg_list), "Expected a type for `align_of`"); return false; } operand->mode = Addressing_Constant; - operand->value = make_value_integer(type_align_of(c->sizes, allocator, type)); + operand->value = make_value_integer(type_align_of(c->sizes, c->allocator, type)); operand->type = &basic_types[Basic_int]; } break; case BuiltinProcedure_align_of_val: + // align_of_val :: proc(val) check_assignment(c, operand, NULL, make_string("argument of `align_of`")); if (operand->mode == Addressing_Invalid) return false; operand->mode = Addressing_Constant; - operand->value = make_value_integer(type_align_of(c->sizes, allocator, operand->type)); + operand->value = make_value_integer(type_align_of(c->sizes, c->allocator, operand->type)); operand->type = &basic_types[Basic_int]; break; case BuiltinProcedure_offset_of: { + // offset_val :: proc(Type, field) Type *type = get_base_type(check_type(c, ce->arg_list)); AstNode *field_arg = unparen_expression(ce->arg_list->next); if (type) { @@ -1072,11 +1079,12 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } operand->mode = Addressing_Constant; - operand->value = make_value_integer(type_offset_of(c->sizes, allocator, type, index)); + operand->value = make_value_integer(type_offset_of(c->sizes, c->allocator, type, index)); operand->type = &basic_types[Basic_int]; } break; case BuiltinProcedure_offset_of_val: { + // offset_val :: proc(val) AstNode *arg = unparen_expression(ce->arg_list); if (arg->kind != AstNode_SelectorExpression) { gbString str = expression_to_string(arg); @@ -1106,12 +1114,13 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } operand->mode = Addressing_Constant; - operand->value = make_value_integer(type_offset_of(c->sizes, allocator, type, index)); + operand->value = make_value_integer(type_offset_of(c->sizes, c->allocator, type, index)); operand->type = &basic_types[Basic_int]; } break; case BuiltinProcedure_static_assert: // static_assert :: proc(cond: bool) + // TODO(bill): Should `static_assert` and `assert` be unified? if (operand->mode != Addressing_Constant || !is_type_boolean(operand->type)) { @@ -1130,6 +1139,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } break; + // TODO(bill): Should these be procedures and are their names appropriate? case BuiltinProcedure_len: case BuiltinProcedure_cap: { Type *t = get_base_type(operand->type); @@ -1176,9 +1186,11 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } break; + // TODO(bill): copy() pointer version? case BuiltinProcedure_copy: { // copy :: proc(x, y: []Type) -> int Type *dest_type = NULL, *src_type = NULL; + Type *d = get_base_type(operand->type); if (d->kind == Type_Slice) dest_type = d->slice.element; @@ -1219,6 +1231,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) case BuiltinProcedure_print: case BuiltinProcedure_println: { for (AstNode *arg = ce->arg_list; arg != NULL; arg = arg->next) { + // TOOD(bill): `check_assignment` doesn't allow tuples at the moment, should it? + // Or should we destruct the tuple and use each element? check_assignment(c, operand, NULL, make_string("argument")); if (operand->mode == Addressing_Invalid) return false; @@ -1358,7 +1372,7 @@ ExpressionKind check_call_expression(Checker *c, Operand *operand, AstNode *call } b32 check_castable_to(Checker *c, Operand *operand, Type *y) { - if (check_assignable_to(c, operand, y)) + if (check_is_assignable_to(c, operand, y)) return true; Type *x = operand->type; @@ -1399,10 +1413,10 @@ b32 check_castable_to(Checker *c, Operand *operand, Type *y) { } void check_cast_expression(Checker *c, Operand *operand, Type *type) { - b32 const_expr = operand->mode == Addressing_Constant; + b32 is_const_expr = operand->mode == Addressing_Constant; b32 can_convert = false; - if (const_expr && is_type_constant_type(type)) { + if (is_const_expr && is_type_constant_type(type)) { Type *t = get_base_type(type); if (t->kind == Type_Basic) { if (check_value_is_expressible(c, operand->value, t, &operand->value)) { @@ -1575,7 +1589,7 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr gb_string_free(str); goto error; } - operand->type = make_type_slice(t->array.element); + operand->type = make_type_slice(c->allocator, t->array.element); operand->mode = Addressing_Value; break; @@ -1586,7 +1600,7 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr case Type_Pointer: valid = true; - operand->type = make_type_slice(get_base_type(t->pointer.element)); + operand->type = make_type_slice(c->allocator, get_base_type(t->pointer.element)); operand->mode = Addressing_Value; break; } @@ -1842,6 +1856,20 @@ gbString write_expression_to_string(gbString str, AstNode *node) { str = gb_string_appendc(str, "]"); break; + case AstNode_SliceExpression: + str = write_expression_to_string(str, node->slice_expression.expression); + str = gb_string_appendc(str, "["); + str = write_expression_to_string(str, node->slice_expression.low); + str = gb_string_appendc(str, ":"); + str = write_expression_to_string(str, node->slice_expression.high); + if (node->slice_expression.triple_indexed) { + str = gb_string_appendc(str, ":"); + str = write_expression_to_string(str, node->slice_expression.max); + } + str = gb_string_appendc(str, "]"); + break; + + case AstNode_CastExpression: str = gb_string_appendc(str, "cast("); str = write_expression_to_string(str, node->cast_expression.type_expression); @@ -1851,7 +1879,7 @@ gbString write_expression_to_string(gbString str, AstNode *node) { case AstNode_PointerType: - str = gb_string_appendc(str, "*"); + str = gb_string_appendc(str, "^"); str = write_expression_to_string(str, node->pointer_type.type_expression); break; case AstNode_ArrayType: @@ -1861,6 +1889,7 @@ gbString write_expression_to_string(gbString str, AstNode *node) { str = write_expression_to_string(str, node->array_type.element); break; + case AstNode_CallExpression: { str = write_expression_to_string(str, node->call_expression.proc); str = gb_string_appendc(str, "("); diff --git a/src/checker/statements.cpp b/src/checker/statements.cpp index 15a0904a6..a7c72d7f0 100644 --- a/src/checker/statements.cpp +++ b/src/checker/statements.cpp @@ -1,6 +1,6 @@ -// +// Statements and Declarations -b32 check_assignable_to(Checker *c, Operand *operand, Type *type) { +b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type) { if (operand->mode == Addressing_Invalid || type == &basic_types[Basic_Invalid]) { return true; @@ -54,6 +54,7 @@ b32 check_assignable_to(Checker *c, Operand *operand, Type *type) { // NOTE(bill): `content_name` is for debugging +// TODO(bill): Maybe allow assignment to tuples? void check_assignment(Checker *c, Operand *operand, Type *type, String context_name) { check_not_tuple(c, operand); if (operand->mode == Addressing_Invalid) @@ -70,7 +71,7 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n } if (type != NULL) { - if (!check_assignable_to(c, operand, type)) { + if (!check_is_assignable_to(c, operand, type)) { gbString type_string = type_to_string(type); gbString op_type_string = type_to_string(operand->type); defer (gb_string_free(type_string)); @@ -90,9 +91,9 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n } -Type *check_assign_variable(Checker *c, Operand *x, AstNode *lhs) { - if (x->mode == Addressing_Invalid || - x->type == &basic_types[Basic_Invalid]) { +Type *check_assign_variable(Checker *c, Operand *op_a, AstNode *lhs) { + if (op_a->mode == Addressing_Invalid || + op_a->type == &basic_types[Basic_Invalid]) { return NULL; } @@ -102,10 +103,10 @@ Type *check_assign_variable(Checker *c, Operand *x, AstNode *lhs) { if (node->kind == AstNode_Identifier && are_strings_equal(node->identifier.token.string, make_string("_"))) { add_entity_definition(c, node, NULL); - check_assignment(c, x, NULL, make_string("assignment to `_` identifier")); - if (x->mode == Addressing_Invalid) + check_assignment(c, op_a, NULL, make_string("assignment to `_` identifier")); + if (op_a->mode == Addressing_Invalid) return NULL; - return x->type; + return op_a->type; } Entity *e = NULL; @@ -119,41 +120,42 @@ Type *check_assign_variable(Checker *c, Operand *x, AstNode *lhs) { } - Operand y = {Addressing_Invalid}; - check_expression(c, &y, lhs); + Operand op_b = {Addressing_Invalid}; + check_expression(c, &op_b, lhs); if (e) e->variable.used = used; - if (y.mode == Addressing_Invalid || - y.type == &basic_types[Basic_Invalid]) { + if (op_b.mode == Addressing_Invalid || + op_b.type == &basic_types[Basic_Invalid]) { return NULL; } - switch (y.mode) { + switch (op_b.mode) { case Addressing_Variable: break; case Addressing_Invalid: return NULL; default: { - if (y.expression->kind == AstNode_SelectorExpression) { + if (op_b.expression->kind == AstNode_SelectorExpression) { // NOTE(bill): Extra error checks - Operand z = {Addressing_Invalid}; - check_expression(c, &z, y.expression->selector_expression.operand); + Operand op_c = {Addressing_Invalid}; + check_expression(c, &op_c, op_b.expression->selector_expression.operand); } - gbString str = expression_to_string(y.expression); + gbString str = expression_to_string(op_b.expression); defer (gb_string_free(str)); - print_checker_error(c, ast_node_token(y.expression), + print_checker_error(c, ast_node_token(op_b.expression), "Cannot assign to `%s`", str); } break; } - check_assignment(c, x, y.type, make_string("assignment")); - if (x->mode == Addressing_Invalid) + check_assignment(c, op_a, op_b.type, make_string("assignment")); + if (op_a->mode == Addressing_Invalid) return NULL; - return x->type; + return op_a->type; } +// TODO(bill): Do I need to pass the *_count? void check_assign_variables(Checker *c, AstNode *lhs_list, isize lhs_count, AstNode *rhs_list, isize rhs_count) { @@ -312,6 +314,10 @@ void check_statement_list(Checker *c, AstNode *node) { check_statement(c, node); } + +// NOTE(bill): The last expression has to be a `return` statement +// TODO(bill): This is a mild hack and should be probably handled +// TODO(bill): Warn/err against code after `return` that it won't be executed b32 check_is_terminating(Checker *c, AstNode *node); b32 check_is_terminating_list(Checker *c, AstNode *node_list) { @@ -545,14 +551,12 @@ void check_statement(Checker *c, AstNode *node) { // Declarations case AstNode_VariableDeclaration: { auto *vd = &node->variable_declaration; - gbAllocator allocator = gb_arena_allocator(&c->entity_arena); - isize entity_count = vd->name_list_count; isize entity_index = 0; - Entity **entities = gb_alloc_array(allocator, Entity *, entity_count); + Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count); switch (vd->kind) { case Declaration_Mutable: { - Entity **new_entities = gb_alloc_array(allocator, Entity *, entity_count); + Entity **new_entities = gb_alloc_array(c->allocator, Entity *, entity_count); isize new_entity_count = 0; for (AstNode *name = vd->name_list; name != NULL; name = name->next) { @@ -564,7 +568,7 @@ void check_statement(Checker *c, AstNode *node) { // NOTE(bill): Ignore assignments to `_` b32 can_be_ignored = are_strings_equal(str, make_string("_")); if (!can_be_ignored) { - found = scope_lookup_entity_current(c->curr_scope, str); + found = current_scope_lookup_entity(c->curr_scope, str); } if (found == NULL) { entity = make_entity_variable(c, c->curr_scope, token, NULL); @@ -650,14 +654,16 @@ 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"); - // TODO(bill): Should this be the case? And are the scopes correct? - // TODO(bill): Should procedures just have global scope? Entity *e = make_entity_procedure(c, c->curr_scope, pd->name->identifier.token, NULL); add_entity(c, c->curr_scope, pd->name, e); - Type *proc_type = make_type_procedure(e->parent, NULL, 0, NULL, 0); + Type *proc_type = make_type_procedure(c->allocator, e->parent, NULL, 0, NULL, 0); e->type = proc_type; + // NOTE(bill): Procedures are just in file scope only. + // This is because closures/lambdas are not supported yet (or maybe never) + Scope *origin_curr_scope = c->curr_scope; + c->curr_scope = c->file_scope; check_open_scope(c, pd->procedure_type); { check_procedure_type(c, proc_type, pd->procedure_type); @@ -676,12 +682,14 @@ void check_statement(Checker *c, AstNode *node) { GB_ASSERT(pd->tag->kind == AstNode_TagExpression); String tag_name = pd->tag->tag_expression.name.string; - if (gb_strncmp("foreign", cast(char *)tag_name.text, tag_name.len) == 0) { + if (are_strings_equal(tag_name, make_string("foreign"))) { // NOTE(bill): Foreign procedure (linking stage) } + // TODO(bill): Other tags } } check_close_scope(c); + c->curr_scope = origin_curr_scope; } break; @@ -691,7 +699,7 @@ void check_statement(Checker *c, AstNode *node) { Entity *e = make_entity_type_name(c, c->curr_scope, name->identifier.token, NULL); add_entity(c, c->curr_scope, name, e); - e->type = make_type_named(e->token.string, NULL, e); + e->type = make_type_named(c->allocator, e->token.string, NULL, e); check_type(c, td->type_expression, e->type); // NOTE(bill): Prevent recursive definition set_base_type(e->type, get_base_type(e->type)); diff --git a/src/checker/type.cpp b/src/checker/type.cpp index 46957601e..ca658c2e4 100644 --- a/src/checker/type.cpp +++ b/src/checker/type.cpp @@ -115,59 +115,58 @@ void set_base_type(Type *t, Type *base) { } -// TODO(bill): Remove heap allocation -Type *alloc_type(TypeKind kind) { - Type *t = gb_alloc_item(gb_heap_allocator(), Type); +Type *alloc_type(gbAllocator a, TypeKind kind) { + Type *t = gb_alloc_item(a, Type); t->kind = kind; return t; } -Type *make_type_basic(BasicType basic) { - Type *t = alloc_type(Type_Basic); +Type *make_type_basic(gbAllocator a, BasicType basic) { + Type *t = alloc_type(a, Type_Basic); t->basic = basic; return t; } -Type *make_type_array(Type *element, i64 count) { - Type *t = alloc_type(Type_Array); +Type *make_type_array(gbAllocator a, Type *element, i64 count) { + Type *t = alloc_type(a, Type_Array); t->array.element = element; t->array.count = count; return t; } -Type *make_type_slice(Type *element) { - Type *t = alloc_type(Type_Slice); +Type *make_type_slice(gbAllocator a, Type *element) { + Type *t = alloc_type(a, Type_Slice); t->array.element = element; return t; } -Type *make_type_structure(void) { - Type *t = alloc_type(Type_Structure); +Type *make_type_structure(gbAllocator a) { + Type *t = alloc_type(a, Type_Structure); return t; } -Type *make_type_pointer(Type *element) { - Type *t = alloc_type(Type_Pointer); +Type *make_type_pointer(gbAllocator a, Type *element) { + Type *t = alloc_type(a, Type_Pointer); t->pointer.element = element; return t; } -Type *make_type_named(String name, Type *base, Entity *type_name) { - Type *t = alloc_type(Type_Named); +Type *make_type_named(gbAllocator a, String name, Type *base, Entity *type_name) { + Type *t = alloc_type(a, Type_Named); t->named.name = name; t->named.base = base; t->named.type_name = type_name; return t; } -Type *make_type_tuple(void) { - Type *t = alloc_type(Type_Tuple); +Type *make_type_tuple(gbAllocator a) { + Type *t = alloc_type(a, Type_Tuple); return t; } -Type *make_type_procedure(Scope *scope, Type *params, isize params_count, Type *results, isize results_count) { - Type *t = alloc_type(Type_Procedure); +Type *make_type_procedure(gbAllocator a, Scope *scope, Type *params, isize params_count, Type *results, isize results_count) { + Type *t = alloc_type(a, Type_Procedure); t->procedure.scope = scope; t->procedure.params = params; t->procedure.params_count = params_count; @@ -382,6 +381,13 @@ Type *default_type(Type *type) { } +// NOTE(bill): Internal sizes of certain types +// string: 2*word_size (ptr+len) +// slice: 3*word_size (ptr+len+cap) +// array: count*size_of(element) aligned + +// NOTE(bill): Alignment of structures and other types are to be compatible with C + struct BaseTypeSizes { i64 word_size; i64 max_align; @@ -406,6 +412,7 @@ gb_global i64 basic_type_sizes[] = { i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t); i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t); +i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, i64 index); i64 align_formula(i64 size, i64 align) { i64 result = size + align-1; @@ -481,6 +488,9 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { return alignment*(count-1) + size; } break; + case Type_Slice: // ptr + len + cap + return 3 * s.word_size; + case Type_Structure: { i64 count = t->structure.field_count; if (count == 0) @@ -516,11 +526,7 @@ gbString write_type_to_string(gbString str, Type *type) { break; case Type_Array: - if (type->array.count >= 0) { - str = gb_string_appendc(str, gb_bprintf("[%td]", type->array.count)); - } else { - str = gb_string_appendc(str, "[]"); - } + str = gb_string_appendc(str, gb_bprintf("[%td]", type->array.count)); str = write_type_to_string(str, type->array.element); break; @@ -553,7 +559,7 @@ gbString write_type_to_string(gbString str, Type *type) { str = gb_string_append_length(str, type->named.name.text, type->named.name.len); } else { // NOTE(bill): Just in case - str = gb_string_appendc(str, ""); + str = gb_string_appendc(str, ""); } break; diff --git a/src/checker/value.cpp b/src/checker/value.cpp index 8027480ec..fcfe6c336 100644 --- a/src/checker/value.cpp +++ b/src/checker/value.cpp @@ -10,7 +10,7 @@ enum ValueKind { Value_String, Value_Integer, Value_Float, - Value_Pointer, // TODO(bill): Value_Pointer + Value_Pointer, // TODO(bill): Handle Value_Pointer correctly Value_Count, }; @@ -154,6 +154,7 @@ Value unary_operator_value(Token op, Value v, i32 precision) { case Value_Invalid: return v; case Value_Integer: + i = v.value_integer; i = ~i; break; default: @@ -163,7 +164,7 @@ Value unary_operator_value(Token op, Value v, i32 precision) { // NOTE(bill): unsigned integers will be negative and will need to be // limited to the types precision if (precision > 0) - i &= ~((-1)<kind) { case Value_Integer: return; @@ -229,17 +230,18 @@ void match_values(Value *x, Value *y) { *x = make_value_float(cast(f64)x->value_integer); return; } - } break; + break; - case Value_Float: { + case Value_Float: if (y->kind == Value_Float) return; - } break; + break; } GB_PANIC("How'd you get here? Invalid Value.kind"); } +// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough? Value binary_operator_value(Token op, Value x, Value y) { match_values(&x, &y); diff --git a/src/common.cpp b/src/common.cpp index 7b8f7cf02..7d7bed316 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -46,6 +46,23 @@ gb_inline u64 hash_pointer(void *ptr) { return p; } + + +// Doubly Linked Lists + +#define DLIST_SET(curr_element, next_element) do { \ + (curr_element)->next = (next_element); \ + (curr_element)->next->prev = (curr_element); \ + (curr_element) = (curr_element)->next; \ +} while (0) + +#define DLIST_APPEND(root_element, curr_element, next_element) do { \ + if ((root_element) == NULL) \ + (root_element) = (curr_element) = (next_element); \ + else \ + DLIST_SET(curr_element, next_element); \ +} while (0) + //////////////////////////////////////////////////////////////// // // Generic Data Structures diff --git a/src/generator.cpp b/src/generator.cpp index bd081f184..d85453a75 100644 --- a/src/generator.cpp +++ b/src/generator.cpp @@ -13,31 +13,25 @@ struct Generator { #define print_generator_error(p, token, fmt, ...) print_generator_error_(p, __FUNCTION__, token, fmt, ##__VA_ARGS__) void print_generator_error_(Generator *g, char *function, Token token, char *fmt, ...) { - va_list va; // NOTE(bill): Duplicate error, skip it - if (g->error_prev_line == token.line && g->error_prev_column == token.column) { - goto error; + if (g->error_prev_line != token.line || g->error_prev_column != token.column) { + va_list va; + + g->error_prev_line = token.line; + g->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", + g->checker->parser->tokenizer.fullpath, token.line, token.column, + gb_bprintf_va(fmt, va)); + va_end(va); + } - g->error_prev_line = token.line; - g->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", - g->checker->parser->tokenizer.fullpath, token.line, token.column, - gb_bprintf_va(fmt, va)); - va_end(va); - -error: g->error_count++; - // NOTE(bill): If there are too many errors, just quit - if (g->error_count > MAX_GENERATOR_ERROR_COUNT) { - gb_exit(1); - return; - } } diff --git a/src/parser.cpp b/src/parser.cpp index 367303ade..bd054cb98 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -4,10 +4,11 @@ struct AstScope; // NOTE(bill): Just used to quickly check if there is double declaration in the same scope // No type checking actually happens +// TODO(bill): Should this be completely handled in the semantic checker or is it better here? struct AstEntity { - Token token; + Token token; AstScope *parent; - AstNode *declaration; + AstNode * declaration; }; struct AstScope { @@ -15,7 +16,6 @@ struct AstScope { Map entities; // Key: Token.string }; - struct Parser { gbArena arena; Tokenizer tokenizer; @@ -53,7 +53,7 @@ AstNode__ExpressionBegin, AstNode__ExpressionEnd, AstNode__StatementBegin, - AstNode_BadStatement, + AstNode_BadStatement, // NOTE(bill): Naughty statement AstNode_EmptyStatement, AstNode_ExpressionStatement, AstNode_IncDecStatement, @@ -70,7 +70,7 @@ AstNode__ComplexStatementEnd, AstNode__StatementEnd, AstNode__DeclarationBegin, - AstNode_BadDeclaration, + AstNode_BadDeclaration, // NOTE(bill): Naughty declaration AstNode_VariableDeclaration, AstNode_ProcedureDeclaration, AstNode_TypeDeclaration, @@ -192,11 +192,13 @@ struct AstNode { AstNode *name; // AstNode_Identifier AstNode *procedure_type; // AstNode_ProcedureType AstNode *body; // AstNode_BlockStatement - AstNode *tag; // AstNode_TagExpression; + AstNode *tag; // AstNode_TagExpression + // TODO(bill): Allow for multiple tags + // TODO(bill): Modifiers: inline, no_inline, etc. } procedure_declaration; struct { Token token; - AstNode *name; + AstNode *name; // AstNode_Identifier AstNode *type_expression; } type_declaration; @@ -207,7 +209,7 @@ struct AstNode { } pointer_type; struct { Token token; - AstNode *count; + AstNode *count; // NOTE(bill): Zero/NULL is probably a slice AstNode *element; } array_type; struct { @@ -219,20 +221,6 @@ struct AstNode { }; -#define DLIST_SET(curr_element, next_element) do { \ - (curr_element)->next = (next_element); \ - (curr_element)->next->prev = (curr_element); \ - (curr_element) = (curr_element)->next; \ -} while (0) - -#define DLIST_APPEND(root_element, curr_element, next_element) do { \ - if ((root_element) == NULL) \ - (root_element) = (curr_element) = (next_element); \ - else \ - DLIST_SET(curr_element, next_element); \ -} while (0) - - gb_inline AstScope *make_ast_scope(Parser *p, AstScope *parent) { AstScope *scope = gb_alloc_item(gb_arena_allocator(&p->arena), AstScope); map_init(&scope->entities, gb_heap_allocator()); @@ -649,31 +637,24 @@ 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, ...) { - va_list va; // NOTE(bill): Duplicate error, skip it - if (p->error_prev_line == token.line && p->error_prev_column == token.column) { - goto error; + 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_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) %s\n", - p->tokenizer.fullpath, token.line, token.column, - gb_bprintf_va(fmt, va)); - va_end(va); - -error: p->error_count++; - // NOTE(bill): If there are too many errors, just quit - if (p->error_count > MAX_PARSER_ERROR_COUNT) { - gb_exit(1); - return; - } } @@ -788,10 +769,6 @@ AstNode *parse_identifier(Parser *p) { return make_identifier(p, identifier); } -AstNode *parse_rhs(Parser *p) { - return parse_expression(p, false); -} - AstNode *unparen_expression(AstNode *node) { for (;;) { if (node->kind != AstNode_ParenExpression) @@ -822,7 +799,7 @@ AstNode *parse_atom_expression(Parser *p, b32 lhs) { Token open, close; // NOTE(bill): Skip the Paren Expression open = expect_token(p, Token_OpenParen); - operand = parse_rhs(p); + operand = parse_expression(p, false); close = expect_token(p, Token_CloseParen); operand = make_paren_expression(p, operand, open, close); } break; @@ -848,7 +825,7 @@ AstNode *parse_atom_expression(Parser *p, b32 lhs) { if (p->cursor[0].kind == Token_Comma) print_parse_error(p, p->cursor[0], "Expected an expression not a ,"); - DLIST_APPEND(arg_list, arg_list_curr, parse_rhs(p)); + DLIST_APPEND(arg_list, arg_list_curr, parse_expression(p, false)); arg_list_count++; if (p->cursor[0].kind != Token_Comma) { @@ -1229,14 +1206,14 @@ AstNode *parse_identifier_or_type(Parser *p) { return parse_identifier(p); case Token_Pointer: - return make_pointer_type(p, expect_token(p, Token_Pointer), parse_type(p)); + return make_pointer_type(p, expect_token(p, Token_Pointer), parse_type(p)); case Token_OpenBracket: { Token token = expect_token(p, Token_OpenBracket); AstNode *count_expression = NULL; if (p->cursor[0].kind != Token_CloseBracket) - count_expression = parse_rhs(p); + count_expression = parse_expression(p, false); expect_token(p, Token_CloseBracket); return make_array_type(p, token, count_expression, parse_type(p)); } @@ -1290,11 +1267,12 @@ AstNode *parse_identifier_or_type(Parser *p) { return NULL; } -AstNode *parse_parameters(Parser *p, AstScope *scope, isize *param_count_, b32 use_parens) { +// 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; - if (use_parens) expect_token(p, Token_OpenParen); + 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++; @@ -1302,7 +1280,7 @@ AstNode *parse_parameters(Parser *p, AstScope *scope, isize *param_count_, b32 u break; next_token(p); } - if (use_parens) expect_token(p, Token_CloseParen); + expect_token(p, Token_CloseParen); if (param_count_) *param_count_ = param_count; return param_list; @@ -1334,7 +1312,7 @@ AstNode *parse_results(Parser *p, AstScope *scope, isize *result_count_) { void parse_procedure_signature(Parser *p, AstScope *scope, AstNode **param_list, isize *param_count, AstNode **result_list, isize *result_count) { - *param_list = parse_parameters(p, scope, param_count, true); + *param_list = parse_parameters(p, scope, param_count); *result_list = parse_results(p, scope, result_count); } diff --git a/src/test.odin b/src/test.odin index eeeacb45d..004fcc537 100644 --- a/src/test.odin +++ b/src/test.odin @@ -2,12 +2,13 @@ type Vec2: struct { x, y: f32; } - print_string_array :: proc(args: []string) { args[0] = ""; } main :: proc() { + x := 0; + thing :: proc(n: int) -> int, f32 { return n*n, 13.37; } @@ -15,4 +16,5 @@ main :: proc() { thang :: proc(a: int, b: f32, s: string) { } + thang(thing(1), "Yep"); } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 073d13e50..78a2c16fe 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -289,6 +289,8 @@ struct Tokenizer { u8 * read_curr; // pos from start u8 * line; // current line pos isize line_count; + + isize error_count; }; @@ -299,7 +301,9 @@ void tokenizer_error_(Tokenizer *t, char *function, char *msg, ...) { if (column < 1) column = 1; +#if 0 gb_printf_err("%s()\n", function); +#endif gb_printf_err("%s(%td:%td) ", t->fullpath, t->line_count, column); va_start(va, msg); @@ -308,7 +312,7 @@ void tokenizer_error_(Tokenizer *t, char *function, char *msg, ...) { gb_printf_err("\n"); - gb_exit(1); + t->error_count++; } void advance_to_next_rune(Tokenizer *t) { @@ -471,7 +475,9 @@ Token scan_number_to_token(Tokenizer *t, b32 seen_decimal_point) { goto fraction; } } - goto end; + + token.string.len = t->curr - token.string.text; + return token; } scan_mantissa(t, 10); @@ -492,7 +498,6 @@ exponent: scan_mantissa(t, 10); } -end: token.string.len = t->curr - token.string.text; return token; }