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 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).?;

View File

@ -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;
}
}

View File

@ -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,

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,
// 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,
},
},