2022-10-28 04:59:24 +01:00
|
|
|
//! Hacky comptime interpreter, courtesy of midnight code run fuelled by spite;
|
|
|
|
//! hope that one day this can use async... <33
|
|
|
|
|
|
|
|
// TODO: DODify
|
|
|
|
|
|
|
|
const std = @import("std");
|
2022-12-27 01:46:57 +00:00
|
|
|
const builtin = @import("builtin");
|
2022-10-28 04:59:24 +01:00
|
|
|
const ast = @import("ast.zig");
|
|
|
|
const zig = std.zig;
|
|
|
|
const Ast = zig.Ast;
|
|
|
|
const analysis = @import("analysis.zig");
|
2023-01-04 10:11:48 +00:00
|
|
|
const offsets = @import("offsets.zig");
|
2022-10-28 04:59:24 +01:00
|
|
|
const DocumentStore = @import("DocumentStore.zig");
|
2022-12-01 23:08:45 +00:00
|
|
|
|
|
|
|
pub const InternPool = @import("InternPool.zig");
|
|
|
|
pub const IPIndex = InternPool.Index;
|
|
|
|
pub const IPKey = InternPool.Key;
|
|
|
|
pub const ComptimeInterpreter = @This();
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2022-11-10 04:46:23 +00:00
|
|
|
const log = std.log.scoped(.comptime_interpreter);
|
|
|
|
|
2022-10-31 20:00:02 +00:00
|
|
|
// TODO: Investigate arena
|
|
|
|
|
2022-10-28 04:59:24 +01:00
|
|
|
allocator: std.mem.Allocator,
|
2022-12-01 23:08:45 +00:00
|
|
|
arena: std.heap.ArenaAllocator,
|
|
|
|
ip: InternPool = .{},
|
2022-10-29 06:46:22 +01:00
|
|
|
document_store: *DocumentStore,
|
2022-11-11 01:51:02 +00:00
|
|
|
uri: DocumentStore.Uri,
|
2022-12-27 01:46:57 +00:00
|
|
|
scopes: std.MultiArrayList(Scope) = .{},
|
2023-01-04 10:11:48 +00:00
|
|
|
decls: std.ArrayListUnmanaged(Decl) = .{},
|
|
|
|
namespaces: std.MultiArrayList(Namespace) = .{},
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2022-10-30 08:07:49 +00:00
|
|
|
/// Interpreter diagnostic errors
|
|
|
|
errors: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, InterpreterError) = .{},
|
|
|
|
|
2022-11-11 01:51:02 +00:00
|
|
|
pub fn getHandle(interpreter: *ComptimeInterpreter) *const DocumentStore.Handle {
|
|
|
|
// This interpreter is loaded from a known-valid handle so a valid handle must exist
|
|
|
|
return interpreter.document_store.getOrLoadHandle(interpreter.uri).?;
|
|
|
|
}
|
|
|
|
|
2022-10-30 08:07:49 +00:00
|
|
|
pub const InterpreterError = struct {
|
|
|
|
code: []const u8,
|
|
|
|
message: []const u8,
|
|
|
|
};
|
|
|
|
|
2022-10-31 20:00:02 +00:00
|
|
|
/// `message` must be allocated with interpreter allocator
|
2022-10-31 05:51:51 +00:00
|
|
|
pub fn recordError(interpreter: *ComptimeInterpreter, node_idx: Ast.Node.Index, code: []const u8, message: []const u8) error{OutOfMemory}!void {
|
2022-10-30 08:07:49 +00:00
|
|
|
try interpreter.errors.put(interpreter.allocator, node_idx, .{
|
|
|
|
.code = code,
|
|
|
|
.message = message,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-10-28 04:59:24 +01:00
|
|
|
pub fn deinit(interpreter: *ComptimeInterpreter) void {
|
2022-10-31 20:00:02 +00:00
|
|
|
var err_it = interpreter.errors.iterator();
|
|
|
|
while (err_it.next()) |entry| interpreter.allocator.free(entry.value_ptr.message);
|
|
|
|
|
|
|
|
interpreter.errors.deinit(interpreter.allocator);
|
2022-12-01 23:08:45 +00:00
|
|
|
interpreter.ip.deinit(interpreter.allocator);
|
2022-12-27 01:46:57 +00:00
|
|
|
|
|
|
|
var i: usize = 0;
|
2023-01-04 10:11:48 +00:00
|
|
|
while (i < interpreter.namespaces.len) : (i += 1) {
|
|
|
|
interpreter.namespaces.items(.decls)[i].deinit(interpreter.allocator);
|
|
|
|
interpreter.namespaces.items(.usingnamespaces)[i].deinit(interpreter.allocator);
|
2022-12-27 01:46:57 +00:00
|
|
|
}
|
2023-01-04 10:11:48 +00:00
|
|
|
interpreter.namespaces.deinit(interpreter.allocator);
|
|
|
|
interpreter.decls.deinit(interpreter.allocator);
|
2022-12-27 01:46:57 +00:00
|
|
|
interpreter.scopes.deinit(interpreter.allocator);
|
2023-01-04 10:11:48 +00:00
|
|
|
|
|
|
|
interpreter.arena.deinit();
|
2022-10-28 04:59:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub const Type = struct {
|
2022-11-01 03:36:13 +00:00
|
|
|
interpreter: *ComptimeInterpreter,
|
2022-10-29 22:28:44 +01:00
|
|
|
|
2022-10-28 04:59:24 +01:00
|
|
|
node_idx: Ast.Node.Index,
|
2022-12-01 23:08:45 +00:00
|
|
|
ty: IPIndex,
|
2022-10-28 04:59:24 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
pub const Value = struct {
|
2022-11-01 03:36:13 +00:00
|
|
|
interpreter: *ComptimeInterpreter,
|
2022-10-29 22:28:44 +01:00
|
|
|
|
2022-10-28 04:59:24 +01:00
|
|
|
node_idx: Ast.Node.Index,
|
2022-12-01 23:08:45 +00:00
|
|
|
ty: IPIndex,
|
|
|
|
val: IPIndex,
|
2022-10-28 04:59:24 +01:00
|
|
|
};
|
|
|
|
|
2023-01-04 10:11:48 +00:00
|
|
|
pub const Decl = struct {
|
|
|
|
name: []const u8,
|
|
|
|
ty: IPIndex,
|
|
|
|
val: IPIndex,
|
|
|
|
alignment: u16,
|
|
|
|
address_space: std.builtin.AddressSpace,
|
|
|
|
is_pub: bool,
|
|
|
|
is_exported: bool,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub const NamespaceIndex = InternPool.NamespaceIndex;
|
|
|
|
|
|
|
|
pub const Namespace = struct {
|
|
|
|
/// always points to Namespace or Index.none
|
|
|
|
parent: NamespaceIndex,
|
|
|
|
/// Will be a struct, enum, union, or opaque.
|
|
|
|
// ty: Index,
|
|
|
|
decls: std.StringArrayHashMapUnmanaged(Decl) = .{},
|
|
|
|
usingnamespaces: std.ArrayListUnmanaged(NamespaceIndex) = .{},
|
|
|
|
};
|
|
|
|
|
2022-10-31 20:00:02 +00:00
|
|
|
// pub const Comptimeness = enum { @"comptime", runtime };
|
|
|
|
|
2022-12-27 01:46:57 +00:00
|
|
|
pub const Scope = struct {
|
2022-10-28 04:59:24 +01:00
|
|
|
interpreter: *ComptimeInterpreter,
|
|
|
|
|
2022-10-31 20:00:02 +00:00
|
|
|
// TODO: Actually use this value
|
|
|
|
// comptimeness: Comptimeness,
|
|
|
|
|
2022-12-27 01:46:57 +00:00
|
|
|
parent: u32, // zero indicates root scope
|
2022-10-28 04:59:24 +01:00
|
|
|
node_idx: Ast.Node.Index,
|
2023-01-04 10:11:48 +00:00
|
|
|
namespace: NamespaceIndex,
|
2022-10-28 04:59:24 +01:00
|
|
|
|
|
|
|
pub const ScopeKind = enum { container, block, function };
|
2022-12-27 01:46:57 +00:00
|
|
|
pub fn scopeKind(scope: Scope) ScopeKind {
|
2022-11-11 01:51:02 +00:00
|
|
|
const tree = scope.interpreter.getHandle().tree;
|
2022-10-28 04:59:24 +01:00
|
|
|
return switch (tree.nodes.items(.tag)[scope.node_idx]) {
|
|
|
|
.container_decl,
|
|
|
|
.container_decl_trailing,
|
|
|
|
.container_decl_arg,
|
|
|
|
.container_decl_arg_trailing,
|
|
|
|
.container_decl_two,
|
|
|
|
.container_decl_two_trailing,
|
|
|
|
.tagged_union,
|
|
|
|
.tagged_union_trailing,
|
|
|
|
.tagged_union_two,
|
|
|
|
.tagged_union_two_trailing,
|
|
|
|
.tagged_union_enum_tag,
|
|
|
|
.tagged_union_enum_tag_trailing,
|
|
|
|
.root,
|
|
|
|
.error_set_decl,
|
|
|
|
=> .container,
|
|
|
|
else => .block,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-12-27 01:46:57 +00:00
|
|
|
pub fn getLabel(scope: Scope) ?Ast.TokenIndex {
|
2022-11-11 01:51:02 +00:00
|
|
|
const tree = scope.interpreter.getHandle().tree;
|
2022-10-28 04:59:24 +01:00
|
|
|
const token_tags = tree.tokens.items(.tag);
|
|
|
|
|
2022-11-01 03:36:13 +00:00
|
|
|
return switch (scope.scopeKind()) {
|
2022-10-28 04:59:24 +01:00
|
|
|
.block => z: {
|
|
|
|
const lbrace = tree.nodes.items(.main_token)[scope.node_idx];
|
|
|
|
break :z if (token_tags[lbrace - 1] == .colon and token_tags[lbrace - 2] == .identifier)
|
|
|
|
lbrace - 2
|
|
|
|
else
|
|
|
|
null;
|
|
|
|
},
|
|
|
|
else => null,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
pub const InterpretResult = union(enum) {
|
|
|
|
@"break": ?[]const u8,
|
|
|
|
break_with_value: struct {
|
|
|
|
label: ?[]const u8,
|
|
|
|
value: Value,
|
|
|
|
},
|
|
|
|
value: Value,
|
|
|
|
@"return",
|
|
|
|
return_with_value: Value,
|
|
|
|
nothing,
|
|
|
|
|
|
|
|
pub fn maybeGetValue(result: InterpretResult) ?Value {
|
|
|
|
return switch (result) {
|
|
|
|
.break_with_value => |v| v.value,
|
|
|
|
.value => |v| v,
|
2022-10-28 19:24:38 +01:00
|
|
|
.return_with_value => |v| v,
|
2022-10-28 04:59:24 +01:00
|
|
|
else => null,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-10-28 06:22:03 +01:00
|
|
|
pub fn getValue(result: InterpretResult) error{ExpectedValue}!Value {
|
|
|
|
return result.maybeGetValue() orelse error.ExpectedValue;
|
2022-10-28 04:59:24 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-10-29 22:28:44 +01:00
|
|
|
fn getDeclCount(tree: Ast, node_idx: Ast.Node.Index) usize {
|
|
|
|
var buffer: [2]Ast.Node.Index = undefined;
|
|
|
|
const members = ast.declMembers(tree, node_idx, &buffer);
|
|
|
|
|
|
|
|
var count: usize = 0;
|
|
|
|
|
|
|
|
for (members) |member| {
|
|
|
|
switch (tree.nodes.items(.tag)[member]) {
|
|
|
|
.global_var_decl,
|
|
|
|
.local_var_decl,
|
|
|
|
.aligned_var_decl,
|
|
|
|
.simple_var_decl,
|
|
|
|
=> count += 1,
|
|
|
|
else => {},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn huntItDown(
|
|
|
|
interpreter: *ComptimeInterpreter,
|
2023-01-04 10:11:48 +00:00
|
|
|
namespace: NamespaceIndex,
|
2022-10-29 22:28:44 +01:00
|
|
|
decl_name: []const u8,
|
|
|
|
options: InterpretOptions,
|
2023-01-04 10:11:48 +00:00
|
|
|
) InterpretError!Decl {
|
2022-12-27 01:46:57 +00:00
|
|
|
_ = options;
|
|
|
|
|
|
|
|
var current_namespace = namespace;
|
2023-01-04 10:11:48 +00:00
|
|
|
while (current_namespace != .none) {
|
|
|
|
const decls: std.StringArrayHashMapUnmanaged(Decl) = interpreter.namespaces.items(.decls)[@enumToInt(current_namespace)];
|
|
|
|
defer current_namespace = interpreter.namespaces.items(.parent)[@enumToInt(current_namespace)];
|
|
|
|
|
|
|
|
if (decls.get(decl_name)) |decl| {
|
|
|
|
return decl;
|
2022-10-29 22:28:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-10 04:46:23 +00:00
|
|
|
log.err("Identifier not found: {s}", .{decl_name});
|
2022-10-29 22:28:44 +01:00
|
|
|
return error.IdentifierNotFound;
|
|
|
|
}
|
|
|
|
|
2022-10-31 05:51:51 +00:00
|
|
|
pub fn cast(
|
|
|
|
interpreter: *ComptimeInterpreter,
|
|
|
|
node_idx: Ast.Node.Index,
|
2022-12-27 01:46:57 +00:00
|
|
|
destination_ty: IPIndex,
|
|
|
|
source_ty: IPIndex,
|
|
|
|
) error{ OutOfMemory, InvalidCast }!IPIndex {
|
|
|
|
_ = node_idx;
|
|
|
|
// TODO return errors
|
|
|
|
return try interpreter.ip.cast(interpreter.allocator, destination_ty, source_ty, builtin.target);
|
2022-10-31 05:51:51 +00:00
|
|
|
}
|
|
|
|
|
2022-10-28 04:59:24 +01:00
|
|
|
// Might be useful in the future
|
|
|
|
pub const InterpretOptions = struct {};
|
|
|
|
|
2022-10-28 06:22:03 +01:00
|
|
|
pub const InterpretError = std.mem.Allocator.Error || std.fmt.ParseIntError || std.fmt.ParseFloatError || error{
|
|
|
|
InvalidCharacter,
|
|
|
|
InvalidBase,
|
|
|
|
ExpectedValue,
|
|
|
|
InvalidOperation,
|
|
|
|
CriticalAstFailure,
|
|
|
|
InvalidBuiltin,
|
2022-10-28 19:24:38 +01:00
|
|
|
IdentifierNotFound,
|
2022-10-29 22:28:44 +01:00
|
|
|
MissingArguments,
|
|
|
|
ImportFailure,
|
2022-10-31 05:51:51 +00:00
|
|
|
InvalidCast,
|
2022-10-28 06:22:03 +01:00
|
|
|
};
|
2022-12-27 01:46:57 +00:00
|
|
|
|
2022-10-28 04:59:24 +01:00
|
|
|
pub fn interpret(
|
|
|
|
interpreter: *ComptimeInterpreter,
|
|
|
|
node_idx: Ast.Node.Index,
|
2022-12-27 02:02:07 +00:00
|
|
|
scope: u32,
|
2022-10-28 04:59:24 +01:00
|
|
|
options: InterpretOptions,
|
|
|
|
) InterpretError!InterpretResult {
|
2022-11-11 01:51:02 +00:00
|
|
|
const tree = interpreter.getHandle().tree;
|
2022-10-28 04:59:24 +01:00
|
|
|
const tags = tree.nodes.items(.tag);
|
|
|
|
const data = tree.nodes.items(.data);
|
|
|
|
const main_tokens = tree.nodes.items(.main_token);
|
|
|
|
|
|
|
|
switch (tags[node_idx]) {
|
|
|
|
.container_decl,
|
|
|
|
.container_decl_trailing,
|
|
|
|
.container_decl_arg,
|
|
|
|
.container_decl_arg_trailing,
|
|
|
|
.container_decl_two,
|
|
|
|
.container_decl_two_trailing,
|
2022-10-29 06:46:22 +01:00
|
|
|
// .tagged_union, // TODO: Fix these
|
|
|
|
// .tagged_union_trailing,
|
|
|
|
// .tagged_union_two,
|
|
|
|
// .tagged_union_two_trailing,
|
|
|
|
// .tagged_union_enum_tag,
|
|
|
|
// .tagged_union_enum_tag_trailing,
|
2022-10-28 04:59:24 +01:00
|
|
|
.root,
|
2022-12-27 01:46:57 +00:00
|
|
|
// .error_set_decl, // TODO
|
2022-10-28 04:59:24 +01:00
|
|
|
=> {
|
2022-12-27 01:46:57 +00:00
|
|
|
const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type });
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2022-12-27 02:02:07 +00:00
|
|
|
try interpreter.scopes.append(interpreter.allocator, .{
|
2022-12-27 01:46:57 +00:00
|
|
|
.interpreter = interpreter,
|
2022-12-27 02:02:07 +00:00
|
|
|
.parent = scope,
|
2022-12-27 01:46:57 +00:00
|
|
|
.node_idx = node_idx,
|
2023-01-04 10:11:48 +00:00
|
|
|
.namespace = .none, // declarations have not been resolved yet
|
2022-12-27 02:02:07 +00:00
|
|
|
});
|
|
|
|
const container_scope = @intCast(u32, interpreter.scopes.len - 1);
|
2022-12-01 23:08:45 +00:00
|
|
|
|
2023-01-06 13:12:29 +00:00
|
|
|
var fields = std.ArrayListUnmanaged(InternPool.Struct.Field){};
|
2022-10-28 04:59:24 +01:00
|
|
|
|
|
|
|
var buffer: [2]Ast.Node.Index = undefined;
|
|
|
|
const members = ast.declMembers(tree, node_idx, &buffer);
|
|
|
|
for (members) |member| {
|
2023-01-04 10:11:48 +00:00
|
|
|
const container_field = ast.containerField(tree, member) orelse {
|
2022-12-27 01:46:57 +00:00
|
|
|
_ = try interpreter.interpret(member, container_scope, options);
|
|
|
|
continue;
|
|
|
|
};
|
2022-12-01 23:08:45 +00:00
|
|
|
|
2023-01-04 10:11:48 +00:00
|
|
|
var init_type_value = try (try interpreter.interpret(container_field.ast.type_expr, container_scope, .{})).getValue();
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2023-01-04 10:11:48 +00:00
|
|
|
var default_value = if (container_field.ast.value_expr == 0)
|
2022-12-27 01:46:57 +00:00
|
|
|
IPIndex.none
|
|
|
|
else
|
2023-01-04 10:11:48 +00:00
|
|
|
(try (try interpreter.interpret(container_field.ast.value_expr, container_scope, .{})).getValue()).val; // TODO check ty
|
2022-12-27 01:46:57 +00:00
|
|
|
|
|
|
|
if (init_type_value.ty != type_type) {
|
|
|
|
try interpreter.recordError(
|
2023-01-04 10:11:48 +00:00
|
|
|
container_field.ast.type_expr,
|
2022-12-27 01:46:57 +00:00
|
|
|
"expected_type",
|
|
|
|
try std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{}'", .{init_type_value.ty.fmtType(&interpreter.ip)}),
|
|
|
|
);
|
|
|
|
continue;
|
2022-10-28 04:59:24 +01:00
|
|
|
}
|
2022-12-27 01:46:57 +00:00
|
|
|
|
|
|
|
const field: InternPool.Struct.Field = .{
|
2023-01-06 13:12:29 +00:00
|
|
|
.name = tree.tokenSlice(container_field.ast.main_token),
|
2022-12-27 01:46:57 +00:00
|
|
|
.ty = init_type_value.val,
|
|
|
|
.default_value = default_value,
|
|
|
|
.alignent = 0, // TODO,
|
|
|
|
.is_comptime = false, // TODO
|
|
|
|
};
|
|
|
|
|
2023-01-06 13:12:29 +00:00
|
|
|
try fields.append(interpreter.arena.allocator(), field);
|
2022-10-28 04:59:24 +01:00
|
|
|
}
|
|
|
|
|
2022-12-01 23:08:45 +00:00
|
|
|
const struct_type = try interpreter.ip.get(interpreter.allocator, IPKey{
|
|
|
|
.struct_type = .{
|
2023-01-06 13:12:29 +00:00
|
|
|
.fields = fields.items,
|
2023-01-04 10:11:48 +00:00
|
|
|
.namespace = .none, // TODO
|
|
|
|
.layout = .Auto, // TODO
|
|
|
|
.backing_int_ty = .none, // TODO
|
2022-12-01 23:08:45 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2022-10-28 04:59:24 +01:00
|
|
|
return InterpretResult{ .value = Value{
|
2022-11-01 03:36:13 +00:00
|
|
|
.interpreter = interpreter,
|
2022-10-28 04:59:24 +01:00
|
|
|
.node_idx = node_idx,
|
2022-12-01 23:08:45 +00:00
|
|
|
.ty = type_type,
|
|
|
|
.val = struct_type,
|
2022-10-28 04:59:24 +01:00
|
|
|
} };
|
|
|
|
},
|
|
|
|
.global_var_decl,
|
|
|
|
.local_var_decl,
|
|
|
|
.aligned_var_decl,
|
|
|
|
.simple_var_decl,
|
|
|
|
=> {
|
2022-10-29 22:28:44 +01:00
|
|
|
// TODO: Add 0 check
|
2022-12-27 01:46:57 +00:00
|
|
|
// const name = analysis.getDeclName(tree, node_idx).?;
|
|
|
|
// if (scope.?.declarations.contains(name))
|
|
|
|
// return InterpretResult{ .nothing = {} };
|
2022-10-29 22:28:44 +01:00
|
|
|
|
2022-12-27 01:46:57 +00:00
|
|
|
// const decl = ast.varDecl(tree, node_idx).?;
|
|
|
|
// if (decl.ast.init_node == 0)
|
|
|
|
// return InterpretResult{ .nothing = {} };
|
2022-10-29 22:28:44 +01:00
|
|
|
|
2022-12-27 01:46:57 +00:00
|
|
|
// try scope.?.declarations.put(interpreter.allocator, name, .{
|
|
|
|
// .scope = scope.?,
|
|
|
|
// .node_idx = node_idx,
|
|
|
|
// .name = name,
|
|
|
|
// });
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2022-11-11 01:51:02 +00:00
|
|
|
// TODO: Am I a dumbo shrimp? (e.g. is this tree shaking correct? works on my machine so like...)
|
|
|
|
|
|
|
|
// if (scope.?.scopeKind() != .container) {
|
2022-12-27 01:46:57 +00:00
|
|
|
// if (scope.?.node_idx != 0)
|
|
|
|
// _ = try scope.?.declarations.getPtr(name).?.getValue();
|
2022-11-01 03:36:13 +00:00
|
|
|
|
2022-10-31 05:51:51 +00:00
|
|
|
return InterpretResult{ .nothing = {} };
|
2022-10-28 04:59:24 +01:00
|
|
|
},
|
|
|
|
.block,
|
|
|
|
.block_semicolon,
|
|
|
|
.block_two,
|
|
|
|
.block_two_semicolon,
|
|
|
|
=> {
|
2022-12-27 02:02:07 +00:00
|
|
|
try interpreter.scopes.append(interpreter.allocator, .{
|
2022-12-27 01:46:57 +00:00
|
|
|
.interpreter = interpreter,
|
2022-12-27 02:02:07 +00:00
|
|
|
.parent = scope,
|
2022-12-27 01:46:57 +00:00
|
|
|
.node_idx = node_idx,
|
2023-01-04 10:11:48 +00:00
|
|
|
.namespace = .none,
|
2022-12-27 02:02:07 +00:00
|
|
|
});
|
|
|
|
const block_scope = @intCast(u32, interpreter.scopes.len - 1);
|
2022-10-28 04:59:24 +01:00
|
|
|
|
|
|
|
var buffer: [2]Ast.Node.Index = undefined;
|
|
|
|
const statements = ast.blockStatements(tree, node_idx, &buffer).?;
|
|
|
|
|
|
|
|
for (statements) |idx| {
|
|
|
|
const ret = try interpreter.interpret(idx, block_scope, options);
|
|
|
|
switch (ret) {
|
|
|
|
.@"break" => |lllll| {
|
2022-12-27 02:02:07 +00:00
|
|
|
const maybe_block_label_string = if (interpreter.scopes.get(scope).getLabel()) |i| tree.tokenSlice(i) else null;
|
2022-10-28 04:59:24 +01:00
|
|
|
if (lllll) |l| {
|
|
|
|
if (maybe_block_label_string) |ls| {
|
|
|
|
if (std.mem.eql(u8, l, ls)) {
|
2022-10-31 05:51:51 +00:00
|
|
|
return InterpretResult{ .nothing = {} };
|
2022-10-28 04:59:24 +01:00
|
|
|
} else return ret;
|
|
|
|
} else return ret;
|
|
|
|
} else {
|
2022-10-31 05:51:51 +00:00
|
|
|
return InterpretResult{ .nothing = {} };
|
2022-10-28 04:59:24 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
.break_with_value => |bwv| {
|
2022-12-27 02:02:07 +00:00
|
|
|
const maybe_block_label_string = if (interpreter.scopes.get(scope).getLabel()) |i| tree.tokenSlice(i) else null;
|
2022-10-28 04:59:24 +01:00
|
|
|
|
|
|
|
if (bwv.label) |l| {
|
|
|
|
if (maybe_block_label_string) |ls| {
|
|
|
|
if (std.mem.eql(u8, l, ls)) {
|
|
|
|
return InterpretResult{ .value = bwv.value };
|
|
|
|
} else return ret;
|
|
|
|
} else return ret;
|
|
|
|
} else {
|
|
|
|
return InterpretResult{ .value = bwv.value };
|
|
|
|
}
|
|
|
|
},
|
|
|
|
.@"return", .return_with_value => return ret,
|
|
|
|
else => {},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-31 05:51:51 +00:00
|
|
|
return InterpretResult{ .nothing = {} };
|
2022-10-28 04:59:24 +01:00
|
|
|
},
|
|
|
|
.identifier => {
|
|
|
|
var value = tree.getNodeSource(node_idx);
|
|
|
|
|
2022-10-28 06:22:03 +01:00
|
|
|
if (std.mem.eql(u8, "bool", value)) return InterpretResult{ .value = Value{
|
2022-11-01 03:36:13 +00:00
|
|
|
.interpreter = interpreter,
|
2022-10-28 06:22:03 +01:00
|
|
|
.node_idx = node_idx,
|
2022-12-01 23:08:45 +00:00
|
|
|
.ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }),
|
|
|
|
.val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }),
|
2022-10-28 06:22:03 +01:00
|
|
|
} };
|
|
|
|
if (std.mem.eql(u8, "true", value)) return InterpretResult{ .value = Value{
|
2022-11-01 03:36:13 +00:00
|
|
|
.interpreter = interpreter,
|
2022-10-28 06:22:03 +01:00
|
|
|
.node_idx = node_idx,
|
2022-12-01 23:08:45 +00:00
|
|
|
.ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }),
|
|
|
|
.val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_true }),
|
2022-10-28 06:22:03 +01:00
|
|
|
} };
|
|
|
|
if (std.mem.eql(u8, "false", value)) return InterpretResult{ .value = Value{
|
2022-11-01 03:36:13 +00:00
|
|
|
.interpreter = interpreter,
|
2022-10-28 06:22:03 +01:00
|
|
|
.node_idx = node_idx,
|
2022-12-01 23:08:45 +00:00
|
|
|
.ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }),
|
|
|
|
.val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_false }),
|
2022-10-28 06:22:03 +01:00
|
|
|
} };
|
|
|
|
|
2022-10-30 08:07:49 +00:00
|
|
|
if (value.len == 5 and (value[0] == 'u' or value[0] == 'i') and std.mem.eql(u8, "size", value[1..])) return InterpretResult{
|
|
|
|
.value = Value{
|
2022-11-01 03:36:13 +00:00
|
|
|
.interpreter = interpreter,
|
2022-10-30 08:07:49 +00:00
|
|
|
.node_idx = node_idx,
|
2022-12-01 23:08:45 +00:00
|
|
|
.ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }),
|
|
|
|
.val = try interpreter.ip.get(interpreter.allocator, IPKey{
|
|
|
|
.simple = if (value[0] == 'u') .usize else .isize,
|
2022-10-31 20:00:02 +00:00
|
|
|
}),
|
2022-10-30 08:07:49 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2022-10-28 04:59:24 +01:00
|
|
|
if (std.mem.eql(u8, "type", value)) {
|
|
|
|
return InterpretResult{ .value = Value{
|
2022-11-01 03:36:13 +00:00
|
|
|
.interpreter = interpreter,
|
2022-10-28 04:59:24 +01:00
|
|
|
.node_idx = node_idx,
|
2022-12-01 23:08:45 +00:00
|
|
|
.ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }),
|
|
|
|
.val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }),
|
2022-10-28 04:59:24 +01:00
|
|
|
} };
|
|
|
|
} else if (value.len >= 2 and (value[0] == 'u' or value[0] == 'i')) int: {
|
|
|
|
return InterpretResult{ .value = Value{
|
2022-11-01 03:36:13 +00:00
|
|
|
.interpreter = interpreter,
|
2022-10-28 04:59:24 +01:00
|
|
|
.node_idx = node_idx,
|
2022-12-01 23:08:45 +00:00
|
|
|
.ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }),
|
|
|
|
.val = try interpreter.ip.get(interpreter.allocator, IPKey{ .int_type = .{
|
|
|
|
.signedness = if (value[0] == 'u') .unsigned else .signed,
|
|
|
|
.bits = std.fmt.parseInt(u16, value[1..], 10) catch break :int,
|
|
|
|
} }),
|
2022-10-28 04:59:24 +01:00
|
|
|
} };
|
|
|
|
}
|
|
|
|
|
2022-10-28 06:22:03 +01:00
|
|
|
// TODO: Floats
|
|
|
|
|
2022-10-28 04:59:24 +01:00
|
|
|
// Logic to find identifiers in accessible scopes
|
2022-12-27 02:02:07 +00:00
|
|
|
const namespace = interpreter.scopes.items(.namespace)[scope];
|
|
|
|
const decl = interpreter.huntItDown(namespace, value, options) catch |err| {
|
2022-10-30 08:07:49 +00:00
|
|
|
if (err == error.IdentifierNotFound) try interpreter.recordError(
|
|
|
|
node_idx,
|
2022-11-08 20:54:30 +00:00
|
|
|
"undeclared_identifier",
|
|
|
|
try std.fmt.allocPrint(interpreter.allocator, "use of undeclared identifier '{s}'", .{value}),
|
2022-10-30 08:07:49 +00:00
|
|
|
);
|
|
|
|
return err;
|
2022-12-27 01:46:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
return InterpretResult{ .value = Value{
|
|
|
|
.interpreter = interpreter,
|
|
|
|
.node_idx = node_idx,
|
|
|
|
.ty = decl.ty,
|
|
|
|
.val = decl.val,
|
|
|
|
} };
|
2022-10-28 04:59:24 +01:00
|
|
|
},
|
2022-10-29 06:46:22 +01:00
|
|
|
.field_access => {
|
|
|
|
if (data[node_idx].rhs == 0) return error.CriticalAstFailure;
|
2022-12-15 22:01:42 +00:00
|
|
|
const rhs_str = tree.tokenSlice(data[node_idx].rhs);
|
2022-10-29 06:46:22 +01:00
|
|
|
|
|
|
|
var ir = try interpreter.interpret(data[node_idx].lhs, scope, options);
|
|
|
|
var irv = try ir.getValue();
|
|
|
|
|
2023-01-04 10:11:48 +00:00
|
|
|
const namespace = interpreter.ip.indexToKey(irv.val).getNamespace();
|
2022-12-27 01:46:57 +00:00
|
|
|
|
|
|
|
var scope_sub_decl = irv.interpreter.huntItDown(namespace, rhs_str, options) catch |err| {
|
2022-10-30 08:07:49 +00:00
|
|
|
if (err == error.IdentifierNotFound) try interpreter.recordError(
|
|
|
|
node_idx,
|
2022-11-08 20:54:30 +00:00
|
|
|
"undeclared_identifier",
|
|
|
|
try std.fmt.allocPrint(interpreter.allocator, "use of undeclared identifier '{s}'", .{rhs_str}),
|
2022-10-30 08:07:49 +00:00
|
|
|
);
|
|
|
|
return err;
|
|
|
|
};
|
2022-10-29 06:46:22 +01:00
|
|
|
|
2022-12-27 01:46:57 +00:00
|
|
|
return InterpretResult{ .value = Value{
|
|
|
|
.interpreter = interpreter,
|
|
|
|
.node_idx = data[node_idx].rhs,
|
|
|
|
.ty = scope_sub_decl.ty,
|
|
|
|
.val = scope_sub_decl.val,
|
|
|
|
} };
|
2022-10-29 06:46:22 +01:00
|
|
|
},
|
2022-10-28 04:59:24 +01:00
|
|
|
.grouped_expression => {
|
|
|
|
return try interpreter.interpret(data[node_idx].lhs, scope, options);
|
|
|
|
},
|
|
|
|
.@"break" => {
|
|
|
|
const label = if (data[node_idx].lhs == 0) null else tree.tokenSlice(data[node_idx].lhs);
|
|
|
|
return if (data[node_idx].rhs == 0)
|
|
|
|
InterpretResult{ .@"break" = label }
|
|
|
|
else
|
2022-10-28 06:22:03 +01:00
|
|
|
InterpretResult{ .break_with_value = .{ .label = label, .value = try (try interpreter.interpret(data[node_idx].rhs, scope, options)).getValue() } };
|
2022-10-28 04:59:24 +01:00
|
|
|
},
|
|
|
|
.@"return" => {
|
|
|
|
return if (data[node_idx].lhs == 0)
|
|
|
|
InterpretResult{ .@"return" = {} }
|
|
|
|
else
|
2022-10-28 06:22:03 +01:00
|
|
|
InterpretResult{ .return_with_value = try (try interpreter.interpret(data[node_idx].lhs, scope, options)).getValue() };
|
2022-10-28 04:59:24 +01:00
|
|
|
},
|
|
|
|
.@"if", .if_simple => {
|
|
|
|
const iff = ast.ifFull(tree, node_idx);
|
|
|
|
// TODO: Don't evaluate runtime ifs
|
|
|
|
// if (options.observe_values) {
|
|
|
|
const ir = try interpreter.interpret(iff.ast.cond_expr, scope, options);
|
2022-12-27 01:46:57 +00:00
|
|
|
|
|
|
|
const false_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_false });
|
|
|
|
const true_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_true });
|
|
|
|
|
|
|
|
const condition = (try ir.getValue()).val;
|
|
|
|
std.debug.assert(condition == false_value or condition == true_value);
|
|
|
|
if (condition == true_value) {
|
2022-10-28 04:59:24 +01:00
|
|
|
return try interpreter.interpret(iff.ast.then_expr, scope, options);
|
|
|
|
} else {
|
|
|
|
if (iff.ast.else_expr != 0) {
|
|
|
|
return try interpreter.interpret(iff.ast.else_expr, scope, options);
|
2022-10-31 05:51:51 +00:00
|
|
|
} else return InterpretResult{ .nothing = {} };
|
2022-10-28 04:59:24 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
.equal_equal => {
|
|
|
|
var a = try interpreter.interpret(data[node_idx].lhs, scope, options);
|
|
|
|
var b = try interpreter.interpret(data[node_idx].rhs, scope, options);
|
2022-12-01 23:08:45 +00:00
|
|
|
return InterpretResult{
|
|
|
|
.value = Value{
|
|
|
|
.interpreter = interpreter,
|
|
|
|
.node_idx = node_idx,
|
|
|
|
.ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }),
|
2022-12-27 01:46:57 +00:00
|
|
|
.val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = if (a.value.val == b.value.val) .bool_true else .bool_false }), // TODO eql function required?
|
2022-12-01 23:08:45 +00:00
|
|
|
},
|
|
|
|
};
|
2022-12-27 01:46:57 +00:00
|
|
|
|
2022-10-28 04:59:24 +01:00
|
|
|
// a.getValue().eql(b.getValue())
|
|
|
|
},
|
|
|
|
.number_literal => {
|
|
|
|
const s = tree.getNodeSource(node_idx);
|
|
|
|
const nl = std.zig.parseNumberLiteral(s);
|
2022-10-31 05:51:51 +00:00
|
|
|
|
2022-12-01 23:08:45 +00:00
|
|
|
if (nl == .failure) return error.CriticalAstFailure;
|
|
|
|
|
|
|
|
const comptime_int_type = try interpreter.ip.get(interpreter.allocator, IPKey{
|
|
|
|
.simple = if (nl == .float) .comptime_float else .comptime_int,
|
|
|
|
});
|
|
|
|
|
|
|
|
const value = try interpreter.ip.get(
|
|
|
|
interpreter.allocator,
|
|
|
|
switch (nl) {
|
|
|
|
.float => IPKey{
|
|
|
|
.float_64_value = try std.fmt.parseFloat(f64, s), // shouldn't this be f128?
|
|
|
|
},
|
|
|
|
.int => if (s[0] == '-') IPKey{
|
|
|
|
.int_i64_value = try std.fmt.parseInt(i64, s, 0),
|
|
|
|
} else IPKey{
|
|
|
|
.int_u64_value = try std.fmt.parseInt(u64, s, 0),
|
|
|
|
},
|
|
|
|
.big_int => @panic("TODO: implement big int"),
|
|
|
|
.failure => return error.CriticalAstFailure,
|
2022-10-28 04:59:24 +01:00
|
|
|
},
|
2022-12-01 23:08:45 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
return InterpretResult{ .value = Value{
|
|
|
|
.interpreter = interpreter,
|
|
|
|
.node_idx = node_idx,
|
|
|
|
.ty = comptime_int_type,
|
|
|
|
.val = value,
|
|
|
|
} };
|
2022-10-28 04:59:24 +01:00
|
|
|
},
|
|
|
|
.assign,
|
|
|
|
.assign_bit_and,
|
|
|
|
.assign_bit_or,
|
|
|
|
.assign_shl,
|
|
|
|
.assign_shr,
|
|
|
|
.assign_bit_xor,
|
|
|
|
.assign_div,
|
|
|
|
.assign_sub,
|
|
|
|
.assign_sub_wrap,
|
|
|
|
.assign_mod,
|
|
|
|
.assign_add,
|
|
|
|
.assign_add_wrap,
|
|
|
|
.assign_mul,
|
|
|
|
.assign_mul_wrap,
|
|
|
|
=> {
|
|
|
|
// TODO: Actually consider operators
|
|
|
|
|
2022-11-08 20:54:30 +00:00
|
|
|
if (std.mem.eql(u8, tree.getNodeSource(data[node_idx].lhs), "_")) {
|
2022-12-27 02:02:07 +00:00
|
|
|
_ = try interpreter.interpret(data[node_idx].rhs, scope, options);
|
2022-11-08 20:54:30 +00:00
|
|
|
return InterpretResult{ .nothing = {} };
|
|
|
|
}
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2022-10-31 20:00:02 +00:00
|
|
|
var ir = try interpreter.interpret(data[node_idx].lhs, scope, options);
|
|
|
|
var to_value = try ir.getValue();
|
2022-12-27 02:02:07 +00:00
|
|
|
var from_value = (try (try interpreter.interpret(data[node_idx].rhs, scope, options)).getValue());
|
2022-10-31 20:00:02 +00:00
|
|
|
|
2022-12-27 01:46:57 +00:00
|
|
|
_ = try interpreter.cast(undefined, to_value.ty, from_value.ty);
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2022-10-31 05:51:51 +00:00
|
|
|
return InterpretResult{ .nothing = {} };
|
2022-10-28 04:59:24 +01:00
|
|
|
},
|
|
|
|
// .@"switch",
|
|
|
|
// .switch_comma,
|
|
|
|
// => {
|
|
|
|
// const cond = data[node_idx].lhs;
|
|
|
|
// const extra = tree.extraData(data[node_idx].rhs, Ast.Node.SubRange);
|
|
|
|
// const cases = tree.extra_data[extra.start..extra.end];
|
|
|
|
|
|
|
|
// for (cases) |case| {
|
|
|
|
// const switch_case: Ast.full.SwitchCase = switch (tags[case]) {
|
|
|
|
// .switch_case => tree.switchCase(case),
|
|
|
|
// .switch_case_one => tree.switchCaseOne(case),
|
|
|
|
// else => continue,
|
|
|
|
// };
|
|
|
|
// }
|
|
|
|
// },
|
|
|
|
.builtin_call,
|
|
|
|
.builtin_call_comma,
|
|
|
|
.builtin_call_two,
|
|
|
|
.builtin_call_two_comma,
|
|
|
|
=> {
|
|
|
|
var buffer: [2]Ast.Node.Index = undefined;
|
|
|
|
const params = ast.builtinCallParams(tree, node_idx, &buffer).?;
|
|
|
|
const call_name = tree.tokenSlice(main_tokens[node_idx]);
|
|
|
|
|
|
|
|
if (std.mem.eql(u8, call_name, "@compileLog")) {
|
2022-10-31 20:00:02 +00:00
|
|
|
var final = std.ArrayList(u8).init(interpreter.allocator);
|
|
|
|
var writer = final.writer();
|
|
|
|
try writer.writeAll("log: ");
|
|
|
|
|
|
|
|
for (params) |param, index| {
|
|
|
|
var value = (try interpreter.interpret(param, scope, options)).maybeGetValue() orelse {
|
|
|
|
try writer.writeAll("indeterminate");
|
|
|
|
continue;
|
|
|
|
};
|
2022-12-27 02:02:07 +00:00
|
|
|
try writer.print("@as({}, {})", .{ value.ty.fmtType(&interpreter.ip), value.val.fmtValue(value.ty, &interpreter.ip) });
|
2022-10-31 20:00:02 +00:00
|
|
|
if (index != params.len - 1)
|
|
|
|
try writer.writeAll(", ");
|
|
|
|
}
|
2022-12-02 20:14:58 +00:00
|
|
|
try interpreter.recordError(node_idx, "compile_log", try final.toOwnedSlice());
|
2022-10-31 20:00:02 +00:00
|
|
|
|
2022-10-31 05:51:51 +00:00
|
|
|
return InterpretResult{ .nothing = {} };
|
2022-10-28 04:59:24 +01:00
|
|
|
}
|
|
|
|
|
2022-10-28 19:24:38 +01:00
|
|
|
if (std.mem.eql(u8, call_name, "@compileError")) {
|
2022-10-31 20:00:02 +00:00
|
|
|
// TODO: Add message
|
|
|
|
try interpreter.recordError(node_idx, "compile_error", try std.fmt.allocPrint(interpreter.allocator, "compile error", .{}));
|
2022-10-31 05:51:51 +00:00
|
|
|
return InterpretResult{ .@"return" = {} };
|
2022-10-28 19:24:38 +01:00
|
|
|
}
|
|
|
|
|
2022-10-29 22:28:44 +01:00
|
|
|
if (std.mem.eql(u8, call_name, "@import")) {
|
|
|
|
if (params.len == 0) return error.InvalidBuiltin;
|
|
|
|
const import_param = params[0];
|
|
|
|
if (tags[import_param] != .string_literal) return error.InvalidBuiltin;
|
|
|
|
|
|
|
|
const import_str = tree.tokenSlice(main_tokens[import_param]);
|
|
|
|
|
2022-11-11 01:51:02 +00:00
|
|
|
log.info("Resolving {s} from {s}", .{ import_str[1 .. import_str.len - 1], interpreter.uri });
|
|
|
|
|
|
|
|
// TODO: Implement root support
|
|
|
|
if (std.mem.eql(u8, import_str[1 .. import_str.len - 1], "root")) {
|
|
|
|
return InterpretResult{ .value = Value{
|
|
|
|
.interpreter = interpreter,
|
|
|
|
.node_idx = node_idx,
|
2022-12-27 01:46:57 +00:00
|
|
|
.ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .struct_type = .{
|
2023-01-06 13:12:29 +00:00
|
|
|
.fields = &.{},
|
2023-01-04 10:11:48 +00:00
|
|
|
.namespace = .none,
|
2022-12-27 01:46:57 +00:00
|
|
|
.layout = .Auto,
|
|
|
|
.backing_int_ty = IPIndex.none,
|
|
|
|
} }),
|
|
|
|
.val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .undefined_value }),
|
2022-11-11 01:51:02 +00:00
|
|
|
} };
|
|
|
|
}
|
|
|
|
|
|
|
|
var import_uri = (try interpreter.document_store.uriFromImportStr(interpreter.allocator, interpreter.getHandle().*, import_str[1 .. import_str.len - 1])) orelse return error.ImportFailure;
|
2022-10-29 22:28:44 +01:00
|
|
|
defer interpreter.allocator.free(import_uri);
|
|
|
|
|
|
|
|
var handle = interpreter.document_store.getOrLoadHandle(import_uri) orelse return error.ImportFailure;
|
|
|
|
try interpreter.document_store.ensureInterpreterExists(handle.uri);
|
|
|
|
|
2022-12-27 02:02:07 +00:00
|
|
|
return InterpretResult{
|
|
|
|
.value = Value{
|
|
|
|
.interpreter = interpreter,
|
|
|
|
.node_idx = node_idx,
|
|
|
|
.ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }),
|
2023-01-06 13:12:29 +00:00
|
|
|
.val = undefined, // TODO
|
2022-12-27 02:02:07 +00:00
|
|
|
},
|
|
|
|
};
|
2022-10-29 22:28:44 +01:00
|
|
|
}
|
|
|
|
|
2022-10-30 08:07:49 +00:00
|
|
|
if (std.mem.eql(u8, call_name, "@TypeOf")) {
|
|
|
|
if (params.len != 1) return error.InvalidBuiltin;
|
|
|
|
|
|
|
|
const value = try (try interpreter.interpret(params[0], scope, options)).getValue();
|
|
|
|
return InterpretResult{ .value = Value{
|
2022-11-01 03:36:13 +00:00
|
|
|
.interpreter = interpreter,
|
2022-10-30 08:07:49 +00:00
|
|
|
.node_idx = node_idx,
|
2022-12-01 23:08:45 +00:00
|
|
|
.ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }),
|
2023-01-06 13:12:29 +00:00
|
|
|
.val = value.ty,
|
2022-10-30 08:07:49 +00:00
|
|
|
} };
|
|
|
|
}
|
|
|
|
|
|
|
|
if (std.mem.eql(u8, call_name, "@hasDecl")) {
|
|
|
|
if (params.len != 2) return error.InvalidBuiltin;
|
|
|
|
|
|
|
|
const value = try (try interpreter.interpret(params[0], scope, options)).getValue();
|
|
|
|
const field_name = try (try interpreter.interpret(params[1], scope, options)).getValue();
|
|
|
|
|
2022-12-27 01:46:57 +00:00
|
|
|
const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type });
|
|
|
|
|
|
|
|
if (value.ty != type_type) return error.InvalidBuiltin;
|
|
|
|
if (interpreter.ip.indexToKey(field_name.ty) != .pointer_type) return error.InvalidBuiltin; // Check if it's a []const u8
|
|
|
|
|
2023-01-04 10:11:48 +00:00
|
|
|
const namespace = interpreter.ip.indexToKey(value.val).getNamespace();
|
|
|
|
if (namespace == .none) return error.InvalidBuiltin;
|
2022-10-30 08:07:49 +00:00
|
|
|
|
2022-12-27 01:46:57 +00:00
|
|
|
const name = interpreter.ip.indexToKey(field_name.val).bytes.data; // TODO add checks
|
2022-10-30 08:07:49 +00:00
|
|
|
|
2023-01-04 10:11:48 +00:00
|
|
|
const decls = interpreter.namespaces.items(.decls)[@enumToInt(namespace)];
|
|
|
|
const has_decl = decls.contains(name);
|
2022-12-01 23:08:45 +00:00
|
|
|
|
2022-10-30 08:07:49 +00:00
|
|
|
return InterpretResult{ .value = Value{
|
2022-11-01 03:36:13 +00:00
|
|
|
.interpreter = interpreter,
|
2022-10-30 08:07:49 +00:00
|
|
|
.node_idx = node_idx,
|
2022-12-01 23:08:45 +00:00
|
|
|
.ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }),
|
|
|
|
.val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = if (has_decl) .bool_true else .bool_false }),
|
2022-10-30 08:07:49 +00:00
|
|
|
} };
|
|
|
|
}
|
|
|
|
|
2022-10-31 05:51:51 +00:00
|
|
|
if (std.mem.eql(u8, call_name, "@as")) {
|
|
|
|
if (params.len != 2) return error.InvalidBuiltin;
|
|
|
|
|
|
|
|
const as_type = try (try interpreter.interpret(params[0], scope, options)).getValue();
|
|
|
|
const value = try (try interpreter.interpret(params[1], scope, options)).getValue();
|
|
|
|
|
2022-12-27 01:46:57 +00:00
|
|
|
const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type });
|
2022-10-31 05:51:51 +00:00
|
|
|
|
2022-12-27 01:46:57 +00:00
|
|
|
if (as_type.ty != type_type) return error.InvalidBuiltin;
|
|
|
|
|
|
|
|
return InterpretResult{ .value = Value{
|
|
|
|
.interpreter = interpreter,
|
|
|
|
.node_idx = node_idx,
|
|
|
|
.ty = type_type,
|
|
|
|
.val = try interpreter.cast(node_idx, as_type.val, value.val),
|
|
|
|
} };
|
2022-10-31 05:51:51 +00:00
|
|
|
}
|
|
|
|
|
2022-11-10 04:46:23 +00:00
|
|
|
log.err("Builtin not implemented: {s}", .{call_name});
|
2022-10-30 08:07:49 +00:00
|
|
|
return error.InvalidBuiltin;
|
2022-10-28 04:59:24 +01:00
|
|
|
},
|
|
|
|
.string_literal => {
|
2022-12-01 23:08:45 +00:00
|
|
|
const str = tree.getNodeSource(node_idx)[1 .. tree.getNodeSource(node_idx).len - 1];
|
|
|
|
|
|
|
|
const string_literal_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .pointer_type = .{
|
|
|
|
.elem_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .array_type = .{
|
|
|
|
.child = try interpreter.ip.get(interpreter.allocator, IPKey{ .int_type = .{
|
|
|
|
.signedness = .unsigned,
|
|
|
|
.bits = 8,
|
|
|
|
} }),
|
2022-12-27 01:46:57 +00:00
|
|
|
.len = @intCast(u32, str.len),
|
2022-12-01 23:08:45 +00:00
|
|
|
.sentinel = try interpreter.ip.get(interpreter.allocator, IPKey{ .int_u64_value = 0 }),
|
|
|
|
} }),
|
|
|
|
.sentinel = .none,
|
|
|
|
.alignment = 0,
|
2022-12-27 01:46:57 +00:00
|
|
|
.size = .One,
|
2022-12-01 23:08:45 +00:00
|
|
|
.is_const = true,
|
|
|
|
.is_volatile = false,
|
|
|
|
.is_allowzero = false,
|
|
|
|
.address_space = .generic,
|
|
|
|
} });
|
|
|
|
|
2022-10-28 04:59:24 +01:00
|
|
|
var val = Value{
|
2022-11-01 03:36:13 +00:00
|
|
|
.interpreter = interpreter,
|
2022-10-28 04:59:24 +01:00
|
|
|
.node_idx = node_idx,
|
2022-12-01 23:08:45 +00:00
|
|
|
.ty = string_literal_type,
|
2022-12-27 01:46:57 +00:00
|
|
|
.val = try interpreter.ip.get(interpreter.allocator, IPKey{ .bytes = .{ .data = str } }), // TODO
|
2022-10-28 04:59:24 +01:00
|
|
|
};
|
|
|
|
|
2022-10-30 08:07:49 +00:00
|
|
|
// TODO: Add type casting, sentinel
|
|
|
|
// TODO: Should this be a `*const [len:0]u8`?
|
|
|
|
// try val.value_data.slice_ptr.append(interpreter.allocator, .{ .unsigned_int = 0 });
|
2022-10-28 04:59:24 +01:00
|
|
|
|
|
|
|
return InterpretResult{ .value = val };
|
|
|
|
},
|
|
|
|
// TODO: Add comptime autodetection; e.g. const MyArrayList = std.ArrayList(u8)
|
|
|
|
.@"comptime" => {
|
|
|
|
return try interpreter.interpret(data[node_idx].lhs, scope, .{});
|
|
|
|
},
|
2023-01-04 10:11:48 +00:00
|
|
|
.fn_proto, .fn_proto_multi, .fn_proto_one, .fn_proto_simple, .fn_decl => {
|
|
|
|
var buf: [1]Ast.Node.Index = undefined;
|
|
|
|
const func = ast.fnProto(tree, node_idx, &buf).?;
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2022-12-01 23:08:45 +00:00
|
|
|
// TODO: Resolve function type
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2023-01-04 10:11:48 +00:00
|
|
|
const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type });
|
|
|
|
|
|
|
|
const function_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .function_type = .{
|
|
|
|
.calling_convention = .Unspecified,
|
|
|
|
.alignment = 0,
|
|
|
|
.is_generic = false,
|
|
|
|
.is_var_args = false,
|
|
|
|
.return_type = IPIndex.none,
|
|
|
|
.args = &.{},
|
|
|
|
} });
|
2022-10-28 04:59:24 +01:00
|
|
|
|
|
|
|
// var it = func.iterate(&tree);
|
|
|
|
// while (ast.nextFnParam(&it)) |param| {
|
|
|
|
// // Add parameter decls
|
|
|
|
// if (param.name_token) |name_token| {
|
|
|
|
// // TODO: Think of new method for functions
|
|
|
|
// if ((try interpreter.interpret(param.type_expr, func_scope_idx, .{ .observe_values = true, .is_comptime = true })).maybeGetValue()) |value| {
|
|
|
|
// try interpreter.addDeclaration(func_scope_idx, value.value_data.@"type");
|
|
|
|
// try fnd.params.append(interpreter.allocator, interpreter.declarations.items.len - 1);
|
|
|
|
// } else {
|
|
|
|
// try interpreter.addDeclaration(parent_scope_idx.?, .{
|
|
|
|
// .node_idx = node_idx,
|
|
|
|
// .name = tree.tokenSlice(name_token),
|
|
|
|
// .scope_idx = func_scope_idx, // orelse std.math.maxInt(usize),
|
|
|
|
// .@"value" = undefined,
|
|
|
|
// .@"type" = interpreter.createType(0, .{ .@"anytype" = .{} }),
|
|
|
|
// });
|
|
|
|
// try fnd.params.append(interpreter.allocator, interpreter.declarations.items.len - 1);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// if ((try interpreter.interpret(func.ast.return_type, func_scope_idx, .{ .observe_values = true, .is_comptime = true })).maybeGetValue()) |value|
|
|
|
|
// fnd.return_type = value.value_data.@"type";
|
|
|
|
|
2023-01-04 10:11:48 +00:00
|
|
|
const name = offsets.tokenToSlice(tree, func.name_token.?);
|
2022-12-27 01:46:57 +00:00
|
|
|
|
2023-01-04 10:11:48 +00:00
|
|
|
const namespace = interpreter.scopes.items(.namespace)[scope];
|
|
|
|
if (namespace != .none) {
|
|
|
|
const decls = &interpreter.namespaces.items(.decls)[@enumToInt(namespace)];
|
|
|
|
try decls.put(interpreter.allocator, name, .{
|
|
|
|
.name = name,
|
|
|
|
.ty = type_type,
|
|
|
|
.val = function_type,
|
|
|
|
.alignment = 0, // TODO
|
|
|
|
.address_space = .generic, // TODO
|
|
|
|
.is_pub = false, // TODO
|
|
|
|
.is_exported = false, // TODO
|
|
|
|
});
|
|
|
|
}
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2022-10-31 05:51:51 +00:00
|
|
|
return InterpretResult{ .nothing = {} };
|
2022-10-28 04:59:24 +01:00
|
|
|
},
|
|
|
|
.call,
|
|
|
|
.call_comma,
|
|
|
|
.async_call,
|
|
|
|
.async_call_comma,
|
|
|
|
.call_one,
|
|
|
|
.call_one_comma,
|
|
|
|
.async_call_one,
|
|
|
|
.async_call_one_comma,
|
|
|
|
=> {
|
2022-10-28 19:24:38 +01:00
|
|
|
var params: [1]Ast.Node.Index = undefined;
|
|
|
|
const call_full = ast.callFull(tree, node_idx, ¶ms) orelse unreachable;
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2022-10-28 19:24:38 +01:00
|
|
|
var args = try std.ArrayListUnmanaged(Value).initCapacity(interpreter.allocator, call_full.ast.params.len);
|
|
|
|
defer args.deinit(interpreter.allocator);
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2022-10-28 19:24:38 +01:00
|
|
|
for (call_full.ast.params) |param| {
|
2023-01-04 10:11:48 +00:00
|
|
|
args.appendAssumeCapacity(try (try interpreter.interpret(param, scope, .{})).getValue());
|
2022-10-28 19:24:38 +01:00
|
|
|
}
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2022-12-27 02:02:07 +00:00
|
|
|
const func_id_result = try interpreter.interpret(call_full.ast.fn_expr, scope, .{});
|
2022-10-29 06:46:22 +01:00
|
|
|
const func_id_val = try func_id_result.getValue();
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2022-12-27 02:02:07 +00:00
|
|
|
const call_res = try interpreter.call(scope, func_id_val.node_idx, args.items, options);
|
2022-10-28 19:24:38 +01:00
|
|
|
// defer call_res.scope.deinit();
|
2022-10-29 06:46:22 +01:00
|
|
|
// TODO: Figure out call result memory model; this is actually fine because newScope
|
|
|
|
// makes this a child of the decl scope which is freed on refresh... in theory
|
2022-10-28 19:24:38 +01:00
|
|
|
|
|
|
|
return switch (call_res.result) {
|
|
|
|
.value => |v| .{ .value = v },
|
|
|
|
.nothing => .{ .nothing = {} },
|
|
|
|
};
|
2022-10-28 04:59:24 +01:00
|
|
|
},
|
2022-10-28 06:22:03 +01:00
|
|
|
.bool_not => {
|
|
|
|
const result = try interpreter.interpret(data[node_idx].lhs, scope, .{});
|
2022-12-01 23:08:45 +00:00
|
|
|
const bool_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool });
|
|
|
|
const value = try result.getValue();
|
2022-12-27 01:46:57 +00:00
|
|
|
if (value.ty == bool_type) {
|
2022-12-01 23:08:45 +00:00
|
|
|
const false_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_false });
|
|
|
|
const true_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_true });
|
|
|
|
|
2022-12-27 01:46:57 +00:00
|
|
|
const not_value = if (value.val == false_value) true_value else if (value.val == true_value) false_value else return error.InvalidOperation;
|
2022-12-01 23:08:45 +00:00
|
|
|
return InterpretResult{
|
|
|
|
.value = .{
|
|
|
|
.interpreter = interpreter,
|
|
|
|
.node_idx = node_idx,
|
|
|
|
.ty = bool_type,
|
|
|
|
.val = not_value,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
// TODO
|
|
|
|
return error.InvalidOperation;
|
|
|
|
}
|
2022-10-31 20:00:02 +00:00
|
|
|
},
|
|
|
|
.address_of => {
|
|
|
|
// TODO: Make const pointers if we're drawing from a const;
|
|
|
|
// variables are the only non-const(?)
|
|
|
|
|
|
|
|
const result = try interpreter.interpret(data[node_idx].lhs, scope, .{});
|
|
|
|
const value = (try result.getValue());
|
|
|
|
|
2022-12-01 23:08:45 +00:00
|
|
|
const pointer_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .pointer_type = .{
|
2022-12-27 01:46:57 +00:00
|
|
|
.elem_type = value.ty,
|
2022-12-01 23:08:45 +00:00
|
|
|
.sentinel = .none,
|
|
|
|
.alignment = 0,
|
2022-12-27 01:46:57 +00:00
|
|
|
.size = .One,
|
2022-12-01 23:08:45 +00:00
|
|
|
.is_const = false,
|
|
|
|
.is_volatile = false,
|
|
|
|
.is_allowzero = false,
|
|
|
|
.address_space = .generic,
|
|
|
|
} });
|
|
|
|
|
|
|
|
return InterpretResult{ .value = .{
|
|
|
|
.interpreter = interpreter,
|
|
|
|
.node_idx = node_idx,
|
|
|
|
.ty = pointer_type,
|
2023-01-06 13:12:29 +00:00
|
|
|
.val = value.val,
|
2022-12-01 23:08:45 +00:00
|
|
|
} };
|
2022-10-31 20:00:02 +00:00
|
|
|
},
|
|
|
|
.deref => {
|
|
|
|
const result = try interpreter.interpret(data[node_idx].lhs, scope, .{});
|
|
|
|
const value = (try result.getValue());
|
|
|
|
|
2022-12-01 23:08:45 +00:00
|
|
|
const type_key = interpreter.ip.indexToKey(value.ty);
|
2022-10-31 20:00:02 +00:00
|
|
|
|
2022-12-27 01:46:57 +00:00
|
|
|
if (type_key != .pointer_type) {
|
2022-10-31 20:00:02 +00:00
|
|
|
try interpreter.recordError(node_idx, "invalid_deref", try std.fmt.allocPrint(interpreter.allocator, "cannot deference non-pointer", .{}));
|
|
|
|
return error.InvalidOperation;
|
|
|
|
}
|
|
|
|
|
2022-12-01 23:08:45 +00:00
|
|
|
return InterpretResult{ .value = .{
|
|
|
|
.interpreter = interpreter,
|
|
|
|
.node_idx = node_idx,
|
|
|
|
.ty = type_key.pointer_type.elem_type,
|
2023-01-06 13:12:29 +00:00
|
|
|
.val = value.val,
|
2022-12-01 23:08:45 +00:00
|
|
|
} };
|
2022-10-28 06:22:03 +01:00
|
|
|
},
|
2022-10-28 04:59:24 +01:00
|
|
|
else => {
|
2022-11-10 04:46:23 +00:00
|
|
|
log.err("Unhandled {any}", .{tags[node_idx]});
|
2022-10-31 05:51:51 +00:00
|
|
|
return InterpretResult{ .nothing = {} };
|
2022-10-28 04:59:24 +01:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const CallResult = struct {
|
2022-12-27 02:02:07 +00:00
|
|
|
scope: u32,
|
2022-10-28 04:59:24 +01:00
|
|
|
result: union(enum) {
|
|
|
|
value: Value,
|
|
|
|
nothing,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn call(
|
|
|
|
interpreter: *ComptimeInterpreter,
|
2022-12-27 02:02:07 +00:00
|
|
|
scope: u32,
|
2022-10-28 04:59:24 +01:00
|
|
|
func_node_idx: Ast.Node.Index,
|
|
|
|
arguments: []const Value,
|
|
|
|
options: InterpretOptions,
|
|
|
|
) InterpretError!CallResult {
|
2022-11-08 20:54:30 +00:00
|
|
|
// _ = options;
|
2022-10-29 22:28:44 +01:00
|
|
|
|
|
|
|
// TODO: type check args
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2022-11-11 01:51:02 +00:00
|
|
|
const tree = interpreter.getHandle().tree;
|
2022-10-28 04:59:24 +01:00
|
|
|
const tags = tree.nodes.items(.tag);
|
|
|
|
|
2022-11-17 00:28:01 +00:00
|
|
|
if (tags[func_node_idx] != .fn_decl) return error.CriticalAstFailure;
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2022-11-01 03:36:13 +00:00
|
|
|
// TODO: Make argument scope to evaluate arguments in
|
2022-12-27 02:02:07 +00:00
|
|
|
try interpreter.scopes.append(interpreter.allocator, Scope{
|
|
|
|
.interpreter = interpreter,
|
|
|
|
.parent = scope,
|
|
|
|
.node_idx = func_node_idx,
|
2023-01-04 10:11:48 +00:00
|
|
|
.namespace = .none,
|
2022-12-27 02:02:07 +00:00
|
|
|
});
|
|
|
|
const fn_scope = @intCast(u32, interpreter.scopes.len - 1);
|
|
|
|
|
|
|
|
const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type });
|
2022-10-28 04:59:24 +01:00
|
|
|
|
2022-10-28 19:24:38 +01:00
|
|
|
var buf: [1]Ast.Node.Index = undefined;
|
|
|
|
var proto = ast.fnProto(tree, func_node_idx, &buf).?;
|
|
|
|
|
|
|
|
var arg_it = proto.iterate(&tree);
|
|
|
|
var arg_index: usize = 0;
|
|
|
|
while (ast.nextFnParam(&arg_it)) |param| {
|
2022-10-29 22:28:44 +01:00
|
|
|
if (arg_index >= arguments.len) return error.MissingArguments;
|
2022-11-08 20:54:30 +00:00
|
|
|
var tex = try (try interpreter.interpret(param.type_expr, fn_scope, options)).getValue();
|
2022-12-27 01:46:57 +00:00
|
|
|
if (tex.ty != type_type) {
|
2022-11-08 20:54:30 +00:00
|
|
|
try interpreter.recordError(
|
|
|
|
param.type_expr,
|
|
|
|
"expected_type",
|
2022-12-27 01:46:57 +00:00
|
|
|
std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{}'", .{tex.ty.fmtType(&interpreter.ip)}) catch return error.CriticalAstFailure,
|
2022-11-08 20:54:30 +00:00
|
|
|
);
|
|
|
|
return error.InvalidCast;
|
|
|
|
}
|
2022-10-28 19:24:38 +01:00
|
|
|
if (param.name_token) |nt| {
|
2022-12-27 02:02:07 +00:00
|
|
|
_ = nt;
|
|
|
|
// const decl = InternPool.Decl{
|
|
|
|
// .name = tree.tokenSlice(nt),
|
|
|
|
// .ty = tex.val,
|
|
|
|
// .val = try interpreter.cast(arguments[arg_index].node_idx, tex.val, arguments[arg_index].val),
|
|
|
|
// .alignment = 0, // TODO
|
|
|
|
// .address_space = .generic, // TODO
|
|
|
|
// .is_pub = true, // TODO
|
|
|
|
// .is_exported = false, // TODO
|
|
|
|
// };
|
|
|
|
// TODO
|
|
|
|
// try fn_scope.declarations.put(interpreter.allocator, tree.tokenSlice(nt), decl);
|
2022-10-28 19:24:38 +01:00
|
|
|
arg_index += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-28 04:59:24 +01:00
|
|
|
const body = tree.nodes.items(.data)[func_node_idx].rhs;
|
|
|
|
const result = try interpreter.interpret(body, fn_scope, .{});
|
|
|
|
|
|
|
|
// TODO: Defers
|
|
|
|
return CallResult{
|
|
|
|
.scope = fn_scope,
|
|
|
|
.result = switch (result) {
|
2022-11-08 20:54:30 +00:00
|
|
|
.@"return", .nothing => .{ .nothing = {} }, // nothing could be due to an error
|
2022-12-01 23:08:45 +00:00
|
|
|
.return_with_value => |v| .{ .value = v },
|
2022-10-28 04:59:24 +01:00
|
|
|
else => @panic("bruh"),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|