PageRenderTime 125ms CodeModel.GetById 30ms app.highlight 84ms RepoModel.GetById 1ms app.codeStats 0ms

/src/erlydtl/erlydtl_compiler.erl

https://code.google.com/p/zotonic/
Erlang | 1632 lines | 1372 code | 168 blank | 92 comment | 20 complexity | 7f76de8eddde3f0e2f21cd53af559fa5 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1%%%-------------------------------------------------------------------
  2%%% File:      erlydtl_compiler.erl
  3%%% @author    Roberto Saccon <rsaccon@gmail.com> [http://rsaccon.com]
  4%%% @author    Evan Miller <emmiller@gmail.com>
  5%%% @copyright 2008 Roberto Saccon, Evan Miller
  6%%% @doc  
  7%%% ErlyDTL template compiler
  8%%% @end  
  9%%%
 10%%% The MIT License
 11%%%
 12%%% Copyright (c) 2007 Roberto Saccon, Evan Miller
 13%%%
 14%%% Permission is hereby granted, free of charge, to any person obtaining a copy
 15%%% of this software and associated documentation files (the "Software"), to deal
 16%%% in the Software without restriction, including without limitation the rights
 17%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 18%%% copies of the Software, and to permit persons to whom the Software is
 19%%% furnished to do so, subject to the following conditions:
 20%%%
 21%%% The above copyright notice and this permission notice shall be included in
 22%%% all copies or substantial portions of the Software.
 23%%%
 24%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 25%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 26%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 27%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 28%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 29%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 30%%% THE SOFTWARE.
 31%%%
 32%%% @since 2007-12-16 by Roberto Saccon, Evan Miller
 33%%%
 34%%%-------------------------------------------------------------------
 35%%% Adapted and expanded for Zotonic by Marc Worrell <marc@worrell.nl>
 36%%%-------------------------------------------------------------------
 37
 38-module(erlydtl_compiler).
 39-author('rsaccon@gmail.com').
 40-author('emmiller@gmail.com').
 41-author('marc@worrell.nl').
 42
 43-include_lib("zotonic.hrl").
 44
 45%% --------------------------------------------------------------------
 46%% Definitions
 47%% --------------------------------------------------------------------
 48-export([compile/3, compile/4, compile/5, parse/1]).
 49
 50-record(dtl_context, {
 51    local_scopes = [], 
 52    block_dict = dict:new(), 
 53    auto_escape = off, 
 54    parse_trail = [],
 55    extends_trail = [],
 56    block_trail = [],
 57    vars = [],
 58    custom_tags_dir = [],
 59    reader = {file, read_file},
 60    finder = undefined,
 61    module = [],
 62    compiler_options = [verbose, report_errors],
 63    force_recompile = false,
 64    z_context = undefined}).
 65
 66-record(ast_info, {
 67    dependencies = [],
 68    var_names = [],
 69    pre_render_asts = []}).
 70    
 71-record(treewalker, {
 72    counter = 0,
 73    has_auto_id = false,
 74    custom_tags = []}).    
 75
 76compile(Binary, Module, ZContext) when is_binary(Binary) ->
 77    compile(Binary, Module, [], ZContext);
 78
 79compile(File, Module, ZContext) ->
 80    compile(File, File, Module, [], ZContext).
 81
 82compile(Binary, Module, Options, ZContext) when is_binary(Binary) ->
 83    compile(Binary, [], Module, Options, ZContext);
 84    
 85compile(File, Module, Options, ZContext) ->  
 86    compile(File, filename:basename(File), Module, Options, ZContext).
 87
 88compile(Binary, BaseFile, Module, Options, ZContext) when is_binary(Binary) ->
 89    TemplateResetCounter =  proplists:get_value(template_reset_counter, Options, 0),
 90    case parse(Binary) of
 91        {ok, DjangoParseTree} ->
 92            case compile_to_binary( BaseFile,
 93                                    DjangoParseTree, 
 94                                    init_dtl_context(BaseFile, BaseFile, Module, Options, ZContext),
 95                                    TemplateResetCounter) of
 96                {ok, Module1, _Bin} ->
 97                    {ok, Module1};
 98                Err ->
 99                    Err
100            end;
101        Err ->
102            Err
103    end;
104
105compile(File, BaseFile, Module, Options, ZContext) ->  
106    Context = init_dtl_context(File, BaseFile, Module, Options, ZContext),
107    TemplateResetCounter =  proplists:get_value(template_reset_counter, Options, 0),
108    case parse(File, Context) of  
109        {ok, DjangoParseTree} ->
110            case compile_to_binary(File, DjangoParseTree, Context, TemplateResetCounter) of
111                {ok, Module1, _Bin} ->
112                    {ok, Module1};
113                Err ->
114                    Err
115            end;
116        {error, {ErrLoc, ErrModule, ["syntax error before: " = ErrMsg, [$[,Text,$]]]}} when is_list(Text) ->
117            Text1 = [ list_to_integer(C) || C <- Text, is_list(C) ],
118            {error, {ErrLoc, ErrModule, ErrMsg ++ Text1}};
119        {error, {ErrLoc, ErrModule, ErrMsg}} ->
120            {error, {ErrLoc, ErrModule, lists:flatten(ErrMsg)}};
121        Err ->
122            Err
123    end.
124    
125
126%%====================================================================
127%% Internal functions
128%%====================================================================
129
130compile_to_binary(File, DjangoParseTree, Context, TemplateResetCounter) ->
131    try body_ast(DjangoParseTree, Context, #treewalker{}) of
132        {{Ast, Info}, TreeWalker} ->
133            case compile:forms(forms(File, Context#dtl_context.module, Ast, Info, Context, TreeWalker, TemplateResetCounter), 
134                    Context#dtl_context.compiler_options) of
135                {ok, Module1, Bin} -> 
136                    code:purge(Module1),
137                    case code:load_binary(Module1, atom_to_list(Module1) ++ ".erl", Bin) of
138                        {module, _} -> {ok, Module1, Bin};
139                        _ -> {error, lists:concat(["code reload failed: ", Module1])}
140                    end;
141                error ->
142                    {error, lists:concat(["compilation failed: ", File])};
143                OtherError ->
144                    OtherError
145            end
146    catch 
147        throw:Error -> Error
148    end.
149
150
151init_dtl_context(File, BaseFile, Module, Options, ZContext) when is_list(Module) ->
152    init_dtl_context(File, BaseFile, list_to_atom(Module), Options, ZContext);
153init_dtl_context(File, BaseFile, Module, Options, ZContext) ->
154    Ctx = #dtl_context{},
155    #dtl_context{
156        local_scopes = [ [{'$autoid', erl_syntax:variable("AutoId_"++z_ids:identifier())}] ],
157        parse_trail = [File], 
158        extends_trail = [BaseFile],
159        module = Module,
160        custom_tags_dir = proplists:get_value(custom_tags_dir, Options, Ctx#dtl_context.custom_tags_dir),
161        vars = proplists:get_value(vars, Options, Ctx#dtl_context.vars), 
162        reader = proplists:get_value(reader, Options, Ctx#dtl_context.reader),
163        finder = proplists:get_value(finder, Options, Ctx#dtl_context.finder),
164        compiler_options = proplists:get_value(compiler_options, Options, Ctx#dtl_context.compiler_options),
165        force_recompile = proplists:get_value(force_recompile, Options, Ctx#dtl_context.force_recompile),
166        z_context = ZContext}.   
167    
168parse(File, Context) ->  
169    {M,F} = Context#dtl_context.reader,
170    case catch M:F(File) of
171        {ok, Data} ->
172            case scan_parse(File, Data) of
173                {ok, Val} ->
174                    {ok, Val};
175                Err ->
176                    Err
177            end;
178        Error ->
179            {error, io_lib:format("reading ~p failed (~p)", [File, Error])}  
180    end.
181
182% Used for parsing tests
183parse(Data) when is_binary(Data) ->
184    scan_parse("string", Data).
185
186scan_parse(SourceRef, Data) ->
187    case erlydtl_scanner:scan(SourceRef, binary_to_list(Data)) of
188        {ok, Tokens} ->
189            erlydtl_parser:parse(Tokens);
190        Err ->
191            Err
192    end.        
193  
194forms(File, Module, BodyAst, BodyInfo, Context, TreeWalker, TemplateResetCounter) ->
195    TemplateResetCounterFunctionAst = erl_syntax:function(
196        erl_syntax:atom(template_reset_counter),
197            [ erl_syntax:clause(
198                    [],
199                    none,
200                    [erl_syntax:integer(TemplateResetCounter)]
201                    )
202            ]),
203
204    TransTableFunctionAst = erl_syntax:function(
205        erl_syntax:atom(trans_table),
206            [ erl_syntax:clause(
207                    [],
208                    none,
209                    [erl_syntax:abstract(z_trans_server:table(Context#dtl_context.z_context))]
210                    )
211            ]),
212
213    Function2 = erl_syntax:application(none, erl_syntax:atom(render2), 
214        [erl_syntax:variable("Variables"), erl_syntax:variable("ZpContext")]),
215    ClauseOk = erl_syntax:clause([erl_syntax:variable("Val")], none,
216        [erl_syntax:tuple([erl_syntax:atom(ok), erl_syntax:variable("Val")])]),     
217    ClauseCatch = erl_syntax:clause([erl_syntax:variable("Err")], none,
218        [erl_syntax:tuple([erl_syntax:atom(error), erl_syntax:variable("Err")])]),            
219    Render2FunctionAst = erl_syntax:function(erl_syntax:atom(render),
220        [erl_syntax:clause([erl_syntax:variable("Variables"), erl_syntax:variable("ZpContext")], none, 
221            [erl_syntax:try_expr([Function2], [ClauseOk], [ClauseCatch])])]),  
222     
223    SourceFunctionAst = erl_syntax:function(
224        erl_syntax:atom(source),
225            [ erl_syntax:clause([], none, [ erl_syntax:string(File) ]) ]),
226
227    Dependencies  = lists:usort([{File, filelib:last_modified(File)} | BodyInfo#ast_info.dependencies]),
228    Dependencies1 = lists:filter(fun({[],0}) -> false; (_) -> true end, Dependencies),
229    DependenciesFunctionAst = erl_syntax:function(
230        erl_syntax:atom(dependencies), [
231                erl_syntax:clause([], none, 
232                    [ erl_syntax:list( lists:map(
233                            fun ({XFile, {{Year,Month,Day},{Hour,Min,Sec}}}) ->
234                                erl_syntax:tuple([
235                                    erl_syntax:string(XFile),
236                                    erl_syntax:tuple([
237                                        erl_syntax:tuple([erl_syntax:integer(Year), erl_syntax:integer(Month), erl_syntax:integer(Day)]),
238                                        erl_syntax:tuple([erl_syntax:integer(Hour), erl_syntax:integer(Min), erl_syntax:integer(Sec)])
239                                    ])
240                                ])
241                            end, 
242                            Dependencies1)) ])
243            ]),     
244
245	BodyLanguageAst = erl_syntax:match_expr(
246							erl_syntax:variable("Language"),
247							erl_syntax:application(
248						        erl_syntax:atom(z_context), 
249						        erl_syntax:atom(language),
250						        [ z_context_ast(Context) ]
251							)
252					),
253
254    BodyRenderAsts = case TreeWalker#treewalker.has_auto_id of
255        false ->
256            [BodyLanguageAst, BodyAst];
257        true -> 
258            AutoIdVar = resolve_scoped_variable_ast("$autoid", Context),
259            BodyAutoIdAst = erl_syntax:match_expr(
260                                    AutoIdVar,
261                                    erl_syntax:application(
262                                                erl_syntax:atom(z_ids),
263                                                erl_syntax:atom(identifier),
264                                                [erl_syntax:integer(8)]
265                                    )
266                             ),
267            [BodyAutoIdAst, BodyLanguageAst, BodyAst]
268    end,
269
270    RenderInternalFunctionAst = erl_syntax:function(
271        erl_syntax:atom(render2), 
272            [ erl_syntax:clause(
273					[erl_syntax:variable("Variables"), erl_syntax:variable("ZpContext")], 
274					none, 
275                	BodyRenderAsts)
276			]),   
277    
278    ModuleAst = erl_syntax:attribute(erl_syntax:atom(module), [erl_syntax:atom(Module)]),
279    
280    ExportAst = erl_syntax:attribute(erl_syntax:atom(export),
281        [erl_syntax:list([
282		            erl_syntax:arity_qualifier(erl_syntax:atom(template_reset_counter), erl_syntax:integer(0)),
283		            erl_syntax:arity_qualifier(erl_syntax:atom(trans_table), erl_syntax:integer(0)),
284					erl_syntax:arity_qualifier(erl_syntax:atom(render), erl_syntax:integer(2)),
285                    erl_syntax:arity_qualifier(erl_syntax:atom(source), erl_syntax:integer(0)),
286                    erl_syntax:arity_qualifier(erl_syntax:atom(dependencies), erl_syntax:integer(0))])]),
287
288    [erl_syntax:revert(X) || X <- [ModuleAst, ExportAst, TemplateResetCounterFunctionAst, TransTableFunctionAst,
289            Render2FunctionAst, SourceFunctionAst, DependenciesFunctionAst, RenderInternalFunctionAst
290            | BodyInfo#ast_info.pre_render_asts]].    
291
292
293find_next([], _Find) -> error;
294find_next([Find,Next|_], Find) -> {ok, Next};
295find_next([_|Rest], Find) -> find_next(Rest, Find).
296
297
298% child templates should only consist of blocks at the top level
299body_extends(Extends, File, ThisParseTree, Context, TreeWalker) ->
300    case lists:member(File, Context#dtl_context.parse_trail) of
301        true ->
302            throw({error, "Circular file inclusion: " ++ File});
303        _ ->
304            notify({debug, template, {extends, Extends, File}}, Context#dtl_context.z_context),
305            case parse(File, Context) of
306                {ok, ParentParseTree} ->
307                    ThisFile = hd(Context#dtl_context.parse_trail),
308                    BlockDict = lists:foldl(
309                        fun
310                            ({block, {identifier, _, Name}, Contents}, Dict) ->
311                                Dict1 = dict:store(Name, {ThisFile, Contents}, Dict),
312                                dict:store({Name, ThisFile}, Contents, Dict1);
313                            (_, Dict) ->
314                                Dict
315                        end, dict:new(), ThisParseTree),
316                    with_dependency(File, body_ast(ParentParseTree, Context#dtl_context{
317                        block_dict = dict:merge(fun(_Key, _ParentVal, ChildVal) -> ChildVal end,
318                                                BlockDict,
319                                                Context#dtl_context.block_dict),
320                        block_trail = [],
321                        parse_trail = [File | Context#dtl_context.parse_trail],
322                        extends_trail = [Extends | Context#dtl_context.extends_trail]}, TreeWalker));
323                Err ->
324                    throw(Err)
325            end        
326    end.
327
328body_ast([overrules | ThisParseTree], Context, TreeWalker) ->
329    CurrentExtend = hd(Context#dtl_context.extends_trail),
330    CurrentFile = hd(Context#dtl_context.parse_trail),
331    Files = full_path(CurrentExtend, true, Context),
332    % Find the first file after the current file
333    case find_next(Files, CurrentFile) of
334        {ok, File} ->
335            notify({debug, template, {overrules, CurrentExtend, File}}, Context#dtl_context.z_context),
336            body_extends(CurrentExtend, File, ThisParseTree, Context, TreeWalker);
337        error ->
338            ?ERROR("body_ast: could not find overruled template for \"~p\" (~p)", [CurrentExtend,CurrentFile]),
339            throw({error, "Could not find the template for overrules: '" ++ CurrentExtend ++ "'"}),
340            {{erl_syntax:string(""), #ast_info{}}, TreeWalker}
341    end;
342    
343body_ast([{extends, {string_literal, _Pos, String}} | ThisParseTree], Context, TreeWalker) ->
344    Extends = unescape_string_literal(String),
345    case full_path(Extends, Context) of
346        {ok, File} ->
347            body_extends(Extends, File, ThisParseTree, Context, TreeWalker);
348       {error, Reason} ->
349            ?ERROR("body_ast: could not find template ~p (~p)", [Extends, Reason]),
350            throw({error, "Could not find the template for extends: '" ++ Extends ++ "'"}),
351            {{erl_syntax:string(""), #ast_info{}}, TreeWalker}
352    end;
353
354body_ast(DjangoParseTree, Context, TreeWalker) ->
355    {AstInfoList, TreeWalker2} = lists:mapfoldl(
356        fun
357            ({'block', {identifier, _, Name}, Contents}, TreeWalkerAcc) ->
358                CurrentFile = case Context#dtl_context.block_trail of
359                                    [] -> hd(Context#dtl_context.parse_trail);
360                                    [{_, F}|_] -> F
361                              end,
362                % remember this block for an 'inherit' tag
363                Context1 = Context#dtl_context{
364                    block_dict=dict:store({Name,CurrentFile}, Contents, Context#dtl_context.block_dict)
365                },
366                % See if this block has been overruled
367                {BlockFile, Block} = case dict:find(Name, Context#dtl_context.block_dict) of
368                    {ok, {_ChildFile, _ChildBlock} = B} ->
369                        B;
370                    error ->
371                        {CurrentFile, Contents}
372                end,
373                % Check if we have a recursive definition
374                case lists:member({Name,BlockFile}, Context#dtl_context.block_trail) of
375                    true ->
376                        ?ERROR("body_ast: recursive block ~p (~p)", [Name, BlockFile]),
377                        throw({error, "Recursive block definition of '" ++ Name ++ "' (" ++ BlockFile ++ ")"});
378                    false ->
379                        body_ast(Block,
380                            Context1#dtl_context{block_trail=[{Name,BlockFile}|Context1#dtl_context.block_trail]}, 
381                            TreeWalkerAcc)
382                end;
383            ('inherit', TreeWalkerAcc) ->
384                inherit_ast(Context, TreeWalkerAcc);
385            ({'comment', _Contents}, TreeWalkerAcc) ->
386                empty_ast(TreeWalkerAcc);
387			({'trans', {trans_text, _Pos, TransLiteral}}, TreeWalkerAcc) ->
388				trans_ast(TransLiteral, Context, TreeWalkerAcc);
389			({'trans_ext', {string_literal, _Pos, String}, Args}, TreeWalkerAcc) ->
390				trans_ext_ast(String, Args, Context, TreeWalkerAcc);
391            ({'date', 'now', {string_literal, _Pos, FormatString}}, TreeWalkerAcc) ->
392                now_ast(FormatString, Context, TreeWalkerAcc);
393            ({'autoescape', {identifier, _, OnOrOff}, Contents}, TreeWalkerAcc) ->
394                body_ast(Contents, Context#dtl_context{auto_escape = list_to_atom(OnOrOff)}, 
395                    TreeWalkerAcc);
396            ({'text', _Pos, String}, TreeWalkerAcc) -> 
397                string_ast(String, TreeWalkerAcc);
398            ({'include', {string_literal, _, File}, Args, All}, TreeWalkerAcc) ->
399                include_ast(unescape_string_literal(File), Args, All, Context, TreeWalkerAcc);
400            ({'catinclude', {string_literal, _, File}, RscId, Args, All}, TreeWalkerAcc) ->
401                catinclude_ast(unescape_string_literal(File), RscId, Args, All, Context, TreeWalkerAcc);
402            ({'if', {'expr', "b_not", E}, Contents}, TreeWalkerAcc) ->
403                {IfAstInfo, TreeWalker1} = empty_ast(TreeWalkerAcc),
404                {ElseAstInfo, TreeWalker2} = body_ast(Contents, Context, TreeWalker1),
405                ifexpr_ast(E, IfAstInfo, ElseAstInfo, Context, TreeWalker2);
406            ({'if', E, Contents}, TreeWalkerAcc) ->
407                {IfAstInfo, TreeWalker1} = body_ast(Contents, Context, TreeWalkerAcc),
408                {ElseAstInfo, TreeWalker2} = empty_ast(TreeWalker1),
409                ifexpr_ast(E, IfAstInfo, ElseAstInfo, Context, TreeWalker2);
410            ({'ifelse', {'expr', "b_not", E}, IfContents, ElseContents}, TreeWalkerAcc) ->
411                {IfAstInfo, TreeWalker1} = body_ast(ElseContents, Context, TreeWalkerAcc),
412                {ElseAstInfo, TreeWalker2} = body_ast(IfContents, Context, TreeWalker1),
413                ifexpr_ast(E, IfAstInfo, ElseAstInfo, Context, TreeWalker2);                  
414            ({'ifelse', E, IfContents, ElseContents}, TreeWalkerAcc) ->
415                {IfAstInfo, TreeWalker1} = body_ast(IfContents, Context, TreeWalkerAcc),
416                {ElseAstInfo, TreeWalker2} = body_ast(ElseContents, Context, TreeWalker1),
417                ifexpr_ast(E, IfAstInfo, ElseAstInfo, Context, TreeWalker2);
418            ({'ifequal', Args, Contents}, TreeWalkerAcc) ->
419                {IfAstInfo, TreeWalker1} = body_ast(Contents, Context, TreeWalkerAcc),
420                {ElseAstInfo, TreeWalker2} = empty_ast(TreeWalker1),
421                ifequalelse_ast(Args, IfAstInfo, ElseAstInfo, Context, TreeWalker2);
422            ({'ifequalelse', Args, IfContents, ElseContents}, TreeWalkerAcc) ->
423                {IfAstInfo, TreeWalker1} = body_ast(IfContents, Context, TreeWalkerAcc), 
424                {ElseAstInfo, TreeWalker2} = body_ast(ElseContents, Context,TreeWalker1),
425                ifequalelse_ast(Args, IfAstInfo, ElseAstInfo, Context, TreeWalker2);                
426            ({'ifnotequal', Args, Contents}, TreeWalkerAcc) ->
427                {IfAstInfo, TreeWalker1} = empty_ast(TreeWalkerAcc),
428                {ElseAstInfo, TreeWalker2} = body_ast(Contents, Context, TreeWalker1),
429                ifequalelse_ast(Args, IfAstInfo, ElseAstInfo, Context, TreeWalker2);
430            ({'ifnotequalelse', Args, IfContents, ElseContents}, TreeWalkerAcc) ->
431                {IfAstInfo, TreeWalker1} = body_ast(ElseContents, Context, TreeWalkerAcc),
432                {ElseAstInfo, TreeWalker2} = body_ast(IfContents, Context, TreeWalker1),
433                ifequalelse_ast(Args, IfAstInfo, ElseAstInfo, Context, TreeWalker2);                    
434            ({'with', [ExprList, Identifiers], WithContents}, TreeWalkerAcc) ->
435                with_ast(ExprList, Identifiers, WithContents, Context, TreeWalkerAcc);
436            ({'for', {'in', IteratorList, Value}, Contents}, TreeWalkerAcc) ->
437                for_loop_ast(IteratorList, Value, Contents, none, Context, TreeWalkerAcc);
438            ({'for', {'in', IteratorList, Value}, Contents, EmptyPartContents}, TreeWalkerAcc) ->
439                for_loop_ast(IteratorList, Value, Contents, EmptyPartContents, Context, TreeWalkerAcc);
440            ({'load', Names}, TreeWalkerAcc) ->
441                load_ast(Names, Context, TreeWalkerAcc);
442            ({'tag', {'identifier', _, Name}, Args, All}, TreeWalkerAcc) ->
443                tag_ast(Name, Args, All, Context, TreeWalkerAcc);
444            ({'call_args', {'identifier', _, Name}, Args}, TreeWalkerAcc) ->
445            	call_ast(Name, Args, Context, TreeWalkerAcc);
446            ({'call_with', {'identifier', _, Name}, With}, TreeWalkerAcc) ->
447            	call_with_ast(Name, With, Context, TreeWalkerAcc);
448            ({'cycle', Names}, TreeWalkerAcc) ->
449                cycle_ast(Names, Context, TreeWalkerAcc);
450            ({'cycle_compat', Names}, TreeWalkerAcc) ->
451                cycle_compat_ast(Names, Context, TreeWalkerAcc);
452            ({'media', Variable, Args}, TreeWalkerAcc) ->
453                media_ast(Variable, Args, Context, TreeWalkerAcc);
454            ({'image', Variable, Args}, TreeWalkerAcc) ->
455                image_ast(Variable, Args, Context, TreeWalkerAcc);
456            ({'image_url', Variable, Args}, TreeWalkerAcc) ->
457                image_url_ast(Variable, Args, Context, TreeWalkerAcc);
458            ({'url', {'identifier', _, Name}, Args}, TreeWalkerAcc) ->
459                url_ast(Name, Args, Context, TreeWalkerAcc);
460            ({'print', Value}, TreeWalkerAcc) ->
461                print_ast(Value, Context, TreeWalkerAcc);
462            ({'lib', LibList, Args}, TreeWalkerAcc) ->
463                lib_ast(LibList, Args, Context, TreeWalkerAcc);
464            ({'cache', [MaxAge, Args], CacheContents}, TreeWalkerAcc) ->
465                cache_ast(MaxAge, Args, CacheContents, Context, TreeWalkerAcc);
466            (ValueToken, TreeWalkerAcc) -> 
467                {{ValueAst,ValueInfo},ValueTreeWalker} = value_ast(ValueToken, true, Context, TreeWalkerAcc),
468                {{format(ValueAst, Context),ValueInfo},ValueTreeWalker}
469        end, TreeWalker, DjangoParseTree),
470    
471    {AstList, {Info, TreeWalker3}} = lists:mapfoldl(
472        fun({Ast, Info}, {InfoAcc, TreeWalkerAcc}) -> 
473                PresetVars = lists:foldl(fun
474                        (X, Acc) ->
475                            case proplists:lookup(list_to_atom(X), Context#dtl_context.vars) of
476                                none ->
477                                    Acc;
478                                Val ->
479                                    [erl_syntax:abstract(Val) | Acc]
480                            end
481                    end, [], Info#ast_info.var_names),
482                case PresetVars of
483                    [] ->
484                        {Ast, {merge_info(Info, InfoAcc), TreeWalkerAcc}};
485                    _ ->
486                        Counter = TreeWalkerAcc#treewalker.counter,
487                        Name = lists:concat([pre_render, Counter]),
488                        Ast1 = erl_syntax:application(none, erl_syntax:atom(Name),
489                            [erl_syntax:list(PresetVars)]),
490                        PreRenderAst = erl_syntax:function(erl_syntax:atom(Name),
491                            [erl_syntax:clause([erl_syntax:variable("Variables")], none, [Ast])]),
492                        PreRenderAsts = Info#ast_info.pre_render_asts,
493                        Info1 = Info#ast_info{pre_render_asts = [PreRenderAst | PreRenderAsts]},     
494                        {Ast1, {merge_info(Info1, InfoAcc), TreeWalkerAcc#treewalker{counter = Counter + 1}}}
495                end
496        end, {#ast_info{}, TreeWalker2}, AstInfoList),
497    {{erl_syntax:list(AstList), Info}, TreeWalker3}.
498
499
500merge_info(Info1, Info2) ->
501    #ast_info{dependencies = 
502        lists:merge(
503            lists:sort(Info1#ast_info.dependencies), 
504            lists:sort(Info2#ast_info.dependencies)),
505        var_names = 
506            lists:merge(
507                lists:sort(Info1#ast_info.var_names), 
508                lists:sort(Info2#ast_info.var_names)),
509        pre_render_asts = 
510            lists:merge(
511                Info1#ast_info.pre_render_asts,
512                Info2#ast_info.pre_render_asts)}.
513
514
515%with_dependencies([], Args) ->
516%    Args;
517%with_dependencies([H, T], Args) ->
518%     with_dependencies(T, with_dependency(H, Args)).
519%        
520with_dependency(FilePath, {{Ast, Info}, TreeWalker}) ->
521    {{Ast, Info#ast_info{dependencies = [{FilePath, filelib:last_modified(FilePath)} | Info#ast_info.dependencies]}}, TreeWalker}.
522
523
524
525inherit_ast(Context, TreeWalker) ->
526    {BlockName,BlockFile} = hd(Context#dtl_context.block_trail),
527    Inherited = [ {F, dict:find({BlockName,F}, Context#dtl_context.block_dict)} 
528                || F <- find_prev_all(Context#dtl_context.parse_trail, BlockFile, []) ],
529    case [ {F,C} || {F,{ok, C}} <- Inherited ] of
530        [{InheritedFile,Content}|_] ->
531            body_ast(Content,
532                     Context#dtl_context{block_trail=[{BlockName,InheritedFile}|Context#dtl_context.block_trail]}, 
533                     TreeWalker);
534        [] ->
535            {{erl_syntax:string(""), #ast_info{}}, TreeWalker}
536    end.
537
538    find_prev_all([], _Find, Acc) -> Acc;
539    find_prev_all([Find|_], Find, Acc) -> Acc;
540    find_prev_all([F|Rest], Find, Acc) -> find_prev_all(Rest, Find, [F|Acc]).
541
542
543empty_ast(TreeWalker) ->
544    {{erl_syntax:list([]), #ast_info{}}, TreeWalker}.
545
546
547value_ast(ValueToken, AsString, Context, TreeWalker) ->
548    case ValueToken of
549        {'expr', Operator, Value} ->
550            {{ValueAst,InfoValue}, TreeWalker1} = value_ast(Value, false, Context, TreeWalker),
551            Ast = erl_syntax:application(erl_syntax:atom(erlydtl_operators), 
552                                         erl_syntax:atom(Operator), 
553                                         [ValueAst, z_context_ast(Context)]),
554            {{Ast, InfoValue}, TreeWalker1};
555        {'expr', Operator, Value1, Value2} ->
556            {{Value1Ast,InfoValue1}, TreeWalker1} = value_ast(Value1, false, Context, TreeWalker),
557            {{Value2Ast,InfoValue2}, TreeWalker2} = value_ast(Value2, false, Context, TreeWalker1),
558            Ast = erl_syntax:application(erl_syntax:atom(erlydtl_operators), 
559                                         erl_syntax:atom(Operator), 
560                                         [Value1Ast, Value2Ast, z_context_ast(Context)]),
561            {{Ast, merge_info(InfoValue1,InfoValue2)}, TreeWalker2};
562        {'string_literal', _Pos, String} ->
563            {{auto_escape(erl_syntax:string(unescape_string_literal(String)), Context), 
564                    #ast_info{}}, TreeWalker};
565		{'trans_literal', _Pos, String} ->
566            {{auto_escape(trans_literal_ast(String, Context), Context), 
567                    #ast_info{}}, TreeWalker};
568        {'number_literal', _Pos, Number} ->
569            case AsString of
570                true  -> string_ast(Number, TreeWalker);
571                false -> {{erl_syntax:integer(list_to_integer(Number)), #ast_info{}}, TreeWalker}
572            end;
573        {'atom_literal', _Pos, String} ->
574            {{erl_syntax:atom(to_atom(unescape_string_literal(String))), #ast_info{}}, TreeWalker};
575        undefined ->
576            {{erl_syntax:atom(undefined), #ast_info{}}, TreeWalker};
577        {'auto_id', Name} ->
578            auto_id_ast(Name, Context, TreeWalker);
579        {'apply_filter', Variable, Filter} ->
580            filter_ast(Variable, Filter, Context, TreeWalker);
581        {'attribute', _} = Variable ->
582            {{Ast, VarName, VarInfo}, TreeWalker1} = resolve_variable_ast(Variable, Context, TreeWalker),
583            {{Ast, merge_info(VarInfo,#ast_info{var_names = [VarName]})}, TreeWalker1};
584        {'variable', _} = Variable ->
585            {{Ast, VarName, VarInfo}, TreeWalker1} = resolve_variable_ast(Variable, Context, TreeWalker),
586            {{Ast, merge_info(VarInfo, #ast_info{var_names = [VarName]})}, TreeWalker1};
587        {'index_value', _, _} = Variable ->
588            {{Ast, VarName, VarInfo}, TreeWalker1} = resolve_indexvariable_ast(Variable, Context, TreeWalker),
589            {{Ast, merge_info(VarInfo, #ast_info{var_names = [VarName]})}, TreeWalker1};
590        {tuple_value, {identifier, _, TupleName}, TupleArgs} ->
591            TupleNameAst = erl_syntax:atom(TupleName),
592            {TupleArgsAst, TreeWalker1} = scomp_ast_list_args(TupleArgs, Context, TreeWalker),
593            {{erl_syntax:tuple([TupleNameAst, TupleArgsAst]), #ast_info{}}, TreeWalker1};
594        {value_list, Values} ->
595            {ValueAstList, ValueInfo, TreeWalker1} = lists:foldl(
596                        fun(V, {Acc,Info,TreeW}) ->
597                            {{Ast,InfoV}, TreeW1} = value_ast(V, false, Context, TreeW),
598                            {[Ast|Acc], merge_info(Info,InfoV), TreeW1}
599                        end,
600                        {[], #ast_info{}, TreeWalker}, 
601                        Values),
602            {{erl_syntax:list(lists:reverse(ValueAstList)), ValueInfo},TreeWalker1}
603    end.
604
605string_ast(String, TreeWalker) ->
606    % {{erl_syntax:string(String), #ast_info{}}, TreeWalker}. %% less verbose AST, better for development and debugging
607    {{erl_syntax:binary([erl_syntax:binary_field(erl_syntax:integer(X)) || X <- String]), #ast_info{}}, TreeWalker}.       
608
609catinclude_ast(File, Id, Args, All, Context, TreeWalker) ->
610    Args1 = [ {{identifier, none, "$file"},{string_literal, none, File}},
611			  {{identifier, none, "id"}, Id} | Args],
612    scomp_ast("catinclude", Args1, All, Context, TreeWalker).
613
614
615include_ast(File, Args, All, Context, TreeWalker) ->
616    {UseScomp, IsSudo} = lists:foldl( fun({{identifier, _, Key}, Val}, {IsC,IsSu}) -> 
617                                case Key of
618                                    "maxage" -> {true, IsSu};
619                                    "vary"   -> {true, IsSu};
620                                    "scomp"  -> {true, IsSu};
621                                    "visible_for" -> {true, IsSu};
622                                    "sudo" ->
623                                        case Val of
624                                            true -> {IsC, true};
625                                            _ -> {IsC, IsSu}
626                                        end;
627                                    _ -> {IsC, IsSu}
628                                end
629                            end,
630                            {false, false},
631                            Args),
632    case UseScomp of
633        false ->
634            {InterpretedArgs, TreeWalker1} = interpreted_args(Args, Context, TreeWalker),
635            {ScopedArgs, ArgAsts} = lists:foldr(
636                fun({AKey, AAst}, {ScopeAcc, AstAcc}) ->
637                    Var = "Arg_" ++ z_ids:identifier(10),
638                    AssignAst = erl_syntax:match_expr(erl_syntax:variable(Var), AAst),
639                    { [{AKey, erl_syntax:variable(Var)}|ScopeAcc], [AssignAst|AstAcc] }
640                end,
641                {[], []},
642                InterpretedArgs),
643
644            {ContextInclude,ArgAsts1} = case IsSudo of
645                                true -> 
646                                    V = "ZpContext_" ++ z_ids:id(10), 
647                                    ZpContextAst = erl_syntax:match_expr(
648                                                        erl_syntax:variable(V),
649                                                        erl_syntax:application(
650                                                                    erl_syntax:atom(z_acl),
651                                                                    erl_syntax:atom(sudo),
652                                                                    [z_context_ast(Context)])),
653                                    LocalScope = [{'ZpContext', erl_syntax:variable(V)}],
654                                    { Context#dtl_context{local_scopes=[LocalScope|Context#dtl_context.local_scopes]},
655                                      [ZpContextAst|ArgAsts] 
656                                    };
657                                false -> 
658                                    {Context, ArgAsts}
659                             end,
660
661            % {AstList, Info, TreeWalker}
662            IncludeFun = fun(FilePath, {AstList, InclInfo, TreeW}) ->
663                    notify({debug, template, {include, File, FilePath}}, ContextInclude#dtl_context.z_context),
664                    case parse(FilePath, ContextInclude) of
665                        {ok, InclusionParseTree} ->
666                            AutoIdVar = "AutoId_"++z_ids:identifier(),
667                            IncludeScope = [ {'$autoid', erl_syntax:variable(AutoIdVar)} | ScopedArgs ],
668
669                            {{Ast,Info}, InclTW2} = 
670                                            with_dependency(FilePath, 
671                                                    body_ast(
672                                                        InclusionParseTree,
673                                                        Context#dtl_context{
674                                                                local_scopes = [ IncludeScope | ContextInclude#dtl_context.local_scopes ],
675                                                                parse_trail = [FilePath | ContextInclude#dtl_context.parse_trail]}, 
676                                                        TreeW#treewalker{has_auto_id=false})),
677                            Ast1 = case InclTW2#treewalker.has_auto_id of
678                                false -> Ast;
679                                true ->  erl_syntax:block_expr(
680                                            [
681                                            erl_syntax:match_expr(
682                                                    erl_syntax:variable(AutoIdVar), 
683                                                    erl_syntax:application(
684                                                        erl_syntax:atom(z_ids),
685                                                        erl_syntax:atom(identifier),
686                                                        [])),
687                                            Ast])
688                            end,
689                            {[Ast1|AstList], merge_info(InclInfo, Info), InclTW2#treewalker{has_auto_id=TreeW#treewalker.has_auto_id}};
690                        Err ->
691                            throw(Err)
692                    end
693            end,
694
695            % Compile all included files, put them in a block expr with a single assignment of the argument vars at the start.
696            case lists:foldl(IncludeFun, {[], #ast_info{}, TreeWalker1}, full_path(File, All, Context)) of
697                {[], _, TreeWalkerN} ->
698                    case All of
699                        false -> ?LOG("include_ast: could not find template ~p", [File]);
700                        true -> ok
701                    end,
702                    {{erl_syntax:string(""), #ast_info{}}, TreeWalkerN};
703                {AstList, AstInfo, TreeWalkerN} ->
704                    AstN = erl_syntax:block_expr(ArgAsts1 ++ [erl_syntax:list(lists:reverse(AstList))]),
705                    {{AstN, AstInfo}, TreeWalkerN}
706            end;
707        true ->
708            Args1 = [{{identifier, none, "$file"},{string_literal, none, File}} | Args],
709            scomp_ast("include", Args1, All, Context, TreeWalker)
710    end.
711
712
713filter_ast(Variable, Filter, Context, TreeWalker) ->
714    % the escape filter is special; it is always applied last, so we have to go digging for it
715
716    % AutoEscape = 'did' means we (will have) decided whether to escape the current variable,
717    % so don't do any more escaping
718    {{UnescapedAst, Info}, TreeWalker2} = filter_ast_noescape(Variable, Filter, Context#dtl_context{auto_escape = did}, TreeWalker),
719    case search_for_escape_filter(Variable, Filter, Context) of
720        on ->
721            {{erl_syntax:application(
722                    erl_syntax:atom(filter_force_escape), 
723                    erl_syntax:atom(force_escape), 
724                    [UnescapedAst, z_context_ast(Context)]), 
725                Info}, TreeWalker2};
726        _ ->
727            {{UnescapedAst, Info}, TreeWalker2}
728    end.
729
730filter_ast_noescape(Variable, {filter, {identifier, _, "escape"}, []}, Context, TreeWalker) ->
731    value_ast(Variable, true, Context, TreeWalker);
732filter_ast_noescape(Variable, Filter, Context, TreeWalker) ->
733    {{VariableAst,Info},TreeWalker2} = value_ast(Variable, true, Context, TreeWalker),
734    {{FilterAst,Info2},TreeWalker3} = filter_ast1(Filter, VariableAst, Context, TreeWalker2),
735    {{FilterAst, merge_info(Info, Info2)}, TreeWalker3}.
736
737filter_ast1({filter, {identifier, _, Name}, []}, VariableAst, Context, TreeWalker) ->
738    FilterAst = erl_syntax:application(erl_syntax:atom(list_to_atom("filter_"++Name)), erl_syntax:atom(Name), [VariableAst, z_context_ast(Context)]),
739    {{FilterAst, #ast_info{}}, TreeWalker};
740filter_ast1({filter, {identifier, _, "default"}, [Arg]}, VariableAst, Context, TreeWalker) ->
741    {{ArgAst, Info},TreeWalker1} = value_ast(Arg, false, Context, TreeWalker),
742    VarAst  = erl_syntax:variable("Default_" ++ z_ids:identifier()),
743    CaseAst = erl_syntax:case_expr(erl_syntax:application(erl_syntax:atom(erlydtl_runtime), erl_syntax:atom(is_false), [VarAst]),
744        [erl_syntax:clause([erl_syntax:atom(true)], none, 
745                [ArgAst]),
746         erl_syntax:clause([erl_syntax:underscore()], none,
747                [VarAst])
748        ]),
749    {{erl_syntax:block_expr([erl_syntax:match_expr(VarAst, VariableAst), CaseAst]), Info}, TreeWalker1};
750filter_ast1({filter, {identifier, _, "default_if_none"}, [Arg]}, VariableAst, Context, TreeWalker) ->
751    {{ArgAst, Info},TreeWalker1} = value_ast(Arg, false, Context, TreeWalker),
752    VarAst  = erl_syntax:variable("Default_" ++ z_ids:identifier()),
753    CaseAst = erl_syntax:case_expr(VariableAst,
754        [erl_syntax:clause([erl_syntax:atom(undefined)], none, 
755                [ArgAst]),
756         erl_syntax:clause([VarAst], none,
757                [VarAst])
758        ]),
759    {{CaseAst, Info}, TreeWalker1};
760filter_ast1({filter, {identifier, Pos, "default_if_undefined"}, Args}, VariableAst, Context, TreeWalker) ->
761    filter_ast1({filter, {identifier, Pos, "default_if_none"}, Args}, VariableAst, Context, TreeWalker);
762filter_ast1({filter, {identifier, _, Name}, Args}, VariableAst, Context, TreeWalker) ->
763    {{ArgAsts, Info}, TreeWalker2} = lists:foldr(
764                        fun(Arg, {{As,In},Tw}) ->
765                            {{ArgAst,ArgIn}, Tw1} = value_ast(Arg, false, Context, Tw),
766                            {{[ArgAst|As], merge_info(In,ArgIn)}, Tw1}
767                        end,
768                        {{[], #ast_info{}}, TreeWalker},
769                        Args),
770    FilterAst = erl_syntax:application(
771                    erl_syntax:atom(list_to_atom("filter_"++Name)), 
772                    erl_syntax:atom(Name), 
773                    [VariableAst|ArgAsts] ++ [z_context_ast(Context)]
774                ),
775    {{FilterAst, Info}, TreeWalker2}.
776    
777 
778search_for_escape_filter(_, _, #dtl_context{auto_escape = on}) ->
779    on;
780search_for_escape_filter(_, _, #dtl_context{auto_escape = did}) ->
781    off;
782search_for_escape_filter(Variable, Filter, _) ->
783    search_for_escape_filter(Variable, Filter).
784
785search_for_escape_filter(_, {filter, {identifier, _, "escape"}, []}) ->
786    on;
787search_for_escape_filter({apply_filter, Variable, Filter}, _) ->
788    search_for_escape_filter(Variable, Filter);
789search_for_escape_filter(_Variable, _Filter) ->
790    off.
791
792
793
794resolve_variable_ast(VarTuple, Context, TreeWalker) ->
795    opttrans_variable_ast(resolve_variable_ast(VarTuple, Context, TreeWalker, 'fetch_value'), Context).
796
797resolve_ifvariable_ast(VarTuple, Context, TreeWalker) ->
798    opttrans_variable_ast(resolve_variable_ast(VarTuple, Context, TreeWalker, 'find_value'), Context).
799
800resolve_indexvariable_ast(VarTuple, Context, TreeWalker) ->
801    opttrans_variable_ast(resolve_variable_ast(VarTuple, Context, TreeWalker, 'fetch_value'), Context).
802
803
804opttrans_variable_ast({{Ast, VarName, Info}, TreeWalker}, Context) ->
805    Ast1 = erl_syntax:application(
806            erl_syntax:atom(z_trans), 
807            erl_syntax:atom(lookup_fallback),
808			[
809				Ast,
810				z_context_ast(Context)
811			]),
812	{{Ast1, VarName, Info}, TreeWalker}.
813
814resolve_variable_ast({index_value, Variable, Index}, Context, TreeWalker, FinderFunction) ->
815    {{IndexAst,Info},TreeWalker2} = value_ast(Index, false, Context, TreeWalker),
816    {{VarAst, VarName, Info2}, TreeWalker3} = resolve_variable_ast(Variable, Context, TreeWalker2, FinderFunction),
817    Ast = erl_syntax:application(
818            erl_syntax:atom(erlydtl_runtime), 
819            erl_syntax:atom(FinderFunction),
820            [IndexAst, VarAst, z_context_ast(Context)]),
821    {{Ast, VarName, merge_info(Info, Info2)}, TreeWalker3};
822
823resolve_variable_ast({attribute, {{identifier, _, Arg}, {variable, {identifier, _, "q"}}}}, Context, TreeWalker, _FinderFunction) ->
824    Ast = erl_syntax:application(
825            erl_syntax:atom(z_context), 
826            erl_syntax:atom(get_q),
827            [erl_syntax:string(Arg), z_context_ast(Context)]),
828    {{Ast, "q", #ast_info{}}, TreeWalker};
829
830resolve_variable_ast({attribute, {{identifier, _, Arg}, {variable, {identifier, _, "q_validated"}}}}, Context, TreeWalker, _FinderFunction) ->
831    Ast = erl_syntax:application(
832            erl_syntax:atom(z_context), 
833            erl_syntax:atom(get_q_validated),
834            [erl_syntax:string(Arg), z_context_ast(Context)]),
835    {{Ast, "q", #ast_info{}}, TreeWalker};
836
837resolve_variable_ast({attribute, {{identifier, _, Model}, {variable, {identifier, _, "m"}}}}, _Context, TreeWalker, _FinderFunction) ->
838    Ast = erl_syntax:tuple([
839            erl_syntax:atom(m),
840            erl_syntax:atom("m_" ++ Model),
841            erl_syntax:atom(undefined)
842        ]),
843    {{Ast, "m", #ast_info{}}, TreeWalker};
844
845resolve_variable_ast({attribute, {{identifier, _, AttrName}, Variable}}, Context, TreeWalker, FinderFunction) ->
846    {{VarAst, VarName, Info}, TreeWalker2} = resolve_variable_ast(Variable, Context, TreeWalker, FinderFunction),
847    Ast = erl_syntax:application(
848            erl_syntax:atom(erlydtl_runtime),
849            erl_syntax:atom(FinderFunction),
850            [erl_syntax:atom(AttrName), VarAst, z_context_ast(Context)]),
851    {{Ast, VarName, Info}, TreeWalker2};
852
853resolve_variable_ast({variable, {identifier, _, "now"}}, Context, TreeWalker, _FinderFunction) ->
854    Ast = case resolve_scoped_variable_ast("now", Context) of
855        undefined ->
856            erl_syntax:application(
857                erl_syntax:atom(erlang),
858                erl_syntax:atom(localtime),
859                []);
860        Val ->
861            Val
862    end,
863    {{Ast, "now", #ast_info{}}, TreeWalker};
864
865resolve_variable_ast({variable, {identifier, _, "z_language"}}, Context, TreeWalker, _FinderFunction) ->
866    Ast = case resolve_scoped_variable_ast("z_language", Context) of
867        undefined ->
868            erl_syntax:application(
869                erl_syntax:atom(z_context),
870                erl_syntax:atom(language),
871                [z_context_ast(Context)]);
872        Val ->
873            Val
874    end,
875    {{Ast, "z_language", #ast_info{}}, TreeWalker};
876
877
878resolve_variable_ast({variable, {identifier, _, VarName}}, Context, TreeWalker, FinderFunction) ->
879    Ast = case resolve_scoped_variable_ast(VarName, Context) of
880        undefined ->
881            erl_syntax:application(
882                erl_syntax:atom(erlydtl_runtime), 
883                erl_syntax:atom(FinderFunction),
884                [erl_syntax:atom(VarName), erl_syntax:variable("Variables"), z_context_ast(Context)]);
885        Val ->
886            Val
887    end,
888    {{Ast, VarName, #ast_info{}}, TreeWalker};
889
890resolve_variable_ast({apply_filter, Variable, Filter}, Context, TreeWalker, FinderFunction) ->
891    {{VarAst, VarName, Info}, TreeWalker2} = resolve_variable_ast(Variable, Context, TreeWalker, FinderFunction),
892    ValueAst = erl_syntax:application(
893            erl_syntax:atom(erlydtl_runtime),
894            erl_syntax:atom(to_value),
895            [VarAst, z_context_ast(Context)]
896        ),
897    {{VarValue, Info2}, TreeWalker3} = filter_ast1(Filter, ValueAst, Context, TreeWalker2),
898    {{VarValue, VarName, merge_info(Info, Info2)}, TreeWalker3};
899
900resolve_variable_ast(ValueToken, Context, TreeWalker, _FinderFunction) ->
901    {{Ast, Info}, TreeWalker1} = value_ast(ValueToken, false, Context, TreeWalker),
902    {{Ast, "$value", Info}, TreeWalker1}.
903
904
905resolve_scoped_variable_ast(VarName, Context) ->
906    lists:foldl(fun(Scope, Value) ->
907                case Value of
908                    undefined -> proplists:get_value(list_to_atom(VarName), Scope);
909                    _ -> Value
910                end
911        end, undefined, Context#dtl_context.local_scopes).
912
913
914%% @doc Return the AST for the z_context var
915z_context_ast(Context) ->
916    case resolve_scoped_variable_ast("ZpContext", Context) of
917        undefined -> erl_syntax:variable("ZpContext"); 
918        Ast -> Ast
919    end.
920
921
922format(Ast, Context) ->
923    auto_escape(stringify(Ast, Context), Context).
924
925stringify(Ast, Context) ->
926    erl_syntax:application(erl_syntax:atom(filter_stringify), erl_syntax:atom(stringify),
927        [Ast, z_context_ast(Context)]).
928
929auto_escape(Value, Context) ->
930    case Context#dtl_context.auto_escape of
931        on ->
932            erl_syntax:application(erl_syntax:atom(filter_force_escape), erl_syntax:atom(force_escape),
933                [Value, z_context_ast(Context)]);
934        _ ->
935            Value
936    end.
937
938ifexpr_ast(Expression, {IfContentsAst, IfContentsInfo}, {ElseContentsAst, ElseContentsInfo}, Context, TreeWalker) ->
939    Info = merge_info(IfContentsInfo, ElseContentsInfo),
940    {{Ast, ExpressionInfo}, TreeWalker1} = value_ast(Expression, false, Context, TreeWalker),
941    {{erl_syntax:case_expr(erl_syntax:application(erl_syntax:atom(erlydtl_runtime), erl_syntax:atom(is_false), [Ast]),
942        [erl_syntax:clause([erl_syntax:atom(true)], none, 
943                [ElseContentsAst]),
944        erl_syntax:clause([erl_syntax:underscore()], none,
945                [IfContentsAst])
946        ]), merge_info(ExpressionInfo, Info)}, TreeWalker1}.
947    
948
949ifequalelse_ast(Args, {IfContentsAst, IfContentsInfo}, {ElseContentsAst, ElseContentsInfo}, Context, TreeWalker) ->
950    Info = merge_info(IfContentsInfo, ElseContentsInfo),
951    {[Arg1Ast, Arg2Ast], VarNames, Info1, TreeWalker1} = lists:foldl(fun
952            (X, {Asts, AccVarNames, Inf, TW}) ->
953                case X of
954					{string_literal, _, Literal} ->
955					    {[erl_syntax:string(unescape_string_literal(Literal)) | Asts], AccVarNames, Inf, TW};
956				    {trans_literal, _, Literal} ->
957				        {[trans_literal_ast(Literal, Context) | Asts], AccVarNames, Inf, TW};
958                    {number_literal, _, Literal} ->
959                        {[erl_syntax:integer(list_to_integer(Literal)) | Asts], AccVarNames, Inf, TW};
960                    Variable ->
961                        {{Ast, VarName, VarInfo}, TW1} = resolve_ifvariable_ast(Variable, Context, TW),
962                        {[Ast | Asts], [VarName | AccVarNames], merge_info(Inf, VarInfo), TW1}
963                end                
964        end,
965        {[], Info#ast_info.var_names, #ast_info{}, TreeWalker},
966        Args),
967    Ast = erl_syntax:case_expr(erl_syntax:application(erl_syntax:atom(erlydtl_runtime), erl_syntax:atom(are_equal),
968            [Arg1Ast, Arg2Ast]),
969        [
970            erl_syntax:clause([erl_syntax:atom(true)], none, [IfContentsAst]),
971            erl_syntax:clause([erl_syntax:underscore()], none, [ElseContentsAst])
972        ]),
973    {{Ast, merge_info(Info1, Info#ast_info{var_names = VarNames})}, TreeWalker1}.         
974
975
976%% With statement with only a single variable, easy & quick match.
977with_ast([Value], [{identifier, _, V}], Contents, Context, TreeWalker) ->
978    Postfix = z_ids:identifier(),
979    VarAst  = erl_syntax:variable("With_" ++ V ++ [$_|Postfix]),
980    {{ValueAst, ValueInfo}, TreeWalker1} = value_ast(Value, false, Context, TreeWalker),
981    LocalScope = [ {list_to_atom(V), VarAst} ],
982    {{InnerAst, InnerInfo}, TreeWalker2} = body_ast(
983            Contents,
984            Context#dtl_context{local_scopes=[LocalScope | Context#dtl_context.local_scopes]}, 
985            TreeWalker1),
986    WithAst = erl_syntax:block_expr([erl_syntax:match_expr(VarAst, ValueAst), InnerAst]),
987    {{WithAst, merge_info(ValueInfo,InnerInfo)}, TreeWalker2};
988    
989%% With statement with multiple vars, match against tuples and lists.
990with_ast([Value], Variables, Contents, Context, TreeWalker) ->
991    Postfix = z_ids:identifier(),
992    VarAsts = lists:map(fun({identifier, _, V}) -> 
993                    erl_syntax:variable("With_" ++ V ++ [$_|Postfix]) 
994            en

Large files files are truncated, but you can click here to view the full file