]> Cypherpunks repositories - gostls13.git/commitdiff
weekly snapshot:
authorRobert Griesemer <gri@golang.org>
Sat, 16 May 2009 01:52:59 +0000 (18:52 -0700)
committerRobert Griesemer <gri@golang.org>
Sat, 16 May 2009 01:52:59 +0000 (18:52 -0700)
format.go:
- better error handling, indentation, support for defaults,
  environments for custom formatters, cleanups (more functionality, less code)

pretty.go:
- better comment printing using format.go

made test script more robust

TBR=r
DELTA=622  (175 added, 305 deleted, 142 changed)
OCL=28956
CL=28956

usr/gri/pretty/ast.txt
usr/gri/pretty/format.go
usr/gri/pretty/format_test.go
usr/gri/pretty/pretty.go
usr/gri/pretty/symboltable.go
usr/gri/pretty/test.sh

index 7bf8d7131edabe896290fceacf99b0f5d34220d9..6354a9ef0fe04905c4b6770c047cf166391b9698 100644 (file)
@@ -49,7 +49,7 @@ ast.Decl =
 // Comments
 
 ast.Comment =
-       Text:string "\n";
+       Text:string [Text:isMultiLineComment "\n"];
 
 ast.Comments =
        {*};
@@ -305,9 +305,13 @@ ast.BadDecl =
 ast.GenDecl =
        Doc
        Tok " "
-       [Lparen:isValidPos "(" >> "\t" "\n"]
-       {Specs / ";\n"}
-       [Rparen:isValidPos << "\n" ")"];
+       (       Lparen:isValidPos
+               >> "\t" "(\n"
+               {Specs / ";\n"}
+               <<
+               "\n)"
+       |       {Specs / ";\n"}
+       );
 
 ast.FuncDecl =
        "func " ["(" Recv ") "] Name Type:funcSignature
index 895cec276c05ef47678765a1e1bf3cdf428f28c6..238d5ca24d01105d2ef3b7347561d294f0edb38d 100644 (file)
@@ -28,23 +28,17 @@ import (
        "io";
        "os";
        "reflect";
+       "runtime";
        "strconv";
        "strings";
 )
 
 
-// TODO should probably do this in a different way
-var (
-       debug = flag.Bool("d", false, "debug mode");
-       trace = flag.Bool("t", false, "trace mode");
-)
-
-
 // ----------------------------------------------------------------------------
 // Format representation
 
 type (
-       Formatter func(w io.Writer, value interface{}, name string) bool;
+       Formatter func(w io.Writer, env, value interface{}, name string) bool;
        FormatterMap map[string]Formatter;
 )
 
