diff --git a/Makefile b/Makefile index 6082f83f..ffba0024 100644 --- a/Makefile +++ b/Makefile @@ -64,4 +64,4 @@ else git tag -a v$(RELEASE_VERSION) -m "New $(RELEASE_VERSION) version" && \ git push origin v$(RELEASE_VERSION) && \ goreleaser -endif \ No newline at end of file +endif diff --git a/cli/browser/flags.go b/cli/browser/flags.go index 47c6875a..da0fadf2 100644 --- a/cli/browser/flags.go +++ b/cli/browser/flags.go @@ -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) } diff --git a/pkg/compiler/visitor.go b/pkg/compiler/visitor.go index 8b87e9a2..e412e6f9 100644 --- a/pkg/compiler/visitor.go +++ b/pkg/compiler/visitor.go @@ -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) } diff --git a/pkg/runtime/collections/collection.go b/pkg/runtime/collections/collection.go index ca48a061..84e9d6b0 100644 --- a/pkg/runtime/collections/collection.go +++ b/pkg/runtime/collections/collection.go @@ -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 { diff --git a/pkg/runtime/core/value.go b/pkg/runtime/core/value.go index fa581889..3c4853c6 100644 --- a/pkg/runtime/core/value.go +++ b/pkg/runtime/core/value.go @@ -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. diff --git a/pkg/runtime/expressions/data_source.go b/pkg/runtime/expressions/data_source.go index eb8f6795..81f352a7 100644 --- a/pkg/runtime/expressions/data_source.go +++ b/pkg/runtime/expressions/data_source.go @@ -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"), ) } } diff --git a/pkg/runtime/expressions/data_source_test.go b/pkg/runtime/expressions/data_source_test.go index caebfb22..1e01e419 100644 --- a/pkg/runtime/expressions/data_source_test.go +++ b/pkg/runtime/expressions/data_source_test.go @@ -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 } diff --git a/pkg/runtime/values/date_time.go b/pkg/runtime/values/date_time.go index d6bec709..584e83ee 100644 --- a/pkg/runtime/values/date_time.go +++ b/pkg/runtime/values/date_time.go @@ -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 diff --git a/pkg/runtime/values/helpers.go b/pkg/runtime/values/helpers.go index a66afed3..bd7fe722 100644 --- a/pkg/runtime/values/helpers.go +++ b/pkg/runtime/values/helpers.go @@ -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 diff --git a/pkg/stdlib/arrays/append_test.go b/pkg/stdlib/arrays/append_test.go index 38053c29..8445be78 100644 --- a/pkg/stdlib/arrays/append_test.go +++ b/pkg/stdlib/arrays/append_test.go @@ -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()) }) } diff --git a/pkg/stdlib/collections/length.go b/pkg/stdlib/collections/length.go index 1d8eb22a..8afe053d 100644 --- a/pkg/stdlib/collections/length.go +++ b/pkg/stdlib/collections/length.go @@ -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"), ) } diff --git a/pkg/stdlib/html/pagination.go b/pkg/stdlib/html/pagination.go index 77f60527..edb8492a 100644 --- a/pkg/stdlib/html/pagination.go +++ b/pkg/stdlib/html/pagination.go @@ -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 } diff --git a/pkg/stdlib/html/wait_class.go b/pkg/stdlib/html/wait_class.go index f2640091..27b8cfa2 100644 --- a/pkg/stdlib/html/wait_class.go +++ b/pkg/stdlib/html/wait_class.go @@ -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) }