%% @author Marc Worrell <marc@worrell.nl>
%% @copyright 2016-2023 Marc Worrell
%% @doc Compile main block elements to erl_syntax trees.
%% @end
%% Copyright 2016-2023 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 <marc@worrell.nl>').
-export([
compile/3
]).
-include_lib("syntax_tools/include/merl.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, SrcPos, Text}, _CState, Ws) ->
{Ws, template_compiler_utils:set_pos(SrcPos, erl_syntax:abstract(Text))};
compile({trans_text, SrcPos, 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, template_compiler_utils:set_pos(SrcPos, Ast)};
compile({trans_ext, Tr, Args}, CState, Ws) ->
trans_ext(Tr, Args, CState, Ws);
compile({value, {_, SrcPos, _}, Expr, []}, #cs{runtime=Runtime} = CState, Ws) ->
{Ws1, ExprAst} = template_compiler_expr:compile(Expr, CState, Ws),
Ast = merl:qquote(
template_compiler_utils:pos(SrcPos),
"_@runtime:to_render_result(_@expr, _@vars, _@context)",
[
{expr, ExprAst},
{runtime, erl_syntax:atom(Runtime)},
{context, erl_syntax:variable(CState#cs.context_var)},
{vars, erl_syntax:variable(CState#cs.vars_var)}
]),
case CState#cs.is_autoescape of
true ->
Ast2 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"_@runtime:escape(_@ast, _@context)",
[
{ast, Ast},
{runtime, erl_syntax:atom(Runtime)},
{context, erl_syntax:variable(CState#cs.context_var)}
]),
{Ws1, Ast2};
false ->
{Ws1, Ast}
end;
compile({value, {_, SrcPos, _} = TagSrc, 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 = template_compiler_utils:set_pos(
SrcPos,
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, TagSrc, Expr, []}, CState2, Ws3),
Ast = merl:qquote(
template_compiler_utils:pos(SrcPos),
"begin "
"_@vars = _@map,"
"_@context1 = _@runtime:set_context_vars(_@vars, _@context),"
"_@with "
"end",
[
{vars, erl_syntax:variable(CState1#cs.vars_var)},
{map, MapAst},
{with, WithAst},
{runtime, erl_syntax:atom(Runtime)},
{context, erl_syntax:variable(CState#cs.context_var)},
{context1, erl_syntax:variable(CState2#cs.context_var)}
]),
{Ws4, Ast};
false ->
{Ws3, WithAst} = compile({value, TagSrc, Expr, []}, CState1, Ws2),
Ast = merl:qquote(
template_compiler_utils:pos(SrcPos),
"begin "
"_@vars = _@map,"
"_@with "
"end",
[
{vars, erl_syntax:variable(CState1#cs.vars_var)},
{map, MapAst},
{with, WithAst}
]),
{Ws3, Ast}
end;
compile({date, now, {_, SrcPos, _}, {string_literal, _SrcPos, Format}}, CState, Ws) ->
Ast = merl:qquote(
template_compiler_utils:pos(SrcPos),
"filter_date:date(erlang:universaltime(), _@format, _@context)",
[
{format, erl_syntax:abstract(Format)},
{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, template_compiler_utils:set_pos(SrcPos, 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}, template_compiler_utils:set_pos(SrcPos, 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, SrcPos, 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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"template_compiler_runtime_internal:call("
"_@module,"
"_@args,"
"_@vars,"
"_@context)",
[
{module, erl_syntax:atom(Module)},
{args, ArgsListAst},
{vars, erl_syntax:variable(CState#cs.vars_var)},
{context, erl_syntax:variable(CState#cs.context_var)}
]),
{Ws1, Ast};
compile({'call_with', {identifier, SrcPos, Name}, Expr}, CState, Ws) ->
{Ws1, ExprAst} = template_compiler_expr:compile(Expr, CState, Ws),
Module = template_compiler_utils:to_atom(Name),
Ast = merl:qquote(
template_compiler_utils:pos(SrcPos),
"template_compiler_runtime_internal:call("
"_@module,"
"[{with, _@expr}],"
"_@vars,"
"_@context)",
[
{module, erl_syntax:atom(Module)},
{expr, ExprAst},
{vars, erl_syntax:variable(CState#cs.vars_var)},
{context, erl_syntax:variable(CState#cs.context_var)}
]),
{Ws1, Ast};
compile({'compose', {TagPos, Template, Args}, Blocks}, CState, Ws) ->
{Ws1, ArgsList} = with_args(Args, CState, Ws, false),
IsContextVar = is_context_vars_arg(Args, CState),
compose(TagPos, Template, ArgsList, IsContextVar, Blocks, CState, Ws1);
compile({'catcompose', {TagPos, Template, IdExpr, Args}, Blocks}, 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),
catcompose(TagPos, Template, IdAst, ArgsList, IsContextVar, Blocks, CState, Ws2);
compile({custom_tag, {identifier, SrcPos, 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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"_@runtime:custom_tag("
"_@tagname,"
"_@args,"
"_@vars,"
"_@context)",
[
{runtime, erl_syntax:atom(Runtime)},
{tagname, erl_syntax:atom(TagName)},
{args, ArgsListAst},
{vars, erl_syntax:variable(CState#cs.vars_var)},
{context, erl_syntax:variable(CState#cs.context_var)}
]),
{Ws1, Ast};
compile({Tag, {_, SrcPos, _}, Expr, Args}, #cs{runtime=Runtime} = CState, Ws)
when Tag =:= image;
Tag =:= image_url;
Tag =:= image_data_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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"begin "
"_@argsvar = _@argslist,"
"_@context1 = _@runtime:set_context_vars(_@args, _@context),"
"_@runtime:builtin_tag("
"_@tag,"
"_@expr,"
"_@argsvar,"
"_@vars,"
"_@context1) "
"end",
[
{argslist, ArgsListAst},
{argsvar, erl_syntax:variable(ArgsVar)},
{runtime, erl_syntax:atom(Runtime)},
{tag, erl_syntax:abstract(Tag)},
{expr, ExprAst},
{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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"_@runtime:builtin_tag("
"_@tag,"
"_@expr,"
"_@argslist,"
"_@vars,"
"_@context)",
[
{runtime, erl_syntax:atom(Runtime)},
{tag, erl_syntax:abstract(Tag)},
{expr, ExprAst},
{argslist, ArgsListAst},
{vars, erl_syntax:variable(CState#cs.vars_var)},
{context, erl_syntax:variable(CState#cs.context_var)}
]),
{Ws2, Ast}
end;
compile({url, {_, SrcPos, _}, 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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"begin "
"_@args = _@argslist,"
"_@context1 = _@runtime:set_context_vars(_@args, _@context),"
"_@runtime:builtin_tag("
"url,"
"_@dispatchrule,"
"_@args,"
"_@vars,"
"_@context1) "
"end",
[
{runtime, erl_syntax:atom(Runtime)},
{args, erl_syntax:variable(ArgsVar)},
{argslist, ArgsListAst},
{dispatchrule, DispatchRuleAst},
{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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"_@runtime:builtin_tag("
"url,"
"_@dispatchrule,"
"_@argslist,"
"_@vars,"
"_@context)",
[
{runtime, erl_syntax:atom(Runtime)},
{argslist, ArgsListAst},
{dispatchrule, DispatchRuleAst},
{vars, erl_syntax:variable(CState#cs.vars_var)},
{context, erl_syntax:variable(CState#cs.context_var)}
]),
{Ws2, Ast}
end;
compile({Lib, {_, SrcPos, _}, LibList, Args}, #cs{runtime=Runtime} = CState, Ws) when Lib =:= lib; Lib =:= lib_url ->
{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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"_@runtime:builtin_tag("
"_@tag,"
"_@libfilenames,"
"_@argslist,"
"_@vars,"
"_@context)",
[
{tag, erl_syntax:atom(Lib)},
{runtime, erl_syntax:atom(Runtime)},
{libfilenames, erl_syntax:abstract(LibFilenames)},
{argslist, ArgsListAst},
{vars, erl_syntax:variable(CState#cs.vars_var)},
{context, erl_syntax:variable(CState#cs.context_var)}
]),
{Ws1, Ast};
compile({print, {_, SrcPos, _}, Expr}, CState, Ws) ->
{Ws1, ExprAst} = template_compiler_expr:compile(Expr, CState, Ws),
Ast = merl:qquote(
template_compiler_utils:pos(SrcPos),
"template_compiler_runtime_internal:print(_@expr)",
[
{expr, ExprAst}
]),
{Ws1, Ast};
compile({'ifequal', {{_, _SrcPos, _} = Token, Expr1, Expr2}, IfElts, ElseElts}, CState, Ws) ->
compile({'if', {'as', Token, {expr, {'eq', Token}, Expr1, Expr2}, undefined}, IfElts, ElseElts}, CState, Ws);
compile({'ifnotequal', {{_, _SrcPos, _} = Token, Expr1, Expr2}, IfElts, ElseElts}, CState, Ws) ->
compile({'if', {'as', Token, {expr, {'ne', Token}, Expr1, Expr2}, undefined}, IfElts, ElseElts}, CState, Ws);
compile({'if', {'as', {_, SrcPos, _}, 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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"case _@runtime:to_bool(_@expr, _@context) of "
"true -> _@ifclause;"
"false -> _@elseclause "
"end",
[
{runtime, erl_syntax:atom(Runtime)},
{expr, ExprAst},
{ifclause, IfClauseAst},
{elseclause, ElseClauseAst},
{context, erl_syntax:variable(CState#cs.context_var)}
]
),
{Ws3, Ast};
compile({'if', {'as', {_, SrcPos, _}, 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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"begin "
"_@v = _@expr,"
"case _@runtime:to_bool(_@v, _@context) of "
"true -> "
"_@vars1 = _@vars#{ _@name => _@v },"
"_@context1 = _@runtime:set_context_vars(_@vars1),"
"_@ifclause;"
"false -> _@elseclause "
"end "
"end",
[
{runtime, erl_syntax:atom(Runtime)},
{v, VAst},
{expr, ExprAst},
{name, erl_syntax:atom(template_compiler_utils:to_atom(Name))},
{ifclause, IfClauseAst},
{elseclause, ElseClauseAst},
{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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"begin "
"_@v = _@expr,"
"case _@runtime:to_bool(_@v, _@context) of "
"true -> _@vars1 = _@vars#{ _@name => _@v }, _@ifclause;"
"false -> _@elseclause "
"end "
"end",
[
{runtime, erl_syntax:atom(Runtime)},
{v, VAst},
{expr, ExprAst},
{name, erl_syntax:atom(template_compiler_utils:to_atom(Name))},
{ifclause, IfClauseAst},
{elseclause, ElseClauseAst},
{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', {_, SrcPos, _}, 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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"template_compiler_runtime_internal:forloop("
"_@isforloopvar,"
"_@expr,"
"_@loopvars,"
"fun(_@varsloop, _@contextloop) -> _@loop end,"
"fun() -> _@empty end,"
"_@runtime,"
"_@is_context_vars,"
"_@vars,"
"_@context"
")",
[
{runtime, erl_syntax:atom(Runtime)},
{expr, ExprAst},
{loopvars, LoopVarsAst},
{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)},
{is_context_vars, erl_syntax:abstract(IsContextVars)},
{loop, LoopAst},
{empty, EmptyAst},
{context, erl_syntax:variable(CState#cs.context_var)},
{contextloop, erl_syntax:variable(CsLoop#cs.context_var)}
]),
{WsExpr, Ast};
compile({'with', {{_, SrcPos, _}, 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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"begin "
"_@vars1 = template_compiler_runtime_internal:with_vars("
"_@varsasts,"
"_@exprlist,"
"_@vars),"
"_@context1 = _@runtime:set_context_vars(_@vars1, _@context),"
"_@body "
"end",
[
{runtime, erl_syntax:atom(Runtime)},
{varsasts, VarsAsts},
{exprlist, ExprListAst},
{body, BodyAst},
{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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"begin "
"_@vars1 = template_compiler_runtime_internal:with_vars("
"_@varsasts,"
"_@exprlist,"
"_@vars),"
"_@body "
"end",
[
{varsasts, VarsAsts},
{exprlist, ExprListAst},
{body, BodyAst},
{vars, erl_syntax:variable(CState#cs.vars_var)},
{vars1, erl_syntax:variable(CsWith#cs.vars_var)}
]),
{Ws3, Ast}
end;
compile({cache, {{_, SrcPos, _}, 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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"_@runtime:cache_tag("
"_@cachetime,"
"_@unique,"
"_@argslist,"
"fun(_@varsbody, _@contextbody) -> _@body end,"
"_@vars,"
"_@context)",
[
{runtime, erl_syntax:atom(Runtime)},
{unique, erl_syntax:abstract(Unique)},
{cachetime, CacheTimeAst},
{argslist, ArgsListAst},
{vars, erl_syntax:variable(CState#cs.vars_var)},
{body, BodyAst},
{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, {_, SrcPos, _}, Elts}, #cs{runtime=Runtime} = CState, Ws) ->
{Ws1, BodyAst} = compile(Elts, CState, Ws),
Ast = merl:qquote(
template_compiler_utils:pos(SrcPos),
"_@runtime:javascript_tag("
"_@body,"
"_@vars,"
"_@context)",
[
{runtime, erl_syntax:atom(Runtime)},
{body, BodyAst},
{vars, erl_syntax:variable(CState#cs.vars_var)},
{context, erl_syntax:variable(CState#cs.context_var)}
]),
{Ws1, Ast};
compile({filter, {{_, SrcPos, _}, 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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"_@runtime:to_render_result(_@expr, _@vars, _@context)",
[
{runtime, erl_syntax:atom(Runtime)},
{expr, ExprAst},
{context, erl_syntax:variable(CState#cs.context_var)},
{vars, erl_syntax:variable(CState#cs.vars_var)}
]),
{Ws2, Ast};
compile({spaceless, {_, SrcPos, _}, Elts}, #cs{runtime=Runtime} = CState, Ws) ->
{Ws1, BodyAst} = compile(Elts, CState, Ws),
Ast = merl:qquote(
template_compiler_utils:pos(SrcPos),
"_@runtime:spaceless_tag(_@body, _@vars, _@context)",
[
{runtime, erl_syntax:atom(Runtime)},
{body, BodyAst},
{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, TagPos, Names}, CState, Ws) ->
Exprs = [{string_literal, Pos, Name} || {identifier, Pos, Name} <- Names ],
compile({cycle, TagPos, Exprs}, CState, Ws);
compile({cycle, _TagPos, []}, _CState, Ws) ->
{Ws, erl_syntax:list([])};
compile({cycle, {_, SrcPos, _} = TagSrc, 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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"maps:get(counter0, _@v) rem _@n",
[
{v, erl_syntax:variable(Var)},
{n, erl_syntax:integer(N)}
]),
Ast = merl:qquote(
template_compiler_utils:pos(SrcPos),
"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)},
{caseast, erl_syntax:case_expr(ValueAst, ClauseAsts)}
]),
compile({value, TagSrc, {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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"template_compiler_runtime_internal:include("
"_@srcpos,"
"_@method,"
"_@template,"
"_@args,"
"_@runtime,"
"_@context_vars,"
"_@is_context_vars,"
"_@vars,"
"_@context)",
[
{srcpos, erl_syntax:abstract(SrcPos)},
{method, erl_syntax:abstract(Method)},
{template, TemplateAst},
{args, ArgsListAst},
{vars, erl_syntax:variable(CState#cs.vars_var)},
{runtime, erl_syntax:atom(Runtime)},
{context, erl_syntax:variable(CState#cs.context_var)},
{context_vars, erl_syntax:abstract(CState#cs.context_vars)},
{is_context_vars, erl_syntax:abstract(IsContextVars)}
]),
Ws2 = maybe_add_include(Template, Method, false, Ws1),
{Ws2, 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('$cat'),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)}
]),
Ws2 = maybe_add_include(Template, Method, true, Ws1),
{Ws2, Ast}.
maybe_add_include({string_literal, SrcPos, Text}, Method, IsCatinclude, Ws) ->
{_, Line, Column} = SrcPos,
Tpl = #{
template => Text,
line => Line,
column => Column,
method => Method,
is_catinclude => IsCatinclude
},
Ws#ws{
includes = [ Tpl | Ws#ws.includes ]
};
maybe_add_include(_Token, _Method, _IsCatinclude, Ws) ->
Ws.
compose({_, SrcPos, _}, Template, ArgsList, IsContextVars, Blocks, #cs{runtime=Runtime, module=Module} = CState, Ws) ->
{Ws1, TemplateAst} = template_compiler_expr:compile(Template, CState, Ws),
ArgsListAst = erl_syntax:list([ erl_syntax:tuple([A,B]) || {A,B} <- ArgsList ]),
{_BlocksWs, BlocksAsts} = template_compiler:compile_blocks(Blocks, CState),
BlockClauses = [
?Q("(_@BlockName@, Vars, Blocks, Context) -> _@BlockAst")
|| {BlockName, BlockAst, _BlockWs} <- BlocksAsts
] ++ [
?Q("(_BlockName, _Vars, _Blocks, _Context) -> <<>>")
],
BlockFunAst = erl_syntax:fun_expr(BlockClauses),
BlockListAst = erl_syntax:abstract([ BlockName || {BlockName, _, _} <- BlocksAsts ]),
Ast = ?Q([
"template_compiler_runtime_internal:compose("
"_@srcpos,"
"_@template,"
"_@args,"
"_@runtime,"
"_@context_vars,"
"_@is_context_vars,"
"_@vars,"
"_@block_list,",
"_@module,",
"_@block_fun,",
"_@context)"
],
[
{srcpos, erl_syntax:abstract(SrcPos)},
{template, TemplateAst},
{args, ArgsListAst},
{vars, erl_syntax:variable(CState#cs.vars_var)},
{runtime, erl_syntax:atom(Runtime)},
{context, erl_syntax:variable(CState#cs.context_var)},
{context_vars, erl_syntax:abstract(CState#cs.context_vars)},
{block_list, BlockListAst},
{module, erl_syntax:atom(Module)},
{block_fun, BlockFunAst},
{is_context_vars, erl_syntax:abstract(IsContextVars)}
]),
Ws2 = maybe_add_include(Template, undefined, false, Ws1),
{Ws2, Ast}.
catcompose({_, SrcPos, _}, Template, IdAst, ArgsList, IsContextVars, Blocks, #cs{runtime=Runtime} = CState, Ws) ->
{Ws1, TemplateAst} = template_compiler_expr:compile(Template, CState, Ws),
ArgsList1 = [ {erl_syntax:atom('$cat'), IdAst} | ArgsList ],
ArgsListAst = erl_syntax:list([ erl_syntax:tuple([A,B]) || {A,B} <- ArgsList1 ]),
{_BlocksWs, BlocksAsts} = template_compiler:compile_blocks(Blocks, CState),
BlockClauses = [
?Q("(_@BlockName@, Vars, Blocks, Context) -> _@BlockAst")
|| {BlockName, BlockAst, _BlockWs} <- BlocksAsts
] ++ [
?Q("(_BlockName, _Vars, _Blocks, _Context) -> <<>>")
],
BlockFunAst = erl_syntax:fun_expr(BlockClauses),
BlockListAst = erl_syntax:abstract([ BlockName || {BlockName, _, _} <- BlocksAsts ]),
Ast = ?Q([
"template_compiler_runtime_internal:compose("
"_@srcpos,"
"{cat, _@template},"
"_@args,"
"_@runtime,"
"_@context_vars,"
"_@is_context_vars,"
"_@vars,"
"_@block_list,",
"_@block_fun,",
"_@context)"
],
[
{srcpos, erl_syntax:abstract(SrcPos)},
{template, TemplateAst},
{args, ArgsListAst},
{vars, erl_syntax:variable(CState#cs.vars_var)},
{runtime, erl_syntax:atom(Runtime)},
{context, erl_syntax:variable(CState#cs.context_var)},
{context_vars, erl_syntax:abstract(CState#cs.context_vars)},
{block_list, BlockListAst},
{block_fun, BlockFunAst},
{is_context_vars, erl_syntax:abstract(IsContextVars)}
]),
Ws2 = maybe_add_include(Template, undefined, false, Ws1),
{Ws2, 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, SrcPos, Text}, Args, CState, Ws) ->
Unescaped = template_compiler_utils:unescape_string_literal(Text),
trans_ext_1({trans, [{en, Unescaped}]}, Args, SrcPos, CState, Ws);
trans_ext({trans_literal, SrcPos, Tr}, Args, CState, Ws) ->
trans_ext_1(Tr, Args, SrcPos, CState, Ws).
trans_ext_1({trans, Tr}, Args, SrcPos, #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 = merl:qquote(
template_compiler_utils:pos(SrcPos),
"(_@runtime:lookup_translation({trans, _@funlist}, _@vars, _@context))()",
[
{runtime, erl_syntax:atom(Runtime)},
{funlist, FunListAst},
{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, {TagSrc, ArgExpr}} || {{identifier, _, Ident} = TagSrc, 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};
{TagSrc, Expr} ->
{WsAcc1, ExprAst} = compile({value, TagSrc, 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, <<Acc/binary, ${>>, Parts);
split_string(<<"}}", T/binary>>, Acc, Parts) ->
split_string(T, <<Acc/binary, $}>>, Parts);
split_string(<<"{", T/binary>>, Acc, Parts) ->
split_string_name(T, <<>>, [Acc|Parts]);
split_string(<<C/utf8, T/binary>>, Acc, Parts) ->
split_string(T, <<Acc/binary, C/utf8>>, 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(<<C/utf8, T/binary>>, Acc, Parts) ->
split_string_name(T, <<Acc/binary, C/utf8>>, Parts).