mirror of
https://github.com/MontFerret/ferret.git
synced 2025-08-15 20:02:56 +02:00
Re-organized project layout
This commit is contained in:
@@ -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
|
||||
|
@@ -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 := `
|
||||
|
14
ferret.go
14
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 {
|
||||
|
@@ -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
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
}))
|
||||
}
|
||||
|
||||
|
@@ -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...)
|
||||
}
|
||||
|
@@ -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"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@@ -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},
|
||||
})
|
||||
}
|
||||
|
@@ -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::".
|
||||
//
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -1,25 +1,24 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type (
|
||||
Variable struct {
|
||||
Name string
|
||||
Register runtime.Operand
|
||||
Register vm.Operand
|
||||
Depth int
|
||||
}
|
||||
|
||||
SymbolTable struct {
|
||||
registers *RegisterAllocator
|
||||
constants []core.Value
|
||||
constants []runtime.Value
|
||||
constantsIndex map[uint64]int
|
||||
params map[string]string
|
||||
globals map[string]runtime.Operand
|
||||
globals map[string]vm.Operand
|
||||
locals []*Variable
|
||||
scope int
|
||||
}
|
||||
@@ -28,10 +27,10 @@ type (
|
||||
func NewSymbolTable(registers *RegisterAllocator) *SymbolTable {
|
||||
return &SymbolTable{
|
||||
registers: registers,
|
||||
constants: make([]core.Value, 0),
|
||||
constants: make([]runtime.Value, 0),
|
||||
constantsIndex: make(map[uint64]int),
|
||||
params: make(map[string]string),
|
||||
globals: make(map[string]runtime.Operand),
|
||||
globals: make(map[string]vm.Operand),
|
||||
locals: make([]*Variable, 0),
|
||||
}
|
||||
}
|
||||
@@ -44,26 +43,26 @@ func (st *SymbolTable) EnterScope() {
|
||||
st.scope++
|
||||
}
|
||||
|
||||
func (st *SymbolTable) AddParam(name string) runtime.Operand {
|
||||
func (st *SymbolTable) AddParam(name string) vm.Operand {
|
||||
st.params[name] = name
|
||||
|
||||
return st.AddConstant(core.NewString(name))
|
||||
return st.AddConstant(runtime.NewString(name))
|
||||
}
|
||||
|
||||
// AddConstant adds a constant to the constants pool and returns its index.
|
||||
// If the constant is a scalar, it will be deduplicated.
|
||||
// If the constant is not a scalar, it will be added to the pool without deduplication.
|
||||
func (st *SymbolTable) AddConstant(constant core.Value) runtime.Operand {
|
||||
func (st *SymbolTable) AddConstant(constant runtime.Value) vm.Operand {
|
||||
var hash uint64
|
||||
isNone := constant == core.None
|
||||
isNone := constant == runtime.None
|
||||
|
||||
if core.IsScalar(constant) {
|
||||
if runtime.IsScalar(constant) {
|
||||
hash = constant.Hash()
|
||||
}
|
||||
|
||||
if hash > 0 || isNone {
|
||||
if p, ok := st.constantsIndex[hash]; ok {
|
||||
return runtime.NewConstantOperand(p)
|
||||
return vm.NewConstantOperand(p)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,34 +74,34 @@ func (st *SymbolTable) AddConstant(constant core.Value) runtime.Operand {
|
||||
}
|
||||
|
||||
// We flip the sign to indicate that this is a constant index, not a register.
|
||||
return runtime.NewConstantOperand(p)
|
||||
return vm.NewConstantOperand(p)
|
||||
}
|
||||
|
||||
// Constant returns a constant by its index.
|
||||
func (st *SymbolTable) Constant(addr runtime.Operand) core.Value {
|
||||
func (st *SymbolTable) Constant(addr vm.Operand) runtime.Value {
|
||||
if !addr.IsConstant() {
|
||||
panic(core.Error(ErrInvalidOperandType, strconv.Itoa(int(addr))))
|
||||
panic(runtime.Error(ErrInvalidOperandType, strconv.Itoa(int(addr))))
|
||||
}
|
||||
|
||||
index := addr.Constant()
|
||||
|
||||
if index < 0 || index >= len(st.constants) {
|
||||
panic(core.Error(ErrConstantNotFound, strconv.Itoa(index)))
|
||||
panic(runtime.Error(ErrConstantNotFound, strconv.Itoa(index)))
|
||||
}
|
||||
|
||||
return st.constants[index]
|
||||
}
|
||||
|
||||
func (st *SymbolTable) DefineVariable(name string) runtime.Operand {
|
||||
func (st *SymbolTable) DefineVariable(name string) vm.Operand {
|
||||
if st.scope == 0 {
|
||||
// Check for duplicate global variable names.
|
||||
_, ok := st.globals[name]
|
||||
|
||||
if ok {
|
||||
panic(core.Error(ErrVariableNotUnique, name))
|
||||
panic(runtime.Error(ErrVariableNotUnique, name))
|
||||
}
|
||||
|
||||
op := st.AddConstant(core.NewString(name))
|
||||
op := st.AddConstant(runtime.NewString(name))
|
||||
// Define global variable.
|
||||
st.globals[name] = op
|
||||
|
||||
@@ -120,25 +119,25 @@ func (st *SymbolTable) DefineVariable(name string) runtime.Operand {
|
||||
return register
|
||||
}
|
||||
|
||||
func (st *SymbolTable) Variable(name string) runtime.Operand {
|
||||
func (st *SymbolTable) Variable(name string) vm.Operand {
|
||||
for i := len(st.locals) - 1; i >= 0; i-- {
|
||||
variable := st.locals[i]
|
||||
if variable.Name == name {
|
||||
return runtime.NewRegisterOperand(int(variable.Register))
|
||||
return vm.NewRegisterOperand(int(variable.Register))
|
||||
}
|
||||
}
|
||||
|
||||
op, ok := st.globals[name]
|
||||
|
||||
if !ok {
|
||||
panic(core.Error(ErrVariableNotFound, name))
|
||||
panic(runtime.Error(ErrVariableNotFound, name))
|
||||
}
|
||||
|
||||
return op
|
||||
}
|
||||
|
||||
// GlobalVariable returns a global variable by its name.
|
||||
func (st *SymbolTable) GlobalVariable(name string) (runtime.Operand, bool) {
|
||||
func (st *SymbolTable) GlobalVariable(name string) (vm.Operand, bool) {
|
||||
op, ok := st.globals[name]
|
||||
|
||||
return op, ok
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
@@ -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)
|
@@ -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))
|
||||
|
@@ -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)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"hash/fnv"
|
@@ -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 {
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
func CastBoolean(input Value) (Boolean, error) {
|
||||
boolean, ok := input.(Boolean)
|
@@ -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:
|
@@ -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
|
||||
}
|
||||
)
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
type (
|
||||
Comparable interface {
|
@@ -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)
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"hash/fnv"
|
@@ -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)
|
@@ -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)
|
||||
}
|
||||
|
@@ -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())
|
||||
})
|
@@ -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}
|
||||
}
|
@@ -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}
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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)
|
||||
}
|
||||
)
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
@@ -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)
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
@@ -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)
|
||||
})
|
||||
})
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
@@ -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)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
@@ -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)
|
@@ -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")
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
import "context"
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
type Namespace interface {
|
||||
Namespace(name string) Namespace
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
@@ -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 {
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
@@ -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()
|
||||
|
@@ -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)
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
type (
|
||||
Location struct {
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
@@ -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")
|
@@ -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"
|
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"encoding/json"
|
@@ -1,9 +0,0 @@
|
||||
package values
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
)
|
||||
|
||||
func NewArray(cap int) core.List {
|
||||
return core.NewArray(cap)
|
||||
}
|
@@ -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]
|
||||
}
|
||||
|
@@ -1,13 +1,13 @@
|
||||
package runtime
|
||||
package vm
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"io"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/logging"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
)
|
||||
|
||||
func WithParams(params map[string]core.Value) EnvironmentOption {
|
||||
func WithParams(params map[string]runtime.Value) EnvironmentOption {
|
||||
return func(env *Environment) {
|
||||
env.params = params
|
||||
}
|
||||
@@ -15,14 +15,14 @@ func WithParams(params map[string]core.Value) EnvironmentOption {
|
||||
|
||||
func WithParam(name string, value interface{}) EnvironmentOption {
|
||||
return func(options *Environment) {
|
||||
options.params[name] = core.Parse(value)
|
||||
options.params[name] = runtime.Parse(value)
|
||||
}
|
||||
}
|
||||
|
||||
func WithFunctions(functions map[string]core.Function) EnvironmentOption {
|
||||
func WithFunctions(functions map[string]runtime.Function) EnvironmentOption {
|
||||
return func(env *Environment) {
|
||||
if env.functions == nil {
|
||||
env.functions = make(map[string]core.Function)
|
||||
env.functions = make(map[string]runtime.Function)
|
||||
}
|
||||
|
||||
for name, function := range functions {
|
||||
@@ -31,7 +31,7 @@ func WithFunctions(functions map[string]core.Function) EnvironmentOption {
|
||||
}
|
||||
}
|
||||
|
||||
func WithFunction(name string, function core.Function) EnvironmentOption {
|
||||
func WithFunction(name string, function runtime.Function) EnvironmentOption {
|
||||
return func(env *Environment) {
|
||||
env.functions[name] = function
|
||||
}
|
27
pkg/vm/errors.go
Normal file
27
pkg/vm/errors.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package vm
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
var (
|
||||
ErrMissedParam = errors.New("missed parameter")
|
||||
ErrFunctionNotFound = errors.New("function not found")
|
||||
)
|
||||
|
||||
type (
|
||||
SourceErrorDetail struct {
|
||||
error
|
||||
BaseError error
|
||||
ComputeError error
|
||||
}
|
||||
)
|
||||
|
||||
func (e *SourceErrorDetail) Error() string {
|
||||
return e.ComputeError.Error()
|
||||
}
|
||||
|
||||
func SourceError(src SourceMap, err error) error {
|
||||
return &SourceErrorDetail{
|
||||
BaseError: err,
|
||||
ComputeError: errors.Errorf("%s: %s", err.Error(), src.String()),
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package runtime
|
||||
package vm
|
||||
|
||||
import "fmt"
|
||||
|
@@ -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)
|
||||
}
|
@@ -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")
|
||||
}
|
@@ -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()
|
||||
}
|
||||
|
91
pkg/vm/internal/helpers.go
Normal file
91
pkg/vm/internal/helpers.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ToNumberOnly(ctx context.Context, input runtime.Value) runtime.Value {
|
||||
switch value := input.(type) {
|
||||
case runtime.Int, runtime.Float:
|
||||
return value
|
||||
case runtime.String:
|
||||
if strings.Contains(value.String(), ".") {
|
||||
if val, err := runtime.ToFloat(ctx, value); err == nil {
|
||||
return val
|
||||
}
|
||||
|
||||
return runtime.ZeroFloat
|
||||
}
|
||||
|
||||
if val, err := runtime.ToInt(ctx, value); err == nil {
|
||||
return val
|
||||
}
|
||||
|
||||
return runtime.ZeroFloat
|
||||
case runtime.Iterable:
|
||||
iterator, err := value.Iterate(ctx)
|
||||
|
||||
if err != nil {
|
||||
return runtime.ZeroInt
|
||||
}
|
||||
|
||||
i := runtime.ZeroInt
|
||||
f := runtime.ZeroFloat
|
||||
|
||||
for hasNext, err := iterator.HasNext(ctx); hasNext && err == nil; {
|
||||
val, _, err := iterator.Next(ctx)
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
out := ToNumberOnly(ctx, val)
|
||||
|
||||
switch item := out.(type) {
|
||||
case runtime.Int:
|
||||
i += item
|
||||
case runtime.Float:
|
||||
f += item
|
||||
}
|
||||
}
|
||||
|
||||
if f == 0 {
|
||||
return i
|
||||
}
|
||||
|
||||
return runtime.Float(i) + f
|
||||
default:
|
||||
if val, err := runtime.ToFloat(ctx, value); err == nil {
|
||||
return val
|
||||
}
|
||||
|
||||
return runtime.ZeroInt
|
||||
}
|
||||
}
|
||||
|
||||
func ToRegexp(input runtime.Value) (*Regexp, error) {
|
||||
switch r := input.(type) {
|
||||
case *Regexp:
|
||||
return r, nil
|
||||
case runtime.String:
|
||||
return NewRegexp(r)
|
||||
default:
|
||||
return nil, runtime.TypeError(input, runtime.TypeString, "regexp")
|
||||
}
|
||||
}
|
||||
|
||||
func Sleep(ctx context.Context, duration runtime.Int) error {
|
||||
timer := time.NewTimer(time.Millisecond * time.Duration(duration))
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
timer.Stop()
|
||||
return ctx.Err()
|
||||
case <-timer.C:
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@@ -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")
|
||||
}
|
@@ -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,
|
16
pkg/vm/internal/noop_iter.go
Normal file
16
pkg/vm/internal/noop_iter.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
)
|
||||
|
||||
type noopIter struct{}
|
||||
|
||||
func (n noopIter) HasNext(_ context.Context) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (n noopIter) Next(_ context.Context) (value runtime.Value, key runtime.Value, err error) {
|
||||
return runtime.None, runtime.None, nil
|
||||
}
|
266
pkg/vm/internal/operators.go
Normal file
266
pkg/vm/internal/operators.go
Normal file
@@ -0,0 +1,266 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/pkg/errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Contains(ctx context.Context, input runtime.Value, value runtime.Value) runtime.Boolean {
|
||||
switch val := input.(type) {
|
||||
case runtime.List:
|
||||
contains, err := val.Contains(ctx, value)
|
||||
if err != nil {
|
||||
return runtime.False
|
||||
}
|
||||
|
||||
return contains
|
||||
case runtime.Map:
|
||||
containsValue, err := val.ContainsValue(ctx, value)
|
||||
|
||||
if err != nil {
|
||||
return runtime.False
|
||||
}
|
||||
|
||||
return containsValue
|
||||
case runtime.String:
|
||||
return runtime.Boolean(strings.Contains(val.String(), value.String()))
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func Add(ctx context.Context, inputL, inputR runtime.Value) runtime.Value {
|
||||
left := runtime.ToNumberOrString(inputL)
|
||||
|
||||
switch leftVal := left.(type) {
|
||||
case runtime.Int:
|
||||
return addLeftInt(leftVal, inputR)
|
||||
case runtime.Float:
|
||||
return addLeftFloat(leftVal, inputR)
|
||||
case runtime.String:
|
||||
return addLeftString(leftVal, inputR)
|
||||
default:
|
||||
return runtime.String(leftVal.String() + inputR.String())
|
||||
}
|
||||
}
|
||||
|
||||
func addLeftInt(integer runtime.Int, input runtime.Value) runtime.Value {
|
||||
right := runtime.ToNumberOrString(input)
|
||||
|
||||
switch rightVal := right.(type) {
|
||||
case runtime.Int:
|
||||
return integer + rightVal
|
||||
case runtime.Float:
|
||||
return runtime.Float(integer) + rightVal
|
||||
default:
|
||||
return runtime.String(integer.String() + rightVal.String())
|
||||
}
|
||||
}
|
||||
|
||||
func addLeftFloat(float runtime.Float, input runtime.Value) runtime.Value {
|
||||
right := runtime.ToNumberOrString(input)
|
||||
|
||||
switch rightVal := right.(type) {
|
||||
case runtime.Int:
|
||||
return float + runtime.Float(rightVal)
|
||||
case runtime.Float:
|
||||
return float + rightVal
|
||||
default:
|
||||
return runtime.String(float.String() + rightVal.String())
|
||||
}
|
||||
}
|
||||
|
||||
func addLeftString(str runtime.String, input runtime.Value) runtime.Value {
|
||||
return runtime.String(str.String() + input.String())
|
||||
}
|
||||
|
||||
func Subtract(ctx context.Context, inputL, inputR runtime.Value) runtime.Value {
|
||||
left := ToNumberOnly(ctx, inputL)
|
||||
|
||||
switch leftVal := left.(type) {
|
||||
case runtime.Int:
|
||||
return subtractLeftInt(ctx, leftVal, inputR)
|
||||
case runtime.Float:
|
||||
return subtractLeftFloat(ctx, leftVal, inputR)
|
||||
default:
|
||||
return runtime.ZeroInt
|
||||
}
|
||||
}
|
||||
|
||||
func subtractLeftInt(ctx context.Context, integer runtime.Int, input runtime.Value) runtime.Value {
|
||||
right := ToNumberOnly(ctx, input)
|
||||
|
||||
switch rightVal := right.(type) {
|
||||
case runtime.Int:
|
||||
return integer - rightVal
|
||||
case runtime.Float:
|
||||
return runtime.Float(integer) - rightVal
|
||||
default:
|
||||
return runtime.ZeroInt
|
||||
}
|
||||
}
|
||||
|
||||
func subtractLeftFloat(ctx context.Context, float runtime.Float, input runtime.Value) runtime.Value {
|
||||
right := ToNumberOnly(ctx, input)
|
||||
|
||||
switch rightVal := right.(type) {
|
||||
case runtime.Int:
|
||||
return float - runtime.Float(rightVal)
|
||||
case runtime.Float:
|
||||
return float - rightVal
|
||||
default:
|
||||
return runtime.ZeroInt
|
||||
}
|
||||
}
|
||||
|
||||
func Multiply(ctx context.Context, inputL, inputR runtime.Value) runtime.Value {
|
||||
left := ToNumberOnly(ctx, inputL)
|
||||
|
||||
switch leftVal := left.(type) {
|
||||
case runtime.Int:
|
||||
return multiplyLeftInt(ctx, leftVal, inputR)
|
||||
case runtime.Float:
|
||||
return multiplyLeftFloat(ctx, leftVal, inputR)
|
||||
default:
|
||||
return runtime.ZeroInt
|
||||
}
|
||||
}
|
||||
|
||||
func multiplyLeftInt(ctx context.Context, integer runtime.Int, input runtime.Value) runtime.Value {
|
||||
right := ToNumberOnly(ctx, input)
|
||||
|
||||
switch rightVal := right.(type) {
|
||||
case runtime.Int:
|
||||
return integer * rightVal
|
||||
case runtime.Float:
|
||||
return runtime.Float(integer) * rightVal
|
||||
default:
|
||||
return runtime.ZeroInt
|
||||
}
|
||||
}
|
||||
|
||||
func multiplyLeftFloat(ctx context.Context, float runtime.Float, input runtime.Value) runtime.Value {
|
||||
right := ToNumberOnly(ctx, input)
|
||||
|
||||
switch rightVal := right.(type) {
|
||||
case runtime.Int:
|
||||
return float * runtime.Float(rightVal)
|
||||
case runtime.Float:
|
||||
return float * rightVal
|
||||
default:
|
||||
return runtime.ZeroInt
|
||||
}
|
||||
}
|
||||
|
||||
func Divide(ctx context.Context, inputL, inputR runtime.Value) runtime.Value {
|
||||
left := ToNumberOnly(ctx, inputL)
|
||||
|
||||
switch leftVal := left.(type) {
|
||||
case runtime.Int:
|
||||
return divideLeftInt(ctx, leftVal, inputR)
|
||||
case runtime.Float:
|
||||
return divideLeftFloat(ctx, leftVal, inputR)
|
||||
default:
|
||||
return runtime.ZeroInt
|
||||
}
|
||||
}
|
||||
|
||||
func divideLeftInt(ctx context.Context, integer runtime.Int, input runtime.Value) runtime.Value {
|
||||
right := ToNumberOnly(ctx, input)
|
||||
|
||||
switch rightVal := right.(type) {
|
||||
case runtime.Int:
|
||||
return integer / rightVal
|
||||
case runtime.Float:
|
||||
return runtime.Float(integer) / rightVal
|
||||
default:
|
||||
return runtime.ZeroInt
|
||||
}
|
||||
}
|
||||
|
||||
func divideLeftFloat(ctx context.Context, float runtime.Float, input runtime.Value) runtime.Value {
|
||||
right := ToNumberOnly(ctx, input)
|
||||
|
||||
switch rightVal := right.(type) {
|
||||
case runtime.Int:
|
||||
return float / runtime.Float(rightVal)
|
||||
case runtime.Float:
|
||||
return float / rightVal
|
||||
default:
|
||||
return runtime.ZeroInt
|
||||
}
|
||||
}
|
||||
|
||||
func Modulus(ctx context.Context, inputL, inputR runtime.Value) runtime.Value {
|
||||
left, _ := runtime.ToInt(ctx, inputL)
|
||||
right, _ := runtime.ToInt(ctx, inputR)
|
||||
|
||||
return left % right
|
||||
}
|
||||
|
||||
func Increment(ctx context.Context, input runtime.Value) runtime.Value {
|
||||
left := ToNumberOnly(ctx, input)
|
||||
|
||||
switch value := left.(type) {
|
||||
case runtime.Int:
|
||||
return value + 1
|
||||
case runtime.Float:
|
||||
return value + 1
|
||||
default:
|
||||
return runtime.None
|
||||
}
|
||||
}
|
||||
|
||||
func Decrement(ctx context.Context, input runtime.Value) runtime.Value {
|
||||
left := ToNumberOnly(ctx, input)
|
||||
|
||||
switch value := left.(type) {
|
||||
case runtime.Int:
|
||||
return value - 1
|
||||
case runtime.Float:
|
||||
return value - 1
|
||||
default:
|
||||
return runtime.None
|
||||
}
|
||||
}
|
||||
|
||||
func ToRange(ctx context.Context, left, right runtime.Value) (runtime.Value, error) {
|
||||
start, err := runtime.ToInt(ctx, left)
|
||||
|
||||
if err != nil {
|
||||
return runtime.ZeroInt, err
|
||||
}
|
||||
|
||||
end, err := runtime.ToInt(ctx, right)
|
||||
|
||||
if err != nil {
|
||||
return runtime.ZeroInt, err
|
||||
}
|
||||
|
||||
return NewRange(int64(start), int64(end)), nil
|
||||
}
|
||||
|
||||
func Like(left, right runtime.Value) (runtime.Boolean, error) {
|
||||
if err := runtime.AssertString(left); err != nil {
|
||||
// TODO: Return the error? AQL just returns false
|
||||
return runtime.False, nil
|
||||
}
|
||||
|
||||
if err := runtime.AssertString(right); err != nil {
|
||||
// TODO: Return the error? AQL just returns false
|
||||
return runtime.False, nil
|
||||
}
|
||||
|
||||
r, err := glob.Compile(right.String())
|
||||
|
||||
if err != nil {
|
||||
return runtime.False, errors.Wrap(err, "invalid glob pattern")
|
||||
}
|
||||
|
||||
result := r.Match(left.String())
|
||||
|
||||
return runtime.NewBoolean(result), nil
|
||||
}
|
@@ -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 {
|
@@ -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
|
||||
}
|
@@ -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})
|
||||
})
|
||||
}
|
||||
|
@@ -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)))
|
||||
}
|
@@ -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")
|
||||
}
|
@@ -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")
|
||||
}
|
@@ -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
|
||||
)
|
@@ -1,4 +1,4 @@
|
||||
package runtime
|
||||
package vm
|
||||
|
||||
import "fmt"
|
||||
|
@@ -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
|
@@ -1,4 +1,4 @@
|
||||
package runtime
|
||||
package vm
|
||||
|
||||
import "fmt"
|
||||
|
@@ -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)
|
@@ -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
|
Reference in New Issue
Block a user