const data = new Uint8Array(wasm_instance.exports.memory.buffer, ptr, len);
const str = new TextDecoder().decode(data);
process.stdout.write(str);
+ },
+
+ exit(status) {
+ process.exit(status);
}
}
}
OnyxContext :: struct {
allocator : Allocator;
temp_allocator : Allocator;
+
+ assert_handler : proc (msg: str, file: str);
}
context : OnyxContext;
+assert :: proc (cond: bool, msg: str, file: str = str.{ null, 0 }) {
+ if !cond do context.assert_handler(msg, file);
+}
+
//
// Basic allocation structures.
Resize;
}
-allocator_proc :: #type proc (rawptr, AllocationAction, u32, u32, rawptr) -> rawptr;
+allocator_proc :: #type proc (data: rawptr, action: AllocationAction, size: u32, align: u32, old_ptr: rawptr) -> rawptr;
Allocator :: struct {
data: rawptr;
--- /dev/null
+package core.io
+
+Error :: enum {
+ None :: 0x00;
+
+ // The procedure is not implemented for this kind of stream.
+ NotImplemented :: 0x01;
+
+ // The stream reached the end.
+ EOF :: 0x02;
+
+ // The vtable was not set for this stream.
+ NoVtable :: 0x03;
+
+ // A seek was outside the bounds of the stream.
+ OutOfBounds :: 0x04;
+}
\ No newline at end of file
--- /dev/null
+package core.io
+
+Reader :: struct {
+ stream : ^Stream;
+}
+
+reader_make :: proc (s: ^Stream) -> Reader {
+ assert(s.vtable != null, "Stream vtable was not setup correctly.");
+
+ return Reader.{ s };
+}
+
+read_u32 :: proc (use reader: ^Reader) -> u32 {
+ n: u32 = 0;
+
+ skip_whitespace(reader);
+
+ curr := cast(u8) 0;
+ stream_peek_byte(stream, ^curr);
+ while curr >= #char "0" && curr <= #char "9" {
+ stream_read_byte(stream, ^curr);
+
+ n *= 10;
+ n += cast(u32) (curr - #char "0");
+
+ if stream_peek_byte(stream, ^curr) == Error.EOF do break;
+ }
+
+ return n;
+}
+
+read_u64 :: proc (use reader: ^Reader) -> u64 {
+ n: u64 = 0;
+
+ skip_whitespace(reader);
+
+ curr := cast(u8) 0;
+ stream_peek_byte(stream, ^curr);
+ while curr >= #char "0" && curr <= #char "9" {
+ stream_read_byte(stream, ^curr);
+
+ n *= 10;
+ n += cast(u64) (curr - #char "0");
+
+ if stream_peek_byte(stream, ^curr) == Error.EOF do break;
+ }
+
+ return n;
+}
+
+read_line :: proc (use reader: ^Reader, allocator := context.allocator) -> str {
+ curr_pos : i32;
+ stream_tell(stream, ^curr_pos);
+
+ count := 0;
+ curr := cast(u8) 0;
+ stream_read_byte(stream, ^curr);
+ while curr != #char "\n" {
+ count += 1;
+ if stream_read_byte(stream, ^curr) != Error.None do break;
+ }
+
+ stream_seek(stream, curr_pos, SeekFrom.Start);
+
+ out := str.{
+ data = raw_alloc(allocator, count * sizeof(u8)),
+ count = count,
+ };
+
+ stream_read(stream, out);
+ return out;
+}
+
+read_word :: proc (use reader: ^Reader, allocator := context.allocator) -> str {
+ skip_whitespace(reader);
+
+ curr_pos : i32;
+ stream_tell(stream, ^curr_pos);
+
+ count := 0;
+ curr := cast(u8) 0;
+ stream_read_byte(stream, ^curr);
+
+ while true {
+ if (curr >= #char "a" && curr <= #char "z")
+ || (curr >= #char "A" && curr <= #char "Z")
+ || curr == #char "_" {
+ count += 1;
+ } else {
+ break;
+ }
+
+ if stream_read_byte(stream, ^curr) != Error.None do break;
+ }
+
+ stream_seek(stream, curr_pos, SeekFrom.Start);
+
+ out := str.{
+ data = raw_alloc(allocator, count * sizeof(u8)),
+ count = count,
+ };
+
+ stream_read(stream, out);
+ return out;
+}
+
+advance_line :: proc (use reader: ^Reader) {
+ curr := cast(u8) 0;
+ if stream_read_byte(stream, ^curr) != Error.None do return;
+ while curr != #char "\n" {
+ if stream_read_byte(stream, ^curr) != Error.None do return;
+ }
+
+ stream_read_byte(stream, ^curr);
+}
+
+skip_whitespace :: proc (use reader: ^Reader) {
+ while true {
+ byte := cast(u8) 0;
+ if stream_peek_byte(stream, ^byte) == Error.EOF do break;
+
+ switch byte {
+ case #char " ", #char "\t", #char "\n", #char "\r" {
+ stream_read_byte(stream, ^byte);
+ }
+
+ case #default do return;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+package core.io
+
+use package core
+
+Stream :: struct {
+ vtable : ^Stream_Vtable;
+}
+
+#private
+Stream_Vtable :: struct {
+ seek : proc (s: ^Stream, to: i32, whence: SeekFrom) -> Error;
+ tell : proc (s: ^Stream, out: ^i32) -> Error;
+
+ read : proc (s: ^Stream, buffer: [] u8, number_read: ^u32) -> Error;
+ read_at : proc (s: ^Stream, at: u32, buffer: [] u8, number_read: ^u32) -> Error;
+ read_byte : proc (s: ^Stream, out: ^u8) -> Error;
+ unread_byte : proc (s: ^Stream) -> Error;
+
+ write : proc (s: ^Stream, buffer: [] u8, number_written: ^u32) -> Error;
+ write_at : proc (s: ^Stream, at: u32, buffer: [] u8, number_written: ^u32) -> Error;
+ write_byte : proc (s: ^Stream, byte: u8) -> Error;
+
+ close : proc (s: ^Stream) -> Error;
+ flush : proc (s: ^Stream) -> Error;
+
+ size : proc (s: ^Stream) -> i32;
+}
+
+SeekFrom :: enum {
+ Start :: 0x00;
+ Current :: 0x01;
+ End :: 0x02;
+}
+
+stream_seek :: proc (use s: ^Stream, to: i32, whence: SeekFrom) -> Error {
+ if vtable == null do return Error.NoVtable;
+ if vtable.seek == null_proc do return Error.NotImplemented;
+
+ return vtable.seek(s, to, whence);
+}
+
+stream_tell :: proc (use s: ^Stream, out: ^i32) -> Error {
+ if vtable == null do return Error.NoVtable;
+ if vtable.tell == null_proc do return Error.NotImplemented;
+
+ return vtable.tell(s, out);
+}
+
+stream_read :: proc (use s: ^Stream, buffer: [] u8, number_read: ^u32 = null) -> Error {
+ if vtable == null do return Error.NoVtable;
+ if vtable.read == null_proc do return Error.NotImplemented;
+
+ return vtable.read(s, buffer, number_read);
+}
+
+stream_read_at :: proc (use s: ^Stream, at: u32, buffer: [] u8, number_read: ^u32 = null) -> Error {
+ if vtable == null do return Error.NoVtable;
+ if vtable.read_at == null_proc do return Error.NotImplemented;
+
+ return vtable.read_at(s, at, buffer, number_read);
+}
+
+stream_read_byte :: proc (use s: ^Stream, out: ^u8) -> Error {
+ if vtable == null do return Error.NoVtable;
+ if vtable.read_byte == null_proc do return Error.NotImplemented;
+
+ return vtable.read_byte(s, out);
+}
+
+stream_unread_byte :: proc (use s: ^Stream) -> Error {
+ if vtable == null do return Error.NoVtable;
+ if vtable.unread_byte == null_proc do return Error.NotImplemented;
+
+ return vtable.unread_byte(s);
+}
+
+stream_write :: proc (use s: ^Stream, buffer: [] u8, number_written: ^u32 = null) -> Error {
+ if vtable == null do return Error.NoVtable;
+ if vtable.write == null_proc do return Error.NotImplemented;
+
+ return vtable.write(s, buffer, number_written);
+}
+
+stream_write_at :: proc (use s: ^Stream, at: u32, buffer: [] u8, number_written: ^u32 = null) -> Error {
+ if vtable == null do return Error.NoVtable;
+ if vtable.write_at == null_proc do return Error.NotImplemented;
+
+ return vtable.write_at(s, at, buffer, number_written);
+}
+
+stream_write_byte :: proc (use s: ^Stream, byte: u8) -> Error {
+ if vtable == null do return Error.NoVtable;
+ if vtable.write_byte == null_proc do return Error.NotImplemented;
+
+ return vtable.write_byte(s, byte);
+}
+
+stream_close :: proc (use s: ^Stream) -> Error {
+ if vtable == null do return Error.NoVtable;
+ if vtable.close == null_proc do return Error.NotImplemented;
+
+ return vtable.close(s);
+}
+
+stream_flush :: proc (use s: ^Stream) -> Error {
+ if vtable == null do return Error.NoVtable;
+ if vtable.flush == null_proc do return Error.NotImplemented;
+
+ return vtable.flush(s);
+}
+
+stream_size :: proc (use s: ^Stream) -> i32 {
+ if vtable == null do return 0;
+ if vtable.size == null_proc do return 0;
+
+ return vtable.size(s);
+}
+
+stream_peek_byte :: proc (use s: ^Stream, out: ^u8) -> Error {
+ if err := stream_read_byte(s, out); err != Error.None do return err;
+ if err := stream_unread_byte(s); err != Error.None do return err;
+ return Error.None;
+}
+
+
+
+StringStream :: struct {
+ use stream : Stream;
+
+ curr_pos : i32;
+ data : str;
+}
+
+string_stream_make :: proc (s: str) -> StringStream {
+ return StringStream.{
+ stream = Stream.{
+ vtable = ^string_stream_vtable,
+ },
+ data = s,
+ curr_pos = 0,
+ };
+}
+
+
+#private
+string_stream_vtable := Stream_Vtable.{
+ seek = proc (s: ^Stream, to: i32, whence: SeekFrom) -> Error {
+ ss : ^StringStream = ~~s;
+ use ss;
+
+ if to >= data.count do return Error.OutOfBounds;
+
+ switch whence {
+ case SeekFrom.Start do curr_pos = to;
+ case SeekFrom.Current do curr_pos += to;
+ case SeekFrom.End do curr_pos = data.count - to; // CHECK: Off by one?
+ }
+
+ return Error.None;
+ },
+
+ tell = proc (s: ^Stream, out: ^i32) -> Error {
+ ss : ^StringStream = ~~s;
+ use ss;
+
+ if out != null do *out = curr_pos;
+ return Error.None;
+ },
+
+ read = proc (s: ^Stream, buffer: [] u8, number_read: ^u32) -> Error {
+ ss : ^StringStream = ~~s;
+ use ss;
+
+ if curr_pos >= data.count do return Error.EOF;
+
+ bytes_to_read := math.min(buffer.count, data.count - curr_pos);
+ memory.copy(buffer.data, ^data.data[curr_pos], bytes_to_read);
+ curr_pos += bytes_to_read;
+
+ if number_read != null do *number_read = bytes_to_read;
+ return Error.None;
+ },
+
+ read_at = proc (s: ^Stream, at: u32, buffer: [] u8, number_read: ^u32) -> Error {
+ ss : ^StringStream = ~~s;
+ use ss;
+
+ if curr_pos >= data.count do return Error.EOF;
+
+ bytes_to_read := math.min(buffer.count, data.count - at);
+ memory.copy(buffer.data, ^data.data[at], bytes_to_read);
+ curr_pos += bytes_to_read;
+
+ if number_read != null do *number_read = bytes_to_read;
+ return Error.None;
+ },
+
+ read_byte = proc (s: ^Stream, out: ^u8) -> Error {
+ ss : ^StringStream = ~~s;
+ use ss;
+
+ if curr_pos >= data.count do return Error.EOF;
+
+ if out != null do *out = data[curr_pos];
+
+ curr_pos += 1;
+ return Error.None;
+ },
+
+ unread_byte = proc (s: ^Stream) -> Error {
+ ss : ^StringStream = ~~s;
+ use ss;
+
+ if curr_pos <= 0 do return Error.OutOfBounds;
+
+ curr_pos -= 1;
+ return Error.None;
+ },
+
+ write = proc (s: ^Stream, buffer: [] u8, number_written: ^u32) -> Error {
+ ss : ^StringStream = ~~s;
+ use ss;
+
+ if curr_pos >= data.count do return Error.EOF;
+
+ bytes_to_write := math.min(buffer.count, data.count - curr_pos);
+ memory.copy(^data.data[curr_pos], buffer.data, bytes_to_write);
+ curr_pos += bytes_to_write;
+
+ if number_written != null do *number_written = bytes_to_write;
+ return Error.None;
+ },
+
+ write_at = proc (s: ^Stream, at: u32, buffer: [] u8, number_written: ^u32) -> Error {
+ ss : ^StringStream = ~~s;
+ use ss;
+
+ if curr_pos >= data.count do return Error.EOF;
+
+ bytes_to_write := math.min(buffer.count, data.count - at);
+ memory.copy(^data.data[at], buffer.data, bytes_to_write);
+ curr_pos += bytes_to_write;
+
+ if number_written != null do *number_written = bytes_to_write;
+ return Error.None;
+ },
+
+ write_byte = proc (s: ^Stream, byte: u8) -> Error {
+ ss : ^StringStream = ~~s;
+ use ss;
+
+ if curr_pos >= data.count do return Error.EOF;
+
+ data[curr_pos] = byte;
+ curr_pos += 1;
+
+ return Error.None;
+ },
+
+ size = proc (s: ^Stream) -> i32 {
+ ss : ^StringStream = ~~s;
+ use ss;
+
+ return data.count;
+ },
+
+ close = null_proc,
+ flush = null_proc,
+}
\ No newline at end of file
#load "core/string/builder"
#load "core/string/reader"
+#load "core/io/io"
+#load "core/io/stream"
+#load "core/io/reader"
+
#load "core/sys/js"
#load "core/string/reader"
#load "core/wasi"
+#load "core/io/io"
+#load "core/io/stream"
+#load "core/io/reader"
+
#load "core/sys/wasi"
output_str :: proc (s: str) -> u32 #foreign "host" "print_str" ---
+assert_handler :: proc (msg: str, file: str) {
+ output_str("Assert failed: ");
+ output_str(msg);
+
+ if file.data != null {
+ output_str(" in ");
+ output_str(file);
+ }
+
+ process_exit :: proc (status: i32) #foreign "host" "exit" ---
+ process_exit(1);
+}
+
// The builtin _start proc.
// Sets up everything needed for execution.
proc () #export "_start" {
context.allocator = alloc.heap_allocator;
context.temp_allocator = alloc.temp_allocator;
+ context.assert_handler = assert_handler;
args : [] cstr;
args.data = null;
return tmp;
}
+assert_handler :: proc (msg: str, file: str) {
+ output_str("Assert failed: ");
+ output_str(msg);
+
+ if file.data != null {
+ output_str(" in ");
+ output_str(file);
+ }
+
+ proc_exit(1);
+}
+
// The builtin _start proc.
// Sets up everything needed for execution.
proc () #export "_start" {
context.allocator = alloc.heap_allocator;
context.temp_allocator = alloc.heap_allocator;
+ context.assert_handler = assert_handler;
argc : Size;
argv_buf_size : Size;
struct AstTyped** initial_value;
b32 included_through_use : 1;
+ b32 used : 1;
} StructMember;
#define TYPE_KINDS \
}
+ case '(': {
+ expect_token(parser, '(');
+
+ *next_insertion = parse_type(parser);
+ next_insertion = NULL;
+
+ expect_token(parser, ')');
+ break;
+ }
+
default:
onyx_report_error(parser->curr->pos, "unexpected token '%b'.", parser->curr->text, parser->curr->length);
consume_token(parser);
case Type_Kind_Pointer: {
if (t2->kind == Type_Kind_Pointer) {
- return types_are_compatible(t1->Pointer.elem, t2->Pointer.elem);
+ if (types_are_compatible(t1->Pointer.elem, t2->Pointer.elem)) return 1;
+
+ if (t1->Pointer.elem->kind == Type_Kind_Struct && t2->Pointer.elem->kind == Type_Kind_Struct) {
+ Type* t1_struct = t1->Pointer.elem;
+ Type* t2_struct = t2->Pointer.elem;
+
+ if (t1_struct->Struct.memarr[0]->used)
+ return types_are_compatible(t2_struct, t1_struct->Struct.memarr[0]->type);
+ }
}
if (t2->kind == Type_Kind_Basic && t2->Basic.kind == Basic_Kind_Rawptr) return 1;
.name = bh_strdup(alloc, (*member)->token->text),
.initial_value = &(*member)->initial_value,
.included_through_use = 0,
+ .used = (((*member)->flags & Ast_Flag_Struct_Mem_Used) != 0),
};
bh_table_put(StructMember, s_type->Struct.members, (*member)->token->text, smem);
.name = (*psmem)->name,
.initial_value = (*psmem)->initial_value,
.included_through_use = 1,
+ .used = 0,
};
bh_table_put(StructMember, s_type->Struct.members, (*psmem)->name, new_smem);
+++ /dev/null
-package main
-
-#load "core/std/js"
-
-use package core
-
-print_arr_details :: proc (arr: ^[..] $T) {
- print("\nArray details:\n\tSize: ");
- println(arr.count);
- print("\tCapacity: ");
- println(arr.capacity);
- print("\tData ptr: ");
- println(cast(^void) arr.data);
- print("\tSize of elements: ");
- println(sizeof T);
- print("\tAlignment of elements: ");
- println(alignof T);
- print("\n");
-}
-
-print_vec :: proc (v: Vec3) #add_overload print {
- print("Vec3(");
- print(v.x);
- print(", ");
- print(v.y);
- print(", ");
- print(v.z);
- print(")");
-}
-
-// This demonstrates that we have something similar to static 'duck' typing.
-get_count :: proc (x: $T) -> u32 do return x.count;
-
-
-// Because of the static duck typing, this will pass as an
-// array/slice in most places.
-Dummy :: struct {
- count : u32 = 5;
- data : [5] u32;
-}
-
-
-/* This demos some of the power you have with the polymorphic types */
-compose :: proc (a: A, f: proc ($A) -> $B, g: proc (B) -> $C) -> C {
- return a |> f() |> g();
-}
-
-
-SOA :: struct {
- a : [..] i32;
- b : [..] i64;
- c : [..] Vec3;
-}
-
-soa_init :: proc (s: ^SOA) {
- array.init(^s.a);
- array.init(^s.b);
- array.init(^s.c);
-}
-
-soa_deinit :: proc (s: ^SOA) {
- array.free(^s.a);
- array.free(^s.b);
- array.free(^s.c);
-}
-
-get_range :: proc (arr: ^[..] $T) -> range {
- return 0 .. arr.count;
-}
-
-// NOTE: This function will be very useful for for loops. i.e.
-// for i: 0 .. 100 |> by(2) {
-// ...
-// }
-by :: proc (r: range, s: u32) -> range {
- return range.{ low = r.low, high = r.high, step = s };
-}
-
-switch_demo :: proc () {
- switch a := 4; a {
- case 4, 5, 6 {
- println("a was 4, 5, or 6");
- fallthrough fallthrough;
- }
-
- case 10 do println("a was 10");
-
- case #default {
- println("a was something else.");
- }
- }
-}
-
-vararg_test :: proc (prefix: str, nums: ..i32) {
- print(prefix);
- for num: nums {
- print(num);
- print(" ");
- }
-}
-
-NumInfo :: struct {
- min : i32;
- max : i32;
- sum : i32;
-}
-
-get_num_info :: proc (nums: ..i32) -> NumInfo {
- ni : NumInfo;
-
- ni.min = nums[0];
- for num: nums do if num < ni.min do ni.min = num;
-
- ni.max = nums[0];
- for num: nums do if num > ni.max do ni.max = num;
-
- ni.sum = 0;
- for num: nums do ni.sum += num;
-
- return ni;
-}
-
-print_strings :: proc (ss: ..str) {
- for s: ss do print_str(s);
-}
-
-multi_max :: proc (nums: ..$T) -> T {
- print("Got this many args: ");
- println(nums.count);
-
- max := nums[0];
- for num: nums do if num > max do max = num;
- return max;
-}
-
-weird_sum :: proc (n1: $T, others: ..T) -> T {
- s := n1;
- for n: others do s += n;
- return s;
-}
-
-main :: proc (args: [] cstr) {
- switch_demo();
-
- print_strings("This ", "is ", "a ", "test.\n");
-
- vararg_test("Here are some numbers:\n", 1, 2, 3, 4, 5);
- print("\n\n");
-
- multi_max(4, 2, 76, 3, 1203, 2, 4) |> println();
- multi_max(4, 2, 76, 3, 1203, 2, 4) |> println();
-
- weird_sum(4, 1) |> println();
-
- ni := get_num_info(1, 2, 3, 4, 5);
- println("Some information about those numbers:");
- print("Min: ");
- println(ni.min);
- print("Max: ");
- println(ni.max);
- print("Sum: ");
- println(ni.sum);
- print("\n");
-
- res := compose(5, proc (x: i32) -> i32 do return x * 3;,
- proc (x: i32) -> i32 do return x + 5;);
- println(res);
-
- s : SOA;
- soa_init(^s);
- defer {
- println("Clearing SOA...");
- soa_deinit(^s);
- }
-
- println("Evens from 6 to 34:");
- for i: 6 .. 34 |> by(2) {
- print(i);
- print(" ");
- }
- print("\n");
-
- print_arr_details(^s.a);
- print_arr_details(^s.b);
-
- for i: 0 .. 100 {
- array.push(^s.a, (5 * i) % 21);
- array.push(^s.b, 3 * cast(i64) i);
- array.push(^s.c, Vec3.{ i, i * i, i * i * i });
- }
-
- r := ^s.a |> get_range() |> by(3);
- print(r);
- print_array(^s.a);
- print("A has 22? ");
- println(array.contains(^s.a, 22));
-
- // NOTE: Iterating by value
- for vec: s.c {
- print(vec);
- print(" ");
- }
- print("\n");
-
- // NOTE: Iterating by pointer
- for ^vec: s.c {
- print(cast(^void) vec);
- print(" ");
- }
- print("\n");
-
- small : [12] i32;
- for ^it: small do *it = 1234 + cast(u32) it;
-
- for it: small {
- print(it);
- print(" ");
- }
- print("\n");
-
-
- array.sort(^s.a, cmp_dec);
- array.sort(^s.b, cmp_dec);
-
- print_array(^s.a);
- print_array(^s.b);
-
- println("After adding...");
- print_arr_details(^s.a);
- print_arr_details(^s.b);
-
- print("Array A sum: ");
- println(array.fold(^s.a, 0, proc (x: i32, acc: i32) -> i32 do return x + acc;));
- print("\n");
-
- pmap : map.Map(rawptr, rawptr);
- map.init(^pmap, null, 50);
- defer map.free(^pmap);
-
- for i: 0 .. 100 do map.put(^pmap, ^s.a[i], ^s.b[i]);
-
- print("Has ^a[20]? ");
- println(map.has(^pmap, ^s.a[20]));
-
- print("Has null? ");
- println(map.has(^pmap, null));
-
- print("Value at ^a[50]: ");
- print(cast(^void) map.get(^pmap, ^s.a[50]));
- print(" == ");
- println(cast(^void) (^s.b[50]));
-
- println("Deleteing ^a[20]");
- map.delete(^pmap, ^s.a[20]);
-
- print("Has ^a[20]? ");
- println(map.has(^pmap, ^s.a[20]));
-}
-
-
-Vec3 :: struct {
- x: i32;
- 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;
-}
--- /dev/null
+package main
+
+#load "core/std/js"
+
+use package core
+
+print_arr_details :: proc (arr: ^[..] $T) {
+ print("\nArray details:\n\tSize: ");
+ println(arr.count);
+ print("\tCapacity: ");
+ println(arr.capacity);
+ print("\tData ptr: ");
+ println(cast(^void) arr.data);
+ print("\tSize of elements: ");
+ println(sizeof T);
+ print("\tAlignment of elements: ");
+ println(alignof T);
+ print("\n");
+}
+
+print_vec :: proc (v: Vec3) #add_overload print {
+ print("Vec3(");
+ print(v.x);
+ print(", ");
+ print(v.y);
+ print(", ");
+ print(v.z);
+ print(")");
+}
+
+// This demonstrates that we have something similar to static 'duck' typing.
+get_count :: proc (x: $T) -> u32 do return x.count;
+
+
+// Because of the static duck typing, this will pass as an
+// array/slice in most places.
+Dummy :: struct {
+ count : u32 = 5;
+ data : [5] u32;
+}
+
+
+/* This demos some of the power you have with the polymorphic types */
+compose :: proc (a: A, f: proc ($A) -> $B, g: proc (B) -> $C) -> C {
+ return a |> f() |> g();
+}
+
+
+SOA :: struct {
+ a : [..] i32;
+ b : [..] i64;
+ c : [..] Vec3;
+}
+
+soa_init :: proc (s: ^SOA) {
+ array.init(^s.a);
+ array.init(^s.b);
+ array.init(^s.c);
+}
+
+soa_deinit :: proc (s: ^SOA) {
+ array.free(^s.a);
+ array.free(^s.b);
+ array.free(^s.c);
+}
+
+get_range :: proc (arr: ^[..] $T) -> range {
+ return 0 .. arr.count;
+}
+
+// NOTE: This function will be very useful for for loops. i.e.
+// for i: 0 .. 100 |> by(2) {
+// ...
+// }
+by :: proc (r: range, s: u32) -> range {
+ return range.{ low = r.low, high = r.high, step = s };
+}
+
+switch_demo :: proc () {
+ switch a := 4; a {
+ case 4, 5, 6 {
+ println("a was 4, 5, or 6");
+ fallthrough fallthrough;
+ }
+
+ case 10 do println("a was 10");
+
+ case #default {
+ println("a was something else.");
+ }
+ }
+}
+
+vararg_test :: proc (prefix: str, nums: ..i32) {
+ print(prefix);
+ for num: nums {
+ print(num);
+ print(" ");
+ }
+}
+
+NumInfo :: struct {
+ min : i32;
+ max : i32;
+ sum : i32;
+}
+
+get_num_info :: proc (nums: ..i32) -> NumInfo {
+ ni : NumInfo;
+
+ ni.min = nums[0];
+ for num: nums do if num < ni.min do ni.min = num;
+
+ ni.max = nums[0];
+ for num: nums do if num > ni.max do ni.max = num;
+
+ ni.sum = 0;
+ for num: nums do ni.sum += num;
+
+ return ni;
+}
+
+print_strings :: proc (ss: ..str) {
+ for s: ss do print_str(s);
+}
+
+multi_max :: proc (nums: ..$T) -> T {
+ print("Got this many args: ");
+ println(nums.count);
+
+ max := nums[0];
+ for num: nums do if num > max do max = num;
+ return max;
+}
+
+weird_sum :: proc (n1: $T, others: ..T) -> T {
+ s := n1;
+ for n: others do s += n;
+ return s;
+}
+
+main :: proc (args: [] cstr) {
+ switch_demo();
+
+ print_strings("This ", "is ", "a ", "test.\n");
+
+ vararg_test("Here are some numbers:\n", 1, 2, 3, 4, 5);
+ print("\n\n");
+
+ multi_max(4, 2, 76, 3, 1203, 2, 4) |> println();
+ multi_max(4, 2, 76, 3, 1203, 2, 4) |> println();
+
+ weird_sum(4, 1) |> println();
+
+ ni := get_num_info(1, 2, 3, 4, 5);
+ println("Some information about those numbers:");
+ print("Min: ");
+ println(ni.min);
+ print("Max: ");
+ println(ni.max);
+ print("Sum: ");
+ println(ni.sum);
+ print("\n");
+
+ res := compose(5, proc (x: i32) -> i32 do return x * 3;,
+ proc (x: i32) -> i32 do return x + 5;);
+ println(res);
+
+ s : SOA;
+ soa_init(^s);
+ defer {
+ println("Clearing SOA...");
+ soa_deinit(^s);
+ }
+
+ println("Evens from 6 to 34:");
+ for i: 6 .. 34 |> by(2) {
+ print(i);
+ print(" ");
+ }
+ print("\n");
+
+ print_arr_details(^s.a);
+ print_arr_details(^s.b);
+
+ for i: 0 .. 100 {
+ array.push(^s.a, (5 * i) % 21);
+ array.push(^s.b, 3 * cast(i64) i);
+ array.push(^s.c, Vec3.{ i, i * i, i * i * i });
+ }
+
+ r := ^s.a |> get_range() |> by(3);
+ print(r);
+ print_array(^s.a);
+ print("A has 22? ");
+ println(array.contains(^s.a, 22));
+
+ // NOTE: Iterating by value
+ for vec: s.c {
+ print(vec);
+ print(" ");
+ }
+ print("\n");
+
+ // NOTE: Iterating by pointer
+ for ^vec: s.c {
+ print(cast(^void) vec);
+ print(" ");
+ }
+ print("\n");
+
+ small : [12] i32;
+ for ^it: small do *it = 1234 + cast(u32) it;
+
+ for it: small {
+ print(it);
+ print(" ");
+ }
+ print("\n");
+
+
+ array.sort(^s.a, cmp_dec);
+ array.sort(^s.b, cmp_dec);
+
+ print_array(^s.a);
+ print_array(^s.b);
+
+ println("After adding...");
+ print_arr_details(^s.a);
+ print_arr_details(^s.b);
+
+ print("Array A sum: ");
+ println(array.fold(^s.a, 0, proc (x: i32, acc: i32) -> i32 do return x + acc;));
+ print("\n");
+
+ pmap : map.Map(rawptr, rawptr);
+ map.init(^pmap, null, 50);
+ defer map.free(^pmap);
+
+ for i: 0 .. 100 do map.put(^pmap, ^s.a[i], ^s.b[i]);
+
+ print("Has ^a[20]? ");
+ println(map.has(^pmap, ^s.a[20]));
+
+ print("Has null? ");
+ println(map.has(^pmap, null));
+
+ print("Value at ^a[50]: ");
+ print(cast(^void) map.get(^pmap, ^s.a[50]));
+ print(" == ");
+ println(cast(^void) (^s.b[50]));
+
+ println("Deleteing ^a[20]");
+ map.delete(^pmap, ^s.a[20]);
+
+ print("Has ^a[20]? ");
+ println(map.has(^pmap, ^s.a[20]));
+}
+
+
+Vec3 :: struct {
+ x: i32;
+ 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;
+}