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

Refactor compiler structure; move internal packages to core and update references

This commit is contained in:
Tim Voronov
2025-06-11 10:00:57 -04:00
parent cf87b63720
commit 09f47e66fc
21 changed files with 141 additions and 127 deletions

View File

@@ -2,6 +2,7 @@ package compiler
import ( import (
"errors" "errors"
"github.com/MontFerret/ferret/pkg/compiler/internal/core"
goruntime "runtime" goruntime "runtime"
"github.com/MontFerret/ferret/pkg/compiler/internal" "github.com/MontFerret/ferret/pkg/compiler/internal"
@@ -37,7 +38,7 @@ func New(setters ...Option) *Compiler {
func (c *Compiler) Compile(query string) (program *vm.Program, err error) { func (c *Compiler) Compile(query string) (program *vm.Program, err error) {
if query == "" { if query == "" {
return nil, internal.ErrEmptyQuery return nil, core.ErrEmptyQuery
} }
defer func() { defer func() {

View File

@@ -1,12 +1,14 @@
package internal package internal
import "github.com/MontFerret/ferret/pkg/compiler/internal/core"
// FuncContext encapsulates the context and state required for compiling and managing functions during code processing. // FuncContext encapsulates the context and state required for compiling and managing functions during code processing.
type FuncContext struct { type FuncContext struct {
Emitter *Emitter Emitter *core.Emitter
Registers *RegisterAllocator Registers *core.RegisterAllocator
Symbols *SymbolTable Symbols *core.SymbolTable
Loops *LoopTable Loops *core.LoopTable
CatchTable *CatchStack CatchTable *core.CatchStack
ExprCompiler *ExprCompiler ExprCompiler *ExprCompiler
LiteralCompiler *LiteralCompiler LiteralCompiler *LiteralCompiler
@@ -19,14 +21,14 @@ type FuncContext struct {
// NewFuncContext initializes and returns a new instance of FuncContext, setting up all required components for compilation. // NewFuncContext initializes and returns a new instance of FuncContext, setting up all required components for compilation.
func NewFuncContext() *FuncContext { func NewFuncContext() *FuncContext {
ctx := &FuncContext{ ctx := &FuncContext{
Emitter: NewEmitter(), Emitter: core.NewEmitter(),
Registers: NewRegisterAllocator(), Registers: core.NewRegisterAllocator(),
Symbols: nil, // set later Symbols: nil, // set later
Loops: nil, // set later Loops: nil, // set later
CatchTable: NewCatchStack(), CatchTable: core.NewCatchStack(),
} }
ctx.Symbols = NewSymbolTable(ctx.Registers) ctx.Symbols = core.NewSymbolTable(ctx.Registers)
ctx.Loops = NewLoopTable(ctx.Registers) ctx.Loops = core.NewLoopTable(ctx.Registers)
ctx.ExprCompiler = NewExprCompiler(ctx) ctx.ExprCompiler = NewExprCompiler(ctx)
ctx.LiteralCompiler = NewLiteralCompiler(ctx) ctx.LiteralCompiler = NewLiteralCompiler(ctx)

View File

@@ -1,4 +1,4 @@
package internal package core
import ( import (
"github.com/MontFerret/ferret/pkg/vm" "github.com/MontFerret/ferret/pkg/vm"

View File

@@ -1,4 +1,4 @@
package internal package core
import ( import (
"github.com/MontFerret/ferret/pkg/runtime" "github.com/MontFerret/ferret/pkg/runtime"

View File

@@ -0,0 +1,8 @@
package core
const (
JumpPlaceholder = -1
UndefinedVariable = -1
IgnorePseudoVariable = "_"
PseudoVariable = "CURRENT"
)

View File

@@ -1,4 +1,4 @@
package internal package core
import ( import (
"github.com/MontFerret/ferret/pkg/vm" "github.com/MontFerret/ferret/pkg/vm"

View File

@@ -1,4 +1,4 @@
package internal package core
import ( import (
"github.com/MontFerret/ferret/pkg/vm" "github.com/MontFerret/ferret/pkg/vm"

View File

@@ -1,4 +1,4 @@
package internal package core
import "github.com/pkg/errors" import "github.com/pkg/errors"

View File

@@ -1,6 +1,8 @@
package internal package core
import "github.com/MontFerret/ferret/pkg/vm" import (
"github.com/MontFerret/ferret/pkg/vm"
)
type LoopType int type LoopType int
@@ -76,5 +78,5 @@ func (l *Loop) EmitFinalization(emitter *Emitter) {
} }
func (l *Loop) canBindVar(name string) bool { func (l *Loop) canBindVar(name string) bool {
return name != "" && name != ignorePseudoVariable return name != "" && name != IgnorePseudoVariable
} }

View File

@@ -1,4 +1,4 @@
package internal package core
import ( import (
"fmt" "fmt"

View File

@@ -1,4 +1,4 @@
package internal package core
import ( import (
"fmt" "fmt"

View File

@@ -1,4 +1,4 @@
package internal package core
import ( import (
"fmt" "fmt"

View File

@@ -1,4 +1,4 @@
package internal_test package core_test
import "testing" import "testing"

View File

@@ -1,6 +1,7 @@
package internal package internal
import ( import (
"github.com/MontFerret/ferret/pkg/compiler/internal/core"
"github.com/MontFerret/ferret/pkg/parser/fql" "github.com/MontFerret/ferret/pkg/parser/fql"
"github.com/MontFerret/ferret/pkg/runtime" "github.com/MontFerret/ferret/pkg/runtime"
"github.com/MontFerret/ferret/pkg/vm" "github.com/MontFerret/ferret/pkg/vm"
@@ -44,13 +45,13 @@ func (ec *ExprCompiler) Compile(ctx fql.IExpressionContext) vm.Operand {
return ec.compilePredicate(c) return ec.compilePredicate(c)
} }
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, ctx.GetText()))
} }
// TODO: Free temporary registers if needed // TODO: Free temporary registers if needed
func (ec *ExprCompiler) compileUnary(ctx fql.IUnaryOperatorContext, parent fql.IExpressionContext) vm.Operand { func (ec *ExprCompiler) compileUnary(ctx fql.IUnaryOperatorContext, parent fql.IExpressionContext) vm.Operand {
src := ec.Compile(parent.GetRight()) src := ec.Compile(parent.GetRight())
dst := ec.ctx.Registers.Allocate(Temp) dst := ec.ctx.Registers.Allocate(core.Temp)
var op vm.Opcode var op vm.Opcode
@@ -61,7 +62,7 @@ func (ec *ExprCompiler) compileUnary(ctx fql.IUnaryOperatorContext, parent fql.I
} else if ctx.Plus() != nil { } else if ctx.Plus() != nil {
op = vm.OpFlipPositive op = vm.OpFlipPositive
} else { } else {
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, ctx.GetText()))
} }
// We do not overwrite the source register // We do not overwrite the source register
@@ -72,7 +73,7 @@ func (ec *ExprCompiler) compileUnary(ctx fql.IUnaryOperatorContext, parent fql.I
// TODO: Free temporary registers if needed // TODO: Free temporary registers if needed
func (ec *ExprCompiler) compileLogicalAnd(ctx fql.IPredicateContext) vm.Operand { func (ec *ExprCompiler) compileLogicalAnd(ctx fql.IPredicateContext) vm.Operand {
dst := ec.ctx.Registers.Allocate(Temp) dst := ec.ctx.Registers.Allocate(core.Temp)
// Execute left expression // Execute left expression
left := ec.compilePredicate(ctx.GetLeft()) left := ec.compilePredicate(ctx.GetLeft())
@@ -81,7 +82,7 @@ func (ec *ExprCompiler) compileLogicalAnd(ctx fql.IPredicateContext) vm.Operand
ec.ctx.Emitter.EmitMove(dst, left) ec.ctx.Emitter.EmitMove(dst, left)
// Test if left is false and jump to the end // Test if left is false and jump to the end
end := ec.ctx.Emitter.EmitJumpIfFalse(dst, jumpPlaceholder) end := ec.ctx.Emitter.EmitJumpIfFalse(dst, core.JumpPlaceholder)
// If left is true, execute right expression // If left is true, execute right expression
right := ec.compilePredicate(ctx.GetRight()) right := ec.compilePredicate(ctx.GetRight())
@@ -96,7 +97,7 @@ func (ec *ExprCompiler) compileLogicalAnd(ctx fql.IPredicateContext) vm.Operand
// TODO: Free temporary registers if needed // TODO: Free temporary registers if needed
func (ec *ExprCompiler) compileLogicalOr(ctx fql.IPredicateContext) vm.Operand { func (ec *ExprCompiler) compileLogicalOr(ctx fql.IPredicateContext) vm.Operand {
dst := ec.ctx.Registers.Allocate(Temp) dst := ec.ctx.Registers.Allocate(core.Temp)
// Execute left expression // Execute left expression
left := ec.compilePredicate(ctx.GetLeft()) left := ec.compilePredicate(ctx.GetLeft())
@@ -105,7 +106,7 @@ func (ec *ExprCompiler) compileLogicalOr(ctx fql.IPredicateContext) vm.Operand {
ec.ctx.Emitter.EmitMove(dst, left) ec.ctx.Emitter.EmitMove(dst, left)
// Test if left is true and jump to the end // Test if left is true and jump to the end
end := ec.ctx.Emitter.EmitJumpIfTrue(dst, jumpPlaceholder) end := ec.ctx.Emitter.EmitJumpIfTrue(dst, core.JumpPlaceholder)
// If left is false, execute right expression // If left is false, execute right expression
right := ec.compilePredicate(ctx.GetRight()) right := ec.compilePredicate(ctx.GetRight())
@@ -120,14 +121,14 @@ func (ec *ExprCompiler) compileLogicalOr(ctx fql.IPredicateContext) vm.Operand {
// TODO: Free temporary registers if needed // TODO: Free temporary registers if needed
func (ec *ExprCompiler) compileTernary(ctx fql.IExpressionContext) vm.Operand { func (ec *ExprCompiler) compileTernary(ctx fql.IExpressionContext) vm.Operand {
dst := ec.ctx.Registers.Allocate(Temp) dst := ec.ctx.Registers.Allocate(core.Temp)
// Compile condition and put result in dst // Compile condition and put result in dst
condReg := ec.Compile(ctx.GetCondition()) condReg := ec.Compile(ctx.GetCondition())
ec.ctx.Emitter.EmitMove(dst, condReg) ec.ctx.Emitter.EmitMove(dst, condReg)
// Jump to 'false' branch if condition is false // Jump to 'false' branch if condition is false
otherwise := ec.ctx.Emitter.EmitJumpIfFalse(dst, jumpPlaceholder) otherwise := ec.ctx.Emitter.EmitJumpIfFalse(dst, core.JumpPlaceholder)
// True branch // True branch
if onTrue := ctx.GetOnTrue(); onTrue != nil { if onTrue := ctx.GetOnTrue(); onTrue != nil {
@@ -137,7 +138,7 @@ func (ec *ExprCompiler) compileTernary(ctx fql.IExpressionContext) vm.Operand {
} }
// Jump over false branch // Jump over false branch
end := ec.ctx.Emitter.EmitJump(jumpPlaceholder) end := ec.ctx.Emitter.EmitJump(core.JumpPlaceholder)
ec.ctx.Emitter.PatchJumpNext(otherwise) ec.ctx.Emitter.PatchJumpNext(otherwise)
// False branch // False branch
@@ -174,7 +175,7 @@ func (ec *ExprCompiler) compilePredicate(ctx fql.IPredicateContext) vm.Operand {
} }
var opcode vm.Opcode var opcode vm.Opcode
dest := ec.ctx.Registers.Allocate(Temp) dest := ec.ctx.Registers.Allocate(core.Temp)
left := ec.compilePredicate(ctx.Predicate(0)) left := ec.compilePredicate(ctx.Predicate(0))
right := ec.compilePredicate(ctx.Predicate(1)) right := ec.compilePredicate(ctx.Predicate(1))
@@ -193,7 +194,7 @@ func (ec *ExprCompiler) compilePredicate(ctx fql.IPredicateContext) vm.Operand {
case "<=": case "<=":
opcode = vm.OpLte opcode = vm.OpLte
default: default:
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, ctx.GetText()))
} }
} else if op := ctx.ArrayOperator(); op != nil { } else if op := ctx.ArrayOperator(); op != nil {
// TODO: Implement me // TODO: Implement me
@@ -233,7 +234,7 @@ func (ec *ExprCompiler) compileAtom(ctx fql.IExpressionAtomContext) vm.Operand {
case "%": case "%":
opcode = vm.OpMod opcode = vm.OpMod
default: default:
panic(runtime.Error(ErrUnexpectedToken, op.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, op.GetText()))
} }
} else if op := ctx.AdditiveOperator(); op != nil { } else if op := ctx.AdditiveOperator(); op != nil {
isSet = true isSet = true
@@ -244,7 +245,7 @@ func (ec *ExprCompiler) compileAtom(ctx fql.IExpressionAtomContext) vm.Operand {
case "-": case "-":
opcode = vm.OpSub opcode = vm.OpSub
default: default:
panic(runtime.Error(ErrUnexpectedToken, op.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, op.GetText()))
} }
} else if op := ctx.RegexpOperator(); op != nil { } else if op := ctx.RegexpOperator(); op != nil {
@@ -256,14 +257,14 @@ func (ec *ExprCompiler) compileAtom(ctx fql.IExpressionAtomContext) vm.Operand {
case "!~": case "!~":
opcode = vm.OpRegexpNegative opcode = vm.OpRegexpNegative
default: default:
panic(runtime.Error(ErrUnexpectedToken, op.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, op.GetText()))
} }
} }
if isSet { if isSet {
regLeft := ec.compileAtom(ctx.ExpressionAtom(0)) regLeft := ec.compileAtom(ctx.ExpressionAtom(0))
regRight := ec.compileAtom(ctx.ExpressionAtom(1)) regRight := ec.compileAtom(ctx.ExpressionAtom(1))
dst := ec.ctx.Registers.Allocate(Temp) dst := ec.ctx.Registers.Allocate(core.Temp)
if opcode == vm.OpRegexpPositive || opcode == vm.OpRegexpNegative { if opcode == vm.OpRegexpPositive || opcode == vm.OpRegexpNegative {
if regRight.IsConstant() { if regRight.IsConstant() {
@@ -299,7 +300,7 @@ func (ec *ExprCompiler) compileAtom(ctx fql.IExpressionAtomContext) vm.Operand {
return ec.Compile(c) return ec.Compile(c)
} }
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, ctx.GetText()))
} }
func (ec *ExprCompiler) CompileMemberExpression(ctx fql.IMemberExpressionContext) vm.Operand { func (ec *ExprCompiler) CompileMemberExpression(ctx fql.IMemberExpressionContext) vm.Operand {
@@ -334,7 +335,7 @@ func (ec *ExprCompiler) CompileMemberExpression(ctx fql.IMemberExpressionContext
src2 = ec.ctx.LiteralCompiler.CompileComputedPropertyName(c) src2 = ec.ctx.LiteralCompiler.CompileComputedPropertyName(c)
} }
dst = ec.ctx.Registers.Allocate(Temp) dst = ec.ctx.Registers.Allocate(core.Temp)
// TODO: Replace with EmitLoadKey // TODO: Replace with EmitLoadKey
if p.ErrorOperator() != nil { if p.ErrorOperator() != nil {
@@ -354,14 +355,14 @@ func (ec *ExprCompiler) CompileVariable(ctx fql.IVariableContext) vm.Operand {
op, _, found := ec.ctx.Symbols.Resolve(ctx.GetText()) op, _, found := ec.ctx.Symbols.Resolve(ctx.GetText())
if !found { if !found {
panic(runtime.Error(ErrVariableNotFound, ctx.GetText())) panic(runtime.Error(core.ErrVariableNotFound, ctx.GetText()))
} }
if op.IsRegister() { if op.IsRegister() {
return op return op
} }
reg := ec.ctx.Registers.Allocate(Temp) reg := ec.ctx.Registers.Allocate(core.Temp)
ec.ctx.Emitter.EmitLoadGlobal(reg, op) ec.ctx.Emitter.EmitLoadGlobal(reg, op)
return reg return reg
@@ -388,7 +389,7 @@ func (ec *ExprCompiler) CompileFunctionCall(ctx fql.IFunctionCallContext, protec
switch name { switch name {
case runtimeLength: case runtimeLength:
dst := ec.ctx.Registers.Allocate(Temp) dst := ec.ctx.Registers.Allocate(core.Temp)
if seq == nil || len(seq) != 1 { if seq == nil || len(seq) != 1 {
panic(runtime.Error(runtime.ErrInvalidArgument, runtimeLength+": expected 1 argument")) panic(runtime.Error(runtime.ErrInvalidArgument, runtimeLength+": expected 1 argument"))
@@ -398,7 +399,7 @@ func (ec *ExprCompiler) CompileFunctionCall(ctx fql.IFunctionCallContext, protec
return dst return dst
case runtimeTypename: case runtimeTypename:
dst := ec.ctx.Registers.Allocate(Temp) dst := ec.ctx.Registers.Allocate(core.Temp)
if seq == nil || len(seq) != 1 { if seq == nil || len(seq) != 1 {
panic(runtime.Error(runtime.ErrInvalidArgument, runtimeTypename+": expected 1 argument")) panic(runtime.Error(runtime.ErrInvalidArgument, runtimeTypename+": expected 1 argument"))
@@ -416,7 +417,7 @@ func (ec *ExprCompiler) CompileFunctionCall(ctx fql.IFunctionCallContext, protec
return seq[0] return seq[0]
default: default:
dest := ec.ctx.Registers.Allocate(Temp) dest := ec.ctx.Registers.Allocate(core.Temp)
ec.ctx.Emitter.EmitLoadConst(dest, ec.ctx.Symbols.AddConstant(name)) ec.ctx.Emitter.EmitLoadConst(dest, ec.ctx.Symbols.AddConstant(name))
if !protected { if !protected {
@@ -429,8 +430,8 @@ func (ec *ExprCompiler) CompileFunctionCall(ctx fql.IFunctionCallContext, protec
} }
} }
func (ec *ExprCompiler) CompileArgumentList(ctx fql.IArgumentListContext) RegisterSequence { func (ec *ExprCompiler) CompileArgumentList(ctx fql.IArgumentListContext) core.RegisterSequence {
var seq RegisterSequence var seq core.RegisterSequence
// Get all array element expressions // Get all array element expressions
exps := ctx.AllExpression() exps := ctx.AllExpression()
size := len(exps) size := len(exps)
@@ -458,7 +459,7 @@ func (ec *ExprCompiler) CompileArgumentList(ctx fql.IArgumentListContext) Regist
} }
func (ec *ExprCompiler) CompileRangeOperator(ctx fql.IRangeOperatorContext) vm.Operand { func (ec *ExprCompiler) CompileRangeOperator(ctx fql.IRangeOperatorContext) vm.Operand {
dst := ec.ctx.Registers.Allocate(Temp) dst := ec.ctx.Registers.Allocate(core.Temp)
start := ec.compileRangeOperand(ctx.GetLeft()) start := ec.compileRangeOperand(ctx.GetLeft())
end := ec.compileRangeOperand(ctx.GetRight()) end := ec.compileRangeOperand(ctx.GetRight())
@@ -480,7 +481,7 @@ func (ec *ExprCompiler) compileRangeOperand(ctx fql.IRangeOperandContext) vm.Ope
return ec.ctx.LiteralCompiler.CompileIntegerLiteral(c) return ec.ctx.LiteralCompiler.CompileIntegerLiteral(c)
} }
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, ctx.GetText()))
} }
func (ec *ExprCompiler) functionName(ctx fql.IFunctionCallContext) runtime.String { func (ec *ExprCompiler) functionName(ctx fql.IFunctionCallContext) runtime.String {

View File

@@ -1,6 +1,7 @@
package internal package internal
import ( import (
"github.com/MontFerret/ferret/pkg/compiler/internal/core"
"github.com/MontFerret/ferret/pkg/vm" "github.com/MontFerret/ferret/pkg/vm"
"github.com/antlr4-go/antlr/v4" "github.com/antlr4-go/antlr/v4"
"strings" "strings"
@@ -11,7 +12,7 @@ import (
) )
func loadConstant(ctx *FuncContext, value runtime.Value) vm.Operand { func loadConstant(ctx *FuncContext, value runtime.Value) vm.Operand {
reg := ctx.Registers.Allocate(Temp) reg := ctx.Registers.Allocate(core.Temp)
ctx.Emitter.EmitLoadConst(reg, ctx.Symbols.AddConstant(value)) ctx.Emitter.EmitLoadConst(reg, ctx.Symbols.AddConstant(value))
return reg return reg
} }

View File

@@ -1,6 +1,7 @@
package internal package internal
import ( import (
"github.com/MontFerret/ferret/pkg/compiler/internal/core"
"github.com/MontFerret/ferret/pkg/parser/fql" "github.com/MontFerret/ferret/pkg/parser/fql"
"github.com/MontFerret/ferret/pkg/runtime" "github.com/MontFerret/ferret/pkg/runtime"
"github.com/MontFerret/ferret/pkg/vm" "github.com/MontFerret/ferret/pkg/vm"
@@ -36,7 +37,7 @@ func (lc *LiteralCompiler) Compile(ctx fql.ILiteralContext) vm.Operand {
return lc.CompileNoneLiteral(c) return lc.CompileNoneLiteral(c)
} }
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, ctx.GetText()))
} }
func (lc *LiteralCompiler) CompileStringLiteral(ctx fql.IStringLiteralContext) vm.Operand { func (lc *LiteralCompiler) CompileStringLiteral(ctx fql.IStringLiteralContext) vm.Operand {
@@ -109,7 +110,7 @@ func (lc *LiteralCompiler) CompileFloatLiteral(ctx fql.IFloatLiteralContext) vm.
} }
func (lc *LiteralCompiler) CompileBooleanLiteral(ctx fql.IBooleanLiteralContext) vm.Operand { func (lc *LiteralCompiler) CompileBooleanLiteral(ctx fql.IBooleanLiteralContext) vm.Operand {
reg := lc.ctx.Registers.Allocate(Temp) reg := lc.ctx.Registers.Allocate(core.Temp)
switch strings.ToLower(ctx.GetText()) { switch strings.ToLower(ctx.GetText()) {
case "true": case "true":
@@ -117,14 +118,14 @@ func (lc *LiteralCompiler) CompileBooleanLiteral(ctx fql.IBooleanLiteralContext)
case "false": case "false":
lc.ctx.Emitter.EmitBoolean(reg, false) lc.ctx.Emitter.EmitBoolean(reg, false)
default: default:
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, ctx.GetText()))
} }
return reg return reg
} }
func (lc *LiteralCompiler) CompileNoneLiteral(_ fql.INoneLiteralContext) vm.Operand { func (lc *LiteralCompiler) CompileNoneLiteral(_ fql.INoneLiteralContext) vm.Operand {
reg := lc.ctx.Registers.Allocate(Temp) reg := lc.ctx.Registers.Allocate(core.Temp)
lc.ctx.Emitter.EmitA(vm.OpLoadNone, reg) lc.ctx.Emitter.EmitA(vm.OpLoadNone, reg)
return reg return reg
@@ -132,7 +133,7 @@ func (lc *LiteralCompiler) CompileNoneLiteral(_ fql.INoneLiteralContext) vm.Oper
func (lc *LiteralCompiler) CompileArrayLiteral(ctx fql.IArrayLiteralContext) vm.Operand { func (lc *LiteralCompiler) CompileArrayLiteral(ctx fql.IArrayLiteralContext) vm.Operand {
// Allocate destination register for the array // Allocate destination register for the array
destReg := lc.ctx.Registers.Allocate(Temp) destReg := lc.ctx.Registers.Allocate(core.Temp)
if list := ctx.ArgumentList(); list != nil { if list := ctx.ArgumentList(); list != nil {
// Get all array element expressions // Get all array element expressions
@@ -174,7 +175,7 @@ func (lc *LiteralCompiler) CompileArrayLiteral(ctx fql.IArrayLiteralContext) vm.
} }
func (lc *LiteralCompiler) CompileObjectLiteral(ctx fql.IObjectLiteralContext) vm.Operand { func (lc *LiteralCompiler) CompileObjectLiteral(ctx fql.IObjectLiteralContext) vm.Operand {
dst := lc.ctx.Registers.Allocate(Temp) dst := lc.ctx.Registers.Allocate(core.Temp)
assignments := ctx.AllPropertyAssignment() assignments := ctx.AllPropertyAssignment()
size := len(assignments) size := len(assignments)
@@ -232,7 +233,7 @@ func (lc *LiteralCompiler) CompilePropertyName(ctx fql.IPropertyNameContext) vm.
} else if word := ctx.UnsafeReservedWord(); word != nil { } else if word := ctx.UnsafeReservedWord(); word != nil {
name = word.GetText() name = word.GetText()
} else { } else {
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, ctx.GetText()))
} }
return loadConstant(lc.ctx, runtime.NewString(name)) return loadConstant(lc.ctx, runtime.NewString(name))

View File

@@ -1,6 +1,7 @@
package internal package internal
import ( import (
"github.com/MontFerret/ferret/pkg/compiler/internal/core"
"github.com/MontFerret/ferret/pkg/parser/fql" "github.com/MontFerret/ferret/pkg/parser/fql"
"github.com/MontFerret/ferret/pkg/runtime" "github.com/MontFerret/ferret/pkg/runtime"
"github.com/MontFerret/ferret/pkg/vm" "github.com/MontFerret/ferret/pkg/vm"
@@ -18,23 +19,23 @@ func NewLoopCompiler(ctx *FuncContext) *LoopCompiler {
func (lc *LoopCompiler) Compile(ctx fql.IForExpressionContext) vm.Operand { func (lc *LoopCompiler) Compile(ctx fql.IForExpressionContext) vm.Operand {
var distinct bool var distinct bool
var returnRuleCtx antlr.RuleContext var returnRuleCtx antlr.RuleContext
var loopType LoopType var loopType core.LoopType
returnCtx := ctx.ForExpressionReturn() returnCtx := ctx.ForExpressionReturn()
if c := returnCtx.ReturnExpression(); c != nil { if c := returnCtx.ReturnExpression(); c != nil {
returnRuleCtx = c returnRuleCtx = c
distinct = c.Distinct() != nil distinct = c.Distinct() != nil
loopType = NormalLoop loopType = core.NormalLoop
} else if c := returnCtx.ForExpression(); c != nil { } else if c := returnCtx.ForExpression(); c != nil {
returnRuleCtx = c returnRuleCtx = c
loopType = PassThroughLoop loopType = core.PassThroughLoop
} }
loop := lc.ctx.Loops.NewLoop(loopType, ForLoop, distinct) loop := lc.ctx.Loops.NewLoop(loopType, core.ForLoop, distinct)
lc.ctx.Symbols.EnterScope() lc.ctx.Symbols.EnterScope()
lc.ctx.Loops.Push(loop) lc.ctx.Loops.Push(loop)
if loop.Kind == ForLoop { if loop.Kind == core.ForLoop {
loop.Src = lc.CompileForExpressionSource(ctx.ForExpressionSource()) loop.Src = lc.CompileForExpressionSource(ctx.ForExpressionSource())
if val := ctx.GetValueVariable(); val != nil { if val := ctx.GetValueVariable(); val != nil {
@@ -63,7 +64,7 @@ func (lc *LoopCompiler) Compile(ctx fql.IForExpressionContext) vm.Operand {
loop = lc.ctx.Loops.Current() loop = lc.ctx.Loops.Current()
// RETURN // RETURN
if loop.Type != PassThroughLoop { if loop.Type != core.PassThroughLoop {
c := returnRuleCtx.(*fql.ReturnExpressionContext) c := returnRuleCtx.(*fql.ReturnExpressionContext)
expReg := lc.ctx.ExprCompiler.Compile(c.Expression()) expReg := lc.ctx.ExprCompiler.Compile(c.Expression())
@@ -111,7 +112,7 @@ func (lc *LoopCompiler) CompileForExpressionSource(ctx fql.IForExpressionSourceC
return lc.ctx.LiteralCompiler.CompileObjectLiteral(c) return lc.ctx.LiteralCompiler.CompileObjectLiteral(c)
} }
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, ctx.GetText()))
} }
func (lc *LoopCompiler) CompileForExpressionStatement(ctx fql.IForExpressionStatementContext) { func (lc *LoopCompiler) CompileForExpressionStatement(ctx fql.IForExpressionStatementContext) {
@@ -168,17 +169,17 @@ func (lc *LoopCompiler) CompileLimitClauseValue(ctx fql.ILimitClauseValueContext
return lc.ctx.ExprCompiler.CompileFunctionCallExpression(c) return lc.ctx.ExprCompiler.CompileFunctionCallExpression(c)
} }
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, ctx.GetText()))
} }
func (lc *LoopCompiler) CompileLimit(src vm.Operand) { func (lc *LoopCompiler) CompileLimit(src vm.Operand) {
state := lc.ctx.Registers.Allocate(State) state := lc.ctx.Registers.Allocate(core.State)
lc.ctx.Emitter.EmitABx(vm.OpIterLimit, state, src, lc.ctx.Loops.Current().Jump) lc.ctx.Emitter.EmitABx(vm.OpIterLimit, state, src, lc.ctx.Loops.Current().Jump)
} }
func (lc *LoopCompiler) CompileOffset(src vm.Operand) { func (lc *LoopCompiler) CompileOffset(src vm.Operand) {
state := lc.ctx.Registers.Allocate(State) state := lc.ctx.Registers.Allocate(core.State)
lc.ctx.Emitter.EmitABx(vm.OpIterSkip, state, src, lc.ctx.Loops.Current().Jump) lc.ctx.Emitter.EmitABx(vm.OpIterSkip, state, src, lc.ctx.Loops.Current().Jump)
} }
@@ -194,7 +195,7 @@ func (lc *LoopCompiler) CompileSortClause(ctx fql.ISortClauseContext) {
// And wrap each loop element by a KeyValuePair // And wrap each loop element by a KeyValuePair
// Where a key is either a single value or a list of values // Where a key is either a single value or a list of values
// These KeyValuePairs are then added to the dataset // These KeyValuePairs are then added to the dataset
kvKeyReg := lc.ctx.Registers.Allocate(Temp) kvKeyReg := lc.ctx.Registers.Allocate(core.Temp)
clauses := ctx.AllSortClauseExpression() clauses := ctx.AllSortClauseExpression()
var directions []runtime.SortDirection var directions []runtime.SortDirection
isSortMany := len(clauses) > 1 isSortMany := len(clauses) > 1
@@ -214,7 +215,7 @@ func (lc *LoopCompiler) CompileSortClause(ctx fql.ISortClauseContext) {
// TODO: Free Registers // TODO: Free Registers
} }
arrReg := lc.ctx.Registers.Allocate(Temp) arrReg := lc.ctx.Registers.Allocate(core.Temp)
lc.ctx.Emitter.EmitAs(vm.OpList, arrReg, keyRegs) lc.ctx.Emitter.EmitAs(vm.OpList, arrReg, keyRegs)
lc.ctx.Emitter.EmitAB(vm.OpMove, kvKeyReg, arrReg) // TODO: Free Registers lc.ctx.Emitter.EmitAB(vm.OpMove, kvKeyReg, arrReg) // TODO: Free Registers
} else { } else {
@@ -229,7 +230,7 @@ func (lc *LoopCompiler) CompileSortClause(ctx fql.ISortClauseContext) {
kvValReg = loop.Value kvValReg = loop.Value
} else { } else {
// If so, we need to load it from the iterator // If so, we need to load it from the iterator
kvValReg = lc.ctx.Registers.Allocate(Temp) kvValReg = lc.ctx.Registers.Allocate(core.Temp)
loop.EmitValue(kvKeyReg, lc.ctx.Emitter) loop.EmitValue(kvKeyReg, lc.ctx.Emitter)
} }
@@ -258,18 +259,18 @@ func (lc *LoopCompiler) CompileCollectClause(ctx fql.ICollectClauseContext) {
} }
// emitIterValue emits an instruction to get the value from the iterator // emitIterValue emits an instruction to get the value from the iterator
func (lc *LoopCompiler) emitLoopBegin(loop *Loop) { func (lc *LoopCompiler) emitLoopBegin(loop *core.Loop) {
if loop.Allocate { if loop.Allocate {
lc.ctx.Emitter.EmitAb(vm.OpDataSet, loop.Result, loop.Distinct) lc.ctx.Emitter.EmitAb(vm.OpDataSet, loop.Result, loop.Distinct)
loop.ResultPos = lc.ctx.Emitter.Size() - 1 loop.ResultPos = lc.ctx.Emitter.Size() - 1
} }
loop.Iterator = lc.ctx.Registers.Allocate(State) loop.Iterator = lc.ctx.Registers.Allocate(core.State)
if loop.Kind == ForLoop { if loop.Kind == core.ForLoop {
lc.ctx.Emitter.EmitAB(vm.OpIter, loop.Iterator, loop.Src) lc.ctx.Emitter.EmitAB(vm.OpIter, loop.Iterator, loop.Src)
// jumpPlaceholder is a placeholder for the exit jump position // core.JumpPlaceholder is a placeholder for the exit jump position
loop.Jump = lc.ctx.Emitter.EmitJumpc(vm.OpIterNext, jumpPlaceholder, loop.Iterator) loop.Jump = lc.ctx.Emitter.EmitJumpc(vm.OpIterNext, core.JumpPlaceholder, loop.Iterator)
if loop.Value != vm.NoopOperand { if loop.Value != vm.NoopOperand {
lc.ctx.Emitter.EmitAB(vm.OpIterValue, loop.Value, loop.Iterator) lc.ctx.Emitter.EmitAB(vm.OpIterValue, loop.Value, loop.Iterator)
@@ -285,7 +286,7 @@ func (lc *LoopCompiler) emitLoopBegin(loop *Loop) {
} }
// emitPatchLoop replaces the source of the loop with a modified dataset // emitPatchLoop replaces the source of the loop with a modified dataset
func (lc *LoopCompiler) emitPatchLoop(loop *Loop) { func (lc *LoopCompiler) emitPatchLoop(loop *core.Loop) {
// Replace source with sorted array // Replace source with sorted array
lc.ctx.Emitter.EmitAB(vm.OpMove, loop.Src, loop.Result) lc.ctx.Emitter.EmitAB(vm.OpMove, loop.Src, loop.Result)
@@ -296,24 +297,24 @@ func (lc *LoopCompiler) emitPatchLoop(loop *Loop) {
lc.emitLoopBegin(loop) lc.emitLoopBegin(loop)
} }
func (lc *LoopCompiler) emitLoopEnd(loop *Loop) vm.Operand { func (lc *LoopCompiler) emitLoopEnd(loop *core.Loop) vm.Operand {
lc.ctx.Emitter.EmitJump(loop.Jump - loop.JumpOffset) lc.ctx.Emitter.EmitJump(loop.Jump - loop.JumpOffset)
// TODO: Do not allocate for pass-through Loops // TODO: Do not allocate for pass-through Loops
dst := lc.ctx.Registers.Allocate(Temp) dst := lc.ctx.Registers.Allocate(core.Temp)
if loop.Allocate { if loop.Allocate {
// TODO: Reuse the dsReg register // TODO: Reuse the dsReg register
lc.ctx.Emitter.EmitA(vm.OpClose, loop.Iterator) lc.ctx.Emitter.EmitA(vm.OpClose, loop.Iterator)
lc.ctx.Emitter.EmitAB(vm.OpMove, dst, loop.Result) lc.ctx.Emitter.EmitAB(vm.OpMove, dst, loop.Result)
if loop.Kind == ForLoop { if loop.Kind == core.ForLoop {
lc.ctx.Emitter.PatchJump(loop.Jump) lc.ctx.Emitter.PatchJump(loop.Jump)
} else { } else {
lc.ctx.Emitter.PatchJumpAB(loop.Jump) lc.ctx.Emitter.PatchJumpAB(loop.Jump)
} }
} else { } else {
if loop.Kind == ForLoop { if loop.Kind == core.ForLoop {
lc.ctx.Emitter.PatchJumpNext(loop.Jump) lc.ctx.Emitter.PatchJumpNext(loop.Jump)
} else { } else {
lc.ctx.Emitter.PatchJumpNextAB(loop.Jump) lc.ctx.Emitter.PatchJumpNextAB(loop.Jump)
@@ -323,14 +324,14 @@ func (lc *LoopCompiler) emitLoopEnd(loop *Loop) vm.Operand {
return dst return dst
} }
func (lc *LoopCompiler) loopKind(ctx *fql.ForExpressionContext) LoopKind { func (lc *LoopCompiler) loopKind(ctx *fql.ForExpressionContext) core.LoopKind {
if ctx.While() == nil { if ctx.While() == nil {
return ForLoop return core.ForLoop
} }
if ctx.Do() == nil { if ctx.Do() == nil {
return WhileLoop return core.WhileLoop
} }
return DoWhileLoop return core.DoWhileLoop
} }

View File

@@ -1,6 +1,7 @@
package internal package internal
import ( import (
"github.com/MontFerret/ferret/pkg/compiler/internal/core"
"github.com/MontFerret/ferret/pkg/parser/fql" "github.com/MontFerret/ferret/pkg/parser/fql"
"github.com/MontFerret/ferret/pkg/vm" "github.com/MontFerret/ferret/pkg/vm"
) )
@@ -19,6 +20,8 @@ func (sc *StmtCompiler) Compile(ctx fql.IBodyContext) {
for _, statement := range ctx.AllBodyStatement() { for _, statement := range ctx.AllBodyStatement() {
sc.CompileBodyStatement(statement) sc.CompileBodyStatement(statement)
} }
sc.CompileBodyExpression(ctx.BodyExpression())
} }
func (sc *StmtCompiler) CompileBodyStatement(ctx fql.IBodyStatementContext) { func (sc *StmtCompiler) CompileBodyStatement(ctx fql.IBodyStatementContext) {
@@ -54,7 +57,7 @@ func (sc *StmtCompiler) CompileBodyExpression(ctx fql.IBodyExpressionContext) {
} }
func (sc *StmtCompiler) CompileVariableDeclaration(ctx fql.IVariableDeclarationContext) vm.Operand { func (sc *StmtCompiler) CompileVariableDeclaration(ctx fql.IVariableDeclarationContext) vm.Operand {
name := ignorePseudoVariable name := core.IgnorePseudoVariable
if id := ctx.Identifier(); id != nil { if id := ctx.Identifier(); id != nil {
name = id.GetText() name = id.GetText()
@@ -64,12 +67,12 @@ func (sc *StmtCompiler) CompileVariableDeclaration(ctx fql.IVariableDeclarationC
src := sc.ctx.ExprCompiler.Compile(ctx.Expression()) src := sc.ctx.ExprCompiler.Compile(ctx.Expression())
if name != ignorePseudoVariable { if name != core.IgnorePseudoVariable {
var dest vm.Operand var dest vm.Operand
if src.IsConstant() { if src.IsConstant() {
dest = sc.ctx.Symbols.DeclareGlobal(name) dest = sc.ctx.Symbols.DeclareGlobal(name)
tmp := sc.ctx.Registers.Allocate(Temp) tmp := sc.ctx.Registers.Allocate(core.Temp)
sc.ctx.Emitter.EmitAB(vm.OpLoadConst, tmp, src) sc.ctx.Emitter.EmitAB(vm.OpLoadConst, tmp, src)
sc.ctx.Emitter.EmitAB(vm.OpStoreGlobal, dest, tmp) sc.ctx.Emitter.EmitAB(vm.OpStoreGlobal, dest, tmp)
} else if sc.ctx.Symbols.Scope() == 0 { } else if sc.ctx.Symbols.Scope() == 0 {

View File

@@ -1,6 +1,7 @@
package internal package internal
import ( import (
"github.com/MontFerret/ferret/pkg/compiler/internal/core"
"github.com/MontFerret/ferret/pkg/parser/fql" "github.com/MontFerret/ferret/pkg/parser/fql"
) )
@@ -10,20 +11,13 @@ type Visitor struct {
Err error Err error
Src string Src string
Emitter *Emitter Emitter *core.Emitter
Registers *RegisterAllocator Registers *core.RegisterAllocator
Symbols *SymbolTable Symbols *core.SymbolTable
Loops *LoopTable Loops *core.LoopTable
CatchTable *CatchStack CatchTable *core.CatchStack
} }
const (
jumpPlaceholder = -1
undefinedVariable = -1
ignorePseudoVariable = "_"
pseudoVariable = "CURRENT"
)
func NewVisitor(src string) *Visitor { func NewVisitor(src string) *Visitor {
v := new(Visitor) v := new(Visitor)
v.BaseFqlParserVisitor = new(fql.BaseFqlParserVisitor) v.BaseFqlParserVisitor = new(fql.BaseFqlParserVisitor)
@@ -44,13 +38,7 @@ func (v *Visitor) VisitProgram(ctx *fql.ProgramContext) interface{} {
v.VisitHead(head.(*fql.HeadContext)) v.VisitHead(head.(*fql.HeadContext))
} }
ctx.Body().Accept(v) v.ctx.StmtCompiler.Compile(ctx.Body())
return nil
}
func (v *Visitor) VisitBody(ctx *fql.BodyContext) interface{} {
v.ctx.StmtCompiler.Compile(ctx)
return nil return nil
} }

View File

@@ -1,6 +1,7 @@
package internal package internal
import ( import (
"github.com/MontFerret/ferret/pkg/compiler/internal/core"
"github.com/MontFerret/ferret/pkg/parser/fql" "github.com/MontFerret/ferret/pkg/parser/fql"
"github.com/MontFerret/ferret/pkg/runtime" "github.com/MontFerret/ferret/pkg/runtime"
"github.com/MontFerret/ferret/pkg/vm" "github.com/MontFerret/ferret/pkg/vm"
@@ -34,7 +35,7 @@ func (wc *WaitCompiler) Compile(ctx fql.IWaitForExpressionContext) vm.Operand {
timeoutReg = wc.CompileTimeoutClauseContext(timeout) timeoutReg = wc.CompileTimeoutClauseContext(timeout)
} }
streamReg := wc.ctx.Registers.Allocate(Temp) streamReg := wc.ctx.Registers.Allocate(core.Temp)
// We move the source object to the stream register in order to re-use it in OpStream // We move the source object to the stream register in order to re-use it in OpStream
wc.ctx.Emitter.EmitMove(streamReg, srcReg) wc.ctx.Emitter.EmitMove(streamReg, srcReg)
@@ -44,10 +45,10 @@ func (wc *WaitCompiler) Compile(ctx fql.IWaitForExpressionContext) vm.Operand {
var valReg vm.Operand var valReg vm.Operand
// Now we start iterating over the stream // Now we start iterating over the stream
jumpToNext := wc.ctx.Emitter.EmitJumpc(vm.OpIterNext, jumpPlaceholder, streamReg) jumpToNext := wc.ctx.Emitter.EmitJumpc(vm.OpIterNext, core.JumpPlaceholder, streamReg)
if filter := ctx.FilterClause(); filter != nil { if filter := ctx.FilterClause(); filter != nil {
valReg = wc.ctx.Symbols.DeclareLocal(pseudoVariable) valReg = wc.ctx.Symbols.DeclareLocal(core.PseudoVariable)
wc.ctx.Emitter.EmitAB(vm.OpIterValue, valReg, streamReg) wc.ctx.Emitter.EmitAB(vm.OpIterValue, valReg, streamReg)
wc.ctx.ExprCompiler.Compile(filter.Expression()) wc.ctx.ExprCompiler.Compile(filter.Expression())
@@ -86,7 +87,7 @@ func (wc *WaitCompiler) CompileWaitForEventName(ctx fql.IWaitForEventNameContext
return wc.ctx.ExprCompiler.CompileFunctionCallExpression(c) return wc.ctx.ExprCompiler.CompileFunctionCallExpression(c)
} }
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, ctx.GetText()))
} }
func (wc *WaitCompiler) CompileWaitForEventSource(ctx fql.IWaitForEventSourceContext) vm.Operand { func (wc *WaitCompiler) CompileWaitForEventSource(ctx fql.IWaitForEventSourceContext) vm.Operand {
@@ -102,7 +103,7 @@ func (wc *WaitCompiler) CompileWaitForEventSource(ctx fql.IWaitForEventSourceCon
return wc.ctx.ExprCompiler.CompileFunctionCallExpression(c) return wc.ctx.ExprCompiler.CompileFunctionCallExpression(c)
} }
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, ctx.GetText()))
} }
func (wc *WaitCompiler) CompileOptionsClause(ctx fql.IOptionsClauseContext) vm.Operand { func (wc *WaitCompiler) CompileOptionsClause(ctx fql.IOptionsClauseContext) vm.Operand {
@@ -110,7 +111,7 @@ func (wc *WaitCompiler) CompileOptionsClause(ctx fql.IOptionsClauseContext) vm.O
return wc.ctx.LiteralCompiler.CompileObjectLiteral(c) return wc.ctx.LiteralCompiler.CompileObjectLiteral(c)
} }
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, ctx.GetText()))
} }
func (wc *WaitCompiler) CompileTimeoutClauseContext(ctx fql.ITimeoutClauseContext) vm.Operand { func (wc *WaitCompiler) CompileTimeoutClauseContext(ctx fql.ITimeoutClauseContext) vm.Operand {
@@ -134,5 +135,5 @@ func (wc *WaitCompiler) CompileTimeoutClauseContext(ctx fql.ITimeoutClauseContex
return wc.ctx.ExprCompiler.CompileFunctionCall(c, false) return wc.ctx.ExprCompiler.CompileFunctionCall(c, false)
} }
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) panic(runtime.Error(core.ErrUnexpectedToken, ctx.GetText()))
} }

View File

@@ -1,30 +1,35 @@
package vm_test package vm_test
import ( import (
"github.com/MontFerret/ferret/pkg/runtime"
"github.com/MontFerret/ferret/pkg/vm"
. "github.com/MontFerret/ferret/test/integration/base" . "github.com/MontFerret/ferret/test/integration/base"
"testing" "testing"
) )
func TestRange(t *testing.T) { func TestRange(t *testing.T) {
RunUseCases(t, []UseCase{ RunUseCases(t, []UseCase{
CaseArray("RETURN 1..10", []any{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}), SkipCaseArray("RETURN 1..10", []any{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "Should return a range from 1 to 10"),
CaseArray("RETURN 10..1", []any{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}), SkipCaseArray("RETURN 10..1", []any{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, "Should return a range from 10 to 1"),
CaseArray( SkipCaseArray(
` `
LET start = 1 LET start = 1
LET end = 10 LET end = 10
RETURN start..end RETURN start..end
`, `,
[]any{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, []any{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
"Should be able to use variables in range",
), ),
//{ CaseArray(`
// ` LET start = @start
//LET start = @start LET end = @end
//LET end = @end RETURN start..end
//RETURN start..end `,
//`, []any{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
// []any{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "Should be able to use parameters in range",
// ShouldEqualJSON, ),
//}, }, vm.WithParams(map[string]runtime.Value{
}) "start": runtime.NewInt(1),
"end": runtime.NewInt(10),
}))
} }