-
Notifications
You must be signed in to change notification settings - Fork 0
/
nodes.go
146 lines (135 loc) · 3.13 KB
/
nodes.go
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package expressions
import (
"strings"
)
// node is a node in the abstract syntax tree of an expression.
type node struct {
left *node
right *node
fn Func
name string
kind nodeKind
}
type nodeKind int8
const (
nodeNone nodeKind = iota
nodeNum // push name parsed as a number, possibly "inf" or "Inf" or "∞"
nodeName // push lookup(name)
// TODO(zeph): how represent e.g. 0F0(; ; z) = exp(z)
nodeCall // name is Func to call, right is link to nodeArg unless niladic
nodeArg // name is "" or "," or ";", eval left, right is link to next arg
nodeNeg // evaluate left, then negate
nodeAdd // evaluate left, add right
nodeSub // evaluate left, sub right
nodeMul // evaluate left, mul right
nodeDiv // evaluate left, div by right
nodePow // evaluate left, exp by right
nodeNop // evaluate left
)
//go:generate go mod edit -require=golang.org/x/[email protected]
//go:generate go mod download
//go:generate go run golang.org/x/tools/cmd/stringer -type=nodeKind -trimprefix=node
//go:generate go mod tidy
func (n *node) String() string {
var b strings.Builder
n.fmt(&b, false, false)
return b.String()
}
func (n *node) fmt(b *strings.Builder, square, alt bool) {
var l, r byte = '(', ')'
if square {
l, r = '[', ']'
}
b.WriteByte(l)
defer b.WriteByte(r)
switch n.kind {
case nodeNone:
// Invalid nodes use invalid characters.
b.WriteByte('$')
if n.left != nil {
n.left.fmt(b, square, alt)
}
b.WriteByte('#')
if n.right != nil {
n.right.fmt(b, square, alt)
}
b.WriteByte('$')
case nodeNum, nodeName:
b.WriteString(n.name)
case nodeCall:
b.WriteString(n.name)
n.fmtargs(b, !square, alt)
case nodeArg:
// Args usually only appear inside calls, which are handled by fmtargs.
b.WriteByte(':')
n.left.fmt(b, !square, alt)
if n.right != nil {
n.right.fmt(b, !square, alt)
}
case nodeNeg:
b.WriteByte('-')
n.left.fmt(b, !square, alt)
case nodeAdd:
n.left.fmt(b, !square, alt)
b.WriteString(" + ")
n.right.fmt(b, !square, alt)
case nodeSub:
n.left.fmt(b, !square, alt)
b.WriteString(" - ")
n.right.fmt(b, !square, alt)
case nodeMul:
n.left.fmt(b, !square, alt)
if !alt {
b.WriteString(" * ")
} else {
b.WriteString(" × ")
}
n.right.fmt(b, !square, alt)
case nodeDiv:
n.left.fmt(b, !square, alt)
if !alt {
b.WriteString(" / ")
} else {
b.WriteString(" ÷ ")
}
n.right.fmt(b, !square, alt)
case nodePow:
n.left.fmt(b, !square, alt)
b.WriteString(" ^ ")
n.right.fmt(b, !square, alt)
case nodeNop:
b.WriteByte('+')
n.left.fmt(b, !square, alt)
default:
panic("expressions: invalid node kind " + n.kind.String() + " after writing " + b.String())
}
}
func (n *node) fmtargs(b *strings.Builder, square, alt bool) {
var l, r byte = '(', ')'
if square {
l, r = '[', ']'
}
b.WriteByte(l)
defer b.WriteByte(r)
if n.right == nil {
// Niladic call.
return
}
n = n.right
if n.kind != nodeArg {
b.WriteString("***")
n.fmt(b, !square, alt)
return
}
n.left.fmt(b, !square, alt)
for n.right != nil {
n = n.right
if n.kind != nodeArg {
b.WriteString("***")
n.fmt(b, !square, alt)
return
}
b.WriteString(", ")
n.left.fmt(b, !square, alt)
}
}