Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix parsing of directive value #578

Merged
merged 4 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 30 additions & 8 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -1726,8 +1726,12 @@ func (n *AliasNode) MarshalYAML() ([]byte, error) {
// DirectiveNode type of directive node
type DirectiveNode struct {
*BaseNode
// Start is '%' token.
Start *token.Token
Value Node
// Name is directive name e.g.) "YAML" or "TAG".
Name Node
// Values is directive values e.g.) "1.2" or "!!" and "tag:clarkevans.com,2002:app/".
Values []Node
}

// Read implements (io.Reader).Read
Expand All @@ -1745,14 +1749,21 @@ func (n *DirectiveNode) GetToken() *token.Token {

// AddColumn add column number to child nodes recursively
func (n *DirectiveNode) AddColumn(col int) {
if n.Value != nil {
n.Value.AddColumn(col)
if n.Name != nil {
n.Name.AddColumn(col)
}
for _, value := range n.Values {
value.AddColumn(col)
}
}

// String directive to text
func (n *DirectiveNode) String() string {
return fmt.Sprintf("%s%s", n.Start.Value, n.Value.String())
values := make([]string, 0, len(n.Values))
for _, val := range n.Values {
values = append(values, val.String())
}
return strings.Join(append([]string{"%" + n.Name.String()}, values...), " ")
}

// MarshalYAML encodes to a YAML text
Expand All @@ -1763,8 +1774,9 @@ func (n *DirectiveNode) MarshalYAML() ([]byte, error) {
// TagNode type of tag node
type TagNode struct {
*BaseNode
Start *token.Token
Value Node
Directive *DirectiveNode
Start *token.Token
Value Node
}

func (n *TagNode) GetValue() any {
Expand Down Expand Up @@ -1940,7 +1952,10 @@ func Walk(v Visitor, node Node) {
Walk(v, n.Value)
case *DirectiveNode:
walkComment(v, n.BaseNode)
Walk(v, n.Value)
Walk(v, n.Name)
for _, value := range n.Values {
Walk(v, value)
}
case *TagNode:
walkComment(v, n.BaseNode)
Walk(v, n.Value)
Expand Down Expand Up @@ -2026,7 +2041,14 @@ func (f *parentFinder) walk(parent, node Node) Node {
case *LiteralNode:
return f.walk(node, n.Value)
case *DirectiveNode:
return f.walk(node, n.Value)
if found := f.walk(node, n.Name); found != nil {
return found
}
for _, value := range n.Values {
if found := f.walk(node, value); found != nil {
return found
}
}
case *TagNode:
return f.walk(node, n.Value)
case *DocumentNode:
Expand Down
10 changes: 10 additions & 0 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,16 @@ func (d *Decoder) nodeToValue(node ast.Node) (any, error) {
case *ast.NanNode:
return n.GetValue(), nil
case *ast.TagNode:
if n.Directive != nil {
v, err := d.nodeToValue(n.Value)
if err != nil {
return nil, err
}
if v == nil {
return "", nil
}
return fmt.Sprint(v), nil
}
switch token.ReservedTagKeyword(n.Start.Value) {
case token.TimestampTag:
t, _ := d.castToTime(n.Value)
Expand Down
131 changes: 119 additions & 12 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,28 @@ func ParseFile(filename string, mode Mode, opts ...Option) (*ast.File, error) {
return f, nil
}

type YAMLVersion string

const (
YAML10 YAMLVersion = "1.0"
YAML11 YAMLVersion = "1.1"
YAML12 YAMLVersion = "1.2"
YAML13 YAMLVersion = "1.3"
)

var yamlVersionMap = map[string]YAMLVersion{
"1.0": YAML10,
"1.1": YAML11,
"1.2": YAML12,
"1.3": YAML13,
}

type parser struct {
tokens []*Token
pathMap map[string]ast.Node
allowDuplicateMapKey bool
tokens []*Token
pathMap map[string]ast.Node
yamlVersion YAMLVersion
allowDuplicateMapKey bool
secondaryTagDirective *ast.DirectiveNode
}

func newParser(tokens token.Tokens, mode Mode, opts []Option) (*parser, error) {
Expand Down Expand Up @@ -122,6 +140,10 @@ func (p *parser) parseDocument(ctx *context, docGroup *TokenGroup) (*ast.Documen
if docGroup.Last().Type() == token.DocumentEndType {
end = docGroup.Last().RawToken()
tokens = tokens[:len(tokens)-1]
defer func() {
// clear yaml version value if DocumentEnd token (...) is specified.
p.yamlVersion = ""
}()
}

if len(tokens) == 0 {
Expand Down Expand Up @@ -162,6 +184,13 @@ func (p *parser) parseToken(ctx *context, tk *Token) (ast.Node, error) {
}
ctx.goNext()
return node, nil
case TokenGroupDirectiveName:
node, err := p.parseDirectiveName(ctx.withGroup(tk.Group))
if err != nil {
return nil, err
}
ctx.goNext()
return node, nil
case TokenGroupAnchor:
node, err := p.parseAnchor(ctx.withGroup(tk.Group), tk.Group)
if err != nil {
Expand Down Expand Up @@ -838,14 +867,26 @@ func (p *parser) parseTag(ctx *context) (*ast.TagNode, error) {
ctx.goNext()

comment := p.parseHeadComment(ctx)
value, err := p.parseTagValue(ctx, tagRawTk, ctx.currentToken())
if err != nil {
return nil, err

var tagValue ast.Node
if p.secondaryTagDirective != nil {
value, err := newStringNode(ctx, ctx.currentToken())
if err != nil {
return nil, err
}
tagValue = value
node.Directive = p.secondaryTagDirective
} else {
value, err := p.parseTagValue(ctx, tagRawTk, ctx.currentToken())
if err != nil {
return nil, err
}
tagValue = value
}
if err := setHeadComment(comment, value); err != nil {
if err := setHeadComment(comment, tagValue); err != nil {
return nil, err
}
node.Value = value
node.Value = tagValue
return node, nil
}

Expand Down Expand Up @@ -1046,16 +1087,82 @@ func (p *parser) parseSequenceValue(ctx *context, seqTk *Token) (ast.Node, error
}

func (p *parser) parseDirective(ctx *context, g *TokenGroup) (*ast.DirectiveNode, error) {
node, err := newDirectiveNode(ctx, g.First())
directiveNameGroup := g.First().Group
directive, err := p.parseDirectiveName(ctx.withGroup(directiveNameGroup))
if err != nil {
return nil, err
}
value, err := p.parseToken(ctx, g.Last())

switch directive.Name.String() {
case "YAML":
if len(g.Tokens) != 2 {
return nil, errors.ErrSyntax("unexpected format YAML directive", g.First().RawToken())
}
valueTk := g.Tokens[1]
valueRawTk := valueTk.RawToken()
value := valueRawTk.Value
ver, exists := yamlVersionMap[value]
if !exists {
return nil, errors.ErrSyntax(fmt.Sprintf("unknown YAML version %q", value), valueRawTk)
}
if p.yamlVersion != "" {
return nil, errors.ErrSyntax("YAML version has already been specified", valueRawTk)
}
p.yamlVersion = ver
versionNode, err := newStringNode(ctx, valueTk)
if err != nil {
return nil, err
}
directive.Values = append(directive.Values, versionNode)
case "TAG":
if len(g.Tokens) != 3 {
return nil, errors.ErrSyntax("unexpected format TAG directive", g.First().RawToken())
}
tagKey, err := newStringNode(ctx, g.Tokens[1])
if err != nil {
return nil, err
}
if tagKey.Value == "!!" {
p.secondaryTagDirective = directive
}
tagValue, err := newStringNode(ctx, g.Tokens[2])
if err != nil {
return nil, err
}
directive.Values = append(directive.Values, tagKey, tagValue)
default:
if len(g.Tokens) > 1 {
for _, tk := range g.Tokens[1:] {
value, err := newStringNode(ctx, tk)
if err != nil {
return nil, err
}
directive.Values = append(directive.Values, value)
}
}
}
return directive, nil
}

func (p *parser) parseDirectiveName(ctx *context) (*ast.DirectiveNode, error) {
directive, err := newDirectiveNode(ctx, ctx.currentToken())
if err != nil {
return nil, err
}
node.Value = value
return node, nil
ctx.goNext()
if ctx.isTokenNotFound() {
return nil, errors.ErrSyntax("could not find directive value", directive.GetToken())
}

directiveName, err := p.parseScalarValue(ctx, ctx.currentToken())
if err != nil {
return nil, err
}
if directiveName == nil {
return nil, errors.ErrSyntax("unexpected directive. directive name is not scalar value", ctx.currentToken().RawToken())
}
directive.Name = directiveName
return directive, nil
}

func (p *parser) parseComment(ctx *context) (ast.Node, error) {
Expand Down
Loading
Loading