Skip to content

Commit

Permalink
. Version 0.2.2
Browse files Browse the repository at this point in the history
. Improvement of the parsing of the source module in clone_module.
. We use the source file of the shell in the module transhell instead of
the beam file.


git-svn-id: http://translib.googlecode.com/svn/trunk@14 e7705c4e-9092-11dd-9c26-113ed4d91f68
  • Loading branch information
christopher.faulet committed Nov 17, 2009
1 parent 8dceac3 commit 369c85b
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 49 deletions.
12 changes: 12 additions & 0 deletions ChangeLog
@@ -1,3 +1,15 @@
2009-11-17 Christopher Faulet <christopher.faulet@capflam.org>

* Version 0.2.2

* src/clone_module.erl: Add the compiler options in the record
#clone_infos. We use these options when we parse the source
module. New options are supported by the clone attribute. The
parsing of the source module are deported in a new function,
parse_source_module.
* src/transhell.erl: Use the source of the shell module to clone
it instead of the beam file.

2009-09-29 Christopher Faulet <christopher.faulet@capflam.org>

* Version 0.2.1
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Expand Up @@ -4,7 +4,7 @@ dnl ------------------------------------------------------------------
dnl Autoconf initialisation.
dnl ------------------------------------------------------------------

AC_INIT([translib], [0.2.1], [christopher.faulet@capflam.org], [translib])
AC_INIT([translib], [0.2.2], [christopher.faulet@capflam.org], [translib])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_AUX_DIR([ac-aux])

