Home | History | Annotate | Download | only in syntax
      1 // Copyright 2016 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // This file implements printing of syntax tree structures.
      6 
      7 package syntax
      8 
      9 import (
     10 	"fmt"
     11 	"io"
     12 	"reflect"
     13 	"unicode"
     14 	"unicode/utf8"
     15 )
     16 
     17 // Fdump dumps the structure of the syntax tree rooted at n to w.
     18 // It is intended for debugging purposes; no specific output format
     19 // is guaranteed.
     20 func Fdump(w io.Writer, n Node) (err error) {
     21 	p := dumper{
     22 		output: w,
     23 		ptrmap: make(map[Node]int),
     24 		last:   '\n', // force printing of line number on first line
     25 	}
     26 
     27 	defer func() {
     28 		if e := recover(); e != nil {
     29 			err = e.(localError).err // re-panics if it's not a localError
     30 		}
     31 	}()
     32 
     33 	if n == nil {
     34 		p.printf("nil\n")
     35 		return
     36 	}
     37 	p.dump(reflect.ValueOf(n), n)
     38 	p.printf("\n")
     39 
     40 	return
     41 }
     42 
     43 type dumper struct {
     44 	output io.Writer
     45 	ptrmap map[Node]int // node -> dump line number
     46 	indent int          // current indentation level
     47 	last   byte         // last byte processed by Write
     48 	line   int          // current line number
     49 }
     50 
     51 var indentBytes = []byte(".  ")
     52 
     53 func (p *dumper) Write(data []byte) (n int, err error) {
     54 	var m int
     55 	for i, b := range data {
     56 		// invariant: data[0:n] has been written
     57 		if b == '\n' {
     58 			m, err = p.output.Write(data[n : i+1])
     59 			n += m
     60 			if err != nil {
     61 				return
     62 			}
     63 		} else if p.last == '\n' {
     64 			p.line++
     65 			_, err = fmt.Fprintf(p.output, "%6d  ", p.line)
     66 			if err != nil {
     67 				return
     68 			}
     69 			for j := p.indent; j > 0; j-- {
     70 				_, err = p.output.Write(indentBytes)
     71 				if err != nil {
     72 					return
     73 				}
     74 			}
     75 		}
     76 		p.last = b
     77 	}
     78 	if len(data) > n {
     79 		m, err = p.output.Write(data[n:])
     80 		n += m
     81 	}
     82 	return
     83 }
     84 
     85 // localError wraps locally caught errors so we can distinguish
     86 // them from genuine panics which we don't want to return as errors.
     87 type localError struct {
     88 	err error
     89 }
     90 
     91 // printf is a convenience wrapper that takes care of print errors.
     92 func (p *dumper) printf(format string, args ...interface{}) {
     93 	if _, err := fmt.Fprintf(p, format, args...); err != nil {
     94 		panic(localError{err})
     95 	}
     96 }
     97 
     98 // dump prints the contents of x.
     99 // If x is the reflect.Value of a struct s, where &s
    100 // implements Node, then &s should be passed for n -
    101 // this permits printing of the unexported span and
    102 // comments fields of the embedded isNode field by
    103 // calling the Span() and Comment() instead of using
    104 // reflection.
    105 func (p *dumper) dump(x reflect.Value, n Node) {
    106 	switch x.Kind() {
    107 	case reflect.Interface:
    108 		if x.IsNil() {
    109 			p.printf("nil")
    110 			return
    111 		}
    112 		p.dump(x.Elem(), nil)
    113 
    114 	case reflect.Ptr:
    115 		if x.IsNil() {
    116 			p.printf("nil")
    117 			return
    118 		}
    119 
    120 		// special cases for identifiers w/o attached comments (common case)
    121 		if x, ok := x.Interface().(*Name); ok {
    122 			p.printf("%s @ %v", x.Value, x.Pos())
    123 			return
    124 		}
    125 
    126 		p.printf("*")
    127 		// Fields may share type expressions, and declarations
    128 		// may share the same group - use ptrmap to keep track
    129 		// of nodes that have been printed already.
    130 		if ptr, ok := x.Interface().(Node); ok {
    131 			if line, exists := p.ptrmap[ptr]; exists {
    132 				p.printf("(Node @ %d)", line)
    133 				return
    134 			}
    135 			p.ptrmap[ptr] = p.line
    136 			n = ptr
    137 		}
    138 		p.dump(x.Elem(), n)
    139 
    140 	case reflect.Slice:
    141 		if x.IsNil() {
    142 			p.printf("nil")
    143 			return
    144 		}
    145 		p.printf("%s (%d entries) {", x.Type(), x.Len())
    146 		if x.Len() > 0 {
    147 			p.indent++
    148 			p.printf("\n")
    149 			for i, n := 0, x.Len(); i < n; i++ {
    150 				p.printf("%d: ", i)
    151 				p.dump(x.Index(i), nil)
    152 				p.printf("\n")
    153 			}
    154 			p.indent--
    155 		}
    156 		p.printf("}")
    157 
    158 	case reflect.Struct:
    159 		typ := x.Type()
    160 
    161 		// if span, ok := x.Interface().(lexical.Span); ok {
    162 		// 	p.printf("%s", &span)
    163 		// 	return
    164 		// }
    165 
    166 		p.printf("%s {", typ)
    167 		p.indent++
    168 
    169 		first := true
    170 		if n != nil {
    171 			p.printf("\n")
    172 			first = false
    173 			// p.printf("Span: %s\n", n.Span())
    174 			// if c := *n.Comments(); c != nil {
    175 			// 	p.printf("Comments: ")
    176 			// 	p.dump(reflect.ValueOf(c), nil) // a Comment is not a Node
    177 			// 	p.printf("\n")
    178 			// }
    179 		}
    180 
    181 		for i, n := 0, typ.NumField(); i < n; i++ {
    182 			// Exclude non-exported fields because their
    183 			// values cannot be accessed via reflection.
    184 			if name := typ.Field(i).Name; isExported(name) {
    185 				if first {
    186 					p.printf("\n")
    187 					first = false
    188 				}
    189 				p.printf("%s: ", name)
    190 				p.dump(x.Field(i), nil)
    191 				p.printf("\n")
    192 			}
    193 		}
    194 
    195 		p.indent--
    196 		p.printf("}")
    197 
    198 	default:
    199 		switch x := x.Interface().(type) {
    200 		case string:
    201 			// print strings in quotes
    202 			p.printf("%q", x)
    203 		default:
    204 			p.printf("%v", x)
    205 		}
    206 	}
    207 }
    208 
    209 func isExported(name string) bool {
    210 	ch, _ := utf8.DecodeRuneInString(name)
    211 	return unicode.IsUpper(ch)
    212 }
    213