Skip to content

Commit

Permalink
Support {% for .. in .. reversed %} syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
evanmiller committed Apr 4, 2013
1 parent 604cdda commit 898fbae
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 9 deletions.
21 changes: 13 additions & 8 deletions src/erlydtl_compiler.erl
Expand Up @@ -570,12 +570,12 @@ body_ast(DjangoParseTree, Context, TreeWalker) ->
filter_tag_ast(FilterList, Contents, Context, TreeWalkerAcc); filter_tag_ast(FilterList, Contents, Context, TreeWalkerAcc);
({'firstof', Vars}, TreeWalkerAcc) -> ({'firstof', Vars}, TreeWalkerAcc) ->
firstof_ast(Vars, Context, TreeWalkerAcc); firstof_ast(Vars, Context, TreeWalkerAcc);
({'for', {'in', IteratorList, Variable}, Contents}, TreeWalkerAcc) -> ({'for', {'in', IteratorList, Variable, Reversed}, Contents}, TreeWalkerAcc) ->
{EmptyAstInfo, TreeWalker1} = empty_ast(TreeWalkerAcc), {EmptyAstInfo, TreeWalker1} = empty_ast(TreeWalkerAcc),
for_loop_ast(IteratorList, Variable, Contents, EmptyAstInfo, Context, TreeWalker1); for_loop_ast(IteratorList, Variable, Reversed, Contents, EmptyAstInfo, Context, TreeWalker1);
({'for', {'in', IteratorList, Variable}, Contents, EmptyPartContents}, TreeWalkerAcc) -> ({'for', {'in', IteratorList, Variable, Reversed}, Contents, EmptyPartContents}, TreeWalkerAcc) ->
{EmptyAstInfo, TreeWalker1} = body_ast(EmptyPartContents, Context, 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) -> ({'if', Expression, Contents, Elif}, TreeWalkerAcc) ->
{IfAstInfo, TreeWalker1} = body_ast(Contents, Context, TreeWalkerAcc), {IfAstInfo, TreeWalker1} = body_ast(Contents, Context, TreeWalkerAcc),
{ElifAstInfo, TreeWalker2} = body_ast(Elif, Context, TreeWalker1), {ElifAstInfo, TreeWalker2} = body_ast(Elif, Context, TreeWalker1),
Expand Down Expand Up @@ -1114,7 +1114,7 @@ regroup_filter({variable,{identifier,_,Var}},Acc) ->
erl_syntax:list([erl_syntax:atom(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}) -> Vars = lists:map(fun({identifier, _, Iterator}) ->
erl_syntax:variable(lists:concat(["Var_", Iterator])) erl_syntax:variable(lists:concat(["Var_", Iterator]))
end, IteratorList), end, IteratorList),
Expand All @@ -1129,11 +1129,16 @@ for_loop_ast(IteratorList, LoopValue, Contents, {EmptyContentsAst, EmptyContents


{{LoopValueAst, LoopValueInfo}, TreeWalker2} = value_ast(LoopValue, false, true, Context, TreeWalker1), {{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 CounterVars0 = case resolve_scoped_variable_ast('forloop', Context) of
undefined -> 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 -> 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, end,
{{erl_syntax:case_expr( {{erl_syntax:case_expr(
erl_syntax:application( erl_syntax:application(
Expand All @@ -1145,7 +1150,7 @@ for_loop_ast(IteratorList, LoopValue, Contents, {EmptyContentsAst, EmptyContents
_ -> [erl_syntax:list(Vars), erl_syntax:variable("Counters")] end, none, _ -> [erl_syntax:list(Vars), erl_syntax:variable("Counters")] end, none,
[erl_syntax:tuple([InnerAst, CounterAst])]) [erl_syntax:tuple([InnerAst, CounterAst])])
]), ]),
CounterVars0, LoopValueAst]), CounterVars0, LoopValueAst0]),
[erl_syntax:clause( [erl_syntax:clause(
[erl_syntax:tuple([erl_syntax:underscore(), [erl_syntax:tuple([erl_syntax:underscore(),
erl_syntax:list([erl_syntax:tuple([erl_syntax:atom(counter), erl_syntax:integer(1)])], erl_syntax:list([erl_syntax:tuple([erl_syntax:atom(counter), erl_syntax:integer(1)])],
Expand Down
4 changes: 3 additions & 1 deletion src/erlydtl_parser.yrl
Expand Up @@ -176,6 +176,7 @@ Terminals
open_var open_var
parsed_keyword parsed_keyword
regroup_keyword regroup_keyword
reversed_keyword
spaceless_keyword spaceless_keyword
ssi_keyword ssi_keyword
string_literal string_literal
Expand Down Expand Up @@ -295,7 +296,8 @@ ForBlock -> ForBraced Elements EmptyBraced Elements EndForBraced : {for, '$1', '
EmptyBraced -> open_tag empty_keyword close_tag. EmptyBraced -> open_tag empty_keyword close_tag.
ForBraced -> open_tag for_keyword ForExpression close_tag : '$3'. ForBraced -> open_tag for_keyword ForExpression close_tag : '$3'.
EndForBraced -> open_tag endfor_keyword close_tag. 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 -> identifier : ['$1'].
ForGroup -> ForGroup ',' identifier : '$1' ++ ['$3']. ForGroup -> ForGroup ',' identifier : '$1' ++ ['$3'].


Expand Down
2 changes: 2 additions & 0 deletions src/erlydtl_scanner.erl
Expand Up @@ -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(T, lists:reverse([{parsed_keyword, Pos, String}, CloseTag], Acc));
mark_keywords([{identifier, Pos, "noop" = String}, {close_tag, _, _} = CloseTag|T], 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(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([{identifier, Pos, "openblock" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
mark_keywords(T, lists:reverse([{openblock_keyword, Pos, String}, CloseTag], Acc)); mark_keywords(T, lists:reverse([{openblock_keyword, Pos, String}, CloseTag], Acc));
mark_keywords([{identifier, Pos, "closeblock" = String}, {close_tag, _, _} = CloseTag|T], Acc) -> mark_keywords([{identifier, Pos, "closeblock" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
Expand Down
3 changes: 3 additions & 0 deletions tests/src/erlydtl_unittests.erl
Expand Up @@ -213,6 +213,9 @@ tests() ->
{"Simple loop", {"Simple loop",
<<"{% for x in list %}{{ x }}{% endfor %}">>, [{'list', ["1", "2", "3"]}], <<"{% for x in list %}{{ x }}{% endfor %}">>, [{'list', ["1", "2", "3"]}],
<<"123">>}, <<"123">>},
{"Reversed loop",
<<"{% for x in list reversed %}{{ x }}{% endfor %}">>, [{'list', ["1", "2", "3"]}],
<<"321">>},
{"Expand list", {"Expand list",
<<"{% for x, y in list %}{{ x }},{{ y }}\n{% endfor %}">>, [{'list', [["X", "1"], ["X", "2"]]}], <<"{% for x, y in list %}{{ x }},{{ y }}\n{% endfor %}">>, [{'list', [["X", "1"], ["X", "2"]]}],
<<"X,1\nX,2\n">>}, <<"X,1\nX,2\n">>},
Expand Down

1 comment on commit 898fbae

@kaos
Copy link
Member

@kaos kaos commented on 898fbae Nov 28, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixes #67.

Please sign in to comment.