mirror of
https://github.com/danog/liquid.git
synced 2024-11-27 03:24:39 +01:00
Un-export ControlTagDefinition; create builder
This commit is contained in:
parent
4189f03261
commit
0c7a8d2569
@ -37,7 +37,7 @@ type ASTObject struct {
|
||||
// ASTControlTag is a control tag.
|
||||
type ASTControlTag struct {
|
||||
Chunk
|
||||
cd *ControlTagDefinition
|
||||
cd *controlTagDefinition
|
||||
Body []ASTNode
|
||||
Branches []*ASTControlTag
|
||||
}
|
||||
|
@ -5,80 +5,81 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// ControlTagDefinitions is a map of tag names to control tag definitions.
|
||||
var ControlTagDefinitions = map[string]*ControlTagDefinition{}
|
||||
// controlTagDefinitions is a map of tag names to control tag definitions.
|
||||
var controlTagDefinitions = map[string]*controlTagDefinition{}
|
||||
|
||||
// ControlTagAction runs the interpreter.
|
||||
type ControlTagAction func(ASTControlTag) func(io.Writer, Context) error
|
||||
|
||||
// ControlTagDefinition tells the parser how to parse control tags.
|
||||
type ControlTagDefinition struct {
|
||||
Name string
|
||||
IsBranchTag bool
|
||||
IsEndTag bool
|
||||
SyntaxModel *ControlTagDefinition
|
||||
Parent *ControlTagDefinition
|
||||
// controlTagDefinition tells the parser how to parse control tags.
|
||||
type controlTagDefinition struct {
|
||||
name string
|
||||
isBranchTag, isEndTag bool
|
||||
syntaxModel *controlTagDefinition
|
||||
parent *controlTagDefinition
|
||||
action ControlTagAction
|
||||
}
|
||||
|
||||
func (c *ControlTagDefinition) CompatibleParent(p *ControlTagDefinition) bool {
|
||||
func (c *controlTagDefinition) compatibleParent(p *controlTagDefinition) bool {
|
||||
if p == nil {
|
||||
return false
|
||||
}
|
||||
if p.SyntaxModel != nil {
|
||||
p = p.SyntaxModel
|
||||
if p.syntaxModel != nil {
|
||||
p = p.syntaxModel
|
||||
}
|
||||
return c.Parent == p
|
||||
return c.parent == p
|
||||
}
|
||||
|
||||
func (c *ControlTagDefinition) RequiresParent() bool {
|
||||
return c.IsBranchTag || c.IsEndTag
|
||||
func (c *controlTagDefinition) requiresParent() bool {
|
||||
return c.isBranchTag || c.isEndTag
|
||||
}
|
||||
|
||||
func (c *ControlTagDefinition) IsStartTag() bool {
|
||||
return !c.IsBranchTag && !c.IsEndTag
|
||||
func (c *controlTagDefinition) isStartTag() bool {
|
||||
return !c.isBranchTag && !c.isEndTag
|
||||
}
|
||||
|
||||
// DefineControlTag defines a control tag and its matching end tag.
|
||||
func DefineControlTag(name string) *ControlTagDefinition {
|
||||
ct := &ControlTagDefinition{Name: name}
|
||||
addControlTagDefinition(ct)
|
||||
addControlTagDefinition(&ControlTagDefinition{Name: "end" + name, IsEndTag: true, Parent: ct})
|
||||
return ct
|
||||
func addControlTagDefinition(ct *controlTagDefinition) {
|
||||
controlTagDefinitions[ct.name] = ct
|
||||
}
|
||||
|
||||
func FindControlDefinition(name string) (*ControlTagDefinition, bool) {
|
||||
ct, found := ControlTagDefinitions[name]
|
||||
func findControlTagDefinition(name string) (*controlTagDefinition, bool) {
|
||||
ct, found := controlTagDefinitions[name]
|
||||
return ct, found
|
||||
}
|
||||
|
||||
func addControlTagDefinition(ct *ControlTagDefinition) {
|
||||
ControlTagDefinitions[ct.Name] = ct
|
||||
type tagBuilder struct {tag *controlTagDefinition}
|
||||
|
||||
// DefineControlTag defines a control tag and its matching end tag.
|
||||
func DefineControlTag(name string) tagBuilder {
|
||||
ct := &controlTagDefinition{name: name}
|
||||
addControlTagDefinition(ct)
|
||||
addControlTagDefinition(&controlTagDefinition{name: "end" + name, isEndTag: true, parent: ct})
|
||||
return tagBuilder{ct}
|
||||
}
|
||||
|
||||
// Branch tells the parser that the named tag can appear immediately between this tag and its end tag,
|
||||
// so long as it is not nested within any other control tags.
|
||||
func (ct *ControlTagDefinition) Branch(name string) *ControlTagDefinition {
|
||||
addControlTagDefinition(&ControlTagDefinition{Name: name, IsBranchTag: true, Parent: ct})
|
||||
return ct
|
||||
func (b tagBuilder) Branch(name string) tagBuilder {
|
||||
addControlTagDefinition(&controlTagDefinition{name: name, isBranchTag: true, parent: b.tag})
|
||||
return b
|
||||
}
|
||||
|
||||
// Governs tells the parser that the tags can appear anywhere between this tag and its end tag.
|
||||
func (ct *ControlTagDefinition) Governs(_ []string) *ControlTagDefinition {
|
||||
return ct
|
||||
func (b tagBuilder) Governs(_ []string) tagBuilder {
|
||||
return b
|
||||
}
|
||||
|
||||
// SameSyntaxAs tells the parser that this tag has the same syntax as the named tag.
|
||||
func (ct *ControlTagDefinition) SameSyntaxAs(name string) *ControlTagDefinition {
|
||||
ot := ControlTagDefinitions[name]
|
||||
func (b tagBuilder) SameSyntaxAs(name string) tagBuilder {
|
||||
ot := controlTagDefinitions[name]
|
||||
if ot == nil {
|
||||
panic(fmt.Errorf("undefined: %s", name))
|
||||
}
|
||||
ct.SyntaxModel = ot
|
||||
return ct
|
||||
b.tag.syntaxModel = ot
|
||||
return b
|
||||
}
|
||||
|
||||
// Action sets the action for a control tag definition.
|
||||
func (ct *ControlTagDefinition) Action(fn ControlTagAction) {
|
||||
ct.action = fn
|
||||
func (b tagBuilder) Action(fn ControlTagAction) {
|
||||
b.tag.action = fn
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ func (n ASTChunks) MarshalYAML() (interface{}, error) {
|
||||
// MarshalYAML marshalls a chunk for debugging.
|
||||
func (n ASTControlTag) MarshalYAML() (interface{}, error) {
|
||||
return map[string]map[string]interface{}{
|
||||
n.cd.Name: {
|
||||
n.cd.name: {
|
||||
"args": n.Args,
|
||||
"body": n.Body,
|
||||
"branches": n.Branches,
|
||||
|
@ -7,14 +7,14 @@ import (
|
||||
// Parse creates an AST from a sequence of Chunks.
|
||||
func Parse(chunks []Chunk) (ASTNode, error) {
|
||||
type frame struct {
|
||||
cd *ControlTagDefinition
|
||||
cd *controlTagDefinition
|
||||
cn *ASTControlTag
|
||||
ap *[]ASTNode
|
||||
}
|
||||
var (
|
||||
root = &ASTSeq{}
|
||||
ap = &root.Children // pointer to current node accumulation slice
|
||||
ccd *ControlTagDefinition
|
||||
ccd *controlTagDefinition
|
||||
ccn *ASTControlTag
|
||||
stack []frame // stack of control structures
|
||||
)
|
||||
@ -25,24 +25,24 @@ func Parse(chunks []Chunk) (ASTNode, error) {
|
||||
case TextChunkType:
|
||||
*ap = append(*ap, &ASTText{Chunk: c})
|
||||
case TagChunkType:
|
||||
if cd, ok := FindControlDefinition(c.Tag); ok {
|
||||
if cd, ok := findControlTagDefinition(c.Tag); ok {
|
||||
switch {
|
||||
case cd.RequiresParent() && !cd.CompatibleParent(ccd):
|
||||
case cd.requiresParent() && !cd.compatibleParent(ccd):
|
||||
suffix := ""
|
||||
if ccd != nil {
|
||||
suffix = "; immediate parent is " + ccd.Name
|
||||
suffix = "; immediate parent is " + ccd.name
|
||||
}
|
||||
return nil, fmt.Errorf("%s not inside %s%s", cd.Name, cd.Parent.Name, suffix)
|
||||
case cd.IsStartTag():
|
||||
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:
|
||||
case cd.isBranchTag:
|
||||
n := &ASTControlTag{Chunk: c, cd: cd}
|
||||
ccn.Branches = append(ccn.Branches, n)
|
||||
ap = &n.Body
|
||||
case cd.IsEndTag:
|
||||
case cd.isEndTag:
|
||||
f := stack[len(stack)-1]
|
||||
ccd, ccn, ap, stack = f.cd, f.cn, f.ap, stack[:len(stack)-1]
|
||||
}
|
||||
@ -68,7 +68,7 @@ func Parse(chunks []Chunk) (ASTNode, error) {
|
||||
}
|
||||
}
|
||||
if ccd != nil {
|
||||
return nil, fmt.Errorf("unterminated %s tag", ccd.Name)
|
||||
return nil, fmt.Errorf("unterminated %s tag", ccd.name)
|
||||
}
|
||||
if len(root.Children) == 1 {
|
||||
return root.Children[0], nil
|
||||
|
@ -45,7 +45,7 @@ func (ctx Context) RenderASTSequence(w io.Writer, seq []ASTNode) error {
|
||||
|
||||
// Render evaluates an AST node and writes the result to an io.Writer.
|
||||
func (n *ASTControlTag) Render(w io.Writer, ctx Context) error {
|
||||
cd, ok := FindControlDefinition(n.Tag)
|
||||
cd, ok := findControlTagDefinition(n.Tag)
|
||||
if !ok || cd.action == nil {
|
||||
return fmt.Errorf("unimplemented tag: %s", n.Tag)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user