Skip to content

Commit 57f6ebf

Browse files
committed
Vendor zig Wasm renderer: ziglang/zig#20958
1 parent b27feb1 commit 57f6ebf

File tree

8 files changed

+5898
-0
lines changed

8 files changed

+5898
-0
lines changed

zig_docs/Decl.zig

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
const Decl = @This();
2+
const std = @import("std");
3+
const Ast = std.zig.Ast;
4+
const Walk = @import("Walk.zig");
5+
const gpa = std.heap.wasm_allocator;
6+
const assert = std.debug.assert;
7+
const log = std.log;
8+
const Oom = error{OutOfMemory};
9+
10+
ast_node: Ast.Node.Index,
11+
file: Walk.File.Index,
12+
/// The decl whose namespace this is in.
13+
parent: Index,
14+
15+
pub const ExtraInfo = struct {
16+
is_pub: bool,
17+
name: []const u8,
18+
/// This might not be a doc_comment token in which case there are no doc comments.
19+
first_doc_comment: Ast.TokenIndex,
20+
};
21+
22+
pub const Index = enum(u32) {
23+
none = std.math.maxInt(u32),
24+
_,
25+
26+
pub fn get(i: Index) *Decl {
27+
return &Walk.decls.items[@intFromEnum(i)];
28+
}
29+
};
30+
31+
pub fn is_pub(d: *const Decl) bool {
32+
return d.extra_info().is_pub;
33+
}
34+
35+
pub fn extra_info(d: *const Decl) ExtraInfo {
36+
const ast = d.file.get_ast();
37+
const token_tags = ast.tokens.items(.tag);
38+
const node_tags = ast.nodes.items(.tag);
39+
switch (node_tags[d.ast_node]) {
40+
.root => return .{
41+
.name = "",
42+
.is_pub = true,
43+
.first_doc_comment = if (token_tags[0] == .container_doc_comment)
44+
0
45+
else
46+
token_tags.len - 1,
47+
},
48+
49+
.global_var_decl,
50+
.local_var_decl,
51+
.simple_var_decl,
52+
.aligned_var_decl,
53+
=> {
54+
const var_decl = ast.fullVarDecl(d.ast_node).?;
55+
const name_token = var_decl.ast.mut_token + 1;
56+
assert(token_tags[name_token] == .identifier);
57+
const ident_name = ast.tokenSlice(name_token);
58+
return .{
59+
.name = ident_name,
60+
.is_pub = var_decl.visib_token != null,
61+
.first_doc_comment = findFirstDocComment(ast, var_decl.firstToken()),
62+
};
63+
},
64+
65+
.fn_proto,
66+
.fn_proto_multi,
67+
.fn_proto_one,
68+
.fn_proto_simple,
69+
.fn_decl,
70+
=> {
71+
var buf: [1]Ast.Node.Index = undefined;
72+
const fn_proto = ast.fullFnProto(&buf, d.ast_node).?;
73+
const name_token = fn_proto.name_token.?;
74+
assert(token_tags[name_token] == .identifier);
75+
const ident_name = ast.tokenSlice(name_token);
76+
return .{
77+
.name = ident_name,
78+
.is_pub = fn_proto.visib_token != null,
79+
.first_doc_comment = findFirstDocComment(ast, fn_proto.firstToken()),
80+
};
81+
},
82+
83+
else => |t| {
84+
log.debug("hit '{s}'", .{@tagName(t)});
85+
unreachable;
86+
},
87+
}
88+
}
89+
90+
pub fn value_node(d: *const Decl) ?Ast.Node.Index {
91+
const ast = d.file.get_ast();
92+
const node_tags = ast.nodes.items(.tag);
93+
const token_tags = ast.tokens.items(.tag);
94+
return switch (node_tags[d.ast_node]) {
95+
.fn_proto,
96+
.fn_proto_multi,
97+
.fn_proto_one,
98+
.fn_proto_simple,
99+
.fn_decl,
100+
.root,
101+
=> d.ast_node,
102+
103+
.global_var_decl,
104+
.local_var_decl,
105+
.simple_var_decl,
106+
.aligned_var_decl,
107+
=> {
108+
const var_decl = ast.fullVarDecl(d.ast_node).?;
109+
if (token_tags[var_decl.ast.mut_token] == .keyword_const)
110+
return var_decl.ast.init_node;
111+
112+
return null;
113+
},
114+
115+
else => null,
116+
};
117+
}
118+
119+
pub fn categorize(decl: *const Decl) Walk.Category {
120+
return decl.file.categorize_decl(decl.ast_node);
121+
}
122+
123+
/// Looks up a direct child of `decl` by name.
124+
pub fn get_child(decl: *const Decl, name: []const u8) ?Decl.Index {
125+
switch (decl.categorize()) {
126+
.alias => |aliasee| return aliasee.get().get_child(name),
127+
.namespace, .container => |node| {
128+
const file = decl.file.get();
129+
const scope = file.scopes.get(node) orelse return null;
130+
const child_node = scope.get_child(name) orelse return null;
131+
return file.node_decls.get(child_node);
132+
},
133+
else => return null,
134+
}
135+
}
136+
137+
/// Looks up a decl by name accessible in `decl`'s namespace.
138+
pub fn lookup(decl: *const Decl, name: []const u8) ?Decl.Index {
139+
const namespace_node = switch (decl.categorize()) {
140+
.namespace, .container => |node| node,
141+
else => decl.parent.get().ast_node,
142+
};
143+
const file = decl.file.get();
144+
const scope = file.scopes.get(namespace_node) orelse return null;
145+
const resolved_node = scope.lookup(&file.ast, name) orelse return null;
146+
return file.node_decls.get(resolved_node);
147+
}
148+
149+
/// Appends the fully qualified name to `out`.
150+
pub fn fqn(decl: *const Decl, out: *std.ArrayListUnmanaged(u8)) Oom!void {
151+
try decl.append_path(out);
152+
if (decl.parent != .none) {
153+
try append_parent_ns(out, decl.parent);
154+
try out.appendSlice(gpa, decl.extra_info().name);
155+
} else {
156+
out.items.len -= 1; // remove the trailing '.'
157+
}
158+
}
159+
160+
pub fn reset_with_path(decl: *const Decl, list: *std.ArrayListUnmanaged(u8)) Oom!void {
161+
list.clearRetainingCapacity();
162+
try append_path(decl, list);
163+
}
164+
165+
pub fn append_path(decl: *const Decl, list: *std.ArrayListUnmanaged(u8)) Oom!void {
166+
const start = list.items.len;
167+
// Prefer the module name alias.
168+
for (Walk.modules.keys(), Walk.modules.values()) |pkg_name, pkg_file| {
169+
if (pkg_file == decl.file) {
170+
try list.ensureUnusedCapacity(gpa, pkg_name.len + 1);
171+
list.appendSliceAssumeCapacity(pkg_name);
172+
list.appendAssumeCapacity('.');
173+
return;
174+
}
175+
}
176+
177+
const file_path = decl.file.path();
178+
try list.ensureUnusedCapacity(gpa, file_path.len + 1);
179+
list.appendSliceAssumeCapacity(file_path);
180+
for (list.items[start..]) |*byte| switch (byte.*) {
181+
'/' => byte.* = '.',
182+
else => continue,
183+
};
184+
if (std.mem.endsWith(u8, list.items, ".zig")) {
185+
list.items.len -= 3;
186+
} else {
187+
list.appendAssumeCapacity('.');
188+
}
189+
}
190+
191+
pub fn append_parent_ns(list: *std.ArrayListUnmanaged(u8), parent: Decl.Index) Oom!void {
192+
assert(parent != .none);
193+
const decl = parent.get();
194+
if (decl.parent != .none) {
195+
try append_parent_ns(list, decl.parent);
196+
try list.appendSlice(gpa, decl.extra_info().name);
197+
try list.append(gpa, '.');
198+
}
199+
}
200+
201+
pub fn findFirstDocComment(ast: *const Ast, token: Ast.TokenIndex) Ast.TokenIndex {
202+
const token_tags = ast.tokens.items(.tag);
203+
var it = token;
204+
while (it > 0) {
205+
it -= 1;
206+
if (token_tags[it] != .doc_comment) {
207+
return it + 1;
208+
}
209+
}
210+
return it;
211+
}
212+
213+
/// Successively looks up each component.
214+
pub fn find(search_string: []const u8) Decl.Index {
215+
var path_components = std.mem.splitScalar(u8, search_string, '.');
216+
const file = Walk.modules.get(path_components.first()) orelse return .none;
217+
var current_decl_index = file.findRootDecl();
218+
while (path_components.next()) |component| {
219+
while (true) switch (current_decl_index.get().categorize()) {
220+
.alias => |aliasee| current_decl_index = aliasee,
221+
else => break,
222+
};
223+
current_decl_index = current_decl_index.get().get_child(component) orelse return .none;
224+
}
225+
return current_decl_index;
226+
}

0 commit comments

Comments
 (0)