From a0158166c0314d76afde47e169dff5729915bdf1 Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Mon, 5 May 2025 16:29:56 -0400 Subject: [PATCH] Re-organized project layout --- e2e/cli.go | 6 +- examples/extensible/main.go | 13 +- ferret.go | 14 +- pkg/compiler/allocator.go | 34 +- pkg/compiler/compiler.go | 10 +- pkg/compiler/compiler_exec_test.go | 147 +++--- pkg/compiler/compiler_setup_test.go | 35 +- pkg/compiler/compiler_test.go | 115 +++-- pkg/compiler/emitter.go | 54 +-- pkg/compiler/helpers.go | 5 +- pkg/compiler/loops.go | 16 +- pkg/compiler/namespace.go | 21 +- pkg/compiler/namespace_test.go | 26 +- pkg/compiler/symbols.go | 49 +- pkg/compiler/visitor.go | 423 +++++++++--------- pkg/runtime/{core => }/array.go | 2 +- pkg/runtime/{core => }/array_iter.go | 2 +- pkg/runtime/{core => }/array_iter_test.go | 20 +- pkg/runtime/{core => }/array_test.go | 150 +++---- pkg/runtime/{core => }/assertions.go | 16 +- pkg/runtime/{core => }/binary.go | 2 +- pkg/runtime/{core => }/boolean.go | 2 +- pkg/runtime/{core => }/boolean_test.go | 16 +- pkg/runtime/{core => }/casting.go | 2 +- pkg/runtime/{core => }/cloneable.go | 4 +- pkg/runtime/{core => }/collections.go | 38 +- pkg/runtime/{core => }/comparable.go | 2 +- pkg/runtime/core/errors.go | 67 --- pkg/runtime/{core => }/date_time.go | 2 +- pkg/runtime/{core => }/date_time_test.go | 10 +- pkg/runtime/errors.go | 73 ++- pkg/runtime/{core => }/errors_test.go | 11 +- pkg/runtime/events/builtin.go | 65 --- pkg/runtime/events/helpers.go | 56 --- pkg/runtime/events/iterator.go | 42 -- pkg/runtime/events/observable.go | 33 -- pkg/runtime/{core => }/float.go | 2 +- pkg/runtime/{core => }/float_test.go | 12 +- pkg/runtime/{core => }/function.go | 2 +- pkg/runtime/{core => }/function_test.go | 39 +- pkg/runtime/{core => }/helpers.go | 2 +- pkg/runtime/{core => }/helpers_test.go | 72 +-- pkg/runtime/{core => }/int.go | 2 +- pkg/runtime/{core => }/int_test.go | 12 +- pkg/runtime/internal/helpers.go | 77 ---- pkg/runtime/internal/noop_iter.go | 16 - pkg/runtime/internal/operators.go | 267 ----------- pkg/runtime/{core => }/iterator.go | 36 +- pkg/runtime/{core => }/measurable.go | 2 +- pkg/runtime/{core => }/namespace.go | 2 +- pkg/runtime/{core => }/none.go | 2 +- pkg/runtime/{core => }/object.go | 4 +- pkg/runtime/{core => }/object_iter.go | 2 +- pkg/runtime/{core => }/object_iter_test.go | 20 +- pkg/runtime/{core => }/object_test.go | 206 ++++----- pkg/runtime/{core => }/source.go | 2 +- pkg/runtime/{core => }/string.go | 2 +- pkg/runtime/{core => }/string_test.go | 18 +- pkg/runtime/{core => }/type.go | 4 +- pkg/runtime/{core => }/value.go | 2 +- pkg/runtime/values/values.go | 9 - pkg/{runtime => vm}/env.go | 20 +- pkg/{runtime => vm}/env_options.go | 14 +- pkg/vm/errors.go | 27 ++ pkg/{runtime => vm}/instruction.go | 2 +- pkg/{runtime => vm}/internal/boxed.go | 5 +- pkg/{runtime => vm}/internal/collector.go | 6 +- pkg/{runtime => vm}/internal/dataset.go | 20 +- pkg/vm/internal/helpers.go | 91 ++++ pkg/{runtime => vm}/internal/iterator.go | 19 +- pkg/{runtime => vm}/internal/kv.go | 11 +- pkg/vm/internal/noop_iter.go | 16 + pkg/vm/internal/operators.go | 266 +++++++++++ pkg/{runtime => vm}/internal/range.go | 11 +- pkg/{runtime => vm}/internal/range_iter.go | 11 +- .../internal/range_iter_test.go | 34 +- pkg/{runtime => vm}/internal/regexp.go | 21 +- pkg/{runtime => vm}/internal/sequence.go | 18 +- pkg/{runtime => vm}/internal/stack.go | 14 +- pkg/{runtime => vm}/opcode.go | 21 +- pkg/{runtime => vm}/operand.go | 2 +- pkg/{runtime => vm}/program.go | 11 +- pkg/{runtime => vm}/source_map.go | 2 +- pkg/{runtime => vm}/source_map_test.go | 6 +- pkg/{runtime => vm}/vm.go | 166 ++++--- 85 files changed, 1556 insertions(+), 1655 deletions(-) rename pkg/runtime/{core => }/array.go (99%) rename pkg/runtime/{core => }/array_iter.go (96%) rename pkg/runtime/{core => }/array_iter_test.go (80%) rename pkg/runtime/{core => }/array_test.go (77%) rename pkg/runtime/{core => }/assertions.go (78%) rename pkg/runtime/{core => }/binary.go (98%) rename pkg/runtime/{core => }/boolean.go (98%) rename pkg/runtime/{core => }/boolean_test.go (84%) rename pkg/runtime/{core => }/casting.go (99%) rename pkg/runtime/{core => }/cloneable.go (82%) rename pkg/runtime/{core => }/collections.go (96%) rename pkg/runtime/{core => }/comparable.go (95%) delete mode 100644 pkg/runtime/core/errors.go rename pkg/runtime/{core => }/date_time.go (99%) rename pkg/runtime/{core => }/date_time_test.go (75%) rename pkg/runtime/{core => }/errors_test.go (81%) delete mode 100644 pkg/runtime/events/builtin.go delete mode 100644 pkg/runtime/events/helpers.go delete mode 100644 pkg/runtime/events/iterator.go delete mode 100644 pkg/runtime/events/observable.go rename pkg/runtime/{core => }/float.go (99%) rename pkg/runtime/{core => }/float_test.go (75%) rename pkg/runtime/{core => }/function.go (99%) rename pkg/runtime/{core => }/function_test.go (79%) rename pkg/runtime/{core => }/helpers.go (99%) rename pkg/runtime/{core => }/helpers_test.go (81%) rename pkg/runtime/{core => }/int.go (99%) rename pkg/runtime/{core => }/int_test.go (76%) delete mode 100644 pkg/runtime/internal/helpers.go delete mode 100644 pkg/runtime/internal/noop_iter.go delete mode 100644 pkg/runtime/internal/operators.go rename pkg/runtime/{core => }/iterator.go (67%) rename pkg/runtime/{core => }/measurable.go (93%) rename pkg/runtime/{core => }/namespace.go (93%) rename pkg/runtime/{core => }/none.go (97%) rename pkg/runtime/{core => }/object.go (99%) rename pkg/runtime/{core => }/object_iter.go (97%) rename pkg/runtime/{core => }/object_iter_test.go (84%) rename pkg/runtime/{core => }/object_test.go (55%) rename pkg/runtime/{core => }/source.go (98%) rename pkg/runtime/{core => }/string.go (99%) rename pkg/runtime/{core => }/string_test.go (76%) rename pkg/runtime/{core => }/type.go (95%) rename pkg/runtime/{core => }/value.go (93%) delete mode 100644 pkg/runtime/values/values.go rename pkg/{runtime => vm}/env.go (62%) rename pkg/{runtime => vm}/env_options.go (69%) create mode 100644 pkg/vm/errors.go rename pkg/{runtime => vm}/instruction.go (93%) rename pkg/{runtime => vm}/internal/boxed.go (88%) rename pkg/{runtime => vm}/internal/collector.go (81%) rename pkg/{runtime => vm}/internal/dataset.go (62%) create mode 100644 pkg/vm/internal/helpers.go rename pkg/{runtime => vm}/internal/iterator.go (71%) rename pkg/{runtime => vm}/internal/kv.go (80%) create mode 100644 pkg/vm/internal/noop_iter.go create mode 100644 pkg/vm/internal/operators.go rename pkg/{runtime => vm}/internal/range.go (89%) rename pkg/{runtime => vm}/internal/range_iter.go (65%) rename pkg/{runtime => vm}/internal/range_iter_test.go (73%) rename pkg/{runtime => vm}/internal/regexp.go (59%) rename pkg/{runtime => vm}/internal/sequence.go (69%) rename pkg/{runtime => vm}/internal/stack.go (68%) rename pkg/{runtime => vm}/opcode.go (64%) rename pkg/{runtime => vm}/operand.go (97%) rename pkg/{runtime => vm}/program.go (91%) rename pkg/{runtime => vm}/source_map.go (95%) rename pkg/{runtime => vm}/source_map_test.go (75%) rename pkg/{runtime => vm}/vm.go (72%) diff --git a/e2e/cli.go b/e2e/cli.go index 2f6a4150..3a3a5dda 100644 --- a/e2e/cli.go +++ b/e2e/cli.go @@ -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 diff --git a/examples/extensible/main.go b/examples/extensible/main.go index 9c3a9b70..ffe20dc7 100644 --- a/examples/extensible/main.go +++ b/examples/extensible/main.go @@ -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 := ` diff --git a/ferret.go b/ferret.go index 03b110e3..c646c9b1 100644 --- a/ferret.go +++ b/ferret.go @@ -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 { diff --git a/pkg/compiler/allocator.go b/pkg/compiler/allocator.go index 17fd4f3a..aabdf496 100644 --- a/pkg/compiler/allocator.go +++ b/pkg/compiler/allocator.go @@ -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 diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index b0748067..6ea967b6 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -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 { diff --git a/pkg/compiler/compiler_exec_test.go b/pkg/compiler/compiler_exec_test.go index 955f3f17..a47663ff 100644 --- a/pkg/compiler/compiler_exec_test.go +++ b/pkg/compiler/compiler_exec_test.go @@ -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 })) } diff --git a/pkg/compiler/compiler_setup_test.go b/pkg/compiler/compiler_setup_test.go index a926a544..a34cdbe2 100644 --- a/pkg/compiler/compiler_setup_test.go +++ b/pkg/compiler/compiler_setup_test.go @@ -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...) } diff --git a/pkg/compiler/compiler_test.go b/pkg/compiler/compiler_test.go index 7674fde8..2e5805c5 100644 --- a/pkg/compiler/compiler_test.go +++ b/pkg/compiler/compiler_test.go @@ -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"), }, }, }, diff --git a/pkg/compiler/emitter.go b/pkg/compiler/emitter.go index a4472041..5d8a2e2e 100644 --- a/pkg/compiler/emitter.go +++ b/pkg/compiler/emitter.go @@ -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}, }) } diff --git a/pkg/compiler/helpers.go b/pkg/compiler/helpers.go index eb3f4ca6..1ffb8feb 100644 --- a/pkg/compiler/helpers.go +++ b/pkg/compiler/helpers.go @@ -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::". // diff --git a/pkg/compiler/loops.go b/pkg/compiler/loops.go index 7aa051ea..3c891fe9 100644 --- a/pkg/compiler/loops.go +++ b/pkg/compiler/loops.go @@ -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 { diff --git a/pkg/compiler/namespace.go b/pkg/compiler/namespace.go index 439671f6..f20e8293 100644 --- a/pkg/compiler/namespace.go +++ b/pkg/compiler/namespace.go @@ -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 } diff --git a/pkg/compiler/namespace_test.go b/pkg/compiler/namespace_test.go index 365f9734..506987ad 100644 --- a/pkg/compiler/namespace_test.go +++ b/pkg/compiler/namespace_test.go @@ -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) diff --git a/pkg/compiler/symbols.go b/pkg/compiler/symbols.go index 2f81711b..cabe57d6 100644 --- a/pkg/compiler/symbols.go +++ b/pkg/compiler/symbols.go @@ -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 diff --git a/pkg/compiler/visitor.go b/pkg/compiler/visitor.go index 7644e4f5..eb20bf59 100644 --- a/pkg/compiler/visitor.go +++ b/pkg/compiler/visitor.go @@ -1,6 +1,8 @@ package compiler import ( + "github.com/MontFerret/ferret/pkg/runtime" + "github.com/MontFerret/ferret/pkg/vm" "regexp" "strconv" "strings" @@ -8,8 +10,6 @@ import ( "github.com/antlr4-go/antlr/v4" "github.com/MontFerret/ferret/pkg/parser/fql" - "github.com/MontFerret/ferret/pkg/runtime" - "github.com/MontFerret/ferret/pkg/runtime/core" ) type visitor struct { @@ -20,7 +20,7 @@ type visitor struct { registers *RegisterAllocator symbols *SymbolTable loops *LoopTable - catchTable []runtime.Catch + catchTable []vm.Catch } const ( @@ -32,6 +32,13 @@ const ( forScope = "for" ) +// Runtime functions +const ( + runtimeTypename = "TYPENAME" + runtimeLength = "LENGTH" + runtimeWait = "WAIT" +) + func newVisitor(src string) *visitor { v := new(visitor) v.BaseFqlParserVisitor = new(fql.BaseFqlParserVisitor) @@ -40,7 +47,7 @@ func newVisitor(src string) *visitor { v.symbols = NewSymbolTable(v.registers) v.loops = NewLoopTable(v.registers) v.emitter = NewEmitter() - v.catchTable = make([]runtime.Catch, 0) + v.catchTable = make([]vm.Catch, 0) return v } @@ -74,25 +81,25 @@ func (v *visitor) VisitBodyStatement(ctx *fql.BodyStatementContext) interface{} return c.Accept(v) } - panic(core.Error(ErrUnexpectedToken, ctx.GetText())) + panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) } func (v *visitor) VisitBodyExpression(ctx *fql.BodyExpressionContext) interface{} { if c := ctx.ForExpression(); c != nil { - out, ok := c.Accept(v).(runtime.Operand) + out, ok := c.Accept(v).(vm.Operand) - if ok && out != runtime.NoopOperand { - v.emitter.EmitAB(runtime.OpMove, runtime.NoopOperand, out) + if ok && out != vm.NoopOperand { + v.emitter.EmitAB(vm.OpMove, vm.NoopOperand, out) } - v.emitter.Emit(runtime.OpReturn) + v.emitter.Emit(vm.OpReturn) return out } else if c := ctx.ReturnExpression(); c != nil { return c.Accept(v) } - panic(core.Error(ErrUnexpectedToken, ctx.GetText())) + panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) } func (v *visitor) VisitHead(_ *fql.HeadContext) interface{} { @@ -116,7 +123,7 @@ func (v *visitor) VisitForExpression(ctx *fql.ForExpressionContext) interface{} loop := v.loops.EnterLoop(v.loopType(ctx), v.loopKind(ctx), distinct) if loop.Kind == ForLoop { - loop.Src = ctx.ForExpressionSource().Accept(v).(runtime.Operand) + loop.Src = ctx.ForExpressionSource().Accept(v).(vm.Operand) if val := ctx.GetValueVariable(); val != nil { if txt := val.GetText(); txt != "" && txt != ignorePseudoVariable { @@ -165,9 +172,9 @@ func (v *visitor) VisitForExpression(ctx *fql.ForExpressionContext) interface{} // RETURN if loop.Type != PassThroughLoop { c := returnRuleCtx.(*fql.ReturnExpressionContext) - expReg := c.Expression().Accept(v).(runtime.Operand) + expReg := c.Expression().Accept(v).(vm.Operand) - v.emitter.EmitAB(runtime.OpLoopPush, loop.Result, expReg) + v.emitter.EmitAB(vm.OpLoopPush, loop.Result, expReg) } else if returnRuleCtx != nil { returnRuleCtx.Accept(v) } @@ -209,7 +216,7 @@ func (v *visitor) VisitForExpressionSource(ctx *fql.ForExpressionSourceContext) return c.Accept(v) } - panic(core.Error(ErrUnexpectedToken, ctx.GetText())) + panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) } func (v *visitor) VisitForExpressionBody(ctx *fql.ForExpressionBodyContext) interface{} { @@ -221,7 +228,7 @@ func (v *visitor) VisitForExpressionBody(ctx *fql.ForExpressionBodyContext) inte return c.Accept(v) } - panic(core.Error(ErrUnexpectedToken, ctx.GetText())) + panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) } func (v *visitor) VisitForExpressionClause(ctx *fql.ForExpressionClauseContext) interface{} { @@ -241,12 +248,12 @@ func (v *visitor) VisitForExpressionClause(ctx *fql.ForExpressionClauseContext) return c.Accept(v) } - panic(core.Error(ErrUnexpectedToken, ctx.GetText())) + panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) } func (v *visitor) VisitFilterClause(ctx *fql.FilterClauseContext) interface{} { - src1 := ctx.Expression().Accept(v).(runtime.Operand) - v.emitter.EmitJumpc(runtime.OpJumpIfFalse, v.loops.Loop().Jump, src1) + src1 := ctx.Expression().Accept(v).(vm.Operand) + v.emitter.EmitJumpc(vm.OpJumpIfFalse, v.loops.Loop().Jump, src1) return nil } @@ -255,10 +262,10 @@ func (v *visitor) VisitLimitClause(ctx *fql.LimitClauseContext) interface{} { clauses := ctx.AllLimitClauseValue() if len(clauses) == 1 { - return v.visitLimit(clauses[0].Accept(v).(runtime.Operand)) + return v.visitLimit(clauses[0].Accept(v).(vm.Operand)) } else { - v.visitOffset(clauses[0].Accept(v).(runtime.Operand)) - v.visitLimit(clauses[1].Accept(v).(runtime.Operand)) + v.visitOffset(clauses[0].Accept(v).(vm.Operand)) + v.visitLimit(clauses[1].Accept(v).(vm.Operand)) } return nil @@ -332,9 +339,9 @@ func (v *visitor) VisitCollectClause(ctx *fql.CollectClauseContext) interface{} // } func (v *visitor) VisitSortClause(ctx *fql.SortClauseContext) interface{} { loop := v.loops.Loop() - v.emitter.EmitAB(runtime.OpLoopPushIter, loop.Result, loop.Iterator) - v.emitter.EmitJump(runtime.OpJump, loop.Jump-loop.JumpOffset) - v.emitter.EmitA(runtime.OpClose, loop.Iterator) + v.emitter.EmitAB(vm.OpLoopPushIter, loop.Result, loop.Iterator) + v.emitter.EmitJump(vm.OpJump, loop.Jump-loop.JumpOffset) + v.emitter.EmitA(vm.OpClose, loop.Iterator) if loop.Kind == ForLoop { v.emitter.PatchJump(loop.Jump) @@ -354,52 +361,52 @@ func (v *visitor) VisitSortClause(ctx *fql.SortClauseContext) interface{} { // Init sort // Create a new stack - v.emitter.EmitA(runtime.OpSortPrep, stack) + v.emitter.EmitA(vm.OpSortPrep, stack) // low = 0 - v.emitter.EmitA(runtime.OpLoadZero, low) + v.emitter.EmitA(vm.OpLoadZero, low) // high = len(arr) - 1 - v.emitter.EmitAB(runtime.OpLength, high, loop.Result) - v.emitter.EmitA(runtime.OpDecr, high) + v.emitter.EmitAB(vm.OpLength, high, loop.Result) + v.emitter.EmitA(vm.OpDecr, high) // Push initial range: stack = append(stack, high, low) - v.emitter.EmitAB(runtime.OpSortPush, stack, high) - v.emitter.EmitAB(runtime.OpSortPush, stack, low) + v.emitter.EmitAB(vm.OpSortPush, stack, high) + v.emitter.EmitAB(vm.OpSortPush, stack, low) // Main stack loop // while len(stack) > 0 stackLoopStart := v.emitter.Size() - stackLoopExitJmp := v.emitter.EmitJumpc(runtime.OpJumpIfEmpty, jumpPlaceholder, stack) + stackLoopExitJmp := v.emitter.EmitJumpc(vm.OpJumpIfEmpty, jumpPlaceholder, stack) // Pop the range: low = stack.pop(), high = stack.pop() - v.emitter.EmitAB(runtime.OpSortPop, low, stack) - v.emitter.EmitAB(runtime.OpSortPop, high, stack) + v.emitter.EmitAB(vm.OpSortPop, low, stack) + v.emitter.EmitAB(vm.OpSortPop, high, stack) // if low < high // comp = low < high - v.emitter.EmitABC(runtime.OpLt, comp, low, high) - v.emitter.EmitJumpc(runtime.OpJumpIfFalse, stackLoopStart, comp) + v.emitter.EmitABC(vm.OpLt, comp, low, high) + v.emitter.EmitJumpc(vm.OpJumpIfFalse, stackLoopStart, comp) // Otherwise start the partition // pivot := arr[high] - v.emitter.EmitABC(runtime.OpLoadProperty, pivot, loop.Result, high) + v.emitter.EmitABC(vm.OpLoadProperty, pivot, loop.Result, high) // i = low - 1 - v.emitter.EmitAB(runtime.OpMove, i, low) - v.emitter.EmitA(runtime.OpDecr, i) + v.emitter.EmitAB(vm.OpMove, i, low) + v.emitter.EmitA(vm.OpDecr, i) // j = low - v.emitter.EmitAB(runtime.OpMove, j, low) + v.emitter.EmitAB(vm.OpMove, j, low) // for j < high partitionLoopStart := v.emitter.Size() - v.emitter.EmitABC(runtime.OpLt, comp, j, high) - partitionLoopEnd := v.emitter.EmitJumpc(runtime.OpJumpIfFalse, jumpPlaceholder, comp) + v.emitter.EmitABC(vm.OpLt, comp, j, high) + partitionLoopEnd := v.emitter.EmitJumpc(vm.OpJumpIfFalse, jumpPlaceholder, comp) // Load current value into n // n = arr[j] - v.emitter.EmitABC(runtime.OpLoadProperty, n, loop.Result, j) + v.emitter.EmitABC(vm.OpLoadProperty, n, loop.Result, j) // Collect all sort clauses (mostly one or two) clauses := ctx.AllSortClauseExpression() @@ -412,23 +419,23 @@ func (v *visitor) VisitSortClause(ctx *fql.SortClauseContext) interface{} { pivotProp := v.visitSortClauseExpression(sort, loop, pivot) currentProp := v.visitSortClauseExpression(sort, loop, n) - comparator := runtime.OpLte + comparator := vm.OpLte direction := clause.SortDirection() if direction != nil && strings.ToLower(direction.GetText()) == "desc" { - comparator = runtime.OpGte + comparator = vm.OpGte } // comp = current <= pivot or current >= pivot v.emitter.EmitABC(comparator, comp, currentProp, pivotProp) // If comp is false, jump to loop end - skipSwapJumps[i] = v.emitter.EmitJumpc(runtime.OpJumpIfFalse, jumpPlaceholder, comp) + skipSwapJumps[i] = v.emitter.EmitJumpc(vm.OpJumpIfFalse, jumpPlaceholder, comp) } // i++ - v.emitter.EmitA(runtime.OpIncr, i) + v.emitter.EmitA(vm.OpIncr, i) // swap arr[i], arr[j] - v.emitter.EmitABC(runtime.OpSortSwap, loop.Result, i, j) + v.emitter.EmitABC(vm.OpSortSwap, loop.Result, i, j) // Patch all clause skip swap jumps for _, jmp := range skipSwapJumps { @@ -436,37 +443,37 @@ func (v *visitor) VisitSortClause(ctx *fql.SortClauseContext) interface{} { } // j++ - v.emitter.EmitA(runtime.OpIncr, j) - v.emitter.EmitJump(runtime.OpJump, partitionLoopStart) + v.emitter.EmitA(vm.OpIncr, j) + v.emitter.EmitJump(vm.OpJump, partitionLoopStart) // End of partition loop v.emitter.PatchJumpNext(partitionLoopEnd) // i++ - v.emitter.EmitA(runtime.OpIncr, i) + v.emitter.EmitA(vm.OpIncr, i) // swap arr[i], arr[high] - v.emitter.EmitABC(runtime.OpSortSwap, loop.Result, i, high) + v.emitter.EmitABC(vm.OpSortSwap, loop.Result, i, high) // Push left subarray: stack = append(stack, low, i-1) - v.emitter.EmitAB(runtime.OpMove, n, i) - v.emitter.EmitA(runtime.OpDecr, n) - v.emitter.EmitAB(runtime.OpSortPush, stack, n) - v.emitter.EmitAB(runtime.OpSortPush, stack, low) + v.emitter.EmitAB(vm.OpMove, n, i) + v.emitter.EmitA(vm.OpDecr, n) + v.emitter.EmitAB(vm.OpSortPush, stack, n) + v.emitter.EmitAB(vm.OpSortPush, stack, low) // Push right subarray: stack = append(stack, {i+1, high}) - v.emitter.EmitAB(runtime.OpMove, n, i) - v.emitter.EmitA(runtime.OpIncr, n) - v.emitter.EmitAB(runtime.OpSortPush, stack, high) - v.emitter.EmitAB(runtime.OpSortPush, stack, n) + v.emitter.EmitAB(vm.OpMove, n, i) + v.emitter.EmitA(vm.OpIncr, n) + v.emitter.EmitAB(vm.OpSortPush, stack, high) + v.emitter.EmitAB(vm.OpSortPush, stack, n) // Jump to stack loop beginning - v.emitter.EmitJump(runtime.OpJump, stackLoopStart) + v.emitter.EmitJump(vm.OpJump, stackLoopStart) // Patch the stack loop exit jump v.emitter.PatchJumpNext(stackLoopExitJmp) // Replace source with sorted array - v.emitter.EmitAB(runtime.OpLoopSequence, loop.Src, loop.Result) + v.emitter.EmitAB(vm.OpLoopSequence, loop.Src, loop.Result) // Create new for loop // TODO: Reuse existing DataSet instance @@ -475,7 +482,7 @@ func (v *visitor) VisitSortClause(ctx *fql.SortClauseContext) interface{} { return nil } -func (v *visitor) visitSortClauseExpression(ctx *fql.SortClauseExpressionContext, loop *Loop, src runtime.Operand) runtime.Operand { +func (v *visitor) visitSortClauseExpression(ctx *fql.SortClauseExpressionContext, loop *Loop, src vm.Operand) vm.Operand { var isVar bool if e := ctx.Expression(); e != nil { @@ -486,7 +493,7 @@ func (v *visitor) visitSortClauseExpression(ctx *fql.SortClauseExpressionContext } } - var dst runtime.Operand + var dst vm.Operand // if the clause uses plain variable that means we need to use value as is if isVar { @@ -499,38 +506,38 @@ func (v *visitor) visitSortClauseExpression(ctx *fql.SortClauseExpressionContext // We override the loop variables with the pivot value if loop.ValueName != "" { - v.emitter.EmitAB(runtime.OpSortValue, dst, src) + v.emitter.EmitAB(vm.OpSortValue, dst, src) } if loop.KeyName != "" { - v.emitter.EmitAB(runtime.OpSortKey, dst, src) + v.emitter.EmitAB(vm.OpSortKey, dst, src) } if !isVar { - return ctx.Expression().Accept(v).(runtime.Operand) + return ctx.Expression().Accept(v).(vm.Operand) } return dst } -func (v *visitor) visitOffset(src1 runtime.Operand) interface{} { +func (v *visitor) visitOffset(src1 vm.Operand) interface{} { state := v.registers.Allocate(State) - v.emitter.EmitA(runtime.OpIncr, state) + v.emitter.EmitA(vm.OpIncr, state) comp := v.registers.Allocate(Temp) - v.emitter.EmitABC(runtime.OpGt, comp, state, src1) - v.emitter.EmitJumpc(runtime.OpJumpIfFalse, v.loops.Loop().Jump, comp) + v.emitter.EmitABC(vm.OpGt, comp, state, src1) + v.emitter.EmitJumpc(vm.OpJumpIfFalse, v.loops.Loop().Jump, comp) return state } -func (v *visitor) visitLimit(src1 runtime.Operand) interface{} { +func (v *visitor) visitLimit(src1 vm.Operand) interface{} { state := v.registers.Allocate(State) - v.emitter.EmitA(runtime.OpIncr, state) + v.emitter.EmitA(vm.OpIncr, state) comp := v.registers.Allocate(Temp) - v.emitter.EmitABC(runtime.OpGt, comp, state, src1) - v.emitter.EmitJumpc(runtime.OpJumpIfTrue, v.loops.Loop().Jump, comp) + v.emitter.EmitABC(vm.OpGt, comp, state, src1) + v.emitter.EmitJumpc(vm.OpJumpIfTrue, v.loops.Loop().Jump, comp) return state } @@ -556,7 +563,7 @@ func (v *visitor) VisitLimitClauseValue(ctx *fql.LimitClauseValueContext) interf return c.Accept(v) } - panic(core.Error(ErrUnexpectedToken, ctx.GetText())) + panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) } func (v *visitor) VisitForExpressionStatement(ctx *fql.ForExpressionStatementContext) interface{} { @@ -568,7 +575,7 @@ func (v *visitor) VisitForExpressionStatement(ctx *fql.ForExpressionStatementCon return c.Accept(v) } - panic(core.Error(ErrUnexpectedToken, ctx.GetText())) + panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) } func (v *visitor) VisitFunctionCallExpression(ctx *fql.FunctionCallExpressionContext) interface{} { @@ -599,8 +606,8 @@ func (v *visitor) VisitMemberExpression(ctx *fql.MemberExpressionContext) interf mesOut = v.visitFunctionCall(c.(*fql.FunctionCallContext), segment.ErrorOperator() != nil) } - var dst runtime.Operand - src1 := mesOut.(runtime.Operand) + var dst vm.Operand + src1 := mesOut.(vm.Operand) for _, segment := range segments { var out2 interface{} @@ -612,13 +619,13 @@ func (v *visitor) VisitMemberExpression(ctx *fql.MemberExpressionContext) interf out2 = c.Accept(v) } - src2 := out2.(runtime.Operand) + src2 := out2.(vm.Operand) dst = v.registers.Allocate(Temp) if p.ErrorOperator() != nil { - v.emitter.EmitABC(runtime.OpLoadPropertyOptional, dst, src1, src2) + v.emitter.EmitABC(vm.OpLoadPropertyOptional, dst, src1, src2) } else { - v.emitter.EmitABC(runtime.OpLoadProperty, dst, src1, src2) + v.emitter.EmitABC(vm.OpLoadProperty, dst, src1, src2) } src1 = dst @@ -629,10 +636,10 @@ func (v *visitor) VisitMemberExpression(ctx *fql.MemberExpressionContext) interf func (v *visitor) VisitRangeOperator(ctx *fql.RangeOperatorContext) interface{} { dst := v.registers.Allocate(Temp) - start := ctx.GetLeft().Accept(v).(runtime.Operand) - end := ctx.GetRight().Accept(v).(runtime.Operand) + start := ctx.GetLeft().Accept(v).(vm.Operand) + end := ctx.GetRight().Accept(v).(vm.Operand) - v.emitter.EmitABC(runtime.OpRange, dst, start, end) + v.emitter.EmitABC(vm.OpRange, dst, start, end) return dst } @@ -650,13 +657,13 @@ func (v *visitor) VisitRangeOperand(ctx *fql.RangeOperandContext) interface{} { return c.Accept(v) } - panic(core.Error(ErrUnexpectedToken, ctx.GetText())) + panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) } func (v *visitor) VisitParam(ctx *fql.ParamContext) interface{} { name := ctx.Identifier().GetText() reg := v.registers.Allocate(Temp) - v.emitter.EmitAB(runtime.OpLoadParam, reg, v.symbols.AddParam(name)) + v.emitter.EmitAB(vm.OpLoadParam, reg, v.symbols.AddParam(name)) return reg } @@ -670,25 +677,25 @@ func (v *visitor) VisitVariableDeclaration(ctx *fql.VariableDeclarationContext) name = reserved.GetText() } - src := ctx.Expression().Accept(v).(runtime.Operand) + src := ctx.Expression().Accept(v).(vm.Operand) if name != ignorePseudoVariable { dest := v.symbols.DefineVariable(name) if src.IsConstant() { tmp := v.registers.Allocate(Temp) - v.emitter.EmitAB(runtime.OpLoadConst, tmp, src) - v.emitter.EmitAB(runtime.OpStoreGlobal, dest, tmp) + v.emitter.EmitAB(vm.OpLoadConst, tmp, src) + v.emitter.EmitAB(vm.OpStoreGlobal, dest, tmp) } else if v.symbols.Scope() == 0 { - v.emitter.EmitAB(runtime.OpStoreGlobal, dest, src) + v.emitter.EmitAB(vm.OpStoreGlobal, dest, src) } else { - v.emitter.EmitAB(runtime.OpMove, dest, src) + v.emitter.EmitAB(vm.OpMove, dest, src) } return dest } - return runtime.NoopOperand + return vm.NoopOperand } func (v *visitor) VisitVariable(ctx *fql.VariableContext) interface{} { @@ -700,7 +707,7 @@ func (v *visitor) VisitVariable(ctx *fql.VariableContext) interface{} { } reg := v.registers.Allocate(Temp) - v.emitter.EmitAB(runtime.OpLoadGlobal, reg, op) + v.emitter.EmitAB(vm.OpLoadGlobal, reg, op) return reg } @@ -721,10 +728,10 @@ func (v *visitor) VisitArrayLiteral(ctx *fql.ArrayLiteralContext) interface{} { // Evaluate each element into seq registers for i, exp := range exps { // Compile expression and move to seq register - srcReg := exp.Accept(v).(runtime.Operand) + srcReg := exp.Accept(v).(vm.Operand) // TODO: Figure out how to remove OpMove and use registers returned from each expression - v.emitter.EmitAB(runtime.OpMove, seq.Registers[i], srcReg) + v.emitter.EmitAB(vm.OpMove, seq.Registers[i], srcReg) // Free source register if temporary if srcReg.IsRegister() { @@ -733,7 +740,7 @@ func (v *visitor) VisitArrayLiteral(ctx *fql.ArrayLiteralContext) interface{} { } // Initialize an array - v.emitter.EmitAs(runtime.OpArray, destReg, seq) + v.emitter.EmitAs(vm.OpArray, destReg, seq) // Free seq registers //v.registers.FreeSequence(seq) @@ -743,7 +750,7 @@ func (v *visitor) VisitArrayLiteral(ctx *fql.ArrayLiteralContext) interface{} { } // Empty array - v.emitter.EmitA(runtime.OpArray, destReg) + v.emitter.EmitA(vm.OpArray, destReg) return destReg } @@ -754,7 +761,7 @@ func (v *visitor) VisitObjectLiteral(ctx *fql.ObjectLiteralContext) interface{} size := len(assignments) if size == 0 { - v.emitter.EmitA(runtime.OpObject, dst) + v.emitter.EmitA(vm.OpObject, dst) return dst } @@ -762,25 +769,25 @@ func (v *visitor) VisitObjectLiteral(ctx *fql.ObjectLiteralContext) interface{} seq := v.registers.AllocateSequence(len(assignments) * 2) for i := 0; i < size; i++ { - var propOp runtime.Operand - var valOp runtime.Operand + var propOp vm.Operand + var valOp vm.Operand pac := assignments[i].(*fql.PropertyAssignmentContext) if prop, ok := pac.PropertyName().(*fql.PropertyNameContext); ok { - propOp = prop.Accept(v).(runtime.Operand) - valOp = pac.Expression().Accept(v).(runtime.Operand) + propOp = prop.Accept(v).(vm.Operand) + valOp = pac.Expression().Accept(v).(vm.Operand) } else if comProp, ok := pac.ComputedPropertyName().(*fql.ComputedPropertyNameContext); ok { - propOp = comProp.Accept(v).(runtime.Operand) - valOp = pac.Expression().Accept(v).(runtime.Operand) + propOp = comProp.Accept(v).(vm.Operand) + valOp = pac.Expression().Accept(v).(vm.Operand) } else if variable := pac.Variable(); variable != nil { - propOp = v.loadConstant(core.NewString(variable.GetText())) - valOp = variable.Accept(v).(runtime.Operand) + propOp = v.loadConstant(runtime.NewString(variable.GetText())) + valOp = variable.Accept(v).(vm.Operand) } regIndex := i * 2 - v.emitter.EmitAB(runtime.OpMove, seq.Registers[regIndex], propOp) - v.emitter.EmitAB(runtime.OpMove, seq.Registers[regIndex+1], valOp) + v.emitter.EmitAB(vm.OpMove, seq.Registers[regIndex], propOp) + v.emitter.EmitAB(vm.OpMove, seq.Registers[regIndex+1], valOp) // Free source register if temporary if propOp.IsRegister() { @@ -788,7 +795,7 @@ func (v *visitor) VisitObjectLiteral(ctx *fql.ObjectLiteralContext) interface{} } } - v.emitter.EmitAs(runtime.OpObject, dst, seq) + v.emitter.EmitAs(vm.OpObject, dst, seq) return dst } @@ -807,10 +814,10 @@ func (v *visitor) VisitPropertyName(ctx *fql.PropertyNameContext) interface{} { } else if word := ctx.UnsafeReservedWord(); word != nil { name = word.GetText() } else { - panic(core.Error(ErrUnexpectedToken, ctx.GetText())) + panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) } - return v.loadConstant(core.NewString(name)) + return v.loadConstant(runtime.NewString(name)) } func (v *visitor) VisitComputedPropertyName(ctx *fql.ComputedPropertyNameContext) interface{} { @@ -863,7 +870,7 @@ func (v *visitor) VisitStringLiteral(ctx *fql.StringLiteralContext) interface{} } } - return v.loadConstant(core.NewString(b.String())) + return v.loadConstant(runtime.NewString(b.String())) } func (v *visitor) VisitIntegerLiteral(ctx *fql.IntegerLiteralContext) interface{} { @@ -874,7 +881,7 @@ func (v *visitor) VisitIntegerLiteral(ctx *fql.IntegerLiteralContext) interface{ } reg := v.registers.Allocate(Temp) - v.emitter.EmitAB(runtime.OpLoadConst, reg, v.symbols.AddConstant(core.NewInt(val))) + v.emitter.EmitAB(vm.OpLoadConst, reg, v.symbols.AddConstant(runtime.NewInt(val))) return reg } @@ -887,7 +894,7 @@ func (v *visitor) VisitFloatLiteral(ctx *fql.FloatLiteralContext) interface{} { } reg := v.registers.Allocate(Temp) - v.emitter.EmitAB(runtime.OpLoadConst, reg, v.symbols.AddConstant(core.NewFloat(val))) + v.emitter.EmitAB(vm.OpLoadConst, reg, v.symbols.AddConstant(runtime.NewFloat(val))) return reg } @@ -897,11 +904,11 @@ func (v *visitor) VisitBooleanLiteral(ctx *fql.BooleanLiteralContext) interface{ switch strings.ToLower(ctx.GetText()) { case "true": - v.emitter.EmitAB(runtime.OpLoadBool, reg, 1) + v.emitter.EmitAB(vm.OpLoadBool, reg, 1) case "false": - v.emitter.EmitAB(runtime.OpLoadBool, reg, 0) + v.emitter.EmitAB(vm.OpLoadBool, reg, 0) default: - panic(core.Error(ErrUnexpectedToken, ctx.GetText())) + panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) } return reg @@ -909,7 +916,7 @@ func (v *visitor) VisitBooleanLiteral(ctx *fql.BooleanLiteralContext) interface{ func (v *visitor) VisitNoneLiteral(_ *fql.NoneLiteralContext) interface{} { reg := v.registers.Allocate(Temp) - v.emitter.EmitA(runtime.OpLoadNone, reg) + v.emitter.EmitA(vm.OpLoadNone, reg) return reg } @@ -931,39 +938,39 @@ func (v *visitor) VisitLiteral(ctx *fql.LiteralContext) interface{} { return c.Accept(v) } - panic(core.Error(ErrUnexpectedToken, ctx.GetText())) + panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) } func (v *visitor) VisitReturnExpression(ctx *fql.ReturnExpressionContext) interface{} { - valReg := ctx.Expression().Accept(v).(runtime.Operand) + valReg := ctx.Expression().Accept(v).(vm.Operand) if valReg.IsConstant() { - v.emitter.EmitAB(runtime.OpLoadGlobal, runtime.NoopOperand, valReg) + v.emitter.EmitAB(vm.OpLoadGlobal, vm.NoopOperand, valReg) } else { - v.emitter.EmitAB(runtime.OpMove, runtime.NoopOperand, valReg) + v.emitter.EmitAB(vm.OpMove, vm.NoopOperand, valReg) } - v.emitter.Emit(runtime.OpReturn) + v.emitter.Emit(vm.OpReturn) - return runtime.NoopOperand + return vm.NoopOperand } func (v *visitor) VisitExpression(ctx *fql.ExpressionContext) interface{} { if uo := ctx.UnaryOperator(); uo != nil { - src := ctx.GetRight().Accept(v).(runtime.Operand) + src := ctx.GetRight().Accept(v).(vm.Operand) dst := v.registers.Allocate(Temp) uoc := uo.(*fql.UnaryOperatorContext) - var op runtime.Opcode + var op vm.Opcode if uoc.Not() != nil { - op = runtime.OpNot + op = vm.OpNot } else if uoc.Minus() != nil { - op = runtime.OpFlipNegative + op = vm.OpFlipNegative } else if uoc.Plus() != nil { - op = runtime.OpFlipPositive + op = vm.OpFlipPositive } else { - panic(core.Error(ErrUnexpectedToken, uoc.GetText())) + panic(runtime.Error(ErrUnexpectedToken, uoc.GetText())) } // We do not overwrite the source register @@ -975,14 +982,14 @@ func (v *visitor) VisitExpression(ctx *fql.ExpressionContext) interface{} { if op := ctx.LogicalAndOperator(); op != nil { dst := v.registers.Allocate(Temp) // Execute left expression - left := ctx.GetLeft().Accept(v).(runtime.Operand) - v.emitter.EmitAB(runtime.OpMove, dst, left) + left := ctx.GetLeft().Accept(v).(vm.Operand) + v.emitter.EmitAB(vm.OpMove, dst, left) // Test if left is false and jump to the end - end := v.emitter.EmitJumpc(runtime.OpJumpIfFalse, jumpPlaceholder, dst) + end := v.emitter.EmitJumpc(vm.OpJumpIfFalse, jumpPlaceholder, dst) // If left is true, execute right expression - right := ctx.GetRight().Accept(v).(runtime.Operand) + right := ctx.GetRight().Accept(v).(vm.Operand) // And move the result to the destination register - v.emitter.EmitAB(runtime.OpMove, dst, right) + v.emitter.EmitAB(vm.OpMove, dst, right) v.emitter.PatchJumpNext(end) return dst @@ -991,15 +998,15 @@ func (v *visitor) VisitExpression(ctx *fql.ExpressionContext) interface{} { if op := ctx.LogicalOrOperator(); op != nil { dst := v.registers.Allocate(Temp) // Execute left expression - left := ctx.GetLeft().Accept(v).(runtime.Operand) + left := ctx.GetLeft().Accept(v).(vm.Operand) // Move the result to the destination register - v.emitter.EmitAB(runtime.OpMove, dst, left) + v.emitter.EmitAB(vm.OpMove, dst, left) // Test if left is true and jump to the end - end := v.emitter.EmitJumpc(runtime.OpJumpIfTrue, jumpPlaceholder, dst) + end := v.emitter.EmitJumpc(vm.OpJumpIfTrue, jumpPlaceholder, dst) // If left is false, execute right expression - right := ctx.GetRight().Accept(v).(runtime.Operand) + right := ctx.GetRight().Accept(v).(vm.Operand) // And move the result to the destination register - v.emitter.EmitAB(runtime.OpMove, dst, right) + v.emitter.EmitAB(vm.OpMove, dst, right) v.emitter.PatchJumpNext(end) return dst @@ -1009,8 +1016,8 @@ func (v *visitor) VisitExpression(ctx *fql.ExpressionContext) interface{} { dst := v.registers.Allocate(Temp) // Compile condition and put result in dst - condReg := ctx.GetCondition().Accept(v).(runtime.Operand) - v.emitter.EmitAB(runtime.OpMove, dst, condReg) + condReg := ctx.GetCondition().Accept(v).(vm.Operand) + v.emitter.EmitAB(vm.OpMove, dst, condReg) // If condition was temporary, free it if condReg.IsRegister() { @@ -1018,12 +1025,12 @@ func (v *visitor) VisitExpression(ctx *fql.ExpressionContext) interface{} { } // Jump to 'false' branch if condition is false - otherwise := v.emitter.EmitJumpc(runtime.OpJumpIfFalse, jumpPlaceholder, dst) + otherwise := v.emitter.EmitJumpc(vm.OpJumpIfFalse, jumpPlaceholder, dst) // True branch if onTrue := ctx.GetOnTrue(); onTrue != nil { - trueReg := onTrue.Accept(v).(runtime.Operand) - v.emitter.EmitAB(runtime.OpMove, dst, trueReg) + trueReg := onTrue.Accept(v).(vm.Operand) + v.emitter.EmitAB(vm.OpMove, dst, trueReg) // Free temporary register if needed if trueReg.IsRegister() { @@ -1032,13 +1039,13 @@ func (v *visitor) VisitExpression(ctx *fql.ExpressionContext) interface{} { } // Jump over false branch - end := v.emitter.EmitJump(runtime.OpJump, jumpPlaceholder) + end := v.emitter.EmitJump(vm.OpJump, jumpPlaceholder) v.emitter.PatchJumpNext(otherwise) // False branch if onFalse := ctx.GetOnFalse(); onFalse != nil { - falseReg := onFalse.Accept(v).(runtime.Operand) - v.emitter.EmitAB(runtime.OpMove, dst, falseReg) + falseReg := onFalse.Accept(v).(vm.Operand) + v.emitter.EmitAB(vm.OpMove, dst, falseReg) // Free temporary register if needed if falseReg.IsRegister() { @@ -1055,7 +1062,7 @@ func (v *visitor) VisitExpression(ctx *fql.ExpressionContext) interface{} { return c.Accept(v) } - panic(core.Error(ErrUnexpectedToken, ctx.GetText())) + panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) } func (v *visitor) VisitPredicate(ctx *fql.PredicateContext) interface{} { @@ -1078,42 +1085,42 @@ func (v *visitor) VisitPredicate(ctx *fql.PredicateContext) interface{} { return reg } - var opcode runtime.Opcode + var opcode vm.Opcode dest := v.registers.Allocate(Temp) - left := ctx.Predicate(0).Accept(v).(runtime.Operand) - right := ctx.Predicate(1).Accept(v).(runtime.Operand) + left := ctx.Predicate(0).Accept(v).(vm.Operand) + right := ctx.Predicate(1).Accept(v).(vm.Operand) if op := ctx.EqualityOperator(); op != nil { switch op.GetText() { case "==": - opcode = runtime.OpEq + opcode = vm.OpEq case "!=": - opcode = runtime.OpNeq + opcode = vm.OpNeq case ">": - opcode = runtime.OpGt + opcode = vm.OpGt case ">=": - opcode = runtime.OpGte + opcode = vm.OpGte case "<": - opcode = runtime.OpLt + opcode = vm.OpLt case "<=": - opcode = runtime.OpLte + opcode = vm.OpLte default: - panic(core.Error(ErrUnexpectedToken, op.GetText())) + panic(runtime.Error(ErrUnexpectedToken, op.GetText())) } } else if op := ctx.ArrayOperator(); op != nil { // TODO: Implement me - panic(core.Error(core.ErrNotImplemented, "array operator")) + panic(runtime.Error(runtime.ErrNotImplemented, "array operator")) } else if op := ctx.InOperator(); op != nil { if op.Not() == nil { - opcode = runtime.OpIn + opcode = vm.OpIn } else { - opcode = runtime.OpNotIn + opcode = vm.OpNotIn } } else if op := ctx.LikeOperator(); op != nil { if op.(*fql.LikeOperatorContext).Not() == nil { - opcode = runtime.OpLike + opcode = vm.OpLike } else { - opcode = runtime.OpNotLike + opcode = vm.OpNotLike } } @@ -1123,7 +1130,7 @@ func (v *visitor) VisitPredicate(ctx *fql.PredicateContext) interface{} { } func (v *visitor) VisitExpressionAtom(ctx *fql.ExpressionAtomContext) interface{} { - var opcode runtime.Opcode + var opcode vm.Opcode var isSet bool if op := ctx.MultiplicativeOperator(); op != nil { @@ -1131,24 +1138,24 @@ func (v *visitor) VisitExpressionAtom(ctx *fql.ExpressionAtomContext) interface{ switch op.GetText() { case "*": - opcode = runtime.OpMulti + opcode = vm.OpMulti case "/": - opcode = runtime.OpDiv + opcode = vm.OpDiv case "%": - opcode = runtime.OpMod + opcode = vm.OpMod default: - panic(core.Error(ErrUnexpectedToken, op.GetText())) + panic(runtime.Error(ErrUnexpectedToken, op.GetText())) } } else if op := ctx.AdditiveOperator(); op != nil { isSet = true switch op.GetText() { case "+": - opcode = runtime.OpAdd + opcode = vm.OpAdd case "-": - opcode = runtime.OpSub + opcode = vm.OpSub default: - panic(core.Error(ErrUnexpectedToken, op.GetText())) + panic(runtime.Error(ErrUnexpectedToken, op.GetText())) } } else if op := ctx.RegexpOperator(); op != nil { @@ -1156,20 +1163,20 @@ func (v *visitor) VisitExpressionAtom(ctx *fql.ExpressionAtomContext) interface{ switch op.GetText() { case "=~": - opcode = runtime.OpRegexpPositive + opcode = vm.OpRegexpPositive case "!~": - opcode = runtime.OpRegexpNegative + opcode = vm.OpRegexpNegative default: - panic(core.Error(ErrUnexpectedToken, op.GetText())) + panic(runtime.Error(ErrUnexpectedToken, op.GetText())) } } if isSet { - regLeft := ctx.ExpressionAtom(0).Accept(v).(runtime.Operand) - regRight := ctx.ExpressionAtom(1).Accept(v).(runtime.Operand) + regLeft := ctx.ExpressionAtom(0).Accept(v).(vm.Operand) + regRight := ctx.ExpressionAtom(1).Accept(v).(vm.Operand) dst := v.registers.Allocate(Temp) - if opcode == runtime.OpRegexpPositive || opcode == runtime.OpRegexpNegative { + if opcode == vm.OpRegexpPositive || opcode == vm.OpRegexpNegative { if regRight.IsConstant() { val := v.symbols.Constant(regRight) @@ -1203,7 +1210,7 @@ func (v *visitor) VisitExpressionAtom(ctx *fql.ExpressionAtomContext) interface{ return c.Accept(v) } - panic(core.Error(ErrUnexpectedToken, ctx.GetText())) + panic(runtime.Error(ErrUnexpectedToken, ctx.GetText())) } func (v *visitor) visitFunctionCall(ctx *fql.FunctionCallContext, protected bool) interface{} { @@ -1222,10 +1229,10 @@ func (v *visitor) visitFunctionCall(ctx *fql.FunctionCallContext, protected bool // Evaluate each element into seq registers for i, exp := range exps { // Compile expression and move to seq register - srcReg := exp.Accept(v).(runtime.Operand) + srcReg := exp.Accept(v).(vm.Operand) // TODO: Figure out how to remove OpMove and use registers returned from each expression - v.emitter.EmitAB(runtime.OpMove, seq.Registers[i], srcReg) + v.emitter.EmitAB(vm.OpMove, seq.Registers[i], srcReg) // Free source register if temporary if srcReg.IsRegister() { @@ -1238,40 +1245,48 @@ func (v *visitor) visitFunctionCall(ctx *fql.FunctionCallContext, protected bool name := v.functionName(ctx) switch name { - case "LENGTH": + case runtimeLength: dst := v.registers.Allocate(Temp) if seq == nil || len(seq.Registers) != 1 { - panic(core.Error(core.ErrInvalidArgument, "LENGTH: expected 1 argument")) + panic(runtime.Error(runtime.ErrInvalidArgument, runtimeLength+": expected 1 argument")) } - v.emitter.EmitAB(runtime.OpLength, dst, seq.Registers[0]) + v.emitter.EmitAB(vm.OpLength, dst, seq.Registers[0]) return dst - case "TYPENAME": + case runtimeTypename: dst := v.registers.Allocate(Temp) if seq == nil || len(seq.Registers) != 1 { - panic(core.Error(core.ErrInvalidArgument, "TYPENAME: expected 1 argument")) + panic(runtime.Error(runtime.ErrInvalidArgument, runtimeTypename+": expected 1 argument")) } - v.emitter.EmitAB(runtime.OpType, dst, seq.Registers[0]) + v.emitter.EmitAB(vm.OpType, dst, seq.Registers[0]) return dst + case runtimeWait: + if seq == nil || len(seq.Registers) != 1 { + panic(runtime.Error(runtime.ErrInvalidArgument, runtimeWait+": expected 1 argument")) + } + + v.emitter.EmitA(vm.OpSleep, seq.Registers[0]) + + return seq.Registers[0] default: nameAndDest := v.loadConstant(v.functionName(ctx)) if !protected { - v.emitter.EmitAs(runtime.OpCall, nameAndDest, seq) + v.emitter.EmitAs(vm.OpCall, nameAndDest, seq) } else { - v.emitter.EmitAs(runtime.OpProtectedCall, nameAndDest, seq) + v.emitter.EmitAs(vm.OpProtectedCall, nameAndDest, seq) } return nameAndDest } } -func (v *visitor) functionName(ctx *fql.FunctionCallContext) core.String { +func (v *visitor) functionName(ctx *fql.FunctionCallContext) runtime.String { var name string funcNS := ctx.Namespace() @@ -1281,27 +1296,27 @@ func (v *visitor) functionName(ctx *fql.FunctionCallContext) core.String { name += ctx.FunctionName().GetText() - return core.NewString(strings.ToUpper(name)) + return runtime.NewString(strings.ToUpper(name)) } func (v *visitor) emitLoopBegin(loop *Loop) { if loop.Allocate { - v.emitter.EmitAb(runtime.OpLoopBegin, loop.Result, loop.Distinct) + v.emitter.EmitAb(vm.OpLoopBegin, loop.Result, loop.Distinct) } loop.Iterator = v.registers.Allocate(State) if loop.Kind == ForLoop { - v.emitter.EmitAB(runtime.OpForLoopPrep, loop.Iterator, loop.Src) + v.emitter.EmitAB(vm.OpForLoopPrep, loop.Iterator, loop.Src) // jumpPlaceholder is a placeholder for the exit jump position - loop.Jump = v.emitter.EmitJumpc(runtime.OpForLoopNext, jumpPlaceholder, loop.Iterator) + loop.Jump = v.emitter.EmitJumpc(vm.OpForLoopNext, jumpPlaceholder, loop.Iterator) - if loop.Value != runtime.NoopOperand { - v.emitter.EmitAB(runtime.OpForLoopValue, loop.Value, loop.Iterator) + if loop.Value != vm.NoopOperand { + v.emitter.EmitAB(vm.OpForLoopValue, loop.Value, loop.Iterator) } - if loop.Key != runtime.NoopOperand { - v.emitter.EmitAB(runtime.OpForLoopKey, loop.Key, loop.Iterator) + if loop.Key != vm.NoopOperand { + v.emitter.EmitAB(vm.OpForLoopKey, loop.Key, loop.Iterator) } } else { //counterReg := v.registers.Allocate(State) @@ -1309,16 +1324,16 @@ func (v *visitor) emitLoopBegin(loop *Loop) { } } -func (v *visitor) emitLoopEnd(loop *Loop) runtime.Operand { - v.emitter.EmitJump(runtime.OpJump, loop.Jump-loop.JumpOffset) +func (v *visitor) emitLoopEnd(loop *Loop) vm.Operand { + v.emitter.EmitJump(vm.OpJump, loop.Jump-loop.JumpOffset) // TODO: Do not allocate for pass-through loops dst := v.registers.Allocate(Temp) if loop.Allocate { // TODO: Reuse the dsReg register - v.emitter.EmitA(runtime.OpClose, loop.Iterator) - v.emitter.EmitAB(runtime.OpLoopEnd, dst, loop.Result) + v.emitter.EmitA(vm.OpClose, loop.Iterator) + v.emitter.EmitAB(vm.OpLoopEnd, dst, loop.Result) if loop.Kind == ForLoop { v.emitter.PatchJump(loop.Jump) @@ -1356,8 +1371,8 @@ func (v *visitor) loopKind(ctx *fql.ForExpressionContext) LoopKind { return DoWhileLoop } -func (v *visitor) loadConstant(constant core.Value) runtime.Operand { +func (v *visitor) loadConstant(constant runtime.Value) vm.Operand { reg := v.registers.Allocate(Temp) - v.emitter.EmitAB(runtime.OpLoadConst, reg, v.symbols.AddConstant(constant)) + v.emitter.EmitAB(vm.OpLoadConst, reg, v.symbols.AddConstant(constant)) return reg } diff --git a/pkg/runtime/core/array.go b/pkg/runtime/array.go similarity index 99% rename from pkg/runtime/core/array.go rename to pkg/runtime/array.go index 1eeb0501..dbf14bd9 100644 --- a/pkg/runtime/core/array.go +++ b/pkg/runtime/array.go @@ -1,4 +1,4 @@ -package core +package runtime import ( "context" diff --git a/pkg/runtime/core/array_iter.go b/pkg/runtime/array_iter.go similarity index 96% rename from pkg/runtime/core/array_iter.go rename to pkg/runtime/array_iter.go index 54572ea4..f772d71b 100644 --- a/pkg/runtime/core/array_iter.go +++ b/pkg/runtime/array_iter.go @@ -1,4 +1,4 @@ -package core +package runtime import ( "context" diff --git a/pkg/runtime/core/array_iter_test.go b/pkg/runtime/array_iter_test.go similarity index 80% rename from pkg/runtime/core/array_iter_test.go rename to pkg/runtime/array_iter_test.go index 0083ad6a..0fb6e389 100644 --- a/pkg/runtime/core/array_iter_test.go +++ b/pkg/runtime/array_iter_test.go @@ -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) diff --git a/pkg/runtime/core/array_test.go b/pkg/runtime/array_test.go similarity index 77% rename from pkg/runtime/core/array_test.go rename to pkg/runtime/array_test.go index 6f26ac8b..cb511006 100644 --- a/pkg/runtime/core/array_test.go +++ b/pkg/runtime/array_test.go @@ -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)) diff --git a/pkg/runtime/core/assertions.go b/pkg/runtime/assertions.go similarity index 78% rename from pkg/runtime/core/assertions.go rename to pkg/runtime/assertions.go index 555e6974..6b2d49f2 100644 --- a/pkg/runtime/core/assertions.go +++ b/pkg/runtime/assertions.go @@ -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) diff --git a/pkg/runtime/core/binary.go b/pkg/runtime/binary.go similarity index 98% rename from pkg/runtime/core/binary.go rename to pkg/runtime/binary.go index d870adf5..2800b007 100644 --- a/pkg/runtime/core/binary.go +++ b/pkg/runtime/binary.go @@ -1,4 +1,4 @@ -package core +package runtime import ( "context" diff --git a/pkg/runtime/core/boolean.go b/pkg/runtime/boolean.go similarity index 98% rename from pkg/runtime/core/boolean.go rename to pkg/runtime/boolean.go index 8a8d0bdf..5b0b49c5 100644 --- a/pkg/runtime/core/boolean.go +++ b/pkg/runtime/boolean.go @@ -1,4 +1,4 @@ -package core +package runtime import ( "hash/fnv" diff --git a/pkg/runtime/core/boolean_test.go b/pkg/runtime/boolean_test.go similarity index 84% rename from pkg/runtime/core/boolean_test.go rename to pkg/runtime/boolean_test.go index 0ba26722..270c2ad1 100644 --- a/pkg/runtime/core/boolean_test.go +++ b/pkg/runtime/boolean_test.go @@ -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 { diff --git a/pkg/runtime/core/casting.go b/pkg/runtime/casting.go similarity index 99% rename from pkg/runtime/core/casting.go rename to pkg/runtime/casting.go index 9dbe0efc..04e67e7f 100644 --- a/pkg/runtime/core/casting.go +++ b/pkg/runtime/casting.go @@ -1,4 +1,4 @@ -package core +package runtime func CastBoolean(input Value) (Boolean, error) { boolean, ok := input.(Boolean) diff --git a/pkg/runtime/core/cloneable.go b/pkg/runtime/cloneable.go similarity index 82% rename from pkg/runtime/core/cloneable.go rename to pkg/runtime/cloneable.go index f402c0f3..da0c706c 100644 --- a/pkg/runtime/core/cloneable.go +++ b/pkg/runtime/cloneable.go @@ -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: diff --git a/pkg/runtime/core/collections.go b/pkg/runtime/collections.go similarity index 96% rename from pkg/runtime/core/collections.go rename to pkg/runtime/collections.go index 458608bb..f9843de0 100644 --- a/pkg/runtime/core/collections.go +++ b/pkg/runtime/collections.go @@ -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 } ) diff --git a/pkg/runtime/core/comparable.go b/pkg/runtime/comparable.go similarity index 95% rename from pkg/runtime/core/comparable.go rename to pkg/runtime/comparable.go index d334f5e4..cfed8a66 100644 --- a/pkg/runtime/core/comparable.go +++ b/pkg/runtime/comparable.go @@ -1,4 +1,4 @@ -package core +package runtime type ( Comparable interface { diff --git a/pkg/runtime/core/errors.go b/pkg/runtime/core/errors.go deleted file mode 100644 index 36396917..00000000 --- a/pkg/runtime/core/errors.go +++ /dev/null @@ -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) -} diff --git a/pkg/runtime/core/date_time.go b/pkg/runtime/date_time.go similarity index 99% rename from pkg/runtime/core/date_time.go rename to pkg/runtime/date_time.go index 2da53c3b..e1be6221 100644 --- a/pkg/runtime/core/date_time.go +++ b/pkg/runtime/date_time.go @@ -1,4 +1,4 @@ -package core +package runtime import ( "hash/fnv" diff --git a/pkg/runtime/core/date_time_test.go b/pkg/runtime/date_time_test.go similarity index 75% rename from pkg/runtime/core/date_time_test.go rename to pkg/runtime/date_time_test.go index 44b750d2..c8973892 100644 --- a/pkg/runtime/core/date_time_test.go +++ b/pkg/runtime/date_time_test.go @@ -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) diff --git a/pkg/runtime/errors.go b/pkg/runtime/errors.go index 07040810..44ba44a8 100644 --- a/pkg/runtime/errors.go +++ b/pkg/runtime/errors.go @@ -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) } diff --git a/pkg/runtime/core/errors_test.go b/pkg/runtime/errors_test.go similarity index 81% rename from pkg/runtime/core/errors_test.go rename to pkg/runtime/errors_test.go index 8866b85c..9f941f84 100644 --- a/pkg/runtime/core/errors_test.go +++ b/pkg/runtime/errors_test.go @@ -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()) }) diff --git a/pkg/runtime/events/builtin.go b/pkg/runtime/events/builtin.go deleted file mode 100644 index b8d84cb2..00000000 --- a/pkg/runtime/events/builtin.go +++ /dev/null @@ -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} -} diff --git a/pkg/runtime/events/helpers.go b/pkg/runtime/events/helpers.go deleted file mode 100644 index 0a507350..00000000 --- a/pkg/runtime/events/helpers.go +++ /dev/null @@ -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} -} diff --git a/pkg/runtime/events/iterator.go b/pkg/runtime/events/iterator.go deleted file mode 100644 index 487b940f..00000000 --- a/pkg/runtime/events/iterator.go +++ /dev/null @@ -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 -} diff --git a/pkg/runtime/events/observable.go b/pkg/runtime/events/observable.go deleted file mode 100644 index 60ddc6e5..00000000 --- a/pkg/runtime/events/observable.go +++ /dev/null @@ -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) - } -) diff --git a/pkg/runtime/core/float.go b/pkg/runtime/float.go similarity index 99% rename from pkg/runtime/core/float.go rename to pkg/runtime/float.go index fa011423..0a2b3b35 100644 --- a/pkg/runtime/core/float.go +++ b/pkg/runtime/float.go @@ -1,4 +1,4 @@ -package core +package runtime import ( "encoding/binary" diff --git a/pkg/runtime/core/float_test.go b/pkg/runtime/float_test.go similarity index 75% rename from pkg/runtime/core/float_test.go rename to pkg/runtime/float_test.go index 2174c83c..e27b326f 100644 --- a/pkg/runtime/core/float_test.go +++ b/pkg/runtime/float_test.go @@ -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) diff --git a/pkg/runtime/core/function.go b/pkg/runtime/function.go similarity index 99% rename from pkg/runtime/core/function.go rename to pkg/runtime/function.go index b326b7a0..4f90680a 100644 --- a/pkg/runtime/core/function.go +++ b/pkg/runtime/function.go @@ -1,4 +1,4 @@ -package core +package runtime import ( "context" diff --git a/pkg/runtime/core/function_test.go b/pkg/runtime/function_test.go similarity index 79% rename from pkg/runtime/core/function_test.go rename to pkg/runtime/function_test.go index d6538b6b..fbf401b6 100644 --- a/pkg/runtime/core/function_test.go +++ b/pkg/runtime/function_test.go @@ -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) }) }) diff --git a/pkg/runtime/core/helpers.go b/pkg/runtime/helpers.go similarity index 99% rename from pkg/runtime/core/helpers.go rename to pkg/runtime/helpers.go index 0c3d0006..6bf66ec5 100644 --- a/pkg/runtime/core/helpers.go +++ b/pkg/runtime/helpers.go @@ -1,4 +1,4 @@ -package core +package runtime import ( "context" diff --git a/pkg/runtime/core/helpers_test.go b/pkg/runtime/helpers_test.go similarity index 81% rename from pkg/runtime/core/helpers_test.go rename to pkg/runtime/helpers_test.go index 5b106e32..544525fe 100644 --- a/pkg/runtime/core/helpers_test.go +++ b/pkg/runtime/helpers_test.go @@ -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) diff --git a/pkg/runtime/core/int.go b/pkg/runtime/int.go similarity index 99% rename from pkg/runtime/core/int.go rename to pkg/runtime/int.go index d325f9e0..3a65cc56 100644 --- a/pkg/runtime/core/int.go +++ b/pkg/runtime/int.go @@ -1,4 +1,4 @@ -package core +package runtime import ( "encoding/binary" diff --git a/pkg/runtime/core/int_test.go b/pkg/runtime/int_test.go similarity index 76% rename from pkg/runtime/core/int_test.go rename to pkg/runtime/int_test.go index e6b4060b..5e930e27 100644 --- a/pkg/runtime/core/int_test.go +++ b/pkg/runtime/int_test.go @@ -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) diff --git a/pkg/runtime/internal/helpers.go b/pkg/runtime/internal/helpers.go deleted file mode 100644 index 976770bf..00000000 --- a/pkg/runtime/internal/helpers.go +++ /dev/null @@ -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") - } -} diff --git a/pkg/runtime/internal/noop_iter.go b/pkg/runtime/internal/noop_iter.go deleted file mode 100644 index f9bdcc88..00000000 --- a/pkg/runtime/internal/noop_iter.go +++ /dev/null @@ -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 -} diff --git a/pkg/runtime/internal/operators.go b/pkg/runtime/internal/operators.go deleted file mode 100644 index e9284e42..00000000 --- a/pkg/runtime/internal/operators.go +++ /dev/null @@ -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 -} diff --git a/pkg/runtime/core/iterator.go b/pkg/runtime/iterator.go similarity index 67% rename from pkg/runtime/core/iterator.go rename to pkg/runtime/iterator.go index fb63fdd5..e99e3455 100644 --- a/pkg/runtime/core/iterator.go +++ b/pkg/runtime/iterator.go @@ -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 } } diff --git a/pkg/runtime/core/measurable.go b/pkg/runtime/measurable.go similarity index 93% rename from pkg/runtime/core/measurable.go rename to pkg/runtime/measurable.go index 8953b35f..238e158b 100644 --- a/pkg/runtime/core/measurable.go +++ b/pkg/runtime/measurable.go @@ -1,4 +1,4 @@ -package core +package runtime import "context" diff --git a/pkg/runtime/core/namespace.go b/pkg/runtime/namespace.go similarity index 93% rename from pkg/runtime/core/namespace.go rename to pkg/runtime/namespace.go index 2d95853e..77a5c0bc 100644 --- a/pkg/runtime/core/namespace.go +++ b/pkg/runtime/namespace.go @@ -1,4 +1,4 @@ -package core +package runtime type Namespace interface { Namespace(name string) Namespace diff --git a/pkg/runtime/core/none.go b/pkg/runtime/none.go similarity index 97% rename from pkg/runtime/core/none.go rename to pkg/runtime/none.go index d02c40ea..b98f4d3a 100644 --- a/pkg/runtime/core/none.go +++ b/pkg/runtime/none.go @@ -1,4 +1,4 @@ -package core +package runtime import ( "context" diff --git a/pkg/runtime/core/object.go b/pkg/runtime/object.go similarity index 99% rename from pkg/runtime/core/object.go rename to pkg/runtime/object.go index 021928ce..d676b7b5 100644 --- a/pkg/runtime/core/object.go +++ b/pkg/runtime/object.go @@ -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 { diff --git a/pkg/runtime/core/object_iter.go b/pkg/runtime/object_iter.go similarity index 97% rename from pkg/runtime/core/object_iter.go rename to pkg/runtime/object_iter.go index e438b819..41a66d6d 100644 --- a/pkg/runtime/core/object_iter.go +++ b/pkg/runtime/object_iter.go @@ -1,4 +1,4 @@ -package core +package runtime import ( "context" diff --git a/pkg/runtime/core/object_iter_test.go b/pkg/runtime/object_iter_test.go similarity index 84% rename from pkg/runtime/core/object_iter_test.go rename to pkg/runtime/object_iter_test.go index 1c03ad61..01aac716 100644 --- a/pkg/runtime/core/object_iter_test.go +++ b/pkg/runtime/object_iter_test.go @@ -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() diff --git a/pkg/runtime/core/object_test.go b/pkg/runtime/object_test.go similarity index 55% rename from pkg/runtime/core/object_test.go rename to pkg/runtime/object_test.go index 12612426..21e8d716 100644 --- a/pkg/runtime/core/object_test.go +++ b/pkg/runtime/object_test.go @@ -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) diff --git a/pkg/runtime/core/source.go b/pkg/runtime/source.go similarity index 98% rename from pkg/runtime/core/source.go rename to pkg/runtime/source.go index 2169bc42..c056696c 100644 --- a/pkg/runtime/core/source.go +++ b/pkg/runtime/source.go @@ -1,4 +1,4 @@ -package core +package runtime type ( Location struct { diff --git a/pkg/runtime/core/string.go b/pkg/runtime/string.go similarity index 99% rename from pkg/runtime/core/string.go rename to pkg/runtime/string.go index 952aa503..7b3370c0 100644 --- a/pkg/runtime/core/string.go +++ b/pkg/runtime/string.go @@ -1,4 +1,4 @@ -package core +package runtime import ( "context" diff --git a/pkg/runtime/core/string_test.go b/pkg/runtime/string_test.go similarity index 76% rename from pkg/runtime/core/string_test.go rename to pkg/runtime/string_test.go index 0026f770..50d6c755 100644 --- a/pkg/runtime/core/string_test.go +++ b/pkg/runtime/string_test.go @@ -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") diff --git a/pkg/runtime/core/type.go b/pkg/runtime/type.go similarity index 95% rename from pkg/runtime/core/type.go rename to pkg/runtime/type.go index 0894d66a..83f08f15 100644 --- a/pkg/runtime/core/type.go +++ b/pkg/runtime/type.go @@ -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" diff --git a/pkg/runtime/core/value.go b/pkg/runtime/value.go similarity index 93% rename from pkg/runtime/core/value.go rename to pkg/runtime/value.go index f46ebb57..7d7d8c26 100644 --- a/pkg/runtime/core/value.go +++ b/pkg/runtime/value.go @@ -1,4 +1,4 @@ -package core +package runtime import ( "encoding/json" diff --git a/pkg/runtime/values/values.go b/pkg/runtime/values/values.go deleted file mode 100644 index 64a761f7..00000000 --- a/pkg/runtime/values/values.go +++ /dev/null @@ -1,9 +0,0 @@ -package values - -import ( - "github.com/MontFerret/ferret/pkg/runtime/core" -) - -func NewArray(cap int) core.List { - return core.NewArray(cap) -} diff --git a/pkg/runtime/env.go b/pkg/vm/env.go similarity index 62% rename from pkg/runtime/env.go rename to pkg/vm/env.go index 4727b219..8bfc194b 100644 --- a/pkg/runtime/env.go +++ b/pkg/vm/env.go @@ -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] } diff --git a/pkg/runtime/env_options.go b/pkg/vm/env_options.go similarity index 69% rename from pkg/runtime/env_options.go rename to pkg/vm/env_options.go index 949af7fc..efc71255 100644 --- a/pkg/runtime/env_options.go +++ b/pkg/vm/env_options.go @@ -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 } diff --git a/pkg/vm/errors.go b/pkg/vm/errors.go new file mode 100644 index 00000000..280e23f9 --- /dev/null +++ b/pkg/vm/errors.go @@ -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()), + } +} diff --git a/pkg/runtime/instruction.go b/pkg/vm/instruction.go similarity index 93% rename from pkg/runtime/instruction.go rename to pkg/vm/instruction.go index e70c3b35..ae5f7424 100644 --- a/pkg/runtime/instruction.go +++ b/pkg/vm/instruction.go @@ -1,4 +1,4 @@ -package runtime +package vm import "fmt" diff --git a/pkg/runtime/internal/boxed.go b/pkg/vm/internal/boxed.go similarity index 88% rename from pkg/runtime/internal/boxed.go rename to pkg/vm/internal/boxed.go index 32c89a79..db3e869f 100644 --- a/pkg/runtime/internal/boxed.go +++ b/pkg/vm/internal/boxed.go @@ -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) } diff --git a/pkg/runtime/internal/collector.go b/pkg/vm/internal/collector.go similarity index 81% rename from pkg/runtime/internal/collector.go rename to pkg/vm/internal/collector.go index 2b76f03b..bc6f9a04 100644 --- a/pkg/runtime/internal/collector.go +++ b/pkg/vm/internal/collector.go @@ -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") } diff --git a/pkg/runtime/internal/dataset.go b/pkg/vm/internal/dataset.go similarity index 62% rename from pkg/runtime/internal/dataset.go rename to pkg/vm/internal/dataset.go index 6088dc8d..7397f18a 100644 --- a/pkg/runtime/internal/dataset.go +++ b/pkg/vm/internal/dataset.go @@ -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() } diff --git a/pkg/vm/internal/helpers.go b/pkg/vm/internal/helpers.go new file mode 100644 index 00000000..9cf02c52 --- /dev/null +++ b/pkg/vm/internal/helpers.go @@ -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 +} diff --git a/pkg/runtime/internal/iterator.go b/pkg/vm/internal/iterator.go similarity index 71% rename from pkg/runtime/internal/iterator.go rename to pkg/vm/internal/iterator.go index 325b77e6..5d247169 100644 --- a/pkg/runtime/internal/iterator.go +++ b/pkg/vm/internal/iterator.go @@ -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") } diff --git a/pkg/runtime/internal/kv.go b/pkg/vm/internal/kv.go similarity index 80% rename from pkg/runtime/internal/kv.go rename to pkg/vm/internal/kv.go index 69db7ac6..f59c315b 100644 --- a/pkg/runtime/internal/kv.go +++ b/pkg/vm/internal/kv.go @@ -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, diff --git a/pkg/vm/internal/noop_iter.go b/pkg/vm/internal/noop_iter.go new file mode 100644 index 00000000..736566de --- /dev/null +++ b/pkg/vm/internal/noop_iter.go @@ -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 +} diff --git a/pkg/vm/internal/operators.go b/pkg/vm/internal/operators.go new file mode 100644 index 00000000..c9520375 --- /dev/null +++ b/pkg/vm/internal/operators.go @@ -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 +} diff --git a/pkg/runtime/internal/range.go b/pkg/vm/internal/range.go similarity index 89% rename from pkg/runtime/internal/range.go rename to pkg/vm/internal/range.go index e430b6b3..3ac86b5d 100644 --- a/pkg/runtime/internal/range.go +++ b/pkg/vm/internal/range.go @@ -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 { diff --git a/pkg/runtime/internal/range_iter.go b/pkg/vm/internal/range_iter.go similarity index 65% rename from pkg/runtime/internal/range_iter.go rename to pkg/vm/internal/range_iter.go index ceb551e6..c66fdf92 100644 --- a/pkg/runtime/internal/range_iter.go +++ b/pkg/vm/internal/range_iter.go @@ -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 } diff --git a/pkg/runtime/internal/range_iter_test.go b/pkg/vm/internal/range_iter_test.go similarity index 73% rename from pkg/runtime/internal/range_iter_test.go rename to pkg/vm/internal/range_iter_test.go index 5349059c..8f683b5e 100644 --- a/pkg/runtime/internal/range_iter_test.go +++ b/pkg/vm/internal/range_iter_test.go @@ -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}) }) } diff --git a/pkg/runtime/internal/regexp.go b/pkg/vm/internal/regexp.go similarity index 59% rename from pkg/runtime/internal/regexp.go rename to pkg/vm/internal/regexp.go index 7b887ba4..4f3e59ec 100644 --- a/pkg/runtime/internal/regexp.go +++ b/pkg/vm/internal/regexp.go @@ -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))) } diff --git a/pkg/runtime/internal/sequence.go b/pkg/vm/internal/sequence.go similarity index 69% rename from pkg/runtime/internal/sequence.go rename to pkg/vm/internal/sequence.go index bb751368..5621f508 100644 --- a/pkg/runtime/internal/sequence.go +++ b/pkg/vm/internal/sequence.go @@ -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") } diff --git a/pkg/runtime/internal/stack.go b/pkg/vm/internal/stack.go similarity index 68% rename from pkg/runtime/internal/stack.go rename to pkg/vm/internal/stack.go index c1b9d674..89526fce 100644 --- a/pkg/runtime/internal/stack.go +++ b/pkg/vm/internal/stack.go @@ -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") } diff --git a/pkg/runtime/opcode.go b/pkg/vm/opcode.go similarity index 64% rename from pkg/runtime/opcode.go rename to pkg/vm/opcode.go index 3ede413d..9b938575 100644 --- a/pkg/runtime/opcode.go +++ b/pkg/vm/opcode.go @@ -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 ) diff --git a/pkg/runtime/operand.go b/pkg/vm/operand.go similarity index 97% rename from pkg/runtime/operand.go rename to pkg/vm/operand.go index 4f63de60..824a20a5 100644 --- a/pkg/runtime/operand.go +++ b/pkg/vm/operand.go @@ -1,4 +1,4 @@ -package runtime +package vm import "fmt" diff --git a/pkg/runtime/program.go b/pkg/vm/program.go similarity index 91% rename from pkg/runtime/program.go rename to pkg/vm/program.go index 9da8c923..d0d168ed 100644 --- a/pkg/runtime/program.go +++ b/pkg/vm/program.go @@ -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 diff --git a/pkg/runtime/source_map.go b/pkg/vm/source_map.go similarity index 95% rename from pkg/runtime/source_map.go rename to pkg/vm/source_map.go index d6b0a611..07e57799 100644 --- a/pkg/runtime/source_map.go +++ b/pkg/vm/source_map.go @@ -1,4 +1,4 @@ -package runtime +package vm import "fmt" diff --git a/pkg/runtime/source_map_test.go b/pkg/vm/source_map_test.go similarity index 75% rename from pkg/runtime/source_map_test.go rename to pkg/vm/source_map_test.go index 53b3b7f7..bb9b6206 100644 --- a/pkg/runtime/source_map_test.go +++ b/pkg/vm/source_map_test.go @@ -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) diff --git a/pkg/runtime/vm.go b/pkg/vm/vm.go similarity index 72% rename from pkg/runtime/vm.go rename to pkg/vm/vm.go index 0cc70f2a..d7d86bd1 100644 --- a/pkg/runtime/vm.go +++ b/pkg/vm/vm.go @@ -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