nil =
; // TODO we see a lot of nil's - why?
-not_empty =
+exists =
*:nil;
// Expressions & Types
ast.Field =
- [Names:not_empty {Names / ", "} " "] Type;
+ [Names:exists {Names / ", "} " "] Type;
ast.BadExpr =
"BAD EXPR";
{Strings / "\n"};
ast.FuncLit =
- "func ";
+ Type " " Body;
ast.CompositeLit =
Type "{" {Elts / ", "} "}";
"[]" Elt;
ast.StructType =
- "struct {"
- [Fields:not_empty
- >> "\t" "\n"
- {Fields / ";\n"}
- << "\n"
+ "struct"
+ [Lbrace:isValidPos " {"]
+ [ Fields:exists
+ >> "\t" "\n"
+ {Fields / ";\n"}
+ << "\n"
]
- "}";
+ [Rbrace:isValidPos "}"];
signature =
- "(" {Params / ", "} ")" [Results:not_empty " (" {Results / ", "} ")"];
+ "(" {Params / ", "} ")" [Results:exists " (" {Results / ", "} ")"];
funcSignature =
*:signature;
ast.FuncType =
- "func" ^:signature;
+ [Position:isValidPos "func"] ^:signature;
ast.InterfaceType =
- "interface {"
- [Methods:not_empty
- >> "\t" "\n"
- {Methods / ";\n"} // TODO should not start with "func"
- << "\n"
+ "interface"
+ [Lbrace:isValidPos " {"]
+ [ Methods:exists
+ >> "\t" "\n"
+ {Methods / ";\n"}
+ << "\n"
]
- "}";
+ [Rbrace:isValidPos "}"];
ast.MapType =
"map[" Key "]" Value;
ast.ChanType =
- "chan";
+ ( Dir:isSend Dir:isRecv
+ "chan "
+ | Dir:isSend
+ "chan <- "
+ | "<-chan "
+ )
+ Value;
// ----------------------------------------------------------------------------
ast.GoStmt =
"go " Call;
+ast.DeferStmt =
+ "defer " Call;
+
ast.ReturnStmt =
"return" {" " Results / ","};
blockStmt = // like ast.BlockStmt but w/o indentation
"{"
- [List:not_empty
+ [List:exists
"\n"
{List / ";\n"}
"\n"
ast.BlockStmt =
"{"
- [List:not_empty
+ [List:exists
>> "\t" "\n"
{List / ";\n"}
<< "\n"
"if " [Init "; "] [Cond " "] Body [" else " Else];
ast.CaseClause =
- ( Values:not_empty "case " {Values / ", "}
- | "default"
+ ( Values:exists "case " {Values / ", "}
+ | "default"
)
":"
- [Body:not_empty
+ [Body:exists
>> "\t" "\n"
{Body / ";\n"}
<<
Body:blockStmtPtr;
ast.TypeCaseClause =
- ( Type:not_empty "case " Type
+ ( Type:exists "case " Type
| "default"
)
":"
- [Body:not_empty
+ [Body:exists
>> "\t" "\n"
{Body / ";\n"}
<<
Body:blockStmtPtr;
ast.CommClause =
- "CommClause";
+ ( "case " [Lhs " " Tok " "] Rhs
+ | "default"
+ )
+ ":"
+ [Body:exists
+ >> "\t" "\n"
+ {Body / ";\n"}
+ <<
+ ];
ast.SelectStmt =
"select "
ast.ForStmt =
"for "
- [ Init:not_empty
- [Init] "; " [Cond] "; " [Post " "]
- | Post:not_empty
+ [ (Init:exists | Post:exists)
[Init] "; " [Cond] "; " [Post " "]
| Cond " "
]
[Name] "\t" {Path};
ast.ValueSpec =
- {Names / ", "} [" " Type] [Values:not_empty " = " {Values / ", "}];
+ {Names / ", "} [" " Type] [Values:exists " = " {Values / ", "}];
ast.TypeSpec =
Name " " // TODO using "\t" instead of " " screws up struct field alignment
ast.GenDecl =
Doc
- Tok " ("
- >> "\t" "\n"
- {Specs / ";\n"}
- <<
- "\n"
- ")";
+ Tok " "
+ [Lparen:isValidPos "(" >> "\t" "\n"]
+ {Specs / ";\n"}
+ [Rparen:isValidPos << "\n" ")"];
ast.FuncDecl =
"func " ["(" Recv ") "] Name Type:funcSignature
"os";
"reflect";
"strconv";
+ "strings";
)
// Parsing
/* TODO
- - installable custom formatters (like for template.go)
- have a format to select type name, field tag, field offset?
- use field tag as default format for that field
*/
type parser struct {
+ // scanning
scanner scanner.Scanner;
-
- // error handling
- lastline int; // > 0 if there was any error
-
- // next token
pos token.Position; // token position
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;
}
if pos.Line != p.lastline {
// 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(os.Stderr, "%d:%d: %s\n", pos.Line, pos.Column, msg);
+ fmt.Fprintf(&p.errors, "%d:%d: %s\n", pos.Line, pos.Column, msg);
}
p.lastline = pos.Line;
}
}
-func readSource(src interface{}, err scanner.ErrorHandler) []byte {
- errmsg := "invalid input type (or nil)";
+type formatError string
+
+func (p formatError) String() string {
+ return 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);
+ return io.StringBytes(s), nil;
+
case []byte:
- return s;
+ 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 s.Data();
+ if s == nil {
+ return nil, formatError("src is nil");
}
+ return s.Data(), nil;
+
case io.Read:
var buf io.ByteBuffer;
- n, os_err := io.Copy(s, &buf);
- if os_err == nil {
- return buf.Data();
+ n, err := io.Copy(s, &buf);
+ if err != nil {
+ return nil, err;
}
- errmsg = os_err.String();
+ return buf.Data(), nil
}
- if err != nil {
- // TODO fix this
- panic();
- //err.Error(noPos, errmsg);
- }
- return nil;
+ return nil, formatError("src type not supported");
}
-// TODO do better error handling
-
// 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.
//
-func Parse(src interface{}, fmap FormatterMap) Format {
- // initialize parser
+func Parse(src interface{}, fmap FormatterMap) (f Format, err os.Error) {
+ s, err := readSource(src);
+ if err != nil {
+ return nil, err;
+ }
+
+ // parse format description
var p parser;
- p.scanner.Init(readSource(src, &p), &p, false);
+ p.scanner.Init(s, &p, false);
p.next();
+ f = p.parseFormat();
- format := p.parseFormat();
- if p.lastline > 0 {
- return nil; // src contains errors
- }
-
- // add custom formatters if any
- if fmap != nil {
- for name, form := range fmap {
- if t, found := format[name]; !found {
- format[name] = &custom{name, form};
- } else {
- p.Error(token.Position{0, 0, 0}, "formatter already declared: " + name);
- }
+ // add custom formatters, if any
+ for name, form := range fmap {
+ if t, found := f[name]; !found {
+ f[name] = &custom{name, form};
+ } else {
+ fmt.Fprintf(&p.errors, "formatter already declared: %s", name);
}
}
- return format;
+ if p.errors.Len() > 0 {
+ return nil, formatError(string(p.errors.Data()));
+ }
+
+ return f, nil;
+}
+
+
+func ParseOrDie(src interface{}, fmap FormatterMap) Format {
+ f, err := Parse(src, fmap);
+ if err != nil {
+ panic(err.String());
+ }
+ return f;
}
// ----------------------------------------------------------------------------
// Formatting
-func fieldIndex(v reflect.StructValue, fieldname string) int {
+func getField(v reflect.StructValue, fieldname string) reflect.Value {
t := v.Type().(reflect.StructType);
- for i := 0; i < v.Len(); i++ {
+ for i := 0; i < t.Len(); i++ {
name, typ, tag, offset := t.Field(i);
if name == fieldname {
- return i;
+ 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);
+ }
}
}
- return -1;
-}
-
-
-func getField(v reflect.StructValue, i int) reflect.Value {
- fld := v.Field(i);
- /*
- if tmp, is_interface := fld.(reflect.InterfaceValue); is_interface {
- // TODO do I have to check something for nil here?
- fld = reflect.NewValue(tmp.Get());
- }
- */
- return fld;
-}
-
-
-func getFieldByName(v reflect.StructValue, fieldname string) reflect.Value {
- i := fieldIndex(v, fieldname);
- if i < 0 {
- panicln(fmt.Sprintf("no field %s int %s", fieldname, v.Type().Name()));
- }
-
- return getField(v, i);
+ panicln(fmt.Sprintf("no field %s int %s", fieldname, t.Name()));
+ return nil;
}
default:
// field
if s, is_struct := value.(reflect.StructValue); is_struct {
- value = getFieldByName(s, t.fname);
+ value = getField(s, t.fname);
} else {
// TODO fix this
panic(fmt.Sprintf("error: %s has no field `%s`\n", value.Type().Name(), t.fname));
func (f Format) Fprint(w io.Write, args ...) {
value := reflect.NewValue(args).(reflect.StructValue);
for i := 0; i < value.Len(); i++ {
- fld := getField(value, i);
+ fld := value.Field(i);
var ps state;
ps.init(f);
ps.print(w, f.getFormat(typename(fld), fld), fld, 0, 0);
func usage() {
fmt.Fprintf(os.Stderr, "usage: pretty { flags } { files }\n");
flag.PrintDefaults();
- sys.Exit(0);
+ sys.Exit(1);
}
}
+func isValidPos(w io.Write, value interface{}, name string) bool {
+ return value.(token.Position).Line > 0;
+}
+
+
+func isSend(w io.Write, value interface{}, name string) bool {
+ return value.(ast.ChanDir) & ast.SEND != 0;
+}
+
+
+func isRecv(w io.Write, value interface{}, name string) bool {
+ return value.(ast.ChanDir) & ast.RECV != 0;
+}
+
+
func main() {
// handle flags
flag.Parse();
fmt.Fprintf(os.Stderr, "%s: %v\n", ast_txt, err);
sys.Exit(1);
}
- ast_format := format.Parse(src, nil);
- if ast_format == nil {
- fmt.Fprintf(os.Stderr, "%s: format errors\n", ast_txt);
+ ast_format, err := format.Parse(src, format.FormatterMap{"isValidPos": isValidPos, "isSend": isSend, "isRecv": isRecv});
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s: format errors:\n%s", ast_txt, err);
sys.Exit(1);
}
// process files
+ exitcode := 0;
for i := 0; i < flag.NArg(); i++ {
filename := flag.Arg(i);
src, err := readFile(filename);
if err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", filename, err);
- continue;
+ exitcode = 1;
+ continue; // proceed with next file
}
prog, ok := parser.Parse(src, &ErrorHandler{filename, 0}, mode);
+ if !ok {
+ exitcode = 1;
+ continue; // proceed with next file
+ }
- if ok && !*silent {
+ if !*silent {
tw := makeTabwriter(os.Stdout);
if *formatter {
ast_format.Fprint(tw, prog);
tw.Flush();
}
}
+
+ sys.Exit(exitcode);
}