ucl/cmdlang/inst.go

106 lines
1.9 KiB
Go
Raw Normal View History

2024-04-10 10:45:58 +00:00
package cmdlang
import (
"context"
2024-04-11 12:05:05 +00:00
"errors"
"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-10 10:45:58 +00:00
rootEC := evalCtx{}
2024-04-11 12:05:05 +00:00
2024-04-10 10:45:58 +00:00
rootEC.addCmd("echo", invokableFunc(echoBuiltin))
rootEC.addCmd("set", invokableFunc(setBuiltin))
2024-04-11 10:47:59 +00:00
rootEC.addCmd("toUpper", invokableStreamFunc(toUpperBuiltin))
rootEC.addCmd("cat", invokableFunc(catBuiltin))
2024-04-11 12:05:05 +00:00
//rootEC.addCmd("testTimebomb", invokableStreamFunc(errorTestBuiltin))
2024-04-10 10:45:58 +00:00
rootEC.setVar("hello", strObject("world"))
2024-04-11 12:05:05 +00:00
inst := &Inst{
out: os.Stdout,
2024-04-10 10:45:58 +00:00
rootEC: &rootEC,
}
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
}
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 {
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
return eval.evalStatement(ctx, inst.rootEC, ast)
2024-04-10 10:45:58 +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)
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
}
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
}
}
return nil
}