mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-04 10:57:50 +08:00
136 lines
3.2 KiB
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)
|
|
}
|