Skip to content

Commit c8683ff

Browse files
griesemermdempsky
authored andcommitted
cmd/compile/internal/syntax: fast Go syntax trees, initial commit.
Syntax tree nodes, scanner, parser, basic printers. Builds syntax trees for entire Go std lib at a rate of ~1.8M lines/s in warmed up state (MacMini, 2.3 GHz Intel Core i7, 8GB RAM): $ go test -run StdLib -fast parsed 1074617 lines (2832 files) in 579.66364ms (1853863 lines/s) allocated 282.212Mb (486.854Mb/s) PASS Change-Id: Ie26d9a7bf4e5ff07457aedfcc9b89f0eba72ae3f Reviewed-on: https://go-review.googlesource.com/27195 Run-TryBot: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Robert Griesemer <gri@golang.org>
1 parent 3b967be commit c8683ff

File tree

13 files changed

+5354
-0
lines changed

13 files changed

+5354
-0
lines changed

misc/nacl/testzip.proto

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ go src=..
1818
asm
1919
testdata
2020
+
21+
compile
22+
internal
23+
syntax
24+
parser.go
2125
doc
2226
main.go
2327
pkg.go
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
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(x.Value)
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+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
package syntax
6+
7+
import (
8+
"os"
9+
"testing"
10+
)
11+
12+
func TestDump(t *testing.T) {
13+
if testing.Short() {
14+
t.Skip("skipping test in short mode")
15+
}
16+
17+
ast, err := ReadFile(*src, nil, 0)
18+
if err != nil {
19+
t.Fatal(err)
20+
}
21+
Fdump(os.Stdout, ast)
22+
}

0 commit comments

Comments
 (0)