Skip to content

Commit

Permalink
Support getting YAMLPath from ast.Node
Browse files Browse the repository at this point in the history
  • Loading branch information
goccy committed Sep 7, 2021
1 parent cb71dc9 commit afed407
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 10 deletions.
49 changes: 49 additions & 0 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ type Node interface {
SetComment(*CommentGroupNode) error
// Comment returns comment token instance
GetComment() *CommentGroupNode
// GetPath returns YAMLPath for the current node
GetPath() string
// SetPath set YAMLPath for the current node
SetPath(string)
// MarshalYAML
MarshalYAML() ([]byte, error)
// already read length
Expand All @@ -198,6 +202,7 @@ type ScalarNode interface {
}

type BaseNode struct {
Path string
Comment *CommentGroupNode
read int
}
Expand All @@ -218,6 +223,22 @@ func (n *BaseNode) addReadLen(len int) {
n.read += len
}

// GetPath returns YAMLPath for the current node.
func (n *BaseNode) GetPath() string {
if n == nil {
return ""
}
return n.Path
}

// SetPath set YAMLPath for the current node.
func (n *BaseNode) SetPath(path string) {
if n == nil {
return
}
n.Path = path
}

// GetComment returns comment token instance
func (n *BaseNode) GetComment() *CommentGroupNode {
return n.Comment
Expand Down Expand Up @@ -1916,42 +1937,70 @@ func Walk(v Visitor, node Node) {
switch n := node.(type) {
case *CommentNode:
case *NullNode:
walkComment(v, n.BaseNode)
case *IntegerNode:
walkComment(v, n.BaseNode)
case *FloatNode:
walkComment(v, n.BaseNode)
case *StringNode:
walkComment(v, n.BaseNode)
case *MergeKeyNode:
walkComment(v, n.BaseNode)
case *BoolNode:
walkComment(v, n.BaseNode)
case *InfinityNode:
walkComment(v, n.BaseNode)
case *NanNode:
walkComment(v, n.BaseNode)
case *LiteralNode:
walkComment(v, n.BaseNode)
Walk(v, n.Value)
case *DirectiveNode:
walkComment(v, n.BaseNode)
Walk(v, n.Value)
case *TagNode:
walkComment(v, n.BaseNode)
Walk(v, n.Value)
case *DocumentNode:
walkComment(v, n.BaseNode)
Walk(v, n.Body)
case *MappingNode:
walkComment(v, n.BaseNode)
for _, value := range n.Values {
Walk(v, value)
}
case *MappingKeyNode:
walkComment(v, n.BaseNode)
Walk(v, n.Value)
case *MappingValueNode:
walkComment(v, n.BaseNode)
Walk(v, n.Key)
Walk(v, n.Value)
case *SequenceNode:
walkComment(v, n.BaseNode)
for _, value := range n.Values {
Walk(v, value)
}
case *AnchorNode:
walkComment(v, n.BaseNode)
Walk(v, n.Name)
Walk(v, n.Value)
case *AliasNode:
walkComment(v, n.BaseNode)
Walk(v, n.Value)
}
}

func walkComment(v Visitor, base *BaseNode) {
if base == nil {
return
}
if base.Comment == nil {
return
}
Walk(v, base.Comment)
}

type filterWalker struct {
typ NodeType
results []Node
Expand Down
38 changes: 37 additions & 1 deletion parser/context.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,42 @@
package parser

import "github.com/goccy/go-yaml/token"
import (
"fmt"

"github.com/goccy/go-yaml/token"
)

// context context at parsing
type context struct {
parent *context
idx int
size int
tokens token.Tokens
mode Mode
path string
}

func (c *context) withChild(path string) *context {
ctx := c.copy()
ctx.path += fmt.Sprintf(".%s", path)
return ctx
}

func (c *context) withIndex(idx uint) *context {
ctx := c.copy()
ctx.path += fmt.Sprintf("[%d]", idx)
return ctx
}

func (c *context) copy() *context {
return &context{
parent: c,
idx: c.idx,
size: c.size,
tokens: append(token.Tokens{}, c.tokens...),
mode: c.mode,
path: c.path,
}
}

func (c *context) next() bool {
Expand All @@ -22,6 +51,9 @@ func (c *context) previousToken() *token.Token {
}

func (c *context) insertToken(idx int, tk *token.Token) {
if c.parent != nil {
c.parent.insertToken(idx, tk)
}
if c.size < idx {
return
}
Expand Down Expand Up @@ -104,6 +136,9 @@ func (c *context) isCurrentCommentToken() bool {
}

func (c *context) progressIgnoreComment(num int) {
if c.parent != nil {
c.parent.progressIgnoreComment(num)
}
if c.size <= c.idx+num {
c.idx = c.size
} else {
Expand Down Expand Up @@ -135,5 +170,6 @@ func newContext(tokens token.Tokens, mode Mode) *context {
size: len(filteredTokens),
tokens: filteredTokens,
mode: mode,
path: "$",
}
}
60 changes: 51 additions & 9 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import (
type parser struct{}

func (p *parser) parseMapping(ctx *context) (ast.Node, error) {
node := ast.Mapping(ctx.currentToken(), true)
mapTk := ctx.currentToken()
node := ast.Mapping(mapTk, true)
node.SetPath(ctx.path)
ctx.progress(1) // skip MappingStart token
for ctx.next() {
tk := ctx.currentToken()
Expand Down Expand Up @@ -43,6 +45,7 @@ func (p *parser) parseMapping(ctx *context) (ast.Node, error) {

func (p *parser) parseSequence(ctx *context) (ast.Node, error) {
node := ast.Sequence(ctx.currentToken(), true)
node.SetPath(ctx.path)
ctx.progress(1) // skip SequenceStart token
for ctx.next() {
tk := ctx.currentToken()
Expand All @@ -54,7 +57,7 @@ func (p *parser) parseSequence(ctx *context) (ast.Node, error) {
continue
}

value, err := p.parseToken(ctx, tk)
value, err := p.parseToken(ctx.withIndex(uint(len(node.Values))), tk)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse sequence value in flow sequence node")
}
Expand All @@ -67,6 +70,7 @@ func (p *parser) parseSequence(ctx *context) (ast.Node, error) {
func (p *parser) parseTag(ctx *context) (ast.Node, error) {
tagToken := ctx.currentToken()
node := ast.Tag(tagToken)
node.SetPath(ctx.path)
ctx.progress(1) // skip tag token
var (
value ast.Node
Expand Down Expand Up @@ -135,6 +139,17 @@ func (p *parser) createNullToken(base *token.Token) *token.Token {
}

func (p *parser) parseMapValue(ctx *context, key ast.Node, colonToken *token.Token) (ast.Node, error) {
node, err := p.createMapValueNode(ctx, key, colonToken)
if err != nil {
return nil, errors.Wrapf(err, "failed to create map value node")
}
if node != nil && node.GetPath() != "" {
node.SetPath(ctx.path)
}
return node, nil
}

func (p *parser) createMapValueNode(ctx *context, key ast.Node, colonToken *token.Token) (ast.Node, error) {
tk := ctx.currentToken()
if tk == nil {
nullToken := p.createNullToken(colonToken)
Expand Down Expand Up @@ -190,6 +205,9 @@ func (p *parser) parseMappingValue(ctx *context) (ast.Node, error) {
if err != nil {
return nil, errors.Wrapf(err, "failed to parse map key")
}
if key != nil {
key.SetPath(ctx.path)
}
if err := p.validateMapKey(key.GetToken()); err != nil {
return nil, errors.Wrapf(err, "validate mapping key error")
}
Expand All @@ -199,7 +217,8 @@ func (p *parser) parseMappingValue(ctx *context) (ast.Node, error) {
return nil, errors.ErrSyntax("unexpected map", key.GetToken())
}
ctx.progress(1) // progress to value token
if err := p.setSameLineCommentIfExists(ctx, key); err != nil {
keyText := key.GetToken().Value
if err := p.setSameLineCommentIfExists(ctx.withChild(keyText), key); err != nil {
return nil, errors.Wrapf(err, "failed to set same line comment to node")
}
if key.GetComment() != nil {
Expand All @@ -208,7 +227,7 @@ func (p *parser) parseMappingValue(ctx *context) (ast.Node, error) {
ctx.progressIgnoreComment(1)
}

value, err := p.parseMapValue(ctx, key, tk)
value, err := p.parseMapValue(ctx.withChild(keyText), key, tk)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse map value")
}
Expand All @@ -217,7 +236,9 @@ func (p *parser) parseMappingValue(ctx *context) (ast.Node, error) {
}

mvnode := ast.MappingValue(tk, key, value)
mvnode.SetPath(ctx.path)
node := ast.Mapping(tk, false, mvnode)
node.SetPath(ctx.path)

ntk := ctx.nextNotCommentToken()
antk := ctx.afterNextNotCommentToken()
Expand Down Expand Up @@ -257,6 +278,7 @@ func (p *parser) parseMappingValue(ctx *context) (ast.Node, error) {
func (p *parser) parseSequenceEntry(ctx *context) (ast.Node, error) {
tk := ctx.currentToken()
sequenceNode := ast.Sequence(tk, false)
sequenceNode.SetPath(ctx.path)
curColumn := tk.Position.Column
for tk.Type == token.SequenceEntryType {
ctx.progress(1) // skip sequence token
Expand All @@ -270,7 +292,7 @@ func (p *parser) parseSequenceEntry(ctx *context) (ast.Node, error) {
}
ctx.progress(1) // skip sequence token
}
value, err := p.parseToken(ctx, ctx.currentToken())
value, err := p.parseToken(ctx.withIndex(uint(len(sequenceNode.Values))), ctx.currentToken())
if err != nil {
return nil, errors.Wrapf(err, "failed to parse sequence")
}
Expand Down Expand Up @@ -298,6 +320,7 @@ func (p *parser) parseSequenceEntry(ctx *context) (ast.Node, error) {
func (p *parser) parseAnchor(ctx *context) (ast.Node, error) {
tk := ctx.currentToken()
anchor := ast.Anchor(tk)
anchor.SetPath(ctx.path)
ntk := ctx.nextToken()
if ntk == nil {
return nil, errors.ErrSyntax("unexpected anchor. anchor name is undefined", tk)
Expand All @@ -324,6 +347,7 @@ func (p *parser) parseAnchor(ctx *context) (ast.Node, error) {
func (p *parser) parseAlias(ctx *context) (ast.Node, error) {
tk := ctx.currentToken()
alias := ast.Alias(tk)
alias.SetPath(ctx.path)
ntk := ctx.nextToken()
if ntk == nil {
return nil, errors.ErrSyntax("unexpected alias. alias name is undefined", tk)
Expand Down Expand Up @@ -366,6 +390,7 @@ func (p *parser) parseScalarValueWithComment(ctx *context, tk *token.Token) (ast
if node == nil {
return nil, nil
}
node.SetPath(ctx.path)
if p.isSameLineComment(ctx.nextToken(), node) {
ctx.progress(1)
if err := p.setSameLineCommentIfExists(ctx, node); err != nil {
Expand Down Expand Up @@ -460,7 +485,9 @@ func (p *parser) setSameLineCommentIfExists(ctx *context, node ast.Node) error {
if !p.isSameLineComment(tk, node) {
return nil
}
if err := node.SetComment(ast.CommentGroup([]*token.Token{tk})); err != nil {
comment := ast.CommentGroup([]*token.Token{tk})
comment.SetPath(ctx.path)
if err := node.SetComment(comment); err != nil {
return errors.Wrapf(err, "failed to set comment token to ast.Node")
}
return nil
Expand Down Expand Up @@ -494,7 +521,9 @@ func (p *parser) parseCommentOnly(ctx *context) *ast.CommentGroupNode {
commentTokens = append(commentTokens, tk)
ctx.progressIgnoreComment(1) // skip comment token
}
return ast.CommentGroup(commentTokens)
group := ast.CommentGroup(commentTokens)
group.SetPath(ctx.path)
return group
}

func (p *parser) parseComment(ctx *context) (ast.Node, error) {
Expand All @@ -513,9 +542,11 @@ func (p *parser) parseComment(ctx *context) (ast.Node, error) {
}

func (p *parser) parseMappingKey(ctx *context) (ast.Node, error) {
node := ast.MappingKey(ctx.currentToken())
keyTk := ctx.currentToken()
node := ast.MappingKey(keyTk)
node.SetPath(ctx.path)
ctx.progress(1) // skip mapping key token
value, err := p.parseToken(ctx, ctx.currentToken())
value, err := p.parseToken(ctx.withChild(keyTk.Value), ctx.currentToken())
if err != nil {
return nil, errors.Wrapf(err, "failed to parse map key")
}
Expand All @@ -524,6 +555,17 @@ func (p *parser) parseMappingKey(ctx *context) (ast.Node, error) {
}

func (p *parser) parseToken(ctx *context, tk *token.Token) (ast.Node, error) {
node, err := p.createNodeFromToken(ctx, tk)
if err != nil {
return nil, errors.Wrapf(err, "failed to create node from token")
}
if node != nil {
node.SetPath(ctx.path)
}
return node, nil
}

func (p *parser) createNodeFromToken(ctx *context, tk *token.Token) (ast.Node, error) {
if tk == nil {
return nil, nil
}
Expand Down
Loading

0 comments on commit afed407

Please sign in to comment.