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 {
Literal astLiteral `parser:"@@"`
Literal *astLiteral `parser:"@@"`
Sub *astPipeline `parser:"| '[' @@ ']'"`
}
type astCmd struct {

View File

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

View File

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

View File

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