Skip to content

Commit 23b5732

Browse files
committed
added debug logging and better reporting of errors
1 parent 23c9d2a commit 23b5732

File tree

4 files changed

+38
-4
lines changed

4 files changed

+38
-4
lines changed

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ In addition to boolean expressions, sepcial contants `True` and `False` may be u
130130

131131
Do not double-quote them, or they will become plain strings!
132132

133+
## MultiValue
134+
135+
This is container `Value`. It can contain zero or any number of `Value`'s. Currently, this is only truly useful with functions, mostly because it is yet undecided how to define what operations would mean on a `MultiValue`.
136+
133137
## Supported operations
134138

135139
* Operators: `+` `-` `*` `/` `%` `**` `<<` `>>` `<` `<=` `==` `!=` `>` `>=` `And` `&&` `Or` `||`
@@ -144,6 +148,7 @@ Do not double-quote them, or they will become plain strings!
144148
* Go classifies bit shift operators with the higher `*`.
145149
* `&&` is synonymous of `And`.
146150
* `||` is synonymous of `Or`.
151+
* Worded operators such as `And` and `Or` are **case-sensitive** and must be followed by a blank character. `True Or (False)` is a Bool expression with the `Or` operator but `True Or(False)` is an invalid expression attempting to call a user-defined function called `Or()`.
147152
* Types: String, Number, Bool, MultiValue
148153
* Associativity with parentheses: `(` and `)`
149154
* Functions:

gal_test.go

+10
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,16 @@ func TestEval_Boolean(t *testing.T) {
134134
expr = `True Or False`
135135
val = gal.Parse(expr).Eval()
136136
assert.Equal(t, gal.True.String(), val.String())
137+
138+
expr = `True Or (False)`
139+
val = gal.Parse(expr).Eval()
140+
assert.Equal(t, gal.True.String(), val.String())
141+
142+
// in this expression, the `()` are attached to `Or` which makes `Or()` a user-defined
143+
// function, rather than the `Or` operator.
144+
expr = `True Or(False)`
145+
val = gal.Parse(expr).Eval()
146+
assert.Equal(t, `undefined: unknown function 'Or'`, val.String())
137147
}
138148

139149
func TestWithVariablesAndFunctions(t *testing.T) {

tree.go

+21-2
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,13 @@ func (tree Tree) Calc(isOperatorInPrecedenceGroup func(Operator) bool, cfg *tree
174174
var val entry
175175
var op Operator = invalidOperator //nolint: stylecheck
176176

177+
slog.Debug("Tree.Calc: start walking Tree", "tree", tree.String())
177178
for i := 0; i < tree.TrunkLen(); i++ {
179+
if v, ok := val.(Undefined); ok {
180+
slog.Debug("Tree.Calc: val is Undefined", "i", i, "val", v.String())
181+
return Tree{v}
182+
}
183+
178184
e := tree[i]
179185
slog.Debug("Tree.Calc: entry in Tree", "i", i, "kind", e.kind().String())
180186
if e == nil {
@@ -211,6 +217,10 @@ func (tree Tree) Calc(isOperatorInPrecedenceGroup func(Operator) bool, cfg *tree
211217
}
212218

213219
rhsVal := e.(Tree).Eval(WithFunctions(cfg.functions), WithVariables(cfg.variables))
220+
if v, ok := rhsVal.(Undefined); ok {
221+
slog.Debug("Tree.Calc: val is Undefined", "i", i, "val", v.String())
222+
return Tree{v}
223+
}
214224
if val == nil {
215225
val = rhsVal
216226
continue
@@ -230,6 +240,7 @@ func (tree Tree) Calc(isOperatorInPrecedenceGroup func(Operator) bool, cfg *tree
230240
outTree = append(outTree, val)
231241
}
232242
outTree = append(outTree, op)
243+
// just found and process the current operator - now, reset val and op and start again from fresh
233244
val = nil
234245
op = invalidOperator
235246

@@ -239,14 +250,20 @@ func (tree Tree) Calc(isOperatorInPrecedenceGroup func(Operator) bool, cfg *tree
239250
if f.BodyFn == nil {
240251
f.BodyFn = cfg.functions.Function(f.Name)
241252
}
253+
242254
rhsVal := f.Eval(WithFunctions(cfg.functions), WithVariables(cfg.variables))
255+
if v, ok := rhsVal.(Undefined); ok {
256+
slog.Debug("Tree.Calc: val is Undefined", "i", i, "val", v.String())
257+
return Tree{v}
258+
}
243259
if val == nil {
244260
val = rhsVal
245261
continue
246262
}
247263

264+
lhsVal := val
248265
val = calculate(val.(Value), op, rhsVal)
249-
slog.Debug("Tree.Calc: functionEntryKind - calculate", "i", i, "val", val.(Value).String(), "op", op.String(), "rhsVal", rhsVal.String(), "result", val.(Value).String())
266+
slog.Debug("Tree.Calc: functionEntryKind - calculate", "i", i, "lhsVal", lhsVal.(Value).String(), "op", op.String(), "rhsVal", rhsVal.String(), "result", val.(Value).String())
250267

251268
case variableEntryKind:
252269
slog.Debug("Tree.Calc: variableEntryKind", "i", i, "name", e.(Variable).Name)
@@ -268,11 +285,13 @@ func (tree Tree) Calc(isOperatorInPrecedenceGroup func(Operator) bool, cfg *tree
268285
slog.Debug("Tree.Calc: variableEntryKind - calculate", "i", i, "val", val.(Value).String(), "op", op.String(), "rhsVal", rhsVal.String(), "result", val.(Value).String())
269286

270287
case unknownEntryKind:
288+
slog.Debug("Tree.Calc: unknownEntryKind", "i", i, "val", val, "op", op.String(), "e", e)
271289
return Tree{e}
272290

273291
default:
292+
slog.Debug("Tree.Calc: default case", "i", i, "val", val, "op", op.String(), "e", e)
274293
return Tree{
275-
NewUndefinedWithReasonf("internal error: unknown entry kind: '%v'", e.kind()),
294+
NewUndefinedWithReasonf("internal error: unknown entry kind: '%s'", e.kind().String()),
276295
}
277296
}
278297
}

value.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func (m MultiValue) AsString() String {
7676

7777
func (m MultiValue) Get(i int) Value {
7878
if i > len(m.values) {
79-
return NewUndefinedWithReasonf(fmt.Sprintf("out of bounds: trying to get arg #%d on MultiValue that has %d arguments", i, len(m.values)))
79+
return NewUndefinedWithReasonf("out of bounds: trying to get arg #%d on MultiValue that has %d arguments", i, len(m.values))
8080
}
8181

8282
return m.values[i]
@@ -400,7 +400,7 @@ func (n Number) Trunc(precision int32) Number {
400400

401401
func (n Number) Factorial() Value {
402402
if !n.value.IsInteger() || n.value.IsNegative() {
403-
return NewUndefinedWithReasonf(fmt.Sprintf("Factorial: requires a positive integer, cannot accept %s", n.String()))
403+
return NewUndefinedWithReasonf("Factorial: requires a positive integer, cannot accept %s", n.String())
404404
}
405405

406406
res := decimal.NewFromInt(1)

0 commit comments

Comments
 (0)