Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / rogpeppe / go-internal / modfile / read.go
1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Module file parser.
6 // This is a simplified copy of Google's buildifier parser.
7
8 package modfile
9
10 import (
11         "bytes"
12         "fmt"
13         "os"
14         "strconv"
15         "strings"
16         "unicode"
17         "unicode/utf8"
18 )
19
20 // A Position describes the position between two bytes of input.
21 type Position struct {
22         Line     int // line in input (starting at 1)
23         LineRune int // rune in line (starting at 1)
24         Byte     int // byte in input (starting at 0)
25 }
26
27 // add returns the position at the end of s, assuming it starts at p.
28 func (p Position) add(s string) Position {
29         p.Byte += len(s)
30         if n := strings.Count(s, "\n"); n > 0 {
31                 p.Line += n
32                 s = s[strings.LastIndex(s, "\n")+1:]
33                 p.LineRune = 1
34         }
35         p.LineRune += utf8.RuneCountInString(s)
36         return p
37 }
38
39 // An Expr represents an input element.
40 type Expr interface {
41         // Span returns the start and end position of the expression,
42         // excluding leading or trailing comments.
43         Span() (start, end Position)
44
45         // Comment returns the comments attached to the expression.
46         // This method would normally be named 'Comments' but that
47         // would interfere with embedding a type of the same name.
48         Comment() *Comments
49 }
50
51 // A Comment represents a single // comment.
52 type Comment struct {
53         Start  Position
54         Token  string // without trailing newline
55         Suffix bool   // an end of line (not whole line) comment
56 }
57
58 // Comments collects the comments associated with an expression.
59 type Comments struct {
60         Before []Comment // whole-line comments before this expression
61         Suffix []Comment // end-of-line comments after this expression
62
63         // For top-level expressions only, After lists whole-line
64         // comments following the expression.
65         After []Comment
66 }
67
68 // Comment returns the receiver. This isn't useful by itself, but
69 // a Comments struct is embedded into all the expression
70 // implementation types, and this gives each of those a Comment
71 // method to satisfy the Expr interface.
72 func (c *Comments) Comment() *Comments {
73         return c
74 }
75
76 // A FileSyntax represents an entire go.mod file.
77 type FileSyntax struct {
78         Name string // file path
79         Comments
80         Stmt []Expr
81 }
82
83 func (x *FileSyntax) Span() (start, end Position) {
84         if len(x.Stmt) == 0 {
85                 return
86         }
87         start, _ = x.Stmt[0].Span()
88         _, end = x.Stmt[len(x.Stmt)-1].Span()
89         return start, end
90 }
91
92 func (x *FileSyntax) addLine(hint Expr, tokens ...string) *Line {
93         if hint == nil {
94                 // If no hint given, add to the last statement of the given type.
95         Loop:
96                 for i := len(x.Stmt) - 1; i >= 0; i-- {
97                         stmt := x.Stmt[i]
98                         switch stmt := stmt.(type) {
99                         case *Line:
100                                 if stmt.Token != nil && stmt.Token[0] == tokens[0] {
101                                         hint = stmt
102                                         break Loop
103                                 }
104                         case *LineBlock:
105                                 if stmt.Token[0] == tokens[0] {
106                                         hint = stmt
107                                         break Loop
108                                 }
109                         }
110                 }
111         }
112
113         if hint != nil {
114                 for i, stmt := range x.Stmt {
115                         switch stmt := stmt.(type) {
116                         case *Line:
117                                 if stmt == hint {
118                                         // Convert line to line block.
119                                         stmt.InBlock = true
120                                         block := &LineBlock{Token: stmt.Token[:1], Line: []*Line{stmt}}
121                                         stmt.Token = stmt.Token[1:]
122                                         x.Stmt[i] = block
123                                         new := &Line{Token: tokens[1:], InBlock: true}
124                                         block.Line = append(block.Line, new)
125                                         return new
126                                 }
127                         case *LineBlock:
128                                 if stmt == hint {
129                                         new := &Line{Token: tokens[1:], InBlock: true}
130                                         stmt.Line = append(stmt.Line, new)
131                                         return new
132                                 }
133                                 for j, line := range stmt.Line {
134                                         if line == hint {
135                                                 // Add new line after hint.
136                                                 stmt.Line = append(stmt.Line, nil)
137                                                 copy(stmt.Line[j+2:], stmt.Line[j+1:])
138                                                 new := &Line{Token: tokens[1:], InBlock: true}
139                                                 stmt.Line[j+1] = new
140                                                 return new
141                                         }
142                                 }
143                         }
144                 }
145         }
146
147         new := &Line{Token: tokens}
148         x.Stmt = append(x.Stmt, new)
149         return new
150 }
151
152 func (x *FileSyntax) updateLine(line *Line, tokens ...string) {
153         if line.InBlock {
154                 tokens = tokens[1:]
155         }
156         line.Token = tokens
157 }
158
159 func (x *FileSyntax) removeLine(line *Line) {
160         line.Token = nil
161 }
162
163 // Cleanup cleans up the file syntax x after any edit operations.
164 // To avoid quadratic behavior, removeLine marks the line as dead
165 // by setting line.Token = nil but does not remove it from the slice
166 // in which it appears. After edits have all been indicated,
167 // calling Cleanup cleans out the dead lines.
168 func (x *FileSyntax) Cleanup() {
169         w := 0
170         for _, stmt := range x.Stmt {
171                 switch stmt := stmt.(type) {
172                 case *Line:
173                         if stmt.Token == nil {
174                                 continue
175                         }
176                 case *LineBlock:
177                         ww := 0
178                         for _, line := range stmt.Line {
179                                 if line.Token != nil {
180                                         stmt.Line[ww] = line
181                                         ww++
182                                 }
183                         }
184                         if ww == 0 {
185                                 continue
186                         }
187                         if ww == 1 {
188                                 // Collapse block into single line.
189                                 line := &Line{
190                                         Comments: Comments{
191                                                 Before: commentsAdd(stmt.Before, stmt.Line[0].Before),
192                                                 Suffix: commentsAdd(stmt.Line[0].Suffix, stmt.Suffix),
193                                                 After:  commentsAdd(stmt.Line[0].After, stmt.After),
194                                         },
195                                         Token: stringsAdd(stmt.Token, stmt.Line[0].Token),
196                                 }
197                                 x.Stmt[w] = line
198                                 w++
199                                 continue
200                         }
201                         stmt.Line = stmt.Line[:ww]
202                 }
203                 x.Stmt[w] = stmt
204                 w++
205         }
206         x.Stmt = x.Stmt[:w]
207 }
208
209 func commentsAdd(x, y []Comment) []Comment {
210         return append(x[:len(x):len(x)], y...)
211 }
212
213 func stringsAdd(x, y []string) []string {
214         return append(x[:len(x):len(x)], y...)
215 }
216
217 // A CommentBlock represents a top-level block of comments separate
218 // from any rule.
219 type CommentBlock struct {
220         Comments
221         Start Position
222 }
223
224 func (x *CommentBlock) Span() (start, end Position) {
225         return x.Start, x.Start
226 }
227
228 // A Line is a single line of tokens.
229 type Line struct {
230         Comments
231         Start   Position
232         Token   []string
233         InBlock bool
234         End     Position
235 }
236
237 func (x *Line) Span() (start, end Position) {
238         return x.Start, x.End
239 }
240
241 // A LineBlock is a factored block of lines, like
242 //
243 //      require (
244 //              "x"
245 //              "y"
246 //      )
247 //
248 type LineBlock struct {
249         Comments
250         Start  Position
251         LParen LParen
252         Token  []string
253         Line   []*Line
254         RParen RParen
255 }
256
257 func (x *LineBlock) Span() (start, end Position) {
258         return x.Start, x.RParen.Pos.add(")")
259 }
260
261 // An LParen represents the beginning of a parenthesized line block.
262 // It is a place to store suffix comments.
263 type LParen struct {
264         Comments
265         Pos Position
266 }
267
268 func (x *LParen) Span() (start, end Position) {
269         return x.Pos, x.Pos.add(")")
270 }
271
272 // An RParen represents the end of a parenthesized line block.
273 // It is a place to store whole-line (before) comments.
274 type RParen struct {
275         Comments
276         Pos Position
277 }
278
279 func (x *RParen) Span() (start, end Position) {
280         return x.Pos, x.Pos.add(")")
281 }
282
283 // An input represents a single input file being parsed.
284 type input struct {
285         // Lexing state.
286         filename  string    // name of input file, for errors
287         complete  []byte    // entire input
288         remaining []byte    // remaining input
289         token     []byte    // token being scanned
290         lastToken string    // most recently returned token, for error messages
291         pos       Position  // current input position
292         comments  []Comment // accumulated comments
293         endRule   int       // position of end of current rule
294
295         // Parser state.
296         file       *FileSyntax // returned top-level syntax tree
297         parseError error       // error encountered during parsing
298
299         // Comment assignment state.
300         pre  []Expr // all expressions, in preorder traversal
301         post []Expr // all expressions, in postorder traversal
302 }
303
304 func newInput(filename string, data []byte) *input {
305         return &input{
306                 filename:  filename,
307                 complete:  data,
308                 remaining: data,
309                 pos:       Position{Line: 1, LineRune: 1, Byte: 0},
310         }
311 }
312
313 // parse parses the input file.
314 func parse(file string, data []byte) (f *FileSyntax, err error) {
315         in := newInput(file, data)
316         // The parser panics for both routine errors like syntax errors
317         // and for programmer bugs like array index errors.
318         // Turn both into error returns. Catching bug panics is
319         // especially important when processing many files.
320         defer func() {
321                 if e := recover(); e != nil {
322                         if e == in.parseError {
323                                 err = in.parseError
324                         } else {
325                                 err = fmt.Errorf("%s:%d:%d: internal error: %v", in.filename, in.pos.Line, in.pos.LineRune, e)
326                         }
327                 }
328         }()
329
330         // Invoke the parser.
331         in.parseFile()
332         if in.parseError != nil {
333                 return nil, in.parseError
334         }
335         in.file.Name = in.filename
336
337         // Assign comments to nearby syntax.
338         in.assignComments()
339
340         return in.file, nil
341 }
342
343 // Error is called to report an error.
344 // The reason s is often "syntax error".
345 // Error does not return: it panics.
346 func (in *input) Error(s string) {
347         if s == "syntax error" && in.lastToken != "" {
348                 s += " near " + in.lastToken
349         }
350         in.parseError = fmt.Errorf("%s:%d:%d: %v", in.filename, in.pos.Line, in.pos.LineRune, s)
351         panic(in.parseError)
352 }
353
354 // eof reports whether the input has reached end of file.
355 func (in *input) eof() bool {
356         return len(in.remaining) == 0
357 }
358
359 // peekRune returns the next rune in the input without consuming it.
360 func (in *input) peekRune() int {
361         if len(in.remaining) == 0 {
362                 return 0
363         }
364         r, _ := utf8.DecodeRune(in.remaining)
365         return int(r)
366 }
367
368 // peekPrefix reports whether the remaining input begins with the given prefix.
369 func (in *input) peekPrefix(prefix string) bool {
370         // This is like bytes.HasPrefix(in.remaining, []byte(prefix))
371         // but without the allocation of the []byte copy of prefix.
372         for i := 0; i < len(prefix); i++ {
373                 if i >= len(in.remaining) || in.remaining[i] != prefix[i] {
374                         return false
375                 }
376         }
377         return true
378 }
379
380 // readRune consumes and returns the next rune in the input.
381 func (in *input) readRune() int {
382         if len(in.remaining) == 0 {
383                 in.Error("internal lexer error: readRune at EOF")
384         }
385         r, size := utf8.DecodeRune(in.remaining)
386         in.remaining = in.remaining[size:]
387         if r == '\n' {
388                 in.pos.Line++
389                 in.pos.LineRune = 1
390         } else {
391                 in.pos.LineRune++
392         }
393         in.pos.Byte += size
394         return int(r)
395 }
396
397 type symType struct {
398         pos    Position
399         endPos Position
400         text   string
401 }
402
403 // startToken marks the beginning of the next input token.
404 // It must be followed by a call to endToken, once the token has
405 // been consumed using readRune.
406 func (in *input) startToken(sym *symType) {
407         in.token = in.remaining
408         sym.text = ""
409         sym.pos = in.pos
410 }
411
412 // endToken marks the end of an input token.
413 // It records the actual token string in sym.text if the caller
414 // has not done that already.
415 func (in *input) endToken(sym *symType) {
416         if sym.text == "" {
417                 tok := string(in.token[:len(in.token)-len(in.remaining)])
418                 sym.text = tok
419                 in.lastToken = sym.text
420         }
421         sym.endPos = in.pos
422 }
423
424 // lex is called from the parser to obtain the next input token.
425 // It returns the token value (either a rune like '+' or a symbolic token _FOR)
426 // and sets val to the data associated with the token.
427 // For all our input tokens, the associated data is
428 // val.Pos (the position where the token begins)
429 // and val.Token (the input string corresponding to the token).
430 func (in *input) lex(sym *symType) int {
431         // Skip past spaces, stopping at non-space or EOF.
432         countNL := 0 // number of newlines we've skipped past
433         for !in.eof() {
434                 // Skip over spaces. Count newlines so we can give the parser
435                 // information about where top-level blank lines are,
436                 // for top-level comment assignment.
437                 c := in.peekRune()
438                 if c == ' ' || c == '\t' || c == '\r' {
439                         in.readRune()
440                         continue
441                 }
442
443                 // Comment runs to end of line.
444                 if in.peekPrefix("//") {
445                         in.startToken(sym)
446
447                         // Is this comment the only thing on its line?
448                         // Find the last \n before this // and see if it's all
449                         // spaces from there to here.
450                         i := bytes.LastIndex(in.complete[:in.pos.Byte], []byte("\n"))
451                         suffix := len(bytes.TrimSpace(in.complete[i+1:in.pos.Byte])) > 0
452                         in.readRune()
453                         in.readRune()
454
455                         // Consume comment.
456                         for len(in.remaining) > 0 && in.readRune() != '\n' {
457                         }
458                         in.endToken(sym)
459
460                         sym.text = strings.TrimRight(sym.text, "\n")
461                         in.lastToken = "comment"
462
463                         // If we are at top level (not in a statement), hand the comment to
464                         // the parser as a _COMMENT token. The grammar is written
465                         // to handle top-level comments itself.
466                         if !suffix {
467                                 // Not in a statement. Tell parser about top-level comment.
468                                 return _COMMENT
469                         }
470
471                         // Otherwise, save comment for later attachment to syntax tree.
472                         if countNL > 1 {
473                                 in.comments = append(in.comments, Comment{sym.pos, "", false})
474                         }
475                         in.comments = append(in.comments, Comment{sym.pos, sym.text, suffix})
476                         countNL = 1
477                         return _EOL
478                 }
479
480                 if in.peekPrefix("/*") {
481                         in.Error(fmt.Sprintf("mod files must use // comments (not /* */ comments)"))
482                 }
483
484                 // Found non-space non-comment.
485                 break
486         }
487
488         // Found the beginning of the next token.
489         in.startToken(sym)
490         defer in.endToken(sym)
491
492         // End of file.
493         if in.eof() {
494                 in.lastToken = "EOF"
495                 return _EOF
496         }
497
498         // Punctuation tokens.
499         switch c := in.peekRune(); c {
500         case '\n':
501                 in.readRune()
502                 return c
503
504         case '(':
505                 in.readRune()
506                 return c
507
508         case ')':
509                 in.readRune()
510                 return c
511
512         case '"', '`': // quoted string
513                 quote := c
514                 in.readRune()
515                 for {
516                         if in.eof() {
517                                 in.pos = sym.pos
518                                 in.Error("unexpected EOF in string")
519                         }
520                         if in.peekRune() == '\n' {
521                                 in.Error("unexpected newline in string")
522                         }
523                         c := in.readRune()
524                         if c == quote {
525                                 break
526                         }
527                         if c == '\\' && quote != '`' {
528                                 if in.eof() {
529                                         in.pos = sym.pos
530                                         in.Error("unexpected EOF in string")
531                                 }
532                                 in.readRune()
533                         }
534                 }
535                 in.endToken(sym)
536                 return _STRING
537         }
538
539         // Checked all punctuation. Must be identifier token.
540         if c := in.peekRune(); !isIdent(c) {
541                 in.Error(fmt.Sprintf("unexpected input character %#q", c))
542         }
543
544         // Scan over identifier.
545         for isIdent(in.peekRune()) {
546                 if in.peekPrefix("//") {
547                         break
548                 }
549                 if in.peekPrefix("/*") {
550                         in.Error(fmt.Sprintf("mod files must use // comments (not /* */ comments)"))
551                 }
552                 in.readRune()
553         }
554         return _IDENT
555 }
556
557 // isIdent reports whether c is an identifier rune.
558 // We treat nearly all runes as identifier runes.
559 func isIdent(c int) bool {
560         return c != 0 && !unicode.IsSpace(rune(c))
561 }
562
563 // Comment assignment.
564 // We build two lists of all subexpressions, preorder and postorder.
565 // The preorder list is ordered by start location, with outer expressions first.
566 // The postorder list is ordered by end location, with outer expressions last.
567 // We use the preorder list to assign each whole-line comment to the syntax
568 // immediately following it, and we use the postorder list to assign each
569 // end-of-line comment to the syntax immediately preceding it.
570
571 // order walks the expression adding it and its subexpressions to the
572 // preorder and postorder lists.
573 func (in *input) order(x Expr) {
574         if x != nil {
575                 in.pre = append(in.pre, x)
576         }
577         switch x := x.(type) {
578         default:
579                 panic(fmt.Errorf("order: unexpected type %T", x))
580         case nil:
581                 // nothing
582         case *LParen, *RParen:
583                 // nothing
584         case *CommentBlock:
585                 // nothing
586         case *Line:
587                 // nothing
588         case *FileSyntax:
589                 for _, stmt := range x.Stmt {
590                         in.order(stmt)
591                 }
592         case *LineBlock:
593                 in.order(&x.LParen)
594                 for _, l := range x.Line {
595                         in.order(l)
596                 }
597                 in.order(&x.RParen)
598         }
599         if x != nil {
600                 in.post = append(in.post, x)
601         }
602 }
603
604 // assignComments attaches comments to nearby syntax.
605 func (in *input) assignComments() {
606         const debug = false
607
608         // Generate preorder and postorder lists.
609         in.order(in.file)
610
611         // Split into whole-line comments and suffix comments.
612         var line, suffix []Comment
613         for _, com := range in.comments {
614                 if com.Suffix {
615                         suffix = append(suffix, com)
616                 } else {
617                         line = append(line, com)
618                 }
619         }
620
621         if debug {
622                 for _, c := range line {
623                         fmt.Fprintf(os.Stderr, "LINE %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte)
624                 }
625         }
626
627         // Assign line comments to syntax immediately following.
628         for _, x := range in.pre {
629                 start, _ := x.Span()
630                 if debug {
631                         fmt.Printf("pre %T :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte)
632                 }
633                 xcom := x.Comment()
634                 for len(line) > 0 && start.Byte >= line[0].Start.Byte {
635                         if debug {
636                                 fmt.Fprintf(os.Stderr, "ASSIGN LINE %q #%d\n", line[0].Token, line[0].Start.Byte)
637                         }
638                         xcom.Before = append(xcom.Before, line[0])
639                         line = line[1:]
640                 }
641         }
642
643         // Remaining line comments go at end of file.
644         in.file.After = append(in.file.After, line...)
645
646         if debug {
647                 for _, c := range suffix {
648                         fmt.Fprintf(os.Stderr, "SUFFIX %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte)
649                 }
650         }
651
652         // Assign suffix comments to syntax immediately before.
653         for i := len(in.post) - 1; i >= 0; i-- {
654                 x := in.post[i]
655
656                 start, end := x.Span()
657                 if debug {
658                         fmt.Printf("post %T :%d:%d #%d :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte, end.Line, end.LineRune, end.Byte)
659                 }
660
661                 // Do not assign suffix comments to end of line block or whole file.
662                 // Instead assign them to the last element inside.
663                 switch x.(type) {
664                 case *FileSyntax:
665                         continue
666                 }
667
668                 // Do not assign suffix comments to something that starts
669                 // on an earlier line, so that in
670                 //
671                 //      x ( y
672                 //              z ) // comment
673                 //
674                 // we assign the comment to z and not to x ( ... ).
675                 if start.Line != end.Line {
676                         continue
677                 }
678                 xcom := x.Comment()
679                 for len(suffix) > 0 && end.Byte <= suffix[len(suffix)-1].Start.Byte {
680                         if debug {
681                                 fmt.Fprintf(os.Stderr, "ASSIGN SUFFIX %q #%d\n", suffix[len(suffix)-1].Token, suffix[len(suffix)-1].Start.Byte)
682                         }
683                         xcom.Suffix = append(xcom.Suffix, suffix[len(suffix)-1])
684                         suffix = suffix[:len(suffix)-1]
685                 }
686         }
687
688         // We assigned suffix comments in reverse.
689         // If multiple suffix comments were appended to the same
690         // expression node, they are now in reverse. Fix that.
691         for _, x := range in.post {
692                 reverseComments(x.Comment().Suffix)
693         }
694
695         // Remaining suffix comments go at beginning of file.
696         in.file.Before = append(in.file.Before, suffix...)
697 }
698
699 // reverseComments reverses the []Comment list.
700 func reverseComments(list []Comment) {
701         for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
702                 list[i], list[j] = list[j], list[i]
703         }
704 }
705
706 func (in *input) parseFile() {
707         in.file = new(FileSyntax)
708         var sym symType
709         var cb *CommentBlock
710         for {
711                 tok := in.lex(&sym)
712                 switch tok {
713                 case '\n':
714                         if cb != nil {
715                                 in.file.Stmt = append(in.file.Stmt, cb)
716                                 cb = nil
717                         }
718                 case _COMMENT:
719                         if cb == nil {
720                                 cb = &CommentBlock{Start: sym.pos}
721                         }
722                         com := cb.Comment()
723                         com.Before = append(com.Before, Comment{Start: sym.pos, Token: sym.text})
724                 case _EOF:
725                         if cb != nil {
726                                 in.file.Stmt = append(in.file.Stmt, cb)
727                         }
728                         return
729                 default:
730                         in.parseStmt(&sym)
731                         if cb != nil {
732                                 in.file.Stmt[len(in.file.Stmt)-1].Comment().Before = cb.Before
733                                 cb = nil
734                         }
735                 }
736         }
737 }
738
739 func (in *input) parseStmt(sym *symType) {
740         start := sym.pos
741         end := sym.endPos
742         token := []string{sym.text}
743         for {
744                 tok := in.lex(sym)
745                 switch tok {
746                 case '\n', _EOF, _EOL:
747                         in.file.Stmt = append(in.file.Stmt, &Line{
748                                 Start: start,
749                                 Token: token,
750                                 End:   end,
751                         })
752                         return
753                 case '(':
754                         in.file.Stmt = append(in.file.Stmt, in.parseLineBlock(start, token, sym))
755                         return
756                 default:
757                         token = append(token, sym.text)
758                         end = sym.endPos
759                 }
760         }
761 }
762
763 func (in *input) parseLineBlock(start Position, token []string, sym *symType) *LineBlock {
764         x := &LineBlock{
765                 Start:  start,
766                 Token:  token,
767                 LParen: LParen{Pos: sym.pos},
768         }
769         var comments []Comment
770         for {
771                 tok := in.lex(sym)
772                 switch tok {
773                 case _EOL:
774                         // ignore
775                 case '\n':
776                         if len(comments) == 0 && len(x.Line) > 0 || len(comments) > 0 && comments[len(comments)-1].Token != "" {
777                                 comments = append(comments, Comment{})
778                         }
779                 case _COMMENT:
780                         comments = append(comments, Comment{Start: sym.pos, Token: sym.text})
781                 case _EOF:
782                         in.Error(fmt.Sprintf("syntax error (unterminated block started at %s:%d:%d)", in.filename, x.Start.Line, x.Start.LineRune))
783                 case ')':
784                         x.RParen.Before = comments
785                         x.RParen.Pos = sym.pos
786                         tok = in.lex(sym)
787                         if tok != '\n' && tok != _EOF && tok != _EOL {
788                                 in.Error("syntax error (expected newline after closing paren)")
789                         }
790                         return x
791                 default:
792                         l := in.parseLine(sym)
793                         x.Line = append(x.Line, l)
794                         l.Comment().Before = comments
795                         comments = nil
796                 }
797         }
798 }
799
800 func (in *input) parseLine(sym *symType) *Line {
801         start := sym.pos
802         end := sym.endPos
803         token := []string{sym.text}
804         for {
805                 tok := in.lex(sym)
806                 switch tok {
807                 case '\n', _EOF, _EOL:
808                         return &Line{
809                                 Start:   start,
810                                 Token:   token,
811                                 End:     end,
812                                 InBlock: true,
813                         }
814                 default:
815                         token = append(token, sym.text)
816                         end = sym.endPos
817                 }
818         }
819 }
820
821 const (
822         _EOF = -(1 + iota)
823         _EOL
824         _IDENT
825         _STRING
826         _COMMENT
827 )
828
829 var (
830         slashSlash = []byte("//")
831         moduleStr  = []byte("module")
832 )
833
834 // ModulePath returns the module path from the gomod file text.
835 // If it cannot find a module path, it returns an empty string.
836 // It is tolerant of unrelated problems in the go.mod file.
837 func ModulePath(mod []byte) string {
838         for len(mod) > 0 {
839                 line := mod
840                 mod = nil
841                 if i := bytes.IndexByte(line, '\n'); i >= 0 {
842                         line, mod = line[:i], line[i+1:]
843                 }
844                 if i := bytes.Index(line, slashSlash); i >= 0 {
845                         line = line[:i]
846                 }
847                 line = bytes.TrimSpace(line)
848                 if !bytes.HasPrefix(line, moduleStr) {
849                         continue
850                 }
851                 line = line[len(moduleStr):]
852                 n := len(line)
853                 line = bytes.TrimSpace(line)
854                 if len(line) == n || len(line) == 0 {
855                         continue
856                 }
857
858                 if line[0] == '"' || line[0] == '`' {
859                         p, err := strconv.Unquote(string(line))
860                         if err != nil {
861                                 return "" // malformed quoted string or multiline module path
862                         }
863                         return p
864                 }
865
866                 return string(line)
867         }
868         return "" // missing module path
869 }