mirror of
https://github.com/golang/go
synced 2024-11-26 07:38:00 -07:00
[dev.typeparams] cmd/compile/internal/syntax: clean up node printing API
Preparation for using the syntax printer as expression printer in types2. - Introduced Form to control printing format - Cleaned up/added String and ShortString convenience functions - Implemented ShortForm format which prints … for non-empty function and composite literal bodies - Added test to check write error handling Change-Id: Ie86e46d766fb60fcf07ef643c7788b2ef440ffa8 Reviewed-on: https://go-review.googlesource.com/c/go/+/282552 Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Griesemer <gri@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
parent
5b9152de57
commit
934f9dc0ef
@ -26,7 +26,7 @@ func Fdump(w io.Writer, n Node) (err error) {
|
||||
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = e.(localError).err // re-panics if it's not a localError
|
||||
err = e.(writeError).err // re-panics if it's not a writeError
|
||||
}
|
||||
}()
|
||||
|
||||
@ -82,16 +82,16 @@ func (p *dumper) Write(data []byte) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// localError wraps locally caught errors so we can distinguish
|
||||
// writeError wraps locally caught write errors so we can distinguish
|
||||
// them from genuine panics which we don't want to return as errors.
|
||||
type localError struct {
|
||||
type writeError struct {
|
||||
err error
|
||||
}
|
||||
|
||||
// printf is a convenience wrapper that takes care of print errors.
|
||||
func (p *dumper) printf(format string, args ...interface{}) {
|
||||
if _, err := fmt.Fprintf(p, format, args...); err != nil {
|
||||
panic(localError{err})
|
||||
panic(writeError{err})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,7 @@ func walkDirs(t *testing.T, dir string, action func(string)) {
|
||||
|
||||
func verifyPrint(t *testing.T, filename string, ast1 *File) {
|
||||
var buf1 bytes.Buffer
|
||||
_, err := Fprint(&buf1, ast1, true)
|
||||
_, err := Fprint(&buf1, ast1, LineForm)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -181,7 +181,7 @@ func verifyPrint(t *testing.T, filename string, ast1 *File) {
|
||||
}
|
||||
|
||||
var buf2 bytes.Buffer
|
||||
_, err = Fprint(&buf2, ast2, true)
|
||||
_, err = Fprint(&buf2, ast2, LineForm)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -13,19 +13,28 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TODO(gri) Consider removing the linebreaks flag from this signature.
|
||||
// Its likely rarely used in common cases.
|
||||
// Form controls print formatting.
|
||||
type Form uint
|
||||
|
||||
func Fprint(w io.Writer, x Node, linebreaks bool) (n int, err error) {
|
||||
const (
|
||||
_ Form = iota // default
|
||||
LineForm // use spaces instead of linebreaks where possible
|
||||
ShortForm // like LineForm but print "…" for non-empty function or composite literal bodies
|
||||
)
|
||||
|
||||
// Fprint prints node x to w in the specified form.
|
||||
// It returns the number of bytes written, and whether there was an error.
|
||||
func Fprint(w io.Writer, x Node, form Form) (n int, err error) {
|
||||
p := printer{
|
||||
output: w,
|
||||
linebreaks: linebreaks,
|
||||
form: form,
|
||||
linebreaks: form == 0,
|
||||
}
|
||||
|
||||
defer func() {
|
||||
n = p.written
|
||||
if e := recover(); e != nil {
|
||||
err = e.(localError).err // re-panics if it's not a localError
|
||||
err = e.(writeError).err // re-panics if it's not a writeError
|
||||
}
|
||||
}()
|
||||
|
||||
@ -35,15 +44,20 @@ func Fprint(w io.Writer, x Node, linebreaks bool) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func String(n Node) string {
|
||||
func asString(n Node, form Form) string {
|
||||
var buf bytes.Buffer
|
||||
_, err := Fprint(&buf, n, false)
|
||||
_, err := Fprint(&buf, n, form)
|
||||
if err != nil {
|
||||
panic(err) // TODO(gri) print something sensible into buf instead
|
||||
fmt.Fprintf(&buf, "<<< ERROR: %s", err)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// String and ShortString are convenience functions that print n in
|
||||
// LineForm or ShortForm respectively, and return the printed string.
|
||||
func String(n Node) string { return asString(n, LineForm) }
|
||||
func ShortString(n Node) string { return asString(n, ShortForm) }
|
||||
|
||||
type ctrlSymbol int
|
||||
|
||||
const (
|
||||
@ -66,6 +80,7 @@ type whitespace struct {
|
||||
type printer struct {
|
||||
output io.Writer
|
||||
written int // number of bytes written
|
||||
form Form
|
||||
linebreaks bool // print linebreaks instead of semis
|
||||
|
||||
indent int // current indentation level
|
||||
@ -81,7 +96,7 @@ func (p *printer) write(data []byte) {
|
||||
n, err := p.output.Write(data)
|
||||
p.written += n
|
||||
if err != nil {
|
||||
panic(localError{err})
|
||||
panic(writeError{err})
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,18 +370,35 @@ func (p *printer) printRawNode(n Node) {
|
||||
p.print(_Name, n.Value) // _Name requires actual value following immediately
|
||||
|
||||
case *FuncLit:
|
||||
p.print(n.Type, blank, n.Body)
|
||||
p.print(n.Type, blank)
|
||||
if n.Body != nil {
|
||||
if p.form == ShortForm {
|
||||
p.print(_Lbrace)
|
||||
if len(n.Body.List) > 0 {
|
||||
p.print(_Name, "…")
|
||||
}
|
||||
p.print(_Rbrace)
|
||||
} else {
|
||||
p.print(n.Body)
|
||||
}
|
||||
}
|
||||
|
||||
case *CompositeLit:
|
||||
if n.Type != nil {
|
||||
p.print(n.Type)
|
||||
}
|
||||
p.print(_Lbrace)
|
||||
if p.form == ShortForm {
|
||||
if len(n.ElemList) > 0 {
|
||||
p.print(_Name, "…")
|
||||
}
|
||||
} else {
|
||||
if n.NKeys > 0 && n.NKeys == len(n.ElemList) {
|
||||
p.printExprLines(n.ElemList)
|
||||
} else {
|
||||
p.printExprList(n.ElemList)
|
||||
}
|
||||
}
|
||||
p.print(_Rbrace)
|
||||
|
||||
case *ParenExpr:
|
||||
@ -450,9 +482,13 @@ func (p *printer) printRawNode(n Node) {
|
||||
}
|
||||
p.print(_Lbrace)
|
||||
if len(n.FieldList) > 0 {
|
||||
if p.linebreaks {
|
||||
p.print(newline, indent)
|
||||
p.printFieldList(n.FieldList, n.TagList)
|
||||
p.print(outdent, newline)
|
||||
} else {
|
||||
p.printFieldList(n.FieldList, n.TagList)
|
||||
}
|
||||
}
|
||||
p.print(_Rbrace)
|
||||
|
||||
@ -467,9 +503,13 @@ func (p *printer) printRawNode(n Node) {
|
||||
}
|
||||
p.print(_Lbrace)
|
||||
if len(n.MethodList) > 0 {
|
||||
if p.linebreaks {
|
||||
p.print(newline, indent)
|
||||
p.printMethodList(n.MethodList)
|
||||
p.print(outdent, newline)
|
||||
} else {
|
||||
p.printMethodList(n.MethodList)
|
||||
}
|
||||
}
|
||||
p.print(_Rbrace)
|
||||
|
||||
|
@ -25,11 +25,38 @@ func TestPrint(t *testing.T) {
|
||||
}
|
||||
|
||||
if ast != nil {
|
||||
Fprint(testOut(), ast, true)
|
||||
Fprint(testOut(), ast, LineForm)
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
type shortBuffer struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func (w *shortBuffer) Write(data []byte) (n int, err error) {
|
||||
w.buf = append(w.buf, data...)
|
||||
n = len(data)
|
||||
if len(w.buf) > 10 {
|
||||
err = io.ErrShortBuffer
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestPrintError(t *testing.T) {
|
||||
const src = "package p; var x int"
|
||||
ast, err := Parse(nil, strings.NewReader(src), nil, nil, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var buf shortBuffer
|
||||
_, err = Fprint(&buf, ast, 0)
|
||||
if err == nil || err != io.ErrShortBuffer {
|
||||
t.Errorf("got err = %s, want %s", err, io.ErrShortBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
var stringTests = []string{
|
||||
"package p",
|
||||
"package p; type _ int; type T1 = struct{}; type ( _ *struct{}; T2 = float32 )",
|
||||
|
Loading…
Reference in New Issue
Block a user