Add struct test
This commit is contained in:
parent
d2640a44ac
commit
06e8756849
@ -21,6 +21,7 @@ type_info: std.ArrayListUnmanaged(TypeInfo) = .{},
|
|||||||
type_info_map: std.HashMapUnmanaged(TypeInfo, usize, TypeInfo.Context, std.hash_map.default_max_load_percentage) = .{},
|
type_info_map: std.HashMapUnmanaged(TypeInfo, usize, TypeInfo.Context, std.hash_map.default_max_load_percentage) = .{},
|
||||||
|
|
||||||
pub fn deinit(interpreter: *ComptimeInterpreter) void {
|
pub fn deinit(interpreter: *ComptimeInterpreter) void {
|
||||||
|
for (interpreter.type_info.items) |*ti| ti.deinit(interpreter.allocator);
|
||||||
interpreter.type_info.deinit(interpreter.allocator);
|
interpreter.type_info.deinit(interpreter.allocator);
|
||||||
interpreter.type_info_map.deinit(interpreter.allocator);
|
interpreter.type_info_map.deinit(interpreter.allocator);
|
||||||
}
|
}
|
||||||
@ -137,6 +138,13 @@ pub const TypeInfo = union(enum) {
|
|||||||
else => {},
|
else => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(ti: *TypeInfo, allocator: std.mem.Allocator) void {
|
||||||
|
switch (ti.*) {
|
||||||
|
.@"struct" => |*s| s.fields.deinit(allocator),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Type = struct {
|
pub const Type = struct {
|
||||||
@ -267,13 +275,16 @@ pub const TypeInfoFormatter = struct {
|
|||||||
.@"bool" => try writer.writeAll("bool"),
|
.@"bool" => try writer.writeAll("bool"),
|
||||||
.@"struct" => |s| {
|
.@"struct" => |s| {
|
||||||
try writer.writeAll("struct {");
|
try writer.writeAll("struct {");
|
||||||
|
for (s.fields.items) |field| {
|
||||||
|
try writer.print("{s}: {s}, ", .{ field.name, value.interpreter.formatTypeInfo(value.interpreter.typeToTypeInfo(field.@"type")) });
|
||||||
|
}
|
||||||
var iterator = s.scope.declarations.iterator();
|
var iterator = s.scope.declarations.iterator();
|
||||||
while (iterator.next()) |di| {
|
while (iterator.next()) |di| {
|
||||||
const decl = di.value_ptr.*;
|
const decl = di.value_ptr.*;
|
||||||
if (decl.isConstant(value.interpreter.tree)) {
|
if (decl.isConstant(value.interpreter.tree)) {
|
||||||
try writer.print("const {s}: , {any}", .{ decl.name, value.interpreter.formatTypeInfo(value.interpreter.typeToTypeInfo(decl.@"type")) });
|
try writer.print("const {s}: {any} = TODO_PRINT_VALUES, ", .{ decl.name, value.interpreter.formatTypeInfo(value.interpreter.typeToTypeInfo(decl.@"type")) });
|
||||||
} else {
|
} else {
|
||||||
try writer.print("var {s}: , {any}", .{ decl.name, value.interpreter.formatTypeInfo(value.interpreter.typeToTypeInfo(decl.@"type")) });
|
try writer.print("var {s}: {any}, ", .{ decl.name, value.interpreter.formatTypeInfo(value.interpreter.typeToTypeInfo(decl.@"type")) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try writer.writeAll("}");
|
try writer.writeAll("}");
|
||||||
@ -388,15 +399,22 @@ pub const InterpretResult = union(enum) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getValue(result: InterpretResult) Value {
|
pub fn getValue(result: InterpretResult) error{ExpectedValue}!Value {
|
||||||
return result.maybeGetValue() orelse @panic("Attempted to get value from non-value interpret result");
|
return result.maybeGetValue() orelse error.ExpectedValue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Might be useful in the future
|
// Might be useful in the future
|
||||||
pub const InterpretOptions = struct {};
|
pub const InterpretOptions = struct {};
|
||||||
|
|
||||||
pub const InterpretError = std.mem.Allocator.Error || std.fmt.ParseIntError || std.fmt.ParseFloatError || error{ InvalidCharacter, InvalidBase };
|
pub const InterpretError = std.mem.Allocator.Error || std.fmt.ParseIntError || std.fmt.ParseFloatError || error{
|
||||||
|
InvalidCharacter,
|
||||||
|
InvalidBase,
|
||||||
|
ExpectedValue,
|
||||||
|
InvalidOperation,
|
||||||
|
CriticalAstFailure,
|
||||||
|
InvalidBuiltin,
|
||||||
|
};
|
||||||
pub fn interpret(
|
pub fn interpret(
|
||||||
interpreter: *ComptimeInterpreter,
|
interpreter: *ComptimeInterpreter,
|
||||||
node_idx: Ast.Node.Index,
|
node_idx: Ast.Node.Index,
|
||||||
@ -455,13 +473,13 @@ pub fn interpret(
|
|||||||
var default_value = if (field_info.ast.value_expr == 0)
|
var default_value = if (field_info.ast.value_expr == 0)
|
||||||
null
|
null
|
||||||
else
|
else
|
||||||
(try interpreter.interpret(field_info.ast.value_expr, container_scope, .{})).getValue();
|
try (try interpreter.interpret(field_info.ast.value_expr, container_scope, .{})).getValue();
|
||||||
|
|
||||||
const name = tree.tokenSlice(field_info.ast.name_token);
|
const name = tree.tokenSlice(field_info.ast.name_token);
|
||||||
const field = FieldDefinition{
|
const field = FieldDefinition{
|
||||||
.node_idx = member,
|
.node_idx = member,
|
||||||
.name = name,
|
.name = name,
|
||||||
.@"type" = init_type.getValue().value_data.@"type",
|
.@"type" = (try init_type.getValue()).value_data.@"type",
|
||||||
.default_value = default_value,
|
.default_value = default_value,
|
||||||
// TODO: Default values
|
// TODO: Default values
|
||||||
// .@"type" = T: {
|
// .@"type" = T: {
|
||||||
@ -489,12 +507,12 @@ pub fn interpret(
|
|||||||
.simple_var_decl,
|
.simple_var_decl,
|
||||||
=> {
|
=> {
|
||||||
const decl = ast.varDecl(tree, node_idx).?;
|
const decl = ast.varDecl(tree, node_idx).?;
|
||||||
var value = (try interpreter.interpret(decl.ast.init_node, scope, options)).getValue();
|
var value = try (try interpreter.interpret(decl.ast.init_node, scope, options)).getValue();
|
||||||
var @"type" = if (decl.ast.type_node == 0) Value{
|
var @"type" = if (decl.ast.type_node == 0) Value{
|
||||||
.node_idx = std.math.maxInt(Ast.Node.Index),
|
.node_idx = std.math.maxInt(Ast.Node.Index),
|
||||||
.@"type" = try interpreter.createType(node_idx, .{ .@"type" = .{} }),
|
.@"type" = try interpreter.createType(node_idx, .{ .@"type" = .{} }),
|
||||||
.value_data = .{ .@"type" = value.@"type" },
|
.value_data = .{ .@"type" = value.@"type" },
|
||||||
} else (try interpreter.interpret(decl.ast.type_node, scope, options)).getValue();
|
} else try (try interpreter.interpret(decl.ast.type_node, scope, options)).getValue();
|
||||||
|
|
||||||
const name = analysis.getDeclName(tree, node_idx).?;
|
const name = analysis.getDeclName(tree, node_idx).?;
|
||||||
try scope.?.declarations.put(interpreter.allocator, name, .{
|
try scope.?.declarations.put(interpreter.allocator, name, .{
|
||||||
@ -560,6 +578,22 @@ pub fn interpret(
|
|||||||
.identifier => {
|
.identifier => {
|
||||||
var value = tree.getNodeSource(node_idx);
|
var value = tree.getNodeSource(node_idx);
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, "bool", value)) return InterpretResult{ .value = Value{
|
||||||
|
.node_idx = node_idx,
|
||||||
|
.@"type" = try interpreter.createType(node_idx, .{ .@"type" = .{} }),
|
||||||
|
.value_data = .{ .@"type" = try interpreter.createType(node_idx, .{ .@"bool" = .{} }) },
|
||||||
|
} };
|
||||||
|
if (std.mem.eql(u8, "true", value)) return InterpretResult{ .value = Value{
|
||||||
|
.node_idx = node_idx,
|
||||||
|
.@"type" = try interpreter.createType(node_idx, .{ .@"bool" = .{} }),
|
||||||
|
.value_data = .{ .@"bool" = true },
|
||||||
|
} };
|
||||||
|
if (std.mem.eql(u8, "false", value)) return InterpretResult{ .value = Value{
|
||||||
|
.node_idx = node_idx,
|
||||||
|
.@"type" = try interpreter.createType(node_idx, .{ .@"bool" = .{} }),
|
||||||
|
.value_data = .{ .@"bool" = false },
|
||||||
|
} };
|
||||||
|
|
||||||
if (std.mem.eql(u8, "type", value)) {
|
if (std.mem.eql(u8, "type", value)) {
|
||||||
return InterpretResult{ .value = Value{
|
return InterpretResult{ .value = Value{
|
||||||
.node_idx = node_idx,
|
.node_idx = node_idx,
|
||||||
@ -579,6 +613,8 @@ pub fn interpret(
|
|||||||
} };
|
} };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Floats
|
||||||
|
|
||||||
// Logic to find identifiers in accessible scopes
|
// Logic to find identifiers in accessible scopes
|
||||||
|
|
||||||
var psi = scope.?.parentScopeIterator();
|
var psi = scope.?.parentScopeIterator();
|
||||||
@ -597,32 +633,26 @@ pub fn interpret(
|
|||||||
return if (data[node_idx].rhs == 0)
|
return if (data[node_idx].rhs == 0)
|
||||||
InterpretResult{ .@"break" = label }
|
InterpretResult{ .@"break" = label }
|
||||||
else
|
else
|
||||||
InterpretResult{ .break_with_value = .{ .label = label, .value = (try interpreter.interpret(data[node_idx].rhs, scope, options)).getValue() } };
|
InterpretResult{ .break_with_value = .{ .label = label, .value = try (try interpreter.interpret(data[node_idx].rhs, scope, options)).getValue() } };
|
||||||
},
|
},
|
||||||
.@"return" => {
|
.@"return" => {
|
||||||
return if (data[node_idx].lhs == 0)
|
return if (data[node_idx].lhs == 0)
|
||||||
InterpretResult{ .@"return" = {} }
|
InterpretResult{ .@"return" = {} }
|
||||||
else
|
else
|
||||||
InterpretResult{ .return_with_value = (try interpreter.interpret(data[node_idx].lhs, scope, options)).getValue() };
|
InterpretResult{ .return_with_value = try (try interpreter.interpret(data[node_idx].lhs, scope, options)).getValue() };
|
||||||
},
|
},
|
||||||
.@"if", .if_simple => {
|
.@"if", .if_simple => {
|
||||||
const iff = ast.ifFull(tree, node_idx);
|
const iff = ast.ifFull(tree, node_idx);
|
||||||
// TODO: Don't evaluate runtime ifs
|
// TODO: Don't evaluate runtime ifs
|
||||||
// if (options.observe_values) {
|
// if (options.observe_values) {
|
||||||
const ir = try interpreter.interpret(iff.ast.cond_expr, scope, options);
|
const ir = try interpreter.interpret(iff.ast.cond_expr, scope, options);
|
||||||
if (ir.getValue().value_data.@"bool") {
|
if ((try ir.getValue()).value_data.@"bool") {
|
||||||
return try interpreter.interpret(iff.ast.then_expr, scope, options);
|
return try interpreter.interpret(iff.ast.then_expr, scope, options);
|
||||||
} else {
|
} else {
|
||||||
if (iff.ast.else_expr != 0) {
|
if (iff.ast.else_expr != 0) {
|
||||||
return try interpreter.interpret(iff.ast.else_expr, scope, options);
|
return try interpreter.interpret(iff.ast.else_expr, scope, options);
|
||||||
} else return InterpretResult{ .nothing = .{} };
|
} else return InterpretResult{ .nothing = .{} };
|
||||||
}
|
}
|
||||||
// } else {
|
|
||||||
// _ = try interpreter.interpret(iff.ast.cond_expr, scope, options);
|
|
||||||
// _ = try interpreter.interpret(iff.ast.then_expr, scope, options);
|
|
||||||
// _ = try interpreter.interpret(iff.ast.else_expr, scope, options);
|
|
||||||
// }
|
|
||||||
@panic("bruh");
|
|
||||||
},
|
},
|
||||||
.equal_equal => {
|
.equal_equal => {
|
||||||
var a = try interpreter.interpret(data[node_idx].lhs, scope, options);
|
var a = try interpreter.interpret(data[node_idx].lhs, scope, options);
|
||||||
@ -630,7 +660,7 @@ pub fn interpret(
|
|||||||
return InterpretResult{ .value = Value{
|
return InterpretResult{ .value = Value{
|
||||||
.node_idx = node_idx,
|
.node_idx = node_idx,
|
||||||
.@"type" = try interpreter.createType(node_idx, .{ .@"bool" = .{} }),
|
.@"type" = try interpreter.createType(node_idx, .{ .@"bool" = .{} }),
|
||||||
.value_data = .{ .@"bool" = a.getValue().eql(b.getValue()) },
|
.value_data = .{ .@"bool" = (try a.getValue()).eql(try b.getValue()) },
|
||||||
} };
|
} };
|
||||||
// a.getValue().eql(b.getValue())
|
// a.getValue().eql(b.getValue())
|
||||||
},
|
},
|
||||||
@ -649,7 +679,7 @@ pub fn interpret(
|
|||||||
try bi.setString(@enumToInt(bii), s[if (bii != .decimal) @as(usize, 2) else @as(usize, 0)..]);
|
try bi.setString(@enumToInt(bii), s[if (bii != .decimal) @as(usize, 2) else @as(usize, 0)..]);
|
||||||
break :ppp .{ .@"comptime_int" = bi };
|
break :ppp .{ .@"comptime_int" = bi };
|
||||||
},
|
},
|
||||||
.failure => @panic("Failed to parse number literal"),
|
.failure => return error.CriticalAstFailure,
|
||||||
},
|
},
|
||||||
} };
|
} };
|
||||||
},
|
},
|
||||||
@ -676,7 +706,7 @@ pub fn interpret(
|
|||||||
var psi = scope.?.parentScopeIterator();
|
var psi = scope.?.parentScopeIterator();
|
||||||
while (psi.next()) |pscope| {
|
while (psi.next()) |pscope| {
|
||||||
if (pscope.declarations.getEntry(value)) |decl|
|
if (pscope.declarations.getEntry(value)) |decl|
|
||||||
decl.value_ptr.value = (try interpreter.interpret(data[node_idx].rhs, scope.?, options)).getValue();
|
decl.value_ptr.value = try (try interpreter.interpret(data[node_idx].rhs, scope.?, options)).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
return InterpretResult{ .nothing = .{} };
|
return InterpretResult{ .nothing = .{} };
|
||||||
@ -707,7 +737,7 @@ pub fn interpret(
|
|||||||
|
|
||||||
if (std.mem.eql(u8, call_name, "@compileLog")) {
|
if (std.mem.eql(u8, call_name, "@compileLog")) {
|
||||||
pp: for (params) |param| {
|
pp: for (params) |param| {
|
||||||
const res = (try interpreter.interpret(param, scope, .{})).getValue();
|
const res = try (try interpreter.interpret(param, scope, .{})).getValue();
|
||||||
const ti = interpreter.type_info.items[res.@"type".info_idx];
|
const ti = interpreter.type_info.items[res.@"type".info_idx];
|
||||||
switch (ti) {
|
switch (ti) {
|
||||||
.pointer => |ptr| {
|
.pointer => |ptr| {
|
||||||
@ -733,6 +763,7 @@ pub fn interpret(
|
|||||||
|
|
||||||
std.log.info("Builtin not implemented: {s}", .{call_name});
|
std.log.info("Builtin not implemented: {s}", .{call_name});
|
||||||
@panic("Builtin not implemented");
|
@panic("Builtin not implemented");
|
||||||
|
// return error.InvalidBuiltin;
|
||||||
},
|
},
|
||||||
.string_literal => {
|
.string_literal => {
|
||||||
const value = tree.getNodeSource(node_idx)[1 .. tree.getNodeSource(node_idx).len - 1];
|
const value = tree.getNodeSource(node_idx)[1 .. tree.getNodeSource(node_idx).len - 1];
|
||||||
@ -887,6 +918,18 @@ pub fn interpret(
|
|||||||
// return null;
|
// return null;
|
||||||
return InterpretResult{ .nothing = .{} };
|
return InterpretResult{ .nothing = .{} };
|
||||||
},
|
},
|
||||||
|
.bool_not => {
|
||||||
|
const result = try interpreter.interpret(data[node_idx].lhs, scope, .{});
|
||||||
|
const value = (try result.getValue());
|
||||||
|
if (value.value_data != .@"bool") return error.InvalidOperation;
|
||||||
|
return InterpretResult{
|
||||||
|
.value = .{
|
||||||
|
.node_idx = node_idx,
|
||||||
|
.@"type" = value.@"type",
|
||||||
|
.value_data = .{ .@"bool" = !value.value_data.@"bool" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
else => {
|
else => {
|
||||||
std.log.err("Unhandled {any}", .{tags[node_idx]});
|
std.log.err("Unhandled {any}", .{tags[node_idx]});
|
||||||
return InterpretResult{ .nothing = .{} };
|
return InterpretResult{ .nothing = .{} };
|
||||||
|
@ -10,7 +10,8 @@ const allocator: std.mem.Allocator = std.testing.allocator;
|
|||||||
test "ComptimeInterpreter - basic test" {
|
test "ComptimeInterpreter - basic test" {
|
||||||
var tree = try std.zig.parse(allocator,
|
var tree = try std.zig.parse(allocator,
|
||||||
\\pub fn ReturnMyType() type {
|
\\pub fn ReturnMyType() type {
|
||||||
\\ if (1 == 1) return u69;
|
\\ var abc = z: {break :z if (!false) 123 else 0;};
|
||||||
|
\\ if (abc == 123) return u69;
|
||||||
\\ return u8;
|
\\ return u8;
|
||||||
\\}
|
\\}
|
||||||
);
|
);
|
||||||
@ -24,3 +25,23 @@ test "ComptimeInterpreter - basic test" {
|
|||||||
|
|
||||||
try std.testing.expectFmt("u69", "{any}", .{interpreter.formatTypeInfo(interpreter.typeToTypeInfo(z.result.value.value_data.@"type"))});
|
try std.testing.expectFmt("u69", "{any}", .{interpreter.formatTypeInfo(interpreter.typeToTypeInfo(z.result.value.value_data.@"type"))});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "ComptimeInterpreter - struct" {
|
||||||
|
var tree = try std.zig.parse(allocator,
|
||||||
|
\\pub fn ReturnMyType() type {
|
||||||
|
\\ return struct {
|
||||||
|
\\ slay: bool,
|
||||||
|
\\ var abc = 123;
|
||||||
|
\\ };
|
||||||
|
\\}
|
||||||
|
);
|
||||||
|
defer tree.deinit(allocator);
|
||||||
|
|
||||||
|
var interpreter = ComptimeInterpreter{ .tree = tree, .allocator = allocator };
|
||||||
|
defer interpreter.deinit();
|
||||||
|
|
||||||
|
const z = try interpreter.call(tree.rootDecls()[0], &.{}, .{});
|
||||||
|
defer z.scope.deinit();
|
||||||
|
|
||||||
|
try std.testing.expectFmt("struct {slay: bool, const abc: comptime_int = TODO_PRINT_VALUES, }", "{any}", .{interpreter.formatTypeInfo(interpreter.typeToTypeInfo(z.result.value.value_data.@"type"))});
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user