-
Notifications
You must be signed in to change notification settings - Fork 15
/
expr_cond.go
87 lines (76 loc) · 2.11 KB
/
expr_cond.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
package rel
import (
"bytes"
"fmt"
"github.com/arr-ai/wbnf/parser"
)
// CondExpr returns the tuple applied to a function, the expression looks like:
// cond (1 > 0: 2 + 1, 3 > 4: 5, _: 10).
// The original keyword was switch (SwitchExpr), and finally it was chanaged from switch to cond.
// The keyword cond is more unusual, therefore less likely to be chosen as a regular name,
// and avoids accidental comparisons with the switch statement in other languages.
type CondExpr struct {
ExprScanner
dicExpr Expr
validValidation func(condition Value, local Scope) (bool, error) // Valid condition validation.
}
// NewCondExpr returns a new CondExpr.
func NewCondExpr(scanner parser.Scanner, dict Expr) Expr {
return CondExpr{ExprScanner{scanner}, dict, func(condition Value, local Scope) (bool, error) {
return condition.IsTrue(), nil
}}
}
// String returns a string representation of the expression.
func (e CondExpr) String() string {
var b bytes.Buffer
b.WriteByte('{')
switch c := e.dicExpr.(type) {
case DictExpr:
for i, expr := range c.entryExprs {
if i > 0 {
b.WriteString(", ")
}
fmt.Fprintf(&b, "%v: %v", expr.at.String(), expr.value.String())
}
}
b.WriteByte('}')
return b.String()
}
// Eval returns the value of true condition, or default condition value.
func (e CondExpr) Eval(local Scope) (Value, error) {
var trueCond *DictEntryTupleExpr
switch c := e.dicExpr.(type) {
case DictExpr:
for _, expr := range c.entryExprs {
tempExpr := expr
// Can't call Eval if it is `_`, so compare its String.
if expr.at.String() == "_" {
trueCond = &tempExpr
break
}
cond, err := tempExpr.at.Eval(local)
if err != nil {
return nil, WrapContext(err, e, local)
}
valid, err := e.validValidation(cond, local)
if err != nil {
return nil, WrapContext(err, e, local)
}
if cond != nil && valid {
trueCond = &tempExpr
break
}
}
}
var finalCond Expr
if trueCond != nil {
finalCond = trueCond.value
} else {
return None, nil
}
value, err := finalCond.Eval(local)
if err != nil {
return nil, WrapContext(err, e, local)
}
return value, nil
}