1
0
mirror of https://github.com/danog/liquid.git synced 2024-12-04 06:07:54 +01:00
liquid/expressions/expressions.y

216 lines
4.7 KiB
Plaintext
Raw Normal View History

2017-06-25 22:21:31 +02:00
%{
2017-07-14 02:18:23 +02:00
package expressions
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{}
s string
ss []string
2017-07-13 17:59:11 +02:00
exprs []Expression
cycle Cycle
cyclefn func(string) Cycle
2017-07-13 16:08:17 +02:00
loop Loop
2017-06-29 02:49:38 +02:00
loopmods loopModifiers
filter_params []valueFn
2017-06-25 22:21:31 +02:00
}
2017-07-13 20:58:36 +02:00
%type <f> expr rel filtered cond int_or_var loop_expr
%type<filter_params> filter_params
2017-07-13 17:59:11 +02:00
%type<exprs> exprs expr2
%type<cycle> cycle
%type<cyclefn> cycle2
%type<ss> cycle3
2017-07-13 16:08:17 +02:00
%type<loop> loop
2017-06-27 21:10:44 +02:00
%type<loopmods> loop_modifiers
%type<s> string
%token <val> LITERAL
%token <name> IDENTIFIER KEYWORD PROPERTY
2017-07-13 17:59:11 +02:00
%token ASSIGN CYCLE LOOP WHEN
%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-07-13 16:08:17 +02:00
yylex.(*lexer).Assignment = Assignment{$2, &expression{$4}}
2017-06-27 19:18:01 +02:00
}
| CYCLE cycle ';' { yylex.(*lexer).Cycle = $2 }
2017-07-13 20:58:36 +02:00
| LOOP loop ';' { yylex.(*lexer).Loop = $2 }
| WHEN exprs ';' { yylex.(*lexer).When = When{$2} }
2017-07-12 20:25:41 +02:00
;
cycle: string cycle2 { $$ = $2($1) };
cycle2:
':' string cycle3 {
h, t := $2, $3
$$ = func(g string) Cycle { return Cycle{g, append([]string{h}, t...)} }
}
| cycle3 {
vals := $1
$$ = func(h string) Cycle { return Cycle{Values: append([]string{h}, vals...)} }
}
;
2017-07-12 20:25:41 +02:00
cycle3:
/* empty */ { $$ = []string{} }
| ',' string cycle3 { $$ = append([]string{$2}, $3...) }
2017-06-27 19:18:01 +02:00
;
2017-07-13 17:59:11 +02:00
exprs: expr expr2 { $$ = append([]Expression{&expression{$1}}, $2...) } ;
expr2:
/* empty */ { $$ = []Expression{} }
| ',' expr expr2 { $$ = append([]Expression{&expression{$2}}, $3...) }
;
string: LITERAL {
s, ok := $1.(string)
if !ok {
panic(ParseError(fmt.Sprintf("expected a string for %q", $1)))
}
$$ = s
};
2017-07-13 20:58:36 +02:00
loop: IDENTIFIER IN loop_expr loop_modifiers {
2017-06-27 21:10:44 +02:00
name, expr, mods := $1, $3, $4
2017-07-13 16:08:17 +02:00
$$ = Loop{name, &expression{expr}, mods}
2017-06-27 19:18:01 +02:00
}
;
2017-06-25 22:21:31 +02:00
2017-07-13 20:58:36 +02:00
loop_expr : '(' int_or_var '.' '.' int_or_var ')' {
$$ = makeRangeExpr($2, $5)
}
| filtered
;
// TODO DRY w/ expr
int_or_var:
LITERAL { val := $1; $$ = func(Context) interface{} { return val } }
| IDENTIFIER { name := $1; $$ = func(ctx Context) interface{} { return ctx.Get(name) } }
;
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 %q", $2)))
2017-06-28 22:18:32 +02:00
}
$$ = $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:
panic(ParseError(fmt.Sprintf("undefined loop modifier %q", $2)))
2017-06-27 21:10:44 +02:00
}
$$ = $1
}
;
expr:
2017-07-13 20:58:36 +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
}
}
;