mirror of
https://github.com/MontFerret/ferret.git
synced 2025-01-04 03:02:02 +02:00
parent
8d7f1dae23
commit
f4876c05a3
1
go.mod
1
go.mod
@ -13,6 +13,7 @@ require (
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
|
||||
github.com/corpix/uarand v0.1.1
|
||||
github.com/derekparker/trie v0.0.0-20200317170641-1fdf38b7b0e9
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gorilla/css v1.0.0
|
||||
github.com/mafredri/cdp v0.30.0
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible
|
||||
|
20
go.sum
20
go.sum
@ -1,3 +1,4 @@
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic=
|
||||
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
@ -13,9 +14,11 @@ github.com/antchfx/xpath v1.1.11 h1:WOFtK8TVAjLm3lbgqeP0arlHpvCEeTANeWZ/csPpJkQ=
|
||||
github.com/antchfx/xpath v1.1.11/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
|
||||
github.com/antlr/antlr4 v0.0.0-20200417160354-8c50731894e0 h1:j7MyDjg6pb7A2ziow17FDZ2Oj5vGnJsLyDmjpN4Jkcg=
|
||||
github.com/antlr/antlr4 v0.0.0-20200417160354-8c50731894e0/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
|
||||
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA=
|
||||
@ -24,43 +27,55 @@ github.com/corpix/uarand v0.1.1 h1:RMr1TWc9F4n5jiPDzFHtmaUXLKLNUFK0SgCLo4BhX/U=
|
||||
github.com/corpix/uarand v0.1.1/go.mod h1:SFKZvkcRoLqVRFZ4u25xPmp6m9ktANfbpXZ7SJ0/FNU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/derekparker/trie v0.0.0-20200317170641-1fdf38b7b0e9 h1:G765iDCq7bP5opdrPkXk+4V3yfkgV9iGFuheWZ/X/zY=
|
||||
github.com/derekparker/trie v0.0.0-20200317170641-1fdf38b7b0e9/go.mod h1:D6ICZm05D9VN1n/8iOtBxLpXtoGp6HDFUJ1RNVieOSE=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mafredri/cdp v0.30.0 h1:Lvcwjajq6wB6Uk8dYeCLrF26LG85rUdpMxgrwdEvU0o=
|
||||
github.com/mafredri/cdp v0.30.0/go.mod h1:71D84qPmWUvBWYj24Zp+U69mrUof4o8qL2X1fQJ/lHc=
|
||||
github.com/mafredri/go-lint v0.0.0-20180911205320-920981dfc79e/go.mod h1:k/zdyxI3q6dup24o8xpYjJKTCf2F7rfxLp6w/efTiWs=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
|
||||
github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.19.0 h1:hYz4ZVdUgjXTBUmrkrw55j1nHx68LfOKIQk5IYtyScg=
|
||||
github.com/rs/zerolog v1.19.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||
github.com/segmentio/encoding v0.1.10 h1:0b8dva47cSuNQR5ZcU3d0pfi9EnPpSK6q7y5ZGEW36Q=
|
||||
github.com/segmentio/encoding v0.1.10/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw=
|
||||
github.com/sethgrid/pester v1.1.0 h1:IyEAVvwSUPjs2ACFZkBe5N59BBUpSIkQ71Hr6cM5A+w=
|
||||
github.com/sethgrid/pester v1.1.0/go.mod h1:Ad7IjTpvzZO8Fl0vh9AzQ+j/jYZfyp2diGwI8m5q+ns=
|
||||
@ -70,6 +85,7 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/wI2L/jettison v0.7.1 h1:XNq/WvSOAiJhFww9F5JZZcBZtKFL2Y/9WHHEHLDq9TE=
|
||||
github.com/wI2L/jettison v0.7.1/go.mod h1:dj49nOP41M7x6Jql62BqqF/+nW+XJgBaWzJR0hd6M84=
|
||||
@ -104,9 +120,13 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20200601175630-2caf76543d99/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
82
pkg/compiler/compiler_like_test.go
Normal file
82
pkg/compiler/compiler_like_test.go
Normal file
@ -0,0 +1,82 @@
|
||||
package compiler_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/MontFerret/ferret/pkg/compiler"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLikeOperator(t *testing.T) {
|
||||
Convey("RETURN \"foo\" LIKE \"f*\" ", t, func() {
|
||||
c := compiler.New()
|
||||
|
||||
out1, err := c.MustCompile(`
|
||||
RETURN "foo" LIKE "f*"
|
||||
`).Run(context.Background())
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(string(out1), ShouldEqual, `true`)
|
||||
})
|
||||
|
||||
Convey("RETURN LIKE('foo', 'f*')", t, func() {
|
||||
c := compiler.New()
|
||||
|
||||
out1, err := c.MustCompile(`
|
||||
RETURN LIKE('foo', 'f*')
|
||||
`).Run(context.Background())
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(string(out1), ShouldEqual, `true`)
|
||||
})
|
||||
|
||||
Convey("RETURN \"foo\" NOT LIKE \"b*\" ", t, func() {
|
||||
c := compiler.New()
|
||||
|
||||
out1, err := c.MustCompile(`
|
||||
RETURN "foo" NOT LIKE "b*"
|
||||
`).Run(context.Background())
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(string(out1), ShouldEqual, `true`)
|
||||
})
|
||||
|
||||
Convey("LET t = \"foo\" LIKE \"f*\" ", t, func() {
|
||||
c := compiler.New()
|
||||
|
||||
out1, err := c.MustCompile(`
|
||||
LET res = "foo" LIKE "f*"
|
||||
|
||||
RETURN res
|
||||
`).Run(context.Background())
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(string(out1), ShouldEqual, `true`)
|
||||
})
|
||||
|
||||
Convey("FOR IN LIKE", t, func() {
|
||||
c := compiler.New()
|
||||
|
||||
out1, err := c.MustCompile(`
|
||||
FOR str IN ["foo", "bar", "qaz"]
|
||||
FILTER str LIKE "*a*"
|
||||
RETURN str
|
||||
`).Run(context.Background())
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(string(out1), ShouldEqual, `["bar","qaz"]`)
|
||||
})
|
||||
|
||||
Convey("FOR IN LIKE 2", t, func() {
|
||||
c := compiler.New()
|
||||
|
||||
out1, err := c.MustCompile(`
|
||||
FOR str IN ["foo", "bar", "qaz"]
|
||||
FILTER str LIKE "*a*"
|
||||
RETURN str
|
||||
`).Run(context.Background())
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(string(out1), ShouldEqual, `["bar","qaz"]`)
|
||||
})
|
||||
}
|
@ -448,47 +448,7 @@ func (v *visitor) doVisitLimitClauseValue(ctx *fql.LimitClauseValueContext, scop
|
||||
}
|
||||
|
||||
func (v *visitor) doVisitFilterClause(ctx *fql.FilterClauseContext, scope *scope) (core.Expression, error) {
|
||||
exp := ctx.Expression().(*fql.ExpressionContext)
|
||||
|
||||
exps, err := v.doVisitAllExpressions(exp.AllExpression(), scope)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(exps) == 2 {
|
||||
left := exps[0]
|
||||
right := exps[1]
|
||||
|
||||
equalityOp := exp.EqualityOperator()
|
||||
|
||||
if equalityOp != nil {
|
||||
return operators.NewEqualityOperator(v.getSourceMap(ctx), left, right, equalityOp.GetText())
|
||||
}
|
||||
|
||||
regexpOp := exp.RegexpOperator()
|
||||
|
||||
if regexpOp != nil {
|
||||
return operators.NewRegexpOperator(v.getSourceMap(ctx), left, right, regexpOp.GetText())
|
||||
}
|
||||
|
||||
logicalAndOp := exp.LogicalAndOperator()
|
||||
|
||||
if logicalAndOp != nil {
|
||||
return operators.NewLogicalOperator(v.getSourceMap(ctx), left, right, logicalAndOp.GetText())
|
||||
}
|
||||
|
||||
logicalOrOp := exp.LogicalOrOperator()
|
||||
|
||||
if logicalOrOp != nil {
|
||||
return operators.NewLogicalOperator(v.getSourceMap(ctx), left, right, logicalOrOp.GetText())
|
||||
}
|
||||
} else {
|
||||
// should be unary operator
|
||||
return v.doVisitExpression(exp, scope)
|
||||
}
|
||||
|
||||
return nil, core.Error(ErrInvalidToken, ctx.GetText())
|
||||
return v.doVisitExpression(ctx.Expression().(*fql.ExpressionContext), scope)
|
||||
}
|
||||
|
||||
func (v *visitor) doVisitSortClause(ctx *fql.SortClauseContext, scope *scope) ([]*clauses.SorterExpression, error) {
|
||||
@ -1288,9 +1248,7 @@ func (v *visitor) doVisitMathOperator(ctx *fql.ExpressionContext, scope *scope)
|
||||
)
|
||||
}
|
||||
|
||||
func (v *visitor) doVisitUnaryOperator(ctx *fql.ExpressionContext, scope *scope) (core.OperatorExpression, error) {
|
||||
op := ctx.UnaryOperator().(*fql.UnaryOperatorContext)
|
||||
|
||||
func (v *visitor) doVisitUnaryOperator(ctx *fql.ExpressionContext, op *fql.UnaryOperatorContext, scope *scope) (core.OperatorExpression, error) {
|
||||
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
|
||||
|
||||
if err != nil {
|
||||
@ -1335,8 +1293,7 @@ func (v *visitor) doVisitLogicalOperator(ctx *fql.ExpressionContext, scope *scop
|
||||
return operators.NewLogicalOperator(v.getSourceMap(ctx), left, right, operator)
|
||||
}
|
||||
|
||||
func (v *visitor) doVisitEqualityOperator(ctx *fql.ExpressionContext, scope *scope) (core.OperatorExpression, error) {
|
||||
equalityOp := ctx.EqualityOperator().(*fql.EqualityOperatorContext)
|
||||
func (v *visitor) doVisitEqualityOperator(ctx *fql.ExpressionContext, op *fql.EqualityOperatorContext, scope *scope) (core.OperatorExpression, error) {
|
||||
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
|
||||
|
||||
if err != nil {
|
||||
@ -1346,11 +1303,10 @@ func (v *visitor) doVisitEqualityOperator(ctx *fql.ExpressionContext, scope *sco
|
||||
left := exps[0]
|
||||
right := exps[1]
|
||||
|
||||
return operators.NewEqualityOperator(v.getSourceMap(equalityOp), left, right, equalityOp.GetText())
|
||||
return operators.NewEqualityOperator(v.getSourceMap(op), left, right, op.GetText())
|
||||
}
|
||||
|
||||
func (v *visitor) doVisitRegexpOperator(ctx *fql.ExpressionContext, scope *scope) (core.Expression, error) {
|
||||
regexpOp := ctx.RegexpOperator().(*fql.RegexpOperatorContext)
|
||||
func (v *visitor) doVisitRegexpOperator(ctx *fql.ExpressionContext, op *fql.RegexpOperatorContext, scope *scope) (core.Expression, error) {
|
||||
rawExps := ctx.AllExpression()
|
||||
exps, err := v.doVisitAllExpressions(rawExps, scope)
|
||||
|
||||
@ -1376,7 +1332,7 @@ func (v *visitor) doVisitRegexpOperator(ctx *fql.ExpressionContext, scope *scope
|
||||
return nil, errors.Wrap(errors.New("expected a string literal or a function call"), src.String())
|
||||
}
|
||||
|
||||
return operators.NewRegexpOperator(v.getSourceMap(regexpOp), left, right, regexpOp.GetText())
|
||||
return operators.NewRegexpOperator(v.getSourceMap(op), left, right, op.GetText())
|
||||
}
|
||||
|
||||
func (v *visitor) doVisitInOperator(ctx *fql.ExpressionContext, scope *scope) (core.OperatorExpression, error) {
|
||||
@ -1403,16 +1359,28 @@ func (v *visitor) doVisitInOperator(ctx *fql.ExpressionContext, scope *scope) (c
|
||||
)
|
||||
}
|
||||
|
||||
func (v *visitor) doVisitLikeOperator(ctx *fql.ExpressionContext, op *fql.LikeOperatorContext, s *scope) (core.Expression, error) {
|
||||
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), s)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
left := exps[0]
|
||||
right := exps[1]
|
||||
|
||||
return operators.NewLikeOperator(v.getSourceMap(op), left, right, op.Not() != nil)
|
||||
}
|
||||
|
||||
func (v *visitor) doVisitArrayOperator(ctx *fql.ExpressionContext, scope *scope) (core.OperatorExpression, error) {
|
||||
var comparator core.OperatorExpression
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case ctx.InOperator() != nil:
|
||||
if op := ctx.InOperator(); op != nil {
|
||||
comparator, err = v.doVisitInOperator(ctx, scope)
|
||||
case ctx.EqualityOperator() != nil:
|
||||
comparator, err = v.doVisitEqualityOperator(ctx, scope)
|
||||
default:
|
||||
} else if op := ctx.EqualityOperator(); op != nil {
|
||||
comparator, err = v.doVisitEqualityOperator(ctx, op.(*fql.EqualityOperatorContext), scope)
|
||||
} else {
|
||||
return nil, v.unexpectedToken(ctx)
|
||||
}
|
||||
|
||||
@ -1459,129 +1427,91 @@ func (v *visitor) doVisitExpressionGroup(ctx *fql.ExpressionGroupContext, scope
|
||||
}
|
||||
|
||||
func (v *visitor) doVisitExpression(ctx *fql.ExpressionContext, scope *scope) (core.Expression, error) {
|
||||
seq := ctx.ExpressionGroup()
|
||||
|
||||
if seq != nil {
|
||||
return v.doVisitExpressionGroup(seq.(*fql.ExpressionGroupContext), scope)
|
||||
if exp := ctx.ExpressionGroup(); exp != nil {
|
||||
return v.doVisitExpressionGroup(exp.(*fql.ExpressionGroupContext), scope)
|
||||
}
|
||||
|
||||
member := ctx.MemberExpression()
|
||||
|
||||
if member != nil {
|
||||
return v.doVisitMemberExpression(member.(*fql.MemberExpressionContext), scope)
|
||||
if exp := ctx.MemberExpression(); exp != nil {
|
||||
return v.doVisitMemberExpression(exp.(*fql.MemberExpressionContext), scope)
|
||||
}
|
||||
|
||||
funCall := ctx.FunctionCallExpression()
|
||||
|
||||
if funCall != nil {
|
||||
return v.doVisitFunctionCallExpression(funCall.(*fql.FunctionCallExpressionContext), scope)
|
||||
if exp := ctx.FunctionCallExpression(); exp != nil {
|
||||
return v.doVisitFunctionCallExpression(exp.(*fql.FunctionCallExpressionContext), scope)
|
||||
}
|
||||
|
||||
notOp := ctx.UnaryOperator()
|
||||
|
||||
if notOp != nil {
|
||||
return v.doVisitUnaryOperator(ctx, scope)
|
||||
if exp := ctx.UnaryOperator(); exp != nil {
|
||||
return v.doVisitUnaryOperator(ctx, exp.(*fql.UnaryOperatorContext), scope)
|
||||
}
|
||||
|
||||
multiOp := ctx.MultiplicativeOperator()
|
||||
|
||||
if multiOp != nil {
|
||||
if exp := ctx.MultiplicativeOperator(); exp != nil {
|
||||
return v.doVisitMathOperator(ctx, scope)
|
||||
}
|
||||
|
||||
addOp := ctx.AdditiveOperator()
|
||||
|
||||
if addOp != nil {
|
||||
if exp := ctx.AdditiveOperator(); exp != nil {
|
||||
return v.doVisitMathOperator(ctx, scope)
|
||||
}
|
||||
|
||||
arrOp := ctx.ArrayOperator()
|
||||
|
||||
if arrOp != nil {
|
||||
if exp := ctx.ArrayOperator(); exp != nil {
|
||||
return v.doVisitArrayOperator(ctx, scope)
|
||||
}
|
||||
|
||||
equalityOp := ctx.EqualityOperator()
|
||||
|
||||
if equalityOp != nil {
|
||||
return v.doVisitEqualityOperator(ctx, scope)
|
||||
if exp := ctx.EqualityOperator(); exp != nil {
|
||||
return v.doVisitEqualityOperator(ctx, exp.(*fql.EqualityOperatorContext), scope)
|
||||
}
|
||||
|
||||
inOp := ctx.InOperator()
|
||||
|
||||
if inOp != nil {
|
||||
if exp := ctx.InOperator(); exp != nil {
|
||||
return v.doVisitInOperator(ctx, scope)
|
||||
}
|
||||
|
||||
logicalAndOp := ctx.LogicalAndOperator()
|
||||
if exp := ctx.LikeOperator(); exp != nil {
|
||||
return v.doVisitLikeOperator(ctx, exp.(*fql.LikeOperatorContext), scope)
|
||||
}
|
||||
|
||||
if logicalAndOp != nil {
|
||||
if exp := ctx.LogicalAndOperator(); exp != nil {
|
||||
return v.doVisitLogicalOperator(ctx, scope)
|
||||
}
|
||||
|
||||
logicalOrOp := ctx.LogicalOrOperator()
|
||||
|
||||
if logicalOrOp != nil {
|
||||
if exp := ctx.LogicalOrOperator(); exp != nil {
|
||||
return v.doVisitLogicalOperator(ctx, scope)
|
||||
}
|
||||
|
||||
regexpOp := ctx.RegexpOperator()
|
||||
|
||||
if regexpOp != nil {
|
||||
return v.doVisitRegexpOperator(ctx, scope)
|
||||
if exp := ctx.RegexpOperator(); exp != nil {
|
||||
return v.doVisitRegexpOperator(ctx, exp.(*fql.RegexpOperatorContext), scope)
|
||||
}
|
||||
|
||||
variable := ctx.Variable()
|
||||
|
||||
if variable != nil {
|
||||
return v.doVisitVariable(variable.(*fql.VariableContext), scope)
|
||||
if exp := ctx.Variable(); exp != nil {
|
||||
return v.doVisitVariable(exp.(*fql.VariableContext), scope)
|
||||
}
|
||||
|
||||
str := ctx.StringLiteral()
|
||||
|
||||
if str != nil {
|
||||
return v.doVisitStringLiteral(str.(*fql.StringLiteralContext))
|
||||
if exp := ctx.StringLiteral(); exp != nil {
|
||||
return v.doVisitStringLiteral(exp.(*fql.StringLiteralContext))
|
||||
}
|
||||
|
||||
integ := ctx.IntegerLiteral()
|
||||
|
||||
if integ != nil {
|
||||
return v.doVisitIntegerLiteral(integ.(*fql.IntegerLiteralContext))
|
||||
if exp := ctx.IntegerLiteral(); exp != nil {
|
||||
return v.doVisitIntegerLiteral(exp.(*fql.IntegerLiteralContext))
|
||||
}
|
||||
|
||||
float := ctx.FloatLiteral()
|
||||
|
||||
if float != nil {
|
||||
return v.doVisitFloatLiteral(float.(*fql.FloatLiteralContext))
|
||||
if exp := ctx.FloatLiteral(); exp != nil {
|
||||
return v.doVisitFloatLiteral(exp.(*fql.FloatLiteralContext))
|
||||
}
|
||||
|
||||
boolean := ctx.BooleanLiteral()
|
||||
|
||||
if boolean != nil {
|
||||
return v.doVisitBooleanLiteral(boolean.(*fql.BooleanLiteralContext))
|
||||
if exp := ctx.BooleanLiteral(); exp != nil {
|
||||
return v.doVisitBooleanLiteral(exp.(*fql.BooleanLiteralContext))
|
||||
}
|
||||
|
||||
arr := ctx.ArrayLiteral()
|
||||
|
||||
if arr != nil {
|
||||
return v.doVisitArrayLiteral(arr.(*fql.ArrayLiteralContext), scope)
|
||||
if exp := ctx.ArrayLiteral(); exp != nil {
|
||||
return v.doVisitArrayLiteral(exp.(*fql.ArrayLiteralContext), scope)
|
||||
}
|
||||
|
||||
obj := ctx.ObjectLiteral()
|
||||
|
||||
if obj != nil {
|
||||
return v.doVisitObjectLiteral(obj.(*fql.ObjectLiteralContext), scope)
|
||||
if exp := ctx.ObjectLiteral(); exp != nil {
|
||||
return v.doVisitObjectLiteral(exp.(*fql.ObjectLiteralContext), scope)
|
||||
}
|
||||
|
||||
none := ctx.NoneLiteral()
|
||||
|
||||
if none != nil {
|
||||
return v.doVisitNoneLiteral(none.(*fql.NoneLiteralContext))
|
||||
if exp := ctx.NoneLiteral(); exp != nil {
|
||||
return v.doVisitNoneLiteral(exp.(*fql.NoneLiteralContext))
|
||||
}
|
||||
|
||||
questionCtx := ctx.QuestionMark()
|
||||
|
||||
if questionCtx != nil {
|
||||
if exp := ctx.QuestionMark(); exp != nil {
|
||||
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
|
||||
|
||||
if err != nil {
|
||||
@ -1595,19 +1525,14 @@ func (v *visitor) doVisitExpression(ctx *fql.ExpressionContext, scope *scope) (c
|
||||
)
|
||||
}
|
||||
|
||||
rangeOp := ctx.RangeOperator()
|
||||
|
||||
if rangeOp != nil {
|
||||
return v.doVisitRangeOperator(rangeOp.(*fql.RangeOperatorContext), scope)
|
||||
if exp := ctx.RangeOperator(); exp != nil {
|
||||
return v.doVisitRangeOperator(exp.(*fql.RangeOperatorContext), scope)
|
||||
}
|
||||
|
||||
param := ctx.Param()
|
||||
|
||||
if param != nil {
|
||||
if param := ctx.Param(); param != nil {
|
||||
return v.doVisitParamContext(param.(*fql.ParamContext), scope)
|
||||
}
|
||||
|
||||
// TODO: Complete it
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
|
@ -55,13 +55,14 @@ Aggregate=54
|
||||
Like=55
|
||||
Not=56
|
||||
In=57
|
||||
While=58
|
||||
Param=59
|
||||
Identifier=60
|
||||
StringLiteral=61
|
||||
IntegerLiteral=62
|
||||
FloatLiteral=63
|
||||
NamespaceSegment=64
|
||||
Do=58
|
||||
While=59
|
||||
Param=60
|
||||
Identifier=61
|
||||
StringLiteral=62
|
||||
IntegerLiteral=63
|
||||
FloatLiteral=64
|
||||
NamespaceSegment=65
|
||||
':'=5
|
||||
';'=6
|
||||
'.'=7
|
||||
@ -109,5 +110,6 @@ NamespaceSegment=64
|
||||
'AGGREGATE'=54
|
||||
'LIKE'=55
|
||||
'IN'=57
|
||||
'WHILE'=58
|
||||
'@'=59
|
||||
'DO'=58
|
||||
'WHILE'=59
|
||||
'@'=60
|
||||
|
@ -285,6 +285,7 @@ expression
|
||||
| expressionGroup
|
||||
| expression arrayOperator (inOperator | equalityOperator) expression
|
||||
| expression inOperator expression
|
||||
| expression likeOperator expression
|
||||
| expression equalityOperator expression
|
||||
| expression regexpOperator expression
|
||||
| expression logicalAndOperator expression
|
||||
@ -320,6 +321,11 @@ inOperator
|
||||
| Not In
|
||||
;
|
||||
|
||||
likeOperator
|
||||
: Like
|
||||
| Not Like
|
||||
;
|
||||
|
||||
equalityOperator
|
||||
: Gt
|
||||
| Lt
|
||||
@ -357,5 +363,4 @@ unaryOperator
|
||||
: Not
|
||||
| Plus
|
||||
| Minus
|
||||
| Like
|
||||
;
|
File diff suppressed because one or more lines are too long
@ -1,4 +1,4 @@
|
||||
// Code generated from antlr/FqlLexer.g4 by ANTLR 4.8. DO NOT EDIT.
|
||||
// Code generated from antlr/FqlLexer.g4 by ANTLR 4.9.1. DO NOT EDIT.
|
||||
|
||||
package fql
|
||||
|
||||
@ -268,9 +268,6 @@ var serializedLexerAtn = []uint16{
|
||||
2, 3, 2,
|
||||
}
|
||||
|
||||
var lexerDeserializer = antlr.NewATNDeserializer(nil)
|
||||
var lexerAtn = lexerDeserializer.DeserializeFromUInt16(serializedLexerAtn)
|
||||
|
||||
var lexerChannelNames = []string{
|
||||
"DEFAULT_TOKEN_CHANNEL", "HIDDEN",
|
||||
}
|
||||
@ -324,18 +321,20 @@ type FqlLexer struct {
|
||||
// TODO: EOF string
|
||||
}
|
||||
|
||||
var lexerDecisionToDFA = make([]*antlr.DFA, len(lexerAtn.DecisionToState))
|
||||
|
||||
func init() {
|
||||
// NewFqlLexer produces a new lexer instance for the optional input antlr.CharStream.
|
||||
//
|
||||
// The *FqlLexer instance produced may be reused by calling the SetInputStream method.
|
||||
// The initial lexer configuration is expensive to construct, and the object is not thread-safe;
|
||||
// however, if used within a Golang sync.Pool, the construction cost amortizes well and the
|
||||
// objects can be used in a thread-safe manner.
|
||||
func NewFqlLexer(input antlr.CharStream) *FqlLexer {
|
||||
l := new(FqlLexer)
|
||||
lexerDeserializer := antlr.NewATNDeserializer(nil)
|
||||
lexerAtn := lexerDeserializer.DeserializeFromUInt16(serializedLexerAtn)
|
||||
lexerDecisionToDFA := make([]*antlr.DFA, len(lexerAtn.DecisionToState))
|
||||
for index, ds := range lexerAtn.DecisionToState {
|
||||
lexerDecisionToDFA[index] = antlr.NewDFA(ds, index)
|
||||
}
|
||||
}
|
||||
|
||||
func NewFqlLexer(input antlr.CharStream) *FqlLexer {
|
||||
|
||||
l := new(FqlLexer)
|
||||
|
||||
l.BaseLexer = antlr.NewBaseLexer(input)
|
||||
l.Interpreter = antlr.NewLexerATNSimulator(l, lexerAtn, lexerDecisionToDFA, antlr.NewPredictionContextCache())
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
// Code generated from antlr/FqlParser.g4 by ANTLR 4.8. DO NOT EDIT.
|
||||
// Code generated from antlr/FqlParser.g4 by ANTLR 4.9.1. DO NOT EDIT.
|
||||
|
||||
package fql // FqlParser
|
||||
import "github.com/antlr/antlr4/runtime/Go/antlr"
|
||||
@ -364,6 +364,12 @@ func (s *BaseFqlParserListener) EnterInOperator(ctx *InOperatorContext) {}
|
||||
// ExitInOperator is called when production inOperator is exited.
|
||||
func (s *BaseFqlParserListener) ExitInOperator(ctx *InOperatorContext) {}
|
||||
|
||||
// EnterLikeOperator is called when production likeOperator is entered.
|
||||
func (s *BaseFqlParserListener) EnterLikeOperator(ctx *LikeOperatorContext) {}
|
||||
|
||||
// ExitLikeOperator is called when production likeOperator is exited.
|
||||
func (s *BaseFqlParserListener) ExitLikeOperator(ctx *LikeOperatorContext) {}
|
||||
|
||||
// EnterEqualityOperator is called when production equalityOperator is entered.
|
||||
func (s *BaseFqlParserListener) EnterEqualityOperator(ctx *EqualityOperatorContext) {}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Code generated from antlr/FqlParser.g4 by ANTLR 4.8. DO NOT EDIT.
|
||||
// Code generated from antlr/FqlParser.g4 by ANTLR 4.9.1. DO NOT EDIT.
|
||||
|
||||
package fql // FqlParser
|
||||
import "github.com/antlr/antlr4/runtime/Go/antlr"
|
||||
@ -235,6 +235,10 @@ func (v *BaseFqlParserVisitor) VisitInOperator(ctx *InOperatorContext) interface
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
func (v *BaseFqlParserVisitor) VisitLikeOperator(ctx *LikeOperatorContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
func (v *BaseFqlParserVisitor) VisitEqualityOperator(ctx *EqualityOperatorContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Code generated from antlr/FqlParser.g4 by ANTLR 4.8. DO NOT EDIT.
|
||||
// Code generated from antlr/FqlParser.g4 by ANTLR 4.9.1. DO NOT EDIT.
|
||||
|
||||
package fql // FqlParser
|
||||
import "github.com/antlr/antlr4/runtime/Go/antlr"
|
||||
@ -178,6 +178,9 @@ type FqlParserListener interface {
|
||||
// EnterInOperator is called when entering the inOperator production.
|
||||
EnterInOperator(c *InOperatorContext)
|
||||
|
||||
// EnterLikeOperator is called when entering the likeOperator production.
|
||||
EnterLikeOperator(c *LikeOperatorContext)
|
||||
|
||||
// EnterEqualityOperator is called when entering the equalityOperator production.
|
||||
EnterEqualityOperator(c *EqualityOperatorContext)
|
||||
|
||||
@ -370,6 +373,9 @@ type FqlParserListener interface {
|
||||
// ExitInOperator is called when exiting the inOperator production.
|
||||
ExitInOperator(c *InOperatorContext)
|
||||
|
||||
// ExitLikeOperator is called when exiting the likeOperator production.
|
||||
ExitLikeOperator(c *LikeOperatorContext)
|
||||
|
||||
// ExitEqualityOperator is called when exiting the equalityOperator production.
|
||||
ExitEqualityOperator(c *EqualityOperatorContext)
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Code generated from antlr/FqlParser.g4 by ANTLR 4.8. DO NOT EDIT.
|
||||
// Code generated from antlr/FqlParser.g4 by ANTLR 4.9.1. DO NOT EDIT.
|
||||
|
||||
package fql // FqlParser
|
||||
import "github.com/antlr/antlr4/runtime/Go/antlr"
|
||||
@ -178,6 +178,9 @@ type FqlParserVisitor interface {
|
||||
// Visit a parse tree produced by FqlParser#inOperator.
|
||||
VisitInOperator(ctx *InOperatorContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FqlParser#likeOperator.
|
||||
VisitLikeOperator(ctx *LikeOperatorContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FqlParser#equalityOperator.
|
||||
VisitEqualityOperator(ctx *EqualityOperatorContext) interface{}
|
||||
|
||||
|
80
pkg/runtime/expressions/operators/like.go
Normal file
80
pkg/runtime/expressions/operators/like.go
Normal file
@ -0,0 +1,80 @@
|
||||
package operators
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
type LikeOperator struct {
|
||||
*baseOperator
|
||||
not bool
|
||||
}
|
||||
|
||||
func NewLikeOperator(
|
||||
src core.SourceMap,
|
||||
left core.Expression,
|
||||
right core.Expression,
|
||||
not bool,
|
||||
) (*LikeOperator, error) {
|
||||
if left == nil {
|
||||
return nil, core.Error(core.ErrMissedArgument, "left expression")
|
||||
}
|
||||
|
||||
if right == nil {
|
||||
return nil, core.Error(core.ErrMissedArgument, "right expression")
|
||||
}
|
||||
|
||||
return &LikeOperator{&baseOperator{src, left, right}, not}, nil
|
||||
}
|
||||
|
||||
func (operator *LikeOperator) Exec(ctx context.Context, scope *core.Scope) (core.Value, error) {
|
||||
left, err := operator.left.Exec(ctx, scope)
|
||||
|
||||
if err != nil {
|
||||
return values.False, core.SourceError(operator.src, err)
|
||||
}
|
||||
|
||||
right, err := operator.right.Exec(ctx, scope)
|
||||
|
||||
if err != nil {
|
||||
return values.False, core.SourceError(operator.src, err)
|
||||
}
|
||||
|
||||
return operator.Eval(ctx, left, right)
|
||||
}
|
||||
|
||||
func (operator *LikeOperator) Eval(_ context.Context, left, right core.Value) (core.Value, error) {
|
||||
err := core.ValidateType(right, types.String)
|
||||
|
||||
if err != nil {
|
||||
// TODO: Return the error? AQL just returns false
|
||||
return values.False, nil
|
||||
}
|
||||
|
||||
err = core.ValidateType(left, types.String)
|
||||
|
||||
if err != nil {
|
||||
// TODO: Return the error? AQL just returns false
|
||||
return values.False, nil
|
||||
}
|
||||
|
||||
r, err := glob.Compile(right.String())
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid glob pattern")
|
||||
}
|
||||
|
||||
result := r.Match(left.String())
|
||||
|
||||
if operator.not {
|
||||
return values.NewBoolean(!result), nil
|
||||
}
|
||||
|
||||
return values.NewBoolean(result), nil
|
||||
}
|
@ -2,12 +2,19 @@ package strings
|
||||
|
||||
import (
|
||||
"context"
|
||||
"unicode"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/gobwas/glob"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
var (
|
||||
deprecatedLikeSyntax = regexp.MustCompile("[%_]")
|
||||
)
|
||||
|
||||
// LIKE checks whether the pattern search is contained in the string text, using wildcard matching.
|
||||
// @param {String} str - The string to search in.
|
||||
// @param {String} search - A search pattern that can contain the wildcard characters.
|
||||
@ -20,46 +27,41 @@ func Like(_ context.Context, args ...core.Value) (core.Value, error) {
|
||||
return values.False, err
|
||||
}
|
||||
|
||||
str := []rune(args[0].String())
|
||||
pattern := []rune(args[1].String())
|
||||
str := args[0].String()
|
||||
pattern := args[1].String()
|
||||
|
||||
if len(pattern) == 0 {
|
||||
return values.NewBoolean(len(str) == 0), nil
|
||||
}
|
||||
|
||||
lookup := make([][]bool, len(str)+1)
|
||||
// TODO: Remove me in next releases
|
||||
replaced := deprecatedLikeSyntax.ReplaceAllFunc([]byte(pattern), func(b []byte) []byte {
|
||||
str := string(b)
|
||||
|
||||
for i := range lookup {
|
||||
lookup[i] = make([]bool, len(pattern)+1)
|
||||
}
|
||||
switch str {
|
||||
case "%":
|
||||
return []byte("*")
|
||||
case "_":
|
||||
return []byte("?")
|
||||
default:
|
||||
return b
|
||||
}
|
||||
})
|
||||
|
||||
lookup[0][0] = true
|
||||
pattern = string(replaced)
|
||||
|
||||
for j := 1; j < len(pattern)+1; j++ {
|
||||
if pattern[j-1] == '%' {
|
||||
lookup[0][j] = lookup[0][j-1]
|
||||
if len(args) > 2 {
|
||||
if values.ToBoolean(args[2]) {
|
||||
str = strings.ToLower(str)
|
||||
pattern = strings.ToLower(pattern)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 1; i < len(str)+1; i++ {
|
||||
for j := 1; j < len(pattern)+1; j++ {
|
||||
switch {
|
||||
case pattern[j-1] == '%':
|
||||
lookup[i][j] = lookup[i][j-1] || lookup[i-1][j]
|
||||
case pattern[j-1] == '_' || str[i-1] == pattern[j-1]:
|
||||
lookup[i][j] = lookup[i-1][j-1]
|
||||
case len(args) > 2:
|
||||
isEq := unicode.ToLower(str[i-1]) == unicode.ToLower(pattern[j-1])
|
||||
if args[2] == values.True && isEq {
|
||||
lookup[i][j] = lookup[i-1][j-1]
|
||||
}
|
||||
default:
|
||||
lookup[i][j] = false
|
||||
}
|
||||
}
|
||||
g, err := glob.Compile(pattern)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
matched := lookup[len(str)][len(pattern)]
|
||||
|
||||
return values.NewBoolean(matched), nil
|
||||
return values.NewBoolean(g.Match(str)), nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user