1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-11-06 08:39:09 +02:00

New type system (#232)

* New type system

* Fixed dot notation for HTML elements
This commit is contained in:
Tim Voronov
2019-02-13 12:31:18 -05:00
committed by GitHub
parent b3bcbda3b9
commit 1af8b37a0f
185 changed files with 1379 additions and 820 deletions

View File

@@ -10,7 +10,7 @@ import (
type (
SortDirection int
Comparator func(ctx context.Context, first, second *core.Scope) (int, error)
Comparator func(ctx context.Context, first, second *core.Scope) (int64, error)
Sorter struct {
fn Comparator
@@ -136,7 +136,7 @@ func (iterator *SortIterator) sort(ctx context.Context, scope *core.Scope) ([]*c
break
}
eq = eq * int(comp.direction)
eq = eq * int64(comp.direction)
if eq == -1 {
out = true

View File

@@ -35,7 +35,7 @@ func TestSort(t *testing.T) {
}
s, _ := collections.NewSorter(
func(ctx context.Context, first, second *core.Scope) (int, error) {
func(ctx context.Context, first, second *core.Scope) (int64, error) {
return first.MustGetVariable(collections.DefaultValueVar).Compare(second.MustGetVariable(collections.DefaultValueVar)), nil
},
collections.SortDirectionAsc,
@@ -72,7 +72,7 @@ func TestSort(t *testing.T) {
}
s, _ := collections.NewSorter(
func(ctx context.Context, first, second *core.Scope) (int, error) {
func(ctx context.Context, first, second *core.Scope) (int64, error) {
return first.MustGetVariable(collections.DefaultValueVar).Compare(second.MustGetVariable(collections.DefaultValueVar)), nil
},
collections.SortDirectionDesc,
@@ -120,7 +120,7 @@ func TestSort(t *testing.T) {
}
s1, _ := collections.NewSorter(
func(ctx context.Context, first, second *core.Scope) (int, error) {
func(ctx context.Context, first, second *core.Scope) (int64, error) {
o1, _ := first.MustGetVariable(collections.DefaultValueVar).(*values.Object).Get("one")
o2, _ := second.MustGetVariable(collections.DefaultValueVar).(*values.Object).Get("one")
@@ -130,7 +130,7 @@ func TestSort(t *testing.T) {
)
s2, _ := collections.NewSorter(
func(ctx context.Context, first, second *core.Scope) (int, error) {
func(ctx context.Context, first, second *core.Scope) (int64, error) {
o1, _ := first.MustGetVariable(collections.DefaultValueVar).(*values.Object).Get("two")
o2, _ := second.MustGetVariable(collections.DefaultValueVar).(*values.Object).Get("two")
@@ -183,7 +183,7 @@ func TestSort(t *testing.T) {
}
s1, _ := collections.NewSorter(
func(ctx context.Context, first, second *core.Scope) (int, error) {
func(ctx context.Context, first, second *core.Scope) (int64, error) {
o1, _ := first.MustGetVariable(collections.DefaultValueVar).(*values.Object).Get("one")
o2, _ := second.MustGetVariable(collections.DefaultValueVar).(*values.Object).Get("one")
@@ -193,7 +193,7 @@ func TestSort(t *testing.T) {
)
s2, _ := collections.NewSorter(
func(ctx context.Context, first, second *core.Scope) (int, error) {
func(ctx context.Context, first, second *core.Scope) (int64, error) {
o1, _ := first.MustGetVariable(collections.DefaultValueVar).(*values.Object).Get("two")
o2, _ := second.MustGetVariable(collections.DefaultValueVar).(*values.Object).Get("two")
@@ -246,7 +246,7 @@ func TestSort(t *testing.T) {
}
s1, _ := collections.NewSorter(
func(ctx context.Context, first, second *core.Scope) (int, error) {
func(ctx context.Context, first, second *core.Scope) (int64, error) {
o1, _ := first.MustGetVariable(collections.DefaultValueVar).(*values.Object).Get("one")
o2, _ := second.MustGetVariable(collections.DefaultValueVar).(*values.Object).Get("one")
@@ -256,7 +256,7 @@ func TestSort(t *testing.T) {
)
s2, _ := collections.NewSorter(
func(ctx context.Context, first, second *core.Scope) (int, error) {
func(ctx context.Context, first, second *core.Scope) (int64, error) {
o1, _ := first.MustGetVariable(collections.DefaultValueVar).(*values.Object).Get("two")
o2, _ := second.MustGetVariable(collections.DefaultValueVar).(*values.Object).Get("two")
@@ -309,7 +309,7 @@ func TestSort(t *testing.T) {
}
s1, _ := collections.NewSorter(
func(ctx context.Context, first, second *core.Scope) (int, error) {
func(ctx context.Context, first, second *core.Scope) (int64, error) {
o1, _ := first.MustGetVariable(collections.DefaultValueVar).(*values.Object).Get("one")
o2, _ := second.MustGetVariable(collections.DefaultValueVar).(*values.Object).Get("one")
@@ -319,7 +319,7 @@ func TestSort(t *testing.T) {
)
s2, _ := collections.NewSorter(
func(ctx context.Context, first, second *core.Scope) (int, error) {
func(ctx context.Context, first, second *core.Scope) (int64, error) {
o1, _ := first.MustGetVariable(collections.DefaultValueVar).(*values.Object).Get("two")
o2, _ := second.MustGetVariable(collections.DefaultValueVar).(*values.Object).Get("two")

View File

@@ -24,17 +24,14 @@ func TestSourceError(t *testing.T) {
func TestTypeError(t *testing.T) {
Convey("Should match", t, func() {
e := core.TypeError(core.BooleanType)
e := core.TypeError(TypeA{})
So(e, ShouldNotBeNil)
e = core.TypeError(core.BooleanType, core.BooleanType)
e = core.TypeError(TypeA{}, TypeB{})
So(e, ShouldNotBeNil)
e = core.TypeError(core.BooleanType, core.BooleanType, core.IntType, core.FloatType)
So(e, ShouldNotBeNil)
cause := errors.New("invalid type: expected none or boolean or int or float, but got none")
e = core.TypeError(core.NoneType, core.NoneType, core.BooleanType, core.IntType, core.FloatType)
cause := errors.New("invalid type: expected type_b or type_c, but got type_a")
e = core.TypeError(TypeA{}, TypeB{}, TypeC{})
So(e.Error(), ShouldEqual, cause.Error())
})
}

View File

@@ -160,39 +160,55 @@ func BenchmarkScope(b *testing.B) {
}
}
type TestCloser struct {
closed bool
type (
TestCloserType struct{}
TestCloserValue struct {
closed bool
}
)
func (tct TestCloserType) ID() int64 {
return 99
}
func (tc *TestCloser) MarshalJSON() ([]byte, error) {
func (tct TestCloserType) String() string {
return "TestCloser"
}
func (tct TestCloserType) Equals(other core.Type) bool {
return other.ID() == tct.ID()
}
func (tc *TestCloserValue) MarshalJSON() ([]byte, error) {
return nil, core.ErrNotImplemented
}
func (tc *TestCloser) Type() core.Type {
return core.NoneType
func (tc *TestCloserValue) Type() core.Type {
return TestCloserType{}
}
func (tc *TestCloser) String() string {
func (tc *TestCloserValue) String() string {
return ""
}
func (tc *TestCloser) Compare(other core.Value) int {
func (tc *TestCloserValue) Compare(other core.Value) int64 {
return 0
}
func (tc *TestCloser) Unwrap() interface{} {
func (tc *TestCloserValue) Unwrap() interface{} {
return tc
}
func (tc *TestCloser) Hash() uint64 {
func (tc *TestCloserValue) Hash() uint64 {
return 0
}
func (tc *TestCloser) Copy() core.Value {
return &TestCloser{}
func (tc *TestCloserValue) Copy() core.Value {
return &TestCloserValue{}
}
func (tc *TestCloser) Close() error {
func (tc *TestCloserValue) Close() error {
if tc.closed {
return core.Error(core.ErrInvalidOperation, "already closed")
}
@@ -206,7 +222,7 @@ func TestCloseFunc(t *testing.T) {
Convey("Should close root scope and close all io.Closer values", t, func() {
rs, cf := core.NewRootScope()
tc := &TestCloser{}
tc := &TestCloserValue{}
rs.SetVariable("disposable", tc)
So(tc.closed, ShouldBeFalse)
@@ -220,7 +236,7 @@ func TestCloseFunc(t *testing.T) {
Convey("Should return error if it's already closed", t, func() {
rs, cf := core.NewRootScope()
tc := &TestCloser{}
tc := &TestCloserValue{}
rs.SetVariable("disposable", tc)
So(tc.closed, ShouldBeFalse)

83
pkg/runtime/core/type.go Normal file
View File

@@ -0,0 +1,83 @@
package core
import (
"github.com/pkg/errors"
"math/rand"
)
// Type represents runtime type with id for quick type check
// and Name for error messages
//revive:disable-next-line:redefines-builtin-id
type (
Type interface {
ID() int64
String() string
Equals(other Type) bool
}
BaseType struct {
id int64
name string
}
)
func NewType(name string) Type {
return BaseType{rand.Int63(), name}
}
func (t BaseType) ID() int64 {
return t.id
}
func (t BaseType) String() string {
return t.name
}
func (t BaseType) Equals(other Type) bool {
return t.id == other.ID()
}
// IsTypeOf return true when value's type
// is equal to check type.
// Returns false, otherwise.
func IsTypeOf(value Value, check Type) bool {
return value.Type().ID() == check.ID()
}
// ValidateType checks the match of
// value's type and required types.
func ValidateType(value Value, required ...Type) error {
var valid bool
tid := value.Type().ID()
for _, t := range required {
if tid == t.ID() {
valid = true
break
}
}
if !valid {
return TypeError(value.Type(), required...)
}
return nil
}
// ValidateValueTypePairs validate pairs of
// Values and Types.
// Returns error when type didn't match
func ValidateValueTypePairs(pairs ...PairValueType) error {
var err error
for idx, pair := range pairs {
err = ValidateType(pair.Value, pair.Types...)
if err != nil {
return errors.Errorf("pair %d: %v", idx, err)
}
}
return nil
}

View File

@@ -0,0 +1,109 @@
package core_test
import (
"github.com/MontFerret/ferret/pkg/runtime/core"
. "github.com/smartystreets/goconvey/convey"
"testing"
)
type (
Value struct {
type_ core.Type
}
TypeA struct{}
TypeB struct{}
TypeC struct{}
)
func (t Value) MarshalJSON() ([]byte, error) {
return nil, nil
}
func (t Value) Type() core.Type {
return t.type_
}
func (t Value) String() string {
return ""
}
func (t Value) Compare(other core.Value) int64 {
return 0
}
func (t Value) Unwrap() interface{} {
return nil
}
func (t Value) Hash() uint64 {
return 0
}
func (t Value) Copy() core.Value {
return t
}
func (t TypeA) ID() int64 {
return 1
}
func (t TypeA) String() string {
return "type_a"
}
func (t TypeA) Equals(other core.Type) bool {
return t.ID() == other.ID()
}
func (t TypeB) ID() int64 {
return 2
}
func (t TypeB) String() string {
return "type_b"
}
func (t TypeB) Equals(other core.Type) bool {
return t.ID() == other.ID()
}
func (t TypeC) ID() int64 {
return 3
}
func (t TypeC) String() string {
return "type_c"
}
func (t TypeC) Equals(other core.Type) bool {
return t.ID() == other.ID()
}
func TestType(t *testing.T) {
typeA := TypeA{}
typeB := TypeB{}
Convey("IsTypeOf", t, func() {
Convey("Should return 'false' when types are different", func() {
vA := Value{typeA}
So(core.IsTypeOf(vA, typeB), ShouldBeFalse)
})
})
}
func TestValidateType(t *testing.T) {
typeA := TypeA{}
typeB := TypeB{}
Convey("Should validate types", t, func() {
vA := Value{typeA}
vB := Value{typeB}
So(core.ValidateType(vA, typeA), ShouldBeNil)
So(core.ValidateType(vB, typeA), ShouldNotBeNil)
})
}

View File

@@ -1,120 +1,41 @@
package core
import (
"context"
"encoding/json"
"github.com/pkg/errors"
)
//revive:disable-next-line:redefines-builtin-id
type Type int64
type (
// Value represents an interface of
// any type that needs to be used during runtime
Value interface {
json.Marshaler
Type() Type
String() string
Compare(other Value) int64
Unwrap() interface{}
Hash() uint64
Copy() Value
}
const (
NoneType Type = 0
BooleanType Type = 1
IntType Type = 2
FloatType Type = 3
StringType Type = 4
DateTimeType Type = 5
ArrayType Type = 6
ObjectType Type = 7
HTMLElementType Type = 8
HTMLDocumentType Type = 9
BinaryType Type = 10
CustomType Type = 99
// Getter represents an interface of
// complex types that needs to be used to read values by path.
// The interface is created to let user-defined types be used in dot notation data access.
Getter interface {
GetIn(ctx context.Context, path []Value) (Value, error)
}
// Setter represents an interface of
// complex types that needs to be used to write values by path.
// The interface is created to let user-defined types be used in dot notation assignment.
Setter interface {
SetIn(ctx context.Context, path []Value, value Value) error
}
// PairValueType is a supporting
// structure that used in validateValueTypePairs.
PairValueType struct {
Value Value
Types []Type
}
)
var typestr = map[Type]string{
NoneType: "none",
BooleanType: "boolean",
IntType: "int",
FloatType: "float",
StringType: "string",
DateTimeType: "datetime",
ArrayType: "array",
ObjectType: "object",
HTMLElementType: "HTMLElement",
HTMLDocumentType: "HTMLDocument",
BinaryType: "BinaryType",
CustomType: "CustomType",
}
func (t Type) String() string {
return typestr[t]
}
// Value represents an interface of
// any type that needs to be used during runtime
type Value interface {
json.Marshaler
Type() Type
String() string
Compare(other Value) int
Unwrap() interface{}
Hash() uint64
Copy() Value
}
// Getter represents an interface of
// complex types that needs to be used to read values by path.
// The interface is created to let user-defined types be used in dot notation data access.
type Getter interface {
GetIn(path []Value) (Value, error)
}
// Setter represents an interface of
// complex types that needs to be used to write values by path.
// The interface is created to let user-defined types be used in dot notation assignment.
type Setter interface {
SetIn(path []Value, value Value) error
}
// IsTypeOf return true when value's type
// is equal to check type.
// Returns false, otherwise.
func IsTypeOf(value Value, check Type) bool {
return value.Type() == check
}
// ValidateType checks the match of
// value's type and required types.
func ValidateType(value Value, required ...Type) error {
var valid bool
ct := value.Type()
for _, t := range required {
if ct == t {
valid = true
break
}
}
if !valid {
return TypeError(value.Type(), required...)
}
return nil
}
// PairValueType is a supporting
// structure that used in validateValueTypePairs.
type PairValueType struct {
Value Value
Types []Type
}
// ValidateValueTypePairs validate pairs of
// Values and Types.
// Returns error when type didn't match
func ValidateValueTypePairs(pairs ...PairValueType) error {
var err error
for idx, pair := range pairs {
err = ValidateType(pair.Value, pair.Types...)
if err != nil {
return errors.Errorf("pair %d: %v", idx, err)
}
}
return nil
}

View File

@@ -1,76 +0,0 @@
package core_test
import (
"testing"
"time"
"github.com/MontFerret/ferret/pkg/drivers/http"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
. "github.com/smartystreets/goconvey/convey"
)
func TestTypeString(t *testing.T) {
Convey("The string representation of the type should match this type", t, func() {
So(core.Type(0).String(), ShouldEqual, "none")
So(core.Type(1).String(), ShouldEqual, "boolean")
So(core.Type(2).String(), ShouldEqual, "int")
So(core.Type(3).String(), ShouldEqual, "float")
So(core.Type(4).String(), ShouldEqual, "string")
So(core.Type(5).String(), ShouldEqual, "datetime")
So(core.Type(6).String(), ShouldEqual, "array")
So(core.Type(7).String(), ShouldEqual, "object")
So(core.Type(8).String(), ShouldEqual, "HTMLElement")
So(core.Type(9).String(), ShouldEqual, "HTMLDocument")
So(core.Type(10).String(), ShouldEqual, "BinaryType")
})
}
func TestIsTypeOf(t *testing.T) {
Convey("Check type by value", t, func() {
So(core.IsTypeOf(values.None, core.NoneType), ShouldBeTrue)
So(core.IsTypeOf(values.NewBoolean(true), core.BooleanType), ShouldBeTrue)
So(core.IsTypeOf(values.NewInt(1), core.IntType), ShouldBeTrue)
So(core.IsTypeOf(values.NewFloat(1.1), core.FloatType), ShouldBeTrue)
So(core.IsTypeOf(values.NewString("test"), core.StringType), ShouldBeTrue)
So(core.IsTypeOf(values.NewDateTime(time.Now()), core.DateTimeType), ShouldBeTrue)
So(core.IsTypeOf(values.NewArray(1), core.ArrayType), ShouldBeTrue)
So(core.IsTypeOf(values.NewObject(), core.ObjectType), ShouldBeTrue)
So(core.IsTypeOf(&http.HTMLElement{}, core.HTMLElementType), ShouldBeTrue)
So(core.IsTypeOf(&http.HTMLDocument{}, core.HTMLDocumentType), ShouldBeTrue)
So(core.IsTypeOf(values.NewBinary([]byte{}), core.BinaryType), ShouldBeTrue)
})
}
func TestValidateType(t *testing.T) {
Convey("Value should match type", t, func() {
So(core.ValidateType(values.None, core.NoneType), ShouldBeNil)
So(core.ValidateType(values.NewBoolean(true), core.BooleanType), ShouldBeNil)
So(core.ValidateType(values.NewInt(1), core.IntType), ShouldBeNil)
So(core.ValidateType(values.NewFloat(1.1), core.FloatType), ShouldBeNil)
So(core.ValidateType(values.NewString("test"), core.StringType), ShouldBeNil)
So(core.ValidateType(values.NewDateTime(time.Now()), core.DateTimeType), ShouldBeNil)
So(core.ValidateType(values.NewArray(1), core.ArrayType), ShouldBeNil)
So(core.ValidateType(values.NewObject(), core.ObjectType), ShouldBeNil)
So(core.ValidateType(&http.HTMLElement{}, core.HTMLElementType), ShouldBeNil)
So(core.ValidateType(&http.HTMLDocument{}, core.HTMLDocumentType), ShouldBeNil)
So(core.ValidateType(values.NewBinary([]byte{}), core.BinaryType), ShouldBeNil)
})
Convey("Value should not match type", t, func() {
So(core.ValidateType(values.None, core.BooleanType), ShouldBeError)
So(core.ValidateType(values.NewBoolean(true), core.IntType, core.NoneType), ShouldBeError)
So(core.ValidateType(values.NewInt(1), core.NoneType), ShouldBeError)
So(core.ValidateType(values.NewFloat(1.1), core.StringType), ShouldBeError)
So(core.ValidateType(values.NewString("test"), core.IntType, core.FloatType), ShouldBeError)
So(core.ValidateType(values.NewDateTime(time.Now()), core.BooleanType), ShouldBeError)
So(core.ValidateType(values.NewArray(1), core.StringType), ShouldBeError)
So(core.ValidateType(values.NewObject(), core.BooleanType), ShouldBeError)
So(core.ValidateType(&http.HTMLElement{}, core.ArrayType), ShouldBeError)
So(core.ValidateType(&http.HTMLDocument{}, core.HTMLElementType), ShouldBeError)
So(core.ValidateType(values.NewBinary([]byte{}), core.NoneType), ShouldBeError)
})
}

View File

@@ -2,9 +2,11 @@ package clauses
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/collections"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type CollectIterator struct {
@@ -59,7 +61,7 @@ func NewCollectIterator(
}
func newGroupSorter(selector *CollectSelector) (*collections.Sorter, error) {
return collections.NewSorter(func(ctx context.Context, first, second *core.Scope) (int, error) {
return collections.NewSorter(func(ctx context.Context, first, second *core.Scope) (int64, error) {
f, err := selector.expression.Exec(ctx, first)
if err != nil {
@@ -213,7 +215,7 @@ func (iterator *CollectIterator) group(ctx context.Context, scope *core.Scope) (
arr, ok := groupValue.(*values.Array)
if !ok {
return nil, core.TypeError(groupValue.Type(), core.IntType)
return nil, core.TypeError(groupValue.Type(), types.Int)
}
value, err := proj.selector.expression.Exec(ctx, dataSourceScope)
@@ -235,7 +237,7 @@ func (iterator *CollectIterator) group(ctx context.Context, scope *core.Scope) (
counter, ok := groupValue.(values.Int)
if !ok {
return nil, core.TypeError(groupValue.Type(), core.IntType)
return nil, core.TypeError(groupValue.Type(), types.Int)
}
groupValue = counter + 1

View File

@@ -2,8 +2,10 @@ package clauses
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/collections"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type LimitClause struct {
@@ -71,13 +73,13 @@ func (clause *LimitClause) Iterate(ctx context.Context, scope *core.Scope) (coll
}
func (clause *LimitClause) parseValue(val core.Value) (int, error) {
if val.Type() == core.IntType {
if val.Type() == types.Int {
return val.Unwrap().(int), nil
}
if val.Type() == core.FloatType {
if val.Type() == types.Float {
return int(val.Unwrap().(float64)), nil
}
return -1, core.TypeError(val.Type(), core.IntType, core.FloatType)
return -1, core.TypeError(val.Type(), types.Int, types.Float)
}

View File

@@ -71,7 +71,7 @@ func (clause *SortClause) Iterate(ctx context.Context, scope *core.Scope) (colle
}
func newSorter(srt *SorterExpression) (*collections.Sorter, error) {
return collections.NewSorter(func(ctx context.Context, first, second *core.Scope) (int, error) {
return collections.NewSorter(func(ctx context.Context, first, second *core.Scope) (int64, error) {
f, err := srt.expression.Exec(ctx, first)
if err != nil {

View File

@@ -2,9 +2,11 @@ package expressions
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/collections"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type DataSource struct {
@@ -44,11 +46,11 @@ func (ds *DataSource) Iterate(ctx context.Context, scope *core.Scope) (collectio
}
switch data.Type() {
case core.ArrayType:
case types.Array:
return collections.NewIndexedIterator(ds.valVariable, ds.keyVariable, data.(collections.IndexedCollection))
case core.ObjectType:
case types.Object:
return collections.NewKeyedIterator(ds.valVariable, ds.keyVariable, data.(collections.KeyedCollection))
case core.HTMLElementType, core.HTMLDocumentType:
case types.HTMLElement, types.HTMLDocument:
return collections.NewHTMLNodeIterator(ds.valVariable, ds.keyVariable, data.(values.HTMLNode))
default:
// fallback to user defined types
@@ -69,10 +71,10 @@ func (ds *DataSource) Iterate(ctx context.Context, scope *core.Scope) (collectio
default:
return nil, core.TypeError(
data.Type(),
core.ArrayType,
core.ObjectType,
core.HTMLDocumentType,
core.HTMLElementType,
types.Array,
types.Object,
types.HTMLDocument,
types.HTMLElement,
)
}
}

View File

@@ -11,6 +11,8 @@ import (
. "github.com/smartystreets/goconvey/convey"
)
var testIterableCollectionType = core.NewType("TestIterableCollection")
type (
testIterableCollection struct {
values collections.IndexedCollection
@@ -32,12 +34,12 @@ func (c *testIterableCollection) MarshalJSON() ([]byte, error) {
return nil, core.ErrInvalidOperation
}
func (c *testIterableCollection) Type() core.Type {
return core.Type(11)
return testIterableCollectionType
}
func (c *testIterableCollection) String() string {
return ""
}
func (c *testIterableCollection) Compare(other core.Value) int {
func (c *testIterableCollection) Compare(other core.Value) int64 {
return 1
}
func (c *testIterableCollection) Unwrap() interface{} {

View File

@@ -2,8 +2,10 @@ package literals
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type (
@@ -49,8 +51,8 @@ func (l *ObjectLiteral) Exec(ctx context.Context, scope *core.Scope) (core.Value
return values.None, err
}
if name.Type() != core.StringType {
return values.None, core.TypeError(name.Type(), core.StringType)
if name.Type() != types.String {
return values.None, core.TypeError(name.Type(), types.String)
}
obj.Set(name.(values.String), val)

View File

@@ -2,6 +2,7 @@ package expressions
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
)
@@ -46,7 +47,7 @@ func (e *MemberExpression) Exec(ctx context.Context, scope *core.Scope) (core.Va
strPath[idx] = segment
}
out, err := values.GetIn(val, strPath)
out, err := values.GetIn(ctx, val, strPath)
if err != nil {
return values.None, core.SourceError(e.src, err)

View File

@@ -2,8 +2,10 @@ package operators
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type (
@@ -88,7 +90,7 @@ func (operator *ArrayOperator) Exec(ctx context.Context, scope *core.Scope) (cor
}
func (operator *ArrayOperator) Eval(ctx context.Context, left, right core.Value) (core.Value, error) {
err := core.ValidateType(left, core.ArrayType)
err := core.ValidateType(left, types.Array)
if err != nil {
// TODO: Return the error? AQL just returns false

View File

@@ -2,6 +2,7 @@ package operators
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
)

View File

@@ -2,8 +2,10 @@ package operators
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type InOperator struct {
@@ -45,7 +47,7 @@ func (operator *InOperator) Exec(ctx context.Context, scope *core.Scope) (core.V
}
func (operator *InOperator) Eval(_ context.Context, left, right core.Value) (core.Value, error) {
err := core.ValidateType(right, core.ArrayType)
err := core.ValidateType(right, types.Array)
if err != nil {
// TODO: Return the error? AQL just returns false

View File

@@ -2,8 +2,10 @@ package operators
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type (
@@ -70,7 +72,7 @@ func (operator *LogicalOperator) Exec(ctx context.Context, scope *core.Scope) (c
leftBool := values.ToBoolean(left)
if operator.value == LogicalOperatorTypeAnd && leftBool == values.False {
if left.Type() == core.BooleanType {
if left.Type() == types.Boolean {
return values.False, nil
}
@@ -98,7 +100,7 @@ func (operator *LogicalOperator) Eval(_ context.Context, left, right core.Value)
leftBool := values.ToBoolean(left)
if operator.value == LogicalOperatorTypeAnd && leftBool == values.False {
if left.Type() == core.BooleanType {
if left.Type() == types.Boolean {
return values.False, nil
}

View File

@@ -2,8 +2,10 @@ package operators
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type (
@@ -89,15 +91,15 @@ func Not(left, _ core.Value) core.Value {
// Adds numbers
// Concats strings
func Add(left, right core.Value) core.Value {
if left.Type() == core.IntType {
if right.Type() == core.IntType {
if left.Type() == types.Int {
if right.Type() == types.Int {
l := left.(values.Int)
r := right.(values.Int)
return l + r
}
if right.Type() == core.FloatType {
if right.Type() == types.Float {
l := left.(values.Int)
r := right.(values.Float)
@@ -105,15 +107,15 @@ func Add(left, right core.Value) core.Value {
}
}
if left.Type() == core.FloatType {
if right.Type() == core.FloatType {
if left.Type() == types.Float {
if right.Type() == types.Float {
l := left.(values.Float)
r := right.(values.Float)
return l + r
}
if right.Type() == core.IntType {
if right.Type() == types.Int {
l := left.(values.Float)
r := right.(values.Int)
@@ -125,15 +127,15 @@ func Add(left, right core.Value) core.Value {
}
func Subtract(left, right core.Value) core.Value {
if left.Type() == core.IntType {
if right.Type() == core.IntType {
if left.Type() == types.Int {
if right.Type() == types.Int {
l := left.(values.Int)
r := right.(values.Int)
return l - r
}
if right.Type() == core.FloatType {
if right.Type() == types.Float {
l := left.(values.Int)
r := right.(values.Float)
@@ -141,15 +143,15 @@ func Subtract(left, right core.Value) core.Value {
}
}
if left.Type() == core.FloatType {
if right.Type() == core.FloatType {
if left.Type() == types.Float {
if right.Type() == types.Float {
l := left.(values.Float)
r := right.(values.Float)
return l - r
}
if right.Type() == core.IntType {
if right.Type() == types.Int {
l := left.(values.Float)
r := right.(values.Int)
@@ -161,15 +163,15 @@ func Subtract(left, right core.Value) core.Value {
}
func Multiply(left, right core.Value) core.Value {
if left.Type() == core.IntType {
if right.Type() == core.IntType {
if left.Type() == types.Int {
if right.Type() == types.Int {
l := left.(values.Int)
r := right.(values.Int)
return l * r
}
if right.Type() == core.FloatType {
if right.Type() == types.Float {
l := left.(values.Int)
r := right.(values.Float)
@@ -177,15 +179,15 @@ func Multiply(left, right core.Value) core.Value {
}
}
if left.Type() == core.FloatType {
if right.Type() == core.FloatType {
if left.Type() == types.Float {
if right.Type() == types.Float {
l := left.(values.Float)
r := right.(values.Float)
return l * r
}
if right.Type() == core.IntType {
if right.Type() == types.Int {
l := left.(values.Float)
r := right.(values.Int)
@@ -197,15 +199,15 @@ func Multiply(left, right core.Value) core.Value {
}
func Divide(left, right core.Value) core.Value {
if left.Type() == core.IntType {
if right.Type() == core.IntType {
if left.Type() == types.Int {
if right.Type() == types.Int {
l := left.(values.Int)
r := right.(values.Int)
return l / r
}
if right.Type() == core.FloatType {
if right.Type() == types.Float {
l := left.(values.Int)
r := right.(values.Float)
@@ -213,15 +215,15 @@ func Divide(left, right core.Value) core.Value {
}
}
if left.Type() == core.FloatType {
if right.Type() == core.FloatType {
if left.Type() == types.Float {
if right.Type() == types.Float {
l := left.(values.Float)
r := right.(values.Float)
return l / r
}
if right.Type() == core.IntType {
if right.Type() == types.Int {
l := left.(values.Float)
r := right.(values.Int)
@@ -233,15 +235,15 @@ func Divide(left, right core.Value) core.Value {
}
func Modulus(left, right core.Value) core.Value {
if left.Type() == core.IntType {
if right.Type() == core.IntType {
if left.Type() == types.Int {
if right.Type() == types.Int {
l := left.(values.Int)
r := right.(values.Int)
return l % r
}
if right.Type() == core.FloatType {
if right.Type() == types.Float {
l := left.(values.Int)
r := right.(values.Float)
@@ -249,15 +251,15 @@ func Modulus(left, right core.Value) core.Value {
}
}
if left.Type() == core.FloatType {
if right.Type() == core.FloatType {
if left.Type() == types.Float {
if right.Type() == types.Float {
l := left.(values.Float)
r := right.(values.Float)
return values.Int(l) % values.Int(r)
}
if right.Type() == core.IntType {
if right.Type() == types.Int {
l := left.(values.Float)
r := right.(values.Int)
@@ -269,13 +271,13 @@ func Modulus(left, right core.Value) core.Value {
}
func Increment(left, _ core.Value) core.Value {
if left.Type() == core.IntType {
if left.Type() == types.Int {
l := left.(values.Int)
return l + 1
}
if left.Type() == core.FloatType {
if left.Type() == types.Float {
l := left.(values.Float)
return l + 1
@@ -285,13 +287,13 @@ func Increment(left, _ core.Value) core.Value {
}
func Decrement(left, _ core.Value) core.Value {
if left.Type() == core.IntType {
if left.Type() == types.Int {
l := left.(values.Int)
return l - 1
}
if left.Type() == core.FloatType {
if left.Type() == types.Float {
l := left.(values.Float)
return l - 1
@@ -301,13 +303,13 @@ func Decrement(left, _ core.Value) core.Value {
}
func Negative(value, _ core.Value) core.Value {
err := core.ValidateType(value, core.IntType, core.FloatType)
err := core.ValidateType(value, types.Int, types.Float)
if err != nil {
return values.ZeroInt
}
if value.Type() == core.IntType {
if value.Type() == types.Int {
return -value.(values.Int)
}
@@ -315,13 +317,13 @@ func Negative(value, _ core.Value) core.Value {
}
func Positive(value, _ core.Value) core.Value {
err := core.ValidateType(value, core.IntType, core.FloatType)
err := core.ValidateType(value, types.Int, types.Float)
if err != nil {
return values.ZeroInt
}
if value.Type() == core.IntType {
if value.Type() == types.Int {
return +value.(values.Int)
}

View File

@@ -2,8 +2,10 @@ package operators
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type RangeOperator struct {
@@ -43,13 +45,13 @@ func (operator *RangeOperator) Exec(ctx context.Context, scope *core.Scope) (cor
}
func (operator *RangeOperator) Eval(_ context.Context, left, right core.Value) (core.Value, error) {
err := core.ValidateType(left, core.IntType, core.FloatType)
err := core.ValidateType(left, types.Int, types.Float)
if err != nil {
return values.None, core.SourceError(operator.src, err)
}
err = core.ValidateType(right, core.IntType, core.FloatType)
err = core.ValidateType(right, types.Int, types.Float)
if err != nil {
return values.None, core.SourceError(operator.src, err)
@@ -58,13 +60,13 @@ func (operator *RangeOperator) Eval(_ context.Context, left, right core.Value) (
var start int
var end int
if left.Type() == core.FloatType {
if left.Type() == types.Float {
start = int(left.(values.Float))
} else {
start = int(left.(values.Int))
}
if right.Type() == core.FloatType {
if right.Type() == types.Float {
end = int(right.(values.Float))
} else {
end = int(right.(values.Int))

View File

@@ -2,6 +2,7 @@ package operators
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
)

View File

@@ -2,6 +2,7 @@ package expressions_test
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
"testing"
"github.com/MontFerret/ferret/pkg/runtime/core"
@@ -41,7 +42,7 @@ func TestParameterExpressionExec(t *testing.T) {
value, err := existExp.Exec(ctx, &core.Scope{})
So(err, ShouldBeNil)
So(value.Type(), ShouldEqual, core.IntType)
So(value.Type().Equals(types.Int), ShouldBeTrue)
So(value.String(), ShouldEqual, "1")
})
@@ -57,6 +58,6 @@ func TestParameterExpressionExec(t *testing.T) {
So(err, ShouldNotBeNil)
So(err, ShouldHaveSameTypeAs, core.ErrNotFound)
So(value.Type(), ShouldEqual, core.NoneType)
So(value.Type().Equals(types.None), ShouldBeTrue)
})
}

View File

@@ -7,6 +7,7 @@ import (
"sort"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type (
@@ -32,7 +33,7 @@ func (t *Array) MarshalJSON() ([]byte, error) {
}
func (t *Array) Type() core.Type {
return core.ArrayType
return types.Array
}
func (t *Array) String() string {
@@ -45,22 +46,23 @@ func (t *Array) String() string {
return string(marshaled)
}
func (t *Array) Compare(other core.Value) int {
switch other.Type() {
case core.ArrayType:
func (t *Array) Compare(other core.Value) int64 {
if other.Type() == types.Array {
other := other.(*Array)
if t.Length() == 0 && other.Length() == 0 {
return 0
}
if t.Length() < other.Length() {
return -1
}
if t.Length() > other.Length() {
return 1
}
var res = 0
var res int64
var val core.Value
other.ForEach(func(otherVal core.Value, idx int) bool {
@@ -71,11 +73,9 @@ func (t *Array) Compare(other core.Value) int {
})
return res
case core.ObjectType:
return -1
default:
return 1
}
return types.Compare(types.Array, other.Type())
}
func (t *Array) Unwrap() interface{} {
@@ -216,9 +216,13 @@ func (t *Array) Clone() core.Cloneable {
var value core.Value
for idx := NewInt(0); idx < t.Length(); idx++ {
value = t.Get(idx)
if IsCloneable(value) {
value = value.(core.Cloneable).Clone()
cloneable, ok := value.(core.Cloneable)
if ok {
value = cloneable.Clone()
}
cloned.Push(value)
}

View File

@@ -2,6 +2,7 @@ package values_test
import (
"encoding/json"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
"testing"
"github.com/MontFerret/ferret/pkg/runtime/core"
@@ -56,7 +57,7 @@ func TestArray(t *testing.T) {
Convey("Should return type", func() {
arr := values.NewArray(1)
So(arr.Type(), ShouldEqual, core.ArrayType)
So(arr.Type().Equals(types.Array), ShouldBeTrue)
})
})

View File

@@ -7,6 +7,7 @@ import (
"io/ioutil"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type Binary []byte
@@ -30,17 +31,16 @@ func (b Binary) MarshalJSON() ([]byte, error) {
}
func (b Binary) Type() core.Type {
return core.BinaryType
return types.Binary
}
func (b Binary) String() string {
return string(b)
}
func (b Binary) Compare(other core.Value) int {
// TODO: Lame comparison, need to think more about it
switch other.Type() {
case core.BooleanType:
func (b Binary) Compare(other core.Value) int64 {
if other.Type() == types.Binary {
// TODO: Lame comparison, need to think more about it
b2 := other.(*Binary)
if b2.Length() == b.Length() {
@@ -52,9 +52,9 @@ func (b Binary) Compare(other core.Value) int {
}
return -1
default:
return 1
}
return types.Compare(types.Binary, other.Type())
}
func (b Binary) Unwrap() interface{} {

View File

@@ -6,12 +6,15 @@ import (
"strings"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type Boolean bool
var False = Boolean(false)
var True = Boolean(true)
const (
False = Boolean(false)
True = Boolean(true)
)
func NewBoolean(input bool) Boolean {
return Boolean(input)
@@ -60,7 +63,7 @@ func (t Boolean) MarshalJSON() ([]byte, error) {
}
func (t Boolean) Type() core.Type {
return core.BooleanType
return types.Boolean
}
func (t Boolean) String() string {
@@ -71,11 +74,10 @@ func (t Boolean) String() string {
return "false"
}
func (t Boolean) Compare(other core.Value) int {
func (t Boolean) Compare(other core.Value) int64 {
raw := bool(t)
switch other.Type() {
case core.BooleanType:
if types.Boolean.Equals(other.Type()) {
i := other.Unwrap().(bool)
if raw == i {
@@ -87,11 +89,9 @@ func (t Boolean) Compare(other core.Value) int {
}
return +1
case core.NoneType:
return 1
default:
return -1
}
return types.Compare(types.Boolean, other.Type())
}
func (t Boolean) Unwrap() interface{} {

View File

@@ -3,6 +3,7 @@ package values_test
import (
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
. "github.com/smartystreets/goconvey/convey"
"testing"
)
@@ -21,7 +22,7 @@ func TestBoolean(t *testing.T) {
Convey(".Type", t, func() {
Convey("Should return a type", func() {
So(values.True.Type(), ShouldEqual, core.BooleanType)
So(values.True.Type().Equals(types.Boolean), ShouldBeTrue)
})
})

View File

@@ -5,6 +5,7 @@ import (
"time"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
const DefaultTimeLayout = time.RFC3339
@@ -59,16 +60,15 @@ func (t DateTime) MarshalJSON() ([]byte, error) {
}
func (t DateTime) Type() core.Type {
return core.DateTimeType
return types.DateTime
}
func (t DateTime) String() string {
return t.Time.String()
}
func (t DateTime) Compare(other core.Value) int {
switch other.Type() {
case core.DateTimeType:
func (t DateTime) Compare(other core.Value) int64 {
if other.Type() == types.DateTime {
other := other.(DateTime)
if t.After(other.Time) {
@@ -80,13 +80,9 @@ func (t DateTime) Compare(other core.Value) int {
}
return 0
default:
if other.Type() > core.DateTimeType {
return -1
}
return 1
}
return types.Compare(types.DateTime, other.Type())
}
func (t DateTime) Unwrap() interface{} {

View File

@@ -9,11 +9,12 @@ import (
"strconv"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type Float float64
var ZeroFloat = Float(0.0)
const ZeroFloat = Float(0.0)
func NewFloat(input float64) Float {
return Float(input)
@@ -75,18 +76,18 @@ func (t Float) MarshalJSON() ([]byte, error) {
}
func (t Float) Type() core.Type {
return core.FloatType
return types.Float
}
func (t Float) String() string {
return fmt.Sprintf("%f", t)
}
func (t Float) Compare(other core.Value) int {
func (t Float) Compare(other core.Value) int64 {
otherType := other.Type()
raw := float64(t)
switch other.Type() {
case core.FloatType:
if otherType == types.Float {
f := other.Unwrap().(float64)
if raw == f {
@@ -98,7 +99,9 @@ func (t Float) Compare(other core.Value) int {
}
return +1
case core.IntType:
}
if otherType == types.Int {
i := other.Unwrap().(int)
f := float64(i)
@@ -111,11 +114,9 @@ func (t Float) Compare(other core.Value) int {
}
return +1
case core.BooleanType, core.NoneType:
return 1
default:
return -1
}
return types.Compare(types.Float, otherType)
}
func (t Float) Unwrap() interface{} {

View File

@@ -1,6 +1,7 @@
package values
import (
"context"
"encoding/binary"
"encoding/json"
"hash/fnv"
@@ -9,9 +10,10 @@ import (
"time"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
func GetIn(from core.Value, byPath []core.Value) (core.Value, error) {
func GetIn(ctx context.Context, from core.Value, byPath []core.Value) (core.Value, error) {
if byPath == nil || len(byPath) == 0 {
return None, nil
}
@@ -27,32 +29,32 @@ func GetIn(from core.Value, byPath []core.Value) (core.Value, error) {
segmentType := segment.Type()
switch result.Type() {
case core.ObjectType:
case types.Object:
obj := result.(*Object)
if segmentType != core.StringType {
return nil, core.TypeError(segmentType, core.StringType)
if segmentType != types.String {
return nil, core.TypeError(segmentType, types.String)
}
result, _ = obj.Get(segment.(String))
break
case core.ArrayType:
case types.Array:
arr := result.(*Array)
if segmentType != core.IntType {
return nil, core.TypeError(segmentType, core.IntType)
if segmentType != types.Int {
return nil, core.TypeError(segmentType, types.Int)
}
result = arr.Get(segment.(Int))
break
case core.HTMLElementType, core.HTMLDocumentType:
case types.HTMLElement, types.HTMLDocument:
el := result.(HTMLNode)
if segmentType == core.IntType {
if segmentType == types.Int {
result = el.GetChildNode(segment.(Int))
} else if segmentType == core.StringType {
} else if segmentType == types.String {
strSegment := segment.(String)
switch strSegment {
@@ -73,7 +75,7 @@ func GetIn(from core.Value, byPath []core.Value) (core.Value, error) {
case "length":
result = el.Length()
case "url":
if result.Type() == core.HTMLDocumentType {
if result.Type() == types.HTMLDocument {
doc, ok := result.(HTMLDocument)
if ok {
@@ -88,22 +90,22 @@ func GetIn(from core.Value, byPath []core.Value) (core.Value, error) {
return None, err
}
} else {
return nil, core.TypeError(segmentType, core.IntType, core.StringType)
return nil, core.TypeError(segmentType, types.Int, types.String)
}
default:
getter, ok := result.(core.Getter)
if ok {
return getter.GetIn(byPath[i:])
return getter.GetIn(ctx, byPath[i:])
}
return None, core.TypeError(
from.Type(),
core.ArrayType,
core.ObjectType,
core.HTMLDocumentType,
core.HTMLElementType,
types.Array,
types.Object,
types.HTMLDocument,
types.HTMLElement,
)
}
}
@@ -111,7 +113,7 @@ func GetIn(from core.Value, byPath []core.Value) (core.Value, error) {
return result, nil
}
func SetIn(to core.Value, byPath []core.Value, value core.Value) error {
func SetIn(ctx context.Context, to core.Value, byPath []core.Value, value core.Value) error {
if byPath == nil || len(byPath) == 0 {
return nil
}
@@ -126,11 +128,11 @@ func SetIn(to core.Value, byPath []core.Value, value core.Value) error {
segmentType := segment.Type()
switch parent.Type() {
case core.ObjectType:
case types.Object:
parent := parent.(*Object)
if segmentType != core.StringType {
return core.TypeError(segmentType, core.StringType)
if segmentType != types.String {
return core.TypeError(segmentType, types.String)
}
if isTarget == false {
@@ -140,9 +142,9 @@ func SetIn(to core.Value, byPath []core.Value, value core.Value) error {
}
break
case core.ArrayType:
if segmentType != core.IntType {
return core.TypeError(segmentType, core.IntType)
case types.Array:
if segmentType != types.Int {
return core.TypeError(segmentType, types.Int)
}
parent := parent.(*Array)
@@ -160,19 +162,19 @@ func SetIn(to core.Value, byPath []core.Value, value core.Value) error {
setter, ok := parent.(core.Setter)
if ok {
return setter.SetIn(byPath[idx:], value)
return setter.SetIn(ctx, byPath[idx:], value)
}
// redefine parent
isArray := segmentType == core.IntType
isArray := segmentType == types.Int
// it's not an index
if isArray == false {
obj := NewObject()
parent = obj
if segmentType != core.StringType {
return core.TypeError(segmentType, core.StringType)
if segmentType != types.String {
return core.TypeError(segmentType, types.String)
}
if isTarget {
@@ -190,7 +192,7 @@ func SetIn(to core.Value, byPath []core.Value, value core.Value) error {
}
// set new parent
if err := SetIn(to, byPath[0:idx-1], parent); err != nil {
if err := SetIn(ctx, to, byPath[0:idx-1], parent); err != nil {
return err
}
@@ -302,9 +304,9 @@ func Unmarshal(value json.RawMessage) (core.Value, error) {
func IsCloneable(value core.Value) Boolean {
switch value.Type() {
case core.ArrayType:
case types.Array:
return NewBoolean(true)
case core.ObjectType:
case types.Object:
return NewBoolean(true)
default:
return NewBoolean(false)
@@ -313,15 +315,15 @@ func IsCloneable(value core.Value) Boolean {
func ToBoolean(input core.Value) core.Value {
switch input.Type() {
case core.BooleanType:
case types.Boolean:
return input
case core.NoneType:
case types.None:
return False
case core.StringType:
case types.String:
return NewBoolean(input.String() != "")
case core.IntType:
case types.Int:
return NewBoolean(input.(Int) != 0)
case core.FloatType:
case types.Float:
return NewBoolean(input.(Float) != 0)
default:
return True
@@ -330,15 +332,15 @@ func ToBoolean(input core.Value) core.Value {
func ToArray(input core.Value) core.Value {
switch input.Type() {
case core.BooleanType,
core.IntType,
core.FloatType,
core.StringType,
core.DateTimeType:
case types.Boolean,
types.Int,
types.Float,
types.String,
types.DateTime:
return NewArrayWith(input)
case core.HTMLElementType,
core.HTMLDocumentType:
case types.HTMLElement,
types.HTMLDocument:
val := input.(HTMLNode)
attrs := val.GetAttributes()
@@ -357,9 +359,9 @@ func ToArray(input core.Value) core.Value {
})
return obj
case core.ArrayType:
case types.Array:
return input.Copy()
case core.ObjectType:
case types.Object:
obj, ok := input.(*Object)
if !ok {

View File

@@ -1,6 +1,7 @@
package values_test
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"testing"
@@ -8,39 +9,41 @@ import (
. "github.com/smartystreets/goconvey/convey"
)
type CustomType struct {
var CustomType = core.NewType("custom")
type CustomValue struct {
properties map[core.Value]core.Value
}
func (t *CustomType) MarshalJSON() ([]byte, error) {
func (t *CustomValue) MarshalJSON() ([]byte, error) {
return nil, core.ErrNotImplemented
}
func (t *CustomType) Type() core.Type {
return core.CustomType
func (t *CustomValue) Type() core.Type {
return CustomType
}
func (t *CustomType) String() string {
func (t *CustomValue) String() string {
return ""
}
func (t *CustomType) Compare(other core.Value) int {
func (t *CustomValue) Compare(other core.Value) int64 {
return other.Compare(t) * -1
}
func (t *CustomType) Unwrap() interface{} {
func (t *CustomValue) Unwrap() interface{} {
return t
}
func (t *CustomType) Hash() uint64 {
func (t *CustomValue) Hash() uint64 {
return 0
}
func (t *CustomType) Copy() core.Value {
func (t *CustomValue) Copy() core.Value {
return values.None
}
func (t *CustomType) GetIn(path []core.Value) (core.Value, error) {
func (t *CustomValue) GetIn(ctx context.Context, path []core.Value) (core.Value, error) {
if path == nil || len(path) == 0 {
return values.None, nil
}
@@ -56,10 +59,10 @@ func (t *CustomType) GetIn(path []core.Value) (core.Value, error) {
return propValue, nil
}
return values.GetIn(propValue, path[1:])
return values.GetIn(ctx, propValue, path[1:])
}
func (t *CustomType) SetIn(path []core.Value, value core.Value) error {
func (t *CustomValue) SetIn(ctx context.Context, path []core.Value, value core.Value) error {
if path == nil || len(path) == 0 {
return nil
}
@@ -77,17 +80,17 @@ func (t *CustomType) SetIn(path []core.Value, value core.Value) error {
return nil
}
return values.SetIn(propValue, path[1:], value)
return values.SetIn(ctx, propValue, path[1:], value)
}
func TestHelpers(t *testing.T) {
Convey("Helpers", t, func() {
Convey("Getter", func() {
Convey("It should get a value by a given path", func() {
ct := &CustomType{
ct := &CustomValue{
properties: map[core.Value]core.Value{
values.NewString("foo"): values.NewInt(1),
values.NewString("bar"): &CustomType{
values.NewString("bar"): &CustomValue{
properties: map[core.Value]core.Value{
values.NewString("qaz"): values.NewInt(2),
},
@@ -95,14 +98,16 @@ func TestHelpers(t *testing.T) {
},
}
foo, err := values.GetIn(ct, []core.Value{
ctx := context.Background()
foo, err := values.GetIn(ctx, ct, []core.Value{
values.NewString("foo"),
})
So(err, ShouldBeNil)
So(foo, ShouldEqual, values.NewInt(1))
qaz, err := values.GetIn(ct, []core.Value{
qaz, err := values.GetIn(ctx, ct, []core.Value{
values.NewString("bar"),
values.NewString("qaz"),
})
@@ -114,10 +119,10 @@ func TestHelpers(t *testing.T) {
Convey("Setter", func() {
Convey("It should get a value by a given path", func() {
ct := &CustomType{
ct := &CustomValue{
properties: map[core.Value]core.Value{
values.NewString("foo"): values.NewInt(1),
values.NewString("bar"): &CustomType{
values.NewString("bar"): &CustomValue{
properties: map[core.Value]core.Value{
values.NewString("qaz"): values.NewInt(2),
},
@@ -125,21 +130,23 @@ func TestHelpers(t *testing.T) {
},
}
err := values.SetIn(ct, []core.Value{
ctx := context.Background()
err := values.SetIn(ctx, ct, []core.Value{
values.NewString("foo"),
}, values.NewInt(2))
So(err, ShouldBeNil)
So(ct.properties[values.NewString("foo")], ShouldEqual, values.NewInt(2))
err = values.SetIn(ct, []core.Value{
err = values.SetIn(ctx, ct, []core.Value{
values.NewString("bar"),
values.NewString("qaz"),
}, values.NewString("foobar"))
So(err, ShouldBeNil)
qaz, err := values.GetIn(ct, []core.Value{
qaz, err := values.GetIn(ctx, ct, []core.Value{
values.NewString("bar"),
values.NewString("qaz"),
})

View File

@@ -7,11 +7,12 @@ import (
"strconv"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type Int int64
var ZeroInt = Int(0)
const ZeroInt = Int(0)
func NewInt(input int) Int {
return Int(int64(input))
@@ -65,16 +66,17 @@ func (t Int) MarshalJSON() ([]byte, error) {
}
func (t Int) Type() core.Type {
return core.IntType
return types.Int
}
func (t Int) String() string {
return strconv.Itoa(int(t))
}
func (t Int) Compare(other core.Value) int {
switch other.Type() {
case core.IntType:
func (t Int) Compare(other core.Value) int64 {
otherType := other.Type()
if otherType == types.Int {
i := other.(Int)
if t == i {
@@ -86,7 +88,9 @@ func (t Int) Compare(other core.Value) int {
}
return +1
case core.FloatType:
}
if otherType == types.Float {
f := other.(Float)
f2 := Float(t)
@@ -99,11 +103,9 @@ func (t Int) Compare(other core.Value) int {
}
return +1
case core.BooleanType, core.NoneType:
return 1
default:
return -1
}
return types.Compare(types.Int, otherType)
}
func (t Int) Unwrap() interface{} {

View File

@@ -2,6 +2,7 @@ package values
import (
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type none struct{}
@@ -13,20 +14,19 @@ func (t *none) MarshalJSON() ([]byte, error) {
}
func (t *none) Type() core.Type {
return core.NoneType
return types.None
}
func (t *none) String() string {
return ""
}
func (t *none) Compare(other core.Value) int {
switch other.Type() {
case core.NoneType:
func (t *none) Compare(other core.Value) int64 {
if other.Type() == types.None {
return 0
default:
return -1
}
return -1
}
func (t *none) Unwrap() interface{} {

View File

@@ -7,14 +7,17 @@ import (
"sort"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type (
ObjectPredicate = func(value core.Value, key string) bool
ObjectProperty struct {
ObjectProperty struct {
key string
value core.Value
}
Object struct {
value map[string]core.Value
}
@@ -43,7 +46,7 @@ func (t *Object) MarshalJSON() ([]byte, error) {
}
func (t *Object) Type() core.Type {
return core.ObjectType
return types.Object
}
func (t *Object) String() string {
@@ -59,22 +62,23 @@ func (t *Object) String() string {
// Compare compares the source object with other core.Value
// The behavior of the Compare is similar
// to the comparison of objects in ArangoDB
func (t *Object) Compare(other core.Value) int {
switch other.Type() {
case core.ObjectType:
func (t *Object) Compare(other core.Value) int64 {
if other.Type() == t.Type() {
other := other.(*Object)
if t.Length() == 0 && other.Length() == 0 {
return 0
}
if t.Length() < other.Length() {
return -1
}
if t.Length() > other.Length() {
return 1
}
var res = 0
var res int64
sortedT := sort.StringSlice(t.Keys())
sortedT.Sort()
@@ -92,6 +96,7 @@ func (t *Object) Compare(other core.Value) int {
tVal, _ = t.Get(NewString(tKey))
otherVal, _ = other.Get(NewString(tKey))
res = tVal.Compare(otherVal)
continue
}
@@ -105,9 +110,9 @@ func (t *Object) Compare(other core.Value) int {
}
return res
default:
return 1
}
return types.Compare(types.Object, other.Type())
}
func (t *Object) Unwrap() interface{} {
@@ -201,10 +206,6 @@ func (t *Object) Get(key String) (core.Value, Boolean) {
return None, NewBoolean(found)
}
func (t *Object) GetIn(path []core.Value) (core.Value, error) {
return GetIn(t, path)
}
func (t *Object) Set(key String, value core.Value) {
if value != nil {
t.value[string(key)] = value
@@ -217,20 +218,20 @@ func (t *Object) Remove(key String) {
delete(t.value, string(key))
}
func (t *Object) SetIn(path []core.Value, value core.Value) error {
return SetIn(t, path, value)
}
func (t *Object) Clone() core.Cloneable {
cloned := NewObject()
var value core.Value
var keyString String
for key := range t.value {
keyString = NewString(key)
value, _ = t.Get(keyString)
if IsCloneable(value) {
value = value.(core.Cloneable).Clone()
cloneable, ok := value.(core.Cloneable)
if ok {
value = cloneable.Clone()
}
cloned.Set(keyString, value)
}

View File

@@ -1,6 +1,7 @@
package values_test
import (
"github.com/MontFerret/ferret/pkg/runtime/values/types"
"testing"
"github.com/MontFerret/ferret/pkg/runtime/core"
@@ -63,7 +64,7 @@ func TestObject(t *testing.T) {
Convey("Should return type", func() {
obj := values.NewObject()
So(obj.Type(), ShouldEqual, core.ObjectType)
So(obj.Type().Equals(types.Object), ShouldBeTrue)
})
})

View File

@@ -7,12 +7,15 @@ import (
"strings"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type String string
var EmptyString = String("")
var SpaceString = String(" ")
const (
EmptyString = String("")
SpaceString = String(" ")
)
func NewString(input string) String {
if input == "" {
@@ -69,24 +72,19 @@ func (t String) MarshalJSON() ([]byte, error) {
}
func (t String) Type() core.Type {
return core.StringType
return types.String
}
func (t String) String() string {
return string(t)
}
func (t String) Compare(other core.Value) int {
switch other.Type() {
case core.StringType:
return strings.Compare(string(t), other.Unwrap().(string))
default:
if other.Type() > core.DateTimeType {
return -1
}
return 1
func (t String) Compare(other core.Value) int64 {
if other.Type() == types.String {
return int64(strings.Compare(string(t), other.Unwrap().(string)))
}
return types.Compare(types.String, other.Type())
}
func (t String) Unwrap() interface{} {

View File

@@ -0,0 +1,44 @@
package types
import "github.com/MontFerret/ferret/pkg/runtime/core"
// Comparison table of builtin types
var typeComparisonTable = map[core.Type]uint64{
None: 0,
Boolean: 1,
Int: 2,
Float: 3,
String: 4,
DateTime: 5,
Array: 6,
Object: 7,
HTMLElement: 8,
HTMLDocument: 9,
Binary: 10,
}
func Compare(first, second core.Type) int64 {
f, ok := typeComparisonTable[first]
// custom type
if !ok {
return -1
}
s, ok := typeComparisonTable[second]
// custom type
if !ok {
return 1
}
if f == s {
return 0
}
if f > s {
return 1
}
return -1
}

View File

@@ -0,0 +1,176 @@
package types_test
import (
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
. "github.com/smartystreets/goconvey/convey"
"testing"
)
func TestHelpers(t *testing.T) {
Convey("Compare", t, func() {
typesList := []core.Type{
types.None,
types.Boolean,
types.Int,
types.Float,
types.String,
types.DateTime,
types.Array,
types.Object,
types.Binary,
}
Convey("None", func() {
So(types.Compare(types.None, types.None), ShouldEqual, 0)
for _, t := range typesList[1:] {
So(types.Compare(types.None, t), ShouldEqual, -1)
}
})
Convey("Boolean", func() {
for _, t := range typesList {
switch t.ID() {
case types.None.ID():
So(types.Compare(types.Boolean, t), ShouldEqual, 1)
case types.Boolean.ID():
So(types.Compare(types.Boolean, t), ShouldEqual, 0)
default:
So(types.Compare(types.Boolean, t), ShouldEqual, -1)
}
}
})
Convey("Int", func() {
for _, t := range typesList {
switch t.ID() {
case types.None.ID():
So(types.Compare(types.Int, t), ShouldEqual, 1)
case types.Boolean.ID():
So(types.Compare(types.Int, t), ShouldEqual, 1)
case types.Int.ID():
So(types.Compare(types.Int, t), ShouldEqual, 0)
default:
So(types.Compare(types.Int, t), ShouldEqual, -1)
}
}
})
Convey("Float", func() {
for _, t := range typesList {
switch t.ID() {
case types.None.ID():
So(types.Compare(types.Float, t), ShouldEqual, 1)
case types.Boolean.ID():
So(types.Compare(types.Float, t), ShouldEqual, 1)
case types.Int.ID():
So(types.Compare(types.Float, t), ShouldEqual, 1)
case types.Float.ID():
So(types.Compare(types.Float, t), ShouldEqual, 0)
default:
So(types.Compare(types.Float, t), ShouldEqual, -1)
}
}
})
Convey("String", func() {
for _, t := range typesList {
switch t.ID() {
case types.None.ID():
So(types.Compare(types.String, t), ShouldEqual, 1)
case types.Boolean.ID():
So(types.Compare(types.String, t), ShouldEqual, 1)
case types.Int.ID():
So(types.Compare(types.String, t), ShouldEqual, 1)
case types.Float.ID():
So(types.Compare(types.String, t), ShouldEqual, 1)
case types.String.ID():
So(types.Compare(types.String, t), ShouldEqual, 0)
default:
So(types.Compare(types.String, t), ShouldEqual, -1)
}
}
})
Convey("DateTime", func() {
for _, t := range typesList {
switch t.ID() {
case types.None.ID():
So(types.Compare(types.DateTime, t), ShouldEqual, 1)
case types.Boolean.ID():
So(types.Compare(types.DateTime, t), ShouldEqual, 1)
case types.Int.ID():
So(types.Compare(types.DateTime, t), ShouldEqual, 1)
case types.Float.ID():
So(types.Compare(types.DateTime, t), ShouldEqual, 1)
case types.String.ID():
So(types.Compare(types.DateTime, t), ShouldEqual, 1)
case types.DateTime.ID():
So(types.Compare(types.DateTime, t), ShouldEqual, 0)
default:
So(types.Compare(types.DateTime, t), ShouldEqual, -1)
}
}
})
Convey("Array", func() {
for _, t := range typesList {
switch t.ID() {
case types.None.ID():
So(types.Compare(types.Array, t), ShouldEqual, 1)
case types.Boolean.ID():
So(types.Compare(types.Array, t), ShouldEqual, 1)
case types.Int.ID():
So(types.Compare(types.Array, t), ShouldEqual, 1)
case types.Float.ID():
So(types.Compare(types.Array, t), ShouldEqual, 1)
case types.String.ID():
So(types.Compare(types.Array, t), ShouldEqual, 1)
case types.DateTime.ID():
So(types.Compare(types.Array, t), ShouldEqual, 1)
case types.Array.ID():
So(types.Compare(types.Array, t), ShouldEqual, 0)
default:
So(types.Compare(types.Array, t), ShouldEqual, -1)
}
}
})
Convey("Object", func() {
for _, t := range typesList {
switch t.ID() {
case types.None.ID():
So(types.Compare(types.Object, t), ShouldEqual, 1)
case types.Boolean.ID():
So(types.Compare(types.Object, t), ShouldEqual, 1)
case types.Int.ID():
So(types.Compare(types.Object, t), ShouldEqual, 1)
case types.Float.ID():
So(types.Compare(types.Object, t), ShouldEqual, 1)
case types.String.ID():
So(types.Compare(types.Object, t), ShouldEqual, 1)
case types.DateTime.ID():
So(types.Compare(types.Object, t), ShouldEqual, 1)
case types.Array.ID():
So(types.Compare(types.Object, t), ShouldEqual, 1)
case types.Object.ID():
So(types.Compare(types.Object, t), ShouldEqual, 0)
default:
So(types.Compare(types.Object, t), ShouldEqual, -1)
}
}
})
Convey("Binary", func() {
for _, t := range typesList {
switch t.ID() {
case types.Binary.ID():
So(types.Compare(types.Binary, t), ShouldEqual, 0)
default:
So(types.Compare(types.Binary, t), ShouldEqual, 1)
}
}
})
})
}

View File

@@ -0,0 +1,17 @@
package types
import "github.com/MontFerret/ferret/pkg/runtime/core"
var (
None = core.NewType("none")
Boolean = core.NewType("boolean")
Int = core.NewType("int")
Float = core.NewType("float")
String = core.NewType("string")
DateTime = core.NewType("date_time")
Array = core.NewType("array")
Object = core.NewType("object")
Binary = core.NewType("binary")
HTMLElement = core.NewType("HTMLElement")
HTMLDocument = core.NewType("HTMLDocument")
)

View File

@@ -0,0 +1,84 @@
package types_test
import (
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
. "github.com/smartystreets/goconvey/convey"
"testing"
)
type TestValue struct {
t core.Type
}
func (v TestValue) MarshalJSON() ([]byte, error) {
return nil, nil
}
func (v TestValue) Type() core.Type {
return v.t
}
func (v TestValue) String() string {
return ""
}
func (v TestValue) Compare(other core.Value) int64 {
return 0
}
func (v TestValue) Unwrap() interface{} {
return nil
}
func (v TestValue) Hash() uint64 {
return 0
}
func (v TestValue) Copy() core.Value {
return v
}
func TestType(t *testing.T) {
Convey(".Name", t, func() {
So(types.None.String(), ShouldEqual, "none")
So(types.Boolean.String(), ShouldEqual, "boolean")
So(types.Int.String(), ShouldEqual, "int")
So(types.Float.String(), ShouldEqual, "float")
So(types.String.String(), ShouldEqual, "string")
So(types.DateTime.String(), ShouldEqual, "date_time")
So(types.Array.String(), ShouldEqual, "array")
So(types.Object.String(), ShouldEqual, "object")
So(types.Binary.String(), ShouldEqual, "binary")
})
Convey("==", t, func() {
typesList := []core.Type{
types.None,
types.Boolean,
types.Int,
types.Float,
types.String,
types.DateTime,
types.Array,
types.Object,
types.Binary,
}
valuesList := []core.Value{
TestValue{types.None},
TestValue{types.Boolean},
TestValue{types.Int},
TestValue{types.Float},
TestValue{types.String},
TestValue{types.DateTime},
TestValue{types.Array},
TestValue{types.Object},
TestValue{types.Binary},
}
for i, t := range typesList {
So(t == valuesList[i].Type(), ShouldBeTrue)
}
})
}