Browse Source

Support {% for .. in .. reversed %} syntax

Evan Miller 12 years ago
parent
commit
898fbae78c
4 changed files with 21 additions and 9 deletions
  1. 13 8
      src/erlydtl_compiler.erl
  2. 3 1
      src/erlydtl_parser.yrl
  3. 2 0
      src/erlydtl_scanner.erl
  4. 3 0
      tests/src/erlydtl_unittests.erl

+ 13 - 8
src/erlydtl_compiler.erl

@@ -570,12 +570,12 @@ body_ast(DjangoParseTree, Context, TreeWalker) ->
                 filter_tag_ast(FilterList, Contents, Context, TreeWalkerAcc);
             ({'firstof', Vars}, TreeWalkerAcc) ->
                 firstof_ast(Vars, Context, TreeWalkerAcc);
-            ({'for', {'in', IteratorList, Variable}, Contents}, TreeWalkerAcc) ->
+            ({'for', {'in', IteratorList, Variable, Reversed}, Contents}, TreeWalkerAcc) ->
                 {EmptyAstInfo, TreeWalker1} = empty_ast(TreeWalkerAcc),
-                for_loop_ast(IteratorList, Variable, Contents, EmptyAstInfo, Context, TreeWalker1);
-            ({'for', {'in', IteratorList, Variable}, Contents, EmptyPartContents}, TreeWalkerAcc) ->
+                for_loop_ast(IteratorList, Variable, Reversed, Contents, EmptyAstInfo, Context, TreeWalker1);
+            ({'for', {'in', IteratorList, Variable, Reversed}, Contents, EmptyPartContents}, TreeWalkerAcc) ->
                 {EmptyAstInfo, TreeWalker1} = body_ast(EmptyPartContents, Context, TreeWalkerAcc),
-                for_loop_ast(IteratorList, Variable, Contents, EmptyAstInfo, Context, TreeWalker1);
+                for_loop_ast(IteratorList, Variable, Reversed, Contents, EmptyAstInfo, Context, TreeWalker1);
             ({'if', Expression, Contents, Elif}, TreeWalkerAcc) ->
                 {IfAstInfo, TreeWalker1} = body_ast(Contents, Context, TreeWalkerAcc),
                 {ElifAstInfo, TreeWalker2} = body_ast(Elif, Context, TreeWalker1),
@@ -1114,7 +1114,7 @@ regroup_filter({variable,{identifier,_,Var}},Acc) ->
     erl_syntax:list([erl_syntax:atom(Var)|Acc]).
 
 
-for_loop_ast(IteratorList, LoopValue, Contents, {EmptyContentsAst, EmptyContentsInfo}, Context, TreeWalker) ->
+for_loop_ast(IteratorList, LoopValue, IsReversed, Contents, {EmptyContentsAst, EmptyContentsInfo}, Context, TreeWalker) ->
     Vars = lists:map(fun({identifier, _, Iterator}) -> 
                 erl_syntax:variable(lists:concat(["Var_", Iterator])) 
             end, IteratorList),
@@ -1129,11 +1129,16 @@ for_loop_ast(IteratorList, LoopValue, Contents, {EmptyContentsAst, EmptyContents
 
     {{LoopValueAst, LoopValueInfo}, TreeWalker2} = value_ast(LoopValue, false, true, Context, TreeWalker1),
 
+    LoopValueAst0 = case IsReversed of
+        true -> erl_syntax:application(erl_syntax:atom(lists), erl_syntax:atom(reverse), [LoopValueAst]);
+        false -> LoopValueAst
+    end,
+
     CounterVars0 = case resolve_scoped_variable_ast('forloop', Context) of
         undefined ->
-            erl_syntax:application(erl_syntax:atom(erlydtl_runtime), erl_syntax:atom(init_counter_stats), [LoopValueAst]);
+            erl_syntax:application(erl_syntax:atom(erlydtl_runtime), erl_syntax:atom(init_counter_stats), [LoopValueAst0]);
         Value ->
-            erl_syntax:application(erl_syntax:atom(erlydtl_runtime), erl_syntax:atom(init_counter_stats), [LoopValueAst, Value])
+            erl_syntax:application(erl_syntax:atom(erlydtl_runtime), erl_syntax:atom(init_counter_stats), [LoopValueAst0, Value])
     end,
     {{erl_syntax:case_expr(
                 erl_syntax:application(
@@ -1145,7 +1150,7 @@ for_loop_ast(IteratorList, LoopValue, Contents, {EmptyContentsAst, EmptyContents
                                         _ -> [erl_syntax:list(Vars), erl_syntax:variable("Counters")] end, none, 
                                     [erl_syntax:tuple([InnerAst, CounterAst])])
                             ]),
-                        CounterVars0, LoopValueAst]),
+                        CounterVars0, LoopValueAst0]),
                 [erl_syntax:clause(
                         [erl_syntax:tuple([erl_syntax:underscore(), 
                                     erl_syntax:list([erl_syntax:tuple([erl_syntax:atom(counter), erl_syntax:integer(1)])], 

+ 3 - 1
src/erlydtl_parser.yrl

@@ -176,6 +176,7 @@ Terminals
     open_var
     parsed_keyword
     regroup_keyword
+    reversed_keyword
     spaceless_keyword
     ssi_keyword
     string_literal
@@ -295,7 +296,8 @@ ForBlock -> ForBraced Elements EmptyBraced Elements EndForBraced : {for, '$1', '
 EmptyBraced -> open_tag empty_keyword close_tag.
 ForBraced -> open_tag for_keyword ForExpression close_tag : '$3'.
 EndForBraced -> open_tag endfor_keyword close_tag.
-ForExpression -> ForGroup in_keyword Variable : {'in', '$1', '$3'}.
+ForExpression -> ForGroup in_keyword Variable : {'in', '$1', '$3', false}.
+ForExpression -> ForGroup in_keyword Variable reversed_keyword : {'in', '$1', '$3', true}.
 ForGroup -> identifier : ['$1'].
 ForGroup -> ForGroup ',' identifier : '$1' ++ ['$3'].
 

+ 2 - 0
src/erlydtl_scanner.erl

@@ -335,6 +335,8 @@ mark_keywords([{identifier, Pos, "parsed" = String}, {close_tag, _, _} = CloseTa
     mark_keywords(T, lists:reverse([{parsed_keyword, Pos, String}, CloseTag], Acc));
 mark_keywords([{identifier, Pos, "noop" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
     mark_keywords(T, lists:reverse([{noop_keyword, Pos, String}, CloseTag], Acc));
+mark_keywords([{identifier, Pos, "reversed" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
+    mark_keywords(T, lists:reverse([{reversed_keyword, Pos, String}, CloseTag], Acc));
 mark_keywords([{identifier, Pos, "openblock" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
     mark_keywords(T, lists:reverse([{openblock_keyword, Pos, String}, CloseTag], Acc));
 mark_keywords([{identifier, Pos, "closeblock" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->

+ 3 - 0
tests/src/erlydtl_unittests.erl

@@ -213,6 +213,9 @@ tests() ->
                 {"Simple loop",
                     <<"{% for x in list %}{{ x }}{% endfor %}">>, [{'list', ["1", "2", "3"]}],
                     <<"123">>},
+                {"Reversed loop",
+                    <<"{% for x in list reversed %}{{ x }}{% endfor %}">>, [{'list', ["1", "2", "3"]}],
+                    <<"321">>},
                 {"Expand list",
                     <<"{% for x, y in list %}{{ x }},{{ y }}\n{% endfor %}">>, [{'list', [["X", "1"], ["X", "2"]]}],
                     <<"X,1\nX,2\n">>},