go-yaml/parser/context.go
2025-05-08 23:26:21 +09:00

187 lines
3.5 KiB
Go

package parser
import (
"fmt"
"strings"
"github.com/goccy/go-yaml/token"
)
// context context at parsing
type context struct {
tokenRef *tokenRef
path string
isFlow bool
}
type tokenRef struct {
tokens []*Token
size int
idx int
}
var pathSpecialChars = []string{
"$", "*", ".", "[", "]",
}
func containsPathSpecialChar(path string) bool {
for _, char := range pathSpecialChars {
if strings.Contains(path, char) {
return true
}
}
return false
}
func normalizePath(path string) string {
if containsPathSpecialChar(path) {
return fmt.Sprintf("'%s'", path)
}
return path
}
func (c *context) currentToken() *Token {
if c.tokenRef.idx >= c.tokenRef.size {
return nil
}
return c.tokenRef.tokens[c.tokenRef.idx]
}
func (c *context) isComment() bool {
return c.currentToken().Type() == token.CommentType
}
func (c *context) nextToken() *Token {
if c.tokenRef.idx+1 >= c.tokenRef.size {
return nil
}
return c.tokenRef.tokens[c.tokenRef.idx+1]
}
func (c *context) nextNotCommentToken() *Token {
for i := c.tokenRef.idx + 1; i < c.tokenRef.size; i++ {
tk := c.tokenRef.tokens[i]
if tk.Type() == token.CommentType {
continue
}
return tk
}
return nil
}
func (c *context) isTokenNotFound() bool {
return c.currentToken() == nil
}
func (c *context) withGroup(g *TokenGroup) *context {
ctx := *c
ctx.tokenRef = &tokenRef{
tokens: g.Tokens,
size: len(g.Tokens),
}
return &ctx
}
func (c *context) withChild(path string) *context {
ctx := *c
ctx.path = c.path + "." + normalizePath(path)
return &ctx
}
func (c *context) withIndex(idx uint) *context {
ctx := *c
ctx.path = c.path + "[" + fmt.Sprint(idx) + "]"
return &ctx
}
func (c *context) withFlow(isFlow bool) *context {
ctx := *c
ctx.isFlow = isFlow
return &ctx
}
func newContext() *context {
return &context{
path: "$",
}
}
func (c *context) goNext() {
ref := c.tokenRef
if ref.size <= ref.idx+1 {
ref.idx = ref.size
} else {
ref.idx++
}
}
func (c *context) next() bool {
return c.tokenRef.idx < c.tokenRef.size
}
func (c *context) insertNullToken(tk *Token) *Token {
nullToken := c.createImplicitNullToken(tk)
c.insertToken(nullToken)
c.goNext()
return nullToken
}
func (c *context) addNullValueToken(tk *Token) *Token {
nullToken := c.createImplicitNullToken(tk)
rawTk := nullToken.RawToken()
// add space for map or sequence value.
rawTk.Position.Column++
c.addToken(nullToken)
c.goNext()
return nullToken
}
func (c *context) createImplicitNullToken(base *Token) *Token {
pos := *(base.RawToken().Position)
pos.Column++
tk := token.New("null", " null", &pos)
tk.Type = token.ImplicitNullType
return &Token{Token: tk}
}
func (c *context) insertToken(tk *Token) {
ref := c.tokenRef
idx := ref.idx
if ref.size < idx {
return
}
if ref.size == idx {
curToken := ref.tokens[ref.size-1]
tk.RawToken().Next = curToken.RawToken()
curToken.RawToken().Prev = tk.RawToken()
ref.tokens = append(ref.tokens, tk)
ref.size = len(ref.tokens)
return
}
curToken := ref.tokens[idx]
tk.RawToken().Next = curToken.RawToken()
curToken.RawToken().Prev = tk.RawToken()
ref.tokens = append(ref.tokens[:idx+1], ref.tokens[idx:]...)
ref.tokens[idx] = tk
ref.size = len(ref.tokens)
}
func (c *context) addToken(tk *Token) {
ref := c.tokenRef
lastTk := ref.tokens[ref.size-1]
if lastTk.Group != nil {
lastTk = lastTk.Group.Last()
}
lastTk.RawToken().Next = tk.RawToken()
tk.RawToken().Prev = lastTk.RawToken()
ref.tokens = append(ref.tokens, tk)
ref.size = len(ref.tokens)
}