Add all loop variables inside a loop (#49)

This commit is contained in:
christianWilling 2021-10-18 16:34:37 +02:00 committed by GitHub
parent 6c85e4133d
commit 355c7f2be5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 72 additions and 11 deletions

24
exec.go
View file

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

View file

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

View file

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