Skip to content

Commit

Permalink
Implemented DISTINCT keyword
Browse files Browse the repository at this point in the history
  • Loading branch information
ziflex committed Mar 26, 2024
1 parent 930adef commit 9d89d9e
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 9 deletions.
13 changes: 10 additions & 3 deletions pkg/compiler/compiler_for_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ func TestFor(t *testing.T) {
},
{
`FOR val, counter IN 1..5
LET x = val
PRINT(counter)
LET x = val
PRINT(counter)
LET y = counter
RETURN [x, y]
`,
Expand Down Expand Up @@ -109,10 +109,17 @@ func TestFor(t *testing.T) {
FOR prop IN ["a", "b", "c"]
RETURN { [prop]: val }
)
RETURN sub`,
[]any{[]any{map[string]any{"a": 1}, map[string]any{"b": 1}, map[string]any{"c": 1}}, []any{map[string]any{"a": 2}, map[string]any{"b": 2}, map[string]any{"c": 2}}, []any{map[string]any{"a": 3}, map[string]any{"b": 3}, map[string]any{"c": 3}}},
ShouldEqualJSON,
},
{
`FOR i IN [ 1, 2, 3, 4, 1, 3 ]
RETURN DISTINCT i
`,
[]any{1, 2, 3, 4},
ShouldEqualJSON,
},
})
}
16 changes: 12 additions & 4 deletions pkg/compiler/visitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ func (v *visitor) VisitHead(_ *fql.HeadContext) interface{} {
func (v *visitor) VisitForExpression(ctx *fql.ForExpressionContext) interface{} {
v.beginScope()

var passThrough = false
var passThrough bool
var distinct bool
var returnRuleCtx antlr.RuleContext
var loopJump, exitJump int
// identify whether it's WHILE or FOR loop
Expand All @@ -127,12 +128,13 @@ func (v *visitor) VisitForExpression(ctx *fql.ForExpressionContext) interface{}

if c := returnCtx.ReturnExpression(); c != nil {
returnRuleCtx = c
distinct = c.Distinct() != nil
} else if c := returnCtx.ForExpression(); c != nil {
returnRuleCtx = c
passThrough = true
}

v.beginLoop(passThrough)
v.beginLoop(passThrough, distinct)

if isForInLoop {
// Loop data source to iterate over
Expand Down Expand Up @@ -764,7 +766,7 @@ func (v *visitor) endScope() {
}
}

func (v *visitor) beginLoop(passThrough bool) {
func (v *visitor) beginLoop(passThrough, distinct bool) {
var allocate bool

// top loop
Expand All @@ -784,7 +786,13 @@ func (v *visitor) beginLoop(passThrough bool) {
offset := 2

if allocate {
v.emit(runtime.OpArray)
var arg int

if distinct {
arg = 1
}

v.emit(runtime.OpLoopDestinationInit, arg)
} else {
offset = offset + len(v.loops)
}
Expand Down
48 changes: 48 additions & 0 deletions pkg/runtime/dataset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package runtime

import (
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
)

type DataSet struct {
hashmap map[uint64]bool
values *values.Array
}

func NewDataSet(distinct bool) *DataSet {
var hasmap map[uint64]bool

if distinct {
hasmap = make(map[uint64]bool)
}

return &DataSet{
hashmap: hasmap,
values: values.NewArray(16),
}
}

func (ds *DataSet) Push(item core.Value) {
if ds.hashmap != nil {
hash := item.Hash()

_, exists := ds.hashmap[hash]

if exists {
return
}

ds.hashmap[hash] = true
}

ds.values.Push(item)
}

func (ds *DataSet) MarshalJSON() ([]byte, error) {
return ds.values.MarshalJSON()
}

func (ds *DataSet) ToArray() *values.Array {
return ds.values
}
1 change: 1 addition & 0 deletions pkg/runtime/opcodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const (
OpJumpIfTrue
OpJump
OpJumpBackward
OpLoopDestinationInit
OpLoopSourceInit
OpLoopHasNext
OpLoopNext
Expand Down
8 changes: 6 additions & 2 deletions pkg/runtime/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,10 @@ loop:
} else {
return nil, err
}
case OpLoopDestinationInit:
ds := NewDataSet(arg == 1)

stack.Push(values.NewBoxedValue(ds))

case OpLoopSourceInit:
// start a new iteration
Expand Down Expand Up @@ -446,8 +450,8 @@ loop:
// pop the return value from the stack
res := stack.Pop()
pos := stack.Len() - arg
arr := stack.Get(pos).(*values.Array)
arr.Push(res)
ds := stack.Get(pos).(*values.Boxed).Unwrap().(*DataSet)
ds.Push(res)

case OpReturn:
break loop
Expand Down

0 comments on commit 9d89d9e

Please sign in to comment.