2017-06-25 22:21:31 +02:00
|
|
|
%{
|
2017-06-26 15:33:07 +02:00
|
|
|
package expressions
|
2017-06-25 22:21:31 +02:00
|
|
|
import (
|
2017-06-26 14:23:50 +02:00
|
|
|
"fmt"
|
2017-06-27 16:28:39 +02:00
|
|
|
"github.com/osteele/liquid/generics"
|
2017-06-25 22:21:31 +02:00
|
|
|
)
|
2017-06-26 14:23:50 +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.
|
2017-06-26 14:23:50 +02:00
|
|
|
_ = fmt.Sprint("")
|
|
|
|
}
|
|
|
|
|
2017-06-25 22:21:31 +02:00
|
|
|
%}
|
|
|
|
%union {
|
2017-06-28 17:24:24 +02:00
|
|
|
name string
|
|
|
|
val interface{}
|
|
|
|
f func(Context) interface{}
|
2017-06-27 21:10:44 +02:00
|
|
|
loopmods LoopModifiers
|
2017-06-28 17:24:24 +02:00
|
|
|
filter_params []valueFn
|
2017-06-25 22:21:31 +02:00
|
|
|
}
|
2017-06-28 17:24:24 +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-06-26 02:47:31 +02:00
|
|
|
%token <val> LITERAL
|
2017-06-27 03:32:08 +02:00
|
|
|
%token <name> IDENTIFIER KEYWORD RELATION
|
2017-06-27 19:18:01 +02:00
|
|
|
%token ASSIGN LOOP
|
2017-06-27 23:54:24 +02:00
|
|
|
%token EQ NEQ FOR 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:
|
2017-06-28 17:24:24 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
| LOOP loop { yylex.(*lexer).val = $2 }
|
|
|
|
;
|
|
|
|
|
2017-06-28 17:24:24 +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-27 21:10:44 +02:00
|
|
|
loop_modifiers: /* empty */ { $$ = LoopModifiers{} }
|
|
|
|
| loop_modifiers IDENTIFIER {
|
|
|
|
if $2 != "reversed" {
|
|
|
|
panic(ParseError(fmt.Sprintf("undefined loop modifier: %s", $2)))
|
|
|
|
}
|
|
|
|
$1.Reversed = true
|
|
|
|
$$ = $1
|
|
|
|
}
|
|
|
|
;
|
|
|
|
|
2017-06-26 02:47:31 +02:00
|
|
|
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) } }
|
2017-06-28 02:32:55 +02:00
|
|
|
| expr '.' IDENTIFIER { $$ = makeObjectPropertyEvaluator($1, $3) }
|
|
|
|
| expr '[' expr ']' { $$ = makeIndexEvaluator($1, $3) }
|
2017-06-28 19:53:37 +02:00
|
|
|
| '(' cond ')' { $$ = $2 }
|
2017-06-28 02:32:55 +02:00
|
|
|
;
|
2017-06-27 03:32:08 +02:00
|
|
|
|
2017-06-28 17:24:24 +02:00
|
|
|
filtered:
|
2017-06-27 03:32:08 +02:00
|
|
|
expr
|
2017-06-28 17:24:24 +02:00
|
|
|
| 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
|
|
|
|
2017-06-28 17:24:24 +02:00
|
|
|
filter_params:
|
|
|
|
expr { $$ = []valueFn{$1} }
|
|
|
|
| filter_params ',' expr
|
|
|
|
{ $$ = append($1, $3) }
|
|
|
|
|
2017-06-26 15:06:55 +02:00
|
|
|
rel:
|
2017-06-28 17:24:24 +02:00
|
|
|
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)
|
|
|
|
return generics.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)
|
|
|
|
return !generics.Equal(a, b)
|
|
|
|
}
|
|
|
|
}| 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)
|
|
|
|
return generics.Less(a, b)
|
2017-06-26 15:06:55 +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)
|
|
|
|
return generics.Less(b, a)
|
2017-06-26 15:06:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
;
|
2017-06-27 23:54:24 +02:00
|
|
|
|
2017-06-28 17:24:24 +02:00
|
|
|
cond:
|
2017-06-27 23:54:24 +02:00
|
|
|
rel
|
2017-06-28 17:24:24 +02:00
|
|
|
| cond AND rel {
|
2017-06-27 23:54:24 +02:00
|
|
|
fa, fb := $1, $3
|
|
|
|
$$ = func(ctx Context) interface{} {
|
|
|
|
return generics.IsTrue(fa(ctx)) && generics.IsTrue(fb(ctx))
|
|
|
|
}
|
|
|
|
}
|
2017-06-28 17:24:24 +02:00
|
|
|
| cond OR rel {
|
2017-06-27 23:54:24 +02:00
|
|
|
fa, fb := $1, $3
|
|
|
|
$$ = func(ctx Context) interface{} {
|
|
|
|
return generics.IsTrue(fa(ctx)) || generics.IsTrue(fb(ctx))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
;
|