1
2
3
4
5 package syntax
6
7
8
9
10
11
12
13
14 import "log"
15
16
17 const debug = false
18
19
20 type Mode uint
21
22 const (
23 RetainComments Mode = 1 << iota
24 )
25
26
27
28
29
30
31
32
33 func Parse(filename string, src interface{}, mode Mode) (f *File, err error) {
34 in, err := newScanner(filename, src, mode&RetainComments != 0)
35 if err != nil {
36 return nil, err
37 }
38 p := parser{in: in}
39 defer p.in.recover(&err)
40
41 p.nextToken()
42 f = p.parseFile()
43 if f != nil {
44 f.Path = filename
45 }
46 p.assignComments(f)
47 return f, nil
48 }
49
50
51
52
53
54
55
56
57 func ParseCompoundStmt(filename string, readline func() ([]byte, error)) (f *File, err error) {
58 in, err := newScanner(filename, readline, false)
59 if err != nil {
60 return nil, err
61 }
62
63 p := parser{in: in}
64 defer p.in.recover(&err)
65
66 p.nextToken()
67
68 var stmts []Stmt
69 switch p.tok {
70 case DEF, IF, FOR, WHILE:
71 stmts = p.parseStmt(stmts)
72 case NEWLINE:
73
74 default:
75 stmts = p.parseSimpleStmt(stmts, false)
76
77 if p.tok != NEWLINE {
78 p.in.errorf(p.in.pos, "invalid syntax")
79 }
80 }
81
82 return &File{Path: filename, Stmts: stmts}, nil
83 }
84
85
86
87
88 func ParseExpr(filename string, src interface{}, mode Mode) (expr Expr, err error) {
89 in, err := newScanner(filename, src, mode&RetainComments != 0)
90 if err != nil {
91 return nil, err
92 }
93 p := parser{in: in}
94 defer p.in.recover(&err)
95
96 p.nextToken()
97
98
99 expr = p.parseExpr(false)
100
101
102
103 if p.tok == NEWLINE {
104 p.nextToken()
105 }
106
107 if p.tok != EOF {
108 p.in.errorf(p.in.pos, "got %#v after expression, want EOF", p.tok)
109 }
110 p.assignComments(expr)
111 return expr, nil
112 }
113
114 type parser struct {
115 in *scanner
116 tok Token
117 tokval tokenValue
118 }
119
120
121
122 func (p *parser) nextToken() Position {
123 oldpos := p.tokval.pos
124 p.tok = p.in.nextToken(&p.tokval)
125
126 if debug {
127 log.Printf("nextToken: %-20s%+v\n", p.tok, p.tokval.pos)
128 }
129 return oldpos
130 }
131
132
133 func (p *parser) parseFile() *File {
134 var stmts []Stmt
135 for p.tok != EOF {
136 if p.tok == NEWLINE {
137 p.nextToken()
138 continue
139 }
140 stmts = p.parseStmt(stmts)
141 }
142 return &File{Stmts: stmts}
143 }
144
145 func (p *parser) parseStmt(stmts []Stmt) []Stmt {
146 if p.tok == DEF {
147 return append(stmts, p.parseDefStmt())
148 } else if p.tok == IF {
149 return append(stmts, p.parseIfStmt())
150 } else if p.tok == FOR {
151 return append(stmts, p.parseForStmt())
152 } else if p.tok == WHILE {
153 return append(stmts, p.parseWhileStmt())
154 }
155 return p.parseSimpleStmt(stmts, true)
156 }
157
158 func (p *parser) parseDefStmt() Stmt {
159 defpos := p.nextToken()
160 id := p.parseIdent()
161 p.consume(LPAREN)
162 params := p.parseParams()
163 p.consume(RPAREN)
164 p.consume(COLON)
165 body := p.parseSuite()
166 return &DefStmt{
167 Def: defpos,
168 Name: id,
169 Params: params,
170 Body: body,
171 }
172 }
173
174 func (p *parser) parseIfStmt() Stmt {
175 ifpos := p.nextToken()
176 cond := p.parseTest()
177 p.consume(COLON)
178 body := p.parseSuite()
179 ifStmt := &IfStmt{
180 If: ifpos,
181 Cond: cond,
182 True: body,
183 }
184 tail := ifStmt
185 for p.tok == ELIF {
186 elifpos := p.nextToken()
187 cond := p.parseTest()
188 p.consume(COLON)
189 body := p.parseSuite()
190 elif := &IfStmt{
191 If: elifpos,
192 Cond: cond,
193 True: body,
194 }
195 tail.ElsePos = elifpos
196 tail.False = []Stmt{elif}
197 tail = elif
198 }
199 if p.tok == ELSE {
200 tail.ElsePos = p.nextToken()
201 p.consume(COLON)
202 tail.False = p.parseSuite()
203 }
204 return ifStmt
205 }
206
207 func (p *parser) parseForStmt() Stmt {
208 forpos := p.nextToken()
209 vars := p.parseForLoopVariables()
210 p.consume(IN)
211 x := p.parseExpr(false)
212 p.consume(COLON)
213 body := p.parseSuite()
214 return &ForStmt{
215 For: forpos,
216 Vars: vars,
217 X: x,
218 Body: body,
219 }
220 }
221
222 func (p *parser) parseWhileStmt() Stmt {
223 whilepos := p.nextToken()
224 cond := p.parseTest()
225 p.consume(COLON)
226 body := p.parseSuite()
227 return &WhileStmt{
228 While: whilepos,
229 Cond: cond,
230 Body: body,
231 }
232 }
233
234
235
236
237 func (p *parser) parseForLoopVariables() Expr {
238
239
240 v := p.parsePrimaryWithSuffix()
241 if p.tok != COMMA {
242 return v
243 }
244
245 list := []Expr{v}
246 for p.tok == COMMA {
247 p.nextToken()
248 if terminatesExprList(p.tok) {
249 break
250 }
251 list = append(list, p.parsePrimaryWithSuffix())
252 }
253 return &TupleExpr{List: list}
254 }
255
256
257
258 func (p *parser) parseSimpleStmt(stmts []Stmt, consumeNL bool) []Stmt {
259 for {
260 stmts = append(stmts, p.parseSmallStmt())
261 if p.tok != SEMI {
262 break
263 }
264 p.nextToken()
265 if p.tok == NEWLINE || p.tok == EOF {
266 break
267 }
268 }
269
270 if p.tok != EOF && consumeNL {
271 p.consume(NEWLINE)
272 }
273
274 return stmts
275 }
276
277
278
279
280
281
282 func (p *parser) parseSmallStmt() Stmt {
283 switch p.tok {
284 case RETURN:
285 pos := p.nextToken()
286 var result Expr
287 if p.tok != EOF && p.tok != NEWLINE && p.tok != SEMI {
288 result = p.parseExpr(false)
289 }
290 return &ReturnStmt{Return: pos, Result: result}
291
292 case BREAK, CONTINUE, PASS:
293 tok := p.tok
294 pos := p.nextToken()
295 return &BranchStmt{Token: tok, TokenPos: pos}
296
297 case LOAD:
298 return p.parseLoadStmt()
299 }
300
301
302 x := p.parseExpr(false)
303 switch p.tok {
304 case EQ, PLUS_EQ, MINUS_EQ, STAR_EQ, SLASH_EQ, SLASHSLASH_EQ, PERCENT_EQ, AMP_EQ, PIPE_EQ, CIRCUMFLEX_EQ, LTLT_EQ, GTGT_EQ:
305 op := p.tok
306 pos := p.nextToken()
307 rhs := p.parseExpr(false)
308 return &AssignStmt{OpPos: pos, Op: op, LHS: x, RHS: rhs}
309 }
310
311
312 return &ExprStmt{X: x}
313 }
314
315
316 func (p *parser) parseLoadStmt() *LoadStmt {
317 loadPos := p.nextToken()
318 lparen := p.consume(LPAREN)
319
320 if p.tok != STRING {
321 p.in.errorf(p.in.pos, "first operand of load statement must be a string literal")
322 }
323 module := p.parsePrimary().(*Literal)
324
325 var from, to []*Ident
326 for p.tok != RPAREN && p.tok != EOF {
327 p.consume(COMMA)
328 if p.tok == RPAREN {
329 break
330 }
331 switch p.tok {
332 case STRING:
333
334
335 lit := p.parsePrimary().(*Literal)
336 id := &Ident{
337 NamePos: lit.TokenPos.add(`"`),
338 Name: lit.Value.(string),
339 }
340 to = append(to, id)
341 from = append(from, id)
342
343 case IDENT:
344
345 id := p.parseIdent()
346 to = append(to, id)
347 if p.tok != EQ {
348 p.in.errorf(p.in.pos, `load operand must be "%[1]s" or %[1]s="originalname" (want '=' after %[1]s)`, id.Name)
349 }
350 p.consume(EQ)
351 if p.tok != STRING {
352 p.in.errorf(p.in.pos, `original name of loaded symbol must be quoted: %s="originalname"`, id.Name)
353 }
354 lit := p.parsePrimary().(*Literal)
355 from = append(from, &Ident{
356 NamePos: lit.TokenPos.add(`"`),
357 Name: lit.Value.(string),
358 })
359
360 case RPAREN:
361 p.in.errorf(p.in.pos, "trailing comma in load statement")
362
363 default:
364 p.in.errorf(p.in.pos, `load operand must be "name" or localname="name" (got %#v)`, p.tok)
365 }
366 }
367 rparen := p.consume(RPAREN)
368
369 if len(to) == 0 {
370 p.in.errorf(lparen, "load statement must import at least 1 symbol")
371 }
372 return &LoadStmt{
373 Load: loadPos,
374 Module: module,
375 To: to,
376 From: from,
377 Rparen: rparen,
378 }
379 }
380
381
382
383 func (p *parser) parseSuite() []Stmt {
384 if p.tok == NEWLINE {
385 p.nextToken()
386 p.consume(INDENT)
387 var stmts []Stmt
388 for p.tok != OUTDENT && p.tok != EOF {
389 stmts = p.parseStmt(stmts)
390 }
391 p.consume(OUTDENT)
392 return stmts
393 }
394
395 return p.parseSimpleStmt(nil, true)
396 }
397
398 func (p *parser) parseIdent() *Ident {
399 if p.tok != IDENT {
400 p.in.error(p.in.pos, "not an identifier")
401 }
402 id := &Ident{
403 NamePos: p.tokval.pos,
404 Name: p.tokval.raw,
405 }
406 p.nextToken()
407 return id
408 }
409
410 func (p *parser) consume(t Token) Position {
411 if p.tok != t {
412 p.in.errorf(p.in.pos, "got %#v, want %#v", p.tok, t)
413 }
414 return p.nextToken()
415 }
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433 func (p *parser) parseParams() []Expr {
434 var params []Expr
435 for p.tok != RPAREN && p.tok != COLON && p.tok != EOF {
436 if len(params) > 0 {
437 p.consume(COMMA)
438 }
439 if p.tok == RPAREN {
440 break
441 }
442
443
444 if p.tok == STAR || p.tok == STARSTAR {
445 op := p.tok
446 pos := p.nextToken()
447 var x Expr
448 if op == STARSTAR || p.tok == IDENT {
449 x = p.parseIdent()
450 }
451 params = append(params, &UnaryExpr{
452 OpPos: pos,
453 Op: op,
454 X: x,
455 })
456 continue
457 }
458
459
460
461 id := p.parseIdent()
462 if p.tok == EQ {
463 eq := p.nextToken()
464 dflt := p.parseTest()
465 params = append(params, &BinaryExpr{
466 X: id,
467 OpPos: eq,
468 Op: EQ,
469 Y: dflt,
470 })
471 continue
472 }
473
474 params = append(params, id)
475 }
476 return params
477 }
478
479
480
481
482
483
484 func (p *parser) parseExpr(inParens bool) Expr {
485 x := p.parseTest()
486 if p.tok != COMMA {
487 return x
488 }
489
490
491 exprs := p.parseExprs([]Expr{x}, inParens)
492 return &TupleExpr{List: exprs}
493 }
494
495
496
497
498 func (p *parser) parseExprs(exprs []Expr, allowTrailingComma bool) []Expr {
499 for p.tok == COMMA {
500 pos := p.nextToken()
501 if terminatesExprList(p.tok) {
502 if !allowTrailingComma {
503 p.in.error(pos, "unparenthesized tuple with trailing comma")
504 }
505 break
506 }
507 exprs = append(exprs, p.parseTest())
508 }
509 return exprs
510 }
511
512
513 func (p *parser) parseTest() Expr {
514 if p.tok == LAMBDA {
515 return p.parseLambda(true)
516 }
517
518 x := p.parseTestPrec(0)
519
520
521 if p.tok == IF {
522 ifpos := p.nextToken()
523 cond := p.parseTestPrec(0)
524 if p.tok != ELSE {
525 p.in.error(ifpos, "conditional expression without else clause")
526 }
527 elsepos := p.nextToken()
528 else_ := p.parseTest()
529 return &CondExpr{If: ifpos, Cond: cond, True: x, ElsePos: elsepos, False: else_}
530 }
531
532 return x
533 }
534
535
536
537 func (p *parser) parseTestNoCond() Expr {
538 if p.tok == LAMBDA {
539 return p.parseLambda(false)
540 }
541 return p.parseTestPrec(0)
542 }
543
544
545
546 func (p *parser) parseLambda(allowCond bool) Expr {
547 lambda := p.nextToken()
548 var params []Expr
549 if p.tok != COLON {
550 params = p.parseParams()
551 }
552 p.consume(COLON)
553
554 var body Expr
555 if allowCond {
556 body = p.parseTest()
557 } else {
558 body = p.parseTestNoCond()
559 }
560
561 return &LambdaExpr{
562 Lambda: lambda,
563 Params: params,
564 Body: body,
565 }
566 }
567
568 func (p *parser) parseTestPrec(prec int) Expr {
569 if prec >= len(preclevels) {
570 return p.parsePrimaryWithSuffix()
571 }
572
573
574 if p.tok == NOT && prec == int(precedence[NOT]) {
575 pos := p.nextToken()
576 x := p.parseTestPrec(prec)
577 return &UnaryExpr{
578 OpPos: pos,
579 Op: NOT,
580 X: x,
581 }
582 }
583
584 return p.parseBinopExpr(prec)
585 }
586
587
588
589 func (p *parser) parseBinopExpr(prec int) Expr {
590 x := p.parseTestPrec(prec + 1)
591 for first := true; ; first = false {
592 if p.tok == NOT {
593 p.nextToken()
594
595
596 if p.tok != IN {
597 p.in.errorf(p.in.pos, "got %#v, want in", p.tok)
598 }
599 p.tok = NOT_IN
600 }
601
602
603 opprec := int(precedence[p.tok])
604 if opprec < prec {
605 return x
606 }
607
608
609 if !first && opprec == int(precedence[EQL]) {
610 p.in.errorf(p.in.pos, "%s does not associate with %s (use parens)",
611 x.(*BinaryExpr).Op, p.tok)
612 }
613
614 op := p.tok
615 pos := p.nextToken()
616 y := p.parseTestPrec(opprec + 1)
617 x = &BinaryExpr{OpPos: pos, Op: op, X: x, Y: y}
618 }
619 }
620
621
622 var precedence [maxToken]int8
623
624
625
626
627
628 var preclevels = [...][]Token{
629 {OR},
630 {AND},
631 {NOT},
632 {EQL, NEQ, LT, GT, LE, GE, IN, NOT_IN},
633 {PIPE},
634 {CIRCUMFLEX},
635 {AMP},
636 {LTLT, GTGT},
637 {MINUS, PLUS},
638 {STAR, PERCENT, SLASH, SLASHSLASH},
639 }
640
641 func init() {
642
643 for i := range precedence {
644 precedence[i] = -1
645 }
646 for level, tokens := range preclevels {
647 for _, tok := range tokens {
648 precedence[tok] = int8(level)
649 }
650 }
651 }
652
653
654
655
656
657 func (p *parser) parsePrimaryWithSuffix() Expr {
658 x := p.parsePrimary()
659 for {
660 switch p.tok {
661 case DOT:
662 dot := p.nextToken()
663 id := p.parseIdent()
664 x = &DotExpr{Dot: dot, X: x, Name: id}
665 case LBRACK:
666 x = p.parseSliceSuffix(x)
667 case LPAREN:
668 x = p.parseCallSuffix(x)
669 default:
670 return x
671 }
672 }
673 }
674
675
676 func (p *parser) parseSliceSuffix(x Expr) Expr {
677 lbrack := p.nextToken()
678 var lo, hi, step Expr
679 if p.tok != COLON {
680 y := p.parseExpr(false)
681
682
683 if p.tok == RBRACK {
684 rbrack := p.nextToken()
685 return &IndexExpr{X: x, Lbrack: lbrack, Y: y, Rbrack: rbrack}
686 }
687
688 lo = y
689 }
690
691
692 if p.tok == COLON {
693 p.nextToken()
694 if p.tok != COLON && p.tok != RBRACK {
695 hi = p.parseTest()
696 }
697 }
698 if p.tok == COLON {
699 p.nextToken()
700 if p.tok != RBRACK {
701 step = p.parseTest()
702 }
703 }
704 rbrack := p.consume(RBRACK)
705 return &SliceExpr{X: x, Lbrack: lbrack, Lo: lo, Hi: hi, Step: step, Rbrack: rbrack}
706 }
707
708
709 func (p *parser) parseCallSuffix(fn Expr) Expr {
710 lparen := p.consume(LPAREN)
711 var rparen Position
712 var args []Expr
713 if p.tok == RPAREN {
714 rparen = p.nextToken()
715 } else {
716 args = p.parseArgs()
717 rparen = p.consume(RPAREN)
718 }
719 return &CallExpr{Fn: fn, Lparen: lparen, Args: args, Rparen: rparen}
720 }
721
722
723
724
725 func (p *parser) parseArgs() []Expr {
726 var args []Expr
727 for p.tok != RPAREN && p.tok != EOF {
728 if len(args) > 0 {
729 p.consume(COMMA)
730 }
731 if p.tok == RPAREN {
732 break
733 }
734
735
736 if p.tok == STAR || p.tok == STARSTAR {
737 op := p.tok
738 pos := p.nextToken()
739 x := p.parseTest()
740 args = append(args, &UnaryExpr{
741 OpPos: pos,
742 Op: op,
743 X: x,
744 })
745 continue
746 }
747
748
749
750
751 x := p.parseTest()
752
753 if p.tok == EQ {
754
755 if _, ok := x.(*Ident); !ok {
756 p.in.errorf(p.in.pos, "keyword argument must have form name=expr")
757 }
758 eq := p.nextToken()
759 y := p.parseTest()
760 x = &BinaryExpr{
761 X: x,
762 OpPos: eq,
763 Op: EQ,
764 Y: y,
765 }
766 }
767
768 args = append(args, x)
769 }
770 return args
771 }
772
773
774
775
776
777
778
779 func (p *parser) parsePrimary() Expr {
780 switch p.tok {
781 case IDENT:
782 return p.parseIdent()
783
784 case INT, FLOAT, STRING, BYTES:
785 var val interface{}
786 tok := p.tok
787 switch tok {
788 case INT:
789 if p.tokval.bigInt != nil {
790 val = p.tokval.bigInt
791 } else {
792 val = p.tokval.int
793 }
794 case FLOAT:
795 val = p.tokval.float
796 case STRING, BYTES:
797 val = p.tokval.string
798 }
799 raw := p.tokval.raw
800 pos := p.nextToken()
801 return &Literal{Token: tok, TokenPos: pos, Raw: raw, Value: val}
802
803 case LBRACK:
804 return p.parseList()
805
806 case LBRACE:
807 return p.parseDict()
808
809 case LPAREN:
810 lparen := p.nextToken()
811 if p.tok == RPAREN {
812
813 rparen := p.nextToken()
814 return &TupleExpr{Lparen: lparen, Rparen: rparen}
815 }
816 e := p.parseExpr(true)
817 rparen := p.consume(RPAREN)
818 return &ParenExpr{
819 Lparen: lparen,
820 X: e,
821 Rparen: rparen,
822 }
823
824 case MINUS, PLUS, TILDE:
825 tok := p.tok
826 pos := p.nextToken()
827 x := p.parsePrimaryWithSuffix()
828 return &UnaryExpr{
829 OpPos: pos,
830 Op: tok,
831 X: x,
832 }
833 }
834 p.in.errorf(p.in.pos, "got %#v, want primary expression", p.tok)
835 panic("unreachable")
836 }
837
838
839
840
841
842 func (p *parser) parseList() Expr {
843 lbrack := p.nextToken()
844 if p.tok == RBRACK {
845
846 rbrack := p.nextToken()
847 return &ListExpr{Lbrack: lbrack, Rbrack: rbrack}
848 }
849
850 x := p.parseTest()
851
852 if p.tok == FOR {
853
854 return p.parseComprehensionSuffix(lbrack, x, RBRACK)
855 }
856
857 exprs := []Expr{x}
858 if p.tok == COMMA {
859
860 exprs = p.parseExprs(exprs, true)
861 }
862
863 rbrack := p.consume(RBRACK)
864 return &ListExpr{Lbrack: lbrack, List: exprs, Rbrack: rbrack}
865 }
866
867
868
869
870 func (p *parser) parseDict() Expr {
871 lbrace := p.nextToken()
872 if p.tok == RBRACE {
873
874 rbrace := p.nextToken()
875 return &DictExpr{Lbrace: lbrace, Rbrace: rbrace}
876 }
877
878 x := p.parseDictEntry()
879
880 if p.tok == FOR {
881
882 return p.parseComprehensionSuffix(lbrace, x, RBRACE)
883 }
884
885 entries := []Expr{x}
886 for p.tok == COMMA {
887 p.nextToken()
888 if p.tok == RBRACE {
889 break
890 }
891 entries = append(entries, p.parseDictEntry())
892 }
893
894 rbrace := p.consume(RBRACE)
895 return &DictExpr{Lbrace: lbrace, List: entries, Rbrace: rbrace}
896 }
897
898
899 func (p *parser) parseDictEntry() *DictEntry {
900 k := p.parseTest()
901 colon := p.consume(COLON)
902 v := p.parseTest()
903 return &DictEntry{Key: k, Colon: colon, Value: v}
904 }
905
906
907
908
909
910
911 func (p *parser) parseComprehensionSuffix(lbrace Position, body Expr, endBrace Token) Expr {
912 var clauses []Node
913 for p.tok != endBrace {
914 if p.tok == FOR {
915 pos := p.nextToken()
916 vars := p.parseForLoopVariables()
917 in := p.consume(IN)
918
919
920
921
922
923
924 x := p.parseTestPrec(0)
925 clauses = append(clauses, &ForClause{For: pos, Vars: vars, In: in, X: x})
926 } else if p.tok == IF {
927 pos := p.nextToken()
928 cond := p.parseTestNoCond()
929 clauses = append(clauses, &IfClause{If: pos, Cond: cond})
930 } else {
931 p.in.errorf(p.in.pos, "got %#v, want '%s', for, or if", p.tok, endBrace)
932 }
933 }
934 rbrace := p.nextToken()
935
936 return &Comprehension{
937 Curly: endBrace == RBRACE,
938 Lbrack: lbrace,
939 Body: body,
940 Clauses: clauses,
941 Rbrack: rbrace,
942 }
943 }
944
945 func terminatesExprList(tok Token) bool {
946 switch tok {
947 case EOF, NEWLINE, EQ, RBRACE, RBRACK, RPAREN, SEMI:
948 return true
949 }
950 return false
951 }
952
953
954
955
956
957
958
959
960
961
962
963 func flattenAST(root Node) (pre, post []Node) {
964 stack := []Node{}
965 Walk(root, func(n Node) bool {
966 if n != nil {
967 pre = append(pre, n)
968 stack = append(stack, n)
969 } else {
970 post = append(post, stack[len(stack)-1])
971 stack = stack[:len(stack)-1]
972 }
973 return true
974 })
975 return pre, post
976 }
977
978
979 func (p *parser) assignComments(n Node) {
980
981 if len(p.in.lineComments)+len(p.in.suffixComments) == 0 {
982 return
983 }
984
985 pre, post := flattenAST(n)
986
987
988 line := p.in.lineComments
989 for _, x := range pre {
990 start, _ := x.Span()
991
992 switch x.(type) {
993 case *File:
994 continue
995 }
996
997 for len(line) > 0 && !start.isBefore(line[0].Start) {
998 x.AllocComments()
999 x.Comments().Before = append(x.Comments().Before, line[0])
1000 line = line[1:]
1001 }
1002 }
1003
1004
1005 if len(line) > 0 {
1006 n.AllocComments()
1007 n.Comments().After = append(n.Comments().After, line...)
1008 }
1009
1010
1011 suffix := p.in.suffixComments
1012 for i := len(post) - 1; i >= 0; i-- {
1013 x := post[i]
1014
1015
1016 switch x.(type) {
1017 case *File:
1018 continue
1019 }
1020
1021 _, end := x.Span()
1022 if len(suffix) > 0 && end.isBefore(suffix[len(suffix)-1].Start) {
1023 x.AllocComments()
1024 x.Comments().Suffix = append(x.Comments().Suffix, suffix[len(suffix)-1])
1025 suffix = suffix[:len(suffix)-1]
1026 }
1027 }
1028 }
1029
View as plain text