126 lines
3.5 KiB
Text
126 lines
3.5 KiB
Text
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);
|
|
});
|
|
}
|
|
|