1
0
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:
Tim Voronov
2025-06-11 08:02:05 -04:00
parent fb26157bf5
commit cf87b63720
17 changed files with 2085 additions and 1791 deletions

View File

@@ -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()

View 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
}

View 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)
//}
}

View File

@@ -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
}

View File

@@ -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{

View File

@@ -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)

View 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))
}

View File

@@ -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::".

View 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())
}

View 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
}

View 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
}

View File

@@ -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

View 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

View 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()))
}

View File

@@ -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

View File

@@ -516,6 +516,7 @@ loop:
}
}
// TODO: Change. Add 'returnReg' to the closure.
return vm.registers[NoopOperand], nil
}