From 959a9dffdebe7e424e52e95bc33bf384978adfc6 Mon Sep 17 00:00:00 2001 From: Ville Vesilehto Date: Tue, 9 Dec 2025 22:46:42 +0200 Subject: [PATCH] feat: support byte slice in matches operator The matches operator now supports matching against byte slices in addition to strings. This allows expressions like `b matches "pattern"` where `b` is a byte slice. Signed-off-by: Ville Vesilehto --- checker/checker.go | 2 +- checker/nature/nature.go | 15 ++++++++++----- vm/vm.go | 14 ++++++++++++-- vm/vm_test.go | 12 ++++++++++++ 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/checker/checker.go b/checker/checker.go index 0210b416..48fca7cf 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -462,7 +462,7 @@ func (v *Checker) binaryNode(node *ast.BinaryNode) Nature { return v.error(node, err.Error()) } } - if l.IsString() && r.IsString() { + if (l.IsString() || l.IsByteSlice()) && r.IsString() { return v.config.NtCache.FromType(boolType) } if l.MaybeCompatible(&v.config.NtCache, r, StringCheck) { diff --git a/checker/nature/nature.go b/checker/nature/nature.go index ae406acb..c96f28c4 100644 --- a/checker/nature/nature.go +++ b/checker/nature/nature.go @@ -10,11 +10,12 @@ import ( ) var ( - intType = reflect.TypeOf(0) - floatType = reflect.TypeOf(float64(0)) - arrayType = reflect.TypeOf([]any{}) - timeType = reflect.TypeOf(time.Time{}) - durationType = reflect.TypeOf(time.Duration(0)) + intType = reflect.TypeOf(0) + floatType = reflect.TypeOf(float64(0)) + arrayType = reflect.TypeOf([]any{}) + byteSliceType = reflect.TypeOf([]byte{}) + timeType = reflect.TypeOf(time.Time{}) + durationType = reflect.TypeOf(time.Duration(0)) builtinInt = map[reflect.Type]struct{}{ reflect.TypeOf(int(0)): {}, @@ -502,6 +503,10 @@ func (n *Nature) IsString() bool { return n.Kind == reflect.String } +func (n *Nature) IsByteSlice() bool { + return n.Type == byteSliceType +} + func (n *Nature) IsArray() bool { return n.Kind == reflect.Slice || n.Kind == reflect.Array } diff --git a/vm/vm.go b/vm/vm.go index 8e95d4ea..7a5b7328 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -299,7 +299,13 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) { vm.push(false) break } - match, err := regexp.MatchString(b.(string), a.(string)) + var match bool + var err error + if s, ok := a.(string); ok { + match, err = regexp.MatchString(b.(string), s) + } else { + match, err = regexp.Match(b.(string), a.([]byte)) + } if err != nil { panic(err) } @@ -312,7 +318,11 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) { break } r := program.Constants[arg].(*regexp.Regexp) - vm.push(r.MatchString(a.(string))) + if s, ok := a.(string); ok { + vm.push(r.MatchString(s)) + } else { + vm.push(r.Match(a.([]byte))) + } case OpContains: b := vm.pop() diff --git a/vm/vm_test.go b/vm/vm_test.go index 6b613d57..6038abca 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -368,6 +368,18 @@ func TestVM_OpcodeOperations(t *testing.T) { expr: `"hello123" matches "^hello\\d+$"`, want: true, }, + { + name: "byte slice matches regex", + expr: `b matches "^hello\\d+$"`, + env: map[string]any{"b": []byte("hello123")}, + want: true, + }, + { + name: "byte slice matches dynamic regex", + expr: `b matches pattern`, + env: map[string]any{"b": []byte("hello123"), "pattern": "^hello\\d+$"}, + want: true, + }, // Data Structure Operations {