-
Notifications
You must be signed in to change notification settings - Fork 0
/
token.go
94 lines (80 loc) · 1.84 KB
/
token.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package asyncpi
import (
"bytes"
"fmt"
"strings"
"unicode/utf8"
)
// Tokens for use with lexer and parser.
// tok is a lexical token.
type tok int
const (
// kILLEGAL is a special token for errors.
kILLEGAL tok = iota
)
var eof = rune(0)
// TokenPos is a pair of coordinate to identify start of token.
type TokenPos struct {
Char int
Lines []int
}
// CaretDiag returns the input b with caret to locate error position for diagnosis.
func (pos TokenPos) CaretDiag(b []byte) []byte {
var lastLine bytes.Buffer
for _, l := range pos.Lines {
if l > 0 {
lastLine.Reset() // New line will replace last line.
}
for c := 0; c < l; {
r, size := utf8.DecodeRune(b)
if r != '\n' {
lastLine.WriteRune(r)
}
b = b[size:]
c += size
}
b = b[1:] // newline
}
var errbuf bytes.Buffer
var caret bytes.Buffer
column := 0
LINE:
for len(b) > 0 {
r, size := utf8.DecodeRune(b)
if r == '\n' {
if column == 0 {
} else {
break LINE
}
} else {
errbuf.WriteRune(r)
}
if column == pos.Char-1 {
caret.WriteRune('↑')
} else if column < pos.Char-1 {
caret.WriteRune(' ')
}
b = b[size:]
column += size
}
var diag bytes.Buffer
prefix := strings.Repeat(" ", len(pos.String()))
if lastLine.String() != "" {
diag.WriteString(fmt.Sprintf("%s %s\n", prefix, lastLine.String()))
}
diag.WriteString(fmt.Sprintf("%s → %s\n", pos.String(), errbuf.String()))
diag.WriteString(fmt.Sprintf("%s %s\n", prefix, caret.String()))
return diag.Bytes()
}
func (p TokenPos) String() string {
return fmt.Sprintf("%d:%d", len(p.Lines)+1, p.Char)
}
func isWhitespace(ch rune) bool {
return ch == ' ' || ch == '\t' || ch == '\n'
}
func isAlphaNum(ch rune) bool {
return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ('0' <= ch && ch <= '9')
}
func isNameSymbols(ch rune) bool {
return ch == '_' || ch == '-'
}