1
0
mirror of https://github.com/danog/liquid.git synced 2024-12-13 16:07:29 +01:00
liquid/chunks/parser.go

69 lines
1.8 KiB
Go
Raw Normal View History

2017-06-26 15:36:52 +02:00
package chunks
2017-06-25 17:23:20 +02:00
import (
"fmt"
)
2017-06-26 18:41:41 +02:00
// Parse creates an AST from a sequence of Chunks.
func Parse(chunks []Chunk) (ASTNode, error) {
2017-06-25 17:23:20 +02:00
type frame struct {
cd *ControlTagDefinition
cn *ASTControlTag
2017-06-26 18:41:41 +02:00
ap *[]ASTNode
2017-06-25 17:23:20 +02:00
}
var (
root = &ASTSeq{}
ap = &root.Children // pointer to current node accumulation slice
ccd *ControlTagDefinition
ccn *ASTControlTag
stack []frame // stack of control structures
)
for _, c := range chunks {
switch c.Type {
2017-06-26 18:41:41 +02:00
case ObjChunkType:
2017-06-25 17:23:20 +02:00
*ap = append(*ap, &ASTObject{chunk: c})
2017-06-26 18:41:41 +02:00
case TextChunkType:
2017-06-25 17:23:20 +02:00
*ap = append(*ap, &ASTText{chunk: c})
2017-06-26 18:41:41 +02:00
case TagChunkType:
2017-06-25 17:23:20 +02:00
if cd, ok := FindControlDefinition(c.Tag); ok {
switch {
2017-06-26 02:11:29 +02:00
case cd.RequiresParent() && !cd.CompatibleParent(ccd):
2017-06-25 17:23:20 +02:00
suffix := ""
if ccd != nil {
suffix = "; immediate parent is " + ccd.Name
}
return nil, fmt.Errorf("%s not inside %s%s", cd.Name, cd.Parent.Name, suffix)
case cd.IsStartTag():
stack = append(stack, frame{cd: ccd, cn: ccn, ap: ap})
ccd, ccn = cd, &ASTControlTag{chunk: c, cd: cd}
*ap = append(*ap, ccn)
ap = &ccn.body
case cd.IsBranchTag:
n := &ASTControlTag{chunk: c, cd: cd}
ccn.branches = append(ccn.branches, n)
ap = &n.body
case cd.IsEndTag:
f := stack[len(stack)-1]
ccd, ccn, ap, stack = f.cd, f.cn, f.ap, stack[:len(stack)-1]
}
} else if len(*ap) > 0 {
switch n := ((*ap)[len(*ap)-1]).(type) {
case *ASTChunks:
n.chunks = append(n.chunks, c)
default:
*ap = append(*ap, &ASTChunks{chunks: []Chunk{c}})
}
} else {
*ap = append(*ap, &ASTChunks{chunks: []Chunk{c}})
}
}
}
if ccd != nil {
return nil, fmt.Errorf("unterminated %s tag", ccd.Name)
}
if len(root.Children) == 1 {
return root.Children[0], nil
}
return root, nil
}