diff --git a/chunks/ast.go b/chunks/ast.go index f8ee100..90ad1b8 100644 --- a/chunks/ast.go +++ b/chunks/ast.go @@ -37,7 +37,7 @@ type ASTObject struct { // ASTControlTag is a control tag. type ASTControlTag struct { Chunk - cd *ControlTagDefinition + cd *controlTagDefinition Body []ASTNode Branches []*ASTControlTag } diff --git a/chunks/control_tags.go b/chunks/control_tags.go index 7e902db..e7f4f41 100644 --- a/chunks/control_tags.go +++ b/chunks/control_tags.go @@ -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 } diff --git a/chunks/marshal.go b/chunks/marshal.go index 8496d17..2f888f0 100644 --- a/chunks/marshal.go +++ b/chunks/marshal.go @@ -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, diff --git a/chunks/parser.go b/chunks/parser.go index be1366d..2c4ad90 100644 --- a/chunks/parser.go +++ b/chunks/parser.go @@ -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 diff --git a/chunks/render.go b/chunks/render.go index ba45893..c248577 100644 --- a/chunks/render.go +++ b/chunks/render.go @@ -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) }