elvish/parse/pprint.go

136 lines
3.2 KiB
Go

package parse
import (
"fmt"
"io"
"reflect"
"strconv"
)
const (
maxL int = 10
maxR = 10
indentInc = 2
)
// PprintAST pretty prints the AST part of a Node.
func PprintAST(n Node, wr io.Writer) {
pprintAST(n, wr, 0, "")
}
type field struct {
name string
tag reflect.StructTag
value interface{}
}
var zeroValue reflect.Value
func pprintAST(n Node, wr io.Writer, indent int, leading string) {
nodeType := reflect.TypeOf((*Node)(nil)).Elem()
var childFields, childrenFields, propertyFields []field
nt := reflect.TypeOf(n).Elem()
nv := reflect.ValueOf(n).Elem()
for i := 0; i < nt.NumField(); i++ {
f := nt.Field(i)
if f.Anonymous {
// embedded node struct, skip
continue
}
ft := f.Type
fv := nv.Field(i)
if ft.Kind() == reflect.Slice {
// list of children
if ft.Elem().Implements(nodeType) {
childrenFields = append(childrenFields,
field{f.Name, f.Tag, fv.Interface()})
continue
}
} else if child, ok := fv.Interface().(Node); ok {
// a child node
if reflect.Indirect(fv) != zeroValue {
childFields = append(childFields,
field{f.Name, f.Tag, child})
}
continue
}
// a property
propertyFields = append(propertyFields,
field{f.Name, f.Tag, fv.Interface()})
}
// has only one child and no properties: coalesce
if len(propertyFields) == 0 && len(n.Children()) == 1 {
pprintAST(n.Children()[0], wr, indent, leading+nt.Name()+"/")
return
}
// print heading
//b := n.n()
//fmt.Fprintf(wr, "%*s%s%s %s %d-%d", indent, "",
// wr.leading, nt.Name(), compactQuote(b.source(src)), b.begin, b.end)
fmt.Fprintf(wr, "%*s%s%s", indent, "", leading, nt.Name())
// print properties
for _, pf := range propertyFields {
fmtstring := pf.tag.Get("fmt")
if len(fmtstring) > 0 {
fmt.Fprintf(wr, " %s="+fmtstring, pf.name, pf.value)
} else {
value := pf.value
if s, ok := value.(string); ok {
value = compactQuote(s)
}
fmt.Fprintf(wr, " %s=%v", pf.name, value)
}
}
fmt.Fprint(wr, "\n")
// print lone children recursively
for _, chf := range childFields {
// TODO the name is omitted
pprintAST(chf.value.(Node), wr, indent+indentInc, "")
}
// print children list recursively
for _, chf := range childrenFields {
children := reflect.ValueOf(chf.value)
if children.Len() == 0 {
continue
}
// fmt.Fprintf(wr, "%*s.%s:\n", indent, "", chf.name)
for i := 0; i < children.Len(); i++ {
n := children.Index(i).Interface().(Node)
pprintAST(n, wr, indent+indentInc, "")
}
}
}
// PprintParseTree pretty prints the parse tree part of a Node.
func PprintParseTree(n Node, wr io.Writer) {
pprintParseTree(n, wr, 0)
}
func pprintParseTree(n Node, wr io.Writer, indent int) {
leading := ""
for len(n.Children()) == 1 {
leading += reflect.TypeOf(n).Elem().Name() + "/"
n = n.Children()[0]
}
fmt.Fprintf(wr, "%*s%s%s\n", indent, "", leading, summary(n))
for _, ch := range n.Children() {
pprintParseTree(ch, wr, indent+indentInc)
}
}
func summary(n Node) string {
return fmt.Sprintf("%s %s %d-%d", reflect.TypeOf(n).Elem().Name(),
compactQuote(n.SourceText()), n.Begin(), n.End())
}
func compactQuote(text string) string {
if len(text) > maxL+maxR+3 {
text = text[0:maxL] + "..." + text[len(text)-maxR:len(text)]
}
return strconv.Quote(text)
}