First cut of procs
These capture the context, making them useful as lambdas
This commit is contained in:
parent
5265efedc4
commit
fe15730769
|
@ -75,6 +75,19 @@ func catBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return &fileLinesStream{filename: filename}, nil
|
||||
}
|
||||
|
||||
func callBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
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 inv.invoke(ctx, args.shift(1))
|
||||
}
|
||||
|
||||
type fileLinesStream struct {
|
||||
filename string
|
||||
f *os.File
|
||||
|
@ -187,3 +200,61 @@ func foreachBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
|||
|
||||
return last, nil
|
||||
}
|
||||
|
||||
func procBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
||||
if args.nargs() < 1 {
|
||||
return nil, errors.New("need at least one arguments")
|
||||
}
|
||||
|
||||
var procName string
|
||||
if args.nargs() == 2 {
|
||||
name, ok := args.shiftIdent(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New("malformed procedure: expected identifier as first argument")
|
||||
}
|
||||
procName = name
|
||||
}
|
||||
|
||||
block, err := args.evalArg(ctx, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockObj, ok := block.(blockObject)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("malformed procedure: expected block object, was %v", block.String())
|
||||
}
|
||||
|
||||
obj := procObject{args.eval, args.ec, blockObj.block}
|
||||
if procName != "" {
|
||||
args.ec.addCmd(procName, obj)
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
type procObject struct {
|
||||
eval evaluator
|
||||
ec *evalCtx
|
||||
block *astBlock
|
||||
}
|
||||
|
||||
func (b procObject) String() string {
|
||||
return "(proc)"
|
||||
}
|
||||
|
||||
func (b procObject) Truthy() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (b procObject) invoke(ctx context.Context, args invocationArgs) (object, error) {
|
||||
newEc := b.ec.fork()
|
||||
|
||||
for i, name := range b.block.Names {
|
||||
if i < len(args.args) {
|
||||
newEc.setVar(name, args.args[i])
|
||||
} else {
|
||||
newEc.setVar(name, nil)
|
||||
}
|
||||
}
|
||||
|
||||
return b.eval.evalBlock(ctx, newEc, b.block)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cmdlang
|
||||
|
||||
type evalCtx struct {
|
||||
root *evalCtx
|
||||
parent *evalCtx
|
||||
commands map[string]invokable
|
||||
macros map[string]macroable
|
||||
|
@ -8,7 +9,7 @@ type evalCtx struct {
|
|||
}
|
||||
|
||||
func (ec *evalCtx) fork() *evalCtx {
|
||||
return &evalCtx{parent: ec}
|
||||
return &evalCtx{parent: ec, root: ec.root}
|
||||
}
|
||||
|
||||
func (ec *evalCtx) addCmd(name string, inv invokable) {
|
||||
|
|
|
@ -24,15 +24,18 @@ func WithOut(out io.Writer) InstOption {
|
|||
}
|
||||
|
||||
func New(opts ...InstOption) *Inst {
|
||||
rootEC := evalCtx{}
|
||||
rootEC := &evalCtx{}
|
||||
rootEC.root = rootEC
|
||||
|
||||
rootEC.addCmd("echo", invokableFunc(echoBuiltin))
|
||||
rootEC.addCmd("set", invokableFunc(setBuiltin))
|
||||
rootEC.addCmd("toUpper", invokableStreamFunc(toUpperBuiltin))
|
||||
rootEC.addCmd("cat", invokableFunc(catBuiltin))
|
||||
rootEC.addCmd("call", invokableFunc(callBuiltin))
|
||||
|
||||
rootEC.addMacro("if", macroFunc(ifBuiltin))
|
||||
rootEC.addMacro("foreach", macroFunc(foreachBuiltin))
|
||||
rootEC.addMacro("proc", macroFunc(procBuiltin))
|
||||
|
||||
//rootEC.addCmd("testTimebomb", invokableStreamFunc(errorTestBuiltin))
|
||||
|
||||
|
@ -40,7 +43,7 @@ func New(opts ...InstOption) *Inst {
|
|||
|
||||
inst := &Inst{
|
||||
out: os.Stdout,
|
||||
rootEC: &rootEC,
|
||||
rootEC: rootEC,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
|
|
|
@ -119,6 +119,19 @@ func (ma macroArgs) identIs(ctx context.Context, n int, expectedIdent string) bo
|
|||
return *lit == expectedIdent
|
||||
}
|
||||
|
||||
func (ma *macroArgs) shiftIdent(ctx context.Context) (string, bool) {
|
||||
if ma.argShift >= len(ma.ast.Args) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
lit := ma.ast.Args[ma.argShift].Ident
|
||||
if lit != nil {
|
||||
ma.argShift += 1
|
||||
return *lit, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (ma macroArgs) evalArg(ctx context.Context, n int) (object, error) {
|
||||
if n >= len(ma.ast.Args[ma.argShift:]) {
|
||||
return nil, errors.New("not enough arguments") // FIX
|
||||
|
@ -177,6 +190,16 @@ func (ia invocationArgs) stringArg(i int) (string, error) {
|
|||
return s.String(), nil
|
||||
}
|
||||
|
||||
func (ia invocationArgs) shift(i int) invocationArgs {
|
||||
return invocationArgs{
|
||||
inst: ia.inst,
|
||||
ec: ia.ec,
|
||||
currentStream: ia.currentStream,
|
||||
args: ia.args[i:],
|
||||
kwargs: ia.kwargs,
|
||||
}
|
||||
}
|
||||
|
||||
// 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