Source file src/cmd/compile/internal/syntax/dumper.go

     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.(writeError).err // re-panics if it's not a writeError
    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  // writeError wraps locally caught write errors so we can distinguish
    86  // them from genuine panics which we don't want to return as errors.
    87  type writeError 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(writeError{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  

View as plain text