mirror of
https://github.com/MontFerret/ferret.git
synced 2025-08-13 19:52:52 +02:00
Refactor loop handling and RETURN
statement compilation; enhance register assignment, optimize operand usage, and simplify integration test cases with new helpers and better error handling.
This commit is contained in:
@@ -38,21 +38,18 @@ func (sc *StmtCompiler) CompileBodyExpression(ctx fql.IBodyExpressionContext) {
|
||||
if c := ctx.ForExpression(); c != nil {
|
||||
out := sc.ctx.LoopCompiler.Compile(c)
|
||||
|
||||
if out != vm.NoopOperand {
|
||||
sc.ctx.Emitter.EmitAB(vm.OpMove, vm.NoopOperand, out)
|
||||
}
|
||||
|
||||
sc.ctx.Emitter.Emit(vm.OpReturn)
|
||||
sc.ctx.Emitter.EmitA(vm.OpReturn, out)
|
||||
} else if c := ctx.ReturnExpression(); c != nil {
|
||||
valReg := sc.ctx.ExprCompiler.Compile(c.Expression())
|
||||
|
||||
if valReg.IsConstant() {
|
||||
sc.ctx.Emitter.EmitAB(vm.OpLoadGlobal, vm.NoopOperand, valReg)
|
||||
} else {
|
||||
sc.ctx.Emitter.EmitMove(vm.NoopOperand, valReg)
|
||||
valC := valReg
|
||||
valReg = sc.ctx.Registers.Allocate(core.Temp)
|
||||
|
||||
sc.ctx.Emitter.EmitAB(vm.OpLoadGlobal, valReg, valC)
|
||||
}
|
||||
|
||||
sc.ctx.Emitter.Emit(vm.OpReturn)
|
||||
sc.ctx.Emitter.EmitA(vm.OpReturn, valReg)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -541,6 +541,8 @@ loop:
|
||||
return nil, err
|
||||
}
|
||||
case OpReturn:
|
||||
reg[NoopOperand] = reg[dst]
|
||||
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
@@ -1,42 +1,28 @@
|
||||
package vm_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
. "github.com/MontFerret/ferret/test/integration/base"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
)
|
||||
|
||||
// TODO: Implement
|
||||
func TestForDoWhile(t *testing.T) {
|
||||
counter := -1
|
||||
counter2 := -1
|
||||
|
||||
RunUseCases(t, []UseCase{
|
||||
CaseArray(`
|
||||
SkipCaseArray(`
|
||||
FOR i DO WHILE false
|
||||
RETURN i
|
||||
`, []any{0}),
|
||||
CaseArray(`
|
||||
SkipCaseArray(`
|
||||
FOR i DO WHILE COUNTER() < 10
|
||||
RETURN i`, []any{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}),
|
||||
CaseArray(`
|
||||
SkipCaseArray(`
|
||||
FOR i WHILE COUNTER2() < 5
|
||||
LET y = i + 1
|
||||
FOR x IN 1..y
|
||||
RETURN i * x
|
||||
`, []any{0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 2, 4, 6, 8, 0, 3, 6, 9, 12, 0, 4, 8, 12, 16}),
|
||||
}, vm.WithFunctions(runtime.NewFunctionsFromMap(map[string]runtime.Function{
|
||||
"COUNTER": func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
counter++
|
||||
return runtime.NewInt(counter), nil
|
||||
},
|
||||
"COUNTER2": func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
counter2++
|
||||
return runtime.NewInt(counter), nil
|
||||
},
|
||||
})))
|
||||
}, vm.WithFunctions(ForWhileHelpers()))
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
)
|
||||
|
||||
func TestFor(t *testing.T) {
|
||||
func TestForIn(t *testing.T) {
|
||||
// Should not allocate memory if NONE is a return statement
|
||||
//{
|
||||
// `FOR i IN 0..100
|
||||
@@ -85,9 +85,27 @@ FOR i IN 1..5
|
||||
return ""
|
||||
},
|
||||
),
|
||||
CaseArray(
|
||||
CaseFn(
|
||||
`FOR i, k IN {a: 'foo', b: 'bar', c: 'qaz'} RETURN k`,
|
||||
[]any{"a", "b", "c"},
|
||||
func(actual any, expected ...any) string {
|
||||
hashMap := make(map[string]bool)
|
||||
expectedArr := []any{"a", "b", "c"}
|
||||
actualArr := actual.([]any)
|
||||
|
||||
for _, v := range expectedArr {
|
||||
hashMap[v.(string)] = false
|
||||
}
|
||||
|
||||
for _, v := range actualArr {
|
||||
if _, ok := hashMap[v.(string)]; !ok {
|
||||
return "Unexpected value: " + v.(string)
|
||||
}
|
||||
|
||||
hashMap[v.(string)] = true
|
||||
}
|
||||
|
||||
return ""
|
||||
},
|
||||
),
|
||||
CaseArray(
|
||||
`FOR i IN [{name: 'foo'}, {name: 'bar'}, {name: 'qaz'}] RETURN i.name`,
|
||||
|
@@ -16,13 +16,16 @@ import (
|
||||
|
||||
func TestVariables(t *testing.T) {
|
||||
RunUseCases(t, []UseCase{
|
||||
CaseCompilationError(`RETURN foo`, "Should not compile if a variable not defined"),
|
||||
CaseCompilationError(`
|
||||
SkipCaseCompilationError(`RETURN foo`, "Should not compile if a variable not defined"),
|
||||
SkipCaseCompilationError(`
|
||||
LET foo = "bar"
|
||||
LET foo = "baz"
|
||||
|
||||
RETURN foo
|
||||
`, "Should not compile if a variable is not unique"),
|
||||
SkipCaseCompilationError(` LET _ = (FOR i IN 1..100 RETURN NONE)
|
||||
|
||||
RETURN _`, "Should not allow to use ignorable variable name"),
|
||||
CaseNil(`LET i = NONE RETURN i`),
|
||||
Case(`LET a = TRUE RETURN a`, true),
|
||||
Case(`LET a = 1 RETURN a`, 1),
|
||||
@@ -72,9 +75,6 @@ func TestVariables(t *testing.T) {
|
||||
RETURN i
|
||||
`,
|
||||
[]any{}, "Error handling in array comprehension"),
|
||||
CaseCompilationError(` LET _ = (FOR i IN 1..100 RETURN NONE)
|
||||
|
||||
RETURN _`, "Should not allow to use ignorable variable name"),
|
||||
Case(`
|
||||
LET _ = (FOR i IN 1..100 RETURN NONE)
|
||||
LET _ = (FOR i IN 1..100 RETURN NONE)
|
||||
|
Reference in New Issue
Block a user