Skip to content

Commit

Permalink
Merge pull request #67 from aisbergg/feat-loop-mod-expr
Browse files Browse the repository at this point in the history
Allow loop modifiers to be more than just literals
  • Loading branch information
danog authored Aug 23, 2023
2 parents 9bd9c58 + c50fc96 commit 66a8e52
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 248 deletions.
42 changes: 9 additions & 33 deletions expressions/expressions.y
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
package expressions
import (
"fmt"
"math"
"github.com/osteele/liquid/values"
)

Expand All @@ -26,7 +25,7 @@ func init() {
loopmods loopModifiers
filter_params []valueFn
}
%type <f> expr rel filtered cond int_or_var loop_expr
%type<f> expr rel filtered cond
%type<filter_params> filter_params
%type<exprs> exprs expr2
%type<cycle> cycle
Expand Down Expand Up @@ -84,25 +83,13 @@ string: LITERAL {
$$ = s
};

loop: IDENTIFIER IN loop_expr loop_modifiers {
loop: IDENTIFIER IN filtered loop_modifiers {
name, expr, mods := $1, $3, $4
$$ = Loop{name, &expression{expr}, mods}
}
;

loop_expr : '(' int_or_var DOTDOT int_or_var ')' {
$$ = makeRangeExpr($2, $4)
}
| filtered
;

// TODO DRY w/ expr
int_or_var:
LITERAL { val := $1; $$ = func(Context) values.Value { return values.ValueOf(val) } }
| IDENTIFIER { name := $1; $$ = func(ctx Context) values.Value { return values.ValueOf(ctx.Get(name)) } }
;

loop_modifiers: /* empty */ { $$ = loopModifiers{Cols: math.MaxInt32} }
loop_modifiers: /* empty */ { $$ = loopModifiers{} }
| loop_modifiers IDENTIFIER {
switch $2 {
case "reversed":
Expand All @@ -112,26 +99,14 @@ loop_modifiers: /* empty */ { $$ = loopModifiers{Cols: math.MaxInt32} }
}
$$ = $1
}
| loop_modifiers KEYWORD LITERAL { // TODO can this be a variable?
switch $2 {
| loop_modifiers KEYWORD expr {
switch $2 {
case "cols":
cols, ok := $3.(int)
if !ok {
panic(SyntaxError(fmt.Sprintf("loop cols must an integer")))
}
$1.Cols = cols
$1.Cols = &expression{$3}
case "limit":
limit, ok := $3.(int)
if !ok {
panic(SyntaxError(fmt.Sprintf("loop limit must an integer")))
}
$1.Limit = &limit
$1.Limit = &expression{$3}
case "offset":
offset, ok := $3.(int)
if !ok {
panic(SyntaxError(fmt.Sprintf("loop offset must an integer")))
}
$1.Offset = offset
$1.Offset = &expression{$3}
default:
panic(SyntaxError(fmt.Sprintf("undefined loop modifier %q", $2)))
}
Expand All @@ -144,6 +119,7 @@ expr:
| IDENTIFIER { name := $1; $$ = func(ctx Context) values.Value { return values.ValueOf(ctx.Get(name)) } }
| expr PROPERTY { $$ = makeObjectPropertyExpr($1, $2) }
| expr '[' expr ']' { $$ = makeIndexExpr($1, $3) }
| '(' expr DOTDOT expr ')' { $$ = makeRangeExpr($2, $4) }
| '(' cond ')' { $$ = $2 }
;

Expand Down
13 changes: 13 additions & 0 deletions expressions/expressions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"
"testing"

"github.com/osteele/liquid/values"
"github.com/stretchr/testify/require"
)

Expand All @@ -27,6 +28,7 @@ var evaluatorTests = []struct {
// Attributes
{`hash.a`, "first"},
{`hash.b.c`, "d"},
{`hash["b"].c`, "d"},
{`hash.x`, nil},
{`fruits.first`, "apples"},
{`fruits.last`, "plums"},
Expand All @@ -44,7 +46,14 @@ var evaluatorTests = []struct {
{`hash[1]`, nil},
{`hash.c[0]`, "r"},

// Range
{`(1..5)`, values.NewRange(1, 5)},
{`(1..range.end)`, values.NewRange(1, 5)},
{`(1..range["end"])`, values.NewRange(1, 5)},
{`(range.begin..range.end)`, values.NewRange(1, 5)},

// Expressions
{`(1)`, 1},
{`(n)`, 123},

// Operators
Expand Down Expand Up @@ -116,6 +125,10 @@ var evaluatorTestBindings = (map[string]interface{}{
"c": []string{"r", "g", "b"},
},
"hash_with_size_key": map[string]interface{}{"size": "key_value"},
"range": map[string]interface{}{
"begin": 1,
"end": 5,
},
})

func TestEvaluateString(t *testing.T) {
Expand Down
6 changes: 3 additions & 3 deletions expressions/statements.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ type Loop struct {
}

type loopModifiers struct {
Limit *int
Offset int
Limit Expression
Offset Expression
Cols Expression
Reversed bool
Cols int
}

// A When is a parse of a {% when %} clause
Expand Down
8 changes: 6 additions & 2 deletions expressions/statements_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ func TestParseStatement(t *testing.T) {
stmt, err := ParseStatement(AssignStatementSelector, "a = b")
require.NoError(t, err)
require.Equal(t, "a", stmt.Assignment.Variable)
require.Implements(t, (*Expression)(nil), stmt.Assignment.ValueFn)

stmt, err = ParseStatement(CycleStatementSelector, "'a', 'b'")
require.NoError(t, err)
Expand All @@ -27,9 +28,12 @@ func TestParseStatement(t *testing.T) {
require.NoError(t, err)
require.Equal(t, "x", stmt.Loop.Variable)
require.True(t, stmt.Loop.Reversed)
require.Equal(t, 2, stmt.Loop.Offset)

require.Nil(t, stmt.Loop.Cols)
require.NotNil(t, stmt.Loop.Limit)
require.Equal(t, 3, *stmt.Loop.Limit)
require.Implements(t, (*Expression)(nil), stmt.Loop.Limit)
require.NotNil(t, stmt.Loop.Offset)
require.Implements(t, (*Expression)(nil), stmt.Loop.Offset)

stmt, err = ParseStatement(WhenStatementSelector, "a, b")
require.NoError(t, err)
Expand Down
Loading

0 comments on commit 66a8e52

Please sign in to comment.