verify :: (block: Code) #expand { for 0..0 #insert,scope(block) block; } c_call :: (block: Code) #expand { push_context context { #insert,scope() block; } } zero_value :: ($T: Type) -> T #expand { zero: T; return zero; } // Allows structs to be copy assigned. with :: (old: $T, $new: Code, location := #caller_location) -> T #modify { return T.(*Type_Info).type == .STRUCT, "with can only be used on structs"; } #expand { using compiler; ensure :: (cond: bool, message: string, args: ..Any, loc := location) { if !cond compiler_report(basic.tprint(message, ..args), loc); } #insert,scope() -> string { b: basic.String_Builder; root := compiler_get_nodes(new).(*Code_Literal); ensure(root.kind == .LITERAL, "argument must be a struct literal"); ensure(root.value_type == .STRUCT, "argument must be a struct literal"); t_info := T.(*Type_Info_Struct); // Ensure the literal we were given is of type T so this operator is more predictable. // i.e. disallowing Vector3.{} | SomeRandomType.{ x = 10 }; n_type := root.struct_literal_info.type_expression; if n_type != null { tmp: basic.String_Builder; pp.print_expression(*tmp, n_type); n_typename := basic.builder_to_string(*tmp); ensure(n_type.result == t_info, "mismatched types, % vs. %", t_info.name, n_typename); } receiver := "old"; if is_constant(old) { receiver = "copy"; basic.print_to_builder(*b, "% := old;\n", receiver); } for root.struct_literal_info.arguments { op := it.(*Code_Binary_Operator); ident := op.left.(*Code_Ident); ensure(op.kind == .BINARY_OPERATOR, "copy-assign requires named fields", loc = make_location(op)); ensure(op.operator_type == #char "=", "copy-assign requires named field assignment", loc = make_location(op)); ensure(ident.kind == .IDENT, "must be an identifier", loc = make_location(op)); // Catch any incorrect field assignments before the compiler does for better error reporting exists := false; for t_info.members if it.name == ident.name exists = true; ensure(exists, "field % does not exist within %", ident.name, t_info.name, loc = make_location(ident)); // receiver.field = value; basic.append(*b, receiver); basic.append(*b, "."); pp.print_expression(*b, op); basic.append(*b, ";\n"); } basic.print_to_builder(*b, "return %;\n", receiver); return basic.builder_to_string(*b); } } operator | :: with; #scope_file; test :: #import,file "test/module.jai"; pp :: #import "Program_Print"; compiler :: #import "Compiler"; #if RUN_TESTS #run { test.run("verify", (t) => { i := 0; verify(#code { i += 1; if i == 1 { break; } i += 2; }); j := 0; verify(#code { for 0..10 { break; } j = 1; }); test.expect(t, i == 1, "i was %", i); test.expect(t, j == 1, "j was %", j); }); test.run("copy assign", (t) => { Value :: struct { x: float; y: float; z: float; } a := Value.{ 10, 20, 30 }; b := with(a, .{ x = 1, z = 1 }); c := b | .{ y = 500 }; test.expect(t, b.x == 1, "was %", b.x); test.expect(t, b.z == 1, "was %", b.z); test.expect(t, c.y == 500, "was %", c.y); }); }