-
Notifications
You must be signed in to change notification settings - Fork 0
/
reader.go
103 lines (88 loc) · 2.26 KB
/
reader.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
package parser
import (
"fmt"
)
const ReaderDone = rune(-1)
// Reader is the input reader.
type Reader struct {
// input is the input.
input []rune
// cursor is the current position in the input.
cursor Cursor
}
// NewReader creates a new reader.
func NewReader(input []rune) (*Reader, error) {
if len(input) == 0 {
return nil, fmt.Errorf("input is empty")
}
return &Reader{
input: input,
cursor: Cursor{
character: input[0],
},
}, nil
}
// Cursor the current cursor.
func (r *Reader) Cursor() Cursor {
return r.cursor
}
// Done returns true if the reader is done.
func (r *Reader) Done() bool {
return len(r.input) <= r.cursor.position
}
// GetInputRange returns the input range from start to end (excl).
func (r *Reader) GetInputRange(start Cursor, end Cursor) []rune {
return r.input[start.position:end.position]
}
// GetLine returns the line from the given cursor starting at the last newline until the end cursor.
func (r *Reader) GetLine(end Cursor) []rune {
position := end.position
if position == len(r.input) {
// Return the last line.
return r.input[end.lastNewline:]
}
for character := r.input[position]; position < len(r.input) && character != '\n' && character != '\r'; character = r.input[position] {
// Reached the end of the input.
if position++; position == len(r.input) {
return r.input[end.lastNewline:]
}
}
return r.input[end.lastNewline:position]
}
// Jump to the given cursor. Ignore if cursor is nil or the reader is done.
func (r *Reader) Jump(marker Cursor) {
r.cursor = marker
}
// Next reads the next rune.
func (r *Reader) Next() *Reader {
r.cursor.position++
if r.Done() {
r.cursor.column++
r.cursor.character = ReaderDone
return r
}
// Check whether the previous character was a newline.
r.cursor.column++
if r.cursor.character == '\n' {
// Covers both \n and \r\n.
r.cursor.line++
r.cursor.column = 0
r.cursor.lastNewline = r.cursor.position
}
next := r.input[r.cursor.position]
if next != '\n' && r.Cursor().character == '\r' {
// Covers \r.
r.cursor.line++
r.cursor.column = 0
r.cursor.lastNewline = r.cursor.position
}
r.cursor.character = next
return r
}
// Rune returns the current rune.
func (r *Reader) Rune() rune {
if r.Done() {
return ReaderDone
}
return r.cursor.character
}