Allowed foreach to pull from the pipe
This commit is contained in:
parent
63762e633c
commit
24a484ab77
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
type astLiteral struct {
|
type astLiteral struct {
|
||||||
Str *string `parser:"@String"`
|
Str *string `parser:"@String"`
|
||||||
|
Int *int `parser:"| @Int"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type astHashKey struct {
|
type astHashKey struct {
|
||||||
|
@ -70,6 +71,7 @@ var scanner = lexer.MustStateful(lexer.Rules{
|
||||||
"Root": {
|
"Root": {
|
||||||
{"Whitespace", `[ \t]+`, nil},
|
{"Whitespace", `[ \t]+`, nil},
|
||||||
{"String", `"(\\"|[^"])*"`, nil},
|
{"String", `"(\\"|[^"])*"`, nil},
|
||||||
|
{"Int", `[-]?[0-9][0-9]*`, nil},
|
||||||
{"DOLLAR", `\$`, nil},
|
{"DOLLAR", `\$`, nil},
|
||||||
{"COLON", `\:`, nil},
|
{"COLON", `\:`, nil},
|
||||||
{"LP", `\(`, nil},
|
{"LP", `\(`, nil},
|
||||||
|
@ -80,7 +82,7 @@ var scanner = lexer.MustStateful(lexer.Rules{
|
||||||
{"RC", `\}`, nil},
|
{"RC", `\}`, nil},
|
||||||
{"NL", `[;\n][; \n\t]*`, nil},
|
{"NL", `[;\n][; \n\t]*`, nil},
|
||||||
{"PIPE", `\|`, nil},
|
{"PIPE", `\|`, nil},
|
||||||
{"Ident", `[\w-]+`, nil},
|
{"Ident", `[-]*[a-zA-Z_][\w-]*`, nil},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
var parser = participle.MustBuild[astScript](participle.Lexer(scanner),
|
var parser = participle.MustBuild[astScript](participle.Lexer(scanner),
|
||||||
|
|
|
@ -238,13 +238,27 @@ func ifBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func foreachBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
func foreachBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
||||||
if args.nargs() < 2 {
|
var (
|
||||||
return nil, errors.New("need at least 2 arguments")
|
items object
|
||||||
}
|
blockIdx int
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if !args.hasPipe {
|
||||||
|
if args.nargs() < 2 {
|
||||||
|
return nil, errors.New("need at least 2 arguments")
|
||||||
|
}
|
||||||
|
|
||||||
items, err := args.evalArg(ctx, 0)
|
items, err = args.evalArg(ctx, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
}
|
||||||
|
blockIdx = 1
|
||||||
|
} else {
|
||||||
|
if args.nargs() < 1 {
|
||||||
|
return nil, errors.New("need at least 1 argument")
|
||||||
|
}
|
||||||
|
items = args.pipeArg
|
||||||
|
blockIdx = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
var last object
|
var last object
|
||||||
|
@ -254,14 +268,14 @@ func foreachBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
||||||
l := t.Len()
|
l := t.Len()
|
||||||
for i := 0; i < l; i++ {
|
for i := 0; i < l; i++ {
|
||||||
v := t.Index(i)
|
v := t.Index(i)
|
||||||
last, err = args.evalBlock(ctx, 1, []object{v}, true) // TO INCLUDE: the index
|
last, err = args.evalBlock(ctx, blockIdx, []object{v}, true) // TO INCLUDE: the index
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case hashObject:
|
case hashObject:
|
||||||
for k, v := range t {
|
for k, v := range t {
|
||||||
last, err = args.evalBlock(ctx, 1, []object{strObject(k), v}, true)
|
last, err = args.evalBlock(ctx, blockIdx, []object{strObject(k), v}, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ func (e evaluator) evalCmd(ctx context.Context, ec *evalCtx, currentPipe object,
|
||||||
if cmd := ec.lookupInvokable(name); cmd != nil {
|
if cmd := ec.lookupInvokable(name); cmd != nil {
|
||||||
return e.evalInvokable(ctx, ec, currentPipe, ast, cmd)
|
return e.evalInvokable(ctx, ec, currentPipe, ast, cmd)
|
||||||
} else if macro := ec.lookupMacro(name); macro != nil {
|
} else if macro := ec.lookupMacro(name); macro != nil {
|
||||||
return e.evalMacro(ctx, ec, currentPipe, ast, macro)
|
return e.evalMacro(ctx, ec, currentPipe != nil, currentPipe, ast, macro)
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("unknown command: " + name)
|
return nil, errors.New("unknown command: " + name)
|
||||||
}
|
}
|
||||||
|
@ -132,10 +132,11 @@ func (e evaluator) evalInvokable(ctx context.Context, ec *evalCtx, currentPipe o
|
||||||
return cmd.invoke(ctx, invArgs)
|
return cmd.invoke(ctx, invArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e evaluator) evalMacro(ctx context.Context, ec *evalCtx, pipeArg object, ast *astCmd, cmd macroable) (object, error) {
|
func (e evaluator) evalMacro(ctx context.Context, ec *evalCtx, hasPipe bool, pipeArg object, ast *astCmd, cmd macroable) (object, error) {
|
||||||
return cmd.invokeMacro(ctx, macroArgs{
|
return cmd.invokeMacro(ctx, macroArgs{
|
||||||
eval: e,
|
eval: e,
|
||||||
ec: ec,
|
ec: ec,
|
||||||
|
hasPipe: hasPipe,
|
||||||
pipeArg: pipeArg,
|
pipeArg: pipeArg,
|
||||||
ast: ast,
|
ast: ast,
|
||||||
})
|
})
|
||||||
|
@ -217,6 +218,8 @@ func (e evaluator) evalLiteral(ctx context.Context, ec *evalCtx, n *astLiteral)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return strObject(uq), nil
|
return strObject(uq), nil
|
||||||
|
case n.Int != nil:
|
||||||
|
return intObject(*n.Int), nil
|
||||||
}
|
}
|
||||||
return nil, errors.New("unhandled literal type")
|
return nil, errors.New("unhandled literal type")
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ func TestInst_Eval(t *testing.T) {
|
||||||
want any
|
want any
|
||||||
}{
|
}{
|
||||||
{desc: "simple string", expr: `firstarg "hello"`, want: "hello"},
|
{desc: "simple string", expr: `firstarg "hello"`, want: "hello"},
|
||||||
|
{desc: "simple int 1", expr: `firstarg 123`, want: 123},
|
||||||
|
{desc: "simple int 2", expr: `firstarg -234`, want: -234},
|
||||||
{desc: "simple ident", expr: `firstarg a-test`, want: "a-test"},
|
{desc: "simple ident", expr: `firstarg a-test`, want: "a-test"},
|
||||||
|
|
||||||
// Sub-expressions
|
// Sub-expressions
|
||||||
|
|
|
@ -78,6 +78,16 @@ func (s strObject) Truthy() bool {
|
||||||
return string(s) != ""
|
return string(s) != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type intObject int
|
||||||
|
|
||||||
|
func (i intObject) String() string {
|
||||||
|
return strconv.Itoa(int(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i intObject) Truthy() bool {
|
||||||
|
return i != 0
|
||||||
|
}
|
||||||
|
|
||||||
type boolObject bool
|
type boolObject bool
|
||||||
|
|
||||||
func (b boolObject) String() string {
|
func (b boolObject) String() string {
|
||||||
|
@ -97,6 +107,8 @@ func toGoValue(obj object) (interface{}, bool) {
|
||||||
return nil, true
|
return nil, true
|
||||||
case strObject:
|
case strObject:
|
||||||
return string(v), true
|
return string(v), true
|
||||||
|
case intObject:
|
||||||
|
return int(v), true
|
||||||
case listObject:
|
case listObject:
|
||||||
xs := make([]interface{}, 0, len(v))
|
xs := make([]interface{}, 0, len(v))
|
||||||
for _, va := range v {
|
for _, va := range v {
|
||||||
|
@ -145,6 +157,7 @@ func fromGoValue(v any) (object, error) {
|
||||||
type macroArgs struct {
|
type macroArgs struct {
|
||||||
eval evaluator
|
eval evaluator
|
||||||
ec *evalCtx
|
ec *evalCtx
|
||||||
|
hasPipe bool
|
||||||
pipeArg object
|
pipeArg object
|
||||||
ast *astCmd
|
ast *astCmd
|
||||||
argShift int
|
argShift int
|
||||||
|
|
|
@ -143,6 +143,7 @@ func TestInst_SetBuiltin(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{descr: "return as is", expr: `countTo3`, want: []string{"1", "2", "3"}},
|
{descr: "return as is", expr: `countTo3`, want: []string{"1", "2", "3"}},
|
||||||
{descr: "iterate over", expr: `foreach (countTo3) { |x| echo $x }`, wantOut: "1\n2\n3\n"},
|
{descr: "iterate over", expr: `foreach (countTo3) { |x| echo $x }`, wantOut: "1\n2\n3\n"},
|
||||||
|
{descr: "iterate via foreach", expr: `["2" "4" "6"] | foreach { |x| echo $x }`, wantOut: "2\n4\n6\n"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
Loading…
Reference in a new issue