Expand Down
1 change: 1 addition & 0 deletions doc/Makefile.am
Expand Up @@ -17,6 +17,7 @@ HTML_FILES = index.html \
packages-frame.html \
gen_trans.html \
lambda_expr.html \
multiparser.html \
recfun.html \
clone_module.html \
transhell.html
Expand Down
136 changes: 89 additions & 47 deletions src/clone_module.erl
Expand Up @@ -29,11 +29,11 @@
%% his beam file (by default) or his source file. This transformation uses the
%% abstract code of a module, the source module, and merges it with the abstract
%% code of another one, the destination module. It copies all attributes and
%% functions found in the abstract code of the source module, except `file' and
%% `module' attributes. For functions with same name and arity in the source
%% module and the destination module, we only keep the ones come from the
%% destination module. To deal with this goal and make the parsing easier, This
%% module also implements the {@link gen_trans} behaviour.
%% functions found in the abstract code of the source module, except `module'
%% attributes. For functions with same name and arity in the source module and
%% the destination module, we only keep the ones coming from the destination
%% module. To deal with this goal and make the parsing easier, This module
%% also implements the {@link gen_trans} behaviour.
%%
%% To perform this kind of transformation, we must define the attribute `clone'
%% following the syntax
Expand All @@ -48,8 +48,14 @@
%% options. Supported options are:
%%
%% <ul>
%% <li>`{source, Filename}': `Filename' refers to an Erlang source file.</li>
%% <li>`{source, Filename}': `Filename' refers to an Erlang source file.
%% it can be an abosulte name or a relative one.</li>
%% <li>`{source, {Apps, Filename}}': `Filename' refers to an Erlang source
%% file. This is a relative name from the directory of the application `Apps'.
%% `Apps' is an atom.</li>
%% <li>`{beam, Filename}': `Filename' refers to an Erlang beam file.</li>
%% <li>`{unused, [{Fun,Arity}]}': With this option, we can define a list of
%% functions that are not cloned.</li>
%% </ul>
%%
%% This is usefull to speficy the Erlang source file of a source module if we
Expand All @@ -64,7 +70,9 @@
%% <div class="example">
%% ```
%% -clone({lists, []}).
%% -clone({lists, [{source, "/usr/lib/erlang/lib/stdlib-1.15.5/src/lists.erl"}]}).
%% -clone({lists, [{source, {"/usr/lib/erlang/lib/stdlib-1.15.5/src/lists.erl"}]}).
%% -clone({lists, [{source, {stdlib, "src/lists.erl"}}]}).
%% -clone({string, [{unused, [{to_float, 1}, {to_integer, 1}]}]}).
%% '''
%% </div>
%%
Expand Down Expand Up @@ -172,7 +180,8 @@
attribute_forms=[],
exports=[],
function_forms={[], []},
other_forms=[]}).
other_forms=[],
options}).

-define(ERR_REDEFINE_CLONE, redefine_clone).
-define(ERR_MODULE_NOT_FOUND, no_module).
Expand Down Expand Up @@ -225,8 +234,8 @@ format_error(Error) ->
%% Parser API
%%====================================================================
%% @hidden
init(_Options) ->
{ok, #clone_infos{}}.
init(Options) ->
{ok, #clone_infos{options=Options}}.

%% @hidden
terminate(_Reason, _State) ->
Expand All @@ -236,7 +245,8 @@ terminate(_Reason, _State) ->
%% get the module name
parse({attribute, _Line, module, Mod}=Form,
#clone_infos{attribute_forms=Attrs}=State) ->
{[], State#clone_infos{local_module=Mod, attribute_forms=[Form|Attrs]}};
{[], State#clone_infos{local_module=Mod,
attribute_forms=[Form|Attrs]}};

%% Find a clone attribute. Try to get the Erlang abstract code of the given
%% module
Expand All @@ -245,37 +255,17 @@ parse({attribute, _Line, clone, {Mod, Args}}=Form,
is_list(Args) ->
AC = case proplists:get_value(source, Args) of
undefined -> load_from_beam(proplists:get_value(beam, Args, Mod));
SrcFile -> load_from_source(SrcFile)
SrcFile -> load_from_source(SrcFile, State#clone_infos.options)
end,
NewState =
lists:foldl(fun({attribute, _, file, _}, S) -> S; %% ignore it
({attribute, _, module, _}, S) -> S; %% ignore it
({eof, _}, S) -> S; %% ignore it
({attribute, _, export, L}, S) ->
S#clone_infos{exports=L++S#clone_infos.exports};
({attribute, _, spec, _}=Spec, S) ->
S#clone_infos{
other_forms=[Spec|S#clone_infos.other_forms]
};
({attribute, _, _, _}=A, S) ->
S#clone_infos{
attribute_forms=[A|S#clone_infos.attribute_forms]
};
({function, L, N, A, C}, S) ->
%% Parse clauses
{NewC, _} = parse(C, S),
F = {function, L, N, A, NewC},
{Local, Cloned} = S#clone_infos.function_forms,
S#clone_infos{function_forms={Local, [F|Cloned]}};
(F, S) ->
S#clone_infos{
other_forms=[F|S#clone_infos.other_forms]
}
end, State#clone_infos{clone_module=Mod}, AC),
{[], NewState#clone_infos{
clone_module=Mod,
attribute_forms=[Form|NewState#clone_infos.attribute_forms]
}};
Opts = Args++State#clone_infos.options,
NewState = lists:foldl(fun parse_source_module/2,
State#clone_infos{clone_module=Mod, options=Opts},
AC),
{[],
NewState#clone_infos{
clone_module=Mod,
attribute_forms=[Form|NewState#clone_infos.attribute_forms]
}};
parse({attribute, _, clone, {_, _}},
#clone_infos{clone_module=_M}) when _M /= undefined ->
erlang:error(?ERR_REDEFINE_CLONE);
Expand Down Expand Up @@ -314,9 +304,7 @@ parse({eof, Line}, State) ->
end, ClonedFuns, LocalFuns),

OtherForms = State#clone_infos.other_forms,

Forms = lists:keysort(2, Attributes) ++ [Exports] ++
lists:keysort(2, FunForms ++ OtherForms),
Forms = Attributes ++ [Exports] ++ lists:keysort(2, FunForms ++ OtherForms),
{Forms, {eof, Line}, [], State};


Expand Down Expand Up @@ -355,19 +343,73 @@ load_from_beam(Mod) ->
erlang:error({?ERR_BEAM_LIB, Error})
end.

load_from_source(File) ->
load_from_source({Apps, File}, Opts) ->
case code:lib_dir(Apps) of
{error, bad_name} -> erlang:error(?ERR_SOURCE_NOT_FOUND);
AppsPath -> load_from_source(filename:join(AppsPath, File), Opts)
end;
load_from_source(File, Opts) ->
case filelib:is_file(File) of
true -> ok;
false -> erlang:error(?ERR_SOURCE_NOT_FOUND)
end,
AC = case epp:parse_file(File, [], []) of
IncPath = [".", filename:dirname(File)|inc_paths(Opts)],
Pdm = pre_defs(Opts),
AC = case epp:parse_file(File, IncPath, Pdm) of
{ok, Forms} -> Forms;
{error, Error} -> erlang:error({?ERR_EPP, Error})
end,
case erl_lint:module(AC) of
case erl_lint:module(AC, File, Opts) of
{ok, _} -> AC;
{error, Errors, _} -> erlang:error({?ERR_LINTER, Errors})
end.

%%====================================================================
parse_source_module({attribute, _, module, _}, S) -> S; %% ignore it
parse_source_module({attribute, _, compile, _}, S) -> S; %% ignore it
parse_source_module({eof, _}, S) -> S; %% ignore it
parse_source_module({attribute, _, export, L}, S) ->
UnusedFuns = case proplists:get_value(unused, S#clone_infos.options) of
undefined -> [];
Res -> Res
end,
ExportedFun = [X || X <- L, not lists:member(X, UnusedFuns)],
S#clone_infos{exports=ExportedFun++S#clone_infos.exports};
parse_source_module({attribute, _, spec, _}=Spec, S) ->
S#clone_infos{other_forms=[Spec|S#clone_infos.other_forms]};
parse_source_module({attribute, _, _, _}=A, S) ->
S#clone_infos{attribute_forms=[A|S#clone_infos.attribute_forms]};
parse_source_module({function, L, N, A, C}, S) ->
UnusedFuns = case proplists:get_value(unused, S#clone_infos.options) of
undefined -> [];
Res -> Res
end,
case lists:member({N,A}, UnusedFuns) of
true ->
S;
false ->
%% Parse clauses
{NewC, _} = parse(C, S),
F = {function, L, N, A, NewC},
{Local, Cloned} = S#clone_infos.function_forms,
S#clone_infos{function_forms={Local, [F|Cloned]}}
end;
parse_source_module(F, S) ->
S#clone_infos{other_forms=[F|S#clone_infos.other_forms]}.

%%====================================================================
%% from compile.erl:
%% pre_defs(Options)
%% inc_paths(Options)
%% Extract the predefined macros and include paths from the option list.

pre_defs([{d,M,V}|Opts]) ->
[{M,V}|pre_defs(Opts)];
pre_defs([{d,M}|Opts]) ->
[M|pre_defs(Opts)];
pre_defs([_|Opts]) ->
pre_defs(Opts);
pre_defs([]) -> [].

inc_paths(Opts) ->
[P || {i,P} <- Opts, is_list(P)].
2 changes: 1 addition & 1 deletion src/transhell.erl
Expand Up @@ -65,7 +65,7 @@

-compile([{parse_transform, clone_module}]).

-clone({shell, []}).
-clone({shell, [{source, {stdlib, "src/shell.erl"}}]}).


-define(DEF_PARSE_TRANSFORM, [recfun, lambda_expr]).
Expand Down

0 comments on commit 369c85b

Please sign in to comment.