Zig has annonymous structs and "typeless" enum literals
Like .{ 1, 2, "hello world" } is a tuple,
it could have the type Tuple(.{ i32, i32, []const u8 }) for example
(the compiler will automatically infer it)
An expression like .word is an enum literal, it can be casted into an enum,
provided that enum has the field "word" defined.
Zig's comptime is dynamically typed and lazy (even tough the runtime
is static and strict) so you can manipulate arbitrarily complicated expressions like that.
>>6
Here I just cooked this up:
const std = @import("std");
pub fn Eval(comptime sexpr: anytype) type {
return switch (sexpr[0]) {
.add => u32,
.print => void,
else => @compileError("invalid opcode"),
};
}
pub fn eval(comptime sexpr: anytype) Eval(sexpr) {
switch (sexpr[0]) {
.add => {
var ret: u32 = 0;
inline for (1..sexpr.len) |i| {
ret += sexpr[i];
}
return ret;
},
.print => {
inline for (1..sexpr.len) |i| {
std.debug.print("-> {any}\n", .{sexpr[i]});
}
},
else => @compileError("invalid opcode"),
}
}
test {
std.debug.print("{d}\n", .{eval(.{ .add, 1, 2, 3 })});
eval(.{ .print, .lol, 777, "hello world" });
}