Browse Source

improved error reporting when compilation fails

git-svn-id: http://erlydtl.googlecode.com/svn/trunk@101 a5195066-8e3e-0410-a82a-05b01b1b9875
rsaccon 17 years ago
parent
commit
918b100c82

+ 12 - 0
demo/templates/test_customtags_error.html

@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Test variable</title>								 
+  </head>
+  <body>
+	before
+	{% flashvideo dom_id="myvideo" width="800" height="600" static="/static" path_to_video="/myvid.mp4" path_to_preview_image="/mypic.jpg" %}
+	after
+  </body>
+</html>

+ 16 - 7
src/demo/erlydtl_demo.erl

@@ -70,6 +70,7 @@ compile_all() ->
     compile("autoescape"),
     compile("autoescape"),
     compile("comment"),
     compile("comment"),
     compile("customtags"),
     compile("customtags"),
+    compile("customtags_error"),
     compile("extends"),
     compile("extends"),
     compile("filters"),
     compile("filters"),
     compile("for"),
     compile("for"),
@@ -172,7 +173,10 @@ compile("for_records_preset" = Name) ->
     
     
 compile("customtags" = Name) ->
 compile("customtags" = Name) ->
     compile(Name, ".html", []);
     compile(Name, ".html", []);
-          
+
+compile("customtags_error" = Name) ->
+    compile(Name, ".html", []);
+                  
 compile(Name) ->
 compile(Name) ->
     io:format("No such template: ~p~n",[Name]).
     io:format("No such template: ~p~n",[Name]).
                
                
@@ -190,11 +194,16 @@ compile(Name, Ext, Vars) ->
     case erlydtl_compiler:compile(File, Module, DocRoot, Vars) of
     case erlydtl_compiler:compile(File, Module, DocRoot, Vars) of
         ok ->
         ok ->
             io:format("compile success: ~p~n",[Module]);
             io:format("compile success: ~p~n",[Module]);
-        _ ->
-            io:format("compile failure: ~p~n",[Module])
+        {error, Reason} ->
+            case string:str(File, "error") of
+                0 ->
+                    io:format("compile failure: ~p ~p~n", [Module, Reason]);
+                _ ->
+                    Explanation = "==>> this is ok, we are testing an error !",
+                    io:format("compile failure: ~p ~p  ~s~n",[Module, Reason, Explanation])
+            end
     end.
     end.
 
 
-
 %%--------------------------------------------------------------------
 %%--------------------------------------------------------------------
 %% @spec () -> any()
 %% @spec () -> any()
 %% @doc renders template to a file
 %% @doc renders template to a file
@@ -326,13 +335,13 @@ render(Name, Args) ->
                 _ ->
                 _ ->
                     io:format("file writing failure: ~p~n",[Module])
                     io:format("file writing failure: ~p~n",[Module])
             end;
             end;
-        {_, Err} ->
+        {error, Reason} ->
             case string:str(Name, "error") of
             case string:str(Name, "error") of
                 0 ->
                 0 ->
-                    io:format("render failure: ~p ~p~n",[Module, Err]);
+                    io:format("render failure: ~p ~p~n",[Module, Reason]);
                 _ ->
                 _ ->
                     Explanation = "==>> this is ok, we are testing an error !",
                     Explanation = "==>> this is ok, we are testing an error !",
-                    io:format("render failure: ~p ~p  ~s~n",[Module, Err, Explanation])
+                    io:format("render failure: ~p ~p  ~s~n",[Module, Reason, Explanation])
             end
             end
     end.    
     end.    
             
             

+ 76 - 76
src/erlydtl/erlydtl_compiler.erl

@@ -72,83 +72,36 @@ compile(File, Module, DocRoot, Vars, Reader) ->
 compile(File, Module, DocRoot, Vars, Reader, OutDir) ->   
 compile(File, Module, DocRoot, Vars, Reader, OutDir) ->   
     case parse(File, Reader) of
     case parse(File, Reader) of
         {ok, DjangoParseTree} ->        
         {ok, DjangoParseTree} ->        
