From 4c4aa60c7b613a9104decd9d716dd752da7e3a51 Mon Sep 17 00:00:00 2001 From: Judah Caruso Date: Sat, 17 May 2025 04:07:42 -0600 Subject: [PATCH] changed 'verify' to 'this_block' + it now uses for_expansion rules, documentation --- _run_all_tests.jai | 1 + hash/module.jai | 2 ++ macros.jai | 80 ++++++++++++++++++++++++++++++++++++++++++---- utils.jai | 17 +++++----- 4 files changed, 84 insertions(+), 16 deletions(-) diff --git a/_run_all_tests.jai b/_run_all_tests.jai index 6bb1237..0fec6bf 100644 --- a/_run_all_tests.jai +++ b/_run_all_tests.jai @@ -4,6 +4,7 @@ #import,file "./module.jai"(true); #import,file "./encoding/module.jai"(true); + #import,file "./hash/module.jai"(true); } diff --git a/hash/module.jai b/hash/module.jai index e870ffa..86fa975 100644 --- a/hash/module.jai +++ b/hash/module.jai @@ -1,2 +1,4 @@ +#module_parameters(RUN_TESTS := false); + #load "murmur.jai"; #load "xxhash.jai"; diff --git a/macros.jai b/macros.jai index 5669bcf..55f54fe 100644 --- a/macros.jai +++ b/macros.jai @@ -1,4 +1,10 @@ -// Allows structs to be copy assigned. +/* + Allows structs to be copied and assigned inline. + + For example, copying a Vector3 while changing a single value: + old := Vector3.{ 10, 20, 30 }; // 10, 20, 30 + new := with(old, .{ y = -20 }); // 10, -20, 30 +*/ with :: (old: $T, $new: Code, location := #caller_location) -> T #modify { return T.(*Type_Info).type == .STRUCT, "with can only be used on structs"; } #expand { @@ -57,34 +63,94 @@ with :: (old: $T, $new: Code, location := #caller_location) -> T } } +// @note(judah): I like doing this with an operator, but your mileage may vary operator | :: with; +/* +Creates a named block that can exit early (via 'break' or 'continue'). + +This mostly replaces the case where you'd like to jump to +the end of a scope based on some logic within. Without +gotos, this is the next best thing. + +Usage: + // within a loop + for this_block() { // this is named 'block' by default + if !moving break; + // do movement here + } + for this_block("render_player") { + if invisible break render_player; + // do rendering here + } +*/ +this_block :: ($name: string = Default_Name) -> Named_Block(name) #expand { return .{}; } + +// Call #c_call procedures inline with the current context: 'c_call(some_c_call_proc(10, 20))' +c_call :: (call: Code) #expand { + push_context context { #insert,scope(call) call; } +} + +// Call #c_call procedures inline with a custom context: 'c_call(some_c_call_proc(10, 20), c_context)' +c_call :: (call: Code, ctx: #Context) #expand { + push_context ctx { #insert,scope(call) call; } +} + #scope_file; +Default_Name :: "block"; +Named_Block :: struct(NAME: string) {} + +for_expansion :: (v: *Named_Block, code: Code, _: For_Flags) #expand { + #insert #run basic.tprint(#string END + for `%: 0..0 { + `it :: #run zero_of(void); + `it_index :: #run zero_of(void); + #insert,scope(code) code; + } + END, + // @note(judah): guards against calling this_block with + // an empty string which results in weird error messages. + ifx v.NAME.count != 0 v.NAME else Default_Name); +} + pp :: #import "Program_Print"; compiler :: #import "Compiler"; #if RUN_TESTS #run { - test.run("verify", (t) => { + test.run("this_block", (t) => { i := 0; - verify(#code { + + for this_block() { i += 1; + for this_block() { + break block; + } + + for this_block() { + continue block; + } + if i == 1 { - break; + break block; } i += 2; - }); + } j := 0; - verify(#code { + for this_block("named") { for 0..10 { break; } + if i != 1 { + break named; + } + j = 1; - }); + } test.expect(t, i == 1, "i was %", i); test.expect(t, j == 1, "j was %", j); diff --git a/utils.jai b/utils.jai index 68ed4df..e8131f5 100644 --- a/utils.jai +++ b/utils.jai @@ -1,12 +1,5 @@ -verify :: (block: Code) #expand { - for 0..0 #insert,scope(block) block; -} - -c_call :: (block: Code) #expand { - push_context context { - #insert,scope() block; - } -} +// These return the bool first so you can check in a conditional, +// rather than having to do '_, ok := ...' check_type_tag :: ($$T: Type, tag: Type_Info_Tag) -> bool, *Type_Info { #if is_constant(T) { @@ -90,5 +83,11 @@ rune_width :: (r: rune) -> int { #if RUN_TESTS #run { test.run("snake_to_pascal", t => { + test.expect(t, snake_to_pascal("some_name") == "SomeName"); + test.expect(t, snake_to_pascal("_some_name") == "SomeName"); + test.expect(t, snake_to_pascal("some__name") == "SomeName"); + test.expect(t, snake_to_pascal("some_name_") == "SomeName"); + test.expect(t, snake_to_pascal("X_Y_Z") == "XYZ"); + test.expect(t, snake_to_pascal("XY_Z") == "XYZ"); }); }