diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e454a52 --- /dev/null +++ b/LICENSE @@ -0,0 +1,178 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/rebar.config b/rebar.config index b04b479..5b1c298 100644 --- a/rebar.config +++ b/rebar.config @@ -1,12 +1,36 @@ -%% -*- erlang -*- +%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% -------------------------------------------------- +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% -------------------------------------------------- + {erl_first_files, ["src/parse_trans.erl", - "src/parse_trans_pp.erl", - "src/parse_trans_codegen.erl"]}. + "src/parse_trans_pp.erl", + "src/parse_trans_codegen.erl"]}. {erl_opts, [debug_info]}. {xref_checks, [undefined_function_calls]}. -{deps, [{edown, ".*", {git, "git://github.com/uwiger/edown.git", {branch, "master"}}}]}. -{edoc_opts, [{doclet, edown_doclet}, - {top_level_readme, - {"./README.md", - "http://github.com/uwiger/parse_trans"}}]}. + +{profiles, + [{docs, + [ + {deps, + [{edown, ".*", + {git, "git://github.com/uwiger/edown.git", {tag, "0.8"}}}]}, + {edoc_opts, [{doclet, edown_doclet}, + {top_level_readme, + {"./README.md", + "http://github.com/uwiger/parse_trans"}}]} + ]} + ]}. diff --git a/src/ct_expand.erl b/src/ct_expand.erl index 8affba3..3012e27 100644 --- a/src/ct_expand.erl +++ b/src/ct_expand.erl @@ -1,28 +1,23 @@ -%%% The contents of this file are subject to the Erlang Public License, -%%% Version 1.1, (the "License"); you may not use this file except in -%%% compliance with the License. You may obtain a copy of the License at -%%% http://www.erlang.org/EPLICENSE +%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% -------------------------------------------------- +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations %% under the License. -%% -%% The Original Code is exprecs-0.2. -%% -%% The Initial Developer of the Original Code is Ericsson AB. -%% Portions created by Ericsson are Copyright (C), 2006, Ericsson AB. -%% All Rights Reserved. -%% -%% Contributor(s): ______________________________________. - -%%------------------------------------------------------------------- +%% -------------------------------------------------- %% File : ct_expand.erl -%% @author : Ulf Wiger +%% @author : Ulf Wiger %% @end -%% Description : -%% -%% Created : 7 Apr 2010 by Ulf Wiger +%% Created : 7 Apr 2010 by Ulf Wiger %%------------------------------------------------------------------- %% @doc Compile-time expansion utility @@ -53,7 +48,7 @@ -export([parse_transform/2]). -export([extract_fun/3, - lfun_rewrite/2]). + lfun_rewrite/2]). -type form() :: any(). -type forms() :: [form()]. @@ -65,37 +60,37 @@ parse_transform(Forms, Options) -> Trace = ct_trace_opt(Options, Forms), case parse_trans:depth_first(fun(T,F,C,A) -> - xform_fun(T,F,C,A,Forms, Trace) - end, [], Forms, Options) of - {error, Es} -> - Es ++ Forms; - {NewForms, _} -> - parse_trans:revert(NewForms) + xform_fun(T,F,C,A,Forms, Trace) + end, [], Forms, Options) of + {error, Es} -> + Es ++ Forms; + {NewForms, _} -> + parse_trans:revert(NewForms) end. ct_trace_opt(Options, Forms) -> case proplists:get_value(ct_expand_trace, Options) of - undefined -> - case [Opt || {attribute,_,ct_expand_trace,Opt} <- Forms] of - [] -> - []; - [_|_] = L -> - lists:last(L) - end; - Flags when is_list(Flags) -> - Flags + undefined -> + case [Opt || {attribute,_,ct_expand_trace,Opt} <- Forms] of + [] -> + []; + [_|_] = L -> + lists:last(L) + end; + Flags when is_list(Flags) -> + Flags end. xform_fun(application, Form, _Ctxt, Acc, Forms, Trace) -> MFA = erl_syntax_lib:analyze_application(Form), case MFA of {?MODULE, {term, 1}} -> - LFH = fun(Name, Args, Bs) -> - eval_lfun( - extract_fun(Name, length(Args), Forms), - Args, Bs, Forms, Trace) - end, - Args = erl_syntax:application_arguments(Form), + LFH = fun(Name, Args, Bs) -> + eval_lfun( + extract_fun(Name, length(Args), Forms), + Args, Bs, Forms, Trace) + end, + Args = erl_syntax:application_arguments(Form), RevArgs = parse_trans:revert(Args), case erl_eval:exprs(RevArgs, [], {eval, LFH}) of {value, Value,[]} -> @@ -113,43 +108,43 @@ xform_fun(_, Form, _Ctxt, Acc, _, _) -> extract_fun(Name, Arity, Forms) -> case [F_ || {function,_,N_,A_,_Cs} = F_ <- Forms, - N_ == Name, A_ == Arity] of - [] -> - erlang:error({undef, [{Name, Arity}]}); - [FForm] -> - FForm + N_ == Name, A_ == Arity] of + [] -> + erlang:error({undef, [{Name, Arity}]}); + [FForm] -> + FForm end. eval_lfun({function,L,F,_,Clauses}, Args, Bs, Forms, Trace) -> try - begin - {ArgsV, Bs1} = lists:mapfoldl( - fun(A, Bs_) -> - {value,AV,Bs1_} = - erl_eval:expr(A, Bs_, lfh(Forms, Trace)), - {abstract(AV), Bs1_} - end, Bs, Args), - Expr = {call, L, {'fun', L, {clauses, lfun_rewrite(Clauses, Forms)}}, ArgsV}, - call_trace(Trace =/= [], L, F, ArgsV), - {value, Ret, _} = - erl_eval:expr(Expr, erl_eval:new_bindings(), lfh(Forms, Trace)), - ret_trace(lists:member(r, Trace) orelse lists:member(x, Trace), - L, F, Args, Ret), - %% restore bindings - {value, Ret, Bs1} - end + begin + {ArgsV, Bs1} = lists:mapfoldl( + fun(A, Bs_) -> + {value,AV,Bs1_} = + erl_eval:expr(A, Bs_, lfh(Forms, Trace)), + {abstract(AV), Bs1_} + end, Bs, Args), + Expr = {call, L, {'fun', L, {clauses, lfun_rewrite(Clauses, Forms)}}, ArgsV}, + call_trace(Trace =/= [], L, F, ArgsV), + {value, Ret, _} = + erl_eval:expr(Expr, erl_eval:new_bindings(), lfh(Forms, Trace)), + ret_trace(lists:member(r, Trace) orelse lists:member(x, Trace), + L, F, Args, Ret), + %% restore bindings + {value, Ret, Bs1} + end catch - error:Err -> - exception_trace(lists:member(x, Trace), L, F, Args, Err), - error(Err) + error:Err -> + exception_trace(lists:member(x, Trace), L, F, Args, Err), + error(Err) end. lfh(Forms, Trace) -> {eval, fun(Name, As, Bs1) -> - eval_lfun( - extract_fun(Name, length(As), Forms), - As, Bs1, Forms, Trace) - end}. + eval_lfun( + extract_fun(Name, length(As), Forms), + As, Bs1, Forms, Trace) + end}. call_trace(false, _, _, _) -> ok; call_trace(true, L, F, As) -> @@ -159,14 +154,14 @@ pp_function(F, []) -> atom_to_list(F) ++ "()"; pp_function(F, [A|As]) -> lists:flatten([atom_to_list(F), "(", - [io_lib:fwrite("~w", [erl_parse:normalise(A)]) | - [[",", io_lib:fwrite("~w", [erl_parse:normalise(A_)])] || A_ <- As]], - ")"]). + [io_lib:fwrite("~w", [erl_parse:normalise(A)]) | + [[",", io_lib:fwrite("~w", [erl_parse:normalise(A_)])] || A_ <- As]], + ")"]). ret_trace(false, _, _, _, _) -> ok; ret_trace(true, L, F, Args, Res) -> io:fwrite("ct_expand (~w): returned from ~w/~w: ~w~n", - [L, F, length(Args), Res]). + [L, F, length(Args), Res]). exception_trace(false, _, _, _, _) -> ok; exception_trace(true, L, F, Args, Err) -> @@ -176,10 +171,10 @@ exception_trace(true, L, F, Args, Err) -> lfun_rewrite(Exprs, Forms) -> parse_trans:plain_transform( fun({'fun',L,{function,F,A}}) -> - {function,_,_,_,Cs} = extract_fun(F, A, Forms), - {'fun',L,{clauses, Cs}}; - (_) -> - continue + {function,_,_,_,Cs} = extract_fun(F, A, Forms), + {'fun',L,{clauses, Cs}}; + (_) -> + continue end, Exprs). @@ -190,15 +185,15 @@ lfun_rewrite(Exprs, Forms) -> AbsTerm :: abstract_expr(). abstract(T) when is_function(T) -> case erlang:fun_info(T, module) of - {module, erl_eval} -> - case erl_eval:fun_data(T) of - {fun_data, _Imports, Clauses} -> - {'fun', 0, {clauses, Clauses}}; - false -> - erlang:error(function_clause) % mimicking erl_parse:abstract(T) - end; - _ -> - erlang:error(function_clause) + {module, erl_eval} -> + case erl_eval:fun_data(T) of + {fun_data, _Imports, Clauses} -> + {'fun', 0, {clauses, Clauses}}; + false -> + erlang:error(function_clause) % mimicking erl_parse:abstract(T) + end; + _ -> + erlang:error(function_clause) end; abstract(T) when is_integer(T) -> {integer,0,T}; abstract(T) when is_float(T) -> {float,0,T}; diff --git a/src/exprecs.erl b/src/exprecs.erl index 3a6262f..fecaea4 100755 --- a/src/exprecs.erl +++ b/src/exprecs.erl @@ -1,51 +1,47 @@ -%%% The contents of this file are subject to the Erlang Public License, -%%% Version 1.1, (the "License"); you may not use this file except in -%%% compliance with the License. You may obtain a copy of the License at -%%% http://www.erlang.org/EPLICENSE -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% The Original Code is exprecs-0.2. -%% -%% Copyright (c) 2014 Ericsson AB -%% The Initial Developer of the Original Code is Ericsson AB. -%% Portions created by Ericsson are Copyright (C), 2006, Ericsson AB. -%% All Rights Reserved. -%% -%% Contributor(s): ______________________________________. - -%%------------------------------------------------------------------- -%% File : exprecs.erl +%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% -------------------------------------------------- +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% -------------------------------------------------- +%% File : exprecs.erl %% @author : Ulf Wiger -%% @end -%% Description : -%% -%% Created : 13 Feb 2006 by Ulf Wiger -%% Rewritten: Jan-Feb 2010 by Ulf Wiger -%%------------------------------------------------------------------- - -%% @doc Parse transform for generating record access functions. -%%

This parse transform can be used to reduce compile-time -%% dependencies in large systems.

-%%

In the old days, before records, Erlang programmers often wrote -%% access functions for tuple data. This was tedious and error-prone. -%% The record syntax made this easier, but since records were implemented -%% fully in the pre-processor, a nasty compile-time dependency was -%% introduced.

-%%

This module automates the generation of access functions for -%% records. While this method cannot fully replace the utility of -%% pattern matching, it does allow a fair bit of functionality on -%% records without the need for compile-time dependencies.

-%%

Whenever record definitions need to be exported from a module, -%% inserting a compiler attribute, -%% export_records([RecName|...]) causes this transform -%% to lay out access functions for the exported records:

-%% -%% As an example, consider the following module: -%%
+%% @end
+%% Description :
+%%
+%% Created : 13 Feb 2006 by Ulf Wiger 
+%% Rewritten: Jan-Feb 2010 by Ulf Wiger 
+%%-------------------------------------------------------------------
+
+%% @doc Parse transform for generating record access functions.
+%% 

This parse transform can be used to reduce compile-time +%% dependencies in large systems.

+%%

In the old days, before records, Erlang programmers often wrote +%% access functions for tuple data. This was tedious and error-prone. +%% The record syntax made this easier, but since records were implemented +%% fully in the pre-processor, a nasty compile-time dependency was +%% introduced.

+%%

This module automates the generation of access functions for +%% records. While this method cannot fully replace the utility of +%% pattern matching, it does allow a fair bit of functionality on +%% records without the need for compile-time dependencies.

+%%

Whenever record definitions need to be exported from a module, +%% inserting a compiler attribute, +%% export_records([RecName|...]) causes this transform +%% to lay out access functions for the exported records:

+%% +%% As an example, consider the following module: +%%
 %% -module(test_exprecs).
 %% -export([f/0]).
 %% -record(r,{a = 0 :: integer(),b = 0 :: integer(),c = 0 :: integer()}).
@@ -324,963 +320,963 @@
 %%     error(bad_record_op, ['#lens-s',Attr]).
 %% f() ->
 %%     {new,'#new-r'([])}.
