@@ -16,7 +16,7 @@ type Num struct {
16
16
}
17
17
18
18
var opPrecedence = map [string ]int {
19
- "(" : 1 , ")" : 1 ,
19
+ // "(": 1, this is for low precedence like function calls, they are handled separately
20
20
"or" : 2 ,
21
21
"and" : 3 ,
22
22
"not" : 4 ,
@@ -26,6 +26,7 @@ var opPrecedence = map[string]int{
26
26
}
27
27
28
28
type stack [T any ] struct {
29
+ name string
29
30
elems []T
30
31
}
31
32
@@ -35,7 +36,7 @@ func (s *stack[T]) push(t T) {
35
36
36
37
func (s * stack [T ]) pop () T {
37
38
if len (s .elems ) == 0 {
38
- panic (" stack is empty" )
39
+ panic (s . name + " stack is empty" )
39
40
}
40
41
t := s .elems [len (s .elems )- 1 ]
41
42
s .elems = s .elems [:len (s .elems )- 1 ]
@@ -44,19 +45,26 @@ func (s *stack[T]) pop() T {
44
45
45
46
func (s * stack [T ]) peek () T {
46
47
if len (s .elems ) == 0 {
47
- panic (" stack is empty" )
48
+ panic (s . name + " stack is empty" )
48
49
}
49
50
return s .elems [len (s .elems )- 1 ]
50
51
}
51
52
52
53
type operator string
53
54
54
- type Eval struct {
55
+ type eval struct {
55
56
stackNum stack [Num ]
56
57
stackOp stack [operator ]
57
58
funcMap map [string ]func ([]Num ) Num
58
59
}
59
60
61
+ func newEval () * eval {
62
+ e := & eval {}
63
+ e .stackNum .name = "num"
64
+ e .stackOp .name = "op"
65
+ return e
66
+ }
67
+
60
68
func toNum (v any ) (Num , error ) {
61
69
switch v := v .(type ) {
62
70
case string :
@@ -144,7 +152,7 @@ func toOp(v any) (operator, error) {
144
152
if v , ok := v .(string ); ok {
145
153
return operator (v ), nil
146
154
}
147
- return "" , fmt .Errorf ("unable to convert %q to operator" , v )
155
+ return "" , fmt .Errorf (`unsupported token type "%T"` , v )
148
156
}
149
157
150
158
func (op operator ) hasOpenBracket () bool {
@@ -184,12 +192,14 @@ func (err ExprError) Unwrap() error {
184
192
return err .err
185
193
}
186
194
187
- func (e * Eval ) applyOp () {
195
+ func (e * eval ) applyOp () {
188
196
op := e .stackOp .pop ()
189
197
if op == "not" {
190
198
num := e .stackNum .pop ()
191
199
i , _ := util .ToInt64 (num .Value )
192
200
e .stackNum .push (Num {truth (i == 0 )})
201
+ } else if op .hasOpenBracket () || op .isCloseBracket () || op .isComma () {
202
+ panic (fmt .Sprintf ("incomplete sub-expression with operator %q" , op ))
193
203
} else {
194
204
num2 := e .stackNum .pop ()
195
205
num1 := e .stackNum .pop ()
@@ -201,7 +211,7 @@ func (e *Eval) applyOp() {
201
211
// If no error occurs, the result is either an int64 or a float64.
202
212
// If all numbers are integer, the result is an int64, otherwise if there is any float number, the result is a float64.
203
213
// Golang's template syntax supports comparable int types: {{if lt $i32 $i64}} is right.
204
- func (e * Eval ) Exec (tokens ... any ) (ret Num , err error ) {
214
+ func (e * eval ) Exec (tokens ... any ) (ret Num , err error ) {
205
215
defer func () {
206
216
if r := recover (); r != nil {
207
217
rErr , ok := r .(error )
@@ -228,7 +238,7 @@ func (e *Eval) Exec(tokens ...any) (ret Num, err error) {
228
238
e .stackOp .push (op )
229
239
case op .isCloseBracket (), op .isComma ():
230
240
var stackTopOp operator
231
- for {
241
+ for len ( e . stackOp . elems ) > 0 {
232
242
stackTopOp = e .stackOp .peek ()
233
243
if stackTopOp .hasOpenBracket () || stackTopOp .isComma () {
234
244
break
@@ -265,19 +275,19 @@ func (e *Eval) Exec(tokens ...any) (ret Num, err error) {
265
275
default :
266
276
for len (e .stackOp .elems ) > 0 && len (e .stackNum .elems ) > 0 {
267
277
stackTopOp := e .stackOp .peek ()
268
- if stackTopOp .isComma () || precedence (stackTopOp , op ) < 0 {
278
+ if stackTopOp .hasOpenBracket () || stackTopOp . isComma () || precedence (stackTopOp , op ) < 0 {
269
279
break
270
280
}
271
281
e .applyOp ()
272
282
}
273
283
e .stackOp .push (op )
274
284
}
275
285
}
276
- for len (e .stackOp .elems ) > 0 {
286
+ for len (e .stackOp .elems ) > 0 && ! e . stackOp . peek (). isComma () {
277
287
e .applyOp ()
278
288
}
279
289
if len (e .stackNum .elems ) != 1 {
280
- return Num {}, ExprError {"too many final values" , tokens , nil }
290
+ return Num {}, ExprError {fmt . Sprintf ( "expect 1 value as final result, but there are %d" , len ( e . stackNum . elems )) , tokens , nil }
281
291
}
282
292
return e .stackNum .pop (), nil
283
293
}
@@ -327,6 +337,7 @@ func fnSum(nums []Num) Num {
327
337
}
328
338
329
339
func Expr (tokens ... any ) (Num , error ) {
330
- e := & Eval {funcMap : map [string ]func ([]Num ) Num {"sum" : fnSum }}
340
+ e := newEval ()
341
+ e .funcMap = map [string ]func ([]Num ) Num {"sum" : fnSum }
331
342
return e .Exec (tokens ... )
332
343
}
0 commit comments