Started working on map functions

This commit is contained in:
Leon Mika 2024-04-18 20:53:25 +10:00
parent fe15730769
commit 4b91bff641
5 changed files with 79 additions and 14 deletions

View File

@ -52,16 +52,26 @@ func toUpperBuiltin(ctx context.Context, inStream stream, args invocationArgs) (
// Handle args
return mapFilterStream{
in: inStream,
mapFn: func(x object) (object, bool) {
mapFn: func(x object) (object, bool, error) {
s, ok := x.(strObject)
if !ok {
return nil, false
return nil, false, nil
}
return strObject(strings.ToUpper(string(s))), true
return strObject(strings.ToUpper(string(s))), true, nil
},
}, nil
}
func concatBuiltin(ctx context.Context, args invocationArgs) (object, error) {
var sb strings.Builder
for _, a := range args.args {
sb.WriteString(a.String())
}
return strObject(sb.String()), nil
}
func catBuiltin(ctx context.Context, args invocationArgs) (object, error) {
if err := args.expectArgn(1); err != nil {
return nil, err
@ -88,6 +98,30 @@ func callBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return inv.invoke(ctx, args.shift(1))
}
func mapBuiltin(ctx context.Context, inStream stream, args invocationArgs) (object, error) {
args, strm, err := args.streamableSource(inStream)
if err != nil {
return nil, err
}
if err := args.expectArgn(1); err != nil {
return nil, err
}
inv, ok := args.args[0].(invokable)
if !ok {
return nil, errors.New("expected invokable")
}
return mapFilterStream{
in: strm,
mapFn: func(x object) (object, bool, error) {
y, err := inv.invoke(ctx, args.fork(nil, []object{x}))
return y, true, err
},
}, nil
}
type fileLinesStream struct {
filename string
f *os.File

View File

@ -3,9 +3,7 @@ package cmdlang
import (
"context"
"errors"
"fmt"
"strconv"
"strings"
)
type evaluator struct {
@ -218,17 +216,15 @@ func (e evaluator) evalSub(ctx context.Context, ec *evalCtx, n *astPipeline) (ob
switch v := pipelineRes.(type) {
case stream:
// TODO: use proper lists here, not a string join
sb := strings.Builder{}
list := listObject{}
if err := forEach(v, func(o object, _ int) error {
// TODO: use o.String()
sb.WriteString(fmt.Sprint(o))
list = append(list, o)
return nil
}); err != nil {
return nil, err
}
return strObject(sb.String()), nil
return list, nil
}
return pipelineRes, nil
}

View File

@ -30,9 +30,13 @@ func New(opts ...InstOption) *Inst {
rootEC.addCmd("echo", invokableFunc(echoBuiltin))
rootEC.addCmd("set", invokableFunc(setBuiltin))
rootEC.addCmd("toUpper", invokableStreamFunc(toUpperBuiltin))
rootEC.addCmd("cat", invokableFunc(catBuiltin))
//rootEC.addCmd("cat", invokableFunc(catBuiltin))
rootEC.addCmd("call", invokableFunc(callBuiltin))
rootEC.addCmd("map", invokableStreamFunc(mapBuiltin))
rootEC.addCmd("cat", invokableFunc(concatBuiltin))
rootEC.addMacro("if", macroFunc(ifBuiltin))
rootEC.addMacro("foreach", macroFunc(foreachBuiltin))
rootEC.addMacro("proc", macroFunc(procBuiltin))

View File

@ -172,6 +172,25 @@ type invocationArgs struct {
kwargs map[string]*listObject
}
// streamableSource takes a stream. If the stream is set, the inStream and invocation arguments are consumed as is.
// If not, then the first argument is consumed and returned as a stream.
func (ia invocationArgs) streamableSource(inStream stream) (invocationArgs, stream, error) {
if inStream != nil {
return ia, inStream, nil
}
if len(ia.args) < 1 {
return ia, nil, errors.New("expected at least 1 argument")
}
switch v := ia.args[0].(type) {
case listObject:
return ia.shift(1), &listIterStream{list: v}, nil
}
return ia, nil, errors.New("expected arg 0 to be streamable")
}
func (ia invocationArgs) expectArgn(x int) error {
if len(ia.args) < x {
return errors.New("expected at least " + strconv.Itoa(x) + " args")
@ -190,6 +209,16 @@ func (ia invocationArgs) stringArg(i int) (string, error) {
return s.String(), nil
}
func (ia invocationArgs) fork(currentStr stream, args []object) invocationArgs {
return invocationArgs{
inst: ia.inst,
ec: ia.ec,
currentStream: currentStr,
args: args,
kwargs: make(map[string]*listObject),
}
}
func (ia invocationArgs) shift(i int) invocationArgs {
return invocationArgs{
inst: ia.inst,

View File

@ -116,7 +116,7 @@ func (s *listIterStream) close() error { return nil }
type mapFilterStream struct {
in stream
mapFn func(x object) (object, bool)
mapFn func(x object) (object, bool, error)
}
func (ms mapFilterStream) String() string {
@ -134,8 +134,10 @@ func (ms mapFilterStream) next() (object, error) {
return nil, err
}
t, ok := ms.mapFn(u)
if ok {
t, ok, err := ms.mapFn(u)
if err != nil {
return nil, err
} else if ok {
return t, nil
}
}