-%% 
-%% -%% It is possible to modify the naming rules of exprecs, through the use -%% of the following attributes (example reflecting the current rules): -%% -%%
-%% -exprecs_prefix(["#", operation, "-"]).
-%% -exprecs_fname([prefix, record]).
-%% -exprecs_vfname([fname, "__", version]).
-%% 
-%% -%% The lists must contain strings or any of the following control atoms: -%%
    -%%
  • in `exprecs_prefix': `operation'
  • -%%
  • in `exprecs_fname': `operation', `record', `prefix'
  • -%%
  • in `exprecs_vfname': `operation', `record', `prefix', `fname', `version' -%%
  • -%%
-%% -%% Exprecs will substitute the control atoms with the string values of the -%% corresponding items. The result will then be flattened and converted to an -%% atom (a valid function or type name). -%% -%% `operation' is one of: -%%
-%%
`new'
Creates a new record
-%%
`get'
Retrieves given attribute values from a record
-%%
`set'
Sets given attribute values in a record
-%%
`fromlist'
Creates a record from a key-value list
-%%
`info'
Equivalent to record_info/2
-%%
`pos'
Returns the position of a given attribute
-%%
`is_record'
Tests if a value is a specific record
-%%
`convert'
Converts an old record to the current version
-%%
`prop'
Used only in type specs
-%%
`attr'
Used only in type specs
-%%
`lens'
Returns a 'lens' (an accessor pair) as described in -%% [http://github.com/jlouis/erl-lenses]
-%%
-%% -%% @end - --module(exprecs). - --export([parse_transform/2, - format_error/1, -% transform/3, - context/2]). - --record(context, {module, - function, - arity}). - --record(pass1, {exports = [], - generated = false, - records = [], - record_types = [], - versions = orddict:new(), - inserted = false, - prefix = ["#", operation, "-"], - fname = [prefix, record], - vfname = [fname, "__", version]}). - --include("../include/codegen.hrl"). - --define(HERE, {?MODULE, ?LINE}). - --define(ERROR(R, F, I), - begin - rpt_error(R, F, I), - throw({error,get_pos(I),{unknown,R}}) - end). - --type form() :: any(). --type forms() :: [form()]. --type options() :: [{atom(), any()}]. - - -get_pos(I) -> - case proplists:get_value(form, I) of - undefined -> - 0; - Form -> - erl_syntax:get_pos(Form) - end. - --spec parse_transform(forms(), options()) -> - forms(). -parse_transform(Forms, Options) -> - parse_trans:top(fun do_transform/2, Forms, Options). - -do_transform(Forms, Context) -> - Acc1 = versioned_records( - add_untyped_recs( - parse_trans:do_inspect(fun inspect_f/4, #pass1{}, - Forms, Context))), - {Forms2, Acc2} = - parse_trans:do_transform(fun generate_f/4, Acc1, Forms, Context), - parse_trans:revert(verify_generated(Forms2, Acc2, Context)). - -add_untyped_recs(#pass1{records = Rs, - record_types = RTypes, - exports = Es} = Acc) -> - Untyped = - [{R, Def} || {R, Def} <- Rs, - lists:member(R, Es), - not lists:keymember(R, 1, RTypes)], - RTypes1 = [{R, lists:map( - fun({record_field,L,{atom,_,A}}) -> {A, t_any(L)}; - ({record_field,L,{atom,_,A},_}) -> {A, t_any(L)}; +%%
+%% +%% It is possible to modify the naming rules of exprecs, through the use +%% of the following attributes (example reflecting the current rules): +%% +%%
+%% -exprecs_prefix(["#", operation, "-"]).
+%% -exprecs_fname([prefix, record]).
+%% -exprecs_vfname([fname, "__", version]).
+%% 
+%% +%% The lists must contain strings or any of the following control atoms: +%%
    +%%
  • in `exprecs_prefix': `operation'
  • +%%
  • in `exprecs_fname': `operation', `record', `prefix'
  • +%%
  • in `exprecs_vfname': `operation', `record', `prefix', `fname', `version' +%%
  • +%%
+%% +%% Exprecs will substitute the control atoms with the string values of the +%% corresponding items. The result will then be flattened and converted to an +%% atom (a valid function or type name). +%% +%% `operation' is one of: +%%
+%%
`new'
Creates a new record
+%%
`get'
Retrieves given attribute values from a record
+%%
`set'
Sets given attribute values in a record
+%%
`fromlist'
Creates a record from a key-value list
+%%
`info'
Equivalent to record_info/2
+%%
`pos'
Returns the position of a given attribute
+%%
`is_record'
Tests if a value is a specific record
+%%
`convert'
Converts an old record to the current version
+%%
`prop'
Used only in type specs
+%%
`attr'
Used only in type specs
+%%
`lens'
Returns a 'lens' (an accessor pair) as described in +%% [http://github.com/jlouis/erl-lenses]
+%%
+%% +%% @end + +-module(exprecs). + +-export([parse_transform/2, + format_error/1, +% transform/3, + context/2]). + +-record(context, {module, + function, + arity}). + +-record(pass1, {exports = [], + generated = false, + records = [], + record_types = [], + versions = orddict:new(), + inserted = false, + prefix = ["#", operation, "-"], + fname = [prefix, record], + vfname = [fname, "__", version]}). + +-include("../include/codegen.hrl"). + +-define(HERE, {?MODULE, ?LINE}). + +-define(ERROR(R, F, I), + begin + rpt_error(R, F, I), + throw({error,get_pos(I),{unknown,R}}) + end). + +-type form() :: any(). +-type forms() :: [form()]. +-type options() :: [{atom(), any()}]. + + +get_pos(I) -> + case proplists:get_value(form, I) of + undefined -> + 0; + Form -> + erl_syntax:get_pos(Form) + end. + +-spec parse_transform(forms(), options()) -> + forms(). +parse_transform(Forms, Options) -> + parse_trans:top(fun do_transform/2, Forms, Options). + +do_transform(Forms, Context) -> + Acc1 = versioned_records( + add_untyped_recs( + parse_trans:do_inspect(fun inspect_f/4, #pass1{}, + Forms, Context))), + {Forms2, Acc2} = + parse_trans:do_transform(fun generate_f/4, Acc1, Forms, Context), + parse_trans:revert(verify_generated(Forms2, Acc2, Context)). + +add_untyped_recs(#pass1{records = Rs, + record_types = RTypes, + exports = Es} = Acc) -> + Untyped = + [{R, Def} || {R, Def} <- Rs, + lists:member(R, Es), + not lists:keymember(R, 1, RTypes)], + RTypes1 = [{R, lists:map( + fun({record_field,L,{atom,_,A}}) -> {A, t_any(L)}; + ({record_field,L,{atom,_,A},_}) -> {A, t_any(L)}; ({typed_record_field, {record_field,L,{atom,_,A}},_}) -> {A, t_any(L)}; ({typed_record_field, {record_field,L,{atom,_,A},_},_}) -> {A, t_any(L)} - end, Def)} || {R, Def} <- Untyped], - Acc#pass1{record_types = RTypes ++ RTypes1}. - -inspect_f(attribute, {attribute,_L,exprecs_prefix,Pattern}, _Ctxt, Acc) -> - {false, Acc#pass1{prefix = Pattern}}; -inspect_f(attribute, {attribute,_L,exprecs_fname,Pattern}, _Ctxt, Acc) -> - {false, Acc#pass1{fname = Pattern}}; -inspect_f(attribute, {attribute,_L,exprecs_vfname,Pattern}, _Ctxt, Acc) -> - {false, Acc#pass1{vfname = Pattern}}; -inspect_f(attribute, {attribute,_L,record,RecDef}, _Ctxt, Acc) -> - Recs0 = Acc#pass1.records, - {false, Acc#pass1{records = [RecDef|Recs0]}}; -inspect_f(attribute, {attribute,_L,export_records, E}, _Ctxt, Acc) -> - Exports0 = Acc#pass1.exports, - NewExports = Exports0 ++ E, - {false, Acc#pass1{exports = NewExports}}; -inspect_f(attribute, {attribute, _L, type, - {{record, R}, RType,_}}, _Ctxt, Acc) -> - Type = lists:map( - fun({typed_record_field, {record_field,_,{atom,_,A}}, T}) -> - {A, T}; - ({typed_record_field, {record_field,_,{atom,_,A},_}, T}) -> - {A, T}; - ({record_field, _, {atom,L,A}, _}) -> - {A, t_any(L)}; - ({record_field, _, {atom,L,A}}) -> - {A, t_any(L)} - end, RType), - {false, Acc#pass1{record_types = [{R, Type}|Acc#pass1.record_types]}}; -inspect_f(_Type, _Form, _Context, Acc) -> - {false, Acc}. - -generate_f(attribute, {attribute,L,export_records,_} = Form, _Ctxt, - #pass1{exports = [_|_] = Es, versions = Vsns, - inserted = false} = Acc) -> - case check_record_names(Es, L, Acc) of - ok -> continue; - {error, Bad} -> - ?ERROR(invalid_record_exports, ?HERE, Bad) - end, - Exports = [{fname(exported_records, Acc), 0}, - {fname(new, Acc), 1}, - {fname(info, Acc), 1}, - {fname(info, Acc), 2}, - {fname(pos, Acc), 2}, - {fname(is_record, Acc), 1}, - {fname(is_record, Acc), 2}, - {fname(get, Acc), 2}, - {fname(set, Acc), 2}, - {fname(fromlist, Acc), 2}, - {fname(lens, Acc), 2} | - lists:flatmap( - fun(Rec) -> - RecS = atom_to_list(Rec), - FNew = fname(new, RecS, Acc), - [{FNew, 0}, {FNew,1}, - {fname(get, RecS, Acc), 2}, - {fname(set, RecS, Acc), 2}, - {fname(pos, RecS, Acc), 1}, - {fname(fromlist, RecS, Acc), 1}, - {fname(fromlist, RecS, Acc), 2}, - {fname(info, RecS, Acc), 1}, - {fname(lens, RecS, Acc), 1}] - end, Es)] ++ version_exports(Vsns, Acc), + end, Def)} || {R, Def} <- Untyped], + Acc#pass1{record_types = RTypes ++ RTypes1}. + +inspect_f(attribute, {attribute,_L,exprecs_prefix,Pattern}, _Ctxt, Acc) -> + {false, Acc#pass1{prefix = Pattern}}; +inspect_f(attribute, {attribute,_L,exprecs_fname,Pattern}, _Ctxt, Acc) -> + {false, Acc#pass1{fname = Pattern}}; +inspect_f(attribute, {attribute,_L,exprecs_vfname,Pattern}, _Ctxt, Acc) -> + {false, Acc#pass1{vfname = Pattern}}; +inspect_f(attribute, {attribute,_L,record,RecDef}, _Ctxt, Acc) -> + Recs0 = Acc#pass1.records, + {false, Acc#pass1{records = [RecDef|Recs0]}}; +inspect_f(attribute, {attribute,_L,export_records, E}, _Ctxt, Acc) -> + Exports0 = Acc#pass1.exports, + NewExports = Exports0 ++ E, + {false, Acc#pass1{exports = NewExports}}; +inspect_f(attribute, {attribute, _L, type, + {{record, R}, RType,_}}, _Ctxt, Acc) -> + Type = lists:map( + fun({typed_record_field, {record_field,_,{atom,_,A}}, T}) -> + {A, T}; + ({typed_record_field, {record_field,_,{atom,_,A},_}, T}) -> + {A, T}; + ({record_field, _, {atom,L,A}, _}) -> + {A, t_any(L)}; + ({record_field, _, {atom,L,A}}) -> + {A, t_any(L)} + end, RType), + {false, Acc#pass1{record_types = [{R, Type}|Acc#pass1.record_types]}}; +inspect_f(_Type, _Form, _Context, Acc) -> + {false, Acc}. + +generate_f(attribute, {attribute,L,export_records,_} = Form, _Ctxt, + #pass1{exports = [_|_] = Es, versions = Vsns, + inserted = false} = Acc) -> + case check_record_names(Es, L, Acc) of + ok -> continue; + {error, Bad} -> + ?ERROR(invalid_record_exports, ?HERE, Bad) + end, + Exports = [{fname(exported_records, Acc), 0}, + {fname(new, Acc), 1}, + {fname(info, Acc), 1}, + {fname(info, Acc), 2}, + {fname(pos, Acc), 2}, + {fname(is_record, Acc), 1}, + {fname(is_record, Acc), 2}, + {fname(get, Acc), 2}, + {fname(set, Acc), 2}, + {fname(fromlist, Acc), 2}, + {fname(lens, Acc), 2} | + lists:flatmap( + fun(Rec) -> + RecS = atom_to_list(Rec), + FNew = fname(new, RecS, Acc), + [{FNew, 0}, {FNew,1}, + {fname(get, RecS, Acc), 2}, + {fname(set, RecS, Acc), 2}, + {fname(pos, RecS, Acc), 1}, + {fname(fromlist, RecS, Acc), 1}, + {fname(fromlist, RecS, Acc), 2}, + {fname(info, RecS, Acc), 1}, + {fname(lens, RecS, Acc), 1}] + end, Es)] ++ version_exports(Vsns, Acc), TypeExports = - lists:flatmap( - fun(Rec) -> - [{fname(prop, Rec, Acc), 0}, - {fname(attr, Rec, Acc), 0}] - end, Es), - {[], Form, + lists:flatmap( + fun(Rec) -> + [{fname(prop, Rec, Acc), 0}, + {fname(attr, Rec, Acc), 0}] + end, Es), + {[], Form, [{attribute,L,export,Exports}, {attribute,L,export_type, TypeExports}], - false, Acc#pass1{inserted = true}}; -generate_f(function, Form, _Context, #pass1{generated = false} = Acc) -> - % Layout record funs before first function - L = erl_syntax:get_pos(Form), - Forms = generate_specs_and_accessors(L, Acc), - {Forms, Form, [], false, Acc#pass1{generated = true}}; -generate_f(_Type, Form, _Ctxt, Acc) -> - {Form, false, Acc}. - -generate_specs_and_accessors(L, #pass1{exports = [_|_] = Es, - record_types = Ts} = Acc) -> - Specs = generate_specs(L, [{R,T} || {R,T} <- Ts, lists:member(R, Es)], Acc), - Funs = generate_accessors(L, Acc), - Specs ++ Funs; -generate_specs_and_accessors(_, _) -> - []. - -verify_generated(Forms, #pass1{} = Acc, _Context) -> - case (Acc#pass1.generated == true) orelse (Acc#pass1.exports == []) of - true -> - Forms; - false -> - % should be re-written to use the parse_trans helper...? - [{eof,Last}|RevForms] = lists:reverse(Forms), - [{function, NewLast, _, _, _}|_] = RevAs = - lists:reverse(generate_specs_and_accessors(Last, Acc)), - lists:reverse([{eof, NewLast+1} | RevAs] ++ RevForms) - end. - - -check_record_names(Es, L, #pass1{records = Rs}) -> - case [E || E <- Es, - not(lists:keymember(E, 1, Rs))] of - [] -> - ok; - Bad -> - {error, [{L,E} || E <- Bad]} - end. - -versioned_records(#pass1{exports = Es, records = Rs} = Pass1) -> - case split_recnames(Rs) of - [] -> - Pass1#pass1{versions = []}; - [_|_] = Versions -> - Exp_vsns = - lists:foldl( - fun(Re, Acc) -> - case orddict:find(atom_to_list(Re), Versions) of - {ok, Vs} -> - orddict:store(Re, Vs, Acc); - error -> - Acc - end - end, orddict:new(), Es), - Pass1#pass1{versions = Exp_vsns} - end. - -version_exports([], _Acc) -> - []; -version_exports([_|_] = _Vsns, Acc) -> - [{list_to_atom(fname_prefix(info, Acc)), 3}, - {list_to_atom(fname_prefix(convert, Acc)), 2}]. - - -version_accessors(_L, #pass1{versions = []}) -> - []; -version_accessors(L, #pass1{versions = Vsns} = Acc) -> - Flat_vsns = flat_versions(Vsns), - [f_convert(Vsns, L, Acc), - f_info_3(Vsns, L, Acc)] - ++ [f_info_1(Rname, Acc, L, V) || {Rname,V} <- Flat_vsns]. - -flat_versions(Vsns) -> - lists:flatmap(fun({R,Vs}) -> - [{R,V} || V <- Vs] - end, Vsns). - -split_recnames(Rs) -> - lists:foldl( - fun({R,_As}, Acc) -> - case re:split(atom_to_list(R), "__", [{return, list}]) of - [Base, V] -> - orddict:append(Base,V,Acc); - [_] -> - Acc - end - end, orddict:new(), Rs). - -generate_specs(L, Specs, Acc) -> - [[ - {attribute, L, type, - {fname(prop, R, Acc), - {type, L, union, - [{type, L, tuple, [{atom,L,A},T]} || {A,T} <- Attrs]}, []}}, - {attribute, L, type, - {fname(attr, R, Acc), - {type, L, union, - [{atom, L, A} || {A,_} <- Attrs]}, []}} - ] || {R, Attrs} <- Specs, Attrs =/= []] ++ - [[{attribute, L, type, - {fname(prop, R, Acc), - {type, L, any, []}, []}}, - {attribute, L, type, - {fname(attr, R, Acc), - {type, L, any, []}, []}}] || {R, []} <- Specs]. - - -generate_accessors(L, Acc) -> - lists:flatten( - [f_exported_recs(Acc, L), - f_new_(Acc, L), - f_info(Acc, L), - f_info_2(Acc, L), - f_pos_2(Acc, L), - f_isrec_1(Acc, L), - f_isrec_2(Acc, L), - f_get(Acc, L), - f_set(Acc, L), - f_fromlist(Acc, L), - f_lens_(Acc, L)| - lists:append( - lists:map( - fun(Rname) -> - Fields = get_flds(Rname, Acc), - [f_new_0(Rname, L, Acc), - f_new_1(Rname, L, Acc), - f_get_2(Rname, Fields, L, Acc), - f_set_2(Rname, Fields, L, Acc), - f_fromlist_1(Rname, L, Acc), - f_fromlist_2(Rname, Fields, L, Acc), - f_pos_1(Rname, Fields, L, Acc), - f_info_1(Rname, Acc, L), - f_lens_1(Rname, Fields, L, Acc)] - end, Acc#pass1.exports))] ++ version_accessors(L, Acc)). - -get_flds(Rname, #pass1{records = Rs}) -> - {_, Flds} = lists:keyfind(Rname, 1, Rs), - lists:map( - fun({record_field,_, {atom,_,N}}) -> N; - ({record_field,_, {atom,_,N}, _}) -> N; + false, Acc#pass1{inserted = true}}; +generate_f(function, Form, _Context, #pass1{generated = false} = Acc) -> + % Layout record funs before first function + L = erl_syntax:get_pos(Form), + Forms = generate_specs_and_accessors(L, Acc), + {Forms, Form, [], false, Acc#pass1{generated = true}}; +generate_f(_Type, Form, _Ctxt, Acc) -> + {Form, false, Acc}. + +generate_specs_and_accessors(L, #pass1{exports = [_|_] = Es, + record_types = Ts} = Acc) -> + Specs = generate_specs(L, [{R,T} || {R,T} <- Ts, lists:member(R, Es)], Acc), + Funs = generate_accessors(L, Acc), + Specs ++ Funs; +generate_specs_and_accessors(_, _) -> + []. + +verify_generated(Forms, #pass1{} = Acc, _Context) -> + case (Acc#pass1.generated == true) orelse (Acc#pass1.exports == []) of + true -> + Forms; + false -> + % should be re-written to use the parse_trans helper...? + [{eof,Last}|RevForms] = lists:reverse(Forms), + [{function, NewLast, _, _, _}|_] = RevAs = + lists:reverse(generate_specs_and_accessors(Last, Acc)), + lists:reverse([{eof, NewLast+1} | RevAs] ++ RevForms) + end. + + +check_record_names(Es, L, #pass1{records = Rs}) -> + case [E || E <- Es, + not(lists:keymember(E, 1, Rs))] of + [] -> + ok; + Bad -> + {error, [{L,E} || E <- Bad]} + end. + +versioned_records(#pass1{exports = Es, records = Rs} = Pass1) -> + case split_recnames(Rs) of + [] -> + Pass1#pass1{versions = []}; + [_|_] = Versions -> + Exp_vsns = + lists:foldl( + fun(Re, Acc) -> + case orddict:find(atom_to_list(Re), Versions) of + {ok, Vs} -> + orddict:store(Re, Vs, Acc); + error -> + Acc + end + end, orddict:new(), Es), + Pass1#pass1{versions = Exp_vsns} + end. + +version_exports([], _Acc) -> + []; +version_exports([_|_] = _Vsns, Acc) -> + [{list_to_atom(fname_prefix(info, Acc)), 3}, + {list_to_atom(fname_prefix(convert, Acc)), 2}]. + + +version_accessors(_L, #pass1{versions = []}) -> + []; +version_accessors(L, #pass1{versions = Vsns} = Acc) -> + Flat_vsns = flat_versions(Vsns), + [f_convert(Vsns, L, Acc), + f_info_3(Vsns, L, Acc)] + ++ [f_info_1(Rname, Acc, L, V) || {Rname,V} <- Flat_vsns]. + +flat_versions(Vsns) -> + lists:flatmap(fun({R,Vs}) -> + [{R,V} || V <- Vs] + end, Vsns). + +split_recnames(Rs) -> + lists:foldl( + fun({R,_As}, Acc) -> + case re:split(atom_to_list(R), "__", [{return, list}]) of + [Base, V] -> + orddict:append(Base,V,Acc); + [_] -> + Acc + end + end, orddict:new(), Rs). + +generate_specs(L, Specs, Acc) -> + [[ + {attribute, L, type, + {fname(prop, R, Acc), + {type, L, union, + [{type, L, tuple, [{atom,L,A},T]} || {A,T} <- Attrs]}, []}}, + {attribute, L, type, + {fname(attr, R, Acc), + {type, L, union, + [{atom, L, A} || {A,_} <- Attrs]}, []}} + ] || {R, Attrs} <- Specs, Attrs =/= []] ++ + [[{attribute, L, type, + {fname(prop, R, Acc), + {type, L, any, []}, []}}, + {attribute, L, type, + {fname(attr, R, Acc), + {type, L, any, []}, []}}] || {R, []} <- Specs]. + + +generate_accessors(L, Acc) -> + lists:flatten( + [f_exported_recs(Acc, L), + f_new_(Acc, L), + f_info(Acc, L), + f_info_2(Acc, L), + f_pos_2(Acc, L), + f_isrec_1(Acc, L), + f_isrec_2(Acc, L), + f_get(Acc, L), + f_set(Acc, L), + f_fromlist(Acc, L), + f_lens_(Acc, L)| + lists:append( + lists:map( + fun(Rname) -> + Fields = get_flds(Rname, Acc), + [f_new_0(Rname, L, Acc), + f_new_1(Rname, L, Acc), + f_get_2(Rname, Fields, L, Acc), + f_set_2(Rname, Fields, L, Acc), + f_fromlist_1(Rname, L, Acc), + f_fromlist_2(Rname, Fields, L, Acc), + f_pos_1(Rname, Fields, L, Acc), + f_info_1(Rname, Acc, L), + f_lens_1(Rname, Fields, L, Acc)] + end, Acc#pass1.exports))] ++ version_accessors(L, Acc)). + +get_flds(Rname, #pass1{records = Rs}) -> + {_, Flds} = lists:keyfind(Rname, 1, Rs), + lists:map( + fun({record_field,_, {atom,_,N}}) -> N; + ({record_field,_, {atom,_,N}, _}) -> N; ({typed_record_field,{record_field,_,{atom,_,N}},_}) -> N; ({typed_record_field,{record_field,_,{atom,_,N},_},_}) -> N - end, Flds). - - -fname_prefix(Op, #pass1{prefix = Pat}) -> - lists:flatten( - lists:map(fun(operation) -> str(Op); - (X) -> str(X) - end, Pat)). -%% fname_prefix(Op, #pass1{} = Acc) -> -%% case Op of -%% new -> "#new-"; -%% get -> "#get-"; -%% set -> "#set-"; -%% fromlist -> "#fromlist-"; -%% info -> "#info-"; -%% pos -> "#pos-"; -%% is_record -> "#is_record-"; -%% convert -> "#convert-"; -%% prop -> "#prop-"; -%% attr -> "#attr-" -%% end. - -%% fname_prefix(Op, Rname, Acc) -> -%% fname_prefix(Op, Acc) ++ str(Rname). - -str(A) when is_atom(A) -> - atom_to_list(A); -str(S) when is_list(S) -> - S. - -fname(Op, #pass1{} = Acc) -> - list_to_atom(fname_prefix(Op, Acc)). - %% list_to_atom(fname_prefix(Op, Acc)). - -fname(Op, Rname, #pass1{fname = FPat} = Acc) -> - Prefix = fname_prefix(Op, Acc), - list_to_atom( - lists:flatten( - lists:map(fun(prefix) -> str(Prefix); - (record) -> str(Rname); - (operation) -> str(Op); - (X) -> str(X) - end, FPat))). - %% list_to_atom(fname_prefix(Op, Rname, Acc)). - -fname(Op, Rname, V, #pass1{vfname = VPat} = Acc) -> - list_to_atom( - lists:flatten( - lists:map(fun(prefix) -> fname_prefix(Op, Acc); - (operation) -> str(Op); - (record) -> str(Rname); - (version) -> str(V); - (fname) -> str(fname(Op, Rname, Acc)); - (X) -> str(X) - end, VPat))). - %% list_to_atom(fname_prefix(Op, Rname, Acc) ++ "__" ++ V). - - -%%% Meta functions - -f_exported_recs(#pass1{exports = Es} = Acc, L) -> - Fname = fname(exported_records, Acc), - [funspec(L, Fname, [], - t_list(L, [t_union(L, [t_atom(L, E) || E <- Es])])), - {function, L, Fname, 0, - [{clause, L, [], [], - [erl_parse:abstract(Es, L)]}]} - ]. - -%%% Accessor functions -%%% -f_new_(#pass1{exports = Es} = Acc, L) -> - Fname = fname(new, Acc), - [funspec(L, Fname, [ {[t_atom(L, E)], t_record(L, E)} || - E <- Es ]), - {function, L, fname(new, Acc), 1, - [{clause, L, [{atom, L, Re}], [], - [{call, L, {atom, L, fname(new, Re, Acc)}, []}]} - || Re <- Es]} - ]. - -f_new_0(Rname, L, Acc) -> - Fname = fname(new, Rname, Acc), - [funspec(L, Fname, [], t_record(L, Rname)), - {function, L, fname(new, Rname, Acc), 0, - [{clause, L, [], [], - [{record, L, Rname, []}]}]} - ]. - - -f_new_1(Rname, L, Acc) -> - Fname = fname(new, Rname, Acc), - [funspec(L, Fname, [t_list(L, [t_prop(L, Rname, Acc)])], - t_record(L, Rname)), - {function, L, Fname, 1, - [{clause, L, [{var, L, 'Vals'}], [], - [{call, L, {atom, L, fname(set, Rname, Acc)}, - [{var, L, 'Vals'}, - {record, L, Rname, []} - ]}] - }]}]. - -funspec(L, Fname, [{H,_} | _] = Alts) -> - Arity = length(H), - {attribute, L, spec, - {{Fname, Arity}, - [{type, L, 'fun', [{type, L, product, Head}, Ret]} || - {Head, Ret} <- Alts, - no_empty_union(Head)]}}. - -no_empty_union({type,_,union,[]}) -> - false; -no_empty_union(T) when is_tuple(T) -> - no_empty_union(tuple_to_list(T)); -no_empty_union([H|T]) -> - no_empty_union(H) andalso no_empty_union(T); -no_empty_union(_) -> - true. - - - - -funspec(L, Fname, Head, Returns) -> - Arity = length(Head), - {attribute, L, spec, - {{Fname, Arity}, - [{type, L, 'fun', - [{type, L, product, Head}, Returns]}]}}. - - -t_prop(L, Rname, Acc) -> {type, L, fname(prop, Rname, Acc), []}. -t_attr(L, Rname, Acc) -> {type, L, fname(attr, Rname, Acc), []}. -t_union(L, Alt) -> {type, L, union, lists:usort(Alt)}. -t_any(L) -> {type, L, any, []}. -t_atom(L) -> {type, L, atom, []}. -t_atom(L, A) -> {atom, L, A}. -t_integer(L) -> {type, L, integer, []}. -t_integer(L, I) -> {integer, L, I}. -t_list(L, Es) -> {type, L, list, Es}. -t_fun(L, As, Res) -> {type, L, 'fun', [{type, L, product, As}, Res]}. -t_tuple(L, Es) -> {type, L, tuple, Es}. + end, Flds). + + +fname_prefix(Op, #pass1{prefix = Pat}) -> + lists:flatten( + lists:map(fun(operation) -> str(Op); + (X) -> str(X) + end, Pat)). +%% fname_prefix(Op, #pass1{} = Acc) -> +%% case Op of +%% new -> "#new-"; +%% get -> "#get-"; +%% set -> "#set-"; +%% fromlist -> "#fromlist-"; +%% info -> "#info-"; +%% pos -> "#pos-"; +%% is_record -> "#is_record-"; +%% convert -> "#convert-"; +%% prop -> "#prop-"; +%% attr -> "#attr-" +%% end. + +%% fname_prefix(Op, Rname, Acc) -> +%% fname_prefix(Op, Acc) ++ str(Rname). + +str(A) when is_atom(A) -> + atom_to_list(A); +str(S) when is_list(S) -> + S. + +fname(Op, #pass1{} = Acc) -> + list_to_atom(fname_prefix(Op, Acc)). + %% list_to_atom(fname_prefix(Op, Acc)). + +fname(Op, Rname, #pass1{fname = FPat} = Acc) -> + Prefix = fname_prefix(Op, Acc), + list_to_atom( + lists:flatten( + lists:map(fun(prefix) -> str(Prefix); + (record) -> str(Rname); + (operation) -> str(Op); + (X) -> str(X) + end, FPat))). + %% list_to_atom(fname_prefix(Op, Rname, Acc)). + +fname(Op, Rname, V, #pass1{vfname = VPat} = Acc) -> + list_to_atom( + lists:flatten( + lists:map(fun(prefix) -> fname_prefix(Op, Acc); + (operation) -> str(Op); + (record) -> str(Rname); + (version) -> str(V); + (fname) -> str(fname(Op, Rname, Acc)); + (X) -> str(X) + end, VPat))). + %% list_to_atom(fname_prefix(Op, Rname, Acc) ++ "__" ++ V). + + +%%% Meta functions + +f_exported_recs(#pass1{exports = Es} = Acc, L) -> + Fname = fname(exported_records, Acc), + [funspec(L, Fname, [], + t_list(L, [t_union(L, [t_atom(L, E) || E <- Es])])), + {function, L, Fname, 0, + [{clause, L, [], [], + [erl_parse:abstract(Es, L)]}]} + ]. + +%%% Accessor functions +%%% +f_new_(#pass1{exports = Es} = Acc, L) -> + Fname = fname(new, Acc), + [funspec(L, Fname, [ {[t_atom(L, E)], t_record(L, E)} || + E <- Es ]), + {function, L, fname(new, Acc), 1, + [{clause, L, [{atom, L, Re}], [], + [{call, L, {atom, L, fname(new, Re, Acc)}, []}]} + || Re <- Es]} + ]. + +f_new_0(Rname, L, Acc) -> + Fname = fname(new, Rname, Acc), + [funspec(L, Fname, [], t_record(L, Rname)), + {function, L, fname(new, Rname, Acc), 0, + [{clause, L, [], [], + [{record, L, Rname, []}]}]} + ]. + + +f_new_1(Rname, L, Acc) -> + Fname = fname(new, Rname, Acc), + [funspec(L, Fname, [t_list(L, [t_prop(L, Rname, Acc)])], + t_record(L, Rname)), + {function, L, Fname, 1, + [{clause, L, [{var, L, 'Vals'}], [], + [{call, L, {atom, L, fname(set, Rname, Acc)}, + [{var, L, 'Vals'}, + {record, L, Rname, []} + ]}] + }]}]. + +funspec(L, Fname, [{H,_} | _] = Alts) -> + Arity = length(H), + {attribute, L, spec, + {{Fname, Arity}, + [{type, L, 'fun', [{type, L, product, Head}, Ret]} || + {Head, Ret} <- Alts, + no_empty_union(Head)]}}. + +no_empty_union({type,_,union,[]}) -> + false; +no_empty_union(T) when is_tuple(T) -> + no_empty_union(tuple_to_list(T)); +no_empty_union([H|T]) -> + no_empty_union(H) andalso no_empty_union(T); +no_empty_union(_) -> + true. + + + + +funspec(L, Fname, Head, Returns) -> + Arity = length(Head), + {attribute, L, spec, + {{Fname, Arity}, + [{type, L, 'fun', + [{type, L, product, Head}, Returns]}]}}. + + +t_prop(L, Rname, Acc) -> {type, L, fname(prop, Rname, Acc), []}. +t_attr(L, Rname, Acc) -> {type, L, fname(attr, Rname, Acc), []}. +t_union(L, Alt) -> {type, L, union, lists:usort(Alt)}. +t_any(L) -> {type, L, any, []}. +t_atom(L) -> {type, L, atom, []}. +t_atom(L, A) -> {atom, L, A}. +t_integer(L) -> {type, L, integer, []}. +t_integer(L, I) -> {integer, L, I}. +t_list(L, Es) -> {type, L, list, Es}. +t_fun(L, As, Res) -> {type, L, 'fun', [{type, L, product, As}, Res]}. +t_tuple(L, Es) -> {type, L, tuple, Es}. t_boolean(L) -> {type, L, boolean, []}. -t_record(L, A) -> {type, L, record, [{atom, L, A}]}. - -f_set_2(Rname, Flds, L, Acc) -> - Fname = fname(set, Rname, Acc), - TRec = t_record(L, Rname), - [funspec(L, Fname, [t_list(L, [t_prop(L, Rname, Acc)]), TRec], TRec), - {function, L, Fname, 2, - [{clause, L, [{var, L, 'Vals'}, {var, L, 'Rec'}], [], - [{match, L, {var, L, 'F'}, - {'fun', L, - {clauses, - [{clause, L, [{nil,L}, - {var,L,'R'}, - {var,L,'_F1'}], - [], - [{var, L, 'R'}]} | - [{clause, L, - [{cons, L, {tuple, L, [{atom, L, Attr}, - {var, L, 'V'}]}, - {var, L, 'T'}}, - {var, L, 'R'}, - {var, L, 'F1'}], - [[{call, L, {atom, L, is_list}, [{var, L, 'T'}]}]], - [{call, L, {var, L, 'F1'}, - [{var,L,'T'}, - {record, L, {var,L,'R'}, Rname, - [{record_field, L, - {atom, L, Attr}, - {var, L, 'V'}}]}, - {var, L, 'F1'}]}]} || Attr <- Flds] - ++ [{clause, L, [{var, L, 'Vs'}, {var,L,'R'},{var,L,'_'}], - [], - [bad_record_op(L, Fname, 'Vs', 'R')]}] - ]}}}, - {call, L, {var, L, 'F'}, [{var, L, 'Vals'}, - {var, L, 'Rec'}, - {var, L, 'F'}]}]}]}]. - -bad_record_op(L, Fname, Val) -> - {call, L, {remote, L, {atom,L,erlang}, {atom,L,error}}, - [{atom,L,bad_record_op}, {cons, L, {atom, L, Fname}, - {cons, L, {var, L, Val}, - {nil, L}}}]}. - -bad_record_op(L, Fname, Val, R) -> - {call, L, {remote, L, {atom,L,erlang}, {atom,L,error}}, - [{atom,L,bad_record_op}, {cons, L, {atom, L, Fname}, - {cons, L, {var, L, Val}, - {cons, L, {var, L, R}, - {nil, L}}}}]}. - - -f_pos_1(Rname, Flds, L, Acc) -> - Fname = fname(pos, Rname, Acc), - FieldList = lists:zip(Flds, lists:seq(2, length(Flds)+1)), - [ - funspec(L, Fname, [t_union(L, [t_attr(L, Rname, Acc), - t_atom(L)])], - t_integer(L)), - {function, L, Fname, 1, - [{clause, L, - [{atom, L, FldName}], - [], - [{integer, L, Pos}]} || {FldName, Pos} <- FieldList] ++ - [{clause, L, - [{var, L, 'A'}], - [[{call, L, {atom, L, is_atom}, [{var, L, 'A'}]}]], - [{integer, L, 0}]}] - }]. - -f_fromlist_1(Rname, L, Acc) -> - Fname = fname(fromlist, Rname, Acc), - [ - funspec(L, Fname, [t_list(L, [t_prop(L, Rname, Acc)])], - t_record(L, Rname)), - {function, L, Fname, 1, - [{clause, L, [{var, L, 'Vals'}], - [[ {call, L, {atom, L, is_list}, [{var, L, 'Vals'}]} ]], - [{call, L, {atom, L, Fname}, - [{var, L, 'Vals'}, - {call, L, {atom, L, fname(new, Rname, Acc)}, []}]} - ]} - ]}]. - -f_fromlist_2(Rname, Flds, L, Acc) -> - Fname = fname(fromlist, Rname, Acc), - FldList = field_list(Flds), - TRec = t_record(L, Rname), - [ - funspec(L, Fname, [t_list(L, [t_prop(L, Rname, Acc)]), TRec], - TRec), - {function, L, Fname, 2, - [{clause, L, [{var, L, 'Vals'}, {var, L, 'Rec'}], [], - [{match, L, {var, L, 'AttrNames'}, FldList}, - {match, L, {var, L, 'F'}, - {'fun', L, - {clauses, - [{clause, L, [{nil, L}, - {var, L,'R'}, - {var, L,'_F1'}], - [], - [{var, L, 'R'}]}, - {clause, L, [{cons, L, - {tuple, L, [{var, L, 'H'}, - {var, L, 'Pos'}]}, - {var, L, 'T'}}, - {var, L, 'R'}, {var, L, 'F1'}], - [[{call, L, {atom, L, is_list}, [{var, L, 'T'}]}]], - [{'case', L, {call, L, {remote, L, - {atom,L,lists},{atom,L,keyfind}}, - [{var,L,'H'},{integer,L,1},{var,L,'Vals'}]}, - [{clause, L, [{atom,L,false}], [], - [{call, L, {var, L, 'F1'}, [{var, L, 'T'}, - {var, L, 'R'}, - {var, L, 'F1'}]}]}, - {clause, L, [{tuple, L, [{var,L,'_'},{var,L,'Val'}]}], - [], - [{call, L, {var, L, 'F1'}, - [{var, L, 'T'}, - {call, L, {atom, L, 'setelement'}, - [{var, L, 'Pos'}, {var, L, 'R'}, {var, L, 'Val'}]}, - {var, L, 'F1'}]}]} - ]} - ]} - ]}}}, - {call, L, {var, L, 'F'}, [{var, L, 'AttrNames'}, - {var, L, 'Rec'}, - {var, L, 'F'}]} - ]} - ]}]. - -field_list(Flds) -> - erl_parse:abstract( - lists:zip(Flds, lists:seq(2, length(Flds)+1))). - - - -f_get_2(R, Flds, L, Acc) -> - FName = fname(get, R, Acc), - {_, Types} = lists:keyfind(R, 1, Acc#pass1.record_types), - [funspec(L, FName, - [{[t_atom(L, A), t_record(L, R)], T} - || {A, T} <- Types] - ++ [{[t_list(L, [t_attr(L, R, Acc)]), t_record(L, R)], - t_list(L, [t_any(L)])}] - ), - {function, L, FName, 2, - [{clause, L, [{var, L, 'Attrs'}, {var, L, 'R'}], - [[{call, L, {atom, L, is_list}, [{var, L, 'Attrs'}]}]], - [{lc, L, {call, L, {atom, L, FName}, [{var, L, 'A'}, {var, L, 'R'}]}, - [{generate, L, {var, L, 'A'}, {var, L, 'Attrs'}}]}] - } | - [{clause, L, [{atom, L, Attr}, {var, L, 'R'}], [], - [{record_field, L, {var, L, 'R'}, R, {atom, L, Attr}}]} || - Attr <- Flds]] ++ - [{clause, L, [{var, L, 'Attr'}, {var, L, 'R'}], [], - [bad_record_op(L, FName, 'Attr', 'R')]}] - }]. - - -f_info(Acc, L) -> - Fname = list_to_atom(fname_prefix(info, Acc)), - [funspec(L, Fname, - [{[t_atom(L, R)], - t_list(L, [t_attr(L, R, Acc)])} - || R <- Acc#pass1.exports]), - {function, L, Fname, 1, - [{clause, L, - [{var, L, 'RecName'}], [], - [{call, L, {atom, L, Fname}, [{var, L, 'RecName'}, {atom, L, fields}]}] - }]} - ]. - -f_isrec_2(#pass1{records = Rs, exports = Es} = Acc, L) -> - Fname = list_to_atom(fname_prefix(is_record, Acc)), - Info = [{R,length(As) + 1} || {R,As} <- Rs, lists:member(R, Es)], - [%% This contract is correct, but is ignored by Dialyzer because it - %% has overlapping domains: - %% funspec(L, Fname, - %% [{[t_atom(L, R), t_record(L, R)], t_atom(L, true)} - %% || R <- Es] ++ - %% [{[t_any(L), t_any(L)], t_atom(L, false)}]), - %% This is less specific, but more useful to Dialyzer: - funspec(L, Fname, [{[t_any(L), t_any(L)], t_boolean(L)}]), - {function, L, Fname, 2, - lists:map( - fun({R, Ln}) -> - {clause, L, - [{atom, L, R}, {var, L, 'Rec'}], - [[{op,L,'==', - {call, L, {atom,L,tuple_size},[{var,L,'Rec'}]}, - {integer, L, Ln}}, - {op,L,'==', - {call,L,{atom,L,element},[{integer,L,1}, - {var,L,'Rec'}]}, - {atom, L, R}}]], - [{atom, L, true}]} - end, Info) ++ - [{clause, L, [{var,L,'_'}, {var,L,'_'}], [], - [{atom, L, false}]}]} - ]. - - -f_info_2(Acc, L) -> - Fname = list_to_atom(fname_prefix(info, Acc)), - [funspec(L, Fname, - lists:flatmap( - fun(Rname) -> - Flds = get_flds(Rname, Acc), - TRec = t_atom(L, Rname), - [{[TRec, t_atom(L, size)], t_integer(L, length(Flds)+1)}, - {[TRec, t_atom(L, fields)], - t_list(L, [t_attr(L, Rname, Acc)])}] - end, Acc#pass1.exports)), - {function, L, Fname, 2, - [{clause, L, - [{atom, L, R}, - {var, L, 'Info'}], - [], - [{call, L, {atom, L, fname(info, R, Acc)}, [{var, L, 'Info'}]}]} || - R <- Acc#pass1.exports]} - ]. - -f_info_3(Versions, L, Acc) -> - Fname = list_to_atom(fname_prefix(info, Acc)), - [ - {function, L, Fname, 3, - [{clause, L, - [{atom, L, R}, - {var, L, 'Info'}, - {string, L, V}], - [], - [{call, L, {atom, L, fname(info,R,V,Acc)}, [{var, L, 'Info'}]}]} || - {R,V} <- flat_versions(Versions)]} - ]. - -f_pos_2(#pass1{exports = Es} = Acc, L) -> - Fname = list_to_atom(fname_prefix(pos, Acc)), - [ - funspec(L, Fname, lists:flatmap( - fun(R) -> - Flds = get_flds(R, Acc), - PFlds = lists:zip( - lists:seq(1, length(Flds)), Flds), - [{[t_atom(L, R), t_atom(L, A)], - t_integer(L, P)} || {P,A} <- PFlds] - end, Es)), - {function, L, Fname, 2, - [{clause, L, - [{atom, L, R}, - {var, L, 'Attr'}], - [], - [{call, L, {atom, L, fname(pos, R, Acc)}, [{var, L, 'Attr'}]}]} || - R <- Acc#pass1.exports]} - ]. - -f_isrec_1(Acc, L) -> - Fname = list_to_atom(fname_prefix(is_record, Acc)), - [%% This contract is correct, but is ignored by Dialyzer because it - %% has overlapping domains: - %% funspec(L, Fname, - %% [{[t_record(L, R)], t_atom(L, true)} - %% || R <- Acc#pass1.exports] - %% ++ [{[t_any(L)], t_atom(L, false)}]), - %% This is less specific, but more useful to Dialyzer: - funspec(L, Fname, [{[t_any(L)], t_boolean(L)}]), - {function, L, Fname, 1, - [{clause, L, - [{var, L, 'X'}], - [], - [{'if',L, - [{clause, L, [], [[{call, L, {atom,L,is_record}, - [{var,L,'X'},{atom,L,R}]}]], - [{atom,L,true}]} || R <- Acc#pass1.exports] ++ - [{clause,L, [], [[{atom,L,true}]], - [{atom, L, false}]}]}]} - ]} - ]. - - - -f_get(#pass1{record_types = RTypes, exports = Es} = Acc, L) -> - Fname = list_to_atom(fname_prefix(get, Acc)), - [funspec(L, Fname, - lists:append( - [[{[t_atom(L, A), t_record(L, R)], T} - || {A, T} <- Types] - || {R, Types} <- RTypes, lists:member(R, Es)]) - ++ [{[t_list(L, [t_attr(L, R, Acc)]), t_record(L, R)], - t_list(L, [t_union(L, [Ts || {_, Ts} <- Types])])} - || {R, Types} <- RTypes, lists:member(R, Es)] - ), - {function, L, Fname, 2, - [{clause, L, - [{var, L, 'Attrs'}, - {var, L, 'Rec'}], - [[{call, L, - {atom, L, is_record}, - [{var, L, 'Rec'}, {atom, L, R}]}]], - [{call, L, {atom, L, fname(get, R, Acc)}, [{var, L, 'Attrs'}, - {var, L, 'Rec'}]}]} || - R <- Es]} - ]. - - -f_set(Acc, L) -> - Fname = list_to_atom(fname_prefix(set, Acc)), - [funspec(L, Fname, - lists:map( - fun(Rname) -> - TRec = t_record(L, Rname), - {[t_list(L, [t_prop(L, Rname, Acc)]), TRec], TRec} - end, Acc#pass1.exports)), - {function, L, Fname, 2, - [{clause, L, - [{var, L, 'Vals'}, - {var, L, 'Rec'}], - [[{call, L, - {atom, L, is_record}, - [{var, L, 'Rec'}, {atom, L, R}]}]], - [{call, L, {atom, L, fname(set, R, Acc)}, [{var, L, 'Vals'}, - {var, L, 'Rec'}]}]} || - R <- Acc#pass1.exports]} - ]. - -f_fromlist(Acc, L) -> - Fname = list_to_atom(fname_prefix(fromlist, Acc)), - [funspec(L, Fname, - lists:map( - fun(Rname) -> - TRec = t_record(L, Rname), - {[t_list(L, [t_prop(L, Rname, Acc)]), TRec], TRec} - end, Acc#pass1.exports)), - {function, L, Fname, 2, - [{clause, L, - [{var, L, 'Vals'}, - {var, L, 'Rec'}], - [[{call, L, - {atom, L, is_record}, - [{var, L, 'Rec'}, {atom, L, R}]}]], - [{call, L, {atom, L, fname(fromlist, R, Acc)}, [{var, L, 'Vals'}, - {var, L, 'Rec'}]}]} || - R <- Acc#pass1.exports]} - ]. - -f_info_1(Rname, Acc, L) -> - Fname = fname(info, Rname, Acc), - Flds = get_flds(Rname, Acc), - [funspec(L, Fname, [{[t_atom(L, fields)], - t_list(L, [t_union(L, [t_atom(L,F) || F <- Flds])])}, - {[t_atom(L, size)], t_integer(L, length(Flds)+1)}]), - {function, L, Fname, 1, - [{clause, L, [{atom, L, fields}], [], - [{call, L, {atom, L, record_info}, - [{atom, L, fields}, {atom, L, Rname}]}] - }, - {clause, L, [{atom, L, size}], [], - [{call, L, {atom, L, record_info}, - [{atom, L, size}, {atom, L, Rname}]}] - }]} - ]. - -f_info_1(Rname, Acc, L, V) -> - f_info_1(recname(Rname, V), Acc, L). - -recname(Rname, V) -> - list_to_atom(lists:concat([Rname,"__",V])). - -f_convert(_Vsns, L, Acc) -> - {function, L, fname(convert, Acc), 2, - [{clause, L, - [{var, L, 'FromVsn'}, - {var, L, 'Rec'}], - [[{call,L,{atom, L, is_tuple}, - [{var, L, 'Rec'}]}]], - [{match, L, {var, L, 'Rname'}, - {call, L, {atom, L, element}, - [{integer, L, 1}, {var, 1, 'Rec'}]}}, - {match,L,{var,L,'Size'}, - {call, L, {atom, L, fname(info, Acc)}, - [{var,L,'Rname'}, {atom, L, size}, {var,L,'FromVsn'}]}}, - {match, L, {var, L, 'Size'}, - {call, L, {atom, L, size}, - [{var, L, 'Rec'}]}}, - %% - %% {match, L, {var, L, 'Old_fields'}, - %% {call, L, {atom,L,fname(info, Acc)}, - %% [{var,L,'Rname'},{atom,L,fields},{var,L,'FromVsn'}]}}, - {match, L, {var, L, 'New_fields'}, - {call, L, {atom,L,fname(info, Acc)}, - [{var,L,'Rname'},{atom,L,fields}]}}, - - {match, L, {var, L, 'Values'}, - {call, L, {remote, L, {atom, L, lists}, {atom, L, zip}}, - [{call, L, {atom,L,fname(info, Acc)}, - [{var,L,'Rname'},{atom,L,fields},{var,L,'FromVsn'}]}, - {call, L, {atom, L, 'tl'}, - [{call, L, {atom, L, tuple_to_list}, - [{var, L, 'Rec'}]}]}]}}, - {match, L, {tuple, L, [{var, L, 'Matching'}, - {var, L, 'Discarded'}]}, - {call, L, {remote, L, {atom, L, lists}, {atom, L, partition}}, - [{'fun',L, - {clauses, - [{clause,L, - [{tuple,L,[{var,L,'F'},{var,L,'_'}]}], - [], - [{call,L, - {remote,L,{atom,L,lists},{atom,L,member}}, - [{var, L, 'F'}, {var,L,'New_fields'}]}]}]}}, - {var, L, 'Values'}]}}, - {tuple, L, [{call, L, {atom, L, fname(set, Acc)}, - [{var, L, 'Matching'}, - {call, L, {atom, L, fname(new, Acc)}, - [{var, L, 'Rname'}]}]}, - {var, L, 'Discarded'}]}] - }]}. - -f_lens_(#pass1{exports = Es} = Acc, L) -> - Fname = fname(lens, Acc), - [ - funspec(L, Fname, [ {[t_attr(L, Rname, Acc), t_atom(L, Rname)], - t_tuple(L, [t_fun(L, [t_record(L, Rname)], t_any(L)), - t_fun(L, [t_any(L), - t_record(L, Rname)], - t_record(L, Rname))])} - || Rname <- Es]), - {function, L, Fname, 2, - [{clause, L, [{var, L, 'Attr'}, {atom, L, Re}], [], - [{call, L, {atom, L, fname(lens, Re, Acc)}, [{var, L, 'Attr'}]}]} - || Re <- Es]} - ]. - -f_lens_1(Rname, Flds, L, Acc) -> - Fname = fname(lens, Rname, Acc), - [funspec(L, Fname, [ {[t_attr(L, Rname, Acc)], - t_tuple(L, [t_fun(L, [t_record(L, Rname)], t_any(L)), - t_fun(L, [t_any(L), - t_record(L, Rname)], - t_record(L, Rname))])} ]), - {function, L, Fname, 1, - [{clause, L, [{atom, L, Attr}], [], - [{tuple, L, [{'fun', L, - {clauses, - [{clause, L, [{var, L, 'R'}], [], - [{call, L, {atom, L, fname(get, Rname, Acc)}, - [{atom, L, Attr}, {var, L, 'R'}]}]} - ]}}, - {'fun', L, - {clauses, - [{clause, L, [{var, L, 'X'}, {var, L, 'R'}], [], - [{call, L, {atom, L, fname(set, Rname, Acc)}, - [{cons,L, {tuple, L, [{atom, L, Attr}, - {var, L, 'X'}]}, {nil,L}}, - {var, L, 'R'}]}] - }]}} - ]}]} || Attr <- Flds] ++ - [{clause, L, [{var, L, 'Attr'}], [], - [bad_record_op(L, Fname, 'Attr')]}] - }]. - -%%% ========== generic parse_transform stuff ============== - --spec context(atom(), #context{}) -> - term(). -%% @hidden -context(module, #context{module = M} ) -> M; -context(function, #context{function = F}) -> F; -context(arity, #context{arity = A} ) -> A. - - - -rpt_error(Reason, Fun, Info) -> - Fmt = lists:flatten( - ["*** ERROR in parse_transform function:~n" - "*** Reason = ~p~n", - "*** Location: ~p~n", - ["*** ~10w = ~p~n" || _ <- Info]]), - Args = [Reason, Fun | - lists:foldr( - fun({K,V}, Acc) -> - [K, V | Acc] - end, [], Info)], - io:format(Fmt, Args). - --spec format_error({atom(), term()}) -> - iolist(). -%% @hidden -format_error({_Cat, Error}) -> - Error. +t_record(L, A) -> {type, L, record, [{atom, L, A}]}. + +f_set_2(Rname, Flds, L, Acc) -> + Fname = fname(set, Rname, Acc), + TRec = t_record(L, Rname), + [funspec(L, Fname, [t_list(L, [t_prop(L, Rname, Acc)]), TRec], TRec), + {function, L, Fname, 2, + [{clause, L, [{var, L, 'Vals'}, {var, L, 'Rec'}], [], + [{match, L, {var, L, 'F'}, + {'fun', L, + {clauses, + [{clause, L, [{nil,L}, + {var,L,'R'}, + {var,L,'_F1'}], + [], + [{var, L, 'R'}]} | + [{clause, L, + [{cons, L, {tuple, L, [{atom, L, Attr}, + {var, L, 'V'}]}, + {var, L, 'T'}}, + {var, L, 'R'}, + {var, L, 'F1'}], + [[{call, L, {atom, L, is_list}, [{var, L, 'T'}]}]], + [{call, L, {var, L, 'F1'}, + [{var,L,'T'}, + {record, L, {var,L,'R'}, Rname, + [{record_field, L, + {atom, L, Attr}, + {var, L, 'V'}}]}, + {var, L, 'F1'}]}]} || Attr <- Flds] + ++ [{clause, L, [{var, L, 'Vs'}, {var,L,'R'},{var,L,'_'}], + [], + [bad_record_op(L, Fname, 'Vs', 'R')]}] + ]}}}, + {call, L, {var, L, 'F'}, [{var, L, 'Vals'}, + {var, L, 'Rec'}, + {var, L, 'F'}]}]}]}]. + +bad_record_op(L, Fname, Val) -> + {call, L, {remote, L, {atom,L,erlang}, {atom,L,error}}, + [{atom,L,bad_record_op}, {cons, L, {atom, L, Fname}, + {cons, L, {var, L, Val}, + {nil, L}}}]}. + +bad_record_op(L, Fname, Val, R) -> + {call, L, {remote, L, {atom,L,erlang}, {atom,L,error}}, + [{atom,L,bad_record_op}, {cons, L, {atom, L, Fname}, + {cons, L, {var, L, Val}, + {cons, L, {var, L, R}, + {nil, L}}}}]}. + + +f_pos_1(Rname, Flds, L, Acc) -> + Fname = fname(pos, Rname, Acc), + FieldList = lists:zip(Flds, lists:seq(2, length(Flds)+1)), + [ + funspec(L, Fname, [t_union(L, [t_attr(L, Rname, Acc), + t_atom(L)])], + t_integer(L)), + {function, L, Fname, 1, + [{clause, L, + [{atom, L, FldName}], + [], + [{integer, L, Pos}]} || {FldName, Pos} <- FieldList] ++ + [{clause, L, + [{var, L, 'A'}], + [[{call, L, {atom, L, is_atom}, [{var, L, 'A'}]}]], + [{integer, L, 0}]}] + }]. + +f_fromlist_1(Rname, L, Acc) -> + Fname = fname(fromlist, Rname, Acc), + [ + funspec(L, Fname, [t_list(L, [t_prop(L, Rname, Acc)])], + t_record(L, Rname)), + {function, L, Fname, 1, + [{clause, L, [{var, L, 'Vals'}], + [[ {call, L, {atom, L, is_list}, [{var, L, 'Vals'}]} ]], + [{call, L, {atom, L, Fname}, + [{var, L, 'Vals'}, + {call, L, {atom, L, fname(new, Rname, Acc)}, []}]} + ]} + ]}]. + +f_fromlist_2(Rname, Flds, L, Acc) -> + Fname = fname(fromlist, Rname, Acc), + FldList = field_list(Flds), + TRec = t_record(L, Rname), + [ + funspec(L, Fname, [t_list(L, [t_prop(L, Rname, Acc)]), TRec], + TRec), + {function, L, Fname, 2, + [{clause, L, [{var, L, 'Vals'}, {var, L, 'Rec'}], [], + [{match, L, {var, L, 'AttrNames'}, FldList}, + {match, L, {var, L, 'F'}, + {'fun', L, + {clauses, + [{clause, L, [{nil, L}, + {var, L,'R'}, + {var, L,'_F1'}], + [], + [{var, L, 'R'}]}, + {clause, L, [{cons, L, + {tuple, L, [{var, L, 'H'}, + {var, L, 'Pos'}]}, + {var, L, 'T'}}, + {var, L, 'R'}, {var, L, 'F1'}], + [[{call, L, {atom, L, is_list}, [{var, L, 'T'}]}]], + [{'case', L, {call, L, {remote, L, + {atom,L,lists},{atom,L,keyfind}}, + [{var,L,'H'},{integer,L,1},{var,L,'Vals'}]}, + [{clause, L, [{atom,L,false}], [], + [{call, L, {var, L, 'F1'}, [{var, L, 'T'}, + {var, L, 'R'}, + {var, L, 'F1'}]}]}, + {clause, L, [{tuple, L, [{var,L,'_'},{var,L,'Val'}]}], + [], + [{call, L, {var, L, 'F1'}, + [{var, L, 'T'}, + {call, L, {atom, L, 'setelement'}, + [{var, L, 'Pos'}, {var, L, 'R'}, {var, L, 'Val'}]}, + {var, L, 'F1'}]}]} + ]} + ]} + ]}}}, + {call, L, {var, L, 'F'}, [{var, L, 'AttrNames'}, + {var, L, 'Rec'}, + {var, L, 'F'}]} + ]} + ]}]. + +field_list(Flds) -> + erl_parse:abstract( + lists:zip(Flds, lists:seq(2, length(Flds)+1))). + + + +f_get_2(R, Flds, L, Acc) -> + FName = fname(get, R, Acc), + {_, Types} = lists:keyfind(R, 1, Acc#pass1.record_types), + [funspec(L, FName, + [{[t_atom(L, A), t_record(L, R)], T} + || {A, T} <- Types] + ++ [{[t_list(L, [t_attr(L, R, Acc)]), t_record(L, R)], + t_list(L, [t_any(L)])}] + ), + {function, L, FName, 2, + [{clause, L, [{var, L, 'Attrs'}, {var, L, 'R'}], + [[{call, L, {atom, L, is_list}, [{var, L, 'Attrs'}]}]], + [{lc, L, {call, L, {atom, L, FName}, [{var, L, 'A'}, {var, L, 'R'}]}, + [{generate, L, {var, L, 'A'}, {var, L, 'Attrs'}}]}] + } | + [{clause, L, [{atom, L, Attr}, {var, L, 'R'}], [], + [{record_field, L, {var, L, 'R'}, R, {atom, L, Attr}}]} || + Attr <- Flds]] ++ + [{clause, L, [{var, L, 'Attr'}, {var, L, 'R'}], [], + [bad_record_op(L, FName, 'Attr', 'R')]}] + }]. + + +f_info(Acc, L) -> + Fname = list_to_atom(fname_prefix(info, Acc)), + [funspec(L, Fname, + [{[t_atom(L, R)], + t_list(L, [t_attr(L, R, Acc)])} + || R <- Acc#pass1.exports]), + {function, L, Fname, 1, + [{clause, L, + [{var, L, 'RecName'}], [], + [{call, L, {atom, L, Fname}, [{var, L, 'RecName'}, {atom, L, fields}]}] + }]} + ]. + +f_isrec_2(#pass1{records = Rs, exports = Es} = Acc, L) -> + Fname = list_to_atom(fname_prefix(is_record, Acc)), + Info = [{R,length(As) + 1} || {R,As} <- Rs, lists:member(R, Es)], + [%% This contract is correct, but is ignored by Dialyzer because it + %% has overlapping domains: + %% funspec(L, Fname, + %% [{[t_atom(L, R), t_record(L, R)], t_atom(L, true)} + %% || R <- Es] ++ + %% [{[t_any(L), t_any(L)], t_atom(L, false)}]), + %% This is less specific, but more useful to Dialyzer: + funspec(L, Fname, [{[t_any(L), t_any(L)], t_boolean(L)}]), + {function, L, Fname, 2, + lists:map( + fun({R, Ln}) -> + {clause, L, + [{atom, L, R}, {var, L, 'Rec'}], + [[{op,L,'==', + {call, L, {atom,L,tuple_size},[{var,L,'Rec'}]}, + {integer, L, Ln}}, + {op,L,'==', + {call,L,{atom,L,element},[{integer,L,1}, + {var,L,'Rec'}]}, + {atom, L, R}}]], + [{atom, L, true}]} + end, Info) ++ + [{clause, L, [{var,L,'_'}, {var,L,'_'}], [], + [{atom, L, false}]}]} + ]. + + +f_info_2(Acc, L) -> + Fname = list_to_atom(fname_prefix(info, Acc)), + [funspec(L, Fname, + lists:flatmap( + fun(Rname) -> + Flds = get_flds(Rname, Acc), + TRec = t_atom(L, Rname), + [{[TRec, t_atom(L, size)], t_integer(L, length(Flds)+1)}, + {[TRec, t_atom(L, fields)], + t_list(L, [t_attr(L, Rname, Acc)])}] + end, Acc#pass1.exports)), + {function, L, Fname, 2, + [{clause, L, + [{atom, L, R}, + {var, L, 'Info'}], + [], + [{call, L, {atom, L, fname(info, R, Acc)}, [{var, L, 'Info'}]}]} || + R <- Acc#pass1.exports]} + ]. + +f_info_3(Versions, L, Acc) -> + Fname = list_to_atom(fname_prefix(info, Acc)), + [ + {function, L, Fname, 3, + [{clause, L, + [{atom, L, R}, + {var, L, 'Info'}, + {string, L, V}], + [], + [{call, L, {atom, L, fname(info,R,V,Acc)}, [{var, L, 'Info'}]}]} || + {R,V} <- flat_versions(Versions)]} + ]. + +f_pos_2(#pass1{exports = Es} = Acc, L) -> + Fname = list_to_atom(fname_prefix(pos, Acc)), + [ + funspec(L, Fname, lists:flatmap( + fun(R) -> + Flds = get_flds(R, Acc), + PFlds = lists:zip( + lists:seq(1, length(Flds)), Flds), + [{[t_atom(L, R), t_atom(L, A)], + t_integer(L, P)} || {P,A} <- PFlds] + end, Es)), + {function, L, Fname, 2, + [{clause, L, + [{atom, L, R}, + {var, L, 'Attr'}], + [], + [{call, L, {atom, L, fname(pos, R, Acc)}, [{var, L, 'Attr'}]}]} || + R <- Acc#pass1.exports]} + ]. + +f_isrec_1(Acc, L) -> + Fname = list_to_atom(fname_prefix(is_record, Acc)), + [%% This contract is correct, but is ignored by Dialyzer because it + %% has overlapping domains: + %% funspec(L, Fname, + %% [{[t_record(L, R)], t_atom(L, true)} + %% || R <- Acc#pass1.exports] + %% ++ [{[t_any(L)], t_atom(L, false)}]), + %% This is less specific, but more useful to Dialyzer: + funspec(L, Fname, [{[t_any(L)], t_boolean(L)}]), + {function, L, Fname, 1, + [{clause, L, + [{var, L, 'X'}], + [], + [{'if',L, + [{clause, L, [], [[{call, L, {atom,L,is_record}, + [{var,L,'X'},{atom,L,R}]}]], + [{atom,L,true}]} || R <- Acc#pass1.exports] ++ + [{clause,L, [], [[{atom,L,true}]], + [{atom, L, false}]}]}]} + ]} + ]. + + + +f_get(#pass1{record_types = RTypes, exports = Es} = Acc, L) -> + Fname = list_to_atom(fname_prefix(get, Acc)), + [funspec(L, Fname, + lists:append( + [[{[t_atom(L, A), t_record(L, R)], T} + || {A, T} <- Types] + || {R, Types} <- RTypes, lists:member(R, Es)]) + ++ [{[t_list(L, [t_attr(L, R, Acc)]), t_record(L, R)], + t_list(L, [t_union(L, [Ts || {_, Ts} <- Types])])} + || {R, Types} <- RTypes, lists:member(R, Es)] + ), + {function, L, Fname, 2, + [{clause, L, + [{var, L, 'Attrs'}, + {var, L, 'Rec'}], + [[{call, L, + {atom, L, is_record}, + [{var, L, 'Rec'}, {atom, L, R}]}]], + [{call, L, {atom, L, fname(get, R, Acc)}, [{var, L, 'Attrs'}, + {var, L, 'Rec'}]}]} || + R <- Es]} + ]. + + +f_set(Acc, L) -> + Fname = list_to_atom(fname_prefix(set, Acc)), + [funspec(L, Fname, + lists:map( + fun(Rname) -> + TRec = t_record(L, Rname), + {[t_list(L, [t_prop(L, Rname, Acc)]), TRec], TRec} + end, Acc#pass1.exports)), + {function, L, Fname, 2, + [{clause, L, + [{var, L, 'Vals'}, + {var, L, 'Rec'}], + [[{call, L, + {atom, L, is_record}, + [{var, L, 'Rec'}, {atom, L, R}]}]], + [{call, L, {atom, L, fname(set, R, Acc)}, [{var, L, 'Vals'}, + {var, L, 'Rec'}]}]} || + R <- Acc#pass1.exports]} + ]. + +f_fromlist(Acc, L) -> + Fname = list_to_atom(fname_prefix(fromlist, Acc)), + [funspec(L, Fname, + lists:map( + fun(Rname) -> + TRec = t_record(L, Rname), + {[t_list(L, [t_prop(L, Rname, Acc)]), TRec], TRec} + end, Acc#pass1.exports)), + {function, L, Fname, 2, + [{clause, L, + [{var, L, 'Vals'}, + {var, L, 'Rec'}], + [[{call, L, + {atom, L, is_record}, + [{var, L, 'Rec'}, {atom, L, R}]}]], + [{call, L, {atom, L, fname(fromlist, R, Acc)}, [{var, L, 'Vals'}, + {var, L, 'Rec'}]}]} || + R <- Acc#pass1.exports]} + ]. + +f_info_1(Rname, Acc, L) -> + Fname = fname(info, Rname, Acc), + Flds = get_flds(Rname, Acc), + [funspec(L, Fname, [{[t_atom(L, fields)], + t_list(L, [t_union(L, [t_atom(L,F) || F <- Flds])])}, + {[t_atom(L, size)], t_integer(L, length(Flds)+1)}]), + {function, L, Fname, 1, + [{clause, L, [{atom, L, fields}], [], + [{call, L, {atom, L, record_info}, + [{atom, L, fields}, {atom, L, Rname}]}] + }, + {clause, L, [{atom, L, size}], [], + [{call, L, {atom, L, record_info}, + [{atom, L, size}, {atom, L, Rname}]}] + }]} + ]. + +f_info_1(Rname, Acc, L, V) -> + f_info_1(recname(Rname, V), Acc, L). + +recname(Rname, V) -> + list_to_atom(lists:concat([Rname,"__",V])). + +f_convert(_Vsns, L, Acc) -> + {function, L, fname(convert, Acc), 2, + [{clause, L, + [{var, L, 'FromVsn'}, + {var, L, 'Rec'}], + [[{call,L,{atom, L, is_tuple}, + [{var, L, 'Rec'}]}]], + [{match, L, {var, L, 'Rname'}, + {call, L, {atom, L, element}, + [{integer, L, 1}, {var, 1, 'Rec'}]}}, + {match,L,{var,L,'Size'}, + {call, L, {atom, L, fname(info, Acc)}, + [{var,L,'Rname'}, {atom, L, size}, {var,L,'FromVsn'}]}}, + {match, L, {var, L, 'Size'}, + {call, L, {atom, L, size}, + [{var, L, 'Rec'}]}}, + %% + %% {match, L, {var, L, 'Old_fields'}, + %% {call, L, {atom,L,fname(info, Acc)}, + %% [{var,L,'Rname'},{atom,L,fields},{var,L,'FromVsn'}]}}, + {match, L, {var, L, 'New_fields'}, + {call, L, {atom,L,fname(info, Acc)}, + [{var,L,'Rname'},{atom,L,fields}]}}, + + {match, L, {var, L, 'Values'}, + {call, L, {remote, L, {atom, L, lists}, {atom, L, zip}}, + [{call, L, {atom,L,fname(info, Acc)}, + [{var,L,'Rname'},{atom,L,fields},{var,L,'FromVsn'}]}, + {call, L, {atom, L, 'tl'}, + [{call, L, {atom, L, tuple_to_list}, + [{var, L, 'Rec'}]}]}]}}, + {match, L, {tuple, L, [{var, L, 'Matching'}, + {var, L, 'Discarded'}]}, + {call, L, {remote, L, {atom, L, lists}, {atom, L, partition}}, + [{'fun',L, + {clauses, + [{clause,L, + [{tuple,L,[{var,L,'F'},{var,L,'_'}]}], + [], + [{call,L, + {remote,L,{atom,L,lists},{atom,L,member}}, + [{var, L, 'F'}, {var,L,'New_fields'}]}]}]}}, + {var, L, 'Values'}]}}, + {tuple, L, [{call, L, {atom, L, fname(set, Acc)}, + [{var, L, 'Matching'}, + {call, L, {atom, L, fname(new, Acc)}, + [{var, L, 'Rname'}]}]}, + {var, L, 'Discarded'}]}] + }]}. + +f_lens_(#pass1{exports = Es} = Acc, L) -> + Fname = fname(lens, Acc), + [ + funspec(L, Fname, [ {[t_attr(L, Rname, Acc), t_atom(L, Rname)], + t_tuple(L, [t_fun(L, [t_record(L, Rname)], t_any(L)), + t_fun(L, [t_any(L), + t_record(L, Rname)], + t_record(L, Rname))])} + || Rname <- Es]), + {function, L, Fname, 2, + [{clause, L, [{var, L, 'Attr'}, {atom, L, Re}], [], + [{call, L, {atom, L, fname(lens, Re, Acc)}, [{var, L, 'Attr'}]}]} + || Re <- Es]} + ]. + +f_lens_1(Rname, Flds, L, Acc) -> + Fname = fname(lens, Rname, Acc), + [funspec(L, Fname, [ {[t_attr(L, Rname, Acc)], + t_tuple(L, [t_fun(L, [t_record(L, Rname)], t_any(L)), + t_fun(L, [t_any(L), + t_record(L, Rname)], + t_record(L, Rname))])} ]), + {function, L, Fname, 1, + [{clause, L, [{atom, L, Attr}], [], + [{tuple, L, [{'fun', L, + {clauses, + [{clause, L, [{var, L, 'R'}], [], + [{call, L, {atom, L, fname(get, Rname, Acc)}, + [{atom, L, Attr}, {var, L, 'R'}]}]} + ]}}, + {'fun', L, + {clauses, + [{clause, L, [{var, L, 'X'}, {var, L, 'R'}], [], + [{call, L, {atom, L, fname(set, Rname, Acc)}, + [{cons,L, {tuple, L, [{atom, L, Attr}, + {var, L, 'X'}]}, {nil,L}}, + {var, L, 'R'}]}] + }]}} + ]}]} || Attr <- Flds] ++ + [{clause, L, [{var, L, 'Attr'}], [], + [bad_record_op(L, Fname, 'Attr')]}] + }]. + +%%% ========== generic parse_transform stuff ============== + +-spec context(atom(), #context{}) -> + term(). +%% @hidden +context(module, #context{module = M} ) -> M; +context(function, #context{function = F}) -> F; +context(arity, #context{arity = A} ) -> A. + + + +rpt_error(Reason, Fun, Info) -> + Fmt = lists:flatten( + ["*** ERROR in parse_transform function:~n" + "*** Reason = ~p~n", + "*** Location: ~p~n", + ["*** ~10w = ~p~n" || _ <- Info]]), + Args = [Reason, Fun | + lists:foldr( + fun({K,V}, Acc) -> + [K, V | Acc] + end, [], Info)], + io:format(Fmt, Args). + +-spec format_error({atom(), term()}) -> + iolist(). +%% @hidden +format_error({_Cat, Error}) -> + Error. diff --git a/src/parse_trans.app.src b/src/parse_trans.app.src index 6d70b70..655d471 100644 --- a/src/parse_trans.app.src +++ b/src/parse_trans.app.src @@ -1,30 +1,32 @@ -%%% -*- erlang -*- -%%% The contents of this file are subject to the Erlang Public License, -%%% Version 1.1, (the "License"); you may not use this file except in -%%% compliance with the License. You may obtain a copy of the License at -%%% http://www.erlang.org/EPLICENSE -%%% -%%% Software distributed under the License is distributed on an "AS IS" -%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%%% the License for the specific language governing rights and limitations -%%% under the License. -%%% -%%% The Original Code is exprecs-0.2. -%%% -%%% The Initial Developer of the Original Code is Ericsson AB. -%%% Portions created by Ericsson are Copyright (C), 2006, Ericsson AB. -%%% All Rights Reserved. -%%% -%%% Contributor(s): ______________________________________. - -%%% @author Ulf Wiger -%%% @doc This is a container for parse_trans modules. -%%% @end +%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% -------------------------------------------------- +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% -------------------------------------------------- +%% +%% @author Ulf Wiger +%% @doc Parse transform library. +%% @end {application, parse_trans, [{description, "Parse transform library"}, {vsn, git}, {registered, []}, {applications, [kernel, stdlib, syntax_tools]}, - {env, []} + {env, []}, + + {maintainers, ["Ulf Wiger"]}, + {licenses, ["Apache 2.0"]}, + {links, [{"Github", "https://github.com/uwiger/parse_trans"}]} ]}. diff --git a/src/parse_trans.erl b/src/parse_trans.erl index f305e80..62f8f25 100644 --- a/src/parse_trans.erl +++ b/src/parse_trans.erl @@ -1,35 +1,32 @@ -%%% The contents of this file are subject to the Erlang Public License, -%%% Version 1.1, (the "License"); you may not use this file except in -%%% compliance with the License. You may obtain a copy of the License at -%%% http://www.erlang.org/EPLICENSE -%%% -%%% Software distributed under the License is distributed on an "AS IS" -%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%%% the License for the specific language governing rights and limitations -%%% under the License. -%%% -%%% The Original Code is exprecs-0.2. -%%% -%%% The Initial Developer of the Original Code is Ericsson AB. -%%% Portions created by Ericsson are Copyright (C), 2006, Ericsson AB. -%%% All Rights Reserved. -%%% -%%% Contributor(s): ______________________________________. - -%%%------------------------------------------------------------------- -%%% File : parse_trans.erl -%%% @author : Ulf Wiger -%%% @end -%%% Description : -%%% -%%% Created : 13 Feb 2006 by Ulf Wiger (then Ericsson) +%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% -------------------------------------------------- +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% -------------------------------------------------- +%% File : parse_trans.erl +%% @author : Ulf Wiger +%% @end +%% Description : +%% +%% Created : 13 Feb 2006 by Ulf Wiger (then Ericsson) %%%------------------------------------------------------------------- -%%% @doc Generic parse transform library for Erlang. -%%% -%%%

...

-%%% -%%% @end +%% @doc Generic parse transform library for Erlang. +%% +%%

...

+%% +%% @end -module(parse_trans). @@ -37,12 +34,12 @@ -export([ inspect/4, - transform/4, + transform/4, depth_first/4, revert/1, - revert_form/1, - format_exception/2, format_exception/3, - return/2 + revert_form/1, + format_exception/2, format_exception/3, + return/2 ]). -export([ @@ -55,13 +52,13 @@ do_inspect/4, do_transform/4, do_depth_first/4, - top/3 + top/3 ]). -export([do_insert_forms/4, - replace_function/4, - replace_function/5, - export_function/3]). + replace_function/4, + replace_function/5, + export_function/3]). -export([ context/2, @@ -85,8 +82,8 @@ ]). -record(context, {module, - function, - arity, + function, + arity, file, options}). @@ -97,7 +94,7 @@ -define(ERROR(R, F, I), begin - Trace = erlang:get_stacktrace(), + Trace = erlang:get_stacktrace(), rpt_error(R, F, I, Trace), throw({error,get_pos(I),{R, Trace}}) end). @@ -110,11 +107,11 @@ -type options() :: [{atom(), any()}]. -type type() :: atom(). -type xform_f_rec() :: fun((type(), form(), #context{}, Acc) -> - {form(), boolean(), Acc} - | {forms(), form(), forms(), boolean(), Acc}). + {form(), boolean(), Acc} + | {forms(), form(), forms(), boolean(), Acc}). -type xform_f_df() :: fun((type(), form(), #context{}, Acc) -> - {form(), Acc} - | {forms(), form(), forms(), Acc}). + {form(), Acc} + | {forms(), form(), forms(), Acc}). -type insp_f() :: fun((type(), form(), #context{}, A) -> {boolean(), A}). @@ -173,17 +170,17 @@ plain_transform1(_, []) -> []; plain_transform1(Fun, [F|Fs]) when is_atom(element(1,F)) -> case Fun(F) of - skip -> - plain_transform1(Fun, Fs); - continue -> - [list_to_tuple(plain_transform1(Fun, tuple_to_list(F))) | - plain_transform1(Fun, Fs)]; - {done, NewF} -> - [NewF | Fs]; - {error, Reason} -> - error(Reason, F, [{form, F}]); - NewF when is_tuple(NewF) -> - [NewF | plain_transform1(Fun, Fs)] + skip -> + plain_transform1(Fun, Fs); + continue -> + [list_to_tuple(plain_transform1(Fun, tuple_to_list(F))) | + plain_transform1(Fun, Fs)]; + {done, NewF} -> + [NewF | Fs]; + {error, Reason} -> + error(Reason, F, [{form, F}]); + NewF when is_tuple(NewF) -> + [NewF | plain_transform1(Fun, Fs)] end; plain_transform1(Fun, [L|Fs]) when is_list(L) -> [plain_transform1(Fun, L) | plain_transform1(Fun, Fs)]; @@ -204,10 +201,10 @@ plain_transform1(_, F) -> integer(). get_pos(I) when is_list(I) -> case proplists:get_value(form, I) of - undefined -> - ?DUMMY_LINE; - Form -> - erl_syntax:get_pos(Form) + undefined -> + ?DUMMY_LINE; + Form -> + erl_syntax:get_pos(Form) end. @@ -243,7 +240,7 @@ get_module(Forms) -> %%% @end %%% -spec get_attribute(atom(), [any()]) -> - 'none' | [erl_syntax:syntaxTree()]. + 'none' | [erl_syntax:syntaxTree()]. %% get_attribute(A, Forms) -> get_attribute(A,Forms,[erl_syntax:atom(undefined)]). get_attribute(A, Forms, Undef) -> @@ -318,17 +315,17 @@ do(Transform, Fun, Acc, Forms, Options) -> Context = initial_context(Forms, Options), File = Context#context.file, try Transform(Fun, Acc, Forms, Context) of - {NewForms, Acc1} when is_list(NewForms) -> - NewForms1 = optionally_renumber(NewForms, Options), - optionally_pretty_print(NewForms1, Options, Context), - {NewForms1, Acc1} + {NewForms, Acc1} when is_list(NewForms) -> + NewForms1 = optionally_renumber(NewForms, Options), + optionally_pretty_print(NewForms1, Options, Context), + {NewForms1, Acc1} catch - error:Reason -> - {error, - [{File, [{?DUMMY_LINE, ?MODULE, - {Reason, erlang:get_stacktrace()}}]}]}; - throw:{error, Ln, What} -> - {error, [{error, {Ln, ?MODULE, What}}]} + error:Reason -> + {error, + [{File, [{?DUMMY_LINE, ?MODULE, + {Reason, erlang:get_stacktrace()}}]}]}; + throw:{error, Ln, What} -> + {error, [{error, {Ln, ?MODULE, What}}]} end. -spec top(function(), forms(), list()) -> @@ -337,18 +334,18 @@ top(F, Forms, Options) -> Context = initial_context(Forms, Options), File = Context#context.file, try F(Forms, Context) of - {error, Reason} -> {error, Reason}; - NewForms when is_list(NewForms) -> - NewForms1 = optionally_renumber(NewForms, Options), + {error, Reason} -> {error, Reason}; + NewForms when is_list(NewForms) -> + NewForms1 = optionally_renumber(NewForms, Options), optionally_pretty_print(NewForms1, Options, Context), - NewForms1 + NewForms1 catch error:Reason -> {error, [{File, [{?DUMMY_LINE, ?MODULE, {Reason, erlang:get_stacktrace()}}]}]}; - throw:{error, Ln, What} -> - {error, [{File, [{Ln, ?MODULE, What}]}], []} + throw:{error, Ln, What} -> + {error, [{File, [{Ln, ?MODULE, What}]}], []} end. replace_function(F, Arity, NewForm, Forms) -> @@ -356,39 +353,39 @@ replace_function(F, Arity, NewForm, Forms) -> replace_function(F, Arity, NewForm, Forms, Opts) -> {NewForms, _} = - do_transform( - fun(function, Form, _Ctxt, Acc) -> - case erl_syntax:revert(Form) of - {function, _, F, Arity, _} = RevForm -> - {[], NewForm, with_original_f(RevForm, Opts), - false, Acc}; - _ -> - {Form, false, Acc} - end; - (_, Form, _Ctxt, Acc) -> - {Form, false, Acc} - end, false, Forms, initial_context(Forms, [])), + do_transform( + fun(function, Form, _Ctxt, Acc) -> + case erl_syntax:revert(Form) of + {function, _, F, Arity, _} = RevForm -> + {[], NewForm, with_original_f(RevForm, Opts), + false, Acc}; + _ -> + {Form, false, Acc} + end; + (_, Form, _Ctxt, Acc) -> + {Form, false, Acc} + end, false, Forms, initial_context(Forms, [])), revert(maybe_export_renamed(NewForms, Arity, Opts)). with_original_f({function,_,_,_,_} = Form, Opts) -> case lists:keyfind(rename_original, 1, Opts) of - {_, NewName} when is_atom(NewName) -> - [setelement(3, Form, NewName)]; - _ -> - [] + {_, NewName} when is_atom(NewName) -> + [setelement(3, Form, NewName)]; + _ -> + [] end. maybe_export_renamed(Forms, Arity, Opts) -> case lists:keyfind(rename_original, 1, Opts) of - {_, NewName} when is_atom(NewName) -> - export_function(NewName, Arity, Forms); - _ -> - Forms + {_, NewName} when is_atom(NewName) -> + export_function(NewName, Arity, Forms); + _ -> + Forms end. export_function(F, Arity, Forms) -> do_insert_forms(above, [{attribute, 1, export, [{F, Arity}]}], Forms, - initial_context(Forms, [])). + initial_context(Forms, [])). -spec do_insert_forms(above | below, forms(), forms(), #context{}) -> forms(). @@ -420,12 +417,12 @@ optionally_pretty_print(Result, Options, Context) -> DoLFs = option_value(pt_log_forms, Options, Result), File = Context#context.file, if DoLFs -> - Out1 = outfile(File, forms), - {ok,Fd} = file:open(Out1, [write]), - try lists:foreach(fun(F) -> io:fwrite(Fd, "~p.~n", [F]) end, Result) - after - ok = file:close(Fd) - end; + Out1 = outfile(File, forms), + {ok,Fd} = file:open(Out1, [write]), + try lists:foreach(fun(F) -> io:fwrite(Fd, "~p.~n", [F]) end, Result) + after + ok = file:close(Fd) + end; true -> ok end, if DoPP -> @@ -437,12 +434,12 @@ optionally_pretty_print(Result, Options, Context) -> optionally_renumber(Result, Options) -> case option_value(pt_renumber, Options, Result) of - true -> - io:fwrite("renumbering...~n", []), - Rev = revert(Result), - renumber_(Rev); - false -> - Result + true -> + io:fwrite("renumbering...~n", []), + Rev = revert(Result), + renumber_(Rev); + false -> + Result end. renumber_(L) when is_list(L) -> @@ -453,15 +450,15 @@ renumber_(L, Acc) when is_list(L) -> lists:mapfoldl(fun renumber_/2, Acc, L); renumber_(T, Prev) when is_tuple(T) -> case is_form(T) of - true -> - New = Prev+1, - T1 = setelement(2, T, New), - {Res, NewAcc} = renumber_(tuple_to_list(T1), New), - {list_to_tuple(Res), NewAcc}; - false -> - L = tuple_to_list(T), - {Res, NewAcc} = renumber_(L, Prev), - {list_to_tuple(Res), NewAcc} + true -> + New = Prev+1, + T1 = setelement(2, T, New), + {Res, NewAcc} = renumber_(tuple_to_list(T1), New), + {list_to_tuple(Res), NewAcc}; + false -> + L = tuple_to_list(T), + {Res, NewAcc} = renumber_(L, Prev), + {list_to_tuple(Res), NewAcc} end; renumber_(X, Prev) -> {X, Prev}. @@ -469,10 +466,10 @@ renumber_(X, Prev) -> is_form(T) when element(1,T)==type -> true; is_form(T) -> try erl_syntax:type(T), - true + true catch - error:_ -> - false + error:_ -> + false end. option_value(Key, Options, Result) -> @@ -606,12 +603,12 @@ revert_form(F) -> revert_form(F, W) -> case erl_syntax:revert(F) of - {attribute,L,A,Tree} when element(1,Tree) == tree -> - {attribute,L,A,erl_syntax:revert(Tree)}; - Result -> - if W -> fix_impl_fun(Result); - true -> Result - end + {attribute,L,A,Tree} when element(1,Tree) == tree -> + {attribute,L,A,erl_syntax:revert(Tree)}; + Result -> + if W -> fix_impl_fun(Result); + true -> Result + end end. fix_impl_fun({'fun',L,{function,{atom,_,Fn},{integer,_,Ay}}}) -> @@ -627,26 +624,26 @@ fix_impl_fun(X) -> needs_revert_workaround() -> case application:get_env(parse_trans,revert_workaround) of - {ok, Bool} when is_boolean(Bool) -> Bool; - _ -> - Res = try lint_reverted() - catch - error:_ -> - true - end, - application:set_env(parse_trans,revert_workaround,Res), - Res + {ok, Bool} when is_boolean(Bool) -> Bool; + _ -> + Res = try lint_reverted() + catch + error:_ -> + true + end, + application:set_env(parse_trans,revert_workaround,Res), + Res end. lint_reverted() -> Ts = [{attribute,1,module,m}, - {attribute,2,export,[{f,0}]}, - erl_syntax:function(erl_syntax:atom(f), - [erl_syntax:clause( - [], - [erl_syntax:implicit_fun( - erl_syntax:atom(f), - erl_syntax:integer(0))])])], + {attribute,2,export,[{f,0}]}, + erl_syntax:function(erl_syntax:atom(f), + [erl_syntax:clause( + [], + [erl_syntax:implicit_fun( + erl_syntax:atom(f), + erl_syntax:integer(0))])])], Rev = erl_syntax:revert_forms(Ts), erl_lint:module(Rev), false. @@ -675,23 +672,23 @@ lint_reverted() -> %%% @end return(Forms, Context) -> JustForms = plain_transform( - fun({error,_}) -> skip; - ({warning,_}) -> skip; - (_) -> continue - end, Forms), + fun({error,_}) -> skip; + ({warning,_}) -> skip; + (_) -> continue + end, Forms), File = case Context of - #context{file = F} -> F; - _ -> "parse_transform" - end, + #context{file = F} -> F; + _ -> "parse_transform" + end, case {find_forms(Forms, error), find_forms(Forms, warning)} of - {[], []} -> - JustForms; - {[], Ws} -> - {warnings, JustForms, [{File, [W || {warning,W} <- Ws]}]}; - {Es, Ws} -> - {error, - [{File, [E || {error,E} <- Es]}], - [{File, [W || {warning,W} <- Ws]}]} + {[], []} -> + JustForms; + {[], Ws} -> + {warnings, JustForms, [{File, [W || {warning,W} <- Ws]}]}; + {Es, Ws} -> + {error, + [{File, [E || {error,E} <- Es]}], + [{File, [W || {warning,W} <- Ws]}]} end. find_forms([H|T], Tag) when element(1, H) == Tag -> @@ -731,18 +728,18 @@ format_exception(Class, Reason) -> %%% @end format_exception(Class, Reason, Lines) -> PrintF = fun(Term, I) -> - io_lib_pretty:print( - Term, I, columns(), ?LINEMAX, ?CHAR_MAX, - record_print_fun()) - end, + io_lib_pretty:print( + Term, I, columns(), ?LINEMAX, ?CHAR_MAX, + record_print_fun()) + end, StackF = fun(_, _, _) -> false end, lines(Lines, lib:format_exception( - 1, Class, Reason, erlang:get_stacktrace(), StackF, PrintF)). + 1, Class, Reason, erlang:get_stacktrace(), StackF, PrintF)). columns() -> case io:columns() of - {ok, N} -> N; - _-> 80 + {ok, N} -> N; + _-> 80 end. lines(infinity, S) -> S; @@ -804,8 +801,8 @@ recurse(Form, Else, F) -> do_transform(F, Acc, Forms, Context) -> Rec = fun do_transform/4, % this function F1 = - fun(Form, Acc0) -> - {Before1, Form1, After1, Recurse, Acc1} = + fun(Form, Acc0) -> + {Before1, Form1, After1, Recurse, Acc1} = this_form_rec(F, Form, Context, Acc0), if Recurse -> {NewForm, NewAcc} = @@ -815,7 +812,7 @@ do_transform(F, Acc, Forms, Context) -> true -> {Before1, Form1, After1, Acc1} end - end, + end, mapfoldl(F1, Acc, Forms). -spec do_depth_first(xform_f_df(), term(), forms(), #context{}) -> @@ -825,7 +822,7 @@ do_depth_first(F, Acc, Forms, Context) -> F1 = fun(Form, Acc0) -> {NewForm, NewAcc} = - enter_subtrees(Form, F, Context, Acc0, Rec), + enter_subtrees(Form, F, Context, Acc0, Rec), this_form_df(F, NewForm, Context, NewAcc) end, mapfoldl(F1, Acc, Forms). @@ -880,13 +877,13 @@ apply_F(F, Type, Form, Context, Acc) -> update_context(Form, Context0) -> case type(Form) of - function -> - {Fun, Arity} = - erl_syntax_lib:analyze_function(Form), - Context0#context{function = Fun, - arity = Arity}; - _ -> - Context0 + function -> + {Fun, Arity} = + erl_syntax_lib:analyze_function(Form), + Context0#context{function = Fun, + arity = Arity}; + _ -> + Context0 end. @@ -897,12 +894,12 @@ update_context(Form, Context0) -> %%% in question. The inserted forms are not transformed afterwards. mapfoldl(F, Accu0, [Hd|Tail]) -> {Before, Res, After, Accu1} = - case F(Hd, Accu0) of - {Be, _, Af, _} = Result when is_list(Be), is_list(Af) -> - Result; - {R1, A1} -> - {[], R1, [], A1} - end, + case F(Hd, Accu0) of + {Be, _, Af, _} = Result when is_list(Be), is_list(Af) -> + Result; + {R1, A1} -> + {[], R1, [], A1} + end, {Rs, Accu2} = mapfoldl(F, Accu1, Tail), {Before ++ [Res| After ++ Rs], Accu2}; mapfoldl(F, Accu, []) when is_function(F, 2) -> {[], Accu}. @@ -910,16 +907,16 @@ mapfoldl(F, Accu, []) when is_function(F, 2) -> {[], Accu}. rpt_error(_Reason, _Fun, _Info, _Trace) -> %% Fmt = lists:flatten( - %% ["*** ERROR in parse_transform function:~n" - %% "*** Reason = ~p~n", + %% ["*** ERROR in parse_transform function:~n" + %% "*** Reason = ~p~n", %% "*** Location: ~p~n", - %% "*** Trace: ~p~n", - %% ["*** ~10w = ~p~n" || _ <- Info]]), + %% "*** Trace: ~p~n", + %% ["*** ~10w = ~p~n" || _ <- Info]]), %% Args = [Reason, Fun, Trace | - %% lists:foldr( - %% fun({K,V}, Acc) -> - %% [K, V | Acc] - %% end, [], Info)], + %% lists:foldr( + %% fun({K,V}, Acc) -> + %% [K, V | Acc] + %% end, [], Info)], %%io:format(Fmt, Args), ok. @@ -927,10 +924,10 @@ rpt_error(_Reason, _Fun, _Info, _Trace) -> iolist(). format_error({E, [{M,F,A}|_]} = Error) -> try lists:flatten(io_lib:fwrite("~p in ~s:~s/~s", [E, atom_to_list(M), - atom_to_list(F), integer_to_list(A)])) + atom_to_list(F), integer_to_list(A)])) catch - error:_ -> - format_error_(Error) + error:_ -> + format_error_(Error) end; format_error(Error) -> format_error_(Error). diff --git a/src/parse_trans_codegen.erl b/src/parse_trans_codegen.erl index d991844..e68a760 100644 --- a/src/parse_trans_codegen.erl +++ b/src/parse_trans_codegen.erl @@ -1,33 +1,29 @@ -%%% The contents of this file are subject to the Erlang Public License, -%%% Version 1.1, (the "License"); you may not use this file except in -%%% compliance with the License. You may obtain a copy of the License at -%%% http://www.erlang.org/EPLICENSE -%%% -%%% Software distributed under the License is distributed on an "AS IS" -%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%%% the License for the specific language governing rights and limitations -%%% under the License. -%%% -%%% The Original Code is exprecs-0.2. -%%% -%%% The Initial Developer of the Original Code is Ericsson AB. -%%% Portions created by Ericsson are Copyright (C), 2006, Ericsson AB. -%%% All Rights Reserved. -%%% -%%% Contributor(s): ______________________________________. +%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% -------------------------------------------------- +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% -------------------------------------------------- +%% File : parse_trans_codegen.erl +%% @author : Ulf Wiger +%% @end +%%------------------------------------------------------------------- -%%%------------------------------------------------------------------- -%%% File : parse_trans_codegen.erl -%%% @author : Ulf Wiger -%%% @end -%%% Description : -%%%------------------------------------------------------------------- - -%%% @doc Parse transform for code generation pseduo functions -%%% -%%%

...

-%%% -%%% @end +%% @doc Parse transform for code generation pseduo functions +%% +%%

...

+%% +%% @end -module(parse_trans_codegen). @@ -150,100 +146,100 @@ parse_transform(Forms, Options) -> Context = parse_trans:initial_context(Forms, Options), {NewForms, _} = - parse_trans:do_depth_first( - fun xform_fun/4, _Acc = Forms, Forms, Context), + parse_trans:do_depth_first( + fun xform_fun/4, _Acc = Forms, Forms, Context), parse_trans:return(parse_trans:revert(NewForms), Context). xform_fun(application, Form, _Ctxt, Acc) -> MFA = erl_syntax_lib:analyze_application(Form), L = erl_syntax:get_pos(Form), case MFA of - {codegen, {gen_module, 3}} -> - [NameF, ExportsF, FunsF] = - erl_syntax:application_arguments(Form), - NewForms = gen_module(NameF, ExportsF, FunsF, L, Acc), - {NewForms, Acc}; - {codegen, {gen_function, 2}} -> - [NameF, FunF] = - erl_syntax:application_arguments(Form), - NewForm = gen_function(NameF, FunF, L, L, Acc), - {NewForm, Acc}; - {codegen, {gen_function, 3}} -> - [NameF, FunF, LineF] = - erl_syntax:application_arguments(Form), - NewForm = gen_function( - NameF, FunF, L, erl_syntax:integer_value(LineF), Acc), - {NewForm, Acc}; - {codegen, {gen_function_alt, 3}} -> - [NameF, FunF, AltF] = - erl_syntax:application_arguments(Form), - NewForm = gen_function_alt(NameF, FunF, AltF, L, L, Acc), - {NewForm, Acc}; - {codegen, {gen_functions, 1}} -> - [List] = erl_syntax:application_arguments(Form), - Elems = erl_syntax:list_elements(List), - NewForms = lists:map( - fun(E) -> - [NameF, FunF] = erl_syntax:tuple_elements(E), - gen_function(NameF, FunF, L, L, Acc) - end, Elems), - {erl_syntax:list(NewForms), Acc}; - {codegen, {exprs, 1}} -> - [FunF] = erl_syntax:application_arguments(Form), - [Clause] = erl_syntax:fun_expr_clauses(FunF), - [{clause,_,_,_,Body}] = parse_trans:revert([Clause]), - NewForm = substitute(erl_parse:abstract(Body)), - {NewForm, Acc}; - _ -> - {Form, Acc} + {codegen, {gen_module, 3}} -> + [NameF, ExportsF, FunsF] = + erl_syntax:application_arguments(Form), + NewForms = gen_module(NameF, ExportsF, FunsF, L, Acc), + {NewForms, Acc}; + {codegen, {gen_function, 2}} -> + [NameF, FunF] = + erl_syntax:application_arguments(Form), + NewForm = gen_function(NameF, FunF, L, L, Acc), + {NewForm, Acc}; + {codegen, {gen_function, 3}} -> + [NameF, FunF, LineF] = + erl_syntax:application_arguments(Form), + NewForm = gen_function( + NameF, FunF, L, erl_syntax:integer_value(LineF), Acc), + {NewForm, Acc}; + {codegen, {gen_function_alt, 3}} -> + [NameF, FunF, AltF] = + erl_syntax:application_arguments(Form), + NewForm = gen_function_alt(NameF, FunF, AltF, L, L, Acc), + {NewForm, Acc}; + {codegen, {gen_functions, 1}} -> + [List] = erl_syntax:application_arguments(Form), + Elems = erl_syntax:list_elements(List), + NewForms = lists:map( + fun(E) -> + [NameF, FunF] = erl_syntax:tuple_elements(E), + gen_function(NameF, FunF, L, L, Acc) + end, Elems), + {erl_syntax:list(NewForms), Acc}; + {codegen, {exprs, 1}} -> + [FunF] = erl_syntax:application_arguments(Form), + [Clause] = erl_syntax:fun_expr_clauses(FunF), + [{clause,_,_,_,Body}] = parse_trans:revert([Clause]), + NewForm = substitute(erl_parse:abstract(Body)), + {NewForm, Acc}; + _ -> + {Form, Acc} end; xform_fun(_, Form, _Ctxt, Acc) -> {Form, Acc}. gen_module(NameF, ExportsF, FunsF, L, Acc) -> case erl_syntax:type(FunsF) of - list -> - try gen_module_(NameF, ExportsF, FunsF, L, Acc) - catch - error:E -> - ErrStr = parse_trans:format_exception(error, E), - {error, {L, ?MODULE, ErrStr}} - end; - _ -> - ErrStr = parse_trans:format_exception( - error, "Argument must be a list"), - {error, {L, ?MODULE, ErrStr}} + list -> + try gen_module_(NameF, ExportsF, FunsF, L, Acc) + catch + error:E -> + ErrStr = parse_trans:format_exception(error, E), + {error, {L, ?MODULE, ErrStr}} + end; + _ -> + ErrStr = parse_trans:format_exception( + error, "Argument must be a list"), + {error, {L, ?MODULE, ErrStr}} end. gen_module_(NameF, ExportsF, FunsF, L0, Acc) -> P = erl_syntax:get_pos(NameF), ModF = case parse_trans:revert_form(NameF) of - {atom,_,_} = Am -> Am; - {tuple,_,[{atom,_,'$var'}, - {var,_,V}]} -> - {var,P,V} - end, + {atom,_,_} = Am -> Am; + {tuple,_,[{atom,_,'$var'}, + {var,_,V}]} -> + {var,P,V} + end, cons( {cons,P, {tuple,P, - [{atom,P,attribute}, - {integer,P,1}, - {atom,P,module}, - ModF]}, + [{atom,P,attribute}, + {integer,P,1}, + {atom,P,module}, + ModF]}, substitute( - abstract( - [{attribute,P,export, - lists:map( - fun(TupleF) -> - [F,A] = erl_syntax:tuple_elements(TupleF), - {erl_syntax:atom_value(F), erl_syntax:integer_value(A)} - end, erl_syntax:list_elements(ExportsF))}]))}, + abstract( + [{attribute,P,export, + lists:map( + fun(TupleF) -> + [F,A] = erl_syntax:tuple_elements(TupleF), + {erl_syntax:atom_value(F), erl_syntax:integer_value(A)} + end, erl_syntax:list_elements(ExportsF))}]))}, lists:map( - fun(FTupleF) -> - Pos = erl_syntax:get_pos(FTupleF), - [FName, FFunF] = erl_syntax:tuple_elements(FTupleF), - gen_function(FName, FFunF, L0, Pos, Acc) - end, erl_syntax:list_elements(FunsF))). + fun(FTupleF) -> + Pos = erl_syntax:get_pos(FTupleF), + [FName, FFunF] = erl_syntax:tuple_elements(FTupleF), + gen_function(FName, FFunF, L0, Pos, Acc) + end, erl_syntax:list_elements(FunsF))). cons({cons,L,H,T}, L2) -> {cons,L,H,cons(T, L2)}; @@ -258,80 +254,80 @@ cons({nil,L}, []) -> gen_function(NameF, FunF, L0, L, Acc) -> try gen_function_(NameF, FunF, [], L, Acc) catch - error:E -> - ErrStr = parse_trans:format_exception(error, E), - {error, {L0, ?MODULE, ErrStr}} + error:E -> + ErrStr = parse_trans:format_exception(error, E), + {error, {L0, ?MODULE, ErrStr}} end. gen_function_alt(NameF, FunF, AltF, L0, L, Acc) -> try gen_function_(NameF, FunF, AltF, L, Acc) catch - error:E -> - ErrStr = parse_trans:format_exception(error, E), - {error, {L0, ?MODULE, ErrStr}} + error:E -> + ErrStr = parse_trans:format_exception(error, E), + {error, {L0, ?MODULE, ErrStr}} end. gen_function_(NameF, FunF, AltF, L, Acc) -> case erl_syntax:type(FunF) of - T when T==implicit_fun; T==fun_expr -> - {Arity, Clauses} = gen_function_clauses(T, NameF, FunF, L, Acc), - {tuple, 1, [{atom, 1, function}, - {integer, 1, L}, - NameF, - {integer, 1, Arity}, - substitute(abstract(Clauses))]}; - list_comp -> - %% Extract the fun from the LC - [Template] = parse_trans:revert( - [erl_syntax:list_comp_template(FunF)]), - %% Process fun in the normal fashion (as above) - {Arity, Clauses} = gen_function_clauses(erl_syntax:type(Template), - NameF, Template, L, Acc), - Body = erl_syntax:list_comp_body(FunF), - %% Collect all variables from the LC generator(s) - %% We want to produce an abstract representation of something like: - %% {function,1,Name,Arity, - %% lists:flatten( - %% [(fun(V1,V2,...) -> - %% ... - %% end)(__V1,__V2,...) || {__V1,__V2,...} <- L])} - %% where the __Vn vars are our renamed versions of the LC generator - %% vars. This allows us to instantiate the clauses at run-time. - Vars = lists:flatten( - [sets:to_list(erl_syntax_lib:variables( - erl_syntax:generator_pattern(G))) - || G <- Body]), - Vars1 = [list_to_atom("__" ++ atom_to_list(V)) || V <- Vars], - VarMap = lists:zip(Vars, Vars1), - Body1 = - [erl_syntax:generator( - rename_vars(VarMap, gen_pattern(G)), - gen_body(G)) || G <- Body], - [RevLC] = parse_trans:revert( - [erl_syntax:list_comp( - {call, 1, - {'fun',1, - {clauses, - [{clause,1,[{var,1,V} || V <- Vars],[], - [substitute( - abstract(Clauses))] - }]} - }, [{var,1,V} || V <- Vars1]}, Body1)]), - AltC = case AltF of - [] -> {nil,1}; - _ -> - {Arity, AltC1} = gen_function_clauses( - erl_syntax:type(AltF), - NameF, AltF, L, Acc), - substitute(abstract(AltC1)) - end, - {tuple,1,[{atom,1,function}, - {integer, 1, L}, - NameF, - {integer, 1, Arity}, - {call, 1, {remote, 1, {atom, 1, lists}, - {atom,1,flatten}}, - [{op, 1, '++', RevLC, AltC}]}]} + T when T==implicit_fun; T==fun_expr -> + {Arity, Clauses} = gen_function_clauses(T, NameF, FunF, L, Acc), + {tuple, 1, [{atom, 1, function}, + {integer, 1, L}, + NameF, + {integer, 1, Arity}, + substitute(abstract(Clauses))]}; + list_comp -> + %% Extract the fun from the LC + [Template] = parse_trans:revert( + [erl_syntax:list_comp_template(FunF)]), + %% Process fun in the normal fashion (as above) + {Arity, Clauses} = gen_function_clauses(erl_syntax:type(Template), + NameF, Template, L, Acc), + Body = erl_syntax:list_comp_body(FunF), + %% Collect all variables from the LC generator(s) + %% We want to produce an abstract representation of something like: + %% {function,1,Name,Arity, + %% lists:flatten( + %% [(fun(V1,V2,...) -> + %% ... + %% end)(__V1,__V2,...) || {__V1,__V2,...} <- L])} + %% where the __Vn vars are our renamed versions of the LC generator + %% vars. This allows us to instantiate the clauses at run-time. + Vars = lists:flatten( + [sets:to_list(erl_syntax_lib:variables( + erl_syntax:generator_pattern(G))) + || G <- Body]), + Vars1 = [list_to_atom("__" ++ atom_to_list(V)) || V <- Vars], + VarMap = lists:zip(Vars, Vars1), + Body1 = + [erl_syntax:generator( + rename_vars(VarMap, gen_pattern(G)), + gen_body(G)) || G <- Body], + [RevLC] = parse_trans:revert( + [erl_syntax:list_comp( + {call, 1, + {'fun',1, + {clauses, + [{clause,1,[{var,1,V} || V <- Vars],[], + [substitute( + abstract(Clauses))] + }]} + }, [{var,1,V} || V <- Vars1]}, Body1)]), + AltC = case AltF of + [] -> {nil,1}; + _ -> + {Arity, AltC1} = gen_function_clauses( + erl_syntax:type(AltF), + NameF, AltF, L, Acc), + substitute(abstract(AltC1)) + end, + {tuple,1,[{atom,1,function}, + {integer, 1, L}, + NameF, + {integer, 1, Arity}, + {call, 1, {remote, 1, {atom, 1, lists}, + {atom,1,flatten}}, + [{op, 1, '++', RevLC, AltC}]}]} end. gen_pattern(G) -> @@ -343,21 +339,21 @@ gen_body(G) -> rename_vars(Vars, Tree) -> erl_syntax_lib:map( fun(T) -> - case erl_syntax:type(T) of - variable -> - V = erl_syntax:variable_name(T), - {_,V1} = lists:keyfind(V,1,Vars), - erl_syntax:variable(V1); - _ -> - T - end + case erl_syntax:type(T) of + variable -> + V = erl_syntax:variable_name(T), + {_,V1} = lists:keyfind(V,1,Vars), + erl_syntax:variable(V1); + _ -> + T + end end, Tree). gen_function_clauses(implicit_fun, _NameF, FunF, _L, Acc) -> AQ = erl_syntax:implicit_fun_name(FunF), Name = erl_syntax:atom_value(erl_syntax:arity_qualifier_body(AQ)), Arity = erl_syntax:integer_value( - erl_syntax:arity_qualifier_argument(AQ)), + erl_syntax:arity_qualifier_argument(AQ)), NewForm = find_function(Name, Arity, Acc), ClauseForms = erl_syntax:function_clauses(NewForm), {Arity, ClauseForms}; @@ -368,32 +364,32 @@ gen_function_clauses(fun_expr, _NameF, FunF, _L, _Acc) -> find_function(Name, Arity, Forms) -> [Form] = [F || {function,_,N,A,_} = F <- Forms, - N == Name, - A == Arity], + N == Name, + A == Arity], Form. abstract(ClauseForms) -> erl_parse:abstract(parse_trans:revert(ClauseForms)). substitute({tuple,L0, - [{atom,_,tuple}, - {integer,_,L}, - {cons,_, - {tuple,_,[{atom,_,atom},{integer,_,_},{atom,_,'$var'}]}, - {cons,_, - {tuple,_,[{atom,_,var},{integer,_,_},{atom,_,V}]}, - {nil,_}}}]}) -> + [{atom,_,tuple}, + {integer,_,L}, + {cons,_, + {tuple,_,[{atom,_,atom},{integer,_,_},{atom,_,'$var'}]}, + {cons,_, + {tuple,_,[{atom,_,var},{integer,_,_},{atom,_,V}]}, + {nil,_}}}]}) -> {call, L0, {remote,L0,{atom,L0,erl_parse}, - {atom,L0,abstract}}, + {atom,L0,abstract}}, [{var, L0, V}, {integer, L0, L}]}; substitute({tuple,L0, - [{atom,_,tuple}, - {integer,_,_}, - {cons,_, - {tuple,_,[{atom,_,atom},{integer,_,_},{atom,_,'$form'}]}, - {cons,_, - {tuple,_,[{atom,_,var},{integer,_,_},{atom,_,F}]}, - {nil,_}}}]}) -> + [{atom,_,tuple}, + {integer,_,_}, + {cons,_, + {tuple,_,[{atom,_,atom},{integer,_,_},{atom,_,'$form'}]}, + {cons,_, + {tuple,_,[{atom,_,var},{integer,_,_},{atom,_,F}]}, + {nil,_}}}]}) -> {var, L0, F}; substitute([]) -> []; @@ -407,17 +403,17 @@ substitute(X) -> get_arity(Clauses) -> Ays = [length(erl_syntax:clause_patterns(C)) || C <- Clauses], case lists:usort(Ays) of - [Ay] -> - Ay; - Other -> - erlang:error(ambiguous, Other) + [Ay] -> + Ay; + Other -> + erlang:error(ambiguous, Other) end. format_error(E) -> case io_lib:deep_char_list(E) of - true -> - E; - _ -> - io_lib:write(E) + true -> + E; + _ -> + io_lib:write(E) end. diff --git a/src/parse_trans_mod.erl b/src/parse_trans_mod.erl index ff13f76..1eb8c64 100644 --- a/src/parse_trans_mod.erl +++ b/src/parse_trans_mod.erl @@ -41,14 +41,14 @@ transform_module(Mod, PT, Options) -> Forms = abstract_code(File), Context = parse_trans:initial_context(Forms, Options), PTMods = if is_atom(PT) -> [PT]; - is_function(PT, 2) -> [PT]; - is_list(PT) -> PT - end, + is_function(PT, 2) -> [PT]; + is_list(PT) -> PT + end, Transformed = lists:foldl(fun(PTx, Fs) when is_function(PTx, 2) -> - PTx(Fs, Options); - (PTMod, Fs) -> - PTMod:parse_transform(Fs, Options) - end, Forms, PTMods), + PTx(Fs, Options); + (PTMod, Fs) -> + PTMod:parse_transform(Fs, Options) + end, Forms, PTMods), parse_trans:optionally_pretty_print(Transformed, Options, Context), compile_and_load_forms(Transformed, get_compile_options(Options)). diff --git a/src/parse_trans_pp.erl b/src/parse_trans_pp.erl index ccc47e7..7478ad2 100644 --- a/src/parse_trans_pp.erl +++ b/src/parse_trans_pp.erl @@ -1,45 +1,42 @@ -%%% The contents of this file are subject to the Erlang Public License, -%%% Version 1.1, (the "License"); you may not use this file except in -%%% compliance with the License. You may obtain a copy of the License at -%%% http://www.erlang.org/EPLICENSE -%%% -%%% Software distributed under the License is distributed on an "AS IS" -%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%%% the License for the specific language governing rights and limitations -%%% under the License. -%%% -%%% The Original Code is exprecs-0.2. -%%% -%%% The Initial Developer of the Original Code is Ericsson AB. -%%% Portions created by Ericsson are Copyright (C), 2006, Ericsson AB. -%%% All Rights Reserved. -%%% -%%% Contributor(s): ______________________________________. +%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% -------------------------------------------------- +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% -------------------------------------------------- +%% File : parse_trans_pp.erl +%% @author : Ulf Wiger +%% @end +%% Description : +%% +%% Created : 3 Aug 2010 by Ulf Wiger +%% -------------------------------------------------- -%%%------------------------------------------------------------------- -%%% File : parse_trans_pp.erl -%%% @author : Ulf Wiger -%%% @end -%%% Description : -%%% -%%% Created : 3 Aug 2010 by Ulf Wiger -%%%------------------------------------------------------------------- - -%%% @doc Generic parse transform library for Erlang. -%%% -%%% This module contains some useful utility functions for inspecting -%%% the results of parse transforms or code generation. -%%% The function `main/1' is called from escript, and can be used to -%%% pretty-print debug info in a .beam file from a Linux shell. -%%% -%%% Using e.g. the following bash alias: -%%%
-%%% alias pp='escript $PARSE_TRANS_ROOT/ebin/parse_trans_pp.beam'
+%% @doc Generic parse transform library for Erlang.
+%%
+%% This module contains some useful utility functions for inspecting
+%% the results of parse transforms or code generation.
+%% The function `main/1' is called from escript, and can be used to
+%% pretty-print debug info in a .beam file from a Linux shell.
+%%
+%% Using e.g. the following bash alias:
+%% 
+%% alias pp='escript $PARSE_TRANS_ROOT/ebin/parse_trans_pp.beam'
 %%% 
-%%% a file could be pretty-printed using the following command: -%%% -%%% `$ pp ex_codegen.beam | less' -%%% @end +%% a file could be pretty-printed using the following command: +%% +%% `$ pp ex_codegen.beam | less' +%% @end -module(parse_trans_pp). @@ -103,13 +100,13 @@ pp_beam(F, Out) -> pp_beam_to_str(F) -> case beam_lib:chunks(F, [abstract_code]) of {ok, {_, [{abstract_code,{_,AC0}}]}} -> - AC = epp:restore_typed_record_fields(AC0), + AC = epp:restore_typed_record_fields(AC0), {ok, lists:flatten( %% io_lib:fwrite("~s~n", [erl_prettypr:format( %% erl_syntax:form_list(AC))]) - io_lib:fwrite("~s~n", [lists:flatten( - [erl_pp:form(Form) || - Form <- AC])]) + io_lib:fwrite("~s~n", [lists:flatten( + [erl_pp:form(Form) || + Form <- AC])]) )}; Other -> {error, Other}