mirror of
https://github.com/danog/liquid.git
synced 2024-11-30 07:18:58 +01:00
Negative integer indexes from end of list
This commit is contained in:
parent
d4c895d635
commit
c1fd00c261
@ -25,7 +25,7 @@ var evaluatorTests = []struct {
|
||||
{"obj.b.c", "d"},
|
||||
{"obj.x", nil},
|
||||
{"ar[1]", "second"},
|
||||
{"ar[-1]", nil},
|
||||
{"ar[-1]", "third"}, // undocumented
|
||||
{"ar[100]", nil},
|
||||
{"obj[1]", nil},
|
||||
{"obj.c[0]", "r"},
|
||||
|
@ -2,7 +2,6 @@
|
||||
package expressions
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"github.com/osteele/liquid/generics"
|
||||
)
|
||||
|
||||
@ -61,39 +60,9 @@ loop_modifiers: /* empty */ { $$ = LoopModifiers{} }
|
||||
expr:
|
||||
LITERAL { val := $1; $$ = func(_ Context) interface{} { return val } }
|
||||
| IDENTIFIER { name := $1; $$ = func(ctx Context) interface{} { return ctx.Get(name) } }
|
||||
| expr '.' IDENTIFIER {
|
||||
e, attr := $1, $3
|
||||
$$ = func(ctx Context) interface{} {
|
||||
obj := e(ctx)
|
||||
ref := reflect.ValueOf(obj)
|
||||
switch ref.Kind() {
|
||||
case reflect.Map:
|
||||
val := ref.MapIndex(reflect.ValueOf(attr))
|
||||
if val.Kind()!= reflect.Invalid {
|
||||
return val.Interface()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
| expr '[' expr ']' {
|
||||
e, i := $1, $3
|
||||
$$ = func(ctx Context) interface{} {
|
||||
ref := reflect.ValueOf(e(ctx))
|
||||
index := reflect.ValueOf(i(ctx))
|
||||
switch ref.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
switch index.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
n := int(index.Int())
|
||||
if 0 <= n && n < ref.Len() {
|
||||
return ref.Index(n).Interface()
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
| expr '.' IDENTIFIER { $$ = makeObjectPropertyEvaluator($1, $3) }
|
||||
| expr '[' expr ']' { $$ = makeIndexEvaluator($1, $3) }
|
||||
;
|
||||
|
||||
expr1:
|
||||
expr
|
||||
|
40
expressions/interpreter.go
Normal file
40
expressions/interpreter.go
Normal file
@ -0,0 +1,40 @@
|
||||
package expressions
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func makeObjectPropertyEvaluator(obj func(Context) interface{}, attr string) func(Context) interface{} {
|
||||
return func(ctx Context) interface{} {
|
||||
ref := reflect.ValueOf(obj(ctx))
|
||||
switch ref.Kind() {
|
||||
case reflect.Map:
|
||||
value := ref.MapIndex(reflect.ValueOf(attr))
|
||||
if value.Kind() != reflect.Invalid {
|
||||
return value.Interface()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func makeIndexEvaluator(obj, indexfn func(Context) interface{}) func(Context) interface{} {
|
||||
return func(ctx Context) interface{} {
|
||||
ref := reflect.ValueOf(obj(ctx))
|
||||
index := reflect.ValueOf(indexfn(ctx))
|
||||
switch ref.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
switch index.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
n := int(index.Int())
|
||||
if n < 0 {
|
||||
n = ref.Len() + n
|
||||
}
|
||||
if 0 <= n && n < ref.Len() {
|
||||
return ref.Index(n).Interface()
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@ import __yyfmt__ "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/osteele/liquid/generics"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -16,7 +15,7 @@ func init() {
|
||||
_ = fmt.Sprint("")
|
||||
}
|
||||
|
||||
//line expressions.y:16
|
||||
//line expressions.y:15
|
||||
type yySymType struct {
|
||||
yys int
|
||||
name string
|
||||
@ -495,13 +494,13 @@ yydefault:
|
||||
|
||||
case 1:
|
||||
yyDollar = yyS[yypt-2 : yypt+1]
|
||||
//line expressions.y:32
|
||||
//line expressions.y:31
|
||||
{
|
||||
yylex.(*lexer).val = yyDollar[1].f
|
||||
}
|
||||
case 2:
|
||||
yyDollar = yyS[yypt-5 : yypt+1]
|
||||
//line expressions.y:33
|
||||
//line expressions.y:32
|
||||
{
|
||||
name, expr := yyDollar[2].name, yyDollar[4].f
|
||||
yylex.(*lexer).val = func(ctx Context) interface{} {
|
||||
@ -511,13 +510,13 @@ yydefault:
|
||||
}
|
||||
case 3:
|
||||
yyDollar = yyS[yypt-2 : yypt+1]
|
||||
//line expressions.y:40
|
||||
//line expressions.y:39
|
||||
{
|
||||
yylex.(*lexer).val = yyDollar[2].f
|
||||
}
|
||||
case 4:
|
||||
yyDollar = yyS[yypt-5 : yypt+1]
|
||||
//line expressions.y:43
|
||||
//line expressions.y:42
|
||||
{
|
||||
name, expr, mods := yyDollar[1].name, yyDollar[3].f, yyDollar[4].loopmods
|
||||
yyVAL.f = func(ctx Context) interface{} {
|
||||
@ -526,13 +525,13 @@ yydefault:
|
||||
}
|
||||
case 5:
|
||||
yyDollar = yyS[yypt-0 : yypt+1]
|
||||
//line expressions.y:51
|
||||
//line expressions.y:50
|
||||
{
|
||||
yyVAL.loopmods = LoopModifiers{}
|
||||
}
|
||||
case 6:
|
||||
yyDollar = yyS[yypt-2 : yypt+1]
|
||||
//line expressions.y:52
|
||||
//line expressions.y:51
|
||||
{
|
||||
if yyDollar[2].name != "reversed" {
|
||||
panic(ParseError(fmt.Sprintf("undefined loop modifier: %s", yyDollar[2].name)))
|
||||
@ -542,72 +541,45 @@ yydefault:
|
||||
}
|
||||
case 7:
|
||||
yyDollar = yyS[yypt-1 : yypt+1]
|
||||
//line expressions.y:62
|
||||
//line expressions.y:61
|
||||
{
|
||||
val := yyDollar[1].val
|
||||
yyVAL.f = func(_ Context) interface{} { return val }
|
||||
}
|
||||
case 8:
|
||||
yyDollar = yyS[yypt-1 : yypt+1]
|
||||
//line expressions.y:63
|
||||
//line expressions.y:62
|
||||
{
|
||||
name := yyDollar[1].name
|
||||
yyVAL.f = func(ctx Context) interface{} { return ctx.Get(name) }
|
||||
}
|
||||
case 9:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
//line expressions.y:64
|
||||
//line expressions.y:63
|
||||
{
|
||||
e, attr := yyDollar[1].f, yyDollar[3].name
|
||||
yyVAL.f = func(ctx Context) interface{} {
|
||||
obj := e(ctx)
|
||||
ref := reflect.ValueOf(obj)
|
||||
switch ref.Kind() {
|
||||
case reflect.Map:
|
||||
val := ref.MapIndex(reflect.ValueOf(attr))
|
||||
if val.Kind() != reflect.Invalid {
|
||||
return val.Interface()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
yyVAL.f = makeObjectPropertyEvaluator(yyDollar[1].f, yyDollar[3].name)
|
||||
}
|
||||
case 10:
|
||||
yyDollar = yyS[yypt-4 : yypt+1]
|
||||
//line expressions.y:79
|
||||
//line expressions.y:64
|
||||
{
|
||||
e, i := yyDollar[1].f, yyDollar[3].f
|
||||
yyVAL.f = func(ctx Context) interface{} {
|
||||
ref := reflect.ValueOf(e(ctx))
|
||||
index := reflect.ValueOf(i(ctx))
|
||||
switch ref.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
switch index.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
n := int(index.Int())
|
||||
if 0 <= n && n < ref.Len() {
|
||||
return ref.Index(n).Interface()
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
yyVAL.f = makeIndexEvaluator(yyDollar[1].f, yyDollar[3].f)
|
||||
}
|
||||
case 12:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
//line expressions.y:100
|
||||
//line expressions.y:69
|
||||
{
|
||||
yyVAL.f = makeFilter(yyDollar[1].f, yyDollar[3].name, nil)
|
||||
}
|
||||
case 13:
|
||||
yyDollar = yyS[yypt-4 : yypt+1]
|
||||
//line expressions.y:101
|
||||
//line expressions.y:70
|
||||
{
|
||||
yyVAL.f = makeFilter(yyDollar[1].f, yyDollar[3].name, yyDollar[4].f)
|
||||
}
|
||||
case 15:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
//line expressions.y:106
|
||||
//line expressions.y:75
|
||||
{
|
||||
fa, fb := yyDollar[1].f, yyDollar[3].f
|
||||
yyVAL.f = func(ctx Context) interface{} {
|
||||
@ -617,7 +589,7 @@ yydefault:
|
||||
}
|
||||
case 16:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
//line expressions.y:113
|
||||
//line expressions.y:82
|
||||
{
|
||||
fa, fb := yyDollar[1].f, yyDollar[3].f
|
||||
yyVAL.f = func(ctx Context) interface{} {
|
||||
@ -627,7 +599,7 @@ yydefault:
|
||||
}
|
||||
case 17:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
//line expressions.y:119
|
||||
//line expressions.y:88
|
||||
{
|
||||
fa, fb := yyDollar[1].f, yyDollar[3].f
|
||||
yyVAL.f = func(ctx Context) interface{} {
|
||||
@ -637,7 +609,7 @@ yydefault:
|
||||
}
|
||||
case 18:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
//line expressions.y:126
|
||||
//line expressions.y:95
|
||||
{
|
||||
fa, fb := yyDollar[1].f, yyDollar[3].f
|
||||
yyVAL.f = func(ctx Context) interface{} {
|
||||
@ -647,7 +619,7 @@ yydefault:
|
||||
}
|
||||
case 20:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
//line expressions.y:137
|
||||
//line expressions.y:106
|
||||
{
|
||||
fa, fb := yyDollar[1].f, yyDollar[3].f
|
||||
yyVAL.f = func(ctx Context) interface{} {
|
||||
@ -656,7 +628,7 @@ yydefault:
|
||||
}
|
||||
case 21:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
//line expressions.y:143
|
||||
//line expressions.y:112
|
||||
{
|
||||
fa, fb := yyDollar[1].f, yyDollar[3].f
|
||||
yyVAL.f = func(ctx Context) interface{} {
|
||||
|
Loading…
Reference in New Issue
Block a user