mirror of
https://github.com/MontFerret/ferret.git
synced 2025-08-13 19:52:52 +02:00
Refactor LoopCollectCompiler
: replace aggregateSelector
with core.AggregateSelector
, introduce CollectorSpec
struct, streamline grouping and aggregation handling, optimize register usage, finalize projection logic, update go.mod
dependencies, and enhance integration tests for COLLECT
queries.
This commit is contained in:
@@ -22,7 +22,7 @@ func New(setters ...Option) *Instance {
|
||||
}
|
||||
|
||||
func (i *Instance) Functions() runtime.Namespace {
|
||||
return i.compiler
|
||||
return nil
|
||||
}
|
||||
|
||||
//func (i *Instance) Drivers() *drivers.Container {
|
||||
|
18
go.mod
18
go.mod
@@ -1,15 +1,15 @@
|
||||
module github.com/MontFerret/ferret
|
||||
|
||||
go 1.22
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.24.4
|
||||
toolchain go1.24.5
|
||||
|
||||
require (
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/jarcoal/httpmock v1.3.1
|
||||
github.com/jarcoal/httpmock v1.4.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rs/zerolog v1.31.0
|
||||
github.com/rs/zerolog v1.34.0
|
||||
github.com/smartystreets/goconvey v1.8.1
|
||||
github.com/wI2L/jettison v0.7.4
|
||||
)
|
||||
@@ -17,9 +17,9 @@ require (
|
||||
require (
|
||||
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
||||
github.com/jtolds/gls v4.20.0+incompatible // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/smarty/assertions v1.15.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/smarty/assertions v1.16.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
)
|
||||
|
41
go.sum
41
go.sum
@@ -1,48 +1,37 @@
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
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/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
|
||||
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
|
||||
github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
|
||||
github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jarcoal/httpmock v1.4.0 h1:BvhqnH0JAYbNudL2GMJKgOHe2CtKlzJ/5rWKyp+hc2k=
|
||||
github.com/jarcoal/httpmock v1.4.0/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0=
|
||||
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/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=
|
||||
github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM=
|
||||
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 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
|
||||
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc=
|
||||
github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
|
||||
github.com/segmentio/encoding v0.3.4 h1:WM4IBnxH8B9TakiM2QD5LyNl9JSndh88QbHqVC+Pauc=
|
||||
github.com/segmentio/encoding v0.3.4/go.mod h1:n0JeuIqEQrQoPDGsjo8UNd1iA0U8d8+oHAA4E3G3OxM=
|
||||
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
||||
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||
github.com/smarty/assertions v1.16.0 h1:EvHNkdRA4QHMrn75NZSoUQ/mAUXAYWfatfB01yTCzfY=
|
||||
github.com/smarty/assertions v1.16.0/go.mod h1:duaaFdCS0K9dnoM50iyek/eYINOZ64gbh1Xlf6LG7AI=
|
||||
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
|
||||
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
|
||||
github.com/wI2L/jettison v0.7.4 h1:ptjriu75R/k5RAZO0DJzy2t55f7g+dPiBxBY38icaKg=
|
||||
github.com/wI2L/jettison v0.7.4/go.mod h1:O+F+T7X7ZN6kTsd167Qk4aZMC8jNrH48SMedNmkfPb0=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4=
|
||||
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
|
@@ -24,6 +24,6 @@ func NewOptions(setters []Option) *Options {
|
||||
|
||||
func WithoutStdlib() Option {
|
||||
return func(opts *Options) {
|
||||
opts.compiler = append(opts.compiler, compiler.WithoutStdlib())
|
||||
//opts.compiler = append(opts.compiler, compiler.WithoutStdlib())
|
||||
}
|
||||
}
|
||||
|
44
pkg/compiler/internal/core/aggregate_selector.go
Normal file
44
pkg/compiler/internal/core/aggregate_selector.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
)
|
||||
|
||||
type AggregateSelector struct {
|
||||
name runtime.String
|
||||
args int
|
||||
funcName runtime.String
|
||||
protectedCall bool
|
||||
register vm.Operand
|
||||
}
|
||||
|
||||
func NewAggregateSelector(name runtime.String, args int, funcName runtime.String, protectedCall bool, register vm.Operand) *AggregateSelector {
|
||||
return &AggregateSelector{
|
||||
name: name,
|
||||
register: register,
|
||||
args: args,
|
||||
funcName: funcName,
|
||||
protectedCall: protectedCall,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *AggregateSelector) Name() runtime.String {
|
||||
return s.name
|
||||
}
|
||||
|
||||
func (s *AggregateSelector) Args() int {
|
||||
return s.args
|
||||
}
|
||||
|
||||
func (s *AggregateSelector) FuncName() runtime.String {
|
||||
return s.funcName
|
||||
}
|
||||
|
||||
func (s *AggregateSelector) ProtectedCall() bool {
|
||||
return s.protectedCall
|
||||
}
|
||||
|
||||
func (s *AggregateSelector) Register() vm.Operand {
|
||||
return s.register
|
||||
}
|
19
pkg/compiler/internal/core/collect_selector.go
Normal file
19
pkg/compiler/internal/core/collect_selector.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
)
|
||||
|
||||
type CollectSelector struct {
|
||||
name runtime.String
|
||||
}
|
||||
|
||||
func NewCollectSelector(name runtime.String) *CollectSelector {
|
||||
return &CollectSelector{
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CollectSelector) Name() runtime.String {
|
||||
return s.name
|
||||
}
|
40
pkg/compiler/internal/core/collector_projection.go
Normal file
40
pkg/compiler/internal/core/collector_projection.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package core
|
||||
|
||||
type CollectorProjection struct {
|
||||
groupsVariable string
|
||||
countVariable string
|
||||
}
|
||||
|
||||
func NewCollectorGroupProjection(groupsVariable string) *CollectorProjection {
|
||||
return &CollectorProjection{
|
||||
groupsVariable: groupsVariable,
|
||||
countVariable: "",
|
||||
}
|
||||
}
|
||||
|
||||
func NewCollectorCountProjection(countVariable string) *CollectorProjection {
|
||||
return &CollectorProjection{
|
||||
groupsVariable: "",
|
||||
countVariable: countVariable,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *CollectorProjection) VariableName() string {
|
||||
if p.groupsVariable != "" {
|
||||
return p.groupsVariable
|
||||
}
|
||||
|
||||
if p.countVariable != "" {
|
||||
return p.countVariable
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *CollectorProjection) IsGrouped() bool {
|
||||
return p.groupsVariable != ""
|
||||
}
|
||||
|
||||
func (p *CollectorProjection) IsCounted() bool {
|
||||
return p.countVariable != ""
|
||||
}
|
74
pkg/compiler/internal/core/collector_spec.go
Normal file
74
pkg/compiler/internal/core/collector_spec.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package core
|
||||
|
||||
type (
|
||||
CollectorType int
|
||||
|
||||
CollectorSpec struct {
|
||||
typ CollectorType
|
||||
projection *CollectorProjection
|
||||
groupSelectors []*CollectSelector
|
||||
aggregationSelectors []*AggregateSelector
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
CollectorTypeCounter CollectorType = iota
|
||||
CollectorTypeKey
|
||||
CollectorTypeKeyCounter
|
||||
CollectorTypeKeyGroup
|
||||
)
|
||||
|
||||
func NewCollectorSpec(type_ CollectorType, projection *CollectorProjection, groupSelectors []*CollectSelector, aggregationSelectors []*AggregateSelector) *CollectorSpec {
|
||||
return &CollectorSpec{
|
||||
typ: type_,
|
||||
projection: projection,
|
||||
groupSelectors: groupSelectors,
|
||||
aggregationSelectors: aggregationSelectors,
|
||||
}
|
||||
}
|
||||
|
||||
func DetermineCollectorType(withGrouping, withAggregation bool, projection *CollectorProjection) CollectorType {
|
||||
withProjection := projection != nil
|
||||
|
||||
if withGrouping {
|
||||
if withProjection && projection.IsCounted() {
|
||||
return CollectorTypeKeyCounter
|
||||
}
|
||||
|
||||
return CollectorTypeKeyGroup
|
||||
}
|
||||
|
||||
if withAggregation {
|
||||
return CollectorTypeKeyGroup
|
||||
}
|
||||
|
||||
return CollectorTypeCounter
|
||||
}
|
||||
|
||||
func (c *CollectorSpec) Type() CollectorType {
|
||||
return c.typ
|
||||
}
|
||||
|
||||
func (c *CollectorSpec) Projection() *CollectorProjection {
|
||||
return c.projection
|
||||
}
|
||||
|
||||
func (c *CollectorSpec) GroupSelectors() []*CollectSelector {
|
||||
return c.groupSelectors
|
||||
}
|
||||
|
||||
func (c *CollectorSpec) AggregationSelectors() []*AggregateSelector {
|
||||
return c.aggregationSelectors
|
||||
}
|
||||
|
||||
func (c *CollectorSpec) HasProjection() bool {
|
||||
return c.projection != nil
|
||||
}
|
||||
|
||||
func (c *CollectorSpec) HasGrouping() bool {
|
||||
return len(c.groupSelectors) > 0
|
||||
}
|
||||
|
||||
func (c *CollectorSpec) HasAggregation() bool {
|
||||
return len(c.aggregationSelectors) > 0
|
||||
}
|
@@ -22,15 +22,6 @@ const (
|
||||
DoWhileLoop
|
||||
)
|
||||
|
||||
type CollectorType int
|
||||
|
||||
const (
|
||||
CollectorTypeCounter CollectorType = iota
|
||||
CollectorTypeKey
|
||||
CollectorTypeKeyCounter
|
||||
CollectorTypeKeyGroup
|
||||
)
|
||||
|
||||
type Loop struct {
|
||||
Kind LoopKind
|
||||
Type LoopType
|
||||
|
@@ -1,11 +1,12 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/compiler/internal/core"
|
||||
"github.com/MontFerret/ferret/pkg/parser/fql"
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// Runtime functions
|
||||
|
@@ -1,9 +1,10 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/parser/fql"
|
||||
"strings"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/parser/fql"
|
||||
|
||||
"github.com/antlr4-go/antlr/v4"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/compiler/internal/core"
|
||||
|
@@ -3,21 +3,13 @@ package internal
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/compiler/internal/core"
|
||||
"github.com/MontFerret/ferret/pkg/parser/fql"
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
)
|
||||
|
||||
type (
|
||||
LoopCollectCompiler struct {
|
||||
ctx *CompilerContext
|
||||
}
|
||||
|
||||
collectorScope struct {
|
||||
Type core.CollectorType
|
||||
Projection string
|
||||
GroupSelectors []fql.ICollectSelectorContext
|
||||
AggregationSelectors []*aggregateSelector
|
||||
}
|
||||
)
|
||||
type LoopCollectCompiler struct {
|
||||
ctx *CompilerContext
|
||||
}
|
||||
|
||||
func NewCollectCompiler(ctx *CompilerContext) *LoopCollectCompiler {
|
||||
return &LoopCollectCompiler{ctx: ctx}
|
||||
@@ -29,50 +21,49 @@ func (c *LoopCollectCompiler) Compile(ctx fql.ICollectClauseContext) {
|
||||
c.compileLoop(scope)
|
||||
}
|
||||
|
||||
func (c *LoopCollectCompiler) compileCollector(ctx fql.ICollectClauseContext) *collectorScope {
|
||||
func (c *LoopCollectCompiler) compileCollector(ctx fql.ICollectClauseContext) *core.CollectorSpec {
|
||||
grouping := ctx.CollectGrouping()
|
||||
counter := ctx.CollectCounter()
|
||||
aggregation := ctx.CollectAggregator()
|
||||
|
||||
// We gather keys and values for the collector.
|
||||
kv, groupSelectors := c.initializeGrouping(grouping)
|
||||
projectionVarName, collectorType := c.initializeProjection(ctx, kv, counter, grouping != nil)
|
||||
|
||||
// If we use aggregators, we need to collect group items by key
|
||||
if aggregation != nil && collectorType != core.CollectorTypeKeyGroup {
|
||||
// We need to patch the loop result to be a collector
|
||||
collectorType = core.CollectorTypeKeyGroup
|
||||
}
|
||||
projection := c.initializeProjection(ctx, kv, counter)
|
||||
|
||||
loop := c.ctx.Loops.Current()
|
||||
collectorType := core.DetermineCollectorType(len(groupSelectors) > 0, aggregation != nil, projection)
|
||||
// We replace DataSet initialization with Collector initialization
|
||||
dst := loop.PatchDestinationAx(c.ctx.Registers, c.ctx.Emitter, vm.OpDataSetCollector, int(collectorType))
|
||||
|
||||
var aggregationSelectors []*aggregateSelector
|
||||
var aggregationSelectors []*core.AggregateSelector
|
||||
|
||||
// Fuse aggregation loop
|
||||
if aggregation != nil {
|
||||
aggregationSelectors = c.initializeAggregation(aggregation, dst, kv, len(aggregationSelectors) > 0)
|
||||
aggregationSelectors = c.initializeAggregation(aggregation, dst, kv, len(groupSelectors) > 0)
|
||||
}
|
||||
|
||||
c.finalizeCollector(dst, kv, len(groupSelectors) > 0, aggregation != nil)
|
||||
scope := core.NewCollectorSpec(collectorType, projection, groupSelectors, aggregationSelectors)
|
||||
|
||||
c.finalizeCollector(dst, kv, scope)
|
||||
|
||||
// We no longer need KV, so we free registers
|
||||
c.ctx.Registers.Free(kv.Key)
|
||||
c.ctx.Registers.Free(kv.Value)
|
||||
|
||||
return &collectorScope{collectorType, projectionVarName, groupSelectors, aggregationSelectors}
|
||||
return scope
|
||||
}
|
||||
|
||||
func (c *LoopCollectCompiler) finalizeCollector(dst vm.Operand, kv *core.KV, withGrouping bool, withAggregation bool) {
|
||||
func (c *LoopCollectCompiler) finalizeCollector(dst vm.Operand, kv *core.KV, spec *core.CollectorSpec) {
|
||||
loop := c.ctx.Loops.Current()
|
||||
|
||||
// If we do not use grouping but use aggregation, we do not need to push the key and value
|
||||
// because they are already pushed by the global aggregation.
|
||||
push := withGrouping || !withAggregation
|
||||
|
||||
if push {
|
||||
c.ctx.Emitter.EmitABC(vm.OpPushKV, dst, kv.Key, kv.Value)
|
||||
if spec.HasGrouping() || !spec.HasAggregation() {
|
||||
c.ctx.Emitter.EmitPushKV(dst, kv.Key, kv.Value)
|
||||
} else if spec.HasProjection() {
|
||||
key := loadConstant(c.ctx, runtime.String(spec.Projection().VariableName()))
|
||||
c.ctx.Emitter.EmitPushKV(dst, key, kv.Value)
|
||||
c.ctx.Registers.Free(key)
|
||||
}
|
||||
|
||||
loop.EmitFinalization(c.ctx.Emitter)
|
||||
@@ -81,7 +72,7 @@ func (c *LoopCollectCompiler) finalizeCollector(dst vm.Operand, kv *core.KV, wit
|
||||
c.ctx.Emitter.EmitMove(loop.Src, dst)
|
||||
}
|
||||
|
||||
func (c *LoopCollectCompiler) compileLoop(scope *collectorScope) {
|
||||
func (c *LoopCollectCompiler) compileLoop(spec *core.CollectorSpec) {
|
||||
loop := c.ctx.Loops.Current()
|
||||
|
||||
// If we are using a projection, we need to ensure the loop is set to ForInLoop
|
||||
@@ -97,27 +88,23 @@ func (c *LoopCollectCompiler) compileLoop(scope *collectorScope) {
|
||||
loop.Key = c.ctx.Registers.Allocate(core.Temp)
|
||||
}
|
||||
|
||||
withGrouping := len(scope.GroupSelectors) > 0
|
||||
withAggregation := len(scope.AggregationSelectors) > 0
|
||||
doInit := withGrouping || !withAggregation
|
||||
doInit := spec.HasGrouping() || !spec.HasAggregation()
|
||||
|
||||
if doInit {
|
||||
loop.EmitInitialization(c.ctx.Registers, c.ctx.Emitter, c.ctx.Loops.Depth())
|
||||
}
|
||||
|
||||
if withAggregation {
|
||||
c.unpackGroupedValues(scope.AggregationSelectors, withGrouping)
|
||||
c.compileAggregation(scope.AggregationSelectors, withGrouping)
|
||||
if spec.HasAggregation() {
|
||||
c.unpackGroupedValues(spec)
|
||||
c.compileAggregation(spec)
|
||||
}
|
||||
|
||||
// If the projection is used, we allocate a new register for the variable and put the iterator's value into it
|
||||
if scope.Projection != "" {
|
||||
// Now we need to expand group variables from the dataset
|
||||
loop.ValueName = scope.Projection
|
||||
c.ctx.Symbols.AssignLocal(loop.ValueName, core.TypeUnknown, loop.Value)
|
||||
if spec.HasProjection() {
|
||||
c.finalizeProjection(spec)
|
||||
}
|
||||
|
||||
if withGrouping {
|
||||
c.compileGrouping(scope.Type, scope.GroupSelectors)
|
||||
if spec.HasGrouping() {
|
||||
c.compileGrouping(spec)
|
||||
}
|
||||
}
|
||||
|
@@ -1,24 +1,17 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/compiler/internal/core"
|
||||
"github.com/MontFerret/ferret/pkg/parser/fql"
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type aggregateSelector struct {
|
||||
Name runtime.String
|
||||
Register vm.Operand
|
||||
Args int
|
||||
FuncName runtime.String
|
||||
ProtectedCall bool
|
||||
}
|
||||
|
||||
func (c *LoopCollectCompiler) initializeAggregation(ctx fql.ICollectAggregatorContext, dst vm.Operand, kv *core.KV, withGrouping bool) []*aggregateSelector {
|
||||
func (c *LoopCollectCompiler) initializeAggregation(ctx fql.ICollectAggregatorContext, dst vm.Operand, kv *core.KV, withGrouping bool) []*core.AggregateSelector {
|
||||
selectors := ctx.AllCollectAggregateSelector()
|
||||
var compiledSelectors []*aggregateSelector
|
||||
var compiledSelectors []*core.AggregateSelector
|
||||
|
||||
// if we have grouping, we need to pack the selectors into the collector value
|
||||
if withGrouping {
|
||||
@@ -34,22 +27,22 @@ func (c *LoopCollectCompiler) initializeAggregation(ctx fql.ICollectAggregatorCo
|
||||
return compiledSelectors
|
||||
}
|
||||
|
||||
func (c *LoopCollectCompiler) packGroupedValues(kv *core.KV, selectors []*aggregateSelector) {
|
||||
func (c *LoopCollectCompiler) packGroupedValues(kv *core.KV, selectors []*core.AggregateSelector) {
|
||||
// We need to add the loop value to the array
|
||||
seq := c.ctx.Registers.AllocateSequence(len(selectors) + 1)
|
||||
c.ctx.Emitter.EmitMove(seq[0], kv.Value)
|
||||
|
||||
for i, selector := range selectors {
|
||||
c.ctx.Emitter.EmitMove(seq[i+1], selector.Register)
|
||||
c.ctx.Registers.Free(selector.Register)
|
||||
c.ctx.Emitter.EmitMove(seq[i+1], selector.Register())
|
||||
c.ctx.Registers.Free(selector.Register())
|
||||
}
|
||||
|
||||
// Now we need to wrap the selectors into a single array with the loop value
|
||||
c.ctx.Emitter.EmitArray(kv.Value, seq)
|
||||
}
|
||||
|
||||
func (c *LoopCollectCompiler) compileGroupedAggregationSelectors(selectors []fql.ICollectAggregateSelectorContext) []*aggregateSelector {
|
||||
wrappedSelectors := make([]*aggregateSelector, 0, len(selectors))
|
||||
func (c *LoopCollectCompiler) compileGroupedAggregationSelectors(selectors []fql.ICollectAggregateSelectorContext) []*core.AggregateSelector {
|
||||
wrappedSelectors := make([]*core.AggregateSelector, 0, len(selectors))
|
||||
|
||||
for i := 0; i < len(selectors); i++ {
|
||||
selector := selectors[i]
|
||||
@@ -79,20 +72,14 @@ func (c *LoopCollectCompiler) compileGroupedAggregationSelectors(selectors []fql
|
||||
isProtected := fce.ErrorOperator() != nil
|
||||
|
||||
// Collect information about the selector to unpack it later
|
||||
wrappedSelectors = append(wrappedSelectors, &aggregateSelector{
|
||||
Name: name,
|
||||
Args: len(args),
|
||||
Register: selectorArg,
|
||||
FuncName: funcName,
|
||||
ProtectedCall: isProtected,
|
||||
})
|
||||
wrappedSelectors = append(wrappedSelectors, core.NewAggregateSelector(name, len(args), funcName, isProtected, selectorArg))
|
||||
}
|
||||
|
||||
return wrappedSelectors
|
||||
}
|
||||
|
||||
func (c *LoopCollectCompiler) compileGlobalAggregationSelectors(selectors []fql.ICollectAggregateSelectorContext, dst vm.Operand) []*aggregateSelector {
|
||||
wrappedSelectors := make([]*aggregateSelector, 0, len(selectors))
|
||||
func (c *LoopCollectCompiler) compileGlobalAggregationSelectors(selectors []fql.ICollectAggregateSelectorContext, dst vm.Operand) []*core.AggregateSelector {
|
||||
wrappedSelectors := make([]*core.AggregateSelector, 0, len(selectors))
|
||||
|
||||
for i := 0; i < len(selectors); i++ {
|
||||
selector := selectors[i]
|
||||
@@ -123,12 +110,7 @@ func (c *LoopCollectCompiler) compileGlobalAggregationSelectors(selectors []fql.
|
||||
isProtected := fce.ErrorOperator() != nil
|
||||
|
||||
// Collect information about the selector to unpack it later
|
||||
wrappedSelectors = append(wrappedSelectors, &aggregateSelector{
|
||||
Name: name,
|
||||
Args: len(args),
|
||||
FuncName: funcName,
|
||||
ProtectedCall: isProtected,
|
||||
})
|
||||
wrappedSelectors = append(wrappedSelectors, core.NewAggregateSelector(name, len(args), funcName, isProtected, vm.NoopOperand))
|
||||
|
||||
c.ctx.Registers.FreeSequence(args)
|
||||
}
|
||||
@@ -136,8 +118,8 @@ func (c *LoopCollectCompiler) compileGlobalAggregationSelectors(selectors []fql.
|
||||
return wrappedSelectors
|
||||
}
|
||||
|
||||
func (c *LoopCollectCompiler) unpackGroupedValues(selectors []*aggregateSelector, withGrouping bool) {
|
||||
if !withGrouping {
|
||||
func (c *LoopCollectCompiler) unpackGroupedValues(spec *core.CollectorSpec) {
|
||||
if !spec.HasGrouping() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -146,22 +128,22 @@ func (c *LoopCollectCompiler) unpackGroupedValues(selectors []*aggregateSelector
|
||||
|
||||
loadIndex(c.ctx, valReg, loop.Value, 0)
|
||||
|
||||
for i, selector := range selectors {
|
||||
loadIndex(c.ctx, selector.Register, loop.Value, i+1)
|
||||
for i, selector := range spec.AggregationSelectors() {
|
||||
loadIndex(c.ctx, selector.Register(), loop.Value, i+1)
|
||||
}
|
||||
|
||||
c.ctx.Registers.Free(valReg)
|
||||
}
|
||||
|
||||
func (c *LoopCollectCompiler) compileAggregation(vars []*aggregateSelector, withGrouping bool) {
|
||||
if withGrouping {
|
||||
c.compileGroupedAggregation(vars)
|
||||
func (c *LoopCollectCompiler) compileAggregation(spec *core.CollectorSpec) {
|
||||
if spec.HasGrouping() {
|
||||
c.compileGroupedAggregation(spec)
|
||||
} else {
|
||||
c.compileGlobalAggregation(vars)
|
||||
c.compileGlobalAggregation(spec)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LoopCollectCompiler) compileGroupedAggregation(selectors []*aggregateSelector) {
|
||||
func (c *LoopCollectCompiler) compileGroupedAggregation(spec *core.CollectorSpec) {
|
||||
//parentLoop := c.ctx.Loops.Current()
|
||||
//// We need to allocate a temporary accumulator to store aggregation results
|
||||
//selectors := ctx.AllCollectAggregateSelector()
|
||||
@@ -191,7 +173,7 @@ func (c *LoopCollectCompiler) compileGroupedAggregation(selectors []*aggregateSe
|
||||
//c.ctx.Registers.Free(accumulator)
|
||||
}
|
||||
|
||||
func (c *LoopCollectCompiler) compileGlobalAggregation(selectors []*aggregateSelector) {
|
||||
func (c *LoopCollectCompiler) compileGlobalAggregation(spec *core.CollectorSpec) {
|
||||
// At this point, it's finalized.
|
||||
prevLoop := c.ctx.Loops.Pop()
|
||||
c.ctx.Registers.Free(prevLoop.Key)
|
||||
@@ -217,12 +199,12 @@ func (c *LoopCollectCompiler) compileGlobalAggregation(selectors []*aggregateSel
|
||||
loop.EmitInitialization(c.ctx.Registers, c.ctx.Emitter, c.ctx.Loops.Depth())
|
||||
|
||||
// We just need to take the grouped values and call aggregation functions using them as args
|
||||
c.compileAggregationFuncCalls(selectors, prevLoop.Dst)
|
||||
c.compileAggregationFuncCalls(spec.AggregationSelectors(), prevLoop.Dst)
|
||||
|
||||
c.ctx.Registers.Free(prevLoop.Dst)
|
||||
}
|
||||
|
||||
func (c *LoopCollectCompiler) compileAggregationFuncCalls(selectors []*aggregateSelector, aggregator vm.Operand) {
|
||||
func (c *LoopCollectCompiler) compileAggregationFuncCalls(selectors []*core.AggregateSelector, aggregator vm.Operand) {
|
||||
// Gets the number of records in the accumulator
|
||||
cond := c.ctx.Registers.Allocate(core.Temp)
|
||||
c.ctx.Emitter.EmitAB(vm.OpLength, cond, aggregator)
|
||||
@@ -242,27 +224,27 @@ func (c *LoopCollectCompiler) compileAggregationFuncCalls(selectors []*aggregate
|
||||
var args core.RegisterSequence
|
||||
|
||||
// We need to unpack arguments
|
||||
if selector.Args > 1 {
|
||||
args = c.ctx.Registers.AllocateSequence(selector.Args)
|
||||
if selector.Args() > 1 {
|
||||
args = c.ctx.Registers.AllocateSequence(selector.Args())
|
||||
|
||||
for y, reg := range args {
|
||||
argKeyReg := c.loadAggregationArgKey(selector.Name, y)
|
||||
argKeyReg := c.loadAggregationArgKey(selector.Name(), y)
|
||||
c.ctx.Emitter.EmitABC(vm.OpLoadKey, reg, aggregator, argKeyReg)
|
||||
c.ctx.Registers.Free(argKeyReg)
|
||||
}
|
||||
} else {
|
||||
key := loadConstant(c.ctx, runtime.String(selector.Name))
|
||||
key := loadConstant(c.ctx, selector.Name())
|
||||
value := c.ctx.Registers.Allocate(core.Temp)
|
||||
c.ctx.Emitter.EmitABC(vm.OpLoadKey, value, aggregator, key)
|
||||
args = core.RegisterSequence{value}
|
||||
c.ctx.Registers.Free(key)
|
||||
}
|
||||
|
||||
result := c.ctx.ExprCompiler.CompileFunctionCallByNameWith(selector.FuncName, selector.ProtectedCall, args)
|
||||
result := c.ctx.ExprCompiler.CompileFunctionCallByNameWith(selector.FuncName(), selector.ProtectedCall(), args)
|
||||
|
||||
// We define the variable for the selector result in the upper scope
|
||||
// Since this temporary scope is only for aggregators and will be closed after the aggregation
|
||||
selectorVarName := selector.Name
|
||||
selectorVarName := selector.Name()
|
||||
varReg := c.ctx.Symbols.DeclareLocal(selectorVarName.String(), core.TypeUnknown)
|
||||
selectorVarRegs[i] = varReg
|
||||
c.ctx.Emitter.EmitAB(vm.OpMove, varReg, result)
|
||||
@@ -280,9 +262,9 @@ func (c *LoopCollectCompiler) compileAggregationFuncCalls(selectors []*aggregate
|
||||
c.ctx.Registers.Free(cond)
|
||||
}
|
||||
|
||||
func (c *LoopCollectCompiler) compileAggregationFuncCall(selector *aggregateSelector) {
|
||||
varReg := c.ctx.Symbols.DeclareLocal(selector.Name.String(), core.TypeUnknown)
|
||||
loadIndex(c.ctx, varReg, selector.Register, 1)
|
||||
func (c *LoopCollectCompiler) compileAggregationFuncCall(selector *core.AggregateSelector) {
|
||||
varReg := c.ctx.Symbols.DeclareLocal(selector.Name().String(), core.TypeUnknown)
|
||||
loadIndex(c.ctx, varReg, selector.Register(), 1)
|
||||
}
|
||||
|
||||
func (c *LoopCollectCompiler) loadAggregationArgKey(selector runtime.String, arg int) vm.Operand {
|
||||
|
@@ -8,8 +8,8 @@ import (
|
||||
)
|
||||
|
||||
// initializeGrouping creates the KeyValue pair for collection, handling both grouping and value setup.
|
||||
func (c *LoopCollectCompiler) initializeGrouping(grouping fql.ICollectGroupingContext) (*core.KV, []fql.ICollectSelectorContext) {
|
||||
var groupSelectors []fql.ICollectSelectorContext
|
||||
func (c *LoopCollectCompiler) initializeGrouping(grouping fql.ICollectGroupingContext) (*core.KV, []*core.CollectSelector) {
|
||||
var groupSelectors []*core.CollectSelector
|
||||
|
||||
kv := core.NewKV(vm.NoopOperand, vm.NoopOperand)
|
||||
loop := c.ctx.Loops.Current()
|
||||
@@ -40,18 +40,20 @@ func (c *LoopCollectCompiler) initializeGrouping(grouping fql.ICollectGroupingCo
|
||||
}
|
||||
|
||||
// compileGroupKeys compiles the grouping keys from the CollectGroupingContext.
|
||||
func (c *LoopCollectCompiler) compileGroupKeys(ctx fql.ICollectGroupingContext) (vm.Operand, []fql.ICollectSelectorContext) {
|
||||
func (c *LoopCollectCompiler) compileGroupKeys(ctx fql.ICollectGroupingContext) (vm.Operand, []*core.CollectSelector) {
|
||||
selectors := ctx.AllCollectSelector()
|
||||
|
||||
if len(selectors) == 0 {
|
||||
return vm.NoopOperand, selectors
|
||||
return vm.NoopOperand, nil
|
||||
}
|
||||
|
||||
var kvKeyReg vm.Operand
|
||||
var collectSelectors []*core.CollectSelector
|
||||
|
||||
if len(selectors) > 1 {
|
||||
// We create a sequence of Registers for the clauses
|
||||
// To pack them into an array
|
||||
collectSelectors = make([]*core.CollectSelector, len(selectors))
|
||||
selectorRegs := c.ctx.Registers.AllocateSequence(len(selectors))
|
||||
|
||||
for i, selector := range selectors {
|
||||
@@ -59,32 +61,36 @@ func (c *LoopCollectCompiler) compileGroupKeys(ctx fql.ICollectGroupingContext)
|
||||
c.ctx.Emitter.EmitAB(vm.OpMove, selectorRegs[i], reg)
|
||||
// Free the register after moving its value to the sequence register
|
||||
c.ctx.Registers.Free(reg)
|
||||
|
||||
collectSelectors[i] = core.NewCollectSelector(runtime.String(selector.Identifier().GetText()))
|
||||
}
|
||||
|
||||
kvKeyReg = c.ctx.Registers.Allocate(core.Temp)
|
||||
c.ctx.Emitter.EmitAs(vm.OpLoadArray, kvKeyReg, selectorRegs)
|
||||
c.ctx.Registers.FreeSequence(selectorRegs)
|
||||
} else {
|
||||
kvKeyReg = c.ctx.ExprCompiler.Compile(selectors[0].Expression())
|
||||
selector := selectors[0]
|
||||
kvKeyReg = c.ctx.ExprCompiler.Compile(selector.Expression())
|
||||
collectSelectors = []*core.CollectSelector{core.NewCollectSelector(runtime.String(selector.Identifier().GetText()))}
|
||||
}
|
||||
|
||||
return kvKeyReg, selectors
|
||||
return kvKeyReg, collectSelectors
|
||||
}
|
||||
|
||||
func (c *LoopCollectCompiler) compileGrouping(collectorType core.CollectorType, selectors []fql.ICollectSelectorContext) {
|
||||
func (c *LoopCollectCompiler) compileGrouping(spec *core.CollectorSpec) {
|
||||
loop := c.ctx.Loops.Current()
|
||||
|
||||
if len(selectors) > 1 {
|
||||
variables := make([]vm.Operand, len(selectors))
|
||||
if len(spec.GroupSelectors()) > 1 {
|
||||
variables := make([]vm.Operand, len(spec.GroupSelectors()))
|
||||
|
||||
for i, selector := range selectors {
|
||||
name := selector.Identifier().GetText()
|
||||
for i, selector := range spec.GroupSelectors() {
|
||||
name := selector.Name()
|
||||
|
||||
if variables[i] == vm.NoopOperand {
|
||||
variables[i] = c.ctx.Symbols.DeclareLocal(name, core.TypeUnknown)
|
||||
variables[i] = c.ctx.Symbols.DeclareLocal(name.String(), core.TypeUnknown)
|
||||
}
|
||||
|
||||
reg := c.selectGroupKey(collectorType, loop)
|
||||
reg := c.selectGroupKey(spec.Type(), loop)
|
||||
|
||||
c.ctx.Emitter.EmitABC(vm.OpLoadIndex, variables[i], reg, loadConstant(c.ctx, runtime.Int(i)))
|
||||
}
|
||||
@@ -95,9 +101,9 @@ func (c *LoopCollectCompiler) compileGrouping(collectorType core.CollectorType,
|
||||
}
|
||||
} else {
|
||||
// Get the variable name
|
||||
name := selectors[0].Identifier().GetText()
|
||||
name := spec.GroupSelectors()[0].Name()
|
||||
// If we have a single selector, we can just use the loops' register directly
|
||||
c.ctx.Symbols.AssignLocal(name, core.TypeUnknown, c.selectGroupKey(collectorType, loop))
|
||||
c.ctx.Symbols.AssignLocal(name.String(), core.TypeUnknown, c.selectGroupKey(spec.Type(), loop))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,49 +1,54 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/antlr4-go/antlr/v4"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/compiler/internal/core"
|
||||
"github.com/MontFerret/ferret/pkg/parser/fql"
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
"github.com/antlr4-go/antlr/v4"
|
||||
)
|
||||
|
||||
// initializeProjection handles the projection setup for group variables and counters.
|
||||
// Returns the projection variable name and the appropriate collector type.
|
||||
func (c *LoopCollectCompiler) initializeProjection(ctx fql.ICollectClauseContext, kv *core.KV, counter fql.ICollectCounterContext, hasGrouping bool) (string, core.CollectorType) {
|
||||
projectionVariableName := ""
|
||||
collectorType := core.CollectorTypeKey
|
||||
|
||||
func (c *LoopCollectCompiler) initializeProjection(ctx fql.ICollectClauseContext, kv *core.KV, counter fql.ICollectCounterContext) *core.CollectorProjection {
|
||||
// Handle group variable projection
|
||||
if groupVar := ctx.CollectGroupVariable(); groupVar != nil {
|
||||
projectionVariableName = c.compileGroupVariableProjection(kv, groupVar)
|
||||
collectorType = core.CollectorTypeKeyGroup
|
||||
return projectionVariableName, collectorType
|
||||
if groupVar := ctx.CollectGroupProjection(); groupVar != nil {
|
||||
varName := c.compileGroupVariableProjection(kv, groupVar)
|
||||
return core.NewCollectorGroupProjection(varName)
|
||||
}
|
||||
|
||||
// Handle counter projection
|
||||
if counter != nil {
|
||||
projectionVariableName = counter.Identifier().GetText()
|
||||
collectorType = c.determineCounterCollectorType(hasGrouping)
|
||||
varName := counter.Identifier().GetText()
|
||||
|
||||
return core.NewCollectorCountProjection(varName)
|
||||
}
|
||||
|
||||
return projectionVariableName, collectorType
|
||||
return nil
|
||||
}
|
||||
|
||||
// determineCounterCollectorType returns the appropriate collector type for counter operations.
|
||||
func (c *LoopCollectCompiler) determineCounterCollectorType(hasGrouping bool) core.CollectorType {
|
||||
if hasGrouping {
|
||||
return core.CollectorTypeKeyCounter
|
||||
}
|
||||
func (c *LoopCollectCompiler) finalizeProjection(spec *core.CollectorSpec) {
|
||||
loop := c.ctx.Loops.Current()
|
||||
varName := spec.Projection().VariableName()
|
||||
|
||||
return core.CollectorTypeCounter
|
||||
if spec.HasGrouping() || !spec.HasAggregation() {
|
||||
// Now we need to expand group variables from the dataset
|
||||
loop.ValueName = varName
|
||||
c.ctx.Symbols.AssignLocal(loop.ValueName, core.TypeUnknown, loop.Value)
|
||||
} else {
|
||||
key := loadConstant(c.ctx, runtime.String(varName))
|
||||
val := c.ctx.Symbols.DeclareLocal(varName, core.TypeUnknown)
|
||||
c.ctx.Emitter.EmitABC(vm.OpLoadKey, val, loop.Dst, key)
|
||||
c.ctx.Registers.Free(key)
|
||||
}
|
||||
}
|
||||
|
||||
// compileGroupVariableProjection processes group variable projections (both default and custom).
|
||||
func (c *LoopCollectCompiler) compileGroupVariableProjection(kv *core.KV, groupVar fql.ICollectGroupVariableContext) string {
|
||||
func (c *LoopCollectCompiler) compileGroupVariableProjection(kv *core.KV, groupVar fql.ICollectGroupProjectionContext) string {
|
||||
// Handle default projection (identifier)
|
||||
if identifier := groupVar.Identifier(); identifier != nil {
|
||||
return c.compileDefaultGroupProjection(kv, identifier, groupVar.CollectGroupVariableKeeper())
|
||||
return c.compileDefaultGroupProjection(kv, identifier, groupVar.CollectGroupProjectionFilter())
|
||||
}
|
||||
|
||||
// Handle custom projection (selector expression)
|
||||
@@ -54,7 +59,7 @@ func (c *LoopCollectCompiler) compileGroupVariableProjection(kv *core.KV, groupV
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *LoopCollectCompiler) compileDefaultGroupProjection(kv *core.KV, identifier antlr.TerminalNode, keeper fql.ICollectGroupVariableKeeperContext) string {
|
||||
func (c *LoopCollectCompiler) compileDefaultGroupProjection(kv *core.KV, identifier antlr.TerminalNode, keeper fql.ICollectGroupProjectionFilterContext) string {
|
||||
if keeper == nil {
|
||||
variables := c.ctx.Symbols.LocalVariables()
|
||||
scope := core.NewScopeProjection(c.ctx.Registers, c.ctx.Emitter, c.ctx.Symbols, variables)
|
||||
|
@@ -110,14 +110,13 @@ sortClauseExpression
|
||||
;
|
||||
|
||||
collectClause
|
||||
: Collect collectCounter
|
||||
| Collect collectAggregator
|
||||
| Collect collectGrouping collectAggregator
|
||||
| Collect collectGrouping collectGroupVariable
|
||||
| Collect collectGrouping collectCounter
|
||||
| Collect collectGrouping
|
||||
: Collect collectGrouping collectCounter
|
||||
| Collect collectGrouping collectAggregator? collectGroupProjection?
|
||||
| Collect collectAggregator collectGroupProjection? // no grouping
|
||||
| Collect collectCounter // pure COUNT
|
||||
;
|
||||
|
||||
|
||||
collectSelector
|
||||
: Identifier Assign expression
|
||||
;
|
||||
@@ -134,12 +133,12 @@ collectAggregateSelector
|
||||
: Identifier Assign functionCallExpression
|
||||
;
|
||||
|
||||
collectGroupVariable
|
||||
collectGroupProjection
|
||||
: Into collectSelector
|
||||
| Into Identifier (collectGroupVariableKeeper)?
|
||||
| Into Identifier (collectGroupProjectionFilter)?
|
||||
;
|
||||
|
||||
collectGroupVariableKeeper
|
||||
collectGroupProjectionFilter
|
||||
: Keep Identifier (Comma Identifier)*
|
||||
;
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -170,18 +170,18 @@ func (s *BaseFqlParserListener) EnterCollectAggregateSelector(ctx *CollectAggreg
|
||||
// ExitCollectAggregateSelector is called when production collectAggregateSelector is exited.
|
||||
func (s *BaseFqlParserListener) ExitCollectAggregateSelector(ctx *CollectAggregateSelectorContext) {}
|
||||
|
||||
// EnterCollectGroupVariable is called when production collectGroupVariable is entered.
|
||||
func (s *BaseFqlParserListener) EnterCollectGroupVariable(ctx *CollectGroupVariableContext) {}
|
||||
// EnterCollectGroupProjection is called when production collectGroupProjection is entered.
|
||||
func (s *BaseFqlParserListener) EnterCollectGroupProjection(ctx *CollectGroupProjectionContext) {}
|
||||
|
||||
// ExitCollectGroupVariable is called when production collectGroupVariable is exited.
|
||||
func (s *BaseFqlParserListener) ExitCollectGroupVariable(ctx *CollectGroupVariableContext) {}
|
||||
// ExitCollectGroupProjection is called when production collectGroupProjection is exited.
|
||||
func (s *BaseFqlParserListener) ExitCollectGroupProjection(ctx *CollectGroupProjectionContext) {}
|
||||
|
||||
// EnterCollectGroupVariableKeeper is called when production collectGroupVariableKeeper is entered.
|
||||
func (s *BaseFqlParserListener) EnterCollectGroupVariableKeeper(ctx *CollectGroupVariableKeeperContext) {
|
||||
// EnterCollectGroupProjectionFilter is called when production collectGroupProjectionFilter is entered.
|
||||
func (s *BaseFqlParserListener) EnterCollectGroupProjectionFilter(ctx *CollectGroupProjectionFilterContext) {
|
||||
}
|
||||
|
||||
// ExitCollectGroupVariableKeeper is called when production collectGroupVariableKeeper is exited.
|
||||
func (s *BaseFqlParserListener) ExitCollectGroupVariableKeeper(ctx *CollectGroupVariableKeeperContext) {
|
||||
// ExitCollectGroupProjectionFilter is called when production collectGroupProjectionFilter is exited.
|
||||
func (s *BaseFqlParserListener) ExitCollectGroupProjectionFilter(ctx *CollectGroupProjectionFilterContext) {
|
||||
}
|
||||
|
||||
// EnterCollectCounter is called when production collectCounter is entered.
|
||||
|
@@ -107,11 +107,11 @@ func (v *BaseFqlParserVisitor) VisitCollectAggregateSelector(ctx *CollectAggrega
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
func (v *BaseFqlParserVisitor) VisitCollectGroupVariable(ctx *CollectGroupVariableContext) interface{} {
|
||||
func (v *BaseFqlParserVisitor) VisitCollectGroupProjection(ctx *CollectGroupProjectionContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
func (v *BaseFqlParserVisitor) VisitCollectGroupVariableKeeper(ctx *CollectGroupVariableKeeperContext) interface{} {
|
||||
func (v *BaseFqlParserVisitor) VisitCollectGroupProjectionFilter(ctx *CollectGroupProjectionFilterContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
|
@@ -82,11 +82,11 @@ type FqlParserListener interface {
|
||||
// EnterCollectAggregateSelector is called when entering the collectAggregateSelector production.
|
||||
EnterCollectAggregateSelector(c *CollectAggregateSelectorContext)
|
||||
|
||||
// EnterCollectGroupVariable is called when entering the collectGroupVariable production.
|
||||
EnterCollectGroupVariable(c *CollectGroupVariableContext)
|
||||
// EnterCollectGroupProjection is called when entering the collectGroupProjection production.
|
||||
EnterCollectGroupProjection(c *CollectGroupProjectionContext)
|
||||
|
||||
// EnterCollectGroupVariableKeeper is called when entering the collectGroupVariableKeeper production.
|
||||
EnterCollectGroupVariableKeeper(c *CollectGroupVariableKeeperContext)
|
||||
// EnterCollectGroupProjectionFilter is called when entering the collectGroupProjectionFilter production.
|
||||
EnterCollectGroupProjectionFilter(c *CollectGroupProjectionFilterContext)
|
||||
|
||||
// EnterCollectCounter is called when entering the collectCounter production.
|
||||
EnterCollectCounter(c *CollectCounterContext)
|
||||
@@ -301,11 +301,11 @@ type FqlParserListener interface {
|
||||
// ExitCollectAggregateSelector is called when exiting the collectAggregateSelector production.
|
||||
ExitCollectAggregateSelector(c *CollectAggregateSelectorContext)
|
||||
|
||||
// ExitCollectGroupVariable is called when exiting the collectGroupVariable production.
|
||||
ExitCollectGroupVariable(c *CollectGroupVariableContext)
|
||||
// ExitCollectGroupProjection is called when exiting the collectGroupProjection production.
|
||||
ExitCollectGroupProjection(c *CollectGroupProjectionContext)
|
||||
|
||||
// ExitCollectGroupVariableKeeper is called when exiting the collectGroupVariableKeeper production.
|
||||
ExitCollectGroupVariableKeeper(c *CollectGroupVariableKeeperContext)
|
||||
// ExitCollectGroupProjectionFilter is called when exiting the collectGroupProjectionFilter production.
|
||||
ExitCollectGroupProjectionFilter(c *CollectGroupProjectionFilterContext)
|
||||
|
||||
// ExitCollectCounter is called when exiting the collectCounter production.
|
||||
ExitCollectCounter(c *CollectCounterContext)
|
||||
|
@@ -82,11 +82,11 @@ type FqlParserVisitor interface {
|
||||
// Visit a parse tree produced by FqlParser#collectAggregateSelector.
|
||||
VisitCollectAggregateSelector(ctx *CollectAggregateSelectorContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FqlParser#collectGroupVariable.
|
||||
VisitCollectGroupVariable(ctx *CollectGroupVariableContext) interface{}
|
||||
// Visit a parse tree produced by FqlParser#collectGroupProjection.
|
||||
VisitCollectGroupProjection(ctx *CollectGroupProjectionContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FqlParser#collectGroupVariableKeeper.
|
||||
VisitCollectGroupVariableKeeper(ctx *CollectGroupVariableKeeperContext) interface{}
|
||||
// Visit a parse tree produced by FqlParser#collectGroupProjectionFilter.
|
||||
VisitCollectGroupProjectionFilter(ctx *CollectGroupProjectionFilterContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FqlParser#collectCounter.
|
||||
VisitCollectCounter(ctx *CollectCounterContext) interface{}
|
||||
|
@@ -5,6 +5,7 @@ import "github.com/pkg/errors"
|
||||
var (
|
||||
ErrMissedParam = errors.New("missed parameter")
|
||||
ErrFunctionNotFound = errors.New("function not found")
|
||||
ErrRuntimePanic = errors.New("runtime panic")
|
||||
)
|
||||
|
||||
type (
|
||||
|
@@ -30,3 +30,9 @@ func Skip(uc TestCase) TestCase {
|
||||
uc.Skip = true
|
||||
return uc
|
||||
}
|
||||
|
||||
func Debug(useCase TestCase) TestCase {
|
||||
useCase.DebugOutput = true
|
||||
|
||||
return useCase
|
||||
}
|
||||
|
@@ -8,3 +8,4 @@ type UseCase = base.TestCase
|
||||
|
||||
var NewCase = base.NewCase
|
||||
var Skip = base.Skip
|
||||
var Debug = base.Debug
|
||||
|
@@ -16,12 +16,6 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
)
|
||||
|
||||
func Debug(useCase UseCase) UseCase {
|
||||
useCase.DebugOutput = true
|
||||
|
||||
return useCase
|
||||
}
|
||||
|
||||
func Case(expression string, expected any, desc ...string) UseCase {
|
||||
return NewCase(expression, expected, ShouldEqual, desc...)
|
||||
}
|
||||
@@ -38,6 +32,10 @@ func SkipCaseNil(expression string, desc ...string) UseCase {
|
||||
return Skip(CaseNil(expression, desc...))
|
||||
}
|
||||
|
||||
func DebugCaseNil(expression string, desc ...string) UseCase {
|
||||
return Debug(CaseNil(expression, desc...))
|
||||
}
|
||||
|
||||
func CaseRuntimeError(expression string, desc ...string) UseCase {
|
||||
return NewCase(expression, nil, ShouldBeError, desc...)
|
||||
}
|
||||
@@ -50,6 +48,10 @@ func SkipCaseRuntimeError(expression string, desc ...string) UseCase {
|
||||
return Skip(CaseRuntimeError(expression, desc...))
|
||||
}
|
||||
|
||||
func DebugCaseRuntimeError(expression string, desc ...string) UseCase {
|
||||
return Debug(CaseRuntimeError(expression, desc...))
|
||||
}
|
||||
|
||||
func SkipCaseRuntimeErrorAs(expression string, expected error, desc ...string) UseCase {
|
||||
return Skip(CaseRuntimeErrorAs(expression, expected, desc...))
|
||||
}
|
||||
@@ -72,6 +74,10 @@ func SkipCaseObject(expression string, expected map[string]any, desc ...string)
|
||||
return Skip(CaseObject(expression, expected, desc...))
|
||||
}
|
||||
|
||||
func DebugCaseObject(expression string, expected map[string]any, desc ...string) UseCase {
|
||||
return Debug(CaseObject(expression, expected, desc...))
|
||||
}
|
||||
|
||||
func CaseArray(expression string, expected []any, desc ...string) UseCase {
|
||||
uc := NewCase(expression, expected, ShouldEqualJSON, desc...)
|
||||
uc.RawOutput = true
|
||||
@@ -82,6 +88,10 @@ func SkipCaseArray(expression string, expected []any, desc ...string) UseCase {
|
||||
return Skip(CaseArray(expression, expected, desc...))
|
||||
}
|
||||
|
||||
func DebugCaseArray(expression string, expected []any, desc ...string) UseCase {
|
||||
return Debug(CaseArray(expression, expected, desc...))
|
||||
}
|
||||
|
||||
func CaseItems(expression string, expected ...any) UseCase {
|
||||
return NewCase(expression, expected, base.ShouldHaveSameItems)
|
||||
}
|
||||
@@ -90,10 +100,22 @@ func CaseFn(expression string, assertion func(actual any, expected ...any) strin
|
||||
return NewCase(expression, nil, assertion)
|
||||
}
|
||||
|
||||
func SkipCaseFn(expression string, assertion func(actual any, expected ...any) string) UseCase {
|
||||
return Skip(CaseFn(expression, assertion))
|
||||
}
|
||||
|
||||
func DebugCaseFn(expression string, assertion func(actual any, expected ...any) string) UseCase {
|
||||
return Debug(CaseFn(expression, assertion))
|
||||
}
|
||||
|
||||
func SkipCaseItems(expression string, expected ...any) UseCase {
|
||||
return Skip(CaseItems(expression, expected...))
|
||||
}
|
||||
|
||||
func DebugCaseItems(expression string, expected ...any) UseCase {
|
||||
return Debug(CaseItems(expression, expected...))
|
||||
}
|
||||
|
||||
func CaseJSON(expression string, expected string, desc ...string) UseCase {
|
||||
uc := NewCase(expression, expected, ShouldEqualJSON, desc...)
|
||||
uc.RawOutput = true
|
||||
@@ -104,6 +126,10 @@ func SkipCaseJSON(expression string, expected string, desc ...string) UseCase {
|
||||
return Skip(CaseJSON(expression, expected, desc...))
|
||||
}
|
||||
|
||||
func DebugCaseJSON(expression string, expected string, desc ...string) UseCase {
|
||||
return Debug(CaseJSON(expression, expected, desc...))
|
||||
}
|
||||
|
||||
func printDebugInfo(name string, uc UseCase, prog *vm.Program) {
|
||||
fmt.Println("")
|
||||
fmt.Println("VM Test:", name)
|
||||
|
@@ -205,7 +205,7 @@ FOR u IN users
|
||||
"highSalaryCount": 3,
|
||||
},
|
||||
}, "Should aggregate with conditional expressions"),
|
||||
CaseArray(`
|
||||
SkipCaseArray(`
|
||||
LET users = [
|
||||
{
|
||||
active: true,
|
||||
@@ -297,6 +297,94 @@ LET users = [
|
||||
`, []any{
|
||||
map[string]any{"ages": []any{31, 25, 36, 69, 45, 31, 25, 36, 69, 45}},
|
||||
}, "Should call aggregation functions with more than one argument"),
|
||||
DebugCaseArray(`
|
||||
LET users = [
|
||||
{
|
||||
active: true,
|
||||
married: true,
|
||||
age: 31,
|
||||
gender: "m"
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
married: false,
|
||||
age: 25,
|
||||
gender: "f"
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
married: false,
|
||||
age: 36,
|
||||
gender: "m"
|
||||
},
|
||||
{
|
||||
active: false,
|
||||
married: true,
|
||||
age: 69,
|
||||
gender: "m"
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
married: true,
|
||||
age: 45,
|
||||
gender: "f"
|
||||
}
|
||||
]
|
||||
FOR u IN users
|
||||
COLLECT AGGREGATE minAge = MIN(u.age), maxAge = MAX(u.age) INTO groupsVariable
|
||||
RETURN {
|
||||
groupsVariable,
|
||||
minAge,
|
||||
maxAge
|
||||
}
|
||||
`, []any{
|
||||
map[string]any{
|
||||
"groupsVariable": []any{
|
||||
map[string]any{
|
||||
"u": map[string]any{
|
||||
"active": true,
|
||||
"married": true,
|
||||
"age": 31,
|
||||
"gender": "m",
|
||||
},
|
||||
},
|
||||
map[string]any{
|
||||
"u": map[string]any{
|
||||
"active": true,
|
||||
"married": false,
|
||||
"age": 25,
|
||||
"gender": "f",
|
||||
},
|
||||
},
|
||||
map[string]any{
|
||||
"u": map[string]any{
|
||||
"active": true,
|
||||
"married": false,
|
||||
"age": 36,
|
||||
"gender": "m",
|
||||
},
|
||||
},
|
||||
map[string]any{
|
||||
"u": map[string]any{
|
||||
"active": false,
|
||||
"married": true,
|
||||
"age": 69,
|
||||
"gender": "m",
|
||||
},
|
||||
},
|
||||
map[string]any{
|
||||
"u": map[string]any{
|
||||
"active": true,
|
||||
"married": true,
|
||||
"age": 45,
|
||||
"gender": "f",
|
||||
},
|
||||
},
|
||||
},
|
||||
"minAge": 25,
|
||||
"maxAge": 69,
|
||||
},
|
||||
}),
|
||||
SkipCaseArray(`
|
||||
LET users = [
|
||||
{
|
||||
|
Reference in New Issue
Block a user