. ./settings.sh
echo "Installing core libs"
+[ -d "$CORE_DIR/core" ] && sudo rm -r "$CORE_DIR/core"
sudo mkdir -p "$CORE_DIR"
sudo cp -r ./core/ "$CORE_DIR"
+++ /dev/null
-package core.alloc
-
-#load "./alloc/arena"
-#load "./alloc/fixed"
-#load "./alloc/heap"
-#load "./alloc/ring"
-#load "./alloc/pool"
-#load "./alloc/logging"
-#load "./alloc/gc"
-
-as_allocator :: #match {
- macro (a: Allocator) => a
-}
-
-// This is similar to alloca in C.
-from_stack :: macro (size: u32) -> rawptr {
- // This should do something about the alignment...
- // Everything so far has assume that the stack is aligned to 16 bytes.
- defer __stack_top = ~~(cast(^u8) __stack_top + size);
- return __stack_top;
-}
-
-array_from_stack :: macro ($T: type_expr, size: u32) -> [] T {
- defer __stack_top = ~~(cast(^u8) __stack_top + size * sizeof T);
- return (cast(^T) __stack_top)[0 .. size];
-}
-
-TEMPORARY_ALLOCATOR_SIZE :: 1 << 16; // 16Kb
-
-// The global heap allocator, set up upon program intialization.
-heap_allocator : Allocator;
-
-// The global temp allocator, set up upon program intialization.
-#local #thread_local
-temp_state : arena.ArenaState;
-
-#thread_local
-temp_allocator : Allocator;
-
-init_temp_allocator :: () {
- temp_state = arena.make(heap_allocator, TEMPORARY_ALLOCATOR_SIZE);
- temp_allocator = as_allocator(^temp_state);
-}
-
-clear_temp_allocator :: () {
- arena.clear(^temp_state);
-}
--- /dev/null
+package core.alloc
+
+#load "./arena"
+#load "./fixed"
+#load "./heap"
+#load "./ring"
+#load "./pool"
+#load "./logging"
+#load "./gc"
+
+as_allocator :: #match {
+ macro (a: Allocator) => a
+}
+
+// This is similar to alloca in C.
+from_stack :: macro (size: u32) -> rawptr {
+ // This should do something about the alignment...
+ // Everything so far has assume that the stack is aligned to 16 bytes.
+ defer __stack_top = ~~(cast(^u8) __stack_top + size);
+ return __stack_top;
+}
+
+array_from_stack :: macro ($T: type_expr, size: u32) -> [] T {
+ defer __stack_top = ~~(cast(^u8) __stack_top + size * sizeof T);
+ return (cast(^T) __stack_top)[0 .. size];
+}
+
+TEMPORARY_ALLOCATOR_SIZE :: 1 << 16; // 16Kb
+
+// The global heap allocator, set up upon program intialization.
+heap_allocator : Allocator;
+
+// The global temp allocator, set up upon program intialization.
+#local #thread_local
+temp_state : arena.ArenaState;
+
+#thread_local
+temp_allocator : Allocator;
+
+init_temp_allocator :: () {
+ temp_state = arena.make(heap_allocator, TEMPORARY_ALLOCATOR_SIZE);
+ temp_allocator = as_allocator(^temp_state);
+}
+
+clear_temp_allocator :: () {
+ arena.clear(^temp_state);
+}
+++ /dev/null
-package core.arg_parse
-
-use core
-
-arg_parse :: (c_args: [] cstr, output: any) -> bool {
- arg_iter := iter.as_iterator(c_args)
- |> iter.map((x) => string.from_cstr(*x));
- defer arg_iter.close(arg_iter.data);
-
- use runtime.info;
-
- ptr_type := cast(^Type_Info_Pointer) get_type_info(output.type);
- if ptr_type.kind != .Pointer do return false;
-
- arg_type := cast(^Type_Info_Struct) get_type_info(ptr_type.to);
- if arg_type.kind != .Struct do return false;
-
- data_base := *cast(^rawptr) output.data;
-
- for #no_close arg: arg_iter {
- for ^member: arg_type.members {
- for ^tag: member.tags {
- if tag.type != str do continue;
-
- to_match := *cast(^str) tag.data;
- if arg != to_match do continue;
-
- switch member.type {
- case bool {
- *(cast(^bool) (cast(^u8) data_base + member.offset)) = !*(cast(^bool) (cast(^u8) data_base + member.offset));
- }
-
- case i32 {
- value_str, success := iter.take_one(arg_iter, no_close=true);
- if !success do return false;
-
- value := conv.str_to_i64(value_str);
- *(cast(^i32) (cast(^u8) data_base + member.offset)) = ~~value;
- }
-
- case str {
- value, success := iter.take_one(arg_iter, no_close=true);
- if !success do return false;
-
- *(cast(^str) (cast(^u8) data_base + member.offset)) = value;
- }
-
- case #default {
- printf("Unsupported argument type, {}.\n", output.type);
- return false;
- }
- }
- }
- }
- }
-
- return true;
-}
\ No newline at end of file
}
format_map :: (output: ^conv.Format_Output, format: ^conv.Format, x: ^Map($K, $V)) {
- output->write("{\n");
- for^ x.entries {
- conv.format(output, " {\"p} => {\"p}\n", it.key, it.value);
+ if format.pretty_printing {
+ output->write("{\n");
+ for^ x.entries {
+ conv.format(output, " {\"p} => {\"p}\n", it.key, it.value);
+ }
+ output->write("}");
+
+ } else {
+ output->write("{ ");
+ for^ x.entries {
+ if !#first do output->write(", ");
+ conv.format(output, "{\"p} => {\"p}", it.key, it.value);
+ }
+ output->write(" }");
}
- output->write("}");
}
//
+++ /dev/null
-package core.conv
-
-Enable_Custom_Formatters :: true
-
-#local {
- use core {map, string, array, math}
-
- custom_formatters: Map(type_expr, #type (^Format_Output, ^Format, rawptr) -> void);
- custom_parsers : Map(type_expr, #type (rawptr, str, Allocator) -> bool);
-}
-
-custom_formatters_initialized :: #init () {
- map.init(^custom_formatters, default=null_proc);
- map.init(^custom_parsers, default=null_proc);
-
- #if Enable_Custom_Formatters {
- use package runtime.info;
-
- for type_idx: type_table.count {
- type := type_table[type_idx];
- if type.kind != .Struct do continue;
-
- s_info := cast(^Type_Info_Struct) type;
- for s_info.tags {
- if it.type == Custom_Format {
- custom_format := cast(^Custom_Format) it.data;
- custom_formatters[cast(type_expr) type_idx] = custom_format.format;
- }
-
- if it.type == Custom_Parse {
- custom_parse := cast(^Custom_Parse) it.data;
- custom_parsers[cast(type_expr) type_idx] = custom_parse.parse;
- }
- }
- }
- }
-}
-
-register_custom_formatter :: (formatter: (^Format_Output, ^Format, ^$T) -> void) {
- custom_formatters[T] = formatter;
-}
-
-register_custom_parser :: (parser: (^$T, str, Allocator) -> bool) {
- custom_parsers[T] = parser;
-}
-
-Custom_Format :: struct {
- format: (^Format_Output, ^Format, rawptr) -> void;
-}
-
-Custom_Parse :: struct {
- parse: (rawptr, str, Allocator) -> bool;
-}
-
-str_to_i64 :: #match #local {}
-
-#overload
-str_to_i64 :: macro (s: str, base: u32 = 10) -> i64 {
- str_to_i64 :: str_to_i64;
- s_ := s;
- return str_to_i64(^s_, base);
-}
-
-#overload
-str_to_i64 :: (s: ^str, base: u32 = 10) -> i64 {
- use package core
-
- value: i64 = 0;
- mul := 1;
-
- if s.data[0] == #char "-" {
- mul = -1;
- string.advance(s, 1);
- }
-
- if s.data[0] == #char "+" {
- string.advance(s, 1);
- }
-
- while !string.empty(*s) {
- switch c := s.data[0]; c {
- case #char "0" .. #char "9" {
- value *= ~~base;
- value += ~~(c - #char "0");
- }
-
- case #char "A" .. #char "Z" {
- if base <= 10 do fallthrough;
-
- value *= ~~base;
- value += ~~((c - #char "A") + 10);
- }
-
- case #char "a" .. #char "z" {
- if base <= 10 do fallthrough;
-
- value *= ~~base;
- value += ~~((c - #char "a") + 10);
- }
-
- case #default do break break;
- }
-
- string.advance(s);
- }
-
- return value * ~~mul;
-}
-
-str_to_f64 :: #match #local {}
-
-#overload
-str_to_f64 :: macro (s: str) -> f64 {
- str_to_f64 :: str_to_f64;
- s_ := s;
- return str_to_f64(^s_);
-}
-
-#overload
-str_to_f64 :: (s: ^str) -> f64 {
- use package core
-
- string.strip_leading_whitespace(s);
-
- sign := parse_sign(s);
- value, _ := parse_digits(s);
-
- if s.data[0] == #char "." {
- string.advance(s, 1);
- fraction, fraction_digits := parse_digits(s);
- while fraction_digits > 0 {
- fraction_digits -= 1;
- fraction /= 10;
- }
- value += fraction;
- }
-
- value *= sign;
-
- if s.data[0] != #char "e" && s.data[0] != #char "E" do return value;
- string.advance(s, 1);
-
- exponent_sign := parse_sign(s);
- exponent, _ := parse_digits(s);
- if exponent_sign > 0 {
- while exponent > 0 {
- value *= 10;
- exponent -= 1;
- }
- } else {
- while exponent > 0 {
- value /= 10;
- exponent -= 1;
- }
- }
-
- return value;
-
-
- parse_sign :: (s: ^str) -> f64 {
- switch s.data[0] {
- case #char "-" { string.advance(s, 1); return -1; }
- case #char "+" { string.advance(s, 1); return 1; }
- case #default { return 1; }
- }
- }
-
- parse_digits :: (s: ^str) -> (f64, digit_count: i32) {
- value: f64 = 0;
- count := 0;
- while s.count > 0 do switch s.data[0] {
- case #char "0" .. #char "9" {
- value = value * 10 + ~~cast(i32)(s.data[0] - #char "0");
- string.advance(s, 1);
- count += 1;
- }
-
- case #default do break break;
- }
- return value, count;
- }
-}
-
-i64_to_str :: (n: i64, base: u64, buf: [] u8, min_length := 0, prefix := false) -> str {
- is_neg := false;
- if n < 0 && base == 10 {
- is_neg = true;
- n = -n;
- }
-
- c := ^buf[buf.count - 1];
- len := 0;
-
- BASE64_MAP := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
-
- while n > 0 {
- m := cast(u64) n % base;
-
- *c = BASE64_MAP[cast(u32) m];
- len += 1;
- c -= 1;
-
- n /= base;
-
- } else {
- *c = #char "0";
- len += 1;
- c -= 1;
- }
-
- if min_length > 0 && len < min_length {
- for i: min_length - len {
- *c = #char "0";
- len += 1;
- c -= 1;
- }
- }
-
- if prefix {
- if base == 16 {
- *c = #char "x";
- len += 1;
- c -= 1;
- *c = #char "0";
- len += 1;
- c -= 1;
- }
-
- if base == 2 {
- *c = #char "b";
- len += 1;
- c -= 1;
- *c = #char "0";
- len += 1;
- c -= 1;
- }
- }
-
- if is_neg {
- *c = #char "-";
- len += 1;
- c -= 1;
- }
-
- return str.{ data = c + 1, count = len };
-}
-
-u64_to_str :: (n: u64, base: u64, buf: [] u8, min_length := 0, prefix := false) -> str {
- c := ^buf[buf.count - 1];
- len := 0;
-
- BASE64_MAP := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
-
- while n > 0 {
- m := cast(u64) n % base;
-
- *c = BASE64_MAP[cast(u32) m];
- len += 1;
- c -= 1;
-
- n /= base;
-
- } else {
- *c = #char "0";
- len += 1;
- c -= 1;
- }
-
- if min_length > 0 && len < min_length {
- for i: min_length - len {
- *c = #char "0";
- len += 1;
- c -= 1;
- }
- }
-
- if prefix {
- if base == 16 {
- *c = #char "x";
- len += 1;
- c -= 1;
- *c = #char "0";
- len += 1;
- c -= 1;
- }
-
- if base == 2 {
- *c = #char "b";
- len += 1;
- c -= 1;
- *c = #char "0";
- len += 1;
- c -= 1;
- }
- }
-
- return str.{ data = c + 1, count = len };
-}
-
-// This is better than what used to be, but still relies on converting the integer
-// part of the float to an integer, which could overflow.
-f64_to_str :: (f: f64, buf: [] u8, digits_after_decimal := 4) -> str {
- if math.is_nan(f) {
- return format(buf, "NaN");
- }
-
- if math.is_inf(f) {
- if f > 0 do return format(buf, "Inf");
- else do return format(buf, "-Inf");
- }
-
- len := 0;
-
- if f < 0 {
- f = -f;
- buf[0] = #char "-";
- len += 1;
- }
-
- dec_part := f - math.trunc(f);
- int_part := f - dec_part;
- dec_part = math.abs(dec_part);
-
- s1 := i64_to_str(~~int_part, 10, buf);
- for i: 0 .. s1.count do buf.data[i + len] = s1.data[i];
- buf.data[s1.count + len] = #char ".";
- len += s1.count + 1;
-
- digits := "0123456789";
-
- for i: digits_after_decimal {
- dec_part *= 10;
- v := math.trunc(dec_part);
- dec_part -= v;
-
- buf.data[len + i] = digits[cast(i32) v];
- }
- len += digits_after_decimal;
-
- return str.{ buf.data, len };
-}
-
-// @Remove // old aliases to not break old programs
-str_format :: format
-str_format_va :: format_va
-
-Format_Flush_Callback :: struct {
- data: rawptr = null;
- func: (rawptr, str) -> bool = null_proc;
-}
-
-Format_Output :: struct {
- data: ^u8;
- count: u32;
- capacity: u32;
-
- // When the data buffer fills, this procedure
- // is called with the data, allowing for the
- // buffer to be cleared and more to be written.
- flush: Format_Flush_Callback;
-
- write :: #match {
- (use output: ^Format_Output, c: u8) {
- if count >= capacity {
- if flush.func == null_proc do return;
- if !flush.func(flush.data, data[0 .. count]) do return;
- count = 0;
- }
-
- data[count] = c;
- count += 1;
- },
-
- (use output: ^Format_Output, s: str) {
- for c: s {
- if count >= capacity {
- if flush.func == null_proc do return;
- if !flush.func(flush.data, data[0 .. count]) do return;
- count = 0;
- }
-
- data[count] = c;
- count += 1;
- }
- }
- }
-}
-
-Format :: struct {
- pretty_printing := false;
- quote_strings := false;
- single_quote_strings := false;
- dereference := false;
- custom_format := true;
- interpret_numbers := true;
- digits_after_decimal := cast(u32) 4;
-
- indentation := cast(u32) 0;
- base := cast(u64) 10;
- minimum_width := cast(u32) 0;
-}
-
-#local
-flush_to_dynstr :: (dynstr: ^[..] u8, to_write: str) => {
- array.concat(dynstr, to_write);
- return true;
-}
-
-
-format :: #match {}
-#match format (buffer: [] u8, format: str, va: ..any) -> str {
- return format_va(buffer, format, ~~va);
-}
-#match format (output: ^Format_Output, format: str, va: ..any) -> str {
- return format_va(output, format, ~~va);
-}
-#match format (buffer: ^[..] u8, format: str, va: ..any) {
- buffer.count = buffer.capacity;
- out := format_va(*buffer, format, ~~va);
- buffer.count = out.count;
-}
-#match format (format: str, va: ..any) -> str {
- buffer : [256] u8;
- out := make([..] u8);
- output := Format_Output.{
- ~~buffer, 0, buffer.count,
- flush=.{ ^out, flush_to_dynstr }
- };
-
- final := format_va(^output, format, ~~va);
- array.concat(^out, final);
-
- return out;
-}
-
-format_va :: #match {}
-#match format_va (buffer: [] u8, format: str, va: [] any, flush := Format_Flush_Callback.{}) -> str {
- output := Format_Output.{ buffer.data, 0, buffer.count, flush };
- return format_va(^output, format, va);
-}
-#match format_va (buffer: ^[..] u8, format: str, va: [] any, flush := Format_Flush_Callback.{}) {
- buffer.count = buffer.capacity;
- out := format_va(*buffer, format, va, flush);
- buffer.count = out.count;
-}
-#match format_va (format: [] u8, va: [] any, allocator := context.allocator) -> str {
- buffer : [256] u8;
- out := make([..] u8, allocator=allocator);
- output := Format_Output.{
- ~~buffer, 0, buffer.count,
- flush=.{ ^out, flush_to_dynstr }
- };
-
- final := format_va(^output, format, ~~va);
- array.concat(^out, final);
-
- return out;
-}
-
-#match format_va (output: ^Format_Output, format: str, va: [] any) -> str {
- vararg_index := 0;
-
- while i := 0; i < format.count {
- defer i += 1;
-
- ch := format[i];
- formatting := Format.{};
-
- if ch == #char "{" {
- if format[i + 1] == #char "{" {
- output->write(#char "{");
- i += 1;
- continue;
- }
-
- i += 1;
- while true {
- ch = format[i];
-
- switch ch {
- case #char "*" {
- i += 1;
- formatting.dereference = true;
- }
-
- case #char "." {
- i += 1;
-
- digits := 0;
- while format[i] >= #char "0" && format[i] <= #char "9" {
- digits *= 10;
- digits += ~~(format[i] - #char "0");
- i += 1;
- }
-
- ch = format[i];
- formatting.digits_after_decimal = digits;
- }
-
- case #char "p" {
- i += 1;
- formatting.pretty_printing = true;
- }
-
- case #char "x" {
- i += 1;
- formatting.base = 16;
- }
-
- case #char "b" {
- i += 1;
-
- digits := 0;
- while format[i] >= #char "0" && format[i] <= #char "9" {
- digits *= 10;
- digits += ~~(format[i] - #char "0");
- i += 1;
- }
-
- formatting.base = ~~digits;
- }
-
- case #char "w" {
- i += 1;
-
- digits := 0;
- while format[i] >= #char "0" && format[i] <= #char "9" {
- digits *= 10;
- digits += ~~(format[i] - #char "0");
- i += 1;
- }
-
- formatting.minimum_width = ~~digits;
- }
-
- case #char "!" {
- i += 1;
- formatting.custom_format = false;
- }
-
- case #char "\"" {
- i += 1;
- formatting.quote_strings = true;
- }
-
- case #char "'" {
- i += 1;
- formatting.single_quote_strings = true;
- }
-
- case #char "d" {
- i += 1;
- formatting.interpret_numbers = false;
- }
-
- case #char "}" {
- arg := va[vararg_index];
- vararg_index += 1;
- format_any(output, ^formatting, arg);
-
- break break;
- }
-
- case #default do break break;
- }
- }
- }
-
- if ch == #char "}" {
- if format[i + 1] == #char "}" {
- output->write(#char "}");
- i += 1;
- continue;
- }
-
- continue;
- }
-
- output->write(ch);
- }
-
- return .{ output.data, output.count };
-}
-
-format_any :: (output: ^Format_Output, formatting: ^Format, v: any) {
- use package runtime.info
- array :: package core.array;
-
- if formatting.dereference {
- ti := get_type_info(v.type);
- if ti.kind == .Pointer {
- formatting.dereference = false;
-
- new_any: any;
- new_any.type = (cast(^Type_Info_Pointer) ti).to;
- new_any.data = *(cast(^rawptr) v.data);
- format_any(output, formatting, new_any);
- return;
- }
- }
-
- if formatting.custom_format && custom_formatters->has(v.type) {
- custom_formatters[v.type](output, formatting, v.data);
- return;
- }
-
- switch v.type {
- case bool {
- value := *(cast(^bool) v.data);
- if value do output->write("true");
- else do output->write("false");
- }
-
- case u8 {
- value := *(cast(^u8) v.data);
-
- if value > 31 {
- output->write(value);
-
- } else {
- ibuf : [128] u8;
- istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true);
- output->write(istr);
- }
- }
-
- int_case :: macro (T: type_expr) {
- case T {
- value := *(cast(^T) v.data);
-
- ibuf : [128] u8;
- istr := i64_to_str(~~value, formatting.base, ~~ibuf, min_length=formatting.minimum_width);
- output->write(istr);
- }
- }
-
- uint_case :: macro (T: type_expr) {
- case T {
- value := *(cast(^T) v.data);
-
- ibuf : [128] u8;
- istr := u64_to_str(~~value, formatting.base, ~~ibuf, min_length=formatting.minimum_width);
- output->write(istr);
- }
- }
-
- int_case(i8);
- int_case(i16);
- int_case(i32);
- int_case(i64);
- uint_case(u16);
- uint_case(u32);
- uint_case(u64);
-
- case f32 {
- value := *(cast(^f32) v.data);
-
- fbuf : [128] u8;
- fstr := f64_to_str(~~value, ~~fbuf, formatting.digits_after_decimal);
- output->write(fstr);
- }
-
- case f64 {
- value := *(cast(^f64) v.data);
-
- fbuf : [128] u8;
- fstr := f64_to_str(~~value, ~~fbuf, formatting.digits_after_decimal);
- output->write(fstr);
- }
-
- case str {
- if formatting.quote_strings do output->write("\"");
- if formatting.single_quote_strings do output->write("'");
- width := formatting.minimum_width;
- to_output := *cast(^str) v.data;
-
- // @Todo // escape '"' when quote_strings is enabled.
- output->write(to_output);
- if to_output.count < width && !(formatting.quote_strings || formatting.single_quote_strings) {
- for width - to_output.count do output->write(#char " ");
- }
-
- if formatting.quote_strings do output->write("\"");
- if formatting.single_quote_strings do output->write("'");
- }
-
- case rawptr {
- value := *(cast(^rawptr) v.data);
-
- if value == null {
- output->write("(null)");
- } else {
- ibuf : [128] u8;
- istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true);
- output->write(istr);
- }
- }
-
- case type_expr {
- value := *(cast(^type_expr) v.data);
-
- io :: package core.io
-
- buf : [256] u8;
-
- // This is a little gross but the only way to output the type name for a type_expr
- // is through a io.Writer. That should maybe be changed in the future? Also, I think
- // 256 bytes is enough for the name of a type but I'm not entirely sure...
- stream := io.buffer_stream_make(~~buf, fixed=true);
- writer := io.writer_make(^stream);
- write_type_name(^writer, value);
-
- output->write(io.buffer_stream_to_str(^stream));
- }
-
- case #default {
- info := get_type_info(v.type);
-
- if info.kind == .Struct {
- s := cast(^Type_Info_Struct) info;
-
- if s.name.count > 0 {
- output->write(s.name);
- output->write(" { ");
- } else {
- output->write("{ ");
- }
-
- {
- format := *formatting;
- format.quote_strings = true;
- if format.pretty_printing {
- format.indentation += 4;
- }
-
- for ^member: s.members {
- if member != s.members.data do output->write(", ");
-
- if formatting.pretty_printing {
- output->write(#char "\n");
- for i: format.indentation do output->write(#char " ");
- }
-
- output->write(member.name);
- output->write(" = ");
-
- format_any(output, ^format, .{ ~~(cast(^u8) v.data + member.offset), member.type });
- }
- }
-
- if formatting.pretty_printing {
- output->write(#char "\n");
- for i: formatting.indentation do output->write(#char " ");
- output->write("}");
-
- } else {
- output->write(" }");
- }
- }
-
- if info.kind == .Function {
- output->write("func[");
-
- value := *(cast(^i32) v.data);
-
- ibuf : [128] u8;
- istr := i64_to_str(~~value, 10, ~~ibuf);
- output->write(istr);
-
- output->write("]");
- }
-
- if info.kind == .Pointer {
- value := *(cast(^rawptr) v.data);
-
- ibuf : [128] u8;
- istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true);
- output->write(istr);
- }
-
- // This assumes that the following type_info kinds are basically the same.
- if info.kind == .Dynamic_Array || info.kind == .Slice || info.kind == .Variadic_Argument {
- if formatting.pretty_printing {
- output->write("[");
- } else {
- output->write("[ ");
- }
-
- a := cast(^Type_Info_Dynamic_Array) info;
- arr := cast(^array.Untyped_Array) v.data;
- data := arr.data;
- count := arr.count;
-
- format := *formatting;
- format.quote_strings = true;
- if format.pretty_printing do format.indentation += 4;
-
- for i: count {
- if i != 0 do output->write(", ");
-
- if formatting.pretty_printing {
- output->write("\n");
- for _: format.indentation do output->write(#char " ");
- }
-
- format_any(output, ^format, .{ ~~(cast(^u8) data + get_type_info(a.of).size * i), a.of });
- }
-
-
- if formatting.pretty_printing {
- format.indentation -= 4;
- output->write("\n");
- for _: format.indentation do output->write(#char " ");
- output->write(#char "]");
-
- } else {
- output->write(" ]");
- }
- }
-
- if info.kind == .Array {
- output->write("[ ");
-
- a := cast(^Type_Info_Array) info;
- data := v.data;
-
- for i: a.count {
- if i != 0 do output->write(", ");
-
- format_any(output, formatting, .{ ~~(cast(^u8) data + get_type_info(a.of).size * i), a.of });
- }
-
- output->write(" ]");
- }
-
- if info.kind == .Enum {
- e := cast(^Type_Info_Enum) info;
-
- value: u64;
- switch e.backing_type {
- case i8, u8 do value = cast(u64) *(cast(^u8) v.data);
- case i16, u16 do value = cast(u64) *(cast(^u16) v.data);
- case i32, u32 do value = cast(u64) *(cast(^u32) v.data);
- case i64, u64 do value = cast(u64) *(cast(^u64) v.data);
- case #default do assert(false, "Bad enum backing type");
- }
-
- if !formatting.interpret_numbers {
- format_any(output, formatting, .{^value, u64});
- break;
- }
-
- if !e.is_flags {
- for ^member: e.members {
- if value == member.value {
- output->write(member.name);
- break break;
- }
- }
-
- output->write("UNKNOWN");
-
- } else {
- first := true;
- for ^member: e.members {
- if value & member.value != 0 {
- if !first do output->write(" | ");
- output->write(member.name);
- first = false;
- }
- }
-
- if first {
- output->write("None");
- }
- }
- }
-
- if info.kind == .Distinct {
- d := cast(^Type_Info_Distinct) info;
-
- if formatting.interpret_numbers {
- output->write(d.name);
- output->write("[");
- }
-
- format_any(output, formatting, any.{ v.data, d.base_type });
-
- if formatting.interpret_numbers {
- output->write("]");
- }
- }
- }
- }
-}
-
-//
-// This should be called with a pointer for the first argument.
-//
-// x: i32;
-// parse_any(^x, "12.34");
-parse_any :: #match {}
-#match parse_any (v: any, to_parse: str, string_allocator := context.allocator) -> bool {
- use package runtime.info;
-
- info := get_type_info(v.type);
- if info.kind != .Pointer do return false;
-
- data_type := (cast(^Type_Info_Pointer) info).to;
- target := *cast(^rawptr) v.data;
- return parse_any(target, data_type, to_parse, string_allocator);
-}
-
-#match parse_any (target: rawptr, data_type: type_expr, to_parse: str, string_allocator := context.allocator) -> bool {
- if custom_parsers->has(data_type) {
- return custom_parsers[data_type](target, to_parse, string_allocator);
- }
-
- use runtime.info;
- info := get_type_info(data_type);
-
- switch data_type {
- case bool {
- dest := cast(^bool) target;
- *dest = false;
- if to_parse[0] == #char "t" || to_parse[0] == #char "T" {
- *dest = true;
- }
- return true;
- }
-
- integer_case :: macro (T: type_expr) {
- case T {
- dest := cast(^T) target;
- *dest = cast(T) str_to_i64(to_parse);
- return true;
- }
- }
-
- integer_case(i8);
- integer_case(i16);
- integer_case(i32);
- integer_case(i64);
- integer_case(u8);
- integer_case(u16);
- integer_case(u32);
- integer_case(u64);
-
- case f32 {
- dest := cast(^f32) target;
- *dest = ~~ str_to_f64(to_parse);
- return true;
- }
-
- case f64 {
- dest := cast(^f64) target;
- *dest = ~~ str_to_f64(to_parse);
- return true;
- }
-
- case str {
- if to_parse.count == 0 do return false;
- if to_parse[0] != #char "\"" do return false;
- line := to_parse;
- string.advance(^line);
-
- //
- // For now, this will return a substring in the original to_parse.
- dest := cast(^str) target;
- *dest = string.read_until(^line, #char "\"") |> string.alloc_copy(string_allocator); // @BUG // This does not handle escaped strings!
- return true;
- }
-
- case #default {
- if info.kind == .Enum {
- // TEMPORARY this needs to look at the backing type for the
- // enum in order to know how large this integer should be.
- *cast(^u32) target = ~~ str_to_i64(to_parse);
- return true;
- }
-
- if info.kind == .Distinct {
- d_info := cast(^Type_Info_Distinct) info;
- return parse_any(target, d_info.base_type, to_parse, string_allocator);
- }
- }
- }
-
- return false;
-}
-
-
--- /dev/null
+package core.conv
+
+Enable_Custom_Formatters :: true
+
+use core {map, string, array, math}
+
+str_to_i64 :: #match #local {}
+
+#overload
+str_to_i64 :: macro (s: str, base: u32 = 10) -> i64 {
+ str_to_i64 :: str_to_i64;
+ s_ := s;
+ return str_to_i64(^s_, base);
+}
+
+#overload
+str_to_i64 :: (s: ^str, base: u32 = 10) -> i64 {
+ use package core
+
+ value: i64 = 0;
+ mul := 1;
+
+ if s.data[0] == #char "-" {
+ mul = -1;
+ string.advance(s, 1);
+ }
+
+ if s.data[0] == #char "+" {
+ string.advance(s, 1);
+ }
+
+ while !string.empty(*s) {
+ switch c := s.data[0]; c {
+ case #char "0" .. #char "9" {
+ value *= ~~base;
+ value += ~~(c - #char "0");
+ }
+
+ case #char "A" .. #char "Z" {
+ if base <= 10 do fallthrough;
+
+ value *= ~~base;
+ value += ~~((c - #char "A") + 10);
+ }
+
+ case #char "a" .. #char "z" {
+ if base <= 10 do fallthrough;
+
+ value *= ~~base;
+ value += ~~((c - #char "a") + 10);
+ }
+
+ case #default do break break;
+ }
+
+ string.advance(s);
+ }
+
+ return value * ~~mul;
+}
+
+str_to_f64 :: #match #local {}
+
+#overload
+str_to_f64 :: macro (s: str) -> f64 {
+ str_to_f64 :: str_to_f64;
+ s_ := s;
+ return str_to_f64(^s_);
+}
+
+#overload
+str_to_f64 :: (s: ^str) -> f64 {
+ use package core
+
+ string.strip_leading_whitespace(s);
+
+ sign := parse_sign(s);
+ value, _ := parse_digits(s);
+
+ if s.data[0] == #char "." {
+ string.advance(s, 1);
+ fraction, fraction_digits := parse_digits(s);
+ while fraction_digits > 0 {
+ fraction_digits -= 1;
+ fraction /= 10;
+ }
+ value += fraction;
+ }
+
+ value *= sign;
+
+ if s.data[0] != #char "e" && s.data[0] != #char "E" do return value;
+ string.advance(s, 1);
+
+ exponent_sign := parse_sign(s);
+ exponent, _ := parse_digits(s);
+ if exponent_sign > 0 {
+ while exponent > 0 {
+ value *= 10;
+ exponent -= 1;
+ }
+ } else {
+ while exponent > 0 {
+ value /= 10;
+ exponent -= 1;
+ }
+ }
+
+ return value;
+
+
+ parse_sign :: (s: ^str) -> f64 {
+ switch s.data[0] {
+ case #char "-" { string.advance(s, 1); return -1; }
+ case #char "+" { string.advance(s, 1); return 1; }
+ case #default { return 1; }
+ }
+ }
+
+ parse_digits :: (s: ^str) -> (f64, digit_count: i32) {
+ value: f64 = 0;
+ count := 0;
+ while s.count > 0 do switch s.data[0] {
+ case #char "0" .. #char "9" {
+ value = value * 10 + ~~cast(i32)(s.data[0] - #char "0");
+ string.advance(s, 1);
+ count += 1;
+ }
+
+ case #default do break break;
+ }
+ return value, count;
+ }
+}
+
+i64_to_str :: (n: i64, base: u64, buf: [] u8, min_length := 0, prefix := false) -> str {
+ is_neg := false;
+ if n < 0 && base == 10 {
+ is_neg = true;
+ n = -n;
+ }
+
+ c := ^buf[buf.count - 1];
+ len := 0;
+
+ BASE64_MAP := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
+
+ while n > 0 {
+ m := cast(u64) n % base;
+
+ *c = BASE64_MAP[cast(u32) m];
+ len += 1;
+ c -= 1;
+
+ n /= base;
+
+ } else {
+ *c = #char "0";
+ len += 1;
+ c -= 1;
+ }
+
+ if min_length > 0 && len < min_length {
+ for i: min_length - len {
+ *c = #char "0";
+ len += 1;
+ c -= 1;
+ }
+ }
+
+ if prefix {
+ if base == 16 {
+ *c = #char "x";
+ len += 1;
+ c -= 1;
+ *c = #char "0";
+ len += 1;
+ c -= 1;
+ }
+
+ if base == 2 {
+ *c = #char "b";
+ len += 1;
+ c -= 1;
+ *c = #char "0";
+ len += 1;
+ c -= 1;
+ }
+ }
+
+ if is_neg {
+ *c = #char "-";
+ len += 1;
+ c -= 1;
+ }
+
+ return str.{ data = c + 1, count = len };
+}
+
+u64_to_str :: (n: u64, base: u64, buf: [] u8, min_length := 0, prefix := false) -> str {
+ c := ^buf[buf.count - 1];
+ len := 0;
+
+ BASE64_MAP := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
+
+ while n > 0 {
+ m := cast(u64) n % base;
+
+ *c = BASE64_MAP[cast(u32) m];
+ len += 1;
+ c -= 1;
+
+ n /= base;
+
+ } else {
+ *c = #char "0";
+ len += 1;
+ c -= 1;
+ }
+
+ if min_length > 0 && len < min_length {
+ for i: min_length - len {
+ *c = #char "0";
+ len += 1;
+ c -= 1;
+ }
+ }
+
+ if prefix {
+ if base == 16 {
+ *c = #char "x";
+ len += 1;
+ c -= 1;
+ *c = #char "0";
+ len += 1;
+ c -= 1;
+ }
+
+ if base == 2 {
+ *c = #char "b";
+ len += 1;
+ c -= 1;
+ *c = #char "0";
+ len += 1;
+ c -= 1;
+ }
+ }
+
+ return str.{ data = c + 1, count = len };
+}
+
+// This is better than what used to be, but still relies on converting the integer
+// part of the float to an integer, which could overflow.
+f64_to_str :: (f: f64, buf: [] u8, digits_after_decimal := 4) -> str {
+ if math.is_nan(f) {
+ return format(buf, "NaN");
+ }
+
+ if math.is_inf(f) {
+ if f > 0 do return format(buf, "Inf");
+ else do return format(buf, "-Inf");
+ }
+
+ len := 0;
+
+ if f < 0 {
+ f = -f;
+ buf[0] = #char "-";
+ len += 1;
+ }
+
+ dec_part := f - math.trunc(f);
+ int_part := f - dec_part;
+ dec_part = math.abs(dec_part);
+
+ s1 := i64_to_str(~~int_part, 10, buf);
+ for i: 0 .. s1.count do buf.data[i + len] = s1.data[i];
+ buf.data[s1.count + len] = #char ".";
+ len += s1.count + 1;
+
+ digits := "0123456789";
+
+ for i: digits_after_decimal {
+ dec_part *= 10;
+ v := math.trunc(dec_part);
+ dec_part -= v;
+
+ buf.data[len + i] = digits[cast(i32) v];
+ }
+ len += digits_after_decimal;
+
+ return str.{ buf.data, len };
+}
+
+
--- /dev/null
+package core.conv
+
+use core {map, string, array, math}
+
+#package {
+ custom_formatters: Map(type_expr, #type (^Format_Output, ^Format, rawptr) -> void);
+ custom_parsers : Map(type_expr, #type (rawptr, str, Allocator) -> bool);
+}
+
+custom_formatters_initialized :: #init () {
+ map.init(^custom_formatters, default=null_proc);
+ map.init(^custom_parsers, default=null_proc);
+
+ #if Enable_Custom_Formatters {
+ use package runtime.info;
+
+ for type_idx: type_table.count {
+ type := type_table[type_idx];
+ if type.kind != .Struct do continue;
+
+ s_info := cast(^Type_Info_Struct) type;
+ for s_info.tags {
+ if it.type == Custom_Format {
+ custom_format := cast(^Custom_Format) it.data;
+ custom_formatters[cast(type_expr) type_idx] = custom_format.format;
+ }
+
+ if it.type == Custom_Parse {
+ custom_parse := cast(^Custom_Parse) it.data;
+ custom_parsers[cast(type_expr) type_idx] = custom_parse.parse;
+ }
+ }
+ }
+ }
+}
+
+register_custom_formatter :: (formatter: (^Format_Output, ^Format, ^$T) -> void) {
+ custom_formatters[T] = formatter;
+}
+
+register_custom_parser :: (parser: (^$T, str, Allocator) -> bool) {
+ custom_parsers[T] = parser;
+}
+
+Custom_Format :: struct {
+ format: (^Format_Output, ^Format, rawptr) -> void;
+}
+
+Custom_Parse :: struct {
+ parse: (rawptr, str, Allocator) -> bool;
+}
+
+// @Remove // old aliases to not break old programs
+str_format :: format
+str_format_va :: format_va
+
+Format_Flush_Callback :: struct {
+ data: rawptr = null;
+ func: (rawptr, str) -> bool = null_proc;
+}
+
+Format_Output :: struct {
+ data: ^u8;
+ count: u32;
+ capacity: u32;
+
+ // When the data buffer fills, this procedure
+ // is called with the data, allowing for the
+ // buffer to be cleared and more to be written.
+ flush: Format_Flush_Callback;
+
+ write :: #match {
+ (use output: ^Format_Output, c: u8) {
+ if count >= capacity {
+ if flush.func == null_proc do return;
+ if !flush.func(flush.data, data[0 .. count]) do return;
+ count = 0;
+ }
+
+ data[count] = c;
+ count += 1;
+ },
+
+ (use output: ^Format_Output, s: str) {
+ for c: s {
+ if count >= capacity {
+ if flush.func == null_proc do return;
+ if !flush.func(flush.data, data[0 .. count]) do return;
+ count = 0;
+ }
+
+ data[count] = c;
+ count += 1;
+ }
+ }
+ }
+}
+
+Format :: struct {
+ pretty_printing := false;
+ quote_strings := false;
+ single_quote_strings := false;
+ dereference := false;
+ custom_format := true;
+ interpret_numbers := true;
+ digits_after_decimal := cast(u32) 4;
+
+ indentation := cast(u32) 0;
+ base := cast(u64) 10;
+ minimum_width := cast(u32) 0;
+}
+
+#local
+flush_to_dynstr :: (dynstr: ^[..] u8, to_write: str) => {
+ array.concat(dynstr, to_write);
+ return true;
+}
+
+
+format :: #match {}
+#match format (buffer: [] u8, format: str, va: ..any) -> str {
+ return format_va(buffer, format, ~~va);
+}
+#match format (output: ^Format_Output, format: str, va: ..any) -> str {
+ return format_va(output, format, ~~va);
+}
+#match format (buffer: ^[..] u8, format: str, va: ..any) {
+ buffer.count = buffer.capacity;
+ out := format_va(*buffer, format, ~~va);
+ buffer.count = out.count;
+}
+#match format (format: str, va: ..any) -> str {
+ buffer : [256] u8;
+ out := make([..] u8);
+ output := Format_Output.{
+ ~~buffer, 0, buffer.count,
+ flush=.{ ^out, flush_to_dynstr }
+ };
+
+ final := format_va(^output, format, ~~va);
+ array.concat(^out, final);
+
+ return out;
+}
+
+format_va :: #match {}
+#match format_va (buffer: [] u8, format: str, va: [] any, flush := Format_Flush_Callback.{}) -> str {
+ output := Format_Output.{ buffer.data, 0, buffer.count, flush };
+ return format_va(^output, format, va);
+}
+#match format_va (buffer: ^[..] u8, format: str, va: [] any, flush := Format_Flush_Callback.{}) {
+ buffer.count = buffer.capacity;
+ out := format_va(*buffer, format, va, flush);
+ buffer.count = out.count;
+}
+#match format_va (format: [] u8, va: [] any, allocator := context.allocator) -> str {
+ buffer : [256] u8;
+ out := make([..] u8, allocator=allocator);
+ output := Format_Output.{
+ ~~buffer, 0, buffer.count,
+ flush=.{ ^out, flush_to_dynstr }
+ };
+
+ final := format_va(^output, format, ~~va);
+ array.concat(^out, final);
+
+ return out;
+}
+
+#match format_va (output: ^Format_Output, format: str, va: [] any) -> str {
+ vararg_index := 0;
+
+ while i := 0; i < format.count {
+ defer i += 1;
+
+ ch := format[i];
+ formatting := Format.{};
+
+ if ch == #char "{" {
+ if format[i + 1] == #char "{" {
+ output->write(#char "{");
+ i += 1;
+ continue;
+ }
+
+ i += 1;
+ while true {
+ ch = format[i];
+
+ switch ch {
+ case #char "*" {
+ i += 1;
+ formatting.dereference = true;
+ }
+
+ case #char "." {
+ i += 1;
+
+ digits := 0;
+ while format[i] >= #char "0" && format[i] <= #char "9" {
+ digits *= 10;
+ digits += ~~(format[i] - #char "0");
+ i += 1;
+ }
+
+ ch = format[i];
+ formatting.digits_after_decimal = digits;
+ }
+
+ case #char "p" {
+ i += 1;
+ formatting.pretty_printing = true;
+ }
+
+ case #char "x" {
+ i += 1;
+ formatting.base = 16;
+ }
+
+ case #char "b" {
+ i += 1;
+
+ digits := 0;
+ while format[i] >= #char "0" && format[i] <= #char "9" {
+ digits *= 10;
+ digits += ~~(format[i] - #char "0");
+ i += 1;
+ }
+
+ formatting.base = ~~digits;
+ }
+
+ case #char "w" {
+ i += 1;
+
+ digits := 0;
+ while format[i] >= #char "0" && format[i] <= #char "9" {
+ digits *= 10;
+ digits += ~~(format[i] - #char "0");
+ i += 1;
+ }
+
+ formatting.minimum_width = ~~digits;
+ }
+
+ case #char "!" {
+ i += 1;
+ formatting.custom_format = false;
+ }
+
+ case #char "\"" {
+ i += 1;
+ formatting.quote_strings = true;
+ }
+
+ case #char "'" {
+ i += 1;
+ formatting.single_quote_strings = true;
+ }
+
+ case #char "d" {
+ i += 1;
+ formatting.interpret_numbers = false;
+ }
+
+ case #char "}" {
+ arg := va[vararg_index];
+ vararg_index += 1;
+ format_any(output, ^formatting, arg);
+
+ break break;
+ }
+
+ case #default do break break;
+ }
+ }
+ }
+
+ if ch == #char "}" {
+ if format[i + 1] == #char "}" {
+ output->write(#char "}");
+ i += 1;
+ continue;
+ }
+
+ continue;
+ }
+
+ output->write(ch);
+ }
+
+ return .{ output.data, output.count };
+}
+
+format_any :: (output: ^Format_Output, formatting: ^Format, v: any) {
+ use package runtime.info
+ array :: package core.array;
+
+ if formatting.dereference {
+ ti := get_type_info(v.type);
+ if ti.kind == .Pointer {
+ formatting.dereference = false;
+
+ new_any: any;
+ new_any.type = (cast(^Type_Info_Pointer) ti).to;
+ new_any.data = *(cast(^rawptr) v.data);
+ format_any(output, formatting, new_any);
+ return;
+ }
+ }
+
+ if formatting.custom_format && custom_formatters->has(v.type) {
+ custom_formatters[v.type](output, formatting, v.data);
+ return;
+ }
+
+ switch v.type {
+ case bool {
+ value := *(cast(^bool) v.data);
+ if value do output->write("true");
+ else do output->write("false");
+ }
+
+ case u8 {
+ value := *(cast(^u8) v.data);
+
+ if value > 31 {
+ output->write(value);
+
+ } else {
+ ibuf : [128] u8;
+ istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true);
+ output->write(istr);
+ }
+ }
+
+ int_case :: macro (T: type_expr) {
+ case T {
+ value := *(cast(^T) v.data);
+
+ ibuf : [128] u8;
+ istr := i64_to_str(~~value, formatting.base, ~~ibuf, min_length=formatting.minimum_width);
+ output->write(istr);
+ }
+ }
+
+ uint_case :: macro (T: type_expr) {
+ case T {
+ value := *(cast(^T) v.data);
+
+ ibuf : [128] u8;
+ istr := u64_to_str(~~value, formatting.base, ~~ibuf, min_length=formatting.minimum_width);
+ output->write(istr);
+ }
+ }
+
+ int_case(i8);
+ int_case(i16);
+ int_case(i32);
+ int_case(i64);
+ uint_case(u16);
+ uint_case(u32);
+ uint_case(u64);
+
+ case f32 {
+ value := *(cast(^f32) v.data);
+
+ fbuf : [128] u8;
+ fstr := f64_to_str(~~value, ~~fbuf, formatting.digits_after_decimal);
+ output->write(fstr);
+ }
+
+ case f64 {
+ value := *(cast(^f64) v.data);
+
+ fbuf : [128] u8;
+ fstr := f64_to_str(~~value, ~~fbuf, formatting.digits_after_decimal);
+ output->write(fstr);
+ }
+
+ case str {
+ if formatting.quote_strings do output->write("\"");
+ if formatting.single_quote_strings do output->write("'");
+ width := formatting.minimum_width;
+ to_output := *cast(^str) v.data;
+
+ // @Todo // escape '"' when quote_strings is enabled.
+ output->write(to_output);
+ if to_output.count < width && !(formatting.quote_strings || formatting.single_quote_strings) {
+ for width - to_output.count do output->write(#char " ");
+ }
+
+ if formatting.quote_strings do output->write("\"");
+ if formatting.single_quote_strings do output->write("'");
+ }
+
+ case rawptr {
+ value := *(cast(^rawptr) v.data);
+
+ if value == null {
+ output->write("(null)");
+ } else {
+ ibuf : [128] u8;
+ istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true);
+ output->write(istr);
+ }
+ }
+
+ case type_expr {
+ value := *(cast(^type_expr) v.data);
+
+ io :: package core.io
+
+ buf : [256] u8;
+
+ // This is a little gross but the only way to output the type name for a type_expr
+ // is through a io.Writer. That should maybe be changed in the future? Also, I think
+ // 256 bytes is enough for the name of a type but I'm not entirely sure...
+ stream := io.buffer_stream_make(~~buf, fixed=true);
+ writer := io.writer_make(^stream);
+ write_type_name(^writer, value);
+
+ output->write(io.buffer_stream_to_str(^stream));
+ }
+
+ case #default {
+ info := get_type_info(v.type);
+
+ if info.kind == .Struct {
+ s := cast(^Type_Info_Struct) info;
+
+ if s.name.count > 0 {
+ output->write(s.name);
+ output->write(" { ");
+ } else {
+ output->write("{ ");
+ }
+
+ {
+ format := *formatting;
+ format.quote_strings = true;
+ if format.pretty_printing {
+ format.indentation += 4;
+ }
+
+ for ^member: s.members {
+ if member != s.members.data do output->write(", ");
+
+ if formatting.pretty_printing {
+ output->write(#char "\n");
+ for i: format.indentation do output->write(#char " ");
+ }
+
+ output->write(member.name);
+ output->write(" = ");
+
+ format_any(output, ^format, .{ ~~(cast(^u8) v.data + member.offset), member.type });
+ }
+ }
+
+ if formatting.pretty_printing {
+ output->write(#char "\n");
+ for i: formatting.indentation do output->write(#char " ");
+ output->write("}");
+
+ } else {
+ output->write(" }");
+ }
+ }
+
+ if info.kind == .Function {
+ output->write("func[");
+
+ value := *(cast(^i32) v.data);
+
+ ibuf : [128] u8;
+ istr := i64_to_str(~~value, 10, ~~ibuf);
+ output->write(istr);
+
+ output->write("]");
+ }
+
+ if info.kind == .Pointer {
+ value := *(cast(^rawptr) v.data);
+
+ ibuf : [128] u8;
+ istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true);
+ output->write(istr);
+ }
+
+ // This assumes that the following type_info kinds are basically the same.
+ if info.kind == .Dynamic_Array || info.kind == .Slice || info.kind == .Variadic_Argument {
+ if formatting.pretty_printing {
+ output->write("[");
+ } else {
+ output->write("[ ");
+ }
+
+ a := cast(^Type_Info_Dynamic_Array) info;
+ arr := cast(^array.Untyped_Array) v.data;
+ data := arr.data;
+ count := arr.count;
+
+ format := *formatting;
+ format.quote_strings = true;
+ if format.pretty_printing do format.indentation += 4;
+
+ for i: count {
+ if i != 0 do output->write(", ");
+
+ if formatting.pretty_printing {
+ output->write("\n");
+ for _: format.indentation do output->write(#char " ");
+ }
+
+ format_any(output, ^format, .{ ~~(cast(^u8) data + get_type_info(a.of).size * i), a.of });
+ }
+
+
+ if formatting.pretty_printing {
+ format.indentation -= 4;
+ output->write("\n");
+ for _: format.indentation do output->write(#char " ");
+ output->write(#char "]");
+
+ } else {
+ output->write(" ]");
+ }
+ }
+
+ if info.kind == .Array {
+ output->write("[ ");
+
+ a := cast(^Type_Info_Array) info;
+ data := v.data;
+
+ for i: a.count {
+ if i != 0 do output->write(", ");
+
+ format_any(output, formatting, .{ ~~(cast(^u8) data + get_type_info(a.of).size * i), a.of });
+ }
+
+ output->write(" ]");
+ }
+
+ if info.kind == .Enum {
+ e := cast(^Type_Info_Enum) info;
+
+ value: u64;
+ switch e.backing_type {
+ case i8, u8 do value = cast(u64) *(cast(^u8) v.data);
+ case i16, u16 do value = cast(u64) *(cast(^u16) v.data);
+ case i32, u32 do value = cast(u64) *(cast(^u32) v.data);
+ case i64, u64 do value = cast(u64) *(cast(^u64) v.data);
+ case #default do assert(false, "Bad enum backing type");
+ }
+
+ if !formatting.interpret_numbers {
+ format_any(output, formatting, .{^value, u64});
+ break;
+ }
+
+ if !e.is_flags {
+ for ^member: e.members {
+ if value == member.value {
+ output->write(member.name);
+ break break;
+ }
+ }
+
+ output->write("UNKNOWN");
+
+ } else {
+ first := true;
+ for ^member: e.members {
+ if value & member.value != 0 {
+ if !first do output->write(" | ");
+ output->write(member.name);
+ first = false;
+ }
+ }
+
+ if first {
+ output->write("None");
+ }
+ }
+ }
+
+ if info.kind == .Distinct {
+ d := cast(^Type_Info_Distinct) info;
+
+ if formatting.interpret_numbers {
+ output->write(d.name);
+ output->write("[");
+ }
+
+ format_any(output, formatting, any.{ v.data, d.base_type });
+
+ if formatting.interpret_numbers {
+ output->write("]");
+ }
+ }
+ }
+ }
+}
--- /dev/null
+package core.conv
+
+use core {map, string, array, math}
+
+//
+// This should be called with a pointer for the first argument.
+//
+// x: i32;
+// parse_any(^x, "12.34");
+parse_any :: #match {}
+#match parse_any (v: any, to_parse: str, string_allocator := context.allocator) -> bool {
+ use package runtime.info;
+
+ info := get_type_info(v.type);
+ if info.kind != .Pointer do return false;
+
+ data_type := (cast(^Type_Info_Pointer) info).to;
+ target := *cast(^rawptr) v.data;
+ return parse_any(target, data_type, to_parse, string_allocator);
+}
+
+#match parse_any (target: rawptr, data_type: type_expr, to_parse: str, string_allocator := context.allocator) -> bool {
+ if custom_parsers->has(data_type) {
+ return custom_parsers[data_type](target, to_parse, string_allocator);
+ }
+
+ use runtime.info;
+ info := get_type_info(data_type);
+
+ switch data_type {
+ case bool {
+ dest := cast(^bool) target;
+ *dest = false;
+ if to_parse[0] == #char "t" || to_parse[0] == #char "T" {
+ *dest = true;
+ }
+ return true;
+ }
+
+ integer_case :: macro (T: type_expr) {
+ case T {
+ dest := cast(^T) target;
+ *dest = cast(T) str_to_i64(to_parse);
+ return true;
+ }
+ }
+
+ integer_case(i8);
+ integer_case(i16);
+ integer_case(i32);
+ integer_case(i64);
+ integer_case(u8);
+ integer_case(u16);
+ integer_case(u32);
+ integer_case(u64);
+
+ case f32 {
+ dest := cast(^f32) target;
+ *dest = ~~ str_to_f64(to_parse);
+ return true;
+ }
+
+ case f64 {
+ dest := cast(^f64) target;
+ *dest = ~~ str_to_f64(to_parse);
+ return true;
+ }
+
+ case str {
+ if to_parse.count == 0 do return false;
+ if to_parse[0] != #char "\"" do return false;
+ line := to_parse;
+ string.advance(^line);
+
+ //
+ // For now, this will return a substring in the original to_parse.
+ dest := cast(^str) target;
+ *dest = string.read_until(^line, #char "\"") |> string.alloc_copy(string_allocator); // @BUG // This does not handle escaped strings!
+ return true;
+ }
+
+ case #default {
+ if info.kind == .Enum {
+ // TEMPORARY this needs to look at the backing type for the
+ // enum in order to know how large this integer should be.
+ *cast(^u32) target = ~~ str_to_i64(to_parse);
+ return true;
+ }
+
+ if info.kind == .Distinct {
+ d_info := cast(^Type_Info_Distinct) info;
+ return parse_any(target, d_info.base_type, to_parse, string_allocator);
+ }
+ }
+ }
+
+ return false;
+}
+++ /dev/null
-package core.hash
-
-to_u32 :: #match {
- (key: rawptr) -> u32 { return 0xcbf29ce7 ^ cast(u32) key; },
- (key: i8) -> u32 { return ~~ key; },
- (key: i16) -> u32 { return 0x9ce7 ^ cast(u32) key; },
- (key: i32) -> u32 { return 0xcbf29ce7 ^ cast(u32) key; },
- (key: i64) -> u32 { return cast(u32) (cast(u64) 0xcbf29ce7 ^ cast(u64) key); },
- (key: str) -> u32 {
- hash: u32 = 5381;
- for ch: key do hash += (hash << 5) + ~~ch;
- return hash;
- },
- (key: type_expr) -> u32 { return to_u32(cast(u32) key); },
-
- #precedence 10000
- macro (key: $T/HasHashMethod) => key->hash()
-}
-
-Hashable :: interface (t: $T) {
- { to_u32(t) } -> u32;
-}
-
-#local
-HasHashMethod :: interface (t: $T) {
- { t->hash() } -> u32;
-}
-
--- /dev/null
+package core.hash
+
+to_u32 :: #match {
+ (key: rawptr) -> u32 { return 0xcbf29ce7 ^ cast(u32) key; },
+ (key: i8) -> u32 { return ~~ key; },
+ (key: i16) -> u32 { return 0x9ce7 ^ cast(u32) key; },
+ (key: i32) -> u32 { return 0xcbf29ce7 ^ cast(u32) key; },
+ (key: i64) -> u32 { return cast(u32) (cast(u64) 0xcbf29ce7 ^ cast(u64) key); },
+ (key: str) -> u32 {
+ hash: u32 = 5381;
+ for ch: key do hash += (hash << 5) + ~~ch;
+ return hash;
+ },
+ (key: type_expr) -> u32 { return to_u32(cast(u32) key); },
+
+ #precedence 10000
+ macro (key: $T/HasHashMethod) => key->hash()
+}
+
+Hashable :: interface (t: $T) {
+ { to_u32(t) } -> u32;
+}
+
+#local
+HasHashMethod :: interface (t: $T) {
+ { t->hash() } -> u32;
+}
+
// that is available. Only set this if you know that the data
// will always be available.
greedy : bool;
+}
+#inject Reader {
is_empty :: reader_empty;
empty :: reader_empty;
read_all :: read_all;
--- /dev/null
+package core
+// Currently, these symbols are dumped in the 'core' namespace, which means
+// most programs that just 'use package core' can access all of them, which
+// is convenient; However, it doesn't hurt to wonder if they should be the
+// 'core.io' package so these would become like 'io.printf(...)'. Of course,
+// you could always still do 'use package core.io', which would bring these
+// in anyway.
+
+
+#if runtime.runtime == .Custom {
+ #error "'stdio' can only be included in the 'wasi' or 'js' runtime."
+}
+
+stdio_stream: io.Stream;
+
+auto_flush_stdio := true
+
+print :: #match #locked {
+ (x: str) {
+ io.write(^stdio.print_writer, x);
+ if x[x.count - 1] == #char "\n" && auto_flush_stdio do __flush_stdio();
+ },
+
+ (x) => { io.write(^stdio.print_writer, x); },
+ (x, y) => { io.write(^stdio.print_writer, x, y); },
+}
+
+println :: (x) => {
+ print(x);
+ print("\n");
+}
+
+// Standard formatted print.
+printf :: (format: str, va: ..any) {
+ flush :: (_, to_output) => {
+ io.write(^stdio.print_writer, to_output);
+ __flush_stdio();
+ return true;
+ }
+
+ buffer: [1024] u8;
+ print(conv.format_va(buffer, format, va, .{null, flush}));
+}
+
+// Print to standard error, if available.
+#if #defined(runtime.__output_error) {
+ eprintf :: (format: str, va: ..any) -> str {
+ flush :: (_, to_output) => {
+ runtime.__output_error(to_output);
+ return true;
+ }
+
+ buffer: [1024] u8;
+ runtime.__output_error(conv.format_va(buffer, format, va, .{null, flush}));
+ }
+}
+
+// Print to a dynamically allocated string.
+aprintf :: (format: str, va: ..any) -> str {
+ buffer: [8196] u8;
+ out := conv.format_va(buffer, format, va);
+ return string.alloc_copy(out);
+}
+
+// Print to a TEMPORARY dynamically allocated string.
+tprintf :: (format: str, va: ..any) -> str {
+ buffer: [8196] u8;
+ out := conv.format_va(buffer, format, va);
+ return string.alloc_copy(out, allocator=context.temp_allocator);
+}
+
+byte_dump :: (ptr: rawptr, byte_count: u32, bytes_per_line := 8) {
+ temp: [3] u8;
+
+ u8_ptr := cast(^u8) ptr;
+ for i: byte_count {
+ val := u8_ptr[i];
+
+ temp[0] = map_to_ascii(val >> 4);
+ temp[1] = map_to_ascii(val & 15);
+ temp[2] = #char " ";
+
+ runtime.__output_string(~~temp);
+
+ if i % bytes_per_line == (bytes_per_line - 1) do runtime.__output_string("\n");
+ }
+
+
+ map_to_ascii :: (x: u8) -> u8 {
+ switch x {
+ case 0 do return #char "0";
+ case 1 do return #char "1";
+ case 2 do return #char "2";
+ case 3 do return #char "3";
+ case 4 do return #char "4";
+ case 5 do return #char "5";
+ case 6 do return #char "6";
+ case 7 do return #char "7";
+ case 8 do return #char "8";
+ case 9 do return #char "9";
+ case 10 do return #char "A";
+ case 11 do return #char "B";
+ case 12 do return #char "C";
+ case 13 do return #char "D";
+ case 14 do return #char "E";
+ case 15 do return #char "F";
+ case #default do return #char "X";
+ }
+ }
+}
+
+
+//
+// Private and internal things
+//
+
+#thread_local stdio : struct {
+ print_stream : io.BufferStream;
+ print_writer : io.Writer;
+}
+
+__stdio_init :: () {
+ stdio.print_stream = io.buffer_stream_make(2048, context.allocator);
+ stdio.print_writer = io.writer_make(^stdio.print_stream);
+
+ // This shouldn't need to be here, but because ^stdin_vtable is not a compile-time
+ // known value (even through it should be).
+ stdio_stream.vtable = ^stdio_vtable;
+}
+
+
+__flush_stdio :: () {
+ if stdio.print_stream.data.count == 0 do return;
+
+ ^stdio.print_stream
+ |> io.buffer_stream_to_str()
+ |> runtime.__output_string();
+
+ ^stdio.print_stream |> io.stream_flush();
+}
+
+#local stdio_vtable := io.Stream_Vtable.{
+ read = (_: ^io.Stream, buffer: [] u8) -> (io.Error, u32) {
+ __flush_stdio();
+ bytes_read := runtime.__read_from_input(buffer);
+ if bytes_read == 0 do return .ReadPending, 0;
+ if bytes_read < 0 do return .EOF, 0;
+
+ return .None, bytes_read;
+ },
+
+ read_byte = (_: ^io.Stream) -> (io.Error, u8) {
+ __flush_stdio();
+ buf: [1] u8;
+ bytes_read := runtime.__read_from_input(buf);
+ if bytes_read <= 0 do return .EOF, 0;
+
+ return .None, buf[0];
+ },
+
+ write = (_: ^io.Stream, buffer: [] u8) -> (io.Error, u32) {
+ return io.stream_write(^stdio.print_stream, buffer);
+ },
+
+ write_byte = (_: ^io.Stream, byte: u8) -> io.Error {
+ return io.stream_write_byte(^stdio.print_stream, byte);
+ },
+
+ flush = (_: ^io.Stream) -> io.Error {
+ __flush_stdio();
+ return .None;
+ }
+}
+++ /dev/null
-package core.math
-
-use core.intrinsics {wasm}
-
-// Things that are useful in any math library:
-// - Trigonometry
-// - modf, fmod
-
-// Other things that can be useful:
-// - Vector math
-// - Matrix math
-// - Why not tensor math??
-// - Complex numbers
-// - Dual numbers
-
-
-E :: 2.71828182845904523536f;
-PI :: 3.14159265f;
-TAU :: 6.28318330f;
-SQRT_2 :: 1.414213562f;
-
-//
-// Trigonometry
-// Basic trig functions have been implemented using taylor series approximations. The
-// approximations are very accurate, but rather computationally expensive. Programs that
-// rely heavily on trig functions would greatly benefit from improvements to the
-// implementations of these functions.
-//
-
-sin :: (t: f32) -> f32 {
- while t >= PI do t -= TAU;
- while t <= -PI do t += TAU;
-
- res := 0.0f;
-
- plus_minus := 1.0f;
- n := 13;
- while n > 1 {
- res += plus_minus;
- res *= t * t / cast(f32) (n * n - n);
-
- plus_minus = -plus_minus;
- n -= 2;
- }
-
- res += 1.0f;
- res *= t;
- return res;
-}
-
-cos :: (t: f32) -> f32 {
- while t >= PI do t -= TAU;
- while t <= -PI do t += TAU;
-
- res := 0.0f;
-
- plus_minus := 1.0f;
- n := 12;
- while n > 1 {
- res += plus_minus;
- res *= t * t / cast(f32) (n * n - n);
-
- plus_minus = -plus_minus;
- n -= 2;
- }
-
- res += 1.0f;
- return res;
-}
-
-asin :: (t: f32) -> f32 {
- assert(false, "asin is not implemented yet!");
- return 0;
-}
-
-acos :: (t: f32) -> f32 {
- assert(false, "acos is not implemented yet!");
- return 0;
-}
-
-atan :: (t: f32) -> f32 {
- assert(false, "atan is not implemented yet!");
- return 0;
-}
-
-atan2 :: (t: f32) -> f32 {
- assert(false, "atan2 is not implemented yet!");
- return 0;
-}
-
-
-
-
-//
-// Hyperbolic trigonometry.
-// The hyperbolic trigonometry functions are implemented using the naive
-// definitions. There may be fancier, faster and far more precise methods
-// of implementing these, but these definitions should suffice.
-//
-
-sinh :: (t: $T) -> T {
- et := exp(t);
- return (et - (1 / et)) / 2;
-}
-
-cosh :: (t: $T) -> T {
- et := exp(t);
- return (et + (1 / et)) / 2;
-}
-
-tanh :: (t: $T) -> T {
- et := exp(t);
- one_over_et := 1 / et;
- return (et - one_over_et) / (et + one_over_et);
-}
-
-asinh :: (t: $T) -> T {
- return ~~ ln(cast(f32) (t + sqrt(t * t + 1)));
-}
-
-acosh :: (t: $T) -> T {
- return ~~ ln(cast(f32) (t + sqrt(t * t - 1)));
-}
-
-atanh :: (t: $T) -> T {
- return ~~ ln(cast(f32) ((1 + t) / (1 - t))) / 2;
-}
-
-
-
-//
-// Exponentials and logarithms.
-// Exponentials with floats are implemented using a binary search using square roots, since
-// square roots are intrinsic to WASM and therefore "fast". Expoentials with integers are
-// implemented using a fast algorithm that minimizes the number of the mulitplications that
-// are needed. Logarithms are implemented using a polynomial that is accurate in the range of
-// [1, 2], and then utilizes this identity for values outside of that range,
-//
-// ln(x) = ln(2^n * v) = n * ln(2) + ln(v), v is in [1, 2]
-//
-
-// FIX: This definition is very wrong. It casts E to be whatever the type of the argument is,
-// which if it is an integer, will be 2! This should always return a floating point number!
-exp :: (p: $T) -> T do return pow(base = cast(T) E, p = p);
-
-pow :: #match {
- // Fast implementation of power when raising to an integer power.
- (base: $T, p: i32) -> T {
- if base == 0 do return 0;
- if p == 0 do return 1;
-
- a: T = 1;
- while p > 0 {
- if p % 2 == 1 do a *= base;
- p = p >> 1;
- base *= base;
- }
-
- return a;
- },
-
- // Also make the implementation work for 64-bit integers.
- (base: $T, p: i64) -> T do return pow(base, cast(i32) p);,
-
- // Generic power implementation for integers using square roots.
- (base: $T, p: T) -> T {
- if p == 0 do return 1;
- if p < 0 do return 1 / pow(base, -p);
-
- if p >= 1 {
- tmp := pow(p = p / 2, base = base);
- return tmp * tmp;
- }
-
- low : T = 0;
- high : T = 1;
-
- sqr := sqrt(base);
- acc := sqr;
- mid := high / 2;
-
- while abs(mid - p) > 0.00001 {
- sqr = sqrt(sqr);
-
- if mid <= p {
- low = mid;
- acc *= sqr;
- } else {
- high = mid;
- acc /= sqr;
- }
-
- mid = (low + high) / 2;
- }
-
- return acc;
- }
-}
-
-power_mod :: (base: u32, exp: u32, mod: u32) -> u32 {
- t: u64 = 1;
- e: u64 = ~~exp;
- b: u64 = ~~base;
- m: u64 = ~~mod;
-
- while e > 0 {
- if e % 2 != 0 do t = (t * b) % m;
-
- b = (b * b) % m;
- e /= 2;
- }
-
- return ~~(t % m);
-}
-
-ln :: (a: f32) -> f32 {
- // FIX: This is probably not the most numerically stable solution.
- if a < 1 {
- return -ln(1 / a);
- // log2 := 63 - cast(i32) clz_i64(cast(i64) (1 / a));
- // x := a / cast(f32) (1 << log2);
- // res := -8.6731532f + (129.946172f + (-558.971892f + (843.967330f - 409.109529f * x) * x) * x) * x;
- // return res + cast(f32) log2 * 0.69314718f; // ln(2) = 0.69314718
- }
-
- log2 := 63 - cast(i32) wasm.clz_i64(cast(i64) a);
- x := a / cast(f32) (1 << log2);
- res := -1.7417939f + (2.8212026f + (-1.4699568f + (0.44717955f - 0.056570851f * x) * x) * x) * x;
- res += cast(f32) log2 * 0.69314718; // ln(2) = 0.69314718
- return res;
-}
-
-log :: (a: $T, base: $R) -> T {
- if a <= 0 || base <= 0 do return 0;
- return ~~(ln(cast(f32) a) / ln(cast(f32) base));
-}
-
-
-
-
-// These function are overloaded in order to use the builtin WASM intrinsics for the
-// operation first, and then default to a polymorphic function that works on any type.
-// The clunky part about these at the moment is that, if you wanted to pass 'max' to
-// a procedure, you would have to pass 'max_poly' instead, because overloaded functions
-// are not resolved when used by value, i.e. foo : (f32, f32) -> f32 = math.max; Even if
-// they would be however, the fact that these overloads are intrinsic means they cannot
-// be reference from the element section and therefore cannot be passed around or used
-// as values.
-max :: #match #local { wasm.max_f32, wasm.max_f64, max_poly }
-max_poly :: (a: $T, b: T) -> T {
- return a if a >= b else b;
-}
-
-min :: #match #local { wasm.min_f32, wasm.min_f64, min_poly }
-min_poly :: (a: $T, b: T) -> T {
- return a if a <= b else b;
-}
-
-clamp :: (v: $T, lo: T, hi: T) -> T {
- if v < lo do return lo;
- if v > hi do return hi;
- return v;
-}
-
-sqrt :: #match #local { wasm.sqrt_f32, wasm.sqrt_f64, sqrt_poly }
-sqrt_poly :: (x: $T) -> T {
- return ~~ wasm.sqrt_f64(~~ x);
-}
-
-abs :: #match #local { wasm.abs_f32, wasm.abs_f64, abs_poly }
-abs_poly :: (x: $T) -> T {
- return x if x >= 0 else -x;
-}
-
-sign :: (x: $T) -> T {
- if x > 0 do return 1;
- if x < 0 do return cast(T) -1;
- return 0;
-}
-
-copysign :: #match #local { wasm.copysign_f32, wasm.copysign_f64, copysign_poly }
-copysign_poly :: (x: $T, y: T) -> T {
- return abs(x) * sign(y);
-}
-
-
-
-
-//
-// Floating point rounding
-//
-
-ceil :: #match #local { wasm.ceil_f32, wasm.ceil_f64 }
-floor :: #match #local { wasm.floor_f32, wasm.floor_f64 }
-trunc :: #match #local { wasm.trunc_f32, wasm.trunc_f64 }
-nearest :: #match #local { wasm.nearest_f32, wasm.nearest_f64 }
-
-
-
-//
-// Integer operations
-//
-
-clz :: #match #local { wasm.clz_i32, wasm.clz_i64 }
-ctz :: #match #local { wasm.ctz_i32, wasm.ctz_i64 }
-popcnt :: #match #local { wasm.popcnt_i32, wasm.popcnt_i64 }
-rotate_left :: #match #local { wasm.rotl_i32, wasm.rotl_i64 }
-rotate_right :: #match #local { wasm.rotr_i32, wasm.rotr_i64 }
-
-
-
-lerp :: (t: f32, a: $T, b: T) -> T {
- return ~~(~~a * (1 - t) + ~~b * t);
-}
-
-choose :: (n: $T, k: T) -> T {
- assert(T == i32 || T == i64 || T == u32 || T == u64, "bad type for choose function");
-
- ret := 1;
- for i: (n - k + 1) .. (n + 1) {
- ret *= i;
- }
-
- for i: 1 .. (k + 1) {
- ret /= i;
- }
-
- return ret;
-}
-
-gcd :: (a: $T, b: T) -> T {
- if a < 0 do a = -a;
- if b < 0 do b = -b;
-
- if b == 0 do return a;
- return gcd(b, a % b);
-}
-
-lcm :: (a: $T, b: T) -> T {
- return (a * b) / gcd(a, b);
-}
-
-is_nan :: #match #local {}
-
-#overload
-is_nan :: (x: f32) -> bool {
- v := x;
- i := *cast(^u32) ^v;
- return (i & 0x7f800000) == 0x7f800000
- && (i & 0x007fffff) != 0;
-}
-
-#overload
-is_nan :: (x: f64) -> bool {
- v := x;
- i := *cast(^u64) ^v;
- return (i & 0x7ff0000000000000) == 0x7ff0000000000000
- && (i & 0x000fffffffffffff) != 0;
-}
-
-
-is_inf :: #match #local {}
-
-#overload
-is_inf :: (x: f32) -> bool {
- v := x;
- i := *cast(^u32) ^v;
- return (i & 0x7f800000) == 0x7f800000
- && (i & 0x007fffff) == 0;
-}
-
-#overload
-is_inf :: (x: f64) -> bool {
- v := x;
- i := *cast(^u64) ^v;
- return (i & 0x7ff0000000000000) == 0x7ff0000000000000
- && (i & 0x000fffffffffffff) == 0;
-}
-
-
-
-//
-// Tests
-//
-
-#if #defined(core.Running_Tests) {
-
-use core {test}
-
-@test.test.{"GCD works"}
-(t: ^test.T) {
- t->assert(gcd(35, 18) == 1, "gcd(35, 18) == 1");
- t->assert(gcd(35, 15) == 5, "gcd(35, 15) == 5");
- t->assert(gcd(35, 21) == 7, "gcd(35, 21) == 7");
- t->assert(gcd(35, 70) == 35, "gcd(35, 70) == 35");
-}
-
-}
--- /dev/null
+package core.math
+
+use core.intrinsics {wasm}
+
+// Things that are useful in any math library:
+// - Trigonometry
+// - modf, fmod
+
+// Other things that can be useful:
+// - Vector math
+// - Matrix math
+// - Why not tensor math??
+// - Complex numbers
+// - Dual numbers
+
+
+E :: 2.71828182845904523536f;
+PI :: 3.14159265f;
+TAU :: 6.28318330f;
+SQRT_2 :: 1.414213562f;
+
+//
+// Trigonometry
+// Basic trig functions have been implemented using taylor series approximations. The
+// approximations are very accurate, but rather computationally expensive. Programs that
+// rely heavily on trig functions would greatly benefit from improvements to the
+// implementations of these functions.
+//
+
+sin :: (t: f32) -> f32 {
+ while t >= PI do t -= TAU;
+ while t <= -PI do t += TAU;
+
+ res := 0.0f;
+
+ plus_minus := 1.0f;
+ n := 13;
+ while n > 1 {
+ res += plus_minus;
+ res *= t * t / cast(f32) (n * n - n);
+
+ plus_minus = -plus_minus;
+ n -= 2;
+ }
+
+ res += 1.0f;
+ res *= t;
+ return res;
+}
+
+cos :: (t: f32) -> f32 {
+ while t >= PI do t -= TAU;
+ while t <= -PI do t += TAU;
+
+ res := 0.0f;
+
+ plus_minus := 1.0f;
+ n := 12;
+ while n > 1 {
+ res += plus_minus;
+ res *= t * t / cast(f32) (n * n - n);
+
+ plus_minus = -plus_minus;
+ n -= 2;
+ }
+
+ res += 1.0f;
+ return res;
+}
+
+asin :: (t: f32) -> f32 {
+ assert(false, "asin is not implemented yet!");
+ return 0;
+}
+
+acos :: (t: f32) -> f32 {
+ assert(false, "acos is not implemented yet!");
+ return 0;
+}
+
+atan :: (t: f32) -> f32 {
+ assert(false, "atan is not implemented yet!");
+ return 0;
+}
+
+atan2 :: (t: f32) -> f32 {
+ assert(false, "atan2 is not implemented yet!");
+ return 0;
+}
+
+
+
+
+//
+// Hyperbolic trigonometry.
+// The hyperbolic trigonometry functions are implemented using the naive
+// definitions. There may be fancier, faster and far more precise methods
+// of implementing these, but these definitions should suffice.
+//
+
+sinh :: (t: $T) -> T {
+ et := exp(t);
+ return (et - (1 / et)) / 2;
+}
+
+cosh :: (t: $T) -> T {
+ et := exp(t);
+ return (et + (1 / et)) / 2;
+}
+
+tanh :: (t: $T) -> T {
+ et := exp(t);
+ one_over_et := 1 / et;
+ return (et - one_over_et) / (et + one_over_et);
+}
+
+asinh :: (t: $T) -> T {
+ return ~~ ln(cast(f32) (t + sqrt(t * t + 1)));
+}
+
+acosh :: (t: $T) -> T {
+ return ~~ ln(cast(f32) (t + sqrt(t * t - 1)));
+}
+
+atanh :: (t: $T) -> T {
+ return ~~ ln(cast(f32) ((1 + t) / (1 - t))) / 2;
+}
+
+
+
+//
+// Exponentials and logarithms.
+// Exponentials with floats are implemented using a binary search using square roots, since
+// square roots are intrinsic to WASM and therefore "fast". Expoentials with integers are
+// implemented using a fast algorithm that minimizes the number of the mulitplications that
+// are needed. Logarithms are implemented using a polynomial that is accurate in the range of
+// [1, 2], and then utilizes this identity for values outside of that range,
+//
+// ln(x) = ln(2^n * v) = n * ln(2) + ln(v), v is in [1, 2]
+//
+
+// FIX: This definition is very wrong. It casts E to be whatever the type of the argument is,
+// which if it is an integer, will be 2! This should always return a floating point number!
+exp :: (p: $T) -> T do return pow(base = cast(T) E, p = p);
+
+pow :: #match {
+ // Fast implementation of power when raising to an integer power.
+ (base: $T, p: i32) -> T {
+ if base == 0 do return 0;
+ if p == 0 do return 1;
+
+ a: T = 1;
+ while p > 0 {
+ if p % 2 == 1 do a *= base;
+ p = p >> 1;
+ base *= base;
+ }
+
+ return a;
+ },
+
+ // Also make the implementation work for 64-bit integers.
+ (base: $T, p: i64) -> T do return pow(base, cast(i32) p);,
+
+ // Generic power implementation for integers using square roots.
+ (base: $T, p: T) -> T {
+ if p == 0 do return 1;
+ if p < 0 do return 1 / pow(base, -p);
+
+ if p >= 1 {
+ tmp := pow(p = p / 2, base = base);
+ return tmp * tmp;
+ }
+
+ low : T = 0;
+ high : T = 1;
+
+ sqr := sqrt(base);
+ acc := sqr;
+ mid := high / 2;
+
+ while abs(mid - p) > 0.00001 {
+ sqr = sqrt(sqr);
+
+ if mid <= p {
+ low = mid;
+ acc *= sqr;
+ } else {
+ high = mid;
+ acc /= sqr;
+ }
+
+ mid = (low + high) / 2;
+ }
+
+ return acc;
+ }
+}
+
+power_mod :: (base: u32, exp: u32, mod: u32) -> u32 {
+ t: u64 = 1;
+ e: u64 = ~~exp;
+ b: u64 = ~~base;
+ m: u64 = ~~mod;
+
+ while e > 0 {
+ if e % 2 != 0 do t = (t * b) % m;
+
+ b = (b * b) % m;
+ e /= 2;
+ }
+
+ return ~~(t % m);
+}
+
+ln :: (a: f32) -> f32 {
+ // FIX: This is probably not the most numerically stable solution.
+ if a < 1 {
+ return -ln(1 / a);
+ // log2 := 63 - cast(i32) clz_i64(cast(i64) (1 / a));
+ // x := a / cast(f32) (1 << log2);
+ // res := -8.6731532f + (129.946172f + (-558.971892f + (843.967330f - 409.109529f * x) * x) * x) * x;
+ // return res + cast(f32) log2 * 0.69314718f; // ln(2) = 0.69314718
+ }
+
+ log2 := 63 - cast(i32) wasm.clz_i64(cast(i64) a);
+ x := a / cast(f32) (1 << log2);
+ res := -1.7417939f + (2.8212026f + (-1.4699568f + (0.44717955f - 0.056570851f * x) * x) * x) * x;
+ res += cast(f32) log2 * 0.69314718; // ln(2) = 0.69314718
+ return res;
+}
+
+log :: (a: $T, base: $R) -> T {
+ if a <= 0 || base <= 0 do return 0;
+ return ~~(ln(cast(f32) a) / ln(cast(f32) base));
+}
+
+
+
+
+// These function are overloaded in order to use the builtin WASM intrinsics for the
+// operation first, and then default to a polymorphic function that works on any type.
+// The clunky part about these at the moment is that, if you wanted to pass 'max' to
+// a procedure, you would have to pass 'max_poly' instead, because overloaded functions
+// are not resolved when used by value, i.e. foo : (f32, f32) -> f32 = math.max; Even if
+// they would be however, the fact that these overloads are intrinsic means they cannot
+// be reference from the element section and therefore cannot be passed around or used
+// as values.
+max :: #match #local { wasm.max_f32, wasm.max_f64, max_poly }
+max_poly :: (a: $T, b: T) -> T {
+ return a if a >= b else b;
+}
+
+min :: #match #local { wasm.min_f32, wasm.min_f64, min_poly }
+min_poly :: (a: $T, b: T) -> T {
+ return a if a <= b else b;
+}
+
+clamp :: (v: $T, lo: T, hi: T) -> T {
+ if v < lo do return lo;
+ if v > hi do return hi;
+ return v;
+}
+
+sqrt :: #match #local { wasm.sqrt_f32, wasm.sqrt_f64, sqrt_poly }
+sqrt_poly :: (x: $T) -> T {
+ return ~~ wasm.sqrt_f64(~~ x);
+}
+
+abs :: #match #local { wasm.abs_f32, wasm.abs_f64, abs_poly }
+abs_poly :: (x: $T) -> T {
+ return x if x >= 0 else -x;
+}
+
+sign :: (x: $T) -> T {
+ if x > 0 do return 1;
+ if x < 0 do return cast(T) -1;
+ return 0;
+}
+
+copysign :: #match #local { wasm.copysign_f32, wasm.copysign_f64, copysign_poly }
+copysign_poly :: (x: $T, y: T) -> T {
+ return abs(x) * sign(y);
+}
+
+
+
+
+//
+// Floating point rounding
+//
+
+ceil :: #match #local { wasm.ceil_f32, wasm.ceil_f64 }
+floor :: #match #local { wasm.floor_f32, wasm.floor_f64 }
+trunc :: #match #local { wasm.trunc_f32, wasm.trunc_f64 }
+nearest :: #match #local { wasm.nearest_f32, wasm.nearest_f64 }
+
+
+
+//
+// Integer operations
+//
+
+clz :: #match #local { wasm.clz_i32, wasm.clz_i64 }
+ctz :: #match #local { wasm.ctz_i32, wasm.ctz_i64 }
+popcnt :: #match #local { wasm.popcnt_i32, wasm.popcnt_i64 }
+rotate_left :: #match #local { wasm.rotl_i32, wasm.rotl_i64 }
+rotate_right :: #match #local { wasm.rotr_i32, wasm.rotr_i64 }
+
+
+
+lerp :: (t: f32, a: $T, b: T) -> T {
+ return ~~(~~a * (1 - t) + ~~b * t);
+}
+
+choose :: (n: $T, k: T) -> T {
+ assert(T == i32 || T == i64 || T == u32 || T == u64, "bad type for choose function");
+
+ ret := 1;
+ for i: (n - k + 1) .. (n + 1) {
+ ret *= i;
+ }
+
+ for i: 1 .. (k + 1) {
+ ret /= i;
+ }
+
+ return ret;
+}
+
+gcd :: (a: $T, b: T) -> T {
+ if a < 0 do a = -a;
+ if b < 0 do b = -b;
+
+ if b == 0 do return a;
+ return gcd(b, a % b);
+}
+
+lcm :: (a: $T, b: T) -> T {
+ return (a * b) / gcd(a, b);
+}
+
+is_nan :: #match #local {}
+
+#overload
+is_nan :: (x: f32) -> bool {
+ v := x;
+ i := *cast(^u32) ^v;
+ return (i & 0x7f800000) == 0x7f800000
+ && (i & 0x007fffff) != 0;
+}
+
+#overload
+is_nan :: (x: f64) -> bool {
+ v := x;
+ i := *cast(^u64) ^v;
+ return (i & 0x7ff0000000000000) == 0x7ff0000000000000
+ && (i & 0x000fffffffffffff) != 0;
+}
+
+
+is_inf :: #match #local {}
+
+#overload
+is_inf :: (x: f32) -> bool {
+ v := x;
+ i := *cast(^u32) ^v;
+ return (i & 0x7f800000) == 0x7f800000
+ && (i & 0x007fffff) == 0;
+}
+
+#overload
+is_inf :: (x: f64) -> bool {
+ v := x;
+ i := *cast(^u64) ^v;
+ return (i & 0x7ff0000000000000) == 0x7ff0000000000000
+ && (i & 0x000fffffffffffff) == 0;
+}
+
+
+
+//
+// Tests
+//
+
+#if #defined(core.Running_Tests) {
+
+use core {test}
+
+@test.test.{"GCD works"}
+(t: ^test.T) {
+ t->assert(gcd(35, 18) == 1, "gcd(35, 18) == 1");
+ t->assert(gcd(35, 15) == 5, "gcd(35, 15) == 5");
+ t->assert(gcd(35, 21) == 7, "gcd(35, 21) == 7");
+ t->assert(gcd(35, 70) == 35, "gcd(35, 70) == 35");
+}
+
+}
+++ /dev/null
-package core.memory
-
-align :: #match {
- (size: ^u64, align: u64) {
- if *size % align != 0 {
- *size += align - (*size % align);
- }
- },
-
- (size: u64, align: u64) -> u64 {
- if size % align != 0 {
- size += align - (size % align);
- }
- return size;
- }
-}
-
-copy :: core.intrinsics.wasm.memory_copy
-set :: core.intrinsics.wasm.memory_fill
-
-alloc_slice :: (sl: ^[] $T, count: i32, allocator := context.allocator) {
- sl.data = raw_alloc(allocator, sizeof T * count);
- sl.count = count;
-}
-
-make_slice :: ($T: type_expr, count: i32, allocator := context.allocator) -> [] T {
- return .{
- data = raw_alloc(allocator, sizeof T * count),
- count = count
- };
-}
-
-free_slice :: (sl: ^[] $T, allocator := context.allocator) {
- if sl.data == null do return;
-
- raw_free(allocator, sl.data);
- sl.data = null;
- sl.count = 0;
-}
-
-copy_slice :: (sl: [] $T, allocator := context.allocator) -> [] T {
- data := raw_alloc(allocator, sl.count * sizeof T);
- copy(data, sl.data, sl.count * sizeof T);
-
- return .{ data = data, count = sl.count };
-}
-
-resize_slice :: (sl: [] $T, new_size: i32, allocator := context.allocator) -> [] T {
- new_slice: [] T;
- new_slice.data = raw_alloc(allocator, sizeof T * new_size);
- new_slice.count = new_size;
-
- copy(new_slice.data, sl.data, sl.count * sizeof T);
-
- return new_slice;
-}
-
-
-#overload
-builtin.__make_overload :: macro (_: ^[] $T, count: u32, allocator := context.allocator) -> [] T {
- return (package core.memory).make_slice(T, count, allocator);
-}
-
-#overload
-builtin.delete :: macro (x: ^[] $T) {
- core.memory.free_slice(x);
-}
--- /dev/null
+package core.memory
+
+align :: #match {
+ (size: ^u64, align: u64) {
+ if *size % align != 0 {
+ *size += align - (*size % align);
+ }
+ },
+
+ (size: u64, align: u64) -> u64 {
+ if size % align != 0 {
+ size += align - (size % align);
+ }
+ return size;
+ }
+}
+
+copy :: core.intrinsics.wasm.memory_copy
+set :: core.intrinsics.wasm.memory_fill
+
+alloc_slice :: (sl: ^[] $T, count: i32, allocator := context.allocator) {
+ sl.data = raw_alloc(allocator, sizeof T * count);
+ sl.count = count;
+}
+
+make_slice :: ($T: type_expr, count: i32, allocator := context.allocator) -> [] T {
+ return .{
+ data = raw_alloc(allocator, sizeof T * count),
+ count = count
+ };
+}
+
+free_slice :: (sl: ^[] $T, allocator := context.allocator) {
+ if sl.data == null do return;
+
+ raw_free(allocator, sl.data);
+ sl.data = null;
+ sl.count = 0;
+}
+
+copy_slice :: (sl: [] $T, allocator := context.allocator) -> [] T {
+ data := raw_alloc(allocator, sl.count * sizeof T);
+ copy(data, sl.data, sl.count * sizeof T);
+
+ return .{ data = data, count = sl.count };
+}
+
+resize_slice :: (sl: [] $T, new_size: i32, allocator := context.allocator) -> [] T {
+ new_slice: [] T;
+ new_slice.data = raw_alloc(allocator, sizeof T * new_size);
+ new_slice.count = new_size;
+
+ copy(new_slice.data, sl.data, sl.count * sizeof T);
+
+ return new_slice;
+}
+
+
+#overload
+builtin.__make_overload :: macro (_: ^[] $T, count: u32, allocator := context.allocator) -> [] T {
+ return (package core.memory).make_slice(T, count, allocator);
+}
+
+#overload
+builtin.delete :: macro (x: ^[] $T) {
+ core.memory.free_slice(x);
+}
--- /dev/null
+package core.arg_parse
+
+use core
+
+arg_parse :: (c_args: [] cstr, output: any) -> bool {
+ arg_iter := iter.as_iterator(c_args)
+ |> iter.map((x) => string.from_cstr(*x));
+ defer arg_iter.close(arg_iter.data);
+
+ use runtime.info;
+
+ ptr_type := cast(^Type_Info_Pointer) get_type_info(output.type);
+ if ptr_type.kind != .Pointer do return false;
+
+ arg_type := cast(^Type_Info_Struct) get_type_info(ptr_type.to);
+ if arg_type.kind != .Struct do return false;
+
+ data_base := *cast(^rawptr) output.data;
+
+ for #no_close arg: arg_iter {
+ for ^member: arg_type.members {
+ for ^tag: member.tags {
+ if tag.type != str do continue;
+
+ to_match := *cast(^str) tag.data;
+ if arg != to_match do continue;
+
+ switch member.type {
+ case bool {
+ *(cast(^bool) (cast(^u8) data_base + member.offset)) = !*(cast(^bool) (cast(^u8) data_base + member.offset));
+ }
+
+ case i32 {
+ value_str, success := iter.take_one(arg_iter, no_close=true);
+ if !success do return false;
+
+ value := conv.str_to_i64(value_str);
+ *(cast(^i32) (cast(^u8) data_base + member.offset)) = ~~value;
+ }
+
+ case str {
+ value, success := iter.take_one(arg_iter, no_close=true);
+ if !success do return false;
+
+ *(cast(^str) (cast(^u8) data_base + member.offset)) = value;
+ }
+
+ case #default {
+ printf("Unsupported argument type, {}.\n", output.type);
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
\ No newline at end of file
type: SocketType;
family: SocketDomain;
+}
+
+// Inject methods for the socket
+#inject Socket {
close :: socket_close
setting :: socket_setting
is_alive :: socket_is_alive
package core.net
-use core {sync, thread, array, memory, alloc, os}
+use core {sync, thread, array, memory, alloc, os, iter}
#if !runtime.Multi_Threading_Enabled {
#error "Expected multi-threading to be enabled for TCP server.";
}
}
-tcp_get_events :: (use conn: ^TCP_Connection) -> Iterator(TCP_Event) {
- next :: (use conn: ^TCP_Connection) -> (TCP_Event, bool) {
- if event_cursor == events.count do return .{}, false;
+tcp_get_events :: (use conn: ^TCP_Connection) => {
+ conn.event_cursor = 0;
- defer event_cursor += 1;
- return events[event_cursor], true;
- }
+ return iter.generator(
+ conn,
- close :: (use conn: ^TCP_Connection) {
- for events {
- switch it.kind {
- case .Data {
- raw_free(event_allocator, (cast(^TCP_Event.Data) it.data).contents.data);
- }
- }
+ (use conn: ^TCP_Connection) -> (TCP_Event, bool) {
+ if event_cursor == events.count do return .{}, false;
- raw_free(event_allocator, it.data);
- }
+ defer event_cursor += 1;
+ return events[event_cursor], true;
+ },
- array.clear(^events);
- }
+ (use conn: ^TCP_Connection) {
+ for events {
+ switch it.kind {
+ case .Data {
+ raw_free(event_allocator, (cast(^TCP_Event.Data) it.data).contents.data);
+ }
+ }
- conn.event_cursor = 0;
+ raw_free(event_allocator, it.data);
+ }
- return .{
- data = conn,
- next = next,
- close = close,
- };
+ array.clear(^events);
+ });
}
TCP_Server :: struct {
use connection: TCP_Connection;
- Client :: struct {
- use socket : Socket;
- address : Socket_Address;
- state : State;
-
- recv_ready_event_present := false;
-
- State :: enum {
- Alive;
- Being_Killed;
- Dying;
- Dead;
- }
-
- read_complete :: (use this: ^Client) {
- recv_ready_event_present = false;
- }
- }
client_allocator: Allocator;
clients: [] ^Client;
kill_client :: tcp_server_kill_client
}
+#inject TCP_Server {
+ Client :: struct {
+ use socket : Socket;
+ address : Socket_Address;
+ state : State;
+
+ recv_ready_event_present := false;
+
+ State :: enum {
+ Alive;
+ Being_Killed;
+ Dying;
+ Dead;
+ }
+ }
+}
+
+#inject TCP_Server.Client {
+ read_complete :: (use this: ^TCP_Server.Client) {
+ recv_ready_event_present = false;
+ }
+}
+
tcp_server_make :: (max_clients := 32, allocator := context.allocator) -> ^TCP_Server {
socket, err := socket_create(.Inet, .Stream); // IPv6?
if err != .None do return null;
+++ /dev/null
-package core.random
-
-#local seed : i64 = 8675309
-
-#local RANDOM_MULTIPLIER :: 25214903917
-#local RANDOM_INCREMENT :: cast(i64) 11
-// #local RANDOM_MODULUS :: 1 << 32
-
-set_seed :: #match {
- (s: u32) do seed = ~~s; ,
- (s: u64) do seed = s; ,
-}
-
-int :: (s := ^seed) -> u32 {
- *s = *s * RANDOM_MULTIPLIER + RANDOM_INCREMENT;
- return cast(u32) ((*s >> 16) & ~~0xffffffff);
-}
-
-between :: (lo: i32, hi: i32) -> i32 do return int () % (hi + 1 - lo) + lo;
-
-float :: (lo := 0.0f, hi := 1.0f) -> f32 {
- return (cast(f32) (int() % (1 << 20)) / cast(f32) (1 << 20)) * (hi - lo) + lo;
-}
-
-choice :: (a: [] $T) -> T {
- return a[between(0, a.count - 1)];
-}
-
-string :: (bytes_long: u32, alpha_numeric := false, allocator := context.allocator) -> str {
- memory :: package core.memory
-
- s := memory.make_slice(u8, bytes_long, allocator=allocator);
- for^ s {
- if alpha_numeric {
- #persist alpha_numeral := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- *it = choice(alpha_numeral);
- } else {
- *it = ~~between(32, 127);
- }
- }
- return s;
-}
--- /dev/null
+package core.random
+
+#local seed : i64 = 8675309
+
+#local RANDOM_MULTIPLIER :: 25214903917
+#local RANDOM_INCREMENT :: cast(i64) 11
+// #local RANDOM_MODULUS :: 1 << 32
+
+set_seed :: #match {
+ (s: u32) do seed = ~~s; ,
+ (s: u64) do seed = s; ,
+}
+
+int :: (s := ^seed) -> u32 {
+ *s = *s * RANDOM_MULTIPLIER + RANDOM_INCREMENT;
+ return cast(u32) ((*s >> 16) & ~~0xffffffff);
+}
+
+between :: (lo: i32, hi: i32) -> i32 do return int () % (hi + 1 - lo) + lo;
+
+float :: (lo := 0.0f, hi := 1.0f) -> f32 {
+ return (cast(f32) (int() % (1 << 20)) / cast(f32) (1 << 20)) * (hi - lo) + lo;
+}
+
+choice :: (a: [] $T) -> T {
+ return a[between(0, a.count - 1)];
+}
+
+string :: (bytes_long: u32, alpha_numeric := false, allocator := context.allocator) -> str {
+ memory :: package core.memory
+
+ s := memory.make_slice(u8, bytes_long, allocator=allocator);
+ for^ s {
+ if alpha_numeric {
+ #persist alpha_numeral := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ *it = choice(alpha_numeral);
+ } else {
+ *it = ~~between(32, 127);
+ }
+ }
+ return s;
+}
package core
-#load "./alloc"
-#load "./memory"
+#load "./alloc/alloc"
+#load "./memory/memory"
#load "./container/array"
#load "./container/avl_tree"
#load "./container/bucket_array"
#load "./container/heap"
-#load "./conv"
-#load "./math"
-#load "./random"
-#load "./hash"
+#load "./conv/conv"
+#load "./conv/format"
+#load "./conv/parse"
-#load "./string"
+#load "./math/math"
+#load "./random/random"
+#load "./hash/hash"
+
+#load "./string/string"
#load "./string/reader"
#load "./string/buffer"
#load "./string/char_utils"
#load "./runtime/build_opts"
#load "./runtime/common"
#load "./runtime/default_link_options"
-#load "./arg_parse"
#load "./test/testing"
+#load "./misc/arg_parse"
#load "./misc/any_utils"
#local runtime :: package runtime
#if runtime.runtime == .Js { #load "./runtime/js" }
#if runtime.runtime != .Custom {
#load "./runtime/info/helper"
- #load "./stdio"
+ #load "./io/stdio"
#load "./encoding/base64"
#load "./encoding/csv"
+++ /dev/null
-package core
-// Currently, these symbols are dumped in the 'core' namespace, which means
-// most programs that just 'use package core' can access all of them, which
-// is convenient; However, it doesn't hurt to wonder if they should be the
-// 'core.io' package so these would become like 'io.printf(...)'. Of course,
-// you could always still do 'use package core.io', which would bring these
-// in anyway.
-
-
-#if runtime.runtime == .Custom {
- #error "'stdio' can only be included in the 'wasi' or 'js' runtime."
-}
-
-stdio_stream: io.Stream;
-
-auto_flush_stdio := true
-
-print :: #match #locked {
- (x: str) {
- io.write(^stdio.print_writer, x);
- if x[x.count - 1] == #char "\n" && auto_flush_stdio do __flush_stdio();
- },
-
- (x) => { io.write(^stdio.print_writer, x); },
- (x, y) => { io.write(^stdio.print_writer, x, y); },
-}
-
-println :: (x) => {
- print(x);
- print("\n");
-}
-
-// Standard formatted print.
-printf :: (format: str, va: ..any) {
- flush :: (_, to_output) => {
- io.write(^stdio.print_writer, to_output);
- __flush_stdio();
- return true;
- }
-
- buffer: [1024] u8;
- print(conv.format_va(buffer, format, va, .{null, flush}));
-}
-
-// Print to standard error, if available.
-#if #defined(runtime.__output_error) {
- eprintf :: (format: str, va: ..any) -> str {
- flush :: (_, to_output) => {
- runtime.__output_error(to_output);
- return true;
- }
-
- buffer: [1024] u8;
- runtime.__output_error(conv.format_va(buffer, format, va, .{null, flush}));
- }
-}
-
-// Print to a dynamically allocated string.
-aprintf :: (format: str, va: ..any) -> str {
- buffer: [8196] u8;
- out := conv.format_va(buffer, format, va);
- return string.alloc_copy(out);
-}
-
-// Print to a TEMPORARY dynamically allocated string.
-tprintf :: (format: str, va: ..any) -> str {
- buffer: [8196] u8;
- out := conv.format_va(buffer, format, va);
- return string.alloc_copy(out, allocator=context.temp_allocator);
-}
-
-byte_dump :: (ptr: rawptr, byte_count: u32, bytes_per_line := 8) {
- temp: [3] u8;
-
- u8_ptr := cast(^u8) ptr;
- for i: byte_count {
- val := u8_ptr[i];
-
- temp[0] = map_to_ascii(val >> 4);
- temp[1] = map_to_ascii(val & 15);
- temp[2] = #char " ";
-
- runtime.__output_string(~~temp);
-
- if i % bytes_per_line == (bytes_per_line - 1) do runtime.__output_string("\n");
- }
-
-
- map_to_ascii :: (x: u8) -> u8 {
- switch x {
- case 0 do return #char "0";
- case 1 do return #char "1";
- case 2 do return #char "2";
- case 3 do return #char "3";
- case 4 do return #char "4";
- case 5 do return #char "5";
- case 6 do return #char "6";
- case 7 do return #char "7";
- case 8 do return #char "8";
- case 9 do return #char "9";
- case 10 do return #char "A";
- case 11 do return #char "B";
- case 12 do return #char "C";
- case 13 do return #char "D";
- case 14 do return #char "E";
- case 15 do return #char "F";
- case #default do return #char "X";
- }
- }
-}
-
-
-//
-// Private and internal things
-//
-
-#thread_local stdio : struct {
- print_stream : io.BufferStream;
- print_writer : io.Writer;
-}
-
-__stdio_init :: () {
- stdio.print_stream = io.buffer_stream_make(2048, context.allocator);
- stdio.print_writer = io.writer_make(^stdio.print_stream);
-
- // This shouldn't need to be here, but because ^stdin_vtable is not a compile-time
- // known value (even through it should be).
- stdio_stream.vtable = ^stdio_vtable;
-}
-
-
-__flush_stdio :: () {
- if stdio.print_stream.data.count == 0 do return;
-
- ^stdio.print_stream
- |> io.buffer_stream_to_str()
- |> runtime.__output_string();
-
- ^stdio.print_stream |> io.stream_flush();
-}
-
-#local stdio_vtable := io.Stream_Vtable.{
- read = (_: ^io.Stream, buffer: [] u8) -> (io.Error, u32) {
- __flush_stdio();
- bytes_read := runtime.__read_from_input(buffer);
- if bytes_read == 0 do return .ReadPending, 0;
- if bytes_read < 0 do return .EOF, 0;
-
- return .None, bytes_read;
- },
-
- read_byte = (_: ^io.Stream) -> (io.Error, u8) {
- __flush_stdio();
- buf: [1] u8;
- bytes_read := runtime.__read_from_input(buf);
- if bytes_read <= 0 do return .EOF, 0;
-
- return .None, buf[0];
- },
-
- write = (_: ^io.Stream, buffer: [] u8) -> (io.Error, u32) {
- return io.stream_write(^stdio.print_stream, buffer);
- },
-
- write_byte = (_: ^io.Stream, byte: u8) -> io.Error {
- return io.stream_write_byte(^stdio.print_stream, byte);
- },
-
- flush = (_: ^io.Stream) -> io.Error {
- __flush_stdio();
- return .None;
- }
-}
+++ /dev/null
-package core.string
-
-use core
-
-as_str :: #match {}
-
-free :: (s: str, allocator := context.allocator) do raw_free(allocator, s.data);
-
-alloc_copy :: (original: str, allocator := context.allocator) -> str {
- new_str : str;
- new_str.data = raw_alloc(allocator, sizeof u8 * original.count);
- new_str.count = original.count;
- copy(original, new_str);
- return new_str;
-}
-
-temp_copy :: (original: str) -> str {
- new_str := make([] u8, original.count, allocator=context.temp_allocator);
- copy(original, new_str);
- return new_str;
-}
-
-copy :: (orig: str, dest: str) {
- len := orig.count;
- if dest.count < len do len = dest.count;
-
- memory.copy(dest.data, orig.data, len);
-}
-
-#match as_str from_cstr
-from_cstr :: (s: cstr) -> str {
- return .{ data = s, count = length(s) };
-}
-
-
-length :: #match #local {}
-
-#overload
-length :: (s: str) => s.count;
-
-#overload
-length :: (s: cstr) -> u32 {
- len := 0;
- c := s;
- while *c != #char "\0" {
- len += 1;
- c += 1;
- }
-
- return len;
-}
-
-
-concat :: #match #local {}
-
-#overload
-concat :: (s1: str, s2: str, allocator := context.allocator) -> str {
- len1 := length(s1);
- len2 := length(s2);
-
- data := cast(^u8) raw_alloc(allocator, len1 + len2);
- memory.copy(data, s1.data, len1);
- memory.copy(data + len1, s2.data, len2);
-
- return str.{ data, len1 + len2 };
-}
-
-// @Cleanup // Don't love that the allocator is necessary here,
-// but it is impossible to specify a default value for the
-// allocator while having a variadic number of strings. This
-// is only due to the languages constraints however. This
-// could easily be changed since there is no ambiguity.
-#overload
-concat :: (allocator: Allocator, strings: ..str) -> str {
- total_length := 0;
- for s: strings do total_length += s.count;
-
- data := cast(^u8) raw_alloc(allocator, total_length);
- offset := 0;
- for s: strings {
- memory.copy(data + offset, s.data, s.count);
- offset += s.count;
- }
-
- return str.{ data, total_length };
-}
-
-#overload
-concat :: (buffer: [] u8, strings: ..str) -> str {
- total_copied := 0;
- for s: strings {
- // Should never greater than, but better safe than sorry.
- if total_copied >= buffer.count do break;
-
- bytes_to_copy := math.min(s.count, buffer.count - total_copied);
- memory.copy(buffer.data + total_copied, s.data, bytes_to_copy);
- total_copied += bytes_to_copy;
- }
-
- return buffer[0 .. total_copied];
-}
-
-#overload
-concat :: (into: ^[..] u8, strings: ..str) -> str {
- for s: strings {
- array.ensure_capacity(into, into.count + s.count);
- memory.copy(into.data + into.count, s.data, s.count);
- into.count += s.count;
- }
- return .{ into.data, into.count };
-}
-
-
-contains :: #match #local {}
-
-#overload
-contains :: (s: str, c: u8) -> bool {
- for ch: s do if ch == c do return true;
- return false;
-}
-
-#overload
-contains :: (s: str, substr: str) -> bool {
- while i := 0; i < s.count {
- while j := 0; j < substr.count {
- if s[i + j] != substr[j] {
- i += j + 1;
- continue continue;
- }
-
- j += 1;
- }
-
- return true;
- }
-
- return false;
-}
-
-
-// @TODO
-// Check this for edge cases and other bugs. I'm not confident
-// it will work perfectly yet. - brendanfh 2020/12/21
-compare :: (str1: str, str2: str) -> i32 {
- i := 0;
- while i < str1.count && i < str2.count {
- if str1[i] == str2[i] do i += 1;
- else do break;
- }
-
- if i == str1.count && i == str2.count do return 0;
- return ~~(str1[i] - str2[i]);
-}
-
-equal :: (str1: str, str2: str) -> bool {
- if str1.count != str2.count do return false;
- while i := 0; i < str1.count {
- if str1[i] != str2[i] do return false;
- i += 1;
- }
- return true;
-}
-
-equal_insensitive :: (s1, s2: str) -> bool {
- if s1.count != s2.count do return false;
- while i := 0; i < s1.count {
- defer i += 1;
- if s1[i] == s2[i] do continue;
-
- c1 := s1[i];
- c2 := s2[i];
- if c1 >= #char "A" && c1 <= #char "Z" do c1 += 32;
- if c2 >= #char "A" && c2 <= #char "Z" do c2 += 32;
- if c1 != c2 do return false;
- }
- return true;
-}
-
-#operator == equal
-#operator != macro (s1: str, s2: str) => !(s1 == s2);
-
-starts_with :: (s: str, prefix: str) -> bool {
- if s.count < prefix.count do return false;
- while i := 0; i < prefix.count {
- if s[i] != prefix[i] do return false;
- i += 1;
- }
- return true;
-}
-
-ends_with :: (s: str, suffix: str) -> bool {
- if s.count < suffix.count do return false;
- while i := 0; i < suffix.count {
- if s[s.count - 1 - i] != suffix[suffix.count - 1 - i] do return false;
- i += 1;
- }
- return true;
-}
-
-empty :: (s: str) => s.count == 0 || s.data == null;
-
-is_empty :: (s: str) -> bool #deprecated "Use 'string.empty' instead." {
- s.count == 0 || s.data == null;
-}
-
-index_of :: (s: str, c: u8) -> i32 {
- for s.count {
- if s[it] == c do return it;
- }
- return -1;
-}
-
-last_index_of :: (s: str, c: u8) -> i32 {
- for range.{s.count, 0, -1} {
- if s[it] == c do return it;
- }
- return -1;
-}
-
-
-strip_whitespace :: #match #local {}
-
-#overload
-strip_whitespace :: (s: ^str) {
- strip_leading_whitespace(s);
- strip_trailing_whitespace(s);
-}
-
-#overload
-strip_whitespace :: (s: str) =>
- s |> strip_leading_whitespace()
- |> strip_trailing_whitespace()
-
-
-strip_leading_whitespace :: #match #local {}
-
-#overload
-strip_leading_whitespace :: (s: ^str) {
- while s.count > 0 do switch s.data[0] {
- case #char " ", #char "\t", #char "\n", #char "\r" {
- s.data += 1;
- s.count -= 1;
- }
-
- case #default do return;
- }
-}
-
-#overload
-strip_leading_whitespace :: (s: str) -> str {
- out := s;
- strip_leading_whitespace(^out);
- return out;
-}
-
-
-strip_trailing_whitespace :: #match #local {}
-
-#overload
-strip_trailing_whitespace :: (s: ^str) {
- while s.count >= 1 do switch s.data[s.count - 1] {
- case #char " ", #char "\t", #char "\n", #char "\r" {
- s.count -= 1;
- }
-
- case #default do return;
- }
-}
-
-#overload
-strip_trailing_whitespace :: (s: str) -> str {
- out := s;
- strip_trailing_whitespace(^out);
- return out;
-}
-
-to_uppercase :: (s: str) -> str {
- for^ ch: s {
- if *ch >= #char "a" && *ch <= #char "z" {
- *ch -= 32;
- }
- }
-
- return s;
-}
-
-to_lowercase :: (s: str) -> str {
- for^ ch: s {
- if *ch >= #char "A" && *ch <= #char "Z" {
- *ch += 32;
- }
- }
-
- return s;
-}
-
-
-trim_start :: #match #local {}
-
-#overload
-trim_start :: (s: ^str, char: u8) {
- while s.data[0] == char {
- s.data += 1;
- s.count -= 1;
- }
-}
-
-#overload
-trim_start :: (s: str, char: u8) -> str {
- out := s;
- trim_start(^out, char);
- return out;
-}
-
-
-trim_end :: #match #local {}
-
-#overload
-trim_end :: (s: ^str, char: u8) {
- while s.data[s.count - 1] == char {
- s.count -= 1;
- }
-}
-
-#overload
-trim_end :: (s: str, char: u8) -> str {
- out := s;
- trim_end(^out, char);
- return out;
-}
-
-
-advance :: #match #local {}
-
-#overload
-advance :: (s: ^str, chars := 1) {
- chars = math.min(chars, s.count);
-
- s.data += chars;
- s.count -= chars;
-}
-
-#overload
-advance :: (s: str, chars := 1) -> str {
- chars = math.min(chars, s.count);
- out := s;
-
- out.data += chars;
- out.count -= chars;
-
- return out;
-}
-
-replace :: (s: str, to_replace: u8, replace_with: u8) {
- for ^c: s {
- if *c == to_replace do *c = replace_with;
- }
-}
-
-read_until :: #match #local {}
-
-#overload
-read_until :: (s: ^str, upto: u8, skip := 0) -> str {
- if s.count == 0 do return "";
-
- out : str;
- out.data = s.data;
- out.count = 0;
-
- rem := skip;
- for ch: *s {
- if ch == upto {
- if rem <= 0 do break;
- else do rem -= 1;
- }
-
- out.count += 1;
- }
-
- s.data += out.count;
- s.count -= out.count;
-
- return out;
-}
-
-#overload
-read_until :: (s: ^str, upto: str, skip := 0) -> str {
- if s.count == 0 do return "";
-
- out := str.{ data = s.data };
-
- rem := skip;
- i := 0;
- while i <= s.count - upto.count {
- match := true;
- j := i;
- for upto {
- if s.data[j] != it {
- match = false;
- break;
- }
-
- j += 1;
- }
-
- if match {
- if rem <= 0 do break;
- else do rem -= 1;
- }
-
- i += 1;
- }
-
- if i > s.count - upto.count {
- out = *s;
- s.data += out.count;
- s.count = 0;
-
- } else {
- out.count = i;
- s.data += out.count;
- s.count -= out.count;
- }
-
- return out;
-}
-
-read_alphanum :: (s: ^str) -> str {
- if s.count == 0 do return "";
-
- out : str;
- out.data = s.data;
- out.count = 0;
-
- for ch: *s {
- switch ch {
- case #char "a" .. #char "z",
- #char "A" .. #char "Z",
- #char "0" .. #char "9" {
- out.count += 1;
- }
-
- case #default {
- break break;
- }
- }
- }
-
- s.data += out.count;
- s.count -= out.count;
-
- return out;
-}
-
-read_until_any :: (s: ^str, skip: u32, uptos: ..u8) -> str {
- if s.count == 0 do return "";
-
- out : str;
- out.data = s.data;
- out.count = 0;
-
- rem := skip;
- for ch: *s {
- for upto: uptos {
- if ch == upto {
- if rem <= 0 do break break;
- else do rem -= 1;
- }
- }
-
- out.count += 1;
- }
-
- s.data += out.count;
- s.count -= out.count;
-
- return out;
-}
-
-advance_line :: (s: ^str) {
- if s.count == 0 do return;
-
- adv := 0;
- while s.data[adv] != #char "\n" do adv += 1;
-
- s.data += adv + 1;
- s.count -= adv + 1;
-}
-
-split :: (s: str, delim: u8, allocator := context.allocator) -> []str {
- delim_count := 0;
- for i: 0 .. s.count do if s[i] == delim do delim_count += 1;
-
- strarr := cast(^str) raw_alloc(allocator, sizeof str * (delim_count + 1));
-
- curr_str := 0;
- begin := 0;
-
- for i: 0 .. s.count {
- if s[i] == delim {
- strarr[curr_str] = s.data[begin .. i];
- begin = i + 1;
- curr_str += 1;
- }
- }
-
- strarr[curr_str] = s.data[begin .. s.count];
-
- return strarr[0 .. delim_count + 1];
-}
-
-split_iter :: (s: str, delim: u8) -> Iterator(str) {
- return iter.generator(
- ^.{ s = s, delim = delim },
-
- (ctx: ^$T) -> (str, bool) {
- if string.empty(ctx.s) {
- return "", false;
- }
-
- ret: str;
- ret, ctx.s = bisect(ctx.s, ctx.delim);
- return ret, true;
- }
- );
-}
-
-//
-// Splits a string into two parts, divided by the
-// first instance of the provided character. Either
-// string can be empty if the first instance of the
-// character occurs at the very beginning or end of
-// the string, or if it does not occur at all.
-//
-bisect :: (s: str, c: u8) -> (str, str) {
- index := index_of(s, c);
- if index == -1 {
- return s, "";
- }
-
- return s[0 .. index], s[index+1 .. s.length];
-}
-
--- /dev/null
+package core.string
+
+use core
+
+as_str :: #match {}
+
+free :: (s: str, allocator := context.allocator) do raw_free(allocator, s.data);
+
+alloc_copy :: (original: str, allocator := context.allocator) -> str {
+ new_str : str;
+ new_str.data = raw_alloc(allocator, sizeof u8 * original.count);
+ new_str.count = original.count;
+ copy(original, new_str);
+ return new_str;
+}
+
+temp_copy :: (original: str) -> str {
+ new_str := make([] u8, original.count, allocator=context.temp_allocator);
+ copy(original, new_str);
+ return new_str;
+}
+
+copy :: (orig: str, dest: str) {
+ len := orig.count;
+ if dest.count < len do len = dest.count;
+
+ memory.copy(dest.data, orig.data, len);
+}
+
+#match as_str from_cstr
+from_cstr :: (s: cstr) -> str {
+ return .{ data = s, count = length(s) };
+}
+
+
+length :: #match #local {}
+
+#overload
+length :: (s: str) => s.count;
+
+#overload
+length :: (s: cstr) -> u32 {
+ len := 0;
+ c := s;
+ while *c != #char "\0" {
+ len += 1;
+ c += 1;
+ }
+
+ return len;
+}
+
+
+concat :: #match #local {}
+
+#overload
+concat :: (s1: str, s2: str, allocator := context.allocator) -> str {
+ len1 := length(s1);
+ len2 := length(s2);
+
+ data := cast(^u8) raw_alloc(allocator, len1 + len2);
+ memory.copy(data, s1.data, len1);
+ memory.copy(data + len1, s2.data, len2);
+
+ return str.{ data, len1 + len2 };
+}
+
+// @Cleanup // Don't love that the allocator is necessary here,
+// but it is impossible to specify a default value for the
+// allocator while having a variadic number of strings. This
+// is only due to the languages constraints however. This
+// could easily be changed since there is no ambiguity.
+#overload
+concat :: (allocator: Allocator, strings: ..str) -> str {
+ total_length := 0;
+ for s: strings do total_length += s.count;
+
+ data := cast(^u8) raw_alloc(allocator, total_length);
+ offset := 0;
+ for s: strings {
+ memory.copy(data + offset, s.data, s.count);
+ offset += s.count;
+ }
+
+ return str.{ data, total_length };
+}
+
+#overload
+concat :: (buffer: [] u8, strings: ..str) -> str {
+ total_copied := 0;
+ for s: strings {
+ // Should never greater than, but better safe than sorry.
+ if total_copied >= buffer.count do break;
+
+ bytes_to_copy := math.min(s.count, buffer.count - total_copied);
+ memory.copy(buffer.data + total_copied, s.data, bytes_to_copy);
+ total_copied += bytes_to_copy;
+ }
+
+ return buffer[0 .. total_copied];
+}
+
+#overload
+concat :: (into: ^[..] u8, strings: ..str) -> str {
+ for s: strings {
+ array.ensure_capacity(into, into.count + s.count);
+ memory.copy(into.data + into.count, s.data, s.count);
+ into.count += s.count;
+ }
+ return .{ into.data, into.count };
+}
+
+
+contains :: #match #local {}
+
+#overload
+contains :: (s: str, c: u8) -> bool {
+ for ch: s do if ch == c do return true;
+ return false;
+}
+
+#overload
+contains :: (s: str, substr: str) -> bool {
+ while i := 0; i < s.count {
+ while j := 0; j < substr.count {
+ if s[i + j] != substr[j] {
+ i += j + 1;
+ continue continue;
+ }
+
+ j += 1;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+
+// @TODO
+// Check this for edge cases and other bugs. I'm not confident
+// it will work perfectly yet. - brendanfh 2020/12/21
+compare :: (str1: str, str2: str) -> i32 {
+ i := 0;
+ while i < str1.count && i < str2.count {
+ if str1[i] == str2[i] do i += 1;
+ else do break;
+ }
+
+ if i == str1.count && i == str2.count do return 0;
+ return ~~(str1[i] - str2[i]);
+}
+
+equal :: (str1: str, str2: str) -> bool {
+ if str1.count != str2.count do return false;
+ while i := 0; i < str1.count {
+ if str1[i] != str2[i] do return false;
+ i += 1;
+ }
+ return true;
+}
+
+equal_insensitive :: (s1, s2: str) -> bool {
+ if s1.count != s2.count do return false;
+ while i := 0; i < s1.count {
+ defer i += 1;
+ if s1[i] == s2[i] do continue;
+
+ c1 := s1[i];
+ c2 := s2[i];
+ if c1 >= #char "A" && c1 <= #char "Z" do c1 += 32;
+ if c2 >= #char "A" && c2 <= #char "Z" do c2 += 32;
+ if c1 != c2 do return false;
+ }
+ return true;
+}
+
+#operator == equal
+#operator != macro (s1: str, s2: str) => !(s1 == s2);
+
+starts_with :: (s: str, prefix: str) -> bool {
+ if s.count < prefix.count do return false;
+ while i := 0; i < prefix.count {
+ if s[i] != prefix[i] do return false;
+ i += 1;
+ }
+ return true;
+}
+
+ends_with :: (s: str, suffix: str) -> bool {
+ if s.count < suffix.count do return false;
+ while i := 0; i < suffix.count {
+ if s[s.count - 1 - i] != suffix[suffix.count - 1 - i] do return false;
+ i += 1;
+ }
+ return true;
+}
+
+empty :: (s: str) => s.count == 0 || s.data == null;
+
+is_empty :: (s: str) -> bool #deprecated "Use 'string.empty' instead." {
+ s.count == 0 || s.data == null;
+}
+
+index_of :: (s: str, c: u8) -> i32 {
+ for s.count {
+ if s[it] == c do return it;
+ }
+ return -1;
+}
+
+last_index_of :: (s: str, c: u8) -> i32 {
+ for range.{s.count, 0, -1} {
+ if s[it] == c do return it;
+ }
+ return -1;
+}
+
+
+strip_whitespace :: #match #local {}
+
+#overload
+strip_whitespace :: (s: ^str) {
+ strip_leading_whitespace(s);
+ strip_trailing_whitespace(s);
+}
+
+#overload
+strip_whitespace :: (s: str) =>
+ s |> strip_leading_whitespace()
+ |> strip_trailing_whitespace()
+
+
+strip_leading_whitespace :: #match #local {}
+
+#overload
+strip_leading_whitespace :: (s: ^str) {
+ while s.count > 0 do switch s.data[0] {
+ case #char " ", #char "\t", #char "\n", #char "\r" {
+ s.data += 1;
+ s.count -= 1;
+ }
+
+ case #default do return;
+ }
+}
+
+#overload
+strip_leading_whitespace :: (s: str) -> str {
+ out := s;
+ strip_leading_whitespace(^out);
+ return out;
+}
+
+
+strip_trailing_whitespace :: #match #local {}
+
+#overload
+strip_trailing_whitespace :: (s: ^str) {
+ while s.count >= 1 do switch s.data[s.count - 1] {
+ case #char " ", #char "\t", #char "\n", #char "\r" {
+ s.count -= 1;
+ }
+
+ case #default do return;
+ }
+}
+
+#overload
+strip_trailing_whitespace :: (s: str) -> str {
+ out := s;
+ strip_trailing_whitespace(^out);
+ return out;
+}
+
+to_uppercase :: (s: str) -> str {
+ for^ ch: s {
+ if *ch >= #char "a" && *ch <= #char "z" {
+ *ch -= 32;
+ }
+ }
+
+ return s;
+}
+
+to_lowercase :: (s: str) -> str {
+ for^ ch: s {
+ if *ch >= #char "A" && *ch <= #char "Z" {
+ *ch += 32;
+ }
+ }
+
+ return s;
+}
+
+
+trim_start :: #match #local {}
+
+#overload
+trim_start :: (s: ^str, char: u8) {
+ while s.data[0] == char {
+ s.data += 1;
+ s.count -= 1;
+ }
+}
+
+#overload
+trim_start :: (s: str, char: u8) -> str {
+ out := s;
+ trim_start(^out, char);
+ return out;
+}
+
+
+trim_end :: #match #local {}
+
+#overload
+trim_end :: (s: ^str, char: u8) {
+ while s.data[s.count - 1] == char {
+ s.count -= 1;
+ }
+}
+
+#overload
+trim_end :: (s: str, char: u8) -> str {
+ out := s;
+ trim_end(^out, char);
+ return out;
+}
+
+
+advance :: #match #local {}
+
+#overload
+advance :: (s: ^str, chars := 1) {
+ chars = math.min(chars, s.count);
+
+ s.data += chars;
+ s.count -= chars;
+}
+
+#overload
+advance :: (s: str, chars := 1) -> str {
+ chars = math.min(chars, s.count);
+ out := s;
+
+ out.data += chars;
+ out.count -= chars;
+
+ return out;
+}
+
+replace :: (s: str, to_replace: u8, replace_with: u8) {
+ for ^c: s {
+ if *c == to_replace do *c = replace_with;
+ }
+}
+
+read_until :: #match #local {}
+
+#overload
+read_until :: (s: ^str, upto: u8, skip := 0) -> str {
+ if s.count == 0 do return "";
+
+ out : str;
+ out.data = s.data;
+ out.count = 0;
+
+ rem := skip;
+ for ch: *s {
+ if ch == upto {
+ if rem <= 0 do break;
+ else do rem -= 1;
+ }
+
+ out.count += 1;
+ }
+
+ s.data += out.count;
+ s.count -= out.count;
+
+ return out;
+}
+
+#overload
+read_until :: (s: ^str, upto: str, skip := 0) -> str {
+ if s.count == 0 do return "";
+
+ out := str.{ data = s.data };
+
+ rem := skip;
+ i := 0;
+ while i <= s.count - upto.count {
+ match := true;
+ j := i;
+ for upto {
+ if s.data[j] != it {
+ match = false;
+ break;
+ }
+
+ j += 1;
+ }
+
+ if match {
+ if rem <= 0 do break;
+ else do rem -= 1;
+ }
+
+ i += 1;
+ }
+
+ if i > s.count - upto.count {
+ out = *s;
+ s.data += out.count;
+ s.count = 0;
+
+ } else {
+ out.count = i;
+ s.data += out.count;
+ s.count -= out.count;
+ }
+
+ return out;
+}
+
+read_alphanum :: (s: ^str) -> str {
+ if s.count == 0 do return "";
+
+ out : str;
+ out.data = s.data;
+ out.count = 0;
+
+ for ch: *s {
+ switch ch {
+ case #char "a" .. #char "z",
+ #char "A" .. #char "Z",
+ #char "0" .. #char "9" {
+ out.count += 1;
+ }
+
+ case #default {
+ break break;
+ }
+ }
+ }
+
+ s.data += out.count;
+ s.count -= out.count;
+
+ return out;
+}
+
+read_until_any :: (s: ^str, skip: u32, uptos: ..u8) -> str {
+ if s.count == 0 do return "";
+
+ out : str;
+ out.data = s.data;
+ out.count = 0;
+
+ rem := skip;
+ for ch: *s {
+ for upto: uptos {
+ if ch == upto {
+ if rem <= 0 do break break;
+ else do rem -= 1;
+ }
+ }
+
+ out.count += 1;
+ }
+
+ s.data += out.count;
+ s.count -= out.count;
+
+ return out;
+}
+
+advance_line :: (s: ^str) {
+ if s.count == 0 do return;
+
+ adv := 0;
+ while s.data[adv] != #char "\n" do adv += 1;
+
+ s.data += adv + 1;
+ s.count -= adv + 1;
+}
+
+split :: (s: str, delim: u8, allocator := context.allocator) -> []str {
+ delim_count := 0;
+ for i: 0 .. s.count do if s[i] == delim do delim_count += 1;
+
+ strarr := cast(^str) raw_alloc(allocator, sizeof str * (delim_count + 1));
+
+ curr_str := 0;
+ begin := 0;
+
+ for i: 0 .. s.count {
+ if s[i] == delim {
+ strarr[curr_str] = s.data[begin .. i];
+ begin = i + 1;
+ curr_str += 1;
+ }
+ }
+
+ strarr[curr_str] = s.data[begin .. s.count];
+
+ return strarr[0 .. delim_count + 1];
+}
+
+split_iter :: (s: str, delim: u8) -> Iterator(str) {
+ return iter.generator(
+ ^.{ s = s, delim = delim },
+
+ (ctx: ^$T) -> (str, bool) {
+ if string.empty(ctx.s) {
+ return "", false;
+ }
+
+ ret: str;
+ ret, ctx.s = bisect(ctx.s, ctx.delim);
+ return ret, true;
+ }
+ );
+}
+
+//
+// Splits a string into two parts, divided by the
+// first instance of the provided character. Either
+// string can be empty if the first instance of the
+// character occurs at the very beginning or end of
+// the string, or if it does not occur at all.
+//
+bisect :: (s: str, c: u8) -> (str, str) {
+ index := index_of(s, c);
+ if index == -1 {
+ return s, "";
+ }
+
+ return s[0 .. index], s[index+1 .. s.length];
+}
+
}
}
+#tag Command.{ "run", "Run the command provided in `config.run_cmd`." }
+run_run_command :: (args: [] cstr) {
+ if !string.empty(config.config.run_cmd) {
+ run_command_and_forward_output(config.config.run_cmd);
+ }
+}
+
+#tag Command.{ "debug", "Run the command provided in `config.debug_cmd`." }
+run_debug_command :: (args: [] cstr) {
+ if !string.empty(config.config.debug_cmd) {
+ run_command_and_forward_output(config.config.debug_cmd);
+ }
+}
+
+#tag Command.{ "test", "Run the command provided in `config.test_cmd`." }
+run_test_command :: (args: [] cstr) {
+ if !string.empty(config.config.test_cmd) {
+ run_command_and_forward_output(config.config.test_cmd);
+ }
+}
+
#local {
#if runtime.compiler_os == .Linux {
}
}
+run_command_and_forward_output :: (cmd: str) => {
+ args := string.split(cmd, #char " ", context.temp_allocator);
+ prog := args[0];
+ args = args[1 .. args.count];
+
+ run_proc := os.process_spawn(prog, args);
+ r := io.reader_make(^run_proc);
+
+ while !(r->empty()) {
+ line := r->read_line(consume_newline=true);
+ print(line);
+ }
+
+ return os.process_wait(^run_proc);
+}
+
#tag conv.Custom_Parse.{parse}
#tag conv.Custom_Format.{format}
Config :: struct {
lib_source_directory: str = "./lib";
lib_bin_directory: str = "./bin";
+ run_cmd: str;
+ debug_cmd: str;
+ test_cmd: str;
}
config: Config = .{};
output.type = (cast(^info.Type_Info_Pointer) t).to;
}
- // Set the temporary allocator to an arena that will be freed at the end.
- alloc.arena.auto(32 * 1024, #(context.temp_allocator));
+ defer alloc.clear_temp_allocator();
active_item_ptr := null;
active_item_type := void;
printf("{}{}\n", map.get(^imap, 50), map.get(^imap, 1234));
- printf("{*}\n", ^imap);
+ printf("{*p}\n", ^imap);
map.delete(^imap, 50);
println(map.has(^imap, 50));