-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathstream.go
358 lines (328 loc) · 8.65 KB
/
stream.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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
package tokenizer
import (
"strconv"
"strings"
)
// Stream iterator via parsed tokens.
// If data reads from an infinite buffer, then the iterator will be read data from the reader chunk-by-chunk.
type Stream struct {
t *Tokenizer
// count of tokens in the stream
len int
// pointer to the node of a token double-linked list
current *Token
// pointer of valid token if current moved to out of bounds (out of end the list)
prev *Token
// pointer of valid token if current moved to out of bounds (out of begin the list)
next *Token
// pointer to head of the list
head *Token
// last whitespaces before the end of a source
wsTail []byte
// count of parsed bytes
parsed int
p *parsing
historySize int
}
func validateToken(t *Token) *Token {
if t != nil {
return t
}
return undefToken
}
// NewStream creates a new parsed stream of tokens.
func NewStream(p *parsing) *Stream {
return &Stream{
t: p.t,
head: validateToken(p.head),
current: validateToken(p.head),
len: p.n,
wsTail: p.tail,
parsed: p.parsed + p.pos,
}
}
// NewInfStream creates new stream with active parser.
func NewInfStream(p *parsing) *Stream {
return &Stream{
t: p.t,
p: p,
len: p.n,
head: validateToken(p.head),
current: validateToken(p.head),
}
}
// SetHistorySize sets the number of tokens that should remain after the current token
func (s *Stream) SetHistorySize(size int) *Stream {
s.historySize = size
return s
}
// Close releases all token objects to pool
func (s *Stream) Close() {
for ptr := s.head; ptr != nil && ptr != undefToken; {
p := ptr.next
s.t.freeToken(ptr)
ptr = p
}
s.next = nil
s.prev = nil
s.head = undefToken
s.current = undefToken
s.len = 0
}
func (s *Stream) String() string {
items := make([]string, 0, s.len)
ptr := s.head
for ptr != nil {
items = append(items, strconv.Itoa(ptr.id)+": "+ptr.String())
ptr = ptr.next
}
return strings.Join(items, "\n")
}
// GetParsedLength returns currently count parsed bytes.
func (s *Stream) GetParsedLength() int {
if s.p == nil {
return s.parsed
} else {
return s.p.parsed + s.p.pos
}
}
// GoNext moves the stream pointer to the next token.
// If there is no token, it initiates the parsing of the next chunk of data.
// If there is no data, the pointer will point to the TokenUndef token.
func (s *Stream) GoNext() *Stream {
if s.current.next != nil {
s.current = s.current.next
if s.current.next == nil && s.p != nil { // lazy load and parse next data-chunk
n := s.p.n
s.p.parse()
s.len += s.p.n - n
}
if s.historySize != 0 && s.current.id-s.head.id > s.historySize {
t := s.head
s.head = s.head.unlink()
s.t.freeToken(t)
s.len--
}
} else if s.current == undefToken {
s.current = s.prev
s.prev = nil
} else {
s.prev = s.current
s.current = undefToken
}
return s
}
// GoPrev moves the pointer of stream to the next token.
// The number of possible calls is limited if you specified SetHistorySize.
// If the beginning of the stream or the end of the history is reached, the pointer will point to the TokenUndef token.
func (s *Stream) GoPrev() *Stream {
if s.current.prev != nil {
s.current = s.current.prev
} else if s.current == undefToken {
s.current = s.next
s.prev = nil
} else {
s.next = s.current
s.current = undefToken
}
return s
}
// GoTo moves the pointer of stream to specific token.
func (s *Stream) GoTo(id int) *Stream {
if s.current == undefToken {
if s.prev != nil && id <= s.prev.id { // we at the end of the stream
s.GoPrev() // now current is available
for s.current != nil && id != s.current.id {
s.GoPrev()
}
} else if s.next != nil && id >= s.prev.id { // we at the beginning of the stream
s.GoNext() // now current is available
for s.current != nil && id != s.current.id {
s.GoNext()
}
}
return s
}
if id > s.current.id {
for s.current != nil && id != s.current.id {
s.GoNext()
}
} else if id < s.current.id {
for s.current != nil && id != s.current.id {
s.GoPrev()
}
}
return s
}
// IsValid checks if stream is valid.
// This means that the pointer has not reached the end of the stream.
func (s *Stream) IsValid() bool {
return s.current != nil && s.current != undefToken
}
// IsNextSequence checks if these are next tokens in exactly the same sequence as specified.
func (s *Stream) IsNextSequence(keys ...TokenKey) bool {
var (
result = true
hSize = 0
)
if s.historySize > 0 && s.historySize < len(keys) {
hSize = s.historySize
s.historySize = len(keys)
}
start := s.current
for _, key := range keys {
if !s.GoNext().CurrentToken().Is(key) {
result = false
break
}
}
s.current = start
if hSize != 0 {
s.SetHistorySize(hSize)
}
return result
}
// IsAnyNextSequence checks that at least one token from each group is contained in a sequence of tokens
func (s *Stream) IsAnyNextSequence(keys ...[]TokenKey) bool {
var (
result = true
hSize = 0
)
if s.historySize > 0 && s.historySize < len(keys) {
hSize = s.historySize
s.historySize = len(keys)
}
start := s.current
for _, key := range keys {
found := false
for _, k := range key {
if s.GoNext().CurrentToken().Is(k) {
found = true
break
}
}
if !found {
result = false
break
}
}
s.current = start
if hSize != 0 {
s.SetHistorySize(hSize)
}
return result
}
// HeadToken returns the pointer to head-token.
// Parser may change Head token if history size is enabled.
func (s *Stream) HeadToken() *Token {
return s.head
}
// CurrentToken always returns the token.
// If the pointer is not valid (see IsValid), CurrentToken will be return TokenUndef token.
// Do not save result (Token) into variables — current token may be changed at any time.
func (s *Stream) CurrentToken() *Token {
return s.current
}
// PrevToken returns previous token from the stream.
// If the previous token doesn't exist, the method returns TypeUndef token.
// Do not save a result (Token) into variables — the previous token may be changed at any time.
func (s *Stream) PrevToken() *Token {
if s.current.prev != nil {
return s.current.prev
}
return undefToken
}
// NextToken returns next token from the stream.
// If next token doesn't exist, the method returns TypeUndef token.
// Do not save a result (Token) into variables — the next token may be changed at any time.
func (s *Stream) NextToken() *Token {
if s.current.next != nil {
return s.current.next
}
return undefToken
}
// GoNextIfNextIs moves the stream pointer to the next token if the next token has specific token keys.
// If keys matched pointer will be updated and the method returned true.
// Otherwise, returned false.
func (s *Stream) GoNextIfNextIs(key TokenKey, otherKeys ...TokenKey) bool {
if s.NextToken().Is(key, otherKeys...) {
s.GoNext()
return true
}
return false
}
// GetSnippet returns slice of tokens.
// Slice generated from current token position and include tokens before and after current token.
func (s *Stream) GetSnippet(before, after int) []Token {
var segment []Token
if s.current == undefToken {
if s.prev != nil && before > s.prev.id-s.head.id {
before = s.prev.id - s.head.id
} else {
before = 0
}
} else if before > s.current.id-s.head.id {
before = s.current.id - s.head.id
}
if after > s.len-before-1 {
after = s.len - before - 1
}
segment = make([]Token, before+after+1)
if len(segment) == 0 {
return segment
}
var ptr *Token
if s.next != nil {
ptr = s.next
} else if s.prev != nil {
ptr = s.prev
} else {
ptr = s.current
}
for p := ptr; p != nil; p, before = ptr.prev, before-1 {
segment[before] = Token{
id: ptr.id,
key: ptr.key,
value: ptr.value,
line: ptr.line,
offset: ptr.offset,
indent: ptr.indent,
string: ptr.string,
}
if before <= 0 {
break
}
}
for p, i := ptr.next, 1; p != nil; p, i = p.next, i+1 {
segment[before+i] = Token{
id: p.id,
key: p.key,
value: p.value,
line: p.line,
offset: p.offset,
indent: p.indent,
string: p.string,
}
if i >= after {
break
}
}
return segment
}
// GetSnippetAsString returns tokens before and after current token as string.
// `maxStringLength` specifies max length of each token string.
// Zero — unlimited token string length.
// If string is greater than maxLength method removes some runes in the middle of the string.
func (s *Stream) GetSnippetAsString(before, after, maxStringLength int) string {
segments := s.GetSnippet(before, after)
str := make([]string, len(segments))
for i, token := range segments {
v := token.ValueString()
if maxStringLength > 4 && len(v) > maxStringLength {
str[i] = v[:maxStringLength/2] + "..." + v[maxStringLength/2+1:]
} else {
str[i] = v
}
}
return strings.Join(str, "")
}