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,
|
handle: *const DocumentStore.Handle,
|
||||||
root_type: ?Type = null,
|
root_type: ?Type = null,
|
||||||
|
|
||||||
|
/// Interpreter diagnostic errors
|
||||||
|
errors: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, InterpreterError) = .{},
|
||||||
|
|
||||||
// TODO: Deduplicate typeinfo across different interpreters
|
// TODO: Deduplicate typeinfo across different interpreters
|
||||||
type_info: std.ArrayListUnmanaged(TypeInfo) = .{},
|
type_info: std.ArrayListUnmanaged(TypeInfo) = .{},
|
||||||
type_info_map: std.HashMapUnmanaged(TypeInfo, usize, TypeInfo.Context, std.hash_map.default_max_load_percentage) = .{},
|
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 {
|
pub fn deinit(interpreter: *ComptimeInterpreter) void {
|
||||||
if (interpreter.root_type) |rt| rt.getTypeInfo().getScopeOfType().?.deinit();
|
if (interpreter.root_type) |rt| rt.getTypeInfo().getScopeOfType().?.deinit();
|
||||||
for (interpreter.type_info.items) |*ti| ti.deinit(interpreter.allocator);
|
for (interpreter.type_info.items) |*ti| ti.deinit(interpreter.allocator);
|
||||||
@ -48,7 +63,7 @@ pub const TypeInfo = union(enum) {
|
|||||||
pub const Struct = struct {
|
pub const Struct = struct {
|
||||||
/// Declarations contained within
|
/// Declarations contained within
|
||||||
scope: *InterpreterScope,
|
scope: *InterpreterScope,
|
||||||
fields: std.ArrayListUnmanaged(FieldDefinition) = .{},
|
fields: std.StringHashMapUnmanaged(FieldDefinition) = .{},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Int = struct {
|
pub const Int = struct {
|
||||||
@ -116,7 +131,9 @@ pub const TypeInfo = union(enum) {
|
|||||||
context.hasher.update(&[_]u8{@enumToInt(ti)});
|
context.hasher.update(&[_]u8{@enumToInt(ti)});
|
||||||
return switch (ti) {
|
return switch (ti) {
|
||||||
.@"struct" => |s| {
|
.@"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
|
// TODO: Fix
|
||||||
// context.hasher.update(std.mem.sliceAsBytes(s.declarations.items));
|
// context.hasher.update(std.mem.sliceAsBytes(s.declarations.items));
|
||||||
},
|
},
|
||||||
@ -167,6 +184,11 @@ pub const Type = struct {
|
|||||||
pub fn getTypeInfo(@"type": Type) TypeInfo {
|
pub fn getTypeInfo(@"type": Type) TypeInfo {
|
||||||
return @"type".handle.interpreter.?.type_info.items[@"type".info_idx];
|
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 {
|
pub const Value = struct {
|
||||||
@ -191,8 +213,8 @@ pub const ValueData = union(enum) {
|
|||||||
|
|
||||||
// },
|
// },
|
||||||
// one_ptr: *anyopaque,
|
// one_ptr: *anyopaque,
|
||||||
/// TODO: Optimize this with an ArrayList that uses anyopaque slice
|
/// Special case slice; this is extremely common at comptime so it makes sense
|
||||||
slice_ptr: std.ArrayListUnmanaged(ValueData),
|
string: []const u8,
|
||||||
|
|
||||||
@"comptime_int": std.math.big.int.Managed,
|
@"comptime_int": std.math.big.int.Managed,
|
||||||
unsigned_int: u64,
|
unsigned_int: u64,
|
||||||
@ -295,9 +317,11 @@ pub const TypeInfoFormatter = struct {
|
|||||||
.@"bool" => try writer.writeAll("bool"),
|
.@"bool" => try writer.writeAll("bool"),
|
||||||
.@"struct" => |s| {
|
.@"struct" => |s| {
|
||||||
try writer.writeAll("struct {");
|
try writer.writeAll("struct {");
|
||||||
for (s.fields.items) |field| {
|
var field_iterator = s.fields.iterator();
|
||||||
try writer.print("{s}: {s}, ", .{ field.name, value.interpreter.formatTypeInfo(field.@"type".getTypeInfo()) });
|
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();
|
var iterator = s.scope.declarations.iterator();
|
||||||
while (iterator.next()) |di| {
|
while (iterator.next()) |di| {
|
||||||
const decl = di.value_ptr.*;
|
const decl = di.value_ptr.*;
|
||||||
@ -560,17 +584,26 @@ pub fn interpret(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (maybe_container_field) |field_info| {
|
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)
|
var default_value = if (field_info.ast.value_expr == 0)
|
||||||
null
|
null
|
||||||
else
|
else
|
||||||
try (try interpreter.interpret(field_info.ast.value_expr, container_scope, .{})).getValue();
|
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 name = tree.tokenSlice(field_info.ast.name_token);
|
||||||
const field = FieldDefinition{
|
const field = FieldDefinition{
|
||||||
.node_idx = member,
|
.node_idx = member,
|
||||||
.name = name,
|
.name = name,
|
||||||
.@"type" = (try init_type.getValue()).value_data.@"type",
|
.@"type" = init_type_value.value_data.@"type",
|
||||||
.default_value = default_value,
|
.default_value = default_value,
|
||||||
// TODO: Default values
|
// TODO: Default values
|
||||||
// .@"type" = T: {
|
// .@"type" = T: {
|
||||||
@ -580,7 +613,9 @@ pub fn interpret(
|
|||||||
// .value = null,
|
// .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 {
|
} else {
|
||||||
_ = try interpreter.interpret(member, container_scope, options);
|
_ = try interpreter.interpret(member, container_scope, options);
|
||||||
}
|
}
|
||||||
@ -701,6 +736,22 @@ pub fn interpret(
|
|||||||
.value_data = .{ .@"bool" = false },
|
.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)) {
|
if (std.mem.eql(u8, "type", value)) {
|
||||||
return InterpretResult{ .value = Value{
|
return InterpretResult{ .value = Value{
|
||||||
.handle = interpreter.handle,
|
.handle = interpreter.handle,
|
||||||
@ -725,7 +776,14 @@ pub fn interpret(
|
|||||||
// TODO: Floats
|
// TODO: Floats
|
||||||
|
|
||||||
// Logic to find identifiers in accessible scopes
|
// 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 => {
|
.field_access => {
|
||||||
if (data[node_idx].rhs == 0) return error.CriticalAstFailure;
|
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 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 = 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{
|
return InterpretResult{
|
||||||
.value = scope_sub_decl.value,
|
.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});
|
std.log.info("Builtin not implemented: {s}", .{call_name});
|
||||||
@panic("Builtin not implemented");
|
// @panic("Builtin not implemented");
|
||||||
// return error.InvalidBuiltin;
|
return error.InvalidBuiltin;
|
||||||
},
|
},
|
||||||
.string_literal => {
|
.string_literal => {
|
||||||
const value = tree.getNodeSource(node_idx)[1 .. tree.getNodeSource(node_idx).len - 1];
|
const value = tree.getNodeSource(node_idx)[1 .. tree.getNodeSource(node_idx).len - 1];
|
||||||
@ -907,13 +1004,12 @@ pub fn interpret(
|
|||||||
.sentinel = .{ .unsigned_int = 0 },
|
.sentinel = .{ .unsigned_int = 0 },
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
.value_data = .{ .slice_ptr = .{} },
|
.value_data = .{ .string = value },
|
||||||
};
|
};
|
||||||
|
|
||||||
for (value) |z| {
|
// TODO: Add type casting, sentinel
|
||||||
try val.value_data.slice_ptr.append(interpreter.allocator, .{ .unsigned_int = z });
|
// TODO: Should this be a `*const [len:0]u8`?
|
||||||
}
|
// try val.value_data.slice_ptr.append(interpreter.allocator, .{ .unsigned_int = 0 });
|
||||||
try val.value_data.slice_ptr.append(interpreter.allocator, .{ .unsigned_int = 0 });
|
|
||||||
|
|
||||||
return InterpretResult{ .value = val };
|
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{
|
try send(writer, server.arena.allocator(), types.Notification{
|
||||||
.method = "textDocument/publishDiagnostics",
|
.method = "textDocument/publishDiagnostics",
|
||||||
.params = .{
|
.params = .{
|
||||||
@ -489,6 +506,16 @@ fn typeToCompletion(
|
|||||||
const ti = co.type.getTypeInfo();
|
const ti = co.type.getTypeInfo();
|
||||||
switch (ti) {
|
switch (ti) {
|
||||||
.@"struct" => |st| {
|
.@"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();
|
var it = st.scope.declarations.iterator();
|
||||||
while (it.next()) |entry| {
|
while (it.next()) |entry| {
|
||||||
try list.append(allocator, .{
|
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);
|
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{
|
try send(writer, server.arena.allocator(), types.Response{
|
||||||
.id = id,
|
.id = id,
|
||||||
|
Loading…
Reference in New Issue
Block a user