]> Cypherpunks repositories - gostls13.git/commitdiff
Support for line comments trailing a field or declaration:
authorRobert Griesemer <gri@golang.org>
Mon, 15 Jun 2009 22:43:11 +0000 (15:43 -0700)
committerRobert Griesemer <gri@golang.org>
Mon, 15 Jun 2009 22:43:11 +0000 (15:43 -0700)
- ast: added extra fields
- parser: extended comment parsing to capture potential trailing comments

Cleanups:
- parser: more documentation, changed various identifiers from _-style to camelCase

R=r,rsc
DELTA=214  (84 added, 13 deleted, 117 changed)
OCL=30259
CL=30299

src/pkg/go/ast/ast.go
src/pkg/go/parser/parser.go

index 6cac8ea1ac023fc4d0ba099d3068009e07ab3cfd..dc96dbb05f1267b5dbd315c29339d923d440ae51 100644 (file)
@@ -113,7 +113,8 @@ type (
                Doc Comments;  // associated documentation; or nil
                Names []*Ident;  // field/method/parameter names; nil if anonymous field
                Type Expr;  // field/method/parameter type
-               Tag []*StringLit;  // field tag; nil if no tag
+               Tag []*StringLit;  // field tag; or nil
+               Comment *Comment;  // trailing comment on same line; or nil
        };
 )
 
@@ -672,15 +673,17 @@ type (
                Doc Comments;  // associated documentation; or nil
                Name *Ident;  // local package name (including "."); or nil
                Path []*StringLit;  // package path
+               Comment *Comment;  // trailing comment on same line; or nil
        };
 
        // A ValueSpec node represents a constant or variable declaration
        // (ConstSpec or VarSpec production).
        ValueSpec struct {
                Doc Comments;  // associated documentation; or nil
-               Names []*Ident;
+               Names []*Ident;  // value names
                Type Expr;  // value type; or nil
-               Values []Expr;
+               Values []Expr;  // initial values; or nil
+               Comment *Comment;  // trailing comment on same line; or nil
        };
 
        // A TypeSpec node represents a type declaration (TypeSpec production).
@@ -688,6 +691,7 @@ type (
                Doc Comments;  // associated documentation; or nil
                Name *Ident;  // type name
                Type Expr;
+               Comment *Comment;  // trailing comment on same line; or nil
        };
 )
 
index 6c607f47316152c27b4fd176c771387d27ddc55c..6179e0743904cbed8e74e7921887fa9688b63a49 100644 (file)
@@ -74,17 +74,19 @@ type parser struct {
        indent uint;  // indentation used for tracing output
 
        // Comments
+       // (comment indices and intervals index the comments vector)
        comments vector.Vector;  // list of collected, unassociated comments
-       last_doc interval;  // last comments interval of consecutive comments
+       lastComment int;  // index of last trailing comment
+       lastDoc interval;  // last interval of consequtive free-standing comments
 
-       // The next token
+       // Next token
        pos token.Position;  // token position
        tok token.Token;  // one token look-ahead
        lit []byte;  // token literal
 
        // Non-syntactic parser control
-       opt_semi bool;  // true if semicolon separator is optional in statement list
-       expr_lev int;  // < 0: in control clause, >= 0: in expression
+       optSemi bool;  // true if semicolon separator is optional in statement list
+       exprLev int;  // < 0: in control clause, >= 0: in expression
 };
 
 
@@ -123,6 +125,7 @@ func un/*trace*/(p *parser) {
 }
 
 
+// Advance to the next token.
 func (p *parser) next0() {
        // Because of one-token look-ahead, print the previous token
        // when tracing as it provides a more readable output. The
@@ -141,14 +144,14 @@ func (p *parser) next0() {
        }
 
        p.pos, p.tok, p.lit = p.scanner.Scan();
-       p.opt_semi = false;
+       p.optSemi = false;
 }
 
 
-// Collect a comment in the parser's comment list and return the line
-// on which the comment ends.
+// Consume a comment, add it to the parser's comment list,
+// and return the line on which the comment ends.
 //
