Skip to content

Commit cfcc41a

Browse files
committed
Add eval
1 parent ce2a54e commit cfcc41a

File tree

3 files changed

+826
-0
lines changed

3 files changed

+826
-0
lines changed

eval.go

Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
package expr
2+
3+
import (
4+
"fmt"
5+
"math"
6+
"regexp"
7+
)
8+
9+
type evaluator interface {
10+
eval(env interface{}) (interface{}, error)
11+
}
12+
13+
// Eval parses and evaluates given input.
14+
func Eval(input string, env interface{}) (node interface{}, err error) {
15+
defer func() {
16+
if r := recover(); r != nil {
17+
err = fmt.Errorf("%v", r)
18+
}
19+
}()
20+
21+
node, err = Parse(input)
22+
23+
if err != nil {
24+
return nil, err
25+
}
26+
return Run(node, env)
27+
}
28+
29+
// Run evaluates given ast.
30+
func Run(node Node, env interface{}) (interface{}, error) {
31+
if e, ok := node.(evaluator); ok {
32+
return e.eval(env)
33+
}
34+
return nil, fmt.Errorf("implement evaluator for %T", node)
35+
}
36+
37+
// eval functions
38+
39+
func (n nilNode) eval(env interface{}) (interface{}, error) {
40+
return nil, nil
41+
}
42+
43+
func (n identifierNode) eval(env interface{}) (interface{}, error) {
44+
return n.value, nil
45+
}
46+
47+
func (n numberNode) eval(env interface{}) (interface{}, error) {
48+
return n.value, nil
49+
}
50+
51+
func (n boolNode) eval(env interface{}) (interface{}, error) {
52+
return n.value, nil
53+
}
54+
55+
func (n textNode) eval(env interface{}) (interface{}, error) {
56+
return n.value, nil
57+
}
58+
59+
func (n nameNode) eval(env interface{}) (interface{}, error) {
60+
return extract(env, n.name)
61+
}
62+
63+
func (n unaryNode) eval(env interface{}) (interface{}, error) {
64+
val, err := Run(n.node, env)
65+
if err != nil {
66+
return nil, err
67+
}
68+
69+
switch n.operator {
70+
case "not", "!":
71+
if !isBool(val) {
72+
return nil, fmt.Errorf("negation of non-bool type %T", val)
73+
}
74+
return !toBool(val), nil
75+
}
76+
77+
v, err := cast(val)
78+
if err != nil {
79+
return nil, err
80+
}
81+
switch n.operator {
82+
case "-":
83+
return -v, nil
84+
case "+":
85+
return +v, nil
86+
}
87+
88+
return nil, fmt.Errorf("implement unary %q operator", n.operator)
89+
}
90+
91+
func (n binaryNode) eval(env interface{}) (interface{}, error) {
92+
left, err := Run(n.left, env)
93+
if err != nil {
94+
return nil, err
95+
}
96+
97+
switch n.operator {
98+
case "or", "||":
99+
if !isBool(left) {
100+
return nil, fmt.Errorf("non-bool value in cond (%T)", left)
101+
}
102+
103+
if toBool(left) {
104+
return true, nil
105+
}
106+
107+
right, err := Run(n.right, env)
108+
if err != nil {
109+
return nil, err
110+
}
111+
if !isBool(right) {
112+
return nil, fmt.Errorf("non-bool value in cond (%T)", right)
113+
}
114+
115+
return toBool(right), nil
116+
117+
case "and", "&&":
118+
if !isBool(left) {
119+
return nil, fmt.Errorf("non-bool value in cond (%T)", left)
120+
}
121+
122+
if toBool(left) {
123+
right, err := Run(n.right, env)
124+
if err != nil {
125+
return nil, err
126+
}
127+
if !isBool(right) {
128+
return nil, fmt.Errorf("non-bool value in cond (%T)", right)
129+
}
130+
return toBool(right), nil
131+
}
132+
133+
return false, nil
134+
}
135+
136+
right, err := Run(n.right, env)
137+
if err != nil {
138+
return nil, err
139+
}
140+
141+
switch n.operator {
142+
case "==":
143+
return equal(left, right), nil
144+
145+
case "!=":
146+
return !equal(left, right), nil
147+
148+
case "in":
149+
ok, err := contains(left, right)
150+
if err != nil {
151+
return nil, err
152+
}
153+
return ok, nil
154+
155+
case "not in":
156+
ok, err := contains(left, right)
157+
if err != nil {
158+
return nil, err
159+
}
160+
return !ok, nil
161+
162+
case "matches":
163+
if isText(left) && isText(right) {
164+
matched, err := regexp.MatchString(toText(right), toText(left))
165+
if err != nil {
166+
return nil, err
167+
}
168+
return matched, nil
169+
}
170+
return nil, fmt.Errorf("operator matches not defined on (%T, %T)", left, right)
171+
172+
case "~":
173+
if isText(left) && isText(right) {
174+
return toText(left) + toText(right), nil
175+
}
176+
return nil, fmt.Errorf("operator ~ not defined on (%T, %T)", left, right)
177+
}
178+
179+
// Next goes operators on numbers
180+
181+
l, err := cast(left)
182+
if err != nil {
183+
return nil, err
184+
}
185+
186+
r, err := cast(right)
187+
if err != nil {
188+
return nil, err
189+
}
190+
191+
switch n.operator {
192+
case "|":
193+
return int(l) | int(r), nil
194+
195+
case "^":
196+
return int(l) ^ int(r), nil
197+
198+
case "&":
199+
return int(l) & int(r), nil
200+
201+
case "<":
202+
return l < r, nil
203+
204+
case ">":
205+
return l > r, nil
206+
207+
case ">=":
208+
return l >= r, nil
209+
210+
case "<=":
211+
return l <= r, nil
212+
213+
case "+":
214+
return l + r, nil
215+
216+
case "-":
217+
return l - r, nil
218+
219+
case "*":
220+
return l * r, nil
221+
222+
case "/":
223+
div := r
224+
if div == 0 {
225+
return nil, fmt.Errorf("division by zero")
226+
}
227+
return l / div, nil
228+
229+
case "%":
230+
numerator := int64(l)
231+
denominator := int64(r)
232+
if denominator == 0 {
233+
return nil, fmt.Errorf("division by zero")
234+
}
235+
return float64(numerator % denominator), nil
236+
237+
case "**":
238+
return math.Pow(l, r), nil
239+
240+
case "..":
241+
return makeRange(int64(l), int64(r)), nil
242+
}
243+
244+
return nil, fmt.Errorf("implement %q operator", n.operator)
245+
}
246+
247+
func makeRange(min, max int64) []float64 {
248+
a := make([]float64, max-min+1)
249+
for i := range a {
250+
a[i] = float64(min + int64(i))
251+
}
252+
return a
253+
}
254+
255+
func (n propertyNode) eval(env interface{}) (interface{}, error) {
256+
v, err := Run(n.node, env)
257+
if err != nil {
258+
return nil, err
259+
}
260+
p, err := Run(n.property, env)
261+
if err != nil {
262+
return nil, err
263+
}
264+
265+
return extract(v, p)
266+
}
267+
268+
func (n methodNode) eval(env interface{}) (interface{}, error) {
269+
v, err := Run(n.node, env)
270+
if err != nil {
271+
return nil, err
272+
}
273+
274+
method, err := extract(v, n.property.value)
275+
if err != nil {
276+
return nil, err
277+
}
278+
279+
return call(n.property.value, method, n.arguments, env)
280+
}
281+
282+
func (n functionNode) eval(env interface{}) (interface{}, error) {
283+
fn, err := extract(env, n.name)
284+
if err != nil {
285+
return nil, err
286+
}
287+
288+
return call(n.name, fn, n.arguments, env)
289+
}
290+
291+
func (n conditionalNode) eval(env interface{}) (interface{}, error) {
292+
cond, err := Run(n.cond, env)
293+
if err != nil {
294+
return nil, err
295+
}
296+
297+
// If
298+
if !isBool(cond) {
299+
return nil, fmt.Errorf("non-bool value used in cond (%T)", cond)
300+
}
301+
// Then
302+
if toBool(cond) {
303+
a, err := Run(n.exp1, env)
304+
if err != nil {
305+
return nil, err
306+
}
307+
return a, nil
308+
}
309+
// Else
310+
b, err := Run(n.exp2, env)
311+
if err != nil {
312+
return nil, err
313+
}
314+
return b, nil
315+
316+
}
317+
318+
func (n arrayNode) eval(env interface{}) (interface{}, error) {
319+
array := make([]interface{}, 0)
320+
for _, node := range n.nodes {
321+
val, err := Run(node, env)
322+
if err != nil {
323+
return nil, err
324+
}
325+
array = append(array, val)
326+
}
327+
return array, nil
328+
}
329+
330+
func (n mapNode) eval(env interface{}) (interface{}, error) {
331+
m := make(map[interface{}]interface{})
332+
for _, pair := range n.pairs {
333+
key, err := Run(pair.key, env)
334+
if err != nil {
335+
return nil, err
336+
}
337+
value, err := Run(pair.value, env)
338+
if err != nil {
339+
return nil, err
340+
}
341+
m[key] = value
342+
}
343+
return m, nil
344+
}

0 commit comments

Comments
 (0)