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 // Handle args
return mapFilterStream{ return mapFilterStream{
in: inStream, in: inStream,
mapFn: func(x object) (object, bool) { mapFn: func(x object) (object, bool, error) {
s, ok := x.(strObject) s, ok := x.(strObject)
if !ok { 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 }, 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) { func catBuiltin(ctx context.Context, args invocationArgs) (object, error) {
if err := args.expectArgn(1); err != nil { if err := args.expectArgn(1); err != nil {
return nil, err return nil, err
@ -88,6 +98,30 @@ func callBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return inv.invoke(ctx, args.shift(1)) 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 { type fileLinesStream struct {
filename string filename string
f *os.File f *os.File

View file

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

View file

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

View file

@ -172,6 +172,25 @@ type invocationArgs struct {
kwargs map[string]*listObject 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 { func (ia invocationArgs) expectArgn(x int) error {
if len(ia.args) < x { if len(ia.args) < x {
return errors.New("expected at least " + strconv.Itoa(x) + " args") 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 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 { func (ia invocationArgs) shift(i int) invocationArgs {
return invocationArgs{ return invocationArgs{
inst: ia.inst, inst: ia.inst,

View file

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