-            {{BodyAst, BodyInfo}, _} = body_ast(DjangoParseTree, #dtl_context{
-                    doc_root = DocRoot, parse_trail = [File], preset_vars = Vars, reader = Reader},
-                    #treewalker{}),
-
-            Render0FunctionAst = erl_syntax:function(erl_syntax:atom(render),
-                [erl_syntax:clause([], none, [erl_syntax:application(none, 
-                                erl_syntax:atom(render), [erl_syntax:list([])])])]),
-
-            Function2 = erl_syntax:application(none, erl_syntax:atom(render2),
-                [erl_syntax:variable("Variables")]),
-            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")])]),            
-            Render1FunctionAst = erl_syntax:function(erl_syntax:atom(render),
-                [erl_syntax:clause([erl_syntax:variable("Variables")], none, 
-                        [erl_syntax:try_expr([Function2], [ClauseOk], [ClauseCatch])])]),  
-
-            SourceFunctionAst = erl_syntax:function(
-                erl_syntax:atom(source),
-                [erl_syntax:clause([], none, [erl_syntax:string(File)])]),
-
-            DependenciesFunctionAst = erl_syntax:function(
-                erl_syntax:atom(dependencies), [erl_syntax:clause([], none, 
-                        [erl_syntax:list(lists:map(fun(Dep) -> erl_syntax:string(Dep) end, 
-                                    BodyInfo#ast_info.dependencies))])]),     
-
-            RenderInternalFunctionAst = erl_syntax:function(
-                erl_syntax:atom(render2), 
-                [erl_syntax:clause([erl_syntax:variable("Variables")], none, 
-                        [BodyAst])]),   
-
-            ProplistsClauseErr = erl_syntax:clause([erl_syntax:atom(undefined)], none, 
-            [erl_syntax:application(none, erl_syntax:atom(throw),
-                [erl_syntax:tuple([erl_syntax:atom(undefined_variable), erl_syntax:variable("Key")])])]),  
-            ProplistsClauseOk = erl_syntax:clause([erl_syntax:variable("Val")], none, 
-                [erl_syntax:variable("Val")]),       
-            ProplistsFunctionAst = erl_syntax:function(erl_syntax:atom(get_value), 
-                [erl_syntax:clause([erl_syntax:variable("Key"), erl_syntax:variable("L")], none, 
-                        [erl_syntax:case_expr(erl_syntax:application(erl_syntax:atom(proplists), 
-                                    erl_syntax:atom(get_value), [erl_syntax:variable("Key"), erl_syntax:variable("L")]), 
-                                [ProplistsClauseErr, ProplistsClauseOk])])]),
-
-            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(render), erl_syntax:integer(0)),
-                            erl_syntax:arity_qualifier(erl_syntax:atom(render), erl_syntax:integer(1)),
-                            erl_syntax:arity_qualifier(erl_syntax:atom(source), erl_syntax:integer(0)),
-                            erl_syntax:arity_qualifier(erl_syntax:atom(dependencies), erl_syntax:integer(0))])]),
-
-            Forms = [erl_syntax:revert(X) || X <- [ModuleAst, ExportAst, Render0FunctionAst,
-                    Render1FunctionAst, SourceFunctionAst, DependenciesFunctionAst, RenderInternalFunctionAst, 
-                    ProplistsFunctionAst | BodyInfo#ast_info.pre_render_asts]],
-
-            case compile:forms(Forms, []) of
-                {ok, Module1, Bin} ->       
-                    BeamFile = filename:join([OutDir, atom_to_list(Module1) ++ ".beam"]),
-                    case file:write_file(BeamFile, Bin) of
-                        ok ->
-                            code:purge(Module1),
-                            case code:load_binary(Module1, atom_to_list(Module1) ++ ".erl", Bin) of
-                                {module, _} -> ok;
-                                _ -> {error, lists:concat(["code reload failed: ", BeamFile])}
+            try body_ast(DjangoParseTree, #dtl_context{
+                    doc_root = DocRoot,
+                    parse_trail = [File], preset_vars = Vars, reader = Reader}, #treewalker{}) of
+                {{Ast, Info}, _} ->
+                    case compile:forms(forms(File, Module, Ast, Info), []) of
+                        {ok, Module1, Bin} ->       
+                            BeamFile = filename:join([OutDir, atom_to_list(Module1) ++ ".beam"]),
+                            case file:write_file(BeamFile, Bin) of
+                                ok ->
+                                    code:purge(Module1),
+                                    case code:load_binary(Module1, atom_to_list(Module1) ++ ".erl", Bin) of
+                                        {module, _} -> ok;
+                                        _ -> {error, lists:concat(["code reload failed: ", BeamFile])}
+                                    end;
+                                {error, Reason} ->
+                                    {error, lists:concat(["beam generation failed (", Reason, "): ", BeamFile])}
                             end;
                             end;
-                        {error, Reason} ->
-                            {error, lists:concat(["beam generation failed (", Reason, "): ", BeamFile])}
-                    end;
-                error ->
-                    {error, lists:concat(["compilation failed: ", File])};
-                Other ->
-                    Other
-            end;
+                        error ->
+                            {error, lists:concat(["compilation failed: ", File])};
+                        OtherError ->
+                            OtherError
+                    end
+            catch 
+                throw:Error -> Error
+            end; 
         Error ->
         Error ->
-            io:format("TRACE ~p:~p ~p~n",[?MODULE, ?LINE, Error]),
             Error
             Error
     end.
     end.
 
 
+
 scan(File, {Module, Function}) ->
 scan(File, {Module, Function}) ->
     case catch Module:Function(File) of
     case catch Module:Function(File) of
         {ok, B} ->
         {ok, B} ->
@@ -157,6 +110,7 @@ scan(File, {Module, Function}) ->
             {error, "reading " ++ File ++ " failed "}
             {error, "reading " ++ File ++ " failed "}
     end.
     end.
 
 
+
 parse(File, Reader) ->
 parse(File, Reader) ->
     case scan(File, Reader) of
     case scan(File, Reader) of
         {ok, Tokens} ->
         {ok, Tokens} ->
@@ -164,6 +118,52 @@ parse(File, Reader) ->
         Err ->
         Err ->
             Err
             Err
     end.
     end.
+  
+  
+forms(File, Module, BodyAst, BodyInfo) ->    
+    Render0FunctionAst = erl_syntax:function(erl_syntax:atom(render),
+        [erl_syntax:clause([], none, [erl_syntax:application(none, 
+                        erl_syntax:atom(render), [erl_syntax:list([])])])]),
+    Function2 = erl_syntax:application(none, erl_syntax:atom(render2),
+        [erl_syntax:variable("Variables")]),
+    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")])]),            
+    Render1FunctionAst = erl_syntax:function(erl_syntax:atom(render),
+        [erl_syntax:clause([erl_syntax:variable("Variables")], none, 
+                [erl_syntax:try_expr([Function2], [ClauseOk], [ClauseCatch])])]),  
+    SourceFunctionAst = erl_syntax:function(
+        erl_syntax:atom(source),
+        [erl_syntax:clause([], none, [erl_syntax:string(File)])]),
+    DependenciesFunctionAst = erl_syntax:function(
+        erl_syntax:atom(dependencies), [erl_syntax:clause([], none, 
+                [erl_syntax:list(lists:map(fun(Dep) -> erl_syntax:string(Dep) end, 
+                            BodyInfo#ast_info.dependencies))])]),     
+    RenderInternalFunctionAst = erl_syntax:function(
+        erl_syntax:atom(render2), 
+        [erl_syntax:clause([erl_syntax:variable("Variables")], none, 
+                [BodyAst])]),   
+    ProplistsClauseErr = erl_syntax:clause([erl_syntax:atom(undefined)], none, 
+    [erl_syntax:application(none, erl_syntax:atom(throw),
+        [erl_syntax:tuple([erl_syntax:atom(undefined_variable), erl_syntax:variable("Key")])])]),  
+    ProplistsClauseOk = erl_syntax:clause([erl_syntax:variable("Val")], none, 
+        [erl_syntax:variable("Val")]),       
+    ProplistsFunctionAst = erl_syntax:function(erl_syntax:atom(get_value), 
+        [erl_syntax:clause([erl_syntax:variable("Key"), erl_syntax:variable("L")], none, 
+                [erl_syntax:case_expr(erl_syntax:application(erl_syntax:atom(proplists), 
+                            erl_syntax:atom(get_value), [erl_syntax:variable("Key"), erl_syntax:variable("L")]), 
+                        [ProplistsClauseErr, ProplistsClauseOk])])]),
+    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(render), erl_syntax:integer(0)),
+                    erl_syntax:arity_qualifier(erl_syntax:atom(render), erl_syntax:integer(1)),
+                    erl_syntax:arity_qualifier(erl_syntax:atom(source), erl_syntax:integer(0)),
+                    erl_syntax:arity_qualifier(erl_syntax:atom(dependencies), erl_syntax:integer(0))])]),
+    [erl_syntax:revert(X) || X <- [ModuleAst, ExportAst, Render0FunctionAst,
+            Render1FunctionAst, SourceFunctionAst, DependenciesFunctionAst, RenderInternalFunctionAst, 
+            ProplistsFunctionAst | BodyInfo#ast_info.pre_render_asts]].    
+
 
 
 full_path(File, DocRoot) ->
 full_path(File, DocRoot) ->
     filename:join([DocRoot, File]).
     filename:join([DocRoot, File]).
@@ -174,7 +174,7 @@ body_ast([{extends, {string_literal, _Pos, String}} | ThisParseTree], Context, T
     File = full_path(unescape_string_literal(String), Context#dtl_context.doc_root),
     File = full_path(unescape_string_literal(String), Context#dtl_context.doc_root),
     case lists:member(File, Context#dtl_context.parse_trail) of
     case lists:member(File, Context#dtl_context.parse_trail) of
         true ->
         true ->
-            {error, "Circular file inclusion!"};
+            throw({error, "Circular file inclusion!"});
         _ ->
         _ ->
             {ok, ParentParseTree} = parse(File, Context#dtl_context.reader),
             {ok, ParentParseTree} = parse(File, Context#dtl_context.reader),
             BlockDict = lists:foldl(
             BlockDict = lists:foldl(
@@ -306,7 +306,7 @@ empty_ast(TreeWalker) ->
     {{erl_syntax:list([]), #ast_info{}}, TreeWalker}.
     {{erl_syntax:list([]), #ast_info{}}, TreeWalker}.
 
 
 string_ast(String, TreeWalker) ->
 string_ast(String, TreeWalker) ->
-    {{erl_syntax:string(String), #ast_info{}}, TreeWalker}. %% less verbose AST, good for debugging
+    {{erl_syntax:string(String), #ast_info{}}, TreeWalker}. %% less verbose AST, better for development and debugging
     % {{erl_syntax:binary([erl_syntax:binary_field(erl_syntax:integer(X)) || X <- String]), #ast_info{}}, TreeWalker}.       
     % {{erl_syntax:binary([erl_syntax:binary_field(erl_syntax:integer(X)) || X <- String]), #ast_info{}}, TreeWalker}.       
 
 
 include_ast(File, Context, TreeWalker) ->
 include_ast(File, Context, TreeWalker) ->
@@ -533,8 +533,8 @@ tag_ast(Name, Args, Context, TreeWalker) ->
                             local_scopes = [ InterpretedArgs | Context#dtl_context.local_scopes ],
                             local_scopes = [ InterpretedArgs | Context#dtl_context.local_scopes ],
                             parse_trail = [ Source | Context#dtl_context.parse_trail ]}, TreeWalker));
                             parse_trail = [ Source | Context#dtl_context.parse_trail ]}, TreeWalker));
                 _ ->
                 _ ->
-                    {error, Name, "Loading tag source failed: " ++ Source}
+                    throw({error, Name, "Loading tag source failed: " ++ Source})
             end;
             end;
         _ ->
         _ ->
-            {error, Name, "Custom tag not loaded"}
+            throw({error, lists:concat(["Custom tag not loaded: ", Name])})
     end.
     end.