-RELEASE=1
+RELEASE=0
OBJ_FILES=\
build/onyxlex.o \
#private
heap_resize :: proc (ptr: rawptr, new_size: u32, align: u32) -> rawptr {
hb_ptr := cast(^heap_block) (cast(u32) ptr - sizeof heap_block);
+ old_size := hb_ptr.size - sizeof heap_block;
// If there is already enough space in the current allocated block,
// just return the block that already exists and has the memory in it.
- if hb_ptr.size >= new_size do return ptr;
+ if old_size >= new_size do return ptr;
// 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 {
- hb_ptr.size = new_size;
+ hb_ptr.size = new_size + sizeof heap_block;
heap_state.next_alloc = cast(rawptr) (cast(u32) ptr + hb_ptr.size);
return ptr;
}
new_ptr := heap_alloc(new_size, align);
- memory_copy(new_ptr, ptr, hb_ptr.size);
+ memory_copy(new_ptr, ptr, old_size);
heap_free(ptr);
return new_ptr;
}
return arr.data[0 : arr.count];
}
+//
+// Simple insertion sort
+// cmp should return >0 if left > right
+//
+array_sort :: proc (arr: ^[..] $T, cmp: proc (T, T) -> i32) {
+ for i: 1, arr.count {
+ x := arr.data[i];
+ j := i - 1;
+
+ while j >= 0 && cmp(arr.data[j], x) > 0 {
+ arr.data[j + 1] = arr.data[j];
+ j -= 1;
+ }
+
+ arr.data[j + 1] = x;
+ }
+}
+
+cmp_asc :: proc (a: $T, b: T) -> i32 do return cast(i32) (a - b);
+cmp_dec :: proc (a: $T, b: T) -> i32 do return cast(i32) (b - a);
context : struct {
allocator : Allocator;
package memory
memory_copy :: proc (dst_: rawptr, src_: rawptr, len: u32) {
- if len % 8 == 0 {
- dst := cast(^u64) dst_;
- src := cast(^u64) src_;
- for i: 0, len >> 3 do dst[i] = src[i];
-
- } elseif len % 4 == 0 {
- dst := cast(^u32) dst_;
- src := cast(^u32) src_;
- for i: 0, len >> 2 do dst[i] = src[i];
-
- } else {
- dst := cast(^u8) dst_;
- src := cast(^u8) src_;
- for i: 0, len do dst[i] = src[i];
- }
+ dst := cast(^u8) dst_;
+ src := cast(^u8) src_;
+ for i: 0, len do dst[i] = src[i];
}
\ No newline at end of file
print_bool :: proc (b: bool) do string_builder_append(^cout_state.sb, b);
print_ptr :: proc (p: rawptr) do string_builder_append(^cout_state.sb, cast(i64) p, 16l);
+// This works on both slices and arrays
+print_array :: proc (arr: $T, sep := " ") {
+ for i: 0, arr.count {
+ print(arr.data[i]);
+ if i != arr.count - 1 do print(sep);
+ }
+
+ print("\n");
+}
+
print :: proc #overloaded {
print_string,
print_cstring,
return false;
}
+string_strip_leading_whitespace :: proc (str: ^string) {
+ while true do switch str.data[0] {
+ case #char " ", #char "\t", #char "\n", #char "\r" {
+ str.data += 1;
+ str.count -= 1;
+ }
+
+ case #default do return;
+ }
+}
+
+string_strip_trailing_whitespace :: proc (str: ^string) {
+ while true do switch str.data[str.count - 1] {
+ case #char " ", #char "\t", #char "\n", #char "\r" {
+ str.count -= 1;
+ }
+
+ case #default do return;
+ }
+}
+
+
+
+
+//
+// String Builder
+//
StringBuilder :: struct {
alloc : Allocator;
string_builder_clear :: proc (use sb: ^StringBuilder) -> ^StringBuilder {
len = 0;
return sb;
-}
\ No newline at end of file
+}
+
+string_read_u32 :: proc (str: ^string, out: ^u32) {
+ n := 0;
+
+ string_strip_leading_whitespace(str);
+ while str.data[0] >= #char "0" && str.data[0] <= #char "9" {
+ n *= 10;
+ n += cast(u32) (str.data[0] - #char "0");
+
+ str.data += 1;
+ str.count -= 1;
+ }
+
+ *out = n;
+}
+
+string_read_char :: proc (str: ^string, out: ^u8) {
+ *out = str.data[0];
+ str.data += 1;
+ str.count -= 1;
+}
+
+// Goes up to but not including the closest newline or EOF
+string_read_line :: proc (str: ^string, out: ^string) {
+ out.data = str.data;
+ out.count = 0;
+
+ for i: 0, str.count {
+ if str.data[i] == #char "\n" do break;
+ out.count += 1;
+ }
+
+ str.data += out.count;
+ str.count -= out.count;
+}
+
+string_advance_line :: proc (str: ^string) {
+ adv := 0;
+ while str.data[adv] != #char "\n" do adv += 1;
+
+ str.data += adv;
+ str.count -= adv;
+}
+
+string_read :: proc #overloaded {
+ string_read_u32, string_read_char
+}
args_sizes_get(^argc, ^argv_buf_size);
- argv := cast(^^u8) calloc(sizeof ^u8 * argc);
- argv_buf := cast(^u8) calloc(argv_buf_size);
+ argv := cast(^cstring) calloc(sizeof cstring * argc);
+ argv_buf := cast(cstring) calloc(argv_buf_size);
args_get(argv, argv_buf);
- add infos to warnings
- no more preformatted strings, just write them inline ffs
- [ ] #file and #line directives
+ [X] #file and #line directives
- string and u32 respectively that represent the current file and line number where the directive is
[ ] transmute
AstTyped* ast_reduce(bh_allocator a, AstTyped* node);
AstNode* ast_clone(bh_allocator a, void* n);
void promote_numlit_to_larger(AstNumLit* num);
-AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, AstCall* call);
+
+typedef enum PolyProcLookupMethod {
+ PPLM_By_Call,
+ PPLM_By_Function_Type,
+} PolyProcLookupMethod;
+AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxFilePos pos);
// NOTE: Useful inlined functions
static inline b32 is_lval(AstNode* node) {
print("\n\n");
}
-// This works on both slices and arrays
-print_arr :: proc (arr: $T, sep := " ") {
- for i: 0, arr.count {
- print(arr.data[i]);
- if i != arr.count - 1 do print(sep);
- }
-
- print("\n");
-}
-
print_vec :: proc (v: Vec3) #add_overload print {
print("Vec3(");
print(v.x);
SOA :: struct {
- a : [..] i32;
b : [..] i64;
+ a : [..] i32;
}
soa_init :: proc (s: ^SOA) {
array_free(^s.b);
}
-main :: proc (args: [] cstring) {
+main2 :: proc (args: [] cstring) {
s : SOA;
soa_init(^s);
defer soa_deinit(^s);
array_push(^s.b, 3l * cast(i64) i);
}
- print_arr(^s.a);
- array_to_slice(^s.a) |> print_arr();
- array_to_slice(^s.b) |> print_arr();
+ array_sort(^s.a, cmp_dec);
+ array_sort(^s.b, cmp_asc);
+
+ print_array(^s.a);
+ print_array(^s.b);
print("After adding...\n");
print_arr_details(^s.a);
print_arr_details(^s.b);
}
-main2 :: proc (args: [] cstring) {
+main :: proc (args: [] cstring) {
print(cast(u32) __heap_start, 16);
iarr_backing : [32] i32;
for i: 0, 12 do array_push(^iarr, i % 5);
print_arr_details(^iarr);
- print_arr(^iarr);
+ print_array(^iarr);
array_delete(^iarr, 4);
print_arr_details(^iarr);
- print_arr(^iarr);
+ print_array(^iarr);
array_remove(^iarr, 1);
array_remove(^iarr, 4);
print_arr_details(^iarr);
- print_arr(^iarr);
+ print_array(^iarr);
array_insert(^iarr, 2, 5678);
print_arr_details(^iarr);
- print_arr(^iarr);
+ print_array(^iarr);
print(array_average(^iarr));
print("\n");
}
print_arr_details(^barr);
- print_arr(^barr);
+ print_array(^barr);
array_average(^barr) |> print();
print("\n");
}
array_push(^varr, Vec3.{ 4, 2, 3 });
+ array_sort(^varr, cmp_vec3);
print_arr_details(^varr);
- print_arr(varr, "\n");
+ print_array(varr, "\n");
dummy := Dummy.{ data = calloc(sizeof [5] i32) };
for i: 0, dummy.count do dummy.data[i] = i * 5;
- print_arr(dummy);
+ print_array(dummy);
print(get_count(iarr));
print("\n");
y: i32;
z: i32;
}
+
+cmp_vec3 :: proc (v1: Vec3, v2: Vec3) -> i32 {
+ if v1.x != v2.x do return v1.x - v2.x;
+ if v1.y != v2.y do return v1.y - v2.y;
+ return v1.z - v2.z;
+}
\ No newline at end of file
}
b32 check_call(AstCall* call) {
+ // All the things that need to be done when checking a call node.
+ // 1. Ensure the callee is not a symbol
+ // 2. Check the callee expression (since it could be a variable or a field access, etc)
+ // 3. Check all arguments
+ // * Cannot pass overloaded functions
+ // * Cannot pass a non-simple struct
+ // 4. If callee is an overloaded function, use the argument types to determine which overload is used.
+ // 5. If callee is polymorphic, use the arguments type to generate a polymorphic function.
+ // 6. If an argument is polymorphic, generate the correct polymorphic function.
+ // 7. Fill in default arguments
+ // 8. If callee is an intrinsic, turn call into an Intrinsic_Call node
+ // 9. Check types of formal and actual params against each other
+
+
+
AstFunction* callee = (AstFunction *) call->callee;
if (callee->kind == Ast_Kind_Symbol) {
if (check_expression(&call->callee)) return 1;
+ b32 has_polymorphic_args = 0;
+
// NOTE: Check arguments
- AstNode** prev_param = (AstNode **) &call->arguments;
- AstArgument* actual_param = call->arguments;
- while (actual_param != NULL) {
- if (check_expression((AstTyped **) &actual_param)) return 1;
+ AstArgument* actual = call->arguments;
+ while (actual != NULL) {
+ if (check_expression((AstTyped **) &actual)) return 1;
- if (actual_param->value->kind == Ast_Kind_Overloaded_Function) {
- onyx_report_error(actual_param->token->pos, "Cannot pass overloaded functions as parameters.");
+ if (actual->value->kind == Ast_Kind_Overloaded_Function) {
+ onyx_report_error(actual->token->pos, "Cannot pass overloaded functions as parameters.");
return 1;
}
- if (type_is_structlike_strict(actual_param->value->type)) {
- if (!type_structlike_is_simple(actual_param->value->type)) {
- onyx_report_error(actual_param->token->pos,
+ if (type_is_structlike_strict(actual->value->type)) {
+ if (!type_structlike_is_simple(actual->value->type)) {
+ onyx_report_error(actual->token->pos,
"Can only pass simple structs as parameters (no nested structures). passing by pointer is the only way for now.");
return 1;
}
}
- prev_param = (AstNode **) &actual_param->next;
- actual_param = (AstArgument *) actual_param->next;
+ if (actual->value->kind == Ast_Kind_Polymorphic_Proc)
+ has_polymorphic_args = 1;
+
+ actual = (AstArgument *) actual->next;
}
if (callee->kind == Ast_Kind_Overloaded_Function) {
}
if (callee->kind == Ast_Kind_Polymorphic_Proc) {
- call->callee = (AstTyped *) polymorphic_proc_lookup((AstPolyProc *) call->callee, call);
+ call->callee = (AstTyped *) polymorphic_proc_lookup(
+ (AstPolyProc *) call->callee,
+ PPLM_By_Call,
+ call,
+ call->token->pos);
+
if (call->callee == NULL) return 1;
callee = (AstFunction *) call->callee;
return 1;
}
+ if (has_polymorphic_args) {
+ actual = call->arguments;
+ u32 arg_idx = 0;
+
+ while (actual != NULL) {
+ if (actual->value->kind == Ast_Kind_Polymorphic_Proc) {
+ actual->value = (AstTyped *) polymorphic_proc_lookup(
+ (AstPolyProc *) actual->value,
+ PPLM_By_Function_Type,
+ callee->type->Function.params[arg_idx],
+ actual->token->pos);
+
+ if (actual->value == NULL) return 1;
+
+ actual->type = actual->value->type;
+ actual->value->flags |= Ast_Flag_Function_Used;
+ }
+
+ actual = (AstArgument *) actual->next;
+ arg_idx++;
+ }
+ }
+
if (callee->kind == Ast_Kind_Function) {
if (call->arg_count < bh_arr_length(callee->params)) {
AstArgument** last_arg = &call->arguments;
call->type = callee->type->Function.return_type;
Type **formal_params = callee->type->Function.params;
- actual_param = call->arguments;
+ actual = call->arguments;
i32 arg_pos = 0;
- while (arg_pos < callee->type->Function.param_count && actual_param != NULL) {
- if (!types_are_compatible(formal_params[arg_pos], actual_param->type)) {
- onyx_report_error(actual_param->token->pos,
+ while (arg_pos < callee->type->Function.param_count && actual != NULL) {
+ if (!types_are_compatible(formal_params[arg_pos], actual->type)) {
+ onyx_report_error(actual->token->pos,
"The function '%b' expects a value of type '%s' for parameter '%d', got '%s'.",
callee->token->text, callee->token->length,
type_get_name(formal_params[arg_pos]),
arg_pos,
- type_get_name(actual_param->type));
+ type_get_name(actual->type));
return 1;
}
arg_pos++;
- actual_param = (AstArgument *) actual_param->next;
+ actual = (AstArgument *) actual->next;
}
if (arg_pos < callee->type->Function.param_count) {
return 1;
}
- if (actual_param != NULL) {
+ if (actual != NULL) {
onyx_report_error(call->token->pos, "Too many arguments to function call.");
return 1;
}
}
if (!type_is_numeric(binop->left->type) && !type_is_pointer(binop->left->type)) {
- onyx_report_error(binop->token->pos, "Expected numeric or pointer type for left side of binary operator.");
+ onyx_report_error(binop->token->pos,
+ "Expected numeric or pointer type for left side of binary operator, got '%s'.",
+ type_get_name(binop->left->type));
return 1;
}
if (!type_is_numeric(binop->right->type)) {
- onyx_report_error(binop->token->pos, "Expected numeric type for right side of binary operator.");
+ onyx_report_error(binop->token->pos,
+ "Expected numeric type for right side of binary operator, got '%s'.",
+ type_get_name(binop->right->type));
return 1;
}
case Ast_Kind_NumLit:
case Ast_Kind_StrLit:
case Ast_Kind_Package:
- case Ast_Kind_Function_Type:
case Ast_Kind_Struct_Type:
case Ast_Kind_Enum_Type:
case Ast_Kind_Enum_Value:
}
}
-static inline i32 ast_kind_to_size(AstKind kind) {
- switch (kind) {
+static inline i32 ast_kind_to_size(AstNode* node) {
+ switch (node->kind) {
case Ast_Kind_Error: return sizeof(AstNode);
case Ast_Kind_Program: return sizeof(AstNode);
case Ast_Kind_Package: return sizeof(AstPackage);
case Ast_Kind_Type: return sizeof(AstType);
case Ast_Kind_Basic_Type: return sizeof(AstBasicType);
case Ast_Kind_Pointer_Type: return sizeof(AstPointerType);
- case Ast_Kind_Function_Type: return sizeof(AstFunctionType);
+ case Ast_Kind_Function_Type: return sizeof(AstFunctionType) + ((AstFunctionType *) node)->param_count * sizeof(AstType *);
case Ast_Kind_Array_Type: return sizeof(AstArrayType);
case Ast_Kind_Slice_Type: return sizeof(AstSliceType);
case Ast_Kind_DynArr_Type: return sizeof(AstDynArrType);
if (node == NULL) return NULL;
if (!should_clone(node)) return node;
- i32 node_size = ast_kind_to_size(node->kind);
+ i32 node_size = ast_kind_to_size(node);
// bh_printf("Cloning %s with size %d\n", onyx_ast_node_kind_string(node->kind), node_size);
AstNode* nn = onyx_ast_node_new(a, node_size, node->kind);
memmove(nn, node, node_size);
- switch (node->kind) {
+ switch ((u16) node->kind) {
case Ast_Kind_Binary_Op:
((AstBinaryOp *) nn)->left = (AstTyped *) ast_clone(a, ((AstBinaryOp *) node)->left);
((AstBinaryOp *) nn)->right = (AstTyped *) ast_clone(a, ((AstBinaryOp *) node)->right);
((AstTypeAlias *) nn)->to = (AstType *) ast_clone(a, ((AstTypeAlias *) node)->to);
break;
+ case Ast_Kind_Function_Type:
+ ((AstFunctionType *) nn)->return_type = (AstType *) ast_clone(a, ((AstFunctionType *) node)->return_type);
+ ((AstFunctionType *) nn)->param_count = ((AstFunctionType *) node)->param_count;
+ fori (i, 0, ((AstFunctionType *) nn)->param_count) {
+ ((AstFunctionType *) nn)->params[i] = (AstType *) ast_clone(a, ((AstFunctionType *) node)->params[i]);
+ }
+ break;
+
case Ast_Kind_Binding:
bh_printf("Cloning binding: %b\n", node->token->text, node->token->length);
((AstTyped *) nn)->type_node = (AstType *) ast_clone(a, ((AstTyped *) node)->type_node);
break;
}
-
- case Ast_Kind_NumLit:
- case Ast_Kind_StrLit:
- case Ast_Kind_File_Contents:
- case Ast_Kind_Jump:
- case Ast_Kind_Type:
- case Ast_Kind_Basic_Type:
- case Ast_Kind_Struct_Member:
- return nn;
-
- //default: {
- // AstNode* new_node = make_node(a, AstNode, node->kind);
- // new_node->token = node->token;
- // new_node->flags = node->flags;
- // new_node->next = ast_clone(a, node->next);
- //}
}
return nn;
return NULL;
}
-AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, AstCall* call) {
+AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxFilePos pos) {
if (pp->concrete_funcs == NULL) {
bh_table_init(global_heap_allocator, pp->concrete_funcs, 8);
}
scope_clear(pp->poly_scope);
bh_arr_each(AstPolyParam, param, pp->poly_params) {
- AstArgument* arg = call->arguments;
- if (param->idx >= call->arg_count) {
- onyx_report_error(call->token->pos, "Not enough arguments to polymorphic procedure.");
- return NULL;
+ Type* actual_type;
+ if (pp_lookup == PPLM_By_Call) {
+ AstArgument* arg = ((AstCall *) actual)->arguments;
+ if (param->idx >= ((AstCall *) actual)->arg_count) {
+ onyx_report_error(pos, "Not enough arguments to polymorphic procedure.");
+ return NULL;
+ }
+
+ fori (i, 0, param->idx) arg = (AstArgument *) arg->next;
+ actual_type = arg->type;
}
- fori (i, 0, param->idx) arg = (AstArgument *) arg->next;
- Type* arg_type = arg->type;
+ else if (pp_lookup == PPLM_By_Function_Type) {
+ Type* ft = (Type*) actual;
+ if (param->idx >= ft->Function.param_count) {
+ onyx_report_error(pos, "Incompatible polymorphic argument to function paramter.");
+ return NULL;
+ }
- Type* resolved_type = solve_poly_type(param->poly_sym, param->type_expr, arg_type);
+ actual_type = ft->Function.params[param->idx];
+ }
+
+ else {
+ onyx_report_error(pos, "Cannot resolve polymorphic function type.");
+ return NULL;
+ }
+
+ Type* resolved_type = solve_poly_type(param->poly_sym, param->type_expr, actual_type);
if (resolved_type == NULL) {
- onyx_report_error(call->token->pos, "Unable to match polymorphic procedure type.");
+ if (pp_lookup == PPLM_By_Call) {
+ onyx_report_error(pos, "Unable to match polymorphic procedure type.");
+ }
+ else if (pp_lookup == PPLM_By_Function_Type) {
+ onyx_report_error(pos, "Unable to match polymorphic procedure type.");
+ }
return NULL;
}
static char key_buf[1024];
fori (i, 0, 1024) key_buf[i] = 0;
bh_table_each_start(AstNode *, pp->poly_scope->symbols);
- strncat(key_buf, bh_bprintf("%s=", key), 1023);
+ strncat(key_buf, key, 1023);
+ strncat(key_buf, "=", 1023);
strncat(key_buf, type_get_name(((AstTypeRawAlias *) value)->to), 1023);
strncat(key_buf, ";", 1023);
bh_table_each_end;
goto no_errors;
has_error:
- onyx_report_error(call->token->pos, "Error in polymorphic procedure generated from this call site.");
+ onyx_report_error(pos, "Error in polymorphic procedure generated from this call site.");
return NULL;
no_errors:
}
}
-static inline b32 should_EMIT_FUNCtion(AstFunction* fd) {
+static inline b32 should_emit_function(AstFunction* fd) {
// NOTE: Don't output intrinsic functions
if (fd->flags & Ast_Flag_Intrinsic) return 0;
}
-static void EMIT_FUNCtion(OnyxWasmModule* mod, AstFunction* fd) {
- if (!should_EMIT_FUNCtion(fd)) return;
+static void emit_function(OnyxWasmModule* mod, AstFunction* fd) {
+ if (!should_emit_function(fd)) return;
i32 type_idx = generate_type_idx(mod, fd->type);
switch (entity->type) {
case Entity_Type_Function_Header: {
- if (!should_EMIT_FUNCtion(entity->function)) break;
+ if (!should_emit_function(entity->function)) break;
u64 func_idx;
if ((entity->function->flags & Ast_Flag_Foreign) != 0)
break;
}
- case Entity_Type_Function: EMIT_FUNCtion(module, entity->function); break;
+ case Entity_Type_Function: emit_function(module, entity->function); break;
case Entity_Type_Global: emit_global(module, entity->global); break;
default: break;