1
0
mirror of https://github.com/danog/liquid.git synced 2024-12-04 17:27:50 +01:00
liquid/expression/expressions.y

183 lines
4.0 KiB
Plaintext
Raw Normal View History

2017-06-25 22:21:31 +02:00
%{
2017-07-07 14:09:16 +02:00
package expression
2017-06-25 22:21:31 +02:00
import (
"fmt"
2017-07-07 14:09:16 +02:00
"github.com/osteele/liquid/evaluator"
2017-06-25 22:21:31 +02:00
)
func init() {
2017-06-27 16:28:39 +02:00
// This allows adding and removing references to fmt in the rules below,
// without having to edit the import statement to avoid erorrs each time.
_ = fmt.Sprint("")
}
2017-06-25 22:21:31 +02:00
%}
%union {
name string
val interface{}
f func(Context) interface{}
2017-07-12 20:25:41 +02:00
arglist []func(Context) interface{}
2017-06-29 02:49:38 +02:00
loopmods loopModifiers
filter_params []valueFn
2017-06-25 22:21:31 +02:00
}
%type <f> expr rel filtered cond loop
%type<filter_params> filter_params
2017-06-27 21:10:44 +02:00
%type<loopmods> loop_modifiers
2017-07-12 20:25:41 +02:00
%type<arglist> arglist moreargs
%token <val> LITERAL
%token <name> IDENTIFIER KEYWORD PROPERTY
2017-07-12 20:25:41 +02:00
%token ARGLIST ASSIGN LOOP
%token EQ NEQ GE LE IN AND OR CONTAINS
2017-06-27 03:32:08 +02:00
%left '.' '|'
2017-06-26 15:06:55 +02:00
%left '<' '>'
2017-06-25 22:21:31 +02:00
%%
2017-06-26 21:36:05 +02:00
start:
cond ';' { yylex.(*lexer).val = $1 }
| ASSIGN IDENTIFIER '=' filtered ';' {
2017-06-26 21:36:05 +02:00
name, expr := $2, $4
yylex.(*lexer).val = func(ctx Context) interface{} {
ctx.Set(name, expr(ctx))
return nil
}
2017-06-27 19:18:01 +02:00
}
2017-07-12 20:25:41 +02:00
| ARGLIST arglist ';' {
args := $2
yylex.(*lexer).val = func(ctx Context) interface{} {
result := make([]interface{}, len(args))
for i, fn := range args {
result[i] = fn(ctx)
}
return result
}
}
| LOOP loop ';' { yylex.(*lexer).val = $2 }
;
arglist:
expr moreargs { $$ = append([]func(Context) interface{}{$1}, $2...) };
moreargs : /* empty */ { $$ = []func(Context) interface{}{}}
| ',' expr moreargs { $$ = append([]func(Context) interface{}{$2}, $3...) }
2017-06-27 19:18:01 +02:00
;
2017-07-12 20:25:41 +02:00
loop: IDENTIFIER IN filtered loop_modifiers {
2017-06-27 21:10:44 +02:00
name, expr, mods := $1, $3, $4
2017-06-27 19:18:01 +02:00
$$ = func(ctx Context) interface{} {
2017-06-27 21:10:44 +02:00
return &Loop{name, expr(ctx), mods}
2017-06-27 19:18:01 +02:00
}
}
;
2017-06-25 22:21:31 +02:00
2017-06-29 02:49:38 +02:00
loop_modifiers: /* empty */ { $$ = loopModifiers{} }
2017-06-27 21:10:44 +02:00
| loop_modifiers IDENTIFIER {
2017-06-28 22:18:32 +02:00
switch $2 {
case "reversed":
$1.Reversed = true
default:
panic(ParseError(fmt.Sprintf("undefined loop modifier: %s", $2)))
}
$$ = $1
}
| loop_modifiers KEYWORD LITERAL { // TODO can this be a variable?
switch $2 {
case "limit":
limit, ok := $3.(int)
if !ok {
panic(ParseError(fmt.Sprintf("loop limit must an integer")))
}
$1.Limit = &limit
case "offset":
offset, ok := $3.(int)
if !ok {
panic(ParseError(fmt.Sprintf("loop offset must an integer")))
}
$1.Offset = offset
default:
2017-06-27 21:10:44 +02:00
panic(ParseError(fmt.Sprintf("undefined loop modifier: %s", $2)))
}
$$ = $1
}
;
expr:
2017-06-26 13:50:53 +02:00
LITERAL { val := $1; $$ = func(_ Context) interface{} { return val } }
2017-06-26 21:36:05 +02:00
| IDENTIFIER { name := $1; $$ = func(ctx Context) interface{} { return ctx.Get(name) } }
| expr PROPERTY { $$ = makeObjectPropertyExpr($1, $2) }
| expr '[' expr ']' { $$ = makeIndexExpr($1, $3) }
2017-06-28 19:53:37 +02:00
| '(' cond ')' { $$ = $2 }
;
2017-06-27 03:32:08 +02:00
filtered:
2017-06-27 03:32:08 +02:00
expr
| filtered '|' IDENTIFIER { $$ = makeFilter($1, $3, nil) }
| filtered '|' KEYWORD filter_params { $$ = makeFilter($1, $3, $4) }
2017-06-26 04:59:33 +02:00
;
2017-06-26 13:50:53 +02:00
filter_params:
expr { $$ = []valueFn{$1} }
| filter_params ',' expr
{ $$ = append($1, $3) }
2017-06-26 15:06:55 +02:00
rel:
filtered
2017-06-26 15:06:55 +02:00
| expr EQ expr {
2017-06-27 16:28:39 +02:00
fa, fb := $1, $3
2017-06-26 15:06:55 +02:00
$$ = func(ctx Context) interface{} {
2017-06-27 16:28:39 +02:00
a, b := fa(ctx), fb(ctx)
2017-07-07 14:09:16 +02:00
return evaluator.Equal(a, b)
2017-06-26 15:06:55 +02:00
}
}
2017-06-27 23:54:24 +02:00
| expr NEQ expr {
fa, fb := $1, $3
$$ = func(ctx Context) interface{} {
a, b := fa(ctx), fb(ctx)
2017-07-07 14:09:16 +02:00
return !evaluator.Equal(a, b)
2017-06-27 23:54:24 +02:00
}
2017-06-28 23:18:48 +02:00
}
| expr '>' expr {
fa, fb := $1, $3
$$ = func(ctx Context) interface{} {
a, b := fa(ctx), fb(ctx)
2017-07-07 14:09:16 +02:00
return evaluator.Less(b, a)
2017-06-28 23:18:48 +02:00
}
}
| expr '<' expr {
2017-06-27 16:28:39 +02:00
fa, fb := $1, $3
2017-06-26 15:06:55 +02:00
$$ = func(ctx Context) interface{} {
2017-06-27 16:28:39 +02:00
a, b := fa(ctx), fb(ctx)
2017-07-07 14:09:16 +02:00
return evaluator.Less(a, b)
2017-06-26 15:06:55 +02:00
}
}
2017-06-28 23:18:48 +02:00
| expr GE expr {
2017-06-27 16:28:39 +02:00
fa, fb := $1, $3
2017-06-26 15:06:55 +02:00
$$ = func(ctx Context) interface{} {
2017-06-27 16:28:39 +02:00
a, b := fa(ctx), fb(ctx)
2017-07-07 14:09:16 +02:00
return evaluator.Less(b, a) || evaluator.Equal(a, b)
2017-06-28 23:18:48 +02:00
}
}
| expr LE expr {
fa, fb := $1, $3
$$ = func(ctx Context) interface{} {
a, b := fa(ctx), fb(ctx)
2017-07-07 14:09:16 +02:00
return evaluator.Less(a, b) || evaluator.Equal(a, b)
2017-06-28 23:18:48 +02:00
}
}
| expr CONTAINS expr { $$ = makeContainsExpr($1, $3) }
2017-06-26 15:06:55 +02:00
;
2017-06-27 23:54:24 +02:00
cond:
2017-06-27 23:54:24 +02:00
rel
| cond AND rel {
2017-06-27 23:54:24 +02:00
fa, fb := $1, $3
$$ = func(ctx Context) interface{} {
2017-07-07 14:09:16 +02:00
return evaluator.IsTrue(fa(ctx)) && evaluator.IsTrue(fb(ctx))
2017-06-27 23:54:24 +02:00
}
}
| cond OR rel {
2017-06-27 23:54:24 +02:00
fa, fb := $1, $3
$$ = func(ctx Context) interface{} {
2017-07-07 14:09:16 +02:00
return evaluator.IsTrue(fa(ctx)) || evaluator.IsTrue(fb(ctx))
2017-06-27 23:54:24 +02:00
}
}
;