Skip to content

Commit 194704c

Browse files
Merge pull request #8 from danielgtaylor/fix-where-ident
fix: improved error msg, treat wehere ident as property
2 parents 2fc17ca + 656596e commit 194704c

File tree

5 files changed

+17
-0
lines changed

5 files changed

+17
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Features:
2222
not (1- <= 5)
2323
......^
2424
```
25+
- Fuzz tested to prevent crashes
2526

2627
## Usage
2728

@@ -216,6 +217,7 @@ not (items where id > 3)
216217

217218
### Map operators
218219

220+
- Accessing values, e.g. `foo.bar.baz`
219221
- `in` (has key), e.g. `"key" in foo`
220222
- `contains` e.g. `foo contains "key"`
221223

conversions.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ func toBool(v interface{}) bool {
127127
return len(n) > 0
128128
case map[string]interface{}:
129129
return len(n) > 0
130+
case map[any]any:
131+
return len(n) > 0
130132
}
131133
return false
132134
}

interpreter.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ func (i *interpreter) run(ast *Node, value any) (any, Error) {
121121
}
122122
return nil, NewError(ast.Offset, ast.Length, "cannot get %v from %v", ast.Value, value)
123123
case NodeFieldSelect:
124+
i.prevFieldSelect = true
124125
leftValue, err := i.run(ast.Left, value)
125126
if err != nil {
126127
return nil, err
@@ -434,6 +435,10 @@ func (i *interpreter) run(ast *Node, value any) (any, Error) {
434435
return nil, nil
435436
}
436437
for _, item := range resultLeft.([]any) {
438+
// In an unquoted string scenario it makes no sense for the first/only
439+
// token after a `where` clause to be treated as a string. Instead we
440+
// treat a `where` the same as a field select `.` in this scenario.
441+
i.prevFieldSelect = true
437442
resultRight, _ := i.run(ast.Right, item)
438443
if i.strict && err != nil {
439444
return nil, err

interpreter_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ func TestInterpreter(t *testing.T) {
8282
{expr: `foo.bar == bar`, opts: []InterpreterOption{UnquotedStrings}, output: false},
8383
{expr: `foo.bar == bar`, skipTC: true, opts: []InterpreterOption{UnquotedStrings}, input: `{"foo": {}}`, output: false},
8484
{expr: `foo.bar == baz`, opts: []InterpreterOption{UnquotedStrings}, input: `{"foo": {"bar": "baz"}}`, output: true},
85+
{expr: `(items where foo).length == 1`, input: `{"items": [{"foo": 1}, {"bar": 2}, {"baz": 3}]}`, opts: []InterpreterOption{UnquotedStrings}, output: true},
86+
{expr: `(items where @.foo).length == 1`, input: `{"items": [{"foo": 1}, {"bar": 2}, {"baz": 3}]}`, opts: []InterpreterOption{UnquotedStrings}, output: true},
87+
{expr: `(items where foo in id).length == 1`, input: `{"items": [{"id": "foo123"}, {"id": "bar456"}, {"id": "baz789"}]}`, opts: []InterpreterOption{UnquotedStrings}, output: true},
8588
// Identifier / fields
8689
{expr: "foo", input: `{"foo": 1.0}`, output: 1.0},
8790
{expr: "foo.bar.baz", input: `{"foo": {"bar": {"baz": 1.0}}}`, output: 1.0},

typecheck.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ func (i *typeChecker) run(ast *Node, value any) (*schema, Error) {
179179
}
180180
return nil, NewError(ast.Offset, ast.Length, "no property %v in %v", ast.Value, errValue)
181181
case NodeFieldSelect:
182+
i.prevFieldSelect = true
182183
leftType, err := i.run(ast.Left, value)
183184
if err != nil {
184185
return nil, err
@@ -272,6 +273,10 @@ func (i *typeChecker) run(ast *Node, value any) (*schema, Error) {
272273
if !leftType.isArray() {
273274
return nil, NewError(ast.Offset, ast.Length, "where clause requires an array, but found %s", leftType)
274275
}
276+
// In an unquoted string scenario it makes no sense for the first/only
277+
// token after a `where` clause to be treated as a string. Instead we
278+
// treat a `where` the same as a field select `.` in this scenario.
279+
i.prevFieldSelect = true
275280
_, err = i.run(ast.Right, leftType.items)
276281
if err != nil {
277282
return nil, err

0 commit comments

Comments
 (0)