mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-19 13:00:28 +00:00
Initial release version
* Code cleanup * Fix some TODOs * Reduce heap allocation use and replace with arena allocation
This commit is contained in:
254
.gitignore
vendored
254
.gitignore
vendored
@@ -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
25
LICENSE
Normal 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
24
README.md
Normal 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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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, "(");
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user