1
1
package gal
2
2
3
- import (
4
- "fmt"
5
-
6
- "github.com/pkg/errors"
7
- )
8
-
9
3
type exprType int
10
4
11
5
const (
@@ -29,287 +23,13 @@ type Value interface {
29
23
entry
30
24
}
31
25
32
- func Eval (expr string ) Value {
33
- tree , err := buildExprTree (expr )
26
+ func Eval (expr string , opts ... option ) Value {
27
+ treeBuilder := NewTreeBuilder (opts ... )
28
+
29
+ tree , err := treeBuilder .FromExpr (expr )
34
30
if err != nil {
35
31
return NewUndefinedWithReasonf (err .Error ())
36
32
}
37
33
38
34
return tree .Eval ()
39
35
}
40
-
41
- func buildExprTree (expr string ) (Tree , error ) {
42
- exprTree := Tree {}
43
-
44
- for idx := 0 ; idx < len (expr ); {
45
- part , ptype , length , err := extractPart (expr [idx :])
46
- if err != nil {
47
- return nil , err
48
- }
49
-
50
- if ptype == blankType {
51
- break
52
- }
53
-
54
- switch ptype {
55
- case numericalType :
56
- v , err := NewNumberFromString (part )
57
- if err != nil {
58
- return nil , err
59
- }
60
- exprTree = append (exprTree , v )
61
-
62
- case stringType :
63
- v := NewString (part )
64
- exprTree = append (exprTree , v )
65
-
66
- case operatorType :
67
- switch part {
68
- case plus .String ():
69
- exprTree = append (exprTree , plus )
70
- case minus .String ():
71
- exprTree = append (exprTree , minus )
72
- case multiply .String ():
73
- exprTree = append (exprTree , multiply )
74
- case divide .String ():
75
- exprTree = append (exprTree , divide )
76
- case modulus .String ():
77
- exprTree = append (exprTree , modulus )
78
- default :
79
- return nil , errors .WithStack (newErrUnknownOperator (part ))
80
- }
81
-
82
- case functionType :
83
- // TODO: squash the leading and trailing '()'
84
- fname , l , _ := readFunctionName (part )
85
- v , err := buildExprTree (part [l + 1 : len (part )- 1 ]) // exclude leading '(' and trailing ')'
86
- if err != nil {
87
- return nil , err
88
- }
89
- if fname == "" {
90
- exprTree = append (exprTree , v )
91
- } else {
92
- exprTree = append (exprTree , NewFunction (fname , v .Split ()... ))
93
- }
94
-
95
- case variableType :
96
- v := NewVariable (part )
97
- exprTree = append (exprTree , v )
98
-
99
- default :
100
- return nil , newErrSyntaxError (fmt .Sprintf ("internal error: unknown expression part type '%v'" , ptype ))
101
- }
102
-
103
- idx += length
104
- }
105
-
106
- return exprTree , nil
107
- }
108
-
109
- // returns the part extracted as string, the type extracted, the cursor position
110
- // after extraction or an error.
111
- func extractPart (expr string ) (string , exprType , int , error ) {
112
- // left trim blanks
113
- pos := 0
114
- for _ , r := range expr {
115
- if ! isBlankSpace (r ) {
116
- break
117
- }
118
- pos ++
119
- }
120
-
121
- // blank: no part
122
- if pos == len (expr ) {
123
- return "" , blankType , pos , nil
124
- }
125
-
126
- // read part - "string"
127
- if expr [pos ] == '"' {
128
- s , l , err := readString (expr [pos :])
129
- if err != nil {
130
- return "" , unknownType , 0 , err
131
- }
132
- return s , stringType , pos + l , nil
133
- }
134
-
135
- // read part - :variable:
136
- if expr [pos ] == ':' {
137
- s , l , err := readVariable (expr [pos :])
138
- if err != nil {
139
- return "" , unknownType , 0 , err
140
- }
141
- return s , variableType , pos + l , nil
142
- }
143
-
144
- // read part - function(...)
145
- // conceptually, parenthesis grouping is a special case of anonymous identity function
146
- if expr [pos ] == '(' || (expr [pos ] >= 'a' && expr [pos ] <= 'z' ) || (expr [pos ] >= 'A' && expr [pos ] <= 'Z' ) {
147
- fname , lf , err := readFunctionName (expr [pos :])
148
- if err != nil {
149
- return "" , unknownType , 0 , err
150
- }
151
- fargs , la , err := readFunctionArguments (expr [pos + lf :])
152
- if err != nil {
153
- return "" , unknownType , 0 , err
154
- }
155
- return fname + fargs , functionType , pos + lf + la , nil
156
- }
157
-
158
- // read part - operator
159
- // TODO: only single character operators are supported
160
- if isOperator (string (expr [pos ])) {
161
- if expr [pos ] == '+' || expr [pos ] == '-' {
162
- s , l := squashPlusMinusChain (expr [pos :])
163
- return s , operatorType , pos + l , nil
164
- }
165
- return string (expr [pos ]), operatorType , pos + 1 , nil
166
- }
167
-
168
- // read part - number
169
- // TODO: complex numbers are not supported
170
- s , l , err := readNumber (expr [pos :])
171
- if err != nil {
172
- return "" , unknownType , 0 , err
173
- }
174
- return s , numericalType , pos + l , nil
175
- }
176
-
177
- func readString (expr string ) (string , int , error ) {
178
- to := 1 // keep leading double-quotes
179
- escapes := 0
180
-
181
- for i , r := range expr [1 :] {
182
- to += 1
183
- if expr [i ] == '\\' {
184
- escapes += 1
185
- continue
186
- }
187
- if r == '"' && (escapes == 0 || escapes & 1 == 0 ) {
188
- break
189
- }
190
-
191
- escapes = 0
192
- }
193
-
194
- if expr [to - 1 ] != '"' {
195
- return "" , 0 , errors .WithStack (newErrSyntaxError (fmt .Sprintf ("non-terminated string '%s'" , expr [:to ])))
196
- }
197
-
198
- return expr [:to ], to , nil
199
- }
200
-
201
- func readVariable (expr string ) (string , int , error ) {
202
- to := 1 // keep leading ':'
203
-
204
- for _ , r := range expr [1 :] {
205
- to += 1
206
- if r == ':' {
207
- break
208
- }
209
- if isBlankSpace (r ) {
210
- return "" , 0 , errors .WithStack (newErrSyntaxError (fmt .Sprintf ("invalid character '%c' for variable name '%s'" , r , expr [:to ])))
211
- }
212
- }
213
-
214
- if expr [to - 1 ] != ':' {
215
- return "" , 0 , errors .WithStack (newErrSyntaxError (fmt .Sprintf ("missing ':' to end variable '%s'" , expr [:to ])))
216
- }
217
-
218
- return expr [:to ], to , nil
219
- }
220
-
221
- func readFunctionName (expr string ) (string , int , error ) {
222
- to := 0 // this could be an anonymous identity function (i.e. simple case of parenthesis grouping)
223
-
224
- for _ , r := range expr {
225
- if r == '(' {
226
- return expr [:to ], to , nil
227
- }
228
- if isBlankSpace (r ) {
229
- return "" , 0 , errors .WithStack (newErrSyntaxError (fmt .Sprintf ("invalid character '%c' for function name '%s'" , r , expr [:to ])))
230
- }
231
- to += 1
232
- }
233
-
234
- return "" , 0 , errors .WithStack (newErrSyntaxError (fmt .Sprintf ("missing '(' for function name '%s'" , expr [:to ])))
235
- }
236
-
237
- func readFunctionArguments (expr string ) (string , int , error ) {
238
- to := 1
239
- bktCount := 1 // the currently opened bracket
240
-
241
- for _ , r := range expr [1 :] {
242
- to += 1
243
- if r == '(' {
244
- bktCount ++
245
- continue
246
- }
247
- if r == ')' {
248
- bktCount --
249
- if bktCount == 0 {
250
- return expr [:to ], to , nil
251
- }
252
- }
253
- // TODO: handle stringType
254
- }
255
-
256
- return "" , 0 , errors .WithStack (newErrSyntaxError (fmt .Sprintf ("missing ')' for function arguments '%s'" , expr [:to ])))
257
- }
258
-
259
- func readNumber (expr string ) (string , int , error ) {
260
- to := 0
261
- isFloat := false
262
-
263
- for _ , r := range expr {
264
- if isBlankSpace (r ) || isOperator (string (r )) {
265
- break
266
- }
267
-
268
- to ++
269
-
270
- if r == '.' && ! isFloat {
271
- isFloat = true
272
- continue
273
- }
274
- if r >= '0' && r <= '9' {
275
- continue
276
- }
277
-
278
- return "" , 0 , errors .WithStack (newErrSyntaxError (fmt .Sprintf ("invalid character '%c' for number '%s'" , r , expr [:to ])))
279
- }
280
-
281
- return expr [:to ], to , nil
282
- }
283
-
284
- func squashPlusMinusChain (expr string ) (string , int ) {
285
- to := 0
286
- outcomeSign := 1
287
-
288
- for _ , r := range expr {
289
- // if isBlankSpace(r) {
290
- // break
291
- // }
292
- if r != '+' && r != '-' && ! isBlankSpace (r ) {
293
- break
294
- }
295
- if r == '-' {
296
- outcomeSign = - outcomeSign
297
- }
298
- to += 1
299
- }
300
-
301
- sign := "-"
302
- if outcomeSign == 1 {
303
- sign = "+"
304
- }
305
-
306
- return sign , to
307
- }
308
-
309
- func isBlankSpace (r rune ) bool {
310
- return r == ' ' || r == '\t' || r == '\n'
311
- }
312
-
313
- func isOperator (s string ) bool {
314
- return s == "+" || s == "-" || s == "/" || s == "*" || s == "^" || s == "%"
315
- }
0 commit comments