1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-08-15 20:02:56 +02:00

Re-organized project layout

This commit is contained in:
Tim Voronov
2025-05-05 16:29:56 -04:00
parent 59c67a7f4c
commit a0158166c0
85 changed files with 1556 additions and 1655 deletions

View File

@@ -7,7 +7,7 @@ import (
"encoding/json"
"flag"
"fmt"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
"io"
"io/ioutil"
"os"
@@ -183,7 +183,7 @@ func (p *Params) ToMap() (map[string]interface{}, error) {
pair := strings.SplitN(entry, ":", 2)
if len(pair) < 2 {
return nil, core.Error(core.ErrInvalidArgument, entry)
return nil, runtime.Error(runtime.ErrInvalidArgument, entry)
}
var value interface{}
@@ -403,7 +403,7 @@ func execFiles(ctx context.Context, engine *ferret.Instance, opts []runtime_old.
logger.Debug().Errs("errors", errList).Msg("executed with errors")
}
return core.Errors(errList...)
return runtime.Errors(errList...)
}
return nil

View File

@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/MontFerret/ferret/pkg/runtime"
"os"
"strings"
@@ -27,26 +28,26 @@ func main() {
func getStrings() ([]string, error) {
// function implements is a type of a function that ferret supports as a runtime function
transform := func(ctx context.Context, args ...core.Value) (core.Value, error) {
transform := func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
// it's just a helper function which helps to validate a number of passed args
err := core.ValidateArgs(args, 1, 1)
err := runtime.ValidateArgs(args, 1, 1)
if err != nil {
// it's recommended to return built-in None type, instead of nil
return core.None, err
return runtime.None, err
}
// this is another helper functions allowing to do type validation
err = core.ValidateType(args[0], types.String)
if err != nil {
return core.None, err
return runtime.None, err
}
// cast to built-in string type
str := args[0].(core.String)
str := args[0].(runtime.String)
return core.NewString(strings.ToUpper(str.String() + "_ferret")), nil
return runtime.NewString(strings.ToUpper(str.String() + "_ferret")), nil
}
query := `

View File

@@ -2,11 +2,11 @@ package ferret
import (
"context"
"github.com/MontFerret/ferret/pkg/vm"
"github.com/MontFerret/ferret/pkg/compiler"
"github.com/MontFerret/ferret/pkg/drivers"
"github.com/MontFerret/ferret/pkg/runtime"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
type Instance struct {
@@ -23,7 +23,7 @@ func New(setters ...Option) *Instance {
}
}
func (i *Instance) Functions() core.Namespace {
func (i *Instance) Functions() runtime.Namespace {
return i.compiler
}
@@ -31,11 +31,11 @@ func (i *Instance) Drivers() *drivers.Container {
return i.drivers
}
func (i *Instance) Compile(query string) (*runtime.Program, error) {
func (i *Instance) Compile(query string) (*vm.Program, error) {
return i.compiler.Compile(query)
}
func (i *Instance) MustCompile(query string) *runtime.Program {
func (i *Instance) MustCompile(query string) *vm.Program {
return i.compiler.MustCompile(query)
}
@@ -61,9 +61,9 @@ func (i *Instance) MustExec(ctx context.Context, query string, opts ...runtime.O
return out
}
func (i *Instance) Run(ctx context.Context, program *runtime.Program, opts ...runtime.Option) ([]byte, error) {
func (i *Instance) Run(ctx context.Context, program *vm.Program, opts ...runtime.Option) ([]byte, error) {
if program == nil {
return nil, core.Error(core.ErrInvalidArgument, "program")
return nil, runtime.Error(runtime.ErrInvalidArgument, "program")
}
ctx = i.drivers.WithContext(ctx)
@@ -71,7 +71,7 @@ func (i *Instance) Run(ctx context.Context, program *runtime.Program, opts ...ru
return program.Run(ctx, opts...)
}
func (i *Instance) MustRun(ctx context.Context, program *runtime.Program, opts ...runtime.Option) []byte {
func (i *Instance) MustRun(ctx context.Context, program *vm.Program, opts ...runtime.Option) []byte {
out, err := i.Run(ctx, program, opts...)
if err != nil {

View File

@@ -1,6 +1,8 @@
package compiler
import "github.com/MontFerret/ferret/pkg/runtime"
import (
"github.com/MontFerret/ferret/pkg/vm"
)
type (
// RegisterType represents the type of a register
@@ -18,13 +20,13 @@ type (
}
RegisterSequence struct {
Registers []runtime.Operand
Registers []vm.Operand
}
// RegisterAllocator manages register allocation
RegisterAllocator struct {
registers map[runtime.Operand]*RegisterStatus
nextRegister runtime.Operand
registers map[vm.Operand]*RegisterStatus
nextRegister vm.Operand
currentInstr int
}
)
@@ -38,13 +40,13 @@ const (
func NewRegisterAllocator() *RegisterAllocator {
return &RegisterAllocator{
registers: make(map[runtime.Operand]*RegisterStatus),
nextRegister: runtime.NoopOperand + 1, // we start at 1 to avoid NoopOperand
registers: make(map[vm.Operand]*RegisterStatus),
nextRegister: vm.NoopOperand + 1, // we start at 1 to avoid NoopOperand
}
}
// Allocate assigns a register based on variable type
func (ra *RegisterAllocator) Allocate(regType RegisterType) runtime.Operand {
func (ra *RegisterAllocator) Allocate(regType RegisterType) vm.Operand {
// Try to find a free register first
reg, found := ra.findFreeRegister()
@@ -66,7 +68,7 @@ func (ra *RegisterAllocator) Allocate(regType RegisterType) runtime.Operand {
}
// Free marks a register as available
func (ra *RegisterAllocator) Free(reg runtime.Operand) {
func (ra *RegisterAllocator) Free(reg vm.Operand) {
//if status, exists := ra.registers[reg]; exists {
//status.IsAllocated = false
//status.Lifetime.End = ra.currentInstr
@@ -81,7 +83,7 @@ func (ra *RegisterAllocator) Free(reg runtime.Operand) {
}
// findFreeRegister looks for an unused register
func (ra *RegisterAllocator) findFreeRegister() (runtime.Operand, bool) {
func (ra *RegisterAllocator) findFreeRegister() (vm.Operand, bool) {
// First, try to find a completely free register
for reg, status := range ra.registers {
if !status.IsAllocated {
@@ -95,7 +97,7 @@ func (ra *RegisterAllocator) findFreeRegister() (runtime.Operand, bool) {
// AllocateSequence allocates a sequence of registers for function arguments or similar uses
func (ra *RegisterAllocator) AllocateSequence(count int) *RegisterSequence {
sequence := &RegisterSequence{
Registers: make([]runtime.Operand, count),
Registers: make([]vm.Operand, count),
}
// First pass: try to find contiguous free registers
@@ -104,7 +106,7 @@ func (ra *RegisterAllocator) AllocateSequence(count int) *RegisterSequence {
if found {
// Use contiguous block
for i := 0; i < count; i++ {
reg := startReg + runtime.Operand(i)
reg := startReg + vm.Operand(i)
sequence.Registers[i] = reg
// Initialize or update register status
@@ -125,14 +127,14 @@ func (ra *RegisterAllocator) AllocateSequence(count int) *RegisterSequence {
}
// findContiguousRegisters attempts to find a block of consecutive free registers
func (ra *RegisterAllocator) findContiguousRegisters(count int) (runtime.Operand, bool) {
func (ra *RegisterAllocator) findContiguousRegisters(count int) (vm.Operand, bool) {
if count <= 0 {
return 0, false
}
// First, try to find a contiguous block in existing registers
maxReg := ra.nextRegister
for start := runtime.NoopOperand + 1; start < maxReg; start++ {
for start := vm.NoopOperand + 1; start < maxReg; start++ {
if ra.isContiguousBlockFree(start, count) {
return start, true
}
@@ -140,15 +142,15 @@ func (ra *RegisterAllocator) findContiguousRegisters(count int) (runtime.Operand
// If no existing contiguous block found, allocate new block
startReg := ra.nextRegister
ra.nextRegister += runtime.Operand(count)
ra.nextRegister += vm.Operand(count)
return startReg, true
}
// isContiguousBlockFree checks if a block of registers is available
func (ra *RegisterAllocator) isContiguousBlockFree(start runtime.Operand, count int) bool {
func (ra *RegisterAllocator) isContiguousBlockFree(start vm.Operand, count int) bool {
for i := 0; i < count; i++ {
reg := start + runtime.Operand(i)
reg := start + vm.Operand(i)
if status := ra.registers[reg]; status != nil && status.IsAllocated {
return false

View File

@@ -2,11 +2,11 @@ package compiler
import (
"errors"
//"github.com/MontFerret/ferret/pkg/stdlib"
"github.com/MontFerret/ferret/pkg/vm"
goruntime "runtime"
"github.com/MontFerret/ferret/pkg/parser"
"github.com/MontFerret/ferret/pkg/runtime"
//"github.com/MontFerret/ferret/pkg/stdlib"
)
type Compiler struct {
@@ -32,7 +32,7 @@ func New(setters ...Option) *Compiler {
return c
}
func (c *Compiler) Compile(query string) (program *runtime.Program, err error) {
func (c *Compiler) Compile(query string) (program *vm.Program, err error) {
if query == "" {
return nil, ErrEmptyQuery
}
@@ -69,7 +69,7 @@ func (c *Compiler) Compile(query string) (program *runtime.Program, err error) {
return nil, l.err
}
program = &runtime.Program{}
program = &vm.Program{}
program.Bytecode = l.emitter.instructions
program.Constants = l.symbols.constants
program.CatchTable = l.catchTable
@@ -83,7 +83,7 @@ func (c *Compiler) Compile(query string) (program *runtime.Program, err error) {
return program, err
}
func (c *Compiler) MustCompile(query string) *runtime.Program {
func (c *Compiler) MustCompile(query string) *vm.Program {
program, err := c.Compile(query)
if err != nil {

View File

@@ -3,6 +3,8 @@ package compiler_test
import (
"context"
"fmt"
"github.com/MontFerret/ferret/pkg/runtime"
"github.com/MontFerret/ferret/pkg/vm"
"regexp"
"strconv"
"strings"
@@ -11,8 +13,6 @@ import (
"github.com/MontFerret/ferret/pkg/parser"
"github.com/MontFerret/ferret/pkg/compiler"
"github.com/MontFerret/ferret/pkg/runtime"
"github.com/MontFerret/ferret/pkg/runtime/core"
. "github.com/smartystreets/goconvey/convey"
)
@@ -144,7 +144,7 @@ func TestVariables(t *testing.T) {
`, true),
})
Convey("Should compile LET i = (FOR i WHILE COUNTER() < 5 RETURN i) RETURN i", t, func() {
SkipConvey("Should compile LET i = (FOR i WHILE COUNTER() < 5 RETURN i) RETURN i", t, func() {
c := compiler.New()
p, err := c.Compile(`
@@ -153,20 +153,20 @@ func TestVariables(t *testing.T) {
`)
So(err, ShouldBeNil)
So(p, ShouldHaveSameTypeAs, &runtime.Program{})
So(p, ShouldHaveSameTypeAs, &vm.Program{})
counter := -1
out, err := Run(p, runtime.WithFunction("COUNTER", func(ctx context.Context, args ...core.Value) (core.Value, error) {
out, err := Run(p, vm.WithFunction("COUNTER", func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
counter++
return core.NewInt(counter), nil
return runtime.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 length(i) == 0", t, func() {
SkipConvey("Should compile LET i = (FOR i WHILE COUNTER() < 5 T::FAIL() RETURN i)? RETURN length(i) == 0", t, func() {
c := compiler.New()
p, err := c.Compile(`
@@ -175,15 +175,15 @@ func TestVariables(t *testing.T) {
`)
So(err, ShouldBeNil)
So(p, ShouldHaveSameTypeAs, &runtime.Program{})
So(p, ShouldHaveSameTypeAs, &vm.Program{})
counter := -1
out, err := Run(p, runtime.WithFunction("COUNTER", func(ctx context.Context, args ...core.Value) (core.Value, error) {
out, err := Run(p, vm.WithFunction("COUNTER", func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
counter++
return core.NewInt(counter), nil
}), runtime.WithFunction("T::FAIL", func(ctx context.Context, args ...core.Value) (core.Value, error) {
return core.None, fmt.Errorf("test")
return runtime.NewInt(counter), nil
}), vm.WithFunction("T::FAIL", func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
return runtime.None, fmt.Errorf("test")
}))
So(err, ShouldBeNil)
@@ -234,7 +234,7 @@ func TestVariables(t *testing.T) {
func TestParam(t *testing.T) {
RunUseCases(t,
[]UseCase{
CaseRuntimeErrorAs(`RETURN @foo`, core.Error(runtime.ErrMissedParam, "@foo")),
CaseRuntimeErrorAs(`RETURN @foo`, runtime.Error(vm.ErrMissedParam, "@foo")),
Case(`RETURN @str`, "bar", "Should return a value of a parameter"),
Case(`RETURN @int + @int`, 2, "Should return a sum of two parameters"),
Case(`RETURN @obj.str1 + @obj.str2`, "foobar", "Should return a concatenated string of two parameter properties"),
@@ -243,14 +243,14 @@ func TestParam(t *testing.T) {
CaseArray(`FOR i IN @start..@end RETURN i`, []any{1, 2, 3, 4, 5}, "Should iterate over a range parameter"),
Case(`RETURN @obj.str1`, "foo", "Should be possible to use in member expression"),
},
runtime.WithParam("str", "bar"),
runtime.WithParam("int", 1),
runtime.WithParam("bool", true),
runtime.WithParam("obj", map[string]interface{}{"str1": "foo", "str2": "bar"}),
runtime.WithParam("values1", []int{1, 2, 3, 4}),
runtime.WithParam("values2", map[string]interface{}{"a": "a", "b": "b", "c": "c", "d": "d"}),
runtime.WithParam("start", 1),
runtime.WithParam("end", 5),
vm.WithParam("str", "bar"),
vm.WithParam("int", 1),
vm.WithParam("bool", true),
vm.WithParam("obj", map[string]interface{}{"str1": "foo", "str2": "bar"}),
vm.WithParam("values1", []int{1, 2, 3, 4}),
vm.WithParam("values2", map[string]interface{}{"a": "a", "b": "b", "c": "c", "d": "d"}),
vm.WithParam("start", 1),
vm.WithParam("end", 5),
)
}
@@ -289,7 +289,7 @@ func TestUnaryOperators(t *testing.T) {
RETURN { enabled: !val }
`)
v1, err := runtime.NewVM(p1).Run(context.Background(), nil)
v1, err := vm.NewVM(p1).Run(context.Background(), nil)
So(err, ShouldBeNil)
@@ -303,7 +303,7 @@ func TestUnaryOperators(t *testing.T) {
RETURN { enabled: !!val }
`)
v2, err := runtime.NewVM(p2).Run(context.Background(), nil)
v2, err := vm.NewVM(p2).Run(context.Background(), nil)
So(err, ShouldBeNil)
@@ -347,8 +347,8 @@ func TestLogicalOperators(t *testing.T) {
Case(`RETURN ERROR()? || 'boo'`, "boo"),
Case(`RETURN !ERROR()? && TRUE`, true),
Case(`LET u = { valid: false } RETURN u.valid || TRUE`, true),
}, runtime.WithFunction("ERROR", func(ctx context.Context, args ...core.Value) (core.Value, error) {
return core.None, fmt.Errorf("test")
}, vm.WithFunction("ERROR", func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
return runtime.None, fmt.Errorf("test")
}))
}
@@ -417,8 +417,8 @@ func TestRegexpOperator(t *testing.T) {
Case(`RETURN "foo" =~ "^f[o].$" `, true),
Case(`RETURN "foo" !~ "[a-z]+bar$"`, true),
Case(`RETURN "foo" !~ T::REGEXP()`, true),
}, runtime.WithFunction("T::REGEXP", func(_ context.Context, _ ...core.Value) (value core.Value, e error) {
return core.NewString("[a-z]+bar$"), nil
}, vm.WithFunction("T::REGEXP", func(_ context.Context, _ ...runtime.Value) (value runtime.Value, e error) {
return runtime.NewString("[a-z]+bar$"), nil
}))
// TODO: Fix
@@ -462,7 +462,8 @@ func TestInOperator(t *testing.T) {
func TestArrayAllOperator(t *testing.T) {
RunUseCases(t, []UseCase{
Case("RETURN [1,2,3] ALL IN [1,2,3]", true, "All elements are in"),
// TODO: Implement
SkipCase("RETURN [1,2,3] ALL IN [1,2,3]", true, "All elements are in"),
})
}
@@ -496,19 +497,19 @@ func TestFunctionCall(t *testing.T) {
Case("RETURN TYPENAME(1.1)", "float"),
Case("WAIT(10) RETURN 1", 1),
Case("RETURN LENGTH([1,2,3])", 3),
Case("RETURN CONCAT('a', 'b', 'c')", "abc"),
Case("RETURN CONCAT(CONCAT('a', 'b'), 'c', CONCAT('d', 'e'))", "abcde", "Nested calls"),
CaseArray(`
SkipCase("RETURN CONCAT('a', 'b', 'c')", "abc"),
SkipCase("RETURN CONCAT(CONCAT('a', 'b'), 'c', CONCAT('d', 'e'))", "abcde", "Nested calls"),
SkipCaseArray(`
LET arr = []
LET a = 1
LET res = APPEND(arr, a)
RETURN res
`,
[]any{1}, "Append to array"),
Case("LET duration = 10 WAIT(duration) RETURN 1", 1),
CaseNil("RETURN (FALSE OR T::FAIL())?"),
CaseNil("RETURN T::FAIL()?"),
CaseArray(`FOR i IN [1, 2, 3, 4]
SkipCase("LET duration = 10 WAIT(duration) RETURN 1", 1),
SkipCaseNil("RETURN (FALSE OR T::FAIL())?"),
SkipCaseNil("RETURN T::FAIL()?"),
SkipCaseArray(`FOR i IN [1, 2, 3, 4]
LET duration = 10
WAIT(duration)
@@ -516,15 +517,17 @@ func TestFunctionCall(t *testing.T) {
RETURN i * 2`,
[]any{2, 4, 6, 8}),
Case(`RETURN FIRST((FOR i IN 1..10 RETURN i * 2))`, 2),
CaseArray(`RETURN UNION((FOR i IN 0..5 RETURN i), (FOR i IN 6..10 RETURN i))`, []any{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}),
SkipCase(`RETURN FIRST((FOR i IN 1..10 RETURN i * 2))`, 2),
SkipCaseArray(`RETURN UNION((FOR i IN 0..5 RETURN i), (FOR i IN 6..10 RETURN i))`, []any{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}),
})
}
func TestBuiltinFunctions(t *testing.T) {
RunUseCases(t, []UseCase{
Case("RETURN LENGTH([1,2,3])", 3),
Case("RETURN TYPENAME([1,2,3])", "array"),
Case("RETURN TYPENAME([1,2,3])", "list"),
Case("RETURN TYPENAME({ a: 1, b: 2 })", "map"),
Case("WAIT(10) RETURN 1", 1),
})
}
@@ -693,7 +696,7 @@ func TestMemberReservedWords(t *testing.T) {
So(err, ShouldBeNil)
out, err := Exec(prog, true, runtime.WithFunctions(c.Functions().Unwrap()))
out, err := Exec(prog, true, vm.WithFunctions(c.Functions().Unwrap()))
So(err, ShouldBeNil)
So(out, ShouldEqual, expected.String())
@@ -724,20 +727,20 @@ func TestOptionalChaining(t *testing.T) {
RETURN obj.foo?.bar?.[0]
`,
"bar"),
CaseNil(`RETURN FIRST([])?.foo`),
Case(
SkipCaseNil(`RETURN FIRST([])?.foo`),
SkipCase(
`
RETURN FIRST([{ foo: "bar" }])?.foo
`,
"bar",
),
CaseNil("RETURN ERROR()?.foo"),
CaseArray(`LET res = (FOR i IN ERROR() RETURN i)? RETURN res`, []any{}),
CaseNil(`LET res = (FOR i IN ERROR() RETURN i)? RETURN res`),
CaseArray(`LET res = (FOR i IN [1, 2, 3, 4] LET y = ERROR() RETURN y+i)? RETURN res`, []any{}, "Error in arrayList comprehension"),
CaseArray(`LET res = (FOR i IN [1, 2, 3, 4] LET y = ERROR() RETURN y+i)? RETURN res`, []any{}, "Error in array comprehension"),
CaseArray(`FOR i IN [1, 2, 3, 4] ERROR()? RETURN i`, []any{1, 2, 3, 4}, "Error in FOR loop"),
}, runtime.WithFunction("ERROR", func(ctx context.Context, args ...core.Value) (core.Value, error) {
return nil, core.ErrNotImplemented
}, vm.WithFunction("ERROR", func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
return nil, runtime.ErrNotImplemented
}))
}
@@ -767,7 +770,7 @@ func TestFor(t *testing.T) {
`
FOR val, counter IN 1..5
LET x = val
PRINT(counter)
TEST_FN(counter)
LET y = counter
RETURN [x, y]
`,
@@ -856,7 +859,9 @@ func TestFor(t *testing.T) {
`,
[]any{1, 2, 3, 4},
),
})
}, vm.WithFunction("TEST_FN", func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
return nil, nil
}))
}
func TestForTernaryExpression(t *testing.T) {
@@ -930,19 +935,19 @@ func TestForWhile(t *testing.T) {
FOR x IN 1..y
RETURN i * x
`, []any{0, 1, 2, 2, 4, 6, 3, 6, 9, 12, 4, 8, 12, 16, 20}),
}, runtime.WithFunctions(map[string]core.Function{
"UNTIL": func(ctx context.Context, args ...core.Value) (core.Value, error) {
if untilCounter < int(core.ToIntSafe(ctx, args[0])) {
}, vm.WithFunctions(map[string]runtime.Function{
"UNTIL": func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
if untilCounter < int(runtime.ToIntSafe(ctx, args[0])) {
untilCounter++
return core.True, nil
return runtime.True, nil
}
return core.False, nil
return runtime.False, nil
},
"COUNTER": func(ctx context.Context, args ...core.Value) (core.Value, error) {
"COUNTER": func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
counter++
return core.NewInt(counter), nil
return runtime.NewInt(counter), nil
},
}))
}
@@ -962,10 +967,10 @@ func TestForTernaryWhileExpression(t *testing.T) {
LET foo = FALSE
RETURN foo ? TRUE : (FOR i WHILE COUNTER() < 10 RETURN i*2)`,
[]any{0, 2, 4, 6, 8, 10, 12, 14, 16, 18}),
}, runtime.WithFunctions(map[string]core.Function{
"COUNTER": func(ctx context.Context, args ...core.Value) (core.Value, error) {
}, vm.WithFunctions(map[string]runtime.Function{
"COUNTER": func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
counter++
return core.NewInt(counter), nil
return runtime.NewInt(counter), nil
},
}))
}
@@ -988,14 +993,14 @@ func TestForDoWhile(t *testing.T) {
FOR x IN 1..y
RETURN i * x
`, []any{0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 2, 4, 6, 8, 0, 3, 6, 9, 12, 0, 4, 8, 12, 16}),
}, runtime.WithFunctions(map[string]core.Function{
"COUNTER": func(ctx context.Context, args ...core.Value) (core.Value, error) {
}, vm.WithFunctions(map[string]runtime.Function{
"COUNTER": func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
counter++
return core.NewInt(counter), nil
return runtime.NewInt(counter), nil
},
"COUNTER2": func(ctx context.Context, args ...core.Value) (core.Value, error) {
"COUNTER2": func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
counter2++
return core.NewInt(counter), nil
return runtime.NewInt(counter), nil
},
}))
}
@@ -1263,16 +1268,16 @@ LET users = [
COUNT_B()
RETURN i + x
`, []any{5, 6, 5}),
}, runtime.WithFunctions(map[string]core.Function{
"COUNT_A": func(ctx context.Context, args ...core.Value) (core.Value, error) {
}, vm.WithFunctions(map[string]runtime.Function{
"COUNT_A": func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
counterA++
return core.None, nil
return runtime.None, nil
},
"COUNT_B": func(ctx context.Context, args ...core.Value) (core.Value, error) {
"COUNT_B": func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
counterB++
return core.None, nil
return runtime.None, nil
},
}))
}
@@ -1325,8 +1330,8 @@ func TestForLimit(t *testing.T) {
LIMIT o[1]
RETURN i
`, []any{1, 2}, "Should be able to use array element"),
}, runtime.WithFunction("LIMIT_VALUE", func(ctx context.Context, args ...core.Value) (core.Value, error) {
return core.NewInt(2), nil
}, vm.WithFunction("LIMIT_VALUE", func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
return runtime.NewInt(2), nil
}))
}
@@ -1534,8 +1539,8 @@ LET users = [
map[string]any{"active": true, "age": 31, "gender": "m"},
map[string]any{"active": true, "age": 36, "gender": "m"},
}, "Should compile query with FILTER and SORT statements"),
}, runtime.WithFunction("TEST", func(ctx context.Context, args ...core.Value) (core.Value, error) {
return core.None, nil
}, vm.WithFunction("TEST", func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
return runtime.None, nil
}))
}

View File

@@ -4,15 +4,14 @@ import (
"context"
j "encoding/json"
"fmt"
"github.com/MontFerret/ferret/pkg/runtime"
"github.com/MontFerret/ferret/pkg/vm"
"strings"
"testing"
"github.com/MontFerret/ferret/pkg/runtime/core"
. "github.com/smartystreets/goconvey/convey"
"github.com/MontFerret/ferret/pkg/compiler"
"github.com/MontFerret/ferret/pkg/runtime"
)
type UseCase struct {
@@ -73,7 +72,7 @@ func CaseCompilationError(expression string, desc ...string) UseCase {
return NewCase(expression, nil, ShouldBeCompilationError, desc...)
}
func SkipCompilationRuntimeError(expression string, desc ...string) UseCase {
func SkipCaseCompilationError(expression string, desc ...string) UseCase {
return Skip(CaseCompilationError(expression, desc...))
}
@@ -111,7 +110,7 @@ func SkipCaseJSON(expression string, expected string, desc ...string) UseCase {
type ExpectedProgram struct {
Disassembly string
Constants []core.Value
Constants []runtime.Value
Registers int
}
@@ -120,14 +119,14 @@ type ByteCodeUseCase struct {
Expected ExpectedProgram
}
func Compile(expression string) (*runtime.Program, error) {
func Compile(expression string) (*vm.Program, error) {
c := compiler.New()
return c.Compile(expression)
}
func Run(p *runtime.Program, opts ...runtime.EnvironmentOption) ([]byte, error) {
vm := runtime.NewVM(p)
func Run(p *vm.Program, opts ...vm.EnvironmentOption) ([]byte, error) {
vm := vm.NewVM(p)
out, err := vm.Run(context.Background(), opts)
@@ -138,7 +137,7 @@ func Run(p *runtime.Program, opts ...runtime.EnvironmentOption) ([]byte, error)
return out.MarshalJSON()
}
func Exec(p *runtime.Program, raw bool, opts ...runtime.EnvironmentOption) (any, error) {
func Exec(p *vm.Program, raw bool, opts ...vm.EnvironmentOption) (any, error) {
out, err := Run(p, opts...)
if err != nil {
@@ -221,7 +220,7 @@ func RunAsmUseCases(t *testing.T, useCases []ByteCodeUseCase) {
}
}
func RunUseCasesWith(t *testing.T, c *compiler.Compiler, useCases []UseCase, opts ...runtime.EnvironmentOption) {
func RunUseCasesWith(t *testing.T, c *compiler.Compiler, useCases []UseCase, opts ...vm.EnvironmentOption) {
for _, useCase := range useCases {
name := useCase.Description
@@ -253,8 +252,8 @@ func RunUseCasesWith(t *testing.T, c *compiler.Compiler, useCases []UseCase, opt
return
}
options := []runtime.EnvironmentOption{
runtime.WithFunctions(c.Functions().Unwrap()),
options := []vm.EnvironmentOption{
vm.WithFunctions(c.Functions().Unwrap()),
}
options = append(options, opts...)
@@ -293,24 +292,24 @@ func RunUseCasesWith(t *testing.T, c *compiler.Compiler, useCases []UseCase, opt
}
}
func RunUseCases(t *testing.T, useCases []UseCase, opts ...runtime.EnvironmentOption) {
func RunUseCases(t *testing.T, useCases []UseCase, opts ...vm.EnvironmentOption) {
RunUseCasesWith(t, compiler.New(), useCases, opts...)
}
func RunBenchmarkWith(b *testing.B, c *compiler.Compiler, expression string, opts ...runtime.EnvironmentOption) {
func RunBenchmarkWith(b *testing.B, c *compiler.Compiler, expression string, opts ...vm.EnvironmentOption) {
prog, err := c.Compile(expression)
if err != nil {
panic(err)
}
options := []runtime.EnvironmentOption{
runtime.WithFunctions(c.Functions().Unwrap()),
options := []vm.EnvironmentOption{
vm.WithFunctions(c.Functions().Unwrap()),
}
options = append(options, opts...)
ctx := context.Background()
vm := runtime.NewVM(prog)
vm := vm.NewVM(prog)
b.ResetTimer()
@@ -323,6 +322,6 @@ func RunBenchmarkWith(b *testing.B, c *compiler.Compiler, expression string, opt
}
}
func RunBenchmark(b *testing.B, expression string, opts ...runtime.EnvironmentOption) {
func RunBenchmark(b *testing.B, expression string, opts ...vm.EnvironmentOption) {
RunBenchmarkWith(b, compiler.New(), expression, opts...)
}

View File

@@ -2,13 +2,12 @@ package compiler_test
import (
"fmt"
"testing"
"github.com/MontFerret/ferret/pkg/runtime"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/vm"
"testing"
)
func Disassembly(instr []string, opcodes ...runtime.Opcode) string {
func Disassembly(instr []string, opcodes ...vm.Opcode) string {
var disassembly string
for i := 0; i < len(instr); i++ {
@@ -30,14 +29,14 @@ func TestCompiler_Variables(t *testing.T) {
3: [%d] MOVE R0 R2
4: [%d] RET
`,
runtime.OpLoadNone,
runtime.OpStoreGlobal,
runtime.OpLoadGlobal,
runtime.OpMove,
runtime.OpReturn,
vm.OpLoadNone,
vm.OpStoreGlobal,
vm.OpLoadGlobal,
vm.OpMove,
vm.OpReturn,
),
Constants: []core.Value{
core.NewString("i"),
Constants: []runtime.Value{
runtime.NewString("i"),
},
},
},
@@ -51,14 +50,14 @@ func TestCompiler_Variables(t *testing.T) {
3: [%d] MOVE R0 R2
4: [%d] RET
`,
runtime.OpLoadBool,
runtime.OpStoreGlobal,
runtime.OpLoadGlobal,
runtime.OpMove,
runtime.OpReturn,
vm.OpLoadBool,
vm.OpStoreGlobal,
vm.OpLoadGlobal,
vm.OpMove,
vm.OpReturn,
),
Constants: []core.Value{
core.NewString("a"),
Constants: []runtime.Value{
runtime.NewString("a"),
},
},
},
@@ -72,14 +71,14 @@ func TestCompiler_Variables(t *testing.T) {
3: [%d] MOVE R0 R2
4: [%d] RET
`,
runtime.OpLoadBool,
runtime.OpStoreGlobal,
runtime.OpLoadGlobal,
runtime.OpMove,
runtime.OpReturn,
vm.OpLoadBool,
vm.OpStoreGlobal,
vm.OpLoadGlobal,
vm.OpMove,
vm.OpReturn,
),
Constants: []core.Value{
core.NewString("a"),
Constants: []runtime.Value{
runtime.NewString("a"),
},
},
},
@@ -93,15 +92,15 @@ func TestCompiler_Variables(t *testing.T) {
3: [%d] MOVE R0 R2
4: [%d] RET
`,
runtime.OpLoadConst,
runtime.OpStoreGlobal,
runtime.OpLoadGlobal,
runtime.OpMove,
runtime.OpReturn,
vm.OpLoadConst,
vm.OpStoreGlobal,
vm.OpLoadGlobal,
vm.OpMove,
vm.OpReturn,
),
Constants: []core.Value{
core.NewFloat(1.1),
core.NewString("a"),
Constants: []runtime.Value{
runtime.NewFloat(1.1),
runtime.NewString("a"),
},
},
},
@@ -121,18 +120,18 @@ RETURN a
5: [%d] MOVE R0 R3
6: [%d] RET
`,
runtime.OpLoadConst,
runtime.OpStoreGlobal,
runtime.OpLoadGlobal,
runtime.OpStoreGlobal,
runtime.OpLoadGlobal,
runtime.OpMove,
runtime.OpReturn,
vm.OpLoadConst,
vm.OpStoreGlobal,
vm.OpLoadGlobal,
vm.OpStoreGlobal,
vm.OpLoadGlobal,
vm.OpMove,
vm.OpReturn,
),
Constants: []core.Value{
core.NewString("foo"),
core.NewString("a"),
core.NewString("b"),
Constants: []runtime.Value{
runtime.NewString("foo"),
runtime.NewString("a"),
runtime.NewString("b"),
},
},
},
@@ -150,13 +149,13 @@ func TestCompiler_FuncCall(t *testing.T) {
2: [%d] MOVE R0 R1
3: [%d] RET
`,
runtime.OpLoadConst,
runtime.OpCall,
runtime.OpMove,
runtime.OpReturn,
vm.OpLoadConst,
vm.OpCall,
vm.OpMove,
vm.OpReturn,
),
Constants: []core.Value{
core.NewString("FOO"),
Constants: []runtime.Value{
runtime.NewString("FOO"),
},
},
},
@@ -171,15 +170,15 @@ func TestCompiler_FuncCall(t *testing.T) {
"MOVE R0 R1",
"RET",
},
runtime.OpLoadConst,
runtime.OpLoadConst,
runtime.OpLoadBool,
runtime.OpCall,
runtime.OpMove,
runtime.OpReturn,
vm.OpLoadConst,
vm.OpLoadConst,
vm.OpLoadBool,
vm.OpCall,
vm.OpMove,
vm.OpReturn,
),
Constants: []core.Value{
core.NewString("FOO"),
Constants: []runtime.Value{
runtime.NewString("FOO"),
},
},
},

View File

@@ -1,14 +1,16 @@
package compiler
import "github.com/MontFerret/ferret/pkg/runtime"
import (
"github.com/MontFerret/ferret/pkg/vm"
)
type Emitter struct {
instructions []runtime.Instruction
instructions []vm.Instruction
}
func NewEmitter() *Emitter {
return &Emitter{
instructions: make([]runtime.Instruction, 0, 8),
instructions: make([]vm.Instruction, 0, 8),
}
}
@@ -17,64 +19,64 @@ func (e *Emitter) Size() int {
}
// EmitJump emits a jump opcode.
func (e *Emitter) EmitJump(op runtime.Opcode, pos int) int {
e.EmitA(op, runtime.Operand(pos))
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 runtime.Opcode, state, cond runtime.Operand, pos int) int {
e.EmitABC(op, state, cond, runtime.Operand(pos))
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 runtime.Opcode, pos int, reg runtime.Operand) int {
e.EmitAB(op, runtime.Operand(pos), reg)
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
}
// PatchJump patches a jump opcode.
func (e *Emitter) PatchJump(instr int) {
e.instructions[instr].Operands[0] = runtime.Operand(len(e.instructions) - 1)
e.instructions[instr].Operands[0] = vm.Operand(len(e.instructions) - 1)
}
// PatchJumpAB patches a jump opcode with a new destination.
func (e *Emitter) PatchJumpAB(inst int) {
e.instructions[inst].Operands[2] = runtime.Operand(len(e.instructions) - 1)
e.instructions[inst].Operands[2] = vm.Operand(len(e.instructions) - 1)
}
// PatchJumpNextAB patches a jump instruction to jump over a current position.
func (e *Emitter) PatchJumpNextAB(instr int) {
e.instructions[instr].Operands[2] = runtime.Operand(len(e.instructions))
e.instructions[instr].Operands[2] = vm.Operand(len(e.instructions))
}
// PatchJumpNext patches a jump instruction to jump over a current position.
func (e *Emitter) PatchJumpNext(instr int) {
e.instructions[instr].Operands[0] = runtime.Operand(len(e.instructions))
e.instructions[instr].Operands[0] = vm.Operand(len(e.instructions))
}
// Emit emits an opcode with no arguments.
func (e *Emitter) Emit(op runtime.Opcode) {
func (e *Emitter) Emit(op vm.Opcode) {
e.EmitABC(op, 0, 0, 0)
}
// EmitA emits an opcode with a single destination register argument.
func (e *Emitter) EmitA(op runtime.Opcode, dest runtime.Operand) {
func (e *Emitter) EmitA(op vm.Opcode, dest vm.Operand) {
e.EmitABC(op, dest, 0, 0)
}
// EmitAB emits an opcode with a destination register and a single source register argument.
func (e *Emitter) EmitAB(op runtime.Opcode, dest, src1 runtime.Operand) {
func (e *Emitter) EmitAB(op vm.Opcode, dest, src1 vm.Operand) {
e.EmitABC(op, dest, src1, 0)
}
// EmitAb emits an opcode with a destination register and a boolean argument.
func (e *Emitter) EmitAb(op runtime.Opcode, dest runtime.Operand, arg bool) {
var src1 runtime.Operand
func (e *Emitter) EmitAb(op vm.Opcode, dest vm.Operand, arg bool) {
var src1 vm.Operand
if arg {
src1 = 1
@@ -84,12 +86,12 @@ func (e *Emitter) EmitAb(op runtime.Opcode, dest runtime.Operand, arg bool) {
}
// EmitAx emits an opcode with a destination register and a custom argument.
func (e *Emitter) EmitAx(op runtime.Opcode, dest runtime.Operand, arg int) {
e.EmitABC(op, dest, runtime.Operand(arg), 0)
func (e *Emitter) EmitAx(op vm.Opcode, dest vm.Operand, arg int) {
e.EmitABC(op, dest, vm.Operand(arg), 0)
}
// EmitAs emits an opcode with a destination register and a sequence of registers.
func (e *Emitter) EmitAs(op runtime.Opcode, dest runtime.Operand, seq *RegisterSequence) {
func (e *Emitter) EmitAs(op vm.Opcode, dest vm.Operand, seq *RegisterSequence) {
if seq != nil {
src1 := seq.Registers[0]
src2 := seq.Registers[len(seq.Registers)-1]
@@ -100,14 +102,14 @@ func (e *Emitter) EmitAs(op runtime.Opcode, dest runtime.Operand, seq *RegisterS
}
// EmitABx emits an opcode with a destination and source register and a custom argument.
func (e *Emitter) EmitABx(op runtime.Opcode, dest runtime.Operand, src runtime.Operand, arg int) {
e.EmitABC(op, dest, src, runtime.Operand(arg))
func (e *Emitter) EmitABx(op vm.Opcode, dest vm.Operand, src vm.Operand, arg int) {
e.EmitABC(op, dest, src, vm.Operand(arg))
}
// EmitABC emits an opcode with a destination register and two source register arguments.
func (e *Emitter) EmitABC(op runtime.Opcode, dest, src1, src2 runtime.Operand) {
e.instructions = append(e.instructions, runtime.Instruction{
func (e *Emitter) EmitABC(op vm.Opcode, dest, src1, src2 vm.Operand) {
e.instructions = append(e.instructions, vm.Instruction{
Opcode: op,
Operands: [3]runtime.Operand{dest, src1, src2},
Operands: [3]vm.Operand{dest, src1, src2},
})
}

View File

@@ -1,14 +1,13 @@
package compiler
import (
"github.com/MontFerret/ferret/pkg/runtime"
"strings"
"github.com/pkg/errors"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
func copyFromNamespace(fns *core.Functions, namespace string) error {
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

@@ -1,6 +1,8 @@
package compiler
import "github.com/MontFerret/ferret/pkg/runtime"
import (
"github.com/MontFerret/ferret/pkg/vm"
)
type (
LoopType int
@@ -14,13 +16,13 @@ type (
Allocate bool
Jump int
JumpOffset int
Src runtime.Operand
Iterator runtime.Operand
Src vm.Operand
Iterator vm.Operand
ValueName string
Value runtime.Operand
Value vm.Operand
KeyName string
Key runtime.Operand
Result runtime.Operand
Key vm.Operand
Result vm.Operand
}
LoopTable struct {
@@ -50,7 +52,7 @@ func NewLoopTable(registers *RegisterAllocator) *LoopTable {
func (lt *LoopTable) EnterLoop(loopType LoopType, kind LoopKind, distinct bool) *Loop {
var allocate bool
var state runtime.Operand
var state vm.Operand
// top loop
if len(lt.loops) == 0 {

View File

@@ -1,12 +1,11 @@
package compiler
import (
"github.com/MontFerret/ferret/pkg/runtime"
"regexp"
"strings"
"github.com/pkg/errors"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
var fnNameValidation = regexp.MustCompile("^[a-zA-Z]+[a-zA-Z0-9_]*(::[a-zA-Z]+[a-zA-Z0-9_]*)*$")
@@ -15,32 +14,32 @@ const emptyNS = ""
const separator = "::"
type NamespaceContainer struct {
funcs core.Functions
funcs runtime.Functions
name string
}
func NewRootNamespace() *NamespaceContainer {
ns := new(NamespaceContainer)
ns.funcs = core.NewFunctions()
ns.funcs = runtime.NewFunctions()
return ns
}
func NewNamespace(funcs core.Functions, name string) *NamespaceContainer {
func NewNamespace(funcs runtime.Functions, name string) *NamespaceContainer {
return &NamespaceContainer{funcs, strings.ToUpper(name)}
}
func (nc *NamespaceContainer) Namespace(name string) core.Namespace {
func (nc *NamespaceContainer) Namespace(name string) runtime.Namespace {
return NewNamespace(nc.funcs, nc.makeFullName(name))
}
func (nc *NamespaceContainer) MustRegisterFunction(name string, fun core.Function) {
func (nc *NamespaceContainer) MustRegisterFunction(name string, fun runtime.Function) {
if err := nc.RegisterFunction(name, fun); err != nil {
panic(err)
}
}
func (nc *NamespaceContainer) RegisterFunction(name string, fun core.Function) error {
func (nc *NamespaceContainer) RegisterFunction(name string, fun runtime.Function) error {
nsName := nc.makeFullName(name)
_, exists := nc.funcs.Get(nsName)
@@ -67,13 +66,13 @@ func (nc *NamespaceContainer) RemoveFunction(name string) {
nc.funcs.Unset(nc.makeFullName(name))
}
func (nc *NamespaceContainer) MustRegisterFunctions(funcs core.Functions) {
func (nc *NamespaceContainer) MustRegisterFunctions(funcs runtime.Functions) {
if err := nc.RegisterFunctions(funcs); err != nil {
panic(err)
}
}
func (nc *NamespaceContainer) RegisterFunctions(funcs core.Functions) error {
func (nc *NamespaceContainer) RegisterFunctions(funcs runtime.Functions) error {
for _, name := range funcs.Names() {
fun, _ := funcs.Get(name)
@@ -104,7 +103,7 @@ func (nc *NamespaceContainer) RegisteredFunctions() []string {
return res
}
func (nc *NamespaceContainer) Functions() core.Functions {
func (nc *NamespaceContainer) Functions() runtime.Functions {
return nc.funcs
}

View File

@@ -2,20 +2,20 @@ package compiler_test
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime"
"testing"
. "github.com/smartystreets/goconvey/convey"
"github.com/MontFerret/ferret/pkg/compiler"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
func TestNamespaceBuilder(t *testing.T) {
Convey("Namespaces", t, func() {
Convey("Should return an error when a function name contains NS separator", func() {
c := compiler.New()
err := c.RegisterFunction("FOO::SPY", func(ctx context.Context, args ...core.Value) (value core.Value, e error) {
return core.None, nil
err := c.RegisterFunction("FOO::SPY", func(ctx context.Context, args ...runtime.Value) (value runtime.Value, e error) {
return runtime.None, nil
})
So(err, ShouldNotBeNil)
@@ -23,8 +23,8 @@ func TestNamespaceBuilder(t *testing.T) {
Convey("Should successfully register a name within a namespace", func() {
c := compiler.New()
err := c.Namespace("FOO").RegisterFunction("SPY", func(ctx context.Context, args ...core.Value) (value core.Value, e error) {
return core.None, nil
err := c.Namespace("FOO").RegisterFunction("SPY", func(ctx context.Context, args ...runtime.Value) (value runtime.Value, e error) {
return runtime.None, nil
})
So(err, ShouldBeNil)
@@ -46,8 +46,8 @@ func TestNamespaceBuilder(t *testing.T) {
Convey("Root namespace should return all registered functions", func() {
c := compiler.New()
err := c.Namespace("FOO").RegisterFunction("SPY", func(ctx context.Context, args ...core.Value) (value core.Value, e error) {
return core.None, nil
err := c.Namespace("FOO").RegisterFunction("SPY", func(ctx context.Context, args ...runtime.Value) (value runtime.Value, e error) {
return runtime.None, nil
})
So(err, ShouldBeNil)
@@ -59,14 +59,14 @@ func TestNamespaceBuilder(t *testing.T) {
Convey("Namespace should return all registered functions", func() {
c := compiler.New()
err := c.Namespace("FOO").RegisterFunction("SPY", func(ctx context.Context, args ...core.Value) (value core.Value, e error) {
return core.None, nil
err := c.Namespace("FOO").RegisterFunction("SPY", func(ctx context.Context, args ...runtime.Value) (value runtime.Value, e error) {
return runtime.None, nil
})
So(err, ShouldBeNil)
err = c.Namespace("FOO").Namespace("UTILS").RegisterFunction("SPY", func(ctx context.Context, args ...core.Value) (value core.Value, e error) {
return core.None, nil
err = c.Namespace("FOO").Namespace("UTILS").RegisterFunction("SPY", func(ctx context.Context, args ...runtime.Value) (value runtime.Value, e error) {
return runtime.None, nil
})
So(err, ShouldBeNil)
@@ -82,8 +82,8 @@ func TestNamespaceBuilder(t *testing.T) {
Convey("Namespace should return an error if namespace name is incorrect", func() {
c := compiler.New()
noop := func(ctx context.Context, args ...core.Value) (value core.Value, e error) {
return core.None, nil
noop := func(ctx context.Context, args ...runtime.Value) (value runtime.Value, e error) {
return runtime.None, nil
}
err := c.Namespace("FOO::").RegisterFunction("SPY", noop)

View File

@@ -1,25 +1,24 @@
package compiler
import (
"strconv"
"github.com/MontFerret/ferret/pkg/runtime"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/vm"
"strconv"
)
type (
Variable struct {
Name string
Register runtime.Operand
Register vm.Operand
Depth int
}
SymbolTable struct {
registers *RegisterAllocator
constants []core.Value
constants []runtime.Value
constantsIndex map[uint64]int
params map[string]string
globals map[string]runtime.Operand
globals map[string]vm.Operand
locals []*Variable
scope int
}
@@ -28,10 +27,10 @@ type (
func NewSymbolTable(registers *RegisterAllocator) *SymbolTable {
return &SymbolTable{
registers: registers,
constants: make([]core.Value, 0),
constants: make([]runtime.Value, 0),
constantsIndex: make(map[uint64]int),
params: make(map[string]string),
globals: make(map[string]runtime.Operand),
globals: make(map[string]vm.Operand),
locals: make([]*Variable, 0),
}
}
@@ -44,26 +43,26 @@ func (st *SymbolTable) EnterScope() {
st.scope++
}
func (st *SymbolTable) AddParam(name string) runtime.Operand {
func (st *SymbolTable) AddParam(name string) vm.Operand {
st.params[name] = name
return st.AddConstant(core.NewString(name))
return st.AddConstant(runtime.NewString(name))
}
// AddConstant adds a constant to the constants pool and returns its index.
// If the constant is a scalar, it will be deduplicated.
// If the constant is not a scalar, it will be added to the pool without deduplication.
func (st *SymbolTable) AddConstant(constant core.Value) runtime.Operand {
func (st *SymbolTable) AddConstant(constant runtime.Value) vm.Operand {
var hash uint64
isNone := constant == core.None
isNone := constant == runtime.None
if core.IsScalar(constant) {
if runtime.IsScalar(constant) {
hash = constant.Hash()
}
if hash > 0 || isNone {
if p, ok := st.constantsIndex[hash]; ok {
return runtime.NewConstantOperand(p)
return vm.NewConstantOperand(p)
}
}
@@ -75,34 +74,34 @@ func (st *SymbolTable) AddConstant(constant core.Value) runtime.Operand {
}
// We flip the sign to indicate that this is a constant index, not a register.
return runtime.NewConstantOperand(p)
return vm.NewConstantOperand(p)
}
// Constant returns a constant by its index.
func (st *SymbolTable) Constant(addr runtime.Operand) core.Value {
func (st *SymbolTable) Constant(addr vm.Operand) runtime.Value {
if !addr.IsConstant() {
panic(core.Error(ErrInvalidOperandType, strconv.Itoa(int(addr))))
panic(runtime.Error(ErrInvalidOperandType, strconv.Itoa(int(addr))))
}
index := addr.Constant()
if index < 0 || index >= len(st.constants) {
panic(core.Error(ErrConstantNotFound, strconv.Itoa(index)))
panic(runtime.Error(ErrConstantNotFound, strconv.Itoa(index)))
}
return st.constants[index]
}
func (st *SymbolTable) DefineVariable(name string) runtime.Operand {
func (st *SymbolTable) DefineVariable(name string) vm.Operand {
if st.scope == 0 {
// Check for duplicate global variable names.
_, ok := st.globals[name]
if ok {
panic(core.Error(ErrVariableNotUnique, name))
panic(runtime.Error(ErrVariableNotUnique, name))
}
op := st.AddConstant(core.NewString(name))
op := st.AddConstant(runtime.NewString(name))
// Define global variable.
st.globals[name] = op
@@ -120,25 +119,25 @@ func (st *SymbolTable) DefineVariable(name string) runtime.Operand {
return register
}
func (st *SymbolTable) Variable(name string) runtime.Operand {
func (st *SymbolTable) Variable(name string) vm.Operand {
for i := len(st.locals) - 1; i >= 0; i-- {
variable := st.locals[i]
if variable.Name == name {
return runtime.NewRegisterOperand(int(variable.Register))
return vm.NewRegisterOperand(int(variable.Register))
}
}
op, ok := st.globals[name]
if !ok {
panic(core.Error(ErrVariableNotFound, name))
panic(runtime.Error(ErrVariableNotFound, name))
}
return op
}
// GlobalVariable returns a global variable by its name.
func (st *SymbolTable) GlobalVariable(name string) (runtime.Operand, bool) {
func (st *SymbolTable) GlobalVariable(name string) (vm.Operand, bool) {
op, ok := st.globals[name]
return op, ok

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
package core
package runtime
import (
"context"

View File

@@ -1,4 +1,4 @@
package core
package runtime
import (
"context"

View File

@@ -1,8 +1,8 @@
package core_test
package runtime_test
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
"testing"
. "github.com/smartystreets/goconvey/convey"
@@ -11,8 +11,8 @@ import (
func TestArrayIterator(t *testing.T) {
Convey("No values", t, func() {
ctx := context.Background()
arr := core.NewArray(0)
iter := core.NewArrayIterator(arr)
arr := runtime.NewArray(0)
iter := runtime.NewArrayIterator(arr)
hasNext, err := iter.HasNext(ctx)
@@ -22,9 +22,9 @@ func TestArrayIterator(t *testing.T) {
Convey("One value", t, func() {
ctx := context.Background()
arr := core.NewArray(1)
arr := runtime.NewArray(1)
arr.Add(ctx, NewInt(1))
iter := core.NewArrayIterator(arr)
iter := runtime.NewArrayIterator(arr)
hasNext, err := iter.HasNext(ctx)
@@ -45,13 +45,13 @@ func TestArrayIterator(t *testing.T) {
Convey("Multiple values", t, func() {
ctx := context.Background()
arr := core.NewArray(5)
arr := runtime.NewArray(5)
arr.Push(NewInt(1))
arr.Push(NewInt(2))
arr.Push(NewInt(3))
arr.Push(NewInt(4))
arr.Push(NewInt(5))
iter := core.NewArrayIterator(arr)
iter := runtime.NewArrayIterator(arr)
actual := make([]Int, 0, 5)
@@ -70,7 +70,7 @@ func TestArrayIterator(t *testing.T) {
func BenchmarkArrayIterator(b *testing.B) {
size := 100
arr := core.NewArray(size)
arr := runtime.NewArray(size)
for i := 0; i < size; i++ {
arr.Push(NewInt(i))
@@ -81,7 +81,7 @@ func BenchmarkArrayIterator(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
iter := core.NewArrayIterator(arr)
iter := runtime.NewArrayIterator(arr)
for {
hasNext, err := iter.HasNext(ctx)

View File

@@ -1,8 +1,8 @@
package core_test
package runtime_test
import (
"encoding/json"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
"testing"
. "github.com/smartystreets/goconvey/convey"
@@ -11,13 +11,13 @@ import (
func TestArray(t *testing.T) {
Convey("#constructor", t, func() {
Convey("Should create an empty array", func() {
arr := core.NewArray(10)
arr := runtime.NewArray(10)
So(arr.Length(), ShouldEqual, 0)
})
Convey("Should create an array, from passed values", func() {
arr := core.NewArrayWith(
arr := runtime.NewArrayWith(
NewInt(1),
NewInt(2),
NewInt(3),
@@ -29,7 +29,7 @@ func TestArray(t *testing.T) {
Convey(".MarshalJSON", t, func() {
Convey("Should serialize empty array", func() {
arr := core.NewArray(10)
arr := runtime.NewArray(10)
marshaled, err := arr.MarshalJSON()
So(err, ShouldBeNil)
@@ -38,7 +38,7 @@ func TestArray(t *testing.T) {
})
Convey("Should serialize full array", func() {
arr := core.NewArrayWith(
arr := runtime.NewArrayWith(
NewInt(1),
NewInt(2),
NewInt(3),
@@ -53,7 +53,7 @@ func TestArray(t *testing.T) {
Convey(".Unwrap", t, func() {
Convey("Should return a an array of unwrapped values", func() {
arr := core.NewArrayWith(
arr := runtime.NewArrayWith(
ZeroInt,
ZeroInt,
)
@@ -66,7 +66,7 @@ func TestArray(t *testing.T) {
Convey(".String", t, func() {
Convey("Should return a string representation ", func() {
arr := core.NewArrayWith(ZeroInt, ZeroInt)
arr := runtime.NewArrayWith(ZeroInt, ZeroInt)
So(arr.String(), ShouldEqual, "[0,0]")
})
@@ -74,7 +74,7 @@ func TestArray(t *testing.T) {
Convey(".CompareValues", t, func() {
Convey("It should return 1 for all non-array and non-object values", func() {
arr := core.NewArrayWith(ZeroInt, ZeroInt)
arr := runtime.NewArrayWith(ZeroInt, ZeroInt)
So(arr.Compare(None), ShouldEqual, 1)
So(arr.Compare(ZeroInt), ShouldEqual, 1)
@@ -83,39 +83,39 @@ func TestArray(t *testing.T) {
})
Convey("It should return -1 for all object values", func() {
arr := core.NewArrayWith(ZeroInt, ZeroInt)
obj := core.NewObject()
arr := runtime.NewArrayWith(ZeroInt, ZeroInt)
obj := runtime.NewObject()
So(arr.Compare(obj), ShouldEqual, -1)
})
Convey("It should return 0 when both arrays are empty", func() {
arr1 := core.NewArray(1)
arr2 := core.NewArray(1)
arr1 := runtime.NewArray(1)
arr2 := runtime.NewArray(1)
So(arr1.Compare(arr2), ShouldEqual, 0)
})
Convey("It should return 1 when other array is empty", func() {
arr1 := core.NewArrayWith(ZeroFloat)
arr2 := core.NewArray(1)
arr1 := runtime.NewArrayWith(ZeroFloat)
arr2 := runtime.NewArray(1)
So(arr1.Compare(arr2), ShouldEqual, 1)
})
Convey("It should return 1 when values are bigger", func() {
arr1 := core.NewArrayWith(NewInt(1))
arr2 := core.NewArrayWith(ZeroInt)
arr1 := runtime.NewArrayWith(NewInt(1))
arr2 := runtime.NewArrayWith(ZeroInt)
So(arr1.Compare(arr2), ShouldEqual, 1)
})
Convey("It should return 0 when arrays are equal", func() {
Convey("When only simple types are nested", func() {
arr1 := core.NewArrayWith(
arr1 := runtime.NewArrayWith(
NewInt(0), NewString("str"),
)
arr2 := core.NewArrayWith(
arr2 := runtime.NewArrayWith(
NewInt(0), NewString("str"),
)
@@ -123,19 +123,19 @@ func TestArray(t *testing.T) {
})
Convey("When object and array are nested at the same time", func() {
arr1 := core.NewArrayWith(
core.NewObjectWith(
core.NewObjectProperty("one", NewInt(1)),
arr1 := runtime.NewArrayWith(
runtime.NewObjectWith(
runtime.NewObjectProperty("one", NewInt(1)),
),
core.NewArrayWith(
runtime.NewArrayWith(
NewInt(2),
),
)
arr2 := core.NewArrayWith(
core.NewObjectWith(
core.NewObjectProperty("one", NewInt(1)),
arr2 := runtime.NewArrayWith(
runtime.NewObjectWith(
runtime.NewObjectProperty("one", NewInt(1)),
),
core.NewArrayWith(
runtime.NewArrayWith(
NewInt(2),
),
)
@@ -144,14 +144,14 @@ func TestArray(t *testing.T) {
})
Convey("When only objects are nested", func() {
arr1 := core.NewArrayWith(
core.NewObjectWith(
core.NewObjectProperty("one", NewInt(1)),
arr1 := runtime.NewArrayWith(
runtime.NewObjectWith(
runtime.NewObjectProperty("one", NewInt(1)),
),
)
arr2 := core.NewArrayWith(
core.NewObjectWith(
core.NewObjectProperty("one", NewInt(1)),
arr2 := runtime.NewArrayWith(
runtime.NewObjectWith(
runtime.NewObjectProperty("one", NewInt(1)),
),
)
@@ -159,13 +159,13 @@ func TestArray(t *testing.T) {
})
Convey("When only arrays are nested", func() {
arr1 := core.NewArrayWith(
core.NewArrayWith(
arr1 := runtime.NewArrayWith(
runtime.NewArrayWith(
NewInt(2),
),
)
arr2 := core.NewArrayWith(
core.NewArrayWith(
arr2 := runtime.NewArrayWith(
runtime.NewArrayWith(
NewInt(2),
),
)
@@ -174,21 +174,21 @@ func TestArray(t *testing.T) {
})
Convey("When simple and complex types at the same time", func() {
arr1 := core.NewArrayWith(
arr1 := runtime.NewArrayWith(
NewInt(0),
core.NewObjectWith(
core.NewObjectProperty("one", NewInt(1)),
runtime.NewObjectWith(
runtime.NewObjectProperty("one", NewInt(1)),
),
core.NewArrayWith(
runtime.NewArrayWith(
NewInt(2),
),
)
arr2 := core.NewArrayWith(
arr2 := runtime.NewArrayWith(
NewInt(0),
core.NewObjectWith(
core.NewObjectProperty("one", NewInt(1)),
runtime.NewObjectWith(
runtime.NewObjectProperty("one", NewInt(1)),
),
core.NewArrayWith(
runtime.NewArrayWith(
NewInt(2),
),
)
@@ -197,17 +197,17 @@ func TestArray(t *testing.T) {
})
Convey("When custom complex type", func() {
arr1 := core.NewArrayWith(
core.NewObjectWith(
core.NewObjectProperty(
"arr", core.NewArrayWith(core.NewObject()),
arr1 := runtime.NewArrayWith(
runtime.NewObjectWith(
runtime.NewObjectProperty(
"arr", runtime.NewArrayWith(runtime.NewObject()),
),
),
)
arr2 := core.NewArrayWith(
core.NewObjectWith(
core.NewObjectProperty(
"arr", core.NewArrayWith(core.NewObject()),
arr2 := runtime.NewArrayWith(
runtime.NewObjectWith(
runtime.NewObjectProperty(
"arr", runtime.NewArrayWith(runtime.NewObject()),
),
),
)
@@ -219,7 +219,7 @@ func TestArray(t *testing.T) {
Convey(".Hash", t, func() {
Convey("It should calculate hash of non-empty array", func() {
arr := core.NewArrayWith(
arr := runtime.NewArrayWith(
NewInt(1),
NewInt(2),
NewInt(3),
@@ -231,7 +231,7 @@ func TestArray(t *testing.T) {
})
Convey("It should calculate hash of empty array", func() {
arr := core.NewArrayWith()
arr := runtime.NewArrayWith()
h := arr.Hash()
@@ -239,14 +239,14 @@ func TestArray(t *testing.T) {
})
Convey("Hash sum should be consistent", func() {
arr := core.NewArrayWith(
arr := runtime.NewArrayWith(
True,
NewInt(1),
NewFloat(1.1),
NewString("foobar"),
NewCurrentDateTime(),
core.NewArrayWith(NewInt(1), True),
core.NewObjectWith(core.NewObjectProperty("foo", NewString("bar"))),
runtime.NewArrayWith(NewInt(1), True),
runtime.NewObjectWith(runtime.NewObjectProperty("foo", NewString("bar"))),
)
h1 := arr.Hash()
@@ -258,13 +258,13 @@ func TestArray(t *testing.T) {
Convey(".Length", t, func() {
Convey("Should return 0 when empty", func() {
arr := core.NewArray(1)
arr := runtime.NewArray(1)
So(arr.Length(), ShouldEqual, 0)
})
Convey("Should return greater than 0 when not empty", func() {
arr := core.NewArrayWith(ZeroInt, ZeroInt)
arr := runtime.NewArrayWith(ZeroInt, ZeroInt)
So(arr.Length(), ShouldEqual, 2)
})
@@ -272,7 +272,7 @@ func TestArray(t *testing.T) {
Convey(".ForEach", t, func() {
Convey("Should iterate over elements", func() {
arr := core.NewArrayWith(
arr := runtime.NewArrayWith(
NewInt(1),
NewInt(2),
NewInt(3),
@@ -289,7 +289,7 @@ func TestArray(t *testing.T) {
})
Convey("Should not iterate when empty", func() {
arr := core.NewArrayWith()
arr := runtime.NewArrayWith()
counter := 0
arr.ForEach(func(value Value, idx int) bool {
@@ -358,7 +358,7 @@ func TestArray(t *testing.T) {
//})
Convey("Should return an error when index is out of bounds", func() {
arr := core.NewArray(10)
arr := runtime.NewArray(10)
err := arr.Set(0, NewInt(1))
@@ -369,7 +369,7 @@ func TestArray(t *testing.T) {
Convey(".Push", t, func() {
Convey("Should add an item", func() {
arr := core.NewArray(10)
arr := runtime.NewArray(10)
src := []Value{
ZeroInt,
@@ -411,7 +411,7 @@ func TestArray(t *testing.T) {
Convey(".Insert", t, func() {
Convey("Should insert an item in the middle of an array", func() {
arr := core.NewArrayWith(
arr := runtime.NewArrayWith(
NewInt(0),
NewInt(1),
NewInt(2),
@@ -433,7 +433,7 @@ func TestArray(t *testing.T) {
Convey(".RemoveAt", t, func() {
Convey("Should remove an item from the middle", func() {
arr := core.NewArrayWith(
arr := runtime.NewArrayWith(
NewInt(0),
NewInt(1),
NewInt(2),
@@ -453,7 +453,7 @@ func TestArray(t *testing.T) {
})
Convey("Should remove an item from the end", func() {
arr := core.NewArrayWith(
arr := runtime.NewArrayWith(
NewInt(0),
NewInt(1),
NewInt(2),
@@ -474,7 +474,7 @@ func TestArray(t *testing.T) {
})
Convey("Should remove an item from the beginning", func() {
arr := core.NewArrayWith(
arr := runtime.NewArrayWith(
NewInt(0),
NewInt(1),
NewInt(2),
@@ -496,24 +496,24 @@ func TestArray(t *testing.T) {
Convey(".Clone", t, func() {
Convey("Cloned array should be equal to source array", func() {
arr := core.NewArrayWith(
arr := runtime.NewArrayWith(
NewInt(0),
core.NewObjectWith(
core.NewObjectProperty("one", NewInt(1)),
runtime.NewObjectWith(
runtime.NewObjectProperty("one", NewInt(1)),
),
core.NewArrayWith(
runtime.NewArrayWith(
NewInt(2),
),
)
clone := arr.Clone().(*core.Array)
clone := arr.Clone().(*runtime.Array)
So(arr.Length(), ShouldEqual, clone.Length())
So(arr.Compare(clone), ShouldEqual, 0)
})
Convey("Cloned array should be independent of the source array", func() {
arr := core.NewArrayWith(
arr := runtime.NewArrayWith(
NewInt(0),
NewInt(1),
NewInt(2),
@@ -522,7 +522,7 @@ func TestArray(t *testing.T) {
NewInt(5),
)
clone := arr.Clone().(*core.Array)
clone := arr.Clone().(*runtime.Array)
arr.Push(NewInt(6))

View File

@@ -1,4 +1,8 @@
package core
package runtime
import "context"
type TypeAssertion func(input Value) error
func AssertString(input Value) error {
_, ok := input.(String)
@@ -74,6 +78,16 @@ func AssertList(input Value) error {
return nil
}
func AssertItemsOf(ctx context.Context, input Iterable, assertion TypeAssertion) error {
return ForEachOf(ctx, input, func(ctx context.Context, value, _ Value) (Boolean, error) {
if err := assertion(value); err != nil {
return false, err
}
return true, nil
})
}
func AssertMap(input Value) error {
_, ok := input.(Map)

View File

@@ -1,4 +1,4 @@
package core
package runtime
import (
"context"

View File

@@ -1,4 +1,4 @@
package core
package runtime
import (
"hash/fnv"

View File

@@ -1,7 +1,7 @@
package core_test
package runtime_test
import (
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
"testing"
. "github.com/smartystreets/goconvey/convey"
@@ -10,7 +10,7 @@ import (
func TestBoolean(t *testing.T) {
Convey(".MarshalJSON", t, func() {
Convey("Should serialize a boolean items", func() {
b := core.True
b := runtime.True
marshaled, err := b.MarshalJSON()
So(err, ShouldBeNil)
@@ -21,19 +21,19 @@ func TestBoolean(t *testing.T) {
Convey(".Unwrap", t, func() {
Convey("Should return an unwrapped items", func() {
So(core.True.Unwrap(), ShouldHaveSameTypeAs, true)
So(runtime.True.Unwrap(), ShouldHaveSameTypeAs, true)
})
})
Convey(".String", t, func() {
Convey("Should return a string representation ", func() {
So(core.True.String(), ShouldEqual, "true")
So(runtime.True.String(), ShouldEqual, "true")
})
})
Convey(".CompareValues", t, func() {
Convey("It should return 1 when compared to None", func() {
So(core.True.Compare(core.None), ShouldEqual, 1)
So(runtime.True.Compare(runtime.None), ShouldEqual, 1)
})
Convey("It should return -1 for all non-boolean values", func() {
@@ -41,8 +41,8 @@ func TestBoolean(t *testing.T) {
NewString("foo"),
NewInt(1),
NewFloat(1.1),
core.NewArray(10),
core.NewObject(),
runtime.NewArray(10),
runtime.NewObject(),
}
for _, v := range vals {

View File

@@ -1,4 +1,4 @@
package core
package runtime
func CastBoolean(input Value) (Boolean, error) {
boolean, ok := input.(Boolean)

View File

@@ -1,4 +1,4 @@
package core
package runtime
import "context"
@@ -22,6 +22,8 @@ func SafeClone(ctx context.Context, origin Cloneable) Cloneable {
return cloned
}
// CloneOrCopy creates a deep copy of the given value.
// If the value does not support cloning, it returns a shallow copy of the value.
func CloneOrCopy(ctx context.Context, val Value) (Value, error) {
switch v := val.(type) {
case Cloneable:

View File

@@ -1,8 +1,10 @@
package core
package runtime
import "context"
type (
Predicate = func(ctx context.Context, value, idx Value) (Boolean, error)
Indexed interface {
Get(ctx context.Context, idx Int) (Value, error)
}
@@ -34,7 +36,12 @@ type (
Collection
Indexed
ForEach(ctx context.Context, predicate IndexedPredicate) error
Add(ctx context.Context, value Value) error
Set(ctx context.Context, idx Int, value Value) error
Insert(ctx context.Context, idx Int, value Value) error
Remove(ctx context.Context, value Value) error
RemoveAt(ctx context.Context, idx Int) (Value, error)
Swap(ctx context.Context, i, j Int) error
Find(ctx context.Context, predicate IndexedPredicate) (List, error)
FindOne(ctx context.Context, predicate IndexedPredicate) (Value, Boolean, error)
@@ -42,16 +49,12 @@ type (
IndexOf(ctx context.Context, value Value) (Int, error)
First(context.Context) (Value, error)
Last(context.Context) (Value, error)
Slice(ctx context.Context, start, end Int) (List, error)
Sort(ctx context.Context, ascending Boolean) (List, error)
SortWith(ctx context.Context, comparator Comparator) (List, error)
Add(ctx context.Context, value Value) error
Set(ctx context.Context, idx Int, value Value) error
Insert(ctx context.Context, idx Int, value Value) error
Remove(ctx context.Context, value Value) error
RemoveAt(ctx context.Context, idx Int) (Value, error)
Swap(ctx context.Context, i, j Int) error
ForEach(ctx context.Context, predicate IndexedPredicate) error
}
// Set represents a set of values.
@@ -70,17 +73,16 @@ type (
Collection
Keyed
ForEach(ctx context.Context, predicate KeyedPredicate) error
Keys(context.Context) ([]Value, error)
Values(context.Context) ([]Value, error)
Find(ctx context.Context, predicate KeyedPredicate) (List, error)
FindOne(ctx context.Context, predicate KeyedPredicate) (Value, Boolean, error)
ContainsKey(ctx context.Context, key Value) (Boolean, error)
ContainsValue(ctx context.Context, value Value) (Boolean, error)
Set(ctx context.Context, key Value, value Value) error
Remove(ctx context.Context, key Value) error
ContainsKey(ctx context.Context, key Value) (Boolean, error)
ContainsValue(ctx context.Context, value Value) (Boolean, error)
Keys(context.Context) ([]Value, error)
Values(context.Context) ([]Value, error)
Find(ctx context.Context, predicate KeyedPredicate) (List, error)
FindOne(ctx context.Context, predicate KeyedPredicate) (Value, Boolean, error)
ForEach(ctx context.Context, predicate KeyedPredicate) error
}
)

View File

@@ -1,4 +1,4 @@
package core
package runtime
type (
Comparable interface {

View File

@@ -1,67 +0,0 @@
package core
import (
"fmt"
"strings"
"github.com/pkg/errors"
)
var (
ErrMissedArgument = errors.New("missed argument")
ErrInvalidArgument = errors.New("invalid argument")
ErrInvalidArgumentNumber = errors.New("invalid argument number")
ErrInvalidType = errors.New("invalid type")
ErrInvalidOperation = errors.New("invalid operation")
ErrNotFound = errors.New("not found")
ErrNotUnique = errors.New("not unique")
ErrTerminated = errors.New("operation is terminated")
ErrUnexpected = errors.New("unexpected error")
ErrTimeout = errors.New("operation timed out")
ErrNotImplemented = errors.New("not implemented")
ErrNotSupported = errors.New("not supported")
)
const typeErrorTemplate = "expected %s, but got %s"
func TypeError(value Value, expected ...string) error {
actual := Reflect(value)
if len(expected) == 0 {
return Error(ErrInvalidType, actual)
}
if len(expected) == 1 {
return Error(ErrInvalidType, fmt.Sprintf(typeErrorTemplate, expected, actual))
}
strs := make([]string, len(expected))
for idx, t := range expected {
strs[idx] = t
}
expectedStr := strings.Join(strs, " or ")
return Error(ErrInvalidType, fmt.Sprintf(typeErrorTemplate, expectedStr, actual))
}
func Error(err error, msg string) error {
return errors.Errorf("%s: %s", err.Error(), msg)
}
func Errorf(err error, format string, args ...interface{}) error {
return errors.Errorf("%s: %s", err.Error(), fmt.Sprintf(format, args...))
}
func Errors(err ...error) error {
message := ""
for _, e := range err {
if e != nil {
message += ": " + e.Error()
}
}
return errors.New(message)
}

View File

@@ -1,4 +1,4 @@
package core
package runtime
import (
"hash/fnv"

View File

@@ -1,8 +1,8 @@
package core_test
package runtime_test
import (
"encoding/json"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
"testing"
"time"
@@ -12,7 +12,7 @@ import (
func TestDateTime(t *testing.T) {
Convey(".Hash", t, func() {
Convey("It should calculate hash", func() {
d := core.NewCurrentDateTime()
d := runtime.NewCurrentDateTime()
h := d.Hash()
@@ -20,7 +20,7 @@ func TestDateTime(t *testing.T) {
})
Convey("Hash sum should be consistent", func() {
d := core.NewCurrentDateTime()
d := runtime.NewCurrentDateTime()
So(d.Hash(), ShouldEqual, d.Hash())
})
@@ -33,7 +33,7 @@ func TestDateTime(t *testing.T) {
json1, err := json.Marshal(value)
So(err, ShouldBeNil)
json2, err := core.NewDateTime(value).MarshalJSON()
json2, err := runtime.NewDateTime(value).MarshalJSON()
So(err, ShouldBeNil)
So(json1, ShouldResemble, json2)

View File

@@ -1,26 +1,67 @@
package runtime
import "github.com/pkg/errors"
import (
"fmt"
"strings"
"github.com/pkg/errors"
)
var (
ErrMissedParam = errors.New("missed parameter")
ErrMissedArgument = errors.New("missed argument")
ErrInvalidArgument = errors.New("invalid argument")
ErrInvalidArgumentNumber = errors.New("invalid argument number")
ErrInvalidType = errors.New("invalid type")
ErrInvalidOperation = errors.New("invalid operation")
ErrNotFound = errors.New("not found")
ErrNotUnique = errors.New("not unique")
ErrTerminated = errors.New("operation is terminated")
ErrUnexpected = errors.New("unexpected error")
ErrTimeout = errors.New("operation timed out")
ErrNotImplemented = errors.New("not implemented")
ErrNotSupported = errors.New("not supported")
)
type (
SourceErrorDetail struct {
error
BaseError error
ComputeError error
}
)
const typeErrorTemplate = "expected %s, but got %s"
func (e *SourceErrorDetail) Error() string {
return e.ComputeError.Error()
func TypeError(value Value, expected ...string) error {
actual := Reflect(value)
if len(expected) == 0 {
return Error(ErrInvalidType, actual)
}
if len(expected) == 1 {
return Error(ErrInvalidType, fmt.Sprintf(typeErrorTemplate, expected, actual))
}
strs := make([]string, len(expected))
for idx, t := range expected {
strs[idx] = t
}
expectedStr := strings.Join(strs, " or ")
return Error(ErrInvalidType, fmt.Sprintf(typeErrorTemplate, expectedStr, actual))
}
func SourceError(src SourceMap, err error) error {
return &SourceErrorDetail{
BaseError: err,
ComputeError: errors.Errorf("%s: %s", err.Error(), src.String()),
}
func Error(err error, msg string) error {
return errors.Errorf("%s: %s", err.Error(), msg)
}
func Errorf(err error, format string, args ...interface{}) error {
return errors.Errorf("%s: %s", err.Error(), fmt.Sprintf(format, args...))
}
func Errors(err ...error) error {
message := ""
for _, e := range err {
if e != nil {
message += ": " + e.Error()
}
}
return errors.New(message)
}

View File

@@ -1,6 +1,7 @@
package core_test
package runtime_test
import (
"github.com/MontFerret/ferret/pkg/runtime"
"github.com/MontFerret/ferret/pkg/runtime/core"
"testing"
@@ -24,14 +25,14 @@ func TestSourceError(t *testing.T) {
func TestTypeError(t *testing.T) {
Convey("Should match", t, func() {
e := core.TypeError(TypeA{})
e := runtime.TypeError(TypeA{})
So(e, ShouldNotBeNil)
e = core.TypeError(TypeA{}, TypeB{})
e = runtime.TypeError(TypeA{}, TypeB{})
So(e, ShouldNotBeNil)
cause := errors.New("invalid type: expected type_b or type_c, but got type_a")
e = core.TypeError(TypeA{}, TypeB{}, TypeC{})
e = runtime.TypeError(TypeA{}, TypeB{}, TypeC{})
So(e.Error(), ShouldEqual, cause.Error())
})
}
@@ -42,7 +43,7 @@ func TestError(t *testing.T) {
cause := errors.New("cause")
e := errors.Errorf("%s: %s", cause.Error(), msg)
ce := core.Error(cause, msg)
ce := runtime.Error(cause, msg)
So(ce, ShouldNotBeNil)
So(ce.Error(), ShouldEqual, e.Error())
})

View File

@@ -1,65 +0,0 @@
package events
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
type (
strm chan Message
msg struct {
value core.Value
err error
}
)
func New(source chan Message) Stream {
return strm(source)
}
func (s strm) Close(_ context.Context) error {
close(s)
return nil
}
func (s strm) Read(ctx context.Context) <-chan Message {
proxy := make(chan Message)
go func() {
defer close(proxy)
for {
select {
case <-ctx.Done():
return
case evt := <-s:
if ctx.Err() != nil {
return
}
proxy <- evt
}
}
}()
return s
}
func (n *msg) Value() core.Value {
return n.value
}
func (n *msg) Err() error {
return n.err
}
func WithValue(val core.Value) Message {
return &msg{value: val}
}
func WithErr(err error) Message {
return &msg{err: err, value: core.None}
}

View File

@@ -1,56 +0,0 @@
package events
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"sync"
)
type mrger struct {
inputs []Stream
}
func (m *mrger) Close(ctx context.Context) error {
errs := make([]error, 0, len(m.inputs))
for _, s := range m.inputs {
if err := s.Close(ctx); err != nil {
errs = append(errs, err)
}
}
if len(errs) == 0 {
return nil
}
return core.Errors(errs...)
}
func (m *mrger) Read(ctx context.Context) <-chan Message {
var wg sync.WaitGroup
wg.Add(len(m.inputs))
out := make(chan Message)
consume := func(c context.Context, input Stream) {
for evt := range input.Read(c) {
out <- evt
}
wg.Done()
}
for _, ch := range m.inputs {
go consume(ctx, ch)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
func Merge(inputs ...Stream) Stream {
return &mrger{inputs}
}

View File

@@ -1,42 +0,0 @@
package events
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
type Iterator struct {
messages <-chan Message
message Message
}
func NewIterator(ch <-chan Message) core.Iterator {
return &Iterator{ch, nil}
}
func (iter *Iterator) HasNext(ctx context.Context) (bool, error) {
select {
case evt, ok := <-iter.messages:
if !ok {
return false, nil
}
iter.message = evt
return true, nil
case <-ctx.Done():
return false, ctx.Err()
}
}
func (iter *Iterator) Next(ctx context.Context) (value core.Value, key core.Value, err error) {
if iter.message != nil {
if err := iter.message.Err(); err != nil {
return core.None, core.None, err
}
return iter.message.Value(), core.None, nil
}
return core.None, core.None, core.ErrNoMoreData
}

View File

@@ -1,33 +0,0 @@
package events
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
type (
// Subscription represents an event subscription object that contains target event name
// and optional event options.
Subscription struct {
EventName string
Options *core.hashMap
}
// Message represents an event message that an Observable can emit.
Message interface {
Value() core.Value
Err() error
}
// Stream represents an event stream that produces target event objects.
Stream interface {
Close(ctx context.Context) error
Read(ctx context.Context) <-chan Message
}
// Observable represents an interface of
// complex types that returns stream of events.
Observable interface {
Subscribe(ctx context.Context, subscription Subscription) (Stream, error)
}
)

View File

@@ -1,4 +1,4 @@
package core
package runtime
import (
"encoding/binary"

View File

@@ -1,8 +1,8 @@
package core_test
package runtime_test
import (
"encoding/json"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
"testing"
. "github.com/smartystreets/goconvey/convey"
@@ -11,19 +11,19 @@ import (
func TestFloat(t *testing.T) {
Convey(".Hash", t, func() {
Convey("It should calculate hash", func() {
v := core.NewFloat(1.1)
v := runtime.NewFloat(1.1)
h := v.Hash()
So(h, ShouldBeGreaterThan, 0)
v2 := core.NewFloat(1.2)
v2 := runtime.NewFloat(1.2)
So(h, ShouldNotEqual, v2.Hash())
})
Convey("Hash sum should be consistent", func() {
v := core.NewFloat(1.1)
v := runtime.NewFloat(1.1)
So(v.Hash(), ShouldEqual, v.Hash())
})
@@ -36,7 +36,7 @@ func TestFloat(t *testing.T) {
json1, err := json.Marshal(value)
So(err, ShouldBeNil)
json2, err := core.NewFloat(value).MarshalJSON()
json2, err := runtime.NewFloat(value).MarshalJSON()
So(err, ShouldBeNil)
So(json1, ShouldResemble, json2)

View File

@@ -1,4 +1,4 @@
package core
package runtime
import (
"context"

View File

@@ -1,37 +1,36 @@
package core_test
package runtime_test
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime"
"strings"
"testing"
. "github.com/smartystreets/goconvey/convey"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
func TestValidateArgs(t *testing.T) {
Convey("Should match", t, func() {
a := []core.Value{core.NewInt(1), core.NewInt(2)}
a := []runtime.Value{runtime.NewInt(1), runtime.NewInt(2)}
e := core.ValidateArgs(a, 1, 2)
e := runtime.ValidateArgs(a, 1, 2)
So(e, ShouldBeNil)
e = core.ValidateArgs(a, 3, 4)
e = runtime.ValidateArgs(a, 3, 4)
So(e, ShouldNotBeNil)
})
}
func TestFunctions(t *testing.T) {
fnTrue := func(ctx context.Context, args ...core.Value) (core.Value, error) {
return core.True, nil
fnTrue := func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
return runtime.True, nil
}
Convey(".Set", t, func() {
Convey("Should set function by name", func() {
fns := core.NewFunctions()
fns := runtime.NewFunctions()
fname := "F"
fns.Set(fname, fnTrue)
@@ -43,7 +42,7 @@ func TestFunctions(t *testing.T) {
})
Convey("Should set function by name at uppercase", func() {
fns := core.NewFunctions()
fns := runtime.NewFunctions()
fname := "f"
fns.Set(fname, fnTrue)
@@ -55,7 +54,7 @@ func TestFunctions(t *testing.T) {
})
Convey("Should set when Functions created not by NewFunctions", func() {
fns := core.Functions{}
fns := runtime.Functions{}
fname := "F"
fns.Set(fname, fnTrue)
@@ -70,7 +69,7 @@ func TestFunctions(t *testing.T) {
Convey(".Get", t, func() {
Convey("Should get function by name", func() {
fns := core.NewFunctions()
fns := runtime.NewFunctions()
fname := "F"
fns.Set(fname, fnTrue)
@@ -81,7 +80,7 @@ func TestFunctions(t *testing.T) {
})
Convey("Should get function by name at uppercase", func() {
fns := core.NewFunctions()
fns := runtime.NewFunctions()
fname := "f"
fns.Set(fname, fnTrue)
@@ -92,7 +91,7 @@ func TestFunctions(t *testing.T) {
})
Convey("Should not panic when Functions created not by NewFunctions", func() {
fns := core.Functions{}
fns := runtime.Functions{}
fn, exists := fns.Get("f")
@@ -104,7 +103,7 @@ func TestFunctions(t *testing.T) {
Convey(".Unset", t, func() {
Convey("Should unset function by name", func() {
fns := core.NewFunctions()
fns := runtime.NewFunctions()
fname := "F"
fns.Set(fname, fnTrue)
@@ -114,7 +113,7 @@ func TestFunctions(t *testing.T) {
})
Convey("Should get function by name at uppercase", func() {
fns := core.NewFunctions()
fns := runtime.NewFunctions()
fname := "f"
fns.Set(fname, fnTrue)
@@ -124,7 +123,7 @@ func TestFunctions(t *testing.T) {
})
Convey("Should not panic when Functions created not by NewFunctions", func() {
fns := core.Functions{}
fns := runtime.Functions{}
fname := "F"
fns.Set(fname, fnTrue)
@@ -137,7 +136,7 @@ func TestFunctions(t *testing.T) {
Convey(".Names", t, func() {
Convey("Should return name", func() {
fns := core.NewFunctions()
fns := runtime.NewFunctions()
fname := "F"
fns.Set(fname, fnTrue)
@@ -148,7 +147,7 @@ func TestFunctions(t *testing.T) {
})
Convey("Should return name at uppercase", func() {
fns := core.NewFunctions()
fns := runtime.NewFunctions()
fname := "f"
fns.Set(fname, fnTrue)
@@ -159,7 +158,7 @@ func TestFunctions(t *testing.T) {
})
Convey("Should not panic when Functions created not by NewFunctions", func() {
fns := core.Functions{}
fns := runtime.Functions{}
So(fns.Names(), ShouldHaveLength, 0)
})
})

View File

@@ -1,4 +1,4 @@
package core
package runtime
import (
"context"

View File

@@ -1,9 +1,9 @@
package core_test
package runtime_test
import (
"context"
"encoding/json"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
"reflect"
"testing"
"unsafe"
@@ -12,7 +12,7 @@ import (
)
type CustomValue struct {
properties map[core.Value]core.Value
properties map[runtime.Value]runtime.Value
}
type DummyStruct struct{}
@@ -20,51 +20,51 @@ type DummyStruct struct{}
func TestIsNil(t *testing.T) {
Convey("Should match", t, func() {
// nil == invalid
t := core.IsNil(nil)
t := runtime.IsNil(nil)
So(t, ShouldBeTrue)
a := []string{}
t = core.IsNil(a)
t = runtime.IsNil(a)
So(t, ShouldBeFalse)
b := make([]string, 1)
t = core.IsNil(b)
t = runtime.IsNil(b)
So(t, ShouldBeFalse)
c := make(map[string]string)
t = core.IsNil(c)
t = runtime.IsNil(c)
So(t, ShouldBeFalse)
var s struct {
Test string
}
t = core.IsNil(s)
t = runtime.IsNil(s)
So(t, ShouldBeFalse)
f := func() {}
t = core.IsNil(f)
t = runtime.IsNil(f)
So(t, ShouldBeFalse)
i := DummyStruct{}
t = core.IsNil(i)
t = runtime.IsNil(i)
So(t, ShouldBeFalse)
ch := make(chan string)
t = core.IsNil(ch)
t = runtime.IsNil(ch)
So(t, ShouldBeFalse)
var y unsafe.Pointer
var vy int
y = unsafe.Pointer(&vy)
t = core.IsNil(y)
t = runtime.IsNil(y)
So(t, ShouldBeFalse)
})
@@ -106,7 +106,7 @@ func TestHelpers(t *testing.T) {
}
for _, input := range inputs {
out := core.Parse(input.Raw)
out := runtime.Parse(input.Raw)
expectedType := reflect.TypeOf(input.Parsed)
actualType := reflect.TypeOf(out)
@@ -175,7 +175,7 @@ func TestHelpers(t *testing.T) {
}
for _, pair := range inputs {
actual := core.ToBoolean(pair[0])
actual := runtime.ToBoolean(pair[0])
expected := pair[1]
So(actual, ShouldEqual, expected)
@@ -186,48 +186,48 @@ func TestHelpers(t *testing.T) {
Convey("ToFloat", func() {
Convey("Should convert Int", func() {
input := NewInt(100)
output := core.ToFloat(input)
output := runtime.ToFloat(input)
So(output, ShouldEqual, NewFloat(100))
})
Convey("Should convert Float", func() {
input := NewFloat(100)
output := core.ToFloat(input)
output := runtime.ToFloat(input)
So(output, ShouldEqual, NewFloat(100))
})
Convey("Should convert String", func() {
input := NewString("100.1")
output := core.ToFloat(input)
output := runtime.ToFloat(input)
So(output, ShouldEqual, NewFloat(100.1))
output2 := core.ToFloat(NewString("foobar"))
output2 := runtime.ToFloat(NewString("foobar"))
So(output2, ShouldEqual, ZeroFloat)
})
Convey("Should convert Boolean", func() {
So(core.ToFloat(True), ShouldEqual, NewFloat(1))
So(core.ToFloat(False), ShouldEqual, NewFloat(0))
So(runtime.ToFloat(True), ShouldEqual, NewFloat(1))
So(runtime.ToFloat(False), ShouldEqual, NewFloat(0))
})
Convey("Should convert Array with single item", func() {
So(core.ToFloat(NewArrayWith(NewFloat(1))), ShouldEqual, NewFloat(1))
So(runtime.ToFloat(NewArrayWith(NewFloat(1))), ShouldEqual, NewFloat(1))
})
Convey("Should convert Array with multiple items", func() {
arg := NewArrayWith(NewFloat(1), NewFloat(1))
So(core.ToFloat(arg), ShouldEqual, NewFloat(2))
So(runtime.ToFloat(arg), ShouldEqual, NewFloat(2))
})
Convey("Should convert DateTime", func() {
dt := NewCurrentDateTime()
ts := dt.Time.Unix()
So(core.ToFloat(dt), ShouldEqual, NewFloat(float64(ts)))
So(runtime.ToFloat(dt), ShouldEqual, NewFloat(float64(ts)))
})
Convey("Should NOT convert other types", func() {
@@ -237,7 +237,7 @@ func TestHelpers(t *testing.T) {
}
for _, input := range inputs {
So(core.ToFloat(input), ShouldEqual, ZeroFloat)
So(runtime.ToFloat(input), ShouldEqual, ZeroFloat)
}
})
})
@@ -245,48 +245,48 @@ func TestHelpers(t *testing.T) {
Convey("ToInt", func() {
Convey("Should convert Int", func() {
input := NewInt(100)
output := core.ToInt(input)
output := runtime.ToInt(input)
So(output, ShouldEqual, NewInt(100))
})
Convey("Should convert Float", func() {
input := NewFloat(100.1)
output := core.ToInt(input)
output := runtime.ToInt(input)
So(output, ShouldEqual, NewInt(100))
})
Convey("Should convert String", func() {
input := NewString("100")
output := core.ToInt(input)
output := runtime.ToInt(input)
So(output, ShouldEqual, NewInt(100))
output2 := core.ToInt(NewString("foobar"))
output2 := runtime.ToInt(NewString("foobar"))
So(output2, ShouldEqual, ZeroInt)
})
Convey("Should convert Boolean", func() {
So(core.ToInt(True), ShouldEqual, NewInt(1))
So(core.ToInt(False), ShouldEqual, NewInt(0))
So(runtime.ToInt(True), ShouldEqual, NewInt(1))
So(runtime.ToInt(False), ShouldEqual, NewInt(0))
})
Convey("Should convert Array with single item", func() {
So(core.ToInt(NewArrayWith(NewFloat(1))), ShouldEqual, NewInt(1))
So(runtime.ToInt(NewArrayWith(NewFloat(1))), ShouldEqual, NewInt(1))
})
Convey("Should convert Array with multiple items", func() {
arg := NewArrayWith(NewFloat(1), NewFloat(1))
So(core.ToInt(arg), ShouldEqual, NewFloat(2))
So(runtime.ToInt(arg), ShouldEqual, NewFloat(2))
})
Convey("Should convert DateTime", func() {
dt := NewCurrentDateTime()
ts := dt.Time.Unix()
So(core.ToInt(dt), ShouldEqual, NewInt(int(ts)))
So(runtime.ToInt(dt), ShouldEqual, NewInt(int(ts)))
})
Convey("Should NOT convert other types", func() {
@@ -296,7 +296,7 @@ func TestHelpers(t *testing.T) {
}
for _, input := range inputs {
So(core.ToInt(input), ShouldEqual, ZeroInt)
So(runtime.ToInt(input), ShouldEqual, ZeroInt)
}
})
})
@@ -333,7 +333,7 @@ func TestHelpers(t *testing.T) {
}
for _, pairs := range inputs {
actual := core.ToList(context.Background(), pairs[0])
actual := runtime.ToList(context.Background(), pairs[0])
expected := pairs[1]
So(actual.Compare(expected), ShouldEqual, 0)
@@ -392,7 +392,7 @@ func TestHelpers(t *testing.T) {
So(err, ShouldBeNil)
val, err := core.Unmarshal(json1)
val, err := runtime.Unmarshal(json1)
So(err, ShouldBeNil)

View File

@@ -1,4 +1,4 @@
package core
package runtime
import (
"encoding/binary"

View File

@@ -1,8 +1,8 @@
package core_test
package runtime_test
import (
"encoding/json"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
"testing"
. "github.com/smartystreets/goconvey/convey"
@@ -11,19 +11,19 @@ import (
func TestInt(t *testing.T) {
Convey(".Hash", t, func() {
Convey("It should calculate hash", func() {
v := core.NewInt(1)
v := runtime.NewInt(1)
h := v.Hash()
So(h, ShouldBeGreaterThan, 0)
v2 := core.NewInt(2)
v2 := runtime.NewInt(2)
So(h, ShouldNotEqual, v2.Hash())
})
Convey("Hash sum should be consistent", func() {
v := core.NewInt(1)
v := runtime.NewInt(1)
So(v.Hash(), ShouldEqual, v.Hash())
})
@@ -36,7 +36,7 @@ func TestInt(t *testing.T) {
json1, err := json.Marshal(value)
So(err, ShouldBeNil)
json2, err := core.NewInt(value).MarshalJSON()
json2, err := runtime.NewInt(value).MarshalJSON()
So(err, ShouldBeNil)
So(json1, ShouldResemble, json2)

View File

@@ -1,77 +0,0 @@
package internal
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"strings"
)
func ToNumberOnly(ctx context.Context, input core.Value) core.Value {
switch value := input.(type) {
case core.Int, core.Float:
return value
case core.String:
if strings.Contains(value.String(), ".") {
if val, err := core.ToFloat(ctx, value); err == nil {
return val
}
return core.ZeroFloat
}
if val, err := core.ToInt(ctx, value); err == nil {
return val
}
return core.ZeroFloat
case core.Iterable:
iterator, err := value.Iterate(ctx)
if err != nil {
return core.ZeroInt
}
i := core.ZeroInt
f := core.ZeroFloat
for hasNext, err := iterator.HasNext(ctx); hasNext && err == nil; {
val, _, err := iterator.Next(ctx)
if err != nil {
continue
}
out := ToNumberOnly(ctx, val)
switch item := out.(type) {
case core.Int:
i += item
case core.Float:
f += item
}
}
if f == 0 {
return i
}
return core.Float(i) + f
default:
if val, err := core.ToFloat(ctx, value); err == nil {
return val
}
return core.ZeroInt
}
}
func ToRegexp(input core.Value) (*Regexp, error) {
switch r := input.(type) {
case *Regexp:
return r, nil
case core.String:
return NewRegexp(r)
default:
return nil, core.TypeError(input, core.TypeString, "regexp")
}
}

View File

@@ -1,16 +0,0 @@
package internal
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
type noopIter struct{}
func (n noopIter) HasNext(_ context.Context) (bool, error) {
return false, nil
}
func (n noopIter) Next(_ context.Context) (value core.Value, key core.Value, err error) {
return core.None, core.None, nil
}

View File

@@ -1,267 +0,0 @@
package internal
import (
"context"
"github.com/gobwas/glob"
"github.com/pkg/errors"
"strings"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
func Contains(ctx context.Context, input core.Value, value core.Value) core.Boolean {
switch val := input.(type) {
case core.List:
contains, err := val.Contains(ctx, value)
if err != nil {
return core.False
}
return contains
case core.Map:
containsValue, err := val.ContainsValue(ctx, value)
if err != nil {
return core.False
}
return containsValue
case core.String:
return core.Boolean(strings.Contains(val.String(), value.String()))
default:
return false
}
}
func Add(ctx context.Context, inputL, inputR core.Value) core.Value {
left := core.ToNumberOrString(inputL)
switch leftVal := left.(type) {
case core.Int:
return addLeftInt(leftVal, inputR)
case core.Float:
return addLeftFloat(leftVal, inputR)
case core.String:
return addLeftString(leftVal, inputR)
default:
return core.String(leftVal.String() + inputR.String())
}
}
func addLeftInt(integer core.Int, input core.Value) core.Value {
right := core.ToNumberOrString(input)
switch rightVal := right.(type) {
case core.Int:
return integer + rightVal
case core.Float:
return core.Float(integer) + rightVal
default:
return core.String(integer.String() + rightVal.String())
}
}
func addLeftFloat(float core.Float, input core.Value) core.Value {
right := core.ToNumberOrString(input)
switch rightVal := right.(type) {
case core.Int:
return float + core.Float(rightVal)
case core.Float:
return float + rightVal
default:
return core.String(float.String() + rightVal.String())
}
}
func addLeftString(str core.String, input core.Value) core.Value {
return core.String(str.String() + input.String())
}
func Subtract(ctx context.Context, inputL, inputR core.Value) core.Value {
left := ToNumberOnly(ctx, inputL)
switch leftVal := left.(type) {
case core.Int:
return subtractLeftInt(ctx, leftVal, inputR)
case core.Float:
return subtractLeftFloat(ctx, leftVal, inputR)
default:
return core.ZeroInt
}
}
func subtractLeftInt(ctx context.Context, integer core.Int, input core.Value) core.Value {
right := ToNumberOnly(ctx, input)
switch rightVal := right.(type) {
case core.Int:
return integer - rightVal
case core.Float:
return core.Float(integer) - rightVal
default:
return core.ZeroInt
}
}
func subtractLeftFloat(ctx context.Context, float core.Float, input core.Value) core.Value {
right := ToNumberOnly(ctx, input)
switch rightVal := right.(type) {
case core.Int:
return float - core.Float(rightVal)
case core.Float:
return float - rightVal
default:
return core.ZeroInt
}
}
func Multiply(ctx context.Context, inputL, inputR core.Value) core.Value {
left := ToNumberOnly(ctx, inputL)
switch leftVal := left.(type) {
case core.Int:
return multiplyLeftInt(ctx, leftVal, inputR)
case core.Float:
return multiplyLeftFloat(ctx, leftVal, inputR)
default:
return core.ZeroInt
}
}
func multiplyLeftInt(ctx context.Context, integer core.Int, input core.Value) core.Value {
right := ToNumberOnly(ctx, input)
switch rightVal := right.(type) {
case core.Int:
return integer * rightVal
case core.Float:
return core.Float(integer) * rightVal
default:
return core.ZeroInt
}
}
func multiplyLeftFloat(ctx context.Context, float core.Float, input core.Value) core.Value {
right := ToNumberOnly(ctx, input)
switch rightVal := right.(type) {
case core.Int:
return float * core.Float(rightVal)
case core.Float:
return float * rightVal
default:
return core.ZeroInt
}
}
func Divide(ctx context.Context, inputL, inputR core.Value) core.Value {
left := ToNumberOnly(ctx, inputL)
switch leftVal := left.(type) {
case core.Int:
return divideLeftInt(ctx, leftVal, inputR)
case core.Float:
return divideLeftFloat(ctx, leftVal, inputR)
default:
return core.ZeroInt
}
}
func divideLeftInt(ctx context.Context, integer core.Int, input core.Value) core.Value {
right := ToNumberOnly(ctx, input)
switch rightVal := right.(type) {
case core.Int:
return integer / rightVal
case core.Float:
return core.Float(integer) / rightVal
default:
return core.ZeroInt
}
}
func divideLeftFloat(ctx context.Context, float core.Float, input core.Value) core.Value {
right := ToNumberOnly(ctx, input)
switch rightVal := right.(type) {
case core.Int:
return float / core.Float(rightVal)
case core.Float:
return float / rightVal
default:
return core.ZeroInt
}
}
func Modulus(ctx context.Context, inputL, inputR core.Value) core.Value {
left, _ := core.ToInt(ctx, inputL)
right, _ := core.ToInt(ctx, inputR)
return left % right
}
func Increment(ctx context.Context, input core.Value) core.Value {
left := ToNumberOnly(ctx, input)
switch value := left.(type) {
case core.Int:
return value + 1
case core.Float:
return value + 1
default:
return core.None
}
}
func Decrement(ctx context.Context, input core.Value) core.Value {
left := ToNumberOnly(ctx, input)
switch value := left.(type) {
case core.Int:
return value - 1
case core.Float:
return value - 1
default:
return core.None
}
}
func ToRange(ctx context.Context, left, right core.Value) (core.Value, error) {
start, err := core.ToInt(ctx, left)
if err != nil {
return core.ZeroInt, err
}
end, err := core.ToInt(ctx, right)
if err != nil {
return core.ZeroInt, err
}
return NewRange(int64(start), int64(end)), nil
}
func Like(left, right core.Value) (core.Boolean, error) {
if err := core.AssertString(left); err != nil {
// TODO: Return the error? AQL just returns false
return core.False, nil
}
if err := core.AssertString(right); err != nil {
// TODO: Return the error? AQL just returns false
return core.False, nil
}
r, err := glob.Compile(right.String())
if err != nil {
return core.False, errors.Wrap(err, "invalid glob pattern")
}
result := r.Match(left.String())
return core.NewBoolean(result), nil
}

View File

@@ -1,6 +1,9 @@
package core
package runtime
import "context"
import (
"context"
"io"
)
type (
// Iterable represents an interface of a value that can be iterated by using an iterator.
@@ -20,7 +23,26 @@ type (
}
)
func ForEach(ctx context.Context, iter Iterator, predicate func(value Value, key Value) bool) error {
func ForEachOf(ctx context.Context, input Iterable, predicate Predicate) error {
iter, err := input.Iterate(ctx)
if err != nil {
return err
}
err = ForEach(ctx, iter, predicate)
closable, ok := iter.(io.Closer)
if ok {
if err := closable.Close(); err != nil {
return err
}
}
return err
}
func ForEach(ctx context.Context, iter Iterator, predicate Predicate) error {
for {
hasNext, err := iter.HasNext(ctx)
@@ -38,7 +60,13 @@ func ForEach(ctx context.Context, iter Iterator, predicate func(value Value, key
return err
}
if !predicate(val, key) {
res, err := predicate(ctx, val, key)
if err != nil {
return err
}
if !res {
return nil
}
}

View File

@@ -1,4 +1,4 @@
package core
package runtime
import "context"

View File

@@ -1,4 +1,4 @@
package core
package runtime
type Namespace interface {
Namespace(name string) Namespace

View File

@@ -1,4 +1,4 @@
package core
package runtime
import (
"context"

View File

@@ -1,4 +1,4 @@
package core
package runtime
import (
"context"
@@ -316,7 +316,7 @@ func (t *Object) Get(_ context.Context, key Value) (Value, error) {
return val, nil
}
return None, nil
return None, ErrNotFound
}
func (t *Object) Set(_ context.Context, key Value, value Value) error {

View File

@@ -1,4 +1,4 @@
package core
package runtime
import (
"context"

View File

@@ -1,8 +1,8 @@
package core_test
package runtime_test
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
"slices"
"testing"
@@ -12,8 +12,8 @@ import (
func TestObjectIterator(t *testing.T) {
Convey("No values", t, func() {
ctx := context.Background()
obj := core.NewObject()
iter := core.NewObjectIterator(obj)
obj := runtime.NewObject()
iter := runtime.NewObjectIterator(obj)
hasNext, err := iter.HasNext(ctx)
@@ -23,9 +23,9 @@ func TestObjectIterator(t *testing.T) {
Convey("One value", t, func() {
ctx := context.Background()
obj := core.NewObject()
obj := runtime.NewObject()
obj.Set(NewString("key"), NewInt(1))
iter := core.NewObjectIterator(obj)
iter := runtime.NewObjectIterator(obj)
hasNext, err := iter.HasNext(ctx)
@@ -46,13 +46,13 @@ func TestObjectIterator(t *testing.T) {
Convey("Multiple values", t, func() {
ctx := context.Background()
obj := core.NewObject()
obj := runtime.NewObject()
obj.Set(NewString("key1"), NewInt(1))
obj.Set(NewString("key2"), NewInt(2))
obj.Set(NewString("key3"), NewInt(3))
obj.Set(NewString("key4"), NewInt(4))
obj.Set(NewString("key5"), NewInt(5))
iter := core.NewObjectIterator(obj)
iter := runtime.NewObjectIterator(obj)
actual := make([][2]Value, 0, 5)
@@ -83,14 +83,14 @@ func TestObjectIterator(t *testing.T) {
func BenchmarkObjectIterator(b *testing.B) {
size := 100
obj := core.NewObject()
obj := runtime.NewObject()
for i := 0; i < size; i++ {
obj.Set(NewString("key"+ToString(NewInt(i)).String()), NewInt(i))
}
ctx := context.Background()
iter := core.NewObjectIterator(obj)
iter := runtime.NewObjectIterator(obj)
b.ResetTimer()

View File

@@ -1,7 +1,7 @@
package core_test
package runtime_test
import (
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
"testing"
. "github.com/smartystreets/goconvey/convey"
@@ -10,20 +10,20 @@ import (
func TestObject(t *testing.T) {
Convey("#constructor", t, func() {
Convey("Should create an empty object", func() {
obj := core.NewObject()
obj := runtime.NewObject()
So(obj.Length(), ShouldEqual, 0)
})
Convey("Should create an object, from passed values", func() {
obj := core.NewObjectWith(
core.NewObjectProperty("none", None),
core.NewObjectProperty("boolean", False),
core.NewObjectProperty("int", NewInt(1)),
core.NewObjectProperty("float", Float(1)),
core.NewObjectProperty("string", NewString("1")),
core.NewObjectProperty("array", NewArray(10)),
core.NewObjectProperty("object", core.NewObject()),
obj := runtime.NewObjectWith(
runtime.NewObjectProperty("none", None),
runtime.NewObjectProperty("boolean", False),
runtime.NewObjectProperty("int", NewInt(1)),
runtime.NewObjectProperty("float", Float(1)),
runtime.NewObjectProperty("string", NewString("1")),
runtime.NewObjectProperty("array", NewArray(10)),
runtime.NewObjectProperty("object", runtime.NewObject()),
)
So(obj.Length(), ShouldEqual, 7)
@@ -32,7 +32,7 @@ func TestObject(t *testing.T) {
Convey(".MarshalJSON", t, func() {
Convey("Should serialize an empty object", func() {
obj := core.NewObject()
obj := runtime.NewObject()
marshaled, err := obj.MarshalJSON()
So(err, ShouldBeNil)
@@ -41,14 +41,14 @@ func TestObject(t *testing.T) {
})
Convey("Should serialize full object", func() {
obj := core.NewObjectWith(
core.NewObjectProperty("none", None),
core.NewObjectProperty("boolean", False),
core.NewObjectProperty("int", NewInt(1)),
core.NewObjectProperty("float", Float(1)),
core.NewObjectProperty("string", NewString("1")),
core.NewObjectProperty("array", NewArray(10)),
core.NewObjectProperty("object", core.NewObject()),
obj := runtime.NewObjectWith(
runtime.NewObjectProperty("none", None),
runtime.NewObjectProperty("boolean", False),
runtime.NewObjectProperty("int", NewInt(1)),
runtime.NewObjectProperty("float", Float(1)),
runtime.NewObjectProperty("string", NewString("1")),
runtime.NewObjectProperty("array", NewArray(10)),
runtime.NewObjectProperty("object", runtime.NewObject()),
)
marshaled, err := obj.MarshalJSON()
@@ -60,9 +60,9 @@ func TestObject(t *testing.T) {
Convey(".Unwrap", t, func() {
Convey("Should return an unwrapped items", func() {
obj := core.NewObjectWith(
core.NewObjectProperty("foo", NewString("foo")),
core.NewObjectProperty("bar", NewString("bar")),
obj := runtime.NewObjectWith(
runtime.NewObjectProperty("foo", NewString("foo")),
runtime.NewObjectProperty("bar", NewString("bar")),
)
for _, val := range obj.Unwrap().(map[string]interface{}) {
@@ -73,8 +73,8 @@ func TestObject(t *testing.T) {
Convey(".String", t, func() {
Convey("Should return a string representation ", func() {
obj := core.NewObjectWith(
core.NewObjectProperty("foo", NewString("bar")),
obj := runtime.NewObjectWith(
runtime.NewObjectProperty("foo", NewString("bar")),
)
So(obj.String(), ShouldEqual, "{\"foo\":\"bar\"}")
@@ -91,7 +91,7 @@ func TestObject(t *testing.T) {
NewString("1"),
NewArray(10),
}
obj := core.NewObject()
obj := runtime.NewObject()
for _, val := range arr {
So(obj.Compare(val), ShouldEqual, 1)
@@ -100,26 +100,26 @@ func TestObject(t *testing.T) {
Convey("It should return -1 for all object values", func() {
arr := NewArrayWith(ZeroInt, ZeroInt)
obj := core.NewObject()
obj := runtime.NewObject()
So(arr.Compare(obj), ShouldEqual, -1)
})
Convey("It should return 0 when both objects are empty", func() {
obj1 := core.NewObject()
obj2 := core.NewObject()
obj1 := runtime.NewObject()
obj2 := runtime.NewObject()
So(obj1.Compare(obj2), ShouldEqual, 0)
})
Convey("It should return 0 when both objects are equal (independent of key order)", func() {
obj1 := core.NewObjectWith(
core.NewObjectProperty("foo", NewString("foo")),
core.NewObjectProperty("bar", NewString("bar")),
obj1 := runtime.NewObjectWith(
runtime.NewObjectProperty("foo", NewString("foo")),
runtime.NewObjectProperty("bar", NewString("bar")),
)
obj2 := core.NewObjectWith(
core.NewObjectProperty("foo", NewString("foo")),
core.NewObjectProperty("bar", NewString("bar")),
obj2 := runtime.NewObjectWith(
runtime.NewObjectProperty("foo", NewString("foo")),
runtime.NewObjectProperty("bar", NewString("bar")),
)
So(obj1.Compare(obj1), ShouldEqual, 0)
@@ -129,82 +129,82 @@ func TestObject(t *testing.T) {
})
Convey("It should return 1 when other array is empty", func() {
obj1 := core.NewObjectWith(core.NewObjectProperty("foo", NewString("bar")))
obj2 := core.NewObject()
obj1 := runtime.NewObjectWith(runtime.NewObjectProperty("foo", NewString("bar")))
obj2 := runtime.NewObject()
So(obj1.Compare(obj2), ShouldEqual, 1)
})
Convey("It should return 1 when values are bigger", func() {
obj1 := core.NewObjectWith(core.NewObjectProperty("foo", NewFloat(3)))
obj2 := core.NewObjectWith(core.NewObjectProperty("foo", NewFloat(2)))
obj1 := runtime.NewObjectWith(runtime.NewObjectProperty("foo", NewFloat(3)))
obj2 := runtime.NewObjectWith(runtime.NewObjectProperty("foo", NewFloat(2)))
So(obj1.Compare(obj2), ShouldEqual, 1)
})
Convey("It should return 1 when values are less", func() {
obj1 := core.NewObjectWith(core.NewObjectProperty("foo", NewFloat(1)))
obj2 := core.NewObjectWith(core.NewObjectProperty("foo", NewFloat(2)))
obj1 := runtime.NewObjectWith(runtime.NewObjectProperty("foo", NewFloat(1)))
obj2 := runtime.NewObjectWith(runtime.NewObjectProperty("foo", NewFloat(2)))
So(obj1.Compare(obj2), ShouldEqual, -1)
})
Convey("ArangoDB compatibility", func() {
Convey("It should return 1 when {a:1} and {b:2}", func() {
obj1 := core.NewObjectWith(core.NewObjectProperty("a", NewInt(1)))
obj2 := core.NewObjectWith(core.NewObjectProperty("b", NewInt(2)))
obj1 := runtime.NewObjectWith(runtime.NewObjectProperty("a", NewInt(1)))
obj2 := runtime.NewObjectWith(runtime.NewObjectProperty("b", NewInt(2)))
So(obj1.Compare(obj2), ShouldEqual, 1)
})
Convey("It should return 0 when {a:1} and {a:1}", func() {
obj1 := core.NewObjectWith(core.NewObjectProperty("a", NewInt(1)))
obj2 := core.NewObjectWith(core.NewObjectProperty("a", NewInt(1)))
obj1 := runtime.NewObjectWith(runtime.NewObjectProperty("a", NewInt(1)))
obj2 := runtime.NewObjectWith(runtime.NewObjectProperty("a", NewInt(1)))
So(obj1.Compare(obj2), ShouldEqual, 0)
})
Convey("It should return 0 {a:1, c:2} and {c:2, a:1}", func() {
obj1 := core.NewObjectWith(
core.NewObjectProperty("a", NewInt(1)),
core.NewObjectProperty("c", NewInt(2)),
obj1 := runtime.NewObjectWith(
runtime.NewObjectProperty("a", NewInt(1)),
runtime.NewObjectProperty("c", NewInt(2)),
)
obj2 := core.NewObjectWith(
core.NewObjectProperty("c", NewInt(2)),
core.NewObjectProperty("a", NewInt(1)),
obj2 := runtime.NewObjectWith(
runtime.NewObjectProperty("c", NewInt(2)),
runtime.NewObjectProperty("a", NewInt(1)),
)
So(obj1.Compare(obj2), ShouldEqual, 0)
})
Convey("It should return -1 when {a:1} and {a:2}", func() {
obj1 := core.NewObjectWith(core.NewObjectProperty("a", NewInt(1)))
obj2 := core.NewObjectWith(core.NewObjectProperty("a", NewInt(2)))
obj1 := runtime.NewObjectWith(runtime.NewObjectProperty("a", NewInt(1)))
obj2 := runtime.NewObjectWith(runtime.NewObjectProperty("a", NewInt(2)))
So(obj1.Compare(obj2), ShouldEqual, -1)
})
Convey("It should return 1 when {a:1, c:2} and {c:2, b:2}", func() {
obj1 := core.NewObjectWith(
core.NewObjectProperty("a", NewInt(1)),
core.NewObjectProperty("c", NewInt(2)),
obj1 := runtime.NewObjectWith(
runtime.NewObjectProperty("a", NewInt(1)),
runtime.NewObjectProperty("c", NewInt(2)),
)
obj2 := core.NewObjectWith(
core.NewObjectProperty("c", NewInt(2)),
core.NewObjectProperty("b", NewInt(2)),
obj2 := runtime.NewObjectWith(
runtime.NewObjectProperty("c", NewInt(2)),
runtime.NewObjectProperty("b", NewInt(2)),
)
So(obj1.Compare(obj2), ShouldEqual, 1)
})
Convey("It should return 1 {a:1, c:3} and {c:2, a:1}", func() {
obj1 := core.NewObjectWith(
core.NewObjectProperty("a", NewInt(1)),
core.NewObjectProperty("c", NewInt(3)),
obj1 := runtime.NewObjectWith(
runtime.NewObjectProperty("a", NewInt(1)),
runtime.NewObjectProperty("c", NewInt(3)),
)
obj2 := core.NewObjectWith(
core.NewObjectProperty("c", NewInt(2)),
core.NewObjectProperty("a", NewInt(1)),
obj2 := runtime.NewObjectWith(
runtime.NewObjectProperty("c", NewInt(2)),
runtime.NewObjectProperty("a", NewInt(1)),
)
So(obj1.Compare(obj2), ShouldEqual, 1)
@@ -214,10 +214,10 @@ func TestObject(t *testing.T) {
Convey(".Hash", t, func() {
Convey("It should calculate hash of non-empty object", func() {
v := core.NewObjectWith(
core.NewObjectProperty("foo", NewString("bar")),
core.NewObjectProperty("faz", NewInt(1)),
core.NewObjectProperty("qaz", True),
v := runtime.NewObjectWith(
runtime.NewObjectProperty("foo", NewString("bar")),
runtime.NewObjectProperty("faz", NewInt(1)),
runtime.NewObjectProperty("qaz", True),
)
h := v.Hash()
@@ -226,7 +226,7 @@ func TestObject(t *testing.T) {
})
Convey("It should calculate hash of empty object", func() {
v := core.NewObject()
v := runtime.NewObject()
h := v.Hash()
@@ -234,14 +234,14 @@ func TestObject(t *testing.T) {
})
Convey("Hash sum should be consistent", func() {
v := core.NewObjectWith(
core.NewObjectProperty("boolean", True),
core.NewObjectProperty("int", NewInt(1)),
core.NewObjectProperty("float", NewFloat(1.1)),
core.NewObjectProperty("string", NewString("foobar")),
core.NewObjectProperty("datetime", NewCurrentDateTime()),
core.NewObjectProperty("array", NewArrayWith(NewInt(1), True)),
core.NewObjectProperty("object", core.NewObjectWith(core.NewObjectProperty("foo", NewString("bar")))),
v := runtime.NewObjectWith(
runtime.NewObjectProperty("boolean", True),
runtime.NewObjectProperty("int", NewInt(1)),
runtime.NewObjectProperty("float", NewFloat(1.1)),
runtime.NewObjectProperty("string", NewString("foobar")),
runtime.NewObjectProperty("datetime", NewCurrentDateTime()),
runtime.NewObjectProperty("array", NewArrayWith(NewInt(1), True)),
runtime.NewObjectProperty("object", runtime.NewObjectWith(runtime.NewObjectProperty("foo", NewString("bar")))),
)
h1 := v.Hash()
@@ -253,15 +253,15 @@ func TestObject(t *testing.T) {
Convey(".Length", t, func() {
Convey("Should return 0 when empty", func() {
obj := core.NewObject()
obj := runtime.NewObject()
So(obj.Length(), ShouldEqual, 0)
})
Convey("Should return greater than 0 when not empty", func() {
obj := core.NewObjectWith(
core.NewObjectProperty("foo", ZeroInt),
core.NewObjectProperty("bar", ZeroInt),
obj := runtime.NewObjectWith(
runtime.NewObjectProperty("foo", ZeroInt),
runtime.NewObjectProperty("bar", ZeroInt),
)
So(obj.Length(), ShouldEqual, 2)
@@ -270,9 +270,9 @@ func TestObject(t *testing.T) {
Convey(".ForEach", t, func() {
Convey("Should iterate over elements", func() {
obj := core.NewObjectWith(
core.NewObjectProperty("foo", ZeroInt),
core.NewObjectProperty("bar", ZeroInt),
obj := runtime.NewObjectWith(
runtime.NewObjectProperty("foo", ZeroInt),
runtime.NewObjectProperty("bar", ZeroInt),
)
counter := 0
@@ -286,7 +286,7 @@ func TestObject(t *testing.T) {
})
Convey("Should not iterate when empty", func() {
obj := core.NewObject()
obj := runtime.NewObject()
counter := 0
obj.ForEach(func(value Value, key string) bool {
@@ -299,12 +299,12 @@ func TestObject(t *testing.T) {
})
Convey("Should break iteration when false returned", func() {
obj := core.NewObjectWith(
core.NewObjectProperty("1", NewInt(1)),
core.NewObjectProperty("2", NewInt(2)),
core.NewObjectProperty("3", NewInt(3)),
core.NewObjectProperty("4", NewInt(4)),
core.NewObjectProperty("5", NewInt(5)),
obj := runtime.NewObjectWith(
runtime.NewObjectProperty("1", NewInt(1)),
runtime.NewObjectProperty("2", NewInt(2)),
runtime.NewObjectProperty("3", NewInt(3)),
runtime.NewObjectProperty("4", NewInt(4)),
runtime.NewObjectProperty("5", NewInt(5)),
)
threshold := 3
counter := 0
@@ -356,23 +356,23 @@ func TestObject(t *testing.T) {
Convey(".Clone", t, func() {
Convey("Cloned object should be equal to source object", func() {
obj := core.NewObjectWith(
core.NewObjectProperty("one", NewInt(1)),
core.NewObjectProperty("two", NewInt(2)),
obj := runtime.NewObjectWith(
runtime.NewObjectProperty("one", NewInt(1)),
runtime.NewObjectProperty("two", NewInt(2)),
)
clone := obj.Clone().(*core.Object)
clone := obj.Clone().(*runtime.Object)
So(obj.Compare(clone), ShouldEqual, 0)
})
Convey("Cloned object should be independent of the source object", func() {
obj := core.NewObjectWith(
core.NewObjectProperty("one", NewInt(1)),
core.NewObjectProperty("two", NewInt(2)),
obj := runtime.NewObjectWith(
runtime.NewObjectProperty("one", NewInt(1)),
runtime.NewObjectProperty("two", NewInt(2)),
)
clone := obj.Clone().(*core.Object)
clone := obj.Clone().(*runtime.Object)
obj.Remove(NewString("one"))
@@ -380,13 +380,13 @@ func TestObject(t *testing.T) {
})
Convey("Cloned object must contain copies of the nested objects", func() {
obj := core.NewObjectWith(
core.NewObjectProperty(
obj := runtime.NewObjectWith(
runtime.NewObjectProperty(
"arr", NewArrayWith(NewInt(1)),
),
)
clone := obj.Clone().(*core.Object)
clone := obj.Clone().(*runtime.Object)
nestedInObj, _ := obj.Get(NewString("arr"))
nestedInObjArr := nestedInObj.(*Array)

View File

@@ -1,4 +1,4 @@
package core
package runtime
type (
Location struct {

View File

@@ -1,4 +1,4 @@
package core
package runtime
import (
"context"

View File

@@ -1,9 +1,9 @@
package core_test
package runtime_test
import (
"encoding/json"
"fmt"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
"testing"
. "github.com/smartystreets/goconvey/convey"
@@ -12,19 +12,19 @@ import (
func TestString(t *testing.T) {
Convey(".Hash", t, func() {
Convey("It should calculate hash", func() {
v := core.NewString("a")
v := runtime.NewString("a")
h := v.Hash()
So(h, ShouldBeGreaterThan, 0)
v2 := core.NewString("b")
v2 := runtime.NewString("b")
So(h, ShouldNotEqual, v2.Hash())
})
Convey("Hash sum should be consistent", func() {
v := core.NewString("foobar")
v := runtime.NewString("foobar")
So(v.Hash(), ShouldEqual, v.Hash())
})
@@ -32,7 +32,7 @@ func TestString(t *testing.T) {
Convey(".Length", t, func() {
Convey("Should return unicode length", func() {
str := core.NewString("Спутник")
str := runtime.NewString("Спутник")
So(str.Length(), ShouldEqual, 7)
})
@@ -45,7 +45,7 @@ func TestString(t *testing.T) {
json1, err := json.Marshal(value)
So(err, ShouldBeNil)
json2, err := core.NewString(value).MarshalJSON()
json2, err := runtime.NewString(value).MarshalJSON()
So(err, ShouldBeNil)
So(json1, ShouldResemble, json2)
@@ -57,7 +57,7 @@ func TestString(t *testing.T) {
json1, err := json.Marshal(value)
So(err, ShouldBeNil)
json2, err := core.NewString(value).MarshalJSON()
json2, err := runtime.NewString(value).MarshalJSON()
So(err, ShouldBeNil)
So(json1, ShouldNotResemble, json2)
@@ -66,7 +66,7 @@ func TestString(t *testing.T) {
})
Convey(".At", t, func() {
Convey("It should return a character", func() {
v := core.NewString("abcdefg")
v := runtime.NewString("abcdefg")
c := v.At(2)
So(string(c), ShouldEqual, "c")

View File

@@ -1,4 +1,4 @@
package core
package runtime
import "reflect"
@@ -13,7 +13,7 @@ const (
TypeSet = "set"
TypeMap = "map"
TypeBinary = "binary"
// Create subtypes for less specific types like interfaces
// Create subtypes for less specific types
TypeIterable = "iterable"
TypeIterator = "iterator"
TypeMeasurable = "measurable"

View File

@@ -1,4 +1,4 @@
package core
package runtime
import (
"encoding/json"

View File

@@ -1,9 +0,0 @@
package values
import (
"github.com/MontFerret/ferret/pkg/runtime/core"
)
func NewArray(cap int) core.List {
return core.NewArray(cap)
}

View File

@@ -1,25 +1,25 @@
package runtime
package vm
import (
"github.com/MontFerret/ferret/pkg/runtime"
"os"
"github.com/MontFerret/ferret/pkg/logging"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
type (
EnvironmentOption func(env *Environment)
Environment struct {
functions map[string]core.Function
params map[string]core.Value
functions map[string]runtime.Function
params map[string]runtime.Value
logging logging.Options
}
)
var noopEnv = &Environment{
functions: make(map[string]core.Function),
params: make(map[string]core.Value),
functions: make(map[string]runtime.Function),
params: make(map[string]runtime.Value),
}
func newEnvironment(opts []EnvironmentOption) *Environment {
@@ -28,8 +28,8 @@ func newEnvironment(opts []EnvironmentOption) *Environment {
}
env := &Environment{
functions: make(map[string]core.Function),
params: make(map[string]core.Value),
functions: make(map[string]runtime.Function),
params: make(map[string]runtime.Value),
logging: logging.Options{
Writer: os.Stdout,
Level: logging.ErrorLevel,
@@ -43,7 +43,7 @@ func newEnvironment(opts []EnvironmentOption) *Environment {
return env
}
func (env *Environment) GetFunction(name string) core.Function {
func (env *Environment) GetFunction(name string) runtime.Function {
return env.functions[name]
}
@@ -53,7 +53,7 @@ func (env *Environment) HasFunction(name string) bool {
return exists
}
func (env *Environment) GetParam(name string) core.Value {
func (env *Environment) GetParam(name string) runtime.Value {
return env.params[name]
}

View File

@@ -1,13 +1,13 @@
package runtime
package vm
import (
"github.com/MontFerret/ferret/pkg/runtime"
"io"
"github.com/MontFerret/ferret/pkg/logging"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
func WithParams(params map[string]core.Value) EnvironmentOption {
func WithParams(params map[string]runtime.Value) EnvironmentOption {
return func(env *Environment) {
env.params = params
}
@@ -15,14 +15,14 @@ func WithParams(params map[string]core.Value) EnvironmentOption {
func WithParam(name string, value interface{}) EnvironmentOption {
return func(options *Environment) {
options.params[name] = core.Parse(value)
options.params[name] = runtime.Parse(value)
}
}
func WithFunctions(functions map[string]core.Function) EnvironmentOption {
func WithFunctions(functions map[string]runtime.Function) EnvironmentOption {
return func(env *Environment) {
if env.functions == nil {
env.functions = make(map[string]core.Function)
env.functions = make(map[string]runtime.Function)
}
for name, function := range functions {
@@ -31,7 +31,7 @@ func WithFunctions(functions map[string]core.Function) EnvironmentOption {
}
}
func WithFunction(name string, function core.Function) EnvironmentOption {
func WithFunction(name string, function runtime.Function) EnvironmentOption {
return func(env *Environment) {
env.functions[name] = function
}

27
pkg/vm/errors.go Normal file
View File

@@ -0,0 +1,27 @@
package vm
import "github.com/pkg/errors"
var (
ErrMissedParam = errors.New("missed parameter")
ErrFunctionNotFound = errors.New("function not found")
)
type (
SourceErrorDetail struct {
error
BaseError error
ComputeError error
}
)
func (e *SourceErrorDetail) Error() string {
return e.ComputeError.Error()
}
func SourceError(src SourceMap, err error) error {
return &SourceErrorDetail{
BaseError: err,
ComputeError: errors.Errorf("%s: %s", err.Error(), src.String()),
}
}

View File

@@ -1,4 +1,4 @@
package runtime
package vm
import "fmt"

View File

@@ -2,11 +2,10 @@ package internal
import (
"fmt"
"github.com/MontFerret/ferret/pkg/runtime"
"hash/fnv"
"github.com/wI2L/jettison"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
// Boxed represents an arbitrary Value that can be boxed as a runtime Value.
@@ -40,6 +39,6 @@ func (b *Boxed) Hash() uint64 {
return h.Sum64()
}
func (b *Boxed) Copy() core.Value {
func (b *Boxed) Copy() runtime.Value {
return NewBoxedValue(b.Value)
}

View File

@@ -1,7 +1,7 @@
package internal
import (
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
)
type Collector struct {
@@ -12,7 +12,7 @@ func NewCollector() *Collector {
return &Collector{values: make(map[uint64][]*KeyValuePair)}
}
func (c *Collector) Add(key core.Value, value core.Value) {
func (c *Collector) Add(key runtime.Value, value runtime.Value) {
hash := key.Hash()
values, exists := c.values[hash]
@@ -40,6 +40,6 @@ func (c *Collector) Hash() uint64 {
panic("not supported")
}
func (c *Collector) Copy() core.Value {
func (c *Collector) Copy() runtime.Value {
panic("not supported")
}

View File

@@ -2,12 +2,12 @@ package internal
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
)
type DataSet struct {
hashmap map[uint64]bool
values core.List
values runtime.List
}
func NewDataSet(distinct bool) *DataSet {
@@ -19,21 +19,21 @@ func NewDataSet(distinct bool) *DataSet {
return &DataSet{
hashmap: hashmap,
values: core.NewArray(16),
values: runtime.NewArray(16),
}
}
func (ds *DataSet) Swap(ctx context.Context, i, j core.Int) {
func (ds *DataSet) Swap(ctx context.Context, i, j runtime.Int) {
ds.values.Swap(ctx, i, j)
}
func (ds *DataSet) Get(ctx context.Context, idx core.Int) core.Value {
func (ds *DataSet) Get(ctx context.Context, idx runtime.Int) runtime.Value {
val, _ := ds.values.Get(ctx, idx)
return val
}
func (ds *DataSet) Push(ctx context.Context, item core.Value) {
func (ds *DataSet) Push(ctx context.Context, item runtime.Value) {
if ds.hashmap != nil {
hash := item.Hash()
@@ -49,15 +49,15 @@ func (ds *DataSet) Push(ctx context.Context, item core.Value) {
_ = ds.values.Add(ctx, item)
}
func (ds *DataSet) Iterate(ctx context.Context) (core.Iterator, error) {
func (ds *DataSet) Iterate(ctx context.Context) (runtime.Iterator, error) {
return ds.values.Iterate(ctx)
}
func (ds *DataSet) Length(ctx context.Context) (core.Int, error) {
func (ds *DataSet) Length(ctx context.Context) (runtime.Int, error) {
return ds.values.Length(ctx)
}
func (ds *DataSet) ToList() core.List {
func (ds *DataSet) ToList() runtime.List {
return ds.values
}
@@ -73,7 +73,7 @@ func (ds *DataSet) Hash() uint64 {
return ds.values.Hash()
}
func (ds *DataSet) Copy() core.Value {
func (ds *DataSet) Copy() runtime.Value {
return ds.values.Copy()
}

View File

@@ -0,0 +1,91 @@
package internal
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime"
"strings"
"time"
)
func ToNumberOnly(ctx context.Context, input runtime.Value) runtime.Value {
switch value := input.(type) {
case runtime.Int, runtime.Float:
return value
case runtime.String:
if strings.Contains(value.String(), ".") {
if val, err := runtime.ToFloat(ctx, value); err == nil {
return val
}
return runtime.ZeroFloat
}
if val, err := runtime.ToInt(ctx, value); err == nil {
return val
}
return runtime.ZeroFloat
case runtime.Iterable:
iterator, err := value.Iterate(ctx)
if err != nil {
return runtime.ZeroInt
}
i := runtime.ZeroInt
f := runtime.ZeroFloat
for hasNext, err := iterator.HasNext(ctx); hasNext && err == nil; {
val, _, err := iterator.Next(ctx)
if err != nil {
continue
}
out := ToNumberOnly(ctx, val)
switch item := out.(type) {
case runtime.Int:
i += item
case runtime.Float:
f += item
}
}
if f == 0 {
return i
}
return runtime.Float(i) + f
default:
if val, err := runtime.ToFloat(ctx, value); err == nil {
return val
}
return runtime.ZeroInt
}
}
func ToRegexp(input runtime.Value) (*Regexp, error) {
switch r := input.(type) {
case *Regexp:
return r, nil
case runtime.String:
return NewRegexp(r)
default:
return nil, runtime.TypeError(input, runtime.TypeString, "regexp")
}
}
func Sleep(ctx context.Context, duration runtime.Int) error {
timer := time.NewTimer(time.Millisecond * time.Duration(duration))
select {
case <-ctx.Done():
timer.Stop()
return ctx.Err()
case <-timer.C:
}
return nil
}

View File

@@ -2,21 +2,20 @@ package internal
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime"
"io"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
type Iterator struct {
src core.Iterator
value core.Value
key core.Value
src runtime.Iterator
value runtime.Value
key runtime.Value
}
var NoopIter = NewIterator(&noopIter{})
func NewIterator(src core.Iterator) *Iterator {
return &Iterator{src, core.None, core.None}
func NewIterator(src runtime.Iterator) *Iterator {
return &Iterator{src, runtime.None, runtime.None}
}
func (it *Iterator) HasNext(ctx context.Context) (bool, error) {
@@ -36,11 +35,11 @@ func (it *Iterator) Next(ctx context.Context) error {
return nil
}
func (it *Iterator) Value() core.Value {
func (it *Iterator) Value() runtime.Value {
return it.value
}
func (it *Iterator) Key() core.Value {
func (it *Iterator) Key() runtime.Value {
return it.key
}
@@ -68,6 +67,6 @@ func (it *Iterator) Hash() uint64 {
panic("not supported")
}
func (it *Iterator) Copy() core.Value {
func (it *Iterator) Copy() runtime.Value {
panic("not supported")
}

View File

@@ -2,14 +2,13 @@ package internal
import (
"encoding/binary"
"github.com/MontFerret/ferret/pkg/runtime"
"hash/fnv"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
type KeyValuePair struct {
Key core.Value
Value core.Value
Key runtime.Value
Value runtime.Value
}
func (p *KeyValuePair) MarshalJSON() ([]byte, error) {
@@ -21,7 +20,7 @@ func (p *KeyValuePair) String() string {
}
func (p *KeyValuePair) Unwrap() interface{} {
return [2]core.Value{p.Key, p.Value}
return [2]runtime.Value{p.Key, p.Value}
}
func (p *KeyValuePair) Hash() uint64 {
@@ -44,7 +43,7 @@ func (p *KeyValuePair) Hash() uint64 {
return h.Sum64()
}
func (p *KeyValuePair) Copy() core.Value {
func (p *KeyValuePair) Copy() runtime.Value {
return &KeyValuePair{
Key: p.Key,
Value: p.Value,

View File

@@ -0,0 +1,16 @@
package internal
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime"
)
type noopIter struct{}
func (n noopIter) HasNext(_ context.Context) (bool, error) {
return false, nil
}
func (n noopIter) Next(_ context.Context) (value runtime.Value, key runtime.Value, err error) {
return runtime.None, runtime.None, nil
}

View File

@@ -0,0 +1,266 @@
package internal
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime"
"github.com/gobwas/glob"
"github.com/pkg/errors"
"strings"
)
func Contains(ctx context.Context, input runtime.Value, value runtime.Value) runtime.Boolean {
switch val := input.(type) {
case runtime.List:
contains, err := val.Contains(ctx, value)
if err != nil {
return runtime.False
}
return contains
case runtime.Map:
containsValue, err := val.ContainsValue(ctx, value)
if err != nil {
return runtime.False
}
return containsValue
case runtime.String:
return runtime.Boolean(strings.Contains(val.String(), value.String()))
default:
return false
}
}
func Add(ctx context.Context, inputL, inputR runtime.Value) runtime.Value {
left := runtime.ToNumberOrString(inputL)
switch leftVal := left.(type) {
case runtime.Int:
return addLeftInt(leftVal, inputR)
case runtime.Float:
return addLeftFloat(leftVal, inputR)
case runtime.String:
return addLeftString(leftVal, inputR)
default:
return runtime.String(leftVal.String() + inputR.String())
}
}
func addLeftInt(integer runtime.Int, input runtime.Value) runtime.Value {
right := runtime.ToNumberOrString(input)
switch rightVal := right.(type) {
case runtime.Int:
return integer + rightVal
case runtime.Float:
return runtime.Float(integer) + rightVal
default:
return runtime.String(integer.String() + rightVal.String())
}
}
func addLeftFloat(float runtime.Float, input runtime.Value) runtime.Value {
right := runtime.ToNumberOrString(input)
switch rightVal := right.(type) {
case runtime.Int:
return float + runtime.Float(rightVal)
case runtime.Float:
return float + rightVal
default:
return runtime.String(float.String() + rightVal.String())
}
}
func addLeftString(str runtime.String, input runtime.Value) runtime.Value {
return runtime.String(str.String() + input.String())
}
func Subtract(ctx context.Context, inputL, inputR runtime.Value) runtime.Value {
left := ToNumberOnly(ctx, inputL)
switch leftVal := left.(type) {
case runtime.Int:
return subtractLeftInt(ctx, leftVal, inputR)
case runtime.Float:
return subtractLeftFloat(ctx, leftVal, inputR)
default:
return runtime.ZeroInt
}
}
func subtractLeftInt(ctx context.Context, integer runtime.Int, input runtime.Value) runtime.Value {
right := ToNumberOnly(ctx, input)
switch rightVal := right.(type) {
case runtime.Int:
return integer - rightVal
case runtime.Float:
return runtime.Float(integer) - rightVal
default:
return runtime.ZeroInt
}
}
func subtractLeftFloat(ctx context.Context, float runtime.Float, input runtime.Value) runtime.Value {
right := ToNumberOnly(ctx, input)
switch rightVal := right.(type) {
case runtime.Int:
return float - runtime.Float(rightVal)
case runtime.Float:
return float - rightVal
default:
return runtime.ZeroInt
}
}
func Multiply(ctx context.Context, inputL, inputR runtime.Value) runtime.Value {
left := ToNumberOnly(ctx, inputL)
switch leftVal := left.(type) {
case runtime.Int:
return multiplyLeftInt(ctx, leftVal, inputR)
case runtime.Float:
return multiplyLeftFloat(ctx, leftVal, inputR)
default:
return runtime.ZeroInt
}
}
func multiplyLeftInt(ctx context.Context, integer runtime.Int, input runtime.Value) runtime.Value {
right := ToNumberOnly(ctx, input)
switch rightVal := right.(type) {
case runtime.Int:
return integer * rightVal
case runtime.Float:
return runtime.Float(integer) * rightVal
default:
return runtime.ZeroInt
}
}
func multiplyLeftFloat(ctx context.Context, float runtime.Float, input runtime.Value) runtime.Value {
right := ToNumberOnly(ctx, input)
switch rightVal := right.(type) {
case runtime.Int:
return float * runtime.Float(rightVal)
case runtime.Float:
return float * rightVal
default:
return runtime.ZeroInt
}
}
func Divide(ctx context.Context, inputL, inputR runtime.Value) runtime.Value {
left := ToNumberOnly(ctx, inputL)
switch leftVal := left.(type) {
case runtime.Int:
return divideLeftInt(ctx, leftVal, inputR)
case runtime.Float:
return divideLeftFloat(ctx, leftVal, inputR)
default:
return runtime.ZeroInt
}
}
func divideLeftInt(ctx context.Context, integer runtime.Int, input runtime.Value) runtime.Value {
right := ToNumberOnly(ctx, input)
switch rightVal := right.(type) {
case runtime.Int:
return integer / rightVal
case runtime.Float:
return runtime.Float(integer) / rightVal
default:
return runtime.ZeroInt
}
}
func divideLeftFloat(ctx context.Context, float runtime.Float, input runtime.Value) runtime.Value {
right := ToNumberOnly(ctx, input)
switch rightVal := right.(type) {
case runtime.Int:
return float / runtime.Float(rightVal)
case runtime.Float:
return float / rightVal
default:
return runtime.ZeroInt
}
}
func Modulus(ctx context.Context, inputL, inputR runtime.Value) runtime.Value {
left, _ := runtime.ToInt(ctx, inputL)
right, _ := runtime.ToInt(ctx, inputR)
return left % right
}
func Increment(ctx context.Context, input runtime.Value) runtime.Value {
left := ToNumberOnly(ctx, input)
switch value := left.(type) {
case runtime.Int:
return value + 1
case runtime.Float:
return value + 1
default:
return runtime.None
}
}
func Decrement(ctx context.Context, input runtime.Value) runtime.Value {
left := ToNumberOnly(ctx, input)
switch value := left.(type) {
case runtime.Int:
return value - 1
case runtime.Float:
return value - 1
default:
return runtime.None
}
}
func ToRange(ctx context.Context, left, right runtime.Value) (runtime.Value, error) {
start, err := runtime.ToInt(ctx, left)
if err != nil {
return runtime.ZeroInt, err
}
end, err := runtime.ToInt(ctx, right)
if err != nil {
return runtime.ZeroInt, err
}
return NewRange(int64(start), int64(end)), nil
}
func Like(left, right runtime.Value) (runtime.Boolean, error) {
if err := runtime.AssertString(left); err != nil {
// TODO: Return the error? AQL just returns false
return runtime.False, nil
}
if err := runtime.AssertString(right); err != nil {
// TODO: Return the error? AQL just returns false
return runtime.False, nil
}
r, err := glob.Compile(right.String())
if err != nil {
return runtime.False, errors.Wrap(err, "invalid glob pattern")
}
result := r.Match(left.String())
return runtime.NewBoolean(result), nil
}

View File

@@ -4,11 +4,10 @@ import (
"context"
"encoding/binary"
"fmt"
"github.com/MontFerret/ferret/pkg/runtime"
"hash/fnv"
"github.com/wI2L/jettison"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
type Range struct {
@@ -65,11 +64,11 @@ func (r *Range) Hash() uint64 {
return h.Sum64()
}
func (r *Range) Copy() core.Value {
func (r *Range) Copy() runtime.Value {
return NewRange(r.start, r.end)
}
func (r *Range) Iterate(_ context.Context) (core.Iterator, error) {
func (r *Range) Iterate(_ context.Context) (runtime.Iterator, error) {
return NewRangeIterator(r), nil
}
@@ -88,11 +87,11 @@ func (r *Range) MarshalJSON() ([]byte, error) {
return jettison.MarshalOpts(arr, jettison.NoHTMLEscaping())
}
func (r *Range) Compare(_ context.Context, other core.Value) (int64, error) {
func (r *Range) Compare(_ context.Context, other runtime.Value) (int64, error) {
otherRange, ok := other.(*Range)
if !ok {
return core.CompareTypes(r, other), nil
return runtime.CompareTypes(r, other), nil
}
if r.start == otherRange.start && r.end == otherRange.end {

View File

@@ -2,8 +2,7 @@ package internal
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
)
type RangeIterator struct {
@@ -13,7 +12,7 @@ type RangeIterator struct {
counter int64
}
func NewRangeIterator(values *Range) core.Iterator {
func NewRangeIterator(values *Range) runtime.Iterator {
if values.start <= values.end {
return &RangeIterator{values: values, pos: values.start, counter: -1}
}
@@ -29,7 +28,7 @@ func (iter *RangeIterator) HasNext(ctx context.Context) (bool, error) {
return iter.values.end <= iter.pos, nil
}
func (iter *RangeIterator) Next(ctx context.Context) (value core.Value, key core.Value, err error) {
func (iter *RangeIterator) Next(ctx context.Context) (value runtime.Value, key runtime.Value, err error) {
iter.counter++
if !iter.descending {
@@ -39,8 +38,8 @@ func (iter *RangeIterator) Next(ctx context.Context) (value core.Value, key core
}
if !iter.descending {
return core.Int(iter.pos - 1), core.Int(iter.counter), nil
return runtime.Int(iter.pos - 1), runtime.Int(iter.counter), nil
}
return core.Int(iter.pos + 1), core.Int(iter.counter), nil
return runtime.Int(iter.pos + 1), runtime.Int(iter.counter), nil
}

View File

@@ -2,7 +2,7 @@ package internal_test
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
"testing"
. "github.com/smartystreets/goconvey/convey"
@@ -21,8 +21,8 @@ func TestRangeIterator(t *testing.T) {
val, key, err := iter.Next(ctx)
So(err, ShouldBeNil)
So(val, ShouldEqual, core.NewInt(0))
So(key, ShouldEqual, core.NewInt(0))
So(val, ShouldEqual, runtime.NewInt(0))
So(key, ShouldEqual, runtime.NewInt(0))
hasNext, err = iter.HasNext(ctx)
So(err, ShouldBeNil)
@@ -41,14 +41,14 @@ func TestRangeIterator(t *testing.T) {
val, key, err := iter.Next(ctx)
So(err, ShouldBeNil)
So(val, ShouldEqual, core.NewInt(0))
So(key, ShouldEqual, core.NewInt(0))
So(val, ShouldEqual, runtime.NewInt(0))
So(key, ShouldEqual, runtime.NewInt(0))
val, key, err = iter.Next(ctx)
So(err, ShouldBeNil)
So(val, ShouldEqual, core.NewInt(1))
So(key, ShouldEqual, core.NewInt(1))
So(val, ShouldEqual, runtime.NewInt(1))
So(key, ShouldEqual, runtime.NewInt(1))
hasNext, err = iter.HasNext(ctx)
So(err, ShouldBeNil)
@@ -67,14 +67,14 @@ func TestRangeIterator(t *testing.T) {
val, key, err := iter.Next(ctx)
So(err, ShouldBeNil)
So(val, ShouldEqual, core.NewInt(1))
So(key, ShouldEqual, core.NewInt(0))
So(val, ShouldEqual, runtime.NewInt(1))
So(key, ShouldEqual, runtime.NewInt(0))
val, key, err = iter.Next(ctx)
So(err, ShouldBeNil)
So(val, ShouldEqual, core.NewInt(2))
So(key, ShouldEqual, core.NewInt(1))
So(val, ShouldEqual, runtime.NewInt(2))
So(key, ShouldEqual, runtime.NewInt(1))
hasNext, err = iter.HasNext(ctx)
So(err, ShouldBeNil)
@@ -86,7 +86,7 @@ func TestRangeIterator(t *testing.T) {
r := NewRange(0, 10)
iter := NewRangeIterator(r)
actual := make([]core.Int, 0, 10)
actual := make([]runtime.Int, 0, 10)
for {
hasNext, err := iter.HasNext(ctx)
@@ -95,10 +95,10 @@ func TestRangeIterator(t *testing.T) {
}
val, _, err := iter.Next(ctx)
actual = append(actual, val.(core.Int))
actual = append(actual, val.(runtime.Int))
}
So(actual, ShouldResemble, []core.Int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
So(actual, ShouldResemble, []runtime.Int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
})
Convey("Multiple descending values", t, func() {
@@ -106,7 +106,7 @@ func TestRangeIterator(t *testing.T) {
r := NewRange(10, 0)
iter := NewRangeIterator(r)
actual := make([]core.Int, 0, 10)
actual := make([]runtime.Int, 0, 10)
for {
hasNext, err := iter.HasNext(ctx)
@@ -115,10 +115,10 @@ func TestRangeIterator(t *testing.T) {
}
val, _, err := iter.Next(ctx)
actual = append(actual, val.(core.Int))
actual = append(actual, val.(runtime.Int))
}
So(actual, ShouldResemble, []core.Int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0})
So(actual, ShouldResemble, []runtime.Int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0})
})
}

View File

@@ -2,18 +2,17 @@ package internal
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime"
"hash/fnv"
"regexp"
"strings"
"github.com/wI2L/jettison"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
type Regexp regexp.Regexp
func NewRegexp(pattern core.String) (*Regexp, error) {
func NewRegexp(pattern runtime.String) (*Regexp, error) {
r, err := regexp.Compile(string(pattern))
if err != nil {
@@ -45,8 +44,8 @@ func (r *Regexp) Hash() uint64 {
return h.Sum64()
}
func (r *Regexp) Copy() core.Value {
copied, err := NewRegexp(core.String(r.String()))
func (r *Regexp) Copy() runtime.Value {
copied, err := NewRegexp(runtime.String(r.String()))
// it should never happen
if err != nil {
@@ -56,20 +55,20 @@ func (r *Regexp) Copy() core.Value {
return copied
}
func (r *Regexp) Compare(_ context.Context, other core.Value) (int64, error) {
func (r *Regexp) Compare(_ context.Context, other runtime.Value) (int64, error) {
otherRegexp, ok := other.(*Regexp)
if !ok {
return core.CompareTypes(r, other), nil
return runtime.CompareTypes(r, other), nil
}
return int64(strings.Compare(r.String(), otherRegexp.String())), nil
}
func (r *Regexp) Match(value core.Value) core.Boolean {
return core.Boolean((*regexp.Regexp)(r).MatchString(value.String()))
func (r *Regexp) Match(value runtime.Value) runtime.Boolean {
return runtime.Boolean((*regexp.Regexp)(r).MatchString(value.String()))
}
func (r *Regexp) MatchString(str core.String) core.Boolean {
return core.Boolean((*regexp.Regexp)(r).MatchString(string(str)))
func (r *Regexp) MatchString(str runtime.String) runtime.Boolean {
return runtime.Boolean((*regexp.Regexp)(r).MatchString(string(str)))
}

View File

@@ -2,22 +2,22 @@ package internal
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
)
type (
Sequence struct {
data core.List
data runtime.List
}
sequenceIterator struct {
data core.List
length core.Int
pos core.Int
data runtime.List
length runtime.Int
pos runtime.Int
}
)
func NewSequence(data core.List) *Sequence {
func NewSequence(data runtime.List) *Sequence {
return &Sequence{data}
}
@@ -25,7 +25,7 @@ func (iter *sequenceIterator) HasNext(_ context.Context) (bool, error) {
return iter.length > iter.pos, nil
}
func (iter *sequenceIterator) Next(ctx context.Context) (value core.Value, key core.Value, err error) {
func (iter *sequenceIterator) Next(ctx context.Context) (value runtime.Value, key runtime.Value, err error) {
iter.pos++
val, err := iter.data.Get(ctx, iter.pos-1)
@@ -39,7 +39,7 @@ func (iter *sequenceIterator) Next(ctx context.Context) (value core.Value, key c
return kv.Value, iter.pos - 1, nil
}
func (s *Sequence) Iterate(ctx context.Context) (core.Iterator, error) {
func (s *Sequence) Iterate(ctx context.Context) (runtime.Iterator, error) {
length, err := s.data.Length(ctx)
if err != nil {
@@ -68,7 +68,7 @@ func (s *Sequence) Hash() uint64 {
panic("implement me")
}
func (s *Sequence) Copy() core.Value {
func (s *Sequence) Copy() runtime.Value {
//TODO implement me
panic("implement me")
}

View File

@@ -1,28 +1,28 @@
package internal
import (
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime"
)
type Stack struct {
data []core.Value
data []runtime.Value
}
func NewStack(size int) *Stack {
return &Stack{data: make([]core.Value, 0, size)}
return &Stack{data: make([]runtime.Value, 0, size)}
}
func (s *Stack) Length() int {
return len(s.data)
}
func (s *Stack) Push(value core.Value) {
func (s *Stack) Push(value runtime.Value) {
s.data = append(s.data, value)
}
func (s *Stack) Pop() core.Value {
func (s *Stack) Pop() runtime.Value {
if len(s.data) == 0 {
return core.None
return runtime.None
}
last := len(s.data) - 1
@@ -48,6 +48,6 @@ func (s *Stack) Hash() uint64 {
panic("not supported")
}
func (s *Stack) Copy() core.Value {
func (s *Stack) Copy() runtime.Value {
panic("not supported")
}

View File

@@ -1,17 +1,17 @@
package runtime
package vm
type Opcode byte
const (
OpReturn Opcode = iota
OpMove // Move a value from register A to register B
OpLoadNone // Set None value to a register
OpLoadBool // Set a boolean value to a register
OpLoadZero // Set a zero value to a register
OpLoadConst // Load a constant to a register or a global variable
OpStoreGlobal // Store a value from register A to a global variable
OpLoadGlobal // Load a global variable to a register A
OpLoadParam // Load a parameter to a register A
OpReturn Opcode = iota
OpMove // Move a value from register A to register B
OpLoadNone // Set None value to a register
OpLoadBool // Set a boolean value to a register
OpLoadZero // Set a zero value to a register
OpLoadConst // Load a constant to a register or a global variable
OpStoreGlobal // Store a value from register A to a global variable
OpLoadGlobal // Load a global variable to a register A
OpLoadParam // Load a parameter to a register A
OpJump
OpJumpIfFalse
@@ -84,4 +84,5 @@ const (
OpWhileLoopPrep
OpWhileLoopNext
OpWhileLoopValue
OpSleep
)

View File

@@ -1,4 +1,4 @@
package runtime
package vm
import "fmt"

View File

@@ -1,22 +1,21 @@
package runtime
package vm
import (
"bytes"
"fmt"
"github.com/MontFerret/ferret/pkg/runtime"
"io"
"text/tabwriter"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
type (
Catch [3]int
Program struct {
Source *core.Source
Locations []core.Location
Source *runtime.Source
Locations []runtime.Location
Bytecode []Instruction
Constants []core.Value
Constants []runtime.Value
CatchTable []Catch
Params []string
Registers int

View File

@@ -1,4 +1,4 @@
package runtime
package vm
import "fmt"

View File

@@ -1,8 +1,8 @@
package runtime_test
package vm_test
import (
"fmt"
"github.com/MontFerret/ferret/pkg/runtime"
"github.com/MontFerret/ferret/pkg/vm"
"testing"
. "github.com/smartystreets/goconvey/convey"
@@ -10,7 +10,7 @@ import (
func TestNewSourceMap(t *testing.T) {
Convey("Should match", t, func() {
s := runtime.NewSourceMap("test", 1, 100)
s := vm.NewSourceMap("test", 1, 100)
sFmt := fmt.Sprintf("%s at %d:%d", "test", 1, 100)
So(s, ShouldNotBeNil)

View File

@@ -1,19 +1,18 @@
package runtime
package vm
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime"
"github.com/MontFerret/ferret/pkg/vm/internal"
"io"
"strings"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/internal"
)
type VM struct {
env *Environment
program *Program
globals map[string]core.Value
registers []core.Value
globals map[string]runtime.Value
registers []runtime.Value
pc int
}
@@ -24,7 +23,7 @@ func NewVM(program *Program) *VM {
return vm
}
func (vm *VM) Run(ctx context.Context, opts []EnvironmentOption) (core.Value, error) {
func (vm *VM) Run(ctx context.Context, opts []EnvironmentOption) (runtime.Value, error) {
tryCatch := func(pos int) (Catch, bool) {
for _, pair := range vm.program.CatchTable {
if pos >= pair[0] && pos <= pair[1] {
@@ -42,8 +41,8 @@ func (vm *VM) Run(ctx context.Context, opts []EnvironmentOption) (core.Value, er
}
vm.env = env
vm.registers = make([]core.Value, vm.program.Registers)
vm.globals = make(map[string]core.Value)
vm.registers = make([]runtime.Value, vm.program.Registers)
vm.globals = make(map[string]runtime.Value)
vm.pc = 0
bytecode := vm.program.Bytecode
constants := vm.program.Constants
@@ -58,11 +57,11 @@ loop:
switch op {
case OpLoadNone:
reg[dst] = core.None
reg[dst] = runtime.None
case OpLoadZero:
reg[dst] = core.ZeroInt
reg[dst] = runtime.ZeroInt
case OpLoadBool:
reg[dst] = core.Boolean(src1 == 1)
reg[dst] = runtime.Boolean(src1 == 1)
case OpMove:
reg[dst] = reg[src1]
case OpLoadConst:
@@ -77,15 +76,15 @@ loop:
case OpJump:
vm.pc = int(dst)
case OpJumpIfFalse:
if !core.ToBoolean(reg[src1]) {
if !runtime.ToBoolean(reg[src1]) {
vm.pc = int(dst)
}
case OpJumpIfTrue:
if core.ToBoolean(reg[src1]) {
if runtime.ToBoolean(reg[src1]) {
vm.pc = int(dst)
}
case OpJumpIfEmpty:
val, ok := reg[src1].(core.Measurable)
val, ok := reg[src1].(runtime.Measurable)
if ok {
size, err := val.Length(ctx)
@@ -116,29 +115,29 @@ loop:
case OpDecr:
reg[dst] = internal.Decrement(ctx, reg[dst])
case OpCastBool:
reg[dst] = core.ToBoolean(reg[src1])
reg[dst] = runtime.ToBoolean(reg[src1])
case OpNegate:
reg[dst] = core.Negate(reg[src1])
reg[dst] = runtime.Negate(reg[src1])
case OpFlipPositive:
reg[dst] = core.Positive(reg[src1])
reg[dst] = runtime.Positive(reg[src1])
case OpFlipNegative:
reg[dst] = core.Negative(reg[src1])
reg[dst] = runtime.Negative(reg[src1])
case OpComp:
reg[dst] = core.Int(core.CompareValues(reg[src1], reg[src2]))
reg[dst] = runtime.Int(runtime.CompareValues(reg[src1], reg[src2]))
case OpNot:
reg[dst] = !core.ToBoolean(reg[src1])
reg[dst] = !runtime.ToBoolean(reg[src1])
case OpEq:
reg[dst] = core.Boolean(core.CompareValues(reg[src1], reg[src2]) == 0)
reg[dst] = runtime.Boolean(runtime.CompareValues(reg[src1], reg[src2]) == 0)
case OpNeq:
reg[dst] = core.Boolean(core.CompareValues(reg[src1], reg[src2]) != 0)
reg[dst] = runtime.Boolean(runtime.CompareValues(reg[src1], reg[src2]) != 0)
case OpGt:
reg[dst] = core.Boolean(core.CompareValues(reg[src1], reg[src2]) > 0)
reg[dst] = runtime.Boolean(runtime.CompareValues(reg[src1], reg[src2]) > 0)
case OpLt:
reg[dst] = core.Boolean(core.CompareValues(reg[src1], reg[src2]) < 0)
reg[dst] = runtime.Boolean(runtime.CompareValues(reg[src1], reg[src2]) < 0)
case OpGte:
reg[dst] = core.Boolean(core.CompareValues(reg[src1], reg[src2]) >= 0)
reg[dst] = runtime.Boolean(runtime.CompareValues(reg[src1], reg[src2]) >= 0)
case OpLte:
reg[dst] = core.Boolean(core.CompareValues(reg[src1], reg[src2]) <= 0)
reg[dst] = runtime.Boolean(runtime.CompareValues(reg[src1], reg[src2]) <= 0)
case OpIn:
reg[dst] = internal.Contains(ctx, reg[src2], reg[src1])
case OpNotIn:
@@ -166,7 +165,7 @@ loop:
if err == nil {
reg[dst] = r.Match(reg[src1])
} else if _, catch := tryCatch(vm.pc); catch {
reg[dst] = core.False
reg[dst] = runtime.False
} else {
return nil, err
}
@@ -177,7 +176,7 @@ loop:
if err == nil {
reg[dst] = !r.Match(reg[src1])
} else if _, catch := tryCatch(vm.pc); catch {
reg[dst] = core.False
reg[dst] = runtime.False
} else {
return nil, err
}
@@ -188,7 +187,7 @@ loop:
size = src2.Register() - src1.Register() + 1
}
arr := core.NewArray(size)
arr := runtime.NewArray(size)
start := int(src1)
end := int(src1) + size
@@ -199,7 +198,7 @@ loop:
reg[dst] = arr
case OpObject:
obj := core.NewObject()
obj := runtime.NewObject()
var args int
if src1 > 0 {
@@ -213,7 +212,7 @@ loop:
key := reg[i]
value := reg[i+1]
_ = obj.Set(ctx, core.ToString(key), value)
_ = obj.Set(ctx, runtime.ToString(key), value)
}
reg[dst] = obj
@@ -222,36 +221,36 @@ loop:
prop := reg[src2]
switch getter := prop.(type) {
case core.String:
case runtime.String:
switch src := val.(type) {
case core.Keyed:
case runtime.Keyed:
out, err := src.Get(ctx, getter)
if err == nil {
reg[dst] = out
} else if op == OpLoadPropertyOptional {
reg[dst] = core.None
reg[dst] = runtime.None
} else {
return nil, err
}
default:
if op != OpLoadPropertyOptional {
return nil, core.TypeError(src, core.TypeMap)
return nil, runtime.TypeError(src, runtime.TypeMap)
}
reg[dst] = core.None
reg[dst] = runtime.None
}
case core.Float, core.Int:
case runtime.Float, runtime.Int:
// TODO: Optimize this. Avoid extra type conversion
idx, _ := core.ToInt(ctx, getter)
idx, _ := runtime.ToInt(ctx, getter)
switch src := val.(type) {
case core.Indexed:
case runtime.Indexed:
out, err := src.Get(ctx, idx)
if err == nil {
reg[dst] = out
} else if op == OpLoadPropertyOptional {
reg[dst] = core.None
reg[dst] = runtime.None
} else {
return nil, err
}
@@ -259,13 +258,25 @@ loop:
reg[dst] = src.Get(ctx, idx)
default:
if op != OpLoadPropertyOptional {
return nil, core.TypeError(src, core.TypeList)
return nil, runtime.TypeError(src, runtime.TypeList)
}
reg[dst] = core.None
reg[dst] = runtime.None
}
}
case OpCall, OpProtectedCall:
fnName := reg[dst].String()
fn := vm.env.GetFunction(fnName)
if fn == nil {
if op == OpProtectedCall {
reg[dst] = runtime.None
continue
} else {
return nil, runtime.Error(ErrFunctionNotFound, fnName)
}
}
var size int
if src1 > 0 {
@@ -274,24 +285,21 @@ loop:
start := int(src1)
end := int(src1) + size
args := make([]core.Value, size)
args := make([]runtime.Value, size)
// Iterate over registers starting from src1 and up to the src2
for i := start; i < end; i++ {
args[i-start] = reg[i]
}
fnName := reg[dst].String()
fn := vm.env.GetFunction(fnName)
out, err := fn(ctx, args...)
if err == nil {
reg[dst] = out
} else if op == OpProtectedCall {
reg[dst] = core.None
reg[dst] = runtime.None
} else if catch, ok := tryCatch(vm.pc); ok {
reg[dst] = core.None
reg[dst] = runtime.None
if catch[2] > 0 {
vm.pc = catch[2]
@@ -300,7 +308,7 @@ loop:
return nil, err
}
case OpLength:
val, ok := reg[src1].(core.Measurable)
val, ok := reg[src1].(runtime.Measurable)
if ok {
length, err := val.Length(ctx)
@@ -315,21 +323,21 @@ loop:
reg[dst] = length
} else if _, catch := tryCatch(vm.pc); catch {
reg[dst] = core.ZeroInt
reg[dst] = runtime.ZeroInt
} else {
return core.None, core.TypeError(reg[src1],
core.TypeString,
core.TypeList,
core.TypeMap,
core.TypeBinary,
core.TypeMeasurable,
return runtime.None, runtime.TypeError(reg[src1],
runtime.TypeString,
runtime.TypeList,
runtime.TypeMap,
runtime.TypeBinary,
runtime.TypeMeasurable,
)
}
case OpType:
reg[dst] = core.String(core.Reflect(reg[src1]))
reg[dst] = runtime.String(runtime.Reflect(reg[src1]))
case OpClose:
val, ok := reg[dst].(io.Closer)
reg[dst] = core.None
reg[dst] = runtime.None
if ok {
err := val.Close()
@@ -351,13 +359,21 @@ loop:
case OpLoopBegin:
reg[dst] = internal.NewDataSet(src1 == 1)
case OpLoopEnd:
ds := reg[src1].(*internal.DataSet)
reg[dst] = ds.ToList()
// TODO: Optimize this. Avoid extra type conversion
ds, ok := reg[src1].(*internal.DataSet)
if ok {
reg[dst] = ds.ToList()
} else {
// Recover from an error
reg[dst] = runtime.None
}
case OpForLoopPrep:
input := reg[src1]
switch src := input.(type) {
case core.Iterable:
case runtime.Iterable:
iterator, err := src.Iterate(ctx)
if err != nil {
@@ -370,7 +386,7 @@ loop:
// Fall back to an empty iterator
reg[dst] = internal.NoopIter
} else {
return nil, core.TypeError(src, core.TypeIterable)
return nil, runtime.TypeError(src, runtime.TypeIterable)
}
}
case OpForLoopNext:
@@ -395,9 +411,9 @@ loop:
iterator := reg[src1].(*internal.Iterator)
reg[dst] = iterator.Key()
case OpWhileLoopPrep:
reg[dst] = core.Int(-1)
reg[dst] = runtime.Int(-1)
case OpWhileLoopNext:
cond := core.ToBoolean(reg[src1])
cond := runtime.ToBoolean(reg[src1])
if cond {
reg[dst] = internal.Increment(ctx, reg[dst])
@@ -435,8 +451,8 @@ loop:
reg[dst] = pair.Key
case OpSortSwap:
ds := reg[dst].(*internal.DataSet)
i, _ := core.ToInt(ctx, reg[src1])
j, _ := core.ToInt(ctx, reg[src2])
i, _ := runtime.ToInt(ctx, reg[src1])
j, _ := runtime.ToInt(ctx, reg[src2])
ds.Swap(ctx, i, j)
case OpGroupPrep:
reg[dst] = internal.NewCollector()
@@ -445,6 +461,20 @@ loop:
key := reg[src1]
value := reg[src2]
collector.Add(key, value)
case OpSleep:
dur, err := runtime.ToInt(ctx, reg[dst])
if err != nil {
if _, catch := tryCatch(vm.pc); catch {
continue
} else {
return nil, err
}
}
if err := internal.Sleep(ctx, dur); err != nil {
return nil, err
}
case OpReturn:
break loop
}
@@ -475,7 +505,7 @@ func (vm *VM) validateParams(env *Environment) error {
}
if len(missedParams) > 0 {
return core.Error(ErrMissedParam, strings.Join(missedParams, ", "))
return runtime.Error(ErrMissedParam, strings.Join(missedParams, ", "))
}
return nil