-func (p *parser) collectComment() int {
+func (p *parser) consumeComment() int {
        // For /*-style comments, the comment may end on a different line.
        // Scan the comment for '\n' chars and adjust the end line accordingly.
        // (Note that the position of the next token may be even further down
@@ -168,48 +171,95 @@ func (p *parser) collectComment() int {
 }
 
 
-func (p *parser) getComments() interval {
-       // group adjacent comments, an empty line terminates a group
+// Consume a group of adjacent comments and return the interval of
+// indices into the parser's comment list. An empty line or non-comment
+// token terminates a comment group.
+//
+func (p *parser) consumeCommentGroup() interval {
        beg := p.comments.Len();
        endline := p.pos.Line;
        for p.tok == token.COMMENT && endline+1 >= p.pos.Line {
-               endline = p.collectComment();
+               endline = p.consumeComment();
        }
        end := p.comments.Len();
-       return interval {beg, end};
+       return interval{beg, end};
 }
 
 
-func (p *parser) getDoc() ast.Comments {
-       doc := p.last_doc;
-       n := doc.end - doc.beg;
+var empty interval;
 
-       if n <= 0 || p.comments.At(doc.end - 1).(*ast.Comment).EndLine + 1 < p.pos.Line {
-               // no comments or empty line between last comment and current token;
-               // do not use as documentation
-               return nil;
+// Advance to the next non-comment token.
+func (p *parser) next() {
+       p.lastComment = -1;
+       p.lastDoc = empty;
+
+       line := p.pos.Line;
+       p.next0();
+
+       if p.tok == token.COMMENT {
+               // the first comment may be a trailing comment
+               if p.pos.Line == line {
+                       // comment is on same line as previous token;
+                       // it is not considered part of a free-standing comment group
+                       p.consumeComment();
+                       if p.pos.Line != line {
+                               // the next token is on a different line, thus
+                               // the last comment is a trailing comment
+                               p.lastComment = p.comments.Len() - 1;
+                       }
+               }
+
+               // consume any successor comments
+               group := empty;
+               for p.tok == token.COMMENT {
+                       group = p.consumeCommentGroup();
+               }
+
+               if group.end > 0 && p.comments.At(group.end - 1).(*ast.Comment).EndLine + 1 == p.pos.Line {
+                       // there is a comment group and the next token is following on the
+                       // line immediately after the group, thus the group may be used as
+                       // documentation
+                       p.lastDoc = group;
+               }
        }
+}
 
-       // found immediately adjacent comment interval;
-       // use as documentation
-       c := make(ast.Comments, n);
-       for i := 0; i < n; i++ {
-               c[i] = p.comments.At(doc.beg + i).(*ast.Comment);
+
+// Get current trailing comment, if any.
+func (p *parser) getComment() *ast.Comment {
+       i := p.lastComment;
+       if i < 0 {
+               // no last comment
+               return nil;
        }
 
-       // remove comments from the general list
-       p.comments.Cut(doc.beg, doc.end);
+       // get comment and remove it from the general list
+       c := p.comments.At(i).(*ast.Comment);
+       p.comments.Set(i, nil);  // clear entry
+       p.lastComment = -1;
 
        return c;
 }
 
 
-func (p *parser) next() {
-       p.next0();
-       p.last_doc = interval{0, 0};
-       for p.tok == token.COMMENT {
-               p.last_doc = p.getComments();
+// Get current documentation comment group, if any.
+func (p *parser) getDoc() ast.Comments {
+       doc := p.lastDoc;
+       n := doc.end - doc.beg;
+       if n == 0 {
+               // no last comment group
+               return nil;
        }
+
+       // get comment group and remove if from the general list
+       c := make(ast.Comments, n);
+       for i := 0; i < n; i++ {
+               c[i] = p.comments.At(doc.beg + i).(*ast.Comment);
+               p.comments.Set(doc.beg + i, nil);  // clear entry
+       }
+       p.lastDoc = empty;
+
+       return c;
 }
 
 
@@ -224,7 +274,7 @@ func (p *parser) Error(pos token.Position, msg string) {
 }
 
 
-func (p *parser) error_expected(pos token.Position, msg string) {
+func (p *parser) errorExpected(pos token.Position, msg string) {
        msg = "expected " + msg;
        if pos.Offset == p.pos.Offset {
                // the error happened at the current position;
@@ -241,7 +291,7 @@ func (p *parser) error_expected(pos token.Position, msg string) {
 func (p *parser) expect(tok token.Token) token.Position {
        pos := p.pos;
        if p.tok != tok {
-               p.error_expected(pos, "'" + tok.String() + "'");
+               p.errorExpected(pos, "'" + tok.String() + "'");
        }
        p.next();  // make progress in any case
        return pos;
@@ -327,7 +377,7 @@ func (p *parser) parseType() ast.Expr {
        typ := p.tryType();
 
        if typ == nil {
-               p.error_expected(p.pos, "type");
+               p.errorExpected(p.pos, "type");
                p.next();  // make progress
                return &ast.BadExpr{p.pos};
        }
@@ -361,14 +411,14 @@ func (p *parser) parseTypeName() ast.Expr {
 }
 
 
-func (p *parser) parseArrayType(ellipsis_ok bool) ast.Expr {
+func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr {
        if p.trace {
                defer un(trace(p, "ArrayType"));
        }
 
        lbrack := p.expect(token.LBRACK);
        var len ast.Expr;
-       if ellipsis_ok && p.tok == token.ELLIPSIS {
+       if ellipsisOk && p.tok == token.ELLIPSIS {
                len = &ast.Ellipsis{p.pos};
                p.next();
        } else if p.tok != token.RBRACK {
@@ -384,10 +434,10 @@ func (p *parser) parseArrayType(ellipsis_ok bool) ast.Expr {
 func (p *parser) makeIdentList(list *vector.Vector) []*ast.Ident {
        idents := make([]*ast.Ident, list.Len());
        for i := 0; i < list.Len(); i++ {
-               ident, is_ident := list.At(i).(*ast.Ident);
-               if !is_ident {
+               ident, isIdent := list.At(i).(*ast.Ident);
+               if !isIdent {
                        pos := list.At(i).(ast.Expr).Pos();
-                       p.error_expected(pos, "identifier");
+                       p.errorExpected(pos, "identifier");
                        idents[i] = &ast.Ident{pos, ""};
                }
                idents[i] = ident;
@@ -435,12 +485,12 @@ func (p *parser) parseFieldDecl() *ast.Field {
                        // TODO check that this looks like a type
                        typ = list.At(0).(ast.Expr);
                } else {
-                       p.error_expected(p.pos, "anonymous field");
+                       p.errorExpected(p.pos, "anonymous field");
                        typ = &ast.BadExpr{p.pos};
                }
        }
 
-       return &ast.Field{doc, idents, typ, tag};
+       return &ast.Field{doc, idents, typ, tag, nil};
 }
 
 
@@ -458,16 +508,19 @@ func (p *parser) parseStructType() *ast.StructType {
 
                list := vector.New(0);
                for p.tok != token.RBRACE && p.tok != token.EOF {
-                       list.Push(p.parseFieldDecl());
+                       f := p.parseFieldDecl();
+                       list.Push(f);
                        if p.tok == token.SEMICOLON {
                                p.next();
+                               f.Comment = p.getComment();
                        } else {
+                               f.Comment = p.getComment();
                                break;
                        }
                }
 
                rbrace = p.expect(token.RBRACE);
-               p.opt_semi = true;
+               p.optSemi = true;
 
                // convert vector
                fields = make([]*ast.Field, list.Len());
@@ -492,8 +545,8 @@ func (p *parser) parsePointerType() *ast.StarExpr {
 }
 
 
-func (p *parser) tryParameterType(ellipsis_ok bool) ast.Expr {
-       if ellipsis_ok && p.tok == token.ELLIPSIS {
+func (p *parser) tryParameterType(ellipsisOk bool) ast.Expr {
+       if ellipsisOk && p.tok == token.ELLIPSIS {
                pos := p.pos;
                p.next();
                if p.tok != token.RPAREN {
@@ -506,10 +559,10 @@ func (p *parser) tryParameterType(ellipsis_ok bool) ast.Expr {
 }
 
 
-func (p *parser) parseParameterType(ellipsis_ok bool) ast.Expr {
-       typ := p.tryParameterType(ellipsis_ok);
+func (p *parser) parseParameterType(ellipsisOk bool) ast.Expr {
+       typ := p.tryParameterType(ellipsisOk);
        if typ == nil {
-               p.error_expected(p.pos, "type");
+               p.errorExpected(p.pos, "type");
                p.next();  // make progress
                typ = &ast.BadExpr{p.pos};
        }
@@ -517,7 +570,7 @@ func (p *parser) parseParameterType(ellipsis_ok bool) ast.Expr {
 }
 
 
-func (p *parser) parseParameterDecl(ellipsis_ok bool) (*vector.Vector, ast.Expr) {
+func (p *parser) parseParameterDecl(ellipsisOk bool) (*vector.Vector, ast.Expr) {
        if p.trace {
                defer un(trace(p, "ParameterDecl"));
        }
@@ -526,7 +579,7 @@ func (p *parser) parseParameterDecl(ellipsis_ok bool) (*vector.Vector, ast.Expr)
        list := vector.New(0);
        for {
                // TODO do not allow ()'s here
-               list.Push(p.parseParameterType(ellipsis_ok));
+               list.Push(p.parseParameterType(ellipsisOk));
                if p.tok == token.COMMA {
                        p.next();
                } else {
@@ -535,36 +588,36 @@ func (p *parser) parseParameterDecl(ellipsis_ok bool) (*vector.Vector, ast.Expr)
        }
 
        // if we had a list of identifiers, it must be followed by a type
-       typ := p.tryParameterType(ellipsis_ok);
+       typ := p.tryParameterType(ellipsisOk);
 
        return list, typ;
 }
 
 
-func (p *parser) parseParameterList(ellipsis_ok bool) []*ast.Field {
+func (p *parser) parseParameterList(ellipsisOk bool) []*ast.Field {
        if p.trace {
                defer un(trace(p, "ParameterList"));
        }
 
-       list, typ := p.parseParameterDecl(ellipsis_ok);
+       list, typ := p.parseParameterDecl(ellipsisOk);
        if typ != nil {
                // IdentifierList Type
                idents := p.makeIdentList(list);
                list.Init(0);
-               list.Push(&ast.Field{nil, idents, typ, nil});
+               list.Push(&ast.Field{nil, idents, typ, nil, nil});
 
                for p.tok == token.COMMA {
                        p.next();
                        idents := p.parseIdentList(nil);
-                       typ := p.parseParameterType(ellipsis_ok);
-                       list.Push(&ast.Field{nil, idents, typ, nil});
+                       typ := p.parseParameterType(ellipsisOk);
+                       list.Push(&ast.Field{nil, idents, typ, nil, nil});
                }
 
        } else {
                // Type { "," Type } (anonymous parameters)
                // convert list of types into list of *Param
                for i := 0; i < list.Len(); i++ {
-                       list.Set(i, &ast.Field{nil, nil, list.At(i).(ast.Expr), nil});
+                       list.Set(i, &ast.Field{Type: list.At(i).(ast.Expr)});
                }
        }
 
@@ -578,7 +631,7 @@ func (p *parser) parseParameterList(ellipsis_ok bool) []*ast.Field {
 }
 
 
-func (p *parser) parseParameters(ellipsis_ok bool) []*ast.Field {
+func (p *parser) parseParameters(ellipsisOk bool) []*ast.Field {
        if p.trace {
                defer un(trace(p, "Parameters"));
        }
@@ -586,7 +639,7 @@ func (p *parser) parseParameters(ellipsis_ok bool) []*ast.Field {
        var params []*ast.Field;
        p.expect(token.LPAREN);
        if p.tok != token.RPAREN {
-               params = p.parseParameterList(ellipsis_ok);
+               params = p.parseParameterList(ellipsisOk);
        }
        p.expect(token.RPAREN);
 
@@ -601,12 +654,12 @@ func (p *parser) parseResult() []*ast.Field {
 
        var results []*ast.Field;
        if p.tok == token.LPAREN {
-               results = p.parseParameters(false);
+               results = p.parseParameters(false);p
        } else if p.tok != token.FUNC {
                typ := p.tryType();
                if typ != nil {
                        results = make([]*ast.Field, 1);
-                       results[0] = &ast.Field{nil, nil, typ, nil};
+                       results[0] = &ast.Field{Type: typ};
                }
        }
 
@@ -647,7 +700,7 @@ func (p *parser) parseMethodSpec() *ast.Field {
        var idents []*ast.Ident;
        var typ ast.Expr;
        x := p.parseQualifiedIdent();
-       if tmp, is_ident := x.(*ast.Ident); is_ident && (p.tok == token.COMMA || p.tok == token.LPAREN) {
+       if tmp, isIdent := x.(*ast.Ident); isIdent && (p.tok == token.COMMA || p.tok == token.LPAREN) {
                // methods
                idents = p.parseIdentList(x);
                params, results := p.parseSignature();
@@ -657,7 +710,7 @@ func (p *parser) parseMethodSpec() *ast.Field {
                typ = x;
        }
 
-       return &ast.Field{doc, idents, typ, nil};
+       return &ast.Field{doc, idents, typ, nil, nil};
 }
 
 
@@ -682,7 +735,7 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType {
                }
 
                rbrace = p.expect(token.RBRACE);
-               p.opt_semi = true;
+               p.optSemi = true;
 
                // convert vector
                methods = make([]*ast.Field, list.Len());
@@ -734,10 +787,10 @@ func (p *parser) parseChanType() *ast.ChanType {
 }
 
 
-func (p *parser) tryRawType(ellipsis_ok bool) ast.Expr {
+func (p *parser) tryRawType(ellipsisOk bool) ast.Expr {
        switch p.tok {
        case token.IDENT: return p.parseTypeName();
-       case token.LBRACK: return p.parseArrayType(ellipsis_ok);
+       case token.LBRACK: return p.parseArrayType(ellipsisOk);
        case token.STRUCT: return p.parseStructType();
        case token.MUL: return p.parsePointerType();
        case token.FUNC: return p.parseFuncType();
@@ -780,19 +833,19 @@ func (p *parser) parseStatementList() []ast.Stmt {
        }
 
        list := vector.New(0);
-       expect_semi := false;
+       expectSemi := false;
        for p.tok != token.CASE && p.tok != token.DEFAULT && p.tok != token.RBRACE && p.tok != token.EOF {
-               if expect_semi {
+               if expectSemi {
                        p.expect(token.SEMICOLON);
-                       expect_semi = false;
+                       expectSemi = false;
                }
                list.Push(p.parseStatement());
                if p.tok == token.SEMICOLON {
                        p.next();
-               } else if p.opt_semi {
-                       p.opt_semi = false;  // "consume" optional semicolon
+               } else if p.optSemi {
+                       p.optSemi = false;  // "consume" optional semicolon
                } else {
-                       expect_semi = true;
+                       expectSemi = true;
                }
        }
 
@@ -808,7 +861,7 @@ func (p *parser) parseBlockStmt() *ast.BlockStmt {
        lbrace := p.expect(token.LBRACE);
        list := p.parseStatementList();
        rbrace := p.expect(token.RBRACE);
-       p.opt_semi = true;
+       p.optSemi = true;
 
        return &ast.BlockStmt{lbrace, list, rbrace};
 }
@@ -848,10 +901,10 @@ func (p *parser) parseFuncLit() ast.Expr {
        }
 
        typ := p.parseFuncType();
-       p.expr_lev++;
+       p.exprLev++;
        body := p.parseBlockStmt();
-       p.opt_semi = false;  // function body requires separating ";"
-       p.expr_lev--;
+       p.optSemi = false;  // function body requires separating ";"
+       p.exprLev--;
 
        return &ast.FuncLit{typ, body};
 }
@@ -895,9 +948,9 @@ func (p *parser) parseOperand() ast.Expr {
        case token.LPAREN:
                lparen := p.pos;
                p.next();
-               p.expr_lev++;
+               p.exprLev++;
                x := p.parseExpression();
-               p.expr_lev--;
+               p.exprLev--;
                rparen := p.expect(token.RPAREN);
                return &ast.ParenExpr{lparen, x, rparen};
 
@@ -911,7 +964,7 @@ func (p *parser) parseOperand() ast.Expr {
                }
        }
 
-       p.error_expected(p.pos, "operand");
+       p.errorExpected(p.pos, "operand");
        p.next();  // make progress
        return &ast.BadExpr{p.pos};
 }
@@ -951,14 +1004,14 @@ func (p *parser) parseIndex(x ast.Expr) ast.Expr {
        }
 
        p.expect(token.LBRACK);
-       p.expr_lev++;
+       p.exprLev++;
        begin := p.parseExpression();
        var end ast.Expr;
        if p.tok == token.COLON {
                p.next();
                end = p.parseExpression();
        }
-       p.expr_lev--;
+       p.exprLev--;
        p.expect(token.RBRACK);
 
        return &ast.IndexExpr{x, begin, end};
@@ -1064,13 +1117,13 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr {
        case *ast.UnaryExpr:
                if t.Op == token.RANGE {
                        // the range operator is only allowed at the top of a for statement
-                       p.error_expected(x.Pos(), "expression");
+                       p.errorExpected(x.Pos(), "expression");
                        x = &ast.BadExpr{x.Pos()};
                }
        case *ast.BinaryExpr:
        default:
                // all other nodes are not proper expressions
-               p.error_expected(x.Pos(), "expression");
+               p.errorExpected(x.Pos(), "expression");
                x = &ast.BadExpr{x.Pos()};
        }
        return x;
@@ -1117,11 +1170,11 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
        case *ast.UnaryExpr:
                if t.Op == token.RANGE {
                        // the range operator is only allowed at the top of a for statement
-                       p.error_expected(x.Pos(), "expression");
+                       p.errorExpected(x.Pos(), "expression");
                        x = &ast.BadExpr{x.Pos()};
                }
        case *ast.ArrayType:
-               if len, is_ellipsis := t.Len.(*ast.Ellipsis); is_ellipsis {
+               if len, isEllipsis := t.Len.(*ast.Ellipsis); isEllipsis {
                        p.Error(len.Pos(), "expected array length, found '...'");
                        x = &ast.BadExpr{x.Pos()};
                }
@@ -1144,7 +1197,7 @@ L:        for {
                case token.LBRACK: x = p.parseIndex(p.checkExpr(x));
                case token.LPAREN: x = p.parseCallOrConversion(p.checkExprOrType(x));
                case token.LBRACE:
-                       if isCompositeLitType(x) && (p.expr_lev >= 0 || !isTypeName(x)) {
+                       if isCompositeLitType(x) && (p.exprLev >= 0 || !isTypeName(x)) {
                                x = p.parseCompositeLit(x);
                        } else {
                                break L;
@@ -1214,7 +1267,7 @@ func (p *parser) parseExpression() ast.Expr {
 // Statements
 
 
-func (p *parser) parseSimpleStmt(label_ok bool) ast.Stmt {
+func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
        if p.trace {
                defer un(trace(p, "SimpleStmt"));
        }
@@ -1225,8 +1278,8 @@ func (p *parser) parseSimpleStmt(label_ok bool) ast.Stmt {
        case token.COLON:
                // labeled statement
                p.next();
-               if label_ok && len(x) == 1 {
-                       if label, is_ident := x[0].(*ast.Ident); is_ident {
+               if labelOk && len(x) == 1 {
+                       if label, isIdent := x[0].(*ast.Ident); isIdent {
                                return &ast.LabeledStmt{label, p.parseStatement()};
                        }
                }
@@ -1267,10 +1320,10 @@ func (p *parser) parseSimpleStmt(label_ok bool) ast.Stmt {
 
 func (p *parser) parseCallExpr() *ast.CallExpr {
        x := p.parseExpression();
-       if call, is_call := x.(*ast.CallExpr); is_call {
+       if call, isCall := x.(*ast.CallExpr); isCall {
                return call;
        }
-       p.error_expected(x.Pos(), "function/method call");
+       p.errorExpected(x.Pos(), "function/method call");
        return nil;
 }
 
@@ -1338,8 +1391,8 @@ func (p *parser) isExpr(s ast.Stmt) bool {
        if s == nil {
                return true;
        }
-       dummy, is_expr := s.(*ast.ExprStmt);
-       return is_expr;
+       dummy, isExpr := s.(*ast.ExprStmt);
+       return isExpr;
 }
 
 
@@ -1347,7 +1400,7 @@ func (p *parser) makeExpr(s ast.Stmt) ast.Expr {
        if s == nil {
                return nil;
        }
-       if es, is_expr := s.(*ast.ExprStmt); is_expr {
+       if es, isExpr := s.(*ast.ExprStmt); isExpr {
                return p.checkExpr(es.X);
        }
        p.Error(s.Pos(), "expected condition, found simple statement");
@@ -1357,8 +1410,8 @@ func (p *parser) makeExpr(s ast.Stmt) ast.Expr {
 
 func (p *parser) parseControlClause(isForStmt bool) (s1, s2, s3 ast.Stmt) {
        if p.tok != token.LBRACE {
-               prev_lev := p.expr_lev;
-               p.expr_lev = -1;
+               prevLev := p.exprLev;
+               p.exprLev = -1;
 
                if p.tok != token.SEMICOLON {
                        s1 = p.parseSimpleStmt(false);
@@ -1379,7 +1432,7 @@ func (p *parser) parseControlClause(isForStmt bool) (s1, s2, s3 ast.Stmt) {
                        s1, s2 = nil, s1;
                }
 
-               p.expr_lev = prev_lev;
+               p.exprLev = prevLev;
        }
 
        return s1, s2, s3;
@@ -1464,7 +1517,7 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
                        cases.Push(p.parseCaseClause());
                }
                rbrace := p.expect(token.RBRACE);
-               p.opt_semi = true;
+               p.optSemi = true;
                body := &ast.BlockStmt{lbrace, makeStmtList(cases), rbrace};
                return &ast.SwitchStmt{pos, s1, p.makeExpr(s2), body};
        }
@@ -1477,7 +1530,7 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
                cases.Push(p.parseTypeCaseClause());
        }
        rbrace := p.expect(token.RBRACE);
-       p.opt_semi = true;
+       p.optSemi = true;
        body := &ast.BlockStmt{lbrace, makeStmtList(cases), rbrace};
        return &ast.TypeSwitchStmt{pos, s1, s2, body};
 }
@@ -1536,7 +1589,7 @@ func (p *parser) parseSelectStmt() *ast.SelectStmt {
                cases.Push(p.parseCommClause());
        }
        rbrace := p.expect(token.RBRACE);
-       p.opt_semi = true;
+       p.optSemi = true;
        body := &ast.BlockStmt{lbrace, makeStmtList(cases), rbrace};
 
        return &ast.SelectStmt{pos, body};
@@ -1552,10 +1605,10 @@ func (p *parser) parseForStmt() ast.Stmt {
        s1, s2, s3 := p.parseControlClause(true);
        body := p.parseBlockStmt();
 
-       if as, is_as := s2.(*ast.AssignStmt); is_as {
+       if as, isAssign := s2.(*ast.AssignStmt); isAssign {
                // possibly a for statement with a range clause; check assignment operator
                if as.Tok != token.ASSIGN && as.Tok != token.DEFINE {
-                       p.error_expected(as.TokPos, "'=' or ':='");
+                       p.errorExpected(as.TokPos, "'=' or ':='");
                        return &ast.BadStmt{pos};
                }
                // check lhs
@@ -1567,19 +1620,19 @@ func (p *parser) parseForStmt() ast.Stmt {
                case 1:
                        key = as.Lhs[0];
                default:
-                       p.error_expected(as.Lhs[0].Pos(), "1 or 2 expressions");
+                       p.errorExpected(as.Lhs[0].Pos(), "1 or 2 expressions");
                        return &ast.BadStmt{pos};
                }
                // check rhs
                if len(as.Rhs) != 1 {
-                       p.error_expected(as.Rhs[0].Pos(), "1 expressions");
+                       p.errorExpected(as.Rhs[0].Pos(), "1 expressions");
                        return &ast.BadStmt{pos};
                }
-               if rhs, is_unary := as.Rhs[0].(*ast.UnaryExpr); is_unary && rhs.Op == token.RANGE {
+               if rhs, isUnary := as.Rhs[0].(*ast.UnaryExpr); isUnary && rhs.Op == token.RANGE {
                        // rhs is range expression; check lhs
                        return &ast.RangeStmt{pos, key, value, as.TokPos, as.Tok, rhs.X, body}
                } else {
-                       p.error_expected(s2.Pos(), "range clause");
+                       p.errorExpected(s2.Pos(), "range clause");
                        return &ast.BadStmt{pos};
                }
        } else {
@@ -1630,7 +1683,7 @@ func (p *parser) parseStatement() ast.Stmt {
        }
 
        // no statement found
-       p.error_expected(p.pos, "statement");
+       p.errorExpected(p.pos, "statement");
        p.next();  // make progress
        return &ast.BadStmt{p.pos};
 }
@@ -1661,7 +1714,7 @@ func parseImportSpec(p *parser, doc ast.Comments) ast.Spec {
                p.expect(token.STRING);  // use expect() error handling
        }
 
-       return &ast.ImportSpec{doc, ident, path};
+       return &ast.ImportSpec{doc, ident, path, nil};
 }
 
 
@@ -1678,7 +1731,8 @@ func parseConstSpec(p *parser, doc ast.Comments) ast.Spec {
                values = p.parseExpressionList();
        }
 
-       return &ast.ValueSpec{doc, idents, typ, values};
+       // TODO get trailing comments
+       return &ast.ValueSpec{doc, idents, typ, values, nil};
 }
 
 
@@ -1690,7 +1744,8 @@ func parseTypeSpec(p *parser, doc ast.Comments) ast.Spec {
        ident := p.parseIdent();
        typ := p.parseType();
 
-       return &ast.TypeSpec{doc, ident, typ};
+       // TODO get trailing comments
+       return &ast.TypeSpec{doc, ident, typ, nil};
 }
 
 
@@ -1707,7 +1762,8 @@ func parseVarSpec(p *parser, doc ast.Comments) ast.Spec {
                values = p.parseExpressionList();
        }
 
-       return &ast.ValueSpec{doc, idents, typ, values};
+       // TODO get trailing comments
+       return &ast.ValueSpec{doc, idents, typ, values, nil};
 }
 
 
@@ -1733,7 +1789,7 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen
                        }
                }
                rparen = p.expect(token.RPAREN);
-               p.opt_semi = true;
+               p.optSemi = true;
        } else {
                list.Push(f(p, doc));
        }
@@ -1757,19 +1813,19 @@ func (p *parser) parseReceiver() *ast.Field {
 
        // must have exactly one receiver
        if len(par) != 1 || len(par) == 1 && len(par[0].Names) > 1 {
-               p.error_expected(pos, "exactly one receiver");
-               return &ast.Field{nil, nil, &ast.BadExpr{noPos}, nil};
+               p.errorExpected(pos, "exactly one receiver");
+               return &ast.Field{Type: &ast.BadExpr{noPos}};
        }
 
        recv := par[0];
 
        // recv type must be TypeName or *TypeName
        base := recv.Type;
-       if ptr, is_ptr := base.(*ast.StarExpr); is_ptr {
+       if ptr, isPtr := base.(*ast.StarExpr); isPtr {
                base = ptr.X;
        }
        if !isTypeName(base) {
-               p.error_expected(base.Pos(), "type name");
+               p.errorExpected(base.Pos(), "type name");
        }
 
        return recv;
@@ -1815,7 +1871,7 @@ func (p *parser) parseDeclaration() ast.Decl {
                return p.parseFunctionDecl();
        default:
                pos := p.pos;
-               p.error_expected(pos, "declaration");
+               p.errorExpected(pos, "declaration");
                p.next();  // make progress
                return &ast.BadDecl{pos};
        }
@@ -1881,9 +1937,20 @@ func (p *parser) parsePackage() *ast.Program {
        }
 
        // convert comments list
-       comments := make([]*ast.Comment, p.comments.Len());
+       // 1) determine number of remaining comments
+       n := 0;
        for i := 0; i < p.comments.Len(); i++ {
-               comments[i] = p.comments.At(i).(*ast.Comment);
+               if p.comments.At(i) != nil {
+                       n++;
+               }
+       }
+       // 2) convert the remaining comments
+       comments := make([]*ast.Comment, n);
+       for i, j := 0, 0; i < p.comments.Len(); i++ {
+               if p.comments.At(i) != nil {
+                       comments[j] = p.comments.At(i).(*ast.Comment);
+                       j++;
+               }
        }
 
        return &ast.Program{comment, pos, ident, decls, comments};