mirror of
https://github.com/MontFerret/ferret.git
synced 2025-08-13 19:52:52 +02:00
Refactor compiler components; introduce CatchStack and enhance FuncContext for improved state management
This commit is contained in:
@@ -75,7 +75,7 @@ func (c *Compiler) Compile(query string) (program *vm.Program, err error) {
|
||||
program = &vm.Program{}
|
||||
program.Bytecode = l.Emitter.Bytecode()
|
||||
program.Constants = l.Symbols.Constants()
|
||||
program.CatchTable = l.CatchTable
|
||||
program.CatchTable = l.CatchTable.All()
|
||||
program.Registers = l.Registers.Size()
|
||||
program.Params = l.Symbols.Params()
|
||||
|
||||
|
47
pkg/compiler/internal/catch_stack.go
Normal file
47
pkg/compiler/internal/catch_stack.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
)
|
||||
|
||||
type CatchStack struct {
|
||||
entries []vm.Catch
|
||||
}
|
||||
|
||||
func NewCatchStack() *CatchStack {
|
||||
return &CatchStack{
|
||||
entries: make([]vm.Catch, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *CatchStack) Push(start, end, jump int) {
|
||||
cs.entries = append(cs.entries, vm.Catch{start, end, jump})
|
||||
}
|
||||
|
||||
func (cs *CatchStack) Pop() {
|
||||
if len(cs.entries) > 0 {
|
||||
cs.entries = cs.entries[:len(cs.entries)-1]
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *CatchStack) Find(pos int) (vm.Catch, bool) {
|
||||
for _, c := range cs.entries {
|
||||
if pos >= c[0] && pos <= c[1] {
|
||||
return c, true
|
||||
}
|
||||
}
|
||||
|
||||
return vm.Catch{}, false
|
||||
}
|
||||
|
||||
func (cs *CatchStack) Clear() {
|
||||
cs.entries = cs.entries[:0]
|
||||
}
|
||||
|
||||
func (cs *CatchStack) Len() int {
|
||||
return len(cs.entries)
|
||||
}
|
||||
|
||||
func (cs *CatchStack) All() []vm.Catch {
|
||||
return cs.entries
|
||||
}
|
56
pkg/compiler/internal/collect_compiler.go
Normal file
56
pkg/compiler/internal/collect_compiler.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/parser/fql"
|
||||
)
|
||||
|
||||
type CollectCompiler struct {
|
||||
Ctx *FuncContext
|
||||
}
|
||||
|
||||
func NewCollectCompiler(ctx *FuncContext) *CollectCompiler {
|
||||
return &CollectCompiler{Ctx: ctx}
|
||||
}
|
||||
|
||||
func (cc *CollectCompiler) Compile(ctx fql.ICollectClauseContext) {
|
||||
//loop := cc.Ctx.Loops.Current()
|
||||
//if loop == nil {
|
||||
// panic("COLLECT clause must appear inside a loop")
|
||||
//}
|
||||
//
|
||||
//// Grouping by key
|
||||
//if group := ctx.CollectGrouping(); group != nil {
|
||||
// // Example: COLLECT key = expr
|
||||
// keyName := group.Variable().GetText()
|
||||
// keyExpr := group.Expression()
|
||||
// keyReg := cc.Ctx.ExprCompiler.Compile(keyExpr)
|
||||
//
|
||||
// loop.Result = cc.Ctx.Registers.Allocate(Result)
|
||||
//
|
||||
// cc.Ctx.Emitter.EmitABC(vm.OpCollect, loop.Result, keyReg, keyReg) // src1=key, src2=key (single-group)
|
||||
// cc.Ctx.Symbols.DeclareLocal(keyName)
|
||||
//}
|
||||
//
|
||||
//// Aggregation
|
||||
//if agg := ctx.CollectAggregator(); agg != nil {
|
||||
// for _, part := range agg.AllCollectGroupVariable() {
|
||||
// name := part.Variable().GetText()
|
||||
// expr := part.Expression()
|
||||
//
|
||||
// src := cc.Ctx.ExprCompiler.Compile(expr)
|
||||
// dst := cc.Ctx.Registers.Allocate(Result)
|
||||
//
|
||||
// cc.Ctx.Emitter.EmitABC(vm.OpCollect, dst, src, src)
|
||||
// cc.Ctx.Symbols.DeclareLocal(name)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// Optional counter
|
||||
//if counter := ctx.CollectCounter(); counter != nil {
|
||||
// name := counter.Variable().GetText()
|
||||
// dst := cc.Ctx.Registers.Allocate(Result)
|
||||
//
|
||||
// cc.Ctx.Emitter.EmitAB(vm.OpCount, dst, loop.Value)
|
||||
// cc.Ctx.Symbols.DeclareLocal(name)
|
||||
//}
|
||||
}
|
@@ -1,22 +1,39 @@
|
||||
package internal
|
||||
|
||||
import "github.com/MontFerret/ferret/pkg/vm"
|
||||
|
||||
// FuncContext encapsulates the context and state required for compiling and managing functions during code processing.
|
||||
type FuncContext struct {
|
||||
Emitter *Emitter
|
||||
Registers *RegisterAllocator
|
||||
Symbols *SymbolTable
|
||||
Loops *LoopTable
|
||||
CatchTable []vm.Catch
|
||||
CatchTable *CatchStack
|
||||
|
||||
ExprCompiler *ExprCompiler
|
||||
LiteralCompiler *LiteralCompiler
|
||||
StmtCompiler *StmtCompiler
|
||||
LoopCompiler *LoopCompiler
|
||||
CollectCompiler *CollectCompiler
|
||||
WaitCompiler *WaitCompiler
|
||||
}
|
||||
|
||||
// NewFuncContext initializes and returns a new instance of FuncContext, setting up all required components for compilation.
|
||||
func NewFuncContext() *FuncContext {
|
||||
registers := NewRegisterAllocator()
|
||||
return &FuncContext{
|
||||
ctx := &FuncContext{
|
||||
Emitter: NewEmitter(),
|
||||
Registers: registers,
|
||||
Symbols: NewSymbolTable(registers),
|
||||
Loops: NewLoopTable(registers),
|
||||
CatchTable: make([]vm.Catch, 0),
|
||||
Registers: NewRegisterAllocator(),
|
||||
Symbols: nil, // set later
|
||||
Loops: nil, // set later
|
||||
CatchTable: NewCatchStack(),
|
||||
}
|
||||
ctx.Symbols = NewSymbolTable(ctx.Registers)
|
||||
ctx.Loops = NewLoopTable(ctx.Registers)
|
||||
|
||||
ctx.ExprCompiler = NewExprCompiler(ctx)
|
||||
ctx.LiteralCompiler = NewLiteralCompiler(ctx)
|
||||
ctx.StmtCompiler = NewStmtCompiler(ctx)
|
||||
ctx.LoopCompiler = NewLoopCompiler(ctx)
|
||||
ctx.CollectCompiler = NewCollectCompiler(ctx)
|
||||
ctx.WaitCompiler = NewWaitCompiler(ctx)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
@@ -23,27 +23,6 @@ func (e *Emitter) Size() int {
|
||||
return len(e.instructions)
|
||||
}
|
||||
|
||||
// EmitJump emits a jump opcode.
|
||||
func (e *Emitter) EmitJump(op vm.Opcode, pos int) int {
|
||||
e.EmitA(op, vm.Operand(pos))
|
||||
|
||||
return len(e.instructions) - 1
|
||||
}
|
||||
|
||||
// EmitJumpAB emits a jump opcode with a state and an argument.
|
||||
func (e *Emitter) EmitJumpAB(op vm.Opcode, state, cond vm.Operand, pos int) int {
|
||||
e.EmitABC(op, state, cond, vm.Operand(pos))
|
||||
|
||||
return len(e.instructions) - 1
|
||||
}
|
||||
|
||||
// EmitJumpc emits a conditional jump opcode.
|
||||
func (e *Emitter) EmitJumpc(op vm.Opcode, pos int, reg vm.Operand) int {
|
||||
e.EmitAB(op, vm.Operand(pos), reg)
|
||||
|
||||
return len(e.instructions) - 1
|
||||
}
|
||||
|
||||
// PatchSwapAB modifies an instruction at the given position to swap operands and update its operation and destination.
|
||||
func (e *Emitter) PatchSwapAB(pos int, op vm.Opcode, dst, src1 vm.Operand) {
|
||||
e.instructions[pos] = vm.Instruction{
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
)
|
||||
|
||||
@@ -51,16 +50,40 @@ func (e *Emitter) EmitClose(reg vm.Operand) {
|
||||
e.EmitA(vm.OpClose, reg)
|
||||
}
|
||||
|
||||
func (e *Emitter) EmitLoadConst(dst vm.Operand, val runtime.Value, symbols *SymbolTable) {
|
||||
e.EmitAB(vm.OpLoadConst, dst, symbols.AddConstant(val))
|
||||
func (e *Emitter) EmitLoadConst(dst vm.Operand, constant vm.Operand) {
|
||||
e.EmitAB(vm.OpLoadConst, dst, constant)
|
||||
}
|
||||
|
||||
func (e *Emitter) EmitLoadGlobal(dst, constant vm.Operand) {
|
||||
e.EmitAB(vm.OpLoadGlobal, dst, constant)
|
||||
}
|
||||
|
||||
func (e *Emitter) EmitLoadParam(constant vm.Operand) {
|
||||
e.EmitA(vm.OpLoadParam, constant)
|
||||
}
|
||||
|
||||
func (e *Emitter) EmitBoolean(dst vm.Operand, value bool) {
|
||||
if value {
|
||||
e.EmitAB(vm.OpLoadBool, dst, 1)
|
||||
} else {
|
||||
e.EmitAB(vm.OpLoadBool, dst, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Data Structures ──────────────────────────────────────────────────────
|
||||
|
||||
func (e *Emitter) EmitEmptyList(dst vm.Operand) {
|
||||
e.EmitA(vm.OpList, dst)
|
||||
}
|
||||
|
||||
func (e *Emitter) EmitList(dst vm.Operand, seq RegisterSequence) {
|
||||
e.EmitAs(vm.OpList, dst, seq)
|
||||
}
|
||||
|
||||
func (e *Emitter) EmitEmptyMap(dst vm.Operand) {
|
||||
e.EmitA(vm.OpMap, dst)
|
||||
}
|
||||
|
||||
func (e *Emitter) EmitMap(dst vm.Operand, seq RegisterSequence) {
|
||||
e.EmitAs(vm.OpMap, dst, seq)
|
||||
}
|
||||
@@ -129,6 +152,34 @@ func (e *Emitter) EmitLte(dst, a, b vm.Operand) {
|
||||
|
||||
// ─── Control Flow ────────────────────────────────────────────────────────
|
||||
|
||||
func (e *Emitter) EmitJump(pos int) int {
|
||||
e.EmitA(vm.OpJump, vm.Operand(pos))
|
||||
|
||||
return len(e.instructions) - 1
|
||||
}
|
||||
|
||||
// EmitJumpAB emits a jump opcode with a state and an argument.
|
||||
func (e *Emitter) EmitJumpAB(op vm.Opcode, state, cond vm.Operand, pos int) int {
|
||||
e.EmitABC(op, state, cond, vm.Operand(pos))
|
||||
|
||||
return len(e.instructions) - 1
|
||||
}
|
||||
|
||||
// EmitJumpc emits a conditional jump opcode.
|
||||
func (e *Emitter) EmitJumpc(op vm.Opcode, pos int, reg vm.Operand) int {
|
||||
e.EmitAB(op, vm.Operand(pos), reg)
|
||||
|
||||
return len(e.instructions) - 1
|
||||
}
|
||||
|
||||
func (e *Emitter) EmitJumpIfFalse(cond vm.Operand, jumpTarget int) int {
|
||||
return e.EmitJumpIf(cond, false, jumpTarget)
|
||||
}
|
||||
|
||||
func (e *Emitter) EmitJumpIfTrue(cond vm.Operand, jumpTarget int) int {
|
||||
return e.EmitJumpIf(cond, true, jumpTarget)
|
||||
}
|
||||
|
||||
func (e *Emitter) EmitJumpIf(cond vm.Operand, isTrue bool, jumpTarget int) int {
|
||||
if isTrue {
|
||||
return e.EmitJumpc(vm.OpJumpIfTrue, jumpTarget, cond)
|
||||
|
497
pkg/compiler/internal/expr_compiler.go
Normal file
497
pkg/compiler/internal/expr_compiler.go
Normal file
@@ -0,0 +1,497 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/parser/fql"
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Runtime functions
|
||||
const (
|
||||
runtimeTypename = "TYPENAME"
|
||||
runtimeLength = "LENGTH"
|
||||
runtimeWait = "WAIT"
|
||||
)
|
||||
|
||||
type ExprCompiler struct {
|
||||
ctx *FuncContext
|
||||
}
|
||||
|
||||
func NewExprCompiler(ctx *FuncContext) *ExprCompiler {
|
||||
return &ExprCompiler{ctx: ctx}
|
||||
}
|
||||
|
||||
func (ec *ExprCompiler) Compile(ctx fql.IExpressionContext) vm.Operand {
|
||||
if c := ctx.UnaryOperator(); c != nil {
|
||||
return ec.compileUnary(c, ctx)
|
||||
}
|
||||
|
||||
if c := ctx.LogicalAndOperator(); c != nil {
|
||||
return ec.compileLogicalAnd(ctx.Predicate())
|
||||
}
|
||||
|
||||
if c := ctx.LogicalOrOperator(); c != nil {
|
||||
return ec.compileLogicalOr(ctx.Predicate())
|
||||
}
|
||||
|
||||
if c := ctx.GetTernaryOperator(); c != nil {
|
||||
return ec.compileTernary(ctx)
|
||||
}
|
||||
|
||||
if c := ctx.Predicate(); c != nil {
|
||||
return ec.compilePredicate(c)
|
||||
}
|
||||
|
||||
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText()))
|
||||
}
|
||||
|
||||
// TODO: Free temporary registers if needed
|
||||
func (ec *ExprCompiler) compileUnary(ctx fql.IUnaryOperatorContext, parent fql.IExpressionContext) vm.Operand {
|
||||
src := ec.Compile(parent.GetRight())
|
||||
dst := ec.ctx.Registers.Allocate(Temp)
|
||||
|
||||
var op vm.Opcode
|
||||
|
||||
if ctx.Not() != nil {
|
||||
op = vm.OpNot
|
||||
} else if ctx.Minus() != nil {
|
||||
op = vm.OpFlipNegative
|
||||
} else if ctx.Plus() != nil {
|
||||
op = vm.OpFlipPositive
|
||||
} else {
|
||||
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText()))
|
||||
}
|
||||
|
||||
// We do not overwrite the source register
|
||||
ec.ctx.Emitter.EmitAB(op, dst, src)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// TODO: Free temporary registers if needed
|
||||
func (ec *ExprCompiler) compileLogicalAnd(ctx fql.IPredicateContext) vm.Operand {
|
||||
dst := ec.ctx.Registers.Allocate(Temp)
|
||||
|
||||
// Execute left expression
|
||||
left := ec.compilePredicate(ctx.GetLeft())
|
||||
|
||||
// Execute left expression
|
||||
ec.ctx.Emitter.EmitMove(dst, left)
|
||||
|
||||
// Test if left is false and jump to the end
|
||||
end := ec.ctx.Emitter.EmitJumpIfFalse(dst, jumpPlaceholder)
|
||||
|
||||
// If left is true, execute right expression
|
||||
right := ec.compilePredicate(ctx.GetRight())
|
||||
|
||||
// And move the result to the destination register
|
||||
ec.ctx.Emitter.EmitMove(dst, right)
|
||||
|
||||
ec.ctx.Emitter.PatchJumpNext(end)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// TODO: Free temporary registers if needed
|
||||
func (ec *ExprCompiler) compileLogicalOr(ctx fql.IPredicateContext) vm.Operand {
|
||||
dst := ec.ctx.Registers.Allocate(Temp)
|
||||
|
||||
// Execute left expression
|
||||
left := ec.compilePredicate(ctx.GetLeft())
|
||||
|
||||
// Execute left expression
|
||||
ec.ctx.Emitter.EmitMove(dst, left)
|
||||
|
||||
// Test if left is true and jump to the end
|
||||
end := ec.ctx.Emitter.EmitJumpIfTrue(dst, jumpPlaceholder)
|
||||
|
||||
// If left is false, execute right expression
|
||||
right := ec.compilePredicate(ctx.GetRight())
|
||||
|
||||
// And move the result to the destination register
|
||||
ec.ctx.Emitter.EmitMove(dst, right)
|
||||
|
||||
ec.ctx.Emitter.PatchJumpNext(end)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// TODO: Free temporary registers if needed
|
||||
func (ec *ExprCompiler) compileTernary(ctx fql.IExpressionContext) vm.Operand {
|
||||
dst := ec.ctx.Registers.Allocate(Temp)
|
||||
|
||||
// Compile condition and put result in dst
|
||||
condReg := ec.Compile(ctx.GetCondition())
|
||||
ec.ctx.Emitter.EmitMove(dst, condReg)
|
||||
|
||||
// Jump to 'false' branch if condition is false
|
||||
otherwise := ec.ctx.Emitter.EmitJumpIfFalse(dst, jumpPlaceholder)
|
||||
|
||||
// True branch
|
||||
if onTrue := ctx.GetOnTrue(); onTrue != nil {
|
||||
trueReg := ec.Compile(onTrue)
|
||||
// Move result of true branch to dst
|
||||
ec.ctx.Emitter.EmitMove(dst, trueReg)
|
||||
}
|
||||
|
||||
// Jump over false branch
|
||||
end := ec.ctx.Emitter.EmitJump(jumpPlaceholder)
|
||||
ec.ctx.Emitter.PatchJumpNext(otherwise)
|
||||
|
||||
// False branch
|
||||
if onFalse := ctx.GetOnFalse(); onFalse != nil {
|
||||
falseReg := ec.Compile(onFalse)
|
||||
// Move result of false branch to dst
|
||||
ec.ctx.Emitter.EmitMove(dst, falseReg)
|
||||
}
|
||||
|
||||
ec.ctx.Emitter.PatchJumpNext(end)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// TODO: Free temporary registers if needed
|
||||
func (ec *ExprCompiler) compilePredicate(ctx fql.IPredicateContext) vm.Operand {
|
||||
if c := ctx.ExpressionAtom(); c != nil {
|
||||
startCatch := ec.ctx.Emitter.Size()
|
||||
reg := ec.compileAtom(c)
|
||||
|
||||
if c.ErrorOperator() != nil {
|
||||
jump := -1
|
||||
endCatch := ec.ctx.Emitter.Size()
|
||||
|
||||
if c.ForExpression() != nil {
|
||||
// We jump back to finalize the loop before exiting
|
||||
jump = endCatch - 1
|
||||
}
|
||||
|
||||
ec.ctx.CatchTable.Push(startCatch, endCatch, jump)
|
||||
}
|
||||
|
||||
return reg
|
||||
}
|
||||
|
||||
var opcode vm.Opcode
|
||||
dest := ec.ctx.Registers.Allocate(Temp)
|
||||
left := ec.compilePredicate(ctx.Predicate(0))
|
||||
right := ec.compilePredicate(ctx.Predicate(1))
|
||||
|
||||
if op := ctx.EqualityOperator(); op != nil {
|
||||
switch ctx.GetText() {
|
||||
case "==":
|
||||
opcode = vm.OpEq
|
||||
case "!=":
|
||||
opcode = vm.OpNeq
|
||||
case ">":
|
||||
opcode = vm.OpGt
|
||||
case ">=":
|
||||
opcode = vm.OpGte
|
||||
case "<":
|
||||
opcode = vm.OpLt
|
||||
case "<=":
|
||||
opcode = vm.OpLte
|
||||
default:
|
||||
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText()))
|
||||
}
|
||||
} else if op := ctx.ArrayOperator(); op != nil {
|
||||
// TODO: Implement me
|
||||
panic(runtime.Error(runtime.ErrNotImplemented, "array operator"))
|
||||
} else if op := ctx.InOperator(); op != nil {
|
||||
if op.Not() == nil {
|
||||
opcode = vm.OpIn
|
||||
} else {
|
||||
opcode = vm.OpNotIn
|
||||
}
|
||||
} else if op := ctx.LikeOperator(); op != nil {
|
||||
if op.(*fql.LikeOperatorContext).Not() == nil {
|
||||
opcode = vm.OpLike
|
||||
} else {
|
||||
opcode = vm.OpNotLike
|
||||
}
|
||||
}
|
||||
|
||||
ec.ctx.Emitter.EmitABC(opcode, dest, left, right)
|
||||
|
||||
return dest
|
||||
}
|
||||
|
||||
// TODO: Free temporary registers if needed
|
||||
func (ec *ExprCompiler) compileAtom(ctx fql.IExpressionAtomContext) vm.Operand {
|
||||
var opcode vm.Opcode
|
||||
var isSet bool
|
||||
|
||||
if op := ctx.MultiplicativeOperator(); op != nil {
|
||||
isSet = true
|
||||
|
||||
switch op.GetText() {
|
||||
case "*":
|
||||
opcode = vm.OpMulti
|
||||
case "/":
|
||||
opcode = vm.OpDiv
|
||||
case "%":
|
||||
opcode = vm.OpMod
|
||||
default:
|
||||
panic(runtime.Error(ErrUnexpectedToken, op.GetText()))
|
||||
}
|
||||
} else if op := ctx.AdditiveOperator(); op != nil {
|
||||
isSet = true
|
||||
|
||||
switch op.GetText() {
|
||||
case "+":
|
||||
opcode = vm.OpAdd
|
||||
case "-":
|
||||
opcode = vm.OpSub
|
||||
default:
|
||||
panic(runtime.Error(ErrUnexpectedToken, op.GetText()))
|
||||
}
|
||||
|
||||
} else if op := ctx.RegexpOperator(); op != nil {
|
||||
isSet = true
|
||||
|
||||
switch op.GetText() {
|
||||
case "=~":
|
||||
opcode = vm.OpRegexpPositive
|
||||
case "!~":
|
||||
opcode = vm.OpRegexpNegative
|
||||
default:
|
||||
panic(runtime.Error(ErrUnexpectedToken, op.GetText()))
|
||||
}
|
||||
}
|
||||
|
||||
if isSet {
|
||||
regLeft := ec.compileAtom(ctx.ExpressionAtom(0))
|
||||
regRight := ec.compileAtom(ctx.ExpressionAtom(1))
|
||||
dst := ec.ctx.Registers.Allocate(Temp)
|
||||
|
||||
if opcode == vm.OpRegexpPositive || opcode == vm.OpRegexpNegative {
|
||||
if regRight.IsConstant() {
|
||||
val := ec.ctx.Symbols.Constant(regRight)
|
||||
|
||||
// Verify that the expression is a valid regular expression
|
||||
regexp.MustCompile(val.String())
|
||||
}
|
||||
}
|
||||
|
||||
ec.ctx.Emitter.EmitABC(opcode, dst, regLeft, regRight)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
if c := ctx.FunctionCallExpression(); c != nil {
|
||||
return ec.CompileFunctionCallExpression(c)
|
||||
} else if c := ctx.RangeOperator(); c != nil {
|
||||
return ec.CompileRangeOperator(c)
|
||||
} else if c := ctx.Literal(); c != nil {
|
||||
return ec.ctx.LiteralCompiler.Compile(c)
|
||||
} else if c := ctx.Variable(); c != nil {
|
||||
return ec.CompileVariable(c)
|
||||
} else if c := ctx.MemberExpression(); c != nil {
|
||||
return ec.CompileMemberExpression(c)
|
||||
} else if c := ctx.Param(); c != nil {
|
||||
return ec.CompileParam(c)
|
||||
} else if c := ctx.ForExpression(); c != nil {
|
||||
return ec.ctx.LoopCompiler.Compile(c)
|
||||
} else if c := ctx.WaitForExpression(); c != nil {
|
||||
return ec.ctx.WaitCompiler.Compile(c)
|
||||
} else if c := ctx.Expression(); c != nil {
|
||||
return ec.Compile(c)
|
||||
}
|
||||
|
||||
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText()))
|
||||
}
|
||||
|
||||
func (ec *ExprCompiler) CompileMemberExpression(ctx fql.IMemberExpressionContext) vm.Operand {
|
||||
mes := ctx.MemberExpressionSource()
|
||||
segments := ctx.AllMemberExpressionPath()
|
||||
|
||||
var src1 vm.Operand
|
||||
|
||||
if c := mes.Variable(); c != nil {
|
||||
src1 = ec.CompileVariable(c)
|
||||
} else if c := mes.Param(); c != nil {
|
||||
src1 = ec.CompileParam(c)
|
||||
} else if c := mes.ObjectLiteral(); c != nil {
|
||||
src1 = ec.ctx.LiteralCompiler.CompileObjectLiteral(c)
|
||||
} else if c := mes.ArrayLiteral(); c != nil {
|
||||
src1 = ec.ctx.LiteralCompiler.CompileArrayLiteral(c)
|
||||
} else if c := mes.FunctionCall(); c != nil {
|
||||
// FOO()?.bar
|
||||
segment := segments[0]
|
||||
src1 = ec.CompileFunctionCall(c, segment.ErrorOperator() != nil)
|
||||
}
|
||||
|
||||
var dst vm.Operand
|
||||
|
||||
for _, segment := range segments {
|
||||
var src2 vm.Operand
|
||||
p := segment.(*fql.MemberExpressionPathContext)
|
||||
|
||||
if c := p.PropertyName(); c != nil {
|
||||
src2 = ec.ctx.LiteralCompiler.CompilePropertyName(c)
|
||||
} else if c := p.ComputedPropertyName(); c != nil {
|
||||
src2 = ec.ctx.LiteralCompiler.CompileComputedPropertyName(c)
|
||||
}
|
||||
|
||||
dst = ec.ctx.Registers.Allocate(Temp)
|
||||
|
||||
// TODO: Replace with EmitLoadKey
|
||||
if p.ErrorOperator() != nil {
|
||||
ec.ctx.Emitter.EmitLoadPropertyOptional(dst, src1, src2)
|
||||
} else {
|
||||
ec.ctx.Emitter.EmitLoadProperty(dst, src1, src2)
|
||||
}
|
||||
|
||||
src1 = dst
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
func (ec *ExprCompiler) CompileVariable(ctx fql.IVariableContext) vm.Operand {
|
||||
// Just return the register / constant index
|
||||
op, _, found := ec.ctx.Symbols.Resolve(ctx.GetText())
|
||||
|
||||
if !found {
|
||||
panic(runtime.Error(ErrVariableNotFound, ctx.GetText()))
|
||||
}
|
||||
|
||||
if op.IsRegister() {
|
||||
return op
|
||||
}
|
||||
|
||||
reg := ec.ctx.Registers.Allocate(Temp)
|
||||
ec.ctx.Emitter.EmitLoadGlobal(reg, op)
|
||||
|
||||
return reg
|
||||
}
|
||||
|
||||
func (ec *ExprCompiler) CompileParam(ctx fql.IParamContext) vm.Operand {
|
||||
name := ctx.Identifier().GetText()
|
||||
reg := ec.ctx.Symbols.BindParam(name)
|
||||
ec.ctx.Emitter.EmitLoadParam(reg)
|
||||
|
||||
return reg
|
||||
}
|
||||
|
||||
func (ec *ExprCompiler) CompileFunctionCallExpression(ctx fql.IFunctionCallExpressionContext) vm.Operand {
|
||||
protected := ctx.ErrorOperator() != nil
|
||||
call := ctx.FunctionCall()
|
||||
|
||||
return ec.CompileFunctionCall(call, protected)
|
||||
}
|
||||
|
||||
func (ec *ExprCompiler) CompileFunctionCall(ctx fql.IFunctionCallContext, protected bool) vm.Operand {
|
||||
name := ec.functionName(ctx)
|
||||
seq := ec.CompileArgumentList(ctx.ArgumentList())
|
||||
|
||||
switch name {
|
||||
case runtimeLength:
|
||||
dst := ec.ctx.Registers.Allocate(Temp)
|
||||
|
||||
if seq == nil || len(seq) != 1 {
|
||||
panic(runtime.Error(runtime.ErrInvalidArgument, runtimeLength+": expected 1 argument"))
|
||||
}
|
||||
|
||||
ec.ctx.Emitter.EmitAB(vm.OpLength, dst, seq[0])
|
||||
|
||||
return dst
|
||||
case runtimeTypename:
|
||||
dst := ec.ctx.Registers.Allocate(Temp)
|
||||
|
||||
if seq == nil || len(seq) != 1 {
|
||||
panic(runtime.Error(runtime.ErrInvalidArgument, runtimeTypename+": expected 1 argument"))
|
||||
}
|
||||
|
||||
ec.ctx.Emitter.EmitAB(vm.OpType, dst, seq[0])
|
||||
|
||||
return dst
|
||||
case runtimeWait:
|
||||
if len(seq) != 1 {
|
||||
panic(runtime.Error(runtime.ErrInvalidArgument, runtimeWait+": expected 1 argument"))
|
||||
}
|
||||
|
||||
ec.ctx.Emitter.EmitA(vm.OpSleep, seq[0])
|
||||
|
||||
return seq[0]
|
||||
default:
|
||||
dest := ec.ctx.Registers.Allocate(Temp)
|
||||
ec.ctx.Emitter.EmitLoadConst(dest, ec.ctx.Symbols.AddConstant(name))
|
||||
|
||||
if !protected {
|
||||
ec.ctx.Emitter.EmitAs(vm.OpCall, dest, seq)
|
||||
} else {
|
||||
ec.ctx.Emitter.EmitAs(vm.OpProtectedCall, dest, seq)
|
||||
}
|
||||
|
||||
return dest
|
||||
}
|
||||
}
|
||||
|
||||
func (ec *ExprCompiler) CompileArgumentList(ctx fql.IArgumentListContext) RegisterSequence {
|
||||
var seq RegisterSequence
|
||||
// Get all array element expressions
|
||||
exps := ctx.AllExpression()
|
||||
size := len(exps)
|
||||
|
||||
if size > 0 {
|
||||
// Allocate seq for function arguments
|
||||
seq = ec.ctx.Registers.AllocateSequence(size)
|
||||
|
||||
// Evaluate each element into seq Registers
|
||||
for i, exp := range exps {
|
||||
// Compile expression and move to seq register
|
||||
srcReg := ec.Compile(exp)
|
||||
|
||||
// TODO: Figure out how to remove OpMove and use Registers returned from each expression
|
||||
ec.ctx.Emitter.EmitMove(seq[i], srcReg)
|
||||
|
||||
// Free source register if temporary
|
||||
if srcReg.IsRegister() {
|
||||
//ec.ctx.Registers.Free(srcReg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return seq
|
||||
}
|
||||
|
||||
func (ec *ExprCompiler) CompileRangeOperator(ctx fql.IRangeOperatorContext) vm.Operand {
|
||||
dst := ec.ctx.Registers.Allocate(Temp)
|
||||
start := ec.compileRangeOperand(ctx.GetLeft())
|
||||
end := ec.compileRangeOperand(ctx.GetRight())
|
||||
|
||||
ec.ctx.Emitter.EmitRange(dst, start, end)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
func (ec *ExprCompiler) compileRangeOperand(ctx fql.IRangeOperandContext) vm.Operand {
|
||||
if c := ctx.Variable(); c != nil {
|
||||
return ec.CompileVariable(c)
|
||||
}
|
||||
|
||||
if c := ctx.Param(); c != nil {
|
||||
return ec.CompileParam(c)
|
||||
}
|
||||
|
||||
if c := ctx.IntegerLiteral(); c != nil {
|
||||
return ec.ctx.LiteralCompiler.CompileIntegerLiteral(c)
|
||||
}
|
||||
|
||||
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText()))
|
||||
}
|
||||
|
||||
func (ec *ExprCompiler) functionName(ctx fql.IFunctionCallContext) runtime.String {
|
||||
var name string
|
||||
funcNS := ctx.Namespace()
|
||||
|
||||
if funcNS != nil {
|
||||
name += funcNS.GetText()
|
||||
}
|
||||
|
||||
name += ctx.FunctionName().GetText()
|
||||
|
||||
return runtime.NewString(strings.ToUpper(name))
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
"github.com/antlr4-go/antlr/v4"
|
||||
"strings"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
@@ -8,6 +10,24 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func loadConstant(ctx *FuncContext, value runtime.Value) vm.Operand {
|
||||
reg := ctx.Registers.Allocate(Temp)
|
||||
ctx.Emitter.EmitLoadConst(reg, ctx.Symbols.AddConstant(value))
|
||||
return reg
|
||||
}
|
||||
|
||||
func sortDirection(dir antlr.TerminalNode) runtime.SortDirection {
|
||||
if dir == nil {
|
||||
return runtime.SortDirectionAsc
|
||||
}
|
||||
|
||||
if strings.ToLower(dir.GetText()) == "desc" {
|
||||
return runtime.SortDirectionDesc
|
||||
}
|
||||
|
||||
return runtime.SortDirectionAsc
|
||||
}
|
||||
|
||||
func copyFromNamespace(fns *runtime.Functions, namespace string) error {
|
||||
// In the name of the function "A::B::C", the namespace is "A::B",
|
||||
// not "A::B::".
|
||||
|
243
pkg/compiler/internal/lit_compiler.go
Normal file
243
pkg/compiler/internal/lit_compiler.go
Normal file
@@ -0,0 +1,243 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"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"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type LiteralCompiler struct {
|
||||
ctx *FuncContext
|
||||
}
|
||||
|
||||
func NewLiteralCompiler(ctx *FuncContext) *LiteralCompiler {
|
||||
return &LiteralCompiler{
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func (lc *LiteralCompiler) Compile(ctx fql.ILiteralContext) vm.Operand {
|
||||
if c := ctx.StringLiteral(); c != nil {
|
||||
return lc.CompileStringLiteral(c)
|
||||
} else if c := ctx.IntegerLiteral(); c != nil {
|
||||
return lc.CompileIntegerLiteral(c)
|
||||
} else if c := ctx.FloatLiteral(); c != nil {
|
||||
return lc.CompileFloatLiteral(c)
|
||||
} else if c := ctx.BooleanLiteral(); c != nil {
|
||||
return lc.CompileBooleanLiteral(c)
|
||||
} else if c := ctx.ArrayLiteral(); c != nil {
|
||||
return lc.CompileArrayLiteral(c)
|
||||
} else if c := ctx.ObjectLiteral(); c != nil {
|
||||
return lc.CompileObjectLiteral(c)
|
||||
} else if c := ctx.NoneLiteral(); c != nil {
|
||||
return lc.CompileNoneLiteral(c)
|
||||
}
|
||||
|
||||
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText()))
|
||||
}
|
||||
|
||||
func (lc *LiteralCompiler) CompileStringLiteral(ctx fql.IStringLiteralContext) vm.Operand {
|
||||
var b strings.Builder
|
||||
|
||||
for _, child := range ctx.GetChildren() {
|
||||
tree := child.(antlr.TerminalNode)
|
||||
sym := tree.GetSymbol()
|
||||
input := sym.GetInputStream()
|
||||
|
||||
if input == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
size := input.Size()
|
||||
// skip quotes
|
||||
start := sym.GetStart() + 1
|
||||
stop := sym.GetStop() - 1
|
||||
|
||||
if stop >= size {
|
||||
stop = size - 1
|
||||
}
|
||||
|
||||
if start < size && stop < size {
|
||||
for i := start; i <= stop; i++ {
|
||||
c := input.GetText(i, i)
|
||||
|
||||
switch c {
|
||||
case "\\":
|
||||
c2 := input.GetText(i, i+1)
|
||||
|
||||
switch c2 {
|
||||
case "\\n":
|
||||
b.WriteString("\n")
|
||||
case "\\t":
|
||||
b.WriteString("\t")
|
||||
default:
|
||||
b.WriteString(c2)
|
||||
}
|
||||
|
||||
i++
|
||||
default:
|
||||
b.WriteString(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return loadConstant(lc.ctx, runtime.NewString(b.String()))
|
||||
}
|
||||
|
||||
func (lc *LiteralCompiler) CompileIntegerLiteral(ctx fql.IIntegerLiteralContext) vm.Operand {
|
||||
val, err := strconv.Atoi(ctx.GetText())
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return loadConstant(lc.ctx, runtime.NewInt(val))
|
||||
}
|
||||
|
||||
func (lc *LiteralCompiler) CompileFloatLiteral(ctx fql.IFloatLiteralContext) vm.Operand {
|
||||
val, err := strconv.ParseFloat(ctx.GetText(), 64)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return loadConstant(lc.ctx, runtime.NewFloat(val))
|
||||
}
|
||||
|
||||
func (lc *LiteralCompiler) CompileBooleanLiteral(ctx fql.IBooleanLiteralContext) vm.Operand {
|
||||
reg := lc.ctx.Registers.Allocate(Temp)
|
||||
|
||||
switch strings.ToLower(ctx.GetText()) {
|
||||
case "true":
|
||||
lc.ctx.Emitter.EmitBoolean(reg, true)
|
||||
case "false":
|
||||
lc.ctx.Emitter.EmitBoolean(reg, false)
|
||||
default:
|
||||
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText()))
|
||||
}
|
||||
|
||||
return reg
|
||||
}
|
||||
|
||||
func (lc *LiteralCompiler) CompileNoneLiteral(_ fql.INoneLiteralContext) vm.Operand {
|
||||
reg := lc.ctx.Registers.Allocate(Temp)
|
||||
lc.ctx.Emitter.EmitA(vm.OpLoadNone, reg)
|
||||
|
||||
return reg
|
||||
}
|
||||
|
||||
func (lc *LiteralCompiler) CompileArrayLiteral(ctx fql.IArrayLiteralContext) vm.Operand {
|
||||
// Allocate destination register for the array
|
||||
destReg := lc.ctx.Registers.Allocate(Temp)
|
||||
|
||||
if list := ctx.ArgumentList(); list != nil {
|
||||
// Get all array element expressions
|
||||
exps := list.(fql.IArgumentListContext).AllExpression()
|
||||
size := len(exps)
|
||||
|
||||
if size > 0 {
|
||||
// Allocate seq for array elements
|
||||
seq := lc.ctx.Registers.AllocateSequence(size)
|
||||
|
||||
// Evaluate each element into seq Registers
|
||||
for i, exp := range exps {
|
||||
// Compile expression and move to seq register
|
||||
srcReg := lc.ctx.ExprCompiler.Compile(exp)
|
||||
|
||||
// TODO: Figure out how to remove OpMove and use Registers returned from each expression
|
||||
lc.ctx.Emitter.EmitMove(seq[i], srcReg)
|
||||
|
||||
// Free source register if temporary
|
||||
if srcReg.IsRegister() {
|
||||
//lc.ctx.Registers.Free(srcReg)
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize an array
|
||||
lc.ctx.Emitter.EmitList(destReg, seq)
|
||||
|
||||
// Free seq Registers
|
||||
//lc.ctx.Registers.FreeSequence(seq)
|
||||
|
||||
return destReg
|
||||
}
|
||||
}
|
||||
|
||||
// Empty array
|
||||
lc.ctx.Emitter.EmitEmptyList(destReg)
|
||||
|
||||
return destReg
|
||||
}
|
||||
|
||||
func (lc *LiteralCompiler) CompileObjectLiteral(ctx fql.IObjectLiteralContext) vm.Operand {
|
||||
dst := lc.ctx.Registers.Allocate(Temp)
|
||||
assignments := ctx.AllPropertyAssignment()
|
||||
size := len(assignments)
|
||||
|
||||
if size == 0 {
|
||||
lc.ctx.Emitter.EmitEmptyMap(dst)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
seq := lc.ctx.Registers.AllocateSequence(len(assignments) * 2)
|
||||
|
||||
for i := 0; i < size; i++ {
|
||||
var propOp vm.Operand
|
||||
var valOp vm.Operand
|
||||
pac := assignments[i]
|
||||
|
||||
if prop := pac.PropertyName(); prop != nil {
|
||||
propOp = lc.CompilePropertyName(prop)
|
||||
valOp = lc.ctx.ExprCompiler.Compile(pac.Expression())
|
||||
} else if comProp := pac.ComputedPropertyName(); comProp != nil {
|
||||
propOp = lc.CompileComputedPropertyName(comProp)
|
||||
valOp = lc.ctx.ExprCompiler.Compile(pac.Expression())
|
||||
} else if variable := pac.Variable(); variable != nil {
|
||||
propOp = loadConstant(lc.ctx, runtime.NewString(variable.GetText()))
|
||||
valOp = lc.ctx.ExprCompiler.CompileVariable(variable)
|
||||
}
|
||||
|
||||
regIndex := i * 2
|
||||
|
||||
lc.ctx.Emitter.EmitMove(seq[regIndex], propOp)
|
||||
lc.ctx.Emitter.EmitMove(seq[regIndex+1], valOp)
|
||||
|
||||
// Free source register if temporary
|
||||
if propOp.IsRegister() {
|
||||
//lc.ctx.Registers.Free(propOp)
|
||||
}
|
||||
}
|
||||
|
||||
lc.ctx.Emitter.EmitMap(dst, seq)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
func (lc *LiteralCompiler) CompilePropertyName(ctx fql.IPropertyNameContext) vm.Operand {
|
||||
if str := ctx.StringLiteral(); str != nil {
|
||||
return lc.CompileStringLiteral(str)
|
||||
}
|
||||
|
||||
var name string
|
||||
|
||||
if id := ctx.Identifier(); id != nil {
|
||||
name = id.GetText()
|
||||
} else if word := ctx.SafeReservedWord(); word != nil {
|
||||
name = word.GetText()
|
||||
} else if word := ctx.UnsafeReservedWord(); word != nil {
|
||||
name = word.GetText()
|
||||
} else {
|
||||
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText()))
|
||||
}
|
||||
|
||||
return loadConstant(lc.ctx, runtime.NewString(name))
|
||||
}
|
||||
|
||||
func (lc *LiteralCompiler) CompileComputedPropertyName(ctx fql.IComputedPropertyNameContext) vm.Operand {
|
||||
return lc.ctx.ExprCompiler.Compile(ctx.Expression())
|
||||
}
|
80
pkg/compiler/internal/loop.go
Normal file
80
pkg/compiler/internal/loop.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package internal
|
||||
|
||||
import "github.com/MontFerret/ferret/pkg/vm"
|
||||
|
||||
type LoopType int
|
||||
|
||||
const (
|
||||
NormalLoop LoopType = iota
|
||||
PassThroughLoop
|
||||
TemporalLoop
|
||||
)
|
||||
|
||||
type LoopKind int
|
||||
|
||||
const (
|
||||
ForLoop LoopKind = iota
|
||||
WhileLoop
|
||||
DoWhileLoop
|
||||
)
|
||||
|
||||
type CollectorType int
|
||||
|
||||
const (
|
||||
CollectorTypeCounter CollectorType = iota
|
||||
CollectorTypeKey
|
||||
CollectorTypeKeyCounter
|
||||
CollectorTypeKeyGroup
|
||||
)
|
||||
|
||||
type Loop struct {
|
||||
Type LoopType
|
||||
Kind LoopKind
|
||||
Distinct bool
|
||||
Allocate bool
|
||||
Jump int
|
||||
JumpOffset int
|
||||
|
||||
Src vm.Operand
|
||||
Iterator vm.Operand
|
||||
|
||||
ValueName string
|
||||
Value vm.Operand
|
||||
KeyName string
|
||||
Key vm.Operand
|
||||
|
||||
Result vm.Operand
|
||||
ResultPos int
|
||||
}
|
||||
|
||||
func (l *Loop) DeclareKeyVar(name string, st *SymbolTable) {
|
||||
if l.canBindVar(name) {
|
||||
l.KeyName = name
|
||||
l.Key = st.DeclareLocal(name)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Loop) DeclareValueVar(name string, st *SymbolTable) {
|
||||
if l.canBindVar(name) {
|
||||
l.ValueName = name
|
||||
l.Value = st.DeclareLocal(name)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Loop) EmitValue(dst vm.Operand, emitter *Emitter) {
|
||||
emitter.EmitIterValue(dst, l.Iterator)
|
||||
}
|
||||
|
||||
func (l *Loop) EmitKey(dst vm.Operand, emitter *Emitter) {
|
||||
emitter.EmitIterKey(dst, l.Iterator)
|
||||
}
|
||||
|
||||
func (l *Loop) EmitFinalization(emitter *Emitter) {
|
||||
emitter.EmitJump(l.Jump - l.JumpOffset)
|
||||
emitter.EmitA(vm.OpClose, l.Iterator)
|
||||
emitter.PatchJump(l.Jump)
|
||||
}
|
||||
|
||||
func (l *Loop) canBindVar(name string) bool {
|
||||
return name != "" && name != ignorePseudoVariable
|
||||
}
|
336
pkg/compiler/internal/loop_compiler.go
Normal file
336
pkg/compiler/internal/loop_compiler.go
Normal file
@@ -0,0 +1,336 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"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 {
|
||||
ctx *FuncContext
|
||||
}
|
||||
|
||||
func NewLoopCompiler(ctx *FuncContext) *LoopCompiler {
|
||||
return &LoopCompiler{ctx: ctx}
|
||||
}
|
||||
|
||||
func (lc *LoopCompiler) Compile(ctx fql.IForExpressionContext) vm.Operand {
|
||||
var distinct bool
|
||||
var returnRuleCtx antlr.RuleContext
|
||||
var loopType LoopType
|
||||
returnCtx := ctx.ForExpressionReturn()
|
||||
|
||||
if c := returnCtx.ReturnExpression(); c != nil {
|
||||
returnRuleCtx = c
|
||||
distinct = c.Distinct() != nil
|
||||
loopType = NormalLoop
|
||||
} else if c := returnCtx.ForExpression(); c != nil {
|
||||
returnRuleCtx = c
|
||||
loopType = PassThroughLoop
|
||||
}
|
||||
|
||||
loop := lc.ctx.Loops.NewLoop(loopType, ForLoop, distinct)
|
||||
lc.ctx.Symbols.EnterScope()
|
||||
lc.ctx.Loops.Push(loop)
|
||||
|
||||
if loop.Kind == ForLoop {
|
||||
loop.Src = lc.CompileForExpressionSource(ctx.ForExpressionSource())
|
||||
|
||||
if val := ctx.GetValueVariable(); val != nil {
|
||||
loop.DeclareValueVar(val.GetText(), lc.ctx.Symbols)
|
||||
}
|
||||
|
||||
if ctr := ctx.GetCounterVariable(); ctr != nil {
|
||||
loop.DeclareKeyVar(ctr.GetText(), lc.ctx.Symbols)
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
lc.emitLoopBegin(loop)
|
||||
|
||||
// body
|
||||
if body := ctx.AllForExpressionBody(); body != nil && len(body) > 0 {
|
||||
for _, b := range body {
|
||||
if c := b.ForExpressionStatement(); c != nil {
|
||||
lc.CompileForExpressionStatement(c)
|
||||
} else if c := b.ForExpressionClause(); c != nil {
|
||||
lc.CompileForExpressionClause(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loop = lc.ctx.Loops.Current()
|
||||
|
||||
// RETURN
|
||||
if loop.Type != PassThroughLoop {
|
||||
c := returnRuleCtx.(*fql.ReturnExpressionContext)
|
||||
expReg := lc.ctx.ExprCompiler.Compile(c.Expression())
|
||||
|
||||
lc.ctx.Emitter.EmitAB(vm.OpPush, loop.Result, expReg)
|
||||
} else if returnRuleCtx != nil {
|
||||
if c, ok := returnRuleCtx.(*fql.ForExpressionContext); ok {
|
||||
lc.Compile(c)
|
||||
}
|
||||
}
|
||||
|
||||
res := lc.emitLoopEnd(loop)
|
||||
|
||||
lc.ctx.Symbols.ExitScope()
|
||||
lc.ctx.Loops.Pop()
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (lc *LoopCompiler) CompileForExpressionSource(ctx fql.IForExpressionSourceContext) vm.Operand {
|
||||
if c := ctx.FunctionCallExpression(); c != nil {
|
||||
return lc.ctx.ExprCompiler.CompileFunctionCallExpression(c)
|
||||
}
|
||||
|
||||
if c := ctx.MemberExpression(); c != nil {
|
||||
return lc.ctx.ExprCompiler.CompileMemberExpression(c)
|
||||
}
|
||||
|
||||
if c := ctx.Variable(); c != nil {
|
||||
return lc.ctx.ExprCompiler.CompileVariable(c)
|
||||
}
|
||||
|
||||
if c := ctx.Param(); c != nil {
|
||||
return lc.ctx.ExprCompiler.CompileParam(c)
|
||||
}
|
||||
|
||||
if c := ctx.RangeOperator(); c != nil {
|
||||
return lc.ctx.ExprCompiler.CompileRangeOperator(c)
|
||||
}
|
||||
|
||||
if c := ctx.ArrayLiteral(); c != nil {
|
||||
return lc.ctx.LiteralCompiler.CompileArrayLiteral(c)
|
||||
}
|
||||
|
||||
if c := ctx.ObjectLiteral(); c != nil {
|
||||
return lc.ctx.LiteralCompiler.CompileObjectLiteral(c)
|
||||
}
|
||||
|
||||
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText()))
|
||||
}
|
||||
|
||||
func (lc *LoopCompiler) CompileForExpressionStatement(ctx fql.IForExpressionStatementContext) {
|
||||
if c := ctx.VariableDeclaration(); c != nil {
|
||||
_ = lc.ctx.StmtCompiler.CompileVariableDeclaration(c)
|
||||
} else if c := ctx.FunctionCallExpression(); c != nil {
|
||||
_ = lc.ctx.ExprCompiler.CompileFunctionCallExpression(c)
|
||||
|
||||
// TODO: Free register if needed
|
||||
}
|
||||
}
|
||||
|
||||
func (lc *LoopCompiler) CompileForExpressionClause(ctx fql.IForExpressionClauseContext) {
|
||||
if c := ctx.LimitClause(); c != nil {
|
||||
lc.CompileLimitClause(c)
|
||||
} else if c := ctx.FilterClause(); c != nil {
|
||||
lc.CompileFilterClause(c)
|
||||
} else if c := ctx.SortClause(); c != nil {
|
||||
lc.CompileSortClause(c)
|
||||
} else if c := ctx.CollectClause(); c != nil {
|
||||
lc.CompileCollectClause(c)
|
||||
}
|
||||
}
|
||||
|
||||
func (lc *LoopCompiler) CompileLimitClause(ctx fql.ILimitClauseContext) {
|
||||
clauses := ctx.AllLimitClauseValue()
|
||||
|
||||
if len(clauses) == 1 {
|
||||
lc.CompileLimit(lc.CompileLimitClauseValue(clauses[0]))
|
||||
} else {
|
||||
lc.CompileOffset(lc.CompileLimitClauseValue(clauses[0]))
|
||||
lc.CompileLimit(lc.CompileLimitClauseValue(clauses[1]))
|
||||
}
|
||||
}
|
||||
|
||||
func (lc *LoopCompiler) CompileLimitClauseValue(ctx fql.ILimitClauseValueContext) vm.Operand {
|
||||
if c := ctx.Param(); c != nil {
|
||||
return lc.ctx.ExprCompiler.CompileParam(c)
|
||||
}
|
||||
|
||||
if c := ctx.IntegerLiteral(); c != nil {
|
||||
return lc.ctx.LiteralCompiler.CompileIntegerLiteral(c)
|
||||
}
|
||||
|
||||
if c := ctx.Variable(); c != nil {
|
||||
return lc.ctx.ExprCompiler.CompileVariable(c)
|
||||
}
|
||||
|
||||
if c := ctx.MemberExpression(); c != nil {
|
||||
return lc.ctx.ExprCompiler.CompileMemberExpression(c)
|
||||
}
|
||||
|
||||
if c := ctx.FunctionCallExpression(); c != nil {
|
||||
return lc.ctx.ExprCompiler.CompileFunctionCallExpression(c)
|
||||
}
|
||||
|
||||
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText()))
|
||||
|
||||
}
|
||||
|
||||
func (lc *LoopCompiler) CompileLimit(src vm.Operand) {
|
||||
state := lc.ctx.Registers.Allocate(State)
|
||||
lc.ctx.Emitter.EmitABx(vm.OpIterLimit, state, src, lc.ctx.Loops.Current().Jump)
|
||||
}
|
||||
|
||||
func (lc *LoopCompiler) CompileOffset(src vm.Operand) {
|
||||
state := lc.ctx.Registers.Allocate(State)
|
||||
lc.ctx.Emitter.EmitABx(vm.OpIterSkip, state, src, lc.ctx.Loops.Current().Jump)
|
||||
}
|
||||
|
||||
func (lc *LoopCompiler) CompileFilterClause(ctx fql.IFilterClauseContext) {
|
||||
src := lc.ctx.ExprCompiler.Compile(ctx.Expression())
|
||||
lc.ctx.Emitter.EmitJumpIfFalse(src, lc.ctx.Loops.Current().Jump)
|
||||
}
|
||||
|
||||
func (lc *LoopCompiler) CompileSortClause(ctx fql.ISortClauseContext) {
|
||||
loop := lc.ctx.Loops.Current()
|
||||
|
||||
// We collect the sorting conditions (keys
|
||||
// And wrap each loop element by a KeyValuePair
|
||||
// Where a key is either a single value or a list of values
|
||||
// These KeyValuePairs are then added to the dataset
|
||||
kvKeyReg := lc.ctx.Registers.Allocate(Temp)
|
||||
clauses := ctx.AllSortClauseExpression()
|
||||
var directions []runtime.SortDirection
|
||||
isSortMany := len(clauses) > 1
|
||||
|
||||
if isSortMany {
|
||||
clausesRegs := make([]vm.Operand, len(clauses))
|
||||
directions = make([]runtime.SortDirection, len(clauses))
|
||||
// We create a sequence of Registers for the clauses
|
||||
// To pack them into an array
|
||||
keyRegs := lc.ctx.Registers.AllocateSequence(len(clauses))
|
||||
|
||||
for i, clause := range clauses {
|
||||
clauseReg := lc.ctx.ExprCompiler.Compile(clause.Expression())
|
||||
lc.ctx.Emitter.EmitMove(keyRegs[i], clauseReg)
|
||||
clausesRegs[i] = keyRegs[i]
|
||||
directions[i] = sortDirection(clause.SortDirection())
|
||||
// TODO: Free Registers
|
||||
}
|
||||
|
||||
arrReg := lc.ctx.Registers.Allocate(Temp)
|
||||
lc.ctx.Emitter.EmitAs(vm.OpList, arrReg, keyRegs)
|
||||
lc.ctx.Emitter.EmitAB(vm.OpMove, kvKeyReg, arrReg) // TODO: Free Registers
|
||||
} else {
|
||||
clausesReg := lc.ctx.ExprCompiler.Compile(clauses[0].Expression())
|
||||
lc.ctx.Emitter.EmitAB(vm.OpMove, kvKeyReg, clausesReg)
|
||||
}
|
||||
|
||||
var kvValReg vm.Operand
|
||||
|
||||
// In case the value is not used in the loop body, and only key is used
|
||||
if loop.ValueName != "" {
|
||||
kvValReg = loop.Value
|
||||
} else {
|
||||
// If so, we need to load it from the iterator
|
||||
kvValReg = lc.ctx.Registers.Allocate(Temp)
|
||||
loop.EmitValue(kvKeyReg, lc.ctx.Emitter)
|
||||
}
|
||||
|
||||
if isSortMany {
|
||||
encoded := runtime.EncodeSortDirections(directions)
|
||||
count := len(clauses)
|
||||
|
||||
lc.ctx.Emitter.PatchSwapAxy(loop.ResultPos, vm.OpDataSetMultiSorter, loop.Result, encoded, count)
|
||||
} else {
|
||||
dir := sortDirection(clauses[0].SortDirection())
|
||||
lc.ctx.Emitter.PatchSwapAx(loop.ResultPos, vm.OpDataSetSorter, loop.Result, int(dir))
|
||||
}
|
||||
|
||||
lc.ctx.Emitter.EmitABC(vm.OpPushKV, loop.Result, kvKeyReg, kvValReg)
|
||||
loop.EmitFinalization(lc.ctx.Emitter)
|
||||
|
||||
// Replace source with the Sorter
|
||||
lc.ctx.Emitter.EmitAB(vm.OpMove, loop.Src, loop.Result)
|
||||
|
||||
// Create a new loop
|
||||
lc.emitLoopBegin(loop)
|
||||
}
|
||||
|
||||
func (lc *LoopCompiler) CompileCollectClause(ctx fql.ICollectClauseContext) {
|
||||
lc.ctx.CollectCompiler.Compile(ctx)
|
||||
}
|
||||
|
||||
// emitIterValue emits an instruction to get the value from the iterator
|
||||
func (lc *LoopCompiler) emitLoopBegin(loop *Loop) {
|
||||
if loop.Allocate {
|
||||
lc.ctx.Emitter.EmitAb(vm.OpDataSet, loop.Result, loop.Distinct)
|
||||
loop.ResultPos = lc.ctx.Emitter.Size() - 1
|
||||
}
|
||||
|
||||
loop.Iterator = lc.ctx.Registers.Allocate(State)
|
||||
|
||||
if loop.Kind == ForLoop {
|
||||
lc.ctx.Emitter.EmitAB(vm.OpIter, loop.Iterator, loop.Src)
|
||||
// jumpPlaceholder is a placeholder for the exit jump position
|
||||
loop.Jump = lc.ctx.Emitter.EmitJumpc(vm.OpIterNext, jumpPlaceholder, loop.Iterator)
|
||||
|
||||
if loop.Value != vm.NoopOperand {
|
||||
lc.ctx.Emitter.EmitAB(vm.OpIterValue, loop.Value, loop.Iterator)
|
||||
}
|
||||
|
||||
if loop.Key != vm.NoopOperand {
|
||||
lc.ctx.Emitter.EmitAB(vm.OpIterKey, loop.Key, loop.Iterator)
|
||||
}
|
||||
} else {
|
||||
//counterReg := lc.ctx.Registers.Allocate(Storage)
|
||||
// TODO: Set JumpOffset here
|
||||
}
|
||||
}
|
||||
|
||||
// emitPatchLoop replaces the source of the loop with a modified dataset
|
||||
func (lc *LoopCompiler) emitPatchLoop(loop *Loop) {
|
||||
// Replace source with sorted array
|
||||
lc.ctx.Emitter.EmitAB(vm.OpMove, loop.Src, loop.Result)
|
||||
|
||||
lc.ctx.Symbols.ExitScope()
|
||||
lc.ctx.Symbols.EnterScope()
|
||||
|
||||
// Create new for loop
|
||||
lc.emitLoopBegin(loop)
|
||||
}
|
||||
|
||||
func (lc *LoopCompiler) emitLoopEnd(loop *Loop) vm.Operand {
|
||||
lc.ctx.Emitter.EmitJump(loop.Jump - loop.JumpOffset)
|
||||
|
||||
// TODO: Do not allocate for pass-through Loops
|
||||
dst := lc.ctx.Registers.Allocate(Temp)
|
||||
|
||||
if loop.Allocate {
|
||||
// TODO: Reuse the dsReg register
|
||||
lc.ctx.Emitter.EmitA(vm.OpClose, loop.Iterator)
|
||||
lc.ctx.Emitter.EmitAB(vm.OpMove, dst, loop.Result)
|
||||
|
||||
if loop.Kind == ForLoop {
|
||||
lc.ctx.Emitter.PatchJump(loop.Jump)
|
||||
} else {
|
||||
lc.ctx.Emitter.PatchJumpAB(loop.Jump)
|
||||
}
|
||||
} else {
|
||||
if loop.Kind == ForLoop {
|
||||
lc.ctx.Emitter.PatchJumpNext(loop.Jump)
|
||||
} else {
|
||||
lc.ctx.Emitter.PatchJumpNextAB(loop.Jump)
|
||||
}
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
func (lc *LoopCompiler) loopKind(ctx *fql.ForExpressionContext) LoopKind {
|
||||
if ctx.While() == nil {
|
||||
return ForLoop
|
||||
}
|
||||
|
||||
if ctx.Do() == nil {
|
||||
return WhileLoop
|
||||
}
|
||||
|
||||
return DoWhileLoop
|
||||
}
|
@@ -7,61 +7,6 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
)
|
||||
|
||||
type LoopType int
|
||||
|
||||
const (
|
||||
NormalLoop LoopType = iota
|
||||
PassThroughLoop
|
||||
TemporalLoop
|
||||
)
|
||||
|
||||
type LoopKind int
|
||||
|
||||
const (
|
||||
ForLoop LoopKind = iota
|
||||
WhileLoop
|
||||
DoWhileLoop
|
||||
)
|
||||
|
||||
type CollectorType int
|
||||
|
||||
const (
|
||||
CollectorTypeCounter CollectorType = iota
|
||||
CollectorTypeKey
|
||||
CollectorTypeKeyCounter
|
||||
CollectorTypeKeyGroup
|
||||
)
|
||||
|
||||
type Loop struct {
|
||||
Type LoopType
|
||||
Kind LoopKind
|
||||
Distinct bool
|
||||
Allocate bool
|
||||
Jump int
|
||||
JumpOffset int
|
||||
|
||||
Src vm.Operand
|
||||
Iterator vm.Operand
|
||||
|
||||
ValueName string
|
||||
Value vm.Operand
|
||||
KeyName string
|
||||
Key vm.Operand
|
||||
|
||||
Result vm.Operand
|
||||
ResultPos int
|
||||
}
|
||||
|
||||
func (l *Loop) BindKeyVar(name string, st *SymbolTable) {
|
||||
l.KeyName = name
|
||||
l.Key = st.DeclareLocal(name)
|
||||
}
|
||||
|
||||
func (l *Loop) BindValueVar(name string, st *SymbolTable) {
|
||||
l.ValueName = name
|
||||
l.Value = st.DeclareLocal(name)
|
||||
}
|
||||
|
||||
type LoopTable struct {
|
||||
stack []*Loop
|
||||
registers *RegisterAllocator
|
||||
|
91
pkg/compiler/internal/stmt_compiler.go
Normal file
91
pkg/compiler/internal/stmt_compiler.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/parser/fql"
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
)
|
||||
|
||||
type StmtCompiler struct {
|
||||
ctx *FuncContext
|
||||
}
|
||||
|
||||
func NewStmtCompiler(ctx *FuncContext) *StmtCompiler {
|
||||
return &StmtCompiler{
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *StmtCompiler) Compile(ctx fql.IBodyContext) {
|
||||
for _, statement := range ctx.AllBodyStatement() {
|
||||
sc.CompileBodyStatement(statement)
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *StmtCompiler) CompileBodyStatement(ctx fql.IBodyStatementContext) {
|
||||
if c := ctx.VariableDeclaration(); c != nil {
|
||||
sc.CompileVariableDeclaration(c)
|
||||
} else if c := ctx.FunctionCallExpression(); c != nil {
|
||||
sc.CompileFunctionCall(c)
|
||||
} else if c := ctx.WaitForExpression(); c != nil {
|
||||
sc.ctx.WaitCompiler.Compile(c)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
} 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)
|
||||
}
|
||||
|
||||
sc.ctx.Emitter.Emit(vm.OpReturn)
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *StmtCompiler) CompileVariableDeclaration(ctx fql.IVariableDeclarationContext) vm.Operand {
|
||||
name := ignorePseudoVariable
|
||||
|
||||
if id := ctx.Identifier(); id != nil {
|
||||
name = id.GetText()
|
||||
} else if reserved := ctx.SafeReservedWord(); reserved != nil {
|
||||
name = reserved.GetText()
|
||||
}
|
||||
|
||||
src := sc.ctx.ExprCompiler.Compile(ctx.Expression())
|
||||
|
||||
if name != ignorePseudoVariable {
|
||||
var dest vm.Operand
|
||||
|
||||
if src.IsConstant() {
|
||||
dest = sc.ctx.Symbols.DeclareGlobal(name)
|
||||
tmp := sc.ctx.Registers.Allocate(Temp)
|
||||
sc.ctx.Emitter.EmitAB(vm.OpLoadConst, tmp, src)
|
||||
sc.ctx.Emitter.EmitAB(vm.OpStoreGlobal, dest, tmp)
|
||||
} else if sc.ctx.Symbols.Scope() == 0 {
|
||||
dest = sc.ctx.Symbols.DeclareGlobal(name)
|
||||
sc.ctx.Emitter.EmitAB(vm.OpStoreGlobal, dest, src)
|
||||
} else {
|
||||
dest = sc.ctx.Symbols.DeclareLocal(name)
|
||||
sc.ctx.Emitter.EmitAB(vm.OpMove, dest, src)
|
||||
}
|
||||
|
||||
return dest
|
||||
}
|
||||
|
||||
return vm.NoopOperand
|
||||
}
|
||||
|
||||
func (sc *StmtCompiler) CompileFunctionCall(ctx fql.IFunctionCallExpressionContext) vm.Operand {
|
||||
return sc.ctx.ExprCompiler.CompileFunctionCallExpression(ctx)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
138
pkg/compiler/internal/wait_compiler.go
Normal file
138
pkg/compiler/internal/wait_compiler.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/parser/fql"
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
)
|
||||
|
||||
type WaitCompiler struct {
|
||||
ctx *FuncContext
|
||||
}
|
||||
|
||||
func NewWaitCompiler(ctx *FuncContext) *WaitCompiler {
|
||||
return &WaitCompiler{
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func (wc *WaitCompiler) Compile(ctx fql.IWaitForExpressionContext) vm.Operand {
|
||||
wc.ctx.Symbols.EnterScope()
|
||||
|
||||
srcReg := wc.CompileWaitForEventSource(ctx.WaitForEventSource())
|
||||
eventReg := wc.CompileWaitForEventName(ctx.WaitForEventName())
|
||||
|
||||
var optsReg vm.Operand
|
||||
|
||||
if opts := ctx.OptionsClause(); opts != nil {
|
||||
optsReg = wc.CompileOptionsClause(opts)
|
||||
}
|
||||
|
||||
var timeoutReg vm.Operand
|
||||
|
||||
if timeout := ctx.TimeoutClause(); timeout != nil {
|
||||
timeoutReg = wc.CompileTimeoutClauseContext(timeout)
|
||||
}
|
||||
|
||||
streamReg := wc.ctx.Registers.Allocate(Temp)
|
||||
|
||||
// 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.EmitABC(vm.OpStream, streamReg, eventReg, optsReg)
|
||||
wc.ctx.Emitter.EmitAB(vm.OpStreamIter, streamReg, timeoutReg)
|
||||
|
||||
var valReg vm.Operand
|
||||
|
||||
// Now we start iterating over the stream
|
||||
jumpToNext := wc.ctx.Emitter.EmitJumpc(vm.OpIterNext, jumpPlaceholder, streamReg)
|
||||
|
||||
if filter := ctx.FilterClause(); filter != nil {
|
||||
valReg = wc.ctx.Symbols.DeclareLocal(pseudoVariable)
|
||||
wc.ctx.Emitter.EmitAB(vm.OpIterValue, valReg, streamReg)
|
||||
|
||||
wc.ctx.ExprCompiler.Compile(filter.Expression())
|
||||
|
||||
wc.ctx.Emitter.EmitJumpc(vm.OpJumpIfFalse, jumpToNext, valReg)
|
||||
|
||||
// TODO: Do we need to use timeout here too? We can really get stuck in the loop if no event satisfies the filter
|
||||
}
|
||||
|
||||
// Clean up the stream
|
||||
wc.ctx.Emitter.EmitA(vm.OpClose, streamReg)
|
||||
|
||||
wc.ctx.Symbols.ExitScope()
|
||||
|
||||
return vm.NoopOperand
|
||||
}
|
||||
|
||||
func (wc *WaitCompiler) CompileWaitForEventName(ctx fql.IWaitForEventNameContext) vm.Operand {
|
||||
if c := ctx.StringLiteral(); c != nil {
|
||||
return wc.ctx.LiteralCompiler.CompileStringLiteral(c)
|
||||
}
|
||||
|
||||
if c := ctx.Variable(); c != nil {
|
||||
return wc.ctx.ExprCompiler.CompileVariable(c)
|
||||
}
|
||||
|
||||
if c := ctx.Param(); c != nil {
|
||||
return wc.ctx.ExprCompiler.CompileParam(c)
|
||||
}
|
||||
|
||||
if c := ctx.MemberExpression(); c != nil {
|
||||
return wc.ctx.ExprCompiler.CompileMemberExpression(c)
|
||||
}
|
||||
|
||||
if c := ctx.FunctionCallExpression(); c != nil {
|
||||
return wc.ctx.ExprCompiler.CompileFunctionCallExpression(c)
|
||||
}
|
||||
|
||||
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText()))
|
||||
}
|
||||
|
||||
func (wc *WaitCompiler) CompileWaitForEventSource(ctx fql.IWaitForEventSourceContext) vm.Operand {
|
||||
if c := ctx.Variable(); c != nil {
|
||||
return wc.ctx.ExprCompiler.CompileVariable(c)
|
||||
}
|
||||
|
||||
if c := ctx.MemberExpression(); c != nil {
|
||||
return wc.ctx.ExprCompiler.CompileMemberExpression(c)
|
||||
}
|
||||
|
||||
if c := ctx.FunctionCallExpression(); c != nil {
|
||||
return wc.ctx.ExprCompiler.CompileFunctionCallExpression(c)
|
||||
}
|
||||
|
||||
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText()))
|
||||
}
|
||||
|
||||
func (wc *WaitCompiler) CompileOptionsClause(ctx fql.IOptionsClauseContext) vm.Operand {
|
||||
if c := ctx.ObjectLiteral(); c != nil {
|
||||
return wc.ctx.LiteralCompiler.CompileObjectLiteral(c)
|
||||
}
|
||||
|
||||
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText()))
|
||||
}
|
||||
|
||||
func (wc *WaitCompiler) CompileTimeoutClauseContext(ctx fql.ITimeoutClauseContext) vm.Operand {
|
||||
if c := ctx.IntegerLiteral(); c != nil {
|
||||
return wc.ctx.LiteralCompiler.CompileIntegerLiteral(c)
|
||||
}
|
||||
|
||||
if c := ctx.Variable(); c != nil {
|
||||
return wc.ctx.ExprCompiler.CompileVariable(c)
|
||||
}
|
||||
|
||||
if c := ctx.Param(); c != nil {
|
||||
return wc.ctx.ExprCompiler.CompileParam(c)
|
||||
}
|
||||
|
||||
if c := ctx.MemberExpression(); c != nil {
|
||||
return wc.ctx.ExprCompiler.CompileMemberExpression(c)
|
||||
}
|
||||
|
||||
if c := ctx.FunctionCall(); c != nil {
|
||||
return wc.ctx.ExprCompiler.CompileFunctionCall(c, false)
|
||||
}
|
||||
|
||||
panic(runtime.Error(ErrUnexpectedToken, ctx.GetText()))
|
||||
}
|
@@ -4,10 +4,9 @@ package fql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/antlr4-go/antlr/v4"
|
||||
"sync"
|
||||
"unicode"
|
||||
|
||||
"github.com/antlr4-go/antlr/v4"
|
||||
)
|
||||
|
||||
// Suppress unused import error
|
||||
|
@@ -516,6 +516,7 @@ loop:
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Change. Add 'returnReg' to the closure.
|
||||
return vm.registers[NoopOperand], nil
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user