Some builtins, rudimentary hacky diagnostics; need to nerf global evaluation
This commit is contained in:
parent
599c134593
commit
d2e166bb0b
@ -18,10 +18,25 @@ document_store: *DocumentStore,
|
||||
handle: *const DocumentStore.Handle,
|
||||
root_type: ?Type = null,
|
||||
|
||||
/// Interpreter diagnostic errors
|
||||
errors: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, InterpreterError) = .{},
|
||||
|
||||
// TODO: Deduplicate typeinfo across different interpreters
|
||||
type_info: std.ArrayListUnmanaged(TypeInfo) = .{},
|
||||
type_info_map: std.HashMapUnmanaged(TypeInfo, usize, TypeInfo.Context, std.hash_map.default_max_load_percentage) = .{},
|
||||
|
||||
pub const InterpreterError = struct {
|
||||
code: []const u8,
|
||||
message: []const u8,
|
||||
};
|
||||
|
||||
pub fn recordError(interpreter: *ComptimeInterpreter, node_idx: Ast.Node.Index, code: []const u8, message: []const u8) !void {
|
||||
try interpreter.errors.put(interpreter.allocator, node_idx, .{
|
||||
.code = code,
|
||||
.message = message,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn deinit(interpreter: *ComptimeInterpreter) void {
|
||||
if (interpreter.root_type) |rt| rt.getTypeInfo().getScopeOfType().?.deinit();
|
||||
for (interpreter.type_info.items) |*ti| ti.deinit(interpreter.allocator);
|
||||
@ -48,7 +63,7 @@ pub const TypeInfo = union(enum) {
|
||||
pub const Struct = struct {
|
||||
/// Declarations contained within
|
||||
scope: *InterpreterScope,
|
||||
fields: std.ArrayListUnmanaged(FieldDefinition) = .{},
|
||||
fields: std.StringHashMapUnmanaged(FieldDefinition) = .{},
|
||||
};
|
||||
|
||||
pub const Int = struct {
|
||||
@ -116,7 +131,9 @@ pub const TypeInfo = union(enum) {
|
||||
context.hasher.update(&[_]u8{@enumToInt(ti)});
|
||||
return switch (ti) {
|
||||
.@"struct" => |s| {
|
||||
context.hasher.update(std.mem.sliceAsBytes(s.fields.items));
|
||||
_ = s;
|
||||
// TODO: Fix
|
||||
// context.hasher.update(std.mem.sliceAsBytes(s.fields.items));
|
||||
// TODO: Fix
|
||||
// context.hasher.update(std.mem.sliceAsBytes(s.declarations.items));
|
||||
},
|
||||
@ -167,6 +184,11 @@ pub const Type = struct {
|
||||
pub fn getTypeInfo(@"type": Type) TypeInfo {
|
||||
return @"type".handle.interpreter.?.type_info.items[@"type".info_idx];
|
||||
}
|
||||
|
||||
/// Be careful with this; typeinfo resizes reassign pointers!
|
||||
pub fn getTypeInfoMutable(@"type": Type) *TypeInfo {
|
||||
return &@"type".handle.interpreter.?.type_info.items[@"type".info_idx];
|
||||
}
|
||||
};
|
||||
|
||||
pub const Value = struct {
|
||||
@ -191,8 +213,8 @@ pub const ValueData = union(enum) {
|
||||
|
||||
// },
|
||||
// one_ptr: *anyopaque,
|
||||
/// TODO: Optimize this with an ArrayList that uses anyopaque slice
|
||||
slice_ptr: std.ArrayListUnmanaged(ValueData),
|
||||
/// Special case slice; this is extremely common at comptime so it makes sense
|
||||
string: []const u8,
|
||||
|
||||
@"comptime_int": std.math.big.int.Managed,
|
||||
unsigned_int: u64,
|
||||
@ -295,9 +317,11 @@ pub const TypeInfoFormatter = struct {
|
||||
.@"bool" => try writer.writeAll("bool"),
|
||||
.@"struct" => |s| {
|
||||
try writer.writeAll("struct {");
|
||||
for (s.fields.items) |field| {
|
||||
try writer.print("{s}: {s}, ", .{ field.name, value.interpreter.formatTypeInfo(field.@"type".getTypeInfo()) });
|
||||
var field_iterator = s.fields.iterator();
|
||||
while (field_iterator.next()) |di| {
|
||||
try writer.print("{s}: {s}, ", .{ di.key_ptr.*, value.interpreter.formatTypeInfo(di.value_ptr.*.@"type".getTypeInfo()) });
|
||||
}
|
||||
|
||||
var iterator = s.scope.declarations.iterator();
|
||||
while (iterator.next()) |di| {
|
||||
const decl = di.value_ptr.*;
|
||||
@ -560,17 +584,26 @@ pub fn interpret(
|
||||
};
|
||||
|
||||
if (maybe_container_field) |field_info| {
|
||||
var init_type = try interpreter.interpret(field_info.ast.type_expr, container_scope, .{});
|
||||
var init_type_value = try (try interpreter.interpret(field_info.ast.type_expr, container_scope, .{})).getValue();
|
||||
var default_value = if (field_info.ast.value_expr == 0)
|
||||
null
|
||||
else
|
||||
try (try interpreter.interpret(field_info.ast.value_expr, container_scope, .{})).getValue();
|
||||
|
||||
if (init_type_value.@"type".getTypeInfo() != .@"type") {
|
||||
try interpreter.recordError(
|
||||
field_info.ast.type_expr,
|
||||
"invalid_field_type_value",
|
||||
"Field type should be a type!",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const name = tree.tokenSlice(field_info.ast.name_token);
|
||||
const field = FieldDefinition{
|
||||
.node_idx = member,
|
||||
.name = name,
|
||||
.@"type" = (try init_type.getValue()).value_data.@"type",
|
||||
.@"type" = init_type_value.value_data.@"type",
|
||||
.default_value = default_value,
|
||||
// TODO: Default values
|
||||
// .@"type" = T: {
|
||||
@ -580,7 +613,9 @@ pub fn interpret(
|
||||
// .value = null,
|
||||
};
|
||||
|
||||
try type_info.@"struct".fields.append(interpreter.allocator, field);
|
||||
// std.log.info("FIELD: {s}", .{name});
|
||||
|
||||
try cont_type.getTypeInfoMutable().@"struct".fields.put(interpreter.allocator, name, field);
|
||||
} else {
|
||||
_ = try interpreter.interpret(member, container_scope, options);
|
||||
}
|
||||
@ -701,6 +736,22 @@ pub fn interpret(
|
||||
.value_data = .{ .@"bool" = false },
|
||||
} };
|
||||
|
||||
if (value.len == 5 and (value[0] == 'u' or value[0] == 'i') and std.mem.eql(u8, "size", value[1..])) return InterpretResult{
|
||||
.value = Value{
|
||||
.handle = interpreter.handle,
|
||||
.node_idx = node_idx,
|
||||
.@"type" = try interpreter.createType(node_idx, .{ .@"type" = .{} }),
|
||||
.value_data = .{
|
||||
.@"type" = try interpreter.createType(node_idx, .{
|
||||
.int = .{
|
||||
.signedness = if (value[0] == 'u') .unsigned else .signed,
|
||||
.bits = 64, // TODO: Platform specific
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (std.mem.eql(u8, "type", value)) {
|
||||
return InterpretResult{ .value = Value{
|
||||
.handle = interpreter.handle,
|
||||
@ -725,7 +776,14 @@ pub fn interpret(
|
||||
// TODO: Floats
|
||||
|
||||
// Logic to find identifiers in accessible scopes
|
||||
return InterpretResult{ .value = (try interpreter.huntItDown(scope.?, value, options)).value };
|
||||
return InterpretResult{ .value = (interpreter.huntItDown(scope.?, value, options) catch |err| {
|
||||
if (err == error.IdentifierNotFound) try interpreter.recordError(
|
||||
node_idx,
|
||||
"identifier_not_found",
|
||||
try std.fmt.allocPrint(interpreter.allocator, "Couldn't find identifier \"{s}\"", .{value}),
|
||||
);
|
||||
return err;
|
||||
}).value };
|
||||
},
|
||||
.field_access => {
|
||||
if (data[node_idx].rhs == 0) return error.CriticalAstFailure;
|
||||
@ -736,7 +794,14 @@ pub fn interpret(
|
||||
|
||||
var sub_scope = irv.value_data.@"type".getTypeInfo().getScopeOfType() orelse return error.IdentifierNotFound;
|
||||
// var scope_sub_decl = sub_scope.declarations.get(rhs_str) orelse return error.IdentifierNotFound;
|
||||
var scope_sub_decl = try irv.value_data.@"type".handle.interpreter.?.huntItDown(sub_scope, rhs_str, options);
|
||||
var scope_sub_decl = irv.value_data.@"type".handle.interpreter.?.huntItDown(sub_scope, rhs_str, options) catch |err| {
|
||||
if (err == error.IdentifierNotFound) try interpreter.recordError(
|
||||
node_idx,
|
||||
"identifier_not_found",
|
||||
try std.fmt.allocPrint(interpreter.allocator, "Couldn't find identifier \"{s}\"", .{rhs_str}),
|
||||
);
|
||||
return err;
|
||||
};
|
||||
|
||||
return InterpretResult{
|
||||
.value = scope_sub_decl.value,
|
||||
@ -884,9 +949,41 @@ pub fn interpret(
|
||||
} };
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, call_name, "@TypeOf")) {
|
||||
if (params.len != 1) return error.InvalidBuiltin;
|
||||
|
||||
const value = try (try interpreter.interpret(params[0], scope, options)).getValue();
|
||||
return InterpretResult{ .value = Value{
|
||||
.handle = interpreter.handle,
|
||||
.node_idx = node_idx,
|
||||
.@"type" = try interpreter.createType(node_idx, .{ .@"type" = .{} }),
|
||||
.value_data = .{ .@"type" = value.@"type" },
|
||||
} };
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, call_name, "@hasDecl")) {
|
||||
if (params.len != 2) return error.InvalidBuiltin;
|
||||
|
||||
const value = try (try interpreter.interpret(params[0], scope, options)).getValue();
|
||||
const field_name = try (try interpreter.interpret(params[1], scope, options)).getValue();
|
||||
|
||||
if (value.@"type".getTypeInfo() != .@"type") return error.InvalidBuiltin;
|
||||
if (field_name.@"type".getTypeInfo() != .@"pointer") return error.InvalidBuiltin; // Check if it's a []const u8
|
||||
|
||||
const ti = value.value_data.@"type".getTypeInfo();
|
||||
if (ti.getScopeOfType() == null) return error.InvalidBuiltin;
|
||||
|
||||
return InterpretResult{ .value = Value{
|
||||
.handle = interpreter.handle,
|
||||
.node_idx = node_idx,
|
||||
.@"type" = try interpreter.createType(node_idx, .{ .@"bool" = .{} }),
|
||||
.value_data = .{ .@"bool" = ti.getScopeOfType().?.declarations.contains(field_name.value_data.string) },
|
||||
} };
|
||||
}
|
||||
|
||||
std.log.info("Builtin not implemented: {s}", .{call_name});
|
||||
@panic("Builtin not implemented");
|
||||
// return error.InvalidBuiltin;
|
||||
// @panic("Builtin not implemented");
|
||||
return error.InvalidBuiltin;
|
||||
},
|
||||
.string_literal => {
|
||||
const value = tree.getNodeSource(node_idx)[1 .. tree.getNodeSource(node_idx).len - 1];
|
||||
@ -907,13 +1004,12 @@ pub fn interpret(
|
||||
.sentinel = .{ .unsigned_int = 0 },
|
||||
},
|
||||
}),
|
||||
.value_data = .{ .slice_ptr = .{} },
|
||||
.value_data = .{ .string = value },
|
||||
};
|
||||
|
||||
for (value) |z| {
|
||||
try val.value_data.slice_ptr.append(interpreter.allocator, .{ .unsigned_int = z });
|
||||
}
|
||||
try val.value_data.slice_ptr.append(interpreter.allocator, .{ .unsigned_int = 0 });
|
||||
// TODO: Add type casting, sentinel
|
||||
// TODO: Should this be a `*const [len:0]u8`?
|
||||
// try val.value_data.slice_ptr.append(interpreter.allocator, .{ .unsigned_int = 0 });
|
||||
|
||||
return InterpretResult{ .value = val };
|
||||
},
|
||||
|
@ -322,6 +322,23 @@ fn publishDiagnostics(server: *Server, writer: anytype, handle: DocumentStore.Ha
|
||||
}
|
||||
}
|
||||
|
||||
if (handle.interpreter) |int| {
|
||||
try diagnostics.ensureUnusedCapacity(allocator, int.errors.count());
|
||||
|
||||
var err_it = int.errors.iterator();
|
||||
|
||||
while (err_it.next()) |err| {
|
||||
try diagnostics.append(allocator, .{
|
||||
.range = offsets.nodeToRange(tree, err.key_ptr.*, server.offset_encoding),
|
||||
.severity = .Error,
|
||||
.code = err.value_ptr.code,
|
||||
.source = "zls",
|
||||
.message = err.value_ptr.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
// try diagnostics.appendSlice(allocator, handle.interpreter.?.diagnostics.items);
|
||||
|
||||
try send(writer, server.arena.allocator(), types.Notification{
|
||||
.method = "textDocument/publishDiagnostics",
|
||||
.params = .{
|
||||
@ -489,6 +506,16 @@ fn typeToCompletion(
|
||||
const ti = co.type.getTypeInfo();
|
||||
switch (ti) {
|
||||
.@"struct" => |st| {
|
||||
var fit = st.fields.iterator();
|
||||
while (fit.next()) |entry| {
|
||||
try list.append(allocator, .{
|
||||
.label = entry.key_ptr.*,
|
||||
.kind = .Field,
|
||||
.insertText = entry.key_ptr.*,
|
||||
.insertTextFormat = .PlainText,
|
||||
});
|
||||
}
|
||||
|
||||
var it = st.scope.declarations.iterator();
|
||||
while (it.next()) |entry| {
|
||||
try list.append(allocator, .{
|
||||
@ -2045,6 +2072,8 @@ fn hoverHandler(server: *Server, writer: anytype, id: types.RequestId, req: requ
|
||||
};
|
||||
|
||||
const hover = maybe_hover orelse return try respondGeneric(writer, id, null_result_response);
|
||||
// TODO: Figure out a better solution for comptime interpreter diags
|
||||
try server.publishDiagnostics(writer, handle.*);
|
||||
|
||||
try send(writer, server.arena.allocator(), types.Response{
|
||||
.id = id,
|
||||
|
Loading…
Reference in New Issue
Block a user