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