Skip to content

Commit d45009e

Browse files
committed
Refactor utils to runtime
1 parent 3e66961 commit d45009e

File tree

6 files changed

+211
-214
lines changed

6 files changed

+211
-214
lines changed

eval.go

Lines changed: 37 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ func (n textNode) Eval(env interface{}) (interface{}, error) {
5050
}
5151

5252
func (n nameNode) Eval(env interface{}) (interface{}, error) {
53-
return extract(env, n.name)
53+
v, ok := extract(env, n.name)
54+
if !ok {
55+
return nil, fmt.Errorf("undefined: %v", n)
56+
}
57+
return v, nil
5458
}
5559

5660
func (n unaryNode) Eval(env interface{}) (interface{}, error) {
@@ -61,16 +65,10 @@ func (n unaryNode) Eval(env interface{}) (interface{}, error) {
6165

6266
switch n.operator {
6367
case "not", "!":
64-
if !isBool(val) {
65-
return nil, fmt.Errorf("negation of non-bool type %T", val)
66-
}
67-
return !toBool(val), nil
68+
return !toBool(n, val), nil
6869
}
6970

70-
v, err := cast(val)
71-
if err != nil {
72-
return nil, err
73-
}
71+
v := toNumber(n, val)
7472
switch n.operator {
7573
case "-":
7674
return -v, nil
@@ -89,40 +87,23 @@ func (n binaryNode) Eval(env interface{}) (interface{}, error) {
8987

9088
switch n.operator {
9189
case "or", "||":
92-
if !isBool(left) {
93-
return nil, fmt.Errorf("non-bool value in cond (%T)", left)
94-
}
95-
96-
if toBool(left) {
90+
if toBool(n.left, left) {
9791
return true, nil
9892
}
99-
10093
right, err := n.right.Eval(env)
10194
if err != nil {
10295
return nil, err
10396
}
104-
if !isBool(right) {
105-
return nil, fmt.Errorf("non-bool value in cond (%T)", right)
106-
}
107-
108-
return toBool(right), nil
97+
return toBool(n.right, right), nil
10998

11099
case "and", "&&":
111-
if !isBool(left) {
112-
return nil, fmt.Errorf("non-bool value in cond (%T)", left)
113-
}
114-
115-
if toBool(left) {
100+
if toBool(n.left, left) {
116101
right, err := n.right.Eval(env)
117102
if err != nil {
118103
return nil, err
119104
}
120-
if !isBool(right) {
121-
return nil, fmt.Errorf("non-bool value in cond (%T)", right)
122-
}
123-
return toBool(right), nil
105+
return toBool(n.right, right), nil
124106
}
125-
126107
return false, nil
127108
}
128109

@@ -153,23 +134,12 @@ func (n binaryNode) Eval(env interface{}) (interface{}, error) {
153134
return !ok, nil
154135

155136
case "~":
156-
if isText(left) && isText(right) {
157-
return toText(left) + toText(right), nil
158-
}
159-
return nil, fmt.Errorf("operator ~ not defined on (%T, %T)", left, right)
137+
return toText(n.left, left) + toText(n.right, right), nil
160138
}
161139

162140
// Next goes operators on numbers
163141

164-
l, err := cast(left)
165-
if err != nil {
166-
return nil, err
167-
}
168-
169-
r, err := cast(right)
170-
if err != nil {
171-
return nil, err
172-
}
142+
l, r := toNumber(n.left, left), toNumber(n.right, right)
173143

174144
switch n.operator {
175145
case "|":
@@ -246,45 +216,47 @@ func (n matchesNode) Eval(env interface{}) (interface{}, error) {
246216
}
247217

248218
if n.r != nil {
249-
if isText(left) {
250-
return n.r.MatchString(toText(left)), nil
251-
}
219+
return n.r.MatchString(toText(n.left, left)), nil
252220
}
253221

254222
right, err := n.right.Eval(env)
255223
if err != nil {
256224
return nil, err
257225
}
258226

259-
if isText(left) && isText(right) {
260-
matched, err := regexp.MatchString(toText(right), toText(left))
261-
if err != nil {
262-
return nil, err
263-
}
264-
return matched, nil
227+
matched, err := regexp.MatchString(toText(n.right, right), toText(n.left, left))
228+
if err != nil {
229+
return nil, err
265230
}
266-
267-
return nil, fmt.Errorf("operator matches doesn't defined on (%T, %T): %v", left, right, n)
231+
return matched, nil
268232
}
269233

270234
func (n propertyNode) Eval(env interface{}) (interface{}, error) {
271235
v, err := n.node.Eval(env)
272236
if err != nil {
273237
return nil, err
274238
}
275-
return extract(v, n.property)
239+
p, ok := extract(v, n.property)
240+
if !ok {
241+
return nil, fmt.Errorf("%v undefined (type %T has no field %v)", n, v, n.property)
242+
}
243+
return p, nil
276244
}
277245

278246
func (n indexNode) Eval(env interface{}) (interface{}, error) {
279247
v, err := n.node.Eval(env)
280248
if err != nil {
281249
return nil, err
282250
}
283-
p, err := n.index.Eval(env)
251+
i, err := n.index.Eval(env)
284252
if err != nil {
285253
return nil, err
286254
}
287-
return extract(v, p)
255+
p, ok := extract(v, i)
256+
if !ok {
257+
return nil, fmt.Errorf("cannot get %q from %T: %v", i, v, n)
258+
}
259+
return p, nil
288260
}
289261

290262
func (n methodNode) Eval(env interface{}) (interface{}, error) {
@@ -293,9 +265,9 @@ func (n methodNode) Eval(env interface{}) (interface{}, error) {
293265
return nil, err
294266
}
295267

296-
method, err := extract(v, n.method)
297-
if err != nil {
298-
return nil, err
268+
method, ok := extract(v, n.method)
269+
if !ok {
270+
return nil, fmt.Errorf("cannot get method %v from %T: %v", n.method, v, n)
299271
}
300272

301273
in := make([]reflect.Value, 0)
@@ -349,9 +321,9 @@ func (n builtinNode) Eval(env interface{}) (interface{}, error) {
349321
}
350322

351323
func (n functionNode) Eval(env interface{}) (interface{}, error) {
352-
fn, err := extract(env, n.name)
353-
if err != nil {
354-
return nil, err
324+
fn, ok := extract(env, n.name)
325+
if !ok {
326+
return nil, fmt.Errorf("undefined: %v", n.name)
355327
}
356328

357329
in := make([]reflect.Value, 0)
@@ -386,11 +358,8 @@ func (n conditionalNode) Eval(env interface{}) (interface{}, error) {
386358
}
387359

388360
// If
389-
if !isBool(cond) {
390-
return nil, fmt.Errorf("non-bool value used in cond (%T)", cond)
391-
}
392-
// Then
393-
if toBool(cond) {
361+
if toBool(n.cond, cond) {
362+
// Then
394363
a, err := n.exp1.Eval(env)
395364
if err != nil {
396365
return nil, err

eval_test.go

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ type evalTest struct {
1515
expected interface{}
1616
}
1717

18+
type evalErrorTest struct {
19+
input string
20+
env interface{}
21+
err string
22+
}
23+
1824
var evalTests = []evalTest{
1925
{
2026
"foo",
@@ -315,27 +321,41 @@ var evalTests = []evalTest{
315321
},
316322
}
317323

318-
type evalErrorTest struct {
319-
input string
320-
env interface{}
321-
err string
322-
}
323-
324324
var evalErrorTests = []evalErrorTest{
325325
{
326326
"bar",
327327
map[string]int{"foo": 1},
328-
`can't get "bar"`,
328+
`undefined: bar`,
329329
},
330330
{
331-
`"foo" ~ nil`,
331+
`"foo" ~ foo`,
332+
map[string]*int{"foo": nil},
333+
`cannot convert foo (type *int) to type string`,
334+
},
335+
{
336+
"1 or 0",
337+
nil,
338+
"cannot convert 1 (type float64) to type bool",
339+
},
340+
{
341+
"not nil",
342+
nil,
343+
"cannot convert not nil (type <nil>) to type bool",
344+
},
345+
{
346+
"nil matches 'nil'",
332347
nil,
333-
`operator ~ not defined on (string, <nil>)`,
348+
"cannot convert nil (type <nil>) to type string",
334349
},
335350
{
336351
"foo['bar'].baz",
337352
map[string]interface{}{"foo": nil},
338-
`can't get "bar" from <nil>`,
353+
`cannot get "bar" from <nil>: foo["bar"]`,
354+
},
355+
{
356+
"foo.bar(abc)",
357+
map[string]interface{}{"foo": nil},
358+
`cannot get method bar from <nil>: foo.bar(abc)`,
339359
},
340360
{
341361
`"seafood" matches "a(b"`,
@@ -350,22 +370,22 @@ var evalErrorTests = []evalErrorTest{
350370
{
351371
`1 matches "1" ~ "2"`,
352372
nil,
353-
"operator matches doesn't defined on (float64, string): (1 matches \"1\" ~ \"2\")",
373+
"cannot convert 1 (type float64) to type string",
354374
},
355375
{
356376
`1 matches "1"`,
357377
nil,
358-
"operator matches doesn't defined on (float64, string): (1 matches \"1\")",
378+
"cannot convert 1 (type float64) to type string",
359379
},
360380
{
361381
`"1" matches 1`,
362382
nil,
363-
"operator matches doesn't defined on (string, float64): (\"1\" matches 1)",
383+
"cannot convert 1 (type float64) to type string",
364384
},
365385
{
366-
`0 ? 1 : 2`,
367-
nil,
368-
`non-bool value used in cond`,
386+
`foo ? 1 : 2`,
387+
map[string]interface{}{"foo": 0},
388+
`cannot convert foo (type int) to type bool`,
369389
},
370390
{
371391
`foo()`,
@@ -395,22 +415,22 @@ var evalErrorTests = []evalErrorTest{
395415
{
396416
"1 + 'a'",
397417
nil,
398-
"can't cast string to float64",
418+
`cannot convert "a" (type string) to type float64`,
399419
},
400420
{
401421
"'a' + 1",
402422
nil,
403-
"can't cast string to float64",
423+
`cannot convert "a" (type string) to type float64`,
404424
},
405425
{
406426
"[1, 2]['a']",
407427
nil,
408-
"can't cast string to float64",
428+
`cannot get "a" from []interface {}: [1, 2]["a"]`,
409429
},
410430
{
411431
`1 in "a"`,
412432
nil,
413-
"operator in not defined on string",
433+
`operator "in" not defined on string`,
414434
},
415435
{
416436
"len()",
@@ -444,9 +464,9 @@ func TestEval(t *testing.T) {
444464

445465
func TestEval_error(t *testing.T) {
446466
for _, test := range evalErrorTests {
447-
_, err := expr.Eval(test.input, test.env)
467+
result, err := expr.Eval(test.input, test.env)
448468
if err == nil {
449-
err = fmt.Errorf("<nil>")
469+
err = fmt.Errorf("%v, <nil>", result)
450470
}
451471
if !strings.HasPrefix(err.Error(), test.err) || test.err == "" {
452472
t.Errorf("%s:\ngot\n\t%+v\nexpected\n\t%v", test.input, err.Error(), test.err)

lexer_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ type lexTest struct {
1010
tokens []token
1111
}
1212

13+
type lexErrorTest struct {
14+
input string
15+
err string
16+
}
17+
1318
var lexTests = []lexTest{
1419
{
1520
"1 02 1e3 1.2e-4",
@@ -94,11 +99,6 @@ var lexTests = []lexTest{
9499
},
95100
}
96101

97-
type lexErrorTest struct {
98-
input string
99-
err string
100-
}
101-
102102
var lexErrorTests = []lexErrorTest{
103103
{
104104
`{[}]`,

parser_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ type parseTest struct {
1212
expected Node
1313
}
1414

15+
type parseErrorTest struct {
16+
input string
17+
err string
18+
}
19+
1520
var parseTests = []parseTest{
1621
{
1722
"a",
@@ -143,11 +148,6 @@ var parseTests = []parseTest{
143148
},
144149
}
145150

146-
type parseErrorTest struct {
147-
input string
148-
err string
149-
}
150-
151151
var parseErrorTests = []parseErrorTest{
152152
{
153153
"foo.",

0 commit comments

Comments
 (0)