Skip to content

Commit

Permalink
Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ziflex committed Nov 6, 2024
1 parent dff7f29 commit fb4d383
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 119 deletions.
100 changes: 50 additions & 50 deletions pkg/compiler/compiler_exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,51 +151,51 @@ func TestVariables(t *testing.T) {
},
})

//Convey("Should compile LET i = (FOR i WHILE COUNTER() < 5 RETURN i) RETURN i", t, func() {
// c := compiler.New()
// counter := -1
// c.RegisterFunction("COUNTER", func(ctx context.visitor, args ...core.Second) (core.Second, error) {
// counter++
//
// return values.NewInt(counter), nil
// })
//
// p, err := c.Compile(`
// LET i = (FOR i WHILE COUNTER() < 5 RETURN i)
// RETURN i
// `)
//
// So(err, ShouldBeNil)
// So(p, ShouldHaveSameTypeAs, &runtime.Program{})
//
// out, err := p.Run(context.Background())
//
// So(err, ShouldBeNil)
// So(string(out), ShouldEqual, "[0,1,2,3,4]")
//})
//
//Convey("Should compile LET i = (FOR i WHILE COUNTER() < 5 T::FAIL() RETURN i)? RETURN i == NONE", t, func() {
// c := compiler.New()
// counter := -1
// c.RegisterFunction("COUNTER", func(ctx context.visitor, args ...core.Second) (core.Second, error) {
// counter++
//
// return values.NewInt(counter), nil
// })
//
// p, err := c.Compile(`
// LET i = (FOR i WHILE COUNTER() < 5 T::FAIL() RETURN i)?
// RETURN i == NONE
// `)
//
// So(err, ShouldBeNil)
// So(p, ShouldHaveSameTypeAs, &runtime.Program{})
//
// out, err := p.Run(context.Background())
//
// So(err, ShouldBeNil)
// So(string(out), ShouldEqual, "true")
//})
Convey("Should compile LET i = (FOR i WHILE COUNTER() < 5 RETURN i) RETURN i", t, func() {
c := compiler.New()

p, err := c.Compile(`
LET i = (FOR i WHILE COUNTER() < 5 RETURN i)
RETURN i
`)

So(err, ShouldBeNil)
So(p, ShouldHaveSameTypeAs, &runtime.Program{})

counter := -1
out, err := Run(p, runtime.WithFunction("COUNTER", func(ctx context.Context, args ...core.Value) (core.Value, error) {
counter++

return values.NewInt(counter), nil
}))

So(err, ShouldBeNil)
So(string(out), ShouldEqual, "[0,1,2,3,4]")
})

Convey("Should compile LET i = (FOR i WHILE COUNTER() < 5 T::FAIL() RETURN i)? RETURN i == NONE", t, func() {
c := compiler.New()

p, err := c.Compile(`
LET i = (FOR i WHILE COUNTER() < 5 T::FAIL() RETURN i)?
RETURN length(i) == 0
`)

So(err, ShouldBeNil)
So(p, ShouldHaveSameTypeAs, &runtime.Program{})

counter := -1
out, err := Run(p, runtime.WithFunction("COUNTER", func(ctx context.Context, args ...core.Value) (core.Value, error) {
counter++

return values.NewInt(counter), nil
}), runtime.WithFunction("T::FAIL", func(ctx context.Context, args ...core.Value) (core.Value, error) {
return values.None, fmt.Errorf("test")
}))

So(err, ShouldBeNil)
So(string(out), ShouldEqual, "true")
})

