1
0
mirror of https://github.com/danog/liquid.git synced 2024-11-26 23:34:47 +01:00

contains operates on strings not arrays

This commit is contained in:
Oliver Steele 2017-06-30 11:21:10 -04:00
parent ebc29dcbed
commit 9dda87f40f
4 changed files with 52 additions and 51 deletions

View File

@ -2,9 +2,46 @@ package expressions
import (
"reflect"
"strings"
)
func makeObjectPropertyEvaluator(obj func(Context) interface{}, attr string) func(Context) interface{} {
func makeContainsExpr(e1, e2 func(Context) interface{}) func(Context) interface{} {
return func(ctx Context) interface{} {
a, aok := e1(ctx).(string)
b, bok := e2(ctx).(string)
return aok && bok && strings.Contains(a, b)
}
}
func makeIndexExpr(obj, index func(Context) interface{}) func(Context) interface{} {
return func(ctx Context) interface{} {
ref := reflect.ValueOf(obj(ctx))
i := reflect.ValueOf(index(ctx))
switch ref.Kind() {
case reflect.Array, reflect.Slice:
switch i.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n := int(i.Int())
if n < 0 {
n = ref.Len() + n
}
if 0 <= n && n < ref.Len() {
return ref.Index(n).Interface()
}
}
case reflect.Map:
if i.Type().ConvertibleTo(ref.Type().Key()) {
item := ref.MapIndex(i.Convert(ref.Type().Key()))
if item.IsValid() {
return item.Interface()
}
}
}
return nil
}
}
func makeObjectPropertyExpr(obj func(Context) interface{}, attr string) func(Context) interface{} {
return func(ctx Context) interface{} {
ref := reflect.ValueOf(obj(ctx))
switch ref.Kind() {
@ -33,31 +70,3 @@ func makeObjectPropertyEvaluator(obj func(Context) interface{}, attr string) fun
return nil
}
}
func makeIndexEvaluator(obj, index func(Context) interface{}) func(Context) interface{} {
return func(ctx Context) interface{} {
ref := reflect.ValueOf(obj(ctx))
i := reflect.ValueOf(index(ctx))
switch ref.Kind() {
case reflect.Array, reflect.Slice:
switch i.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n := int(i.Int())
if n < 0 {
n = ref.Len() + n
}
if 0 <= n && n < ref.Len() {
return ref.Index(n).Interface()
}
}
case reflect.Map:
if i.Type().ConvertibleTo(ref.Type().Key()) {
item := ref.MapIndex(i.Convert(ref.Type().Key()))
if item.IsValid() {
return item.Interface()
}
}
}
return nil
}
}

View File

@ -83,8 +83,8 @@ 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 { $$ = makeObjectPropertyEvaluator($1, $3) }
| expr '[' expr ']' { $$ = makeIndexEvaluator($1, $3) }
| expr '.' IDENTIFIER { $$ = makeObjectPropertyExpr($1, $3) }
| expr '[' expr ']' { $$ = makeIndexExpr($1, $3) }
| '(' cond ')' { $$ = $2 }
;
@ -143,12 +143,7 @@ rel:
return generics.Less(a, b) || generics.Equal(a, b)
}
}
| expr CONTAINS expr {
fa, fb := $1, $3
$$ = func(ctx Context) interface{} {
return generics.Contains(fa(ctx), fb(ctx))
}
}
| expr CONTAINS expr { $$ = makeContainsExpr($1, $3) }
;
cond:

View File

@ -34,9 +34,9 @@ var evaluatorTests = []struct {
{`fruits.size`, 4},
// Indices
{`ar[1]`, "second"},
{`ar[-1]`, "third"}, // undocumented
{`ar[100]`, nil},
{`array[1]`, "second"},
{`array[-1]`, "third"}, // undocumented
{`array[100]`, nil},
{`obj[1]`, nil},
{`obj.c[0]`, "r"},
@ -89,13 +89,13 @@ var evaluatorTests = []struct {
{`false or false`, false},
{`false or true`, true},
{`ar contains "first"`, true},
{`ar contains "missing"`, false},
{`"seafood" contains "foo"`, true},
{`"seafood" contains "bar"`, false},
}
var evaluatorTestContext = NewContext(map[string]interface{}{
"n": 123,
"ar": []string{"first", "second", "third"},
"array": []string{"first", "second", "third"},
"empty_list": []interface{}{},
"fruits": []string{"apples", "oranges", "peaches", "plums"},
"obj": map[string]interface{}{

View File

@ -591,13 +591,13 @@ yydefault:
yyDollar = yyS[yypt-3 : yypt+1]
//line expressions.y:86
{
yyVAL.f = makeObjectPropertyEvaluator(yyDollar[1].f, yyDollar[3].name)
yyVAL.f = makeObjectPropertyExpr(yyDollar[1].f, yyDollar[3].name)
}
case 11:
yyDollar = yyS[yypt-4 : yypt+1]
//line expressions.y:87
{
yyVAL.f = makeIndexEvaluator(yyDollar[1].f, yyDollar[3].f)
yyVAL.f = makeIndexExpr(yyDollar[1].f, yyDollar[3].f)
}
case 12:
yyDollar = yyS[yypt-3 : yypt+1]
@ -693,14 +693,11 @@ yydefault:
yyDollar = yyS[yypt-3 : yypt+1]
//line expressions.y:146
{
fa, fb := yyDollar[1].f, yyDollar[3].f
yyVAL.f = func(ctx Context) interface{} {
return generics.Contains(fa(ctx), fb(ctx))
}
yyVAL.f = makeContainsExpr(yyDollar[1].f, yyDollar[3].f)
}
case 27:
yyDollar = yyS[yypt-3 : yypt+1]
//line expressions.y:156
//line expressions.y:151
{
fa, fb := yyDollar[1].f, yyDollar[3].f
yyVAL.f = func(ctx Context) interface{} {
@ -709,7 +706,7 @@ yydefault:
}
case 28:
yyDollar = yyS[yypt-3 : yypt+1]
//line expressions.y:162
//line expressions.y:157
{
fa, fb := yyDollar[1].f, yyDollar[3].f
yyVAL.f = func(ctx Context) interface{} {