Field access, function calls based on function value

This commit is contained in:
Auguste Rame 2022-10-29 01:46:22 -04:00
parent 779c3c0710
commit da00751726
No known key found for this signature in database
GPG Key ID: 3A5E3F90DF2AAEFE
4 changed files with 121 additions and 48 deletions

View File

@ -13,14 +13,16 @@ const analysis = @import("analysis.zig");
const DocumentStore = @import("DocumentStore.zig"); const DocumentStore = @import("DocumentStore.zig");
const ComptimeInterpreter = @This(); const ComptimeInterpreter = @This();
tree: Ast,
root_scope: *InterpreterScope = undefined,
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
document_store: *DocumentStore,
handle: *const DocumentStore.Handle,
root_scope: ?*InterpreterScope = null,
type_info: std.ArrayListUnmanaged(TypeInfo) = .{}, type_info: std.ArrayListUnmanaged(TypeInfo) = .{},
type_info_map: std.HashMapUnmanaged(TypeInfo, usize, TypeInfo.Context, std.hash_map.default_max_load_percentage) = .{}, type_info_map: std.HashMapUnmanaged(TypeInfo, usize, TypeInfo.Context, std.hash_map.default_max_load_percentage) = .{},
pub fn deinit(interpreter: *ComptimeInterpreter) void { pub fn deinit(interpreter: *ComptimeInterpreter) void {
if (interpreter.root_scope) |rs| rs.deinit();
for (interpreter.type_info.items) |*ti| ti.deinit(interpreter.allocator); for (interpreter.type_info.items) |*ti| ti.deinit(interpreter.allocator);
interpreter.type_info.deinit(interpreter.allocator); interpreter.type_info.deinit(interpreter.allocator);
interpreter.type_info_map.deinit(interpreter.allocator); interpreter.type_info_map.deinit(interpreter.allocator);
@ -83,6 +85,7 @@ pub const TypeInfo = union(enum) {
@"struct": Struct, @"struct": Struct,
pointer: Pointer, pointer: Pointer,
@"fn": Fn,
int: Int, int: Int,
@"comptime_int", @"comptime_int",
@ -145,6 +148,13 @@ pub const TypeInfo = union(enum) {
else => {}, else => {},
} }
} }
pub fn getScopeOfType(ti: TypeInfo) ?*InterpreterScope {
return switch (ti) {
.@"struct" => |s| s.scope,
else => null,
};
}
}; };
pub const Type = struct { pub const Type = struct {
@ -180,6 +190,7 @@ pub const ValueData = union(enum) {
signed_int: i64, signed_int: i64,
float: f64, float: f64,
@"fn",
runtime, runtime,
comptime_undetermined, comptime_undetermined,
@ -233,7 +244,7 @@ pub const Declaration = struct {
.aligned_var_decl, .aligned_var_decl,
.simple_var_decl, .simple_var_decl,
=> { => {
return tree.tokenSlice(ast.varDecl(tree, declaration.node_idx).?.ast.mut_token).len == 3; return tree.tokenSlice(ast.varDecl(tree, declaration.node_idx).?.ast.mut_token).len != 3;
}, },
else => false, else => false,
}; };
@ -261,7 +272,7 @@ pub fn typeToTypeInfo(interpreter: ComptimeInterpreter, @"type": Type) TypeInfo
} }
pub const TypeInfoFormatter = struct { pub const TypeInfoFormatter = struct {
interpreter: *ComptimeInterpreter, interpreter: *const ComptimeInterpreter,
ti: TypeInfo, ti: TypeInfo,
pub fn format(value: TypeInfoFormatter, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { pub fn format(value: TypeInfoFormatter, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
@ -285,7 +296,7 @@ pub const TypeInfoFormatter = struct {
var iterator = s.scope.declarations.iterator(); var iterator = s.scope.declarations.iterator();
while (iterator.next()) |di| { while (iterator.next()) |di| {
const decl = di.value_ptr.*; const decl = di.value_ptr.*;
if (decl.isConstant(value.interpreter.tree)) { if (decl.isConstant(value.interpreter.handle.tree)) {
try writer.print("const {s}: {any} = TODO_PRINT_VALUES, ", .{ decl.name, value.interpreter.formatTypeInfo(value.interpreter.typeToTypeInfo(decl.@"type")) }); try writer.print("const {s}: {any} = TODO_PRINT_VALUES, ", .{ decl.name, value.interpreter.formatTypeInfo(value.interpreter.typeToTypeInfo(decl.@"type")) });
} else { } else {
try writer.print("var {s}: {any}, ", .{ decl.name, value.interpreter.formatTypeInfo(value.interpreter.typeToTypeInfo(decl.@"type")) }); try writer.print("var {s}: {any}, ", .{ decl.name, value.interpreter.formatTypeInfo(value.interpreter.typeToTypeInfo(decl.@"type")) });
@ -298,7 +309,7 @@ pub const TypeInfoFormatter = struct {
} }
}; };
pub fn formatTypeInfo(interpreter: *ComptimeInterpreter, ti: TypeInfo) TypeInfoFormatter { pub fn formatTypeInfo(interpreter: *const ComptimeInterpreter, ti: TypeInfo) TypeInfoFormatter {
return TypeInfoFormatter{ .interpreter = interpreter, .ti = ti }; return TypeInfoFormatter{ .interpreter = interpreter, .ti = ti };
} }
@ -365,11 +376,13 @@ pub const InterpreterScope = struct {
} }
pub fn deinit(scope: *InterpreterScope) void { pub fn deinit(scope: *InterpreterScope) void {
scope.declarations.deinit(scope.interpreter.allocator); const allocator = scope.interpreter.allocator;
for (scope.child_scopes.items) |child| child.deinit();
scope.child_scopes.deinit(scope.interpreter.allocator);
scope.interpreter.allocator.destroy(scope); scope.declarations.deinit(allocator);
for (scope.child_scopes.items) |child| child.deinit();
scope.child_scopes.deinit(allocator);
allocator.destroy(scope);
} }
}; };
@ -431,7 +444,7 @@ pub fn interpret(
// _ = node; // _ = node;
// _ = observe_values; // _ = observe_values;
const tree = interpreter.tree; const tree = interpreter.handle.tree;
const tags = tree.nodes.items(.tag); const tags = tree.nodes.items(.tag);
const data = tree.nodes.items(.data); const data = tree.nodes.items(.data);
const main_tokens = tree.nodes.items(.main_token); const main_tokens = tree.nodes.items(.main_token);
@ -445,12 +458,12 @@ pub fn interpret(
.container_decl_arg_trailing, .container_decl_arg_trailing,
.container_decl_two, .container_decl_two,
.container_decl_two_trailing, .container_decl_two_trailing,
.tagged_union, // .tagged_union, // TODO: Fix these
.tagged_union_trailing, // .tagged_union_trailing,
.tagged_union_two, // .tagged_union_two,
.tagged_union_two_trailing, // .tagged_union_two_trailing,
.tagged_union_enum_tag, // .tagged_union_enum_tag,
.tagged_union_enum_tag_trailing, // .tagged_union_enum_tag_trailing,
.root, .root,
.error_set_decl, .error_set_decl,
=> { => {
@ -631,6 +644,20 @@ pub fn interpret(
std.log.err("Identifier not found: {s}", .{value}); std.log.err("Identifier not found: {s}", .{value});
return error.IdentifierNotFound; return error.IdentifierNotFound;
}, },
.field_access => {
if (data[node_idx].rhs == 0) return error.CriticalAstFailure;
const rhs_str = ast.tokenSlice(tree, data[node_idx].rhs) catch return error.CriticalAstFailure;
var ir = try interpreter.interpret(data[node_idx].lhs, scope, options);
var irv = try ir.getValue();
var sub_scope = interpreter.typeToTypeInfo(irv.value_data.@"type").getScopeOfType() orelse return error.IdentifierNotFound;
var scope_sub_decl = sub_scope.declarations.get(rhs_str) orelse return error.IdentifierNotFound;
return InterpretResult{
.value = scope_sub_decl.value,
};
},
.grouped_expression => { .grouped_expression => {
return try interpreter.interpret(data[node_idx].lhs, scope, options); return try interpreter.interpret(data[node_idx].lhs, scope, options);
}, },
@ -796,12 +823,11 @@ pub fn interpret(
// TODO: Add params // TODO: Add params
// var type_info = TypeInfo{ var type_info = TypeInfo{
// .@"fn" = .{ .@"fn" = .{
// .definition_scope = scope.?, .return_type = null,
// .node_idx = node_idx, },
// }, };
// };
// var it = func.iterate(&tree); // var it = func.iterate(&tree);
// while (ast.nextFnParam(&it)) |param| { // while (ast.nextFnParam(&it)) |param| {
@ -827,20 +853,19 @@ pub fn interpret(
// if ((try interpreter.interpret(func.ast.return_type, func_scope_idx, .{ .observe_values = true, .is_comptime = true })).maybeGetValue()) |value| // 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"; // fnd.return_type = value.value_data.@"type";
// var value = Value{ var value = Value{
// .node_idx = node_idx, .node_idx = node_idx,
// .@"type" = try interpreter.createType(node_idx, type_info), .@"type" = try interpreter.createType(node_idx, type_info),
// .value_data = .{ .@"fn" = .{} }, .value_data = .{ .@"fn" = .{} },
// }; };
// const name = analysis.getDeclName(tree, node_idx).?; const name = analysis.getDeclName(tree, node_idx).?;
// // TODO: DANGER DANGER DANGER try scope.?.declarations.put(interpreter.allocator, name, .{
// try scope.?.declarations.put(interpreter.allocator, name, .{ .node_idx = node_idx,
// .node_idx = node_idx, .name = name,
// .name = name, .@"type" = value.@"type",
// .@"type" = undefined, .@"value" = value,
// .@"value" = undefined, });
// });
return InterpretResult{ .nothing = .{} }; return InterpretResult{ .nothing = .{} };
}, },
@ -863,13 +888,15 @@ pub fn interpret(
try args.append(interpreter.allocator, try (try interpreter.interpret(param, scope, .{})).getValue()); try args.append(interpreter.allocator, try (try interpreter.interpret(param, scope, .{})).getValue());
} }
// TODO: Make this actually resolve function; requires interpreting whole file std.log.err("AEWEWEWE: {s}", .{tree.getNodeSource(call_full.ast.fn_expr)});
// const res = try interpreter.interpret(call_full.ast.fn_expr, scope, .{});
// const value = try res.getValue();
const call_res = try interpreter.call(tree.rootDecls()[0], args.items, options); const func_id_result = try interpreter.interpret(call_full.ast.fn_expr, interpreter.root_scope, .{});
const func_id_val = try func_id_result.getValue();
const call_res = try interpreter.call(interpreter.root_scope, func_id_val.node_idx, args.items, options);
// defer call_res.scope.deinit(); // defer call_res.scope.deinit();
// TODO: Figure out call result memory model // 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
return switch (call_res.result) { return switch (call_res.result) {
.value => |v| .{ .value = v }, .value => |v| .{ .value = v },
@ -905,6 +932,7 @@ pub const CallResult = struct {
pub fn call( pub fn call(
interpreter: *ComptimeInterpreter, interpreter: *ComptimeInterpreter,
scope: ?*InterpreterScope,
func_node_idx: Ast.Node.Index, func_node_idx: Ast.Node.Index,
arguments: []const Value, arguments: []const Value,
options: InterpretOptions, options: InterpretOptions,
@ -915,12 +943,12 @@ pub fn call(
_ = options; _ = options;
// _ = arguments; // _ = arguments;
const tree = interpreter.tree; const tree = interpreter.handle.tree;
const tags = tree.nodes.items(.tag); const tags = tree.nodes.items(.tag);
std.debug.assert(tags[func_node_idx] == .fn_decl); std.debug.assert(tags[func_node_idx] == .fn_decl);
var fn_scope = try interpreter.newScope(null, func_node_idx); var fn_scope = try interpreter.newScope(scope, func_node_idx);
var buf: [1]Ast.Node.Index = undefined; var buf: [1]Ast.Node.Index = undefined;
var proto = ast.fnProto(tree, func_node_idx, &buf).?; var proto = ast.fnProto(tree, func_node_idx, &buf).?;

View File

@ -12,6 +12,7 @@ const BuildConfig = @import("special/build_runner.zig").BuildConfig;
const tracy = @import("tracy.zig"); const tracy = @import("tracy.zig");
const Config = @import("Config.zig"); const Config = @import("Config.zig");
const translate_c = @import("translate_c.zig"); const translate_c = @import("translate_c.zig");
const ComptimeInterpreter = @import("ComptimeInterpreter.zig");
const DocumentStore = @This(); const DocumentStore = @This();
@ -55,6 +56,8 @@ pub const Handle = struct {
uri: Uri, uri: Uri,
text: [:0]const u8, text: [:0]const u8,
tree: Ast, tree: Ast,
/// Not null if a ComptimeInterpreter is actually used
interpreter: ?*ComptimeInterpreter = null,
document_scope: analysis.DocumentScope, document_scope: analysis.DocumentScope,
/// Contains one entry for every import in the document /// Contains one entry for every import in the document
import_uris: std.ArrayListUnmanaged(Uri) = .{}, import_uris: std.ArrayListUnmanaged(Uri) = .{},
@ -189,6 +192,11 @@ pub fn refreshDocument(self: *DocumentStore, uri: Uri, new_text: [:0]const u8) !
const handle = self.handles.get(uri) orelse unreachable; const handle = self.handles.get(uri) orelse unreachable;
if (handle.interpreter) |int| {
int.deinit();
handle.interpreter = null;
}
self.allocator.free(handle.text); self.allocator.free(handle.text);
handle.text = new_text; handle.text = new_text;
@ -927,3 +935,18 @@ pub fn errorCompletionItems(self: DocumentStore, arena: std.mem.Allocator, handl
pub fn enumCompletionItems(self: DocumentStore, arena: std.mem.Allocator, handle: Handle) ![]types.CompletionItem { pub fn enumCompletionItems(self: DocumentStore, arena: std.mem.Allocator, handle: Handle) ![]types.CompletionItem {
return try self.tagStoreCompletionItems(arena, handle, "enum_completions"); return try self.tagStoreCompletionItems(arena, handle, "enum_completions");
} }
pub fn ensureInterpreterExists(self: *DocumentStore, uri: Uri) !void {
var handle = self.handles.get(uri) orelse unreachable;
if (handle.interpreter == null) {
var int = try self.allocator.create(ComptimeInterpreter);
int.* = ComptimeInterpreter{
.allocator = self.allocator,
.document_store = self,
.handle = handle,
};
_ = try int.interpret(0, null, .{});
handle.interpreter = int;
}
}

View File

@ -18,6 +18,7 @@ const Ast = std.zig.Ast;
const tracy = @import("tracy.zig"); const tracy = @import("tracy.zig");
const uri_utils = @import("uri.zig"); const uri_utils = @import("uri.zig");
const diff = @import("diff.zig"); const diff = @import("diff.zig");
const ComptimeInterpreter = @import("ComptimeInterpreter.zig");
const data = @import("data/data.zig"); const data = @import("data/data.zig");
const snipped_data = @import("data/snippets.zig"); const snipped_data = @import("data/snippets.zig");
@ -492,7 +493,7 @@ fn typeToCompletion(
while (it.next()) |entry| { while (it.next()) |entry| {
try list.append(allocator, .{ try list.append(allocator, .{
.label = entry.key_ptr.*, .label = entry.key_ptr.*,
.kind = if (entry.value_ptr.isConstant(co.interpreter.tree)) .Constant else .Variable, .kind = if (entry.value_ptr.isConstant(co.interpreter.handle.tree)) .Constant else .Variable,
.insertText = entry.key_ptr.*, .insertText = entry.key_ptr.*,
.insertTextFormat = .PlainText, .insertTextFormat = .PlainText,
}); });
@ -819,7 +820,10 @@ fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle) error{OutO
const resolved_type = try decl_handle.resolveType(&server.document_store, &server.arena, &bound_type_params); const resolved_type = try decl_handle.resolveType(&server.document_store, &server.arena, &bound_type_params);
const resolved_type_str = if (resolved_type) |rt| const resolved_type_str = if (resolved_type) |rt|
if (rt.type.is_type_val) "type" else switch (rt.type.data) { // TODO: Investigate random weird numbers like 897 that cause index of bounds if (rt.type.is_type_val) switch (rt.type.data) {
.@"comptime" => |*co| try std.fmt.allocPrint(server.arena.allocator(), "{ }", .{co.interpreter.formatTypeInfo(co.interpreter.typeToTypeInfo(co.type))}),
else => "type",
} else switch (rt.type.data) { // TODO: Investigate random weird numbers like 897 that cause index of bounds
.pointer, .pointer,
.slice, .slice,
.error_union, .error_union,

View File

@ -749,14 +749,32 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl
// TODO: Better case-by-case; we just use the ComptimeInterpreter when all else fails, // TODO: Better case-by-case; we just use the ComptimeInterpreter when all else fails,
// probably better to use it more liberally // probably better to use it more liberally
// TODO: Handle non-isolate args; e.g. `const T = u8; TypeFunc(T);` // TODO: Handle non-isolate args; e.g. `const T = u8; TypeFunc(T);`
var interpreter = ComptimeInterpreter{ .tree = tree, .allocator = arena.allocator() }; // var interpreter = ComptimeInterpreter{ .tree = tree, .allocator = arena.allocator() };
const result = interpreter.interpret(node, null, .{}) catch |err| { // var top_decl = try (try interpreter.interpret(0, null, .{})).getValue();
// var top_scope = interpreter.typeToTypeInfo(top_decl.@"type".info_idx).@"struct".scope;
// var fn_decl_scope = top_scope.getParentScopeFromNode(node);
store.ensureInterpreterExists(handle.uri) catch |err| {
std.log.err("Interpreter error: {s}", .{@errorName(err)}); std.log.err("Interpreter error: {s}", .{@errorName(err)});
return null; return null;
}; };
var interpreter = handle.interpreter.?;
// TODO: Start from current/nearest-current scope
const result = interpreter.interpret(node, interpreter.root_scope, .{}) catch |err| {
std.log.err("Interpreter error: {s}", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*);
}
return null;
};
const val = result.getValue() catch |err| { const val = result.getValue() catch |err| {
std.log.err("Interpreter error: {s}", .{@errorName(err)}); std.log.err("Interpreter error: {s}", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*);
}
return null; return null;
}; };
@ -997,7 +1015,7 @@ pub const Type = struct {
primitive: Ast.Node.Index, primitive: Ast.Node.Index,
array_index, array_index,
@"comptime": struct { @"comptime": struct {
interpreter: ComptimeInterpreter, interpreter: *ComptimeInterpreter,
type: ComptimeInterpreter.Type, type: ComptimeInterpreter.Type,
}, },
}, },