2024-04-10 10:45:58 +00:00
|
|
|
package cmdlang
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-04-11 12:05:05 +00:00
|
|
|
"errors"
|
2024-04-10 11:58:06 +00:00
|
|
|
"fmt"
|
2024-04-11 12:05:05 +00:00
|
|
|
"io"
|
|
|
|
"os"
|
2024-04-10 10:45:58 +00:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Inst struct {
|
2024-04-11 12:05:05 +00:00
|
|
|
out io.Writer
|
|
|
|
|
2024-04-10 10:45:58 +00:00
|
|
|
rootEC *evalCtx
|
|
|
|
}
|
|
|
|
|
2024-04-11 12:05:05 +00:00
|
|
|
type InstOption func(*Inst)
|
|
|
|
|
|
|
|
func WithOut(out io.Writer) InstOption {
|
|
|
|
return func(i *Inst) {
|
|
|
|
i.out = out
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func New(opts ...InstOption) *Inst {
|
2024-04-18 10:26:02 +00:00
|
|
|
rootEC := &evalCtx{}
|
|
|
|
rootEC.root = rootEC
|
2024-04-11 12:05:05 +00:00
|
|
|
|
2024-04-10 10:45:58 +00:00
|
|
|
rootEC.addCmd("echo", invokableFunc(echoBuiltin))
|
2024-04-11 10:58:59 +00:00
|
|
|
rootEC.addCmd("set", invokableFunc(setBuiltin))
|
2024-04-11 10:47:59 +00:00
|
|
|
rootEC.addCmd("toUpper", invokableStreamFunc(toUpperBuiltin))
|
2024-04-18 10:53:25 +00:00
|
|
|
//rootEC.addCmd("cat", invokableFunc(catBuiltin))
|
2024-04-18 10:26:02 +00:00
|
|
|
rootEC.addCmd("call", invokableFunc(callBuiltin))
|
2024-04-10 11:58:06 +00:00
|
|
|
|
2024-04-18 10:53:25 +00:00
|
|
|
rootEC.addCmd("map", invokableStreamFunc(mapBuiltin))
|
2024-04-18 12:24:19 +00:00
|
|
|
rootEC.addCmd("head", invokableStreamFunc(firstBuiltin))
|
2024-04-18 10:53:25 +00:00
|
|
|
|
2024-04-18 12:24:19 +00:00
|
|
|
rootEC.addCmd("eq", invokableFunc(eqBuiltin))
|
2024-04-18 10:53:25 +00:00
|
|
|
rootEC.addCmd("cat", invokableFunc(concatBuiltin))
|
|
|
|
|
2024-04-13 11:46:50 +00:00
|
|
|
rootEC.addMacro("if", macroFunc(ifBuiltin))
|
2024-04-16 12:28:12 +00:00
|
|
|
rootEC.addMacro("foreach", macroFunc(foreachBuiltin))
|
2024-04-18 10:26:02 +00:00
|
|
|
rootEC.addMacro("proc", macroFunc(procBuiltin))
|
2024-04-13 11:46:50 +00:00
|
|
|
|
2024-04-11 12:05:05 +00:00
|
|
|
//rootEC.addCmd("testTimebomb", invokableStreamFunc(errorTestBuiltin))
|
2024-04-10 10:45:58 +00:00
|
|
|
|
2024-04-11 10:58:59 +00:00
|
|
|
rootEC.setVar("hello", strObject("world"))
|
|
|
|
|
2024-04-11 12:05:05 +00:00
|
|
|
inst := &Inst{
|
|
|
|
out: os.Stdout,
|
2024-04-18 10:26:02 +00:00
|
|
|
rootEC: rootEC,
|
2024-04-10 10:45:58 +00:00
|
|
|
}
|
2024-04-11 12:05:05 +00:00
|
|
|
|
|
|
|
for _, opt := range opts {
|
|
|
|
opt(inst)
|
|
|
|
}
|
|
|
|
|
|
|
|
return inst
|
2024-04-10 10:45:58 +00:00
|
|
|
}
|
|
|
|
|
2024-04-12 23:25:16 +00:00
|
|
|
func (inst *Inst) Out() io.Writer {
|
|
|
|
if inst.out == nil {
|
|
|
|
return os.Stdout
|
|
|
|
}
|
|
|
|
return inst.out
|
|
|
|
}
|
|
|
|
|
2024-04-10 11:58:06 +00:00
|
|
|
func (inst *Inst) Eval(ctx context.Context, expr string) (any, error) {
|
2024-04-11 12:05:05 +00:00
|
|
|
res, err := inst.eval(ctx, expr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
goRes, ok := toGoValue(res)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("result not convertable to go")
|
|
|
|
}
|
|
|
|
|
|
|
|
return goRes, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (inst *Inst) eval(ctx context.Context, expr string) (object, error) {
|
2024-04-10 10:45:58 +00:00
|
|
|
ast, err := parse(strings.NewReader(expr))
|
|
|
|
if err != nil {
|
2024-04-10 11:58:06 +00:00
|
|
|
return nil, err
|
2024-04-10 10:45:58 +00:00
|
|
|
}
|
|
|
|
|
2024-04-12 23:25:16 +00:00
|
|
|
eval := evaluator{inst: inst}
|
2024-04-11 12:05:05 +00:00
|
|
|
|
2024-04-18 12:24:19 +00:00
|
|
|
// TODO: this should be a separate forkAndIsolate() session
|
2024-04-13 11:46:50 +00:00
|
|
|
return eval.evalScript(ctx, inst.rootEC, ast)
|
2024-04-10 10:45:58 +00:00
|
|
|
}
|
2024-04-10 11:58:06 +00:00
|
|
|
|
|
|
|
func (inst *Inst) EvalAndDisplay(ctx context.Context, expr string) error {
|
2024-04-11 12:05:05 +00:00
|
|
|
res, err := inst.eval(ctx, expr)
|
2024-04-10 11:58:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return inst.display(ctx, res)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (inst *Inst) display(ctx context.Context, res object) (err error) {
|
|
|
|
switch v := res.(type) {
|
2024-04-12 23:25:16 +00:00
|
|
|
case nil:
|
|
|
|
if _, err = fmt.Fprintln(inst.out, "(nil)"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-04-10 11:58:06 +00:00
|
|
|
case stream:
|
2024-04-11 12:05:05 +00:00
|
|
|
return forEach(v, func(o object, _ int) error { return inst.display(ctx, o) })
|
|
|
|
default:
|
|
|
|
if _, err = fmt.Fprintln(inst.out, v.String()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-04-10 11:58:06 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|