Skip to content

Commit

Permalink
implement support for else clauses in for loops. fixes #46
Browse files Browse the repository at this point in the history
  • Loading branch information
codykrieger committed Oct 5, 2024
1 parent 9d616d8 commit a9b2aa0
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 17 deletions.
45 changes: 29 additions & 16 deletions tags/iteration_tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,29 +67,42 @@ func loopTagCompiler(node render.BlockNode) (func(io.Writer, render.Context) err
if err != nil {
return nil, err
}
return loopRenderer{stmt.Loop, node.Name}.render, nil

return func(w io.Writer, ctx render.Context) error {
// loop modifiers
val, err := ctx.Evaluate(stmt.Loop.Expr)
if err != nil {
return err
}

iter := makeIterator(val)
if iter == nil {
return nil
}

iter, err = applyLoopModifiers(stmt.Loop, ctx, iter)
if err != nil {
return err
}

if len(node.Clauses) > 1 {
return fmt.Errorf("for loops accept at most one else clause")
}

if iter.Len() == 0 && len(node.Clauses) == 1 && node.Clauses[0].Name == "else" {
return ctx.RenderBlock(w, node.Clauses[0])
}

return loopRenderer{stmt.Loop, node.Name}.render(iter, w, ctx)
}, nil
}

type loopRenderer struct {
expressions.Loop
tagName string
}

func (loop loopRenderer) render(w io.Writer, ctx render.Context) error {
// loop modifiers
val, err := ctx.Evaluate(loop.Expr)
if err != nil {
return err
}
iter := makeIterator(val)
if iter == nil {
return nil
}
iter, err = applyLoopModifiers(loop.Loop, ctx, iter)
if err != nil {
return err
}

func (loop loopRenderer) render(iter iterable, w io.Writer, ctx render.Context) error {
// loop decorator
decorator, err := makeLoopDecorator(loop, ctx)
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions tags/iteration_tags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (

var iterationTests = []struct{ in, expected string }{
{`{% for a in array %}{{ a }} {% endfor %}`, "first second third "},
{`{% for a in array %}{{ a }} {% else %}else{% endfor %}`, "first second third "},
{`{% for a in nil %}{{ a }}.{% endfor %}`, ""},
{`{% for a in false %}{{ a }}.{% endfor %}`, ""},
{`{% for a in 2 %}{{ a }}.{% endfor %}`, ""},
Expand All @@ -37,6 +38,7 @@ var iterationTests = []struct{ in, expected string }{
{`{% for a in array offset: loopmods["offset"] %}{{ a }}.{% endfor %}`, "second.third."},
{`{% for a in array reversed limit: 1 %}{{ a }}.{% endfor %}`, "third."},
{`{% for a in array limit: 0 %}{{ a }}.{% endfor %}`, ""},
{`{% for a in array limit: 0 %}{{ a }}.{% else %}ELSE{% endfor %}`, "ELSE"},
{`{% for a in array offset: 3 %}{{ a }}.{% endfor %}`, ""},
{`{% for a in array offset: 10 %}{{ a }}.{% endfor %}`, ""},
// TODO investigate how these combine; does it depend on the order?
Expand Down Expand Up @@ -126,6 +128,7 @@ var iterationErrorTests = []struct{ in, expected string }{
{`{% cycle 'a', 'b' %}`, "cycle must be within a forloop"},
{`{% for a in array | undefined_filter %}{% endfor %}`, "undefined filter"},
{`{% for a in array %}{{ a | undefined_filter }}{% endfor %}`, "undefined filter"},
{`{% for a in array %}{% else %}{% else %}{% endfor %}`, "for loops accept at most one else clause"},
}

var iterationTestBindings = map[string]interface{}{
Expand Down
2 changes: 1 addition & 1 deletion tags/standard_tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func AddStandardTags(c render.Config) {
c.AddBlock("capture").Compiler(captureTagCompiler)
c.AddBlock("case").Clause("when").Clause("else").Compiler(caseTagCompiler)
c.AddBlock("comment")
c.AddBlock("for").Compiler(loopTagCompiler)
c.AddBlock("for").Clause("else").Compiler(loopTagCompiler)
c.AddBlock("if").Clause("else").Clause("elsif").Compiler(ifTagCompiler(true))
c.AddBlock("raw")
c.AddBlock("tablerow").Compiler(loopTagCompiler)
Expand Down

0 comments on commit a9b2aa0

Please sign in to comment.