1
0
mirror of https://github.com/golang/go synced 2024-11-11 20:50:23 -07:00

weekly snapshot:

- template-driven ast printing now can successfully
  reproduce entire Go programs

next steps:
- fine-tuning of output
- print interspersed comments
- cleanup and testing against all Go programs
- replace astprinter

R=r
OCL=28181
CL=28181
This commit is contained in:
Robert Griesemer 2009-05-01 19:32:41 -07:00
parent 49f7494894
commit 0d6a5f417f
4 changed files with 578 additions and 270 deletions

View File

@ -2,216 +2,314 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Format file for printing AST nodes (package "ast").
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Debugging // Debugging
token.Token = token.Token =
^ : "%s"; ^:string;
array =
*;
pointer =
*;
string =
"%s";
char =
"%c";
bytes =
{*};
nil =
; // TODO we see a lot of nil's - why?
not_empty =
*:nil;
// Format file for printing AST nodes (package "ast").
ast;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// TODO should these be automatic? // TODO these are implicit - only here for debugging
Expr = ast.Expr =
"expr ";
//*;
Stmt =
*; *;
Decl = ast.Stmt =
*; *;
ast.Decl =
*;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Comments // Comments
Comment = ast.Comment =
Text : "%s\n"; Text:string "\n";
Comments = ast.Comments =
{*}; {*};
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Expressions & Types // Expressions & Types
Field = ast.Field =
{Names ", "} Type; [Names:not_empty {Names / ", "} " "] Type;
BadExpr = ast.BadExpr =
"BAD EXPR"; "BAD EXPR";
Ident = ast.Ident =
Value; Value;
Elipsis = ast.Ellipsis =
"..."; "...";
IntLit = ast.IntLit =
Value : "%s"; Value:string;
FloatLit = ast.FloatLit =
Value : "%s"; Value:string;
CharLit = ast.CharLit =
Value : "%s"; Value:string;
StringLit = ast.StringLit =
Value : "%s"; Value:string;
StringList = ast.StringList =
{ Strings }; {Strings / "\n"};
FuncLit = ast.FuncLit =
"func "; "func ";
CompositeLit = ast.CompositeLit =
Type "{}"; Type "{" {Elts / ", "} "}";
ParenExpr = ast.ParenExpr =
"(" X ")"; "(" X ")";
SelectorExpr = ast.SelectorExpr =
X "." Sel; X "." Sel;
IndexExpr = ast.IndexExpr =
X "[" Index "]"; X "[" Index "]";
SliceExpr = ast.SliceExpr =
X "[" Begin " : " End "]"; X "[" Begin ":" End "]";
TypeAssertExpr = ast.TypeAssertExpr =
X ".(" Type ")"; X ".(" Type ")";
CallExpr = ast.CallExpr =
Fun "(" {Args} ")"; Fun "(" {Args / ", "} ")";
StarExpr = ast.StarExpr =
"*" X; "*" X;
UnaryExpr = ast.UnaryExpr =
Op X; Op X;
BinaryExpr = ast.BinaryExpr =
X Op Y; X " " Op " " Y;
KeyValueExpr = ast.KeyValueExpr =
Key ": " Value; Key ": " Value;
ArrayType = ast.ArrayType =
"[" Len "]" Elt; "[" Len "]" Elt;
SliceType = ast.SliceType =
"[]" Elt; "[]" Elt;
StructType = ast.StructType =
"struct {\n" "struct {"
[Fields:not_empty
>> "\t" "\n"
{Fields / ";\n"}
<< "\n"
]
"}"; "}";
FuncType = signature =
"(" {Params " "} ")"; "(" {Params / ", "} ")" [Results:not_empty " (" {Results / ", "} ")"];
// BUG take this one away and the code crashes funcSignature =
InterfaceType = *:signature;
"interface {}";
MapType = ast.FuncType =
"func" ^:signature;
ast.InterfaceType =
"interface {"
[Methods:not_empty
>> "\t" "\n"
{Methods / ";\n"} // TODO should not start with "func"
<< "\n"
]
"}";
ast.MapType =
"map[" Key "]" Value; "map[" Key "]" Value;
ChanType = ast.ChanType =
"chan"; "chan";
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Statements // Statements
BadStmt = ast.BadStmt =
"BAD STMT"; "BAD STMT";
DeclStmt = ast.DeclStmt =
Decl; Decl;
EmptyStmt = ast.EmptyStmt =
; ;
LabeledStmt = ast.LabeledStmt =
Label ":\t" Stmt; Label ":\t" Stmt;
ExprStmt = ast.ExprStmt =
X; X;
IncDecStmt = ast.IncDecStmt =
X Tok; X Tok;
AssignStmt = ast.AssignStmt =
"assignment " {Lhs ", "}; {Lhs / ", "} " " Tok " " {Rhs / ", "};
//{Lhs ", "} Tok {Rhs ", "};
GoStmt = ast.GoStmt =
"go " Call; "go " Call;
ReturnStmt = ast.ReturnStmt =
"return" {" " Results}; "return" {" " Results / ","};
BranchStmt = ast.BranchStmt =
Tok [" " Label]; Tok [" " Label];
BlockStmt = blockStmt = // like ast.BlockStmt but w/o indentation
"{\n" {List ";\n"} "}\n"; "{"
[List:not_empty
"\n"
{List / ";\n"}
"\n"
]
"}";
IfStmt = blockStmtPtr =
"if " "{" [Body] "}" [Else]; *:blockStmt;
SwitchStmt = ast.BlockStmt =
"switch {}"; "{"
[List:not_empty
>> "\t" "\n"
{List / ";\n"}
<< "\n"
]
"}";
TypeSwitchStmt = ast.IfStmt =
"switch {}"; "if " [Init "; "] [Cond " "] Body [" else " Else];
SelectStmt = ast.CaseClause =
"select {}"; ( Values:not_empty "case " {Values / ", "}
| "default"
)
":"
[Body:not_empty
>> "\t" "\n"
{Body / ";\n"}
<<
];
ForStmt = ast.SwitchStmt =
"for {}"; "switch " [Init "; "] [Tag " "]
Body:blockStmtPtr;
RangeStmt = ast.TypeCaseClause =
"range"; ( Type:not_empty "case " Type
| "default"
)
":"
[Body:not_empty
>> "\t" "\n"
{Body / ";\n"}
<<
];
ast.TypeSwitchStmt =
"switch " Assign " "
Body:blockStmtPtr;
ast.CommClause =
"CommClause";
ast.SelectStmt =
"select "
Body:blockStmtPtr;
ast.ForStmt =
"for "
[ Init:not_empty
[Init] "; " [Cond] "; " [Post " "]
| Post:not_empty
[Init] "; " [Cond] "; " [Post " "]
| Cond " "
]
Body;
ast.RangeStmt =
"for " Key [", " Value] " " Tok " range " X
" "
Body;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Declarations // Declarations
Spec = ast.Spec =
*; *;
ImportSpec = ast.ImportSpec =
"import"; Doc
[Name] "\t" {Path};
ValueSpec = ast.ValueSpec =
"value"; {Names / ", "} [" " Type] [Values:not_empty " = " {Values / ", "}];
TypeSpec = ast.TypeSpec =
"type"; Name " " // TODO using "\t" instead of " " screws up struct field alignment
Type;
BadDecl = ast.BadDecl =
"BAD DECL"; "BAD DECL";
GenDecl = ast.GenDecl =
Doc Doc
Tok " (\n" Tok " ("
")\n"; >> "\t" "\n"
{Specs / ";\n"}
FuncDecl = <<
"func " ["(" Recv ") "] Name Type [" " Body]; "\n"
")";
ast.FuncDecl =
"func " ["(" Recv ") "] Name Type:funcSignature
[" " Body]
"\n";
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Program // Program
Program = ast.Program =
Doc Doc
"package " Name "\n\n" "package " Name "\n\n"
{Decls "\n\n"}; {Decls / "\n\n"};

