2024-12-11 09:47:05 +00:00
|
|
|
package repl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2024-12-11 11:30:14 +00:00
|
|
|
"io"
|
2024-12-11 09:47:05 +00:00
|
|
|
"os"
|
2024-12-11 11:30:14 +00:00
|
|
|
"reflect"
|
|
|
|
"strings"
|
2024-12-11 09:47:05 +00:00
|
|
|
"ucl.lmika.dev/ucl"
|
|
|
|
)
|
|
|
|
|
2024-12-11 10:16:08 +00:00
|
|
|
type NoResults struct{}
|
|
|
|
|
2024-12-11 09:47:05 +00:00
|
|
|
func (r *REPL) EvalAndDisplay(ctx context.Context, expr string) error {
|
|
|
|
res, err := r.inst.Eval(ctx, expr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-12-11 11:30:14 +00:00
|
|
|
return r.displayResult(ctx, os.Stdout, res, false)
|
2024-12-11 09:47:05 +00:00
|
|
|
}
|
|
|
|
|
2024-12-11 11:30:14 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2024-12-11 09:47:05 +00:00
|
|
|
switch v := res.(type) {
|
|
|
|
case nil:
|
|
|
|
if _, err = fmt.Fprintln(os.Stdout, "(nil)"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-12-11 10:16:08 +00:00
|
|
|
case ucl.Listable:
|
2024-12-11 11:30:14 +00:00
|
|
|
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")
|
2024-12-11 09:47:05 +00:00
|
|
|
}
|
|
|
|
}
|
2024-12-11 10:16:08 +00:00
|
|
|
case ucl.Object:
|
2024-12-11 11:30:14 +00:00
|
|
|
if _, err = fmt.Fprint(w, v.String()); err != nil {
|
2024-12-11 09:47:05 +00:00
|
|
|
return err
|
|
|
|
}
|
2024-12-11 11:30:14 +00:00
|
|
|
if !concise {
|
|
|
|
fmt.Fprintln(w)
|
|
|
|
}
|
2024-12-11 10:16:08 +00:00
|
|
|
default:
|
2024-12-11 11:30:14 +00:00
|
|
|
if _, err = fmt.Fprint(w, v); err != nil {
|
2024-12-11 10:16:08 +00:00
|
|
|
return err
|
|
|
|
}
|
2024-12-11 11:30:14 +00:00
|
|
|
if !concise {
|
|
|
|
fmt.Fprintln(w)
|
|
|
|
}
|
2024-12-11 09:47:05 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|