Allowed foreach to pull from the pipe

This commit is contained in:
Leon Mika 2024-04-24 20:12:39 +10:00
parent 63762e633c
commit 24a484ab77
6 changed files with 46 additions and 11 deletions

View file

@ -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),

View file

@ -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
} }

View file

@ -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")
} }

View file

@ -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

View file

@ -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

View file

@ -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 {