Skip to content

Commit 4dae6ee

Browse files
committed
Implement default traversal for the entire AST
1 parent 7b57a54 commit 4dae6ee

File tree

8 files changed

+1408
-256
lines changed

8 files changed

+1408
-256
lines changed

packages/cxx-gen-ast/src/gen.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { gen_ast_slot_cc } from "./gen_ast_slot_cc.js";
3131
import { gen_ast_slot_h } from "./get_ast_slot_h.js";
3232
import { gen_ast_ts } from "./gen_ast_ts.js";
3333
import { gen_ast_visitor_h } from "./gen_ast_visitor_h.js";
34+
import { gen_ast_visitor_cc } from "./gen_ast_visitor_cc.js";
3435
import { gen_ast_visitor_ts } from "./gen_ast_visitor_ts.js";
3536
import { parseAST } from "./parseAST.js";
3637
import { gen_ast_kind_ts } from "./gen_ast_kind_ts.js";
@@ -67,6 +68,10 @@ gen_ast_visitor_h({
6768
ast,
6869
output: path.join(outdir, "src/parser/cxx/ast_visitor.h"),
6970
});
71+
gen_ast_visitor_cc({
72+
ast,
73+
output: path.join(outdir, "src/parser/cxx/ast_visitor.cc"),
74+
});
7075
gen_ast_printer_h({
7176
ast,
7277
output: path.join(outdir, "src/parser/cxx/ast_printer.h"),
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright (c) 2024 Roberto Raggi <[email protected]>
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
// SOFTWARE.
20+
21+
import { groupNodesByBaseType } from "./groupNodesByBaseType.js";
22+
import { AST, getASTNodes } from "./parseAST.js";
23+
import { cpy_header } from "./cpy_header.js";
24+
import * as fs from "fs";
25+
26+
export function gen_ast_visitor_cc({
27+
ast,
28+
output,
29+
}: {
30+
ast: AST;
31+
output: string;
32+
}) {
33+
const code: string[] = [];
34+
const emit = (line = "") => code.push(line);
35+
36+
const by_base = groupNodesByBaseType(ast);
37+
38+
const types = new Set<string>();
39+
40+
ast.nodes.forEach((node) => {
41+
node.members.forEach((m) => {
42+
if (m.kind === "node" || m.kind === "node-list") types.add(m.type);
43+
});
44+
});
45+
46+
by_base.forEach((nodes) => {
47+
nodes.forEach(({ name, members }) => {
48+
members = getASTNodes(members);
49+
50+
emit();
51+
emit(`void ASTVisitor::visit(${name}* ast) {`);
52+
members.forEach((m) => {
53+
if (m.kind === "node") {
54+
emit(`accept(ast->${m.name});`);
55+
} else if (m.kind === "node-list") {
56+
emit(`for (auto node : ListView{ast->${m.name}}) {`);
57+
emit(`accept(node);`);
58+
emit(`}`);
59+
}
60+
});
61+
emit(`}`);
62+
});
63+
});
64+
65+
const out = `${cpy_header}
66+
#include <cxx/ast_visitor.h>
67+
68+
// cxx
69+
#include <cxx/ast.h>
70+
71+
namespace cxx {
72+
73+
auto ASTVisitor::preVisit(AST*) -> bool {
74+
return true;
75+
}
76+
77+
void ASTVisitor::postVisit(AST*) {}
78+
79+
void ASTVisitor::accept(AST* ast) {
80+
if (!ast) return;
81+
if (preVisit(ast)) ast->accept(this);
82+
postVisit(ast);
83+
}
84+
85+
${code.join("\n")}
86+
87+
} // namespace cxx
88+
`;
89+
90+
fs.writeFileSync(output, out);
91+
}

packages/cxx-gen-ast/src/gen_ast_visitor_h.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export function gen_ast_visitor_h({
4040
emit();
4141
emit(` // ${base}`);
4242
nodes.forEach(({ name }) => {
43-
emit(` virtual void visit(${name}* ast) = 0;`);
43+
emit(` virtual void visit(${name}* ast);`);
4444
});
4545
});
4646

@@ -55,6 +55,12 @@ namespace cxx {
5555
class ASTVisitor {
5656
public:
5757
virtual ~ASTVisitor() = default;
58+
59+
void accept(AST* ast);
60+
61+
[[nodiscard]] virtual bool preVisit(AST* ast);
62+
virtual void postVisit(AST* ast);
63+
5864
${code.join("\n")}
5965
};
6066

src/frontend/cxx/frontend.cc

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
// SOFTWARE.
2020

2121
#include <cxx/ast.h>
22-
#include <cxx/ast_cursor.h>
2322
#include <cxx/ast_pretty_printer.h>
2423
#include <cxx/ast_printer.h>
2524
#include <cxx/ast_visitor.h>
@@ -56,43 +55,48 @@ namespace {
5655

5756
using namespace cxx;
5857

59-
struct CheckExpressionTypes {
58+
class CheckExpressionTypes final : private ASTVisitor {
59+
public:
6060
[[nodiscard]] auto operator()(TranslationUnit* unit) {
6161
std::size_t missingTypes = 0;
62+
std::swap(unit_, unit);
63+
std::swap(missingTypes_, missingTypes);
6264

63-
// iterate over all expressions and check if they have a type
64-
for (auto cursor = ASTCursor{unit->ast(), "unit"}; cursor;
65-
cursor = ++cursor) {
66-
const auto& current = *cursor;
65+
accept(unit_->ast());
6766

68-
if (!std::holds_alternative<AST*>(current.node)) {
69-
// skip non-AST nodes
70-
continue;
71-
}
67+
std::swap(unit_, unit);
68+
std::swap(missingTypes_, missingTypes);
7269

73-
auto ast = std::get<AST*>(current.node);
74-
auto expression = ast_cast<ExpressionAST>(ast);
75-
if (!expression) {
76-
// skip non-expression nodes
77-
continue;
78-
}
70+
return missingTypes == 0;
71+
}
7972

80-
auto type = expression->type;
81-
if (type) {
82-
// the expression has a type
83-
continue;
84-
}
73+
private:
74+
using ASTVisitor::visit;
8575

86-
const auto loc = expression->firstSourceLocation();
76+
auto preVisit(AST* ast) -> bool override {
77+
if (ast_cast<TemplateDeclarationAST>(ast)) {
78+
// skip template declarations, as they are not instantiated yet
79+
return false;
80+
}
81+
82+
if (auto expression = ast_cast<ExpressionAST>(ast)) {
83+
if (!expression->type) {
84+
const auto loc = expression->firstSourceLocation();
8785

88-
unit->warning(loc, std::format("untyped expression of kind '{}'",
89-
to_string(expression->kind())));
86+
unit_->warning(loc, std::format("untyped expression of kind '{}'",
87+
to_string(expression->kind())));
9088

91-
++missingTypes;
89+
++missingTypes_;
90+
return false;
91+
}
9292
}
9393

94-
return missingTypes == 0;
94+
return true; // visit children
9595
}
96+
97+
private:
98+
TranslationUnit* unit_ = nullptr;
99+
std::size_t missingTypes_ = 0;
96100
};
97101

98102
auto readAll(const std::string& fileName, std::istream& in)

src/parser/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ add_library(cxx-parser
2727
cxx/ast_printer.cc
2828
cxx/ast_rewriter.cc
2929
cxx/ast_slot.cc
30+
cxx/ast_visitor.cc
3031
cxx/ast.cc
3132
cxx/base_classes.cc
3233
cxx/binder.cc

0 commit comments

Comments
 (0)