context.go raw
1 package scanner
2
3 import (
4 "sync"
5
6 "github.com/goccy/go-yaml/token"
7 )
8
9 const whitespace = ' '
10
11 // Context context at scanning
12 type Context struct {
13 idx int
14 size int
15 notSpaceCharPos int
16 notSpaceOrgCharPos int
17 src []rune
18 buf []rune
19 obuf []rune
20 tokens token.Tokens
21 isRawFolded bool
22 isLiteral bool
23 isFolded bool
24 isSingleLine bool
25 literalOpt string
26 }
27
28 var (
29 ctxPool = sync.Pool{
30 New: func() interface{} {
31 return createContext()
32 },
33 }
34 )
35
36 func createContext() *Context {
37 return &Context{
38 idx: 0,
39 tokens: token.Tokens{},
40 isSingleLine: true,
41 }
42 }
43
44 func newContext(src []rune) *Context {
45 ctx := ctxPool.Get().(*Context)
46 ctx.reset(src)
47 return ctx
48 }
49
50 func (c *Context) release() {
51 ctxPool.Put(c)
52 }
53
54 func (c *Context) reset(src []rune) {
55 c.idx = 0
56 c.size = len(src)
57 c.src = src
58 c.tokens = c.tokens[:0]
59 c.resetBuffer()
60 c.isRawFolded = false
61 c.isSingleLine = true
62 c.isLiteral = false
63 c.isFolded = false
64 c.literalOpt = ""
65 }
66
67 func (c *Context) resetBuffer() {
68 c.buf = c.buf[:0]
69 c.obuf = c.obuf[:0]
70 c.notSpaceCharPos = 0
71 c.notSpaceOrgCharPos = 0
72 }
73
74 func (c *Context) isSaveIndentMode() bool {
75 return c.isLiteral || c.isFolded || c.isRawFolded
76 }
77
78 func (c *Context) breakLiteral() {
79 c.isLiteral = false
80 c.isRawFolded = false
81 c.isFolded = false
82 c.literalOpt = ""
83 }
84
85 func (c *Context) addToken(tk *token.Token) {
86 if tk == nil {
87 return
88 }
89 c.tokens = append(c.tokens, tk)
90 }
91
92 func (c *Context) addBuf(r rune) {
93 if len(c.buf) == 0 && r == ' ' {
94 return
95 }
96 c.buf = append(c.buf, r)
97 if r != ' ' && r != '\t' {
98 c.notSpaceCharPos = len(c.buf)
99 }
100 }
101
102 func (c *Context) addOriginBuf(r rune) {
103 c.obuf = append(c.obuf, r)
104 if r != ' ' && r != '\t' {
105 c.notSpaceOrgCharPos = len(c.obuf)
106 }
107 }
108
109 func (c *Context) removeRightSpaceFromBuf() int {
110 trimmedBuf := c.obuf[:c.notSpaceOrgCharPos]
111 buflen := len(trimmedBuf)
112 diff := len(c.obuf) - buflen
113 if diff > 0 {
114 c.obuf = c.obuf[:buflen]
115 c.buf = c.bufferedSrc()
116 }
117 return diff
118 }
119
120 func (c *Context) isDocument() bool {
121 return c.isLiteral || c.isFolded || c.isRawFolded
122 }
123
124 func (c *Context) isEOS() bool {
125 return len(c.src)-1 <= c.idx
126 }
127
128 func (c *Context) isNextEOS() bool {
129 return len(c.src)-1 <= c.idx+1
130 }
131
132 func (c *Context) next() bool {
133 return c.idx < c.size
134 }
135
136 func (c *Context) source(s, e int) string {
137 return string(c.src[s:e])
138 }
139
140 func (c *Context) previousChar() rune {
141 if c.idx > 0 {
142 return c.src[c.idx-1]
143 }
144 return rune(0)
145 }
146
147 func (c *Context) currentChar() rune {
148 if c.size > c.idx {
149 return c.src[c.idx]
150 }
151 return rune(0)
152 }
153
154 func (c *Context) currentCharWithSkipWhitespace() rune {
155 idx := c.idx
156 for c.size > idx {
157 ch := c.src[idx]
158 if ch != whitespace {
159 return ch
160 }
161 idx++
162 }
163 return rune(0)
164 }
165
166 func (c *Context) nextChar() rune {
167 if c.size > c.idx+1 {
168 return c.src[c.idx+1]
169 }
170 return rune(0)
171 }
172
173 func (c *Context) repeatNum(r rune) int {
174 cnt := 0
175 for i := c.idx; i < c.size; i++ {
176 if c.src[i] == r {
177 cnt++
178 } else {
179 break
180 }
181 }
182 return cnt
183 }
184
185 func (c *Context) progress(num int) {
186 c.idx += num
187 }
188
189 func (c *Context) nextPos() int {
190 return c.idx + 1
191 }
192
193 func (c *Context) existsBuffer() bool {
194 return len(c.bufferedSrc()) != 0
195 }
196
197 func (c *Context) bufferedSrc() []rune {
198 src := c.buf[:c.notSpaceCharPos]
199 if len(src) > 0 && src[len(src)-1] == '\n' && c.isDocument() && c.literalOpt == "-" {
200 // remove end '\n' character
201 src = src[:len(src)-1]
202 }
203 return src
204 }
205
206 func (c *Context) bufferedToken(pos *token.Position) *token.Token {
207 if c.idx == 0 {
208 return nil
209 }
210 source := c.bufferedSrc()
211 if len(source) == 0 {
212 return nil
213 }
214 var tk *token.Token
215 if c.isDocument() {
216 tk = token.String(string(source), string(c.obuf), pos)
217 } else {
218 tk = token.New(string(source), string(c.obuf), pos)
219 }
220 c.resetBuffer()
221 return tk
222 }
223
224 func (c *Context) lastToken() *token.Token {
225 if len(c.tokens) != 0 {
226 return c.tokens[len(c.tokens)-1]
227 }
228 return nil
229 }
230