Added custom printers
This commit is contained in:
parent
62724e3f37
commit
7a92eeddac
7 changed files with 287 additions and 63 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue