From babfc3e527155f059c389e927c099674a5ddc7ac Mon Sep 17 00:00:00 2001 From: Oliver Steele Date: Tue, 27 Jun 2017 13:18:01 -0400 Subject: [PATCH] Implement loop tag --- README.md | 2 +- chunks/context.go | 20 +++ expressions/context.go | 5 + expressions/expressions.y | 18 ++- expressions/scanner.go | 282 ++++++++++++++++++++------------------ expressions/scanner.rl | 6 +- expressions/y.go | 112 +++++++++------ tags/loop.go | 43 ++++++ tags/tags.go | 2 +- tags/tags_test.go | 2 +- 10 files changed, 306 insertions(+), 186 deletions(-) create mode 100644 tags/loop.go diff --git a/README.md b/README.md index 4a400b3..02b1ca4 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ - [x] unless - [ ] case/when - [ ] Iteration - - [ ] for + - [x] for - [ ] limit, offset, range, reversed - [ ] break, continue - [ ] loop variables diff --git a/chunks/context.go b/chunks/context.go index 5f717be..2833982 100644 --- a/chunks/context.go +++ b/chunks/context.go @@ -21,6 +21,26 @@ func NewContext(scope map[string]interface{}) Context { return Context{vars} } +func (c *Context) Set(name string, value interface{}) { + c.vars[name] = value +} + +// Evaluate evaluates an expression within the template context. +func (c *Context) Evaluate(expr expressions.Expression) (out interface{}, err error) { + defer func() { + if r := recover(); r != nil { + switch e := r.(type) { + case expressions.InterpreterError: + err = e + default: + // fmt.Println(string(debug.Stack())) + panic(e) + } + } + }() + return expr.Evaluate(expressions.NewContext(c.vars)) +} + // EvaluateExpr evaluates an expression within the template context. func (c *Context) EvaluateExpr(source string) (out interface{}, err error) { defer func() { diff --git a/expressions/context.go b/expressions/context.go index df62fe0..1258ea4 100644 --- a/expressions/context.go +++ b/expressions/context.go @@ -29,3 +29,8 @@ func (c *context) Set(name string, value interface{}) { // } c.vars[name] = value } + +type Loop struct { + Name string + Expr interface{} +} diff --git a/expressions/expressions.y b/expressions/expressions.y index 408de9e..61cfdf1 100644 --- a/expressions/expressions.y +++ b/expressions/expressions.y @@ -18,11 +18,11 @@ func init() { val interface{} f func(Context) interface{} } -%type expr rel expr1 +%type expr rel expr1 loop %token LITERAL %token IDENTIFIER KEYWORD RELATION -%token ASSIGN -%token EQ +%token ASSIGN LOOP +%token EQ FOR IN %left '.' '|' %left '<' '>' %% @@ -34,7 +34,17 @@ start: ctx.Set(name, expr(ctx)) return nil } -}; +} +| LOOP loop { yylex.(*lexer).val = $2 } +; + +loop: IDENTIFIER IN expr1 ';' { + name, expr := $1, $3 + $$ = func(ctx Context) interface{} { + return &Loop{name, expr(ctx)} + } +} +; expr: LITERAL { val := $1; $$ = func(_ Context) interface{} { return val } } diff --git a/expressions/scanner.go b/expressions/scanner.go index 9266adf..d56aaf5 100644 --- a/expressions/scanner.go +++ b/expressions/scanner.go @@ -9,151 +9,158 @@ import "strconv" //line scanner.go:11 var _expression_actions []byte = []byte{ - 0, 1, 0, 1, 1, 1, 2, 1, 9, - 1, 10, 1, 11, 1, 12, 1, 13, - 1, 14, 1, 15, 1, 16, 1, 17, - 1, 18, 1, 19, 1, 20, 1, 21, + 0, 1, 0, 1, 1, 1, 2, 1, 10, + 1, 11, 1, 12, 1, 13, 1, 14, + 1, 15, 1, 16, 1, 17, 1, 18, + 1, 19, 1, 20, 1, 21, 1, 22, 2, 2, 3, 2, 2, 4, 2, 2, 5, 2, 2, 6, 2, 2, 7, 2, - 2, 8, + 2, 8, 2, 2, 9, } var _expression_key_offsets []byte = []byte{ 0, 1, 2, 3, 4, 5, 6, 7, - 9, 36, 39, 40, 41, 42, 43, 46, - 48, 51, 52, 60, 69, 78, 87, 96, - 105, 114, 123, 132, 141, 150, 159, 168, - 177, 186, 195, + 8, 9, 10, 12, 37, 40, 41, 42, + 44, 45, 48, 50, 53, 54, 62, 71, + 80, 89, 98, 107, 116, 125, 134, 143, + 153, 162, 171, 180, 189, 198, 207, 216, } var _expression_trans_keys []byte = []byte{ - 34, 115, 115, 105, 103, 110, 39, 48, - 57, 32, 33, 34, 37, 39, 45, 46, - 59, 61, 91, 93, 95, 97, 99, 102, - 111, 116, 9, 13, 48, 57, 60, 62, - 65, 90, 98, 122, 32, 9, 13, 61, - 34, 97, 39, 46, 48, 57, 48, 57, - 46, 48, 57, 61, 58, 95, 48, 57, - 65, 90, 97, 122, 58, 95, 110, 48, - 57, 65, 90, 97, 122, 58, 95, 100, + 34, 115, 115, 105, 103, 110, 111, 111, + 112, 39, 48, 57, 32, 33, 34, 37, + 39, 45, 46, 61, 95, 97, 99, 102, + 105, 111, 116, 9, 13, 48, 57, 60, + 62, 65, 90, 98, 122, 32, 9, 13, + 61, 34, 97, 108, 39, 46, 48, 57, + 48, 57, 46, 48, 57, 61, 58, 95, 48, 57, 65, 90, 97, 122, 58, 95, - 111, 48, 57, 65, 90, 97, 122, 58, - 95, 110, 48, 57, 65, 90, 97, 122, - 58, 95, 116, 48, 57, 65, 90, 97, - 122, 58, 95, 97, 48, 57, 65, 90, - 98, 122, 58, 95, 105, 48, 57, 65, - 90, 97, 122, 58, 95, 110, 48, 57, - 65, 90, 97, 122, 58, 95, 115, 48, - 57, 65, 90, 97, 122, 58, 95, 97, - 48, 57, 65, 90, 98, 122, 58, 95, - 108, 48, 57, 65, 90, 97, 122, 58, - 95, 115, 48, 57, 65, 90, 97, 122, - 58, 95, 101, 48, 57, 65, 90, 97, - 122, 58, 95, 114, 48, 57, 65, 90, - 97, 122, 58, 95, 114, 48, 57, 65, - 90, 97, 122, 58, 95, 117, 48, 57, - 65, 90, 97, 122, + 110, 48, 57, 65, 90, 97, 122, 58, + 95, 100, 48, 57, 65, 90, 97, 122, + 58, 95, 111, 48, 57, 65, 90, 97, + 122, 58, 95, 110, 48, 57, 65, 90, + 97, 122, 58, 95, 116, 48, 57, 65, + 90, 97, 122, 58, 95, 97, 48, 57, + 65, 90, 98, 122, 58, 95, 105, 48, + 57, 65, 90, 97, 122, 58, 95, 110, + 48, 57, 65, 90, 97, 122, 58, 95, + 115, 48, 57, 65, 90, 97, 122, 58, + 95, 97, 111, 48, 57, 65, 90, 98, + 122, 58, 95, 108, 48, 57, 65, 90, + 97, 122, 58, 95, 115, 48, 57, 65, + 90, 97, 122, 58, 95, 101, 48, 57, + 65, 90, 97, 122, 58, 95, 114, 48, + 57, 65, 90, 97, 122, 58, 95, 110, + 48, 57, 65, 90, 97, 122, 58, 95, + 114, 48, 57, 65, 90, 97, 122, 58, + 95, 114, 48, 57, 65, 90, 97, 122, + 58, 95, 117, 48, 57, 65, 90, 97, + 122, } var _expression_single_lengths []byte = []byte{ - 1, 1, 1, 1, 1, 1, 1, 0, - 17, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 2, 3, 3, 3, 3, 3, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 15, 1, 1, 1, 2, + 1, 1, 0, 1, 1, 2, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, } var _expression_range_lengths []byte = []byte{ - 0, 0, 0, 0, 0, 0, 0, 1, - 5, 1, 0, 0, 0, 0, 1, 1, - 1, 0, 3, 3, 3, 3, 3, 3, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 5, 1, 0, 0, 0, + 0, 1, 1, 1, 0, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, } var _expression_index_offsets []byte = []byte{ 0, 2, 4, 6, 8, 10, 12, 14, - 16, 39, 42, 44, 46, 48, 50, 53, - 55, 58, 60, 66, 73, 80, 87, 94, - 101, 108, 115, 122, 129, 136, 143, 150, - 157, 164, 171, + 16, 18, 20, 22, 43, 46, 48, 50, + 53, 55, 58, 60, 63, 65, 71, 78, + 85, 92, 99, 106, 113, 120, 127, 134, + 142, 149, 156, 163, 170, 177, 184, 191, } var _expression_indicies []byte = []byte{ 2, 1, 3, 0, 4, 0, 5, 0, - 6, 0, 7, 0, 2, 8, 9, 0, - 11, 12, 13, 14, 15, 16, 17, 19, - 21, 19, 19, 22, 23, 24, 25, 26, - 27, 11, 18, 20, 22, 22, 10, 11, - 11, 28, 30, 29, 2, 1, 32, 31, - 2, 8, 33, 18, 31, 9, 29, 9, - 18, 34, 35, 31, 36, 22, 22, 22, - 22, 29, 36, 22, 38, 22, 22, 22, - 37, 36, 22, 39, 22, 22, 22, 37, - 36, 22, 40, 22, 22, 22, 37, 36, - 22, 41, 22, 22, 22, 37, 36, 22, - 42, 22, 22, 22, 37, 36, 22, 43, - 22, 22, 22, 37, 36, 22, 44, 22, - 22, 22, 37, 36, 22, 45, 22, 22, - 22, 37, 36, 22, 39, 22, 22, 22, - 37, 36, 22, 46, 22, 22, 22, 37, - 36, 22, 47, 22, 22, 22, 37, 36, - 22, 48, 22, 22, 22, 37, 36, 22, - 49, 22, 22, 22, 37, 36, 22, 39, - 22, 22, 22, 37, 36, 22, 50, 22, - 22, 22, 37, 36, 22, 48, 22, 22, - 22, 37, + 6, 0, 7, 0, 8, 0, 9, 0, + 10, 0, 2, 11, 12, 0, 14, 15, + 16, 17, 18, 19, 20, 22, 23, 24, + 25, 26, 27, 28, 29, 14, 21, 15, + 23, 23, 13, 14, 14, 30, 32, 31, + 2, 1, 33, 34, 31, 2, 11, 35, + 21, 31, 12, 36, 12, 21, 37, 38, + 31, 39, 23, 23, 23, 23, 36, 39, + 23, 41, 23, 23, 23, 40, 39, 23, + 42, 23, 23, 23, 40, 39, 23, 43, + 23, 23, 23, 40, 39, 23, 44, 23, + 23, 23, 40, 39, 23, 45, 23, 23, + 23, 40, 39, 23, 46, 23, 23, 23, + 40, 39, 23, 47, 23, 23, 23, 40, + 39, 23, 48, 23, 23, 23, 40, 39, + 23, 42, 23, 23, 23, 40, 39, 23, + 49, 50, 23, 23, 23, 40, 39, 23, + 51, 23, 23, 23, 40, 39, 23, 52, + 23, 23, 23, 40, 39, 23, 53, 23, + 23, 23, 40, 39, 23, 54, 23, 23, + 23, 40, 39, 23, 55, 23, 23, 23, + 40, 39, 23, 42, 23, 23, 23, 40, + 39, 23, 56, 23, 23, 23, 40, 39, + 23, 52, 23, 23, 23, 40, } var _expression_trans_targs []byte = []byte{ - 8, 0, 8, 2, 3, 4, 5, 8, - 6, 15, 8, 9, 10, 11, 12, 13, - 14, 15, 16, 8, 10, 17, 18, 19, - 21, 28, 32, 33, 8, 8, 8, 8, - 1, 7, 8, 8, 8, 8, 20, 18, - 22, 23, 24, 25, 26, 27, 29, 30, - 31, 18, 34, + 11, 0, 11, 2, 3, 4, 5, 11, + 7, 8, 11, 9, 18, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, + 22, 24, 31, 36, 37, 38, 11, 11, + 11, 1, 6, 10, 11, 11, 11, 11, + 11, 23, 21, 25, 26, 27, 28, 29, + 30, 32, 35, 33, 34, 21, 21, 21, + 39, } var _expression_trans_actions []byte = []byte{ - 29, 0, 9, 0, 0, 0, 0, 7, - 0, 33, 19, 0, 48, 5, 5, 5, - 5, 39, 0, 11, 39, 0, 45, 0, - 0, 0, 0, 0, 25, 31, 15, 27, - 0, 0, 21, 13, 17, 23, 0, 42, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 36, 0, + 29, 0, 11, 0, 0, 0, 0, 7, + 0, 0, 9, 0, 33, 19, 0, 0, + 5, 5, 5, 5, 51, 0, 0, 48, + 0, 0, 0, 0, 0, 0, 25, 27, + 15, 0, 0, 0, 31, 21, 13, 17, + 23, 0, 39, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 42, 45, + 0, } var _expression_to_state_actions []byte = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, } var _expression_from_state_actions []byte = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, } var _expression_eof_trans []byte = []byte{ 1, 1, 1, 1, 1, 1, 1, 1, - 0, 29, 30, 32, 32, 32, 32, 30, - 35, 32, 30, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, + 1, 1, 1, 0, 31, 32, 32, 32, + 32, 32, 37, 38, 32, 37, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, } -const expression_start int = 8 -const expression_first_final int = 8 +const expression_start int = 11 +const expression_first_final int = 11 const expression_error int = -1 -const expression_en_main int = 8 +const expression_en_main int = 11 //line scanner.rl:13 @@ -176,7 +183,7 @@ func newLexer(data []byte) *lexer { pe: len(data), } -//line scanner.go:180 +//line scanner.go:187 { lex.cs = expression_start lex.ts = 0 @@ -193,7 +200,7 @@ func (lex *lexer) Lex(out *yySymType) int { tok := 0 -//line scanner.go:197 +//line scanner.go:204 { var _klen int var _trans int @@ -213,7 +220,7 @@ _resume: //line NONE:1 lex.ts = ( lex.p) -//line scanner.go:217 +//line scanner.go:224 } } @@ -289,28 +296,36 @@ _eof_trans: case 3: //line scanner.rl:59 - lex.act = 3; + lex.act = 4; case 4: //line scanner.rl:40 - lex.act = 5; - case 5: -//line scanner.rl:87 lex.act = 6; - case 6: + case 5: //line scanner.rl:74 lex.act = 9; + case 6: +//line scanner.rl:91 + lex.act = 10; case 7: -//line scanner.rl:45 +//line scanner.rl:92 lex.act = 11; case 8: -//line scanner.rl:94 +//line scanner.rl:45 lex.act = 13; case 9: +//line scanner.rl:96 + lex.act = 15; + case 10: //line scanner.rl:82 lex.te = ( lex.p)+1 { tok = ASSIGN; ( lex.p)++; goto _out } - case 10: + case 11: +//line scanner.rl:83 + lex.te = ( lex.p)+1 +{ tok = LOOP; ( lex.p)++; goto _out + } + case 12: //line scanner.rl:68 lex.te = ( lex.p)+1 { @@ -320,32 +335,27 @@ _eof_trans: ( lex.p)++; goto _out } - case 11: -//line scanner.rl:87 - lex.te = ( lex.p)+1 -{ tok = int(lex.data[lex.ts]); ( lex.p)++; goto _out - } - case 12: + case 13: //line scanner.rl:88 lex.te = ( lex.p)+1 { tok = EQ; ( lex.p)++; goto _out } - case 13: + case 14: //line scanner.rl:74 lex.te = ( lex.p)+1 { tok = RELATION; out.name = lex.token(); ( lex.p)++; goto _out } - case 14: -//line scanner.rl:91 + case 15: +//line scanner.rl:93 lex.te = ( lex.p)+1 { tok = KEYWORD; out.name = string(lex.data[lex.ts:lex.te-1]); ( lex.p)++; goto _out } - case 15: -//line scanner.rl:94 + case 16: +//line scanner.rl:96 lex.te = ( lex.p)+1 { tok = int(lex.data[lex.ts]); ( lex.p)++; goto _out } - case 16: + case 17: //line scanner.rl:50 lex.te = ( lex.p) ( lex.p)-- @@ -359,7 +369,7 @@ _eof_trans: ( lex.p)++; goto _out } - case 17: + case 18: //line scanner.rl:45 lex.te = ( lex.p) ( lex.p)-- @@ -369,26 +379,26 @@ _eof_trans: ( lex.p)++; goto _out } - case 18: -//line scanner.rl:93 + case 19: +//line scanner.rl:95 lex.te = ( lex.p) ( lex.p)-- - case 19: -//line scanner.rl:94 + case 20: +//line scanner.rl:96 lex.te = ( lex.p) ( lex.p)-- { tok = int(lex.data[lex.ts]); ( lex.p)++; goto _out } - case 20: -//line scanner.rl:94 + case 21: +//line scanner.rl:96 ( lex.p) = ( lex.te) - 1 { tok = int(lex.data[lex.ts]); ( lex.p)++; goto _out } - case 21: + case 22: //line NONE:1 switch lex.act { - case 3: + case 4: {( lex.p) = ( lex.te) - 1 tok = LITERAL @@ -400,7 +410,7 @@ _eof_trans: ( lex.p)++; goto _out } - case 5: + case 6: {( lex.p) = ( lex.te) - 1 tok = LITERAL @@ -408,15 +418,19 @@ _eof_trans: ( lex.p)++; goto _out } - case 6: - {( lex.p) = ( lex.te) - 1 - tok = int(lex.data[lex.ts]); ( lex.p)++; goto _out - } case 9: {( lex.p) = ( lex.te) - 1 tok = RELATION; out.name = lex.token(); ( lex.p)++; goto _out + } + case 10: + {( lex.p) = ( lex.te) - 1 + tok = IN; ( lex.p)++; goto _out } case 11: + {( lex.p) = ( lex.te) - 1 + tok = IN; ( lex.p)++; goto _out + } + case 13: {( lex.p) = ( lex.te) - 1 tok = IDENTIFIER @@ -424,13 +438,13 @@ _eof_trans: ( lex.p)++; goto _out } - case 13: + case 15: {( lex.p) = ( lex.te) - 1 tok = int(lex.data[lex.ts]); ( lex.p)++; goto _out } } -//line scanner.go:434 +//line scanner.go:448 } } @@ -444,7 +458,7 @@ _again: //line NONE:1 lex.ts = 0 -//line scanner.go:448 +//line scanner.go:462 } } @@ -463,7 +477,7 @@ _again: _out: {} } -//line scanner.rl:98 +//line scanner.rl:100 return tok diff --git a/expressions/scanner.rl b/expressions/scanner.rl index fd00235..9d04b2d 100644 --- a/expressions/scanner.rl +++ b/expressions/scanner.rl @@ -80,14 +80,16 @@ func (lex *lexer) Lex(out *yySymType) int { main := |* "%assign" => { tok = ASSIGN; fbreak; }; + "%loop" => { tok = LOOP; fbreak; }; int => Int; float => Float; string => String; ("true" | "false") => Bool; - [.;<>] | '[' | ']' => { tok = int(lex.data[lex.ts]); fbreak; }; "==" => { tok = EQ; fbreak; }; - ("!=" | ">" | ">" | ">=" | "<=") => Relation; + ("!=" | ">=" | "<=") => Relation; ("and" | "or" | "contains") => Relation; + "for" => { tok = IN; fbreak; }; + "in" => { tok = IN; fbreak; }; ident ':' => { tok = KEYWORD; out.name = string(lex.data[lex.ts:lex.te-1]); fbreak; }; ident => Ident; space+; diff --git a/expressions/y.go b/expressions/y.go index 99f0ce0..43b057a 100644 --- a/expressions/y.go +++ b/expressions/y.go @@ -29,7 +29,10 @@ const IDENTIFIER = 57347 const KEYWORD = 57348 const RELATION = 57349 const ASSIGN = 57350 -const EQ = 57351 +const LOOP = 57351 +const EQ = 57352 +const FOR = 57353 +const IN = 57354 var yyToknames = [...]string{ "$end", @@ -40,7 +43,10 @@ var yyToknames = [...]string{ "KEYWORD", "RELATION", "ASSIGN", + "LOOP", "EQ", + "FOR", + "IN", "'.'", "'|'", "'<'", @@ -65,46 +71,50 @@ var yyExca = [...]int{ const yyPrivate = 57344 -const yyLast = 35 +const yyLast = 42 var yyAct = [...]int{ - 5, 13, 11, 11, 14, 15, 11, 4, 12, 12, - 27, 8, 12, 20, 21, 22, 23, 25, 10, 26, - 16, 28, 10, 19, 24, 6, 7, 17, 18, 3, - 6, 7, 9, 1, 2, + 29, 6, 16, 14, 5, 14, 14, 17, 18, 15, + 32, 15, 15, 19, 9, 13, 24, 25, 26, 27, + 20, 21, 22, 31, 28, 30, 13, 13, 23, 34, + 33, 7, 8, 7, 8, 3, 4, 12, 10, 1, + 11, 2, } var yyPact = [...]int{ - 21, -1000, -3, 27, 11, -8, -1000, -1000, -1000, 5, - 22, 18, 26, 26, 26, 26, 26, -1000, 26, -1000, - -7, -4, -4, -4, 7, -4, -4, -1000, -1000, + 27, -1000, -3, 33, 32, 1, -8, -1000, -1000, -1000, + -5, -1000, 8, 16, 23, 29, 29, 29, 29, 29, + 29, -1000, 29, -1000, -10, -7, -7, -7, 13, -7, + 12, -7, -1000, -1000, -1000, } var yyPgo = [...]int{ - 0, 0, 34, 7, 33, + 0, 0, 41, 4, 40, 39, } var yyR1 = [...]int{ - 0, 4, 4, 1, 1, 1, 1, 3, 3, 3, - 2, 2, 2, 2, + 0, 5, 5, 5, 4, 1, 1, 1, 1, 3, + 3, 3, 2, 2, 2, 2, } var yyR2 = [...]int{ - 0, 2, 5, 1, 1, 3, 4, 1, 3, 4, - 1, 3, 3, 3, + 0, 2, 5, 2, 4, 1, 1, 3, 4, 1, + 3, 4, 1, 3, 3, 3, } var yyChk = [...]int{ - -1000, -4, -2, 8, -3, -1, 4, 5, 14, 5, - 11, 10, 16, 9, 12, 13, 15, 5, 6, 5, - -1, -1, -1, -1, -3, -1, -1, 17, 14, + -1000, -5, -2, 8, 9, -3, -1, 4, 5, 17, + 5, -4, 5, 14, 13, 19, 10, 15, 16, 18, + 12, 5, 6, 5, -1, -1, -1, -1, -3, -1, + -3, -1, 20, 17, 17, } var yyDef = [...]int{ - 0, -2, 0, 0, 10, 7, 3, 4, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 8, 0, 5, - 0, 11, 12, 13, 0, 7, 9, 6, 2, + 0, -2, 0, 0, 0, 12, 9, 5, 6, 1, + 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 0, 7, 0, 13, 14, 15, 0, 9, + 0, 11, 8, 2, 4, } var yyTok1 = [...]int{ @@ -112,19 +122,20 @@ var yyTok1 = [...]int{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 10, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 14, - 12, 15, 13, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 13, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 17, + 15, 18, 16, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 16, 3, 17, 3, 3, 3, 3, 3, 3, + 3, 19, 3, 20, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 11, + 3, 3, 3, 3, 14, } var yyTok2 = [...]int{ - 2, 3, 4, 5, 6, 7, 8, 9, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, } var yyTok3 = [...]int{ 0, @@ -484,22 +495,37 @@ yydefault: } } case 3: + yyDollar = yyS[yypt-2 : yypt+1] + //line expressions.y:38 + { + yylex.(*lexer).val = yyDollar[2].f + } + case 4: + yyDollar = yyS[yypt-4 : yypt+1] + //line expressions.y:41 + { + name, expr := yyDollar[1].name, yyDollar[3].f + yyVAL.f = func(ctx Context) interface{} { + return &Loop{name, expr(ctx)} + } + } + case 5: yyDollar = yyS[yypt-1 : yypt+1] - //line expressions.y:40 + //line expressions.y:50 { val := yyDollar[1].val yyVAL.f = func(_ Context) interface{} { return val } } - case 4: + case 6: yyDollar = yyS[yypt-1 : yypt+1] - //line expressions.y:41 + //line expressions.y:51 { name := yyDollar[1].name yyVAL.f = func(ctx Context) interface{} { return ctx.Get(name) } } - case 5: + case 7: yyDollar = yyS[yypt-3 : yypt+1] - //line expressions.y:42 + //line expressions.y:52 { e, attr := yyDollar[1].f, yyDollar[3].name yyVAL.f = func(ctx Context) interface{} { @@ -515,9 +541,9 @@ yydefault: return nil } } - case 6: + case 8: yyDollar = yyS[yypt-4 : yypt+1] - //line expressions.y:57 + //line expressions.y:67 { e, i := yyDollar[1].f, yyDollar[3].f yyVAL.f = func(ctx Context) interface{} { @@ -536,21 +562,21 @@ yydefault: return nil } } - case 8: + case 10: yyDollar = yyS[yypt-3 : yypt+1] - //line expressions.y:78 + //line expressions.y:88 { yyVAL.f = makeFilter(yyDollar[1].f, yyDollar[3].name, nil) } - case 9: + case 11: yyDollar = yyS[yypt-4 : yypt+1] - //line expressions.y:79 + //line expressions.y:89 { yyVAL.f = makeFilter(yyDollar[1].f, yyDollar[3].name, yyDollar[4].f) } - case 11: + case 13: yyDollar = yyS[yypt-3 : yypt+1] - //line expressions.y:84 + //line expressions.y:94 { fa, fb := yyDollar[1].f, yyDollar[3].f yyVAL.f = func(ctx Context) interface{} { @@ -558,9 +584,9 @@ yydefault: return generics.Equal(a, b) } } - case 12: + case 14: yyDollar = yyS[yypt-3 : yypt+1] - //line expressions.y:91 + //line expressions.y:101 { fa, fb := yyDollar[1].f, yyDollar[3].f yyVAL.f = func(ctx Context) interface{} { @@ -568,9 +594,9 @@ yydefault: return generics.Less(a, b) } } - case 13: + case 15: yyDollar = yyS[yypt-3 : yypt+1] - //line expressions.y:98 + //line expressions.y:108 { fa, fb := yyDollar[1].f, yyDollar[3].f yyVAL.f = func(ctx Context) interface{} { diff --git a/tags/loop.go b/tags/loop.go new file mode 100644 index 0000000..4e47d7c --- /dev/null +++ b/tags/loop.go @@ -0,0 +1,43 @@ +package tags + +import ( + "io" + "reflect" + + "github.com/osteele/liquid/chunks" + "github.com/osteele/liquid/expressions" +) + +func parseLoop(source string) (expressions.Expression, error) { + expr, err := expressions.Parse("%loop " + source) + if err != nil { + return nil, err + } + return expr, nil +} + +func loopTag(node chunks.ASTControlTag) func(io.Writer, chunks.Context) error { + expr, err := parseLoop(node.Args) + if err != nil { + return func(io.Writer, chunks.Context) error { return err } + } + return func(w io.Writer, ctx chunks.Context) error { + val, err := ctx.Evaluate(expr) + if err != nil { + return err + } + loop := val.(*expressions.Loop) + rt := reflect.ValueOf(loop.Expr) + if rt.Kind() != reflect.Array && rt.Kind() != reflect.Slice { + return nil + } + for i := 0; i < rt.Len(); i++ { + ctx.Set(loop.Name, rt.Index(i).Interface()) + err := ctx.RenderASTSequence(w, node.Body) + if err != nil { + return err + } + } + return nil + } +} diff --git a/tags/tags.go b/tags/tags.go index a1f574a..e50451c 100644 --- a/tags/tags.go +++ b/tags/tags.go @@ -14,7 +14,7 @@ func DefineStandardTags() { chunks.DefineControlTag("if").Branch("else").Branch("elsif").Action(ifTagAction(true)) chunks.DefineControlTag("unless").SameSyntaxAs("if").Action(ifTagAction(false)) chunks.DefineControlTag("case").Branch("when") - chunks.DefineControlTag("for").Governs(loopTags) + chunks.DefineControlTag("for").Governs(loopTags).Action(loopTag) chunks.DefineControlTag("tablerow").Governs(loopTags) chunks.DefineControlTag("capture") } diff --git a/tags/tags_test.go b/tags/tags_test.go index 3bb02e0..9178128 100644 --- a/tags/tags_test.go +++ b/tags/tags_test.go @@ -41,7 +41,7 @@ var tagTests = []struct{ in, expected string }{ {"{%assign av = 1%}{{av}}", "1"}, {"{%assign av = obj.a%}{{av}}", "1"}, - // {"{%for a in ar%}{{a}} {{%endfor%}", "first second third "}, + {"{%for a in ar%}{{a}} {%endfor%}", "first second third "}, } var tagTestContext = chunks.NewContext(map[string]interface{}{