]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: handle pragmas immediately with -newparser=1
authorMatthew Dempsky <mdempsky@google.com>
Tue, 30 Aug 2016 23:31:53 +0000 (16:31 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Wed, 31 Aug 2016 19:49:53 +0000 (19:49 +0000)
Instead of saving all pragmas and processing them after parsing is
finished, process them immediately during scanning like the current
lexer does.

This is a bit unfortunate because it means we can't use
syntax.ParseFile to concurrently parse files yet, but it fixes how we
report syntax errors in the presence of //line pragmas.

While here, add a bunch more gcCompat entries to syntax/parser.go to
get "go build -toolexec='toolstash -cmp' std cmd" passing. There are
still a few remaining cases only triggered building unit tests, but
this seems like a nice checkpoint.

Change-Id: Iaf3bbcf2849857a460496f31eea228e0c585ce13
Reviewed-on: https://go-review.googlesource.com/28226
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/cmd/compile/internal/gc/lex.go
src/cmd/compile/internal/gc/noder.go
src/cmd/compile/internal/syntax/dumper_test.go
src/cmd/compile/internal/syntax/nodes.go
src/cmd/compile/internal/syntax/parser.go
src/cmd/compile/internal/syntax/parser_test.go
src/cmd/compile/internal/syntax/printer_test.go
src/cmd/compile/internal/syntax/scanner.go
src/cmd/compile/internal/syntax/scanner_test.go
src/cmd/compile/internal/syntax/syntax.go

index 309da9045884f0ef752e1d5821554ec3ecd35a86..5d3da580102376f6c1e0c8a2ad5e873874693658 100644 (file)
@@ -7,6 +7,7 @@ package gc
 import (
        "bufio"
        "bytes"
+       "cmd/compile/internal/syntax"
        "cmd/internal/obj"
        "fmt"
        "io"
@@ -60,7 +61,7 @@ func plan9quote(s string) string {
        return s
 }
 
-type Pragma uint16
+type Pragma syntax.Pragma
 
 const (
        Nointerface       Pragma = 1 << iota
index 8e2b9ef5fc708bdbcd258be26bf41a3d2170543d..a945038c7a16d289031dfd426cc8a40e1f5f73fb 100644 (file)
@@ -6,7 +6,6 @@ package gc
 
 import (
        "fmt"
-       "sort"
        "strconv"
        "strings"
        "unicode/utf8"
@@ -15,23 +14,19 @@ import (
 )
 
 func parseFile(filename string) {
-       errh := func(_, line int, msg string) {
-               yyerrorl(lexlineno+int32(line)-1, "%s", msg)
-       }
-
-       file, err := syntax.ReadFile(filename, errh, 0)
+       p := noder{baseline: lexlineno}
+       file, err := syntax.ReadFile(filename, p.error, p.pragma, 0)
        if err != nil {
                Fatalf("syntax.ReadFile %s: %v", filename, err)
        }
 
-       p := noder{pragmas: file.Pragmas}
-
-       p.lineno(file.PkgName)
-       mkpackage(file.PkgName.Value)
+       p.file(file)
 
-       xtop = append(xtop, p.decls(file.DeclList, true)...)
-       p.globalPragmas()
-       lexlineno += p.maxline
+       if !imported_unsafe {
+               for _, x := range p.linknames {
+                       p.error(0, x, "//go:linkname only allowed in Go files that import \"unsafe\"")
+               }
+       }
 
        if nsyntaxerrors == 0 {
                testdclstack()
@@ -40,13 +35,21 @@ func parseFile(filename string) {
 
 // noder transforms package syntax's AST into a Nod tree.
 type noder struct {
-       indent  []byte
-       pragmas []syntax.Pragma
-       pline   int32
-       maxline int32
+       baseline  int32
+       linknames []int // tracks //go:linkname lines
 }
 
-func (p *noder) decls(decls []syntax.Decl, top bool) (l []*Node) {
+func (p *noder) file(file *syntax.File) {
+       p.lineno(file.PkgName)
+       mkpackage(file.PkgName.Value)
+
+       xtop = append(xtop, p.decls(file.DeclList)...)
+
+       lexlineno = p.baseline + int32(file.Lines) - 1
+       lineno = lexlineno
+}
+
+func (p *noder) decls(decls []syntax.Decl) (l []*Node) {
        var lastConstGroup *syntax.Group
        var lastConstRHS []*Node
        var iotaVal int32
@@ -59,6 +62,7 @@ func (p *noder) decls(decls []syntax.Decl, top bool) (l []*Node) {
 
                case *syntax.VarDecl:
                        l = append(l, p.varDecl(decl)...)
+
                case *syntax.ConstDecl:
                        // Tricky to handle golang.org/issue/15550 correctly.
 
@@ -91,10 +95,6 @@ func (p *noder) decls(decls []syntax.Decl, top bool) (l []*Node) {
                default:
                        panic("unhandled Decl")
                }
-
-               if top {
-                       p.pline = p.maxline
-               }
        }
 
        return
@@ -102,7 +102,7 @@ func (p *noder) decls(decls []syntax.Decl, top bool) (l []*Node) {
 
 func (p *noder) importDecl(imp *syntax.ImportDecl) {
        val := p.basicLit(imp.Path)
-       importfile(&val, p.indent)
+       importfile(&val, nil)
        ipkg := importpkg
        importpkg = nil
 
@@ -159,6 +159,7 @@ func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
                exprs = p.exprList(decl.Values)
        }
 
+       p.lineno(decl)
        return variter(names, typ, exprs)
 }
 
@@ -216,7 +217,7 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node {
                }
        }
 
-       pragma := p.pragma()
+       pragma := Pragma(fun.Pragma)
 
        f.Nbody.Set(body)
        f.Noescape = pragma&Noescape != 0
@@ -224,7 +225,7 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node {
                Yyerror("can only use //go:noescape with external func implementations")
        }
        f.Func.Pragma = pragma
-       lineno = lexlineno + int32(fun.EndLine) - 1
+       lineno = p.baseline + int32(fun.EndLine) - 1
        f.Func.Endlineno = lineno
 
        funcbody(f)
@@ -345,15 +346,19 @@ func (p *noder) expr(expr syntax.Expr) *Node {
                if expr.Type != nil {
                        n.Right = p.expr(expr.Type)
                }
-               // TODO(mdempsky): Should apply wrapname to n.List nodes.
-               n.List.Set(p.exprs(expr.ElemList))
+               l := p.exprs(expr.ElemList)
+               for i, e := range l {
+                       l[i] = p.wrapname(expr.ElemList[i], e)
+               }
+               n.List.Set(l)
+               lineno = p.baseline + int32(expr.EndLine) - 1
                return n
        case *syntax.KeyValueExpr:
                return p.nod(expr, OKEY, p.expr(expr.Key), p.wrapname(expr.Value, p.expr(expr.Value)))
        case *syntax.FuncLit:
                closurehdr(p.typeExpr(expr.Type))
                body := p.stmts(expr.Body)
-               lineno = lexlineno + int32(expr.EndLine) - 1
+               lineno = p.baseline + int32(expr.EndLine) - 1
                return p.setlineno(expr, closurebody(body))
        case *syntax.ParenExpr:
                return p.nod(expr, OPAREN, p.expr(expr.X), nil)
@@ -398,7 +403,9 @@ func (p *noder) expr(expr syntax.Expr) *Node {
                                x = unparen(x) // TODO(mdempsky): Needed?
                                if x.Op == OCOMPLIT {
                                        // Special case for &T{...}: turn into (*T){...}.
-                                       x.Right = p.nod(expr, OIND, x.Right, nil)
+                                       // TODO(mdempsky): Switch back to p.nod after we
+                                       // get rid of gcCompat.
+                                       x.Right = Nod(OIND, x.Right, nil)
                                        x.Right.Implicit = true
                                        return x
                                }
@@ -577,7 +584,7 @@ func (p *noder) stmt(stmt syntax.Stmt) *Node {
        case *syntax.SendStmt:
                return p.nod(stmt, OSEND, p.expr(stmt.Chan), p.expr(stmt.Value))
        case *syntax.DeclStmt:
-               return liststmt(p.decls(stmt.DeclList, false))
+               return liststmt(p.decls(stmt.DeclList))
        case *syntax.AssignStmt:
                if stmt.Op != 0 && stmt.Op != syntax.Def {
                        n := p.nod(stmt, OASOP, p.expr(stmt.Lhs), p.expr(stmt.Rhs))
@@ -975,10 +982,7 @@ func (p *noder) setlineno(src syntax.Node, dst *Node) *Node {
                // TODO(mdempsky): Shouldn't happen. Fix package syntax.
                return dst
        }
-       if l > p.maxline {
-               p.maxline = l
-       }
-       dst.Lineno = lexlineno + l - 1
+       dst.Lineno = p.baseline + l - 1
        return dst
 }
 
@@ -991,78 +995,57 @@ func (p *noder) lineno(n syntax.Node) {
                // TODO(mdempsky): Shouldn't happen. Fix package syntax.
                return
        }
-       if l > p.maxline {
-               p.maxline = l
-       }
-       lineno = lexlineno + l - 1
+       lineno = p.baseline + l - 1
 }
 
-func (p *noder) pragma() Pragma {
-       lo := sort.Search(len(p.pragmas), func(i int) bool { return int32(p.pragmas[i].Line) >= p.pline })
-       hi := sort.Search(len(p.pragmas), func(i int) bool { return int32(p.pragmas[i].Line) > p.maxline })
+func (p *noder) error(_, line int, msg string) {
+       yyerrorl(p.baseline+int32(line)-1, "%s", msg)
+}
 
-       var res Pragma
-       for _, prag := range p.pragmas[lo:hi] {
-               text := prag.Text
-               if !strings.HasPrefix(text, "go:") {
-                       continue
+func (p *noder) pragma(pos, line int, text string) syntax.Pragma {
+       switch {
+       case strings.HasPrefix(text, "line "):
+               i := strings.IndexByte(text, ':')
+               if i < 0 {
+                       break
+               }
+               n, err := strconv.Atoi(text[i+1:])
+               if err != nil {
+                       // todo: make this an error instead? it is almost certainly a bug.
+                       break
                }
+               if n > 1e8 {
+                       p.error(pos, line, "line number out of range")
+                       errorexit()
+               }
+               if n <= 0 {
+                       break
+               }
+               lexlineno = p.baseline + int32(line)
+               linehistupdate(text[5:i], n)
+
+       case strings.HasPrefix(text, "go:linkname "):
+               // Record line number so we can emit an error later if
+               // the file doesn't import package unsafe.
+               p.linknames = append(p.linknames, line)
+
+               f := strings.Fields(text)
+               if len(f) != 3 {
+                       p.error(pos, line, "usage: //go:linkname localname linkname")
+                       break
+               }
+               Lookup(f[1]).Linkname = f[2]
 
+       case strings.HasPrefix(text, "go:cgo_"):
+               pragcgobuf += pragcgo(text)
+               fallthrough // because of //go:cgo_unsafe_args
+       default:
                verb := text
                if i := strings.Index(text, " "); i >= 0 {
                        verb = verb[:i]
                }
-
-               res |= PragmaValue(verb)
+               return syntax.Pragma(PragmaValue(verb))
        }
-       return res
-}
-
-func (p *noder) globalPragmas() {
-       origlexlineno := lexlineno
-       defer func() {
-               lexlineno = origlexlineno
-       }()
-
-       for _, prag := range p.pragmas {
-               text := prag.Text
-
-               if strings.HasPrefix(text, "go:cgo_") {
-                       pragcgobuf += pragcgo(text)
-               }
 
-               if strings.HasPrefix(text, "go:linkname ") {
-                       if !imported_unsafe {
-                               Yyerror("//go:linkname only allowed in Go files that import \"unsafe\"")
-                       }
-                       f := strings.Fields(text)
-                       if len(f) != 3 {
-                               Yyerror("usage: //go:linkname localname linkname")
-                               break
-                       }
-                       Lookup(f[1]).Linkname = f[2]
-               }
-
-               // TODO(mdempsky): Move into package syntax.
-               if strings.HasPrefix(text, "line ") {
-                       i := strings.IndexByte(text, ':')
-                       if i < 0 {
-                               continue
-                       }
-                       n, err := strconv.Atoi(text[i+1:])
-                       if err != nil {
-                               // todo: make this an error instead? it is almost certainly a bug.
-                               continue
-                       }
-                       if n > 1e8 {
-                               Yyerror("line number out of range")
-                               errorexit()
-                       }
-                       if n <= 0 {
-                               continue
-                       }
-                       lexlineno = origlexlineno + int32(prag.Line)
-                       linehistupdate(text[5:i], n)
-               }
-       }
+       return 0
 }
index fd38e7ca78cfb2d19c170c4258f9df757f17d995..1ee1d982d0b6990e5986bf180944a1aa1316117a 100644 (file)
@@ -14,7 +14,7 @@ func TestDump(t *testing.T) {
                t.Skip("skipping test in short mode")
        }
 
-       ast, err := ReadFile(*src, nil, 0)
+       ast, err := ReadFile(*src, nil, nil, 0)
        if err != nil {
                t.Fatal(err)
        }
index e56b1235fe075a7e4197b1b26e425e58ecd1f83d..280a2e8f694fc62ad6dbd5706d6ae22920d7dca1 100644 (file)
@@ -10,6 +10,7 @@ package syntax
 type Node interface {
        Line() uint32
        aNode()
+       init(p *parser)
 }
 
 type node struct {
@@ -35,16 +36,10 @@ func (n *node) init(p *parser) {
 type File struct {
        PkgName  *Name
        DeclList []Decl
-       Pragmas  []Pragma
        Lines    int
        node
 }
 
-type Pragma struct {
-       Line int
-       Text string
-}
-
 // ----------------------------------------------------------------------------
 // Declarations
 
@@ -90,6 +85,7 @@ type (
                Name    *Name
                Type    *FuncType
                Body    []Stmt // nil means no body (forward declaration)
+               Pragma  Pragma // TODO(mdempsky): Cleaner solution.
                EndLine uint32 // TODO(mdempsky): Cleaner solution.
                decl
        }
@@ -130,7 +126,8 @@ type (
        CompositeLit struct {
                Type     Expr // nil means no literal type
                ElemList []Expr
-               NKeys    int // number of elements with keys
+               NKeys    int    // number of elements with keys
+               EndLine  uint32 // TODO(mdempsky): Cleaner solution.
                expr
        }
 
index 9544001a2e096ec57604e96e7cc0d29c8899b7f8..ae9c14c81155e9231d3ae1a13bc5448436ee7c2d 100644 (file)
@@ -28,7 +28,7 @@ type parser struct {
        nerrors int // error count
 }
 
-func (p *parser) init(src io.Reader, errh ErrorHandler) {
+func (p *parser) init(src io.Reader, errh ErrorHandler, pragh PragmaHandler) {
        p.scanner.init(src, func(pos, line int, msg string) {
                p.nerrors++
                if !debug && errh != nil {
@@ -36,7 +36,7 @@ func (p *parser) init(src io.Reader, errh ErrorHandler) {
                        return
                }
                panic(fmt.Sprintf("%d: %s\n", line, msg))
-       })
+       }, pragh)
 
        p.fnest = 0
        p.xnest = 0
@@ -245,6 +245,10 @@ func (p *parser) file() *File {
                        continue
                }
 
+               // Reset p.pragma BEFORE advancing to the next token (consuming ';')
+               // since comments before may set pragmas for the next function decl.
+               p.pragma = 0
+
                if p.tok != _EOF && !p.got(_Semi) {
                        p.syntax_error("after top level declaration")
                        p.advance(_Const, _Type, _Var, _Func)
@@ -253,7 +257,6 @@ func (p *parser) file() *File {
        // p.tok == _EOF
 
        f.Lines = p.source.line
-       f.Pragmas = p.pragmas
 
        return f
 }
@@ -372,6 +375,9 @@ func (p *parser) varDecl(group *Group) Decl {
                }
        }
        d.Group = group
+       if gcCompat {
+               d.init(p)
+       }
 
        return d
 }
@@ -426,8 +432,12 @@ func (p *parser) funcDecl() *FuncDecl {
 
        f.Name = p.name()
        f.Type = p.funcType()
+       if gcCompat {
+               f.node = f.Type.node
+       }
        f.Body = p.funcBody()
 
+       f.Pragma = p.pragma
        f.EndLine = uint32(p.line)
 
        // TODO(gri) deal with function properties
@@ -465,6 +475,9 @@ func (p *parser) binaryExpr(prec int) Expr {
                tprec := p.prec
                p.next()
                t.Y = p.binaryExpr(tprec)
+               if gcCompat {
+                       t.init(p)
+               }
                x = t
        }
        return x
@@ -485,6 +498,9 @@ func (p *parser) unaryExpr() Expr {
                        x.Op = p.op
                        p.next()
                        x.X = p.unaryExpr()
+                       if gcCompat {
+                               x.init(p)
+                       }
                        return x
 
                case And:
@@ -730,6 +746,9 @@ loop:
                                p.syntax_error("expecting name or (")
                                p.advance(_Semi, _Rparen)
                        }
+                       if gcCompat {
+                               x.init(p)
+                       }
 
                case _Lbrack:
                        p.next()
@@ -851,6 +870,9 @@ func (p *parser) complitexpr() *CompositeLit {
                        l.init(p)
                        l.Key = e
                        l.Value = p.bare_complitexpr()
+                       if gcCompat {
+                               l.init(p)
+                       }
                        e = l
                        x.NKeys++
                }
@@ -860,6 +882,7 @@ func (p *parser) complitexpr() *CompositeLit {
                }
        }
 
+       x.EndLine = uint32(p.line)
        p.xnest--
        p.want(_Rbrace)
 
@@ -996,6 +1019,9 @@ func (p *parser) funcType() *FuncType {
        typ.init(p)
        typ.ParamList = p.paramList()
        typ.ResultList = p.funcResult()
+       if gcCompat {
+               typ.init(p)
+       }
        return typ
 }
 
@@ -1134,6 +1160,10 @@ func (p *parser) addField(styp *StructType, name *Name, typ Expr, tag *BasicLit)
        f.Type = typ
        styp.FieldList = append(styp.FieldList, f)
 
+       if gcCompat && name != nil {
+               f.node = name.node
+       }
+
        if debug && tag != nil && len(styp.FieldList) != len(styp.TagList) {
                panic("inconsistent struct field list")
        }
@@ -1443,6 +1473,9 @@ func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
                        s.init(p)
                        s.Chan = lhs
                        s.Value = p.expr()
+                       if gcCompat {
+                               s.init(p)
+                       }
                        return s
 
                default:
@@ -1509,6 +1542,9 @@ func (p *parser) rangeClause(lhs Expr, def bool) *RangeClause {
        r.Lhs = lhs
        r.Def = def
        r.X = p.expr()
+       if gcCompat {
+               r.init(p)
+       }
        return r
 }
 
@@ -1583,6 +1619,9 @@ func (p *parser) forStmt() Stmt {
 
        p.want(_For)
        s.Init, s.Cond, s.Post = p.header(true)
+       if gcCompat {
+               s.init(p)
+       }
        s.Body = p.stmtBody("for clause")
 
        return s
@@ -1672,6 +1711,10 @@ func (p *parser) ifStmt() *IfStmt {
                p.error("missing condition in if statement")
        }
 
+       if gcCompat {
+               s.init(p)
+       }
+
        s.Then = p.stmtBody("if clause")
 
        if p.got(_Else) {
@@ -1914,6 +1957,9 @@ func (p *parser) stmt() Stmt {
                if p.tok != _Semi && p.tok != _Rbrace {
                        s.Results = p.exprList()
                }
+               if gcCompat {
+                       s.init(p)
+               }
                return s
 
        case _Semi:
index 12fc0194149b51d88cddb95a9a455cda17415b46..8e6b77d0c687eed9655d0bec3f931368cf407186 100644 (file)
@@ -22,7 +22,7 @@ var src = flag.String("src", "parser.go", "source file to parse")
 var verify = flag.Bool("verify", false, "verify idempotent printing")
 
 func TestParse(t *testing.T) {
-       _, err := ReadFile(*src, nil, 0)
+       _, err := ReadFile(*src, nil, nil, 0)
        if err != nil {
                t.Fatal(err)
        }
@@ -52,7 +52,7 @@ func TestStdLib(t *testing.T) {
                                if debug {
                                        fmt.Printf("parsing %s\n", filename)
                                }
-                               ast, err := ReadFile(filename, nil, 0)
+                               ast, err := ReadFile(filename, nil, nil, 0)
                                if err != nil {
                                        t.Fatal(err)
                                }
@@ -133,7 +133,7 @@ func verifyPrint(filename string, ast1 *File) {
                panic(err)
        }
 
-       ast2, err := ReadBytes(buf1.Bytes(), nil, 0)
+       ast2, err := ReadBytes(buf1.Bytes(), nil, nil, 0)
        if err != nil {
                panic(err)
        }
index e8c2201e60d773404c31971eefee66105114a1c3..a2d43068ddc0c27c2e13db62d6045aae42060200 100644 (file)
@@ -15,7 +15,7 @@ func TestPrint(t *testing.T) {
                t.Skip("skipping test in short mode")
        }
 
-       ast, err := ReadFile(*src, nil, 0)
+       ast, err := ReadFile(*src, nil, nil, 0)
        if err != nil {
                t.Fatal(err)
        }
index d02bb6d11b988d72a18869224a8e1e4e69178975..e78950ad1a4cf35c2c7ec40dd379e0a1298fa306 100644 (file)
@@ -15,6 +15,7 @@ import (
 type scanner struct {
        source
        nlsemi bool // if set '\n' and EOF translate to ';'
+       pragma Pragma
 
        // current token, valid after calling next()
        pos, line int
@@ -24,12 +25,13 @@ type scanner struct {
        op        Operator // valid if tok is _Operator, _AssignOp, or _IncOp
        prec      int      // valid if tok is _Operator, _AssignOp, or _IncOp
 
-       pragmas []Pragma
+       pragh PragmaHandler
 }
 
-func (s *scanner) init(src io.Reader, errh ErrorHandler) {
+func (s *scanner) init(src io.Reader, errh ErrorHandler, pragh PragmaHandler) {
        s.source.init(src, errh)
        s.nlsemi = false
+       s.pragh = pragh
 }
 
 func (s *scanner) next() {
@@ -540,6 +542,10 @@ func (s *scanner) lineComment() {
        // recognize pragmas
        var prefix string
        r := s.getr()
+       if s.pragh == nil {
+               goto skip
+       }
+
        switch r {
        case 'g':
                prefix = "go:"
@@ -565,10 +571,7 @@ func (s *scanner) lineComment() {
                }
                r = s.getr()
        }
-       s.pragmas = append(s.pragmas, Pragma{
-               Line: s.line,
-               Text: strings.TrimSuffix(string(s.stopLit()), "\r"),
-       })
+       s.pragma |= s.pragh(0, s.line, strings.TrimSuffix(string(s.stopLit()), "\r"))
        return
 
 skip:
index 70b986ed4e2515c6281ce28403d60f4e9e2c240a..38a7e0da4c6209282313e3e9a082fbd9484e7ef4 100644 (file)
@@ -22,7 +22,7 @@ func TestScanner(t *testing.T) {
        defer src.Close()
 
        var s scanner
-       s.init(src, nil)
+       s.init(src, nil, nil)
        for {
                s.next()
                if s.tok == _EOF {
@@ -51,7 +51,7 @@ func TestTokens(t *testing.T) {
 
        // scan source
        var got scanner
-       got.init(&bytesReader{buf}, nil)
+       got.init(&bytesReader{buf}, nil, nil)
        got.next()
        for i, want := range sampleTokens {
                nlsemi := false
@@ -338,7 +338,7 @@ func TestScanErrors(t *testing.T) {
                        } else if nerrors > 1 {
                                t.Errorf("%q: got unexpected %q at pos = %d, line = %d", test.src, msg, pos, line)
                        }
-               })
+               }, nil)
 
                for {
                        s.next()
index 85dddaa47fb49e923e9da920d70a49acb954a943..6c0abd118df12cf1d24468e5b7ecc82a0a501954 100644 (file)
@@ -12,17 +12,27 @@ import (
 
 type Mode uint
 
+// A Pragma value is a set of flags that augment a function
+// declaration. Callers may assign meaning to the flags as
+// appropriate.
+type Pragma uint16
+
 type ErrorHandler func(pos, line int, msg string)
 
+// A PragmaHandler is used to process //line and //go: directives as
+// they're scanned. The returned Pragma value will be unioned into the
+// next FuncDecl node.
+type PragmaHandler func(pos, line int, text string) Pragma
+
 // TODO(gri) These need a lot more work.
 
-func ReadFile(filename string, errh ErrorHandler, mode Mode) (*File, error) {
+func ReadFile(filename string, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) {
        src, err := os.Open(filename)
        if err != nil {
                return nil, err
        }
        defer src.Close()
-       return Read(src, errh, mode)
+       return Read(src, errh, pragh, mode)
 }
 
 type bytesReader struct {
@@ -38,13 +48,13 @@ func (r *bytesReader) Read(p []byte) (int, error) {
        return 0, io.EOF
 }
 
-func ReadBytes(src []byte, errh ErrorHandler, mode Mode) (*File, error) {
-       return Read(&bytesReader{src}, errh, mode)
+func ReadBytes(src []byte, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) {
+       return Read(&bytesReader{src}, errh, pragh, mode)
 }
 
-func Read(src io.Reader, errh ErrorHandler, mode Mode) (*File, error) {
+func Read(src io.Reader, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) {
        var p parser
-       p.init(src, errh)
+       p.init(src, errh, pragh)
 
        p.next()
        ast := p.file()