|
@@ -38,21 +38,21 @@
|
|
-export([compile/2, compile/3, compile/4, compile/5, parse/1, scan/1]).
|
|
-export([compile/2, compile/3, compile/4, compile/5, parse/1, scan/1]).
|
|
|
|
|
|
-record(dtl_context, {
|
|
-record(dtl_context, {
|
|
- local_scopes = [],
|
|
|
|
- block_dict = dict:new(),
|
|
|
|
- auto_escape = off,
|
|
|
|
- doc_root = "",
|
|
|
|
- parse_trail = []}).
|
|
|
|
|
|
+ local_scopes = [],
|
|
|
|
+ block_dict = dict:new(),
|
|
|
|
+ auto_escape = off,
|
|
|
|
+ doc_root = "",
|
|
|
|
+ parse_trail = []}).
|
|
|
|
|
|
compile(File, Module) ->
|
|
compile(File, Module) ->
|
|
compile(File, "", Module).
|
|
compile(File, "", Module).
|
|
|
|
|
|
compile(File, DocRoot, Module) ->
|
|
compile(File, DocRoot, Module) ->
|
|
compile(File, DocRoot, Module, "render").
|
|
compile(File, DocRoot, Module, "render").
|
|
-
|
|
|
|
|
|
+
|
|
compile(File, DocRoot, Module, Function) ->
|
|
compile(File, DocRoot, Module, Function) ->
|
|
compile(File, DocRoot, Module, Function, "ebin").
|
|
compile(File, DocRoot, Module, Function, "ebin").
|
|
-
|
|
|
|
|
|
+
|
|
compile(File, DocRoot, Module, Function, OutDir) ->
|
|
compile(File, DocRoot, Module, Function, OutDir) ->
|
|
case parse(File) of
|
|
case parse(File) of
|
|
{ok, DjangoAst} ->
|
|
{ok, DjangoAst} ->
|
|
@@ -77,39 +77,39 @@ compile(File, DocRoot, Module, Function, OutDir) ->
|
|
|
|
|
|
case compile:forms(Forms) of
|
|
case compile:forms(Forms) of
|
|
{ok, Module1, Bin} ->
|
|
{ok, Module1, Bin} ->
|
|
- Path = filename:join([OutDir, atom_to_list(Module1) ++ ".beam"]),
|
|
|
|
- case file:write_file(Path, Bin) of
|
|
|
|
- ok ->
|
|
|
|
- code:purge(Module1),
|
|
|
|
- case code:load_binary(Module1, atom_to_list(Module1) ++ ".erl", Bin) of
|
|
|
|
- {module, _} -> ok;
|
|
|
|
- _ -> {error, "code reload failed"}
|
|
|
|
|
|
+ Path = filename:join([OutDir, atom_to_list(Module1) ++ ".beam"]),
|
|
|
|
+ case file:write_file(Path, Bin) of
|
|
|
|
+ ok ->
|
|
|
|
+ code:purge(Module1),
|
|
|
|
+ case code:load_binary(Module1, atom_to_list(Module1) ++ ".erl", Bin) of
|
|
|
|
+ {module, _} -> ok;
|
|
|
|
+ _ -> {error, "code reload failed"}
|
|
|
|
+ end;
|
|
|
|
+ _ ->
|
|
|
|
+ {error, "beam generation failed"}
|
|
end;
|
|
end;
|
|
_ ->
|
|
_ ->
|
|
- {error, "beam generation failed"}
|
|
|
|
- end;
|
|
|
|
- _ ->
|
|
|
|
- {error, "compilation failed"}
|
|
|
|
|
|
+ {error, "compilation failed"}
|
|
end;
|
|
end;
|
|
Error ->
|
|
Error ->
|
|
Error
|
|
Error
|
|
- end.
|
|
|
|
|
|
+ end.
|
|
|
|
+
|
|
|
|
|
|
-
|
|
|
|
scan(File) ->
|
|
scan(File) ->
|
|
case file:read_file(File) of
|
|
case file:read_file(File) of
|
|
{ok, B} ->
|
|
{ok, B} ->
|
|
- erlydtl_scanner:scan(binary_to_list(B));
|
|
|
|
- _ ->
|
|
|
|
- {error, "reading " ++ File ++ " failed "}
|
|
|
|
|
|
+ erlydtl_scanner:scan(binary_to_list(B));
|
|
|
|
+ _ ->
|
|
|
|
+ {error, "reading " ++ File ++ " failed "}
|
|
end.
|
|
end.
|
|
|
|
|
|
parse(File) ->
|
|
parse(File) ->
|
|
case scan(File) of
|
|
case scan(File) of
|
|
- {ok, Tokens} ->
|
|
|
|
- erlydtl_parser:parse(Tokens);
|
|
|
|
- Err ->
|
|
|
|
- Err
|
|
|
|
|
|
+ {ok, Tokens} ->
|
|
|
|
+ erlydtl_parser:parse(Tokens);
|
|
|
|
+ Err ->
|
|
|
|
+ Err
|
|
end.
|
|
end.
|
|
|
|
|
|
full_path(File, DocRoot) ->
|
|
full_path(File, DocRoot) ->
|
|
@@ -123,87 +123,88 @@ body_ast([{extends, {string_literal, _Pos, String}} | ThisAst], Context) ->
|
|
File = full_path(unescape_string_literal(String), Context#dtl_context.doc_root),
|
|
File = full_path(unescape_string_literal(String), Context#dtl_context.doc_root),
|
|
case lists:member(File, Context#dtl_context.parse_trail) of
|
|
case lists:member(File, Context#dtl_context.parse_trail) of
|
|
true ->
|
|
true ->
|
|
- {error, "Circular file inclusion!"};
|
|
|
|
|
|
+ {error, "Circular file inclusion!"};
|
|
_ ->
|
|
_ ->
|
|
- {ok, ParentAst} = parse(File),
|
|
|
|
- BlockDict = lists:foldl(
|
|
|
|
- fun
|
|
|
|
- ({block, {identifier, _, Name}, Contents}, Dict) ->
|
|
|
|
- dict:store(Name, Contents, Dict);
|
|
|
|
- (_, Dict) ->
|
|
|
|
- Dict
|
|
|
|
- end, dict:new(), ThisAst),
|
|
|
|
- body_ast(ParentAst, Context#dtl_context{
|
|
|
|
- block_dict =
|
|
|
|
- dict:merge(fun(_Key, _ParentVal, ChildVal) -> ChildVal end,
|
|
|
|
- BlockDict, Context#dtl_context.block_dict),
|
|
|
|
- parse_trail = [File | Context#dtl_context.parse_trail]
|
|
|
|
- })
|
|
|
|
|
|
+ {ok, ParentAst} = parse(File),
|
|
|
|
+ BlockDict = lists:foldl(
|
|
|
|
+ fun
|
|
|
|
+ ({block, {identifier, _, Name}, Contents}, Dict) ->
|
|
|
|
+ dict:store(Name, Contents, Dict);
|
|
|
|
+ (_, Dict) ->
|
|
|
|
+ Dict
|
|
|
|
+ end, dict:new(), ThisAst),
|
|
|
|
+ body_ast(ParentAst, Context#dtl_context{
|
|
|
|
+ block_dict =
|
|
|
|
+ dict:merge(fun(_Key, _ParentVal, ChildVal) -> ChildVal end,
|
|
|
|
+ BlockDict, Context#dtl_context.block_dict),
|
|
|
|
+ parse_trail = [File | Context#dtl_context.parse_trail]
|
|
|
|
+ })
|
|
end;
|
|
end;
|
|
|
|
|
|
body_ast(DjangoAst, Context) ->
|
|
body_ast(DjangoAst, Context) ->
|
|
erl_syntax:list(
|
|
erl_syntax:list(
|
|
- lists:map(fun
|
|
|
|
- ({'block', {identifier, _, Name}, Contents}) ->
|
|
|
|
- Block = case dict:find(Name, Context#dtl_context.block_dict) of
|
|
|
|
- {ok, ChildBlock} ->
|
|
|
|
- ChildBlock;
|
|
|
|
- _ ->
|
|
|
|
- Contents
|
|
|
|
- end,
|
|
|
|
- body_ast(Block, Context);
|
|
|
|
- ({'comment', _Contents}) ->
|
|
|
|
- erl_syntax:list([]);
|
|
|
|
- ({'autoescape', {identifier, _, OnOrOff}, Contents}) ->
|
|
|
|
- body_ast(Contents, Context#dtl_context{auto_escape = list_to_atom(OnOrOff)});
|
|
|
|
- ({'text', _Pos, String}) ->
|
|
|
|
- erl_syntax:string(String);
|
|
|
|
- ({'string_literal', _Pos, String}) ->
|
|
|
|
- erl_syntax:string(unescape_string_literal(String));
|
|
|
|
- ({'number_literal', _Pos, Number}) ->
|
|
|
|
- erl_syntax:string(Number);
|
|
|
|
- ({'variable', Variable}) ->
|
|
|
|
- resolve_variable_ast(Variable, Context);
|
|
|
|
- ({'tag', {identifier, _, Name}, Args}) ->
|
|
|
|
- tag_ast(Name, Args, Context);
|
|
|
|
- ({'include', {string_literal, _, File}}) ->
|
|
|
|
- FilePath = full_path(unescape_string_literal(File), Context#dtl_context.doc_root),
|
|
|
|
- {ok, IncludeAst} = parse(FilePath),
|
|
|
|
- body_ast(IncludeAst,
|
|
|
|
- Context#dtl_context{parse_trail =
|
|
|
|
- [FilePath | Context#dtl_context.parse_trail]});
|
|
|
|
- ({'if', {variable, Variable}, Contents}) ->
|
|
|
|
- ifelse_ast(Variable, body_ast(Contents, Context),
|
|
|
|
- erl_syntax:list([]), Context);
|
|
|
|
- ({'if', {'not', {variable, Variable}}, Contents}) ->
|
|
|
|
- ifelse_ast(Variable, erl_syntax:list([]),
|
|
|
|
- body_ast(Contents, Context), Context);
|
|
|
|
- ({'ifelse', {variable, Variable}, IfContents, ElseContents}) ->
|
|
|
|
- ifelse_ast(Variable, body_ast(IfContents, Context),
|
|
|
|
- body_ast(ElseContents, Context), Context);
|
|
|
|
- ({'ifelse', {'not', {variable, Variable}}, IfContents, ElseContents}) ->
|
|
|
|
- ifelse_ast(Variable, body_ast(ElseContents, Context),
|
|
|
|
- body_ast(IfContents, Context), Context);
|
|
|
|
- ({'apply_filter', Variable, Filter}) ->
|
|
|
|
- filter_ast(Variable, Filter, Context);
|
|
|
|
- ({'for', {'in', {identifier, _, Iterator}, {identifier, _, List}}, Contents}) ->
|
|
|
|
- for_loop_ast(Iterator, List, Contents, Context);
|
|
|
|
- ({'for', {'in', IteratorList, {identifier, _, List}}, Contents}) when is_list(IteratorList) ->
|
|
|
|
- for_list_loop_ast(IteratorList, List, Contents, Context)
|
|
|
|
- end, DjangoAst)
|
|
|
|
|
|
+ lists:map(
|
|
|
|
+ fun
|
|
|
|
+ ({'block', {identifier, _, Name}, Contents}) ->
|
|
|
|
+ Block = case dict:find(Name, Context#dtl_context.block_dict) of
|
|
|
|
+ {ok, ChildBlock} ->
|
|
|
|
+ ChildBlock;
|
|
|
|
+ _ ->
|
|
|
|
+ Contents
|
|
|
|
+ end,
|
|
|
|
+ body_ast(Block, Context);
|
|
|
|
+ ({'comment', _Contents}) ->
|
|
|
|
+ erl_syntax:list([]);
|
|
|
|
+ ({'autoescape', {identifier, _, OnOrOff}, Contents}) ->
|
|
|
|
+ body_ast(Contents, Context#dtl_context{auto_escape = list_to_atom(OnOrOff)});
|
|
|
|
+ ({'text', _Pos, String}) ->
|
|
|
|
+ erl_syntax:string(String);
|
|
|
|
+ ({'string_literal', _Pos, String}) ->
|
|
|
|
+ erl_syntax:string(unescape_string_literal(String));
|
|
|
|
+ ({'number_literal', _Pos, Number}) ->
|
|
|
|
+ erl_syntax:string(Number);
|
|
|
|
+ ({'variable', Variable}) ->
|
|
|
|
+ resolve_variable_ast(Variable, Context);
|
|
|
|
+ ({'tag', {identifier, _, Name}, Args}) ->
|
|
|
|
+ tag_ast(Name, Args, Context);
|
|
|
|
+ ({'include', {string_literal, _, File}}) ->
|
|
|
|
+ FilePath = full_path(unescape_string_literal(File), Context#dtl_context.doc_root),
|
|
|
|
+ {ok, IncludeAst} = parse(FilePath),
|
|
|
|
+ body_ast(IncludeAst,
|
|
|
|
+ Context#dtl_context{parse_trail =
|
|
|
|
+ [FilePath | Context#dtl_context.parse_trail]});
|
|
|
|
+ ({'if', {variable, Variable}, Contents}) ->
|
|
|
|
+ ifelse_ast(Variable, body_ast(Contents, Context),
|
|
|
|
+ erl_syntax:list([]), Context);
|
|
|
|
+ ({'if', {'not', {variable, Variable}}, Contents}) ->
|
|
|
|
+ ifelse_ast(Variable, erl_syntax:list([]),
|
|
|
|
+ body_ast(Contents, Context), Context);
|
|
|
|
+ ({'ifelse', {variable, Variable}, IfContents, ElseContents}) ->
|
|
|
|
+ ifelse_ast(Variable, body_ast(IfContents, Context),
|
|
|
|
+ body_ast(ElseContents, Context), Context);
|
|
|
|
+ ({'ifelse', {'not', {variable, Variable}}, IfContents, ElseContents}) ->
|
|
|
|
+ ifelse_ast(Variable, body_ast(ElseContents, Context),
|
|
|
|
+ body_ast(IfContents, Context), Context);
|
|
|
|
+ ({'apply_filter', Variable, Filter}) ->
|
|
|
|
+ filter_ast(Variable, Filter, Context);
|
|
|
|
+ ({'for', {'in', {identifier, _, Iterator}, {identifier, _, List}}, Contents}) ->
|
|
|
|
+ for_loop_ast(Iterator, List, Contents, Context);
|
|
|
|
+ ({'for', {'in', IteratorList, {identifier, _, List}}, Contents}) when is_list(IteratorList) ->
|
|
|
|
+ for_list_loop_ast(IteratorList, List, Contents, Context)
|
|
|
|
+ end, DjangoAst)
|
|
).
|
|
).
|
|
|
|
|
|
filter_ast(Variable, Filter, Context) ->
|
|
filter_ast(Variable, Filter, Context) ->
|
|
-% the escape filter is special; it is always applied last, so we have to go digging for it
|
|
|
|
|
|
+ % the escape filter is special; it is always applied last, so we have to go digging for it
|
|
|
|
|
|
-% AutoEscape = 'did' means we (will have) decided whether to escape the current variable,
|
|
|
|
-% so don't do any more escaping
|
|
|
|
|
|
+ % AutoEscape = 'did' means we (will have) decided whether to escape the current variable,
|
|
|
|
+ % so don't do any more escaping
|
|
case search_for_escape_filter(Variable, Filter, Context) of
|
|
case search_for_escape_filter(Variable, Filter, Context) of
|
|
on ->
|
|
on ->
|
|
- erl_syntax:application(erl_syntax:atom(erlydtl_filters), erl_syntax:atom(force_escape),
|
|
|
|
- [filter_ast_noescape(Variable, Filter, Context#dtl_context{auto_escape = did})]);
|
|
|
|
- _ ->
|
|
|
|
- filter_ast_noescape(Variable, Filter, Context#dtl_context{auto_escape = did})
|
|
|
|
|
|
+ erl_syntax:application(erl_syntax:atom(erlydtl_filters), erl_syntax:atom(force_escape),
|
|
|
|
+ [filter_ast_noescape(Variable, Filter, Context#dtl_context{auto_escape = did})]);
|
|
|
|
+ _ ->
|
|
|
|
+ filter_ast_noescape(Variable, Filter, Context#dtl_context{auto_escape = did})
|
|
end.
|
|
end.
|
|
|
|
|
|
filter_ast_noescape(Variable, [{identifier, _, "escape"}], Context) ->
|
|
filter_ast_noescape(Variable, [{identifier, _, "escape"}], Context) ->
|
|
@@ -211,13 +212,13 @@ filter_ast_noescape(Variable, [{identifier, _, "escape"}], Context) ->
|
|
filter_ast_noescape(Variable, [{identifier, _, Name} | Arg], Context) ->
|
|
filter_ast_noescape(Variable, [{identifier, _, Name} | Arg], Context) ->
|
|
erl_syntax:application(erl_syntax:atom(erlydtl_filters), erl_syntax:atom(Name),
|
|
erl_syntax:application(erl_syntax:atom(erlydtl_filters), erl_syntax:atom(Name),
|
|
[body_ast([Variable], Context) | case Arg of
|
|
[body_ast([Variable], Context) | case Arg of
|
|
- [{string_literal, _, ArgName}] ->
|
|
|
|
- [erl_syntax:string(unescape_string_literal(ArgName))];
|
|
|
|
- [{number_literal, _, ArgName}] ->
|
|
|
|
- [erl_syntax:integer(list_to_integer(ArgName))];
|
|
|
|
- _ ->
|
|
|
|
- []
|
|
|
|
- end]).
|
|
|
|
|
|
+ [{string_literal, _, ArgName}] ->
|
|
|
|
+ [erl_syntax:string(unescape_string_literal(ArgName))];
|
|
|
|
+ [{number_literal, _, ArgName}] ->
|
|
|
|
+ [erl_syntax:integer(list_to_integer(ArgName))];
|
|
|
|
+ _ ->
|
|
|
|
+ []
|
|
|
|
+ end]).
|
|
|
|
|
|
search_for_escape_filter(_, _, #dtl_context{auto_escape = on}) ->
|
|
search_for_escape_filter(_, _, #dtl_context{auto_escape = on}) ->
|
|
on;
|
|
on;
|
|
@@ -238,23 +239,23 @@ resolve_variable_ast({{identifier, _, VarName}}, Context) ->
|
|
|
|
|
|
resolve_variable_ast({{identifier, _, VarName}, {identifier, _, AttrName}}, Context) ->
|
|
resolve_variable_ast({{identifier, _, VarName}, {identifier, _, AttrName}}, Context) ->
|
|
erl_syntax:application(erl_syntax:atom(proplists), erl_syntax:atom(get_value),
|
|
erl_syntax:application(erl_syntax:atom(proplists), erl_syntax:atom(get_value),
|
|
- [erl_syntax:atom(AttrName), resolve_variable_name_ast(VarName, Context)]).
|
|
|
|
|
|
+ [erl_syntax:atom(AttrName), resolve_variable_name_ast(VarName, Context)]).
|
|
|
|
|
|
resolve_variable_name_ast(VarName, Context) ->
|
|
resolve_variable_name_ast(VarName, Context) ->
|
|
VarValue = lists:foldl(fun(Scope, Value) ->
|
|
VarValue = lists:foldl(fun(Scope, Value) ->
|
|
- case Value of
|
|
|
|
- undefined ->
|
|
|
|
- proplists:get_value(list_to_atom(VarName), Scope);
|
|
|
|
- _ ->
|
|
|
|
- Value
|
|
|
|
- end
|
|
|
|
|
|
+ case Value of
|
|
|
|
+ undefined ->
|
|
|
|
+ proplists:get_value(list_to_atom(VarName), Scope);
|
|
|
|
+ _ ->
|
|
|
|
+ Value
|
|
|
|
+ end
|
|
end, undefined, Context#dtl_context.local_scopes),
|
|
end, undefined, Context#dtl_context.local_scopes),
|
|
VarValue1 = case VarValue of
|
|
VarValue1 = case VarValue of
|
|
- undefined ->
|
|
|
|
- erl_syntax:application(erl_syntax:atom(proplists), erl_syntax:atom(get_value),
|
|
|
|
- [erl_syntax:atom(VarName), erl_syntax:variable("Variables")]);
|
|
|
|
- _ ->
|
|
|
|
- VarValue
|
|
|
|
|
|
+ undefined ->
|
|
|
|
+ erl_syntax:application(erl_syntax:atom(proplists), erl_syntax:atom(get_value),
|
|
|
|
+ [erl_syntax:atom(VarName), erl_syntax:variable("Variables")]);
|
|
|
|
+ _ ->
|
|
|
|
+ VarValue
|
|
end,
|
|
end,
|
|
case Context#dtl_context.auto_escape of
|
|
case Context#dtl_context.auto_escape of
|
|
on ->
|
|
on ->
|
|
@@ -267,56 +268,56 @@ resolve_variable_name_ast(VarName, Context) ->
|
|
ifelse_ast(Variable, IfContentsAst, ElseContentsAst, Context) ->
|
|
ifelse_ast(Variable, IfContentsAst, ElseContentsAst, Context) ->
|
|
erl_syntax:case_expr(resolve_variable_ast(Variable, Context),
|
|
erl_syntax:case_expr(resolve_variable_ast(Variable, Context),
|
|
[erl_syntax:clause([erl_syntax:string("")], none,
|
|
[erl_syntax:clause([erl_syntax:string("")], none,
|
|
- [ElseContentsAst]),
|
|
|
|
- erl_syntax:clause([erl_syntax:atom(undefined)], none,
|
|
|
|
- [ElseContentsAst]),
|
|
|
|
- erl_syntax:clause([erl_syntax:integer(0)], none,
|
|
|
|
- [ElseContentsAst]),
|
|
|
|
- erl_syntax:clause([erl_syntax:underscore()], none,
|
|
|
|
- [IfContentsAst])
|
|
|
|
|
|
+ [ElseContentsAst]),
|
|
|
|
+ erl_syntax:clause([erl_syntax:atom(undefined)], none,
|
|
|
|
+ [ElseContentsAst]),
|
|
|
|
+ erl_syntax:clause([erl_syntax:integer(0)], none,
|
|
|
|
+ [ElseContentsAst]),
|
|
|
|
+ erl_syntax:clause([erl_syntax:underscore()], none,
|
|
|
|
+ [IfContentsAst])
|
|
]).
|
|
]).
|
|
|
|
|
|
for_loop_ast(Iterator, List, Contents, Context) ->
|
|
for_loop_ast(Iterator, List, Contents, Context) ->
|
|
erl_syntax:application(erl_syntax:atom(lists), erl_syntax:atom(map),
|
|
erl_syntax:application(erl_syntax:atom(lists), erl_syntax:atom(map),
|
|
- [erl_syntax:fun_expr([
|
|
|
|
- erl_syntax:clause([erl_syntax:variable("Var_" ++ Iterator)],
|
|
|
|
- none, [body_ast(Contents,
|
|
|
|
- Context#dtl_context{local_scopes =
|
|
|
|
- [[{list_to_atom(Iterator), erl_syntax:variable("Var_" ++ Iterator)}]
|
|
|
|
- | Context#dtl_context.local_scopes]
|
|
|
|
- })]
|
|
|
|
- )]),
|
|
|
|
- resolve_variable_name_ast(list_to_atom(List), Context)]).
|
|
|
|
|
|
+ [erl_syntax:fun_expr([
|
|
|
|
+ erl_syntax:clause([erl_syntax:variable("Var_" ++ Iterator)],
|
|
|
|
+ none, [body_ast(Contents,
|
|
|
|
+ Context#dtl_context{local_scopes =
|
|
|
|
+ [[{list_to_atom(Iterator), erl_syntax:variable("Var_" ++ Iterator)}]
|
|
|
|
+ | Context#dtl_context.local_scopes]
|
|
|
|
+ })]
|
|
|
|
+ )]),
|
|
|
|
+ resolve_variable_name_ast(list_to_atom(List), Context)]).
|
|
|
|
|
|
for_list_loop_ast(IteratorList, List, Contents, Context) ->
|
|
for_list_loop_ast(IteratorList, List, Contents, Context) ->
|
|
erl_syntax:application(erl_syntax:atom(lists), erl_syntax:atom(map),
|
|
erl_syntax:application(erl_syntax:atom(lists), erl_syntax:atom(map),
|
|
- [erl_syntax:fun_expr([
|
|
|
|
- erl_syntax:clause([erl_syntax:list(
|
|
|
|
- lists:map(fun({identifier, _, Iterator}) ->
|
|
|
|
- erl_syntax:variable("Var_" ++ Iterator)
|
|
|
|
- end, IteratorList))],
|
|
|
|
- none, [body_ast(Contents,
|
|
|
|
- Context#dtl_context{local_scopes = [lists:map(fun({identifier, _, Iterator}) ->
|
|
|
|
- {list_to_atom(Iterator), erl_syntax:variable("Var_" ++ Iterator)}
|
|
|
|
- end, IteratorList)
|
|
|
|
- | Context#dtl_context.local_scopes]})]
|
|
|
|
- )]),
|
|
|
|
- resolve_variable_name_ast(list_to_atom(List), Context)]).
|
|
|
|
|
|
+ [erl_syntax:fun_expr([
|
|
|
|
+ erl_syntax:clause([erl_syntax:list(
|
|
|
|
+ lists:map(fun({identifier, _, Iterator}) ->
|
|
|
|
+ erl_syntax:variable("Var_" ++ Iterator)
|
|
|
|
+ end, IteratorList))],
|
|
|
|
+ none, [body_ast(Contents,
|
|
|
|
+ Context#dtl_context{local_scopes = [lists:map(fun({identifier, _, Iterator}) ->
|
|
|
|
+ {list_to_atom(Iterator), erl_syntax:variable("Var_" ++ Iterator)}
|
|
|
|
+ end, IteratorList)
|
|
|
|
+ | Context#dtl_context.local_scopes]})]
|
|
|
|
+ )]),
|
|
|
|
+ resolve_variable_name_ast(list_to_atom(List), Context)]).
|
|
|
|
|
|
tag_ast(Name, Args, Context) ->
|
|
tag_ast(Name, Args, Context) ->
|
|
InterpretedArgs = lists:map(fun
|
|
InterpretedArgs = lists:map(fun
|
|
- ({{identifier, _, Key}, {string_literal, _, Value}}) ->
|
|
|
|
- {list_to_atom(Key), erl_syntax:string(unescape_string_literal(Value))};
|
|
|
|
- ({{identifier, _, Key}, {variable, Value}}) ->
|
|
|
|
- {list_to_atom(Key), resolve_variable_ast(Value, Context)}
|
|
|
|
|
|
+ ({{identifier, _, Key}, {string_literal, _, Value}}) ->
|
|
|
|
+ {list_to_atom(Key), erl_syntax:string(unescape_string_literal(Value))};
|
|
|
|
+ ({{identifier, _, Key}, {variable, Value}}) ->
|
|
|
|
+ {list_to_atom(Key), resolve_variable_ast(Value, Context)}
|
|
end, Args),
|
|
end, Args),
|
|
Source = filename:join([erlydtl_deps:get_base_dir(), "priv", "tags", Name]),
|
|
Source = filename:join([erlydtl_deps:get_base_dir(), "priv", "tags", Name]),
|
|
case parse(Source) of
|
|
case parse(Source) of
|
|
{ok, TagAst} ->
|
|
{ok, TagAst} ->
|
|
- body_ast(TagAst, Context#dtl_context{
|
|
|
|
- local_scopes = [ InterpretedArgs | Context#dtl_context.local_scopes ]});
|
|
|
|
- _ ->
|
|
|
|
- {error, Name, "Loading tag source failed: " ++ Source}
|
|
|
|
|
|
+ body_ast(TagAst, Context#dtl_context{
|
|
|
|
+ local_scopes = [ InterpretedArgs | Context#dtl_context.local_scopes ]});
|
|
|
|
+ _ ->
|
|
|
|
+ {error, Name, "Loading tag source failed: " ++ Source}
|
|
end.
|
|
end.
|
|
|
|
|
|
unescape_string_literal(String) ->
|
|
unescape_string_literal(String) ->
|