Added custom printers
This commit is contained in:
parent
62724e3f37
commit
7a92eeddac
57
cmd/cmsh/fancy.go
Normal file
57
cmd/cmsh/fancy.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"ucl.lmika.dev/repl"
|
||||
"ucl.lmika.dev/ucl"
|
||||
)
|
||||
|
||||
type FancyType struct {
|
||||
Foo string
|
||||
Bar string
|
||||
}
|
||||
|
||||
var newFancyDoc = repl.Doc{
|
||||
Brief: "returns something fancy",
|
||||
Detailed: `
|
||||
This will create and return something fancy.
|
||||
`,
|
||||
}
|
||||
|
||||
func newFancy(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||
return FancyType{Foo: "is foo", Bar: "is bar"}, nil
|
||||
}
|
||||
|
||||
func manyFancies(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||
return []FancyType{
|
||||
{Foo: "foo 1", Bar: "bar 1"},
|
||||
{Foo: "foo 2", Bar: "bar 2"},
|
||||
{Foo: "foo 3", Bar: "bar 3"},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func displayFancy(w io.Writer, f FancyType, concise bool) error {
|
||||
if concise {
|
||||
_, err := fmt.Fprintf(w, "%s:%s", f.Foo, f.Bar)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "Foo.. %s\n", f.Foo)
|
||||
fmt.Fprintf(w, "Bar.. %s\n", f.Bar)
|
||||
return nil
|
||||
}
|
||||
|
||||
func displayFancies(w io.Writer, fs []FancyType, concise bool) error {
|
||||
if concise {
|
||||
_, err := fmt.Fprintf(w, "%d fancies", len(fs))
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintln(w, "FOO\tBAR")
|
||||
for _, f := range fs {
|
||||
fmt.Fprintf(w, "%v\t%v\n", f.Foo, f.Bar)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -33,6 +33,11 @@ func main() {
|
|||
It then terminates.
|
||||
`,
|
||||
})
|
||||
instRepl.SetCommand("new-fancy", newFancy, newFancyDoc)
|
||||
instRepl.SetCommand("many-fancies", manyFancies)
|
||||
|
||||
repl.AddTypePrinter(instRepl, displayFancy)
|
||||
repl.AddTypePrinter(instRepl, displayFancies)
|
||||
|
||||
for {
|
||||
line, err := rl.Readline()
|
||||
|
|
|
@ -3,7 +3,10 @@ package repl
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"ucl.lmika.dev/ucl"
|
||||
)
|
||||
|
||||
|
@ -15,31 +18,118 @@ func (r *REPL) EvalAndDisplay(ctx context.Context, expr string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
return displayResult(ctx, r.inst, res)
|
||||
return r.displayResult(ctx, os.Stdout, res, false)
|
||||
}
|
||||
|
||||
func displayResult(ctx context.Context, inst *ucl.Inst, res any) (err error) {
|
||||
func (r *REPL) echoPrinter(ctx context.Context, w io.Writer, args []any) (err error) {
|
||||
if len(args) == 0 {
|
||||
_, err := fmt.Fprintln(w)
|
||||
return err
|
||||
}
|
||||
|
||||
var line strings.Builder
|
||||
for _, arg := range args {
|
||||
if err := r.displayResult(ctx, &line, arg, len(args) > 1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
res := line.String()
|
||||
if strings.HasSuffix(res, "\n") {
|
||||
res = res[:len(res)-1]
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintln(w, res)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *REPL) displayResult(ctx context.Context, w io.Writer, res any, concise bool) (err error) {
|
||||
// Check type printers
|
||||
tp, ok := r.typePrinters[reflect.TypeOf(res)]
|
||||
if ok {
|
||||
return tp(w, res, concise)
|
||||
}
|
||||
|
||||
switch v := res.(type) {
|
||||
case NoResults:
|
||||
return nil
|
||||
case nil:
|
||||
if _, err = fmt.Fprintln(os.Stdout, "(nil)"); err != nil {
|
||||
return err
|
||||
}
|
||||
case ucl.Listable:
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if err = displayResult(ctx, inst, v.Index(i)); err != nil {
|
||||
return err
|
||||
if concise {
|
||||
fmt.Fprintf(w, "[")
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(w, " ")
|
||||
}
|
||||
if err = r.displayResult(ctx, w, v.Index(i), true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(w, "]")
|
||||
} else {
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if err = r.displayResult(ctx, w, v.Index(i), true); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
}
|
||||
case []interface{}:
|
||||
if concise {
|
||||
fmt.Fprintf(w, "[")
|
||||
for i := 0; i < len(v); i++ {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(w, " ")
|
||||
}
|
||||
if err = r.displayResult(ctx, w, v[i], true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(w, "]")
|
||||
} else {
|
||||
// In the off-chance that this is actually a slice of printables
|
||||
vt := reflect.SliceOf(reflect.TypeOf(v[0]))
|
||||
if tp, ok := r.typePrinters[vt]; ok {
|
||||
canDisplay := true
|
||||
|
||||
typeSlice := reflect.MakeSlice(vt, len(v), len(v))
|
||||
for i := 0; i < len(v); i++ {
|
||||
vv := reflect.ValueOf(v[i])
|
||||
if vv.CanConvert(vt.Elem()) {
|
||||
typeSlice.Index(i).Set(vv)
|
||||
} else {
|
||||
canDisplay = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if canDisplay {
|
||||
return tp(w, typeSlice.Interface(), concise)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(v); i++ {
|
||||
if err = r.displayResult(ctx, w, v[i], true); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
}
|
||||
case ucl.Object:
|
||||
if _, err = fmt.Fprintln(os.Stdout, v.String()); err != nil {
|
||||
if _, err = fmt.Fprint(w, v.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
if !concise {
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
default:
|
||||
if _, err = fmt.Fprintln(os.Stdout, v); err != nil {
|
||||
if _, err = fmt.Fprint(w, v); err != nil {
|
||||
return err
|
||||
}
|
||||
if !concise {
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
18
repl/repl.go
18
repl/repl.go
|
@ -1,6 +1,9 @@
|
|||
package repl
|
||||
|
||||
import (
|
||||
"io"
|
||||
"reflect"
|
||||
"slices"
|
||||
"ucl.lmika.dev/ucl"
|
||||
)
|
||||
|
||||
|
@ -11,16 +14,19 @@ type CommandOpt interface {
|
|||
type REPL struct {
|
||||
inst *ucl.Inst
|
||||
|
||||
commandDocs map[string]Doc
|
||||
commandDocs map[string]Doc
|
||||
typePrinters map[reflect.Type]func(w io.Writer, v any, brief bool) error
|
||||
}
|
||||
|
||||
func New(opts ...ucl.InstOption) *REPL {
|
||||
inst := ucl.New(opts...)
|
||||
|
||||
r := &REPL{
|
||||
inst: inst,
|
||||
commandDocs: make(map[string]Doc),
|
||||
commandDocs: make(map[string]Doc),
|
||||
typePrinters: make(map[reflect.Type]func(w io.Writer, v any, brief bool) error),
|
||||
}
|
||||
|
||||
instOpts := append(slices.Clone(opts), ucl.WithCustomEchoPrinter(r.echoPrinter))
|
||||
r.inst = ucl.New(instOpts...)
|
||||
|
||||
r.SetCommand("help", r.helpBuiltin, Doc{
|
||||
Brief: "displays help about a command",
|
||||
Usage: "[command]",
|
||||
|
@ -36,6 +42,8 @@ func New(opts ...ucl.InstOption) *REPL {
|
|||
`,
|
||||
})
|
||||
|
||||
AddTypePrinter(r, func(w io.Writer, t NoResults, concise bool) error { return nil })
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
|
|
20
repl/typeprinter.go
Normal file
20
repl/typeprinter.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package repl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func AddTypePrinter[T any](r *REPL, p func(w io.Writer, t T, concise bool) error) {
|
||||
var t T
|
||||
|
||||
tt := reflect.TypeOf(t)
|
||||
r.typePrinters[tt] = func(w io.Writer, v any, concise bool) error {
|
||||
vt, ok := v.(T)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot convert %v to T", v)
|
||||
}
|
||||
return p(w, vt, concise)
|
||||
}
|
||||
}
|
133
ucl/builtins.go
133
ucl/builtins.go
|
@ -4,10 +4,24 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func echoBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func echoBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
echoPrinter := args.inst.echoPrinter
|
||||
if echoPrinter != nil {
|
||||
convertedArgs := make([]interface{}, len(args.args))
|
||||
for i, arg := range args.args {
|
||||
if convArg, ok := toGoValue(arg); ok {
|
||||
convertedArgs[i] = convArg
|
||||
} else {
|
||||
convertedArgs[i] = arg
|
||||
}
|
||||
}
|
||||
return nil, echoPrinter(ctx, args.inst.out, convertedArgs)
|
||||
}
|
||||
|
||||
if len(args.args) == 0 {
|
||||
if _, err := fmt.Fprintln(args.inst.Out()); err != nil {
|
||||
return nil, err
|
||||
|
@ -28,7 +42,7 @@ func echoBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func addBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func addBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if len(args.args) == 0 {
|
||||
return intObject(0), nil
|
||||
}
|
||||
|
@ -52,7 +66,7 @@ func addBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return intObject(n), nil
|
||||
}
|
||||
|
||||
func subBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func subBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if len(args.args) == 0 {
|
||||
return intObject(0), nil
|
||||
}
|
||||
|
@ -82,7 +96,7 @@ func subBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return intObject(n), nil
|
||||
}
|
||||
|
||||
func mupBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func mupBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if len(args.args) == 0 {
|
||||
return intObject(1), nil
|
||||
}
|
||||
|
@ -106,7 +120,7 @@ func mupBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return intObject(n), nil
|
||||
}
|
||||
|
||||
func divBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func divBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if len(args.args) == 0 {
|
||||
return intObject(1), nil
|
||||
}
|
||||
|
@ -136,7 +150,7 @@ func divBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return intObject(n), nil
|
||||
}
|
||||
|
||||
func modBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func modBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if len(args.args) == 0 {
|
||||
return intObject(0), nil
|
||||
}
|
||||
|
@ -166,7 +180,7 @@ func modBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return intObject(n), nil
|
||||
}
|
||||
|
||||
func setBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func setBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -182,7 +196,7 @@ func setBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return newVal, nil
|
||||
}
|
||||
|
||||
func toUpperBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func toUpperBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -193,7 +207,7 @@ func toUpperBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return strObject(strings.ToUpper(sarg)), nil
|
||||
}
|
||||
|
||||
func eqBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func eqBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -204,7 +218,7 @@ func eqBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return boolObject(objectsEqual(l, r)), nil
|
||||
}
|
||||
|
||||
func neBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func neBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -215,7 +229,7 @@ func neBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return boolObject(!objectsEqual(l, r)), nil
|
||||
}
|
||||
|
||||
func ltBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func ltBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -227,7 +241,7 @@ func ltBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return boolObject(isLess), nil
|
||||
}
|
||||
|
||||
func leBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func leBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -239,7 +253,7 @@ func leBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return boolObject(isLess || objectsEqual(args.args[0], args.args[1])), nil
|
||||
}
|
||||
|
||||
func gtBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func gtBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -251,7 +265,7 @@ func gtBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return boolObject(isGreater), nil
|
||||
}
|
||||
|
||||
func geBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func geBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -263,7 +277,7 @@ func geBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return boolObject(isGreater || objectsEqual(args.args[0], args.args[1])), nil
|
||||
}
|
||||
|
||||
func andBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func andBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -276,7 +290,7 @@ func andBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return args.args[len(args.args)-1], nil
|
||||
}
|
||||
|
||||
func orBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func orBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -289,7 +303,7 @@ func orBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return boolObject(false), nil
|
||||
}
|
||||
|
||||
func notBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func notBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -299,7 +313,7 @@ func notBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
|
||||
var errObjectsNotEqual = errors.New("objects not equal")
|
||||
|
||||
func objectsEqual(l, r object) bool {
|
||||
func objectsEqual(l, r Object) bool {
|
||||
if l == nil || r == nil {
|
||||
return l == nil && r == nil
|
||||
}
|
||||
|
@ -317,8 +331,8 @@ func objectsEqual(l, r object) bool {
|
|||
if rv, ok := r.(boolObject); ok {
|
||||
return lv == rv
|
||||
}
|
||||
case listable:
|
||||
rv, ok := r.(listable)
|
||||
case Listable:
|
||||
rv, ok := r.(Listable)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
@ -341,7 +355,7 @@ func objectsEqual(l, r object) bool {
|
|||
if lv.Len() != rv.Len() {
|
||||
return false
|
||||
}
|
||||
if err := lv.Each(func(k string, lkv object) error {
|
||||
if err := lv.Each(func(k string, lkv Object) error {
|
||||
rkv := rv.Value(k)
|
||||
if rkv == nil {
|
||||
return errObjectsNotEqual
|
||||
|
@ -357,7 +371,7 @@ func objectsEqual(l, r object) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func objectsLessThan(l, r object) (bool, error) {
|
||||
func objectsLessThan(l, r Object) (bool, error) {
|
||||
switch lv := l.(type) {
|
||||
case strObject:
|
||||
if rv, ok := r.(strObject); ok {
|
||||
|
@ -371,7 +385,7 @@ func objectsLessThan(l, r object) (bool, error) {
|
|||
return false, errors.New("objects are not comparable")
|
||||
}
|
||||
|
||||
func strBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func strBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -383,7 +397,7 @@ func strBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return strObject(args.args[0].String()), nil
|
||||
}
|
||||
|
||||
func intBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func intBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -411,7 +425,7 @@ func intBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return nil, errors.New("cannot convert to int")
|
||||
}
|
||||
|
||||
func concatBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func concatBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
var sb strings.Builder
|
||||
|
||||
for _, a := range args.args {
|
||||
|
@ -424,7 +438,7 @@ func concatBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return strObject(sb.String()), nil
|
||||
}
|
||||
|
||||
func callBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func callBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -437,7 +451,7 @@ func callBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return inv.invoke(ctx, args.shift(1))
|
||||
}
|
||||
|
||||
func lenBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func lenBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -445,7 +459,7 @@ func lenBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
switch v := args.args[0].(type) {
|
||||
case strObject:
|
||||
return intObject(len(string(v))), nil
|
||||
case listable:
|
||||
case Listable:
|
||||
return intObject(v.Len()), nil
|
||||
case hashable:
|
||||
return intObject(v.Len()), nil
|
||||
|
@ -454,9 +468,9 @@ func lenBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return intObject(0), nil
|
||||
}
|
||||
|
||||
func indexLookup(ctx context.Context, obj, elem object) (object, error) {
|
||||
func indexLookup(ctx context.Context, obj, elem Object) (Object, error) {
|
||||
switch v := obj.(type) {
|
||||
case listable:
|
||||
case Listable:
|
||||
intIdx, ok := elem.(intObject)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
|
@ -475,7 +489,7 @@ func indexLookup(ctx context.Context, obj, elem object) (object, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func indexBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func indexBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -492,7 +506,28 @@ func indexBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
return val, nil
|
||||
}
|
||||
|
||||
func mapBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func keysBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
val := args.args[0]
|
||||
switch v := val.(type) {
|
||||
case hashable:
|
||||
keys := make(listObject, 0, v.Len())
|
||||
if err := v.Each(func(k string, _ Object) error {
|
||||
keys = append(keys, strObject(k))
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func mapBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if err := args.expectArgn(2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -503,12 +538,12 @@ func mapBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
}
|
||||
|
||||
switch t := args.args[0].(type) {
|
||||
case listable:
|
||||
case Listable:
|
||||
l := t.Len()
|
||||
newList := listObject{}
|
||||
for i := 0; i < l; i++ {
|
||||
v := t.Index(i)
|
||||
m, err := inv.invoke(ctx, args.fork([]object{v}))
|
||||
m, err := inv.invoke(ctx, args.fork([]Object{v}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -525,7 +560,7 @@ func firstBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
}
|
||||
|
||||
switch t := args.args[0].(type) {
|
||||
case listable:
|
||||
case Listable:
|
||||
if t.Len() == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -561,7 +596,7 @@ func (s seqObject) Len() int {
|
|||
return l
|
||||
}
|
||||
|
||||
func (s seqObject) Index(i int) object {
|
||||
func (s seqObject) Index(i int) Object {
|
||||
l := s.Len()
|
||||
if i < 0 || i > l {
|
||||
return nil
|
||||
|
@ -572,7 +607,7 @@ func (s seqObject) Index(i int) object {
|
|||
return intObject(s.from + i)
|
||||
}
|
||||
|
||||
func seqBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func seqBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
inclusive := false
|
||||
if inc, ok := args.kwargs["inc"]; ok {
|
||||
inclusive = (inc.Len() == 0) || inc.Truthy()
|
||||
|
@ -601,7 +636,7 @@ func seqBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func ifBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
||||
func ifBuiltin(ctx context.Context, args macroArgs) (Object, error) {
|
||||
if args.nargs() < 2 {
|
||||
return nil, errors.New("need at least 2 arguments")
|
||||
}
|
||||
|
@ -641,7 +676,7 @@ func ifBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
|||
|
||||
func foreachBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
||||
var (
|
||||
items object
|
||||
items Object
|
||||
blockIdx int
|
||||
err error
|
||||
)
|
||||
|
@ -664,16 +699,16 @@ func foreachBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
|||
}
|
||||
|
||||
var (
|
||||
last object
|
||||
last Object
|
||||
breakErr errBreak
|
||||
)
|
||||
|
||||
switch t := items.(type) {
|
||||
case listable:
|
||||
case Listable:
|
||||
l := t.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
v := t.Index(i)
|
||||
last, err = args.evalBlock(ctx, blockIdx, []object{v}, true) // TO INCLUDE: the index
|
||||
last, err = args.evalBlock(ctx, blockIdx, []Object{v}, true) // TO INCLUDE: the index
|
||||
if err != nil {
|
||||
if errors.As(err, &breakErr) {
|
||||
if !breakErr.isCont {
|
||||
|
@ -685,8 +720,8 @@ func foreachBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
|||
}
|
||||
}
|
||||
case hashable:
|
||||
err := t.Each(func(k string, v object) error {
|
||||
last, err = args.evalBlock(ctx, blockIdx, []object{strObject(k), v}, true)
|
||||
err := t.Each(func(k string, v Object) error {
|
||||
last, err = args.evalBlock(ctx, blockIdx, []Object{strObject(k), v}, true)
|
||||
return err
|
||||
})
|
||||
if errors.As(err, &breakErr) {
|
||||
|
@ -701,25 +736,25 @@ func foreachBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
|||
return last, nil
|
||||
}
|
||||
|
||||
func breakBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func breakBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if len(args.args) < 1 {
|
||||
return nil, errBreak{}
|
||||
}
|
||||
return nil, errBreak{ret: args.args[0]}
|
||||
}
|
||||
|
||||
func continueBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func continueBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
return nil, errBreak{isCont: true}
|
||||
}
|
||||
|
||||
func returnBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func returnBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
if len(args.args) < 1 {
|
||||
return nil, errReturn{}
|
||||
}
|
||||
return nil, errReturn{ret: args.args[0]}
|
||||
}
|
||||
|
||||
func procBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
||||
func procBuiltin(ctx context.Context, args macroArgs) (Object, error) {
|
||||
if args.nargs() < 1 {
|
||||
return nil, errors.New("need at least one arguments")
|
||||
}
|
||||
|
@ -763,7 +798,7 @@ func (b procObject) Truthy() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (b procObject) invoke(ctx context.Context, args invocationArgs) (object, error) {
|
||||
func (b procObject) invoke(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
newEc := b.ec.fork()
|
||||
|
||||
for i, name := range b.block.Names {
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
type Inst struct {
|
||||
out io.Writer
|
||||
missingBuiltinHandler MissingBuiltinHandler
|
||||
echoPrinter EchoPrinter
|
||||
|
||||
rootEC *evalCtx
|
||||
}
|
||||
|
@ -37,6 +38,14 @@ func WithModule(module Module) InstOption {
|
|||
}
|
||||
}
|
||||
|
||||
type EchoPrinter func(ctx context.Context, w io.Writer, args []any) error
|
||||
|
||||
func WithCustomEchoPrinter(echoPrinter EchoPrinter) InstOption {
|
||||
return func(i *Inst) {
|
||||
i.echoPrinter = echoPrinter
|
||||
}
|
||||
}
|
||||
|
||||
type Module struct {
|
||||
Name string
|
||||
Builtins map[string]BuiltinHandler
|
||||
|
|
Loading…
Reference in a new issue