1
0
mirror of https://github.com/golang/go synced 2024-11-26 03:57:57 -07:00

- implemented heuristic for composite literals starting with a type name:

if in a if, for, or switch header, must be parenthesized
- implemented string concatenation
- simplified a lot of code
- added many more tests: can now parse all *.go files I got my hands on
- printing output currently broken in some cases, use with -s (silent) option

R=r
OCL=16932
CL=16934
This commit is contained in:
Robert Griesemer 2008-10-10 16:03:32 -07:00
parent 827dcb86b7
commit 89319cfbf9
3 changed files with 117 additions and 145 deletions

View File

@ -11,18 +11,11 @@ pretty: pretty.6
test: pretty
pretty -s *.go
pretty -s ../gosrc/*.go
pretty -s $(GOROOT)/test/235.go
pretty -s $(GOROOT)/test/args.go
pretty -s $(GOROOT)/test/bufiolib.go
pretty -s $(GOROOT)/test/char_lit.go
pretty -s $(GOROOT)/test/sieve.go
pretty -s $(GOROOT)/test/*.go
pretty -s $(GOROOT)/src/pkg/*.go
pretty -s $(GOROOT)/src/lib/flag.go
pretty -s $(GOROOT)/src/lib/fmt.go
pretty -s $(GOROOT)/src/lib/rand.go
pretty -s $(GOROOT)/src/lib/math/*.go
pretty -s $(GOROOT)/src/lib/container/*.go
pretty -s $(GOROOT)/src/lib/syscall/*.go
pretty -s $(GOROOT)/src/lib/*.go
pretty -s $(GOROOT)/src/lib/*/*.go
pretty -s $(GOROOT)/usr/r/*/*.go
echo "DONE"
testnoisy: pretty
@ -42,6 +35,17 @@ testnoisy: pretty
pretty $(GOROOT)/src/lib/syscall/*.go
echo "DONE"
# These tests don't work yet
testfull: pretty
pretty *.go
pretty ../gosrc/*.go
pretty $(GOROOT)/test/*.go
pretty $(GOROOT)/src/pkg/*.go
pretty $(GOROOT)/src/lib/*.go
pretty $(GOROOT)/src/lib/*/*.go
pretty $(GOROOT)/usr/r/*/*.go
echo "DONE"
install: pretty
cp pretty $(HOME)/bin/pretty

View File

@ -18,10 +18,13 @@ export type Parser struct {
pos int; // token source position
tok int; // one token look-ahead
val string; // token value (for IDENT, NUMBER, STRING only)
semi bool; // true if a semicolon was inserted by the previous statement
// Non-syntactic parser control
opt_semi bool; // true if semicolon is optional
// Nesting level
level int; // 0 = global scope, -1 = function scope of global functions, etc.
// Nesting levels
expr_lev int; // 0 = control clause level, 1 = expr inside ()'s
scope_lev int; // 0 = global scope, 1 = function scope of global functions, etc.
};
@ -60,7 +63,7 @@ func (P *Parser) Next() {
t := <-P.tokchan;
P.tok, P.pos, P.val = t.tok, t.pos, t.val;
}
P.semi = false;
P.opt_semi = false;
if P.verbose {
P.PrintIndent();
print("[", P.pos, "] ", Scanner.TokenName(P.tok), "\n");
@ -74,7 +77,8 @@ func (P *Parser) Open(verbose bool, scanner *Scanner.Scanner, tokchan *<-chan *S
P.scanner = scanner;
P.tokchan = tokchan;
P.Next();
P.level = 0;
P.expr_lev = 1;
P.scope_lev = 0;
}
@ -98,17 +102,6 @@ func (P *Parser) OptSemicolon() {
}
// ----------------------------------------------------------------------------
// Scopes
func (P *Parser) OpenScope() {
}
func (P *Parser) CloseScope() {
}
// ----------------------------------------------------------------------------
// Common productions
@ -498,8 +491,8 @@ func (P *Parser) ParseStatementList() *AST.List {
stats.Add(P.ParseStatement());
if P.tok == Scanner.SEMICOLON {
P.Next();
} else if P.semi {
P.semi = false; // consume inserted ";"
} else if P.opt_semi {
P.opt_semi = false; // "consume" optional semicolon
} else {
break;
}
@ -517,14 +510,12 @@ func (P *Parser) ParseBlock() *AST.Block {
block.pos = P.pos;
P.Expect(Scanner.LBRACE);
P.OpenScope();
if P.tok != Scanner.RBRACE {
block.stats = P.ParseStatementList();
}
P.OptSemicolon();
P.CloseScope();
P.Expect(Scanner.RBRACE);
P.semi = true; // allow optional semicolon
P.opt_semi = true;
P.Ecart();
return block;
@ -562,7 +553,9 @@ func (P *Parser) ParseFunctionLit() *AST.FunctionLit {
P.Expect(Scanner.FUNC);
fun.typ = P.ParseFunctionType();
P.scope_lev++;
fun.body = P.ParseBlock();
P.scope_lev--;
P.Ecart();
return fun;
@ -588,6 +581,7 @@ func (P *Parser) ParseExpressionPairList(list *AST.List) {
list.Add(P.ParseExpressionPair());
for P.tok == Scanner.COMMA {
P.Next();
list.Add(P.ParseExpressionPair());
}
@ -595,48 +589,6 @@ func (P *Parser) ParseExpressionPairList(list *AST.List) {
}
func (P *Parser) ParseCompositeLit(typ AST.Type) AST.Expr {
P.Trace("CompositeLit");
lit := new(AST.CompositeLit);
lit.pos = P.pos;
lit.typ = typ;
lit.vals = AST.NewList();
P.Expect(Scanner.LBRACE);
// TODO: should allow trailing ','
if P.tok != Scanner.RBRACE {
x := P.ParseExpression();
if P.tok == Scanner.COMMA {
P.Next();
lit.vals.Add(x);
if P.tok != Scanner.RBRACE {
P.ParseExpressionList(lit.vals);
}
} else if P.tok == Scanner.COLON {
p := new(AST.Pair);
p.pos = P.pos;
p.x = x;
P.Next();
p.y = P.ParseExpression();
lit.vals.Add(p);
if P.tok == Scanner.COMMA {
P.Next();
if P.tok != Scanner.RBRACE {
P.ParseExpressionPairList(lit.vals);
}
}
} else {
lit.vals.Add(x);
}
}
P.Expect(Scanner.RBRACE);
P.Ecart();
return lit;
}
func (P *Parser) ParseOperand() AST.Expr {
P.Trace("Operand");
@ -648,31 +600,34 @@ func (P *Parser) ParseOperand() AST.Expr {
case Scanner.LPAREN:
P.Next();
P.expr_lev++;
op = P.ParseExpression();
P.expr_lev--;
P.Expect(Scanner.RPAREN);
case Scanner.INT, Scanner.FLOAT, Scanner.STRING:
case Scanner.INT, Scanner.FLOAT:
lit := new(AST.Literal);
lit.pos, lit.tok, lit.val = P.pos, P.tok, P.val;
op = lit;
P.Next();
case Scanner.STRING:
lit := new(AST.Literal);
lit.pos, lit.tok = P.pos, P.tok;
for P.tok == Scanner.STRING {
lit.val += P.val;
P.Next();
}
op = lit;
case Scanner.FUNC:
op = P.ParseFunctionLit();
case Scanner.HASH:
P.Next();
typ := P.ParseType();
P.ParseCompositeLit(typ);
op = AST.NIL;
default:
if P.tok != Scanner.IDENT {
typ, ok := P.TryType();
if ok {
op = P.ParseCompositeLit(typ);
break;
}
typ, ok := P.TryType();
if ok {
op = typ;
break;
}
P.Error(P.pos, "operand expected");
@ -739,28 +694,7 @@ func (P *Parser) ParseCall(x AST.Expr) *AST.Call {
P.Expect(Scanner.LPAREN);
if P.tok != Scanner.RPAREN {
// first arguments could be a type if the call is to "new"
// - exclude type names because they could be expression starts
// - exclude "("'s because function types are not allowed and they indicate an expression
// - still a problem for "new(*T)" (the "*")
// - possibility: make "new" a keyword again (or disallow "*" types in new)
if P.tok != Scanner.IDENT && P.tok != Scanner.LPAREN {
typ, ok := P.TryType();
if ok {
call.args = AST.NewList();
call.args.Add(typ);
if P.tok == Scanner.COMMA {
P.Next();
if P.tok != Scanner.RPAREN {
P.ParseExpressionList(call.args);
}
}
} else {
call.args = P.ParseNewExpressionList();
}
} else {
call.args = P.ParseNewExpressionList();
}
call.args = P.ParseNewExpressionList();
}
P.Expect(Scanner.RPAREN);
@ -769,6 +703,47 @@ func (P *Parser) ParseCall(x AST.Expr) *AST.Call {
}
func (P *Parser) ParseCompositeLit(typ AST.Type) AST.Expr {
P.Trace("CompositeLit");
lit := new(AST.CompositeLit);
lit.pos = P.pos;
lit.typ = typ;
lit.vals = AST.NewList();
P.Expect(Scanner.LBRACE);
if P.tok != Scanner.RBRACE {
x := P.ParseExpression();
if P.tok == Scanner.COMMA {
P.Next();
lit.vals.Add(x);
if P.tok != Scanner.RBRACE {
P.ParseExpressionList(lit.vals);
}
} else if P.tok == Scanner.COLON {
p := new(AST.Pair);
p.pos = P.pos;
p.x = x;
P.Next();
p.y = P.ParseExpression();
lit.vals.Add(p);
if P.tok == Scanner.COMMA {
P.Next();
if P.tok != Scanner.RBRACE {
P.ParseExpressionPairList(lit.vals);
}
}
} else {
lit.vals.Add(x);
}
}
P.Expect(Scanner.RBRACE);
P.Ecart();
return lit;
}
func (P *Parser) ParsePrimaryExpr() AST.Expr {
P.Trace("PrimaryExpr");
@ -778,6 +753,12 @@ func (P *Parser) ParsePrimaryExpr() AST.Expr {
case Scanner.PERIOD: x = P.ParseSelectorOrTypeGuard(x);
case Scanner.LBRACK: x = P.ParseIndexOrSlice(x);
case Scanner.LPAREN: x = P.ParseCall(x);
case Scanner.LBRACE:
if P.expr_lev > 0 {
x = P.ParseCompositeLit(x);
} else {
goto exit;
}
default: goto exit;
}
}
@ -872,7 +853,7 @@ func (P *Parser) ParseSimpleStat() AST.Stat {
l.ident = AST.NIL;
}
P.Next(); // consume ":"
P.semi = true; // allow optional semicolon
P.opt_semi = true;
stat = l;
case
@ -969,6 +950,8 @@ func (P *Parser) ParseControlClause(keyword int) *AST.ControlClause {
P.Expect(keyword);
if P.tok != Scanner.LBRACE {
prev_lev := P.expr_lev;
P.expr_lev = 0;
if P.tok != Scanner.SEMICOLON {
ctrl.init = P.ParseSimpleStat();
ctrl.has_init = true;
@ -990,6 +973,7 @@ func (P *Parser) ParseControlClause(keyword int) *AST.ControlClause {
ctrl.expr, ctrl.has_expr = ctrl.init, ctrl.has_init;
ctrl.init, ctrl.has_init = AST.NIL, false;
}
P.expr_lev = prev_lev;
}
P.Ecart();
@ -1079,7 +1063,7 @@ func (P *Parser) ParseSwitchStat() *AST.SwitchStat {
stat.cases.Add(P.ParseCaseClause());
}
P.Expect(Scanner.RBRACE);
P.semi = true; // allow optional semicolon
P.opt_semi = true;
P.Ecart();
return stat;
@ -1127,7 +1111,7 @@ func (P *Parser) ParseSelectStat() {
P.ParseCommClause();
}
P.Expect(Scanner.RBRACE);
P.semi = true; // allow optional semicolon
P.opt_semi = true;
P.Ecart();
}
@ -1172,7 +1156,11 @@ func (P *Parser) ParseStatement() AST.Stat {
case Scanner.FUNC:
// for now we do not allow local function declarations
fallthrough;
case Scanner.MUL, Scanner.ARROW, Scanner.IDENT, Scanner.LPAREN:
case
// only the tokens that are legal top-level expression starts
Scanner.IDENT, Scanner.INT, Scanner.FLOAT, Scanner.STRING, Scanner.LPAREN, // operand
Scanner.LBRACK, Scanner.STRUCT, // composite type
Scanner.MUL, Scanner.AND, Scanner.ARROW: // unary
stat = P.ParseSimpleStat();
case Scanner.GO:
stat = P.ParseGoStat();
@ -1259,7 +1247,7 @@ func (P *Parser) ParseTypeSpec(exported bool) *AST.TypeDecl {
decl := new(AST.TypeDecl);
decl.ident = P.ParseIdent();
decl.typ = P.ParseType();
P.semi = true; // allow optional semicolon
P.opt_semi = true;
P.Ecart();
return decl;
@ -1320,7 +1308,7 @@ func (P *Parser) ParseDecl(exported bool, keyword int) *AST.Declaration {
}
}
P.Expect(Scanner.RPAREN);
P.semi = true; // allow optional semicolon
P.opt_semi = true;
} else {
decl.decls.Add(P.ParseSpec(exported, keyword));
@ -1348,9 +1336,6 @@ func (P *Parser) ParseFunctionDecl(exported bool) *AST.FuncDecl {
P.Expect(Scanner.FUNC);
P.OpenScope();
P.level--;
var recv *AST.VarDeclList;
if P.tok == Scanner.LPAREN {
pos := P.pos;
@ -1367,11 +1352,10 @@ func (P *Parser) ParseFunctionDecl(exported bool) *AST.FuncDecl {
fun.typ = P.ParseFunctionType();
fun.typ.recv = recv;
P.level++;
P.CloseScope();
if P.tok == Scanner.LBRACE {
P.scope_lev++;
fun.body = P.ParseBlock();
P.scope_lev--;
}
P.Ecart();
@ -1414,7 +1398,7 @@ func (P *Parser) ParseDeclaration() AST.Node {
exported := false;
if P.tok == Scanner.EXPORT {
if P.level == 0 {
if P.scope_lev == 0 {
exported = true;
} else {
P.Error(P.pos, "local declarations cannot be exported");
@ -1456,34 +1440,21 @@ func (P *Parser) ParseDeclaration() AST.Node {
func (P *Parser) ParseProgram() *AST.Program {
P.Trace("Program");
P.OpenScope();
pos := P.pos;
P.Expect(Scanner.PACKAGE);
ident := P.ParseIdent();
decls := AST.NewList();
{ P.OpenScope();
if P.level != 0 {
panic("incorrect scope level");
}
for P.tok == Scanner.IMPORT {
decls.Add(P.ParseDecl(false, Scanner.IMPORT));
P.OptSemicolon();
}
for P.tok == Scanner.IMPORT {
decls.Add(P.ParseDecl(false, Scanner.IMPORT));
P.OptSemicolon();
}
for P.tok != Scanner.EOF {
decls.Add(P.ParseDeclaration());
P.OptSemicolon();
}
if P.level != 0 {
panic("incorrect scope level");
}
P.CloseScope();
for P.tok != Scanner.EOF {
decls.Add(P.ParseDeclaration());
P.OptSemicolon();
}
P.CloseScope();
P.Ecart();
x := new(AST.Program);

View File

@ -58,7 +58,6 @@ export const (
DEFINE;
NOT;
ELLIPSIS;
HASH;
LPAREN;
RPAREN;
@ -158,7 +157,6 @@ export func TokenName(tok int) string {
case DEFINE: return ":=";
case NOT: return "!";
case ELLIPSIS: return "...";
case HASH: return "#";
case LPAREN: return "(";
case RPAREN: return ")";
@ -784,7 +782,6 @@ func (S *Scanner) Scan() (pos, tok int, val string) {
case '!': tok = S.Select2(NOT, NEQ);
case '&': tok = S.Select3(AND, AND_ASSIGN, '&', LAND);
case '|': tok = S.Select3(OR, OR_ASSIGN, '|', LOR);
case '#': tok = HASH;
default:
S.Error(pos, "illegal character " + CharString(ch));
tok = ILLEGAL;