1
0
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:
Oliver Steele 2017-06-27 12:23:07 -04:00
parent 4189f03261
commit 0c7a8d2569
5 changed files with 52 additions and 51 deletions

View File

@ -37,7 +37,7 @@ type ASTObject struct {
// ASTControlTag is a control tag.
type ASTControlTag struct {
Chunk
cd *ControlTagDefinition
cd *controlTagDefinition
Body []ASTNode
Branches []*ASTControlTag
}

View File

@ -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
}

View File

@ -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,

View File

@ -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

View File

@ -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)
}