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

Move value specific Iterable and Iterator interfaces into core module (#233)

* Move value specific Iterable and Iterator interfaces into core module

* Update Makefile

Reverted test command

* Update collection.go

* Fixed wrong iterator usage

* Updated use of switch statements
This commit is contained in:
Tim Voronov 2019-02-15 07:41:08 -05:00 committed by GitHub
parent 1af8b37a0f
commit f8e061cc80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 82 additions and 93 deletions

View File

@ -64,4 +64,4 @@ else
git tag -a v$(RELEASE_VERSION) -m "New $(RELEASE_VERSION) version" && \
git push origin v$(RELEASE_VERSION) && \
goreleaser
endif
endif

View File

@ -78,11 +78,11 @@ func (flags Flags) List() []string {
continue
}
switch val.(type) {
switch v := val.(type) {
case int:
arg = fmt.Sprintf("--%s=%d", arg, val.(int))
arg = fmt.Sprintf("--%s=%d", arg, v)
case string:
arg = fmt.Sprintf("--%s=%s", arg, val.(string))
arg = fmt.Sprintf("--%s=%s", arg, v)
default:
arg = fmt.Sprintf("--%s", arg)
}

View File

@ -1430,37 +1430,37 @@ func (v *visitor) visit(node antlr.Tree, scope *scope) (core.Expression, error)
var out core.Expression
var err error
switch node.(type) {
switch ctx := node.(type) {
case *fql.BodyContext:
out, err = v.doVisitBody(node.(*fql.BodyContext), scope)
out, err = v.doVisitBody(ctx, scope)
case *fql.ExpressionContext:
out, err = v.doVisitExpression(node.(*fql.ExpressionContext), scope)
out, err = v.doVisitExpression(ctx, scope)
case *fql.ForExpressionContext:
out, err = v.doVisitForExpression(node.(*fql.ForExpressionContext), scope)
out, err = v.doVisitForExpression(ctx, scope)
case *fql.ReturnExpressionContext:
out, err = v.doVisitReturnExpression(node.(*fql.ReturnExpressionContext), scope)
out, err = v.doVisitReturnExpression(ctx, scope)
case *fql.ArrayLiteralContext:
out, err = v.doVisitArrayLiteral(node.(*fql.ArrayLiteralContext), scope)
out, err = v.doVisitArrayLiteral(ctx, scope)
case *fql.ObjectLiteralContext:
out, err = v.doVisitObjectLiteral(node.(*fql.ObjectLiteralContext), scope)
out, err = v.doVisitObjectLiteral(ctx, scope)
case *fql.StringLiteralContext:
out, err = v.doVisitStringLiteral(node.(*fql.StringLiteralContext))
out, err = v.doVisitStringLiteral(ctx)
case *fql.IntegerLiteralContext:
out, err = v.doVisitIntegerLiteral(node.(*fql.IntegerLiteralContext))
out, err = v.doVisitIntegerLiteral(ctx)
case *fql.FloatLiteralContext:
out, err = v.doVisitFloatLiteral(node.(*fql.FloatLiteralContext))
out, err = v.doVisitFloatLiteral(ctx)
case *fql.BooleanLiteralContext:
out, err = v.doVisitBooleanLiteral(node.(*fql.BooleanLiteralContext))
out, err = v.doVisitBooleanLiteral(ctx)
case *fql.NoneLiteralContext:
out, err = v.doVisitNoneLiteral(node.(*fql.NoneLiteralContext))
out, err = v.doVisitNoneLiteral(ctx)
case *fql.VariableContext:
out, err = v.doVisitVariable(node.(*fql.VariableContext), scope)
out, err = v.doVisitVariable(ctx, scope)
case *fql.VariableDeclarationContext:
out, err = v.doVisitVariableDeclaration(node.(*fql.VariableDeclarationContext), scope)
out, err = v.doVisitVariableDeclaration(ctx, scope)
case *fql.FunctionCallExpressionContext:
out, err = v.doVisitFunctionCallExpression(node.(*fql.FunctionCallExpressionContext), scope)
out, err = v.doVisitFunctionCallExpression(ctx, scope)
case *fql.ParamContext:
out, err = v.doVisitParamContext(node.(*fql.ParamContext), scope)
out, err = v.doVisitParamContext(ctx, scope)
default:
err = v.unexpectedToken(node)
}

View File

@ -7,49 +7,42 @@ import (
)
type (
Collection interface {
core.Value
// Measurable represents an interface of a value that can has length.
Measurable interface {
Length() values.Int
}
IndexedCollection interface {
Collection
core.Value
Measurable
Get(idx values.Int) core.Value
Set(idx values.Int, value core.Value) error
}
KeyedCollection interface {
Collection
core.Value
Measurable
Keys() []string
Get(key values.String) (core.Value, values.Boolean)
Set(key values.String, value core.Value)
}
IterableCollection interface {
core.Value
Iterate(ctx context.Context) (CollectionIterator, error)
}
CollectionIterator interface {
Next(ctx context.Context) (value core.Value, key core.Value, err error)
}
collectionIteratorWrapper struct {
coreIterator struct {
valVar string
keyVar string
values CollectionIterator
values core.Iterator
}
)
func NewCollectionIterator(
func NewCoreIterator(
valVar,
keyVar string,
values CollectionIterator,
values core.Iterator,
) (Iterator, error) {
return &collectionIteratorWrapper{valVar, keyVar, values}, nil
return &coreIterator{valVar, keyVar, values}, nil
}
func (iterator *collectionIteratorWrapper) Next(ctx context.Context, scope *core.Scope) (*core.Scope, error) {
func (iterator *coreIterator) Next(ctx context.Context, scope *core.Scope) (*core.Scope, error) {
val, key, err := iterator.values.Next(ctx)
if err != nil {

View File

@ -18,6 +18,17 @@ type (
Copy() Value
}
// Iterable represents an interface of a value that can be iterated by using an iterator.
Iterable interface {
Iterate(ctx context.Context) (Iterator, error)
}
// Iterator represents an interface of a value iterator.
// When iterator is exhausted it must return None as a value.
Iterator interface {
Next(ctx context.Context) (value Value, key Value, err error)
}
// 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.

View File

@ -54,20 +54,19 @@ func (ds *DataSource) Iterate(ctx context.Context, scope *core.Scope) (collectio
return collections.NewHTMLNodeIterator(ds.valVariable, ds.keyVariable, data.(values.HTMLNode))
default:
// fallback to user defined types
switch data.(type) {
case collections.IterableCollection:
collection := data.(collections.IterableCollection)
switch collection := data.(type) {
case core.Iterable:
iterator, err := collection.Iterate(ctx)
if err != nil {
return nil, err
}
return collections.NewCollectionIterator(ds.valVariable, ds.keyVariable, iterator)
return collections.NewCoreIterator(ds.valVariable, ds.keyVariable, iterator)
case collections.KeyedCollection:
return collections.NewIndexedIterator(ds.valVariable, ds.keyVariable, data.(collections.IndexedCollection))
return collections.NewKeyedIterator(ds.valVariable, ds.keyVariable, collection)
case collections.IndexedCollection:
return collections.NewKeyedIterator(ds.valVariable, ds.keyVariable, data.(collections.KeyedCollection))
return collections.NewIndexedIterator(ds.valVariable, ds.keyVariable, collection)
default:
return nil, core.TypeError(
data.Type(),
@ -75,6 +74,7 @@ func (ds *DataSource) Iterate(ctx context.Context, scope *core.Scope) (collectio
types.Object,
types.HTMLDocument,
types.HTMLElement,
core.NewType("Iterable"),
)
}
}

View File

@ -2,12 +2,12 @@ package expressions_test
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/expressions"
"github.com/MontFerret/ferret/pkg/runtime/values"
"testing"
"github.com/MontFerret/ferret/pkg/runtime/collections"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/expressions"
"github.com/MontFerret/ferret/pkg/runtime/values"
. "github.com/smartystreets/goconvey/convey"
)
@ -51,7 +51,7 @@ func (c *testIterableCollection) Hash() uint64 {
func (c *testIterableCollection) Copy() core.Value {
return c
}
func (c *testIterableCollection) Iterate(ctx context.Context) (collections.CollectionIterator, error) {
func (c *testIterableCollection) Iterate(ctx context.Context) (core.Iterator, error) {
return &testCollectionIterator{c.values, -1}, nil
}

View File

@ -31,9 +31,9 @@ func ParseDateTime(input interface{}) (DateTime, error) {
}
func ParseDateTimeWith(input interface{}, layout string) (DateTime, error) {
switch input.(type) {
switch value := input.(type) {
case string:
t, err := time.Parse(layout, input.(string))
t, err := time.Parse(layout, value)
if err != nil {
return DateTime{time.Now()}, err

View File

@ -206,44 +206,42 @@ func SetIn(ctx context.Context, to core.Value, byPath []core.Value, value core.V
}
func Parse(input interface{}) core.Value {
switch input.(type) {
switch value := input.(type) {
case bool:
return NewBoolean(input.(bool))
return NewBoolean(value)
case string:
return NewString(input.(string))
return NewString(value)
case int:
return NewInt(input.(int))
return NewInt(value)
case float64:
return NewFloat(input.(float64))
return NewFloat(value)
case float32:
return NewFloat(float64(input.(float32)))
return NewFloat(float64(value))
case time.Time:
return NewDateTime(input.(time.Time))
return NewDateTime(value)
case []interface{}:
input := input.([]interface{})
arr := NewArray(len(input))
arr := NewArray(len(value))
for _, el := range input {
for _, el := range value {
arr.Push(Parse(el))
}
return arr
case map[string]interface{}:
input := input.(map[string]interface{})
obj := NewObject()
for key, el := range input {
for key, el := range value {
obj.Set(NewString(key), Parse(el))
}
return obj
case []byte:
return NewBinary(input.([]byte))
return NewBinary(value)
case nil:
return None
default:
v := reflect.ValueOf(input)
t := reflect.TypeOf(input)
v := reflect.ValueOf(value)
t := reflect.TypeOf(value)
kind := t.Kind()
if kind == reflect.Slice || kind == reflect.Array {
@ -251,8 +249,8 @@ func Parse(input interface{}) core.Value {
arr := NewArray(size)
for i := 0; i < size; i++ {
value := v.Index(i)
arr.Push(Parse(value.Interface()))
curVal := v.Index(i)
arr.Push(Parse(curVal.Interface()))
}
return arr
@ -264,9 +262,9 @@ func Parse(input interface{}) core.Value {
for _, k := range keys {
key := Parse(k.Interface())
value := v.MapIndex(k)
curVal := v.MapIndex(k)
obj.Set(NewString(key.String()), Parse(value.Interface()))
obj.Set(NewString(key.String()), Parse(curVal.Interface()))
}
return obj
@ -278,9 +276,9 @@ func Parse(input interface{}) core.Value {
for i := 0; i < size; i++ {
field := t.Field(i)
value := v.Field(i)
fieldValue := v.Field(i)
obj.Set(NewString(field.Name), Parse(value.Interface()))
obj.Set(NewString(field.Name), Parse(fieldValue.Interface()))
}
return obj

View File

@ -23,7 +23,7 @@ func TestAppend(t *testing.T) {
So(err, ShouldBeNil)
So(out, ShouldNotEqual, arr)
So(out.(collections.Collection).Length(), ShouldBeGreaterThan, arr.Length())
So(out.(collections.Measurable).Length(), ShouldBeGreaterThan, arr.Length())
})
Convey("Should ignore non-unique items", t, func() {
@ -39,12 +39,12 @@ func TestAppend(t *testing.T) {
So(err, ShouldBeNil)
So(out, ShouldNotEqual, arr)
So(out.(collections.Collection).Length(), ShouldEqual, arr.Length())
So(out.(collections.Measurable).Length(), ShouldEqual, arr.Length())
out2, err := arrays.Append(context.Background(), arr, values.NewInt(6), values.True)
So(err, ShouldBeNil)
So(out2, ShouldNotEqual, arr)
So(out2.(collections.Collection).Length(), ShouldBeGreaterThan, arr.Length())
So(out2.(collections.Measurable).Length(), ShouldBeGreaterThan, arr.Length())
})
}

View File

@ -18,7 +18,7 @@ func Length(_ context.Context, inputs ...core.Value) (core.Value, error) {
value := inputs[0]
c, ok := value.(collections.Collection)
c, ok := value.(collections.Measurable)
if !ok {
return values.None, core.TypeError(value.Type(),
@ -28,7 +28,7 @@ func Length(_ context.Context, inputs ...core.Value) (core.Value, error) {
types.HTMLElement,
types.HTMLDocument,
types.Binary,
core.NewType("Collection"),
core.NewType("Measurable"),
)
}

View File

@ -3,7 +3,6 @@ package html
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"
@ -81,7 +80,7 @@ func (p *Paging) Copy() core.Value {
return values.None
}
func (p *Paging) Iterate(_ context.Context) (collections.CollectionIterator, error) {
func (p *Paging) Iterate(_ context.Context) (core.Iterator, error) {
return &PagingIterator{p.document, p.selector, -1}, nil
}

View File

@ -41,7 +41,7 @@ func WaitClass(_ context.Context, args ...core.Value) (core.Value, error) {
timeout := values.NewInt(defaultTimeout)
// lets figure out what is passed as 1st argument
switch args[0].(type) {
switch obj := args[0].(type) {
case values.DHTMLDocument:
// revalidate args with more accurate amount
err := core.ValidateArgs(args, 3, 4)
@ -57,12 +57,6 @@ func WaitClass(_ context.Context, args ...core.Value) (core.Value, error) {
return values.None, err
}
doc, ok := args[0].(values.DHTMLDocument)
if !ok {
return values.None, core.Errors(core.ErrInvalidType, ErrNotDynamic)
}
selector := args[1].(values.String)
class := args[2].(values.String)
@ -76,14 +70,8 @@ func WaitClass(_ context.Context, args ...core.Value) (core.Value, error) {
timeout = args[3].(values.Int)
}
return values.None, doc.WaitForClass(selector, class, timeout)
return values.None, obj.WaitForClass(selector, class, timeout)
case values.DHTMLNode:
el, ok := args[0].(values.DHTMLNode)
if !ok {
return values.None, core.Errors(core.ErrInvalidType, ErrNotDynamic)
}
class := args[1].(values.String)
if len(args) == 3 {
@ -96,7 +84,7 @@ func WaitClass(_ context.Context, args ...core.Value) (core.Value, error) {
timeout = args[2].(values.Int)
}
return values.None, el.WaitForClass(class, timeout)
return values.None, obj.WaitForClass(class, timeout)
default:
return values.None, core.Errors(core.ErrInvalidType, ErrNotDynamic)
}