'enum' module (#166)
* 1. adding more methods to enum module 2. added ModuleMap.AddMap() 3. added bytes iterator * add builtin functions 'is_enumerable' and 'is_array_like' * builtin function 'is_iterable' * first iteration on 'enum' module * fix 'is_iterable' builtin function
This commit is contained in:
parent
b9c1c92d2d
commit
2f86800724
16 changed files with 418 additions and 78 deletions
|
@ -195,6 +195,10 @@ Returns `true` if the object's type is map. Or it returns `false`.
|
|||
|
||||
Returns `true` if the object's type is immutable map. Or it returns `false`.
|
||||
|
||||
## is_iterable
|
||||
|
||||
Returns `true` if the object's type is iterable: array, immutable array, map, immutable map, string, and bytes are iterable types in Tengo.
|
||||
|
||||
## is_time
|
||||
|
||||
Returns `true` if the object's type is time. Or it returns `false`.
|
||||
|
|
|
@ -61,6 +61,7 @@ _* time.Unix(): use `time.Unix(v, 0)` to convert to Time_
|
|||
- `bytes(x)`: tries to convert `x` into bytes; returns `undefined` if failed
|
||||
- `bytes(N)`: as a special case this will create a Bytes variable with the given size `N` (only if `N` is int)
|
||||
- `time(x)`: tries to convert `x` into time; returns `undefined` if failed
|
||||
- See [Builtins](https://github.com/d5/tengo/blob/master/docs/builtins.md) for the full list of builtin functions.
|
||||
|
||||
## Type Checking Builtin Functions
|
||||
|
||||
|
@ -77,3 +78,4 @@ _* time.Unix(): use `time.Unix(v, 0)` to convert to Time_
|
|||
- `is_time(x)`: return `true` if `x` is time; `false` otherwise
|
||||
- `is_error(x)`: returns `true` if `x` is error; `false` otherwise
|
||||
- `is_undefined(x)`: returns `true` if `x` is undefined; `false` otherwise
|
||||
- See [Builtins](https://github.com/d5/tengo/blob/master/docs/builtins.md) for the full list of builtin functions.
|
19
docs/stdlib-enum.md
Normal file
19
docs/stdlib-enum.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Module - "enum"
|
||||
|
||||
```golang
|
||||
enum := import("enum")
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
- `all(x, fn) => bool`: returns true if the given function `fn` evaluates to a truthy value on all of the items in `x`. It returns undefined if `x` is not enumerable.
|
||||
- `any(x, fn) => bool`: returns true if the given function `fn` evaluates to a truthy value on any of the items in `x`. It returns undefined if `x` is not enumerable.
|
||||
- `chunk(x, size) => [object]`: returns an array of elements split into groups the length of size. If `x` can't be split evenly, the final chunk will be the remaining elements. It returns undefined if `x` is not array.
|
||||
- `at(x, key) => object`: returns an element at the given index (if `x` is array) or key (if `x` is map). It returns undefined if `x` is not enumerable.
|
||||
- `each(x, fn)`: iterates over elements of `x` and invokes `fn` for each element. `fn` is invoked with two arguments: `key` and `value`. `key` is an int index if `x` is array. `key` is a string key if `x` is map. It does not iterate and returns undefined if `x` is not enumerable.`
|
||||
- `filter(x, fn) => [object]`: iterates over elements of `x`, returning an array of all elements `fn` returns truthy for. `fn` is invoked with two arguments: `key` and `value`. `key` is an int index if `x` is array. `key` is a string key if `x` is map. It returns undefined if `x` is not enumerable.
|
||||
- `find(x, fn) => object`: iterates over elements of `x`, returning value of the first element `fn` returns truthy for. `fn` is invoked with two arguments: `key` and `value`. `key` is an int index if `x` is array. `key` is a string key if `x` is map. It returns undefined if `x` is not enumerable.
|
||||
- `find_key(x, fn) => int/string`: iterates over elements of `x`, returning key or index of the first element `fn` returns truthy for. `fn` is invoked with two arguments: `key` and `value`. `key` is an int index if `x` is array. `key` is a string key if `x` is map. It returns undefined if `x` is not enumerable.
|
||||
- `map(x, fn) => [object]`: creates an array of values by running each element in `x` through `fn`. `fn` is invoked with two arguments: `key` and `value`. `key` is an int index if `x` is array. `key` is a string key if `x` is map. It returns undefined if `x` is not enumerable.
|
||||
- `key(k, _) => object`: returns the first argument.
|
||||
- `value(_, v) => object`: returns the second argument.
|
|
@ -7,3 +7,4 @@
|
|||
- [rand](https://github.com/d5/tengo/blob/master/docs/stdlib-rand.md): random functions
|
||||
- [fmt](https://github.com/d5/tengo/blob/master/docs/stdlib-fmt.md): formatting functions
|
||||
- [json](https://github.com/d5/tengo/blob/master/docs/stdlib-json.md): JSON functions
|
||||
- [enum](https://github.com/d5/tengo/blob/master/docs/stdlib-enum.md): Enumeration functions
|
|
@ -181,3 +181,15 @@ func builtinIsCallable(args ...Object) (Object, error) {
|
|||
|
||||
return FalseValue, nil
|
||||
}
|
||||
|
||||
func builtinIsIterable(args ...Object) (Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, ErrWrongNumArguments
|
||||
}
|
||||
|
||||
if _, ok := args[0].(Iterable); ok {
|
||||
return TrueValue, nil
|
||||
}
|
||||
|
||||
return FalseValue, nil
|
||||
}
|
||||
|
|
|
@ -83,6 +83,10 @@ var Builtins = []*BuiltinFunction{
|
|||
Name: "is_immutable_map",
|
||||
Value: builtinIsImmutableMap,
|
||||
},
|
||||
{
|
||||
Name: "is_iterable",
|
||||
Value: builtinIsIterable,
|
||||
},
|
||||
{
|
||||
Name: "is_time",
|
||||
Value: builtinIsTime,
|
||||
|
|
|
@ -79,3 +79,11 @@ func (o *Bytes) IndexGet(index Object) (res Object, err error) {
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
// Iterate creates a bytes iterator.
|
||||
func (o *Bytes) Iterate() Iterator {
|
||||
return &BytesIterator{
|
||||
v: o.Value,
|
||||
l: len(o.Value),
|
||||
}
|
||||
}
|
||||
|
|
57
objects/bytes_iterator.go
Normal file
57
objects/bytes_iterator.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package objects
|
||||
|
||||
import "github.com/d5/tengo/compiler/token"
|
||||
|
||||
// BytesIterator represents an iterator for a string.
|
||||
type BytesIterator struct {
|
||||
v []byte
|
||||
i int
|
||||
l int
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (i *BytesIterator) TypeName() string {
|
||||
return "bytes-iterator"
|
||||
}
|
||||
|
||||
func (i *BytesIterator) String() string {
|
||||
return "<bytes-iterator>"
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (i *BytesIterator) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (i *BytesIterator) IsFalsy() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (i *BytesIterator) Equals(Object) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (i *BytesIterator) Copy() Object {
|
||||
return &BytesIterator{v: i.v, i: i.i, l: i.l}
|
||||
}
|
||||
|
||||
// Next returns true if there are more elements to iterate.
|
||||
func (i *BytesIterator) Next() bool {
|
||||
i.i++
|
||||
return i.i <= i.l
|
||||
}
|
||||
|
||||
// Key returns the key or index value of the current element.
|
||||
func (i *BytesIterator) Key() Object {
|
||||
return &Int{Value: int64(i.i - 1)}
|
||||
}
|
||||
|
||||
// Value returns the value of the current element.
|
||||
func (i *BytesIterator) Value() Object {
|
||||
return &Int{Value: int64(i.v[i.i-1])}
|
||||
}
|
|
@ -68,3 +68,10 @@ func (m *ModuleMap) Copy() *ModuleMap {
|
|||
func (m *ModuleMap) Len() int {
|
||||
return len(m.m)
|
||||
}
|
||||
|
||||
// AddMap adds named modules from another module map.
|
||||
func (m *ModuleMap) AddMap(o *ModuleMap) {
|
||||
for name, mod := range o.m {
|
||||
m.m[name] = mod
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,140 @@ package runtime_test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
"github.com/d5/tengo/stdlib"
|
||||
)
|
||||
|
||||
func TestSourceModules(t *testing.T) {
|
||||
expect(t, `enum := import("enum"); out = enum.any([1,2,3], func(i, v) { return v == 2 })`,
|
||||
Opts().Module("enum", stdlib.SourceModules["enum"]),
|
||||
true)
|
||||
testEnumModule(t, `out = enum.key(0, 20)`, 0)
|
||||
testEnumModule(t, `out = enum.key(10, 20)`, 10)
|
||||
testEnumModule(t, `out = enum.value(0, 0)`, 0)
|
||||
testEnumModule(t, `out = enum.value(10, 20)`, 20)
|
||||
|
||||
testEnumModule(t, `out = enum.all([], enum.value)`, true)
|
||||
testEnumModule(t, `out = enum.all([1], enum.value)`, true)
|
||||
testEnumModule(t, `out = enum.all([true, 1], enum.value)`, true)
|
||||
testEnumModule(t, `out = enum.all([true, 0], enum.value)`, false)
|
||||
testEnumModule(t, `out = enum.all([true, 0, 1], enum.value)`, false)
|
||||
testEnumModule(t, `out = enum.all(immutable([true, 0, 1]), enum.value)`, false) // immutable-array
|
||||
testEnumModule(t, `out = enum.all({}, enum.value)`, true)
|
||||
testEnumModule(t, `out = enum.all({a:1}, enum.value)`, true)
|
||||
testEnumModule(t, `out = enum.all({a:true, b:1}, enum.value)`, true)
|
||||
testEnumModule(t, `out = enum.all(immutable({a:true, b:1}), enum.value)`, true) // immutable-map
|
||||
testEnumModule(t, `out = enum.all({a:true, b:0}, enum.value)`, false)
|
||||
testEnumModule(t, `out = enum.all({a:true, b:0, c:1}, enum.value)`, false)
|
||||
testEnumModule(t, `out = enum.all(0, enum.value)`, objects.UndefinedValue) // non-enumerable: undefined
|
||||
testEnumModule(t, `out = enum.all("123", enum.value)`, objects.UndefinedValue) // non-enumerable: undefined
|
||||
|
||||
testEnumModule(t, `out = enum.any([], enum.value)`, false)
|
||||
testEnumModule(t, `out = enum.any([1], enum.value)`, true)
|
||||
testEnumModule(t, `out = enum.any([true, 1], enum.value)`, true)
|
||||
testEnumModule(t, `out = enum.any([true, 0], enum.value)`, true)
|
||||
testEnumModule(t, `out = enum.any([true, 0, 1], enum.value)`, true)
|
||||
testEnumModule(t, `out = enum.any(immutable([true, 0, 1]), enum.value)`, true) // immutable-array
|
||||
testEnumModule(t, `out = enum.any([false], enum.value)`, false)
|
||||
testEnumModule(t, `out = enum.any([false, 0], enum.value)`, false)
|
||||
testEnumModule(t, `out = enum.any({}, enum.value)`, false)
|
||||
testEnumModule(t, `out = enum.any({a:1}, enum.value)`, true)
|
||||
testEnumModule(t, `out = enum.any({a:true, b:1}, enum.value)`, true)
|
||||
testEnumModule(t, `out = enum.any({a:true, b:0}, enum.value)`, true)
|
||||
testEnumModule(t, `out = enum.any({a:true, b:0, c:1}, enum.value)`, true)
|
||||
testEnumModule(t, `out = enum.any(immutable({a:true, b:0, c:1}), enum.value)`, true) // immutable-map
|
||||
testEnumModule(t, `out = enum.any({a:false}, enum.value)`, false)
|
||||
testEnumModule(t, `out = enum.any({a:false, b:0}, enum.value)`, false)
|
||||
testEnumModule(t, `out = enum.any(0, enum.value)`, objects.UndefinedValue) // non-enumerable: undefined
|
||||
testEnumModule(t, `out = enum.any("123", enum.value)`, objects.UndefinedValue) // non-enumerable: undefined
|
||||
|
||||
testEnumModule(t, `out = enum.chunk([], 1)`, ARR{})
|
||||
testEnumModule(t, `out = enum.chunk([1], 1)`, ARR{ARR{1}})
|
||||
testEnumModule(t, `out = enum.chunk([1,2,3], 1)`, ARR{ARR{1}, ARR{2}, ARR{3}})
|
||||
testEnumModule(t, `out = enum.chunk([1,2,3], 2)`, ARR{ARR{1, 2}, ARR{3}})
|
||||
testEnumModule(t, `out = enum.chunk([1,2,3], 3)`, ARR{ARR{1, 2, 3}})
|
||||
testEnumModule(t, `out = enum.chunk([1,2,3], 4)`, ARR{ARR{1, 2, 3}})
|
||||
testEnumModule(t, `out = enum.chunk([1,2,3,4], 3)`, ARR{ARR{1, 2, 3}, ARR{4}})
|
||||
testEnumModule(t, `out = enum.chunk([], 0)`, objects.UndefinedValue) // size=0: undefined
|
||||
testEnumModule(t, `out = enum.chunk([1], 0)`, objects.UndefinedValue) // size=0: undefined
|
||||
testEnumModule(t, `out = enum.chunk([1,2,3], 0)`, objects.UndefinedValue) // size=0: undefined
|
||||
testEnumModule(t, `out = enum.chunk({a:1,b:2,c:3}, 1)`, objects.UndefinedValue) // map: undefined
|
||||
testEnumModule(t, `out = enum.chunk(0, 1)`, objects.UndefinedValue) // non-enumerable: undefined
|
||||
testEnumModule(t, `out = enum.chunk("123", 1)`, objects.UndefinedValue) // non-enumerable: undefined
|
||||
|
||||
testEnumModule(t, `out = enum.at([], 0)`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.at([], 1)`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.at([], -1)`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.at(["one"], 0)`, "one")
|
||||
testEnumModule(t, `out = enum.at(["one"], 1)`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.at(["one"], -1)`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.at(["one","two","three"], 0)`, "one")
|
||||
testEnumModule(t, `out = enum.at(["one","two","three"], 1)`, "two")
|
||||
testEnumModule(t, `out = enum.at(["one","two","three"], 2)`, "three")
|
||||
testEnumModule(t, `out = enum.at(["one","two","three"], -1)`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.at(["one","two","three"], 3)`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.at(["one","two","three"], "1")`, objects.UndefinedValue) // non-int index: undefined
|
||||
testEnumModule(t, `out = enum.at({}, "a")`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.at({a:"one"}, "a")`, "one")
|
||||
testEnumModule(t, `out = enum.at({a:"one"}, "b")`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.at({a:"one",b:"two",c:"three"}, "a")`, "one")
|
||||
testEnumModule(t, `out = enum.at({a:"one",b:"two",c:"three"}, "b")`, "two")
|
||||
testEnumModule(t, `out = enum.at({a:"one",b:"two",c:"three"}, "c")`, "three")
|
||||
testEnumModule(t, `out = enum.at({a:"one",b:"two",c:"three"}, "d")`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.at({a:"one",b:"two",c:"three"}, 'a')`, objects.UndefinedValue) // non-string index: undefined
|
||||
testEnumModule(t, `out = enum.at(0, 1)`, objects.UndefinedValue) // non-enumerable: undefined
|
||||
testEnumModule(t, `out = enum.at("abc", 1)`, objects.UndefinedValue) // non-enumerable: undefined
|
||||
|
||||
testEnumModule(t, `out=0; enum.each([],func(k,v){out+=v})`, 0)
|
||||
testEnumModule(t, `out=0; enum.each([1,2,3],func(k,v){out+=v})`, 6)
|
||||
testEnumModule(t, `out=0; enum.each([1,2,3],func(k,v){out+=k})`, 3)
|
||||
testEnumModule(t, `out=0; enum.each({a:1,b:2,c:3},func(k,v){out+=v})`, 6)
|
||||
testEnumModule(t, `out=""; enum.each({a:1,b:2,c:3},func(k,v){out+=k}); out=len(out)`, 3)
|
||||
testEnumModule(t, `out=0; enum.each(5,func(k,v){out+=v})`, 0) // non-enumerable: no iteration
|
||||
testEnumModule(t, `out=0; enum.each("123",func(k,v){out+=v})`, 0) // non-enumerable: no iteration
|
||||
|
||||
testEnumModule(t, `out = enum.filter([], enum.value)`, ARR{})
|
||||
testEnumModule(t, `out = enum.filter([false,1,2], enum.value)`, ARR{1, 2})
|
||||
testEnumModule(t, `out = enum.filter([false,1,0,2], enum.value)`, ARR{1, 2})
|
||||
testEnumModule(t, `out = enum.filter({}, enum.value)`, objects.UndefinedValue) // non-array: undefined
|
||||
testEnumModule(t, `out = enum.filter(0, enum.value)`, objects.UndefinedValue) // non-array: undefined
|
||||
testEnumModule(t, `out = enum.filter("123", enum.value)`, objects.UndefinedValue) // non-array: undefined
|
||||
|
||||
testEnumModule(t, `out = enum.find([], enum.value)`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.find([0], enum.value)`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.find([1], enum.value)`, 1)
|
||||
testEnumModule(t, `out = enum.find([false,0,undefined,1], enum.value)`, 1)
|
||||
testEnumModule(t, `out = enum.find([1,2,3], enum.value)`, 1)
|
||||
testEnumModule(t, `out = enum.find({}, enum.value)`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.find({a:0}, enum.value)`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.find({a:1}, enum.value)`, 1)
|
||||
testEnumModule(t, `out = enum.find({a:false,b:0,c:undefined,d:1}, enum.value)`, 1)
|
||||
//testEnumModule(t, `out = enum.find({a:1,b:2,c:3}, enum.value)`, 1)
|
||||
testEnumModule(t, `out = enum.find(0, enum.value)`, objects.UndefinedValue) // non-enumerable: undefined
|
||||
testEnumModule(t, `out = enum.find("123", enum.value)`, objects.UndefinedValue) // non-enumerable: undefined
|
||||
|
||||
testEnumModule(t, `out = enum.find_key([], enum.value)`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.find_key([0], enum.value)`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.find_key([1], enum.value)`, 0)
|
||||
testEnumModule(t, `out = enum.find_key([false,0,undefined,1], enum.value)`, 3)
|
||||
testEnumModule(t, `out = enum.find_key([1,2,3], enum.value)`, 0)
|
||||
testEnumModule(t, `out = enum.find_key({}, enum.value)`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.find_key({a:0}, enum.value)`, objects.UndefinedValue)
|
||||
testEnumModule(t, `out = enum.find_key({a:1}, enum.value)`, "a")
|
||||
testEnumModule(t, `out = enum.find_key({a:false,b:0,c:undefined,d:1}, enum.value)`, "d")
|
||||
//testEnumModule(t, `out = enum.find_key({a:1,b:2,c:3}, enum.value)`, "a")
|
||||
testEnumModule(t, `out = enum.find_key(0, enum.value)`, objects.UndefinedValue) // non-enumerable: undefined
|
||||
testEnumModule(t, `out = enum.find_key("123", enum.value)`, objects.UndefinedValue) // non-enumerable: undefined
|
||||
|
||||
testEnumModule(t, `out = enum.map([], enum.value)`, ARR{})
|
||||
testEnumModule(t, `out = enum.map([1,2,3], enum.value)`, ARR{1, 2, 3})
|
||||
testEnumModule(t, `out = enum.map([1,2,3], enum.key)`, ARR{0, 1, 2})
|
||||
testEnumModule(t, `out = enum.map([1,2,3], func(k,v) { return v*2 })`, ARR{2, 4, 6})
|
||||
testEnumModule(t, `out = enum.map({}, enum.value)`, ARR{})
|
||||
testEnumModule(t, `out = enum.map({a:1}, func(k,v) { return v*2 })`, ARR{2})
|
||||
testEnumModule(t, `out = enum.map(0, enum.value)`, objects.UndefinedValue) // non-enumerable: undefined
|
||||
testEnumModule(t, `out = enum.map("123", enum.value)`, objects.UndefinedValue) // non-enumerable: undefined
|
||||
}
|
||||
|
||||
func testEnumModule(t *testing.T, input string, expected interface{}) {
|
||||
expect(t, `enum := import("enum"); `+input,
|
||||
Opts().Module("enum", stdlib.SourceModules["enum"]),
|
||||
expected)
|
||||
}
|
||||
|
|
49
runtime/vm_srcmod_test.go
Normal file
49
runtime/vm_srcmod_test.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package runtime_test
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSrcModEnum(t *testing.T) {
|
||||
expect(t, `
|
||||
x := import("enum")
|
||||
out = x.all([1, 2, 3], func(_, v) { return v >= 1 })
|
||||
`, Opts().Stdlib(), true)
|
||||
expect(t, `
|
||||
x := import("enum")
|
||||
out = x.all([1, 2, 3], func(_, v) { return v >= 2 })
|
||||
`, Opts().Stdlib(), false)
|
||||
|
||||
expect(t, `
|
||||
x := import("enum")
|
||||
out = x.any([1, 2, 3], func(_, v) { return v >= 1 })
|
||||
`, Opts().Stdlib(), true)
|
||||
expect(t, `
|
||||
x := import("enum")
|
||||
out = x.any([1, 2, 3], func(_, v) { return v >= 2 })
|
||||
`, Opts().Stdlib(), true)
|
||||
|
||||
expect(t, `
|
||||
x := import("enum")
|
||||
out = x.chunk([1, 2, 3], 1)
|
||||
`, Opts().Stdlib(), ARR{ARR{1}, ARR{2}, ARR{3}})
|
||||
expect(t, `
|
||||
x := import("enum")
|
||||
out = x.chunk([1, 2, 3], 2)
|
||||
`, Opts().Stdlib(), ARR{ARR{1, 2}, ARR{3}})
|
||||
expect(t, `
|
||||
x := import("enum")
|
||||
out = x.chunk([1, 2, 3], 3)
|
||||
`, Opts().Stdlib(), ARR{ARR{1, 2, 3}})
|
||||
expect(t, `
|
||||
x := import("enum")
|
||||
out = x.chunk([1, 2, 3], 4)
|
||||
`, Opts().Stdlib(), ARR{ARR{1, 2, 3}})
|
||||
expect(t, `
|
||||
x := import("enum")
|
||||
out = x.chunk([1, 2, 3, 4, 5, 6], 2)
|
||||
`, Opts().Stdlib(), ARR{ARR{1, 2}, ARR{3, 4}, ARR{5, 6}})
|
||||
|
||||
expect(t, `
|
||||
x := import("enum")
|
||||
out = x.at([1, 2, 3], 0)
|
||||
`, Opts().Stdlib(), 1)
|
||||
}
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/d5/tengo/compiler/source"
|
||||
"github.com/d5/tengo/objects"
|
||||
"github.com/d5/tengo/runtime"
|
||||
"github.com/d5/tengo/stdlib"
|
||||
)
|
||||
|
||||
const testOut = "out"
|
||||
|
@ -52,6 +53,11 @@ func (o *testopts) copy() *testopts {
|
|||
return c
|
||||
}
|
||||
|
||||
func (o *testopts) Stdlib() *testopts {
|
||||
o.modules.AddMap(stdlib.GetModuleMap(stdlib.AllModuleNames()...))
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *testopts) Module(name string, mod interface{}) *testopts {
|
||||
c := o.copy()
|
||||
switch mod := mod.(type) {
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package stdlib_test
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestEnum(t *testing.T) {
|
||||
|
||||
}
|
|
@ -7,6 +7,7 @@ import (
|
|||
"io/ioutil"
|
||||
"log"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var tengoModFileRE = regexp.MustCompile(`^srcmod_(\w+).tengo$`)
|
||||
|
@ -41,7 +42,7 @@ package stdlib
|
|||
// SourceModules are source type standard library modules.
|
||||
var SourceModules = map[string]string{` + "\n")
|
||||
for modName, modSrc := range modules {
|
||||
out.WriteString("\t\"" + modName + "\": `" + modSrc + "`,\n")
|
||||
out.WriteString("\t\"" + modName + "\": " + strconv.Quote(modSrc) + ",\n")
|
||||
}
|
||||
out.WriteString("}\n")
|
||||
|
||||
|
|
|
@ -4,45 +4,5 @@ package stdlib
|
|||
|
||||
// SourceModules are source type standard library modules.
|
||||
var SourceModules = map[string]string{
|
||||
"enum": `export {
|
||||
// all returns true if the given function fn evaluates to a truthy value on
|
||||
// all of the items in the enumerable.
|
||||
all: func(enumerable, fn) {
|
||||
for k, v in enumerable {
|
||||
if !fn(k, v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
// any returns true if the given function fn evaluates to a truthy value on
|
||||
// any of the items in the enumerable.
|
||||
any: func(enumerable, fn) {
|
||||
for k, v in enumerable {
|
||||
if fn(k, v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
// chunk returns an array of elements split into groups the length of size.
|
||||
// If the enumerable can't be split evenly, the final chunk will be the
|
||||
// remaining elements.
|
||||
chunk: func(enumerable, size) {
|
||||
numElements := len(enumerable)
|
||||
|
||||
if !numElements {
|
||||
return []
|
||||
}
|
||||
|
||||
res := []
|
||||
idx := 0
|
||||
for idx < numElements {
|
||||
res = append(res, enumerable[idx:idx+size])
|
||||
idx += size
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
`,
|
||||
"enum": "is_enumerable := func(x) {\n return is_array(x) || is_map(x) || is_immutable_array(x) || is_immutable_map(x)\n}\n\nis_array_like := func(x) {\n return is_array(x) || is_immutable_array(x)\n}\n\nexport {\n // all returns true if the given function `fn` evaluates to a truthy value on\n // all of the items in `x`. It returns undefined if `x` is not enumerable.\n all: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if !fn(k, v) { return false }\n }\n\n return true\n },\n // any returns true if the given function `fn` evaluates to a truthy value on\n // any of the items in `x`. It returns undefined if `x` is not enumerable.\n any: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if fn(k, v) { return true }\n }\n\n return false\n },\n // chunk returns an array of elements split into groups the length of size.\n // If `x` can't be split evenly, the final chunk will be the remaining elements.\n // It returns undefined if `x` is not array.\n chunk: func(x, size) {\n if !is_array_like(x) || !size { return undefined }\n\n numElements := len(x)\n if !numElements { return [] }\n\n res := []\n idx := 0\n for idx < numElements {\n res = append(res, x[idx:idx+size])\n idx += size\n }\n\n return res\n },\n // at returns an element at the given index (if `x` is array) or\n // key (if `x` is map). It returns undefined if `x` is not enumerable.\n at: func(x, key) {\n if !is_enumerable(x) { return undefined }\n\n if is_array_like(x) {\n if !is_int(key) { return undefined }\n } else {\n if !is_string(key) { return undefined }\n }\n\n return x[key]\n },\n // each iterates over elements of `x` and invokes `fn` for each element. `fn` is\n // invoked with two arguments: `key` and `value`. `key` is an int index\n // if `x` is array. `key` is a string key if `x` is map. It does not iterate\n // and returns undefined if `x` is not enumerable.\n each: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n fn(k, v)\n }\n },\n // filter iterates over elements of `x`, returning an array of all elements `fn`\n // returns truthy for. `fn` is invoked with two arguments: `key` and `value`.\n // `key` is an int index if `x` is array. `key` is a string key if `x` is map.\n // It returns undefined if `x` is not enumerable.\n filter: func(x, fn) {\n if !is_array_like(x) { return undefined }\n\n dst := []\n for k, v in x {\n if fn(k, v) { dst = append(dst, v) }\n }\n\n return dst\n },\n // find iterates over elements of `x`, returning value of the first element `fn`\n // returns truthy for. `fn` is invoked with two arguments: `key` and `value`.\n // `key` is an int index if `x` is array. `key` is a string key if `x` is map.\n // It returns undefined if `x` is not enumerable.\n find: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if fn(k, v) { return v }\n }\n },\n // find_key iterates over elements of `x`, returning key or index of the first\n // element `fn` returns truthy for. `fn` is invoked with two arguments: `key`\n // and `value`. `key` is an int index if `x` is array. `key` is a string key if\n // `x` is map. It returns undefined if `x` is not enumerable.\n find_key: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if fn(k, v) { return k }\n }\n },\n // map creates an array of values by running each element in `x` through `fn`.\n // `fn` is invoked with two arguments: `key` and `value`. `key` is an int index\n // if `x` is array. `key` is a string key if `x` is map. It returns undefined\n // if `x` is not enumerable.\n map: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n dst := []\n for k, v in x {\n dst = append(dst, fn(k, v))\n }\n\n return dst\n },\n // key returns the first argument.\n key: func(k, _) { return k },\n // value returns the second argument.\n value: func(_, v) { return v }\n}\n",
|
||||
}
|
||||
|
|
|
@ -1,40 +1,128 @@
|
|||
is_enumerable := func(x) {
|
||||
return is_array(x) || is_map(x) || is_immutable_array(x) || is_immutable_map(x)
|
||||
}
|
||||
|
||||
is_array_like := func(x) {
|
||||
return is_array(x) || is_immutable_array(x)
|
||||
}
|
||||
|
||||
export {
|
||||
// all returns true if the given function fn evaluates to a truthy value on
|
||||
// all of the items in the enumerable.
|
||||
all: func(enumerable, fn) {
|
||||
for k, v in enumerable {
|
||||
if !fn(k, v) {
|
||||
return false
|
||||
}
|
||||
// all returns true if the given function `fn` evaluates to a truthy value on
|
||||
// all of the items in `x`. It returns undefined if `x` is not enumerable.
|
||||
all: func(x, fn) {
|
||||
if !is_enumerable(x) { return undefined }
|
||||
|
||||
for k, v in x {
|
||||
if !fn(k, v) { return false }
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
// any returns true if the given function fn evaluates to a truthy value on
|
||||
// any of the items in the enumerable.
|
||||
any: func(enumerable, fn) {
|
||||
for k, v in enumerable {
|
||||
if fn(k, v) {
|
||||
return true
|
||||
}
|
||||
// any returns true if the given function `fn` evaluates to a truthy value on
|
||||
// any of the items in `x`. It returns undefined if `x` is not enumerable.
|
||||
any: func(x, fn) {
|
||||
if !is_enumerable(x) { return undefined }
|
||||
|
||||
for k, v in x {
|
||||
if fn(k, v) { return true }
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
// chunk returns an array of elements split into groups the length of size.
|
||||
// If the enumerable can't be split evenly, the final chunk will be the
|
||||
// remaining elements.
|
||||
chunk: func(enumerable, size) {
|
||||
numElements := len(enumerable)
|
||||
// If `x` can't be split evenly, the final chunk will be the remaining elements.
|
||||
// It returns undefined if `x` is not array.
|
||||
chunk: func(x, size) {
|
||||
if !is_array_like(x) || !size { return undefined }
|
||||
|
||||
if !numElements {
|
||||
return []
|
||||
}
|
||||
numElements := len(x)
|
||||
if !numElements { return [] }
|
||||
|
||||
res := []
|
||||
idx := 0
|
||||
for idx < numElements {
|
||||
res = append(res, enumerable[idx:idx+size])
|
||||
res = append(res, x[idx:idx+size])
|
||||
idx += size
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
},
|
||||
// at returns an element at the given index (if `x` is array) or
|
||||
// key (if `x` is map). It returns undefined if `x` is not enumerable.
|
||||
at: func(x, key) {
|
||||
if !is_enumerable(x) { return undefined }
|
||||
|
||||
if is_array_like(x) {
|
||||
if !is_int(key) { return undefined }
|
||||
} else {
|
||||
if !is_string(key) { return undefined }
|
||||
}
|
||||
|
||||
return x[key]
|
||||
},
|
||||
// each iterates over elements of `x` and invokes `fn` for each element. `fn` is
|
||||
// invoked with two arguments: `key` and `value`. `key` is an int index
|
||||
// if `x` is array. `key` is a string key if `x` is map. It does not iterate
|
||||
// and returns undefined if `x` is not enumerable.
|
||||
each: func(x, fn) {
|
||||
if !is_enumerable(x) { return undefined }
|
||||
|
||||
for k, v in x {
|
||||
fn(k, v)
|
||||
}
|
||||
},
|
||||
// filter iterates over elements of `x`, returning an array of all elements `fn`
|
||||
// returns truthy for. `fn` is invoked with two arguments: `key` and `value`.
|
||||
// `key` is an int index if `x` is array. `key` is a string key if `x` is map.
|
||||
// It returns undefined if `x` is not enumerable.
|
||||
filter: func(x, fn) {
|
||||
if !is_array_like(x) { return undefined }
|
||||
|
||||
dst := []
|
||||
for k, v in x {
|
||||
if fn(k, v) { dst = append(dst, v) }
|
||||
}
|
||||
|
||||
return dst
|
||||
},
|
||||
// find iterates over elements of `x`, returning value of the first element `fn`
|
||||
// returns truthy for. `fn` is invoked with two arguments: `key` and `value`.
|
||||
// `key` is an int index if `x` is array. `key` is a string key if `x` is map.
|
||||
// It returns undefined if `x` is not enumerable.
|
||||
find: func(x, fn) {
|
||||
if !is_enumerable(x) { return undefined }
|
||||
|
||||
for k, v in x {
|
||||
if fn(k, v) { return v }
|
||||
}
|
||||
},
|
||||
// find_key iterates over elements of `x`, returning key or index of the first
|
||||
// element `fn` returns truthy for. `fn` is invoked with two arguments: `key`
|
||||
// and `value`. `key` is an int index if `x` is array. `key` is a string key if
|
||||
// `x` is map. It returns undefined if `x` is not enumerable.
|
||||
find_key: func(x, fn) {
|
||||
if !is_enumerable(x) { return undefined }
|
||||
|
||||
for k, v in x {
|
||||
if fn(k, v) { return k }
|
||||
}
|
||||
},
|
||||
// map creates an array of values by running each element in `x` through `fn`.
|
||||
// `fn` is invoked with two arguments: `key` and `value`. `key` is an int index
|
||||
// if `x` is array. `key` is a string key if `x` is map. It returns undefined
|
||||
// if `x` is not enumerable.
|
||||
map: func(x, fn) {
|
||||
if !is_enumerable(x) { return undefined }
|
||||
|
||||
dst := []
|
||||
for k, v in x {
|
||||
dst = append(dst, fn(k, v))
|
||||
}
|
||||
|
||||
return dst
|
||||
},
|
||||
// key returns the first argument.
|
||||
key: func(k, _) { return k },
|
||||
// value returns the second argument.
|
||||
value: func(_, v) { return v }
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue