-
Notifications
You must be signed in to change notification settings - Fork 0
/
scanner.go
154 lines (143 loc) · 2.58 KB
/
scanner.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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package grape
import (
"bytes"
"fmt"
)
const (
tkIndent = iota
tkQuestionMark
tkIdentifier
tkSelector
tkRegexp
tkLeftBracket
tkRightBracket
tkLineBreak
tkEOF
)
type scanner struct {
start, i int
src string
line int
inBrackets bool
regexp bytes.Buffer
}
func (s *scanner) prev() {
s.i--
if s.src[s.i] == '\n' {
s.line--
}
}
func (s *scanner) next() (byte, bool) {
if s.i < len(s.src) {
c := s.src[s.i]
if c == '\n' {
s.line++
}
s.i++
return c, false
}
return 0, true
}
func (s *scanner) pos() (int, int, int) {
return s.line, s.start, s.i
}
func (s *scanner) data() string {
return s.src[s.start:s.i]
}
func (s *scanner) scan() (int, error) {
s.start = s.i
c, eof := s.next()
if eof {
return tkEOF, nil
}
switch c {
case '\t':
if s.skip(func(c byte) bool { return c != '\t' }) {
return tkEOF, nil
}
return tkIndent, nil
case '\n':
if s.skip(func(c byte) bool { return c != ' ' && c != '\n' }) {
return tkEOF, nil
}
return tkLineBreak, nil
case ' ':
if s.skip(func(c byte) bool { return c != ' ' && c != '\n' }) {
return tkEOF, nil
}
case '(':
s.inBrackets = true
return tkLeftBracket, nil
case ')':
s.inBrackets = false
return tkRightBracket, nil
case '=':
c, eof := s.next()
if eof {
return tkEOF, fmt.Errorf("unexpected EOF")
}
if c != '/' {
return tkEOF, fmt.Errorf("expected '/', found %q", c)
}
s.regexp.Reset()
var escape bool
for {
c, eof := s.next()
if eof {
return tkEOF, fmt.Errorf("unexpected EOF")
}
if escape {
switch c {
case '/':
s.regexp.WriteByte('/')
case '\\':
s.regexp.WriteByte('\\')
case 'n':
s.regexp.WriteByte('\n')
case 't':
s.regexp.WriteByte('\t')
default:
s.regexp.WriteByte('\\')
s.regexp.WriteByte(c)
}
escape = false
} else {
switch c {
case '/':
return tkRegexp, nil
case '\\':
escape = true
default:
s.regexp.WriteByte(c)
}
}
}
case '?':
return tkQuestionMark, nil
default:
if s.inBrackets {
s.skip(func(c byte) bool {
return c == '\t' || c == ' ' || c == '\n' || c == '(' || c == ')' || c == '='
})
return tkIdentifier, nil
}
s.skip(func(c byte) bool { return c == '\t' || c == ' ' || c == '\n' })
return tkSelector, nil
}
return s.scan()
}
func (s *scanner) skip(stop func(byte) bool) bool {
for {
c, eof := s.next()
if eof {
return true
}
if stop(c) {
s.prev()
return false
}
}
}
func (s *scanner) skipWhitespace() bool {
return s.skip(func(c byte) bool { return c != '\n' && c != '\t' && c != ' ' })
}