Hacky mess but it works (only if your function is the first root decl tho :P)
This commit is contained in:
parent
06e8756849
commit
779c3c0710
@ -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, ¶ms) orelse unreachable;
|
||||
var params: [1]Ast.Node.Index = undefined;
|
||||
const call_full = ast.callFull(tree, node_idx, ¶ms) 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, .{});
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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" {
|
||||
|
Loading…
Reference in New Issue
Block a user