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

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