@@ -52,43 +46,35 @@ type (
 // A production expression is built from the following nodes.
 //
 type (
-       expr interface {
-               String() string;
-       };
+       expr interface {};
 
        alternative struct {
-               x, y expr;
+               x, y expr;  // x | y
        };
 
        sequence struct {
-               x, y expr;
+               x, y expr;  // x y
        };
 
        literal struct {
-               // TODO should there be other types or should it all be string literals?
                value []byte;
        };
 
-       indentation struct {
-               iexpr expr;  // outdent if nil
-       };
-
        field struct {
                fname string;  // including "^", "*"
                tname string;  // "" if no tname specified
        };
 
-       negation struct {
-               neg expr;
+       indentation struct {
+               indent, body expr;  // >> indent body <<
        };
 
        option struct {
-               opt expr;
+               body expr;  // [body]
        };
 
        repetition struct {
-               rep expr;
-               div expr;
+               body, div expr;  // {body / div}
        };
 
        custom struct {
@@ -98,60 +84,6 @@ type (
 )
 
 
-func (x *alternative) String() string {
-       return fmt.Sprintf("(%v | %v)", x.x, x.y);
-}
-
-
-func (x *sequence) String() string {
-       return fmt.Sprintf("%v %v", x.x, x.y);
-}
-
-
-func (x *literal) String() string {
-       return strconv.Quote(string(x.value));
-}
-
-
-func (x *indentation) String() string {
-       if x.iexpr != nil {
-               fmt.Sprintf(">> %s", x.iexpr);
-       }
-       return "<<";
-}
-
-
-func (x *field) String() string {
-       if x.tname == "" {
-               return x.fname;
-       }
-       return x.fname + " : " + x.tname;
-}
-
-
-func (x *negation) String() string {
-       return fmt.Sprintf("!%v", x.neg);
-}
-
-
-func (x *option) String() string {
-       return fmt.Sprintf("[%v]", x.opt);
-}
-
-
-func (x *repetition) String() string {
-       if x.div == nil {
-               return fmt.Sprintf("{%v}", x.rep);
-       }
-       return fmt.Sprintf("{%v / %v}", x.rep, x.div);
-}
-
-
-func (x *custom) String() string {
-       return "<" + x.name + ">";
-}
-
-
 /*     A Format is a set of production expressions. A new format is
        created explicitly by calling Parse, or implicitly by one of
        the Xprintf functions.
@@ -159,17 +91,16 @@ func (x *custom) String() string {
        Formatting rules are specified in the following syntax:
 
                Format      = Production { ";" Production } [ ";" ] .
-               Production  = Name "=" Expression .
+               Production  = ( Name | "default" | "/" ) "=" Expression .
                Name        = identifier { "." identifier } .
                Expression  = [ Term ] { "|" [ Term ] } .
                Term        = Factor { Factor } .
-               Factor      = string_literal | Indent | Field | Negation | Group | Option | Repetition .
-               Indent      = ">>" Factor | "<<" .
+               Factor      = string_literal | Indent | Field | Group | Option | Repetition .
                Field       = ( "^" | "*" | Name ) [ ":" Name ] .
-               Negation    = "!" Factor .
+               Indent      = ">>" Factor Expression "<<" .
                Group       = "(" Expression ")" .
                Option      = "[" Expression "]" .
-               Repetition  = "{" Expression "}" .
+               Repetition  = "{" Expression [ "/" Expression ] "}" .
 
        The syntax of white space, comments, identifiers, and string literals is
        the same as in Go.
@@ -198,17 +129,46 @@ func (x *custom) String() string {
 
        A field may contain a format specifier of the form
 
-               : Expression
-
-       which specifies the field format irrespective of the field type.
+               : Name
 
-       Default formats are used for types without specific formating rules:
-       The "%v" format is used for values of all types expect pointer, array,
-       map, and interface types. They are using the "^" designator.
+       which specifies the field format rule irrespective of the field type.
 
        TODO complete this description
 */
-type Format map [string] expr;
+type Format struct {
+       // TODO(gri) Eventually have import path info here
+       //           once reflect provides import paths.
+       rules map [string] expr;
+}
+
+
+
+// ----------------------------------------------------------------------------
+// Error handling
+
+// Error implements an os.Error that may be returned as a
+// result of calling Parse or any of the print functions.
+//
+type Error struct {
+       Pos token.Position;  // source position, if any (otherwise Pos.Line == 0)
+       Msg string;  // error message
+       Next *Error;  // next error, if any (or nil)
+}
+
+
+// String converts a list of Error messages into a string,
+// with one error per line.
+//
+func (e *Error) String() string {
+       var buf io.ByteBuffer;
+       for ; e != nil; e = e.Next {
+               if e.Pos.Line > 0 {
+                       fmt.Fprintf(&buf, "%d:%d: ", e.Pos.Line, e.Pos.Column);
+               }
+               fmt.Fprintf(&buf, "%s\n", e.Msg);
+       }
+       return string(buf.Data());
+}
 
 
 // ----------------------------------------------------------------------------
@@ -226,20 +186,24 @@ type parser struct {
        tok token.Token;  // one token look-ahead
        lit []byte;  // token literal
 
-       // error handling
-       errors io.ByteBuffer;  // errors.Len() > 0 if there were errors
-       lastline int;
+       // errors
+       first, last *Error;
 }
 
 
 // The parser implements the scanner.ErrorHandler interface.
 func (p *parser) Error(pos token.Position, msg string) {
-       if pos.Line != p.lastline {
+       if p.last == nil || p.last.Pos.Line != pos.Line {
                // only report error if not on the same line as previous error
                // in the hope to reduce number of follow-up errors reported
-               fmt.Fprintf(&p.errors, "%d:%d: %s\n", pos.Line, pos.Column, msg);
+               err := &Error{pos, msg, nil};
+               if p.last == nil {
+                       p.first = err;
+               } else {
+                       p.last.Next = err;
+               }
+               p.last = err;
        }
-       p.lastline = pos.Line;
 }
 
 
@@ -299,7 +263,7 @@ func (p *parser) parseValue() []byte {
        //      (change value to string?)
        s, err := strconv.Unquote(string(p.lit));
        if err != nil {
-               panic("scanner error?");
+               panic("scanner error");
        }
 
        p.next();
@@ -308,7 +272,7 @@ func (p *parser) parseValue() []byte {
 
 
 func (p *parser) parseFactor() (x expr)
-func (p *parser) parseExpr() expr
+func (p *parser) parseExpression() expr
 
 func (p *parser) parseField() expr {
        var fname string;
@@ -344,33 +308,28 @@ func (p *parser) parseFactor() (x expr) {
 
        case token.SHR:
                p.next();
-               x = &indentation{p.parseFactor()};
-
-       case token.SHL:
-               p.next();
-               x = &indentation{nil};
-
-       case token.NOT:
-               p.next();
-               x = &negation{p.parseFactor()};
+               iexpr := p.parseFactor();
+               body := p.parseExpression();
+               p.expect(token.SHL);
+               return &indentation{iexpr, body};
 
        case token.LPAREN:
                p.next();
-               x = p.parseExpr();
+               x = p.parseExpression();
                p.expect(token.RPAREN);
 
        case token.LBRACK:
                p.next();
-               x = &option{p.parseExpr()};
+               x = &option{p.parseExpression()};
                p.expect(token.RBRACK);
 
        case token.LBRACE:
                p.next();
-               x = p.parseExpr();
+               x = p.parseExpression();
                var div expr;
                if p.tok == token.QUO {
                        p.next();
-                       div = p.parseExpr();
+                       div = p.parseExpression();
                }
                x = &repetition{x, div};
                p.expect(token.RBRACE);
@@ -400,7 +359,7 @@ func (p *parser) parseTerm() expr {
 }
 
 
-func (p *parser) parseExpr() expr {
+func (p *parser) parseExpression() expr {
        x := p.parseTerm();
 
        for p.tok == token.OR {
@@ -413,24 +372,34 @@ func (p *parser) parseExpr() expr {
 }
 
 
-func (p *parser) parseProd() (string, expr) {
-       name := p.parseName();
+func (p *parser) parseProduction() (string, expr) {
+       var name string;
+       switch p.tok {
+       case token.DEFAULT:
+               p.next();
+               name = "default";
+       case token.QUO:
+               p.next();
+               name = "/";
+       default:
+               name = p.parseName();
+       }
        p.expect(token.ASSIGN);
-       x := p.parseExpr();
+       x := p.parseExpression();
        return name, x;
 }
 
 
-func (p *parser) parseFormat() Format {
-       format := make(Format);
+func (p *parser) parseFormat() *Format {
+       rules := make(map [string] expr);
 
        for p.tok != token.EOF {
                pos := p.pos;
-               name, x := p.parseProd();
+               name, x := p.parseProduction();
 
-               // add production to format
-               if t, found := format[name]; !found {
-                       format[name] = x;
+               // add production to rules
+               if t, found := rules[name]; !found {
+                       rules[name] = x;
                } else {
                        p.Error(pos, "production already declared: " + name);
                }
@@ -443,198 +412,153 @@ func (p *parser) parseFormat() Format {
        }
        p.expect(token.EOF);
 
-       return format;
+       return &Format{rules};
 }
 
 
-type formatError string
-
-func (p formatError) String() string {
-       return string(p);
-}
-
-
-func readSource(src interface{}) ([]byte, os.Error) {
-       if src == nil {
-               return nil, formatError("src is nil");
-       }
-
-       switch s := src.(type) {
-       case string:
-               return io.StringBytes(s), nil;
-
-       case []byte:
-               if s == nil {
-                       return nil, formatError("src is nil");
-               }
-               return s, nil;
-
-       case *io.ByteBuffer:
-               // is io.Read, but src is already available in []byte form
-               if s == nil {
-                       return nil, formatError("src is nil");
-               }
-               return s.Data(), nil;
-
-       case io.Reader:
-               var buf io.ByteBuffer;
-               n, err := io.Copy(s, &buf);
-               if err != nil {
-                       return nil, err;
-               }
-               return buf.Data(), nil
-       }
-
-       return nil, formatError("src type not supported");
-}
-
-
-// Parse parses a set of format productions. The format src may be
-// a string, a []byte, or implement io.Read. The result is a Format
-// if no errors occured; otherwise Parse returns nil.
+// 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 the os.Error string contains a line for each error encountered.
 //
-func Parse(src interface{}, fmap FormatterMap) (f Format, err os.Error) {
-       s, err := readSource(src);
-       if err != nil {
-               return nil, err;
-       }
-
-       // parse format description
+func Parse(src []byte, fmap FormatterMap) (*Format, os.Error) {
+       // parse source
        var p parser;
-       p.scanner.Init(s, &p, false);
+       p.scanner.Init(src, &p, false);
        p.next();
-       f = p.parseFormat();
+       f := p.parseFormat();
 
        // add custom formatters, if any
+       // TODO should we test that name is a legal name?
        for name, form := range fmap {
-               if t, found := f[name]; !found {
-                       f[name] = &custom{name, form};
+               if t, found := f.rules[name]; !found {
+                       f.rules[name] = &custom{name, form};
                } else {
-                       fmt.Fprintf(&p.errors, "formatter already declared: %s", name);
+                       p.Error(token.Position{0, 0, 0}, "formatter already declared: " + name);
                }
        }
 
-       if p.errors.Len() > 0 {
-               return nil, formatError(string(p.errors.Data()));
+       if p.first != nil {
+               return nil, p.first;
        }
 
        return f, nil;
 }
 
 
-func ParseOrDie(src interface{}, fmap FormatterMap) Format {
-       f, err := Parse(src, fmap);
-       if err != nil {
-               panic(err.String());
-       }
-       return f;
+// ----------------------------------------------------------------------------
+// Formatting
+
+type state struct {
+       f *Format;
+       env interface{};
+       sep expr;
+       errors chan os.Error;  // not chan *Error: errors <- nil would be wrong!
+       indent io.ByteBuffer;
 }
 
 
-func (f Format) Dump() {
-       for name, form := range f {
-               fmt.Printf("%s = %v;\n", name, form);
+func (ps *state) init(f *Format, env interface{}) {
+       ps.f = f;
+       ps.env = env;
+       // if we have a separator ("/") production, cache it for easy access
+       if sep, has_sep := f.rules["/"]; has_sep {
+               ps.sep = sep;
        }
+       ps.errors = make(chan os.Error);
 }
 
 
-// ----------------------------------------------------------------------------
-// Formatting
+func (ps *state) error(msg string) {
+       ps.errors <- &Error{token.Position{0, 0, 0}, msg, nil};
+       runtime.Goexit();
+}
+
+
+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;
+               }
+       }
 
-func getField(v reflect.StructValue, fieldname string) reflect.Value {
-       t := v.Type().(reflect.StructType);
-       for i := 0; i < t.Len(); i++ {
-               name, typ, tag, offset := t.Field(i);
-               if name == fieldname {
-                       return v.Field(i);
-               } else if name == "" {
-                       // anonymous field - check type name
-                       // TODO this is only going down one level - fix
-                       if strings.HasSuffix(typ.Name(), "." + fieldname) {
-                               return v.Field(i);
+       // look for field in anonymous fields
+       var field reflect.Value;
+       level := 1000;  // infinity
+       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;
                        }
                }
        }
-       panicln(fmt.Sprintf("no field %s int %s", fieldname, t.Name()));
-       return nil;
+       
+       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 != "" {
-               return name;
-       }
-
-       switch value.Kind() {
-       case reflect.ArrayKind: name = "array";
-       case reflect.BoolKind: name = "bool";
-       case reflect.ChanKind: name = "chan";
-       case reflect.DotDotDotKind: name = "ellipsis";
-       case reflect.FloatKind: name = "float";
-       case reflect.Float32Kind: name = "float32";
-       case reflect.Float64Kind: name = "float64";
-       case reflect.FuncKind: name = "func";
-       case reflect.IntKind: name = "int";
-       case reflect.Int16Kind: name = "int16";
-       case reflect.Int32Kind: name = "int32";
-       case reflect.Int64Kind: name = "int64";
-       case reflect.Int8Kind: name = "int8";
-       case reflect.InterfaceKind: name = "interface";
-       case reflect.MapKind: name = "map";
-       case reflect.PtrKind: name = "pointer";
-       case reflect.StringKind: name = "string";
-       case reflect.StructKind: name = "struct";
-       case reflect.UintKind: name = "uint";
-       case reflect.Uint16Kind: name = "uint16";
-       case reflect.Uint32Kind: name = "uint32";
-       case reflect.Uint64Kind: name = "uint64";
-       case reflect.Uint8Kind: name = "uint8";
-       case reflect.UintptrKind: name = "uintptr";
+       if name == "" {
+               if default_name, found := default_names[value.Kind()]; found {
+                       name = default_name;
+               }
        }
-
        return name;
 }
 
 
-var defaults = map [int] expr {
-       reflect.ArrayKind: &field{"*", ""},
-       reflect.DotDotDotKind: &field{"*", ""},
-       reflect.InterfaceKind: &field{"*", ""},
-       reflect.MapKind: &field{"*", ""},
-       reflect.PtrKind: &field{"*", ""},
-       reflect.StringKind: &literal{io.StringBytes("%s")},
-}
-
-var catchAll = &literal{io.StringBytes("%v")};
-
-func (f Format) getFormat(name string, value reflect.Value) expr {
-       /*
-       if name == "nil" {
-               fmt.Printf("value = %T %v, kind = %d\n", value, value, value.Kind());
-               panic();
-       }
-       */
-
-       if fexpr, found := f[name]; found {
+func (ps *state) getFormat(name string) expr {
+       if fexpr, found := ps.f.rules[name]; found {
                return fexpr;
        }
 
-       if *debug {
-               fmt.Printf("no production for type: %s\n", name);
-       }
-
-       // no fexpr found - return kind-specific default value, if any
-       if fexpr, found := defaults[value.Kind()]; found {
+       if fexpr, found := ps.f.rules["default"]; found {
                return fexpr;
        }
 
-       if *debug {
-               fmt.Printf("no default for type: %s\n", name);
-       }
-
-       return catchAll;
+       ps.error(fmt.Sprintf("no production for type: '%s'\n", name));
+       panic("unreachable");
+       return nil;
 }
 
 
@@ -654,7 +578,7 @@ func percentCount(s []byte) int {
 }
 
 
-func rawPrintf(w io.Writer, format []byte, value reflect.Value) {
+func (ps *state) rawPrintf(w io.Writer, format []byte, value reflect.Value) {
        // TODO find a better way to do this
        x := value.Interface();
        switch percentCount(format) {
@@ -668,69 +592,13 @@ func rawPrintf(w io.Writer, format []byte, value reflect.Value) {
 }
 
 
-// TODO this should become a Go built-in
-func push(dst []int, x int) []int {
-       n := len(dst);
-       if n > cap(dst) {
-               panic("dst too small");
-       }
-       dst = dst[0 : n+1];
-       dst[n] = x;
-       return dst;
-}
-
-
-func append(dst, src []byte) []byte {
-       n, m := len(dst), len(src);
-       if n+m > cap(dst) {
-               panic("dst too small");
-       }
-       dst = dst[0 : n+m];
-       for i := 0; i < m; i++ {
-               dst[n+i] = src[i];
-       }
-       return dst;
-}
-
-
-type state struct {
-       f Format;
-
-       // indentation
-       indent_text []byte;
-       indent_widths []int;
-}
-
-
-func (ps *state) init(f Format) {
-       ps.f = f;
-       ps.indent_text = make([]byte, 0, 1000);  // TODO don't use fixed cap
-       ps.indent_widths = make([]int, 0, 100);  // TODO don't use fixed cap
-}
-
-
-func (ps *state) indent(text []byte) {
-       ps.indent_widths = push(ps.indent_widths, len(ps.indent_text));
-       ps.indent_text = append(ps.indent_text, text);
-}
-
-
-func (ps *state) outdent() {
-       i := len(ps.indent_widths);
-       if i > 0 {
-               ps.indent_text = ps.indent_text[0 : ps.indent_widths[i-1]];
-               ps.indent_widths = ps.indent_widths[0 : i-1];
-       }
-}
-
-
 func (ps *state) printIndented(w io.Writer, s []byte) {
        // replace each '\n' with the indent + '\n'
        i0 := 0;
        for i := 0; i < len(s); i++ {
                if s[i] == '\n' {
                        w.Write(s[i0 : i+1]);
-                       w.Write(ps.indent_text);
+                       w.Write(ps.indent.Data());
                        i0 = i+1;
                }
        }
@@ -739,22 +607,20 @@ func (ps *state) printIndented(w io.Writer, s []byte) {
 
 
 func (ps *state) printf(w io.Writer, format []byte, value reflect.Value) {
-       if len(ps.indent_widths) == 0 {
+       if ps.indent.Len()== 0 {
                // no indentation
-               rawPrintf(w, format, value);
+               ps.rawPrintf(w, format, value);
        } else {
                // print into temporary buffer
                var buf io.ByteBuffer;
-               rawPrintf(&buf, format, value);
+               ps.rawPrintf(&buf, format, value);
                ps.printIndented(w, buf.Data());
        }
 }
 
 
-func (ps *state) print(w io.Writer, fexpr expr, value reflect.Value, index, level int) bool
-
 // Returns true if a non-empty field value was found.
-func (ps *state) print0(w io.Writer, fexpr expr, value reflect.Value, index, level int) bool {
+func (ps *state) print(w io.Writer, fexpr expr, value reflect.Value, index int) bool {
        if fexpr == nil {
                return true;
        }
@@ -764,12 +630,12 @@ func (ps *state) print0(w io.Writer, fexpr expr, value reflect.Value, index, lev
                // - print the contents of the first alternative with a non-empty field
                // - result is true if there is at least one non-empty field
                var buf io.ByteBuffer;
-               if ps.print(&buf, t.x, value, 0, level) {
+               if ps.print(&buf, t.x, value, 0) {
                        w.Write(buf.Data());
                        return true;
                } else {
                        var buf io.ByteBuffer;
-                       if ps.print(&buf, t.y, value, 0, level) {
+                       if ps.print(&buf, t.y, value, 0) {
                                w.Write(buf.Data());
                                return true;
                        }
@@ -780,9 +646,12 @@ func (ps *state) print0(w io.Writer, fexpr expr, value reflect.Value, index, lev
                // - print the contents of the sequence
                // - result is true if there is no empty field
                // TODO do we need to buffer here? why not?
-               b1 := ps.print(w, t.x, value, index, level);
-               b2 := ps.print(w, t.y, value, index, level);
-               return b1 && b2;
+               b := ps.print(w, t.x, value, index);
+               // TODO should invoke separator only inbetween terminal symbols?
+               if ps.sep != nil {
+                       b = ps.print(w, ps.sep, value, index) && b;
+               }
+               return ps.print(w, t.y, value, index) && b;
 
        case *literal:
                // - print the literal
@@ -790,19 +659,6 @@ func (ps *state) print0(w io.Writer, fexpr expr, value reflect.Value, index, lev
                ps.printf(w, t.value, value);
                return true;
 
-       case *indentation:
-               if t.iexpr != nil {
-                       // indent
-                       var buf io.ByteBuffer;
-                       ps.print(&buf, t.iexpr, value, index, level);
-                       ps.indent(buf.Data());
-
-               } else {
-                       // outdent
-                       ps.outdent();
-               }
-               return true;
-
        case *field:
                // - print the contents of the field
                // - format is either the field format or the type-specific format
@@ -822,7 +678,7 @@ func (ps *state) print0(w io.Writer, fexpr expr, value reflect.Value, index, lev
                                value = v.Elem(index);
 
                        case reflect.MapValue:
-                               panic("reflection support for maps incomplete");
+                               ps.error("reflection support for maps incomplete\n");
 
                        case reflect.PtrValue:
                                if v.Get() == nil {
@@ -838,17 +694,16 @@ func (ps *state) print0(w io.Writer, fexpr expr, value reflect.Value, index, lev
 
                        default:
                                // TODO fix this
-                               panic(fmt.Sprintf("error: * does not apply to `%s`\n", value.Type().Name()));
+                               ps.error(fmt.Sprintf("error: * does not apply to `%s`\n", value.Type().Name()));
                        }
 
                default:
                        // field
-                       if s, is_struct := value.(reflect.StructValue); is_struct {
-                               value = getField(s, t.fname);
-                       } else {
-                               // TODO fix this
-                               panic(fmt.Sprintf("error: %s has no field `%s`\n", value.Type().Name(), t.fname));
+                       field, _ := getField(value, t.fname);
+                       if field == nil {
+                               ps.error(fmt.Sprintf("error: no field `%s` in `%s`\n", t.fname, value.Type().Name()));
                        }
+                       value = field;
                }
 
                // determine format
@@ -856,23 +711,21 @@ func (ps *state) print0(w io.Writer, fexpr expr, value reflect.Value, index, lev
                if tname == "" {
                        tname = typename(value)
                }
-               fexpr = ps.f.getFormat(tname, value);
+               fexpr = ps.getFormat(tname);
 
-               return ps.print(w, fexpr, value, index, level);
+               return ps.print(w, fexpr, value, index);
 
-       case *negation:
-               // TODO is this operation useful at all?
-               // print the contents of the option if is contains an empty field
-               var buf io.ByteBuffer;
-               if !ps.print(&buf, t.neg, value, 0, level) {
-                       w.Write(buf.Data());
-               }
-               return true;
+       case *indentation:
+               saved_len := ps.indent.Len();
+               ps.print(&ps.indent, t.indent, value, index);  // add additional indentation
+               b := ps.print(w, t.body, value, index);
+               ps.indent.Truncate(saved_len);  // reset indentation
+               return b;
 
        case *option:
                // print the contents of the option if it contains a non-empty field
                var buf io.ByteBuffer;
-               if ps.print(&buf, t.opt, value, 0, level) {
+               if ps.print(&buf, t.body, value, 0) {
                        w.Write(buf.Data());
                }
                return true;
@@ -880,9 +733,9 @@ func (ps *state) print0(w io.Writer, fexpr expr, value reflect.Value, index, lev
        case *repetition:
                // print the contents of the repetition while there is a non-empty field
                var buf io.ByteBuffer;
-               for i := 0; ps.print(&buf, t.rep, value, i, level); i++ {
+               for i := 0; ps.print(&buf, t.body, value, i); i++ {
                        if i > 0 {
-                               ps.print(w, t.div, value, i, level);
+                               ps.print(w, t.div, value, i);
                        }
                        w.Write(buf.Data());
                        buf.Reset();
@@ -891,7 +744,7 @@ func (ps *state) print0(w io.Writer, fexpr expr, value reflect.Value, index, lev
 
        case *custom:
                var buf io.ByteBuffer;
-               if t.form(&buf, value.Interface(), t.name) {
+               if t.form(&buf, ps.env, value.Interface(), t.name) {
                        ps.printIndented(w, buf.Data());
                        return true;
                }
@@ -903,63 +756,41 @@ func (ps *state) print0(w io.Writer, fexpr expr, value reflect.Value, index, lev
 }
 
 
-func printTrace(indent int, format string, a ...) {
-       const dots =
-               ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "
-               ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ";
-       const n = len(dots);
-       i := 2*indent;
-       for ; i > n; i -= n {
-               fmt.Print(dots);
-       }
-       fmt.Print(dots[0 : i]);
-       fmt.Printf(format, a);
-}
-
-
-func (ps *state) print(w io.Writer, fexpr expr, value reflect.Value, index, level int) bool {
-       if *trace {
-               printTrace(level, "%v, %d {\n", fexpr, /*value.Interface(), */index);
-       }
-
-       result := ps.print0(w, fexpr, value, index, level+1);
-
-       if *trace {
-               printTrace(level, "} %v\n", result);
-       }
-       return result;
-}
-
-
-// TODO proper error reporting
-
 // Fprint formats each argument according to the format f
 // and writes to w.
 //
-func (f Format) Fprint(w io.Writer, args ...) {
-       value := reflect.NewValue(args).(reflect.StructValue);
-       for i := 0; i < value.Len(); i++ {
-               fld := value.Field(i);
-               var ps state;
-               ps.init(f);
-               ps.print(w, f.getFormat(typename(fld), fld), fld, 0, 0);
-       }
+func (f *Format) Fprint(w io.Writer, env interface{}, args ...) (int, os.Error) {
+       var ps state;
+       ps.init(f, env);
+
+       go func() {
+               value := reflect.NewValue(args).(reflect.StructValue);
+               for i := 0; i < value.Len(); i++ {
+                       fld := value.Field(i);
+                       ps.print(w, ps.getFormat(typename(fld)), fld, 0);
+               }
+               ps.errors <- nil;  // no errors
+       }();
+
+       // TODO return correct value for count instead of 0
+       return 0, <-ps.errors;
 }
 
 
 // Print formats each argument according to the format f
 // and writes to standard output.
 //
-func (f Format) Print(args ...) {
-       f.Fprint(os.Stdout, args);
+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.
 //
-func (f Format) Sprint(args ...) string {
+func (f *Format) Sprint(args ...) string {
        var buf io.ByteBuffer;
-       f.Fprint(&buf, args);
+       // TODO what to do in case of errors?
+       f.Fprint(&buf, nil, args);
        return string(buf.Data());
 }
index 65ce83a4f41e75c6273935833c6f87159a43bcf3..c23ad48ed4a09a0d842c7f7cfb2da37621645fb1 100644 (file)
@@ -6,12 +6,17 @@ package format
 
 import (
        "format";
+       "io";
        "testing";
 )
 
 
 func check(t *testing.T, form, expected string, args ...) {
-       result := format.ParseOrDie(form, nil).Sprint(args);
+       f, err := format.Parse(io.StringBytes(form), nil);
+       if err != nil {
+               panic(err.String());
+       }
+       result := f.Sprint(args);
        if result != expected {
                t.Errorf(
                        "format  : %s\nresult  : `%s`\nexpected: `%s`\n\n",
@@ -39,7 +44,6 @@ func Test0(t *testing.T) {
 
 
 // ----------------------------------------------------------------------------
-// - default formatting of basic type int
 // - formatting of a struct
 
 type T1 struct {
@@ -47,6 +51,7 @@ type T1 struct {
 }
 
 const F1 =
+       `int = "%d";`
        `format.T1 = "<" a ">";`
 
 func Test1(t *testing.T) {
@@ -56,7 +61,6 @@ func Test1(t *testing.T) {
 
 // ----------------------------------------------------------------------------
 // - formatting of a struct with an optional field (pointer)
-// - default formatting for pointers
 
 type T2 struct {
        s string;
@@ -65,11 +69,14 @@ type T2 struct {
 
 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) {
@@ -88,9 +95,14 @@ type T3 struct {
 }
 
 const F3a =
+       `default = "%v";`
+       `array = *;`
        `format.T3 = s  {" " a a / ","};`
 
 const F3b =
+       `int = "%d";`
+       `string = "%s";`
+       `array = *;`
        `nil = ;`
        `empty = *:nil;`
        `format.T3 = s [a:empty ": " {a / "-"}]`
@@ -112,11 +124,17 @@ type T4 struct {
 }
 
 const F4a =
+       `int = "%d";`
+       `pointer = *;`
+       `array = *;`
        `nil = ;`
        `empty = *:nil;`
        `format.T4 = "<" (x:empty x | "-") ">" `
 
 const F4b =
+       `int = "%d";`
+       `pointer = *;`
+       `array = *;`
        `nil = ;`
        `empty = *:nil;`
        `format.T4 = "<" (a:empty {a / ", "} | "-") ">" `
index dce3a286bf6cde19027fde2fbf47938a1a32bdcc..90cc96dbbc5a6a7f86a35ae7e768b8ce8f7fe764 100644 (file)
@@ -94,20 +94,32 @@ func (h *ErrorHandler) Error(pos token.Position, msg string) {
 }
 
 
-func isValidPos(w io.Writer, value interface{}, name string) bool {
+func isValidPos(w io.Writer, env, value interface{}, name string) bool {
        return value.(token.Position).Line > 0;
 }
 
 
-func isSend(w io.Writer, value interface{}, name string) bool {
+func isSend(w io.Writer, env, value interface{}, name string) bool {
        return value.(ast.ChanDir) & ast.SEND != 0;
 }
 
 
-func isRecv(w io.Writer, value interface{}, name string) bool {
+func isRecv(w io.Writer, env, value interface{}, 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] == '*'
+}
+
+
+var fmap = format.FormatterMap{
+       "isValidPos": isValidPos,
+       "isSend": isSend,
+       "isRecv": isRecv,
+       "isMultiLineComment": isMultiLineComment,
+}
+
 
 func main() {
        // handle flags
@@ -129,7 +141,7 @@ func main() {
                fmt.Fprintf(os.Stderr, "%s: %v\n", ast_txt, err);
                os.Exit(1);
        }
-       ast_format, err := format.Parse(src, format.FormatterMap{"isValidPos": isValidPos, "isSend": isSend, "isRecv": isRecv});
+       ast_format, err := format.Parse(src, fmap);
        if err != nil {
                fmt.Fprintf(os.Stderr, "%s: format errors:\n%s", ast_txt, err);
                os.Exit(1);
@@ -156,7 +168,13 @@ func main() {
                if !*silent {
                        tw := makeTabwriter(os.Stdout);
                        if *formatter {
-                               ast_format.Fprint(tw, prog);
+                               var optSemi bool;  // formatting environment
+                               _, err := ast_format.Fprint(tw, &optSemi, prog);
+                               if err != nil {
+                                       fmt.Fprintf(os.Stderr, "format error$$: %s", err);
+                                       exitcode = 1;
+                                       continue;  // proceed with next file
+                               }
                        } else {
                                var p astPrinter.Printer;
                                p.Init(tw, nil, nil /*prog.Comments*/, false);
index 910f2fc13e3a67260a1065d2d9ee55ed2e12250d..11960250e98be758edf7d279fd8beb21c7ca6c32 100644 (file)
@@ -69,7 +69,7 @@ type Object struct {
 func (obj *Object) IsExported() bool {
        switch obj.Kind {
        case NONE /* FUNC for now */, CONST, TYPE, VAR, FUNC:
-               ch, size := utf8.DecodeRuneInString(obj.Ident,  0);
+               ch, size := utf8.DecodeRuneInString(obj.Ident);
                return unicode.IsUpper(ch);
        }
        return false;
index 9abb047d5fee95e7f393d5fd166a3efc9762ed9a..f0d2ac83971903ec6abd9827f2a3f52d71d97a0e 100755 (executable)
@@ -36,8 +36,7 @@ apply1() {
 
 # apply to local files
 applydot() {
-       for F in *.go
-       do
+       for F in `find . -name "*.go" | grep -v "OLD" | grep -v "._"`; do
                apply1 $1 $F
        done
 }
@@ -45,7 +44,7 @@ applydot() {
 
 # apply to all .go files we can find
 apply() {
-       for F in `find $GOROOT -name "*.go" | grep -v "OLD"`; do
+       for F in `find $GOROOT -name "*.go" | grep -v "OLD" | grep -v "._"`; do
                apply1 $1 $F
        done
 }