Some builtins, rudimentary hacky diagnostics; need to nerf global evaluation

This commit is contained in:
Auguste Rame 2022-10-30 04:07:49 -04:00
parent 599c134593
commit d2e166bb0b
No known key found for this signature in database
GPG Key ID: 3A5E3F90DF2AAEFE
2 changed files with 143 additions and 18 deletions

View File

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

View File

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