Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Use merl [1] for compiling the erlang AST tree (fixes #123).

Also, `number_literals` are now converted to erlang integers in the scanner.

[1] https://github.com/richcarl/merl
  • Loading branch information...
commit a1c91987b709c49ec3791b7aca3b3ae4a532069a 1 parent 8c876fd
@kaos kaos authored
View
8 .travis.yml
@@ -1,7 +1,11 @@
language: erlang
otp_release:
-# - R16B03 (not yet available on travis)
+# - 17.0-rc1
+ - R16B03-1
- R16B02
- R15B03
# - R14B04 (seems lists:concat/1 is broken on R14B04..)
-script: "make test"
+
+# since Travis is naughty and calls rebar get-deps behind our backs,
+# we'll have to clean it up and build merl our selves..
+script: "make -C deps/merl && make test"
View
13 Makefile
@@ -4,20 +4,28 @@ REBAR=./rebar $(REBAR_ARGS)
all: compile
-compile: check-slex
+compile: check-slex deps/merl
@$(REBAR) compile
check-slex: src/erlydtl_scanner.erl
src/erlydtl_scanner.erl: src/erlydtl_scanner.slex
@echo Notice: $@ is outdated by $<, consider running "'make slex'".
+deps/merl:
+ @$(REBAR) get-deps
+ @echo "Make merl..." ; $(MAKE) -C deps/merl
+
+update:
+ @$(REBAR) update-deps
+ @echo "Make merl..." ; $(MAKE) -C deps/merl
+
compile_test:
-mkdir -p ebintest
$(ERLC) -o tests/src -I include/erlydtl_preparser.hrl tests/src/erlydtl_extension_testparser.yrl
$(ERL) -make
test: compile compile_test
- $(ERL) -noshell -pa ebin -pa ebintest \
+ $(ERL) -noshell -pa ebin -pa ebintest -pa deps/merl/ebin \
-eval \
"try \
erlydtl_functional_tests:run_tests(), \
@@ -43,6 +51,7 @@ plt:
tools webtool hipe inets eunit
clean:
+ @echo "Clean merl..." ; $(MAKE) -C deps/merl clean
@$(REBAR) clean
rm -fv ebintest/*
rm -fv erl_crash.dump
View
5 rebar.config
@@ -2,3 +2,8 @@
{erl_opts, [debug_info]}.
{yrl_opts, [{includefile, "include/erlydtl_preparser.hrl"}]}.
+{deps,
+ [{merl, ".*",
+ {git, "git://github.com/erlydtl/merl.git", {branch, "erlydtl"}},
+ [raw]}
+ ]}.
View
727 src/erlydtl_compiler.erl
@@ -59,6 +59,7 @@
unescape_string_literal/1
]).
+-include_lib("merl/include/merl.hrl").
-include("erlydtl_ext.hrl").
default_options() -> [verbose, report].
@@ -220,12 +221,12 @@ env_default_opts() ->
shorten_filename(Name0) ->
{ok,Cwd} = file:get_cwd(),
case lists:prefix(Cwd, Name0) of
- false -> Name0;
- true ->
- case lists:nthtail(length(Cwd), Name0) of
- "/"++N -> N;
- N -> N
- end
+ false -> Name0;
+ true ->
+ case lists:nthtail(length(Cwd), Name0) of
+ "/"++N -> N;
+ N -> N
+ end
end.
compile(Context) ->
@@ -291,16 +292,7 @@ compile_multiple_to_binary(Dir, ParserResults, Context0) ->
{FilePath, CheckSum},
body_ast(DjangoParseTree, Ctx, TreeWalker)),
FunctionName = filename:rootname(filename:basename(File)),
- Function1 = erl_syntax:function(
- erl_syntax:atom(FunctionName),
- [erl_syntax:clause(
- [erl_syntax:variable("_Variables")],
- none,
- [erl_syntax:application(
- none, erl_syntax:atom(FunctionName),
- [erl_syntax:variable("_Variables"), erl_syntax:list([])])
- ])
- ]),
+ Function1 = ?Q("_@FunctionName@(_Variables) -> _@FunctionName@(_Variables, [])"),
Function2 = erl_syntax:function(
erl_syntax:atom(FunctionName),
[erl_syntax:clause(
@@ -499,7 +491,7 @@ get_error_info_opts(Class, Options) ->
end,
{Value, proplists:get_bool(Key, Options)}
end || Flag <- Flags].
-
+
init_treewalker(Context) ->
TreeWalker = #treewalker{},
case call_extension(Context, init_treewalker, [TreeWalker]) of
@@ -682,16 +674,10 @@ custom_tags_clauses_ast1([], _ExcludeTags, ClauseAcc, InfoAcc, Context, TreeWalk
{{DefaultAst, DefaultInfo}, TreeWalker1} =
case call_extension(Context, custom_tag_ast, [Context, TreeWalker]) of
undefined ->
- {{erl_syntax:clause(
- [erl_syntax:variable("_TagName"), erl_syntax:underscore(), erl_syntax:underscore()],
- none,
- [erl_syntax:list([])]),
- InfoAcc},
- TreeWalker};
+ {{?Q("(_TagName, _, _) -> []"), InfoAcc}, TreeWalker};
{{ExtAst, ExtInfo}, ExtTreeWalker} ->
- Clause = erl_syntax:clause(
- [erl_syntax:variable("TagName"), erl_syntax:variable("_Variables"), erl_syntax:variable("RenderOptions")],
- none, options_match_ast(Context, ExtTreeWalker) ++ [ExtAst]),
+ Clause = ?Q("(TagName, _Variables, RenderOptions) -> _@tag",
+ [{tag, options_match_ast(Context, ExtTreeWalker) ++ [ExtAst]}]),
{{Clause, merge_info(ExtInfo, InfoAcc)}, ExtTreeWalker}
end,
{{lists:reverse([DefaultAst|ClauseAcc]), DefaultInfo}, TreeWalker1};
@@ -709,12 +695,7 @@ custom_tags_clauses_ast1([Tag|CustomTags], ExcludeTags, ClauseAcc, InfoAcc, Cont
{CustomTagFile, CheckSum},
body_ast(DjangoParseTree, Context, TreeWalker)),
MatchAst = options_match_ast(Context, TreeWalker),
- Clause = erl_syntax:clause(
- [erl_syntax:atom(Tag),
- erl_syntax:variable("_Variables"),
- erl_syntax:variable("RenderOptions")],
- none,
- MatchAst ++ [BodyAst]),
+ Clause = ?Q("(_@Tag@, _Variables, RenderOptions) -> _@MatchAst, _@BodyAst"),
custom_tags_clauses_ast1(
CustomTags, [Tag|ExcludeTags],
[Clause|ClauseAcc], merge_info(BodyAstInfo, InfoAcc),
@@ -729,12 +710,8 @@ custom_tags_clauses_ast1([Tag|CustomTags], ExcludeTags, ClauseAcc, InfoAcc, Cont
CustomTags, [Tag | ExcludeTags],
ClauseAcc, InfoAcc, Context, TreeWalker);
{{Ast, Info}, TW} ->
- Clause = erl_syntax:clause(
- [erl_syntax:atom(Tag),
- erl_syntax:variable("_Variables"),
- erl_syntax:variable("RenderOptions")],
- none,
- options_match_ast(Context, TW) ++ [Ast]),
+ Clause = ?Q("(_@Tag@, _Variables, RenderOptions) -> _@match, _@Ast",
+ [{match, options_match_ast(Context, TW)}]),
custom_tags_clauses_ast1(
CustomTags, [Tag | ExcludeTags],
[Clause|ClauseAcc], merge_info(Info, InfoAcc),
@@ -744,76 +721,34 @@ custom_tags_clauses_ast1([Tag|CustomTags], ExcludeTags, ClauseAcc, InfoAcc, Cont
end.
dependencies_function(Dependencies) ->
- erl_syntax:function(
- erl_syntax:atom(dependencies),
- [erl_syntax:clause(
- [], none,
- [erl_syntax:list(
- lists:map(
- fun ({XFile, XCheckSum}) ->
- erl_syntax:tuple([erl_syntax:string(XFile), erl_syntax:string(XCheckSum)])
- end,
- Dependencies))
- ])
- ]).
+ ?Q("dependencies() -> _@Dependencies@.").
translatable_strings_function(TranslatableStrings) ->
- erl_syntax:function(
- erl_syntax:atom(translatable_strings),
- [erl_syntax:clause(
- [], none,
- [erl_syntax:list(
- lists:map(
- fun(String) ->
- erl_syntax:string(String)
- end,
- TranslatableStrings))
- ])
- ]).
+ ?Q("translatable_strings() -> _@TranslatableStrings@.").
translated_blocks_function(TranslatedBlocks) ->
- erl_syntax:function(
- erl_syntax:atom(translated_blocks),
- [erl_syntax:clause(
- [], none,
- [erl_syntax:list(
- lists:map(
- fun(String) ->
- erl_syntax:string(String)
- end,
- TranslatedBlocks))
- ])
- ]).
+ ?Q("translated_blocks() -> _@TranslatedBlocks@.").
variables_function(Variables) ->
- erl_syntax:function(
- erl_syntax:atom(variables),
- [erl_syntax:clause(
- [], none,
- [erl_syntax:list(
- [erl_syntax:atom(S) || S <- lists:usort(Variables)])
- ])
- ]).
+ ?Q("variables() -> _@vars.",
+ [{vars, merl:term(lists:usort(Variables))}]).
custom_forms(Dir, Module, Functions, AstInfo) ->
- ModuleAst = erl_syntax:attribute(erl_syntax:atom(module), [erl_syntax:atom(Module)]),
- ExportAst = erl_syntax:attribute(
- erl_syntax:atom(export),
- [erl_syntax:list(
- [erl_syntax:arity_qualifier(erl_syntax:atom(source_dir), erl_syntax:integer(0)),
- erl_syntax:arity_qualifier(erl_syntax:atom(dependencies), erl_syntax:integer(0)),
- erl_syntax:arity_qualifier(erl_syntax:atom(translatable_strings), erl_syntax:integer(0))
- | lists:foldl(
- fun({FunctionName, _, _}, Acc) ->
- [erl_syntax:arity_qualifier(erl_syntax:atom(FunctionName), erl_syntax:integer(1)),
- erl_syntax:arity_qualifier(erl_syntax:atom(FunctionName), erl_syntax:integer(2))
- |Acc]
- end, [], Functions)
- ])
- ]),
- SourceFunctionAst = erl_syntax:function(
- erl_syntax:atom(source_dir),
- [erl_syntax:clause([], none, [erl_syntax:string(Dir)])]),
+ Exported = [erl_syntax:arity_qualifier(erl_syntax:atom(source_dir), erl_syntax:integer(0)),
+ erl_syntax:arity_qualifier(erl_syntax:atom(dependencies), erl_syntax:integer(0)),
+ erl_syntax:arity_qualifier(erl_syntax:atom(translatable_strings), erl_syntax:integer(0))
+ | lists:foldl(
+ fun({FunctionName, _, _}, Acc) ->
+ [erl_syntax:arity_qualifier(erl_syntax:atom(FunctionName), erl_syntax:integer(1)),
+ erl_syntax:arity_qualifier(erl_syntax:atom(FunctionName), erl_syntax:integer(2))
+ |Acc]
+ end, [], Functions)
+ ],
+ ModuleAst = ?Q("-module('@Module@')."),
+ ExportAst = ?Q("-export(['@_Exported'/1])"),
+
+ SourceFunctionAst = ?Q("source_dir() -> _@Dir@."),
+
DependenciesFunctionAst = dependencies_function(AstInfo#ast_info.dependencies),
TranslatableStringsFunctionAst = translatable_strings_function(AstInfo#ast_info.translatable_strings),
FunctionAsts = lists:foldl(fun({_, Function1, Function2}, Acc) -> [Function1, Function2 | Acc] end, [], Functions),
@@ -824,58 +759,24 @@ custom_forms(Dir, Module, Functions, AstInfo) ->
].
stringify(BodyAst, #dtl_context{ binary_strings=BinaryStrings }) ->
- [erl_syntax:application(
- erl_syntax:atom(erlydtl_runtime),
- erl_syntax:atom(stringify_final),
- [BodyAst, erl_syntax:atom(BinaryStrings)])
- ].
+ [?Q("erlydtl_runtime:stringify_final(_@BodyAst, '@BinaryStrings@')")].
forms(Module, {BodyAst, BodyInfo}, {CustomTagsFunctionAst, CustomTagsInfo}, CheckSum, TreeWalker,
#dtl_context{ parse_trail=[File|_] }=Context) ->
MergedInfo = merge_info(BodyInfo, CustomTagsInfo),
- Render0FunctionAst = erl_syntax:function(
- erl_syntax:atom(render),
- [erl_syntax:clause(
- [],
- none,
- [erl_syntax:application(
- none, erl_syntax:atom(render),
- [erl_syntax:list([])])
- ])
- ]),
- Render1FunctionAst = erl_syntax:function(
- erl_syntax:atom(render),
- [erl_syntax:clause(
- [erl_syntax:variable("Variables")],
- none,
- [erl_syntax:application(
- none, erl_syntax:atom(render),
- [erl_syntax:variable("Variables"),
- erl_syntax:list([])])
- ])
- ]),
- Function2 = erl_syntax:application(none, erl_syntax:atom(render_internal),
- [erl_syntax:variable("Variables"), erl_syntax:variable("RenderOptions")]),
- ClauseOk = erl_syntax:clause([erl_syntax:variable("Val")],
- none,
- [erl_syntax:tuple([erl_syntax:atom(ok), erl_syntax:variable("Val")])]),
- ClauseCatch = erl_syntax:clause([erl_syntax:variable("Err")],
- none,
- [erl_syntax:tuple([erl_syntax:atom(error), erl_syntax:variable("Err")])]),
- Render2FunctionAst = erl_syntax:function(
- erl_syntax:atom(render),
- [erl_syntax:clause(
- [erl_syntax:variable("Variables"),
- erl_syntax:variable("RenderOptions")],
- none,
- [erl_syntax:try_expr([Function2], [ClauseOk], [ClauseCatch])])
- ]),
-
- SourceFunctionTuple = erl_syntax:tuple(
- [erl_syntax:string(File), erl_syntax:string(CheckSum)]),
- SourceFunctionAst = erl_syntax:function(
- erl_syntax:atom(source),
- [erl_syntax:clause([], none, [SourceFunctionTuple])]),
+
+ Render0FunctionAst = ?Q("render() -> render([])."),
+ Render1FunctionAst = ?Q("render(Variables) -> render(Variables, [])."),
+
+ Render2FunctionAst = ?Q(["render(Variables, RenderOptions) ->",
+ " try render_internal(Variables, RenderOptions) of",
+ " Val -> {ok, Val}",
+ " catch",
+ " Err -> {error, Err}",
+ "end."
+ ]),
+
+ SourceFunctionAst = ?Q("source() -> {_@File@, _@CheckSum@}."),
DependenciesFunctionAst = dependencies_function(MergedInfo#ast_info.dependencies),
@@ -886,19 +787,10 @@ forms(Module, {BodyAst, BodyInfo}, {CustomTagsFunctionAst, CustomTagsInfo}, Chec
VariablesAst = variables_function(MergedInfo#ast_info.var_names),
MatchAst = options_match_ast(Context, TreeWalker),
-
BodyAstTmp = MatchAst ++ stringify(BodyAst, Context),
+ RenderInternalFunctionAst = ?Q("render_internal(_Variables, RenderOptions) -> _@BodyAstTmp."),
- RenderInternalFunctionAst = erl_syntax:function(
- erl_syntax:atom(render_internal),
- [erl_syntax:clause(
- [erl_syntax:variable("_Variables"),
- erl_syntax:variable("RenderOptions")],
- none,
- BodyAstTmp)
- ]),
-
- ModuleAst = erl_syntax:attribute(erl_syntax:atom(module), [erl_syntax:atom(Module)]),
+ ModuleAst = ?Q("-module('@Module@')."),
ExportAst = erl_syntax:attribute(
erl_syntax:atom(export),
@@ -926,27 +818,15 @@ forms(Module, {BodyAst, BodyInfo}, {CustomTagsFunctionAst, CustomTagsInfo}, Chec
options_match_ast(Context) -> options_match_ast(Context, undefined).
options_match_ast(Context, TreeWalker) ->
[
- erl_syntax:match_expr(
- erl_syntax:variable("_TranslationFun"),
- erl_syntax:application(
- erl_syntax:atom(proplists),
- erl_syntax:atom(get_value),
- [erl_syntax:atom(translation_fun), erl_syntax:variable("RenderOptions"), erl_syntax:atom(none)])),
- erl_syntax:match_expr(
- erl_syntax:variable("_CurrentLocale"),
- erl_syntax:application(
- erl_syntax:atom(proplists),
- erl_syntax:atom(get_value),
- [erl_syntax:atom(locale), erl_syntax:variable("RenderOptions"), erl_syntax:atom(none)])),
- erl_syntax:match_expr(
- erl_syntax:variable("_RecordInfo"),
- erl_syntax:abstract(Context#dtl_context.record_info))
+ ?Q("_TranslationFun = proplists:get_value(translation_fun, RenderOptions, none)"),
+ ?Q("_CurrentLocale = proplists:get_value(locale, RenderOptions, none)"),
+ ?Q("_RecordInfo = _@info", [{info, merl:term(Context#dtl_context.record_info)}])
| case call_extension(Context, setup_render_ast, [Context, TreeWalker]) of
undefined -> [];
Ast when is_list(Ast) -> Ast
end].
- % child templates should only consist of blocks at the top level
+%% child templates should only consist of blocks at the top level
body_ast([{'extends', {string_literal, _Pos, String}} | ThisParseTree], Context, TreeWalker) ->
File = full_path(unescape_string_literal(String), Context#dtl_context.doc_root),
case lists:member(File, Context#dtl_context.parse_trail) of
@@ -1089,10 +969,8 @@ body_ast(DjangoParseTree, Context, TreeWalker) ->
PresetVars = lists:foldl(fun
(X, Acc) ->
case proplists:lookup(X, Context#dtl_context.vars) of
- none ->
- Acc;
- Val ->
- [erl_syntax:abstract(Val) | Acc]
+ none -> Acc;
+ Val -> [Val|Acc]
end
end, [], Info#ast_info.var_names),
case PresetVars of
@@ -1100,16 +978,10 @@ body_ast(DjangoParseTree, Context, TreeWalker) ->
{Ast, {merge_info(Info, InfoAcc), TreeWalkerAcc}};
_ ->
Counter = TreeWalkerAcc#treewalker.counter,
- Name = lists:concat([pre_render, Counter]),
- Ast1 = erl_syntax:application(none, erl_syntax:atom(Name),
- [erl_syntax:list(PresetVars),
- erl_syntax:variable("RenderOptions")]),
- PreRenderAst = erl_syntax:function(erl_syntax:atom(Name),
- [erl_syntax:clause([erl_syntax:variable("_Variables"),
- erl_syntax:variable("RenderOptions")],
- none,
- options_match_ast(Context, TreeWalkerAcc)
- ++ [Ast])]),
+ Name = list_to_atom(lists:concat([pre_render, Counter])),
+ Ast1 = ?Q("'@Name@'(_@PresetVars@, RenderOptions)"),
+ PreRenderAst = ?Q("'@Name@'(_Variables, RenderOptions) -> _@match, _@Ast.",
+ [{match, options_match_ast(Context, TreeWalkerAcc)}]),
PreRenderAsts = Info#ast_info.pre_render_asts,
Info1 = Info#ast_info{pre_render_asts = [PreRenderAst | PreRenderAsts]},
{Ast1, {merge_info(Info1, InfoAcc), TreeWalkerAcc#treewalker{counter = Counter + 1}}}
@@ -1122,23 +994,21 @@ value_ast(ValueToken, AsString, EmptyIfUndefined, Context, TreeWalker) ->
case ValueToken of
{'expr', Operator, Value} ->
{{ValueAst,InfoValue}, TreeWalker1} = value_ast(Value, false, EmptyIfUndefined, Context, TreeWalker),
- Ast = erl_syntax:application(erl_syntax:atom(erlydtl_runtime),
- erl_syntax:atom(Operator),
- [ValueAst]),
+ Op = list_to_atom(Operator),
+ Ast = ?Q("erlydtl_runtime:_@Op@(_@ValueAst)"),
{{Ast, InfoValue}, TreeWalker1};
{'expr', Operator, Value1, Value2} ->
{{Value1Ast,InfoValue1}, TreeWalker1} = value_ast(Value1, false, EmptyIfUndefined, Context, TreeWalker),
{{Value2Ast,InfoValue2}, TreeWalker2} = value_ast(Value2, false, EmptyIfUndefined, Context, TreeWalker1),
- Ast = erl_syntax:application(erl_syntax:atom(erlydtl_runtime),
- erl_syntax:atom(Operator),
- [Value1Ast, Value2Ast]),
+ Op = list_to_atom(Operator),
+ Ast = ?Q("erlydtl_runtime:_@Op@(_@Value1Ast, _@Value2Ast)"),
{{Ast, merge_info(InfoValue1,InfoValue2)}, TreeWalker2};
{'string_literal', _Pos, String} ->
string_ast(unescape_string_literal(String), Context, TreeWalker);
{'number_literal', _Pos, Number} ->
case AsString of
true -> string_ast(Number, Context, TreeWalker);
- false -> {{erl_syntax:integer(list_to_integer(Number)), #ast_info{}}, TreeWalker}
+ false -> {{erl_syntax:integer(Number), #ast_info{}}, TreeWalker}
end;
{'apply_filter', Variable, Filter} ->
filter_ast(Variable, Filter, Context, TreeWalker);
@@ -1224,11 +1094,10 @@ blocktrans_ast(ArgList, Contents, Context, TreeWalker) ->
{ok, DjangoParseTree} = do_parse(Body, Context),
{{ThisAst, ThisAstInfo}, TreeWalker3} = body_ast(DjangoParseTree, NewContext, ThisTreeWalker),
{merge_info(ThisAstInfo, AstInfoAcc), TreeWalker3,
- [erl_syntax:clause([erl_syntax:string(Locale)], none, [ThisAst])|ClauseAcc]}
+ [?Q("_@Locale@ -> _@ThisAst")|ClauseAcc]}
end
end, {MergedInfo, TreeWalker2, []}, Context#dtl_context.trans_locales),
- Ast = erl_syntax:case_expr(erl_syntax:variable("_CurrentLocale"),
- Clauses ++ [erl_syntax:clause([erl_syntax:underscore()], none, [DefaultAst])]),
+ Ast = ?Q("case _CurrentLocale of _@_Clauses -> _; _ -> _@DefaultAst end"),
{{Ast, FinalAstInfo#ast_info{ translated_blocks = [SourceText] }}, FinalTreeWalker}
end.
@@ -1241,22 +1110,16 @@ blocktrans_runtime_ast({DefaultAst, Info}, Walker, SourceText, Contents, Context
end, [Var || {variable, _}=Var <- Contents]),
VarBuilder = fun({variable, {identifier, _, Name}}=Var, Walker1) ->
{{Ast2, _InfoIgn}, Walker2} = resolve_variable_ast(Var, Context, Walker1, false),
- KVAst = erl_syntax:tuple([erl_syntax:string(atom_to_list(Name)), Ast2]),
- {KVAst, Walker2}
+ {?Q("{_@name, _@Ast2}", [{name, merl:term(atom_to_list(Name))}]),
+ Walker2}
end,
{VarAsts, Walker2} = lists:mapfoldl(VarBuilder, Walker, USortedVariables),
VarListAst = erl_syntax:list(VarAsts),
- RuntimeTransAst = [erl_syntax:application(
- erl_syntax:atom(erlydtl_runtime),
- erl_syntax:atom(translate_block),
- [erl_syntax:string(SourceText),
- erl_syntax:variable("_TranslationFun"),
- VarListAst])],
- Ast1 = erl_syntax:case_expr(erl_syntax:variable("_TranslationFun"),
- [erl_syntax:clause([erl_syntax:atom(none)], none, [DefaultAst]),
- erl_syntax:clause([erl_syntax:underscore()], none,
- RuntimeTransAst)]),
- {{Ast1, Info}, Walker2}.
+ BlockTransAst = ?Q(["if _TranslationFun =:= none -> _@DefaultAst;",
+ " true -> erlydtl_runtime:translate_block(",
+ " _@SourceText@, _TranslationFun, _@VarListAst)",
+ "end"]),
+ {{BlockTransAst, Info}, Walker2}.
translated_ast({string_literal, _, String}, Context, TreeWalker) ->
UnescapedStr = unescape_string_literal(String),
@@ -1275,39 +1138,32 @@ translated_ast(ValueToken, Context, TreeWalker) ->
runtime_trans_ast(Ast, Info, TreeWalker1).
runtime_trans_ast(ValueAst, AstInfo, TreeWalker) ->
- StringLookupAst = erl_syntax:application(
- erl_syntax:atom(erlydtl_runtime),
- erl_syntax:atom(translate),
- [ValueAst, erl_syntax:variable("_TranslationFun")]),
- {{StringLookupAst, AstInfo}, TreeWalker}.
+ {{?Q("erlydtl_runtime:translate(_@ValueAst, _TranslationFun)"),
+ AstInfo},
+ TreeWalker}.
compiletime_trans_ast(String, AstInfo,
#dtl_context{trans_fun=TFun,
trans_locales=TLocales}=Context,
TreeWalker) ->
- {{DefaultAst, Info1}, TWalker1} = Default = string_ast(String, Context, TreeWalker),
- DefaultClauseAst = erl_syntax:clause([erl_syntax:underscore()], none, [DefaultAst]), %or runtime trans?
- FoldFun = fun(Locale, {ClausesAcc, Info2, TWalker2}) ->
- {{TranslatedAst, Info3}, TWalker3} =
- case TFun(String, Locale) of
- default -> Default; %or runtime trans?
- Translated ->
- string_ast(binary_to_list(Translated), Context, TWalker2)
- end,
- ClauseAst = erl_syntax:clause(
- [erl_syntax:string(Locale)],
- none,
- [TranslatedAst]),
- {[ClauseAst | ClausesAcc], merge_info(Info2, Info3), TWalker3}
+ ClAst = lists:foldl(
+ fun(Locale, ClausesAcc) ->
+ [?Q("_@Locale@ -> _@translated",
+ [{translated, case TFun(String, Locale) of
+ default -> string_ast(String, Context);
+ Translated -> string_ast(Translated, Context)
+ end}])
+ |ClausesAcc]
end,
- {ClAst, ClInfo, ClTreeWalker} = lists:foldl(
- FoldFun,
- {[DefaultClauseAst], merge_info(AstInfo, Info1), TWalker1},
- TLocales),
- CaseAst = erl_syntax:case_expr(erl_syntax:variable("_CurrentLocale"), ClAst),
- {{CaseAst, ClInfo}, ClTreeWalker}.
-
- % Completely unnecessary in ErlyDTL (use {{ "{%" }} etc), but implemented for compatibility.
+ [], TLocales),
+ CaseAst = ?Q(["case _CurrentLocale of",
+ " _@_ClAst -> _;",
+ " _ -> _@string",
+ "end"],
+ [{string, string_ast(String, Context)}]),
+ {{CaseAst, AstInfo}, TreeWalker}.
+
+%% Completely unnecessary in ErlyDTL (use {{ "{%" }} etc), but implemented for compatibility.
templatetag_ast("openblock", Context, TreeWalker) ->
string_ast("{%", Context, TreeWalker);
templatetag_ast("closeblock", Context, TreeWalker) ->
@@ -1330,21 +1186,37 @@ widthratio_ast(Numerator, Denominator, Scale, Context, TreeWalker) ->
{{NumAst, NumInfo}, TreeWalker1} = value_ast(Numerator, false, true, Context, TreeWalker),
{{DenAst, DenInfo}, TreeWalker2} = value_ast(Denominator, false, true, Context, TreeWalker1),
{{ScaleAst, ScaleInfo}, TreeWalker3} = value_ast(Scale, false, true, Context, TreeWalker2),
- {{format_number_ast(erl_syntax:application(
- erl_syntax:atom(erlydtl_runtime),
- erl_syntax:atom(widthratio),
- [NumAst, DenAst, ScaleAst])), merge_info(ScaleInfo, merge_info(NumInfo, DenInfo))},
+ {{format_number_ast(?Q("erlydtl_runtime:widthratio(_@NumAst, _@DenAst, _@ScaleAst)")),
+ merge_info(ScaleInfo, merge_info(NumInfo, DenInfo))},
TreeWalker3}.
-binary_string(String) ->
- erl_syntax:binary([erl_syntax:binary_field(erl_syntax:integer(X)) || X <- String]).
-string_ast(String, #dtl_context{ binary_strings = true }, TreeWalker) when is_list(String) ->
- {{binary_string(String), #ast_info{}}, TreeWalker};
-string_ast(String, #dtl_context{ binary_strings = false }, TreeWalker) when is_list(String) ->
- {{erl_syntax:string(String), #ast_info{}}, TreeWalker}; %% less verbose AST, better for development and debugging
-string_ast(S, Context, TreeWalker) when is_atom(S) ->
- string_ast(atom_to_list(S), Context, TreeWalker).
+string_ast(Arg, #dtl_context{ binary_strings = true }) ->
+ if is_binary(Arg) ->
+ merl:term(Arg);
+ is_list(Arg) ->
+ merl:term(list_to_binary(Arg));
+ is_integer(Arg) ->
+ case erlang:function_exported(erlang, integer_to_binary, 1) of
+ true -> merl:term(erlang:integer_to_binary(Arg));
+ false -> merl:term(list_to_binary(integer_to_list(Arg)))
+ end;
+ is_atom(Arg) ->
+ merl:term(atom_to_binary(Arg, latin1)) %% latin1 so we match atom_to_list/1 behaviour
+ end;
+string_ast(Arg, #dtl_context{ binary_strings = false }) ->
+ if is_list(Arg) ->
+ merl:term(Arg);
+ is_binary(Arg) ->
+ merl:term(binary_to_list(Arg));
+ is_integer(Arg) ->
+ merl:term(integer_to_list(Arg));
+ is_atom(Arg) ->
+ merl:term(atom_to_list(Arg))
+ end.
+
+string_ast(Arg, Context, TreeWalker) ->
+ {{string_ast(Arg, Context), #ast_info{}}, TreeWalker}.
include_ast(File, ArgList, Scopes, Context, TreeWalker) ->
@@ -1371,14 +1243,12 @@ include_ast(File, ArgList, Scopes, Context, TreeWalker) ->
Err -> throw(Err)
end.
- % include at run-time
+%% include at run-time
ssi_ast(FileName, Context, TreeWalker) ->
- {{Ast, Info}, TreeWalker1} = value_ast(FileName, true, true, Context, TreeWalker),
+ {{FileAst, Info}, TreeWalker1} = value_ast(FileName, true, true, Context, TreeWalker),
{Mod, Fun} = Context#dtl_context.reader,
- {{erl_syntax:application(
- erl_syntax:atom(erlydtl_runtime),
- erl_syntax:atom(read_file),
- [erl_syntax:atom(Mod), erl_syntax:atom(Fun), erl_syntax:string(Context#dtl_context.doc_root), Ast]), Info}, TreeWalker1}.
+ Dir = Context#dtl_context.doc_root,
+ {{?Q("erlydtl_runtime:read_file(_@Mod@, _@Fun@, _@Dir@, _@FileAst)"), Info}, TreeWalker1}.
filter_tag_ast(FilterList, Contents, Context, TreeWalker) ->
{{InnerAst, Info}, TreeWalker1} = body_ast(Contents, Context#dtl_context{auto_escape = did}, TreeWalker),
@@ -1391,22 +1261,12 @@ filter_tag_ast(FilterList, Contents, Context, TreeWalker) ->
{{Ast, AstInfo}, TW} = filter_ast1(Filter, AstAcc, Context, TreeWalkerAcc),
{{Ast, merge_info(InfoAcc, AstInfo)}, TW}
end,
- {{erl_syntax:application(
- erl_syntax:atom(erlang),
- erl_syntax:atom(iolist_to_binary),
- [InnerAst]),
- Info},
- TreeWalker1},
+ {{?Q("erlang:iolist_to_binary(_@InnerAst)"), Info}, TreeWalker1},
FilterList),
EscapedAst = case search_for_escape_filter(lists:reverse(FilterList), Context) of
- on ->
- erl_syntax:application(
- erl_syntax:atom(erlydtl_filters),
- erl_syntax:atom(force_escape),
- [FilteredAst]);
- _ ->
- FilteredAst
+ on -> ?Q("erlydtl_filters:force_escape(_@FilteredAst)");
+ _ -> FilteredAst
end,
{{EscapedAst, FilteredInfo}, TreeWalker2}.
@@ -1435,13 +1295,8 @@ filter_ast(Variable, Filter, Context, TreeWalker) ->
TreeWalker),
EscapedAst = case search_for_escape_filter(Variable, Filter, Context) of
- on ->
- erl_syntax:application(
- erl_syntax:atom(erlydtl_filters),
- erl_syntax:atom(force_escape),
- [UnescapedAst]);
- _ ->
- UnescapedAst
+ on -> ?Q("erlydtl_filters:force_escape(_@UnescapedAst)");
+ _ -> UnescapedAst
end,
{{EscapedAst, Info}, TreeWalker2}.
@@ -1467,11 +1322,7 @@ filter_ast1({{identifier, _, Name}, Args}, ValueAst, Context, TreeWalker) ->
filter_ast2(Name, Args, #dtl_context{ filter_modules = [Module|Rest] } = Context) ->
case lists:member({Name, length(Args)}, Module:module_info(exports)) of
- true ->
- erl_syntax:application(
- erl_syntax:atom(Module),
- erl_syntax:atom(Name),
- Args);
+ true -> ?Q("'@Module@':'@Name@'(_@Args)");
false ->
filter_ast2(Name, Args, Context#dtl_context{ filter_modules = Rest })
end;
@@ -1515,59 +1366,36 @@ resolve_variable_ast(VarTuple, Context, TreeWalker, EmptyIfUndefined)
resolve_variable_ast(VarTuple, Context, TreeWalker, FinderFunction) ->
resolve_variable_ast1(VarTuple, Context, TreeWalker, FinderFunction).
-resolve_variable_ast1({attribute, {{AttrKind, Pos, Attr}, Variable}}, Context, TreeWalker, FinderFunction) ->
+resolve_variable_ast1({attribute, {{_, Pos, Attr}, Variable}}, Context, TreeWalker, FinderFunction) ->
{{VarAst, VarInfo}, TreeWalker1} = resolve_variable_ast(Variable, Context, TreeWalker, FinderFunction),
- FileNameAst = erl_syntax:tuple(
- [erl_syntax:atom(filename),
- case Context#dtl_context.parse_trail of
- [] -> erl_syntax:atom(undefined);
- [H|_] -> erl_syntax:string(H)
- end]),
- AttrAst = erl_syntax:abstract(
- case AttrKind of
- number_literal -> erlang:list_to_integer(Attr);
- _ -> Attr
- end),
+ FileName = case Context#dtl_context.parse_trail of
+ [] -> undefined;
+ [H|_] -> H
+ end,
{Runtime, Finder} = FinderFunction,
- {{erl_syntax:application(
- erl_syntax:atom(Runtime),
- erl_syntax:atom(Finder),
- [AttrAst, VarAst,
- erl_syntax:list(
- [FileNameAst,
- erl_syntax:abstract({pos, Pos}),
- erl_syntax:tuple([erl_syntax:atom(record_info),
- erl_syntax:variable("_RecordInfo")]),
- erl_syntax:tuple([erl_syntax:atom(render_options),
- erl_syntax:variable("RenderOptions")])
- ])
- ]),
+ {{?Q(["'@Runtime@':'@Finder@'(",
+ " _@Attr@, _@VarAst,",
+ " [{filename, _@FileName@},",
+ " {pos, _@Pos@},",
+ " {record_info, _RecordInfo},",
+ " {render_options, RenderOptions}])"]),
VarInfo},
-
TreeWalker1};
resolve_variable_ast1({variable, {identifier, Pos, VarName}}, Context, TreeWalker, FinderFunction) ->
VarValue = case resolve_scoped_variable_ast(VarName, Context) of
undefined ->
- FileNameAst = erl_syntax:tuple(
- [erl_syntax:atom(filename),
- case Context#dtl_context.parse_trail of
- [] -> erl_syntax:atom(undefined);
- [H|_] -> erl_syntax:string(H)
- end]),
+ FileName = case Context#dtl_context.parse_trail of
+ [] -> undefined;
+ [H|_] -> H
+ end,
{Runtime, Finder} = FinderFunction,
- erl_syntax:application(
- erl_syntax:atom(Runtime), erl_syntax:atom(Finder),
- [erl_syntax:atom(VarName), erl_syntax:variable("_Variables"),
- erl_syntax:list(
- [FileNameAst,
- erl_syntax:abstract({pos, Pos}),
- erl_syntax:tuple([erl_syntax:atom(record_info),
- erl_syntax:variable("_RecordInfo")]),
- erl_syntax:tuple([erl_syntax:atom(render_options),
- erl_syntax:variable("RenderOptions")])
- ])
- ]);
+ ?Q(["'@Runtime@':'@Finder@'(",
+ " _@VarName@, _Variables,",
+ " [{filename, _@FileName@},",
+ " {pos, _@Pos@},",
+ " {record_info, _RecordInfo},",
+ " {render_options, RenderOptions}])"]);
Val ->
Val
end,
@@ -1591,14 +1419,13 @@ format(Ast, Context, TreeWalker) ->
auto_escape(format_number_ast(Ast), Context, TreeWalker).
format_number_ast(Ast) ->
- erl_syntax:application(erl_syntax:atom(erlydtl_filters), erl_syntax:atom(format_number),
- [Ast]).
+ ?Q("erlydtl_filters:format_number(_@Ast)").
auto_escape(Value, _, #treewalker{safe = true}) ->
Value;
auto_escape(Value, #dtl_context{auto_escape = on}, _) ->
- erl_syntax:application(erl_syntax:atom(erlydtl_filters), erl_syntax:atom(force_escape), [Value]);
+ ?Q("erlydtl_filters:force_escape(_@Value)");
auto_escape(Value, _, _) ->
Value.
@@ -1617,12 +1444,12 @@ firstof_ast(Vars, Context, TreeWalker) ->
ifelse_ast(Expression, {IfContentsAst, IfContentsInfo}, {ElseContentsAst, ElseContentsInfo}, Context, TreeWalker) ->
Info = merge_info(IfContentsInfo, ElseContentsInfo),
{{Ast, ExpressionInfo}, TreeWalker1} = value_ast(Expression, false, false, Context, TreeWalker),
- {{erl_syntax:case_expr(erl_syntax:application(erl_syntax:atom(erlydtl_runtime), erl_syntax:atom(is_true), [Ast]),
- [erl_syntax:clause([erl_syntax:atom(true)], none,
- [IfContentsAst]),
- erl_syntax:clause([erl_syntax:underscore()], none,
- [ElseContentsAst])
- ]), merge_info(ExpressionInfo, Info)}, TreeWalker1}.
+ {{?Q(["case erlydtl_runtime:is_true(_@Ast) of",
+ " true -> _@IfContentsAst;",
+ " _ -> _@ElseContentsAst",
+ "end"]),
+ merge_info(ExpressionInfo, Info)},
+ TreeWalker1}.
with_ast(ArgList, Contents, Context, TreeWalker) ->
{ArgAstList, {ArgInfo, TreeWalker1}} =
@@ -1643,30 +1470,27 @@ with_ast(ArgList, Contents, Context, TreeWalker) ->
Context#dtl_context{local_scopes = [NewScope|Context#dtl_context.local_scopes]},
TreeWalker1),
- {{erl_syntax:application(
- erl_syntax:fun_expr(
- [erl_syntax:clause(
- lists:map(fun({_, Var}) -> Var end, NewScope),
- none,
- [InnerAst])]),
- ArgAstList),
+ {{?Q("fun (_@args) -> _@InnerAst end (_@ArgAstList)",
+ [{args, element(2, lists:unzip(NewScope))}]),
merge_info(ArgInfo, InnerInfo)},
TreeWalker2}.
regroup_ast(ListVariable, GrouperVariable, LocalVarName, Contents, Context, TreeWalker) ->
{{ListAst, ListInfo}, TreeWalker1} = value_ast(ListVariable, false, true, Context, TreeWalker),
- NewScope = [{LocalVarName, erl_syntax:variable(lists:concat(["Var_", LocalVarName]))}],
+ LocalVarAst = erl_syntax:variable(lists:concat(["Var_", LocalVarName])),
+ NewScope = [{LocalVarName, LocalVarAst}],
{{InnerAst, InnerInfo}, TreeWalker2} = body_ast(Contents,
- Context#dtl_context{ local_scopes = [NewScope|Context#dtl_context.local_scopes] }, TreeWalker1),
-
- Ast = {erl_syntax:application(
- erl_syntax:fun_expr([
- erl_syntax:clause([erl_syntax:variable(lists:concat(["Var_", LocalVarName]))], none,
- [InnerAst])]),
- [erl_syntax:application(erl_syntax:atom(erlydtl_runtime), erl_syntax:atom(regroup),
- [ListAst, regroup_filter(GrouperVariable,[])])]), merge_info(ListInfo, InnerInfo)},
- {Ast,TreeWalker2}.
+ Context#dtl_context{
+ local_scopes = [NewScope|Context#dtl_context.local_scopes]
+ },
+ TreeWalker1),
+
+ {{?Q(["fun (_@LocalVarAst) -> _@InnerAst end",
+ "(erlydtl_runtime:regroup(_@ListAst, _@regroup))"],
+ [{regroup, regroup_filter(GrouperVariable, [])}]),
+ merge_info(ListInfo, InnerInfo)},
+ TreeWalker2}.
regroup_filter({attribute,{{identifier,_,Ident},Next}},Acc) ->
regroup_filter(Next,[erl_syntax:atom(Ident)|Acc]);
@@ -1674,10 +1498,7 @@ regroup_filter({variable,{identifier,_,Var}},Acc) ->
erl_syntax:list([erl_syntax:atom(Var)|Acc]).
to_list_ast(Value, IsReversed) ->
- erl_syntax:application(
- erl_syntax:atom(erlydtl_runtime),
- erl_syntax:atom(to_list),
- [Value, IsReversed]).
+ ?Q("erlydtl_runtime:to_list(_@Value, _@IsReversed)").
to_list_ast(Value, IsReversed, Context, TreeWalker) ->
case call_extension(Context, to_list_ast, [Value, IsReversed, Context, TreeWalker]) of
@@ -1691,8 +1512,8 @@ for_loop_ast(IteratorList, LoopValue, IsReversed, Contents, {EmptyContentsAst, E
{Row, Col} = element(2, hd(IteratorList)),
ForId = lists:concat(["/", Level, "_", Row, ":", Col]),
- Counters = lists:concat(["Counters", ForId]),
- Vars = lists:concat(["Vars", ForId]),
+ Counters = merl:var(lists:concat(["Counters", ForId])),
+ Vars = merl:var(lists:concat(["Vars", ForId])),
%% setup
VarScope = lists:map(
@@ -1710,16 +1531,11 @@ for_loop_ast(IteratorList, LoopValue, IsReversed, Contents, {EmptyContentsAst, E
Contents,
Context#dtl_context{
local_scopes =
- [[{'forloop', erl_syntax:variable(Counters)} | VarScope]
+ [[{'forloop', Counters} | VarScope]
| 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)]),
-
{{LoopValueAst, LoopValueInfo}, TreeWalker2} = value_ast(LoopValue, false, true, Context, TreeWalker1),
LoopValueAst0 = to_list_ast(LoopValueAst, erl_syntax:atom(IsReversed), Context, TreeWalker2),
@@ -1727,66 +1543,25 @@ for_loop_ast(IteratorList, LoopValue, IsReversed, Contents, {EmptyContentsAst, E
ParentLoop = resolve_scoped_variable_ast('forloop', Context, erl_syntax:atom(undefined)),
%% call for loop
- {{erl_syntax:case_expr(
- erl_syntax:application(
- erl_syntax:atom('erlydtl_runtime'), erl_syntax:atom('forloop'),
- [erl_syntax:fun_expr(
- [erl_syntax:clause(
- [erl_syntax:variable(Vars),
- erl_syntax:variable(Counters)],
- none,
- [erl_syntax:match_expr(
- erl_syntax:tuple(IteratorVars),
- erl_syntax:if_expr(
- [erl_syntax:clause(
- [], [erl_syntax:application(none, erl_syntax:atom(is_tuple), [erl_syntax:variable(Vars)]),
- erl_syntax:infix_expr(
- erl_syntax:application(none, erl_syntax:atom(size), [erl_syntax:variable(Vars)]),
- erl_syntax:operator('=='),
- erl_syntax:integer(IteratorCount))
- ],
- [erl_syntax:variable(Vars)])
- | if IteratorCount > 1 ->
- [erl_syntax:clause(
- [], [erl_syntax:application(none, erl_syntax:atom(is_list), [erl_syntax:variable(Vars)]),
- erl_syntax:infix_expr(
- erl_syntax:application(none, erl_syntax:atom(length), [erl_syntax:variable(Vars)]),
- erl_syntax:operator('=='),
- erl_syntax:integer(IteratorCount))
- ],
- [erl_syntax:application(none, erl_syntax:atom(list_to_tuple), [erl_syntax:variable(Vars)])]),
- erl_syntax:clause(
- [], [erl_syntax:atom(true)],
- [erl_syntax:application(
- none, erl_syntax:atom(throw),
- [erl_syntax:tuple(
- [erl_syntax:atom(for_loop),
- erl_syntax:abstract(Iterators),
- erl_syntax:variable(Vars)])
- ])
- ])
- ];
- true ->
- [erl_syntax:clause(
- [], [erl_syntax:atom(true)],
- [erl_syntax:tuple([erl_syntax:variable(Vars)])])
- ]
- end
- ])),
- erl_syntax:tuple([LoopBodyAst, CounterAst])
- ])
- ]),
- LoopValueAst0, ParentLoop
- ]),
- %% of
- [erl_syntax:clause(
- [erl_syntax:atom(empty)],
- none,
- [EmptyContentsAst]),
- erl_syntax:clause(
- [erl_syntax:tuple([erl_syntax:variable("L"), erl_syntax:underscore()])],
- none, [erl_syntax:variable("L")])
- ]),
+ {{?Q(["case erlydtl_runtime:forloop(",
+ " fun (_@Vars, _@Counters) ->",
+ " {_@IteratorVars} = if is_tuple(_@Vars), size(_@Vars) == _@IteratorCount@ -> _@Vars;",
+ " _@___ifclauses -> _",
+ " end,",
+ " {_@LoopBodyAst, erlydtl_runtime:increment_counter_stats(_@Counters)}",
+ " end,",
+ " _@LoopValueAst0, _@ParentLoop)",
+ "of",
+ " empty -> _@EmptyContentsAst;",
+ " {L, _} -> L",
+ "end"],
+ [{ifclauses, if IteratorCount > 1 ->
+ ?Q(["() when is_list(_@Vars), length(_@Vars) == _@IteratorCount@ ->",
+ " list_to_tuple(_@Vars);",
+ "() when true -> throw({for_loop, _@Iterators@, _@Vars})"]);
+ true ->
+ ?Q("() when true -> {_@Vars}")
+ end}]),
merge_info(merge_info(Info, EmptyContentsInfo), LoopValueInfo)},
TreeWalker2}.
@@ -1794,31 +1569,33 @@ ifchanged_values_ast(Values, {IfContentsAst, IfContentsInfo}, {ElseContentsAst,
Info = merge_info(IfContentsInfo, ElseContentsInfo),
ValueAstFun = fun(Expr, {LTreeWalker, LInfo, Acc}) ->
{{EAst, EInfo}, ETw} = value_ast(Expr, false, true, Context, LTreeWalker),
- {ETw, merge_info(LInfo, EInfo), [erl_syntax:tuple([erl_syntax:integer(erlang:phash2(Expr)), EAst])|Acc]} end,
- {TreeWalker1, MergedInfo, Changed} = lists:foldl(ValueAstFun, {TreeWalker, Info, []}, Values),
- {{erl_syntax:case_expr(erl_syntax:application(erl_syntax:atom(erlydtl_runtime), erl_syntax:atom(ifchanged), [erl_syntax:list(Changed)]),
- [erl_syntax:clause([erl_syntax:atom(true)], none,
- [IfContentsAst]),
- erl_syntax:clause([erl_syntax:underscore()], none,
- [ElseContentsAst])
- ]), MergedInfo}, TreeWalker1}.
+ {ETw, merge_info(LInfo, EInfo),
+ [?Q("{_@hash, _@EAst}",
+ [{hash, merl:term(erlang:phash2(Expr))}])
+ |Acc]}
+ end,
+ {TreeWalker1, MergedInfo, Changed} = lists:foldl(ValueAstFun, {TreeWalker, Info, []}, Values),
+ {{?Q(["case erlydtl_runtime:ifchanged([_@Changed]) of",
+ " true -> _@IfContentsAst;",
+ " _ -> _@ElseContentsAst",
+ "end"]),
+ MergedInfo},
+ TreeWalker1}.
ifchanged_contents_ast(Contents, {IfContentsAst, IfContentsInfo}, {ElseContentsAst, ElseContentsInfo}, _Context, TreeWalker) ->
- Info = merge_info(IfContentsInfo, ElseContentsInfo),
- Key = erl_syntax:integer(erlang:phash2(Contents)),
- {{erl_syntax:case_expr(erl_syntax:application(erl_syntax:atom(erlydtl_runtime), erl_syntax:atom(ifchanged), [erl_syntax:list([erl_syntax:tuple([Key, IfContentsAst])])]),
- [erl_syntax:clause([erl_syntax:atom(true)], none,
- [IfContentsAst]),
- erl_syntax:clause([erl_syntax:underscore()], none,
- [ElseContentsAst])
- ]), Info}, TreeWalker}.
-
+ {{?Q(["case erlydtl_runtime:ifchanged([{_@hash, _@IfContentsAst}]) of",
+ " true -> _@IfContentsAst;",
+ " _ -> _@ElseContentsAst",
+ "end"],
+ [{hash, merl:term(erlang:phash2(Contents))}]),
+ merge_info(IfContentsInfo, ElseContentsInfo)},
+ TreeWalker}.
cycle_ast(Names, Context, TreeWalker) ->
{NamesTuple, VarNames}
= lists:mapfoldl(
fun ({string_literal, _, Str}, VarNamesAcc) ->
- {{S, _}, _} = string_ast(unescape_string_literal(Str), Context, TreeWalker),
+ S = string_ast(unescape_string_literal(Str), Context),
{S, VarNamesAcc};
({variable, _}=Var, VarNamesAcc) ->
{{V, #ast_info{ var_names=[VarName] }}, _} = resolve_variable_ast(Var, Context, TreeWalker, true),
@@ -1828,9 +1605,8 @@ cycle_ast(Names, Context, TreeWalker) ->
(_, VarNamesAcc) ->
{[], VarNamesAcc}
end, [], Names),
- {{erl_syntax:application(
- erl_syntax:atom('erlydtl_runtime'), erl_syntax:atom('cycle'),
- [erl_syntax:tuple(NamesTuple), resolve_scoped_variable_ast('forloop', Context)]),
+ {{?Q("erlydtl_runtime:cycle({_@NamesTuple}, _@forloop)",
+ [{forloop, resolve_scoped_variable_ast('forloop', Context)}]),
#ast_info{ var_names = VarNames }},
TreeWalker}.
@@ -1838,12 +1614,10 @@ cycle_ast(Names, Context, TreeWalker) ->
cycle_compat_ast(Names, Context, TreeWalker) ->
NamesTuple = lists:map(
fun ({identifier, _, X}) ->
- {{S, _}, _} = string_ast(X, Context, TreeWalker),
- S
+ string_ast(X, Context)
end, Names),
- {{erl_syntax:application(
- erl_syntax:atom('erlydtl_runtime'), erl_syntax:atom('cycle'),
- [erl_syntax:tuple(NamesTuple), resolve_scoped_variable_ast('forloop', Context)]),
+ {{?Q("erlydtl_runtime:cycle({_@NamesTuple}, _@forloop)",
+ [{forloop, resolve_scoped_variable_ast('forloop', Context)}]),
#ast_info{}},
TreeWalker}.
@@ -1854,17 +1628,11 @@ now_ast(FormatString, Context, TreeWalker) ->
%% i.e. \"foo\" becomes "foo"
UnescapeOuter = string:strip(FormatString, both, 34),
{{StringAst, Info}, TreeWalker1} = string_ast(UnescapeOuter, Context, TreeWalker),
- {{erl_syntax:application(
- erl_syntax:atom(erlydtl_dateformat),
- erl_syntax:atom(format),
- [StringAst]), Info}, TreeWalker1}.
+ {{?Q("erlydtl_dateformat:format(_@StringAst)"), Info}, TreeWalker1}.
spaceless_ast(Contents, Context, TreeWalker) ->
{{Ast, Info}, TreeWalker1} = body_ast(Contents, Context, TreeWalker),
- {{erl_syntax:application(
- erl_syntax:atom(erlydtl_runtime),
- erl_syntax:atom(spaceless),
- [Ast]), Info}, TreeWalker1}.
+ {{?Q("erlydtl_runtime:spaceless(_@Ast)"), Info}, TreeWalker1}.
unescape_string_literal(String) ->
unescape_string_literal(string:strip(String, both, 34), [], noslash).
@@ -1904,7 +1672,7 @@ interpret_args(Args, Context, TreeWalker) ->
lists:foldr(
fun ({{identifier, _, Key}, Value}, {{ArgsAcc, AstInfoAcc}, TreeWalkerAcc}) ->
{{Ast0, AstInfo0}, TreeWalker0} = interpret_value(Value, Context, TreeWalkerAcc),
- {{[erl_syntax:tuple([erl_syntax:atom(Key), Ast0])|ArgsAcc], merge_info(AstInfo0, AstInfoAcc)}, TreeWalker0};
+ {{[?Q("{_@Key@, _@Ast0}")|ArgsAcc], merge_info(AstInfo0, AstInfoAcc)}, TreeWalker0};
(Value, {{ArgsAcc, AstInfoAcc}, TreeWalkerAcc}) ->
{{Ast0, AstInfo0}, TreeWalker0} = value_ast(Value, false, false, Context, TreeWalkerAcc),
{{[Ast0|ArgsAcc], merge_info(AstInfo0, AstInfoAcc)}, TreeWalker0}
@@ -1916,23 +1684,17 @@ tag_ast(Name, Args, Context, TreeWalker) ->
{{RenderAst, merge_info(AstInfo1, RenderInfo)}, TreeWalker1}.
custom_tags_modules_ast(Name, InterpretedArgs, #dtl_context{ custom_tags_modules = [], is_compiling_dir = false }) ->
- {erl_syntax:application(none, erl_syntax:atom(render_tag),
- [erl_syntax:atom(Name), erl_syntax:list(InterpretedArgs),
- erl_syntax:variable("RenderOptions")]),
+ {?Q("render_tag(_@Name@, [_@InterpretedArgs], RenderOptions)"),
#ast_info{custom_tags = [Name]}};
custom_tags_modules_ast(Name, InterpretedArgs, #dtl_context{ custom_tags_modules = [], is_compiling_dir = true, module = Module }) ->
- {erl_syntax:application(erl_syntax:atom(Module), erl_syntax:atom(Name),
- [erl_syntax:list(InterpretedArgs), erl_syntax:variable("RenderOptions")]),
+ {?Q("'@Module@':'@Name@'([_@InterpretedArgs], RenderOptions)"),
#ast_info{ custom_tags = [Name] }};
custom_tags_modules_ast(Name, InterpretedArgs, #dtl_context{ custom_tags_modules = [Module|Rest] } = Context) ->
try lists:max([I || {N,I} <- Module:module_info(exports), N =:= Name]) of
2 ->
- {erl_syntax:application(erl_syntax:atom(Module), erl_syntax:atom(Name),
- [erl_syntax:list(InterpretedArgs),
- erl_syntax:variable("RenderOptions")]), #ast_info{}};
+ {?Q("'@Module@':'@Name@'([_@InterpretedArgs], RenderOptions)"), #ast_info{}};
1 ->
- {erl_syntax:application(erl_syntax:atom(Module), erl_syntax:atom(Name),
- [erl_syntax:list(InterpretedArgs)]), #ast_info{}};
+ {?Q("'@Module@':'@Name@'([_@InterpretedArgs])"), #ast_info{}};
I ->
throw({unsupported_custom_tag_fun, {Module, Name, I}})
catch _:function_clause ->
@@ -1941,33 +1703,18 @@ custom_tags_modules_ast(Name, InterpretedArgs, #dtl_context{ custom_tags_modules
end.
call_ast(Module, TreeWalkerAcc) ->
- call_ast(Module, erl_syntax:variable("_Variables"), #ast_info{}, TreeWalkerAcc).
+ call_ast(Module, merl:var("_Variables"), #ast_info{}, TreeWalkerAcc).
call_with_ast(Module, Variable, Context, TreeWalker) ->
{{VarAst, VarInfo}, TreeWalker2} = resolve_variable_ast(Variable, Context, TreeWalker, false),
call_ast(Module, VarAst, VarInfo, TreeWalker2).
call_ast(Module, Variable, AstInfo, TreeWalker) ->
- AppAst = erl_syntax:application(
- erl_syntax:atom(Module),
- erl_syntax:atom(render),
- [Variable, erl_syntax:variable("RenderOptions")]),
- RenderedAst = erl_syntax:variable("Rendered"),
- OkAst = erl_syntax:clause(
- [erl_syntax:tuple([erl_syntax:atom(ok), RenderedAst])],
- none,
- [RenderedAst]),
- ReasonAst = erl_syntax:variable("Reason"),
- ErrStrAst = erl_syntax:application(
- erl_syntax:atom(io_lib),
- erl_syntax:atom(format),
- [erl_syntax:string("error: ~p"), erl_syntax:list([ReasonAst])]),
- ErrorAst = erl_syntax:clause(
- [erl_syntax:tuple([erl_syntax:atom(error), ReasonAst])],
- none,
- [ErrStrAst]),
- CallAst = erl_syntax:case_expr(AppAst, [OkAst, ErrorAst]),
- with_dependencies(Module:dependencies(), {{CallAst, AstInfo}, TreeWalker}).
+ Ast = ?Q(["case '@Module@':render(_@Variable, RenderOptions) of",
+ " {ok, Rendered} -> Rendered;",
+ " {error, Reason} -> io_lib:format(\"error: ~p\", [Reason])",
+ "end"]),
+ with_dependencies(Module:dependencies(), {{Ast, AstInfo}, TreeWalker}).
print(Fmt, Args, #dtl_context{ verbose = true }) -> io:format(Fmt, Args);
View
7 src/erlydtl_scanner.erl
@@ -36,7 +36,7 @@
%%%-------------------------------------------------------------------
-module(erlydtl_scanner).
-%% This file was generated 2014-02-17 11:20:49 UTC by slex 0.2.1.
+%% This file was generated 2014-02-21 13:38:27 UTC by slex 0.2.1.
%% http://github.com/erlydtl/slex
-slex_source(["src/erlydtl_scanner.slex"]).
@@ -532,7 +532,10 @@ post_process(_, {string, _, L} = T, _) ->
post_process(_, {string_literal, _, L} = T, _) ->
setelement(3, T, begin L1 = lists:reverse(L), L1 end);
post_process(_, {number_literal, _, L} = T, _) ->
- setelement(3, T, begin L1 = lists:reverse(L), L1 end);
+ setelement(3, T,
+ begin
+ L1 = lists:reverse(L), L2 = list_to_integer(L1), L2
+ end);
post_process(_, {open_var, _, L} = T, _) ->
setelement(3, T, begin L1 = to_atom(L), L1 end);
post_process(_, {close_var, _, L} = T, _) ->
View
2  src/erlydtl_scanner.slex
@@ -257,7 +257,7 @@ end.
string: lists reverse.
string_literal: lists reverse.
-number_literal: lists reverse.
+number_literal: lists reverse, list_to_integer.
open_var: to_atom.
close_var: to_atom.
open_tag: to_atom.
View
2  tests/src/erlydtl_functional_tests.erl
@@ -256,7 +256,7 @@ test_compile_render(Name) ->
force_recompile,
return_errors,
return_warnings,
- %% debug_compiler,
+ %% debug_info,
{custom_tags_modules, [erlydtl_custom_tags]}],
io:format("compiling ... "),
case erlydtl:compile(File, Module, Options) of
Please sign in to comment.
Something went wrong with that request. Please try again.