Convey("Should not compile FOR foo IN foo", t, func() {
c := compiler.New()
Expand Down Expand Up @@ -1088,11 +1088,11 @@ func TestFor(t *testing.T) {
func TestForWhile(t *testing.T) {
var counter int64
RunUseCases(t, []UseCase{
//{
// "FOR i WHILE false RETURN i",
// []any{},
// ShouldEqualJSON,
//},
{
"FOR i WHILE false RETURN i",
[]any{},
ShouldEqualJSON,
},
{
"FOR i WHILE UNTIL(5) RETURN i",
[]any{0, 1, 2, 3, 4},
Expand Down
54 changes: 26 additions & 28 deletions pkg/compiler/visitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ func (v *visitor) VisitForExpression(ctx *fql.ForExpressionContext) interface{}
var passThrough bool
var distinct bool
var returnRuleCtx antlr.RuleContext
var jumpOffset int
// identify whether it's WHILE or FOR loop
isForLoop := ctx.While() == nil
var isWhileFn bool
returnCtx := ctx.ForExpressionReturn()

if c := returnCtx.ReturnExpression(); c != nil {
Expand Down Expand Up @@ -168,21 +168,14 @@ func (v *visitor) VisitForExpression(ctx *fql.ForExpressionContext) interface{}

// Create initial value for the loop counter
v.emitter.EmitA(runtime.OpWhileLoopInit, counterReg)
beforeExp := v.emitter.Size()
// Loop data source to iterate over
cond := srcExpr.Accept(v).(runtime.Operand)
jumpOffset = v.emitter.Size() - beforeExp

// jumpPlaceholder is a placeholder for the exit jump position
loop.Next = v.emitter.EmitJumpAB(runtime.OpWhileLoopNext, counterReg, cond, jumpPlaceholder)

// Fix jump for function calls
if predicate := srcExpr.Predicate(); predicate != nil {
if atom := predicate.ExpressionAtom(); atom != nil {
if fcExpr := atom.FunctionCallExpression(); fcExpr != nil {
isWhileFn = true
}
}
}

counterVar := ctx.GetCounterVariable().GetText()

// declare counter variable
Expand All @@ -207,17 +200,7 @@ func (v *visitor) VisitForExpression(ctx *fql.ForExpressionContext) interface{}
returnRuleCtx.Accept(v)
}

if isForLoop {
v.emitter.EmitJump(runtime.OpJump, loop.Next)
} else {

if !isWhileFn {
v.emitter.EmitJump(runtime.OpJump, loop.Next-1)
} else {
v.emitter.EmitJump(runtime.OpJump, loop.Next-2)
}

}
v.emitter.EmitJump(runtime.OpJump, loop.Next-jumpOffset)

// TODO: Do not allocate for pass-through loops
dst := v.registers.Allocate(Temp)
Expand Down Expand Up @@ -976,15 +959,30 @@ func (v *visitor) visitFunctionCall(ctx *fql.FunctionCallContext, safeCall bool)
}
}

nameAndDest := v.loadConstant(v.functionName(ctx))
name := v.functionName(ctx)

if !safeCall {
v.emitter.EmitAs(runtime.OpCall, nameAndDest, seq)
} else {
v.emitter.EmitAs(runtime.OpCallSafe, nameAndDest, seq)
}
switch name {
case "LENGTH":
dst := v.registers.Allocate(Temp)

if seq == nil || len(seq.Registers) > 1 {
panic(core.Error(core.ErrInvalidArgument, "LENGTH: expected 1 argument"))
}

v.emitter.EmitAB(runtime.OpLength, dst, seq.Registers[0])

return dst
default:
nameAndDest := v.loadConstant(v.functionName(ctx))

if !safeCall {
v.emitter.EmitAs(runtime.OpCall, nameAndDest, seq)
} else {
v.emitter.EmitAs(runtime.OpCallSafe, nameAndDest, seq)
}

return nameAndDest
return nameAndDest
}
}

func (v *visitor) functionName(ctx *fql.FunctionCallContext) values.String {
Expand Down
2 changes: 2 additions & 0 deletions pkg/runtime/opcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ const (
OpLoadProperty
OpLoadPropertyOptional

OpLength

OpCall
OpCallSafe

Expand Down
16 changes: 16 additions & 0 deletions pkg/runtime/values/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -585,3 +585,19 @@ func ToNumberOnly(input core.Value) core.Value {
func CompareStrings(a, b String) Int {
return Int(strings.Compare(a.String(), b.String()))
}

func Length(value core.Value) (Int, error) {
c, ok := value.(core.Measurable)

if !ok {
return 0, core.TypeError(value,
types.String,
types.Array,
types.Object,
types.Binary,
types.Measurable,
)
}

return Int(c.Length()), nil
}
18 changes: 18 additions & 0 deletions pkg/runtime/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func NewVM(program *Program) *VM {
}

func (vm *VM) Run(ctx context.Context, opts []EnvironmentOption) (core.Value, error) {
// TODO: Return jump position if an error occurred within a wrapped loop
tryCatch := func(pos int) bool {
for _, pair := range vm.program.CatchTable {
if pos >= pair[0] && pos <= pair[1] {
Expand All @@ -43,6 +44,7 @@ func (vm *VM) Run(ctx context.Context, opts []EnvironmentOption) (core.Value, er
vm.pc = 0
program := vm.program

// TODO: Add panic handling and snapshot the last instruction and frame that caused it
loop:
for vm.pc < len(program.Bytecode) {
inst := program.Bytecode[vm.pc]
Expand Down Expand Up @@ -264,6 +266,22 @@ loop:
} else {
return nil, err
}
case OpLength:
val, ok := reg[src1].(core.Measurable)

if ok {
reg[dst] = values.NewInt(val.Length())
} else if tryCatch(vm.pc) {
reg[dst] = values.ZeroInt
} else {
return values.None, core.TypeError(reg[src1],
types.String,
types.Array,
types.Object,
types.Binary,
types.Measurable,
)
}
case OpRange:
res, err := operators.Range(reg[src1], reg[src2])

Expand Down
36 changes: 0 additions & 36 deletions pkg/stdlib/collections/length.go

This file was deleted.

1 change: 0 additions & 1 deletion pkg/stdlib/collections/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ func RegisterLib(ns core.Namespace) error {
return ns.RegisterFunctions(
core.NewFunctionsFromMap(map[string]core.Function{
"INCLUDES": Includes,
"LENGTH": Length,
"REVERSE": Reverse,
}))
}
3 changes: 1 addition & 2 deletions pkg/stdlib/testing/empty.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (

"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/collections"
"github.com/MontFerret/ferret/pkg/stdlib/testing/base"
)

Expand All @@ -19,7 +18,7 @@ var Empty = base.Assertion{
MinArgs: 1,
MaxArgs: 2,
Fn: func(ctx context.Context, args []core.Value) (bool, error) {
size, err := collections.Length(ctx, args[0])
size, err := values.Length(args[0])

if err != nil {
return false, err
Expand Down
3 changes: 1 addition & 2 deletions pkg/stdlib/testing/len.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"github.com/MontFerret/ferret/pkg/runtime/values"

"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/stdlib/collections"
"github.com/MontFerret/ferret/pkg/stdlib/testing/base"
)

Expand All @@ -25,7 +24,7 @@ var Len = base.Assertion{
col := args[0]
size := args[1]

out, err := collections.Length(ctx, col)
out, err := values.Length(col)

if err != nil {
return false, err
Expand Down

0 comments on commit fb4d383

Please sign in to comment.