-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstate.go
331 lines (299 loc) · 7.6 KB
/
state.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
package lr0
import (
"fmt"
"io"
"strings"
"unicode/utf8"
"github.com/pkg/errors"
)
const stateFormatContext = 30
// NewState creates new State for the given buffer `input` pointing to its start
func NewState(input []byte) *State {
return &State{
source: input,
}
}
// State describes an immutable state of reading the underlying buffer at the
// given position
type State struct {
source []byte
at int
}
// to returns new State for the same buffer pointing to the given position `pos`
func (s *State) to(pos int) *State {
pos = fixOffset(pos, len(s.source))
if pos == s.at {
return s
}
return &State{
source: s.source,
at: pos,
}
}
// IsEOF checks if the position is at EOF
func (s *State) IsEOF() bool {
return s.at >= len(s.source)
}
// Len returns length of the underlying buffer
func (s *State) Len() int {
return len(s.source)
}
// Offset returns the current offset
func (s *State) Offset() int {
return s.at
}
// RestLen returns the rest unread length of the underlying buffer
func (s *State) RestLen() int {
return len(s.source) - s.at
}
// RestBytes returns slice of rest bytes from the current position
func (s *State) RestBytes() []byte {
return s.source[s.at:]
}
// BytesTo returns slice of underlying buffer from current position to the given
// State position
func (s *State) BytesTo(to *State) []byte {
return s.BytesToOffset(to.at)
}
// BytesToOffset returns slice of underlying buffer from current position to the
// given position
func (s *State) BytesToOffset(offset int) []byte {
to := fixOffset(offset, len(s.source))
if to < s.at {
panic(errors.Wrapf(ErrNegativeOffset, "from offset %v to backward offset %v", s.at, to))
}
return s.source[s.at:to]
}
// Byte returns a byte from current position
//
// panics at EOF
func (s *State) Byte() byte {
if s.IsEOF() {
panic(io.EOF)
}
return s.source[s.at]
}
// Rune returns a rune from the current position
//
// panics at EOF
func (s *State) Rune() (r rune, n int) {
if s.IsEOF() {
panic(io.EOF)
}
cut := s.cutUpTo(4)
r, n = utf8.DecodeRune(cut)
return
}
// FF returns new State for the same buffer at the position next n bytes to
// current
//
// n can also be negative, but will panic if refers to negative position
func (s *State) FF(n int) *State {
return s.to(s.at + n)
}
// TakeByte returns a byte from the current position and new State with next
// position
//
// A combination of Byte() and FF(1).
func (s *State) TakeByte() (*State, byte) {
b := s.Byte()
return s.FF(1), b
}
// TakeByteFunc returns a byte from the current position and new State with next
// position only if the current byte match the given callback.
// If no match, returns `nil`
func (s *State) TakeByteFunc(valid func(byte) bool) (*State, byte) {
b := s.Byte()
if !valid(b) {
return nil, 0
}
return s.FF(1), b
}
// TakeBytes returns a slice of bytes up to n length from the current position,
// truncated by EOF
func (s *State) TakeBytes(n int) (*State, []byte) {
next := s.FF(n)
return next, s.source[s.at:next.at]
}
// TakeBytesFunc return next State and slice of bytes which are valid by the
// result of the given func.
//
// At EOF the `valid` will not be called.
//
// If none valid bytes found, the result is the input state itself and nil
// slice.
func (s *State) TakeBytesFunc(valid func(byte) bool) (*State, []byte) {
next := s
for !next.IsEOF() && valid(next.Byte()) {
next = next.FF(1)
}
if next.at == s.at {
return s, nil
}
return next, s.source[s.at:next.at]
}
// TakeRune returns a rune from the current position and new State with next
// position
//
// A combination of Rune() and FF(n), where n is size of the rune read.
func (s *State) TakeRune() (*State, rune) {
r, n := s.Rune()
return s.FF(n), r
}
// TakeRunes returns a slice of runes up to n length from the current position,
// truncated by EOF
func (s *State) TakeRunes(n int) (*State, []rune) {
next := s
var rr []rune
var r rune
for i := 0; !next.IsEOF() && i < n; i++ {
next, r = next.TakeRune()
rr = append(rr, r)
}
return next, rr
}
// TakeRunesFunc return next State and slice of runes which are valid by the
// result of the given func.
//
// At EOF the `valid` will not be called.
//
// If none valid runes found, the result is the input state itself and nil
// slice.
func (s *State) TakeRunesFunc(valid func(rune) bool) (*State, []rune) {
ret := s
var rr []rune
var r rune
var n int
for !ret.IsEOF() {
r, n = ret.Rune()
if !valid(r) {
break
}
rr = append(rr, r)
ret = ret.FF(n)
}
return ret, rr
}
// ExpectByte returns next state only if the given slice of bytes will match
// all corresponding bytes in the current position.
// Returns `nil` when no full match.
func (s *State) ExpectByte(b ...byte) *State {
next, ok := s.ExpectByteOk(b...)
if !ok {
return nil
}
return next
}
// ExpectByteOk checks if bytes in the current position will match the given
// slice of bytes.
// The returned `State` is the last State checked.
// The returned `bool` will `true` only if all bytes matched.
func (s *State) ExpectByteOk(b ...byte) (next *State, ok bool) {
next = s
for !next.IsEOF() && len(b) != 0 && next.Byte() == b[0] {
next = next.FF(1)
b = b[1:]
}
ok = len(b) == 0
return
}
// TODO: ExpectRune(r... rune) *State
// TODO: ExpectRuneOk(r... rune) (next *State, ok bool)
// cutUpTo returns a slice of buffer starting from current position and with
// size up to n bytes, truncated by EOF
func (s *State) cutUpTo(n int) []byte {
return s.BytesToOffset(s.at + n)
}
func fixOffset(offset, length int) int {
if offset < 0 {
panic(ErrNegativeOffset)
}
if offset > length {
return length
}
return offset
}
func (s *State) getBefore() string {
from := s.at
rest := stateFormatContext
for from > 0 && rest > 0 {
from--
if utf8.RuneStart(s.source[from]) {
rest--
}
}
return string(s.source[from:s.at])
}
func (s *State) getAfter() string {
to := s.at
L := len(s.source)
started := 0
for to < L {
if utf8.RuneStart(s.source[to]) {
started++
if started > stateFormatContext {
break
}
}
to++
}
return string(s.BytesToOffset(to))
}
func (s *State) String() string {
var str string
if before := ctlCharReplacer.Replace(s.getBefore()); before != "" {
str += "⟪" + before + "⟫"
}
str += "⏵"
if after := ctlCharReplacer.Replace(s.getAfter()); after != "" {
str += "⟪" + after + "⟫"
} else {
str += "<EOF>"
}
return str
}
func (s *State) Format(st fmt.State, verb rune) {
before := ctlCharReplacer.Replace(s.getBefore())
after := ctlCharReplacer.Replace(s.getAfter())
switch verb {
case 'v':
if st.Flag('+') {
if before != "" {
io.WriteString(st, before)
}
if after != "" {
io.WriteString(st, after)
} else {
io.WriteString(st, "<EOF>")
}
io.WriteString(st, "\n")
if b := utf8.RuneCount([]byte(before)); b > 0 {
io.WriteString(st, strings.Repeat("-", b))
}
io.WriteString(st, "^\n")
return
}
fallthrough
case 's', 'q':
if before != "" {
io.WriteString(st, "⟪"+before+"⟫")
}
io.WriteString(st, "⏵")
if after != "" {
io.WriteString(st, "⟪"+after+"⟫")
} else {
io.WriteString(st, "<EOF>")
}
}
}
var ctlCharReplacer = strings.NewReplacer(
"\x00", "␀", "\x01", "␁", "\x02", "␂", "\x03", "␃",
"\x04", "␄", "\x05", "␅", "\x06", "␆", "\x07", "␇",
"\x08", "␈", "\x09", "␉", "\x0A", "␊", "\x0B", "␋",
"\x0C", "␌", "\x0D", "␍", "\x0E", "␎", "\x0F", "␏",
"\x10", "␐", "\x11", "␑", "\x12", "␒", "\x13", "␓",
"\x14", "␔", "\x15", "␕", "\x16", "␖", "\x17", "␗",
"\x18", "␘", "\x19", "␙", "\x1A", "␚", "\x1B", "␛",
"\x1C", "␜", "\x1D", "␝", "\x1E", "␞", "\x1F", "␟",
" ", "␠", "\x7F", "␡",
)