const std = @import("std");
const TreeConfig = struct {
name: []const u8,
branches: []const TreeConfig = &.{},
};
pub fn ParsedTree(comptime tree: TreeConfig) type {
const BranchTaggedUnion = blk: {
var field_types: [tree.branches.len]type = undefined;
var field_names: [tree.branches.len][]const u8 = undefined;
inline for (&field_types, &field_names, tree.branches) |*field_type, *field_name, branch| {
field_type.* = ParsedTree(branch);
field_name.* = branch.name;
}
const field_attrs: [tree.branches.len]std.builtin.Type.UnionField.Attributes = @splat(.{});
const bits = if (field_names.len != 0) std.math.log2_int_ceil(usize, field_names.len) else 0;
const TagInt = @Int(.unsigned, bits);
comptime var field_values: [field_names.len]TagInt = undefined;
comptime for (0..field_names.len) |id| {
field_values[id] = @intCast(id);
};
const E = @Enum(TagInt, .exhaustive, &field_names, &field_values);
break :blk @Union(
.auto,
E,
&field_names,
&field_types,
&field_attrs,
);
};
return struct {
branch: ?BranchTaggedUnion,
};
}
inline fn descentPathRecursive(comptime accumulator: []const []const u8, comptime tree: TreeConfig, parsed: ParsedTree(tree)) []const []const u8 {
const result = accumulator ++ [_][]const u8{tree.name};
if (parsed.branch) |branch| {
switch (branch) {
inline else => |value, tag| {
inline for (tree.branches) |branch_config| {
if (comptime std.mem.eql(u8, branch_config.name, @tagName(tag))) {
return descentPathRecursive(result, branch_config, value);
}
}
},
}
}
return result;
}
test {
const tree: TreeConfig = .{
.name = "root",
.branches = &.{
.{ .name = "branch1" },
.{ .name = "branch2" },
},
};
var parsed: ParsedTree(tree) = .{ .branch = .{ .branch1 = .{ .branch = null } } };
_ = &parsed;
try std.testing.expectEqual(null, parsed.branch.?.branch1.branch);
const descent_path = comptime descentPathRecursive(&.{}, tree, parsed);
try std.testing.expectEqualSlices(
[]const u8,
&.{ "root", "branch1" },
descent_path,
);
}
test7.zig:66:68: error: unable to resolve comptime value
const descent_path = comptime descentPathRecursive(&.{}, tree, parsed);
^~~~~~
test7.zig:66:26: note: 'comptime' keyword forces comptime evaluation
const descent_path = comptime descentPathRecursive(&.{}, tree, parsed);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If I remove comptime from the test, the test passes. But if I have comptime in there it says I cannot resolve it. The return value of descentPathRecursive is only constructed from comptime known values, and it breaks the laws of physics (returns reference to local). Whats going on here? I think I should be generating all possible values for descentPath at comptime from my inline fors.