github.com/hugelgupf/u-root@v0.0.0-20191023214958-4807c632154c/cmds/core/elvish/parse/pprint.go (about)

     1  package parse
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"reflect"
     8  	"strconv"
     9  )
    10  
    11  const (
    12  	maxL      int = 10
    13  	maxR          = 10
    14  	indentInc     = 2
    15  )
    16  
    17  // PPrintAST pretty-prints the AST part of a Node.
    18  func PPrintAST(n Node) string {
    19  	var b bytes.Buffer
    20  	PPrintASTTo(n, &b)
    21  	return b.String()
    22  }
    23  
    24  // PPrintASTTo pretty-prints the AST part of a Node to a Writer.
    25  func PPrintASTTo(n Node, wr io.Writer) {
    26  	pprintAST(n, wr, 0, "")
    27  }
    28  
    29  type field struct {
    30  	name  string
    31  	tag   reflect.StructTag
    32  	value interface{}
    33  }
    34  
    35  var zeroValue reflect.Value
    36  
    37  func pprintAST(n Node, wr io.Writer, indent int, leading string) {
    38  	nodeType := reflect.TypeOf((*Node)(nil)).Elem()
    39  
    40  	var childFields, childrenFields, propertyFields []field
    41  
    42  	nt := reflect.TypeOf(n).Elem()
    43  	nv := reflect.ValueOf(n).Elem()
    44  
    45  	for i := 0; i < nt.NumField(); i++ {
    46  		f := nt.Field(i)
    47  		if f.Anonymous {
    48  			// embedded node struct, skip
    49  			continue
    50  		}
    51  		ft := f.Type
    52  		fv := nv.Field(i)
    53  		if ft.Kind() == reflect.Slice {
    54  			// list of children
    55  			if ft.Elem().Implements(nodeType) {
    56  				childrenFields = append(childrenFields,
    57  					field{f.Name, f.Tag, fv.Interface()})
    58  				continue
    59  			}
    60  		} else if child, ok := fv.Interface().(Node); ok {
    61  			// a child node
    62  			if reflect.Indirect(fv) != zeroValue {
    63  				childFields = append(childFields,
    64  					field{f.Name, f.Tag, child})
    65  			}
    66  			continue
    67  		}
    68  		// a property
    69  		propertyFields = append(propertyFields,
    70  			field{f.Name, f.Tag, fv.Interface()})
    71  	}
    72  
    73  	// has only one child and nothing more : coalesce
    74  	if len(n.Children()) == 1 &&
    75  		n.Children()[0].SourceText() == n.SourceText() {
    76  		pprintAST(n.Children()[0], wr, indent, leading+nt.Name()+"/")
    77  		return
    78  	}
    79  	// print heading
    80  	//b := n.n()
    81  	//fmt.Fprintf(wr, "%*s%s%s %s %d-%d", indent, "",
    82  	//	wr.leading, nt.Name(), compactQuote(b.source(src)), b.begin, b.end)
    83  	fmt.Fprintf(wr, "%*s%s%s", indent, "", leading, nt.Name())
    84  	// print properties
    85  	for _, pf := range propertyFields {
    86  		fmtstring := pf.tag.Get("fmt")
    87  		if len(fmtstring) > 0 {
    88  			fmt.Fprintf(wr, " %s="+fmtstring, pf.name, pf.value)
    89  		} else {
    90  			value := pf.value
    91  			if s, ok := value.(string); ok {
    92  				value = compactQuote(s)
    93  			}
    94  			fmt.Fprintf(wr, " %s=%v", pf.name, value)
    95  		}
    96  	}
    97  	fmt.Fprint(wr, "\n")
    98  	// print lone children recursively
    99  	for _, chf := range childFields {
   100  		// TODO the name is omitted
   101  		pprintAST(chf.value.(Node), wr, indent+indentInc, "")
   102  	}
   103  	// print children list recursively
   104  	for _, chf := range childrenFields {
   105  		children := reflect.ValueOf(chf.value)
   106  		if children.Len() == 0 {
   107  			continue
   108  		}
   109  		// fmt.Fprintf(wr, "%*s.%s:\n", indent, "", chf.name)
   110  		for i := 0; i < children.Len(); i++ {
   111  			n := children.Index(i).Interface().(Node)
   112  			pprintAST(n, wr, indent+indentInc, "")
   113  		}
   114  	}
   115  }
   116  
   117  // PPrintParseTree pretty-prints the parse tree part of a Node.
   118  func PPrintParseTree(n Node) string {
   119  	var b bytes.Buffer
   120  	PPrintParseTreeTo(n, &b)
   121  	return b.String()
   122  }
   123  
   124  // PPrintParseTreeTo pretty-prints the parse tree part of a Node to a Writer.
   125  func PPrintParseTreeTo(n Node, wr io.Writer) {
   126  	pprintParseTree(n, wr, 0)
   127  }
   128  
   129  func pprintParseTree(n Node, wr io.Writer, indent int) {
   130  	leading := ""
   131  	for len(n.Children()) == 1 {
   132  		leading += reflect.TypeOf(n).Elem().Name() + "/"
   133  		n = n.Children()[0]
   134  	}
   135  	fmt.Fprintf(wr, "%*s%s%s\n", indent, "", leading, summary(n))
   136  	for _, ch := range n.Children() {
   137  		pprintParseTree(ch, wr, indent+indentInc)
   138  	}
   139  }
   140  
   141  func summary(n Node) string {
   142  	return fmt.Sprintf("%s %s %d-%d", reflect.TypeOf(n).Elem().Name(),
   143  		compactQuote(n.SourceText()), n.Begin(), n.End())
   144  }
   145  
   146  func compactQuote(text string) string {
   147  	if len(text) > maxL+maxR+3 {
   148  		text = text[0:maxL] + "..." + text[len(text)-maxR:]
   149  	}
   150  	return strconv.Quote(text)
   151  }