From 1337f529df90ab8d28bcf4203ec1ea56f3c24345 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Mon, 13 Feb 2023 21:58:55 -0600 Subject: [PATCH] more standard library documentation --- core/intrinsics/onyx.onyx | 7 +++++++ core/misc/arg_parse.onyx | 22 ++++++++++++++++++++++ core/misc/method_ops.onyx | 19 +++++++++++++++++++ core/onyx/cptr.onyx | 27 +++++++++++++++++++++++++++ core/threads/thread.onyx | 24 ++++++++++++++++++++++++ 5 files changed, 99 insertions(+) diff --git a/core/intrinsics/onyx.onyx b/core/intrinsics/onyx.onyx index ff060005..5476a046 100644 --- a/core/intrinsics/onyx.onyx +++ b/core/intrinsics/onyx.onyx @@ -1,7 +1,14 @@ package core.intrinsics.onyx +// +// Special intrinsic function that initializes a value. +// This is generally used on structure to execute their +// default initializer expressions. __initialize :: (val: ^$T) -> void #intrinsic --- +// +// Helpful macro to create an initialized value. +// Mostly pointless now that T.{} does the same thing. init :: macro ($T: type_expr) -> T { __initialize :: __initialize diff --git a/core/misc/arg_parse.onyx b/core/misc/arg_parse.onyx index 623e857c..032b4b0f 100644 --- a/core/misc/arg_parse.onyx +++ b/core/misc/arg_parse.onyx @@ -1,5 +1,27 @@ package core.arg_parse +// +// This is currently a very basic argument parsing library. +// The options are given through a structure like so: +// +// Options :: struct { +// @"--option_1" +// option_1: str; +// +// @"--option_2", "-o2" +// option_2: bool; +// } +// +// main :: (args) => { +// o: Options; +// arg_parse.arg_parse(args, ^o); +// } +// +// Options that are strings and integers expect an argument after +// them to specify their value. Options that are bool default to +// false and are true if one or more of the option values are present. +// + use core arg_parse :: (c_args: [] cstr, output: any) -> bool { diff --git a/core/misc/method_ops.onyx b/core/misc/method_ops.onyx index 9862aea8..1e347dec 100644 --- a/core/misc/method_ops.onyx +++ b/core/misc/method_ops.onyx @@ -1,5 +1,24 @@ package core +// +// This file defines an optional language feature called method operators. +// Instead of defining an operator overload using the #operator directive, +// one can simply define a __op method in the structure to define how to +// perform the operations. For example, +// +// Vec2 :: struct { x, y: f32 } +// +// #inject Vec2 { +// __add :: (v1, v2: Vec2) => Vec2.{ v1.x + v2.x, v1.y + v2.y }; +// } +// +// This is an optional language feature because it currently significantly +// affects compile-time, on average adding 30% to the total compilation time. +// To enable this feature, add this somewhere: +// +// #inject runtime.vars.Onyx_Enable_Operator_Methods :: true +// + #local { __HasEqMethod :: interface (t: $T, r: $R) { T.__eq(t, r); } __HasNeMethod :: interface (t: $T, r: $R) { T.__ne(t, r); } diff --git a/core/onyx/cptr.onyx b/core/onyx/cptr.onyx index a27aab1d..80fb096c 100644 --- a/core/onyx/cptr.onyx +++ b/core/onyx/cptr.onyx @@ -1,22 +1,42 @@ package core +// +// Sometimes when interfacing with libraries from C, it is really helpful +// to be able to represent pointers to things outside of the bounds of the +// WASM memory. For one concrete example, glGetString takes an enum and +// procudes a character pointer to a static string with that enum's name. +// Without this library, it would be impossible to access this memory, +// without first copying it into the WASM memory. That requires a lot of +// overhead, so instead this library allows Onxy to access memory outside +// the normal bounds. +// +// cptr(T) is a C-pointer to a value of type T. It has a couple useful +// methods, namely: read(), extract_str(), and to_rawptr(). +// + @conv.Custom_Format.{ #solidify cptr.format {T=T} } cptr :: struct (T: type_expr) { data: u64; } #inject cptr { + // + // Creates a new C-pointer from an Onyx pointer. make :: macro (ptr: ^$T) -> cptr(T) { __cptr_make :: __cptr_make return .{ __cptr_make(ptr) }; } + // + // Extract the data out of a C pointer into a buffer in the Onyx memory. read :: (this: cptr($T)) -> T { buf: [sizeof T] u8; __cptr_read(this.data, ~~buf, sizeof T); return *cast(^T) buf; } + // + // Helper procedures for quickly reading an integer of various sizes. read_u8 :: (this: cptr(u8)) => __cptr_read_u8(this.data); read_u16 :: (this: cptr(u16)) => __cptr_read_u16(this.data); read_u32 :: (this: cptr(u32)) => __cptr_read_u32(this.data); @@ -26,6 +46,7 @@ cptr :: struct (T: type_expr) { read_i32 :: (this: cptr(i32)) => cast(i32) __cptr_read_u32(this.data); read_i64 :: (this: cptr(i64)) => cast(i64) __cptr_read_u64(this.data); + // // When given a non-zero-sized dest, this procedure // fills the dest buffer with the contents of the string // up to the number bytes in the dest buffer. This @@ -34,6 +55,9 @@ cptr :: struct (T: type_expr) { // using __cptr_read_u8 would be slow compared to strlen(). extract_str :: (this: cptr(u8), dest: [] u8) => __cptr_extract_str(this.data, dest); + // + // This procedure attempts to convert a C-pointer back into an + // Onyx pointer, if the pointer lives with the Onyx memory space. to_rawptr :: (this: cptr($T)) -> ^T { // I'm treating NULL as more of a concept, than as an actual value here, // because if something returns a NULL pointer, it should logically map @@ -54,6 +78,9 @@ cptr :: struct (T: type_expr) { } } + +// +// Allows for pointer addition. #operator + macro (p: cptr($T), v: i32) -> cptr(T) { return .{ p.data + ~~(v * sizeof T) }; } diff --git a/core/threads/thread.onyx b/core/threads/thread.onyx index 3e7823a2..c234a834 100644 --- a/core/threads/thread.onyx +++ b/core/threads/thread.onyx @@ -9,13 +9,24 @@ use core.intrinsics.atomics thread_map : Map(Thread_ID, ^Thread); } +// +// An id of a thread. Thread_ID :: #type i32 +// +// Represents a thread. Currently, this is very simple; just the id +// of the thread and whether or not it is alive. Thread :: struct { id : Thread_ID; alive : bool; } +// +// Spawns a new thread using the runtime.__spawn_thread function. +// The primary job of this function is to create the thread-local +// storage and stack for the new thread, and pass those on. +// Currently the stack size is not controllable, but that could +// be remedied. spawn :: (t: ^Thread, data: ^$T, func: (^T) -> void) { sync.scoped_mutex(^thread_mutex); @@ -33,6 +44,10 @@ spawn :: (t: ^Thread, data: ^$T, func: (^T) -> void) { runtime.__spawn_thread(t.id, tls_base, stack_base, func, data); } +// +// Waits for a thread to finish before returning. +// If the thread was not alive in the first place, +// immediately return. join :: (t: ^Thread) { while t.alive { #if runtime.Wait_Notify_Available { @@ -44,6 +59,9 @@ join :: (t: ^Thread) { } } +// +// Forcefully kill a thread using runtime.__kill_thread. +// Does nothing if the thread was not alive. kill :: (t: ^Thread) -> i32 { if !t.alive do return -1; @@ -53,10 +71,16 @@ kill :: (t: ^Thread) -> i32 { return 1; } +// +// Special procedure that should only be called once globally +// that initialize the map of thread ids to thread data. __initialize :: () { thread_map->init(); } +// +// Special procedure that is called when a thread exits, +// or by kill() above. __exited :: (id: i32) { sync.scoped_mutex(^thread_mutex); -- 2.25.1