From da00751726dd53492660e5bcdbdc495640bff0dd Mon Sep 17 00:00:00 2001 From: Auguste Rame <19855629+SuperAuguste@users.noreply.github.com> Date: Sat, 29 Oct 2022 01:46:22 -0400 Subject: [PATCH] Field access, function calls based on function value --- src/ComptimeInterpreter.zig | 114 ++++++++++++++++++++++-------------- src/DocumentStore.zig | 23 ++++++++ src/Server.zig | 8 ++- src/analysis.zig | 24 +++++++- 4 files changed, 121 insertions(+), 48 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 26d8c37..6ba572c 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -13,14 +13,16 @@ const analysis = @import("analysis.zig"); const DocumentStore = @import("DocumentStore.zig"); const ComptimeInterpreter = @This(); -tree: Ast, -root_scope: *InterpreterScope = undefined, allocator: std.mem.Allocator, +document_store: *DocumentStore, +handle: *const DocumentStore.Handle, +root_scope: ?*InterpreterScope = null, type_info: std.ArrayListUnmanaged(TypeInfo) = .{}, type_info_map: std.HashMapUnmanaged(TypeInfo, usize, TypeInfo.Context, std.hash_map.default_max_load_percentage) = .{}, pub fn deinit(interpreter: *ComptimeInterpreter) void { + if (interpreter.root_scope) |rs| rs.deinit(); for (interpreter.type_info.items) |*ti| ti.deinit(interpreter.allocator); interpreter.type_info.deinit(interpreter.allocator); interpreter.type_info_map.deinit(interpreter.allocator); @@ -83,6 +85,7 @@ pub const TypeInfo = union(enum) { @"struct": Struct, pointer: Pointer, + @"fn": Fn, int: Int, @"comptime_int", @@ -145,6 +148,13 @@ pub const TypeInfo = union(enum) { else => {}, } } + + pub fn getScopeOfType(ti: TypeInfo) ?*InterpreterScope { + return switch (ti) { + .@"struct" => |s| s.scope, + else => null, + }; + } }; pub const Type = struct { @@ -180,6 +190,7 @@ pub const ValueData = union(enum) { signed_int: i64, float: f64, + @"fn", runtime, comptime_undetermined, @@ -233,7 +244,7 @@ pub const Declaration = struct { .aligned_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, }; @@ -261,7 +272,7 @@ pub fn typeToTypeInfo(interpreter: ComptimeInterpreter, @"type": Type) TypeInfo } pub const TypeInfoFormatter = struct { - interpreter: *ComptimeInterpreter, + interpreter: *const ComptimeInterpreter, ti: TypeInfo, 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(); while (iterator.next()) |di| { 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")) }); } else { 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 }; } @@ -365,11 +376,13 @@ pub const InterpreterScope = struct { } pub fn deinit(scope: *InterpreterScope) void { - scope.declarations.deinit(scope.interpreter.allocator); - for (scope.child_scopes.items) |child| child.deinit(); - scope.child_scopes.deinit(scope.interpreter.allocator); + const allocator = 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; // _ = observe_values; - const tree = interpreter.tree; + const tree = interpreter.handle.tree; const tags = tree.nodes.items(.tag); const data = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); @@ -445,12 +458,12 @@ pub fn interpret( .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, + // .tagged_union, // TODO: Fix these + // .tagged_union_trailing, + // .tagged_union_two, + // .tagged_union_two_trailing, + // .tagged_union_enum_tag, + // .tagged_union_enum_tag_trailing, .root, .error_set_decl, => { @@ -631,6 +644,20 @@ pub fn interpret( std.log.err("Identifier not found: {s}", .{value}); 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 => { return try interpreter.interpret(data[node_idx].lhs, scope, options); }, @@ -796,12 +823,11 @@ pub fn interpret( // TODO: Add params - // var type_info = TypeInfo{ - // .@"fn" = .{ - // .definition_scope = scope.?, - // .node_idx = node_idx, - // }, - // }; + var type_info = TypeInfo{ + .@"fn" = .{ + .return_type = null, + }, + }; // var it = func.iterate(&tree); // 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| // fnd.return_type = value.value_data.@"type"; - // var value = Value{ - // .node_idx = node_idx, - // .@"type" = try interpreter.createType(node_idx, type_info), - // .value_data = .{ .@"fn" = .{} }, - // }; + var value = Value{ + .node_idx = node_idx, + .@"type" = try interpreter.createType(node_idx, type_info), + .value_data = .{ .@"fn" = .{} }, + }; - // const name = analysis.getDeclName(tree, node_idx).?; - // // TODO: DANGER DANGER DANGER - // try scope.?.declarations.put(interpreter.allocator, name, .{ - // .node_idx = node_idx, - // .name = name, - // .@"type" = undefined, - // .@"value" = undefined, - // }); + const name = analysis.getDeclName(tree, node_idx).?; + try scope.?.declarations.put(interpreter.allocator, name, .{ + .node_idx = node_idx, + .name = name, + .@"type" = value.@"type", + .@"value" = value, + }); return InterpretResult{ .nothing = .{} }; }, @@ -863,13 +888,15 @@ pub fn interpret( try args.append(interpreter.allocator, try (try interpreter.interpret(param, scope, .{})).getValue()); } - // TODO: Make this actually resolve function; requires interpreting whole file - // const res = try interpreter.interpret(call_full.ast.fn_expr, scope, .{}); - // const value = try res.getValue(); + std.log.err("AEWEWEWE: {s}", .{tree.getNodeSource(call_full.ast.fn_expr)}); - 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(); - // 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) { .value => |v| .{ .value = v }, @@ -905,6 +932,7 @@ pub const CallResult = struct { pub fn call( interpreter: *ComptimeInterpreter, + scope: ?*InterpreterScope, func_node_idx: Ast.Node.Index, arguments: []const Value, options: InterpretOptions, @@ -915,12 +943,12 @@ pub fn call( _ = options; // _ = arguments; - const tree = interpreter.tree; + const tree = interpreter.handle.tree; const tags = tree.nodes.items(.tag); 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 proto = ast.fnProto(tree, func_node_idx, &buf).?; diff --git a/src/DocumentStore.zig b/src/DocumentStore.zig index 2144a29..a10c7f5 100644 --- a/src/DocumentStore.zig +++ b/src/DocumentStore.zig @@ -12,6 +12,7 @@ const BuildConfig = @import("special/build_runner.zig").BuildConfig; const tracy = @import("tracy.zig"); const Config = @import("Config.zig"); const translate_c = @import("translate_c.zig"); +const ComptimeInterpreter = @import("ComptimeInterpreter.zig"); const DocumentStore = @This(); @@ -55,6 +56,8 @@ pub const Handle = struct { uri: Uri, text: [:0]const u8, tree: Ast, + /// Not null if a ComptimeInterpreter is actually used + interpreter: ?*ComptimeInterpreter = null, document_scope: analysis.DocumentScope, /// Contains one entry for every import in the document 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; + if (handle.interpreter) |int| { + int.deinit(); + handle.interpreter = null; + } + self.allocator.free(handle.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 { 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; + } +} diff --git a/src/Server.zig b/src/Server.zig index d563ede..68dab07 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -18,6 +18,7 @@ const Ast = std.zig.Ast; const tracy = @import("tracy.zig"); const uri_utils = @import("uri.zig"); const diff = @import("diff.zig"); +const ComptimeInterpreter = @import("ComptimeInterpreter.zig"); const data = @import("data/data.zig"); const snipped_data = @import("data/snippets.zig"); @@ -492,7 +493,7 @@ fn typeToCompletion( while (it.next()) |entry| { try list.append(allocator, .{ .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.*, .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_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, .slice, .error_union, diff --git a/src/analysis.zig b/src/analysis.zig index e9a1143..aa490db 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -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, // probably better to use it more liberally // 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)}); 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| { std.log.err("Interpreter error: {s}", .{@errorName(err)}); + if (@errorReturnTrace()) |trace| { + std.debug.dumpStackTrace(trace.*); + } return null; }; @@ -997,7 +1015,7 @@ pub const Type = struct { primitive: Ast.Node.Index, array_index, @"comptime": struct { - interpreter: ComptimeInterpreter, + interpreter: *ComptimeInterpreter, type: ComptimeInterpreter.Type, }, },