Added symbol tables
This commit is contained in:
parent
c2f8537d7b
commit
0a9cf87585
402
src/analysis.zig
402
src/analysis.zig
@ -1013,9 +1013,7 @@ pub fn declsFromIndexInternal(
|
||||
}
|
||||
}
|
||||
},
|
||||
.Use => {
|
||||
|
||||
},
|
||||
.Use => {},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
@ -1288,3 +1286,401 @@ pub fn getDocumentSymbols(allocator: *std.mem.Allocator, tree: *ast.Tree) ![]typ
|
||||
|
||||
return symbols.items;
|
||||
}
|
||||
|
||||
const DocumentStore = @import("document_store.zig");
|
||||
|
||||
// TODO Will I need to store a handle with the nodes?
|
||||
pub const Declaration = union(enum) {
|
||||
ast_node: *ast.Node,
|
||||
param_decl: *ast.Node.FnProto.ParamDecl,
|
||||
pointer_payload: struct {
|
||||
node: *ast.Node.PointerPayload,
|
||||
condition: *ast.Node,
|
||||
},
|
||||
array_payload: struct {
|
||||
identifier: *ast.Node,
|
||||
array_expr: *ast.Node,
|
||||
},
|
||||
switch_payload: struct {
|
||||
node: *ast.Node.PointerPayload,
|
||||
items: []const *ast.Node,
|
||||
},
|
||||
};
|
||||
|
||||
pub const DocumentScope = struct {
|
||||
scopes: []const Scope,
|
||||
|
||||
// TODO Methods
|
||||
|
||||
// TODO When looking up a symbol, have an accept_field argument.
|
||||
|
||||
pub fn debugPrint(self: DocumentScope) void {
|
||||
for (self.scopes) |scope| {
|
||||
std.debug.warn(
|
||||
\\--------------------------
|
||||
\\Scope {}, range: [{}, {})
|
||||
\\ {} usingnamespaces
|
||||
\\Decls:
|
||||
, .{
|
||||
scope.data,
|
||||
scope.range.start,
|
||||
scope.range.end,
|
||||
scope.uses.len,
|
||||
});
|
||||
|
||||
var decl_it = scope.decls.iterator();
|
||||
var idx: usize = 0;
|
||||
while (decl_it.next()) |name_decl| : (idx += 1) {
|
||||
if (idx != 0) std.debug.warn(", ", .{});
|
||||
std.debug.warn("{}", .{name_decl.key});
|
||||
}
|
||||
std.debug.warn("\n--------------------------\n", .{});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: DocumentScope, allocator: *std.mem.Allocator) void {
|
||||
for (self.scopes) |scope| {
|
||||
scope.decls.deinit();
|
||||
allocator.free(scope.uses);
|
||||
}
|
||||
allocator.free(self.scopes);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Scope = struct {
|
||||
pub const Data = union(enum) {
|
||||
container: *ast.Node, // .id is ContainerDecl or Root
|
||||
function: *ast.Node, // .id is FnProto
|
||||
block: *ast.Node, // .id is Block
|
||||
other,
|
||||
};
|
||||
|
||||
range: SourceRange,
|
||||
decls: std.StringHashMap(Declaration),
|
||||
uses: []const *ast.Node.Use,
|
||||
|
||||
data: Data,
|
||||
|
||||
// TODO Methods
|
||||
};
|
||||
|
||||
pub fn makeDocumentScope(allocator: *std.mem.Allocator, tree: *ast.Tree) !DocumentScope {
|
||||
var scopes = std.ArrayList(Scope).init(allocator);
|
||||
errdefer scopes.deinit();
|
||||
|
||||
try makeScopeInternal(allocator, &scopes, tree, &tree.root_node.base);
|
||||
return DocumentScope{
|
||||
.scopes = scopes.toOwnedSlice(),
|
||||
};
|
||||
}
|
||||
|
||||
fn nodeSourceRange(tree: *ast.Tree, node: *ast.Node) SourceRange {
|
||||
return SourceRange{
|
||||
.start = tree.token_locs[node.firstToken()].start,
|
||||
.end = tree.token_locs[node.lastToken()].end,
|
||||
};
|
||||
}
|
||||
|
||||
fn makeScopeInternal(
|
||||
allocator: *std.mem.Allocator,
|
||||
scopes: *std.ArrayList(Scope),
|
||||
tree: *ast.Tree,
|
||||
node: *ast.Node,
|
||||
) error{OutOfMemory}!void {
|
||||
if (node.id == .Root or node.id == .ContainerDecl) {
|
||||
const ast_decls = switch (node.id) {
|
||||
.ContainerDecl => node.cast(ast.Node.ContainerDecl).?.fieldsAndDeclsConst(),
|
||||
.Root => node.cast(ast.Node.Root).?.declsConst(),
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
(try scopes.addOne()).* = .{
|
||||
.range = nodeSourceRange(tree, node),
|
||||
.decls = std.StringHashMap(Declaration).init(allocator),
|
||||
.uses = &[0]*ast.Node.Use{},
|
||||
.data = .{ .container = node },
|
||||
};
|
||||
var scope_idx = scopes.items.len - 1;
|
||||
var uses = std.ArrayList(*ast.Node.Use).init(allocator);
|
||||
|
||||
errdefer {
|
||||
scopes.items[scope_idx].decls.deinit();
|
||||
uses.deinit();
|
||||
}
|
||||
|
||||
for (ast_decls) |decl| {
|
||||
if (decl.cast(ast.Node.Use)) |use| {
|
||||
try uses.append(use);
|
||||
continue;
|
||||
}
|
||||
|
||||
try makeScopeInternal(allocator, scopes, tree, decl);
|
||||
const name = getDeclName(tree, decl) orelse continue;
|
||||
if (try scopes.items[scope_idx].decls.put(name, .{ .ast_node = decl })) |existing| {
|
||||
// TODO Record a redefinition error.
|
||||
}
|
||||
}
|
||||
|
||||
scopes.items[scope_idx].uses = uses.toOwnedSlice();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (node.id) {
|
||||
.FnProto => {
|
||||
const func = node.cast(ast.Node.FnProto).?;
|
||||
|
||||
(try scopes.addOne()).* = .{
|
||||
.range = nodeSourceRange(tree, node),
|
||||
.decls = std.StringHashMap(Declaration).init(allocator),
|
||||
.uses = &[0]*ast.Node.Use{},
|
||||
.data = .{ .function = node },
|
||||
};
|
||||
var scope_idx = scopes.items.len - 1;
|
||||
errdefer scopes.items[scope_idx].decls.deinit();
|
||||
|
||||
for (func.params()) |*param| {
|
||||
if (param.name_token) |name_tok| {
|
||||
if (try scopes.items[scope_idx].decls.put(tree.tokenSlice(name_tok), .{ .param_decl = param })) |existing| {
|
||||
// TODO Record a redefinition error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (func.body_node) |body| {
|
||||
try makeScopeInternal(allocator, scopes, tree, body);
|
||||
}
|
||||
|
||||
return;
|
||||
},
|
||||
.TestDecl => {
|
||||
return try makeScopeInternal(allocator, scopes, tree, node.cast(ast.Node.TestDecl).?.body_node);
|
||||
},
|
||||
.Block => {
|
||||
(try scopes.addOne()).* = .{
|
||||
.range = nodeSourceRange(tree, node),
|
||||
.decls = std.StringHashMap(Declaration).init(allocator),
|
||||
.uses = &[0]*ast.Node.Use{},
|
||||
.data = .{ .block = node },
|
||||
};
|
||||
var scope_idx = scopes.items.len - 1;
|
||||
var uses = std.ArrayList(*ast.Node.Use).init(allocator);
|
||||
|
||||
errdefer {
|
||||
scopes.items[scope_idx].decls.deinit();
|
||||
uses.deinit();
|
||||
}
|
||||
|
||||
var child_idx: usize = 0;
|
||||
while (node.iterate(child_idx)) |child_node| : (child_idx += 1) {
|
||||
if (child_node.cast(ast.Node.Use)) |use| {
|
||||
try uses.append(use);
|
||||
continue;
|
||||
}
|
||||
|
||||
try makeScopeInternal(allocator, scopes, tree, child_node);
|
||||
if (child_node.cast(ast.Node.VarDecl)) |var_decl| {
|
||||
const name = tree.tokenSlice(var_decl.name_token);
|
||||
if (try scopes.items[scope_idx].decls.put(name, .{ .ast_node = child_node })) |existing| {
|
||||
// TODO Record a redefinition error.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scopes.items[scope_idx].uses = uses.toOwnedSlice();
|
||||
return;
|
||||
},
|
||||
.Comptime => {
|
||||
return try makeScopeInternal(allocator, scopes, tree, node.cast(ast.Node.Comptime).?.expr);
|
||||
},
|
||||
.If => {
|
||||
const if_node = node.cast(ast.Node.If).?;
|
||||
|
||||
if (if_node.payload) |payload| {
|
||||
std.debug.assert(payload.id == .PointerPayload);
|
||||
var scope = try scopes.addOne();
|
||||
scope.* = .{
|
||||
.range = .{
|
||||
.start = tree.token_locs[payload.firstToken()].start,
|
||||
.end = tree.token_locs[if_node.body.lastToken()].end,
|
||||
},
|
||||
.decls = std.StringHashMap(Declaration).init(allocator),
|
||||
.uses = &[0]*ast.Node.Use{},
|
||||
.data = .other,
|
||||
};
|
||||
errdefer scope.decls.deinit();
|
||||
|
||||
const ptr_payload = payload.cast(ast.Node.PointerPayload).?;
|
||||
std.debug.assert(ptr_payload.value_symbol.id == .Identifier);
|
||||
const name = tree.tokenSlice(ptr_payload.value_symbol.firstToken());
|
||||
try scope.decls.putNoClobber(name, .{
|
||||
.pointer_payload = .{
|
||||
.node = ptr_payload,
|
||||
.condition = if_node.condition,
|
||||
},
|
||||
});
|
||||
}
|
||||
try makeScopeInternal(allocator, scopes, tree, if_node.body);
|
||||
|
||||
if (if_node.@"else") |else_node| {
|
||||
if (else_node.payload) |payload| {
|
||||
std.debug.assert(payload.id == .Payload);
|
||||
var scope = try scopes.addOne();
|
||||
scope.* = .{
|
||||
.range = .{
|
||||
.start = tree.token_locs[payload.firstToken()].start,
|
||||
.end = tree.token_locs[else_node.body.lastToken()].end,
|
||||
},
|
||||
.decls = std.StringHashMap(Declaration).init(allocator),
|
||||
.uses = &[0]*ast.Node.Use{},
|
||||
.data = .other,
|
||||
};
|
||||
errdefer scope.decls.deinit();
|
||||
|
||||
const err_payload = payload.cast(ast.Node.Payload).?;
|
||||
std.debug.assert(err_payload.error_symbol.id == .Identifier);
|
||||
const name = tree.tokenSlice(err_payload.error_symbol.firstToken());
|
||||
try scope.decls.putNoClobber(name, .{ .ast_node = payload });
|
||||
}
|
||||
try makeScopeInternal(allocator, scopes, tree, else_node.body);
|
||||
}
|
||||
},
|
||||
.While => {
|
||||
const while_node = node.cast(ast.Node.While).?;
|
||||
if (while_node.payload) |payload| {
|
||||
std.debug.assert(payload.id == .PointerPayload);
|
||||
var scope = try scopes.addOne();
|
||||
scope.* = .{
|
||||
.range = .{
|
||||
.start = tree.token_locs[payload.firstToken()].start,
|
||||
.end = tree.token_locs[while_node.body.lastToken()].end,
|
||||
},
|
||||
.decls = std.StringHashMap(Declaration).init(allocator),
|
||||
.uses = &[0]*ast.Node.Use{},
|
||||
.data = .other,
|
||||
};
|
||||
errdefer scope.decls.deinit();
|
||||
|
||||
const ptr_payload = payload.cast(ast.Node.PointerPayload).?;
|
||||
std.debug.assert(ptr_payload.value_symbol.id == .Identifier);
|
||||
const name = tree.tokenSlice(ptr_payload.value_symbol.firstToken());
|
||||
try scope.decls.putNoClobber(name, .{
|
||||
.pointer_payload = .{
|
||||
.node = ptr_payload,
|
||||
.condition = while_node.condition,
|
||||
},
|
||||
});
|
||||
}
|
||||
try makeScopeInternal(allocator, scopes, tree, while_node.body);
|
||||
|
||||
if (while_node.@"else") |else_node| {
|
||||
if (else_node.payload) |payload| {
|
||||
std.debug.assert(payload.id == .Payload);
|
||||
var scope = try scopes.addOne();
|
||||
scope.* = .{
|
||||
.range = .{
|
||||
.start = tree.token_locs[payload.firstToken()].start,
|
||||
.end = tree.token_locs[else_node.body.lastToken()].end,
|
||||
},
|
||||
.decls = std.StringHashMap(Declaration).init(allocator),
|
||||
.uses = &[0]*ast.Node.Use{},
|
||||
.data = .other,
|
||||
};
|
||||
errdefer scope.decls.deinit();
|
||||
|
||||
const err_payload = payload.cast(ast.Node.Payload).?;
|
||||
std.debug.assert(err_payload.error_symbol.id == .Identifier);
|
||||
const name = tree.tokenSlice(err_payload.error_symbol.firstToken());
|
||||
try scope.decls.putNoClobber(name, .{ .ast_node = payload });
|
||||
}
|
||||
try makeScopeInternal(allocator, scopes, tree, else_node.body);
|
||||
}
|
||||
},
|
||||
.For => {
|
||||
const for_node = node.cast(ast.Node.For).?;
|
||||
std.debug.assert(for_node.payload.id == .PointerIndexPayload);
|
||||
const ptr_idx_payload = for_node.payload.cast(ast.Node.PointerIndexPayload).?;
|
||||
std.debug.assert(ptr_idx_payload.value_symbol.id == .Identifier);
|
||||
|
||||
var scope = try scopes.addOne();
|
||||
scope.* = .{
|
||||
.range = .{
|
||||
.start = tree.token_locs[ptr_idx_payload.firstToken()].start,
|
||||
.end = tree.token_locs[for_node.body.lastToken()].end,
|
||||
},
|
||||
.decls = std.StringHashMap(Declaration).init(allocator),
|
||||
.uses = &[0]*ast.Node.Use{},
|
||||
.data = .other,
|
||||
};
|
||||
errdefer scope.decls.deinit();
|
||||
|
||||
const value_name = tree.tokenSlice(ptr_idx_payload.value_symbol.firstToken());
|
||||
try scope.decls.putNoClobber(value_name, .{
|
||||
.array_payload = .{
|
||||
.identifier = ptr_idx_payload.value_symbol,
|
||||
.array_expr = for_node.array_expr,
|
||||
},
|
||||
});
|
||||
|
||||
if (ptr_idx_payload.index_symbol) |index_symbol| {
|
||||
std.debug.assert(index_symbol.id == .Identifier);
|
||||
const index_name = tree.tokenSlice(index_symbol.firstToken());
|
||||
if (try scope.decls.put(index_name, .{ .ast_node = index_symbol })) |existing| {
|
||||
// TODO Record a redefinition error
|
||||
}
|
||||
}
|
||||
|
||||
try makeScopeInternal(allocator, scopes, tree, for_node.body);
|
||||
if (for_node.@"else") |else_node| {
|
||||
std.debug.assert(else_node.payload == null);
|
||||
try makeScopeInternal(allocator, scopes, tree, else_node.body);
|
||||
}
|
||||
},
|
||||
.Switch => {
|
||||
const switch_node = node.cast(ast.Node.Switch).?;
|
||||
for (switch_node.casesConst()) |case| {
|
||||
if (case.*.cast(ast.Node.SwitchCase)) |case_node| {
|
||||
if (case_node.payload) |payload| {
|
||||
std.debug.assert(payload.id == .PointerPayload);
|
||||
var scope = try scopes.addOne();
|
||||
scope.* = .{
|
||||
.range = .{
|
||||
.start = tree.token_locs[payload.firstToken()].start,
|
||||
.end = tree.token_locs[case_node.expr.lastToken()].end,
|
||||
},
|
||||
.decls = std.StringHashMap(Declaration).init(allocator),
|
||||
.uses = &[0]*ast.Node.Use{},
|
||||
.data = .other,
|
||||
};
|
||||
errdefer scope.decls.deinit();
|
||||
|
||||
const ptr_payload = payload.cast(ast.Node.PointerPayload).?;
|
||||
std.debug.assert(ptr_payload.value_symbol.id == .Identifier);
|
||||
const name = tree.tokenSlice(ptr_payload.value_symbol.firstToken());
|
||||
try scope.decls.putNoClobber(name, .{
|
||||
.switch_payload = .{
|
||||
.node = ptr_payload,
|
||||
.items = case_node.itemsConst(),
|
||||
},
|
||||
});
|
||||
}
|
||||
try makeScopeInternal(allocator, scopes, tree, case_node.expr);
|
||||
}
|
||||
}
|
||||
},
|
||||
.VarDecl => {
|
||||
const var_decl = node.cast(ast.Node.VarDecl).?;
|
||||
if (var_decl.type_node) |type_node| {
|
||||
try makeScopeInternal(allocator, scopes, tree, type_node);
|
||||
}
|
||||
if (var_decl.init_node) |init_node| {
|
||||
try makeScopeInternal(allocator, scopes, tree, init_node);
|
||||
}
|
||||
},
|
||||
else => {
|
||||
var child_idx: usize = 0;
|
||||
while (node.iterate(child_idx)) |child_node| : (child_idx += 1) {
|
||||
try makeScopeInternal(allocator, scopes, tree, child_node);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ pub const Handle = struct {
|
||||
count: usize,
|
||||
import_uris: std.ArrayList([]const u8),
|
||||
tree: *std.zig.ast.Tree,
|
||||
document_scope: analysis.DocumentScope,
|
||||
|
||||
associated_build_file: ?*BuildFile,
|
||||
is_build_file: ?*BuildFile,
|
||||
@ -191,6 +192,12 @@ fn newDocument(self: *DocumentStore, uri: []const u8, text: []u8) anyerror!*Hand
|
||||
var handle = try self.allocator.create(Handle);
|
||||
errdefer self.allocator.destroy(handle);
|
||||
|
||||
const tree = try std.zig.parse(self.allocator, text);
|
||||
errdefer tree.deinit();
|
||||
|
||||
const document_scope = try analysis.makeDocumentScope(self.allocator, tree);
|
||||
errdefer document_scope.deinit(self.allocator);
|
||||
|
||||
handle.* = Handle{
|
||||
.count = 1,
|
||||
.import_uris = std.ArrayList([]const u8).init(self.allocator),
|
||||
@ -199,7 +206,8 @@ fn newDocument(self: *DocumentStore, uri: []const u8, text: []u8) anyerror!*Hand
|
||||
.text = text,
|
||||
.mem = text,
|
||||
},
|
||||
.tree = try std.zig.parse(self.allocator, text),
|
||||
.tree = tree,
|
||||
.document_scope = document_scope,
|
||||
.associated_build_file = null,
|
||||
.is_build_file = null,
|
||||
};
|
||||
@ -362,6 +370,9 @@ fn refreshDocument(self: *DocumentStore, handle: *Handle, zig_lib_path: ?[]const
|
||||
handle.tree.deinit();
|
||||
handle.tree = try std.zig.parse(self.allocator, handle.document.text);
|
||||
|
||||
handle.document_scope.deinit(self.allocator);
|
||||
handle.document_scope = try analysis.makeDocumentScope(self.allocator, handle.tree);
|
||||
|
||||
// TODO: Better algorithm or data structure?
|
||||
// Removing the imports is costly since they live in an array list
|
||||
// Perhaps we should use an AutoHashMap([]const u8, {}) ?
|
||||
@ -700,6 +711,8 @@ pub fn deinit(self: *DocumentStore) void {
|
||||
entry.value.import_uris.deinit();
|
||||
self.allocator.free(entry.key);
|
||||
self.allocator.destroy(entry.value);
|
||||
|
||||
entry.value.document_scope.deinit(self.allocator);
|
||||
}
|
||||
|
||||
self.handles.deinit();
|
||||
|
Loading…
Reference in New Issue
Block a user