Skip to content

Commit 969f16f

Browse files
committed
#2 Manual merge tree walking blog
1 parent fb7e666 commit 969f16f

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
---
2+
layout: post
3+
title: "Functional Tree Traversal"
4+
date: 2020-02-03 17:58:46 +0100
5+
categories: [blog]
6+
tags: [guide, danny]
7+
permalink: /functional-tree-traversal/
8+
---
9+
10+
## Traversing the AST
11+
12+
When you read the book, you will learn about traversing the AST by using visitors.
13+
Since 2017, there is an alternative that was suggested/inspired by Federico Tomassetti.
14+
Instead of using the visitor pattern,
15+
this one walks through the AST in a pattern of your choosing,
16+
and passes every node it finds to you for processing.
17+
18+
# The base: iterators
19+
20+
Various iterators have been defined in `Node`.
21+
Here we use a breadth-first iterator:
22+
```java
23+
public class Test {
24+
public static void main(String[] args) {
25+
CompilationUnit cu = StaticJavaParser.parse("class X{void y(){int z;}}");
26+
Node.BreadthFirstIterator iterator = new Node.BreadthFirstIterator(cu);
27+
while (iterator.hasNext()) {
28+
System.out.println("* " + iterator.next());
29+
}
30+
}
31+
}
32+
```
33+
What's nice here is that you are in control.
34+
You explicitly ask for the next node,
35+
and you can stop asking at any moment.
36+
37+
What's not so nice is that it's a smelly old iterator :-(
38+
But it serves as a very generic base for the rest.
39+
40+
# The functional family
41+
42+
```java
43+
public class Test {
44+
public static void main(String[] args) {
45+
CompilationUnit cu = StaticJavaParser.parse("class X{void y(){int z;}}");
46+
47+
// "Walk" is a very general method that takes the pattern to walk, and the action to do for each walked node:
48+
cu.walk(Node.TreeTraversal.PREORDER, node -> System.out.println("* " + node));
49+
50+
// And this is the familiar Java 8 stream API:
51+
cu.stream(Node.TreeTraversal.PREORDER).forEach(node -> System.out.println("* " + node));
52+
53+
// Now let's assume pre-order traversal. Much nicer:
54+
cu.walk(node -> System.out.println("* " + node));
55+
cu.stream().forEach(node -> System.out.println("* " + node));
56+
57+
// Based on "walk" we have several useful variants that take care of filtering on instance,
58+
// which is a bit painful using streams.
59+
// We can find all nodes of a specific type:
60+
cu.findAll(VariableDeclarationExpr.class).forEach(node -> System.out.println("* " + node));
61+
// We can find the first node of a specific type:
62+
cu.findFirst(VariableDeclarationExpr.class).ifPresent(node -> System.out.println("* " + node));
63+
// ... and several other variations. Use your IDE to find them, or check the Javadoc.
64+
65+
// Care has been taken to prevent trouble with modifying the AST while traversing it:
66+
cu.findAll(MethodDeclaration.class).forEach(Node::remove);
67+
// Tada! The method has been removed and everything worked just fine:
68+
System.out.println(cu);
69+
}
70+
}
71+
```
72+
### The walking patterns
73+
74+
A nice list of patterns can be found in the `Node.TreeTraversal` enum.
75+
76+
*Pre-order* is very useful for walking the nodes in the AST in the order they were encountered in the source code.
77+
78+
*Post-order* is very useful for walking the nodes from the most distant children, slowly towards the start node.
79+
80+
Take a look at [this YouTube video](https://youtu.be/WLvU5EQVZqY) which does a great job of explaining these two.
81+
82+
*Breadth-first* traversal will visit the start node, then all its direct children, then these children's direct children, and so on.
83+
It's honestly not very useful.
84+
85+
The *parents* pattern is pretty odd since it goes in an unexpected direction: it starts at the parent node of the start node,
86+
then goes up towards the root node by walking through all the parents.
87+
This is useful for finding a specific parent node, especially when you don't know how far "above" the start node it can be found.
88+
So useful indeed, that it has its own set of methods called `findAncestor` .
89+
90+
*Direct-children* is a simple pattern: it takes all children of the start node and goes through them, but *not* through their children.
91+
It is probably not very useful, but it is there when you need it.
92+
93+
### Warnings!
94+
95+
- the start node is included in most patterns.
96+
That means that if you are looking for a specific *child* node, you may want to exclude the start node with a well-placed `filter` call,
97+
or predicate.
98+
- all of this code is based on `Node.getChildNodes()`, and since the list of child nodes gets mixed up when you mutate the AST,
99+
it is a bad idea to expect the children of a node to get visited in a specific order if you, well, especially if you add nodes.

blog.html

+19
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,25 @@ <h1 class="display-4 color-1 mb-4">JavaParser Blog</h1>
9898
<section class="section">
9999
<div class="container">
100100
<div class="row gap-y">
101+
<div class="col-md-6 col-lg-4">
102+
<div class="card card-blog shadow-box shadow-hover border-0">
103+
<div class="card-body">
104+
<div class="d-flex align-items-center justify-content-between">
105+
<div class="author d-flex align-items-center">
106+
<p class="small bold my-0">Danny van Bruggen</p>
107+
</div>
108+
</div>
109+
<hr>
110+
<p class="card-title regular">
111+
<a href="/functional-tree-traversal">Functional Tree Traversal</a>
112+
</p>
113+
<p class="card-text color-2">When you read the book, you will learn about traversing the AST by using visitors ...</p>
114+
<p class="bold small color-2 my-0">
115+
<small>Mar 29 2020</small>
116+
</p>
117+
</div>
118+
</div>
119+
</div>
101120
<div class="col-md-6 col-lg-4">
102121
<div class="card card-blog shadow-box shadow-hover border-0">
103122
<div class="card-body">

0 commit comments

Comments
 (0)