diff --git a/ucl/ast.go b/ucl/ast.go index 4814288..ab2e2bc 100644 --- a/ucl/ast.go +++ b/ucl/ast.go @@ -105,8 +105,7 @@ type astDot struct { type astCmd struct { Pos lexer.Position Name astDot `parser:"@@"` - Assign *astDot `parser:"( EQ @@"` - InvokeArgs []astDot `parser:" | @@+ )?"` + InvokeArgs []astDot `parser:"@@*"` } type astPipeline struct { @@ -114,9 +113,15 @@ type astPipeline struct { Rest []*astCmd `parser:"( PIPE @@ )*"` } +type astAssignOrPipeline struct { + First *astCmd `parser:"@@"` + Assign *astPipeline `parser:"( EQ @@ "` + Pipeline []*astCmd `parser:"| ( PIPE @@ )+ )?"` +} + type astStatements struct { - First *astPipeline `parser:"@@"` - Rest []*astPipeline `parser:"( NL+ @@ )*"` // TODO: also add support for newlines + First *astAssignOrPipeline `parser:"@@"` + Rest []*astAssignOrPipeline `parser:"( NL+ @@ )*"` // TODO: also add support for newlines } type astScript struct { diff --git a/ucl/eval.go b/ucl/eval.go index ae71896..89172f8 100644 --- a/ucl/eval.go +++ b/ucl/eval.go @@ -32,7 +32,7 @@ func (e evaluator) evalStatement(ctx context.Context, ec *evalCtx, n *astStateme return nil, nil } - res, err := e.evalPipeline(ctx, ec, n.First) + res, err := e.evalAssignOrPipeline(ctx, ec, n.First) if err != nil { return nil, err } @@ -41,7 +41,7 @@ func (e evaluator) evalStatement(ctx context.Context, ec *evalCtx, n *astStateme } for _, rest := range n.Rest { - out, err := e.evalPipeline(ctx, ec, rest) + out, err := e.evalAssignOrPipeline(ctx, ec, rest) if err != nil { return nil, err } @@ -50,6 +50,36 @@ func (e evaluator) evalStatement(ctx context.Context, ec *evalCtx, n *astStateme return res, nil } +func (e evaluator) evalAssignOrPipeline(ctx context.Context, ec *evalCtx, n *astAssignOrPipeline) (Object, error) { + switch { + case n.Assign != nil: + // Assignment + assignVal, err := e.evalPipeline(ctx, ec, n.Assign) + if err != nil { + return nil, err + } + + return e.assignCmd(ctx, ec, n.First, assignVal) + case len(n.Pipeline) > 0: + res, err := e.evalCmd(ctx, ec, nil, n.First) + if err != nil { + return nil, err + } + + for _, rest := range n.Pipeline { + out, err := e.evalCmd(ctx, ec, res, rest) + if err != nil { + return nil, err + } + res = out + } + return res, nil + + } + + return e.evalCmd(ctx, ec, nil, n.First) +} + func (e evaluator) evalPipeline(ctx context.Context, ec *evalCtx, n *astPipeline) (Object, error) { res, err := e.evalCmd(ctx, ec, nil, n.First) if err != nil { @@ -72,14 +102,6 @@ func (e evaluator) evalPipeline(ctx context.Context, ec *evalCtx, n *astPipeline func (e evaluator) evalCmd(ctx context.Context, ec *evalCtx, currentPipe Object, ast *astCmd) (Object, error) { switch { - case ast.Assign != nil: - // Assignment - assignVal, err := e.evalDot(ctx, ec, *ast.Assign) - if err != nil { - return nil, err - } - - return e.assignDot(ctx, ec, ast.Name, assignVal) case (ast.Name.Arg.Ident != nil) && len(ast.Name.DotSuffix) == 0: name := ast.Name.Arg.Ident.String() @@ -114,6 +136,13 @@ func (e evaluator) evalCmd(ctx context.Context, ec *evalCtx, currentPipe Object, return nameElem, nil } +func (e evaluator) assignCmd(ctx context.Context, ec *evalCtx, ast *astCmd, toVal Object) (Object, error) { + if len(ast.InvokeArgs) != 0 { + return nil, errors.New("cannot assign to multiple values") + } + return e.assignDot(ctx, ec, ast.Name, toVal) +} + func (e evaluator) evalInvokable(ctx context.Context, ec *evalCtx, currentPipe Object, ast *astCmd, cmd invokable) (Object, error) { var ( pargs ListObject