Skip to content
Browse files

Support for {% empty %} block in for loops.

  • Loading branch information...
1 parent e66e38a commit 25abda55ebb768ae3582ee2a5ae7d8972b3c1151 @evanmiller evanmiller committed
View
2 src/erlydtl/erlydtl.app
@@ -1,7 +1,7 @@
%% -*- mode: erlang -*-
{application, erlydtl,
[{description, "ErlyDTL implements most but not all of the Django Template Language"},
- {vsn, "0.5.3"},
+ {vsn, "0.6.0"},
{modules, [
erlydtl,
erlydtl_compiler,
View
53 src/erlydtl/erlydtl_compiler.erl
@@ -352,7 +352,11 @@ body_ast(DjangoParseTree, Context, TreeWalker) ->
{ElseAstInfo, TreeWalker2} = body_ast(ElseContents, Context, TreeWalker1),
ifelse_ast({'expr', "ne", Arg1, Arg2}, IfAstInfo, ElseAstInfo, Context, TreeWalker2);
({'for', {'in', IteratorList, Variable}, Contents}, TreeWalkerAcc) ->
- for_loop_ast(IteratorList, Variable, Contents, Context, TreeWalkerAcc);
+ {EmptyAstInfo, TreeWalker1} = empty_ast(TreeWalkerAcc),
+ for_loop_ast(IteratorList, Variable, Contents, EmptyAstInfo, Context, TreeWalker1);
+ ({'for', {'in', IteratorList, Variable}, Contents, EmptyPartContents}, TreeWalkerAcc) ->
+ {EmptyAstInfo, TreeWalker1} = body_ast(EmptyPartContents, Context, TreeWalkerAcc),
+ for_loop_ast(IteratorList, Variable, Contents, EmptyAstInfo, Context, TreeWalker1);
({'load', Names}, TreeWalkerAcc) ->
load_ast(Names, Context, TreeWalkerAcc);
({'tag', {identifier, _, Name}, Args}, TreeWalkerAcc) ->
@@ -610,11 +614,11 @@ ifelse_ast(Expression, {IfContentsAst, IfContentsInfo}, {ElseContentsAst, ElseCo
]), merge_info(ExpressionInfo, Info)}, TreeWalker1}.
-for_loop_ast(IteratorList, Variable, Contents, Context, TreeWalker) ->
+for_loop_ast(IteratorList, LoopValue, Contents, {EmptyContentsAst, EmptyContentsInfo}, Context, TreeWalker) ->
Vars = lists:map(fun({identifier, _, Iterator}) ->
erl_syntax:variable("Var_" ++ Iterator)
end, IteratorList),
- {{InnerAst, Info}, TreeWalker2} = body_ast(Contents,
+ {{InnerAst, Info}, TreeWalker1} = body_ast(Contents,
Context#dtl_context{local_scopes = [
[{'forloop', erl_syntax:variable("Counters")} | lists:map(
fun({identifier, _, Iterator}) ->
@@ -622,26 +626,37 @@ for_loop_ast(IteratorList, Variable, Contents, Context, TreeWalker) ->
end, IteratorList)] | Context#dtl_context.local_scopes]}, TreeWalker),
CounterAst = erl_syntax:application(erl_syntax:atom(erlydtl_runtime),
erl_syntax:atom(increment_counter_stats), [erl_syntax:variable("Counters")]),
- {ListAst, VarName} = resolve_variable_ast(Variable, Context),
+
+ {{LoopValueAst, LoopValueInfo}, TreeWalker2} = value_ast(LoopValue, false, Context, TreeWalker1),
+
CounterVars0 = case resolve_scoped_variable_ast("forloop", Context) of
undefined ->
- erl_syntax:application(erl_syntax:atom(erlydtl_runtime), erl_syntax:atom(init_counter_stats), [ListAst]);
+ 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), [ListAst, Value])
+ erl_syntax:application(erl_syntax:atom(erlydtl_runtime), erl_syntax:atom(init_counter_stats), [LoopValueAst, Value])
end,
- {{erl_syntax:application(
- erl_syntax:atom('erlang'), erl_syntax:atom('element'),
- [erl_syntax:integer(1), erl_syntax:application(
- erl_syntax:atom('lists'), erl_syntax:atom('mapfoldl'),
- [erl_syntax:fun_expr([
- erl_syntax:clause([erl_syntax:tuple(Vars), erl_syntax:variable("Counters")], none,
- [erl_syntax:tuple([InnerAst, CounterAst])]),
- erl_syntax:clause(case Vars of [H] -> [H, erl_syntax:variable("Counters")];
- _ -> [erl_syntax:list(Vars), erl_syntax:variable("Counters")] end, none,
- [erl_syntax:tuple([InnerAst, CounterAst])])
- ]),
- CounterVars0, ListAst])]),
- Info#ast_info{var_names = [VarName]}}, TreeWalker2}.
+ {{erl_syntax:case_expr(
+ erl_syntax:application(
+ erl_syntax:atom('lists'), erl_syntax:atom('mapfoldl'),
+ [erl_syntax:fun_expr([
+ erl_syntax:clause([erl_syntax:tuple(Vars), erl_syntax:variable("Counters")], none,
+ [erl_syntax:tuple([InnerAst, CounterAst])]),
+ erl_syntax:clause(case Vars of [H] -> [H, erl_syntax:variable("Counters")];
+ _ -> [erl_syntax:list(Vars), erl_syntax:variable("Counters")] end, none,
+ [erl_syntax:tuple([InnerAst, CounterAst])])
+ ]),
+ CounterVars0, LoopValueAst]),
+ [erl_syntax:clause(
+ [erl_syntax:tuple([erl_syntax:underscore(),
+ erl_syntax:list([erl_syntax:tuple([erl_syntax:atom(counter), erl_syntax:integer(1)])],
+ erl_syntax:underscore())])],
+ none, [EmptyContentsAst]),
+ erl_syntax:clause(
+ [erl_syntax:tuple([erl_syntax:variable("L"), erl_syntax:underscore()])],
+ none, [erl_syntax:variable("L")])]
+ ),
+ merge_info(merge_info(Info, EmptyContentsInfo), LoopValueInfo)
+ }, TreeWalker2}.
load_ast(Names, _Context, TreeWalker) ->
CustomTags = lists:merge([X || {identifier, _ , X} <- Names], TreeWalker#treewalker.custom_tags),
View
4 src/erlydtl/erlydtl_parser.yrl
@@ -60,6 +60,7 @@ Nonterminals
ForBlock
ForBraced
+ EmptyBraced
EndForBraced
ForExpression
ForGroup
@@ -114,6 +115,7 @@ Terminals
cycle_keyword
dot
else_keyword
+ empty_keyword
endautoescape_keyword
endblock_keyword
endcomment_keyword
@@ -221,6 +223,8 @@ FirstofValues -> FirstofValues Value : ['$2'|'$1'].
FirstofValues -> Value : ['$1'].
ForBlock -> ForBraced Elements EndForBraced : {for, '$1', '$2'}.
+ForBlock -> ForBraced Elements EmptyBraced Elements EndForBraced : {for, '$1', '$2', '$4'}.
+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'}.
View
2 src/erlydtl/erlydtl_scanner.erl
@@ -57,7 +57,7 @@ scan([], Scanned, _, in_text) ->
fun
({identifier, Pos, String}) ->
RevString = lists:reverse(String),
- Keywords = ["for", "endfor", "in", "include", "block", "endblock",
+ Keywords = ["for", "empty", "endfor", "in", "include", "block", "endblock",
"extends", "autoescape", "endautoescape", "if", "else", "endif",
"not", "or", "and", "comment", "endcomment", "cycle", "firstof",
"ifchanged", "ifequal", "endifequal", "ifnotequal", "endifnotequal",
View
8 src/tests/erlydtl_unittests.erl
@@ -248,6 +248,14 @@ tests() ->
<<"{% for outer in list %}{% for inner in outer %}({{ forloop.parentloop.counter0 }}, {{ forloop.counter0 }})\n{% endfor %}{% endfor %}">>,
[{'list', [["One", "two"], ["One", "two"]]}], <<"(0, 0)\n(0, 1)\n(1, 0)\n(1, 1)\n">>}
]},
+ {"for/empty", [
+ {"Simple loop",
+ <<"{% for x in list %}{{ x }}{% empty %}shucks{% endfor %}">>, [{'list', ["1", "2", "3"]}],
+ <<"123">>},
+ {"Simple loop (empty)",
+ <<"{% for x in list %}{{ x }}{% empty %}shucks{% endfor %}">>, [{'list', []}],
+ <<"shucks">>}
+ ]},
{"ifequal", [
{"Compare variable to literal",
<<"{% ifequal var1 \"foo\" %}yay{% endifequal %}">>,

0 comments on commit 25abda5

Please sign in to comment.
Something went wrong with that request. Please try again.