Initial release version

* Code cleanup
* Fix some TODOs
* Reduce heap allocation use and replace with arena allocation
This commit is contained in:
gingerBill
2016-07-09 00:31:57 +01:00
parent 9ba2a6d02c
commit f7a669d342
14 changed files with 598 additions and 254 deletions

254
.gitignore vendored
View File

@@ -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
LICENSE Normal file
View File

@@ -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
README.md Normal file
View File

@@ -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

View File

@@ -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,32 +73,12 @@ enum BuiltinProcedureId {
BuiltinProcedure_Count,
};
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<Scope *> scopes; // Key: AstNode * | Node -> Scope
Map<ExpressionInfo> 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<TypeAndValue> types; // Key: AstNode * | Expression -> Type (and value)
Map<Entity *> definitions; // Key: AstNode * | Identifier -> Entity
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 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;
}

View File

@@ -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);

View File

@@ -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, "(");

View File

@@ -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));

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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");
}

View File

@@ -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;
}