aboutsummaryrefslogtreecommitdiff
path: root/usr/gri
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2009-06-01 19:13:44 -0700
committerRobert Griesemer <gri@golang.org>2009-06-01 19:13:44 -0700
commitd7acfc75cd5012829c01ff2dbe0aaf71961c6c2b (patch)
treeb3d675756909a84c0098851a592cb7f4b376596e /usr/gri
parent2494bcb4b1914c17dce8491cf2244fd0fd63115b (diff)
downloadgo-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/Makefile2
-rw-r--r--usr/gri/pretty/ast.txt60
-rw-r--r--usr/gri/pretty/format.go967
-rw-r--r--usr/gri/pretty/format_test.go153
-rw-r--r--usr/gri/pretty/pretty.go56
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
}