1
2
3
4
5
6 package gval
7
8 import (
9 "context"
10 "fmt"
11 "math"
12 "reflect"
13 "text/scanner"
14 "time"
15
16 "github.com/shopspring/decimal"
17 )
18
19
20 func Evaluate(expression string, parameter interface{}, opts ...Language) (interface{}, error) {
21 return EvaluateWithContext(context.Background(), expression, parameter, opts...)
22 }
23
24
25 func EvaluateWithContext(c context.Context, expression string, parameter interface{}, opts ...Language) (interface{}, error) {
26 l := full
27 if len(opts) > 0 {
28 l = NewLanguage(append([]Language{l}, opts...)...)
29 }
30 return l.EvaluateWithContext(c, expression, parameter)
31 }
32
33
34
35
36
37
38
39 func Full(extensions ...Language) Language {
40 if len(extensions) == 0 {
41 return full
42 }
43 return NewLanguage(append([]Language{full}, extensions...)...)
44 }
45
46
47
48
49
50
51
52 func Arithmetic() Language {
53 return arithmetic
54 }
55
56
57
58
59
60
61
62
63 func DecimalArithmetic() Language {
64 return decimalArithmetic
65 }
66
67
68
69
70
71
72 func Bitmask() Language {
73 return bitmask
74 }
75
76
77
78 func Text() Language {
79 return text
80 }
81
82
83
84
85
86
87
88 func PropositionalLogic() Language {
89 return propositionalLogic
90 }
91
92
93
94 func JSON() Language {
95 return ljson
96 }
97
98
99 func Parentheses() Language {
100 return parentheses
101 }
102
103
104 func Ident() Language {
105 return ident
106 }
107
108
109
110 func Base() Language {
111 return base
112 }
113
114 var full = NewLanguage(arithmetic, bitmask, text, propositionalLogic, ljson,
115
116 InfixOperator("in", inArray),
117
118 InfixShortCircuit("??", func(a interface{}) (interface{}, bool) {
119 v := reflect.ValueOf(a)
120 return a, a != nil && !v.IsZero()
121 }),
122 InfixOperator("??", func(a, b interface{}) (interface{}, error) {
123 if v := reflect.ValueOf(a); a == nil || v.IsZero() {
124 return b, nil
125 }
126 return a, nil
127 }),
128
129 PostfixOperator("?", parseIf),
130
131 Function("date", func(arguments ...interface{}) (interface{}, error) {
132 if len(arguments) != 1 {
133 return nil, fmt.Errorf("date() expects exactly one string argument")
134 }
135 s, ok := arguments[0].(string)
136 if !ok {
137 return nil, fmt.Errorf("date() expects exactly one string argument")
138 }
139 for _, format := range [...]string{
140 time.ANSIC,
141 time.UnixDate,
142 time.RubyDate,
143 time.Kitchen,
144 time.RFC3339,
145 time.RFC3339Nano,
146 "2006-01-02",
147 "2006-01-02 15:04",
148 "2006-01-02 15:04:05",
149 "2006-01-02 15:04:05-07:00",
150 "2006-01-02T15Z0700",
151 "2006-01-02T15:04Z0700",
152 "2006-01-02T15:04:05Z0700",
153 "2006-01-02T15:04:05.999999999Z0700",
154 } {
155 ret, err := time.ParseInLocation(format, s, time.Local)
156 if err == nil {
157 return ret, nil
158 }
159 }
160 return nil, fmt.Errorf("date() could not parse %s", s)
161 }),
162 )
163
164 var ljson = NewLanguage(
165 PrefixExtension('[', parseJSONArray),
166 PrefixExtension('{', parseJSONObject),
167 )
168
169 var arithmetic = NewLanguage(
170 InfixNumberOperator("+", func(a, b float64) (interface{}, error) { return a + b, nil }),
171 InfixNumberOperator("-", func(a, b float64) (interface{}, error) { return a - b, nil }),
172 InfixNumberOperator("*", func(a, b float64) (interface{}, error) { return a * b, nil }),
173 InfixNumberOperator("/", func(a, b float64) (interface{}, error) { return a / b, nil }),
174 InfixNumberOperator("%", func(a, b float64) (interface{}, error) { return math.Mod(a, b), nil }),
175 InfixNumberOperator("**", func(a, b float64) (interface{}, error) { return math.Pow(a, b), nil }),
176
177 InfixNumberOperator(">", func(a, b float64) (interface{}, error) { return a > b, nil }),
178 InfixNumberOperator(">=", func(a, b float64) (interface{}, error) { return a >= b, nil }),
179 InfixNumberOperator("<", func(a, b float64) (interface{}, error) { return a < b, nil }),
180 InfixNumberOperator("<=", func(a, b float64) (interface{}, error) { return a <= b, nil }),
181
182 InfixNumberOperator("==", func(a, b float64) (interface{}, error) { return a == b, nil }),
183 InfixNumberOperator("!=", func(a, b float64) (interface{}, error) { return a != b, nil }),
184
185 base,
186 )
187
188 var decimalArithmetic = NewLanguage(
189 InfixDecimalOperator("+", func(a, b decimal.Decimal) (interface{}, error) { return a.Add(b), nil }),
190 InfixDecimalOperator("-", func(a, b decimal.Decimal) (interface{}, error) { return a.Sub(b), nil }),
191 InfixDecimalOperator("*", func(a, b decimal.Decimal) (interface{}, error) { return a.Mul(b), nil }),
192 InfixDecimalOperator("/", func(a, b decimal.Decimal) (interface{}, error) { return a.Div(b), nil }),
193 InfixDecimalOperator("%", func(a, b decimal.Decimal) (interface{}, error) { return a.Mod(b), nil }),
194 InfixDecimalOperator("**", func(a, b decimal.Decimal) (interface{}, error) { return a.Pow(b), nil }),
195
196 InfixDecimalOperator(">", func(a, b decimal.Decimal) (interface{}, error) { return a.GreaterThan(b), nil }),
197 InfixDecimalOperator(">=", func(a, b decimal.Decimal) (interface{}, error) { return a.GreaterThanOrEqual(b), nil }),
198 InfixDecimalOperator("<", func(a, b decimal.Decimal) (interface{}, error) { return a.LessThan(b), nil }),
199 InfixDecimalOperator("<=", func(a, b decimal.Decimal) (interface{}, error) { return a.LessThanOrEqual(b), nil }),
200
201 InfixDecimalOperator("==", func(a, b decimal.Decimal) (interface{}, error) { return a.Equal(b), nil }),
202 InfixDecimalOperator("!=", func(a, b decimal.Decimal) (interface{}, error) { return !a.Equal(b), nil }),
203 base,
204
205 PrefixExtension(scanner.Int, parseDecimal),
206 PrefixExtension(scanner.Float, parseDecimal),
207 PrefixOperator("-", func(c context.Context, v interface{}) (interface{}, error) {
208 i, ok := convertToFloat(v)
209 if !ok {
210 return nil, fmt.Errorf("unexpected %v(%T) expected number", v, v)
211 }
212 return decimal.NewFromFloat(i).Neg(), nil
213 }),
214 )
215
216 var bitmask = NewLanguage(
217 InfixNumberOperator("^", func(a, b float64) (interface{}, error) { return float64(int64(a) ^ int64(b)), nil }),
218 InfixNumberOperator("&", func(a, b float64) (interface{}, error) { return float64(int64(a) & int64(b)), nil }),
219 InfixNumberOperator("|", func(a, b float64) (interface{}, error) { return float64(int64(a) | int64(b)), nil }),
220 InfixNumberOperator("<<", func(a, b float64) (interface{}, error) { return float64(int64(a) << uint64(b)), nil }),
221 InfixNumberOperator(">>", func(a, b float64) (interface{}, error) { return float64(int64(a) >> uint64(b)), nil }),
222
223 PrefixOperator("~", func(c context.Context, v interface{}) (interface{}, error) {
224 i, ok := convertToFloat(v)
225 if !ok {
226 return nil, fmt.Errorf("unexpected %T expected number", v)
227 }
228 return float64(^int64(i)), nil
229 }),
230 )
231
232 var text = NewLanguage(
233 InfixTextOperator("+", func(a, b string) (interface{}, error) { return fmt.Sprintf("%v%v", a, b), nil }),
234
235 InfixTextOperator("<", func(a, b string) (interface{}, error) { return a < b, nil }),
236 InfixTextOperator("<=", func(a, b string) (interface{}, error) { return a <= b, nil }),
237 InfixTextOperator(">", func(a, b string) (interface{}, error) { return a > b, nil }),
238 InfixTextOperator(">=", func(a, b string) (interface{}, error) { return a >= b, nil }),
239
240 InfixEvalOperator("=~", regEx),
241 InfixEvalOperator("!~", notRegEx),
242 base,
243 )
244
245 var propositionalLogic = NewLanguage(
246 PrefixOperator("!", func(c context.Context, v interface{}) (interface{}, error) {
247 b, ok := convertToBool(v)
248 if !ok {
249 return nil, fmt.Errorf("unexpected %T expected bool", v)
250 }
251 return !b, nil
252 }),
253
254 InfixShortCircuit("&&", func(a interface{}) (interface{}, bool) { return false, a == false }),
255 InfixBoolOperator("&&", func(a, b bool) (interface{}, error) { return a && b, nil }),
256 InfixShortCircuit("||", func(a interface{}) (interface{}, bool) { return true, a == true }),
257 InfixBoolOperator("||", func(a, b bool) (interface{}, error) { return a || b, nil }),
258
259 InfixBoolOperator("==", func(a, b bool) (interface{}, error) { return a == b, nil }),
260 InfixBoolOperator("!=", func(a, b bool) (interface{}, error) { return a != b, nil }),
261
262 base,
263 )
264
265 var parentheses = NewLanguage(
266 PrefixExtension('(', parseParentheses),
267 )
268
269 var ident = NewLanguage(
270 PrefixMetaPrefix(scanner.Ident, parseIdent),
271 )
272
273 var base = NewLanguage(
274 PrefixExtension(scanner.Int, parseNumber),
275 PrefixExtension(scanner.Float, parseNumber),
276 PrefixOperator("-", func(c context.Context, v interface{}) (interface{}, error) {
277 i, ok := convertToFloat(v)
278 if !ok {
279 return nil, fmt.Errorf("unexpected %v(%T) expected number", v, v)
280 }
281 return -i, nil
282 }),
283
284 PrefixExtension(scanner.String, parseString),
285 PrefixExtension(scanner.Char, parseString),
286 PrefixExtension(scanner.RawString, parseString),
287
288 Constant("true", true),
289 Constant("false", false),
290
291 InfixOperator("==", func(a, b interface{}) (interface{}, error) { return reflect.DeepEqual(a, b), nil }),
292 InfixOperator("!=", func(a, b interface{}) (interface{}, error) { return !reflect.DeepEqual(a, b), nil }),
293 parentheses,
294
295 Precedence("??", 0),
296
297 Precedence("||", 20),
298 Precedence("&&", 21),
299
300 Precedence("==", 40),
301 Precedence("!=", 40),
302 Precedence(">", 40),
303 Precedence(">=", 40),
304 Precedence("<", 40),
305 Precedence("<=", 40),
306 Precedence("=~", 40),
307 Precedence("!~", 40),
308 Precedence("in", 40),
309
310 Precedence("^", 60),
311 Precedence("&", 60),
312 Precedence("|", 60),
313
314 Precedence("<<", 90),
315 Precedence(">>", 90),
316
317 Precedence("+", 120),
318 Precedence("-", 120),
319
320 Precedence("*", 150),
321 Precedence("/", 150),
322 Precedence("%", 150),
323
324 Precedence("**", 200),
325
326 ident,
327 )
328
View as plain text