-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrawtextchunk.go
executable file
·153 lines (136 loc) · 4.89 KB
/
rawtextchunk.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
package main
import (
"bytes"
"errors"
"fmt"
)
// RawTextChunk denotes raw text without any escape inside. Even meta chars in side it are treated as raw text.
type RawTextChunk struct {
Position int
Value string
}
// GetPosition implements the Chunk interface
func (p *RawTextChunk) GetPosition() int {
return p.Position
}
// SetPosition implements the Chunk interface
func (p *RawTextChunk) SetPosition(pos int) {
p.Position = pos
}
// GetValue implements the Chunk interface
func (p *RawTextChunk) GetValue() string {
return p.Value
}
// String implements the Stringer interface
func (p RawTextChunk) String() string {
return fmt.Sprintf("RawTextChunk{Position:%v,Value:'%v'}", p.Position, p.Value)
}
// RawTextChunkHandle handles raw text chunks, chunks other than RawTextChunk will be stored as PlainTextChunk
// the result list contains RawTextChunk and PlainTextChunk
func RawTextChunkHandle(input string) ([]Chunk, error) {
//the states of the handling
//it is not possible to compare whether a variable equals a closure(a closure variable can only compare to nil),
//so use these states to know the present state
const (
waitEscapeChar int = iota //Escape Char is \
waitRawTextChar //RawTextChar is r
waitFillCharOrLeftBrace //Fille char is #
waitRawTextEnd //RawTextEnd is calculated
)
type stateHandle func(int, rune) (stateHandle, error)
var (
waitEscapeCharStateHandle stateHandle
waitRawTextCharStateHandle stateHandle
waitFillCharOrLeftBraceStateHandle stateHandle
waitRawTextEndStateHandle stateHandle
buf bytes.Buffer
chunks []Chunk
numIgnoreChar int
startPos int
rawTextFillStr string
state int
)
waitEscapeCharStateHandle = func(pos int, arune rune) (stateHandle, error) {
astr := string(arune)
if astr == EscapeChar {
state = waitRawTextChar
return waitRawTextCharStateHandle, nil //state changes
}
buf.WriteRune(arune)
return waitEscapeCharStateHandle, nil //state not changes
}
waitRawTextCharStateHandle = func(pos int, arune rune) (stateHandle, error) {
astr := string(arune)
if astr == RawTextChar {
rawTextFillStr = "" //reset
state = waitFillCharOrLeftBrace
return waitFillCharOrLeftBraceStateHandle, nil //state changes
}
buf.WriteString(EscapeChar) //write the EscapeChar in last position
buf.WriteRune(arune) //write the current Char in this position
state = waitEscapeChar
return waitEscapeCharStateHandle, nil //state changes to original
}
waitFillCharOrLeftBraceStateHandle = func(pos int, arune rune) (stateHandle, error) {
astr := string(arune)
if astr == FillerChar {
rawTextFillStr += FillerChar
return waitFillCharOrLeftBraceStateHandle, nil // state not change
}
if astr == LeftBraceChar {
if buf.Len() > 0 {
chunk := &PlainTextChunk{Position: startPos, Value: buf.String()}
chunks = append(chunks, chunk) //store the previous plain text chunk
}
buf.Reset()
startPos = pos + 1
state = waitRawTextEnd
return waitRawTextEndStateHandle, nil //state changes
}
return nil, errors.New("expect either raw text filler char(~) or LeftBraceChar")
}
waitRawTextEndStateHandle = func(pos int, arune rune) (stateHandle, error) {
rawTextRightDelimiter := RightBraceChar + rawTextFillStr
totalLen := len(input)
delimiterLen := len(rawTextRightDelimiter)
if pos+delimiterLen > totalLen {
return nil, errors.New("come to the end, but does not find the end raw text chunk")
}
if input[pos:pos+delimiterLen] == rawTextRightDelimiter {
if buf.Len() > 0 {
chunk := &RawTextChunk{Position: startPos, Value: buf.String()}
chunks = append(chunks, chunk) //store the Raw text chunk
}
buf.Reset()
startPos = pos + 1
numIgnoreChar = delimiterLen - 1
state = waitEscapeChar
return waitEscapeCharStateHandle, nil //state changes to original
}
buf.WriteRune(arune)
return waitRawTextEndStateHandle, nil //state not change
}
handler := waitEscapeCharStateHandle
state = waitEscapeChar
for i, arune := range input {
if numIgnoreChar > 0 {
numIgnoreChar--
continue
}
newHandler, err := handler(i, arune)
if err != nil {
return chunks, err
}
handler = newHandler
}
//check if the current state is legal
if state != waitEscapeChar {
return nil, fmt.Errorf("illegal state after handling the whole input text state=%d", state)
}
//check if there are remaining content in the buf at last
if buf.Len() > 0 {
chunk := &PlainTextChunk{Position: startPos, Value: buf.String()}
chunks = append(chunks, chunk) //store the previous plain text chunk
}
return chunks, nil
}