From 898fbae78cd199795653b0d741073c7aa8d976d9 Mon Sep 17 00:00:00 2001 From: Evan Miller Date: Thu, 4 Apr 2013 07:24:46 -0500 Subject: [PATCH] Support {% for .. in .. reversed %} syntax --- src/erlydtl_compiler.erl | 21 +++++++++++++-------- src/erlydtl_parser.yrl | 4 +++- src/erlydtl_scanner.erl | 2 ++ tests/src/erlydtl_unittests.erl | 3 +++ 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/erlydtl_compiler.erl b/src/erlydtl_compiler.erl index 8006f64..bef0c40 100755 --- a/src/erlydtl_compiler.erl +++ b/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)])], diff --git a/src/erlydtl_parser.yrl b/src/erlydtl_parser.yrl index bdce088..bd146e0 100644 --- a/src/erlydtl_parser.yrl +++ b/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']. diff --git a/src/erlydtl_scanner.erl b/src/erlydtl_scanner.erl index 64d6c76..7751a6a 100644 --- a/src/erlydtl_scanner.erl +++ b/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) -> diff --git a/tests/src/erlydtl_unittests.erl b/tests/src/erlydtl_unittests.erl index 7ba1a5f..8b36727 100644 --- a/tests/src/erlydtl_unittests.erl +++ b/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">>},