repr.go 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. package vals
  2. import (
  3. "fmt"
  4. "math"
  5. "math/big"
  6. "reflect"
  7. "strconv"
  8. "src.elv.sh/pkg/parse"
  9. )
  10. // Reprer wraps the Repr method.
  11. type Reprer interface {
  12. // Repr returns a string that represents a Value. The string either be a
  13. // literal of that Value that is preferably deep-equal to it (like `[a b c]`
  14. // for a list), or a string enclosed in "<>" containing the kind and
  15. // identity of the Value(like `<fn 0xdeadcafe>`).
  16. //
  17. // If indent is at least 0, it should be pretty-printed with the current
  18. // indentation level of indent; the indent of the first line has already
  19. // been written and shall not be written in Repr. The returned string
  20. // should never contain a trailing newline.
  21. Repr(indent int) string
  22. }
  23. // ReprPlain is like Repr, but without pretty-printing.
  24. func ReprPlain(v interface{}) string {
  25. return Repr(v, math.MinInt)
  26. }
  27. // Repr returns the representation for a value, a string that is preferably
  28. // (but not necessarily) an Elvish expression that evaluates to the argument.
  29. // The representation is pretty-printed, using indent as the initial level of
  30. // indentation. It is implemented for the builtin types nil, bool and string,
  31. // the File, List and Map types, StructMap types, and types satisfying the
  32. // Reprer interface. For other types, it uses fmt.Sprint with the format
  33. // "<unknown %v>".
  34. func Repr(v interface{}, indent int) string {
  35. switch v := v.(type) {
  36. case nil:
  37. return "$nil"
  38. case bool:
  39. if v {
  40. return "$true"
  41. }
  42. return "$false"
  43. case string:
  44. return parse.Quote(v)
  45. case int:
  46. return "(num " + strconv.Itoa(v) + ")"
  47. case *big.Int:
  48. return "(num " + v.String() + ")"
  49. case *big.Rat:
  50. return "(num " + v.String() + ")"
  51. case float64:
  52. return "(num " + formatFloat64(v) + ")"
  53. case Reprer:
  54. return v.Repr(indent)
  55. case File:
  56. return fmt.Sprintf("<file{%s %d}>", parse.Quote(v.Name()), v.Fd())
  57. case List:
  58. b := NewListReprBuilder(indent)
  59. for it := v.Iterator(); it.HasElem(); it.Next() {
  60. b.WriteElem(Repr(it.Elem(), indent+1))
  61. }
  62. return b.String()
  63. case Map:
  64. builder := NewMapReprBuilder(indent)
  65. for it := v.Iterator(); it.HasElem(); it.Next() {
  66. k, v := it.Elem()
  67. builder.WritePair(Repr(k, indent+1), indent+2, Repr(v, indent+2))
  68. }
  69. return builder.String()
  70. case StructMap:
  71. return reprStructMap(v, indent)
  72. case PseudoStructMap:
  73. return reprStructMap(v.Fields(), indent)
  74. default:
  75. return fmt.Sprintf("<unknown %v>", v)
  76. }
  77. }
  78. func reprStructMap(v StructMap, indent int) string {
  79. vValue := reflect.ValueOf(v)
  80. vType := vValue.Type()
  81. builder := NewMapReprBuilder(indent)
  82. it := iterateStructMap(vType)
  83. for it.Next() {
  84. k, v := it.Get(vValue)
  85. builder.WritePair(Repr(k, indent+1), indent+2, Repr(v, indent+2))
  86. }
  87. return builder.String()
  88. }