package repl import ( "context" "errors" "fmt" "io" "os" "reflect" "strings" "ucl.lmika.dev/ucl" ) type NoResults struct{} func (r *REPL) EvalAndDisplay(ctx context.Context, expr string) error { res, err := r.inst.Eval(ctx, expr) if err != nil { if errors.Is(err, ucl.ErrNotConvertable) { return nil } return err } return r.displayResult(ctx, r.inst.Out(), res, false) } 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 nil: if _, err = fmt.Fprintln(os.Stdout, "(nil)"); err != nil { return err } case ucl.Listable: 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 if len(v) > 0 { 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.Fprint(w, v.String()); err != nil { return err } if !concise { fmt.Fprintln(w) } default: if _, err = fmt.Fprint(w, v); err != nil { return err } if !concise { fmt.Fprintln(w) } } return nil }