notation/reflect.go
2020-12-06 23:40:30 +01:00

351 lines
6.7 KiB
Go

package notation
import (
"bytes"
"fmt"
"reflect"
"sort"
"strconv"
"strings"
)
func withType(o opts) (opts, bool, bool) {
if o&types == 0 && o&allTypes == 0 {
return o, false, false
}
if o&skipTypes != 0 && o&allTypes == 0 {
return o &^ skipTypes, false, false
}
return o, true, o&allTypes != 0
}
func reflectPrimitive(o opts, r reflect.Value, v interface{}, suppressType ...string) node {
s := fmt.Sprint(v)
if s[0] == '(' && s[len(s)-1] == ')' {
s = s[1 : len(s)-1]
}
_, t, a := withType(o)
if !t {
return nodeOf(s)
}
tn := reflectType(r.Type())
if a {
return nodeOf(tn, "(", s, ")")
}
for _, suppress := range suppressType {
if tn.parts[0] == suppress {
return nodeOf(s)
}
}
return nodeOf(tn, "(", s, ")")
}
func reflectNil(o opts, groupUnnamedType bool, r reflect.Value) node {
if _, _, a := withType(o); !a {
return nodeOf("nil")
}
rt := r.Type()
if groupUnnamedType && rt.Name() == "" {
return nodeOf("(", reflectType(rt), ")(nil)")
}
return nodeOf(reflectType(rt), "(nil)")
}
func reflectItems(o opts, p *pending, prefix string, r reflect.Value) node {
typ := r.Type()
var w wrapper
if typ.Elem().Kind() == reflect.Uint8 {
w.sep = " "
w.mode = line
for i := 0; i < r.Len(); i++ {
w.items = append(
w.items,
nodeOf(fmt.Sprintf("%02x", r.Index(i).Uint())),
)
}
} else {
w.sep = ", "
w.suffix = ","
itemOpts := o | skipTypes
for i := 0; i < r.Len(); i++ {
w.items = append(
w.items,
reflectValue(itemOpts, p, r.Index(i)),
)
}
}
if _, t, _ := withType(o); t {
return nodeOf(reflectType(typ), "{", w, "}")
}
return nodeOf(prefix, "{", w, "}")
}
func reflectHidden(o opts, hidden string, r reflect.Value) node {
if r.IsNil() {
return reflectNil(o, true, r)
}
if _, t, _ := withType(o); t {
return reflectType(r.Type())
}
return nodeOf(hidden)
}
func reflectArray(o opts, p *pending, r reflect.Value) node {
return reflectItems(o, p, fmt.Sprintf("[%d]", r.Len()), r)
}
func reflectChan(o opts, r reflect.Value) node {
return reflectHidden(o, "chan", r)
}
func reflectFunc(o opts, r reflect.Value) node {
return reflectHidden(o, "func()", r)
}
func reflectInterface(o opts, p *pending, r reflect.Value) node {
if r.IsNil() {
return reflectNil(o, false, r)
}
e := reflectValue(o&^skipTypes, p, r.Elem())
if _, t, _ := withType(o); !t {
return e
}
return nodeOf(
reflectType(r.Type()),
"(",
wrapper{items: []node{e}},
")",
)
}
func reflectMap(o opts, p *pending, r reflect.Value) node {
if r.IsNil() {
return reflectNil(o, true, r)
}
var skeys []string
itemOpts := o | skipTypes
sv := make(map[string]reflect.Value)
sn := make(map[string]node)
for _, key := range r.MapKeys() {
kn := reflectValue(itemOpts, p, key)
knExt := reflectValue(itemOpts|_pointerValues, p, key)
var b bytes.Buffer
wr := writer{w: &b}
fprint(&wr, 0, knExt)
skey := b.String()
skeys = append(skeys, skey)
sv[skey] = key
sn[skey] = kn
}
if o&randomMaps == 0 {
sort.Strings(skeys)
}
w := wrapper{sep: ", ", suffix: ","}
for _, skey := range skeys {
vn := reflectValue(itemOpts, p, r.MapIndex(sv[skey]))
w.items = append(
w.items,
nodeOf(sn[skey], ": ", vn),
)
}
if _, t, _ := withType(o); !t {
return nodeOf("map{", w, "}")
}
return nodeOf(reflectType(r.Type()), "{", w, "}")
}
func reflectPointer(o opts, p *pending, r reflect.Value) node {
if r.IsNil() {
return reflectNil(o, true, r)
}
e := reflectValue(o, p, r.Elem())
if o&_pointerValues != 0 {
e = nodeOf(e, "_", r.Pointer())
}
if _, t, _ := withType(o); !t {
return e
}
return nodeOf("*", e)
}
func reflectList(o opts, p *pending, r reflect.Value) node {
if r.IsNil() {
return reflectNil(o, true, r)
}
return reflectItems(o, p, "[]", r)
}
func reflectString(o opts, r reflect.Value) node {
sv := r.String()
s := str{val: strconv.Quote(sv)}
if !strings.Contains(sv, "`") && strings.Contains(sv, "\n") {
s.raw = fmt.Sprintf("`%s`", sv)
}
n := nodeOf(s)
_, t, a := withType(o)
if !t {
return n
}
tn := reflectType(r.Type())
if !a && tn.parts[0] == "string" {
return n
}
return nodeOf(tn, "(", wrapper{items: []node{n}}, ")")
}
func reflectStruct(o opts, p *pending, r reflect.Value) node {
wr := wrapper{sep: ", ", suffix: ","}
fieldOpts := o | skipTypes
rt := r.Type()
for i := 0; i < r.NumField(); i++ {
name := rt.Field(i).Name
fv := reflectValue(fieldOpts, p, r.FieldByName(name))
wr.items = append(
wr.items,
nodeOf(name, ": ", fv),
)
}
if _, t, _ := withType(o); !t {
return nodeOf("{", wr, "}")
}
return nodeOf(reflectType(rt), "{", wr, "}")
}
func reflectUnsafePointer(o opts, r reflect.Value) node {
if r.IsNil() {
return reflectNil(o, false, r)
}
if _, _, a := withType(o); !a {
return nodeOf("pointer")
}
return nodeOf(reflectType(r.Type()), "(pointer)")
}
func checkPending(p *pending, r reflect.Value) (applyRef func(node) node, ref node, isPending bool) {
applyRef = func(n node) node { return n }
switch r.Kind() {
case reflect.Slice, reflect.Map, reflect.Ptr:
default:
return
}
if r.IsNil() {
return
}
var nr nodeRef
key := r.Pointer()
nr, isPending = p.values[key]
if isPending {
nr.refCount++
p.values[key] = nr
ref = nodeOf("r", nr.id)
return
}
nr = nodeRef{id: p.idCounter}
p.idCounter++
p.values[key] = nr
applyRef = func(n node) node {
nr = p.values[key]
if nr.refCount > 0 {
def := []interface{}{"r", nr.id, "="}
pp := make([]interface{}, len(def)+len(n.parts))
copy(pp, def)
copy(pp[len(def):], n.parts)
n.parts = pp
}
delete(p.values, key)
return n
}
return
}
func reflectValue(o opts, p *pending, r reflect.Value) node {
applyRef, ref, isPending := checkPending(p, r)
if isPending {
return ref
}
var n node
switch r.Kind() {
case reflect.Bool:
n = reflectPrimitive(o, r, r.Bool(), "bool")
case
reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Int64:
n = reflectPrimitive(o, r, r.Int(), "int")
case
reflect.Uint,
reflect.Uint8,
reflect.Uint16,
reflect.Uint32,
reflect.Uint64,
reflect.Uintptr:
n = reflectPrimitive(o, r, r.Uint())
case reflect.Float32, reflect.Float64:
n = reflectPrimitive(o, r, r.Float())
case reflect.Complex64, reflect.Complex128:
n = reflectPrimitive(o, r, r.Complex())
case reflect.Array:
n = reflectArray(o, p, r)
case reflect.Chan:
n = reflectChan(o, r)
case reflect.Func:
n = reflectFunc(o, r)
case reflect.Interface:
n = reflectInterface(o, p, r)
case reflect.Map:
n = reflectMap(o, p, r)
case reflect.Ptr:
n = reflectPointer(o, p, r)
case reflect.Slice:
n = reflectList(o, p, r)
case reflect.String:
n = reflectString(o, r)
case reflect.UnsafePointer:
n = reflectUnsafePointer(o, r)
default:
n = reflectStruct(o, p, r)
}
return applyRef(n)
}