diff options
| author | Robert Griesemer <gri@golang.org> | 2009-06-01 19:13:44 -0700 |
|---|---|---|
| committer | Robert Griesemer <gri@golang.org> | 2009-06-01 19:13:44 -0700 |
| commit | d7acfc75cd5012829c01ff2dbe0aaf71961c6c2b (patch) | |
| tree | b3d675756909a84c0098851a592cb7f4b376596e /usr/gri | |
| parent | 2494bcb4b1914c17dce8491cf2244fd0fd63115b (diff) | |
| download | go-d7acfc75cd5012829c01ff2dbe0aaf71961c6c2b.tar.xz | |
format package
R=r,rsc
DELTA=2871 (1712 added, 1118 deleted, 41 changed)
OCL=29222
CL=29704
Diffstat (limited to 'usr/gri')
| -rw-r--r-- | usr/gri/pretty/Makefile | 2 | ||||
| -rw-r--r-- | usr/gri/pretty/ast.txt | 60 | ||||
| -rw-r--r-- | usr/gri/pretty/format.go | 967 | ||||
| -rw-r--r-- | usr/gri/pretty/format_test.go | 153 | ||||
| -rw-r--r-- | usr/gri/pretty/pretty.go | 56 |
5 files changed, 81 insertions, 1157 deletions
diff --git a/usr/gri/pretty/Makefile b/usr/gri/pretty/Makefile index b0662bc74b..0ede5d470a 100644 --- a/usr/gri/pretty/Makefile +++ b/usr/gri/pretty/Makefile @@ -32,7 +32,7 @@ clean: godoc.6: astprinter.6 -pretty.6: astprinter.6 format.6 +pretty.6: astprinter.6 %.6: %.go $(G) $(F) $< diff --git a/usr/gri/pretty/ast.txt b/usr/gri/pretty/ast.txt index 85373d6b34..b51146c5c1 100644 --- a/usr/gri/pretty/ast.txt +++ b/usr/gri/pretty/ast.txt @@ -13,7 +13,7 @@ token "token"; array = *; -pointer = +ptr = *; string = @@ -47,6 +47,9 @@ token.Token = ^:string; ast.Comment = + // TODO this doesn't indent properly after //-style comments because + // the '\n'-char is printed as part of the comment - need to + // address this Text:string [Text:isMultiLineComment "\n"]; ast.Comments = @@ -84,7 +87,7 @@ ast.StringList = {Strings / "\n"}; ast.FuncLit = - Type " " Body; + Type " " Body ^:clearOptSemi; // no optional ; after a func literal body ast.CompositeLit = Type "{" {Elts / ", "} "}"; @@ -123,9 +126,9 @@ ast.StructType = "struct" [Lbrace:isValidPos " {"] [ Fields:exists - >> "\t" "\n" + ( "\t" >> "\n" {Fields / ";\n"} - << "\n" + ) "\n" ] [Rbrace:isValidPos "}"]; @@ -142,9 +145,9 @@ ast.InterfaceType = "interface" [Lbrace:isValidPos " {"] [ Methods:exists - >> "\t" "\n" + ( "\t" >> "\n" {Methods / ";\n"} - << "\n" + ) "\n" ] [Rbrace:isValidPos "}"]; @@ -197,14 +200,17 @@ ast.ReturnStmt = ast.BranchStmt = Tok [" " Label]; +stmtList = + {^ / ^:optSemi "\n"}; + blockStmt = // like ast.BlockStmt but w/o indentation "{" [List:exists "\n" - {List / ";\n"} + List:stmtList "\n" ] - "}"; + "}" ^:setOptSemi; blockStmtPtr = *:blockStmt; @@ -212,11 +218,11 @@ blockStmtPtr = ast.BlockStmt = "{" [List:exists - >> "\t" "\n" - {List / ";\n"} - << "\n" + ( "\t" >> "\n" + List:stmtList + ) "\n" ] - "}"; + "}" ^:setOptSemi; ast.IfStmt = "if " [Init "; "] [Cond " "] Body [" else " Else]; @@ -227,9 +233,9 @@ ast.CaseClause = ) ":" [Body:exists - >> "\t" "\n" - {Body / ";\n"} - << + ( "\t" >> "\n" + Body:stmtList + ) ]; ast.SwitchStmt = @@ -242,9 +248,9 @@ ast.TypeCaseClause = ) ":" [Body:exists - >> "\t" "\n" - {Body / ";\n"} - << + ( "\t" >> "\n" + Body:stmtList + ) ]; ast.TypeSwitchStmt = @@ -257,9 +263,9 @@ ast.CommClause = ) ":" [Body:exists - >> "\t" "\n" - {Body / ";\n"} - << + ( "\t" >> "\n" + Body:stmtList + ) ]; ast.SelectStmt = @@ -303,11 +309,13 @@ ast.BadDecl = ast.GenDecl = Doc Tok " " - ( Lparen:isValidPos - >> "\t" "(\n" - {Specs / ";\n"} - << - "\n)" + ( Lparen:isValidPos "(" + [Specs:exists + ( "\t" >> "\n" + {Specs / ";\n"} + ) "\n" + ] + ")" ^:setOptSemi | {Specs / ";\n"} ); diff --git a/usr/gri/pretty/format.go b/usr/gri/pretty/format.go deleted file mode 100644 index a671beba3c..0000000000 --- a/usr/gri/pretty/format.go +++ /dev/null @@ -1,967 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* The format package implements syntax-directed, type-driven formatting - of arbitrary data structures. Formatting a data structure consists of - two phases: first, a format specification is parsed (once per format) - which results in a "compiled" format. The format can then be used - repeatedly to print arbitrary values to a io.Writer. - - A format specification consists of a set of named format rules in EBNF. - The rule names correspond to the type names of the data structure to be - formatted. Each format rule consists of literal values and struct field - names which are combined into sequences, alternatives, grouped, optional, - repeated, or indented sub-expressions. Additionally, format rules may be - specified via Go formatter functions. - - When formatting a value, its type name determines the format rule. The - syntax of the rule or the corresponding formatter function determines - if and how the value is formatted. A format rule may refer to a struct - field of the current value. In this case the same mechanism is applied - recursively to that field. -*/ -package format - -import ( - "container/vector"; - "flag"; - "fmt"; - "go/scanner"; - "go/token"; - "io"; - "os"; - "reflect"; - "runtime"; - "strconv"; - "strings"; -) - - -// ---------------------------------------------------------------------------- -// Format representation - -// Custom formatters implement the Formatter function type. -// A formatter is invoked with a writer w, an environment env -// (provided to format.Fprint and simply passed through), the -// value to format, and the rule name under which the formatter -// was installed (the same formatter function may be installed -// under different names). -// -type Formatter func(w io.Writer, env, value interface{}, rule_name string) bool - - -// A FormatterMap is a set of custom formatters. -// It maps a rule name to a formatter. -// -type FormatterMap map [string] Formatter; - - -// A production expression is built from the following nodes. -// -type ( - expr interface {}; - - alternatives []expr; // x | y | z - - sequence []expr; // x y z - - // a literal is represented as string or []byte - - field struct { - field_name string; // including "^", "*" - rule_name string; // "" if no rule name specified - }; - - indentation struct { - indent, body expr; // >> indent body << - }; - - option struct { - body expr; // [body] - }; - - repetition struct { - body, div expr; // {body / div} - }; - - custom struct { - rule_name string; - form Formatter - }; -) - - -/* The syntax of a format specification is presented in the same EBNF - notation as used in the Go language spec. The syntax of white space, - comments, identifiers, and string literals is the same as in Go. - - A format specification consists of a possibly empty set of package - declarations and format rules: - - Format = [ Entry { ";" Entry } ] [ ";" ] . - Entry = PackageDecl | FormatRule . - - A package declaration binds a package name (such as 'ast') to a - package import path (such as '"go/ast"'). A package name must be - declared at most once. - - PackageDecl = PackageName ImportPath . - PackageName = identifier . - ImportPath = string . - - A format rule binds a rule name to a format expression. A rule name - may be a type name or one of the special names 'default' (denoting - the default rule) or '/' (denoting the global "divider" rule - see - below). A type name may be the name of a predeclared type ('int', - 'float32', etc.), the name of an anonymous composite type ('array', - 'pointer', etc.), or the name of a user-defined type qualified by - the corresponding package name (for instance 'ast.MapType'). The - package name must have been declared already. A rule name must be - declared at most once. - - FormatRule = RuleName "=" Expression . - RuleName = TypeName | "default" | "/" . - TypeName = [ PackageName "." ] identifier . - - A format expression specifies how a value is to be formatted. In its - most general form, a format expression is a set of alternatives separated - by "|". Each alternative and the entire expression may be empty. - - Expression = [ Sequence ] { "|" [ Sequence ] } . - Sequence = Operand { Operand } . - Operand = Literal | Field | Indentation | Group | Option | Repetition . - - Literal = string . - Field = FieldName [ ":" RuleName ] . - FieldName = identifier | "^" | "*" . - - Indent = ">>" Operand Expression "<<" . - Group = "(" Expression ")" . - Option = "[" Expression "]" . - Repetition = "{" Expression [ "/" Expression ] "}" . - - TODO complete this comment -*/ -type Format map [string] expr; - - -// ---------------------------------------------------------------------------- -// Error handling - -// Error describes an individual error. The position Pos, if valid, -// indicates the format source position the error relates to. The -// error is specified with the Msg string. -// -type Error struct { - Pos token.Position; - Msg string; -} - - -// Error implements the os.Error interface. -func (e *Error) String() string { - pos := ""; - if e.Pos.IsValid() { - pos = fmt.Sprintf("%d:%d: ", e.Pos.Line, e.Pos.Column); - } - return pos + e.Msg; -} - - -// Multiple parser errors are returned as an ErrorList. -type ErrorList []*Error - - -// ErrorList implements the SortInterface. -func (p ErrorList) Len() int { return len(p); } -func (p ErrorList) Swap(i, j int) { p[i], p[j] = p[j], p[i]; } -func (p ErrorList) Less(i, j int) bool { return p[i].Pos.Offset < p[j].Pos.Offset; } - - -// ErrorList implements the os.Error interface. -func (p ErrorList) String() string { - switch len(p) { - case 0: return "unspecified error"; - case 1: return p[0].String(); - } - return fmt.Sprintf("%s (and %d more errors)", p[0].String(), len(p) - 1); -} - - -// ---------------------------------------------------------------------------- -// Parsing - -type parser struct { - errors vector.Vector; - scanner scanner.Scanner; - pos token.Position; // token position - tok token.Token; // one token look-ahead - lit []byte; // token literal - - packs map [string] string; // PackageName -> ImportPath - rules Format; // RuleName -> Expression -} - - -// The parser implements scanner.Error. -func (p *parser) Error(pos token.Position, msg string) { - // Don't collect errors that are on the same line as the previous error - // in the hope to reduce the number of spurious errors due to incorrect - // parser synchronization. - if p.errors.Len() == 0 || p.errors.Last().(*Error).Pos.Line != pos.Line { - p.errors.Push(&Error{pos, msg}); - } -} - - -func (p *parser) next() { - p.pos, p.tok, p.lit = p.scanner.Scan(); -} - - -func (p *parser) error_expected(pos token.Position, msg string) { - msg = "expected " + msg; - if pos.Offset == p.pos.Offset { - // the error happened at the current position; - // make the error message more specific - msg += ", found '" + p.tok.String() + "'"; - if p.tok.IsLiteral() { - msg += " " + string(p.lit); - } - } - p.Error(pos, msg); -} - - -func (p *parser) expect(tok token.Token) token.Position { - pos := p.pos; - if p.tok != tok { - p.error_expected(pos, "'" + tok.String() + "'"); - } - p.next(); // make progress in any case - return pos; -} - - -func (p *parser) parseIdentifier() string { - name := string(p.lit); - p.expect(token.IDENT); - return name; -} - - -func (p *parser) parseTypeName() (string, bool) { - pos := p.pos; - name, is_ident := p.parseIdentifier(), true; - if p.tok == token.PERIOD { - // got a package name, lookup package - if import_path, found := p.packs[name]; found { - name = import_path; - } else { - p.Error(pos, "package not declared: " + name); - } - p.next(); - name, is_ident = name + "." + p.parseIdentifier(), false; - } - return name, is_ident; -} - - -// Parses a rule name and returns it. If the rule name is -// a package-qualified type name, the package name is resolved. -// The 2nd result value is true iff the rule name consists of a -// single identifier only (and thus could be a package name). -// -func (p *parser) parseRuleName() (string, bool) { - name, is_ident := "", false; - switch p.tok { - case token.IDENT: - name, is_ident = p.parseTypeName(); - case token.DEFAULT: - name = "default"; - p.next(); - case token.QUO: - name = "/"; - p.next(); - default: - p.error_expected(p.pos, "rule name"); - p.next(); // make progress in any case - } - return name, is_ident; -} - - -func asLiteral(x interface{}) expr { - s := x.(string); - if len(s) > 0 && s[0] == '%' { - // literals containing format characters are represented as strings - return s; - } - // all other literals are represented as []byte for faster writing - return io.StringBytes(s); -} - - -func (p *parser) parseLiteral() expr { - if p.tok != token.STRING { - p.expect(token.STRING); - return ""; - } - - s, err := strconv.Unquote(string(p.lit)); - if err != nil { - panic("scanner error"); - } - p.next(); - - // A string literal may contain newline characters and %-format specifiers. - // To simplify and speed up printing of the literal, split it into segments - // that start with "\n" or "%" (but noy "%%"), possibly followed by a last - // segment that starts with some other character. If there is more than one - // such segment, return a sequence of "simple" literals, otherwise just - // return the string. - - // split string - var list vector.Vector; - list.Init(0); - i0 := 0; - for i := 0; i < len(s); i++ { - switch s[i] { - case '\n': - // next segment starts with '\n' - case '%': - if i+1 >= len(s) || s[i+1] == '%' { - i++; - continue; // "%%" is not a format-% - } - // next segment starts with '%' - default: - // all other cases do not split the string - continue; - } - // split off the current segment - if i0 < i { - list.Push(s[i0 : i]); - i0 = i; - } - } - // the final segment may start with any character - // (it is empty iff the string is empty) - list.Push(s[i0 : len(s)]); - - // no need for a sequence there is only one segment - if list.Len() == 1 { - return asLiteral(list.At(0)); - } - - // convert list into a sequence - seq := make(sequence, list.Len()); - for i := 0; i < list.Len(); i++ { - seq[i] = asLiteral(list.At(i)); - } - return seq; -} - - -func (p *parser) parseField() expr { - var fname string; - switch p.tok { - case token.XOR: - fname = "^"; - p.next(); - case token.MUL: - fname = "*"; - p.next(); - case token.IDENT: - // TODO(gri) could use reflect.ExpandType() to lookup a field - // at parse-time - would provide "compile-time" errors and - // faster printing. - fname = p.parseIdentifier(); - default: - return nil; - } - - var rule_name string; - if p.tok == token.COLON { - p.next(); - var _ bool; - rule_name, _ = p.parseRuleName(); - } - - return &field{fname, rule_name}; -} - - -func (p *parser) parseExpression() expr - -func (p *parser) parseOperand() (x expr) { - switch p.tok { - case token.STRING: - x = p.parseLiteral(); - - case token.SHR: - p.next(); - x = &indentation{p.parseOperand(), p.parseExpression()}; - p.expect(token.SHL); - - case token.LPAREN: - p.next(); - x = p.parseExpression(); - p.expect(token.RPAREN); - - case token.LBRACK: - p.next(); - x = &option{p.parseExpression()}; - p.expect(token.RBRACK); - - case token.LBRACE: - p.next(); - x = p.parseExpression(); - var div expr; - if p.tok == token.QUO { - p.next(); - div = p.parseExpression(); - } - x = &repetition{x, div}; - p.expect(token.RBRACE); - - default: - x = p.parseField(); // may be nil - } - - return x; -} - - -func (p *parser) parseSequence() expr { - var list vector.Vector; - list.Init(0); - - for x := p.parseOperand(); x != nil; x = p.parseOperand() { - list.Push(x); - } - - // no need for a sequence if list.Len() < 2 - switch list.Len() { - case 0: return nil; - case 1: return list.At(0).(expr); - } - - // convert list into a sequence - seq := make(sequence, list.Len()); - for i := 0; i < list.Len(); i++ { - seq[i] = list.At(i).(expr); - } - return seq; -} - - -func (p *parser) parseExpression() expr { - var list vector.Vector; - list.Init(0); - - for { - x := p.parseSequence(); - if x != nil { - list.Push(x); - } - if p.tok != token.OR { - break; - } - p.next(); - } - - // no need for an alternatives if list.Len() < 2 - switch list.Len() { - case 0: return nil; - case 1: return list.At(0).(expr); - } - - // convert list into a alternatives - alt := make(alternatives, list.Len()); - for i := 0; i < list.Len(); i++ { - alt[i] = list.At(i).(expr); - } - return alt; -} - - -func (p *parser) parseFormat() { - for p.tok != token.EOF { - pos := p.pos; - - name, is_ident := p.parseRuleName(); - switch p.tok { - case token.STRING: - // package declaration - import_path, err := strconv.Unquote(string(p.lit)); - if err != nil { - panic("scanner error"); - } - p.next(); - - // add package declaration - if !is_ident { - p.Error(pos, "illegal package name: " + name); - } else if _, found := p.packs[name]; !found { - p.packs[name] = import_path; - } else { - p.Error(pos, "package already declared: " + name); - } - - case token.ASSIGN: - // format rule - p.next(); - x := p.parseExpression(); - - // add rule - if _, found := p.rules[name]; !found { - p.rules[name] = x; - } else { - p.Error(pos, "format rule already declared: " + name); - } - - default: - p.error_expected(p.pos, "package declaration or format rule"); - p.next(); // make progress in any case - } - - if p.tok == token.SEMICOLON { - p.next(); - } else { - break; - } - } - p.expect(token.EOF); -} - - -func (p *parser) remap(pos token.Position, name string) string { - i := strings.Index(name, "."); - if i >= 0 { - package_name := name[0 : i]; - type_name := name[i : len(name)]; - // lookup package - if import_path, found := p.packs[package_name]; found { - name = import_path + "." + type_name; - } else { - p.Error(pos, "package not declared: " + package_name); - } - } - return name; -} - - -// Parse parses a set of format productions from source src. If there are no -// errors, the result is a Format and the error is nil. Otherwise the format -// is nil and a non-empty ErrorList is returned. -// -func Parse(src []byte, fmap FormatterMap) (Format, os.Error) { - // parse source - var p parser; - p.errors.Init(0); - p.scanner.Init(src, &p, false); - p.next(); - p.packs = make(map [string] string); - p.rules = make(Format); - p.parseFormat(); - - // add custom formatters, if any - var invalidPos token.Position; - for name, form := range fmap { - name = p.remap(invalidPos, name); - if t, found := p.rules[name]; !found { - p.rules[name] = &custom{name, form}; - } else { - var invalidPos token.Position; - p.Error(invalidPos, "formatter already declared: " + name); - } - } - - // convert errors list, if any - if p.errors.Len() > 0 { - errors := make(ErrorList, p.errors.Len()); - for i := 0; i < p.errors.Len(); i++ { - errors[i] = p.errors.At(i).(*Error); - } - return nil, errors; - } - - return p.rules, nil; -} - - -// ---------------------------------------------------------------------------- -// Formatting - -// The current formatting state. -type state struct { - f Format; // the format used - env interface{}; // the user-supplied environment, simply passed through - def expr; // the default rule, if any - div expr; // the global divider rule, if any - writediv bool; // true if the divider needs to be written - errors chan os.Error; // not chan *Error: errors <- nil would be wrong! - indent io.ByteBuffer; // the current indentation -} - - -func (ps *state) init(f Format, env interface{}, errors chan os.Error) { - ps.f = f; - ps.env = env; - // if we have a default ("default") rule, cache it for fast access - if def, has_def := f["default"]; has_def { - ps.def = def; - } - // if we have a divider ("/") rule, cache it for fast access - if div, has_div := f["/"]; has_div { - ps.div = div; - } - ps.errors = errors; -} - - -func (ps *state) error(msg string) { - ps.errors <- os.NewError(msg); - runtime.Goexit(); -} - - -// Get a field value given a field name. Returns the field value and -// the "embedding level" at which it was found. The embedding level -// is 0 for top-level fields in a struct. -// -func getField(val reflect.Value, fieldname string) (reflect.Value, int) { - // do we have a struct in the first place? - if val.Kind() != reflect.StructKind { - return nil, 0; - } - - sval, styp := val.(reflect.StructValue), val.Type().(reflect.StructType); - - // look for field at the top level - for i := 0; i < styp.Len(); i++ { - name, typ, tag, offset := styp.Field(i); - if name == fieldname || name == "" && strings.HasSuffix(typ.Name(), "." + fieldname) /* anonymous field */ { - return sval.Field(i), 0; - } - } - - // look for field in anonymous fields - var field reflect.Value; - level := 1000; // infinity (no struct has that many levels) - for i := 0; i < styp.Len(); i++ { - name, typ, tag, offset := styp.Field(i); - if name == "" { - f, l := getField(sval.Field(i), fieldname); - // keep the most shallow field - if f != nil && l < level { - field, level = f, l; - } - } - } - - return field, level + 1; -} - - -var default_names = map[int]string { - reflect.ArrayKind: "array", - reflect.BoolKind: "bool", - reflect.ChanKind: "chan", - reflect.DotDotDotKind: "ellipsis", - reflect.FloatKind: "float", - reflect.Float32Kind: "float32", - reflect.Float64Kind: "float64", - reflect.FuncKind: "func", - reflect.IntKind: "int", - reflect.Int16Kind: "int16", - reflect.Int32Kind: "int32", - reflect.Int64Kind: "int64", - reflect.Int8Kind: "int8", - reflect.InterfaceKind: "interface", - reflect.MapKind: "map", - reflect.PtrKind: "pointer", - reflect.StringKind: "string", - reflect.StructKind: "struct", - reflect.UintKind: "uint", - reflect.Uint16Kind: "uint16", - reflect.Uint32Kind: "uint32", - reflect.Uint64Kind: "uint64", - reflect.Uint8Kind: "uint8", - reflect.UintptrKind: "uintptr", -} - - -func typename(value reflect.Value) string { - name := value.Type().Name(); - if name == "" { - if default_name, found := default_names[value.Kind()]; found { - name = default_name; - } - } - return name; -} - - -func (ps *state) getFormat(name string) expr { - if fexpr, found := ps.f[name]; found { - return fexpr; - } - - if ps.def != nil { - return ps.def; - } - - ps.error(fmt.Sprintf("no production for type: '%s'\n", name)); - return nil; -} - - -func (ps *state) printf(w io.Writer, fexpr expr, value reflect.Value, index int) bool - - -func (ps *state) printDiv(w io.Writer, value reflect.Value) { - if ps.div != nil && ps.writediv { - div := ps.div; - ps.div = nil; - ps.printf(w, div, value, 0); - ps.div = div; - } - ps.writediv = true; -} - - -func (ps *state) writeIndented(w io.Writer, s []byte) { - // write indent after each '\n' - i0 := 0; - for i := 0; i < len(s); i++ { - if s[i] == '\n' { - w.Write(s[i0 : i+1]); - w.Write(ps.indent.Data()); - i0 = i+1; - } - } - w.Write(s[i0 : len(s)]); -} - - -// TODO complete this comment -// Returns true if a non-empty field value was found. -func (ps *state) printf(w io.Writer, fexpr expr, value reflect.Value, index int) bool { - if fexpr == nil { - return true; - } - - switch t := fexpr.(type) { - case alternatives: - // - write first non-empty alternative - // - result is not empty iff there is an non-empty alternative - for _, x := range t { - var buf io.ByteBuffer; - if ps.printf(&buf, x, value, 0) { - w.Write(buf.Data()); - return true; - } - } - return false; - - case sequence: - // - write every element of the sequence - // - result is not empty iff no element was empty - b := true; - for _, x := range t { - b = ps.printf(w, x, value, index) && b; - } - return b; - - case []byte: - // write literal, may start with "\n" - ps.printDiv(w, value); - if len(t) > 0 && t[0] == '\n' && ps.indent.Len() > 0 { - // newline must be followed by indentation - w.Write([]byte{'\n'}); - w.Write(ps.indent.Data()); - t = t[1 : len(t)]; - } - w.Write(t); - return true; - - case string: - // write format literal with value, starts with "%" (but not "%%") - ps.printDiv(w, value); - fmt.Fprintf(w, t, value.Interface()); - return true; - - case *field: - // - write the contents of the field - // - format is either the field format or the type-specific format - // - result is not empty iff the field is not empty - switch t.field_name { - case "^": - // identity - value doesn't change - - case "*": - // indirect - switch v := value.(type) { - case reflect.ArrayValue: - if v.Len() <= index { - return false; - } - value = v.Elem(index); - - case reflect.MapValue: - ps.error("reflection support for maps incomplete\n"); - - case reflect.PtrValue: - if v.Get() == nil { - return false; - } - value = v.Sub(); - - case reflect.InterfaceValue: - if v.Get() == nil { - return false; - } - value = v.Value(); - - default: - ps.error(fmt.Sprintf("error: * does not apply to `%s`\n", value.Type().Name())); - } - - default: - // field - field, _ := getField(value, t.field_name); - if field == nil { - ps.error(fmt.Sprintf("error: no field `%s` in `%s`\n", t.field_name, value.Type().Name())); - } - value = field; - } - - // field-specific rule name - rule_name := t.rule_name; - if rule_name == "" { - rule_name = typename(value) - } - fexpr = ps.getFormat(rule_name); - - return ps.printf(w, fexpr, value, index); - - case *indentation: - // - write the body within the given indentation - // - the result is not empty iff the body is not empty - saved_len := ps.indent.Len(); - ps.printf(&ps.indent, t.indent, value, index); // add additional indentation - b := ps.printf(w, t.body, value, index); - ps.indent.Truncate(saved_len); // reset indentation - return b; - - case *option: - // - write body if it is not empty - // - the result is always not empty - var buf io.ByteBuffer; - if ps.printf(&buf, t.body, value, 0) { - w.Write(buf.Data()); - } - return true; - - case *repetition: - // - write body until as long as it is not empty - // - the result is always not empty - var buf io.ByteBuffer; - for i := 0; ps.printf(&buf, t.body, value, i); i++ { - if i > 0 { - ps.printf(w, t.div, value, i); - } - w.Write(buf.Data()); - buf.Reset(); - } - return true; - - case *custom: - // - invoke custom formatter - var buf io.ByteBuffer; - if t.form(&buf, ps.env, value.Interface(), t.rule_name) { - ps.writeIndented(w, buf.Data()); - return true; - } - return false; - } - - panic("unreachable"); - return false; -} - - -// Sandbox to wrap a writer. -// Counts total number of bytes written and handles write errors. -// -type sandbox struct { - writer io.Writer; - written int; - errors chan os.Error; -} - - -// Write data to the sandboxed writer. If an error occurs, Write -// doesn't return. Instead it reports the error to the errors -// channel and exits the current goroutine. -// -func (s *sandbox) Write(data []byte) (int, os.Error) { - n, err := s.writer.Write(data); - s.written += n; - if err != nil { - s.errors <- err; - runtime.Goexit(); - } - return n, nil; -} - - -// Fprint formats each argument according to the format f -// and writes to w. The result is the total number of bytes -// written and an os.Error, if any. -// -func (f Format) Fprint(w io.Writer, env interface{}, args ...) (int, os.Error) { - errors := make(chan os.Error); - sw := sandbox{w, 0, errors}; - - var ps state; - ps.init(f, env, errors); - - go func() { - value := reflect.NewValue(args).(reflect.StructValue); - for i := 0; i < value.Len(); i++ { - fld := value.Field(i); - ps.printf(&sw, ps.getFormat(typename(fld)), fld, 0); - } - errors <- nil; // no errors - }(); - - return sw.written, <-errors; -} - - -// Print formats each argument according to the format f -// and writes to standard output. The result is the total -// number of bytes written and an os.Error, if any. -// -func (f Format) Print(args ...) (int, os.Error) { - return f.Fprint(os.Stdout, nil, args); -} - - -// Sprint formats each argument according to the format f -// and returns the resulting string. If an error occurs -// during formatting, the result contains the respective -// error message at the end. -// -func (f Format) Sprint(args ...) string { - var buf io.ByteBuffer; - n, err := f.Fprint(&buf, nil, args); - if err != nil { - fmt.Fprintf(&buf, "--- Sprint(%v) failed: %v", args, err); - } - return string(buf.Data()); -} diff --git a/usr/gri/pretty/format_test.go b/usr/gri/pretty/format_test.go deleted file mode 100644 index 2add36f09b..0000000000 --- a/usr/gri/pretty/format_test.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package format - -import ( - "format"; - "io"; - "testing"; -) - - -func check(t *testing.T, form, expected string, args ...) { - f, err := format.Parse(io.StringBytes(form), nil); - if err != nil { - panic(form + ": " + err.String()); - } - result := f.Sprint(args); - if result != expected { - t.Errorf( - "format : %s\nresult : `%s`\nexpected: `%s`\n\n", - form, result, expected - ) - } -} - - -// ---------------------------------------------------------------------------- -// Syntax - -func TestA(t *testing.T) { - // TODO fill this in -} - - -// ---------------------------------------------------------------------------- -// - formatting of basic types - -func Test0(t *testing.T) { - check(t, `bool = "%v"`, "false", false); - check(t, `int = "%b %d %o 0x%x"`, "101010 42 52 0x2a", 42); -} - - -// ---------------------------------------------------------------------------- -// - formatting of a struct - -type T1 struct { - a int; -} - -const F1 = - `format "format";` - `int = "%d";` - `format.T1 = "<" a ">";` - -func Test1(t *testing.T) { - check(t, F1, "<42>", T1{42}); -} - - -// ---------------------------------------------------------------------------- -// - formatting of a struct with an optional field (pointer) - -type T2 struct { - s string; - p *T1; -} - -const F2a = - F1 + - `string = "%s";` - `pointer = *;` - `format.T2 = s ["-" p "-"];` - -const F2b = - F1 + - `string = "%s";` - `pointer = *;` - `format.T2 = s ("-" p "-" | "empty");`; - -func Test2(t *testing.T) { - check(t, F2a, "foo", T2{"foo", nil}); - check(t, F2a, "bar-<17>-", T2{"bar", &T1{17}}); - check(t, F2b, "fooempty", T2{"foo", nil}); -} - - -// ---------------------------------------------------------------------------- -// - formatting of a struct with a repetitive field (slice) - -type T3 struct { - s string; - a []int; -} - -const F3a = - `format "format";` - `default = "%v";` - `array = *;` - `format.T3 = s {" " a a / ","};` - -const F3b = - `format "format";` - `int = "%d";` - `string = "%s";` - `array = *;` - `nil = ;` - `empty = *:nil;` - `format.T3 = s [a:empty ": " {a / "-"}]` - -func Test3(t *testing.T) { - check(t, F3a, "foo", T3{"foo", nil}); - check(t, F3a, "foo 00, 11, 22", T3{"foo", []int{0, 1, 2}}); - check(t, F3b, "bar", T3{"bar", nil}); - check(t, F3b, "bal: 2-3-5", T3{"bal", []int{2, 3, 5}}); -} - - -// ---------------------------------------------------------------------------- -// - formatting of a struct with alternative field - -type T4 struct { - x *int; - a []int; -} - -const F4a = - `format "format";` - `int = "%d";` - `pointer = *;` - `array = *;` - `nil = ;` - `empty = *:nil;` - `format.T4 = "<" (x:empty x | "-") ">" ` - -const F4b = - `format "format";` - `int = "%d";` - `pointer = *;` - `array = *;` - `nil = ;` - `empty = *:nil;` - `format.T4 = "<" (a:empty {a / ", "} | "-") ">" ` - -func Test4(t *testing.T) { - x := 7; - check(t, F4a, "<->", T4{nil, nil}); - check(t, F4a, "<7>", T4{&x, nil}); - check(t, F4b, "<->", T4{nil, nil}); - check(t, F4b, "<2, 3, 7>", T4{nil, []int{2, 3, 7}}); -} diff --git a/usr/gri/pretty/pretty.go b/usr/gri/pretty/pretty.go index dfad300510..9916f5babb 100644 --- a/usr/gri/pretty/pretty.go +++ b/usr/gri/pretty/pretty.go @@ -70,31 +70,67 @@ func makeTabwriter(writer io.Writer) *tabwriter.Writer { } -func isValidPos(w io.Writer, env, value interface{}, name string) bool { +func isValidPos(state *format.State, value interface{}, rule_name string) bool { pos := value.(token.Position); return pos.IsValid(); } -func isSend(w io.Writer, env, value interface{}, name string) bool { +func isSend(state *format.State, value interface{}, rule_name string) bool { return value.(ast.ChanDir) & ast.SEND != 0; } -func isRecv(w io.Writer, env, value interface{}, name string) bool { +func isRecv(state *format.State, value interface{}, rule_name string) bool { return value.(ast.ChanDir) & ast.RECV != 0; } -func isMultiLineComment(w io.Writer, env, value interface{}, name string) bool { - return value.([]byte)[1] == '*' + +func isMultiLineComment(state *format.State, value interface{}, rule_name string) bool { + return value.([]byte)[1] == '*'; +} + + +type environment struct { + optSemi *bool; +} + + +func (e environment) Copy() format.Environment { + optSemi := *e.optSemi; + return environment{&optSemi}; +} + + +func clearOptSemi(state *format.State, value interface{}, rule_name string) bool { + *state.Env().(environment).optSemi = false; + return true; } -var fmap = format.FormatterMap{ +func setOptSemi(state *format.State, value interface{}, rule_name string) bool { + *state.Env().(environment).optSemi = true; + return true; +} + + +func optSemi(state *format.State, value interface{}, rule_name string) bool { + if !*state.Env().(environment).optSemi { + state.Write([]byte{';'}); + } + return true; +} + + +var fmap = format.FormatterMap { "isValidPos": isValidPos, "isSend": isSend, "isRecv": isRecv, "isMultiLineComment": isMultiLineComment, + "/": clearOptSemi, + "clearOptSemi": clearOptSemi, + "setOptSemi": setOptSemi, + "optSemi": optSemi, } @@ -120,7 +156,7 @@ func main() { } ast_format, err := format.Parse(src, fmap); if err != nil { - fmt.Fprintf(os.Stderr, "%s:%v\n", ast_txt, err); + fmt.Fprintf(os.Stderr, "%s: %v\n", ast_txt, err); os.Exit(1); } @@ -153,10 +189,10 @@ func main() { if !*silent { tw := makeTabwriter(os.Stdout); if *formatter { - var optSemi bool; // formatting environment - _, err := ast_format.Fprint(tw, &optSemi, prog); + env := environment{new(bool)}; + _, err := ast_format.Fprint(tw, env, prog); if err != nil { - fmt.Fprintf(os.Stderr, "format error$$: %s", err); + fmt.Fprintf(os.Stderr, "format error: %v\n", err); exitcode = 1; continue; // proceed with next file } |
