context.go raw
1 package parser
2
3 import (
4 "fmt"
5 "strings"
6
7 "github.com/goccy/go-yaml/token"
8 )
9
10 // context context at parsing
11 type context struct {
12 parent *context
13 idx int
14 size int
15 tokens token.Tokens
16 mode Mode
17 path string
18 }
19
20 var pathSpecialChars = []string{
21 "$", "*", ".", "[", "]",
22 }
23
24 func containsPathSpecialChar(path string) bool {
25 for _, char := range pathSpecialChars {
26 if strings.Contains(path, char) {
27 return true
28 }
29 }
30 return false
31 }
32
33 func normalizePath(path string) string {
34 if containsPathSpecialChar(path) {
35 return fmt.Sprintf("'%s'", path)
36 }
37 return path
38 }
39
40 func (c *context) withChild(path string) *context {
41 ctx := c.copy()
42 path = normalizePath(path)
43 ctx.path += fmt.Sprintf(".%s", path)
44 return ctx
45 }
46
47 func (c *context) withIndex(idx uint) *context {
48 ctx := c.copy()
49 ctx.path += fmt.Sprintf("[%d]", idx)
50 return ctx
51 }
52
53 func (c *context) copy() *context {
54 return &context{
55 parent: c,
56 idx: c.idx,
57 size: c.size,
58 tokens: append(token.Tokens{}, c.tokens...),
59 mode: c.mode,
60 path: c.path,
61 }
62 }
63
64 func (c *context) next() bool {
65 return c.idx < c.size
66 }
67
68 func (c *context) previousToken() *token.Token {
69 if c.idx > 0 {
70 return c.tokens[c.idx-1]
71 }
72 return nil
73 }
74
75 func (c *context) insertToken(idx int, tk *token.Token) {
76 if c.parent != nil {
77 c.parent.insertToken(idx, tk)
78 }
79 if c.size < idx {
80 return
81 }
82 if c.size == idx {
83 curToken := c.tokens[c.size-1]
84 tk.Next = curToken
85 curToken.Prev = tk
86
87 c.tokens = append(c.tokens, tk)
88 c.size = len(c.tokens)
89 return
90 }
91
92 curToken := c.tokens[idx]
93 tk.Next = curToken
94 curToken.Prev = tk
95
96 c.tokens = append(c.tokens[:idx+1], c.tokens[idx:]...)
97 c.tokens[idx] = tk
98 c.size = len(c.tokens)
99 }
100
101 func (c *context) currentToken() *token.Token {
102 if c.idx >= c.size {
103 return nil
104 }
105 return c.tokens[c.idx]
106 }
107
108 func (c *context) nextToken() *token.Token {
109 if c.idx+1 >= c.size {
110 return nil
111 }
112 return c.tokens[c.idx+1]
113 }
114
115 func (c *context) afterNextToken() *token.Token {
116 if c.idx+2 >= c.size {
117 return nil
118 }
119 return c.tokens[c.idx+2]
120 }
121
122 func (c *context) nextNotCommentToken() *token.Token {
123 for i := c.idx + 1; i < c.size; i++ {
124 tk := c.tokens[i]
125 if tk.Type == token.CommentType {
126 continue
127 }
128 return tk
129 }
130 return nil
131 }
132
133 func (c *context) afterNextNotCommentToken() *token.Token {
134 notCommentTokenCount := 0
135 for i := c.idx + 1; i < c.size; i++ {
136 tk := c.tokens[i]
137 if tk.Type == token.CommentType {
138 continue
139 }
140 notCommentTokenCount++
141 if notCommentTokenCount == 2 {
142 return tk
143 }
144 }
145 return nil
146 }
147
148 func (c *context) enabledComment() bool {
149 return c.mode&ParseComments != 0
150 }
151
152 func (c *context) isCurrentCommentToken() bool {
153 tk := c.currentToken()
154 if tk == nil {
155 return false
156 }
157 return tk.Type == token.CommentType
158 }
159
160 func (c *context) progressIgnoreComment(num int) {
161 if c.parent != nil {
162 c.parent.progressIgnoreComment(num)
163 }
164 if c.size <= c.idx+num {
165 c.idx = c.size
166 } else {
167 c.idx += num
168 }
169 }
170
171 func (c *context) progress(num int) {
172 if c.isCurrentCommentToken() {
173 return
174 }
175 c.progressIgnoreComment(num)
176 }
177
178 func newContext(tokens token.Tokens, mode Mode) *context {
179 filteredTokens := []*token.Token{}
180 if mode&ParseComments != 0 {
181 filteredTokens = tokens
182 } else {
183 for _, tk := range tokens {
184 if tk.Type == token.CommentType {
185 continue
186 }
187 // keep prev/next reference between tokens containing comments
188 // https://github.com/goccy/go-yaml/issues/254
189 filteredTokens = append(filteredTokens, tk)
190 }
191 }
192 return &context{
193 idx: 0,
194 size: len(filteredTokens),
195 tokens: token.Tokens(filteredTokens),
196 mode: mode,
197 path: "$",
198 }
199 }
200