-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathfold_demo.cpp
More file actions
106 lines (90 loc) · 3.24 KB
/
Copy pathfold_demo.cpp
File metadata and controls
106 lines (90 loc) · 3.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// Folding a tree into a value with ts::fold.
//
// ts::fold computes a value from the bottom up. Each node's result is built
// from the results already computed for its children, and the engine handles
// the underlying bookkeeping so you can just write:
// onNode(node, childResults)
// Leaves get an empty span of results because they have no chldren. The
// result type of the fold is explicit at the call site because nothing in
// the folder's signature has to name it:
// ts::fold<int>(...)
//
// Unlike ts::visit, a fold allocates because the engine holds a stack of
// frames containing the child results.
//
// Building your own objects out of a parse tree, a typed AST for example, has
// three reasonable shapes.
//
// * Recursive descent over ts::Node, using field accessors, optional
// navigation, and children views by hand. Try this first for an AST
// shaped like the grammar, since each recursive call can return the type
// its production implies.
// * ts::fold when a node's value is a straightforward combination of its
// children's values.
// * A visitor with a stack of partial values, when construction is naturally
// bracketed. Push a partial value in onEnter, then pop it in onLeave and
// attach it to its parent.
//
// ts::typedFold, in typed_demo.cpp, adds dispatch by node type.
#include <algorithm>
#include <charconv>
#include <cstdlib>
#include <print>
#include <span>
#include <string_view>
#include <cpp-tree-sitter.h>
#include <cts/format.h>
extern "C" TSLanguage* tree_sitter_json();
namespace {
// Leaf-driven. Every `number` contributes its own value.
// Every other node just passes its children's total upwards.
struct SumNumbers {
std::string_view source;
int onNode(ts::Node node, std::span<int> childResults) {
int total = 0;
for (int const value : childResults) {
total += value;
}
if (node.getType() == "number") {
// from_chars parses straight out of the source buffer and leaves
// `value` untouched if the text does not scan as an int.
std::string_view const text = node.getSourceRange(source);
int value = 0;
std::from_chars(text.data(), text.data() + text.size(), value);
total += value;
}
return total;
}
};
// Children-driven. A node's answer depends only on its children's answers.
// Leaves get an empty span, so they fall out as depth 1.
struct MaxDepth {
int onNode(ts::Node node, std::span<int> childResults) {
int deepest = 0;
for (int const depth : childResults) {
deepest = std::max(deepest, depth);
}
return node.isNamed() ? deepest + 1 : deepest;
}
};
}
int
main() {
auto parser = ts::Parser::create(tree_sitter_json());
if (!parser) {
std::println(stderr, "error: {}", parser.error());
return EXIT_FAILURE;
}
constexpr std::string_view source = R"({"a": [1, 2, 3], "b": {"c": 4}})";
auto tree = parser->parse(source);
if (!tree) {
std::println(stderr, "error: {}", tree.error());
return EXIT_FAILURE;
}
ts::Node const root = tree->getRootNode();
SumNumbers summer{source};
std::println("sum of every number: {}", ts::fold<int>(root, summer));
MaxDepth depth;
std::println("deepest named nesting: {}", ts::fold<int>(root, depth));
return EXIT_SUCCESS;
}