return cast(rawptr) (cast(u32) ret + sizeof heap_block);
}
- new_pages :: ((size - heap_state.remaining_space) >> 16) + 1;
+ new_pages := ((size - heap_state.remaining_space) >> 16) + 1;
if memory_grow(new_pages) == -1 {
// out of memory
return null;
// If we are at the end of the allocation space, just extend it
if hb_ptr.size + cast(u32) ptr >= cast(u32) heap_state.next_alloc {
if new_size - old_size >= heap_state.remaining_space {
- new_pages :: ((new_size - old_size - heap_state.remaining_space) >> 16) + 1;
+ new_pages := ((new_size - old_size - heap_state.remaining_space) >> 16) + 1;
if memory_grow(new_pages) == -1 {
// out of memory
return null;
c := ^buf[buf.count - 1];
len := 0;
- s :: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
+ // BUGFIX: Make this work with '::';
+ BASE64_MAP := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
while n > 0 {
- m :: n % base;
+ m := n % base;
- *c = s[cast(u32) m];
+ *c = BASE64_MAP[cast(u32) m];
len += 1;
c -= 1;
package core.string
make :: proc (s: cstr) -> str {
- len :: length(s);
+ len := length(s);
return str.{ count = len, data = s };
}
}
concat :: proc (s1: str, s2: str) -> str {
- len1 :: length(s1);
- len2 :: length(s2);
+ len1 := length(s1);
+ len2 := length(s2);
data := cast(^u8) calloc(len1 + len2);
for i: 0 .. len1 do data[i] = s1[i];
}
add_str :: proc (use sb: ^Builder, s: str) -> ^Builder {
- len_total :: data.count + s.count;
+ len_total := data.count + s.count;
if data.capacity < len_total do #context_scope {
context.allocator = alloc;
List of known bugs:
[ ] Using an auto-cast on an argument when calling an overloaded proc leads
- to an unexpected error. Take the following example:
- ```
- overloaded :: proc {
- proc (x: f32, y: str) ---,
- proc (x: i32, y: i32) ---,
- }
-
- foo :: proc () {
- x: i32 = 1234;
- overloaded(~~x, 4);
- }
- ```
- Compiles with the following error:
- ```
- (/home/brendan/dev/c/onyx/a.onyx:8,15) unable to match overloaded function with provided argument types: (f32, unsized int)
- 8 | overloaded(~~x, 4);
- ```
-
- This is because in trying the first overload, the auto-cast is consumed
- and converted to a cast(f32). Then, when it doesn't match the first one
- and it tries the second, the parameter types are f32 and unsized int,
- which is doesn't match the second one, when the original parameters would
- have matched correctly.
+ to an unexpected error. Take the following example:
+ ```
+ overloaded :: proc {
+ proc (x: f32, y: str) ---,
+ proc (x: i32, y: i32) ---,
+ }
+
+ foo :: proc () {
+ x: i32 = 1234;
+ overloaded(~~x, 4);
+ }
+ ```
+ Compiles with the following error:
+ ```
+ (/home/brendan/dev/c/onyx/a.onyx:8,15) unable to match overloaded function with provided argument types: (f32, unsized int)
+ 8 | overloaded(~~x, 4);
+ ```
+
+ This is because in trying the first overload, the auto-cast is consumed
+ and converted to a cast(f32). Then, when it doesn't match the first one
+ and it tries the second, the parameter types are f32 and unsized int,
+ which is doesn't match the second one, when the original parameters would
+ have matched correctly.
[ ] `defer` statements are not executed at the end of a loop if the loop is
exited using a `break` statement, or a `continue` statement. The semantics
an infinite loop.
[ ] The following code causes an infinite loop somewhere.
- ```
- get_neighbor_count :: proc (grid: ^map.Map(Vec2, Cell), pos: Vec2) -> u32 {
- count := 0;
+ ```
+ get_neighbor_count :: proc (grid: ^map.Map(Vec2, Cell), pos: Vec2) -> u32 {
+ count := 0;
- for ^dir: Hex_Directions {
- pos := Vec2.{ x = pos.x + dir.x, y = pos.y + dir.y };
- cell := map.get(grid, pos, Cell.{});
- if cell.alive do count += 1;
- }
+ for ^dir: Hex_Directions {
+ pos := Vec2.{ x = pos.x + dir.x, y = pos.y + dir.y };
+ cell := map.get(grid, pos, Cell.{});
+ if cell.alive do count += 1;
+ }
- return count;
- }
- ```
+ return count;
+ }
+ ```
[ ] Polymorphic structs do not recognize default values for members.
+[ ] `use` on struct members does not work correctly if the type is a union.
+ ```
+ BadUnion :: struct {
+ use container : struct #union {
+ int: i32;
+ float: f32;
+ };
+ }
+
+ test :: proc () do print(sizeof BadUnion == 4);
+ ```
+
+[ ] `use` on struct members breaks struct literals.
+ ```
+ Vec2 :: struct { x: i32; y: i32; }
+ Entity :: struct { use pos: Vec2; }
+
+ e := Entity.{ pos = Vec2.{ 1, 2 } };
+
+ // This does work already.
+ e2 := Entity.{ x = 1, y = 2 };
+ ```
+
[X] `TileData :: [TILE_DATA_WIDTH * TILE_DATA_HEIGHT] bool;` results in a
segfault because it is an invalid top level node, but that is not checked
before it is tried to be used.
[X] `TileData :: #type [TILE_DATA_WIDTH * TILE_DATA_HEIGHT] bool;` produces the
- following error:
- ```
- (/home/brendan/dev/onyx/aoc/day20.onyx:25,19) Array type expects type 'i32' for size, got 'unsized int'.
- 25 | TileData :: #type [TILE_DATA_WIDTH * TILE_DATA_HEIGHT] bool;
- ```
+ following error:
+ ```
+ (/home/brendan/dev/onyx/aoc/day20.onyx:25,19) Array type expects type 'i32' for size, got 'unsized int'.
+ 25 | TileData :: #type [TILE_DATA_WIDTH * TILE_DATA_HEIGHT] bool;
+ ```
- This because the expression for the array size is not reducing and getting
- converted to the fixed size integer. I suspect this is because for sizeof
- and alignof expression, `fill_in_type` is not used, and that function has
- the logic to handle the array subscript reduction and type resolution.
+ This because the expression for the array size is not reducing and getting
+ converted to the fixed size integer. I suspect this is because for sizeof
+ and alignof expression, `fill_in_type` is not used, and that function has
+ the logic to handle the array subscript reduction and type resolution.
[X] The following struct is causing a seg fault in the compiler. I believe it
- is because of the duplicate struct member names and failing to get the position
- in the file to report the error.
- ```
- Tile :: struct {
- id : u32;
- orientation : TileOrientation;
- data : [] bool;
- edges : [] u32;
-
- pos_x : u32 = 0;
- pos_x : u32 = 0;
- }
- ```
+ is because of the duplicate struct member names and failing to get the position
+ in the file to report the error.
+ ```
+ Tile :: struct {
+ id : u32;
+ orientation : TileOrientation;
+ data : [] bool;
+ edges : [] u32;
+
+ pos_x : u32 = 0;
+ pos_x : u32 = 0;
+ }
+ ```
[ ] Add macros.
+ [ ] enum #flags should be able to be used as so:
+ ```
+ SomeEnum :: enum #flags {
+ Property1;
+ Property2;
+ Property3;
+ }
+
+ val : SomeEnum = ~~0;
+ val.Property1 = true;
+ val.Property3 = true;
+
+ // This should also be allowed.
+ val |= SomeEnum.Property2;
+
+ if val.Property2 { ... }
+ ```
+
[X] #solidify polymoprhic procedures.
API Expansion:
[ ] Make README on GitHub better, and actually describe what to do
[ ] Make compiler work on Windows
[ ] Make compiler work on MacOS
+ [ ] Add examples for the following language features:
+ - Slices
+ - Dynamic Arrays
+ - Structs and unions
+ - Enums
+ - switch statements
+ - for loops
+ - Maps
+ - varargs
+ - `use` keyword
+ - pipe operator
+ - Overloaded procedures
+ - Polymorphic procedures
+ - WASM directives (#export, #foreign)
+ - #solidify directive
+ - SIMD
AstNode *body;
Scope *scope;
- bh_arr(AstTyped *) allocate_exprs;
+ bh_arr(AstTyped *) allocate_exprs;
+ bh_arr(AstBinding *) bindings;
};
struct AstDefer { AstNode_base; AstNode *stmt; };
struct AstFor {
PolymorphicContext polymorph_context;
+ bh_arr(AstBlock *) block_stack;
+
b32 hit_unexpected_token : 1;
} OnyxParser;
return val;
}
+BadUnion :: struct {
+ use container : struct #union {
+ int: i32;
+ float: f32;
+ };
+}
+
+Vec2 :: struct { x: i32; y: i32; }
+Entity :: struct { use pos: Vec2; }
+
main :: proc (args: [] cstr) {
+ a : BadUnion;
+ a.int = 1234;
+ a.float = 0.5;
+ printf("%i == 4\n", sizeof BadUnion);
+ printf("%p\n", a.int);
+ printf("%f\n", a.float);
+
+ e := Entity.{ pos = Vec2.{ 1, 2 } };
+
{
foo : [5] [2] u32;
foo = f();
defer array.free(^arr);
- for i: 0 .. 10 do array.push(^arr, i);
+ for i: 0 .. 10 do array.push(^arr, ~~i);
print_array(arr);
array_map(arr, double);
case Ast_Kind_Defer: return sizeof(AstDefer);
case Ast_Kind_Switch: return sizeof(AstSwitch);
case Ast_Kind_Switch_Case: return sizeof(AstSwitchCase);
+ case Ast_Kind_Directive_Solidify: return sizeof(AstDirectiveSolidify);
case Ast_Kind_Count: return 0;
}
}
static AstTyped* parse_global_declaration(OnyxParser* parser);
static AstEnumType* parse_enum_declaration(OnyxParser* parser);
static AstTyped* parse_top_level_expression(OnyxParser* parser);
+static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol);
static AstNode* parse_top_level_statement(OnyxParser* parser);
static AstPackage* parse_package_name(OnyxParser* parser);
if ((parser->curr + 1)->type != ':') return 0;
OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
- consume_token(parser);
- AstType* type_node = NULL;
+ expect_token(parser, ':');
+
+ if (parser->curr->type == ':') {
+ AstBinding* binding = parse_top_level_binding(parser, symbol);
+
+ bh_arr_push(parser->block_stack[0]->bindings, binding);
+ return 1;
+ }
// NOTE: var: type
- if (parser->curr->type != ':'
- && parser->curr->type != '=') {
+ AstType* type_node = NULL;
+ if (parser->curr->type != '=') {
type_node = parse_type(parser);
}
local->type_node = type_node;
*ret = (AstNode *) local;
- if (parser->curr->type == '=' || parser->curr->type == ':') {
- if (parser->curr->type == ':') {
- local->flags |= Ast_Flag_Const;
- }
-
+ if (parser->curr->type == '=') {
AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
assignment->operation = Binary_Op_Assign;
local->next = (AstNode *) assignment;
AstBlock* block = make_node(AstBlock, Ast_Kind_Block);
bh_arr_new(global_heap_allocator, block->allocate_exprs, 4);
+ bh_arr_push(parser->block_stack, block);
+
// NOTE: --- is for an empty block
if (parser->curr->type == Token_Type_Empty_Block) {
block->token = expect_token(parser, Token_Type_Empty_Block);
+ bh_arr_pop(parser->block_stack);
return block;
}
if (parser->curr->type == Token_Type_Keyword_Do) {
block->token = expect_token(parser, Token_Type_Keyword_Do);
block->body = parse_statement(parser);
+ bh_arr_pop(parser->block_stack);
return block;
}
if (parser->curr->type != '{') {
expect_token(parser, '{');
find_token(parser, '}');
+ bh_arr_pop(parser->block_stack);
return block;
}
block->token = expect_token(parser, '{');
expect_token(parser, '}');
+ bh_arr_pop(parser->block_stack);
return block;
}
}
}
+static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol) {
+ expect_token(parser, ':');
+
+ AstTyped* node = parse_top_level_expression(parser);
+ if (parser->hit_unexpected_token || node == NULL)
+ return NULL;
+
+ if (node->kind == Ast_Kind_Function) {
+ AstFunction* func = (AstFunction *) node;
+
+ if (func->exported_name == NULL)
+ func->exported_name = symbol;
+
+ func->name = symbol;
+
+ } else if (node->kind == Ast_Kind_Polymorphic_Proc) {
+ AstPolyProc* proc = (AstPolyProc *) node;
+
+ if (proc->base_func->exported_name == NULL)
+ proc->base_func->exported_name = symbol;
+
+ proc->base_func->name = symbol;
+
+ } else if (node->kind == Ast_Kind_Global) {
+ AstGlobal* global = (AstGlobal *) node;
+
+ if (global->exported_name == NULL)
+ global->exported_name = symbol;
+
+ global->name = symbol;
+
+ } else if (node->kind != Ast_Kind_Overloaded_Function
+ && node->kind != Ast_Kind_StrLit) {
+
+ if (node->kind == Ast_Kind_Struct_Type
+ || node->kind == Ast_Kind_Enum_Type
+ || node->kind == Ast_Kind_Poly_Struct_Type) {
+ ((AstStructType *)node)->name = bh_aprintf(global_heap_allocator,
+ "%b", symbol->text, symbol->length);
+ }
+
+ if (node->kind == Ast_Kind_Type_Alias) {
+ node->token = symbol;
+ }
+
+ // HACK
+ add_node_to_process(parser, (AstNode *) node);
+ }
+
+ AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
+ binding->token = symbol;
+ binding->node = (AstNode *) node;
+
+ return binding;
+}
+
// 'use' <string>
// <symbol> :: <expr>
static AstNode* parse_top_level_statement(OnyxParser* parser) {
expect_token(parser, ':');
if (parser->curr->type == ':') {
- expect_token(parser, ':');
-
- AstTyped* node = parse_top_level_expression(parser);
- if (parser->hit_unexpected_token || node == NULL)
- return NULL;
-
- node->flags |= private_kind;
-
- if (node->kind == Ast_Kind_Function) {
- AstFunction* func = (AstFunction *) node;
-
- if (func->exported_name == NULL)
- func->exported_name = symbol;
-
- func->name = symbol;
-
- } else if (node->kind == Ast_Kind_Polymorphic_Proc) {
- AstPolyProc* proc = (AstPolyProc *) node;
-
- if (proc->base_func->exported_name == NULL)
- proc->base_func->exported_name = symbol;
-
- proc->base_func->name = symbol;
-
- } else if (node->kind == Ast_Kind_Global) {
- AstGlobal* global = (AstGlobal *) node;
-
- if (global->exported_name == NULL)
- global->exported_name = symbol;
-
- global->name = symbol;
-
- } else if (node->kind != Ast_Kind_Overloaded_Function
- && node->kind != Ast_Kind_StrLit) {
-
- if (node->kind == Ast_Kind_Struct_Type
- || node->kind == Ast_Kind_Enum_Type
- || node->kind == Ast_Kind_Poly_Struct_Type) {
- ((AstStructType *)node)->name = bh_aprintf(global_heap_allocator,
- "%b", symbol->text, symbol->length);
- }
-
- if (node->kind == Ast_Kind_Type_Alias) {
- node->token = symbol;
- }
-
- // HACK
- add_node_to_process(parser, (AstNode *) node);
- }
-
- AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
- binding->token = symbol;
- binding->node = (AstNode *) node;
+ AstBinding* binding = parse_top_level_binding(parser, symbol);
+ binding->node->flags |= private_kind;
return (AstNode *) binding;
+
} else {
AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres);
memres->token = symbol;
parser.prev = NULL;
parser.program = program;
parser.hit_unexpected_token = 0;
+ parser.block_stack = NULL;
parser.results = (ParseResults) {
.allocator = global_heap_allocator,
.nodes_to_process = NULL,
-
};
parser.polymorph_context = (PolymorphicContext) {
.poly_params = NULL,
};
+ bh_arr_new(global_heap_allocator, parser.block_stack, 4);
bh_arr_new(parser.results.allocator, parser.results.nodes_to_process, 4);
return parser;
}
void onyx_parser_free(OnyxParser* parser) {
+ bh_arr_free(parser->block_stack);
}
ParseResults onyx_parse(OnyxParser *parser) {
scope_enter(block->scope);
bh_arr_push(semstate.block_stack, block);
+ bh_arr_each(AstBinding *, binding, block->bindings)
+ symbol_introduce(semstate.curr_scope, (*binding)->token, (*binding)->node);
+
if (block->body)
symres_statement_chain(&block->body);