View File

@ -32,13 +32,22 @@ import (
) )
// TODO remove once the code works // TODO should probably do this in a different way
var debug = flag.Bool("d", false, "debug mode"); var (
debug = flag.Bool("d", false, "debug mode");
trace = flag.Bool("t", false, "trace mode");
)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Format representation // Format representation
type (
Formatter func(w io.Write, value interface{}, name string) bool;
FormatterMap map[string]Formatter;
)
// A production expression is built from the following nodes. // A production expression is built from the following nodes.
// //
type ( type (
@ -54,28 +63,36 @@ type (
x, y expr; x, y expr;
}; };
field struct {
name string; // including "^", "*"
fexpr expr; // nil if no fexpr specified
};
literal struct { literal struct {
// TODO should there be other types or should it all be string literals? // TODO should there be other types or should it all be string literals?
value []byte; value []byte;
}; };
indentation struct {
iexpr expr; // outdent if nil
};
field struct {
fname string; // including "^", "*"
tname string; // "" if no tname specified
};
negation struct {
neg expr;
};
option struct { option struct {
x expr opt expr;
}; };
repetition struct { repetition struct {
x expr rep expr;
div expr;
}; };
// TODO custom formats are not yet used
custom struct { custom struct {
name string; name string;
f func(w io.Write, value interface{}, name string) bool form Formatter
}; };
) )
@ -90,31 +107,47 @@ func (x *sequence) String() string {
} }
func (x *field) String() string {
if x.fexpr == nil {
return x.name;
}
return fmt.Sprintf("%s: (%v)", x.name, x.fexpr);
}
func (x *literal) String() string { func (x *literal) String() string {
return strconv.Quote(string(x.value)); 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 { func (x *option) String() string {
return fmt.Sprintf("[%v]", x.x); return fmt.Sprintf("[%v]", x.opt);
} }
func (x *repetition) String() string { func (x *repetition) String() string {
return fmt.Sprintf("{%v}", x.x); if x.div == nil {
return fmt.Sprintf("{%v}", x.rep);
}
return fmt.Sprintf("{%v / %v}", x.rep, x.div);
} }
func (x *custom) String() string { func (x *custom) String() string {
return fmt.Sprintf("<custom %s>", x.name); return "<" + x.name + ">";
} }
@ -124,13 +157,15 @@ func (x *custom) String() string {
Formatting rules are specified in the following syntax: Formatting rules are specified in the following syntax:
Format = { Production } . Format = Production { ";" Production } [ ";" ] .
Production = Name [ "=" [ Expression ] ] ";" . Production = Name "=" Expression .
Name = identifier { "." identifier } . Name = identifier { "." identifier } .
Expression = Term { "|" Term } . Expression = [ Term ] { "|" [ Term ] } .
Term = Factor { Factor } . Term = Factor { Factor } .
Factor = string_literal | Field | Group | Option | Repetition . Factor = string_literal | Indent | Field | Negation | Group | Option | Repetition .
Field = ( "^" | "*" | Name ) [ ":" Expression ] . Indent = ">>" Factor | "<<" .
Field = ( "^" | "*" | Name ) [ ":" Name ] .
Negation = "!" Factor .
Group = "(" Expression ")" . Group = "(" Expression ")" .
Option = "[" Expression "]" . Option = "[" Expression "]" .
Repetition = "{" Expression "}" . Repetition = "{" Expression "}" .
@ -182,9 +217,6 @@ type Format map [string] expr;
- installable custom formatters (like for template.go) - installable custom formatters (like for template.go)
- have a format to select type name, field tag, field offset? - have a format to select type name, field tag, field offset?
- use field tag as default format for that field - use field tag as default format for that field
- field format override (":") is not working as it should
(cannot refer to another production - syntactially not possible
at the moment)
*/ */
type parser struct { type parser struct {
@ -257,12 +289,6 @@ func (p *parser) parseName() string {
} }
// TODO WriteByte should be a ByteBuffer method
func writeByte(buf *io.ByteBuffer, b byte) {
buf.Write([]byte{b});
}
func (p *parser) parseValue() []byte { func (p *parser) parseValue() []byte {
if p.tok != token.STRING { if p.tok != token.STRING {
p.expect(token.STRING); p.expect(token.STRING);
@ -281,30 +307,33 @@ func (p *parser) parseValue() []byte {
} }
func (p *parser) parseFactor() (x expr)
func (p *parser) parseExpr() expr func (p *parser) parseExpr() expr
func (p *parser) parseField() expr { func (p *parser) parseField() expr {
var name string; var fname string;
switch p.tok { switch p.tok {
case token.XOR: case token.XOR:
name = "^"; fname = "^";
p.next(); p.next();
case token.MUL: case token.MUL:
name = "*"; fname = "*";
p.next(); p.next();
case token.IDENT: case token.IDENT:
name = p.parseName(); // TODO use reflect.ExpandType() to lookup a field
// during parse-time if posssible
fname = p.parseName();
default: default:
return nil; return nil;
} }
var fexpr expr; var tname string;
if p.tok == token.COLON { if p.tok == token.COLON {
p.next(); p.next();
fexpr = p.parseExpr(); tname = p.parseName();
} }
return &field{name, fexpr}; return &field{fname, tname};
} }
@ -313,6 +342,18 @@ func (p *parser) parseFactor() (x expr) {
case token.STRING: case token.STRING:
x = &literal{p.parseValue()}; x = &literal{p.parseValue()};
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()};
case token.LPAREN: case token.LPAREN:
p.next(); p.next();
x = p.parseExpr(); x = p.parseExpr();
@ -325,7 +366,13 @@ func (p *parser) parseFactor() (x expr) {
case token.LBRACE: case token.LBRACE:
p.next(); p.next();
x = &repetition{p.parseExpr()}; x = p.parseExpr();
var div expr;
if p.tok == token.QUO {
p.next();
div = p.parseExpr();
}
x = &repetition{x, div};
p.expect(token.RBRACE); p.expect(token.RBRACE);
default: default:
@ -338,18 +385,15 @@ func (p *parser) parseFactor() (x expr) {
func (p *parser) parseTerm() expr { func (p *parser) parseTerm() expr {
x := p.parseFactor(); x := p.parseFactor();
if x == nil {
p.error_expected(p.pos, "factor");
p.next(); // make progress
return nil;
}
for { if x != nil {
y := p.parseFactor(); for {
if y == nil { y := p.parseFactor();
break; if y == nil {
break;
}
x = &sequence{x, y};
} }
x = &sequence{x, y};
} }
return x; return x;
@ -369,36 +413,35 @@ func (p *parser) parseExpr() expr {
} }
func (p *parser) parseProd() (string, expr) {
name := p.parseName();
p.expect(token.ASSIGN);
x := p.parseExpr();
return name, x;
}
func (p *parser) parseFormat() Format { func (p *parser) parseFormat() Format {
format := make(Format); format := make(Format);
prefix := "";
for p.tok != token.EOF { for p.tok != token.EOF {
pos := p.pos; pos := p.pos;
name := p.parseName(); name, x := p.parseProd();
if p.tok == token.ASSIGN { // add production to format
// production if t, found := format[name]; !found {
p.next(); format[name] = x;
var x expr;
if p.tok != token.SEMICOLON {
x = p.parseExpr();
}
// add production to format
name = prefix + name;
if t, found := format[name]; !found {
format[name] = x;
} else {
p.Error(pos, "production already declared: " + name);
}
} else { } else {
// prefix only p.Error(pos, "production already declared: " + name);
prefix = name + "."; }
if p.tok == token.SEMICOLON {
p.next();
} else {
break;
} }
p.expect(token.SEMICOLON);
} }
p.expect(token.EOF);
return format; return format;
} }
@ -441,18 +484,36 @@ func readSource(src interface{}, err scanner.ErrorHandler) []byte {
// a string, a []byte, or implement io.Read. The result is a Format // a string, a []byte, or implement io.Read. The result is a Format
// if no errors occured; otherwise Parse returns nil. // if no errors occured; otherwise Parse returns nil.
// //
func Parse(src interface{}) Format { func Parse(src interface{}, fmap FormatterMap) Format {
// initialize parser // initialize parser
var p parser; var p parser;
p.scanner.Init(readSource(src, &p), &p, false); p.scanner.Init(readSource(src, &p), &p, false);
p.next(); p.next();
f := p.parseFormat(); format := p.parseFormat();
if p.lastline > 0 { if p.lastline > 0 {
return nil; // src contains errors return nil; // src contains errors
} }
return f;
// 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);
}
}
}
return format;
}
func (f Format) Dump() {
for name, form := range f {
fmt.Printf("%s = %v;\n", name, form);
}
} }
@ -473,10 +534,12 @@ func fieldIndex(v reflect.StructValue, fieldname string) int {
func getField(v reflect.StructValue, i int) reflect.Value { func getField(v reflect.StructValue, i int) reflect.Value {
fld := v.Field(i); fld := v.Field(i);
/*
if tmp, is_interface := fld.(reflect.InterfaceValue); is_interface { if tmp, is_interface := fld.(reflect.InterfaceValue); is_interface {
// TODO do I have to check something for nil here? // TODO do I have to check something for nil here?
fld = reflect.NewValue(tmp.Get()); fld = reflect.NewValue(tmp.Get());
} }
*/
return fld; return fld;
} }
@ -484,7 +547,7 @@ func getField(v reflect.StructValue, i int) reflect.Value {
func getFieldByName(v reflect.StructValue, fieldname string) reflect.Value { func getFieldByName(v reflect.StructValue, fieldname string) reflect.Value {
i := fieldIndex(v, fieldname); i := fieldIndex(v, fieldname);
if i < 0 { if i < 0 {
panicln("field not found:", fieldname); panicln(fmt.Sprintf("no field %s int %s", fieldname, v.Type().Name()));
} }
return getField(v, i); return getField(v, i);
@ -502,7 +565,7 @@ func typename(value reflect.Value) string {
case reflect.ArrayKind: name = "array"; case reflect.ArrayKind: name = "array";
case reflect.BoolKind: name = "bool"; case reflect.BoolKind: name = "bool";
case reflect.ChanKind: name = "chan"; case reflect.ChanKind: name = "chan";
case reflect.DotDotDotKind: name = "..."; case reflect.DotDotDotKind: name = "ellipsis";
case reflect.FloatKind: name = "float"; case reflect.FloatKind: name = "float";
case reflect.Float32Kind: name = "float32"; case reflect.Float32Kind: name = "float32";
case reflect.Float64Kind: name = "float64"; case reflect.Float64Kind: name = "float64";
@ -530,91 +593,177 @@ func typename(value reflect.Value) string {
var defaults = map [int] expr { var defaults = map [int] expr {
reflect.ArrayKind: &field{"*", nil}, reflect.ArrayKind: &field{"*", ""},
reflect.DotDotDotKind: &field{"*", nil}, reflect.DotDotDotKind: &field{"*", ""},
reflect.InterfaceKind: &field{"*", nil}, reflect.InterfaceKind: &field{"*", ""},
reflect.MapKind: &field{"*", nil}, reflect.MapKind: &field{"*", ""},
reflect.PtrKind: &field{"*", nil}, reflect.PtrKind: &field{"*", ""},
reflect.StringKind: &literal{io.StringBytes("%s")},
} }
var catchAll = &literal{io.StringBytes("%v")}; var catchAll = &literal{io.StringBytes("%v")};
func (f Format) getFormat(value reflect.Value) expr { func (f Format) getFormat(name string, value reflect.Value) expr {
if fexpr, found := f[typename(value)]; found { /*
if name == "nil" {
fmt.Printf("value = %T %v, kind = %d\n", value, value, value.Kind());
panic();
}
*/
if fexpr, found := f[name]; found {
return fexpr; return fexpr;
} }
if *debug {
fmt.Printf("no production for type: %s\n", name);
}
// no fexpr found - return kind-specific default value, if any // no fexpr found - return kind-specific default value, if any
if fexpr, found := defaults[value.Kind()]; found { if fexpr, found := defaults[value.Kind()]; found {
return fexpr; return fexpr;
} }
if *debug {
fmt.Printf("no default for type: %s\n", name);
}
return catchAll; return catchAll;
} }
// Count the number of printf-style '%' formatters in s. // Count the number of printf-style '%' formatters in s.
// The result is 0, 1, or 2 (where 2 stands for 2 or more).
// //
func percentCount(s []byte) int { func percentCount(s []byte) int {
n := 0; n := 0;
for i := 0; n < 2 && i < len(s); i++ { for i := 0; i < len(s); i++ {
// TODO should not count "%%"'s
if s[i] == '%' { if s[i] == '%' {
n++; i++;
if i >= len(s) || s[i] != '%' { // don't count "%%"
n++;
}
} }
} }
return n; return n;
} }
func printf(w io.Write, format []byte, value reflect.Value) { func rawPrintf(w io.Write, format []byte, value reflect.Value) {
// TODO this seems a bit of a hack // TODO find a better way to do this
if percentCount(format) == 1 { x := value.Interface();
// exactly one '%' format specifier - try to use it switch percentCount(format) {
fmt.Fprintf(w, string(format), value.Interface()); case 0: w.Write(format);
case 1: fmt.Fprintf(w, string(format), x);
case 2: fmt.Fprintf(w, string(format), x, x);
case 3: fmt.Fprintf(w, string(format), x, x, x);
case 4: fmt.Fprintf(w, string(format), x, x, x, x);
default: panic("no support for more than 4 '%'-format chars yet");
}
}
// 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.Write, 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);
i0 = i+1;
}
}
w.Write(s[i0 : len(s)]);
}
func (ps *state) printf(w io.Write, format []byte, value reflect.Value) {
if len(ps.indent_widths) == 0 {
// no indentation
rawPrintf(w, format, value);
} else { } else {
// 0 or more then 1 '%' format specifier - ignore them // print into temporary buffer
w.Write(format); var buf io.ByteBuffer;
rawPrintf(&buf, format, value);
ps.printIndented(w, buf.Data());
} }
} }
// TODO once 6g bug found func (ps *state) print(w io.Write, fexpr expr, value reflect.Value, index, level int) bool
func print(s string, a ...) {
/*
f0 := reflect.NewValue(a).(reflect.StructValue).Field(0);
if t, is_iface := f0.(reflect.InterfaceValue); is_iface {
f0 = reflect.NewValue(t.Get());
}
*/
fmt.Printf(s, a)
}
func (f Format) print(w io.Write, fexpr expr, value reflect.Value, index, level int) bool
// Returns true if a non-empty field value was found. // Returns true if a non-empty field value was found.
func (f Format) print0(w io.Write, fexpr expr, value reflect.Value, index, level int) bool { func (ps *state) print0(w io.Write, fexpr expr, value reflect.Value, index, level int) bool {
if fexpr == nil { if fexpr == nil {
return true; return true;
} }
if value == nil {
panic("should not be possible");
}
switch t := fexpr.(type) { switch t := fexpr.(type) {
case *alternative: case *alternative:
// - print the contents of the first alternative with a non-empty field // - print the contents of the first alternative with a non-empty field
// - result is true if there is at least one non-empty field // - result is true if there is at least one non-empty field
var buf io.ByteBuffer; var buf io.ByteBuffer;
if f.print(&buf, t.x, value, index, level) { if ps.print(&buf, t.x, value, 0, level) {
w.Write(buf.Data()); w.Write(buf.Data());
return true; return true;
} else { } else {
buf.Reset(); var buf io.ByteBuffer;
if f.print(&buf, t.y, value, 0, level) { if ps.print(&buf, t.y, value, 0, level) {
w.Write(buf.Data()); w.Write(buf.Data());
return true; return true;
} }
@ -625,27 +774,43 @@ func (f Format) print0(w io.Write, fexpr expr, value reflect.Value, index, level
// - print the contents of the sequence // - print the contents of the sequence
// - result is true if there is no empty field // - result is true if there is no empty field
// TODO do we need to buffer here? why not? // TODO do we need to buffer here? why not?
b1 := f.print(w, t.x, value, index, level); b1 := ps.print(w, t.x, value, index, level);
b2 := f.print(w, t.y, value, index, level); b2 := ps.print(w, t.y, value, index, level);
return b1 && b2; return b1 && b2;
case *literal:
// - print the literal
// - result is always true (literal is never empty)
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: case *field:
// - print the contents of the field // - print the contents of the field
// - format is either the field format or the type-specific format // - format is either the field format or the type-specific format
// - TODO look at field tag for default format // - TODO look at field tag for default format
// - result is true if the field is not empty // - result is true if the field is not empty
switch t.name { switch t.fname {
case "^": case "^":
// identity - value doesn't change // identity - value doesn't change
case "*": case "*":
// indirect // indirect
if value.Addr() == nil { // TODO is this right?
return false;
}
switch v := value.(type) { switch v := value.(type) {
case reflect.ArrayValue: case reflect.ArrayValue:
if index < 0 || v.Len() <= index { if v.Len() <= index {
return false; return false;
} }
value = v.Elem(index); value = v.Elem(index);
@ -654,54 +819,54 @@ func (f Format) print0(w io.Write, fexpr expr, value reflect.Value, index, level
panic("reflection support for maps incomplete"); panic("reflection support for maps incomplete");
case reflect.PtrValue: case reflect.PtrValue:
if v.Get() == nil { // TODO is this right? if v.Get() == nil {
return false; return false;
} }
value = v.Sub(); value = v.Sub();
case reflect.InterfaceValue: case reflect.InterfaceValue:
if v.Get() == nil { // TODO is this right? if v.Get() == nil {
return false; return false;
} }
value = v.Value(); value = v.Value();
default: default:
panic("not a ptr, array, map, or interface"); // TODO fix this // TODO fix this
} panic(fmt.Sprintf("error: * does not apply to `%s`\n", value.Type().Name()));
if value == nil {
fmt.Fprint(w, "NIL"); // TODO debugging
return false;
} }
default: default:
// field // field
if s, is_struct := value.(reflect.StructValue); is_struct { if s, is_struct := value.(reflect.StructValue); is_struct {
value = getFieldByName(s, t.name); value = getFieldByName(s, t.fname);
} else { } else {
panic ("not a struct"); // TODO fix this // TODO fix this
panic(fmt.Sprintf("error: %s has no field `%s`\n", value.Type().Name(), t.fname));
} }
} }
// determine format // determine format
fexpr = t.fexpr; tname := t.tname;
if fexpr == nil { if tname == "" {
// no field format - use type-specific format tname = typename(value)
fexpr = f.getFormat(value);
} }
fexpr = ps.f.getFormat(tname, value);
return f.print(w, fexpr, value, index, level); return ps.print(w, fexpr, value, index, level);
case *literal: case *negation:
// - print the literal // TODO is this operation useful at all?
// - result is always true (literal is never empty) // print the contents of the option if is contains an empty field
printf(w, t.value, value); var buf io.ByteBuffer;
if !ps.print(&buf, t.neg, value, 0, level) {
w.Write(buf.Data());
}
return true; return true;
case *option: case *option:
// print the contents of the option if it contains a non-empty field // print the contents of the option if it contains a non-empty field
var buf io.ByteBuffer; var buf io.ByteBuffer;
if f.print(&buf, t.x, value, 0, level) { if ps.print(&buf, t.opt, value, 0, level) {
w.Write(buf.Data()); w.Write(buf.Data());
} }
return true; return true;
@ -709,14 +874,22 @@ func (f Format) print0(w io.Write, fexpr expr, value reflect.Value, index, level
case *repetition: case *repetition:
// print the contents of the repetition while there is a non-empty field // print the contents of the repetition while there is a non-empty field
var buf io.ByteBuffer; var buf io.ByteBuffer;
for i := 0; f.print(&buf, t.x, value, i, level); i++ { for i := 0; ps.print(&buf, t.rep, value, i, level); i++ {
if i > 0 {
ps.print(w, t.div, value, i, level);
}
w.Write(buf.Data()); w.Write(buf.Data());
buf.Reset(); buf.Reset();
} }
return true; return true;
case *custom: case *custom:
return t.f(w, value.Interface(), t.name); var buf io.ByteBuffer;
if t.form(&buf, value.Interface(), t.name) {
ps.printIndented(w, buf.Data());
return true;
}
return false;
} }
panic("unreachable"); panic("unreachable");
@ -738,14 +911,14 @@ func printTrace(indent int, format string, a ...) {
} }
func (f Format) print(w io.Write, fexpr expr, value reflect.Value, index, level int) bool { func (ps *state) print(w io.Write, fexpr expr, value reflect.Value, index, level int) bool {
if *debug { if *trace {
printTrace(level, "%v, %d {\n", fexpr, /*value.Interface(), */index); printTrace(level, "%v, %d {\n", fexpr, /*value.Interface(), */index);
} }
result := f.print0(w, fexpr, value, index, level+1); result := ps.print0(w, fexpr, value, index, level+1);
if *debug { if *trace {
printTrace(level, "} %v\n", result); printTrace(level, "} %v\n", result);
} }
return result; return result;
@ -761,7 +934,9 @@ func (f Format) Fprint(w io.Write, args ...) {
value := reflect.NewValue(args).(reflect.StructValue); value := reflect.NewValue(args).(reflect.StructValue);
for i := 0; i < value.Len(); i++ { for i := 0; i < value.Len(); i++ {
fld := getField(value, i); fld := getField(value, i);
f.print(w, f.getFormat(fld), fld, -1, 0); var ps state;
ps.init(f);
ps.print(w, f.getFormat(typename(fld), fld), fld, 0, 0);
} }
} }
@ -770,7 +945,7 @@ func (f Format) Fprint(w io.Write, args ...) {
// and writes to standard output. // and writes to standard output.
// //
func (f Format) Print(args ...) { func (f Format) Print(args ...) {
f.Print(os.Stdout, args); f.Fprint(os.Stdout, args);
} }

View File

@ -11,10 +11,10 @@ import (
func check(t *testing.T, form, expected string, args ...) { func check(t *testing.T, form, expected string, args ...) {
result := format.Parse(form).Sprint(args); result := format.Parse(form, nil).Sprint(args);
if result != expected { if result != expected {
t.Errorf( t.Errorf(
"format : %s\nresult : %s\nexpected: %s\n\n", "format : %s\nresult : `%s`\nexpected: `%s`\n\n",
form, result, expected form, result, expected
) )
} }
@ -22,13 +22,19 @@ func check(t *testing.T, form, expected string, args ...) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// - formatting of basic type int // Syntax
const F0 = func TestA(t *testing.T) {
`int = "0x%x";` // TODO fill this in
}
// ----------------------------------------------------------------------------
// - formatting of basic types
func Test0(t *testing.T) { func Test0(t *testing.T) {
check(t, F0, "0x2a", 42); check(t, `bool = "%v"`, "false", false);
check(t, `int = "%b %d %o 0x%x"`, "101010 42 52 0x2a", 42);
} }
@ -60,7 +66,7 @@ type T2 struct {
const F2a = const F2a =
F1 + F1 +
`pointer = *;` `pointer = *;`
`format.T2 = s ["-" p "-"];`; `format.T2 = s ["-" p "-"];`
const F2b = const F2b =
F1 + F1 +
@ -82,14 +88,43 @@ type T3 struct {
} }
const F3a = const F3a =
`format.T3 = s { " " a a "," };`; `format.T3 = s {" " a a / ","};`
const F3b = const F3b =
`format.T3 = [a:""] s | "nothing";`; // use 'a' to select alternative w/o printing a `nil = ;`
`empty = *:nil;`
`format.T3 = s [a:empty ": " {a / "-"}]`
func Test3(t *testing.T) { func Test3(t *testing.T) {
check(t, F3a, "foo", T3{"foo", nil}); check(t, F3a, "foo", T3{"foo", nil});
check(t, F3a, "foo 00, 11, 22,", T3{"foo", []int{0, 1, 2}}); check(t, F3a, "foo 00, 11, 22", T3{"foo", []int{0, 1, 2}});
//check(t, F3b, "nothing", T3{"bar", nil}); // TODO fix this check(t, F3b, "bar", T3{"bar", nil});
check(t, F3b, "bar", T3{"bar", []int{0}}); 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 =
`nil = ;`
`empty = *:nil;`
`format.T4 = "<" (x:empty x | "-") ">" `
const F4b =
`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}});
} }

View File

@ -66,7 +66,7 @@ func makeTabwriter(writer io.Write) *tabwriter.Writer {
if *usetabs { if *usetabs {
padchar = '\t'; padchar = '\t';
} }
return tabwriter.NewWriter(writer, *tabwidth, 1, padchar, tabwriter.FilterHTML); return tabwriter.NewWriter(writer, *tabwidth, 1, padchar, 0);
} }
@ -114,7 +114,7 @@ func main() {
fmt.Fprintf(os.Stderr, "%s: %v\n", ast_txt, err); fmt.Fprintf(os.Stderr, "%s: %v\n", ast_txt, err);
sys.Exit(1); sys.Exit(1);
} }
ast_format := format.Parse(src); ast_format := format.Parse(src, nil);
if ast_format == nil { if ast_format == nil {
fmt.Fprintf(os.Stderr, "%s: format errors\n", ast_txt); fmt.Fprintf(os.Stderr, "%s: format errors\n", ast_txt);
sys.Exit(1); sys.Exit(1);