Started working on placeholders

This commit is contained in:
Leon Mika 2024-04-10 22:19:11 +10:00
parent 3b3bac4a7f
commit fd177280d2
4 changed files with 70 additions and 11 deletions

View file

@ -11,7 +11,8 @@ type astLiteral struct {
} }
type astCmdArg struct { type astCmdArg struct {
Literal astLiteral `parser:"@@"` Literal *astLiteral `parser:"@@"`
Sub *astPipeline `parser:"| '[' @@ ']'"`
} }
type astCmd struct { type astCmd struct {

View file

@ -4,6 +4,7 @@ import (
"bufio" "bufio"
"context" "context"
"errors" "errors"
"fmt"
"io" "io"
"os" "os"
"strings" "strings"
@ -16,7 +17,9 @@ func echoBuiltin(ctx context.Context, args invocationArgs) (object, error) {
var line strings.Builder var line strings.Builder
for _, arg := range args.args { for _, arg := range args.args {
line.WriteString(arg) if s, ok := arg.(fmt.Stringer); ok {
line.WriteString(s.String())
}
} }
return asStream(line.String()), nil return asStream(line.String()), nil
@ -41,7 +44,12 @@ func catBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return nil, err 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 { type fileLinesStream struct {

View file

@ -3,8 +3,10 @@ package cmdlang
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"github.com/lmika/gopkgs/fp/slices" "github.com/lmika/gopkgs/fp/slices"
"strconv" "strconv"
"strings"
) )
type evaluator struct { type evaluator struct {
@ -36,7 +38,7 @@ func (e evaluator) evalCmd(ctx context.Context, ec *evalCtx, ast *astCmd) (objec
return nil, err 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) return e.evalArg(ctx, ec, a)
}) })
if err != nil { 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) { 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) 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 { switch {
case n.Str != nil: case n.Str != nil:
uq, err := strconv.Unquote(*n.Str) uq, err := strconv.Unquote(*n.Str)
if err != nil { if err != nil {
return "", err return "", err
} }
return uq, nil return strObject(uq), nil
case n.Ident != nil: case n.Ident != nil:
return *n.Ident, nil return strObject(*n.Ident), nil
} }
return "", errors.New("unhandled literal type") 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
}

View file

@ -3,13 +3,21 @@ package cmdlang
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"strconv" "strconv"
) )
type object = any type object interface {
}
type strObject string
func (s strObject) String() string {
return string(s)
}
type invocationArgs struct { type invocationArgs struct {
args []string args []object
inStream stream inStream stream
} }
@ -20,6 +28,17 @@ func (ia invocationArgs) expectArgn(x int) error {
return nil 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 // invokable is an object that can be executed as a command
type invokable interface { type invokable interface {
invoke(ctx context.Context, args invocationArgs) (object, error) invoke(ctx context.Context, args invocationArgs) (object, error)