add basic comptime interpreter tests
This commit is contained in:
parent
06fcfcb3db
commit
34b2643b33
@ -727,12 +727,14 @@ pub fn interpret(
|
||||
|
||||
if (as_type.ty != type_type) return error.InvalidBuiltin;
|
||||
|
||||
return InterpretResult{ .value = Value{
|
||||
return InterpretResult{
|
||||
.value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = type_type,
|
||||
.val = try interpreter.cast(node_idx, as_type.val, value.val),
|
||||
} };
|
||||
.ty = as_type.val,
|
||||
.val = value.val, // TODO port Sema.coerceExtra to InternPool
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
log.err("Builtin not implemented: {s}", .{call_name});
|
||||
|
@ -63,7 +63,7 @@ pub const ErrorSet = struct {
|
||||
names: []const []const u8,
|
||||
|
||||
pub fn sort(self: *ErrorSet) void {
|
||||
std.sort.sort([][]const u8, self.names, u8, std.mem.lessThan);
|
||||
std.sort.sort([]const []const u8, self.names, u8, std.mem.lessThan);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -11,6 +11,40 @@ const Key = InternPool.Key;
|
||||
|
||||
const allocator: std.mem.Allocator = std.testing.allocator;
|
||||
|
||||
test "ComptimeInterpreter - primitive types" {
|
||||
try testExprCheck("true", .{ .simple = .bool }, .{ .simple = .bool_true });
|
||||
try testExprCheck("false", .{ .simple = .bool }, .{ .simple = .bool_false });
|
||||
try testExprCheck("5", .{ .simple = .comptime_int }, .{ .int_u64_value = 5 });
|
||||
// TODO try testExprCheck("-2", .{ .simple = .comptime_int }, .{ .int_i64_value = -2 });
|
||||
try testExprCheck("3.0", .{ .simple = .comptime_float }, null);
|
||||
|
||||
if (true) return error.SkipZigTest; // TODO
|
||||
try testExprCheck("null", .{ .simple = .null_type }, .{ .simple = .null_value });
|
||||
try testExprCheck("void", .{ .simple = .void }, .{ .simple = .void_value });
|
||||
try testExprCheck("undefined", .{ .simple = .undefined_type }, .{ .simple = .undefined_value });
|
||||
try testExprCheck("noreturn", .{ .simple = .noreturn }, .{ .simple = .unreachable_value });
|
||||
}
|
||||
|
||||
test "ComptimeInterpreter - expressions" {
|
||||
if (true) return error.SkipZigTest; // TODO
|
||||
try testExprCheck("5 + 3", .{ .simple = .comptime_int }, .{ .int_u64_value = 8 });
|
||||
try testExprCheck("5.2 + 4.2", .{ .simple = .comptime_float }, null);
|
||||
|
||||
try testExprCheck("3 == 3", .{ .simple = .bool }, .{ .simple = .bool_true });
|
||||
try testExprCheck("5.2 == 2.1", .{ .simple = .bool }, .{ .simple = .bool_false });
|
||||
|
||||
try testExprCheck("@as(?bool, null) orelse true", .{ .simple = .bool }, .{ .simple = .bool_true });
|
||||
}
|
||||
|
||||
test "ComptimeInterpreter - builtins" {
|
||||
if (true) return error.SkipZigTest; // TODO
|
||||
try testExprCheck("@as(bool, true)", .{ .simple = .bool }, .{ .simple = .bool_true });
|
||||
try testExprCheck("@as(u32, 3)", .{ .int_type = .{
|
||||
.signedness = .unsigned,
|
||||
.bits = 32,
|
||||
} }, .{ .int_u64_value = 3 });
|
||||
}
|
||||
|
||||
test "ComptimeInterpreter - call return primitive type" {
|
||||
try testCallCheck(
|
||||
\\pub fn Foo() type {
|
||||
@ -48,7 +82,9 @@ test "ComptimeInterpreter - call return struct" {
|
||||
\\}
|
||||
, &.{});
|
||||
defer result.deinit();
|
||||
const struct_info = result.key.struct_type;
|
||||
try std.testing.expect(result.ty == .simple);
|
||||
try std.testing.expect(result.ty.simple == .type);
|
||||
const struct_info = result.val.struct_type;
|
||||
try std.testing.expectEqual(Index.none, struct_info.backing_int_ty);
|
||||
try std.testing.expectEqual(std.builtin.Type.ContainerLayout.Auto, struct_info.layout);
|
||||
try std.testing.expectEqual(@as(usize, 1), struct_info.fields.len);
|
||||
@ -72,7 +108,9 @@ test "ComptimeInterpreter - call comptime argument" {
|
||||
},
|
||||
});
|
||||
defer result1.deinit();
|
||||
try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 8 } }, result1.key);
|
||||
try std.testing.expect(result1.ty == .simple);
|
||||
try std.testing.expect(result1.ty.simple == .type);
|
||||
try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 8 } }, result1.val);
|
||||
|
||||
var result2 = try testCall(source, &.{
|
||||
Value{
|
||||
@ -81,18 +119,21 @@ test "ComptimeInterpreter - call comptime argument" {
|
||||
},
|
||||
});
|
||||
defer result2.deinit();
|
||||
try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 69 } }, result2.key);
|
||||
try std.testing.expect(result2.ty == .simple);
|
||||
try std.testing.expect(result2.ty.simple == .type);
|
||||
try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 69 } }, result2.val);
|
||||
}
|
||||
|
||||
//
|
||||
// Helper functions
|
||||
//
|
||||
|
||||
const CallResult = struct {
|
||||
const Result = struct {
|
||||
interpreter: ComptimeInterpreter,
|
||||
key: Key,
|
||||
ty: Key,
|
||||
val: Key,
|
||||
|
||||
pub fn deinit(self: *CallResult) void {
|
||||
pub fn deinit(self: *Result) void {
|
||||
self.interpreter.deinit();
|
||||
}
|
||||
};
|
||||
@ -102,7 +143,7 @@ const Value = struct {
|
||||
val: Key,
|
||||
};
|
||||
|
||||
fn testCall(source: []const u8, arguments: []const Value) !CallResult {
|
||||
fn testCall(source: []const u8, arguments: []const Value) !Result {
|
||||
var config = zls.Config{};
|
||||
var doc_store = zls.DocumentStore{
|
||||
.allocator = allocator,
|
||||
@ -144,20 +185,89 @@ fn testCall(source: []const u8, arguments: []const Value) !CallResult {
|
||||
|
||||
const call_result = try interpreter.call(.none, func_node, args, .{});
|
||||
|
||||
try std.testing.expectEqual(Key{ .simple = .type }, interpreter.ip.indexToKey(call_result.result.value.ty));
|
||||
|
||||
return CallResult{
|
||||
return Result{
|
||||
.interpreter = interpreter,
|
||||
.key = interpreter.ip.indexToKey(call_result.result.value.val),
|
||||
.ty = interpreter.ip.indexToKey(call_result.result.value.ty),
|
||||
.val = interpreter.ip.indexToKey(call_result.result.value.val),
|
||||
};
|
||||
}
|
||||
|
||||
fn testCallCheck(
|
||||
source: []const u8,
|
||||
arguments: []const Value,
|
||||
expected: Key,
|
||||
expected_ty: Key,
|
||||
) !void {
|
||||
var result = try testCall(source, arguments);
|
||||
defer result.deinit();
|
||||
try std.testing.expectEqual(expected, result.key);
|
||||
try std.testing.expect(result.ty == .simple);
|
||||
try std.testing.expect(result.ty.simple == .type);
|
||||
if (!expected_ty.eql(result.val)) {
|
||||
std.debug.print("expected type `{}`, found `{}`\n", .{ expected_ty.fmtType(result.interpreter.ip), result.val.fmtType(result.interpreter.ip) });
|
||||
return error.TestExpectedEqual;
|
||||
}
|
||||
}
|
||||
|
||||
fn testInterpret(source: []const u8, node_idx: Ast.Node.Index) !Result {
|
||||
var config = zls.Config{};
|
||||
var doc_store = zls.DocumentStore{
|
||||
.allocator = allocator,
|
||||
.config = &config,
|
||||
};
|
||||
defer doc_store.deinit();
|
||||
|
||||
const test_uri: []const u8 = switch (builtin.os.tag) {
|
||||
.windows => "file:///C:\\test.zig",
|
||||
else => "file:///test.zig",
|
||||
};
|
||||
|
||||
const handle = try doc_store.openDocument(test_uri, source);
|
||||
|
||||
var interpreter = ComptimeInterpreter{
|
||||
.allocator = allocator,
|
||||
.document_store = &doc_store,
|
||||
.uri = handle.uri,
|
||||
};
|
||||
errdefer interpreter.deinit();
|
||||
|
||||
_ = try interpreter.interpret(0, .none, .{});
|
||||
|
||||
const result = try interpreter.interpret(node_idx, .none, .{});
|
||||
|
||||
return Result{
|
||||
.interpreter = interpreter,
|
||||
.ty = interpreter.ip.indexToKey(result.value.ty),
|
||||
.val = interpreter.ip.indexToKey(result.value.val),
|
||||
};
|
||||
}
|
||||
|
||||
fn testExprCheck(
|
||||
expr: []const u8,
|
||||
expected_ty: Key,
|
||||
expected_val: ?Key,
|
||||
) !void {
|
||||
const source = try std.fmt.allocPrint(allocator,
|
||||
\\const foobarbaz = {s};
|
||||
, .{expr});
|
||||
defer allocator.free(source);
|
||||
|
||||
var result = try testInterpret(source, 1);
|
||||
defer result.deinit();
|
||||
var ip: *InternPool = &result.interpreter.ip;
|
||||
|
||||
if (!expected_ty.eql(result.ty)) {
|
||||
std.debug.print("expected type `{}`, found `{}`\n", .{ expected_ty.fmtType(ip.*), result.ty.fmtType(ip.*) });
|
||||
return error.TestExpectedEqual;
|
||||
}
|
||||
|
||||
if (expected_val) |expected_value| {
|
||||
if (!expected_value.eql(result.val)) {
|
||||
const expected_ty_index = try ip.get(allocator, expected_ty);
|
||||
const actual_ty_index = try ip.get(allocator, result.ty);
|
||||
std.debug.print("expected value `{}`, found `{}`\n", .{
|
||||
expected_value.fmtValue(expected_ty_index, ip.*),
|
||||
result.val.fmtValue(actual_ty_index, ip.*),
|
||||
});
|
||||
return error.TestExpectedEqual;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user