-
Notifications
You must be signed in to change notification settings - Fork 16
/
sum.go
144 lines (129 loc) · 3.83 KB
/
sum.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
package dynaml
import (
"fmt"
"github.com/mandelsoft/spiff/debug"
"github.com/mandelsoft/spiff/yaml"
)
type SumExpr struct {
A Expression
I Expression
Lambda Expression
}
func (e SumExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) {
resolved := true
debug.Debug("evaluate sum")
value, info, ok := ResolveExpressionOrPushEvaluation(&e.A, &resolved, nil, binding, true)
if !ok {
return nil, info, false
}
initial, info, ok := ResolveExpressionOrPushEvaluation(&e.I, &resolved, &info, binding, false)
if !ok {
return nil, info, false
}
undef := info.Undefined // TODO
debug.Debug("sum: initial undef: %t", undef)
inline := isInline(e.Lambda)
lvalue, infoe, ok := ResolveExpressionOrPushEvaluation(&e.Lambda, &resolved, nil, binding, false)
if !ok {
return nil, infoe, false
}
if !resolved {
return e, info.Join(infoe), ok
}
lambda, ok := lvalue.(LambdaValue)
if !ok {
return infoe.Error("sum requires a lambda value")
}
debug.Debug("map: using lambda %+v\n", lambda)
var result interface{}
switch value.(type) {
case []yaml.Node:
resolved, result, info, ok = sumList(inline, value.([]yaml.Node), lambda, initial, binding)
case map[string]yaml.Node:
resolved, result, info, ok = sumMap(inline, value.(map[string]yaml.Node), lambda, initial, binding)
default:
return info.Error("map or list required for sum")
}
if !ok {
return nil, info, false
}
if !resolved {
return e, info, true
}
debug.Debug("sum: --> %+v\n", result)
return result, info, true
}
func (e SumExpr) String() string {
lambda, ok := e.Lambda.(LambdaExpr)
if ok {
return fmt.Sprintf("sum[%s|%s%s]", e.A, e.I, fmt.Sprintf("%s", lambda)[len("lambda"):])
} else {
return fmt.Sprintf("sum[%s|%s|%s]", e.A, e.I, e.Lambda)
}
}
func sumList(inline bool, source []yaml.Node, e LambdaValue, initial interface{}, binding Binding) (bool, interface{}, EvaluationInfo, bool) {
inp := make([]interface{}, len(e.lambda.Parameters))
result := initial
info := DefaultInfo()
if len(e.lambda.Parameters) > 3 {
info.SetError("mapping expression take a maximum of 3 arguments")
return true, nil, info, false
}
if len(e.lambda.Parameters) < 2 {
info.SetError("mapping expression take a minimum of 2 arguments")
return true, nil, info, false
}
debug.Debug("sum: initial: %+v\n", initial)
for i, n := range source {
debug.Debug("sum: mapping for %d: %+v\n", i, n)
inp[0] = result
inp[1] = i
inp[len(inp)-1] = n.Value()
resolved, mapped, info, ok := e.Evaluate(inline, false, false, nil, inp, binding, false)
if !ok {
debug.Debug("sum: %d %+v: failed\n", i, n)
return true, nil, info, false
}
if !resolved {
return false, nil, info, ok
}
_, ok = mapped.(Expression)
if ok {
debug.Debug("sum: %d unresolved -> KEEP\n", i)
return false, nil, info, true
}
debug.Debug("sum: %d --> %+v\n", i, mapped)
result = mapped
}
debug.Debug("sum: result: %+v\n", result)
return true, result, info, true
}
func sumMap(inline bool, source map[string]yaml.Node, e LambdaValue, initial interface{}, binding Binding) (bool, interface{}, EvaluationInfo, bool) {
inp := make([]interface{}, len(e.lambda.Parameters))
result := initial
info := DefaultInfo()
keys := getSortedKeys(source)
for _, k := range keys {
n := source[k]
debug.Debug("map: mapping for %s: %+v\n", k, n)
inp[0] = result
inp[1] = k
inp[len(inp)-1] = n.Value()
resolved, mapped, info, ok := e.Evaluate(inline, false, false, nil, inp, binding, false)
if !ok {
debug.Debug("map: %s %+v: failed\n", k, n)
return true, nil, info, false
}
if !resolved {
return false, nil, info, ok
}
_, ok = mapped.(Expression)
if ok {
debug.Debug("map: %s unresolved -> KEEP\n", k)
return false, nil, info, true
}
debug.Debug("map: %s --> %+v\n", k, mapped)
result = mapped
}
return true, result, info, true
}