Skip to content

Commit 25c7c94

Browse files
on-keydayclaude
andcommitted
ast: print side table entries alongside the tree
pretty_print only walked the arena, so there was no way to see what a binder had written. SideTables also had no way to enumerate its tables - table<T>() only reaches one you already name - so for_each_table is generated now, the counterpart of Arena::for_each_pool. `-- statements[0]: Field #4 |-- [DocComment].leading = 3:0 |-- [IsMutated] = true `-- name: Ident #5 |-- [Resolution].target -> Field #4 `-- identifier = "value" All three storage kinds render: dense and sparse through the entry's for_each_field, flag as bare presence since it carries no value. Tables are keyed by id regardless of the node type they declare, so the key is rebuilt from the runtime type. A Node inside an entry is shown as a reference rather than descended into. Following it overflows the stack - Resolution points from an Ident back to the Field that owns it, so Field -> name -> Ident -> [Resolution].target -> Field repeats. A side table is not an owning edge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 73fbdee commit 25c7c94

3 files changed

Lines changed: 71 additions & 1 deletion

File tree

src/core/nast/gen/tables.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,12 @@ def _emit_aggregate(w: Writer, schema: Schema) -> None:
222222
w.write(" public:\n")
223223
w.write(f" template<> constexpr auto& table<{name}>() {{ return {name}_; }}\n")
224224
w.write(f" template<> constexpr const auto& table<{name}>() const {{ return {name}_; }}\n")
225+
# 表を名前つきで列挙する。Arena::for_each_pool と同じ役目で、
226+
# どの表があるかを静的に知らない側 (printer など) が回すのに要る。
227+
w.write(" constexpr void for_each_table(auto&& f_) const {\n")
228+
for name in names:
229+
w.write(f' f_("{name}",{name}_);\n')
230+
w.write(" }\n")
225231
w.write(" void as_json(auto&& s) const {\n")
226232
w.write(" auto obj_ = s.object();\n")
227233
for name in names:

src/core/nast/printer.h

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,25 @@ namespace brgen::nast {
2323
// 「そのノードが持ちうるフィールド」が全部並ぶので、
2424
// parser がどこを埋め損ねたかを見るときに要る。
2525
bool show_null = false;
26+
// side table のエントリを、キーになっているノードの下に併記する。
27+
// binder が何をどこに書いたかを木の形のまま見るためのもの。
28+
bool show_tables = true;
2629
};
2730

2831
struct PrettyPrinter {
2932
Arena* arena = nullptr;
33+
// 省略可。渡すと各ノードの下にそのノードを指す表の中身を出す。
34+
const SideTables* tables = nullptr;
3035
std::string out;
3136
bool show_weak = true;
3237
bool show_null = false;
38+
bool show_tables = true;
3339

3440
explicit PrettyPrinter(Arena& a, PrintOptions opt = {})
35-
: arena(&a), show_weak(opt.show_weak), show_null(opt.show_null) {}
41+
: arena(&a), show_weak(opt.show_weak), show_null(opt.show_null), show_tables(opt.show_tables) {}
42+
43+
PrettyPrinter(Arena& a, const SideTables& t, PrintOptions opt = {})
44+
: arena(&a), tables(&t), show_weak(opt.show_weak), show_null(opt.show_null), show_tables(opt.show_tables) {}
3645

3746
template <class T>
3847
void print(Node<T> root) {
@@ -97,6 +106,36 @@ namespace brgen::nast {
97106
items.push_back(PrintItem{name, 0, NodeType{}, false, false, scalar_of(v)});
98107
}
99108

109+
// このノードを指す side table の中身を項目として足す。
110+
// 表は node_type で型付けされているが contains/get は id しか見ないので、
111+
// 実行時の型で Node を組み直して引く。
112+
void add_table_entries(std::vector<PrintItem>& items, std::uint32_t id, NodeType type) {
113+
if (!tables || !show_tables) {
114+
return;
115+
}
116+
tables->for_each_table([&](const char* table_name, const auto& table) {
117+
using table_t = std::decay_t<decltype(table)>;
118+
using key_node = Node<typename table_t::node_type>;
119+
auto key = key_node::from_unique_id((std::uint64_t(type) << 32) | id);
120+
if (!table.contains(key)) {
121+
return;
122+
}
123+
if constexpr (requires { table.get(key); }) {
124+
if (const auto* entry = table.get(key)) {
125+
entry->for_each_field([&](const char* field, const auto& v, bool) {
126+
// 表の中の Node は所有辺ではない。降りると
127+
// Ident -> [Resolution].target -> Field -> name -> Ident で回る。
128+
add(items, (std::string("[") + table_name + "]." + field).c_str(), v, true);
129+
});
130+
return;
131+
}
132+
}
133+
// flag は値を持たないので、在ることだけを出す
134+
items.push_back(PrintItem{std::string("[") + table_name + "]", 0, NodeType{},
135+
false, false, "true"});
136+
});
137+
}
138+
100139
void walk(std::uint32_t id, const std::string& prefix, const std::string& branch, bool last) {
101140
out += prefix;
102141
out += branch;
@@ -117,6 +156,7 @@ namespace brgen::nast {
117156
out += "\n";
118157

119158
std::vector<PrintItem> items;
159+
add_table_entries(items, id, h->type);
120160
auto index = h->data_index;
121161
visit_node_type(h->type, [&](auto tag) {
122162
using T = typename decltype(tag)::type;
@@ -159,4 +199,12 @@ namespace brgen::nast {
159199
return std::move(p.out);
160200
}
161201

202+
// 表つき。binder が何をどこに書いたかを木の形のまま見る。
203+
template <class T>
204+
std::string pretty_print(Arena& a, const SideTables& tables, Node<T> root, PrintOptions opt = {}) {
205+
PrettyPrinter p{a, tables, opt};
206+
p.print(root);
207+
return std::move(p.out);
208+
}
209+
162210
} // namespace brgen::nast

src/core/nast/test.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,22 @@ int main(int argc, char** argv) {
244244
check(std::count(text.begin(), text.end(), '\n') < 40,
245245
"weak edges do not cause the walk to recurse");
246246

247+
// ---- side table を木に併記する ------------------------------------
248+
// binder が何をどこに書いたかを、木の形のまま見るためのもの。
249+
SideTables pt;
250+
pt.table<Resolution>().set(fld_name.id(), Resolution{.target = fld.id()}); // dense
251+
pt.table<DocComment>().set(fld.id(), DocComment{.leading = {.line = 3}}); // sparse
252+
pt.table<IsMutated>().set(fld.id()); // flag
253+
auto with_tables = pretty_print(pa, pt, mod.id());
254+
dump("pretty_print (with side tables)", with_tables);
255+
check(with_tables.find("[Resolution].target -> Field #") != std::string::npos &&
256+
with_tables.find("[DocComment].leading = 3:0") != std::string::npos &&
257+
with_tables.find("[IsMutated] = true") != std::string::npos,
258+
"side table entries are printed under the node they key on");
259+
// 表の中の Node を降りると Ident -> Resolution -> Field -> name -> Ident で回る
260+
check(std::count(with_tables.begin(), with_tables.end(), '\n') < 40,
261+
"table entries are shown as references, not descended into");
262+
247263
// ---- 親から子へ辿る (traverse.h) ----------------------------------
248264
// field<"..."> はパスがコンパイル時に決まるので、深さが実行時に
249265
// 決まる走査はこちら。weak は所有辺でないので渡さない。

0 commit comments

Comments
 (0)