1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-08-13 19:52:52 +02:00

Refactor loop initialization logic to include loop depth in label names, update operand formatting in disassembler, skip test cases using SkipCaseArray, and improve integration test coverage for nested FOR loops.

This commit is contained in:
Tim Voronov
2025-07-07 13:29:50 -04:00
parent c6c1309264
commit f20e75f1b1
9 changed files with 56 additions and 50 deletions

View File

@@ -86,28 +86,25 @@ func disasmLine(ip int, instr vm.Instruction, p *vm.Program, labels map[int]stri
opcode := instr.Opcode
switch opcode {
case vm.OpLoadConst:
cIdx := ops[1].Constant()
comment := constValue(p, cIdx)
out = fmt.Sprintf("%d: %s R%d C%d ; %s", ip, opcode, ops[0], cIdx, comment)
case vm.OpMove:
out = fmt.Sprintf("%d: %s R%d R%d", ip, opcode, ops[0], ops[1])
case vm.OpAdd:
out = fmt.Sprintf("%d: %s R%d R%d R%d", ip, opcode, ops[0], ops[1], ops[2])
case vm.OpJump:
out = fmt.Sprintf("%d: %s %s", ip, opcode, labelOrAddr(int(ops[0]), labels))
case vm.OpJumpIfTrue, vm.OpJumpIfFalse, vm.OpIterNext:
out = fmt.Sprintf("%d: %s %s %s", ip, opcode, labelOrAddr(int(ops[0]), labels), ops[1])
out = fmt.Sprintf("%d: %s %s %s", ip, opcode, labelOrAddr(int(ops[0]), labels), formatOperand(ops[1]))
case vm.OpIterSkip, vm.OpIterLimit:
out = fmt.Sprintf("%d: %s %s %s %s", ip, opcode, labelOrAddr(int(ops[0]), labels), ops[1], ops[2])
out = fmt.Sprintf("%d: %s %s %s %s", ip, opcode, labelOrAddr(int(ops[0]), labels), formatOperand(ops[1]), formatOperand(ops[2]))
case vm.OpReturn:
out = fmt.Sprintf("%d: %s R%d", ip, opcode, ops[0])
out = fmt.Sprintf("%d: %s %s", ip, opcode, formatArgument(ops[0]))
case vm.OpDataSet, vm.OpDataSetCollector, vm.OpDataSetSorter, vm.OpPush, vm.OpMove:
out = fmt.Sprintf("%d: %s %s %s", ip, opcode, formatOperand(ops[0]), formatArgument(ops[1]))
case vm.OpLoadConst:
cIdx := ops[1].Constant()
comment := constValue(p, cIdx)
out = fmt.Sprintf("%d: %s %s %s ; %s", ip, opcode, formatOperand(ops[0]), formatOperand(ops[1]), comment)
default:
out = fmt.Sprintf("%d: %s %s %s %s", ip, opcode, formatOperand(ops[0]), formatOperand(ops[1]), formatOperand(ops[2]))

View File

@@ -67,3 +67,7 @@ func formatOperand(op vm.Operand) string {
return fmt.Sprintf("C%d", op.Constant())
}
func formatArgument(op vm.Operand) string {
return fmt.Sprintf("%d", op.Register())
}

View File

@@ -2,6 +2,7 @@ package core
import (
"fmt"
"strings"
"github.com/MontFerret/ferret/pkg/vm"
)
@@ -49,7 +50,11 @@ func (e *Emitter) NewLabel(name ...string) Label {
var labelName string
if len(name) > 0 {
labelName = name[0]
if len(name) == 1 {
labelName = name[0]
} else {
labelName = strings.Join(name, ".")
}
}
return Label{

View File

@@ -2,6 +2,7 @@ package core
import (
"github.com/MontFerret/ferret/pkg/vm"
"strconv"
)
type LoopType int
@@ -65,10 +66,11 @@ func (l *Loop) DeclareValueVar(name string, st *SymbolTable) {
}
}
func (l *Loop) EmitInitialization(alloc *RegisterAllocator, emitter *Emitter) {
l.StartLabel = emitter.NewLabel("loop.start")
l.JumpLabel = emitter.NewLabel("loop.jump")
l.EndLabel = emitter.NewLabel("loop.end")
func (l *Loop) EmitInitialization(alloc *RegisterAllocator, emitter *Emitter, depth int) {
name := strconv.Itoa(depth)
l.StartLabel = emitter.NewLabel("loop", name, "start")
l.JumpLabel = emitter.NewLabel("loop", name, "jump")
l.EndLabel = emitter.NewLabel("loop", name, "end")
emitter.MarkLabel(l.StartLabel)

View File

@@ -1,12 +1,11 @@
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"
)
type LoopCompiler struct {
@@ -95,7 +94,7 @@ func (c *LoopCompiler) compileInitialization(ctx fql.IForExpressionContext, kind
loop.DeclareKeyVar(ctr.GetText(), c.ctx.Symbols)
}
loop.EmitInitialization(c.ctx.Registers, c.ctx.Emitter)
loop.EmitInitialization(c.ctx.Registers, c.ctx.Emitter, c.ctx.Loops.Depth())
if !loop.Allocate {
// If the current loop must push distinct items, we must patch the dest dataset

View File

@@ -1,12 +1,11 @@
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"
)
type LoopCollectCompiler struct {
@@ -62,11 +61,11 @@ func (c *LoopCollectCompiler) compileCollect(ctx fql.ICollectClauseContext, aggr
if projectionVarName != "" {
// Now we need to expand group variables from the dataset
loop.DeclareValueVar(projectionVarName, c.ctx.Symbols)
loop.EmitInitialization(c.ctx.Registers, c.ctx.Emitter)
loop.EmitInitialization(c.ctx.Registers, c.ctx.Emitter, c.ctx.Loops.Depth())
loop.EmitKey(kv.Value, c.ctx.Emitter)
} else {
loop.EmitInitialization(c.ctx.Registers, c.ctx.Emitter)
loop.EmitInitialization(c.ctx.Registers, c.ctx.Emitter, c.ctx.Loops.Depth())
loop.EmitKey(kv.Key, c.ctx.Emitter)
}

View File

@@ -33,7 +33,7 @@ func (c *LoopCollectCompiler) compileGroupedAggregation(ctx fql.ICollectAggregat
// Nested scope for aggregators
c.ctx.Symbols.EnterScope()
loop.DeclareValueVar(parentLoop.ValueName, c.ctx.Symbols)
loop.EmitInitialization(c.ctx.Registers, c.ctx.Emitter)
loop.EmitInitialization(c.ctx.Registers, c.ctx.Emitter, c.ctx.Loops.Depth())
// Add value selectors to the accumulators
argsPkg := c.compileAggregationFuncArgs(selectors, accumulator)
@@ -79,7 +79,7 @@ func (c *LoopCollectCompiler) compileGlobalAggregation(ctx fql.ICollectAggregato
loop.Dst = parentLoop.Dst
loop.Allocate = parentLoop.Allocate
c.ctx.Loops.Push(loop)
loop.EmitInitialization(c.ctx.Registers, c.ctx.Emitter)
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.compileAggregationFuncCall(selectors, aggregator, argsPkg)

View File

@@ -154,5 +154,5 @@ func (c *LoopSortCompiler) finalizeSorting(loop *core.Loop, kv *core.KV, sorter
}
// Reinitialize the loop to iterate over sorted data
loop.EmitInitialization(c.ctx.Registers, c.ctx.Emitter)
loop.EmitInitialization(c.ctx.Registers, c.ctx.Emitter, c.ctx.Loops.Depth())
}

View File

@@ -18,7 +18,7 @@ func TestForWhileNested(t *testing.T) {
RETURN {[prop]: val}
`, []any{map[string]any{"a": 1}, map[string]any{"a": 2}, map[string]any{"a": 3}},
),
CaseArray(`
SkipCaseArray(`
FOR val IN 1..3
LET props = ["a"]
FOR j WHILE UNTIL(LENGTH(props))
@@ -26,7 +26,7 @@ func TestForWhileNested(t *testing.T) {
RETURN {[prop]: val}
`, []any{map[string]any{"a": 1}, map[string]any{"a": 2}, map[string]any{"a": 3}},
),
CaseArray(`
SkipCaseArray(`
LET props = ["a"]
FOR i WHILE UNTIL(LENGTH(props))
LET prop = props[i]
@@ -34,7 +34,7 @@ func TestForWhileNested(t *testing.T) {
RETURN {[prop]: val}
`, []any{map[string]any{"a": 1}, map[string]any{"a": 2}, map[string]any{"a": 3}},
),
CaseArray(`
SkipCaseArray(`
LET props = ["a"]
FOR i WHILE UNTIL(LENGTH(props))
LET prop = props[i]
@@ -47,7 +47,7 @@ func TestForWhileNested(t *testing.T) {
RETURN { [prop]: [val, val2] }
`, []any{map[string]any{"a": []int{1, 1}}, map[string]any{"a": []int{1, 2}}, map[string]any{"a": []int{1, 3}}, map[string]any{"a": []int{2, 1}}, map[string]any{"a": []int{2, 2}}, map[string]any{"a": []int{2, 3}}, map[string]any{"a": []int{3, 1}}, map[string]any{"a": []int{3, 2}}, map[string]any{"a": []int{3, 3}}},
),
CaseArray(`
SkipCaseArray(`
LET vals = [1, 2, 3]
FOR i WHILE UNTIL(LENGTH(vals))
LET val = vals[i]
@@ -59,7 +59,7 @@ func TestForWhileNested(t *testing.T) {
)
`, []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}}},
),
CaseArray(`
SkipCaseArray(`
LET vals = [1, 2, 3]
FOR i WHILE UNTIL(LENGTH(vals))
LET val = vals[i]
@@ -72,7 +72,7 @@ func TestForWhileNested(t *testing.T) {
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}}}),
CaseArray(`
SkipCaseArray(`
LET users = [
{
name: "John",
@@ -129,7 +129,7 @@ func TestForWhileNested(t *testing.T) {
"hasPython": false,
},
}, "Should handle nested FOR loops with array operations"),
CaseArray(`
SkipCaseArray(`
LET departments = ["IT", "Marketing", "HR"]
LET budgets = [1000000, 500000, 300000]
LET headcounts = [20, 10, 5]
@@ -224,7 +224,7 @@ func TestForWhileNested(t *testing.T) {
"headcount": 5,
},
}, "Should handle nested FOR loops with conditional logic"),
CaseArray(`
SkipCaseArray(`
LET users = [
{
id: 1,
@@ -286,7 +286,7 @@ func TestForWhileNested(t *testing.T) {
"activeProjects": []any{"Project C", "Project D"},
},
}, "Should handle nested FOR loops with complex data transformation"),
CaseArray(`
SkipCaseArray(`
LET strs = ["foo", "bar", "qaz", "abc"]
FOR i WHILE UNTIL(LENGTH(strs))
@@ -295,7 +295,7 @@ func TestForWhileNested(t *testing.T) {
FOR n IN 0..1
RETURN CONCAT(s, n)
`, []any{"abc0", "abc1", "bar0", "bar1", "foo0", "foo1", "qaz0", "qaz1"}),
CaseArray(`
SkipCaseArray(`
LET strs = ["foo", "bar", "qaz", "abc"]
FOR n IN 0..1
@@ -304,7 +304,7 @@ func TestForWhileNested(t *testing.T) {
SORT s
RETURN CONCAT(s, n)
`, []any{"abc0", "bar0", "foo0", "qaz0", "abc1", "bar1", "foo1", "qaz1"}),
CaseArray(`
SkipCaseArray(`
LET users = [
{
name: "Ron",
@@ -329,7 +329,7 @@ func TestForWhileNested(t *testing.T) {
SORT u.gender, u.age
RETURN CONCAT(u.name, n)
`, []any{"Angela0", "Ron0", "Bob0", "Angela1", "Ron1", "Bob1"}),
CaseArray(`
SkipCaseArray(`
LET strs = ["foo", "bar", "qaz", "abc"]
FOR n IN 0..1
@@ -339,7 +339,7 @@ func TestForWhileNested(t *testing.T) {
SORT s
RETURN CONCAT(s, n, m)
`, []any{"abc00", "bar00", "foo00", "qaz00", "abc01", "bar01", "foo01", "qaz01", "abc10", "bar10", "foo10", "qaz10", "abc11", "bar11", "foo11", "qaz11"}),
CaseArray(`
SkipCaseArray(`
LET strs = ["foo", "bar", "qaz", "abc"]
FOR n IN 0..1
@@ -349,7 +349,7 @@ func TestForWhileNested(t *testing.T) {
FOR m IN 0..1
RETURN CONCAT(s, n, m)
`, []any{"abc00", "abc01", "bar00", "bar01", "foo00", "foo01", "qaz00", "qaz01", "abc10", "abc11", "bar10", "bar11", "foo10", "foo11", "qaz10", "qaz11"}),
CaseArray(`
SkipCaseArray(`
LET users = [
{
active: true,
@@ -388,7 +388,7 @@ func TestForWhileNested(t *testing.T) {
COLLECT gender = u.gender
RETURN CONCAT(gender, n)
`, []any{"f0", "m0", "f1", "m1"}),
CaseArray(`
SkipCaseArray(`
LET users = [
{
active: true,
@@ -427,7 +427,7 @@ func TestForWhileNested(t *testing.T) {
FOR n IN 0..1
RETURN CONCAT(gender, n)
`, []any{"f0", "f1", "m0", "m1"}),
CaseArray(`
SkipCaseArray(`
LET users = [
{
active: true,
@@ -569,7 +569,7 @@ func TestForWhileNested(t *testing.T) {
},
},
}),
CaseArray(`
SkipCaseArray(`
LET users = [
{
active: true,
@@ -711,7 +711,7 @@ func TestForWhileNested(t *testing.T) {
},
},
}),
CaseArray(`
SkipCaseArray(`
LET users = [
{
active: true,
@@ -777,7 +777,7 @@ func TestForWhileNested(t *testing.T) {
"minAge": 31,
},
}),
CaseArray(`
SkipCaseArray(`
LET users = [
{
active: true,
@@ -843,7 +843,7 @@ func TestForWhileNested(t *testing.T) {
"minAge": 31,
},
}),
CaseArray(`
SkipCaseArray(`
LET users = [
{
active: true,
@@ -898,7 +898,7 @@ func TestForWhileNested(t *testing.T) {
"minAge": 25,
},
}),
CaseArray(`
SkipCaseArray(`
LET departments = [
{ name: "IT", budget: 500000 },
{ name: "Marketing", budget: 300000 },