diff --git a/pkg/compiler/compiler_filter_test.go b/pkg/compiler/compiler_filter_test.go index b87b0d22..aef35ad7 100644 --- a/pkg/compiler/compiler_filter_test.go +++ b/pkg/compiler/compiler_filter_test.go @@ -51,5 +51,96 @@ func TestForFilter(t *testing.T) { []any{map[string]any{"age": 29, "gender": "f", "name": "Mary"}, map[string]any{"age": 36, "gender": "m", "name": "Peter"}}, ShouldEqualJSON, }, + { + ` + LET users = [ + { + active: true, + age: 31, + gender: "m" + }, + { + active: true, + age: 29, + gender: "f" + }, + { + active: true, + age: 36, + gender: "m" + } + ] + FOR u IN users + FILTER u.active == true + FILTER u.age < 35 + RETURN u + `, + []any{map[string]any{"active": true, "gender": "m", "age": 31}, map[string]any{"active": true, "gender": "f", "age": 29}}, + ShouldEqualJSON, + }, + { + ` + LET users = [ + { + active: true, + age: 31, + gender: "m" + }, + { + active: true, + age: 29, + gender: "f" + }, + { + active: true, + age: 36, + gender: "m" + }, + { + active: false, + age: 69, + gender: "m" + } + ] + FOR u IN users + FILTER u.active + RETURN u + `, + []any{map[string]any{"active": true, "gender": "m", "age": 31}, map[string]any{"active": true, "gender": "f", "age": 29}, map[string]any{"active": true, "gender": "m", "age": 36}}, + ShouldEqualJSON, + }, + { + ` + LET users = [ + { + active: true, + age: 31, + gender: "m" + }, + { + active: true, + age: 29, + gender: "f" + }, + { + active: true, + age: 36, + gender: "m" + }, + { + active: false, + age: 69, + gender: "m" + } + ] + FOR u IN users + FILTER u.active == true + LIMIT 2 + FILTER u.gender == "m" + RETURN u + `, + []any{map[string]any{"active": true, "gender": "m", "age": 31}}, + ShouldEqualJSON, + }, }) } diff --git a/pkg/runtime/values/helpers.go b/pkg/runtime/values/helpers.go index 87329365..5cd5d276 100644 --- a/pkg/runtime/values/helpers.go +++ b/pkg/runtime/values/helpers.go @@ -4,6 +4,7 @@ import ( "context" "encoding/binary" "encoding/json" + "github.com/MontFerret/ferret/pkg/runtime/values/types" "hash/fnv" "reflect" "sort" @@ -412,6 +413,17 @@ func ToBinary(input core.Value) Binary { return NewBinary([]byte(input.String())) } +func ToRegexp(input core.Value) (*Regexp, error) { + switch r := input.(type) { + case *Regexp: + return r, nil + case String: + return NewRegexp(r) + default: + return nil, core.TypeError(input, types.String, types.Regexp) + } +} + func Hash(typename string, content []byte) uint64 { h := fnv.New64a() diff --git a/pkg/runtime/values/regexp.go b/pkg/runtime/values/regexp.go index c64d367f..0adaef33 100644 --- a/pkg/runtime/values/regexp.go +++ b/pkg/runtime/values/regexp.go @@ -13,8 +13,8 @@ import ( type Regexp regexp.Regexp -func NewRegexp(pattern string) (*Regexp, error) { - r, err := regexp.Compile(pattern) +func NewRegexp(pattern String) (*Regexp, error) { + r, err := regexp.Compile(string(pattern)) if err != nil { return nil, err @@ -60,7 +60,7 @@ func (r *Regexp) Hash() uint64 { } func (r *Regexp) Copy() core.Value { - copied, err := NewRegexp(r.String()) + copied, err := NewRegexp(String(r.String())) // it should never happen if err != nil { diff --git a/pkg/runtime/vm.go b/pkg/runtime/vm.go index 33780d1d..cb9ca9d9 100644 --- a/pkg/runtime/vm.go +++ b/pkg/runtime/vm.go @@ -270,12 +270,26 @@ loop: stack.Push(operators.Decrement(stack.Pop())) case OpRegexpPositive: - reg := program.Constants[arg].(*values.Regexp) - stack.Push(reg.Match(stack.Pop())) + reg, err := values.ToRegexp(stack.Pop()) + + if err == nil { + stack.Push(reg.Match(stack.Pop())) + } else if tryCatch(vm.ip) { + stack.Push(values.False) + } else { + return nil, err + } case OpRegexpNegative: - reg := program.Constants[arg].(*values.Regexp) - stack.Push(!reg.Match(stack.Pop())) + reg, err := values.ToRegexp(stack.Pop()) + + if err == nil { + stack.Push(!reg.Match(stack.Pop())) + } else if tryCatch(vm.ip) { + stack.Push(values.True) + } else { + return nil, err + } case OpCall, OpCallSafe: fnName := stack.Pop().String()