Add all loop variables inside a loop (#49)
This commit is contained in:
parent
6c85e4133d
commit
355c7f2be5
24
exec.go
24
exec.go
|
@ -342,10 +342,28 @@ func (s *state) walkForNode(node *parse.ForNode) error {
|
|||
defer s.scope.pop()
|
||||
|
||||
if kn != "" {
|
||||
s.scope.Set(kn, k)
|
||||
s.scope.setLocal(kn, k)
|
||||
}
|
||||
s.scope.Set(vn, v)
|
||||
s.scope.Set("loop", l)
|
||||
s.scope.setLocal(vn, v)
|
||||
loopValue := map[string]Value{
|
||||
"Last": l.Last,
|
||||
"Index": l.Index,
|
||||
"Index0": l.Index0,
|
||||
"last": l.Last,
|
||||
"index": l.Index,
|
||||
"index0": l.Index0,
|
||||
"revindex": l.Revindex,
|
||||
"revindex0": l.Revindex0,
|
||||
"first": l.First,
|
||||
"length": l.Length,
|
||||
}
|
||||
|
||||
parent, hasParent := s.scope.Get("loop")
|
||||
if hasParent {
|
||||
loopValue["parent"] = parent
|
||||
}
|
||||
|
||||
s.scope.setLocal("loop", loopValue)
|
||||
|
||||
err := s.walk(node.Body)
|
||||
if err != nil {
|
||||
|
|
19
exec_test.go
19
exec_test.go
|
@ -43,10 +43,27 @@ var tests = []execTest{
|
|||
{"Chained attributes", `{{ entity.attr.Name }}`, map[string]Value{"entity": map[string]Value{"attr": struct{ Name string }{"Tyler"}}}, expect(`Tyler`)},
|
||||
{"Attribute method call", `{{ entity.Name('lower') }}`, map[string]Value{"entity": &testPerson{"Johnny"}}, expect(`lowerJohnny`)},
|
||||
{"For loop", `{% for i in 1..3 %}{{ i }}{% endfor %}`, emptyCtx, expect(`123`)},
|
||||
{
|
||||
"For loop with inner loop",
|
||||
`{% for i in test %}{% for j in i %}{{ j }}{{ loop.index }}{{ loop.parent.index }}{% if loop.first %},{% endif %}{% if loop.last %};{% endif %}{% endfor %}{% if loop.first %}f{% endif %}{% if loop.last %}l{% endif %}:{% endfor %}`,
|
||||
map[string]Value{
|
||||
"test": [][]int{
|
||||
{1, 2, 3},
|
||||
{4, 5, 6},
|
||||
{7, 8, 9}},
|
||||
},
|
||||
expect(`111,221331;f:412,522632;:713,823933;l:`),
|
||||
},
|
||||
{
|
||||
"For loop variables",
|
||||
`{% for i in 1..3 %}{{ i }}{{ loop.index }}{{ loop.index0 }}{{ loop.revindex }}{{ loop.revindex0 }}{{ loop.length }}{% if loop.first %}f{% endif %}{% if loop.last %}l{% endif %}{% endfor %}`,
|
||||
emptyCtx,
|
||||
expect(`110323f221213332103l`),
|
||||
},
|
||||
{"For else", `{% for i in emptySet %}{{ i }}{% else %}No results.{% endfor %}`, map[string]Value{"emptySet": []int{}}, expect(`No results.`)},
|
||||
{
|
||||
"For map",
|
||||
`{% for k, v in data %}Record {{ loop.Index }}: {{ k }}: {{ v }}{% if not loop.Last %} - {% endif %}{% endfor %}`,
|
||||
`{% for k, v in data %}Record {{ loop.index }}: {{ k }}: {{ v }}{% if not loop.last %} - {% endif %}{% endfor %}`,
|
||||
map[string]Value{"data": map[string]float64{"Group A": 5.12, "Group B": 5.09}},
|
||||
optionExpect(`Record 1: Group A: 5.12 - Record 2: Group B: 5.09`, `Record 1: Group B: 5.09 - Record 2: Group A: 5.12`),
|
||||
},
|
||||
|
|
40
value.go
40
value.go
|
@ -291,9 +291,13 @@ type Iteratee func(k, v Value, l Loop) (brk bool, err error)
|
|||
|
||||
// Loop contains metadata about the current state of a loop.
|
||||
type Loop struct {
|
||||
Last bool
|
||||
Index int
|
||||
Index0 int
|
||||
Last bool
|
||||
Index int
|
||||
Index0 int
|
||||
Revindex int
|
||||
Revindex0 int
|
||||
First bool
|
||||
Length int
|
||||
}
|
||||
|
||||
// IsArray returns true if the given Value is a slice or array.
|
||||
|
@ -334,7 +338,15 @@ func Iterate(val Value, it Iteratee) (int, error) {
|
|||
switch r.Kind() {
|
||||
case reflect.Slice, reflect.Array:
|
||||
ln := r.Len()
|
||||
l := Loop{ln == 1, 1, 0}
|
||||
l := Loop{
|
||||
ln == 1,
|
||||
1,
|
||||
0,
|
||||
ln,
|
||||
ln - 1,
|
||||
true,
|
||||
ln,
|
||||
}
|
||||
for i := 0; i < ln; i++ {
|
||||
v := r.Index(i)
|
||||
brk, err := it(i, v.Interface(), l)
|
||||
|
@ -345,12 +357,23 @@ func Iterate(val Value, it Iteratee) (int, error) {
|
|||
l.Index++
|
||||
l.Index0++
|
||||
l.Last = ln == l.Index
|
||||
l.Revindex--
|
||||
l.Revindex0--
|
||||
l.First = false
|
||||
}
|
||||
return ln, nil
|
||||
case reflect.Map:
|
||||
keys := r.MapKeys()
|
||||
ln := r.Len()
|
||||
l := Loop{ln == 1, 1, 0}
|
||||
l := Loop{
|
||||
ln == 1,
|
||||
1,
|
||||
0,
|
||||
ln,
|
||||
ln - 1,
|
||||
true,
|
||||
ln,
|
||||
}
|
||||
for i, k := range keys {
|
||||
v := r.MapIndex(k)
|
||||
brk, err := it(k.Interface(), v.Interface(), l)
|
||||
|
@ -361,6 +384,9 @@ func Iterate(val Value, it Iteratee) (int, error) {
|
|||
l.Index++
|
||||
l.Index0++
|
||||
l.Last = ln == l.Index
|
||||
l.Revindex--
|
||||
l.Revindex0--
|
||||
l.First = false
|
||||
}
|
||||
return ln, nil
|
||||
default:
|
||||
|
@ -368,7 +394,7 @@ func Iterate(val Value, it Iteratee) (int, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Len returns the length of Value.
|
||||
// Len returns the Length of Value.
|
||||
func Len(val Value) (int, error) {
|
||||
if val == nil {
|
||||
return 0, nil
|
||||
|
@ -378,7 +404,7 @@ func Len(val Value) (int, error) {
|
|||
case reflect.Slice, reflect.Array, reflect.Map:
|
||||
return r.Len(), nil
|
||||
}
|
||||
return 0, fmt.Errorf(`stick: could not get length of %s "%v"`, r.Kind(), val)
|
||||
return 0, fmt.Errorf(`stick: could not get Length of %s "%v"`, r.Kind(), val)
|
||||
}
|
||||
|
||||
// Equal returns true if the two Values are considered equal.
|
||||
|
|
Reference in a new issue