get :: get
get_ptr :: get_ptr
get_opt :: get_opt
+ get_ptr_or_create :: get_ptr_or_create
put :: put
delete :: delete
update :: update
return null;
}
+//
+// Returns a pointer to the value at the specified key. If the key
+// is not in the map, a new value is created and inserted, then the
+// pointer to that value is returned.
+get_ptr_or_create :: (use map: ^Map, key: map.Key_Type) -> ^map.Value_Type {
+ lr := lookup(map, key);
+ if lr.entry_index < 0 {
+ put(map, key, .{});
+ lr = lookup(map, key);
+ }
+
+ return ^entries[lr.entry_index].value;
+}
+
//
// Returns an Optional of the value at the specified key. The Optional
// has a value if the key is present, otherwise the optional does not
custom_parsers : Map(type_expr, #type (rawptr, str, Allocator) -> bool);
}
+//
+// This procedure is run before main() as it is an #init procedure.
+// It looks for all custom formatting and parsing definitions and
+// registers them to be used in format_any and parse_any.
custom_formatters_initialized :: #init () {
map.init(^custom_formatters, default=null_proc);
map.init(^custom_parsers, default=null_proc);
}
}
+//
+// Registers a formatting function for a particular type. This type is
+// inferred from the type of the third argument in the given function.
register_custom_formatter :: (formatter: (^Format_Output, ^Format, ^$T) -> void) {
custom_formatters[T] = formatter;
}
+
+//
+// Registers a parsing function for a particular type. This type is
+// inferred from the type of the first argument in the given function.
register_custom_parser :: (parser: (^$T, str, Allocator) -> bool) {
custom_parsers[T] = parser;
}
+
+//
+// Tag-type used to specify how to format a structure.
+//
+// @conv.Custom_Format.{ format_structure }
+// TheStructure :: struct { ... }
+//
Custom_Format :: struct {
format: (^Format_Output, ^Format, rawptr) -> void;
}
+
+//
+// Tag-type used to specify that a certain procedure should be used
+// to format a type.
+//
+// @conv.Custom_Format_Proc.{ TheStructure }
+// format_structure :: (output: ^conv.Format_Output, format: ^conv.Format, data: ^TheStructure) { ... }
+//
Custom_Format_Proc :: struct {
type: type_expr;
}
+
+//
+// Tag-type used to specify how to parse a structure.
+//
+// @conv.Custom_Parse.{ parse_structure }
+// TheStructure :: struct { ... }
+//
Custom_Parse :: struct {
parse: (rawptr, str, Allocator) -> bool;
}
+
+//
+// Tag-type used to specify that a certain procedure should be used
+// to parse a type.
+//
+// @conv.Custom_Parse_Proc.{ TheStructure }
+// parse_structure :: (data: ^TheStructure, input: str, allocator: Allocator) -> bool { ... }
+//
Custom_Parse_Proc :: struct {
type: type_expr;
}
-// @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;
-}
-
+//
+// Passed to any custom formatter. Wraps outputting data to any source,
+// using a `flush` callback function. Use `write` to output a string.
+// When the internal buffer is filled, `flush` is called to empty the
+// buffer to the final destination.
Format_Output :: struct {
data: ^u8;
count: u32;
}
}
+Format_Flush_Callback :: struct {
+ data: rawptr = null;
+ func: (rawptr, str) -> bool = null_proc;
+}
+
+
+//
+// Formatting options passed to a custom formatter.
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;
+ pretty_printing := false; // p
+ quote_strings := false; // "
+ single_quote_strings := false; // '
+ dereference := false; // *
+ custom_format := true; // ! to disable
+ interpret_numbers := true; // d to disable
+ digits_after_decimal := cast(u32) 4; // .2
indentation := cast(u32) 0;
- base := cast(u64) 10;
- minimum_width := cast(u32) 0;
+ base := cast(u64) 10; // b16
+ minimum_width := cast(u32) 0; // w12
}
#local
}
+
+//
+// Old aliases to not break old programs. Use format and format_va instead.
+str_format :: format
+str_format_va :: format_va
+
+
+//
+// Formats a string using the provided arguments and format specified string.
+// This has many overloads to make it easy to work with.
format :: #match {}
#overload
return out;
}
+//
+// Like format(), but takes the arguments as an array of `any`s, not a variadic argument array.
format_va :: #match {}
#overload
}
+//
+// This procedure converts any value into a string, using the type information system.
+// If a custom formatter is specified for the type, that is used instead.
+// This procedure is generally not used directly; instead, through format or format_va.
format_any :: (output: ^Format_Output, formatting: ^Format, v: any) {
use package runtime.info
array :: package core.array;
+ //
+ // Dereference the any if the '*' format specifier was given.
+ // Ignored if the value given is not a pointer.
if formatting.dereference {
ti := get_type_info(v.type);
if ti.kind == .Pointer {
}
}
+ //
+ // Use a custom formatter, if one is registered for the type.
if formatting.custom_format && custom_formatters->has(v.type) {
custom_formatters[v.type](output, formatting, v.data);
return;
use core {map, string, array, math}
//
-// This should be called with a pointer for the first argument.
-//
-// x: i32;
-// parse_any(^x, "12.34");
+// Parses many different types from a string into a value.
+// Uses a custom parser if one has been specified for the type given.
parse_any :: #match {}
#overload
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;
// return // ...
// }
// }
-
+//
hash :: #match -> u32 {
// Does this need to have a higher precedence value?
// Because if I wanted to have a custom type as the key
macro (key: $T/HasHashMethod) => key->hash()
}
+//
+// Interface that holds true when the type has a hash() overload defined.
+// Useful in datastructure when the ability to hash is dependent on whether
+// the stored type is hashable. See core/container/pair.onyx for an example.
Hashable :: interface (t: $T) {
{ hash(t) } -> u32;
}
-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.
+package core
+//
+// Ensure this file did not get included in a custom runtime, as it is
+// not possible to print anything if the runtime is not known. This check
+// could be replaced with something like !#defined(runtime.__output_string),
+// but that can happen when custom runtimes are more needed.
#if runtime.runtime == .Custom {
- #error "'stdio' can only be included in the 'wasi' or 'js' runtime."
+ #error "'stdio' cannot be used in a custom runtime."
}
-stdio_stream: io.Stream = .{ vtable = ^stdio_vtable, flags = .Block_On_Read };
+//
+// Every thread contains its own copy of standard input and output options.
+#thread_local stdio : struct {
+ // Two-way stream of standard input and output.
+ stream : io.Stream;
+
+ // These fields are used to create a buffer of standard output.
+ print_stream : io.BufferStream;
+ print_writer : io.Writer;
+
+ // When this is true, the print_stream is flushed when the last character
+ // is a newline. When this is false, all flushing is entirely manual.
+ auto_flush : bool;
+}
+
+//
+// Internal procedure called at thread initialization to setup the
+// `stdio` object. This should not be called more than once per thread,
+// so you will likely never need to call it.
+__stdio_init :: () {
+ stdio.print_stream = io.buffer_stream_make(2048, context.allocator);
+ stdio.print_writer = io.writer_make(^stdio.print_stream, 0);
-auto_flush_stdio := true
+ stdio.stream = .{ vtable = ^stdio_vtable, flags = .Block_On_Read };
+
+ stdio.auto_flush = true;
+}
+
+
+//
+// Internal procedure to flush data from the print_stream to standard
+// output. Can be used directly, or through io.stream_flush(^stdio.stream);
+__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();
+}
+//
+// Generic procedure to print something. This is normally not used
+// directly, but does support passing a different number of arguments
+// that are sent directly to io.write.
print :: #match #locked {
(x: str) {
io.write(^stdio.print_writer, x);
- if x[x.count - 1] == #char "\n" && auto_flush_stdio do __flush_stdio();
+ if x[x.count - 1] == #char "\n" && stdio.auto_flush do __flush_stdio();
},
(x) => { io.write(^stdio.print_writer, x); },
(x, y) => { io.write(^stdio.print_writer, x, y); },
}
+//
+// Helper procedure that prints something, then prints a newline.
println :: (x) => {
print(x);
print("\n");
}
-// Standard formatted print.
+//
+// Standard formatted print to standard output.
printf :: (format: str, va: ..any) {
flush :: (_, to_output) => {
io.write(^stdio.print_writer, to_output);
print(conv.format_va(buffer, format, va, .{null, flush}));
}
-// Print to standard error, if available.
#if #defined(runtime.__output_error) {
+ //
+ // Prints to standard error, if available.
eprintf :: (format: str, va: ..any) -> str {
flush :: (_, to_output) => {
runtime.__output_error(to_output);
}
}
-// Print to a dynamically allocated string.
+//
+// Prints to a dynamically allocated string, and returns the string.
+// It is the callers responsibility to free the 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.
+//
+// Prints to a dynamically allocated string in the temporary allocator,
+// and returns the 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) {
+
+//
+// Helper procedure that outputs a set of bytes at a certain location,
+// useful in debugging. The implementation does not use printf or
+// stdio.stream, because if those are corrupted due to a heap/memory
+// bug, they cannot be used.
+__byte_dump :: (ptr: rawptr, byte_count: u32, bytes_per_line := 8) {
temp: [3] u8;
u8_ptr := cast(^u8) ptr;
// 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, 0);
-}
-
-
-__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();
-}
-
+//
+// The v-table for the stream in stdio.
#local stdio_vtable := io.Stream_Vtable.{
read = (_: ^io.Stream, buffer: [] u8) -> (io.Error, u32) {
__flush_stdio();
//
// 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
+// square roots are intrinsic to WASM and therefore "fast". Exponentials 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,
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;
- }
-}
-
+//
+// Re-exports the memory_copy intrinsics. Behaves like memmove() in C.
copy :: core.intrinsics.wasm.memory_copy
+
+//
+// Re-exports the memory_fill intrinsics. Behaves like memset() in C.
set :: core.intrinsics.wasm.memory_fill
+//
+// Initializes a slice by allocating memory for it out of the allocator.
alloc_slice :: (sl: ^[] $T, count: i32, allocator := context.allocator) {
sl.data = raw_alloc(allocator, sizeof T * count);
sl.count = count;
}
+//
+// Constructs an intialized slice of `T` with `count` elements from the allocator.
make_slice :: ($T: type_expr, count: i32, allocator := context.allocator) -> [] T {
return .{
data = raw_alloc(allocator, sizeof T * count),
};
}
+//
+// Releases the memory for the slice, as well as setting the fields of the
+// slice to be 0 so it cannot be used again.
free_slice :: (sl: ^[] $T, allocator := context.allocator) {
if sl.data == null do return;
sl.count = 0;
}
+//
+// Copies a slice into a new slice, allocated from the allocator.
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;
+//
+// Aligns a number to the next multiple of `align`. Can be used
+// in place when a pointer is passed, otherwise returns the new
+// aligned number.
+align :: #match #local {}
- copy(new_slice.data, sl.data, sl.count * sizeof T);
+#overload
+align :: (size: ^u64, align: u64) {
+ if *size % align != 0 {
+ *size += align - (*size % align);
+ }
+}
- return new_slice;
+#overload
+align :: (size: u64, align: u64) -> u64 {
+ if size % align != 0 {
+ size += align - (size % align);
+ }
+ return size;
}
+//
+// Allows for make([] i32).
#overload
builtin.__make_overload :: macro (_: ^[] $T, count: u32, allocator := context.allocator) -> [] T {
- ret := (package core.memory).make_slice(T, count, allocator);
- (package core.memory).set(ret.data, 0, sizeof T * count);
+ ret := #this_package.make_slice(T, count, allocator);
+ #this_package.set(ret.data, 0, sizeof T * count);
return ret;
}
+//
+// Allows for delete(^sl);
#overload
builtin.delete :: macro (x: ^[] $T) {
- core.memory.free_slice(x);
+ #this_package.free_slice(x);
}
+
return cast(^T) a.data;
}
-// Dereference an pointer any.
+//
+// Dereference a pointer any.
any_dereference :: (v: any) -> any {
t := get_type_info(v.type);
if t.kind == .Pointer {
return v;
}
+//
// Subscript an array-like any.
any_subscript :: (v: any, index: i32) -> any {
base_ptr, elem_type, count := any_as_array(v);
};
}
+//
// Select a member from an any.
any_selector :: (v: any, member_name: str) -> any {
t := get_type_info(v.type);
return .{null, void};
}
+//
// This selector works with selecting "foo.bar.joe"
any_nested_selector :: (v: any, member_name: str) -> any {
t := get_type_info(v.type);
return .{null, void};
}
+//
+// Convert a structure or pointer to a structure to a Map with
+// keys representing the fields of the structure, and values
+// representing the value of each field.
+//
+// T :: struct {
+// x := 123;
+// y := "test";
+// }
+//
+// m := any_to_map(T.{});
+//
+// `m` would have two keys, "x" and "y".
+//
any_to_map :: (v: any) -> (Map(str, any), success: bool) {
vals := v;
if get_type_info(vals.type).kind == .Pointer {
return out, true;
}
+//
// Creates an iterator out of an array-like any.
any_iter :: (arr: any) -> Iterator(any) {
base_ptr, elem_type, count := any_as_array(arr);
#overload
Once.exec :: (o: ^Once, f: () -> $R) {
+ scoped_mutex(^o.mutex);
if o.done do return;
- mutex_lock(^o.mutex);
o.done = true;
f();
- mutex_unlock(^o.mutex);
}
#overload
Once.exec :: (o: ^Once, ctx: $Ctx, f: (Ctx) -> $R) {
+ scoped_mutex(^o.mutex);
if o.done do return;
- mutex_lock(^o.mutex);
o.done = true;
f(ctx);
- mutex_unlock(^o.mutex);
}
}
// @TODO // Validation for these fields.
- r := io.reader_make(^stdio_stream);
+ r := io.reader_make(^stdio.stream);
read_field("Package name: ", ^config.metadata.name, "");
read_field("Package description: ", ^config.metadata.description, "");
read_field("Package url: ", ^config.metadata.url, "");
return;
}
- r := io.reader_make(^stdio_stream);
+ r := io.reader_make(^stdio.stream);
while true {
printf("Is this a m[a]jor, m[i]nor, or [p]atch release? or [c]ancel? (a/i/p/c) ");