Skip to content

Commit 21a015b

Browse files
committed
itent-types
Signed-off-by: yaacov <[email protected]>
1 parent 41d3f37 commit 21a015b

File tree

4 files changed

+87
-22
lines changed

4 files changed

+87
-22
lines changed

v6/pkg/walkers/semantics/dates.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ func toDate(value interface{}) (time.Time, bool) {
77
case time.Time:
88
return v, true
99
case string:
10-
date, err := time.Parse(time.RFC3339, v)
11-
if err == nil {
10+
// try full RFC3339 first
11+
if date, err := time.Parse(time.RFC3339, v); err == nil {
12+
return date, true
13+
}
14+
// then try short date format YYYY-MM-DD
15+
if date, err := time.Parse("2006-01-02", v); err == nil {
1216
return date, true
1317
}
1418
}

v6/pkg/walkers/semantics/evaluators.go

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,29 @@ import (
99
"github.com/yaacov/tree-search-language/v6/pkg/tsl"
1010
)
1111

12+
// processArray applies processItem over all elements
13+
func processArray(arr []interface{}) []interface{} {
14+
out := make([]interface{}, len(arr))
15+
for i, it := range arr {
16+
out[i], _ = processValue(it)
17+
}
18+
return out
19+
}
20+
21+
// processValue dispatches on value type: array, string/date, number, or other
22+
func processValue(value interface{}) (interface{}, error) {
23+
if arr, ok := value.([]interface{}); ok {
24+
return processArray(arr), nil
25+
}
26+
if date, ok := toDate(value); ok {
27+
return date, nil
28+
}
29+
if num, ok := toFloat64(value); ok {
30+
return num, nil
31+
}
32+
return value, nil
33+
}
34+
1235
// handleIdentifier evaluates an identifier node using the provided eval function
1336
func handleIdentifier(n *tsl.TSLNode, eval EvalFunc) (interface{}, error) {
1437
if n == nil {
@@ -32,14 +55,7 @@ func handleIdentifier(n *tsl.TSLNode, eval EvalFunc) (interface{}, error) {
3255
return nil, tsl.KeyNotFoundError{Key: identName}
3356
}
3457

35-
// Check if value is a string and matches ISO date and time format
36-
if strValue, ok := value.(string); ok {
37-
if t, err := time.Parse(time.RFC3339, strValue); err == nil {
38-
return t, nil
39-
}
40-
}
41-
42-
return value, nil
58+
return processValue(value)
4359
}
4460

4561
// isValueInArray checks if a value is in a list of values

v6/pkg/walkers/semantics/walk.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,13 @@ func evaluateBinaryExpression(operator tsl.Operator, leftVal, rightVal interface
133133

134134
switch operator {
135135
case tsl.OpEQ:
136-
return leftVal == rightVal, nil
136+
return evaluateEquality(leftVal, rightVal)
137137
case tsl.OpNE:
138-
return leftVal != rightVal, nil
138+
matched, err := evaluateEquality(leftVal, rightVal)
139+
if err != nil {
140+
return nil, err
141+
}
142+
return !matched, nil
139143
case tsl.OpLT, tsl.OpLE, tsl.OpGT, tsl.OpGE:
140144
return evaluateCompareExpressions(operator, leftVal, rightVal)
141145
case tsl.OpREQ:
@@ -183,6 +187,30 @@ func evaluateBinaryExpression(operator tsl.Operator, leftVal, rightVal interface
183187
}
184188
}
185189

190+
// evaluateEquality performs a type‑aware equality check.
191+
func evaluateEquality(leftVal, rightVal interface{}) (bool, error) {
192+
// Numeric comparison
193+
if leftNumber, leftIsNumber := toFloat64(leftVal); leftIsNumber {
194+
if rightNumber, rightIsNumber := toFloat64(rightVal); rightIsNumber {
195+
return leftNumber == rightNumber, nil
196+
}
197+
}
198+
// Date/time comparison
199+
if leftDateValue, leftIsDate := toDate(leftVal); leftIsDate {
200+
if rightDateValue, rightIsDate := toDate(rightVal); rightIsDate {
201+
return leftDateValue.Equal(rightDateValue), nil
202+
}
203+
}
204+
// String comparison
205+
if leftStringValue, leftIsString := leftVal.(string); leftIsString {
206+
if rightStringValue, rightIsString := rightVal.(string); rightIsString {
207+
return leftStringValue == rightStringValue, nil
208+
}
209+
}
210+
// Fallback to Go’s built‑in equality for identical types
211+
return leftVal == rightVal, nil
212+
}
213+
186214
func evaluateMathExpression(operator tsl.Operator, leftVal, rightVal interface{}) (interface{}, error) {
187215
leftNum, ok := toFloat64(leftVal)
188216
if !ok {

v6/pkg/walkers/semantics/walk_test.go

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,18 @@ var _ = Describe("Walk", func() {
3636
// This is the record that we will use for all the tests:
3737
date, _ := time.Parse(time.RFC3339, "2020-01-01T00:00:00Z")
3838
record := map[string]interface{}{
39-
"title": "A good book",
40-
"author": "Joe",
41-
"spec.pages": 14.0,
42-
"spec.rating": 5.0,
43-
"loaned": true,
44-
"date": date,
45-
"tags": []interface{}{"fiction", "bestseller"},
46-
"price": 29.99,
47-
"numbers": []interface{}{1.0, 2.0, 3.0},
48-
"booleans": []interface{}{true, false, true},
39+
"title": "A good book",
40+
"author": "Joe",
41+
"spec.pages": 14.0,
42+
"spec.rating": 5.0,
43+
"loaned": true,
44+
"date": date,
45+
"tags": []interface{}{"fiction", "bestseller"},
46+
"price": 29.99,
47+
"numbers": []interface{}{1.0, 2.0, 3.0},
48+
"booleans": []interface{}{true, false, true},
49+
"dateStr": "2020-01-01T00:00:00Z", // full ISO string
50+
"shortDateStr": "2020-01-01", // short date
4951
}
5052

5153
// This is the evaluation function that we will use:
@@ -197,6 +199,21 @@ var _ = Describe("Walk", func() {
197199
Entry("sum identifier array", "sum numbers", 6.0),
198200
Entry("sum on computed array", "sum (numbers * 2)", 12.0),
199201
Entry("sum in expression", "sum numbers + 4", 10.0),
202+
203+
// Numeric literal on left
204+
Entry("literal equals number (int/float mix)", "14 = spec.pages", true),
205+
Entry("literal equals number (float literal)", "14.0 = spec.pages", true),
206+
207+
// Date‐string identifiers
208+
Entry("full‐string date identifier", "dateStr", date),
209+
Entry("short‐string date identifier", "shortDateStr", date),
210+
211+
// Date comparisons with string‐based fields
212+
Entry("string date equals full literal", "dateStr = '2020-01-01T00:00:00Z'", true),
213+
Entry("string date equals short literal", "shortDateStr = '2020-01-01'", true),
214+
Entry("literal full date = identifier", "'2020-01-01T00:00:00Z' = dateStr", true),
215+
Entry("literal short date = identifier", "'2020-01-01' = shortDateStr", true),
216+
Entry("string date > earlier literal", "dateStr > '2019-12-31T00:00:00Z'", true),
200217
)
201218
})
202219

0 commit comments

Comments
 (0)