src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/parse/pprint.go (about)

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