Source file src/pkg/cmd/go/internal/modfile/read.go
1
2
3
4
5
6
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
21 type Position struct {
22 Line int
23 LineRune int
24 Byte int
25 }
26
27
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
40 type Expr interface {
41
42
43 Span() (start, end Position)
44
45
46
47
48 Comment() *Comments
49 }
50
51
52 type Comment struct {
53 Start Position
54 Token string
55 Suffix bool
56 }
57
58
59 type Comments struct {
60 Before []Comment
61 Suffix []Comment
62
63
64
65 After []Comment
66 }
67
68
69
70
71
72 func (c *Comments) Comment() *Comments {
73 return c
74 }
75
76
77 type FileSyntax struct {
78 Name string
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
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
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
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
164
165
166
167
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
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
218
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
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
242
243
244
245
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
262
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
273
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
284 type input struct {
285
286 filename string
287 complete []byte
288 remaining []byte
289 token []byte
290 lastToken string
291 pos Position
292 comments []Comment
293 endRule int
294
295
296 file *FileSyntax
297 parseError error
298
299
300 pre []Expr
301 post []Expr
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
314 func parse(file string, data []byte) (f *FileSyntax, err error) {
315 in := newInput(file, data)
316
317
318
319
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
331 in.parseFile()
332 if in.parseError != nil {
333 return nil, in.parseError
334 }
335 in.file.Name = in.filename
336
337
338 in.assignComments()
339
340 return in.file, nil
341 }
342
343
344
345
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
355 func (in *input) eof() bool {
356 return len(in.remaining) == 0
357 }
358
359
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
369 func (in *input) peekPrefix(prefix string) bool {
370
371
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
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
404
405
406 func (in *input) startToken(sym *symType) {
407 in.token = in.remaining
408 sym.text = ""
409 sym.pos = in.pos
410 }
411
412
413
414
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
425
426
427
428
429
430 func (in *input) lex(sym *symType) int {
431
432 countNL := 0
433 for !in.eof() {
434
435
436
437 c := in.peekRune()
438 if c == ' ' || c == '\t' || c == '\r' {
439 in.readRune()
440 continue
441 }
442
443
444 if in.peekPrefix("//") {
445 in.startToken(sym)
446
447
448
449
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
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
464
465
466 if !suffix {
467
468 return _COMMENT
469 }
470
471
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
485 break
486 }
487
488
489 in.startToken(sym)
490 defer in.endToken(sym)
491
492
493 if in.eof() {
494 in.lastToken = "EOF"
495 return _EOF
496 }
497
498
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 '"', '`':
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
540 if c := in.peekRune(); !isIdent(c) {
541 in.Error(fmt.Sprintf("unexpected input character %#q", c))
542 }
543
544
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
558
559 func isIdent(c int) bool {
560 return c != 0 && !unicode.IsSpace(rune(c))
561 }
562
563
564
565
566
567
568
569
570
571
572
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
582 case *LParen, *RParen:
583
584 case *CommentBlock:
585
586 case *Line:
587
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
605 func (in *input) assignComments() {
606 const debug = false
607
608
609 in.order(in.file)
610
611
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
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
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
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
662
663 switch x.(type) {
664 case *FileSyntax:
665 continue
666 }
667
668
669
670
671
672
673
674
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
689
690
691 for _, x := range in.post {
692 reverseComments(x.Comment().Suffix)
693 }
694
695
696 in.file.Before = append(in.file.Before, suffix...)
697 }
698
699
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
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
835
836
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 ""
862 }
863 return p
864 }
865
866 return string(line)
867 }
868 return ""
869 }
870
View as plain text