-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathstate.go
More file actions
110 lines (98 loc) · 2.26 KB
/
state.go
File metadata and controls
110 lines (98 loc) · 2.26 KB
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
// parser state 包参考了 golang 的内置包定义,部分代码模仿或来自 text/scanner ,其中有向
// https://github.com/sanyaade-buildtools/goparsec 学习一部分设计思路
package goparsec
import (
"errors"
"fmt"
"io"
)
type ParseError struct {
Line int
Column int
Pos int
Message string
}
func (err ParseError) Error() string {
return fmt.Sprintf("pos %d line %d column %d:\n%s",
err.Pos, err.Line, err.Column, err.Message)
}
type ParseState interface {
Next(pred func(rune) bool) (r rune, ok bool, err error)
Line() int
Column() int
Pos() int
SeekTo(int)
Trap(message string, args ...interface{}) error
}
type StateInMemory struct {
buffer []rune
newLines []int
line int
column int
pos int
}
func MemoryParseState(data string) ParseState {
buffer := ([]rune)(data)
newLines := []int{}
last := len(buffer) - 1
for idx, r := range buffer {
if r == '\n' {
newLines = append(newLines, idx)
}
}
if buffer[last] != '\n' {
newLines = append(newLines, last)
}
return &StateInMemory{buffer, newLines, 1, 1, 0}
}
func (this *StateInMemory) Next(pred func(rune) bool) (r rune, match bool, err error) {
buffer := (*this).buffer
if (*this).pos < len(buffer) {
ru := buffer[(*this).pos]
if pred(ru) {
(*this).pos++
if ru == '\r' {
(*this).line++
(*this).column = 0
} else {
(*this).column++
}
return ru, true, nil
} else {
return ru, false, nil
}
} else {
return '\000', false, io.EOF
}
}
func (this *StateInMemory) Line() int {
return (*this).line
}
func (this *StateInMemory) Column() int {
return (*this).column
}
func (this *StateInMemory) Pos() int {
return (*this).pos
}
func (this *StateInMemory) SeekTo(pos int) {
end := len((*this).buffer)
if pos < 0 || pos > end {
message := fmt.Sprintf("%d out range [0, %d]", pos, end)
panic(errors.New(message))
}
(*this).pos = pos
top := len((*this).newLines) - 1
for idx, _ := range (*this).newLines {
line := top - idx
start := (*this).newLines[line]
if start < pos {
(*this).line = line + 2
(*this).column = pos - start
return
}
}
}
func (this *StateInMemory) Trap(message string, args ...interface{}) error {
return ParseError{(*this).line, (*this).column, (*this).pos,
fmt.Sprintf(message, args...)}
}