%% @author Marc Worrell %% @copyright 2016 Marc Worrell %% @doc Compile main block elements to erl_syntax trees. %% Copyright 2016 Marc Worrell %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. -module(template_compiler_element). -author('Marc Worrell '). -export([ compile/3 ]). -include_lib("syntax_tools/include/merl.hrl"). -include("template_compiler.hrl"). -include("template_compiler_internal.hrl"). -spec compile(element()|elements(), #cs{}, #ws{}) -> {#ws{}, erl_syntax:syntaxTree()}. compile([], _CState, Ws) -> {Ws, erl_syntax:abstract(<<>>)}; compile(L, CState, Ws) when is_list(L) -> {Ws1, Asts} = lists:foldr( fun(Elt, {WsAcc, AstAcc}) -> {WsAcc1, Ast} = compile(Elt, CState, WsAcc), {WsAcc1, [Ast|AstAcc]} end, {Ws,[]}, L), {Ws1, erl_syntax:list(Asts)}; compile({text, _Pos, Text}, _CState, Ws) -> {Ws, erl_syntax:abstract(Text)}; compile({trans_text, _Pos, Tr}, #cs{runtime=Runtime} = CState, Ws) -> Ast = erl_syntax:application( erl_syntax:atom(Runtime), erl_syntax:atom(lookup_translation), [ erl_syntax:abstract(Tr), erl_syntax:variable(CState#cs.vars_var), erl_syntax:variable(CState#cs.context_var) ]), {Ws, Ast}; compile({trans_ext, Tr, Args}, CState, Ws) -> trans_ext(Tr, Args, CState, Ws); compile({value, Expr, []}, #cs{runtime=Runtime} = CState, Ws) -> {Ws1, ExprAst} = template_compiler_expr:compile(Expr, CState, Ws), Ast = ?Q("'@Runtime@':to_render_result(_@ExprAst, _@vars, _@context)", [ {context, erl_syntax:variable(CState#cs.context_var)}, {vars, erl_syntax:variable(CState#cs.vars_var)} ]), case CState#cs.is_autoescape of true -> Ast2 = ?Q("'@Runtime@':escape(_@Ast, _@context)", [ {context, erl_syntax:variable(CState#cs.context_var)} ]), {Ws1, Ast2}; false -> {Ws1, Ast} end; compile({value, Expr, With}, #cs{runtime=Runtime} = CState, Ws) -> {Ws1, WithExprAsts} = with_args(With, CState, Ws, false), {CState1, Ws2} = template_compiler_utils:next_vars_var(CState, Ws1), MapAst = erl_syntax:map_expr( erl_syntax:variable(CState#cs.vars_var), [ erl_syntax:map_field_assoc(WName, WExpr) || {WName, WExpr} <- WithExprAsts ]), case is_context_vars_arg(With, CState) of true -> {CState2, Ws3} = template_compiler_utils:next_context_var(CState1, Ws2), {Ws4, WithAst} = compile({value, Expr, []}, CState2, Ws3), Ast = ?Q([ "begin", "_@vars = _@MapAst,", "_@context1 = '@Runtime@':set_context_vars(_@vars, _@context),", "_@WithAst", "end"], [ {vars, erl_syntax:variable(CState1#cs.vars_var)}, {context, erl_syntax:variable(CState#cs.context_var)}, {context1, erl_syntax:variable(CState2#cs.context_var)} ]), {Ws4, Ast}; false -> {Ws3, WithAst} = compile({value, Expr, []}, CState1, Ws2), Ast = ?Q([ "begin", "_@vars = _@MapAst,", "_@WithAst", "end"], [ {vars, erl_syntax:variable(CState1#cs.vars_var)} ]), {Ws3, Ast} end; compile({date, now, {string_literal, _Pos, Format}}, CState, Ws) -> FormatAst = erl_syntax:abstract(Format), Ast = ?Q("filter_date:date(erlang:universaltime(), _@FormatAst, _@context)", [{context, erl_syntax:variable(CState#cs.context_var)}]), {Ws, Ast}; compile({load, Names}, _CState, Ws) -> % We don't do anything with this. Present for compatibility only. CustomTags = [ Name || {identifier, _, Name} <- Names ], {Ws#ws{custom_tags=CustomTags ++ Ws#ws.custom_tags}, <<>>}; compile({block, {identifier, SrcPos, Name}, _Elts}, CState, Ws) -> BlockName = template_compiler_utils:to_atom(Name), Ast = erl_syntax:application( erl_syntax:atom(template_compiler_runtime_internal), erl_syntax:atom(block_call), [ erl_syntax:abstract(SrcPos), erl_syntax:atom(BlockName), erl_syntax:variable(CState#cs.vars_var), erl_syntax:variable("Blocks"), erl_syntax:atom(CState#cs.runtime), erl_syntax:variable(CState#cs.context_var) ]), {value, {BlockName, _Tree, BlockWs}} = lists:keysearch(BlockName, 1, CState#cs.blocks), Ws1 = Ws#ws{is_forloop_var = Ws#ws.is_forloop_var or BlockWs#ws.is_forloop_var}, {Ws1, Ast}; compile({inherit, {_, _SrcPos, _}}, #cs{block=undefined}, Ws) -> {Ws, erl_syntax:abstract(<<>>)}; compile({inherit, {_, SrcPos, _}}, #cs{block=Block, module=Module} = CState, Ws) -> Ast = erl_syntax:application( erl_syntax:atom(template_compiler_runtime_internal), erl_syntax:atom(block_inherit), [ erl_syntax:abstract(SrcPos), erl_syntax:atom(Module), erl_syntax:atom(Block), erl_syntax:variable(CState#cs.vars_var), erl_syntax:variable("Blocks"), erl_syntax:atom(CState#cs.runtime), erl_syntax:variable(CState#cs.context_var) ]), {Ws#ws{is_forloop_var=true}, Ast}; compile({'include', TagPos, Method, Template, Args}, CState, Ws) -> {Ws1, ArgsList} = with_args(Args, CState, Ws, false), IsContextVar = is_context_vars_arg(Args, CState), include(TagPos, Method, Template, ArgsList, IsContextVar, CState, Ws1); compile({'catinclude', TagPos, Method, Template, IdExpr, Args}, CState, Ws) -> {Ws1, ArgsList} = with_args(Args, CState, Ws, false), {Ws2, IdAst} = template_compiler_expr:compile(IdExpr, CState, Ws1), IsContextVar = is_context_vars_arg(Args, CState), catinclude(TagPos, Method, Template, IdAst, ArgsList, IsContextVar, CState, Ws2); compile({'call', {identifier, _, Name}, Args}, CState, Ws) -> {Ws1, ArgsList} = with_args(Args, CState, Ws, false), Module = template_compiler_utils:to_atom(Name), ArgsListAst = erl_syntax:list([ erl_syntax:tuple([A,B]) || {A,B} <- ArgsList ]), Ast = ?Q([ "template_compiler_runtime_internal:call(", "_@Module@,", "_@ArgsListAst,", "_@vars,", "_@context)" ], [ {vars, erl_syntax:variable(CState#cs.vars_var)}, {context, erl_syntax:variable(CState#cs.context_var)} ]), {Ws1, Ast}; compile({'call_with', {identifier, _, Name}, Expr}, CState, Ws) -> {Ws1, ExprAst} = template_compiler_expr:compile(Expr, CState, Ws), Module = template_compiler_utils:to_atom(Name), Ast = ?Q([ "template_compiler_runtime_internal:call(", "_@Module@,", "[{with, _@ExprAst}],", "_@vars,", "_@context)" ], [ {vars, erl_syntax:variable(CState#cs.vars_var)}, {context, erl_syntax:variable(CState#cs.context_var)} ]), {Ws1, Ast}; compile({custom_tag, {identifier, _, Name}, Args}, #cs{runtime=Runtime} = CState, Ws) -> {Ws1, ArgsList} = with_args(Args, CState, Ws, true), TagName = template_compiler_utils:to_atom(Name), ArgsListAst = erl_syntax:list([ erl_syntax:tuple([A,B]) || {A,B} <- ArgsList ]), Ast = ?Q([ "'@Runtime@':custom_tag(", "_@TagName@,", "_@ArgsListAst,", "_@vars,", "_@context)" ], [ {vars, erl_syntax:variable(CState#cs.vars_var)}, {context, erl_syntax:variable(CState#cs.context_var)} ]), {Ws1, Ast}; compile({Tag, Expr, Args}, #cs{runtime=Runtime} = CState, Ws) when Tag =:= image; Tag =:= image_url; Tag =:= media -> {Ws1, ArgsList} = with_args(Args, CState, Ws, false), {Ws2, ExprAst} = template_compiler_expr:compile(Expr, CState, Ws1), ArgsListAst = erl_syntax:list([ erl_syntax:tuple([A,B]) || {A,B} <- ArgsList ]), case is_context_vars_arg(Args, CState) of true -> {Ws3, ArgsVar} = template_compiler_utils:var(Ws2), {CsCtx, Ws4} = template_compiler_utils:next_context_var(CState, Ws3), Ast = ?Q([ "begin", "_@args = _@ArgsListAst," "_@context1 = '@Runtime@':set_context_vars(_@args, _@context)," "'@Runtime@':builtin_tag(", "_@Tag@,", "_@ExprAst,", "_@args,", "_@vars,", "_@context1)", "end" ], [ {args, erl_syntax:variable(ArgsVar)}, {vars, erl_syntax:variable(CState#cs.vars_var)}, {context, erl_syntax:variable(CState#cs.context_var)}, {context1, erl_syntax:variable(CsCtx#cs.context_var)} ]), {Ws4, Ast}; false -> Ast = ?Q([ "'@Runtime@':builtin_tag(", "_@Tag@,", "_@ExprAst,", "_@ArgsListAst,", "_@vars,", "_@context)" ], [ {vars, erl_syntax:variable(CState#cs.vars_var)}, {context, erl_syntax:variable(CState#cs.context_var)} ]), {Ws2, Ast} end; compile({url, Expr, Args}, #cs{runtime=Runtime} = CState, Ws) -> {Ws1, ArgsList} = with_args(Args, CState, Ws, false), {Ws2, DispatchRuleAst} = case Expr of {find_value, [{identifier, _, Name}]} -> {Ws1, erl_syntax:atom(template_compiler_utils:to_atom(Name))}; _ -> template_compiler_expr:compile(Expr, CState, Ws1) end, ArgsListAst = erl_syntax:list([ erl_syntax:tuple([A,B]) || {A,B} <- ArgsList ]), case is_context_vars_arg(Args, CState) of true -> {Ws3, ArgsVar} = template_compiler_utils:var(Ws2), {CsCtx, Ws4} = template_compiler_utils:next_context_var(CState, Ws3), Ast = ?Q([ "begin", "_@args = _@ArgsListAst," "_@context1 = '@Runtime@':set_context_vars(_@args, _@context)," "'@Runtime@':builtin_tag(", "url,", "_@DispatchRuleAst,", "_@args,", "_@vars,", "_@context1)", "end" ], [ {args, erl_syntax:variable(ArgsVar)}, {vars, erl_syntax:variable(CState#cs.vars_var)}, {context, erl_syntax:variable(CState#cs.context_var)}, {context1, erl_syntax:variable(CsCtx#cs.context_var)} ]), {Ws4, Ast}; false -> Ast = ?Q([ "'@Runtime@':builtin_tag(", "url,", "_@DispatchRuleAst,", "_@ArgsListAst,", "_@vars,", "_@context)" ], [ {vars, erl_syntax:variable(CState#cs.vars_var)}, {context, erl_syntax:variable(CState#cs.context_var)} ]), {Ws2, Ast} end; compile({lib, LibList, Args}, #cs{runtime=Runtime} = CState, Ws) -> {Ws1, ArgsList} = with_args(Args, CState, Ws, false), ArgsListAst = erl_syntax:list([ erl_syntax:tuple([A,B]) || {A,B} <- ArgsList ]), LibFilenames = lists:map(fun({string_literal, _, Filename}) -> Filename end, LibList), Ast = ?Q([ "'@Runtime@':builtin_tag(", "lib,", "_@LibFilenames@,", "_@ArgsListAst,", "_@vars,", "_@context)" ], [ {vars, erl_syntax:variable(CState#cs.vars_var)}, {context, erl_syntax:variable(CState#cs.context_var)} ]), {Ws1, Ast}; compile({print, Expr}, CState, Ws) -> {Ws1, ExprAst} = template_compiler_expr:compile(Expr, CState, Ws), Ast = ?Q("template_compiler_runtime_internal:print(_@ExprAst)"), {Ws1, Ast}; compile({'if', {'as', Expr, undefined}, IfElts, ElseElts}, #cs{runtime=Runtime} = CState, Ws) -> {Ws1, ExprAst} = template_compiler_expr:compile(Expr, CState, Ws), {Ws2, IfClauseAst} = compile(IfElts, CState, Ws1), {Ws3, ElseClauseAst} = compile(ElseElts, CState, Ws2), Ast = ?Q([ "case '@Runtime@':to_bool(_@ExprAst, _@context) of ", "true -> _@IfClauseAst;", "false -> _@ElseClauseAst", "end"], [ {context, erl_syntax:variable(CState#cs.context_var)} ] ), {Ws3, Ast}; compile({'if', {'as', Expr, {identifier, _Pos, Name} = Ident}, IfElts, ElseElts}, #cs{runtime=Runtime} = CState, Ws) -> {Ws1, V} = template_compiler_utils:var(Ws), VAst = erl_syntax:variable(V), {CState1, Ws2} = template_compiler_utils:next_vars_var(CState, Ws1), {Ws3, ExprAst} = template_compiler_expr:compile(Expr, CState, Ws2), {Ws4, ElseClauseAst} = compile(ElseElts, CState, Ws3), case is_context_vars_ident(Ident, CState) of true -> {CsCtx, Ws5} = template_compiler_utils:next_context_var(CState1, Ws4), {Ws6, IfClauseAst} = compile(IfElts, CsCtx, Ws5), Ast = ?Q([ "begin", "_@VAst = _@ExprAst,", "case '@Runtime@':to_bool(_@VAst, _@context) of ", "true -> ", "_@vars1 = _@vars#{ _@name => _@VAst },", "_@context1 = '@Runtime@':set_context_vars(_@vars1),", "_@IfClauseAst;", "false -> _@ElseClauseAst", "end", "end"], [ {name, erl_syntax:atom(template_compiler_utils:to_atom(Name))}, {context, erl_syntax:variable(CState#cs.context_var)}, {context1, erl_syntax:variable(CsCtx#cs.context_var)}, {vars, erl_syntax:variable(CState#cs.vars_var)}, {vars1, erl_syntax:variable(CState1#cs.vars_var)} ]), {Ws6, Ast}; false -> {Ws5, IfClauseAst} = compile(IfElts, CState1, Ws4), Ast = ?Q([ "begin", "_@VAst = _@ExprAst,", "case '@Runtime@':to_bool(_@VAst, _@context) of ", "true -> _@vars1 = _@vars#{ _@name => _@VAst }, _@IfClauseAst;", "false -> _@ElseClauseAst", "end", "end"], [ {name, erl_syntax:atom(template_compiler_utils:to_atom(Name))}, {context, erl_syntax:variable(CState#cs.context_var)}, {vars, erl_syntax:variable(CState#cs.vars_var)}, {vars1, erl_syntax:variable(CState1#cs.vars_var)} ]), {Ws5, Ast} end; compile({'for', {'in', Idents, ListExpr}, LoopElts, EmptyElts}, #cs{runtime=Runtime} = CState, Ws) -> {CsLoop0, WsLoop0} = template_compiler_utils:next_vars_var(CState, Ws#ws{is_forloop_var=false}), {CsLoop, WsLoop} = template_compiler_utils:next_context_var(CsLoop0, WsLoop0), {WsLoop1, LoopAst} = compile(LoopElts, CsLoop, WsLoop), WsEmpty = WsLoop1#ws{ is_forloop_var=Ws#ws.is_forloop_var }, {WsEmpty1, EmptyAst} = compile(EmptyElts, CState, WsEmpty), {WsExpr, ExprAst} = template_compiler_expr:compile(ListExpr, CState, WsEmpty1), LoopVarsAst = erl_syntax:abstract(idents_as_atoms(Idents)), IsContextVars = is_context_vars_ident(Idents, CState), Ast = ?Q([ "template_compiler_runtime_internal:forloop(", "_@isforloopvar," "_@ExprAst," "_@LoopVarsAst,", "fun(_@varsloop, _@contextloop) -> _@LoopAst end," "fun() -> _@EmptyAst end,", "_@Runtime@,", "_@IsContextVars@," "_@vars,", "_@context" ")" ], [ {isforloopvar, erl_syntax:atom(WsLoop1#ws.is_forloop_var)}, {varsloop, erl_syntax:variable(CsLoop#cs.vars_var)}, {vars, erl_syntax:variable(CState#cs.vars_var)}, {context, erl_syntax:variable(CState#cs.context_var)}, {contextloop, erl_syntax:variable(CsLoop#cs.context_var)} ]), {WsExpr, Ast}; compile({'with', {Exprs, Idents}, Elts}, #cs{runtime=Runtime} = CState, Ws) -> {Ws1, ExprAsts} = expr_list(Exprs, CState, Ws), VarsAsts = erl_syntax:abstract(idents_as_atoms(Idents)), ExprListAst = erl_syntax:list(ExprAsts), {CsWith, Ws2} = template_compiler_utils:next_vars_var(CState, Ws1), case is_context_vars_ident(Idents, CState) of true -> {CsCtx, Ws3} = template_compiler_utils:next_context_var(CsWith, Ws2), {Ws4, BodyAst} = compile(Elts, CsCtx, Ws3), Ast = ?Q([ "begin", "_@vars1 = template_compiler_runtime_internal:with_vars(", "_@VarsAsts,", "_@ExprListAst,", "_@vars),", "_@context1 = '@Runtime@':set_context_vars(_@vars1, _@context)," "_@BodyAst", "end" ], [ {vars, erl_syntax:variable(CState#cs.vars_var)}, {vars1, erl_syntax:variable(CsCtx#cs.vars_var)}, {context, erl_syntax:variable(CState#cs.context_var)}, {context1, erl_syntax:variable(CsCtx#cs.context_var)} ]), {Ws4, Ast}; false -> {Ws3, BodyAst} = compile(Elts, CsWith, Ws2), Ast = ?Q([ "begin", "_@vars1 = template_compiler_runtime_internal:with_vars(", "_@VarsAsts,", "_@ExprListAst,", "_@vars),", "_@BodyAst", "end" ], [ {vars, erl_syntax:variable(CState#cs.vars_var)}, {vars1, erl_syntax:variable(CsWith#cs.vars_var)} ]), {Ws3, Ast} end; compile({cache, {CacheTime, Args}, Elts}, #cs{runtime=Runtime} = CState, Ws) -> {Ws1, ArgsList} = with_args(Args, CState, Ws, false), {Ws2, CacheTimeAst} = template_compiler_expr:compile(CacheTime, CState, Ws1), {CsBody, Ws3} = template_compiler_utils:next_vars_var(CState, Ws2), {CsBody1, Ws4} = template_compiler_utils:next_vars_var(CsBody, Ws3), {Ws5, BodyAst} = compile(Elts, CsBody1, Ws4), Unique = template_compiler_runtime_internal:unique(), ArgsListAst = erl_syntax:list([ erl_syntax:tuple([A,B]) || {A,B} <- ArgsList ]), Ast = ?Q([ "'@Runtime@':cache_tag(", "_@CacheTimeAst,", "_@Unique@," "_@ArgsListAst,", "fun(_@varsbody, _@contextbody) -> _@BodyAst end,", "_@vars,", "_@context)" ], [ {vars, erl_syntax:variable(CState#cs.vars_var)}, {context, erl_syntax:variable(CState#cs.context_var)}, {varsbody, erl_syntax:variable(CsBody1#cs.vars_var)}, {contextbody, erl_syntax:variable(CsBody1#cs.context_var)} ]), {Ws5, Ast}; compile({javascript, Elts}, #cs{runtime=Runtime} = CState, Ws) -> {Ws1, BodyAst} = compile(Elts, CState, Ws), Ast = ?Q([ "'@Runtime@':javascript_tag(", "_@BodyAst,", "_@vars,", "_@context)" ], [ {vars, erl_syntax:variable(CState#cs.vars_var)}, {context, erl_syntax:variable(CState#cs.context_var)} ]), {Ws1, Ast}; compile({filter, Filters, Elts}, #cs{runtime=Runtime} = CState, Ws) -> {Ws1, BodyAst} = compile(Elts, CState, Ws), Expr = lists:foldl( fun({filter, Name, Args}, Acc) -> {apply_filter, Acc, {filter, Name, Args}} end, {ast, BodyAst}, Filters), {Ws2, ExprAst} = template_compiler_expr:compile(Expr, CState, Ws1), Ast = ?Q("'@Runtime@':to_render_result(_@ExprAst, _@vars, _@context)", [ {context, erl_syntax:variable(CState#cs.context_var)}, {vars, erl_syntax:variable(CState#cs.vars_var)} ]), {Ws2, Ast}; compile({spaceless, Elts}, #cs{runtime=Runtime} = CState, Ws) -> {Ws1, BodyAst} = compile(Elts, CState, Ws), Ast = ?Q("'@Runtime@':spaceless_tag(_@BodyAst, _@vars, _@context)", [ {context, erl_syntax:variable(CState#cs.context_var)}, {vars, erl_syntax:variable(CState#cs.vars_var)} ]), {Ws1, Ast}; compile({autoescape, {identifier, _, <<"on">>}, Elts}, CState, Ws) -> compile(Elts, CState#cs{is_autoescape = true}, Ws); compile({autoescape, {identifier, _, <<"off">>}, Elts}, CState, Ws) -> compile(Elts, CState#cs{is_autoescape = false}, Ws); compile({autoescape, {identifier, _, OnOff}, Elts}, CState, Ws) -> compile(Elts, CState#cs{is_autoescape = z_convert:to_bool(OnOff)}, Ws); compile({cycle_compat, Names}, CState, Ws) -> Exprs = [{string_literal, Pos, Name} || {identifier, Pos, Name} <- Names ], compile({cycle, Exprs}, CState, Ws); compile({cycle, []}, _CState, Ws) -> {Ws, erl_syntax:list([])}; compile({cycle, Exprs}, CState, Ws) -> {Ws1, Var} = template_compiler_utils:var(Ws), {Ws2, ExprList} = expr_list(Exprs, CState, Ws1), N = length(Exprs), Clauses = lists:zip(lists:seq(0,N-1), ExprList), ClauseAsts = [ erl_syntax:clause([erl_syntax:integer(Nr)], none, [Expr]) || {Nr,Expr} <- Clauses ], ValueAst = ?Q("maps:get(counter0, _@v) rem _@N@", [ {v, erl_syntax:variable(Var)} ]), CaseAst = erl_syntax:case_expr(ValueAst, ClauseAsts), Ast = ?Q([ "case maps:get(forloop, _@vars, undefined) of", "undefined -> _@first;", "_@v -> _@CaseAst", "end" ], [ {first, hd(ExprList)}, {v, erl_syntax:variable(Var)}, {vars, erl_syntax:variable(CState#cs.vars_var)} ]), compile({value, {ast, Ast}, []}, CState, Ws2#ws{is_forloop_var=true}). include({_, SrcPos, _}, Method, Template, ArgsList, IsContextVars, #cs{runtime=Runtime} = CState, Ws) when is_atom(Method) -> {Ws1, TemplateAst} = template_compiler_expr:compile(Template, CState, Ws), ArgsListAst = erl_syntax:list([ erl_syntax:tuple([A,B]) || {A,B} <- ArgsList ]), Ast = ?Q([ "template_compiler_runtime_internal:include(", "_@SrcPos@,", "_@Method@,", "_@TemplateAst,", "_@ArgsListAst,", "_@Runtime@,", "_@context_vars,", "_@IsContextVars@,", "_@vars,", "_@context)" ], [ {vars, erl_syntax:variable(CState#cs.vars_var)}, {context, erl_syntax:variable(CState#cs.context_var)}, {context_vars, erl_syntax:abstract(CState#cs.context_vars)} ]), {Ws1, Ast}. catinclude({_, SrcPos, _}, Method, Template, IdAst, ArgsList, IsContextVars, #cs{runtime=Runtime} = CState, Ws) when is_atom(Method) -> {Ws1, TemplateAst} = template_compiler_expr:compile(Template, CState, Ws), ArgsList1 = [ {erl_syntax:atom(id),IdAst} | ArgsList ], ArgsListAst = erl_syntax:list([ erl_syntax:tuple([A,B]) || {A,B} <- ArgsList1 ]), Ast = ?Q([ "template_compiler_runtime_internal:include(", "_@SrcPos@,", "_@Method@,", "{cat, _@TemplateAst},", "_@ArgsListAst,", "_@Runtime@,", "_@context_vars,", "_@IsContextVars@,", "_@vars,", "_@context)" ], [ {vars, erl_syntax:variable(CState#cs.vars_var)}, {context, erl_syntax:variable(CState#cs.context_var)}, {context_vars, erl_syntax:abstract(CState#cs.context_vars)} ]), {Ws1, Ast}. expr_list(ExprList, CState, Ws) -> lists:foldr( fun(E, {WsAcc, ExprAcc}) -> {WsAcc1, EAst} = template_compiler_expr:compile(E, CState, WsAcc), {WsAcc1, [EAst|ExprAcc]} end, {Ws, []}, ExprList). is_context_vars_arg({{identifier, _, Ident}, _Val}, CState) -> lists:member(Ident, CState#cs.context_vars); is_context_vars_arg(Args, CState) when is_list(Args) -> lists:any( fun ({{identifier, _, Ident}, _Val}) -> lists:member(Ident, CState#cs.context_vars) end, Args). is_context_vars_ident({identifier, _, Ident}, CState) -> lists:member(Ident, CState#cs.context_vars); is_context_vars_ident(Idents, CState) when is_list(Idents) -> lists:any( fun ({identifier, _, Ident}) -> lists:member(Ident, CState#cs.context_vars) end, Idents). with_args(With, CState, Ws, IsPostback) -> lists:foldl( fun ({Ident, true}, {WsAcc, Acc}) -> VarAst = erl_syntax:atom(ident_as_atom(Ident)), {WsAcc, [{VarAst, erl_syntax:atom(true)}|Acc]}; ({{identifier, _, <<"postback">>}, {string_literal, _, Postback}}, {WsAcc, Acc}) when IsPostback -> ExprAst = erl_syntax:atom(template_compiler_utils:to_atom(Postback)), VarAst = erl_syntax:atom(postback), {WsAcc, [{VarAst, ExprAst}|Acc]}; ({Ident, Expr}, {WsAcc, Acc}) -> {Ws1, ExprAst} = template_compiler_expr:compile(Expr, CState, WsAcc), VarAst = erl_syntax:atom(ident_as_atom(Ident)), {Ws1, [{VarAst, ExprAst}|Acc]} end, {Ws, []}, With). -spec idents_as_atoms([identifier_token()]) -> [ atom() ]. idents_as_atoms(Idents) -> [ ident_as_atom(Ident) || Ident <- Idents ]. ident_as_atom({identifier, _SrcPos, Ident}) -> template_compiler_utils:to_atom(Ident). trans_ext({string_literal, _, Text}, Args, CState, Ws) -> Unescaped = template_compiler_utils:unescape_string_literal(Text), trans_ext_1({trans, [{en, Unescaped}]}, Args, CState, Ws); trans_ext({trans_literal, _, Tr}, Args, CState, Ws) -> trans_ext_1(Tr, Args, CState, Ws). trans_ext_1({trans, Tr}, Args, #cs{runtime=Runtime} = CState, Ws) -> Split = [ {Lang, split_string(Txt, <<>>, [])} || {Lang, Txt} <- Tr ], {FunAsts, Ws1} = lists:foldl( fun({Lang, Parts}, {FAcc, WsAcc}) -> {WsAcc1, Fun} = trans_ext_fun(Parts, Args, CState, WsAcc), {[{Lang, Fun}|FAcc], WsAcc1} end, {[], Ws}, Split), FunListAst = erl_syntax:list( lists:map( fun ({Lng,FunAst}) -> erl_syntax:tuple([ erl_syntax:atom(Lng), FunAst ]) end, FunAsts)), Ast = ?Q("(_@Runtime@:lookup_translation({trans, _@FunListAst}, _@vars, _@context))()", [ {context, erl_syntax:variable(CState#cs.context_var)}, {vars, erl_syntax:variable(CState#cs.vars_var)} ]), {Ws1, Ast}. trans_ext_fun(Parts, Args, CState, Ws) -> Parts1 = [ P || P <- Parts, P =/= <<>> ], Args1 = [{Ident,ArgExpr} || {{identifier, _, Ident}, ArgExpr} <- Args], {Asts,Ws1} = lists:foldr( fun (B, {Acc, WsAcc}) when is_binary(B) -> {[erl_syntax:abstract(B)|Acc], WsAcc}; ({var, Name}, {Acc, WsAcc}) -> case proplists:get_value(Name, Args1) of undefined -> {Acc, WsAcc}; Expr -> {WsAcc1, ExprAst} = compile({value, Expr, []}, CState, WsAcc), {[ExprAst|Acc], WsAcc1} end end, {[], Ws}, Parts1), {Ws1, ?Q("fun() -> _@list end", [{list, erl_syntax:list(Asts)}])}. split_string(<<>>, Acc, Parts) -> lists:reverse([Acc|Parts]); split_string(<<"{{", T/binary>>, Acc, Parts) -> split_string(T, <>, Parts); split_string(<<"}}", T/binary>>, Acc, Parts) -> split_string(T, <>, Parts); split_string(<<"{", T/binary>>, Acc, Parts) -> split_string_name(T, <<>>, [Acc|Parts]); split_string(<>, Acc, Parts) -> split_string(T, <>, Parts). split_string_name(<<>>, Acc, Parts) -> lists:reverse([Acc|Parts]); split_string_name(<<"}", T/binary>>, Acc, Parts) -> split_string(T, <<>>, [{var, z_string:trim(Acc)}|Parts]); split_string_name(<>, Acc, Parts) -> split_string_name(T, <>, Parts).