ported ct_expand, bug fixes

git-svn-id: http://svn.ulf.wiger.net/parse_trans/trunk/parse_trans@5 ae7daa23-5771-0410-ae54-ec81a0701e84
This commit is contained in:
uwiger 2009-09-11 09:27:16 +00:00
parent 2fc581c60f
commit 2b89a55e5b
5 changed files with 204 additions and 60 deletions

View File

@ -13,6 +13,9 @@ MODULELIST := $(subst $(space),$(comma),$(MODULES))
TEST_SOURCES := $(wildcard test/*.erl)
TEST_BEAMS := $(patsubst %.erl,%.beam, $(TEST_SOURCES))
EXAMPLE_SOURCES := $(wildcard examples/*.erl)
EXAMPLE_BEAMS := $(patsubst %.erl,%.beam, $(EXAMPLE_SOURCES))
include vsn.mk
.PHONY: all clean dialyzer
@ -29,6 +32,12 @@ test/%.beam: test/%.erl
@echo Compiling $<
@erlc +debug_info -o test/ $<
examples: $(EXAMPLE_BEAMS)
examples/%.beam: examples/%.erl
@echo Compiling $<
@erlc -pa ebin -pa examples +debug_info -o examples/ $<
$(APP_FILE): src/$(APPLICATION).app.src
@echo Generating $@
@sed -e 's/@MODULES@/$(MODULELIST)/' -e 's/@VSN@/$(VSN)/' $< > $@
@ -43,17 +52,21 @@ ebin:
doc: doc/edoc-info
dialyzer:
dialyzer: util/my_plt.plt
@echo Running dialyzer on sources
@dialyzer --src -r src/
@dialyzer --src -r src/ --plt util/my_plt.plt
doc/edoc-info: doc/overview.edoc $(SOURCES)
@erlc -o util/ util/make_doc.erl
@echo Generating documentation from edoc
@erl -pa util/ -noinput -s make_doc edoc
util/%.beam: util/%.erl
@erlc -o util/ util/run_test.erl
@erlc -o util/ $<
util/my_plt.plt: util/make_plt.beam
@erl -noinput -pa util -eval 'make_plt:add([syntax_tools],"util/my_plt.plt")'
clean:
@echo Cleaning

View File

@ -3,7 +3,48 @@
This library is intended to simplify the task of writing parse transform
modules for Erlang.
<h1>Introduction</h1>
<h1>Introduction to parse transforms</h1>
<h2>The simplest transform</h2>
<p>The very simplest transform we can make is one that doesn't
change a thing. For convenience, we will at least print the forms.
This will enlighten us as to what the forms actually look like.</p>
<pre>
-module(test_pt).
-export([parse_transform/2]).
parse_transform(Forms, _Options) -&gt;
io:fwrite("Forms = ~p~n", [Forms]),
Forms.
</pre>
<p>Trying this with a very simple module:</p>
<pre>
-module(ex1).
-export([add/2]).
add(X,Y) -&gt;
X + Y.
</pre>
<pre>
1&gt; c(ex1, [{parse_transform,test_pt}]).
Forms = [{attribute,1,file,{"./ex1.erl",1}},
{attribute,1,module,ex1},
{attribute,2,export,[{add,2}]},
{function,4,add,2,
[{clause,4,
[{var,4,'X'},{var,4,'Y'}],
[],
[{op,5,'+',{var,5,'X'},{var,5,'Y'}}]}]},
{eof,6}]
{ok,ex1}
</pre>
<h2>`transform/4'</h2>
<p>...</p>

View File

@ -1,10 +1,8 @@
-module(test_pt).
-export([parse_transform/2]).
-compile(export_all).
parse_transform(Forms, Options) ->
parse_transform(Forms, _Options) ->
io:fwrite("Forms = ~p~n", [Forms]),
Forms.

View File

@ -36,6 +36,7 @@
-export([
inspect/4,
transform/4,
depth_first/4,
revert/1
]).
@ -47,7 +48,8 @@
-export([
initial_context/2,
do_inspect/4,
do_transform/4
do_transform/4,
do_depth_first/4
]).
-export([do_insert_forms/4]).
@ -89,6 +91,15 @@
throw({error,get_pos(I),{unknown,R}})
end).
%% Typer typedefs
-type form() :: any().
-type forms() :: [form()].
-type options() :: [{atom(), any()}].
-type type() :: atom().
-type xform_f() :: fun((type(), form(), #context{}, Acc) ->
{form(), bool(), Acc}
| {forms(), form(), forms(), bool(), Acc}).
-type insp_f() :: fun((type(), form(), #context{}, A) -> {bool(), A}).
%%% @spec (Reason, Form, Info) -> throw()
@ -98,11 +109,15 @@
%%% <p>Used to report errors detected during the parse transform.</p>
%%% @end
%%%
-spec error(string(), any(), [{any(),any()}]) ->
none().
error(R, F, I) ->
rpt_error(R, F, I),
throw({error,get_pos(I),{unknown,R}}).
-spec get_pos(list()) ->
integer().
get_pos(I) when is_list(I) ->
case proplists:get_value(form, I) of
undefined ->
@ -117,6 +132,8 @@ get_pos(I) when is_list(I) ->
%%% Returns the name of the file being compiled.
%%% @end
%%%
-spec get_file(forms()) ->
string().
get_file(Forms) ->
string_value(hd(get_attribute(file, Forms))).
@ -127,6 +144,8 @@ get_file(Forms) ->
%%% Returns the name of the module being compiled.
%%% @end
%%%
-spec get_module([any()]) ->
atom().
get_module(Forms) ->
atom_value(hd(get_attribute(module, Forms))).
@ -139,18 +158,29 @@ get_module(Forms) ->
%%% Returns the value of the first occurence of attribute A.
%%% @end
%%%
get_attribute(A, [F|Forms]) ->
-spec get_attribute(atom(), [any()]) ->
false | list().
get_attribute(A, Forms) ->
case find_attribute(A, Forms) of
false ->
throw({error, ?DUMMY_LINE, {missing_attribute, A}});
Other ->
Other
end.
find_attribute(A, [F|Forms]) ->
case type(F) == attribute
andalso atom_value(attribute_name(F)) == A of
true ->
attribute_arguments(F);
false ->
get_attribute(A, Forms)
find_attribute(A, Forms)
end;
get_attribute(A, []) ->
throw({error, ?DUMMY_LINE, {missing_attribute, A}}).
find_attribute(_, []) ->
false.
-spec function_exists(atom(), integer(), forms()) ->
bool().
function_exists(Fname, Arity, Forms) ->
Fns = proplists:get_value(
functions, erl_syntax_lib:analyze_forms(Forms), []),
@ -166,6 +196,8 @@ function_exists(Fname, Arity, Forms) ->
%%% name and the options passed to the transform function.
%%% @end
%%%
-spec initial_context(forms(), options()) ->
#context{}.
initial_context(Forms, Options) ->
File = get_file(Forms),
io:fwrite("File = ~p~n", [File]),
@ -182,11 +214,22 @@ initial_context(Forms, Options) ->
%%% @doc
%%% Makes one pass
%%% @end
transform(Fun, Acc, Forms, Options) when is_function(Fun, 5) ->
-spec transform(xform_f(), Acc, forms(), options()) ->
{forms(), Acc} | {error, list()}.
transform(Fun, Acc, Forms, Options) when is_function(Fun, 4) ->
do(fun do_transform/4, Fun, Acc, Forms, Options).
-spec depth_first(xform_f(), Acc, forms(), options()) ->
{forms(), Acc} | {error, list()}.
depth_first(Fun, Acc, Forms, Options) when is_function(Fun, 4) ->
do(fun do_depth_first/4, Fun, Acc, Forms, Options).
do(Transform, Fun, Acc, Forms, Options) ->
Context = initial_context(Forms, Options),
File = Context#context.file,
try do_transform(Fun, Acc, Forms, Context) of
{_, NewForms} = Result ->
try Transform(Fun, Acc, Forms, Context) of
{NewForms, _} = Result ->
optionally_pretty_print(NewForms, Options, Context),
Result
catch
@ -198,7 +241,8 @@ transform(Fun, Acc, Forms, Options) when is_function(Fun, 5) ->
{error, [{File, [{Ln, ?MODULE, What}]}], []}
end.
-spec do_insert_forms(above | below, forms(), forms(), #context{}) ->
forms().
do_insert_forms(above, Insert, Forms, Context) when is_list(Insert) ->
{NewForms, _} =
do_transform(
@ -208,11 +252,11 @@ do_insert_forms(above, Insert, Forms, Context) when is_list(Insert) ->
{F, _Recurse = false, Acc}
end, false, Forms, Context),
NewForms;
do_insert_forms(below, Insert, Forms, Context) when is_list(Insert) ->
do_insert_forms(below, Insert, Forms, _Context) when is_list(Insert) ->
insert_below(Forms, Insert).
insert_below([F|Rest] = Forms, Insert) ->
insert_below([F|Rest], Insert) ->
case type(F) of
eof_marker ->
Insert ++ [F];
@ -220,15 +264,28 @@ insert_below([F|Rest] = Forms, Insert) ->
[F|insert_below(Rest, Insert)]
end.
-spec optionally_pretty_print(forms(), options(), #context{}) ->
ok.
optionally_pretty_print(Result, Options, Context) ->
case lists:member(pt_pp_src, Options) of
true ->
DoPP =
case proplists:get_value(pt_pp_src, Options) of
undefined ->
case find_attribute(pt_pp_src,Result) of
[Expr] ->
type(Expr) == atom andalso
atom_value(Expr) == true;
_ ->
false
end;
V when is_boolean(V) ->
V
end,
if DoPP ->
File = Context#context.file,
Out = outfile(File),
pp_src(Result, Out),
io:fwrite("Pretty-printed in ~p~n", [Out]);
_ ->
io:fwrite("Will not pretty-print~n", []),
true ->
ok
end.
@ -239,6 +296,8 @@ optionally_pretty_print(Result, Options, Context) ->
%%% Equvalent to do_inspect(Fun,Acc,Forms,initial_context(Forms,Options)).
%%% @end
%%%
-spec inspect(insp_f(), A, forms(), options()) ->
A.
inspect(F, Acc, Forms, Options) ->
Context = initial_context(Forms, Options),
do_inspect(F, Acc, Forms, Context).
@ -312,7 +371,7 @@ do_inspect(F, Acc, Forms, Context) ->
Type = type(Form),
{Recurse, Acc1} = apply_F(F, Type, Form, Context, Acc0),
if_recurse(
Recurse, Form, Acc1,
Recurse, Form, _Else = Acc1,
fun(ListOfLists) ->
lists:foldl(
fun(L, AccX) ->
@ -324,37 +383,80 @@ do_inspect(F, Acc, Forms, Context) ->
end,
lists:foldl(F1, Forms, Acc).
if_recurse(true, Form, Else, F) -> recurse(Form, Else, F);
if_recurse(false, _, Else, _) -> Else.
recurse(Form, Else, F) ->
case erl_syntax:subtrees(Form) of
[] ->
Else;
[_|_] = ListOfLists ->
F(ListOfLists)
end.
do_transform(F, Acc, Forms, Context) ->
Rec = fun do_transform/4, % this function
F1 =
fun(Form, Acc0) ->
Type = type(Form),
{Before1, Form1, After1, Recurse, Acc1} =
case apply_F(F, Type, Form, Context, Acc0) of
this_form_rec(F, Form, Context, Acc0),
if Recurse ->
{NewForm, NewAcc} =
enter_subtrees(Form1, F, Context, Acc1, Rec),
{Before1, NewForm, After1, NewAcc};
true ->
{Before1, Form1, After1, Acc1}
end
end,
mapfoldl(F1, Acc, Forms).
do_depth_first(F, Acc, Forms, Context) ->
Rec = fun do_depth_first/4, % this function
F1 =
fun(Form, Acc0) ->
{NewForm, NewAcc} = enter_subtrees(Form, F, Context, Acc, Rec),
this_form_df(F, NewForm, Context, NewAcc)
end,
mapfoldl(F1, Acc, Forms).
enter_subtrees(Form, F, Context, Acc, Recurse) ->
case erl_syntax:subtrees(Form) of
[] ->
{Form, Acc};
[_|_] = ListOfLists ->
{NewListOfLists, NewAcc} =
mapfoldl(
fun(L, AccX) ->
Recurse(F, AccX, L, Context)
end, Acc, ListOfLists),
NewForm =
erl_syntax:update_tree(
Form, NewListOfLists),
{NewForm, NewAcc}
end.
this_form_rec(F, Form, Context, Acc) ->
Type = type(Form),
case apply_F(F, Type, Form, Context, Acc) of
{Form1x, Rec1x, A1x} ->
{[], Form1x, [], Rec1x, A1x};
{_Be1, _F1, _Af1, _Rec1, _Ac1} = Res1 ->
Res1
end,
if_recurse(
Recurse, Form,
{Before1, Form1, After1, Acc1},
fun(ListOfLists) ->
{NewListOfLists, NewAcc} =
mapfoldl(
fun(L, AccX) ->
do_transform(
F, AccX, L,
update_context(
Form1, Context))
end, Acc1, ListOfLists),
NewForm =
erl_syntax:update_tree(
Form, NewListOfLists),
{Before1, NewForm, After1, NewAcc}
end)
end,
mapfoldl(F1, Acc, Forms).
end.
this_form_df(F, Form, Context, Acc) ->
Type = type(Form),
case apply_F(F, Type, Form, Context, Acc) of
{Form1x, A1x} ->
{[], Form1x, [], A1x};
{_Be1, _F1, _Af1, _Ac1} = Res1 ->
Res1
end.
apply_F(F, Type, Form, Context, Acc) ->
try F(Type, Form, Context, Acc)
@ -365,20 +467,10 @@ apply_F(F, Type, Form, Context, Acc) ->
[{type, Type},
{context, Context},
{acc, Acc},
{apply_f, F},
{form, Form}])
end.
if_recurse(true, Form, Else, F) ->
case erl_syntax:subtrees(Form) of
[] ->
Else;
[_|_] = ListOfLists ->
F(ListOfLists)
end;
if_recurse(false, _, Else, _) ->
Else.
update_context(Form, Context0) ->
case type(Form) of

Binary file not shown.