Started working on placeholders
This commit is contained in:
parent
3b3bac4a7f
commit
fd177280d2
|
@ -11,7 +11,8 @@ type astLiteral struct {
|
|||
}
|
||||
|
||||
type astCmdArg struct {
|
||||
Literal astLiteral `parser:"@@"`
|
||||
Literal *astLiteral `parser:"@@"`
|
||||
Sub *astPipeline `parser:"| '[' @@ ']'"`
|
||||
}
|
||||
|
||||
type astCmd struct {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
@ -16,7 +17,9 @@ func echoBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
|
||||
var line strings.Builder
|
||||
for _, arg := range args.args {
|
||||
line.WriteString(arg)
|
||||
if s, ok := arg.(fmt.Stringer); ok {
|
||||
line.WriteString(s.String())
|
||||
}
|
||||
}
|
||||
|
||||
return asStream(line.String()), nil
|
||||
|
@ -41,7 +44,12 @@ func catBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return &fileLinesStream{filename: args.args[0]}, nil
|
||||
filename, err := args.stringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &fileLinesStream{filename: filename}, nil
|
||||
}
|
||||
|
||||
type fileLinesStream struct {
|
||||
|
|
|
@ -3,8 +3,10 @@ package cmdlang
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/lmika/gopkgs/fp/slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type evaluator struct {
|
||||
|
@ -36,7 +38,7 @@ func (e evaluator) evalCmd(ctx context.Context, ec *evalCtx, ast *astCmd) (objec
|
|||
return nil, err
|
||||
}
|
||||
|
||||
args, err := slices.MapWithError(ast.Args, func(a astCmdArg) (string, error) {
|
||||
args, err := slices.MapWithError(ast.Args, func(a astCmdArg) (object, error) {
|
||||
return e.evalArg(ctx, ec, a)
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -49,20 +51,49 @@ func (e evaluator) evalCmd(ctx context.Context, ec *evalCtx, ast *astCmd) (objec
|
|||
})
|
||||
}
|
||||
|
||||
func (e evaluator) evalArg(ctx context.Context, ec *evalCtx, n astCmdArg) (string, error) {
|
||||
return e.evalLiteral(ctx, ec, n.Literal)
|
||||
func (e evaluator) evalArg(ctx context.Context, ec *evalCtx, n astCmdArg) (object, error) {
|
||||
switch {
|
||||
case n.Literal != nil:
|
||||
return e.evalLiteral(ctx, ec, n.Literal)
|
||||
case n.Sub != nil:
|
||||
return e.evalSub(ctx, ec, n.Sub)
|
||||
}
|
||||
return nil, errors.New("unhandled arg type")
|
||||
}
|
||||
|
||||
func (e evaluator) evalLiteral(ctx context.Context, ec *evalCtx, n astLiteral) (string, error) {
|
||||
func (e evaluator) evalLiteral(ctx context.Context, ec *evalCtx, n *astLiteral) (object, error) {
|
||||
switch {
|
||||
case n.Str != nil:
|
||||
uq, err := strconv.Unquote(*n.Str)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return uq, nil
|
||||
return strObject(uq), nil
|
||||
case n.Ident != nil:
|
||||
return *n.Ident, nil
|
||||
return strObject(*n.Ident), nil
|
||||
}
|
||||
return "", errors.New("unhandled literal type")
|
||||
}
|
||||
|
||||
func (e evaluator) evalSub(ctx context.Context, ec *evalCtx, n *astPipeline) (object, error) {
|
||||
pipelineRes, err := e.evaluate(ctx, ec, n)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch v := pipelineRes.(type) {
|
||||
case stream:
|
||||
// TODO: use proper lists here, not a string join
|
||||
sb := strings.Builder{}
|
||||
if err := forEach(v, func(o object) error {
|
||||
// TODO: use o.String()
|
||||
sb.WriteString(fmt.Sprint(o))
|
||||
return nil
|
||||
}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strObject(sb.String()), nil
|
||||
}
|
||||
return pipelineRes, nil
|
||||
}
|
||||
|
|
|
@ -3,13 +3,21 @@ package cmdlang
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type object = any
|
||||
type object interface {
|
||||
}
|
||||
|
||||
type strObject string
|
||||
|
||||
func (s strObject) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
type invocationArgs struct {
|
||||
args []string
|
||||
args []object
|
||||
inStream stream
|
||||
}
|
||||
|
||||
|
@ -20,6 +28,17 @@ func (ia invocationArgs) expectArgn(x int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ia invocationArgs) stringArg(i int) (string, error) {
|
||||
if len(ia.args) < i {
|
||||
return "", errors.New("expected at least " + strconv.Itoa(i) + " args")
|
||||
}
|
||||
s, ok := ia.args[i].(fmt.Stringer)
|
||||
if !ok {
|
||||
return "", errors.New("expected a string arg")
|
||||
}
|
||||
return s.String(), nil
|
||||
}
|
||||
|
||||
// invokable is an object that can be executed as a command
|
||||
type invokable interface {
|
||||
invoke(ctx context.Context, args invocationArgs) (object, error)
|
||||
|
|
Loading…
Reference in a new issue