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:
parent
49f7494894
commit
0d6a5f417f
@ -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"};
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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}});
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user