Browse Source

Initial release version
* Code cleanup
* Fix some TODOs
* Reduce heap allocation use and replace with arena allocation

gingerBill 9 years ago
parent
commit
f7a669d342
14 changed files with 591 additions and 247 deletions
  1. 253 1
      .gitignore
  2. 25 0
      LICENSE
  3. 24 0
      README.md
  4. 60 58
      src/checker/checker.cpp
  5. 3 3
      src/checker/entity.cpp
  6. 71 42
      src/checker/expression.cpp
  7. 40 32
      src/checker/statements.cpp
  8. 31 25
      src/checker/type.cpp
  9. 8 6
      src/checker/value.cpp
  10. 17 0
      src/common.cpp
  11. 15 21
      src/generator.cpp
  12. 33 55
      src/parser.cpp
  13. 3 1
      src/test.odin
  14. 8 3
      src/tokenizer.cpp

+ 253 - 1
.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

+ 25 - 0
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.

+ 24 - 0
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
+

+ 60 - 58
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<Entity *>    elements; // Key: String
+	Scope *prev, *next;
+	Scope *first_child, *last_child;
+	Map<Entity *> 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,18 +73,42 @@ enum BuiltinProcedureId {
 
 	BuiltinProcedure_Count,
 };
+struct BuiltinProcedure {
+	String name;
+	isize arg_count;
+	b32 variadic;
+	ExpressionKind kind;
+};
+gb_global BuiltinProcedure builtin_procedures[BuiltinProcedure_Count] = {
+	{STR_LIT(""),                 0, false, Expression_Statement},
+	{STR_LIT("size_of"),          1, false, Expression_Expression},
+	{STR_LIT("size_of_val"),      1, false, Expression_Expression},
+	{STR_LIT("align_of"),         1, false, Expression_Expression},
+	{STR_LIT("align_of_val"),     1, false, Expression_Expression},
+	{STR_LIT("offset_of"),        2, false, Expression_Expression},
+	{STR_LIT("offset_of_val"),    1, false, Expression_Expression},
+	{STR_LIT("static_assert"),    1, false, Expression_Statement},
+	{STR_LIT("len"),              1, false, Expression_Expression},
+	{STR_LIT("cap"),              1, false, Expression_Expression},
+	{STR_LIT("copy"),             2, false, Expression_Expression},
+	{STR_LIT("print"),            1, true,  Expression_Statement},
+	{STR_LIT("println"),          1, true,  Expression_Statement},
+};
+
+
 
 struct Checker {
 	Parser *            parser;
 	Map<TypeAndValue>   types;       // Key: AstNode * | Expression -> Type (and value)
 	Map<Entity *>       definitions; // Key: AstNode * | Identifier -> Entity
-	Map<Entity *>       uses;        // Key: AstNode * | Identifier -> Entity (Anonymous field)
+	Map<Entity *>       uses;        // Key: AstNode * | Identifier -> Entity
 	Map<Scope *>        scopes;      // Key: AstNode * | Node       -> Scope
 	Map<ExpressionInfo> untyped;     // Key: AstNode * | Expression -> ExpressionInfo
 	BaseTypeSizes       sizes;
 	Scope *             file_scope;
 
-	gbArena entity_arena;
+	gbArena     arena;
+	gbAllocator allocator;
 
 	Scope *curr_scope;
 	gbArray(Type *) procedure_stack;
@@ -105,42 +123,23 @@ struct Checker {
 
 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},
-	{STR_LIT("size_of_val"),      1, false, Expression_Expression},
-	{STR_LIT("align_of"),         1, false, Expression_Expression},
-	{STR_LIT("align_of_val"),     1, false, Expression_Expression},
-	{STR_LIT("offset_of"),        2, false, Expression_Expression},
-	{STR_LIT("offset_of_val"),    1, false, Expression_Expression},
-	{STR_LIT("static_assert"),    1, false, Expression_Statement},
-	{STR_LIT("len"),              1, false, Expression_Expression},
-	{STR_LIT("cap"),              1, false, Expression_Expression},
-	{STR_LIT("copy"),             2, false, Expression_Expression},
-	{STR_LIT("print"),            1, true,  Expression_Statement},
-	{STR_LIT("println"),          1, true,  Expression_Statement},
-};
-
 
-// TODO(bill): Arena allocation
-Scope *make_scope(Scope *parent) {
-	gbAllocator a = gb_heap_allocator();
-	Scope *s = gb_alloc_item(a, Scope);
+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;
 }

+ 3 - 3
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);

+ 71 - 42
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, "(");

+ 40 - 32
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));

+ 31 - 25
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, "<Type_Named>");
+			str = gb_string_appendc(str, "<named type>");
 		}
 		break;
 

+ 8 - 6
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)<<precision);
+			i &= ~((~0ll)<<precision);
 
 		return make_value_integer(i);
 	} break;
@@ -220,7 +221,7 @@ void match_values(Value *x, Value *y) {
 	case Value_String:
 		return;
 
-	case Value_Integer: {
+	case Value_Integer:
 		switch (y->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);
 

+ 17 - 0
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

+ 15 - 21
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;
-	}
 }
 
 

+ 33 - 55
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<AstEntity> 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);
 }
 

+ 3 - 1
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");
 }

+ 8 - 3
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;
 }