mirror of
https://github.com/Ed94/WATL_Exercise.git
synced 2025-08-05 14:52:44 -07:00
will be removing str8cache from the v0 and moving that to a another version. I want to maybe test using llms to remove code defs for this exercise.
191 lines
7.3 KiB
Markdown
191 lines
7.3 KiB
Markdown
# watl.v0.msvc.c Docs
|
|
|
|
Documention for the file is kept separate for the sake of compact definitions.
|
|
|
|
## Organization
|
|
|
|
The file is segregated with region pragmas with the following hierarchy
|
|
|
|
* DSL: Intrinsic includes, typedefs, and macros to make C more ergonomic to write and read.
|
|
* Memory: Basic memory operations and slice definitions
|
|
* Strings: Definition of UTF-8 strings.
|
|
* Allocator Interface: Generalized runtime allocator interface definitions
|
|
* Hashing: Cryptographic hashing definitions (only `hash64_djb8`)
|
|
* Key Tables: KT1L & KT1LCX generic hash table definitions
|
|
* String Operations: Basic String Ops & String Generation
|
|
* Debug: Runtime assertion definitions
|
|
* File System: File I/O
|
|
* WATL: Lexer & Parser definitons for the WATL format
|
|
* Implementation: Resolving all definitions that were originally just forward signatures before. Preserves the order of the above with sub-regions.
|
|
|
|
## Macro Usage
|
|
|
|
There is an attempt to keep macro usage to a low-degree of concatenation. Most of the macros consist of at most 3-4 layers of expansion with the majority of n-layers of expressions beyond the first being related to the usage of:
|
|
|
|
* glue: Like tmpl but for arbitrary concatentation of symbols
|
|
* optional_args: Used with functional macros implementing the optional arg pattern.
|
|
* def_struct: Used with-in def_range, def_slice as a secondary expansion
|
|
* tmpl: Used with templating definitions not utilizing a stage metaprogram to define.
|
|
* Can be found at arbitrary expansion depths.
|
|
|
|
### Macros of notable complexity
|
|
|
|
#### optional_args(symbol, ...)
|
|
|
|
In C, there is no-language provided feature for optional arguments in the sense where you can in C++ do:
|
|
|
|
```c
|
|
void Options(int some_param = 4);
|
|
```
|
|
|
|
Instead, the preprocessor can be utilized with a quirk of the struct temporary initialization syntax:
|
|
|
|
```c
|
|
typedef struct Options; struct Options {
|
|
int some_param;
|
|
};
|
|
void do_something(Options* optional)
|
|
void example {
|
|
do_something(& (Options){});
|
|
// or
|
|
do_something(& (Options){.some_param = 1});
|
|
}
|
|
```
|
|
|
|
In the above case, we can directly define a value for the optional pointer. The compiler will automatically initialize the struct and place on the stack for the lifetime of the `do__something`'s scope; while also providing the procedure an address to it.
|
|
|
|
Because its valid to have no arguments within the braces of the struct initalization we can utilize the following expansion:
|
|
|
|
```c
|
|
&(symbol){ __VA_ARGS__ }
|
|
```
|
|
|
|
Where the __VA_ARGS__ can be any valid syntax for initializing the struct's members.
|
|
|
|
The following convention can be used for any procedure we would like optional arguments for:
|
|
|
|
```c
|
|
typedef struct Options; struct Options {
|
|
int some_param;
|
|
};
|
|
void do___something(Options* opts)
|
|
#define do_something(...) do__something(&(Options){__VA_ARGS__})
|
|
```
|
|
|
|
To signify intent we utilize the macro:
|
|
|
|
```c
|
|
#define optional_args(...) &(symbol){__VA_ARGS__}
|
|
#define do_something(...) proc_identifier(optional_args(Options, __VA_ARGS__))
|
|
void example {
|
|
do_something();
|
|
// or
|
|
do_something(.some_param = 1);
|
|
}
|
|
```
|
|
|
|
### slice_arg_from_array
|
|
|
|
This exercise makes heavy use of the slice pattern:
|
|
|
|
```c
|
|
struct Slice_<type> { type* ptr; SSIZE len; }
|
|
```
|
|
|
|
We can utilize an array initalization pattern with slices to behave as an alternative to varadic arguments.
|
|
|
|
```c
|
|
#define lit(str) (Str8){ str, size_of(str) - 1 };
|
|
typedef struct Str8 Str8; struct Str8 { UTF8* ptr; SSIZE len; };
|
|
typedef struct Slice_Str8 Slice_Str8; struct Slice_Str8 { Str8* ptr; SSIZE len; };
|
|
void str8_fmt(Str8 fmt_template, Slice_Str8* args);
|
|
void example {
|
|
str8_fmt(lit("Hello str8_fmt: <an_arg>"), &(Slice_Str8) {
|
|
.ptr = (Str8[]){ lit("an_arg"), lit("a subst!") },
|
|
.len = (SSIZE)sizeof( (Str8[]){ lit("an_arg"), lit("a subst!") } ) / size_of(Str8)
|
|
});
|
|
}
|
|
```
|
|
|
|
In the above, we utilized the same temporary value pattern we did with structs for optional arguments, but now for a fixed-size stack array. Naturally without the preprocessor, its far too tedius to write the out:
|
|
|
|
```c
|
|
#define tmpl(prefix, type) prefix ## _ ## type
|
|
#define slice_arg_from_array(type, ...) & (tmpl(Slice,type)) { \
|
|
.ptr = (type[]){__VA_ARGS__}, \
|
|
.len = (SSIZE)sizeof( (type[]){__VA_ARGS__} ) / size_of(type) \
|
|
}
|
|
void example {
|
|
str8_fmt(lit("hello str8_fmt: <an_arg>"), slice_arg_from_array(
|
|
lit("an_arg"), lit("a subst!")
|
|
));
|
|
// or
|
|
str8_fmt(lit("hello str8_fmt: <an_arg>"), slice_arg_from_array());
|
|
}
|
|
```
|
|
|
|
To make it more ergonomic we embed the slice_arg_from_array into the frontend macro for the procedure like before for optionals:
|
|
|
|
```c
|
|
#define slice_arg_from_array(type, ...) & (tmpl(Slice,type)) { \
|
|
.ptr = (type[]){__VA_ARGS__}, \
|
|
.len = (SSIZE)sizeof( (type[]){__VA_ARGS__} ) / size_of(type) \
|
|
}
|
|
void str8__fmt(Str8 fmt_template, Slice_Str8* args);
|
|
#define str8_fmt(fmt_template, ...) str8__fmt(fmt_template, slice_arg_from_array(__VA_ARGS__))
|
|
void example {
|
|
str8_fmt(lit("hello str8_fmt: <an_arg>"),
|
|
lit("an_arg"), lit("a subst!")
|
|
);
|
|
// or
|
|
str8_fmt(lit("hello str8_fmt: <an_arg>"));
|
|
}
|
|
```
|
|
|
|
The actual macro uses farray helper macros:
|
|
|
|
```c
|
|
#define slice_arg_from_array(type, ...) & (tmpl(Slice,type)) { \
|
|
.ptr = farray_init(type, __VA_ARGS__), \
|
|
.len = farray_len( farray_init(type, __VA_ARGS__)) \
|
|
}
|
|
```
|
|
|
|
### Type definition helpers
|
|
|
|
`def_enum` and `def_struct` are used to reduce the redundancy of having to typedef a struct definition in order to expose it to the translation unit's namespace.
|
|
For enums, we specify the underlying type then begin the `enum` keyword and follow with defining the enum values.
|
|
|
|
```c
|
|
#define def_struct(symbol) struct symbol symbol; struct symbol
|
|
#define def_enum(underlying_type, symbol) underlying_type symbol; enum symbol
|
|
```
|
|
|
|
### Iteration helpers
|
|
|
|
`range_iter` & `slice_iter` are utilized for simplifying for-loop iteration with a macro to help reduce user-error.
|
|
|
|
`range_iter` is used with Range types that must be defined ahead of type by the user with `def_range`.
|
|
`def_range` produces both a `Range_<type>` type and a `Iter_Range_<type>` type, the `Iter_Range_<type>` contains the range along with a cursor.
|
|
|
|
## Procedure Signature Convention
|
|
|
|
Inline procedures without optionals are as usual.
|
|
|
|
Procedures which behave as initializer have two formats:
|
|
|
|
```c
|
|
void <prefix>_init(<struct>* data, ...);
|
|
<struct> <prefix>_make(...);
|
|
```
|
|
|
|
`<prefix>_init` lets the user define where struct lives, while `<prefix>_make` will allocate at minimum a temporary on the stack.
|
|
|
|
A third type is exported symbols. These have `api_<prefix>_<symbol>` as their conventional name.
|
|
They follow a similar pattern to `<prefix>_init` except they're meant to be used as cold procedures which heavy amounts of data passed into them or formatted out to the user via *"out"* parameters.
|
|
|
|
Generally if a process from a heavy procedure can support graceful failures, then a `struct <prefix>_<symbol>Info` will be utilized as an encapsulated payload for the user. It will contain a slice or linked-list of messages along with an aggregate set of top-level status signals on how the process went along with the intended payload the user wanted resolved for the operation.
|
|
|
|
The WATL Lexer & Parser will use this API convention.
|
|
The File System api wrapper however will not support messaging or a signal state (just returns null on failures).
|