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 (
|
2017-07-15 16:38:12 +02:00
|
|
|
"fmt"
|
|
|
|
"math"
|
2017-07-07 14:09:16 +02:00
|
|
|
"github.com/osteele/liquid/evaluator"
|
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,
|
2017-07-14 20:28:02 +02:00
|
|
|
// without having to comment and un-comment the import statement above.
|
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{}
|
2017-07-20 15:12:31 +02:00
|
|
|
f func(Context) evaluator.Value
|
2017-07-13 17:07:45 +02:00
|
|
|
s string
|
|
|
|
ss []string
|
2017-07-13 17:59:11 +02:00
|
|
|
exprs []Expression
|
2017-07-13 17:07:45 +02:00
|
|
|
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
|
2017-06-28 17:24:24 +02:00
|
|
|
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
|
2017-06-28 17:24:24 +02:00
|
|
|
%type<filter_params> filter_params
|
2017-07-13 17:59:11 +02:00
|
|
|
%type<exprs> exprs expr2
|
2017-07-13 17:07:45 +02:00
|
|
|
%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
|
2017-07-13 17:07:45 +02:00
|
|
|
%type<s> string
|
2017-06-26 02:47:31 +02:00
|
|
|
%token <val> LITERAL
|
2017-07-07 14:09:42 +02:00
|
|
|
%token <name> IDENTIFIER KEYWORD PROPERTY
|
2017-07-13 17:59:11 +02:00
|
|
|
%token ASSIGN CYCLE LOOP WHEN
|
2017-07-16 18:54:20 +02:00
|
|
|
%token EQ NEQ GE LE IN AND OR CONTAINS DOTDOT
|
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-07-13 16:08:17 +02:00
|
|
|
yylex.(*lexer).Assignment = Assignment{$2, &expression{$4}}
|
2017-06-27 19:18:01 +02:00
|
|
|
}
|
2017-07-13 17:07:45 +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
|
|
|
;
|
|
|
|
|
2017-07-13 17:07:45 +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
|
|
|
|
2017-07-13 17:07:45 +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...) }
|
|
|
|
;
|
|
|
|
|
2017-07-13 17:07:45 +02:00
|
|
|
string: LITERAL {
|
|
|
|
s, ok := $1.(string)
|
|
|
|
if !ok {
|
2017-07-20 03:30:25 +02:00
|
|
|
panic(SyntaxError(fmt.Sprintf("expected a string for %q", $1)))
|
2017-07-13 17:07:45 +02:00
|
|
|
}
|
|
|
|
$$ = 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-16 18:54:20 +02:00
|
|
|
loop_expr : '(' int_or_var DOTDOT int_or_var ')' {
|
|
|
|
$$ = makeRangeExpr($2, $4)
|
2017-07-13 20:58:36 +02:00
|
|
|
}
|
|
|
|
| filtered
|
|
|
|
;
|
|
|
|
|
|
|
|
// TODO DRY w/ expr
|
|
|
|
int_or_var:
|
2017-07-20 15:12:31 +02:00
|
|
|
LITERAL { val := $1; $$ = func(Context) evaluator.Value { return evaluator.ValueOf(val) } }
|
|
|
|
| IDENTIFIER { name := $1; $$ = func(ctx Context) evaluator.Value { return evaluator.ValueOf(ctx.Get(name)) } }
|
2017-07-13 20:58:36 +02:00
|
|
|
;
|
|
|
|
|
2017-07-15 16:38:12 +02:00
|
|
|
loop_modifiers: /* empty */ { $$ = loopModifiers{Cols: math.MaxUint32} }
|
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:
|
2017-07-20 03:30:25 +02:00
|
|
|
panic(SyntaxError(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 {
|
2017-07-15 16:38:12 +02:00
|
|
|
case "cols":
|
|
|
|
cols, ok := $3.(int)
|
|
|
|
if !ok {
|
2017-07-20 03:30:25 +02:00
|
|
|
panic(SyntaxError(fmt.Sprintf("loop cols must an integer")))
|
2017-07-15 16:38:12 +02:00
|
|
|
}
|
|
|
|
$1.Cols = cols
|
2017-06-28 22:18:32 +02:00
|
|
|
case "limit":
|
|
|
|
limit, ok := $3.(int)
|
|
|
|
if !ok {
|
2017-07-20 03:30:25 +02:00
|
|
|
panic(SyntaxError(fmt.Sprintf("loop limit must an integer")))
|
2017-06-28 22:18:32 +02:00
|
|
|
}
|
|
|
|
$1.Limit = &limit
|
|
|
|
case "offset":
|
|
|
|
offset, ok := $3.(int)
|
|
|
|
if !ok {
|
2017-07-20 03:30:25 +02:00
|
|
|
panic(SyntaxError(fmt.Sprintf("loop offset must an integer")))
|
2017-06-28 22:18:32 +02:00
|
|
|
}
|
|
|
|
$1.Offset = offset
|
|
|
|
default:
|
2017-07-20 03:30:25 +02:00
|
|
|
panic(SyntaxError(fmt.Sprintf("undefined loop modifier %q", $2)))
|
2017-06-27 21:10:44 +02:00
|
|
|
}
|
|
|
|
$$ = $1
|
|
|
|
}
|
|
|
|
;
|
|
|
|
|
2017-06-26 02:47:31 +02:00
|
|
|
expr:
|
2017-07-20 15:12:31 +02:00
|
|
|
LITERAL { val := $1; $$ = func(Context) evaluator.Value { return evaluator.ValueOf(val) } }
|
|
|
|
| IDENTIFIER { name := $1; $$ = func(ctx Context) evaluator.Value { return evaluator.ValueOf(ctx.Get(name)) } }
|
2017-07-07 14:09:42 +02:00
|
|
|
| expr PROPERTY { $$ = makeObjectPropertyExpr($1, $2) }
|
2017-06-30 17:21:10 +02:00
|
|
|
| expr '[' expr ']' { $$ = makeIndexExpr($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-07-20 15:12:31 +02:00
|
|
|
$$ = func(ctx Context) evaluator.Value {
|
2017-06-27 16:28:39 +02:00
|
|
|
a, b := fa(ctx), fb(ctx)
|
2017-07-20 15:12:31 +02:00
|
|
|
return evaluator.ValueOf(a.Equal(b))
|
2017-06-26 15:06:55 +02:00
|
|
|
}
|
|
|
|
}
|
2017-06-27 23:54:24 +02:00
|
|
|
| expr NEQ expr {
|
|
|
|
fa, fb := $1, $3
|
2017-07-20 15:12:31 +02:00
|
|
|
$$ = func(ctx Context) evaluator.Value {
|
2017-06-27 23:54:24 +02:00
|
|
|
a, b := fa(ctx), fb(ctx)
|
2017-07-20 15:12:31 +02:00
|
|
|
return evaluator.ValueOf(!a.Equal(b))
|
2017-06-27 23:54:24 +02:00
|
|
|
}
|
2017-06-28 23:18:48 +02:00
|
|
|
}
|
|
|
|
| expr '>' expr {
|
|
|
|
fa, fb := $1, $3
|
2017-07-20 15:12:31 +02:00
|
|
|
$$ = func(ctx Context) evaluator.Value {
|
2017-06-28 23:18:48 +02:00
|
|
|
a, b := fa(ctx), fb(ctx)
|
2017-07-20 15:12:31 +02:00
|
|
|
return evaluator.ValueOf(b.Less(a))
|
2017-06-28 23:18:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
| expr '<' expr {
|
2017-06-27 16:28:39 +02:00
|
|
|
fa, fb := $1, $3
|
2017-07-20 15:12:31 +02:00
|
|
|
$$ = func(ctx Context) evaluator.Value {
|
2017-06-27 16:28:39 +02:00
|
|
|
a, b := fa(ctx), fb(ctx)
|
2017-07-20 15:12:31 +02:00
|
|
|
return evaluator.ValueOf(a.Less(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-07-20 15:12:31 +02:00
|
|
|
$$ = func(ctx Context) evaluator.Value {
|
2017-06-27 16:28:39 +02:00
|
|
|
a, b := fa(ctx), fb(ctx)
|
2017-07-20 15:12:31 +02:00
|
|
|
return evaluator.ValueOf(b.Less(a) || a.Equal(b))
|
2017-06-28 23:18:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
| expr LE expr {
|
|
|
|
fa, fb := $1, $3
|
2017-07-20 15:12:31 +02:00
|
|
|
$$ = func(ctx Context) evaluator.Value {
|
2017-06-28 23:18:48 +02:00
|
|
|
a, b := fa(ctx), fb(ctx)
|
2017-07-20 15:12:31 +02:00
|
|
|
return evaluator.ValueOf(a.Less(b) || a.Equal(b))
|
2017-06-28 23:18:48 +02:00
|
|
|
}
|
|
|
|
}
|
2017-06-30 17:21:10 +02:00
|
|
|
| expr CONTAINS expr { $$ = makeContainsExpr($1, $3) }
|
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
|
2017-07-20 15:12:31 +02:00
|
|
|
$$ = func(ctx Context) evaluator.Value {
|
|
|
|
return evaluator.ValueOf(fa(ctx).Test() && fb(ctx).Test())
|
2017-06-27 23:54:24 +02:00
|
|
|
}
|
|
|
|
}
|
2017-06-28 17:24:24 +02:00
|
|
|
| cond OR rel {
|
2017-06-27 23:54:24 +02:00
|
|
|
fa, fb := $1, $3
|
2017-07-20 15:12:31 +02:00
|
|
|
$$ = func(ctx Context) evaluator.Value {
|
|
|
|
return evaluator.ValueOf(fa(ctx).Test() || fb(ctx).Test())
|
2017-06-27 23:54:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
;
|