add predefined values to Index

This commit is contained in:
Techarix 2023-02-08 21:01:15 +01:00
parent 6278880f42
commit 57fc10eec0
6 changed files with 426 additions and 395 deletions

View File

@ -20,7 +20,7 @@ pub const ComptimeInterpreter = @This();
const log = std.log.scoped(.comptime_interpreter); const log = std.log.scoped(.comptime_interpreter);
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
ip: InternPool = .{}, ip: InternPool,
document_store: *DocumentStore, document_store: *DocumentStore,
uri: DocumentStore.Uri, uri: DocumentStore.Uri,
namespaces: std.MultiArrayList(Namespace) = .{}, namespaces: std.MultiArrayList(Namespace) = .{},
@ -209,8 +209,6 @@ pub fn interpret(
// .tagged_union_enum_tag_trailing, // .tagged_union_enum_tag_trailing,
.root, .root,
=> { => {
const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type });
try interpreter.namespaces.append(interpreter.allocator, .{ try interpreter.namespaces.append(interpreter.allocator, .{
.parent = namespace, .parent = namespace,
.node_idx = node_idx, .node_idx = node_idx,
@ -243,7 +241,7 @@ pub fn interpret(
else else
(try (try interpreter.interpret(container_field.ast.value_expr, container_namespace, .{})).getValue()).val; // TODO check ty (try (try interpreter.interpret(container_field.ast.value_expr, container_namespace, .{})).getValue()).val; // TODO check ty
if (init_type_value.ty != type_type) { if (init_type_value.ty != Index.type) {
try interpreter.recordError( try interpreter.recordError(
container_field.ast.type_expr, container_field.ast.type_expr,
"expected_type", "expected_type",
@ -269,7 +267,7 @@ pub fn interpret(
return InterpretResult{ .value = Value{ return InterpretResult{ .value = Value{
.interpreter = interpreter, .interpreter = interpreter,
.node_idx = node_idx, .node_idx = node_idx,
.ty = type_type, .ty = Index.type,
.val = struct_type, .val = struct_type,
} }; } };
}, },
@ -316,8 +314,7 @@ pub fn interpret(
if (type_value == null and init_value == null) return InterpretResult{ .nothing = {} }; if (type_value == null and init_value == null) return InterpretResult{ .nothing = {} };
if (type_value) |v| { if (type_value) |v| {
const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); if (v.ty != Index.type) return InterpretResult{ .nothing = {} };
if (v.ty != type_type) return InterpretResult{ .nothing = {} };
} }
const decl = interpreter.ip.getDecl(decl_index); const decl = interpreter.ip.getDecl(decl_index);
@ -385,44 +382,53 @@ pub fn interpret(
.identifier => { .identifier => {
const identifier = offsets.nodeToSlice(tree, node_idx); const identifier = offsets.nodeToSlice(tree, node_idx);
const simples = std.ComptimeStringMap(InternPool.Simple, .{ const simples = std.ComptimeStringMap(Index, .{
.{ "anyerror", .anyerror }, .{ "anyerror", Index.anyerror },
.{ "anyframe", .@"anyframe" }, .{ "anyframe", Index.@"anyframe" },
.{ "anyopaque", .anyopaque }, .{ "anyopaque", Index.anyopaque },
.{ "bool", .bool }, .{ "bool", Index.bool },
.{ "c_int", .c_int }, .{ "c_int", Index.c_int },
.{ "c_long", .c_long }, .{ "c_long", Index.c_long },
.{ "c_longdouble", .c_longdouble }, .{ "c_longdouble", Index.c_longdouble },
.{ "c_longlong", .c_longlong }, .{ "c_longlong", Index.c_longlong },
.{ "c_short", .c_short }, .{ "c_short", Index.c_short },
.{ "c_uint", .c_uint }, .{ "c_uint", Index.c_uint },
.{ "c_ulong", .c_ulong }, .{ "c_ulong", Index.c_ulong },
.{ "c_ulonglong", .c_ulonglong }, .{ "c_ulonglong", Index.c_ulonglong },
.{ "c_ushort", .c_ushort }, .{ "c_ushort", Index.c_ushort },
.{ "comptime_float", .comptime_float }, .{ "comptime_float", Index.comptime_float },
.{ "comptime_int", .comptime_int }, .{ "comptime_int", Index.comptime_int },
.{ "f128", .f128 }, .{ "f128", Index.f128 },
.{ "f16", .f16 }, .{ "f16", Index.f16 },
.{ "f32", .f32 }, .{ "f32", Index.f32 },
.{ "f64", .f64 }, .{ "f64", Index.f64 },
.{ "f80", .f80 }, .{ "f80", Index.f80 },
.{ "false", .bool_false }, .{ "false", Index.bool_false },
.{ "isize", .isize }, .{ "isize", Index.isize },
.{ "noreturn", .noreturn }, .{ "noreturn", Index.noreturn },
.{ "null", .null_value }, .{ "null", Index.null_value },
.{ "true", .bool_true }, .{ "true", Index.bool_true },
.{ "type", .type }, .{ "type", Index.type },
.{ "undefined", .undefined_value }, .{ "undefined", Index.undefined_value },
.{ "usize", .usize }, .{ "usize", Index.usize },
.{ "void", .void }, .{ "void", Index.void },
}); });
if (simples.get(identifier)) |simple| { if (simples.get(identifier)) |index| {
const ty: Index = switch (index) {
.undefined_value => .undefined_type,
.void_value => .void,
.unreachable_value => .noreturn,
.null_value => .null_type,
.bool_true => .bool,
.bool_false => .bool,
else => .type,
};
return InterpretResult{ .value = Value{ return InterpretResult{ .value = Value{
.interpreter = interpreter, .interpreter = interpreter,
.node_idx = node_idx, .node_idx = node_idx,
.ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = simple.toType() }), .ty = ty,
.val = try interpreter.ip.get(interpreter.allocator, Key{ .simple = simple }), .val = index,
} }; } };
} }
@ -430,7 +436,7 @@ pub fn interpret(
return InterpretResult{ .value = Value{ return InterpretResult{ .value = Value{
.interpreter = interpreter, .interpreter = interpreter,
.node_idx = node_idx, .node_idx = node_idx,
.ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }), .ty = Index.type,
.val = try interpreter.ip.get(interpreter.allocator, Key{ .int_type = .{ .val = try interpreter.ip.get(interpreter.allocator, Key{ .int_type = .{
.signedness = if (identifier[0] == 'u') .unsigned else .signed, .signedness = if (identifier[0] == 'u') .unsigned else .signed,
.bits = std.fmt.parseInt(u16, identifier[1..], 10) catch break :blk, .bits = std.fmt.parseInt(u16, identifier[1..], 10) catch break :blk,
@ -456,7 +462,8 @@ pub fn interpret(
"use of undeclared identifier '{s}'", "use of undeclared identifier '{s}'",
.{identifier}, .{identifier},
); );
return error.IdentifierNotFound; // return error.IdentifierNotFound;
return InterpretResult{ .nothing = {} };
}, },
.field_access => { .field_access => {
if (data[node_idx].rhs == 0) return error.CriticalAstFailure; if (data[node_idx].rhs == 0) return error.CriticalAstFailure;
@ -472,7 +479,7 @@ pub fn interpret(
}; };
const can_have_fields: bool = switch (inner_lhs) { const can_have_fields: bool = switch (inner_lhs) {
.simple => |simple| switch (simple) { .simple_type => |simple| switch (simple) {
.type => blk: { .type => blk: {
if (irv.val == .none) break :blk true; if (irv.val == .none) break :blk true;
@ -530,7 +537,7 @@ pub fn interpret(
.value = Value{ .value = Value{
.interpreter = interpreter, .interpreter = interpreter,
.node_idx = data[node_idx].rhs, .node_idx = data[node_idx].rhs,
.ty = try interpreter.ip.get(interpreter.allocator, .{ .simple = .usize }), .ty = Index.usize,
.val = .none, // TODO resolve length of Slice .val = .none, // TODO resolve length of Slice
}, },
}; };
@ -541,7 +548,7 @@ pub fn interpret(
.value = Value{ .value = Value{
.interpreter = interpreter, .interpreter = interpreter,
.node_idx = data[node_idx].rhs, .node_idx = data[node_idx].rhs,
.ty = try interpreter.ip.get(interpreter.allocator, .{ .simple = .usize }), .ty = Index.usize,
.val = .none, // TODO resolve length of Slice .val = .none, // TODO resolve length of Slice
}, },
}; };
@ -556,7 +563,7 @@ pub fn interpret(
return InterpretResult{ .value = Value{ return InterpretResult{ .value = Value{
.interpreter = interpreter, .interpreter = interpreter,
.node_idx = data[node_idx].rhs, .node_idx = data[node_idx].rhs,
.ty = try interpreter.ip.get(interpreter.allocator, .{ .simple = .comptime_int }), .ty = Index.comptime_int,
.val = len_value, .val = len_value,
} }; } };
} }
@ -564,8 +571,7 @@ pub fn interpret(
}, },
.optional_type => |optional_info| blk: { .optional_type => |optional_info| blk: {
if (!std.mem.eql(u8, field_name, "?")) break :blk false; if (!std.mem.eql(u8, field_name, "?")) break :blk false;
const null_value = try interpreter.ip.get(interpreter.allocator, .{ .simple = .null_value }); if (irv.val == Index.null_value) {
if (irv.val == null_value) {
try interpreter.recordError( try interpreter.recordError(
node_idx, node_idx,
"null_unwrap", "null_unwrap",
@ -613,7 +619,7 @@ pub fn interpret(
else => false, else => false,
}; };
const accessed_ty = if (inner_lhs == .simple and inner_lhs.simple == .type) irv.val else irv.ty; const accessed_ty = if (inner_lhs == .simple_type and inner_lhs.simple_type == .type) irv.val else irv.ty;
if (accessed_ty != .none) { if (accessed_ty != .none) {
if (can_have_fields) { if (can_have_fields) {
try interpreter.recordError( try interpreter.recordError(
@ -657,12 +663,9 @@ pub fn interpret(
// if (options.observe_values) { // if (options.observe_values) {
const ir = try interpreter.interpret(if_info.ast.cond_expr, namespace, options); const ir = try interpreter.interpret(if_info.ast.cond_expr, namespace, options);
const false_value = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool_false });
const true_value = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool_true });
const condition = (try ir.getValue()).val; const condition = (try ir.getValue()).val;
std.debug.assert(condition == false_value or condition == true_value); std.debug.assert(condition == Index.bool_false or condition == Index.bool_true);
if (condition == true_value) { if (condition == Index.bool_true) {
return try interpreter.interpret(if_info.ast.then_expr, namespace, options); return try interpreter.interpret(if_info.ast.then_expr, namespace, options);
} else { } else {
if (if_info.ast.else_expr != 0) { if (if_info.ast.else_expr != 0) {
@ -679,8 +682,8 @@ pub fn interpret(
.value = Value{ .value = Value{
.interpreter = interpreter, .interpreter = interpreter,
.node_idx = node_idx, .node_idx = node_idx,
.ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool }), .ty = Index.bool,
.val = try interpreter.ip.get(interpreter.allocator, Key{ .simple = if (a_value.val == b_value.val) .bool_true else .bool_false }), // TODO eql function required? .val = if (a_value.val == b_value.val) Index.bool_true else Index.bool_false, // TODO eql function required?
}, },
}; };
}, },
@ -690,9 +693,7 @@ pub fn interpret(
if (nl == .failure) return error.CriticalAstFailure; if (nl == .failure) return error.CriticalAstFailure;
const number_type = try interpreter.ip.get(interpreter.allocator, Key{ const number_type = if (nl == .float) Index.comptime_float else Index.comptime_int;
.simple = if (nl == .float) .comptime_float else .comptime_int,
});
const value = try interpreter.ip.get( const value = try interpreter.ip.get(
interpreter.allocator, interpreter.allocator,
@ -826,7 +827,7 @@ pub fn interpret(
.interpreter = interpreter, .interpreter = interpreter,
.node_idx = node_idx, .node_idx = node_idx,
.ty = try interpreter.ip.get(interpreter.allocator, Key{ .struct_type = struct_index }), .ty = try interpreter.ip.get(interpreter.allocator, Key{ .struct_type = struct_index }),
.val = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .undefined_value }), .val = Index.undefined_value,
} }; } };
} }
@ -834,13 +835,13 @@ pub fn interpret(
defer interpreter.allocator.free(import_uri); defer interpreter.allocator.free(import_uri);
var handle = interpreter.document_store.getOrLoadHandle(import_uri) orelse return error.ImportFailure; var handle = interpreter.document_store.getOrLoadHandle(import_uri) orelse return error.ImportFailure;
try interpreter.document_store.ensureInterpreterExists(handle.uri); _ = try interpreter.document_store.ensureInterpreterExists(handle.uri);
return InterpretResult{ return InterpretResult{
.value = Value{ .value = Value{
.interpreter = interpreter, .interpreter = interpreter,
.node_idx = node_idx, .node_idx = node_idx,
.ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }), .ty = Index.type,
.val = .none, // TODO .val = .none, // TODO
}, },
}; };
@ -853,7 +854,7 @@ pub fn interpret(
return InterpretResult{ .value = Value{ return InterpretResult{ .value = Value{
.interpreter = interpreter, .interpreter = interpreter,
.node_idx = node_idx, .node_idx = node_idx,
.ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }), .ty = Index.type,
.val = value.ty, .val = value.ty,
} }; } };
} }
@ -864,9 +865,7 @@ pub fn interpret(
const value = try (try interpreter.interpret(params[0], namespace, options)).getValue(); const value = try (try interpreter.interpret(params[0], namespace, options)).getValue();
const field_name = try (try interpreter.interpret(params[1], namespace, options)).getValue(); const field_name = try (try interpreter.interpret(params[1], namespace, options)).getValue();
const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); if (value.ty != Index.type or value.ty == .none) return error.InvalidBuiltin;
if (value.ty != type_type or value.ty == .none) return error.InvalidBuiltin;
if (interpreter.ip.indexToKey(field_name.ty) != .pointer_type) return error.InvalidBuiltin; // Check if it's a []const u8 if (interpreter.ip.indexToKey(field_name.ty) != .pointer_type) return error.InvalidBuiltin; // Check if it's a []const u8
if (value.val == .none) return error.InvalidBuiltin; if (value.val == .none) return error.InvalidBuiltin;
@ -881,8 +880,8 @@ pub fn interpret(
return InterpretResult{ .value = Value{ return InterpretResult{ .value = Value{
.interpreter = interpreter, .interpreter = interpreter,
.node_idx = node_idx, .node_idx = node_idx,
.ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool }), .ty = Index.bool,
.val = try interpreter.ip.get(interpreter.allocator, Key{ .simple = if (has_decl) .bool_true else .bool_false }), .val = if (has_decl) Index.bool_true else Index.bool_false,
} }; } };
} }
@ -892,9 +891,7 @@ pub fn interpret(
const as_type = try (try interpreter.interpret(params[0], namespace, options)).getValue(); const as_type = try (try interpreter.interpret(params[0], namespace, options)).getValue();
const value = try (try interpreter.interpret(params[1], namespace, options)).getValue(); const value = try (try interpreter.interpret(params[1], namespace, options)).getValue();
const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); if (as_type.ty != Index.type) return error.InvalidBuiltin;
if (as_type.ty != type_type) return error.InvalidBuiltin;
return InterpretResult{ return InterpretResult{
.value = Value{ .value = Value{
@ -955,8 +952,6 @@ pub fn interpret(
// TODO: Resolve function type // TODO: Resolve function type
const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type });
const function_type = try interpreter.ip.get(interpreter.allocator, Key{ .function_type = .{ const function_type = try interpreter.ip.get(interpreter.allocator, Key{ .function_type = .{
.calling_convention = .Unspecified, .calling_convention = .Unspecified,
.alignment = 0, .alignment = 0,
@ -995,7 +990,7 @@ pub fn interpret(
const decl_index = try interpreter.ip.createDecl(interpreter.allocator, .{ const decl_index = try interpreter.ip.createDecl(interpreter.allocator, .{
.name = name, .name = name,
.ty = type_type, .ty = Index.type,
.val = function_type, .val = function_type,
.alignment = 0, // TODO .alignment = 0, // TODO
.address_space = .generic, // TODO .address_space = .generic, // TODO
@ -1040,10 +1035,9 @@ pub fn interpret(
}, },
.bool_not => { .bool_not => {
const result = try interpreter.interpret(data[node_idx].lhs, namespace, .{}); const result = try interpreter.interpret(data[node_idx].lhs, namespace, .{});
const bool_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool });
const value = try result.getValue(); const value = try result.getValue();
if (value.ty != bool_type) { if (value.ty != Index.bool) {
try interpreter.recordError( try interpreter.recordError(
node_idx, node_idx,
"invalid_deref", "invalid_deref",
@ -1053,15 +1047,12 @@ pub fn interpret(
return error.InvalidOperation; return error.InvalidOperation;
} }
const false_value = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool_false }); std.debug.assert(value.val == Index.bool_false or value.val == Index.bool_true);
const true_value = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool_true });
std.debug.assert(value.val == false_value or value.val == true_value);
return InterpretResult{ .value = .{ return InterpretResult{ .value = .{
.interpreter = interpreter, .interpreter = interpreter,
.node_idx = node_idx, .node_idx = node_idx,
.ty = bool_type, .ty = Index.bool,
.val = if (value.val == false_value) true_value else false_value, .val = if (value.val == Index.bool_false) Index.bool_true else Index.bool_false,
} }; } };
}, },
.address_of => { .address_of => {
@ -1146,14 +1137,12 @@ pub fn call(
}); });
const fn_namespace = @intToEnum(Namespace.Index, interpreter.namespaces.len - 1); const fn_namespace = @intToEnum(Namespace.Index, interpreter.namespaces.len - 1);
const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type });
var arg_it = proto.iterate(&tree); var arg_it = proto.iterate(&tree);
var arg_index: usize = 0; var arg_index: usize = 0;
while (ast.nextFnParam(&arg_it)) |param| { while (ast.nextFnParam(&arg_it)) |param| {
if (arg_index >= arguments.len) return error.MissingArguments; if (arg_index >= arguments.len) return error.MissingArguments;
var tex = try (try interpreter.interpret(param.type_expr, fn_namespace, options)).getValue(); var tex = try (try interpreter.interpret(param.type_expr, fn_namespace, options)).getValue();
if (tex.ty != type_type) { if (tex.ty != Index.type) {
try interpreter.recordError( try interpreter.recordError(
param.type_expr, param.type_expr,
"expected_type", "expected_type",

View File

@ -989,16 +989,26 @@ pub fn enumCompletionItems(self: DocumentStore, arena: std.mem.Allocator, handle
return try self.tagStoreCompletionItems(arena, handle, "enum_completions"); return try self.tagStoreCompletionItems(arena, handle, "enum_completions");
} }
pub fn ensureInterpreterExists(self: *DocumentStore, uri: Uri) !void { pub fn ensureInterpreterExists(self: *DocumentStore, uri: Uri) !*ComptimeInterpreter {
var handle = self.handles.get(uri).?; var handle = self.handles.get(uri).?;
if (handle.interpreter == null) { if (handle.interpreter != null) return handle.interpreter.?;
var int = try self.allocator.create(ComptimeInterpreter);
int.* = ComptimeInterpreter{ {
var interpreter = try self.allocator.create(ComptimeInterpreter);
errdefer self.allocator.destroy(interpreter);
var ip = try ComptimeInterpreter.InternPool.init(self.allocator);
errdefer ip.deinit(self.allocator);
interpreter.* = ComptimeInterpreter{
.allocator = self.allocator, .allocator = self.allocator,
.ip = ip,
.document_store = self, .document_store = self,
.uri = uri, .uri = uri,
}; };
handle.interpreter = int; handle.interpreter = interpreter;
_ = try int.interpret(0, .none, .{});
} }
_ = try handle.interpreter.?.interpret(0, .none, .{});
return handle.interpreter.?;
} }

File diff suppressed because it is too large Load Diff

View File

@ -21,7 +21,7 @@ pub fn dotCompletions(
}; };
switch (inner_key) { switch (inner_key) {
.simple => |simple| switch (simple) { .simple_type => |simple| switch (simple) {
.type => { .type => {
const ty_key = ip.indexToKey(val); const ty_key = ip.indexToKey(val);
const namespace = ty_key.getNamespace(ip.*); const namespace = ty_key.getNamespace(ip.*);
@ -147,6 +147,7 @@ pub fn dotCompletions(
.anyframe_type, .anyframe_type,
=> {}, => {},
.simple_value,
.int_u64_value, .int_u64_value,
.int_i64_value, .int_i64_value,
.int_big_value, .int_big_value,

View File

@ -7,6 +7,7 @@ const URI = @import("uri.zig");
const log = std.log.scoped(.analysis); const log = std.log.scoped(.analysis);
const ast = @import("ast.zig"); const ast = @import("ast.zig");
const ComptimeInterpreter = @import("ComptimeInterpreter.zig"); const ComptimeInterpreter = @import("ComptimeInterpreter.zig");
const InternPool = ComptimeInterpreter.InternPool;
var using_trail: std.ArrayList([*]const u8) = undefined; var using_trail: std.ArrayList([*]const u8) = undefined;
var resolve_trail: std.ArrayList(NodeWithHandle) = undefined; var resolve_trail: std.ArrayList(NodeWithHandle) = undefined;
@ -765,14 +766,13 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl
log.info("Invoking interpreter!", .{}); log.info("Invoking interpreter!", .{});
store.ensureInterpreterExists(handle.uri) catch |err| { const interpreter = store.ensureInterpreterExists(handle.uri) catch |err| {
log.err("Failed to interpret file: {s}", .{@errorName(err)}); log.err("Failed to interpret file: {s}", .{@errorName(err)});
if (@errorReturnTrace()) |trace| { if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*); std.debug.dumpStackTrace(trace.*);
} }
return null; return null;
}; };
var interpreter: *ComptimeInterpreter = handle.interpreter.?;
const root_namespace = @intToEnum(ComptimeInterpreter.Namespace.Index, 0); const root_namespace = @intToEnum(ComptimeInterpreter.Namespace.Index, 0);
@ -792,16 +792,13 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl
return null; return null;
}; };
const type_type = try interpreter.ip.get(interpreter.allocator, ComptimeInterpreter.Key{ .simple = .type });
const is_type_val = value.ty == type_type;
return TypeWithHandle{ return TypeWithHandle{
.type = .{ .type = .{
.data = .{ .@"comptime" = .{ .data = .{ .@"comptime" = .{
.interpreter = interpreter, .interpreter = interpreter,
.value = value, .value = value,
} }, } },
.is_type_val = is_type_val, .is_type_val = value.ty == InternPool.Index.type,
}, },
.handle = node_handle.handle, .handle = node_handle.handle,
}; };

View File

@ -14,32 +14,32 @@ const offsets = zls.offsets;
const allocator: std.mem.Allocator = std.testing.allocator; const allocator: std.mem.Allocator = std.testing.allocator;
test "ComptimeInterpreter - primitive types" { test "ComptimeInterpreter - primitive types" {
try testExpr("true", .{ .simple = .bool }, .{ .simple = .bool_true }); try testExpr("true", .{ .simple_type = .bool }, .{ .simple_value = .bool_true });
try testExpr("false", .{ .simple = .bool }, .{ .simple = .bool_false }); try testExpr("false", .{ .simple_type = .bool }, .{ .simple_value = .bool_false });
try testExpr("5", .{ .simple = .comptime_int }, .{ .int_u64_value = 5 }); try testExpr("5", .{ .simple_type = .comptime_int }, .{ .int_u64_value = 5 });
// TODO try testExpr("-2", .{ .simple = .comptime_int }, .{ .int_i64_value = -2 }); // TODO try testExpr("-2", .{ .simple_type = .comptime_int }, .{ .int_i64_value = -2 });
try testExpr("3.0", .{ .simple = .comptime_float }, null); try testExpr("3.0", .{ .simple_type = .comptime_float }, null);
try testExpr("null", .{ .simple = .null_type }, .{ .simple = .null_value }); try testExpr("null", .{ .simple_type = .null_type }, .{ .simple_value = .null_value });
try testExpr("void", .{ .simple = .type }, .{ .simple = .void }); try testExpr("void", .{ .simple_type = .type }, .{ .simple_type = .void });
try testExpr("undefined", .{ .simple = .undefined_type }, .{ .simple = .undefined_value }); try testExpr("undefined", .{ .simple_type = .undefined_type }, .{ .simple_value = .undefined_value });
try testExpr("noreturn", .{ .simple = .type }, .{ .simple = .noreturn }); try testExpr("noreturn", .{ .simple_type = .type }, .{ .simple_type = .noreturn });
} }
test "ComptimeInterpreter - expressions" { test "ComptimeInterpreter - expressions" {
if (true) return error.SkipZigTest; // TODO if (true) return error.SkipZigTest; // TODO
try testExpr("5 + 3", .{ .simple = .comptime_int }, .{ .int_u64_value = 8 }); try testExpr("5 + 3", .{ .simple_type = .comptime_int }, .{ .int_u64_value = 8 });
try testExpr("5.2 + 4.2", .{ .simple = .comptime_float }, null); try testExpr("5.2 + 4.2", .{ .simple_type = .comptime_float }, null);
try testExpr("3 == 3", .{ .simple = .bool }, .{ .simple = .bool_true }); try testExpr("3 == 3", .{ .simple_type = .bool }, .{ .simple_valueclear = .bool_true });
try testExpr("5.2 == 2.1", .{ .simple = .bool }, .{ .simple = .bool_false }); try testExpr("5.2 == 2.1", .{ .simple_type = .bool }, .{ .simple_value = .bool_false });
try testExpr("@as(?bool, null) orelse true", .{ .simple = .bool }, .{ .simple = .bool_true }); try testExpr("@as(?bool, null) orelse true", .{ .simple_type = .bool }, .{ .simple_value = .bool_true });
} }
test "ComptimeInterpreter - builtins" { test "ComptimeInterpreter - builtins" {
if (true) return error.SkipZigTest; // TODO if (true) return error.SkipZigTest; // TODO
try testExpr("@as(bool, true)", .{ .simple = .bool }, .{ .simple = .bool_true }); try testExpr("@as(bool, true)", .{ .simple_type = .bool }, .{ .simple_value = .bool_true });
try testExpr("@as(u32, 3)", .{ .int_type = .{ try testExpr("@as(u32, 3)", .{ .int_type = .{
.signedness = .unsigned, .signedness = .unsigned,
.bits = 32, .bits = 32,
@ -64,12 +64,12 @@ test "ComptimeInterpreter - labeled block" {
\\blk: { \\blk: {
\\ break :blk true; \\ break :blk true;
\\} \\}
, .{ .simple = .bool }, .{ .simple = .bool_true }); , .{ .simple_type = .bool }, .{ .simple_value = .bool_true });
try testExpr( try testExpr(
\\blk: { \\blk: {
\\ break :blk 3; \\ break :blk 3;
\\} \\}
, .{ .simple = .comptime_int }, .{ .int_u64_value = 3 }); , .{ .simple_type = .comptime_int }, .{ .int_u64_value = 3 });
} }
test "ComptimeInterpreter - if" { test "ComptimeInterpreter - if" {
@ -77,18 +77,18 @@ test "ComptimeInterpreter - if" {
\\blk: { \\blk: {
\\ break :blk if (true) true else false; \\ break :blk if (true) true else false;
\\} \\}
, .{ .simple = .bool }, .{ .simple = .bool_true }); , .{ .simple_type = .bool }, .{ .simple_value = .bool_true });
try testExpr( try testExpr(
\\blk: { \\blk: {
\\ break :blk if (false) true else false; \\ break :blk if (false) true else false;
\\} \\}
, .{ .simple = .bool }, .{ .simple = .bool_false }); , .{ .simple_type = .bool }, .{ .simple_value = .bool_false });
try testExpr( try testExpr(
\\blk: { \\blk: {
\\ if (false) break :blk true; \\ if (false) break :blk true;
\\ break :blk false; \\ break :blk false;
\\} \\}
, .{ .simple = .bool }, .{ .simple = .bool_false }); , .{ .simple_type = .bool }, .{ .simple_value = .bool_false });
// TODO // TODO
// try testExpr( // try testExpr(
// \\outer: { // \\outer: {
@ -97,7 +97,7 @@ test "ComptimeInterpreter - if" {
// \\ }) break :outer true; // \\ }) break :outer true;
// \\ break :outer false; // \\ break :outer false;
// \\} // \\}
// , .{ .simple = .bool }, .{ .simple = .bool_true }); // , .{ .simple_type = .bool }, .{ .simple_value = .bool_true });
} }
test "ComptimeInterpreter - variable lookup" { test "ComptimeInterpreter - variable lookup" {
@ -106,7 +106,7 @@ test "ComptimeInterpreter - variable lookup" {
\\ var foo = 42; \\ var foo = 42;
\\ break :blk foo; \\ break :blk foo;
\\} \\}
, .{ .simple = .comptime_int }, .{ .int_u64_value = 42 }); , .{ .simple_type = .comptime_int }, .{ .int_u64_value = 42 });
try testExpr( try testExpr(
\\blk: { \\blk: {
\\ var foo = 1; \\ var foo = 1;
@ -114,7 +114,7 @@ test "ComptimeInterpreter - variable lookup" {
\\ var baz = 3; \\ var baz = 3;
\\ break :blk bar; \\ break :blk bar;
\\} \\}
, .{ .simple = .comptime_int }, .{ .int_u64_value = 2 }); , .{ .simple_type = .comptime_int }, .{ .int_u64_value = 2 });
var context = try Context.init( var context = try Context.init(
\\const bar = foo; \\const bar = foo;
@ -123,7 +123,7 @@ test "ComptimeInterpreter - variable lookup" {
defer context.deinit(); defer context.deinit();
const result = try context.interpret(context.findVar("bar")); const result = try context.interpret(context.findVar("bar"));
try expectEqualKey(context.interpreter.ip, .{ .simple = .comptime_int }, result.ty); try expectEqualKey(context.interpreter.ip, .{ .simple_type = .comptime_int }, result.ty);
try expectEqualKey(context.interpreter.ip, .{ .int_u64_value = 3 }, result.val); try expectEqualKey(context.interpreter.ip, .{ .int_u64_value = 3 }, result.val);
} }
@ -133,7 +133,7 @@ test "ComptimeInterpreter - field access" {
\\ const foo: struct {alpha: u64, beta: bool} = undefined; \\ const foo: struct {alpha: u64, beta: bool} = undefined;
\\ break :blk foo.beta; \\ break :blk foo.beta;
\\} \\}
, .{ .simple = .bool }, null); , .{ .simple_type = .bool }, null);
try testExpr( try testExpr(
\\blk: { \\blk: {
\\ const foo: struct {alpha: u64, beta: bool} = undefined; \\ const foo: struct {alpha: u64, beta: bool} = undefined;
@ -152,13 +152,13 @@ test "ComptimeInterpreter - optional operations" {
\\ const foo: ?bool = true; \\ const foo: ?bool = true;
\\ break :blk foo.?; \\ break :blk foo.?;
\\} \\}
, .{ .simple = .bool }, .{ .simple = .bool_true }); , .{ .simple_type = .bool }, .{ .simple_value = .bool_true });
try testExpr( try testExpr(
\\blk: { \\blk: {
\\ const foo: ?bool = true; \\ const foo: ?bool = true;
\\ break :blk foo == null; \\ break :blk foo == null;
\\} \\}
, .{ .simple = .bool }, .{ .simple = .bool_false }); , .{ .simple_type = .bool }, .{ .simple_value = .bool_false });
} }
test "ComptimeInterpreter - pointer operations" { test "ComptimeInterpreter - pointer operations" {
@ -168,20 +168,20 @@ test "ComptimeInterpreter - pointer operations" {
\\ const foo: []const u8 = ""; \\ const foo: []const u8 = "";
\\ break :blk foo.len; \\ break :blk foo.len;
\\} \\}
, .{ .simple = .usize }, .{ .bytes = "" }); , .{ .simple_type = .usize }, .{ .bytes = "" });
try testExpr( try testExpr(
\\blk: { \\blk: {
\\ const foo = true; \\ const foo = true;
\\ break :blk &foo; \\ break :blk &foo;
\\} \\}
, @panic("TODO"), .{ .simple = .bool_true }); , @panic("TODO"), .{ .simple_value = .bool_true });
try testExpr( try testExpr(
\\blk: { \\blk: {
\\ const foo = true; \\ const foo = true;
\\ const bar = &foo; \\ const bar = &foo;
\\ break :blk bar.*; \\ break :blk bar.*;
\\} \\}
, @panic("TODO"), .{ .simple = .bool_true }); , @panic("TODO"), .{ .simple_value = .bool_true });
} }
test "ComptimeInterpreter - call return primitive type" { test "ComptimeInterpreter - call return primitive type" {
@ -189,7 +189,7 @@ test "ComptimeInterpreter - call return primitive type" {
\\pub fn Foo() type { \\pub fn Foo() type {
\\ return bool; \\ return bool;
\\} \\}
, &.{}, .{ .simple = .bool }); , &.{}, .{ .simple_type = .bool });
try testCall( try testCall(
\\pub fn Foo() type { \\pub fn Foo() type {
@ -223,17 +223,15 @@ test "ComptimeInterpreter - call return struct" {
defer context.deinit(); defer context.deinit();
const result = try context.call(context.findFn("Foo"), &.{}); const result = try context.call(context.findFn("Foo"), &.{});
try std.testing.expect(result.ty == .simple); try std.testing.expect(result.ty == .simple_type);
try std.testing.expect(result.ty.simple == .type); try std.testing.expect(result.ty.simple_type == .type);
const struct_info = context.interpreter.ip.getStruct(result.val.?.struct_type); const struct_info = context.interpreter.ip.getStruct(result.val.?.struct_type);
try std.testing.expectEqual(Index.none, struct_info.backing_int_ty); 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(std.builtin.Type.ContainerLayout.Auto, struct_info.layout);
const bool_type = try context.interpreter.ip.get(allocator, .{ .simple = .bool });
try std.testing.expectEqual(@as(usize, 1), struct_info.fields.count()); try std.testing.expectEqual(@as(usize, 1), struct_info.fields.count());
try std.testing.expectEqualStrings("slay", struct_info.fields.keys()[0]); try std.testing.expectEqualStrings("slay", struct_info.fields.keys()[0]);
try std.testing.expect(struct_info.fields.values()[0].ty == bool_type); try std.testing.expect(struct_info.fields.values()[0].ty == Index.bool);
} }
test "ComptimeInterpreter - call comptime argument" { test "ComptimeInterpreter - call comptime argument" {
@ -247,19 +245,19 @@ test "ComptimeInterpreter - call comptime argument" {
defer context.deinit(); defer context.deinit();
const result1 = try context.call(context.findFn("Foo"), &.{KV{ const result1 = try context.call(context.findFn("Foo"), &.{KV{
.ty = .{ .simple = .bool }, .ty = .{ .simple_type = .bool },
.val = .{ .simple = .bool_true }, .val = .{ .simple_value = .bool_true },
}}); }});
try std.testing.expect(result1.ty == .simple); try std.testing.expect(result1.ty == .simple_type);
try std.testing.expect(result1.ty.simple == .type); try std.testing.expect(result1.ty.simple_type == .type);
try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 8 } }, result1.val.?); try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 8 } }, result1.val.?);
var result2 = try context.call(context.findFn("Foo"), &.{KV{ var result2 = try context.call(context.findFn("Foo"), &.{KV{
.ty = .{ .simple = .bool }, .ty = .{ .simple_type = .bool },
.val = .{ .simple = .bool_false }, .val = .{ .simple_value = .bool_false },
}}); }});
try std.testing.expect(result2.ty == .simple); try std.testing.expect(result2.ty == .simple_type);
try std.testing.expect(result2.ty.simple == .type); try std.testing.expect(result2.ty.simple_type == .type);
try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 69 } }, result2.val.?); try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 69 } }, result2.val.?);
} }
@ -303,6 +301,7 @@ const Context = struct {
interpreter.* = .{ interpreter.* = .{
.allocator = allocator, .allocator = allocator,
.ip = try InternPool.init(allocator),
.document_store = document_store, .document_store = document_store,
.uri = handle.uri, .uri = handle.uri,
}; };
@ -399,7 +398,7 @@ fn testCall(
const result = try context.call(context.findFn("Foo"), arguments); const result = try context.call(context.findFn("Foo"), arguments);
try expectEqualKey(context.interpreter.ip, Key{ .simple = .type }, result.ty); try expectEqualKey(context.interpreter.ip, Key{ .simple_type = .type }, result.ty);
try expectEqualKey(context.interpreter.ip, expected_ty, result.val); try expectEqualKey(context.interpreter.ip, expected_ty, result.val);
} }