scanner.mx raw
1 package syntax
2
3 import (
4 "io"
5 "git.smesh.lol/moxie/pkg/token"
6 "unicode/utf8"
7 )
8
9 const (
10 comments uint32 = 1 << iota
11 directives
12 )
13
14 const maxRune = 0x10FFFF
15
16 func isLetter(ch rune) (ok bool) {
17 if ch < 0x80 {
18 return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_'
19 }
20 return ch >= 0xC0
21 }
22
23 func isDigit(ch rune) (ok bool) {
24 if ch < 0x80 {
25 return ch >= '0' && ch <= '9'
26 }
27 return false
28 }
29
30
31 type Scanner struct {
32 Source
33 mode uint32
34 nlsemi bool
35
36 Line, Col uint32
37 Blank bool
38 Tok token.Token
39 Lit string
40 Bad bool
41 Kind token.LitKind
42 Op token.Operator
43 Prec int32
44
45 keywordMap [1 << 6]token.Token
46 keywordsReady bool
47 }
48
49 func (s *Scanner) Init(src io.Reader, errh func(line, col uint32, msg string), mode uint32) {
50 s.Source.init(src, errh)
51 s.mode = mode
52 s.nlsemi = false
53 s.initKeywords()
54 }
55
56 func (s *Scanner) InitBytes(src []byte, errh func(line, col uint32, msg string), mode uint32) {
57 s.Source.initBytes(src, errh)
58 s.mode = 0
59 s.nlsemi = false
60 s.initKeywords()
61 }
62
63 func (s *Scanner) Errorf(msg string) {
64 s.error(msg)
65 }
66
67 func (s *Scanner) ErrorAtf(offset int32, msg string) {
68 s.errh(s.line, s.col+uint32(offset), msg)
69 }
70
71 func (s *Scanner) SetLit(kind token.LitKind, ok bool) {
72 s.nlsemi = true
73 s.Tok = token.Literal
74 s.Lit = string(s.segmentCopy())
75 s.Bad = !ok
76 s.Kind = kind
77 }
78
79 func (s *Scanner) Next() {
80 nlsemi := s.nlsemi
81 s.nlsemi = false
82
83 redo:
84 s.stop()
85 startLine, startCol := s.pos()
86 for s.ch == ' ' || s.ch == '\t' || s.ch == '\n' && !nlsemi || s.ch == '\r' {
87 s.nextch()
88 }
89
90 s.Line, s.Col = s.pos()
91 s.Blank = s.line > startLine || startCol == Colbase
92 s.start()
93 if IsLetter(s.ch) || s.ch >= utf8.RuneSelf && s.AtIdentChar(true) {
94 s.nextch()
95 s.Ident()
96 return
97 }
98
99 switch s.ch {
100 case -1:
101 if nlsemi {
102 s.Lit = "EOF"
103 s.Tok = token.Semi
104 break
105 }
106 s.Tok = token.EOF
107
108 case '\n':
109 s.nextch()
110 s.Lit = "newline"
111 s.Tok = token.Semi
112
113 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
114 s.Number(false)
115
116 case '"':
117 s.stdString()
118
119 case '`':
120 s.rawString()
121
122 case '\'':
123 s.rune()
124
125 case '(':
126 s.nextch()
127 s.Tok = token.Lparen
128
129 case '[':
130 s.nextch()
131 s.Tok = token.Lbrack
132
133 case '{':
134 s.nextch()
135 s.Tok = token.Lbrace
136
137 case ',':
138 s.nextch()
139 s.Tok = token.Comma
140
141 case ';':
142 s.nextch()
143 s.Lit = "semicolon"
144 s.Tok = token.Semi
145
146 case ')':
147 s.nextch()
148 s.nlsemi = true
149 s.Tok = token.Rparen
150
151 case ']':
152 s.nextch()
153 s.nlsemi = true
154 s.Tok = token.Rbrack
155
156 case '}':
157 s.nextch()
158 s.nlsemi = true
159 s.Tok = token.Rbrace
160
161 case ':':
162 s.nextch()
163 if s.ch == '=' {
164 s.nextch()
165 s.Tok = token.Define
166 break
167 }
168 s.Tok = token.Colon
169
170 case '.':
171 s.nextch()
172 if IsDecimal(s.ch) {
173 s.Number(true)
174 break
175 }
176 if s.ch == '.' {
177 s.nextch()
178 if s.ch == '.' {
179 s.nextch()
180 s.Tok = token.DotDotDot
181 break
182 }
183 s.rewind()
184 s.nextch()
185 }
186 s.Tok = token.Dot
187
188 case '+':
189 s.nextch()
190 s.Op, s.Prec = token.Add, token.PrecAdd
191 if s.ch != '+' {
192 s.assignOp()
193 return
194 }
195 s.nextch()
196 s.nlsemi = true
197 s.Tok = token.IncOp
198
199 case '-':
200 s.nextch()
201 s.Op, s.Prec = token.Sub, token.PrecAdd
202 if s.ch != '-' {
203 s.assignOp()
204 return
205 }
206 s.nextch()
207 s.nlsemi = true
208 s.Tok = token.IncOp
209
210 case '*':
211 s.nextch()
212 s.Op, s.Prec = token.Mul, token.PrecMul
213 if s.ch == '=' {
214 s.nextch()
215 s.Tok = token.AssignOp
216 break
217 }
218 s.Tok = token.Star
219
220 case '/':
221 s.nextch()
222 if s.ch == '/' {
223 s.nextch()
224 s.lineComment()
225 goto redo
226 }
227 if s.ch == '*' {
228 s.nextch()
229 s.fullComment()
230 if line, _ := s.pos(); line > s.Line && nlsemi {
231 s.Lit = "newline"
232 s.Tok = token.Semi
233 break
234 }
235 goto redo
236 }
237 s.Op, s.Prec = token.Div, token.PrecMul
238 if s.ch == '=' {
239 s.nextch()
240 s.Tok = token.AssignOp
241 } else {
242 s.Tok = token.OperatorType
243 }
244
245 case '%':
246 s.nextch()
247 s.Op, s.Prec = token.Rem, token.PrecMul
248 if s.ch == '=' {
249 s.nextch()
250 s.Tok = token.AssignOp
251 } else {
252 s.Tok = token.OperatorType
253 }
254
255 case '&':
256 s.nextch()
257 if s.ch == '&' {
258 s.nextch()
259 s.Op, s.Prec = token.AndAnd, token.PrecAndAnd
260 s.Tok = token.OperatorType
261 break
262 }
263 s.Op, s.Prec = token.And, token.PrecMul
264 if s.ch == '^' {
265 s.nextch()
266 s.Op = token.AndNot
267 }
268 if s.ch == '=' {
269 s.nextch()
270 s.Tok = token.AssignOp
271 } else {
272 s.Tok = token.OperatorType
273 }
274
275 case '|':
276 s.nextch()
277 if s.ch == '|' {
278 s.nextch()
279 s.Op, s.Prec = token.OrOr, token.PrecOrOr
280 s.Tok = token.OperatorType
281 break
282 }
283 s.Op, s.Prec = token.Or, token.PrecAdd
284 if s.ch == '=' {
285 s.nextch()
286 s.Tok = token.AssignOp
287 } else {
288 s.Tok = token.OperatorType
289 }
290
291 case '^':
292 s.nextch()
293 s.Op, s.Prec = token.Xor, token.PrecAdd
294 if s.ch == '=' {
295 s.nextch()
296 s.Tok = token.AssignOp
297 } else {
298 s.Tok = token.OperatorType
299 }
300
301 case '<':
302 s.nextch()
303 if s.ch == '=' {
304 s.nextch()
305 s.Op, s.Prec = token.Leq, token.PrecCmp
306 s.Tok = token.OperatorType
307 break
308 }
309 if s.ch == '<' {
310 s.nextch()
311 s.Op, s.Prec = token.Shl, token.PrecMul
312 s.assignOp()
313 return
314 }
315 if s.ch == '-' {
316 s.nextch()
317 s.Tok = token.Arrow
318 break
319 }
320 s.Op, s.Prec = token.Lss, token.PrecCmp
321 s.Tok = token.OperatorType
322
323 case '>':
324 s.nextch()
325 if s.ch == '=' {
326 s.nextch()
327 s.Op, s.Prec = token.Geq, token.PrecCmp
328 s.Tok = token.OperatorType
329 break
330 }
331 if s.ch == '>' {
332 s.nextch()
333 s.Op, s.Prec = token.Shr, token.PrecMul
334 s.assignOp()
335 return
336 }
337 s.Op, s.Prec = token.Gtr, token.PrecCmp
338 s.Tok = token.OperatorType
339
340 case '=':
341 s.nextch()
342 if s.ch == '=' {
343 s.nextch()
344 s.Op, s.Prec = token.Eql, token.PrecCmp
345 s.Tok = token.OperatorType
346 break
347 }
348 s.Tok = token.Assign
349
350 case '!':
351 s.nextch()
352 if s.ch == '=' {
353 s.nextch()
354 s.Op, s.Prec = token.Neq, token.PrecCmp
355 s.Tok = token.OperatorType
356 break
357 }
358 s.Op, s.Prec = token.Not, 0
359 s.Tok = token.OperatorType
360
361 case '~':
362 s.nextch()
363 s.Op, s.Prec = token.Tilde, 0
364 s.Tok = token.OperatorType
365
366 default:
367 s.Errorf("invalid character " | fmtRuneU(s.ch))
368 s.nextch()
369 goto redo
370 }
371
372 return
373 }
374
375 func (s *Scanner) assignOp() {
376 if s.ch == '=' {
377 s.nextch()
378 s.Tok = token.AssignOp
379 return
380 }
381 s.Tok = token.OperatorType
382 }
383
384 func (s *Scanner) Ident() {
385 for IsLetter(s.ch) || IsDecimal(s.ch) {
386 s.nextch()
387 }
388
389 if s.ch >= utf8.RuneSelf {
390 for s.AtIdentChar(false) {
391 s.nextch()
392 }
393 }
394
395 lit := s.segmentCopy()
396 if len(lit) >= 2 {
397 h := (uint32(lit[0])<<4 ^ uint32(lit[1]) + uint32(len(lit))) & 63
398 if tok := s.keywordMap[h]; tok != 0 && tokStrFast(tok) == string(lit) {
399 s.nlsemi = token.Contains(1<<token.Break|1<<token.Continue|1<<token.Fallthrough|1<<token.Return, tok)
400 s.Tok = tok
401 return
402 }
403 }
404
405 s.nlsemi = true
406 c := []byte{:len(lit)}
407 copy(c, lit)
408 s.Lit = string(c)
409 s.Tok = token.NameType
410 }
411
412 func tokStrFast(tok token.Token) (s string) {
413 idx := [48]uint8{0, 3, 7, 14, 16, 19, 23, 24, 26, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 42, 47, 51, 55, 60, 68, 75, 80, 84, 95, 98, 102, 104, 108, 110, 116, 125, 128, 135, 140, 146, 152, 158, 164, 168, 171, 171}
414 return token.Token_name[idx[tok-1]:idx[tok]]
415 }
416
417 func (s *Scanner) AtIdentChar(first bool) (ok bool) {
418 switch {
419 case isLetter(s.ch) || s.ch == '_':
420 case isDigit(s.ch):
421 if first {
422 s.Errorf("identifier cannot begin with digit " | fmtRuneU(s.ch))
423 }
424 case s.ch >= utf8.RuneSelf:
425 s.Errorf("invalid character " | fmtRuneU(s.ch) | " in identifier")
426 default:
427 return false
428 }
429 return true
430 }
431
432 func (s *Scanner) initKeywords() {
433 if s.keywordsReady {
434 return
435 }
436 s.keywordsReady = true
437 for tok := token.Break; tok <= token.Var; tok++ {
438 b := []byte(tok.String())
439 h := (uint32(b[0])<<4 ^ uint32(b[1]) + uint32(len(b))) & 63
440 if s.keywordMap[h] != 0 {
441 panic("imperfect hash")
442 }
443 s.keywordMap[h] = tok
444 }
445 }
446
447 func Lower(ch rune) (r rune) { return ('a' - 'A') | ch }
448 func IsLetter(ch rune) (ok bool) { return 'a' <= Lower(ch) && Lower(ch) <= 'z' || ch == '_' }
449 func IsDecimal(ch rune) (ok bool) { return '0' <= ch && ch <= '9' }
450 func IsHex(ch rune) (ok bool) { return '0' <= ch && ch <= '9' || 'a' <= Lower(ch) && Lower(ch) <= 'f' }
451
452 func (s *Scanner) Digits(base int32, invalid *int32) (digsep int32) {
453 if base <= 10 {
454 hi := rune('0' + base)
455 for IsDecimal(s.ch) || s.ch == '_' {
456 ds := int32(1)
457 if s.ch == '_' {
458 ds = 2
459 } else if s.ch >= hi && *invalid < 0 {
460 _, col := s.pos()
461 *invalid = int32(col - s.col)
462 }
463 digsep |= ds
464 s.nextch()
465 }
466 } else {
467 for IsHex(s.ch) || s.ch == '_' {
468 ds := int32(1)
469 if s.ch == '_' {
470 ds = 2
471 }
472 digsep |= ds
473 s.nextch()
474 }
475 }
476 return
477 }
478
479 func (s *Scanner) Number(seenPoint bool) {
480 ok := true
481 kind := token.IntLit
482 base := int32(10)
483 prefix := rune(0)
484 digsep := int32(0)
485 invalid := int32(-1)
486
487 if !seenPoint {
488 if s.ch == '0' {
489 s.nextch()
490 switch Lower(s.ch) {
491 case 'x':
492 s.nextch()
493 base, prefix = 16, 'x'
494 case 'o':
495 s.nextch()
496 base, prefix = 8, 'o'
497 case 'b':
498 s.nextch()
499 base, prefix = 2, 'b'
500 default:
501 base, prefix = 8, '0'
502 digsep = 1
503 }
504 }
505 digsep |= s.Digits(base, &invalid)
506 if s.ch == '.' {
507 if prefix == 'o' || prefix == 'b' {
508 s.Errorf("invalid radix point in " | baseName(base) | " literal")
509 ok = false
510 }
511 s.nextch()
512 seenPoint = true
513 }
514 }
515
516 if seenPoint {
517 kind = token.FloatLit
518 digsep |= s.Digits(base, &invalid)
519 }
520
521 if digsep&1 == 0 && ok {
522 s.Errorf(baseName(base) | " literal has no digits")
523 ok = false
524 }
525
526 if e := Lower(s.ch); e == 'e' || e == 'p' {
527 if ok {
528 switch {
529 case e == 'e' && prefix != 0 && prefix != '0':
530 s.Errorf(fmtQuoteRune(s.ch) | " exponent requires decimal mantissa")
531 ok = false
532 case e == 'p' && prefix != 'x':
533 s.Errorf(fmtQuoteRune(s.ch) | " exponent requires hexadecimal mantissa")
534 ok = false
535 }
536 }
537 s.nextch()
538 kind = token.FloatLit
539 if s.ch == '+' || s.ch == '-' {
540 s.nextch()
541 }
542 digsep = s.Digits(10, nil) | digsep&2
543 if digsep&1 == 0 && ok {
544 s.Errorf("exponent has no digits")
545 ok = false
546 }
547 } else if prefix == 'x' && kind == token.FloatLit && ok {
548 s.Errorf("hexadecimal mantissa requires a 'p' exponent")
549 ok = false
550 }
551
552 if s.ch == 'i' {
553 kind = token.ImagLit
554 s.nextch()
555 }
556
557 s.SetLit(kind, ok)
558
559 if kind == token.IntLit && invalid >= 0 && ok {
560 s.ErrorAtf(invalid, "invalid digit " | fmtQuoteRune(rune(s.Lit[invalid])) | " in " | baseName(base) | " literal")
561 ok = false
562 }
563
564 if digsep&2 != 0 && ok {
565 if i := invalidSep(s.Lit); i >= 0 {
566 s.ErrorAtf(i, "'_' must separate successive digits")
567 ok = false
568 }
569 }
570
571 s.Bad = !ok
572 }
573
574 func baseName(base int32) (s string) {
575 switch base {
576 case 2:
577 return "binary"
578 case 8:
579 return "octal"
580 case 10:
581 return "decimal"
582 case 16:
583 return "hexadecimal"
584 }
585 panic("invalid base")
586 }
587
588 func invalidSep(x string) (n int32) {
589 x1 := ' '
590 d := '.'
591 i := int32(0)
592
593 if len(x) >= 2 && x[0] == '0' {
594 x1 = Lower(rune(x[1]))
595 if x1 == 'x' || x1 == 'o' || x1 == 'b' {
596 d = '0'
597 i = 2
598 }
599 }
600
601 for ; i < int32(len(x)); i++ {
602 p := d
603 d = rune(x[i])
604 switch {
605 case d == '_':
606 if p != '0' {
607 return i
608 }
609 case IsDecimal(d) || x1 == 'x' && IsHex(d):
610 d = '0'
611 default:
612 if p == '_' {
613 return i - 1
614 }
615 d = '.'
616 }
617 }
618 if d == '_' {
619 return int32(len(x)) - 1
620 }
621
622 return -1
623 }
624
625 func (s *Scanner) rune() {
626 ok := true
627 s.nextch()
628
629 n := 0
630 for ; ; n++ {
631 if s.ch == '\'' {
632 if ok {
633 if n == 0 {
634 s.Errorf("empty rune literal or unescaped '")
635 ok = false
636 } else if n != 1 {
637 s.ErrorAtf(0, "more than one character in rune literal")
638 ok = false
639 }
640 }
641 s.nextch()
642 break
643 }
644 if s.ch == '\\' {
645 s.nextch()
646 if !s.escape('\'') {
647 ok = false
648 }
649 continue
650 }
651 if s.ch == '\n' {
652 if ok {
653 s.Errorf("newline in rune literal")
654 ok = false
655 }
656 break
657 }
658 if s.ch < 0 {
659 if ok {
660 s.ErrorAtf(0, "rune literal not terminated")
661 ok = false
662 }
663 break
664 }
665 s.nextch()
666 }
667
668 s.SetLit(token.RuneLit, ok)
669 }
670
671 func (s *Scanner) stdString() {
672 ok := true
673 s.nextch()
674
675 for {
676 if s.ch == '"' {
677 s.nextch()
678 break
679 }
680 if s.ch == '\\' {
681 s.nextch()
682 if !s.escape('"') {
683 ok = false
684 }
685 continue
686 }
687 if s.ch == '\n' {
688 s.Errorf("newline in string")
689 ok = false
690 break
691 }
692 if s.ch < 0 {
693 s.ErrorAtf(0, "string not terminated")
694 ok = false
695 break
696 }
697 s.nextch()
698 }
699
700 s.SetLit(token.StringLit, ok)
701 }
702
703 func (s *Scanner) rawString() {
704 ok := true
705 s.nextch()
706
707 for {
708 if s.ch == '`' {
709 s.nextch()
710 break
711 }
712 if s.ch < 0 {
713 s.ErrorAtf(0, "string not terminated")
714 ok = false
715 break
716 }
717 s.nextch()
718 }
719
720 s.SetLit(token.StringLit, ok)
721 }
722
723 func (s *Scanner) comment(text string) {
724 s.ErrorAtf(0, text)
725 }
726
727 func (s *Scanner) skipLine() {
728 for s.ch >= 0 && s.ch != '\n' {
729 s.nextch()
730 }
731 }
732
733 func (s *Scanner) lineComment() {
734 if s.mode&comments != 0 {
735 s.skipLine()
736 s.comment(string(s.segmentCopy()))
737 return
738 }
739
740 if s.mode&directives == 0 || (s.ch != 'g' && s.ch != 'l') {
741 s.stop()
742 s.skipLine()
743 return
744 }
745
746 prefix := "go:"
747 if s.ch == 'l' {
748 prefix = "line "
749 }
750
751 for _, r := range prefix {
752 if s.ch != rune(r) {
753 s.stop()
754 s.skipLine()
755 return
756 }
757 s.nextch()
758 }
759 s.skipLine()
760 s.comment(string(s.segmentCopy()))
761 }
762
763 func (s *Scanner) skipComment() (ok bool) {
764 for s.ch >= 0 {
765 for s.ch == '*' {
766 s.nextch()
767 if s.ch == '/' {
768 s.nextch()
769 return true
770 }
771 }
772 s.nextch()
773 }
774 s.ErrorAtf(0, "comment not terminated")
775 return false
776 }
777
778 func (s *Scanner) fullComment() {
779 if s.mode&comments != 0 {
780 if s.skipComment() {
781 s.comment(string(s.segmentCopy()))
782 }
783 return
784 }
785
786 if s.mode&directives == 0 || s.ch != 'l' {
787 s.stop()
788 s.skipComment()
789 return
790 }
791
792 const prefix = "line "
793
794 for _, r := range prefix {
795 if s.ch != rune(r) {
796 s.stop()
797 s.skipComment()
798 return
799 }
800 s.nextch()
801 }
802 if s.skipComment() {
803 s.comment(string(s.segmentCopy()))
804 }
805 }
806
807 func (s *Scanner) escape(quote rune) (ok bool) {
808 var n int32
809 var base, maxVal uint32
810
811 switch s.ch {
812 case quote, 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\':
813 s.nextch()
814 return true
815 case '0', '1', '2', '3', '4', '5', '6', '7':
816 n, base, maxVal = 3, 8, 255
817 case 'x':
818 s.nextch()
819 n, base, maxVal = 2, 16, 255
820 case 'u':
821 s.nextch()
822 n, base, maxVal = 4, 16, maxRune
823 case 'U':
824 s.nextch()
825 n, base, maxVal = 8, 16, maxRune
826 default:
827 if s.ch < 0 {
828 return true
829 }
830 s.Errorf("unknown escape")
831 return false
832 }
833
834 var x uint32
835 for i := n; i > 0; i-- {
836 if s.ch < 0 {
837 return true
838 }
839 d := base
840 if IsDecimal(s.ch) {
841 d = uint32(s.ch) - '0'
842 } else if 'a' <= Lower(s.ch) && Lower(s.ch) <= 'f' {
843 d = uint32(Lower(s.ch)) - 'a' + 10
844 }
845 if d >= base {
846 s.Errorf("invalid character " | fmtQuoteRune(s.ch) | " in " | baseName(int32(base)) | " escape")
847 return false
848 }
849 x = x*base + d
850 s.nextch()
851 }
852
853 if x > maxVal && base == 8 {
854 s.Errorf("octal escape value " | token.SimpleItoaU32(x) | " > 255")
855 return false
856 }
857
858 if x > maxVal || 0xD800 <= x && x < 0xE000 {
859 s.Errorf("escape is invalid Unicode code point " | fmtRuneU(rune(x)))
860 return false
861 }
862
863 return true
864 }
865
866 func String(n Node) (s string) {
867 switch n.(type) {
868 case *Name:
869 return "Name"
870 case *BasicLit:
871 return "BasicLit"
872 case *CompositeLit:
873 return "CompositeLit"
874 case *FuncLit:
875 return "FuncLit"
876 case *ParenExpr:
877 return "ParenExpr"
878 case *SelectorExpr:
879 return "SelectorExpr"
880 case *IndexExpr:
881 return "IndexExpr"
882 case *SliceExpr:
883 return "SliceExpr"
884 case *AssertExpr:
885 return "AssertExpr"
886 case *TypeSwitchGuard:
887 return "TypeSwitchGuard"
888 case *Operation:
889 return "Operation"
890 case *CallExpr:
891 return "CallExpr"
892 case *ListExpr:
893 return "ListExpr"
894 case *ArrayType:
895 return "ArrayType"
896 case *SliceType:
897 return "SliceType"
898 case *DotsType:
899 return "DotsType"
900 case *StructType:
901 return "StructType"
902 case *InterfaceType:
903 return "InterfaceType"
904 case *FuncType:
905 return "FuncType"
906 case *ChanType:
907 return "ChanType"
908 case *MapType:
909 return "MapType"
910 case *BadExpr:
911 return "BadExpr"
912 case *EmptyStmt:
913 return "EmptyStmt"
914 case *LabeledStmt:
915 return "LabeledStmt"
916 case *BlockStmt:
917 return "BlockStmt"
918 case *ExprStmt:
919 return "ExprStmt"
920 case *SendStmt:
921 return "SendStmt"
922 case *DeclStmt:
923 return "DeclStmt"
924 case *AssignStmt:
925 return "AssignStmt"
926 case *BranchStmt:
927 return "BranchStmt"
928 case *CallStmt:
929 return "CallStmt"
930 case *ReturnStmt:
931 return "ReturnStmt"
932 case *IfStmt:
933 return "IfStmt"
934 case *ForStmt:
935 return "ForStmt"
936 case *SwitchStmt:
937 return "SwitchStmt"
938 case *SelectStmt:
939 return "SelectStmt"
940 case *RangeClause:
941 return "RangeClause"
942 case *CaseClause:
943 return "CaseClause"
944 case *CommClause:
945 return "CommClause"
946 case *ImportDecl:
947 return "ImportDecl"
948 case *ConstDecl:
949 return "ConstDecl"
950 case *TypeDecl:
951 return "TypeDecl"
952 case *VarDecl:
953 return "VarDecl"
954 case *FuncDecl:
955 return "FuncDecl"
956 case *File:
957 return "File"
958 }
959 return "Node"
960 }
961
962 func StartPos(n Node) (p token.Pos) {
963 return n.Pos()
964 }
965
966 // fmtRuneU formats a rune as "U+XXXX 'c'" (like %#U).
967 func fmtRuneU(r rune) (s string) {
968 hex := fmtHex32(uint32(r))
969 s = "U+" | hex
970 if r >= 0x20 && r < 0x7f {
971 s = s | " '" | string([]byte{byte(r)}) | "'"
972 }
973 return s
974 }
975
976 // fmtQuoteRune formats a rune as 'c' (like %q for rune).
977 func fmtQuoteRune(r rune) (s string) {
978 if r >= 0x20 && r < 0x7f {
979 return "'" | string([]byte{byte(r)}) | "'"
980 }
981 return "U+" | fmtHex32(uint32(r))
982 }
983
984 // fmtHex32 formats a uint32 as uppercase hex with at least 4 digits.
985 func fmtHex32(v uint32) (s string) {
986 const digits = "0123456789ABCDEF"
987 buf := []byte{:0:8}
988 if v == 0 {
989 return "0000"
990 }
991 for v > 0 {
992 push(buf, digits[v&0xf])
993 v >>= 4
994 }
995 for len(buf) < 4 {
996 push(buf, '0')
997 }
998 for i, j := int32(0), len(buf)-1; i < j; i, j = i+1, j-1 {
999 buf[i], buf[j] = buf[j], buf[i]
1000 }
1001 return string(buf)
1002 }
1003