Added the seq builtin
This commit is contained in:
parent
7bb26465f2
commit
20ea8bac06
|
@ -361,6 +361,73 @@ func firstBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||||
return nil, errors.New("expected listable")
|
return nil, errors.New("expected listable")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type seqObject struct {
|
||||||
|
from int
|
||||||
|
to int
|
||||||
|
inclusive bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s seqObject) String() string {
|
||||||
|
return fmt.Sprintf("%d:%d", s.from, s.to)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s seqObject) Truthy() bool {
|
||||||
|
return s.from != s.to || s.inclusive
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s seqObject) Len() int {
|
||||||
|
var l int
|
||||||
|
if s.from > s.to {
|
||||||
|
l = s.from - s.to
|
||||||
|
} else {
|
||||||
|
l = s.to - s.from
|
||||||
|
}
|
||||||
|
if s.inclusive {
|
||||||
|
l += 1
|
||||||
|
}
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s seqObject) Index(i int) object {
|
||||||
|
l := s.Len()
|
||||||
|
if i < 0 || i > l {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if s.from > s.to {
|
||||||
|
return intObject(s.from - i)
|
||||||
|
}
|
||||||
|
return intObject(s.from + i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func seqBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||||
|
inclusive := false
|
||||||
|
if inc, ok := args.kwargs["inc"]; ok {
|
||||||
|
inclusive = (inc.Len() == 0) || inc.Truthy()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(args.args) {
|
||||||
|
case 1:
|
||||||
|
n, err := args.intArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return seqObject{from: 0, to: n, inclusive: inclusive}, nil
|
||||||
|
case 2:
|
||||||
|
f, err := args.intArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := args.intArg(1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return seqObject{from: f, to: t, inclusive: inclusive}, nil
|
||||||
|
default:
|
||||||
|
return nil, errors.New("expected either 1 or 2 arguments")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ifBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
func ifBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
||||||
if args.nargs() < 2 {
|
if args.nargs() < 2 {
|
||||||
return nil, errors.New("need at least 2 arguments")
|
return nil, errors.New("need at least 2 arguments")
|
||||||
|
|
|
@ -53,6 +53,7 @@ func New(opts ...InstOption) *Inst {
|
||||||
rootEC.addCmd("keys", invokableFunc(keysBuiltin))
|
rootEC.addCmd("keys", invokableFunc(keysBuiltin))
|
||||||
rootEC.addCmd("index", invokableFunc(indexBuiltin))
|
rootEC.addCmd("index", invokableFunc(indexBuiltin))
|
||||||
rootEC.addCmd("call", invokableFunc(callBuiltin))
|
rootEC.addCmd("call", invokableFunc(callBuiltin))
|
||||||
|
rootEC.addCmd("seq", invokableFunc(seqBuiltin))
|
||||||
|
|
||||||
rootEC.addCmd("map", invokableFunc(mapBuiltin))
|
rootEC.addCmd("map", invokableFunc(mapBuiltin))
|
||||||
rootEC.addCmd("filter", invokableFunc(filterBuiltin))
|
rootEC.addCmd("filter", invokableFunc(filterBuiltin))
|
||||||
|
|
13
ucl/objs.go
13
ucl/objs.go
|
@ -298,6 +298,19 @@ func (ia invocationArgs) stringArg(i int) (string, error) {
|
||||||
return s.String(), nil
|
return s.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ia invocationArgs) intArg(i int) (int, error) {
|
||||||
|
if len(ia.args) < i {
|
||||||
|
return 0, errors.New("expected at least " + strconv.Itoa(i) + " args")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := ia.args[i].(type) {
|
||||||
|
case intObject:
|
||||||
|
return int(v), nil
|
||||||
|
default:
|
||||||
|
return 0, errors.New("expected an int arg")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (ia invocationArgs) invokableArg(i int) (invokable, error) {
|
func (ia invocationArgs) invokableArg(i int) (invokable, error) {
|
||||||
if len(ia.args) < i {
|
if len(ia.args) < i {
|
||||||
return nil, errors.New("expected at least " + strconv.Itoa(i) + " args")
|
return nil, errors.New("expected at least " + strconv.Itoa(i) + " args")
|
||||||
|
|
|
@ -461,6 +461,69 @@ func TestBuiltins_Return(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuiltins_Seq(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
expr string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{desc: "empty seq", expr: `seq 0 0`, want: ``},
|
||||||
|
{desc: "simple seq 1", expr: `seq 5`, want: "0\n1\n2\n3\n4\n"},
|
||||||
|
{desc: "simple seq 2", expr: `seq 3`, want: "0\n1\n2\n"},
|
||||||
|
{desc: "simple seq 3", expr: `seq -5`, want: "0\n-1\n-2\n-3\n-4\n"},
|
||||||
|
{desc: "asc seq 1", expr: `seq 3 5`, want: "3\n4\n"},
|
||||||
|
{desc: "asc seq 2", expr: `seq 3 8`, want: "3\n4\n5\n6\n7\n"},
|
||||||
|
{desc: "desc seq 1", expr: `seq 8 0`, want: "8\n7\n6\n5\n4\n3\n2\n1\n"},
|
||||||
|
{desc: "desc seq 2", expr: `seq 5 2`, want: "5\n4\n3\n"},
|
||||||
|
{desc: "desc seq 3", expr: `seq 3 -3`, want: "3\n2\n1\n0\n-1\n-2\n"},
|
||||||
|
{desc: "inclusive seq 1", expr: `seq 5 -inc`, want: "0\n1\n2\n3\n4\n5\n"},
|
||||||
|
{desc: "inclusive seq 2", expr: `seq -3 -inc`, want: "0\n-1\n-2\n-3\n"},
|
||||||
|
{desc: "inclusive seq 3", expr: `seq 5 8 -inc`, want: "5\n6\n7\n8\n"},
|
||||||
|
{desc: "inclusive seq 4", expr: `seq 4 0 -inc`, want: "4\n3\n2\n1\n0\n"},
|
||||||
|
|
||||||
|
{desc: "len of empty seq", expr: `seq 0 0 | len`, want: "0\n"},
|
||||||
|
{desc: "len of simple seq 1", expr: `seq 5 | len`, want: "5\n"},
|
||||||
|
{desc: "len of simple seq 2", expr: `seq 3 | len`, want: "3\n"},
|
||||||
|
{desc: "len of simple seq 3", expr: `seq -5 | len`, want: "5\n"},
|
||||||
|
{desc: "len of asc seq 1", expr: `seq 3 5 | len`, want: "2\n"},
|
||||||
|
{desc: "len of asc seq 2", expr: `seq 3 8 | len`, want: "5\n"},
|
||||||
|
{desc: "len of desc seq 1", expr: `seq 8 0 | len`, want: "8\n"},
|
||||||
|
{desc: "len of desc seq 2", expr: `seq 5 2 | len`, want: "3\n"},
|
||||||
|
{desc: "len of desc seq 3", expr: `seq 3 -3 | len`, want: "6\n"},
|
||||||
|
{desc: "len of inclusive seq 1", expr: `seq 5 -inc | len`, want: "6\n"},
|
||||||
|
{desc: "len of inclusive seq 2", expr: `seq -3 -inc | len`, want: "4\n"},
|
||||||
|
{desc: "len of inclusive seq 3", expr: `seq 5 8 -inc | len`, want: "4\n"},
|
||||||
|
{desc: "len of inclusive seq 4", expr: `seq 4 0 -inc | len`, want: "5\n"},
|
||||||
|
|
||||||
|
{desc: "truthy of empty seq 1", expr: `if (seq 0 0) { echo "t" }`, want: "(nil)\n"},
|
||||||
|
{desc: "truthy of empty seq 2", expr: `if (seq 3 3) { echo "t" }`, want: "(nil)\n"},
|
||||||
|
{desc: "truthy of empty seq 3", expr: `if (seq -5 -5) { echo "t" }`, want: "(nil)\n"},
|
||||||
|
{desc: "truthy of empty seq 4", expr: `if (seq 0) { echo "t" }`, want: "(nil)\n"},
|
||||||
|
{desc: "truthy simple seq", expr: `if (seq 5) { echo "t" }`, want: "t\n(nil)\n"},
|
||||||
|
{desc: "truthy asc seq", expr: `if (seq 3 5) { echo "t" }`, want: "t\n(nil)\n"},
|
||||||
|
{desc: "truthy desc seq", expr: `if (seq 3 -6) { echo "t" }`, want: "t\n(nil)\n"},
|
||||||
|
{desc: "truthy inclusive 1", expr: `if (seq 0 -inc) { echo "t" }`, want: "t\n(nil)\n"},
|
||||||
|
{desc: "truthy inclusive 2", expr: `if (seq 0 0 -inc) { echo "t" }`, want: "t\n(nil)\n"},
|
||||||
|
{desc: "truthy inclusive 3", expr: `if (seq 3 3 -inc) { echo "t" }`, want: "t\n(nil)\n"},
|
||||||
|
|
||||||
|
{desc: "map seq", expr: `seq 4 | map { |x| cat "[" $x "]" }`, want: "[0]\n[1]\n[2]\n[3]\n"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
outW := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
|
inst := New(WithOut(outW), WithTestBuiltin())
|
||||||
|
err := EvalAndDisplay(ctx, inst, tt.expr)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tt.want, outW.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuiltins_Map(t *testing.T) {
|
func TestBuiltins_Map(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
|
Loading…
Reference in a new issue