From 1cf10478e29b71470d5e1882245043ab0cfdfb11 Mon Sep 17 00:00:00 2001 From: Leon Mika Date: Thu, 18 Apr 2024 21:11:17 +1000 Subject: [PATCH] Added the ability to use single values as the first argument --- cmdlang/ast.go | 8 ++++++-- cmdlang/eval.go | 42 +++++++++++++++++++++++++++++++++++------- cmdlang/streams.go | 6 +++++- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/cmdlang/ast.go b/cmdlang/ast.go index ed79476..c3ff3e1 100644 --- a/cmdlang/ast.go +++ b/cmdlang/ast.go @@ -34,17 +34,21 @@ type astBlock struct { Statements []*astStatements `parser:"@@ NL? RC"` } +type astMaybeSub struct { + Sub *astPipeline `parser:"@@?"` +} + type astCmdArg struct { Literal *astLiteral `parser:"@@"` Ident *string `parser:"| @Ident"` Var *string `parser:"| DOLLAR @Ident"` - Sub *astPipeline `parser:"| LP @@ RP"` + MaybeSub *astMaybeSub `parser:"| LP @@ RP"` ListOrHash *astListOrHash `parser:"| @@"` Block *astBlock `parser:"| @@"` } type astCmd struct { - Name string `parser:"@Ident"` + Name astCmdArg `parser:"@@"` Args []astCmdArg `parser:"@@*"` } diff --git a/cmdlang/eval.go b/cmdlang/eval.go index 89437f3..df795d1 100644 --- a/cmdlang/eval.go +++ b/cmdlang/eval.go @@ -73,13 +73,37 @@ func (e evaluator) evalPipeline(ctx context.Context, ec *evalCtx, n *astPipeline } func (e evaluator) evalCmd(ctx context.Context, ec *evalCtx, currentStream stream, ast *astCmd) (object, error) { - if cmd := ec.lookupInvokable(ast.Name); cmd != nil { - return e.evalInvokable(ctx, ec, currentStream, ast, cmd) - } else if macro := ec.lookupMacro(ast.Name); macro != nil { - return e.evalMacro(ctx, ec, currentStream, ast, macro) + switch { + case ast.Name.Ident != nil: + name := *ast.Name.Ident + + // Regular command + if cmd := ec.lookupInvokable(name); cmd != nil { + return e.evalInvokable(ctx, ec, currentStream, ast, cmd) + } else if macro := ec.lookupMacro(name); macro != nil { + return e.evalMacro(ctx, ec, currentStream, ast, macro) + } else { + return nil, errors.New("unknown command") + } + case len(ast.Args) > 0: + nameElem, err := e.evalArg(ctx, ec, ast.Name) + if err != nil { + return nil, err + } + + inv, ok := nameElem.(invokable) + if !ok { + return nil, errors.New("command is not invokable") + } + + return e.evalInvokable(ctx, ec, currentStream, ast, inv) } - return nil, errors.New("unknown command") + nameElem, err := e.evalArg(ctx, ec, ast.Name) + if err != nil { + return nil, err + } + return nameElem, nil } func (e evaluator) evalInvokable(ctx context.Context, ec *evalCtx, currentStream stream, ast *astCmd, cmd invokable) (object, error) { @@ -143,8 +167,12 @@ func (e evaluator) evalArg(ctx context.Context, ec *evalCtx, n astCmdArg) (objec return v, nil } return nil, nil - case n.Sub != nil: - return e.evalSub(ctx, ec, n.Sub) + case n.MaybeSub != nil: + sub := n.MaybeSub.Sub + if sub == nil { + return nil, nil + } + return e.evalSub(ctx, ec, sub) case n.ListOrHash != nil: return e.evalListOrHash(ctx, ec, n.ListOrHash) case n.Block != nil: diff --git a/cmdlang/streams.go b/cmdlang/streams.go index 69cfcbe..2ffb021 100644 --- a/cmdlang/streams.go +++ b/cmdlang/streams.go @@ -47,9 +47,13 @@ func forEach(s stream, f func(object, int) error) (err error) { // asStream converts an object to a stream. If t is already a stream, it's returned as is. // Otherwise, a singleton stream is returned. func asStream(v object) stream { - if s, ok := v.(stream); ok { + switch s := v.(type) { + case stream: return s + case listObject: + return &listIterStream{list: s} } + return &singletonStream{t: v} }