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
|
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 {
|
type fileLinesStream struct {
|
||||||
filename string
|
filename string
|
||||||
f *os.File
|
f *os.File
|
||||||
|
@ -187,3 +200,61 @@ func foreachBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
||||||
|
|
||||||
return last, nil
|
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
|
package cmdlang
|
||||||
|
|
||||||
type evalCtx struct {
|
type evalCtx struct {
|
||||||
|
root *evalCtx
|
||||||
parent *evalCtx
|
parent *evalCtx
|
||||||
commands map[string]invokable
|
commands map[string]invokable
|
||||||
macros map[string]macroable
|
macros map[string]macroable
|
||||||
|
@ -8,7 +9,7 @@ type evalCtx struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *evalCtx) fork() *evalCtx {
|
func (ec *evalCtx) fork() *evalCtx {
|
||||||
return &evalCtx{parent: ec}
|
return &evalCtx{parent: ec, root: ec.root}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *evalCtx) addCmd(name string, inv invokable) {
|
func (ec *evalCtx) addCmd(name string, inv invokable) {
|
||||||
|
|
|
@ -24,15 +24,18 @@ func WithOut(out io.Writer) InstOption {
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(opts ...InstOption) *Inst {
|
func New(opts ...InstOption) *Inst {
|
||||||
rootEC := evalCtx{}
|
rootEC := &evalCtx{}
|
||||||
|
rootEC.root = rootEC
|
||||||
|
|
||||||
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.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.addCmd("testTimebomb", invokableStreamFunc(errorTestBuiltin))
|
//rootEC.addCmd("testTimebomb", invokableStreamFunc(errorTestBuiltin))
|
||||||
|
|
||||||
|
@ -40,7 +43,7 @@ func New(opts ...InstOption) *Inst {
|
||||||
|
|
||||||
inst := &Inst{
|
inst := &Inst{
|
||||||
out: os.Stdout,
|
out: os.Stdout,
|
||||||
rootEC: &rootEC,
|
rootEC: rootEC,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
|
|
|
@ -119,6 +119,19 @@ func (ma macroArgs) identIs(ctx context.Context, n int, expectedIdent string) bo
|
||||||
return *lit == expectedIdent
|
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) {
|
func (ma macroArgs) evalArg(ctx context.Context, n int) (object, error) {
|
||||||
if n >= len(ma.ast.Args[ma.argShift:]) {
|
if n >= len(ma.ast.Args[ma.argShift:]) {
|
||||||
return nil, errors.New("not enough arguments") // FIX
|
return nil, errors.New("not enough arguments") // FIX
|
||||||
|
@ -177,6 +190,16 @@ func (ia invocationArgs) stringArg(i int) (string, error) {
|
||||||
return s.String(), nil
|
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
|
// 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)
|
||||||
|
|
Loading…
Reference in a new issue