This commit is contained in:
parent
722d446220
commit
c7d614c1f8
|
@ -29,6 +29,27 @@ error MSG
|
||||||
Raises an error with MSG as the given value. This will start unrolling the stack until a `try` block is
|
Raises an error with MSG as the given value. This will start unrolling the stack until a `try` block is
|
||||||
encountered, or until it reaches the top level stack, at which it will be displayed as a "runtime error".
|
encountered, or until it reaches the top level stack, at which it will be displayed as a "runtime error".
|
||||||
|
|
||||||
|
### filter
|
||||||
|
|
||||||
|
```
|
||||||
|
filter COL BLOCK
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns a copy of COL with elements where the predicate BLOCK returns a truthy value.
|
||||||
|
|
||||||
|
### first
|
||||||
|
|
||||||
|
```
|
||||||
|
first COL BLOCK
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns the first element of COL where the predicate BLOCK returns a truthy value. COL can either be a list or
|
||||||
|
an iterator. If no value satisfying BLOCK is found, `first` will return nil.
|
||||||
|
|
||||||
|
If COL is a list, `first` will consume from index 0 and will continue until either finding a value that
|
||||||
|
satisfies the predicate, or until the end of the list is reached. If COL is an iterator, `first` will continue
|
||||||
|
consuming values until a value satisfying the predicate is found, or until the iterator is exhausted.
|
||||||
|
|
||||||
### foreach
|
### foreach
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -765,7 +765,7 @@ func reduceBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
return nil, errors.New("expected listable")
|
return nil, errors.New("expected listable")
|
||||||
}
|
}
|
||||||
|
|
||||||
func firstBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
func headBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
if err := args.expectArgn(1); err != nil {
|
if err := args.expectArgn(1); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -785,6 +785,47 @@ func firstBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
return nil, errors.New("expected listable")
|
return nil, errors.New("expected listable")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func firstBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
|
if err := args.expectArgn(2); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
inv, err := args.invokableArg(1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t := args.args[0].(type) {
|
||||||
|
case Listable:
|
||||||
|
l := t.Len()
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
v := t.Index(i)
|
||||||
|
m, err := inv.invoke(ctx, args.fork([]Object{v}))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if isTruthy(m) {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
case Iterable:
|
||||||
|
for t.HasNext() {
|
||||||
|
v, err := t.Next(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m, err := inv.invoke(ctx, args.fork([]Object{v}))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if isTruthy(m) {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("expected listable")
|
||||||
|
}
|
||||||
|
|
||||||
type seqObject struct {
|
type seqObject struct {
|
||||||
from int
|
from int
|
||||||
to int
|
to int
|
||||||
|
|
|
@ -72,7 +72,8 @@ func New(opts ...InstOption) *Inst {
|
||||||
|
|
||||||
rootEC.addCmd("map", invokableFunc(mapBuiltin))
|
rootEC.addCmd("map", invokableFunc(mapBuiltin))
|
||||||
rootEC.addCmd("filter", invokableFunc(filterBuiltin))
|
rootEC.addCmd("filter", invokableFunc(filterBuiltin))
|
||||||
rootEC.addCmd("head", invokableFunc(firstBuiltin))
|
rootEC.addCmd("first", invokableFunc(firstBuiltin))
|
||||||
|
rootEC.addCmd("head", invokableFunc(headBuiltin))
|
||||||
rootEC.addCmd("reduce", invokableFunc(reduceBuiltin))
|
rootEC.addCmd("reduce", invokableFunc(reduceBuiltin))
|
||||||
|
|
||||||
rootEC.addCmd("eq", invokableFunc(eqBuiltin))
|
rootEC.addCmd("eq", invokableFunc(eqBuiltin))
|
||||||
|
|
|
@ -1235,6 +1235,36 @@ func TestBuiltins_Filter(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuiltins_First(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
expr string
|
||||||
|
want any
|
||||||
|
}{
|
||||||
|
{desc: "first list 1", expr: `first [1 2 3] { |x| eq $x 2 }`, want: 2},
|
||||||
|
{desc: "first list 2", expr: `first ["flim" "flam" "fla"] { |x| eq $x "flam" }`, want: "flam"},
|
||||||
|
{desc: "first list 3", expr: `first ["flim" "flam" "fla"] { |x| eq $x "bogie" }`, want: nil},
|
||||||
|
{desc: "first list 4", expr: `first [() () ()] { |x| $x }`, want: nil},
|
||||||
|
{desc: "first list 5", expr: `first [] { |x| $x }`, want: nil},
|
||||||
|
|
||||||
|
{desc: "first itr 1", expr: `itr | first { |x| ne $x 2 }`, want: 1},
|
||||||
|
{desc: "first itr 2", expr: `set t (itr) ; set x (first $t { |x| ne $x 2 }) ; set y (first $t { |x| ne $x 2 }) ; "$x $y"`, want: "1 3"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
outW := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
|
inst := New(WithOut(outW), WithTestBuiltin())
|
||||||
|
|
||||||
|
res, err := inst.Eval(ctx, tt.expr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tt.want, res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuiltins_Reduce(t *testing.T) {
|
func TestBuiltins_Reduce(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
|
Loading…
Reference in a new issue