Skip to content

Commit 40d583c

Browse files
committed
Add dot format ast exporter
1 parent e4dc07f commit 40d583c

File tree

2 files changed

+198
-1
lines changed

2 files changed

+198
-1
lines changed

cmd/exp/dot.go

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
. "github.com/antonmedv/expr/ast"
6+
"os"
7+
)
8+
9+
const format = `digraph {
10+
ranksep=.3;
11+
12+
node [shape=box, fixedsize=false, fontsize=12, fontname="Helvetica-bold", fontcolor="#1259FF", width=.25, height=.25, color="black", fillcolor="white", style="filled, solid, bold"];
13+
edge [arrowsize=.5, color="black", style="bold"]
14+
15+
%v
16+
%v}
17+
`
18+
19+
func dotAst(node Node) {
20+
v := &visitor{}
21+
Walk(node, v)
22+
dot := fmt.Sprintf(format, v.nodes, v.links)
23+
_, _ = fmt.Fprintf(os.Stdout, dot)
24+
}
25+
26+
type visitor struct {
27+
nodes string
28+
links string
29+
index int
30+
stack []int
31+
}
32+
33+
func (v *visitor) push(label string) {
34+
v.index++
35+
v.nodes += fmt.Sprintf(" n%v [label=%q];\n", v.index, label)
36+
v.stack = append(v.stack, v.index)
37+
}
38+
39+
func (v *visitor) pop() int {
40+
node := v.stack[len(v.stack)-1]
41+
v.stack = v.stack[:len(v.stack)-1]
42+
return node
43+
}
44+
45+
func (v *visitor) link(node int) {
46+
v.links += fmt.Sprintf(" n%v -> n%v\n", v.index, node)
47+
}
48+
49+
func (v *visitor) NilNode(node *NilNode) {
50+
v.push("nil")
51+
}
52+
53+
func (v *visitor) IdentifierNode(node *IdentifierNode) {
54+
v.push(node.Value)
55+
}
56+
57+
func (v *visitor) IntegerNode(node *IntegerNode) {
58+
v.push(fmt.Sprintf("%v", node.Value))
59+
}
60+
61+
func (v *visitor) FloatNode(node *FloatNode) {
62+
v.push(fmt.Sprintf("%v", node.Value))
63+
}
64+
65+
func (v *visitor) BoolNode(node *BoolNode) {
66+
v.push(fmt.Sprintf("%v", node.Value))
67+
}
68+
69+
func (v *visitor) StringNode(node *StringNode) {
70+
v.push(fmt.Sprintf("%q", node.Value))
71+
}
72+
73+
func (v *visitor) UnaryNode(node *UnaryNode) {
74+
n := v.pop()
75+
v.push("-")
76+
v.link(n)
77+
}
78+
79+
func (v *visitor) BinaryNode(node *BinaryNode) {
80+
b := v.pop()
81+
a := v.pop()
82+
v.push(node.Operator)
83+
v.link(a)
84+
v.link(b)
85+
}
86+
87+
func (v *visitor) MatchesNode(node *MatchesNode) {
88+
b := v.pop()
89+
a := v.pop()
90+
v.push("matches")
91+
v.link(a)
92+
v.link(b)
93+
}
94+
95+
func (v *visitor) PropertyNode(node *PropertyNode) {
96+
a := v.pop()
97+
v.push(fmt.Sprintf(".%v", node.Property))
98+
v.link(a)
99+
}
100+
101+
func (v *visitor) IndexNode(node *IndexNode) {
102+
b := v.pop()
103+
a := v.pop()
104+
v.push("[...]")
105+
v.link(a)
106+
v.link(b)
107+
}
108+
109+
func (v *visitor) MethodNode(node *MethodNode) {
110+
args := make([]int, 0)
111+
for range node.Arguments {
112+
args = append(args, v.pop())
113+
}
114+
a := v.pop()
115+
v.push(fmt.Sprintf(".%v(...)", node.Method))
116+
v.link(a)
117+
for i := len(args) - 1; i >= 0; i-- {
118+
v.link(args[i])
119+
}
120+
}
121+
122+
func (v *visitor) FunctionNode(node *FunctionNode) {
123+
args := make([]int, 0)
124+
for range node.Arguments {
125+
args = append(args, v.pop())
126+
}
127+
v.push(fmt.Sprintf("%v(...)", node.Name))
128+
for i := len(args) - 1; i >= 0; i-- {
129+
v.link(args[i])
130+
}
131+
}
132+
133+
func (v *visitor) BuiltinNode(node *BuiltinNode) {
134+
args := make([]int, 0)
135+
for range node.Arguments {
136+
args = append(args, v.pop())
137+
}
138+
v.push(fmt.Sprintf("%v(...)", node.Name))
139+
for i := len(args) - 1; i >= 0; i-- {
140+
v.link(args[i])
141+
}
142+
}
143+
144+
func (v *visitor) ClosureNode(node *ClosureNode) {
145+
a := v.pop()
146+
v.push("func {...}")
147+
v.link(a)
148+
}
149+
150+
func (v *visitor) PointerNode(node *PointerNode) {
151+
v.push("#")
152+
}
153+
154+
func (v *visitor) ConditionalNode(node *ConditionalNode) {
155+
e2 := v.pop()
156+
e1 := v.pop()
157+
c := v.pop()
158+
v.push("? :")
159+
v.link(c)
160+
v.link(e1)
161+
v.link(e2)
162+
}
163+
164+
func (v *visitor) ArrayNode(node *ArrayNode) {
165+
n := make([]int, 0)
166+
for range node.Nodes {
167+
n = append(n, v.pop())
168+
}
169+
v.push("[...]")
170+
for i := len(n) - 1; i >= 0; i-- {
171+
v.link(n[i])
172+
}
173+
}
174+
175+
func (v *visitor) MapNode(node *MapNode) {
176+
n := make([]int, 0)
177+
for range node.Pairs {
178+
n = append(n, v.pop())
179+
}
180+
v.push("{...}")
181+
for i := len(n) - 1; i >= 0; i-- {
182+
v.link(n[i])
183+
}
184+
}
185+
186+
func (v *visitor) PairNode(node *PairNode) {
187+
a := v.pop()
188+
v.push(fmt.Sprintf("%q:", node.Key.(*StringNode).Value))
189+
v.link(a)
190+
}

cmd/exp/main.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ var (
1717
debug bool
1818
run bool
1919
ast bool
20+
dot bool
2021
repl bool
2122
)
2223

@@ -25,6 +26,7 @@ func init() {
2526
flag.BoolVar(&debug, "debug", false, "debug program")
2627
flag.BoolVar(&run, "run", false, "run program")
2728
flag.BoolVar(&ast, "ast", false, "print ast")
29+
flag.BoolVar(&dot, "dot", false, "dot format")
2830
flag.BoolVar(&repl, "repl", false, "start repl")
2931
}
3032

@@ -74,7 +76,12 @@ func check(err error) {
7476
func printAst() {
7577
tree, err := parser.Parse(input())
7678
check(err)
77-
litter.Dump(tree.Node)
79+
if !dot {
80+
litter.Dump(tree.Node)
81+
return
82+
}
83+
84+
dotAst(tree.Node)
7885
}
7986

8087
func printDisassemble() {

0 commit comments

Comments
 (0)