Просмотр исходного кода

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
Andreas Stenius 11 лет назад
Родитель
Сommit
a1c91987b7
7 измененных файлов с 266 добавлено и 498 удалено
  1. 6 2
      .travis.yml
  2. 11 2
      Makefile
  3. 5 0
      rebar.config
  4. 237 490
      src/erlydtl_compiler.erl
  5. 5 2
      src/erlydtl_scanner.erl
  6. 1 1
      src/erlydtl_scanner.slex
  7. 1 1
      tests/src/erlydtl_functional_tests.erl

+ 6 - 2
.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"

+ 11 - 2
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

+ 5 - 0
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]}
+ ]}.

+ 237 - 490
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);

+ 5 - 2
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, _) ->

+ 1 - 1
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.

+ 1 - 1
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