Hacky mess but it works (only if your function is the first root decl tho :P)

This commit is contained in:
Auguste Rame 2022-10-28 14:24:38 -04:00
parent 06e8756849
commit 779c3c0710
No known key found for this signature in database
GPG Key ID: 3A5E3F90DF2AAEFE
4 changed files with 133 additions and 116 deletions

View File

@ -180,6 +180,9 @@ pub const ValueData = union(enum) {
signed_int: i64,
float: f64,
runtime,
comptime_undetermined,
pub fn eql(data: ValueData, other_data: ValueData) bool {
if (std.meta.activeTag(data) != std.meta.activeTag(other_data)) return false;
// std.enums.
@ -190,7 +193,8 @@ pub const ValueData = union(enum) {
.unsigned_int => return data.unsigned_int == other_data.unsigned_int,
.signed_int => return data.signed_int == other_data.signed_int,
.float => return data.float == other_data.float,
else => @panic("Simple eql not implemented!"),
else => return false,
}
}
};
@ -395,6 +399,7 @@ pub const InterpretResult = union(enum) {
return switch (result) {
.break_with_value => |v| v.value,
.value => |v| v,
.return_with_value => |v| v,
else => null,
};
}
@ -414,6 +419,7 @@ pub const InterpretError = std.mem.Allocator.Error || std.fmt.ParseIntError || s
InvalidOperation,
CriticalAstFailure,
InvalidBuiltin,
IdentifierNotFound,
};
pub fn interpret(
interpreter: *ComptimeInterpreter,
@ -623,7 +629,7 @@ pub fn interpret(
}
std.log.err("Identifier not found: {s}", .{value});
@panic("Could not find identifier");
return error.IdentifierNotFound;
},
.grouped_expression => {
return try interpreter.interpret(data[node_idx].lhs, scope, options);
@ -733,34 +739,17 @@ pub fn interpret(
=> {
var buffer: [2]Ast.Node.Index = undefined;
const params = ast.builtinCallParams(tree, node_idx, &buffer).?;
_ = params;
const call_name = tree.tokenSlice(main_tokens[node_idx]);
if (std.mem.eql(u8, call_name, "@compileLog")) {
pp: for (params) |param| {
const res = try (try interpreter.interpret(param, scope, .{})).getValue();
const ti = interpreter.type_info.items[res.@"type".info_idx];
switch (ti) {
.pointer => |ptr| {
const child = interpreter.type_info.items[ptr.child.info_idx];
if (ptr.size == .slice and child == .int and child.int.bits == 8 and child.int.signedness == .unsigned) {
// TODO: Fix once I optimize slices
std.debug.print("@compileLog output: ", .{});
for (res.value_data.slice_ptr.items) |i| std.debug.print("{c}", .{@truncate(u8, i.unsigned_int)});
std.debug.print("\n", .{});
break :pp;
}
},
else => {},
}
@panic("compileLog argument type not printable!");
}
return InterpretResult{ .nothing = .{} };
}
if (std.mem.eql(u8, call_name, "@compileError")) {
return InterpretResult{ .@"return" = .{} };
}
std.log.info("Builtin not implemented: {s}", .{call_name});
@panic("Builtin not implemented");
// return error.InvalidBuiltin;
@ -844,12 +833,13 @@ pub fn interpret(
// .value_data = .{ .@"fn" = .{} },
// };
// const name = ast.getDeclName(tree, node_idx).?;
// 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" = value.@"type",
// .@"value" = value,
// .@"type" = undefined,
// .@"value" = undefined,
// });
return InterpretResult{ .nothing = .{} };
@ -863,60 +853,28 @@ pub fn interpret(
.async_call_one,
.async_call_one_comma,
=> {
// var params: [1]Ast.Node.Index = undefined;
// const call = ast.callFull(tree, node_idx, &params) orelse unreachable;
var params: [1]Ast.Node.Index = undefined;
const call_full = ast.callFull(tree, node_idx, &params) orelse unreachable;
// const callee = .{ .node = call.ast.fn_expr, .handle = handle };
// const decl = (try resolveTypeOfNodeInternal(store, arena, callee, bound_type_params)) orelse
// return null;
var args = try std.ArrayListUnmanaged(Value).initCapacity(interpreter.allocator, call_full.ast.params.len);
defer args.deinit(interpreter.allocator);
// if (decl.type.is_type_val) return null;
// const decl_node = switch (decl.type.data) {
// .other => |n| n,
// else => return null,
// };
// var buf: [1]Ast.Node.Index = undefined;
// const func_maybe = ast.fnProto(decl.handle.tree, decl_node, &buf);
for (call_full.ast.params) |param| {
try args.append(interpreter.allocator, try (try interpreter.interpret(param, scope, .{})).getValue());
}
// if (func_maybe) |fn_decl| {
// var expected_params = fn_decl.ast.params.len;
// // If we call as method, the first parameter should be skipped
// // TODO: Back-parse to extract the self argument?
// var it = fn_decl.iterate(&decl.handle.tree);
// if (token_tags[call.ast.lparen - 2] == .period) {
// if (try hasSelfParam(arena, store, decl.handle, fn_decl)) {
// _ = ast.nextFnParam(&it);
// expected_params -= 1;
// }
// }
// 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();
// // Bind type params to the arguments passed in the call.
// const param_len = std.math.min(call.ast.params.len, expected_params);
// var i: usize = 0;
// while (ast.nextFnParam(&it)) |decl_param| : (i += 1) {
// if (i >= param_len) break;
// if (!isMetaType(decl.handle.tree, decl_param.type_expr))
// continue;
const call_res = try interpreter.call(tree.rootDecls()[0], args.items, options);
// defer call_res.scope.deinit();
// TODO: Figure out call result memory model
// const argument = .{ .node = call.ast.params[i], .handle = handle };
// const argument_type = (try resolveTypeOfNodeInternal(
// store,
// arena,
// argument,
// bound_type_params,
// )) orelse
// continue;
// if (!argument_type.type.is_type_val) continue;
// try bound_type_params.put(arena.allocator(), decl_param, argument_type);
// }
// const has_body = decl.handle.tree.nodes.items(.tag)[decl_node] == .fn_decl;
// const body = decl.handle.tree.nodes.items(.data)[decl_node].rhs;
// return try resolveReturnType(store, arena, fn_decl, decl.handle, bound_type_params, if (has_body) body else null);
// }
// return null;
return InterpretResult{ .nothing = .{} };
return switch (call_res.result) {
.value => |v| .{ .value = v },
.nothing => .{ .nothing = {} },
};
},
.bool_not => {
const result = try interpreter.interpret(data[node_idx].lhs, scope, .{});
@ -955,16 +913,33 @@ pub fn call(
// TODO: Arguments
_ = options;
_ = arguments;
// _ = arguments;
const tree = interpreter.tree;
const tags = tree.nodes.items(.tag);
std.debug.assert(tags[func_node_idx] == .fn_decl);
// TODO: Parent sc]ope exploration (consts, typefuncs, etc.)
var fn_scope = try interpreter.newScope(null, func_node_idx);
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| {
if (param.name_token) |nt| {
const decl = Declaration{
.node_idx = param.type_expr,
.name = tree.tokenSlice(nt),
.@"type" = arguments[arg_index].@"type",
.value = arguments[arg_index],
};
try fn_scope.declarations.put(interpreter.allocator, tree.tokenSlice(nt), decl);
arg_index += 1;
}
}
const body = tree.nodes.items(.data)[func_node_idx].rhs;
const result = try interpreter.interpret(body, fn_scope, .{});

View File

@ -484,6 +484,23 @@ fn typeToCompletion(
null,
),
.primitive, .array_index => {},
.@"comptime" => |co| {
const ti = co.interpreter.typeToTypeInfo(co.type);
switch (ti) {
.@"struct" => |st| {
var it = st.scope.declarations.iterator();
while (it.next()) |entry| {
try list.append(allocator, .{
.label = entry.key_ptr.*,
.kind = if (entry.value_ptr.isConstant(co.interpreter.tree)) .Constant else .Variable,
.insertText = entry.key_ptr.*,
.insertTextFormat = .PlainText,
});
}
},
else => {},
}
},
}
}
@ -2663,8 +2680,7 @@ pub fn processJsonRpc(server: *Server, writer: anytype, json: []const u8) !void
if (s.len == 0) {
if (field.field_type == ?[]const u8) {
break :blk null;
}
else {
} else {
break :blk s;
}
}
@ -2727,35 +2743,7 @@ pub fn processJsonRpc(server: *Server, writer: anytype, json: []const u8) !void
}
}
const method_map = .{
.{ "initialized", void, initializedHandler },
.{"$/cancelRequest"},
.{"textDocument/willSave"},
.{ "initialize", requests.Initialize, initializeHandler },
.{ "shutdown", void, shutdownHandler },
.{ "exit", void, exitHandler },
.{ "textDocument/didOpen", requests.OpenDocument, openDocumentHandler },
.{ "textDocument/didChange", requests.ChangeDocument, changeDocumentHandler },
.{ "textDocument/didSave", requests.SaveDocument, saveDocumentHandler },
.{ "textDocument/didClose", requests.CloseDocument, closeDocumentHandler },
.{ "textDocument/semanticTokens/full", requests.SemanticTokensFull, semanticTokensFullHandler },
.{ "textDocument/inlayHint", requests.InlayHint, inlayHintHandler },
.{ "textDocument/completion", requests.Completion, completionHandler },
.{ "textDocument/signatureHelp", requests.SignatureHelp, signatureHelpHandler },
.{ "textDocument/definition", requests.GotoDefinition, gotoDefinitionHandler },
.{ "textDocument/typeDefinition", requests.GotoDefinition, gotoDefinitionHandler },
.{ "textDocument/implementation", requests.GotoDefinition, gotoDefinitionHandler },
.{ "textDocument/declaration", requests.GotoDeclaration, gotoDeclarationHandler },
.{ "textDocument/hover", requests.Hover, hoverHandler },
.{ "textDocument/documentSymbol", requests.DocumentSymbols, documentSymbolsHandler },
.{ "textDocument/formatting", requests.Formatting, formattingHandler },
.{ "textDocument/rename", requests.Rename, renameHandler },
.{ "textDocument/references", requests.References, referencesHandler },
.{ "textDocument/documentHighlight", requests.DocumentHighlight, documentHighlightHandler },
.{ "textDocument/codeAction", requests.CodeAction, codeActionHandler },
.{ "workspace/didChangeConfiguration", Config.DidChangeConfigurationParams, didChangeConfigurationHandler },
.{ "textDocument/foldingRange", requests.FoldingRange, foldingRangeHandler },
};
const method_map = .{ .{ "initialized", void, initializedHandler }, .{"$/cancelRequest"}, .{"textDocument/willSave"}, .{ "initialize", requests.Initialize, initializeHandler }, .{ "shutdown", void, shutdownHandler }, .{ "exit", void, exitHandler }, .{ "textDocument/didOpen", requests.OpenDocument, openDocumentHandler }, .{ "textDocument/didChange", requests.ChangeDocument, changeDocumentHandler }, .{ "textDocument/didSave", requests.SaveDocument, saveDocumentHandler }, .{ "textDocument/didClose", requests.CloseDocument, closeDocumentHandler }, .{ "textDocument/semanticTokens/full", requests.SemanticTokensFull, semanticTokensFullHandler }, .{ "textDocument/inlayHint", requests.InlayHint, inlayHintHandler }, .{ "textDocument/completion", requests.Completion, completionHandler }, .{ "textDocument/signatureHelp", requests.SignatureHelp, signatureHelpHandler }, .{ "textDocument/definition", requests.GotoDefinition, gotoDefinitionHandler }, .{ "textDocument/typeDefinition", requests.GotoDefinition, gotoDefinitionHandler }, .{ "textDocument/implementation", requests.GotoDefinition, gotoDefinitionHandler }, .{ "textDocument/declaration", requests.GotoDeclaration, gotoDeclarationHandler }, .{ "textDocument/hover", requests.Hover, hoverHandler }, .{ "textDocument/documentSymbol", requests.DocumentSymbols, documentSymbolsHandler }, .{ "textDocument/formatting", requests.Formatting, formattingHandler }, .{ "textDocument/rename", requests.Rename, renameHandler }, .{ "textDocument/references", requests.References, referencesHandler }, .{ "textDocument/documentHighlight", requests.DocumentHighlight, documentHighlightHandler }, .{ "textDocument/codeAction", requests.CodeAction, codeActionHandler }, .{ "workspace/didChangeConfiguration", Config.DidChangeConfigurationParams, didChangeConfigurationHandler }, .{ "textDocument/foldingRange", requests.FoldingRange, foldingRangeHandler } };
if (zig_builtin.zig_backend == .stage1) {
// Hack to avoid `return`ing in the inline for, which causes bugs.

View File

@ -5,6 +5,7 @@ const types = @import("types.zig");
const offsets = @import("offsets.zig");
const log = std.log.scoped(.analysis);
const ast = @import("ast.zig");
const ComptimeInterpreter = @import("ComptimeInterpreter.zig");
var using_trail: std.ArrayList([*]const u8) = undefined;
var resolve_trail: std.ArrayList(NodeWithHandle) = undefined;
@ -491,7 +492,7 @@ fn resolveUnwrapErrorType(store: *DocumentStore, arena: *std.heap.ArenaAllocator
.type = .{ .data = .{ .other = n }, .is_type_val = rhs.type.is_type_val },
.handle = rhs.handle,
},
.primitive, .slice, .pointer, .array_index => return null,
.primitive, .slice, .pointer, .array_index, .@"comptime" => return null,
};
if (rhs.handle.tree.nodes.items(.tag)[rhs_node] == .error_union) {
@ -742,7 +743,37 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl
const has_body = decl.handle.tree.nodes.items(.tag)[decl_node] == .fn_decl;
const body = decl.handle.tree.nodes.items(.data)[decl_node].rhs;
return try resolveReturnType(store, arena, fn_decl, decl.handle, bound_type_params, if (has_body) body else null);
if (try resolveReturnType(store, arena, fn_decl, decl.handle, bound_type_params, if (has_body) body else null)) |ret| {
return ret;
} else {
// 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() };
const result = interpreter.interpret(node, null, .{}) catch |err| {
std.log.err("Interpreter error: {s}", .{@errorName(err)});
return null;
};
const val = result.getValue() catch |err| {
std.log.err("Interpreter error: {s}", .{@errorName(err)});
return null;
};
const ti = interpreter.typeToTypeInfo(val.@"type");
if (ti != .@"type") {
std.log.err("Not a type: { }", .{interpreter.formatTypeInfo(ti)});
return null;
}
return TypeWithHandle{
.type = .{
.data = .{ .@"comptime" = .{ .interpreter = interpreter, .type = val.value_data.@"type" } },
.is_type_val = true,
},
.handle = node_handle.handle,
};
}
}
return null;
},
@ -965,6 +996,10 @@ pub const Type = struct {
other: Ast.Node.Index,
primitive: Ast.Node.Index,
array_index,
@"comptime": struct {
interpreter: ComptimeInterpreter,
type: ComptimeInterpreter.Type,
},
},
/// If true, the type `type`, the attached data is the value of the type value.
is_type_val: bool,

View File

@ -9,8 +9,8 @@ const allocator: std.mem.Allocator = std.testing.allocator;
test "ComptimeInterpreter - basic test" {
var tree = try std.zig.parse(allocator,
\\pub fn ReturnMyType() type {
\\ var abc = z: {break :z if (!false) 123 else 0;};
\\pub fn ReturnMyType(comptime my_arg: bool) type {
\\ var abc = z: {break :z if (!my_arg) 123 else 0;};
\\ if (abc == 123) return u69;
\\ return u8;
\\}
@ -20,10 +20,29 @@ test "ComptimeInterpreter - basic test" {
var interpreter = ComptimeInterpreter{ .tree = tree, .allocator = allocator };
defer interpreter.deinit();
const z = try interpreter.call(tree.rootDecls()[0], &.{}, .{});
defer z.scope.deinit();
var bool_type = try interpreter.createType(std.math.maxInt(std.zig.Ast.Node.Index), .{ .@"bool" = .{} });
var arg_false = ComptimeInterpreter.Value{
.node_idx = std.math.maxInt(std.zig.Ast.Node.Index),
.@"type" = bool_type,
.value_data = .{ .@"bool" = false },
};
var arg_true = ComptimeInterpreter.Value{
.node_idx = std.math.maxInt(std.zig.Ast.Node.Index),
.@"type" = bool_type,
.value_data = .{ .@"bool" = true },
};
try std.testing.expectFmt("u69", "{any}", .{interpreter.formatTypeInfo(interpreter.typeToTypeInfo(z.result.value.value_data.@"type"))});
const call_with_false = try interpreter.call(tree.rootDecls()[0], &.{
arg_false,
}, .{});
defer call_with_false.scope.deinit();
const call_with_true = try interpreter.call(tree.rootDecls()[0], &.{
arg_true,
}, .{});
defer call_with_true.scope.deinit();
try std.testing.expectFmt("u69", "{any}", .{interpreter.formatTypeInfo(interpreter.typeToTypeInfo(call_with_false.result.value.value_data.@"type"))});
try std.testing.expectFmt("u8", "{any}", .{interpreter.formatTypeInfo(interpreter.typeToTypeInfo(call_with_true.result.value.value_data.@"type"))});
}
test "ComptimeInterpreter - struct" {