mirror of
https://github.com/valitydev/parse_trans.git
synced 2024-11-06 00:25:16 +00:00
added pos functions to exprecs
This commit is contained in:
parent
275690db26
commit
6b9510fa74
2
Makefile
2
Makefile
@ -73,7 +73,7 @@ util/my_plt.plt: util/make_plt.beam
|
||||
clean:
|
||||
@echo Cleaning
|
||||
@rm -f ebin/*.{beam,app} test/*.beam doc/*.{html,css,png} doc/edoc-info
|
||||
@rm -r cover_report
|
||||
# @rm -r cover_report
|
||||
@rm -f util/*.beam
|
||||
|
||||
release: clean all test dialyzer
|
||||
|
@ -25,6 +25,6 @@
|
||||
<hr>
|
||||
|
||||
<div class="navbar"><a name="#navbar_bottom"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
|
||||
<p><i>Generated by EDoc, Jan 10 2010, 19:56:29.</i></p>
|
||||
<p><i>Generated by EDoc, Feb 25 2010, 07:32:25.</i></p>
|
||||
</body>
|
||||
</html>
|
||||
|
166
doc/exprecs.html
166
doc/exprecs.html
@ -14,84 +14,102 @@
|
||||
<p><b>Authors:</b> : Ulf Wiger (<a href="mailto:ulf.wiger@ericsson.com"><tt>ulf.wiger@ericsson.com</tt></a>).</p>
|
||||
|
||||
<h2><a name="description">Description</a></h2>Parse transform for generating record access functions.
|
||||
<p>This parse transform can be used to reduce compile-time
|
||||
dependencies in large systems.</p>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<p>Whenever record definitions need to be exported from a module,
|
||||
inserting a compiler attribute,
|
||||
<code>export_records([RecName|...])</code> causes this transform
|
||||
to lay out access functions for the exported records:</p>
|
||||
|
||||
<pre>
|
||||
-module(foo)
|
||||
-compile({parse_transform, dia_exprecs}).
|
||||
|
||||
-record(a, {a, b, c}).
|
||||
-export_records([a]).
|
||||
-export(['#info-'/1, '#info-'/2,
|
||||
'#get-'/2, '#set-'/2,
|
||||
'#new-a'/0, '#new-a'/1,
|
||||
'#get-a'/2, '#set-a'/2,
|
||||
'#info-a'/1]).
|
||||
|
||||
'#info-'(Rec) ->
|
||||
'#info-'(Rec, fields).
|
||||
|
||||
'#info-'(a, Info) ->
|
||||
'#info-a'(Info).
|
||||
|
||||
'#new-a'() -> #a{}.
|
||||
'#new-a'(Vals) -> '#set-a'(Vals, #a{}).
|
||||
|
||||
'#get-'(Attrs, Rec) when is_record(Rec, a) ->
|
||||
'#get-a'(Attrs, Rec).
|
||||
|
||||
'#get-a'(Attrs, R) when is_list(Attrs) ->
|
||||
['#get-a'(A, R) || A <- Attrs];
|
||||
'#get-a'(a, R) -> R#a.a;
|
||||
'#get-a'(b, R) -> R#a.b;
|
||||
'#get-a'(c, R) -> R#a.c.
|
||||
|
||||
'#set-'(Vals, Rec) when is_record(Rec, a) ->
|
||||
'#set-a'(Vals, Rec).
|
||||
|
||||
'#set-a'(Vals, Rec) ->
|
||||
F = fun ([], R, _F1) -> R;
|
||||
([{a, V} | T], R, F1) -> F1(T, R#a{a = V}, F1);
|
||||
([{b, V} | T], R, F1) -> F1(T, R#a{b = V}, F1);
|
||||
([{c, V} | T], R, F1) -> F1(T, R#a{c = V}, F1)
|
||||
end,
|
||||
F(Vals, Rec, F).
|
||||
|
||||
'#info-a'(fields) -> record_info(fields, a);
|
||||
'#info-a'(size) -> record_info(size, a).
|
||||
</pre>
|
||||
<p>This parse transform can be used to reduce compile-time
|
||||
dependencies in large systems.</p>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<p>Whenever record definitions need to be exported from a module,
|
||||
inserting a compiler attribute,
|
||||
<code>export_records([RecName|...])</code> causes this transform
|
||||
to lay out access functions for the exported records:</p>
|
||||
|
||||
<pre>
|
||||
-module(test_exprecs).
|
||||
|
||||
-record(r, {a, b, c}).
|
||||
-export_records([r]).
|
||||
|
||||
-export(['#new-'/1, '#info-'/1, '#info-'/2, '#pos-'/2,
|
||||
'#is_record-'/2, '#get-'/2, '#set-'/2, '#fromlist-'/2,
|
||||
'#new-r'/0, '#new-r'/1, '#get-r'/2, '#set-r'/2,
|
||||
'#pos-r'/1, '#fromlist-r'/2, '#info-r'/1]).
|
||||
|
||||
'#new-'(r) -> '#new-r'().
|
||||
|
||||
'#info-'(RecName) -> '#info-'(RecName, fields).
|
||||
|
||||
'#info-'(r, Info) -> '#info-r'(Info).
|
||||
|
||||
'#pos-'(r, Attr) -> '#pos-r'(Attr).
|
||||
|
||||
'#is_record-'(r, Rec)
|
||||
when tuple_size(Rec) == 3, element(1, Rec) == r ->
|
||||
true;
|
||||
'#is_record-'(_, _) -> false.
|
||||
|
||||
'#get-'(Attrs, Rec) when is_record(Rec, r) ->
|
||||
'#get-r'(Attrs, Rec).
|
||||
|
||||
'#set-'(Vals, Rec) when is_record(Rec, r) ->
|
||||
'#set-r'(Vals, Rec).
|
||||
|
||||
'#fromlist-'(Vals, Rec) when is_record(Rec, r) ->
|
||||
'#fromlist-r'(Vals, Rec).
|
||||
|
||||
'#new-r'() -> #r{}.
|
||||
|
||||
'#new-r'(Vals) -> '#set-r'(Vals, #r{}).
|
||||
|
||||
'#get-r'(Attrs, R) when is_list(Attrs) ->
|
||||
['#get-r'(A, R) || A <- Attrs];
|
||||
'#get-r'(a, R) -> R#r.a;
|
||||
'#get-r'(b, R) -> R#r.b;
|
||||
'#get-r'(c, R) -> R#r.c;
|
||||
'#get-r'(Attr, R) ->
|
||||
erlang:error(bad_record_op, ['#get-r', Attr, R]).
|
||||
|
||||
'#set-r'(Vals, Rec) ->
|
||||
F = fun ([], R, _F1) -> R;
|
||||
([{a, V} | T], R, F1) -> F1(T, R#r{a = V}, F1);
|
||||
([{b, V} | T], R, F1) -> F1(T, R#r{b = V}, F1);
|
||||
([{c, V} | T], R, F1) -> F1(T, R#r{c = V}, F1);
|
||||
(Vs, R, _) ->
|
||||
erlang:error(bad_record_op, ['#set-r', Vs, R])
|
||||
end,
|
||||
F(Vals, Rec, F).
|
||||
|
||||
'#fromlist-r'(Vals, Rec) ->
|
||||
AttrNames = [{a, 2}, {b, 3}, {c, 4}],
|
||||
F = fun ([], R, _F1) -> R;
|
||||
([{H, Pos} | T], R, F1) ->
|
||||
case lists:keyfind(H, 1, Vals) of
|
||||
false -> F1(T, R, F1);
|
||||
{_, Val} -> F1(T, setelement(Pos, R, Val), F1)
|
||||
end
|
||||
end,
|
||||
F(AttrNames, Rec, F).
|
||||
|
||||
'#pos-r'(a) -> 2;
|
||||
'#pos-r'(b) -> 3;
|
||||
'#pos-r'(c) -> 4;
|
||||
'#pos-r'(_) -> 0.
|
||||
|
||||
'#info-r'(fields) -> record_info(fields, r);
|
||||
'#info-r'(size) -> record_info(size, r).
|
||||
</pre>
|
||||
<h2><a name="index">Function Index</a></h2>
|
||||
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#context-2">context/2</a></td><td></td></tr>
|
||||
<tr><td valign="top"><a href="#format_error-1">format_error/1</a></td><td></td></tr>
|
||||
<tr><td valign="top"><a href="#parse_transform-2">parse_transform/2</a></td><td></td></tr>
|
||||
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#parse_transform-2">parse_transform/2</a></td><td></td></tr>
|
||||
</table>
|
||||
|
||||
<h2><a name="functions">Function Details</a></h2>
|
||||
|
||||
<h3 class="function"><a name="context-2">context/2</a></h3>
|
||||
<div class="spec">
|
||||
<p><tt>context(X1, Context) -> any()</tt></p>
|
||||
</div>
|
||||
|
||||
<h3 class="function"><a name="format_error-1">format_error/1</a></h3>
|
||||
<div class="spec">
|
||||
<p><tt>format_error(X1) -> any()</tt></p>
|
||||
</div>
|
||||
|
||||
<h3 class="function"><a name="parse_transform-2">parse_transform/2</a></h3>
|
||||
<div class="spec">
|
||||
<p><tt>parse_transform(Forms, Options) -> any()</tt></p>
|
||||
@ -99,6 +117,6 @@
|
||||
<hr>
|
||||
|
||||
<div class="navbar"><a name="#navbar_bottom"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
|
||||
<p><i>Generated by EDoc, Jan 10 2010, 19:56:29.</i></p>
|
||||
<p><i>Generated by EDoc, Feb 25 2010, 07:32:25.</i></p>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -63,6 +63,6 @@ Forms = [{attribute,1,file,{"./ex1.erl",1}},
|
||||
|
||||
<hr>
|
||||
<div class="navbar"><a name="#navbar_bottom"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
|
||||
<p><i>Generated by EDoc, Jan 10 2010, 19:56:29.</i></p>
|
||||
<p><i>Generated by EDoc, Feb 25 2010, 07:32:25.</i></p>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -27,7 +27,8 @@
|
||||
<tr><td valign="top"><a href="#do_transform-4">do_transform/4</a></td><td></td></tr>
|
||||
<tr><td valign="top"><a href="#error-3">error/3</a></td><td>.</td></tr>
|
||||
<tr><td valign="top"><a href="#format_error-1">format_error/1</a></td><td></td></tr>
|
||||
<tr><td valign="top"><a href="#function_exists-3">function_exists/3</a></td><td></td></tr>
|
||||
<tr><td valign="top"><a href="#function_exists-3">function_exists/3</a></td><td>
|
||||
Checks whether the given function is defined in Forms.</td></tr>
|
||||
<tr><td valign="top"><a href="#get_attribute-2">get_attribute/2</a></td><td>
|
||||
Returns the value of the first occurence of attribute A.</td></tr>
|
||||
<tr><td valign="top"><a href="#get_file-1">get_file/1</a></td><td>
|
||||
@ -35,13 +36,20 @@
|
||||
<tr><td valign="top"><a href="#get_module-1">get_module/1</a></td><td>
|
||||
Returns the name of the module being compiled.</td></tr>
|
||||
<tr><td valign="top"><a href="#get_orig_syntax_tree-1">get_orig_syntax_tree/1</a></td><td>.</td></tr>
|
||||
<tr><td valign="top"><a href="#get_pos-1">get_pos/1</a></td><td></td></tr>
|
||||
<tr><td valign="top"><a href="#get_pos-1">get_pos/1</a></td><td>
|
||||
Tries to retrieve the line number from an erl_syntax form.</td></tr>
|
||||
<tr><td valign="top"><a href="#initial_context-2">initial_context/2</a></td><td>
|
||||
Initializes a context record.</td></tr>
|
||||
<tr><td valign="top"><a href="#inspect-4">inspect/4</a></td><td>
|
||||
Equvalent to do_inspect(Fun,Acc,Forms,initial_context(Forms,Options)).</td></tr>
|
||||
<tr><td valign="top"><a href="#optionally_pretty_print-3">optionally_pretty_print/3</a></td><td></td></tr>
|
||||
<tr><td valign="top"><a href="#pp_src-2">pp_src/2</a></td><td></td></tr>
|
||||
<tr><td valign="top"><a href="#pp_beam-1">pp_beam/1</a></td><td>
|
||||
Reads debug_info from the beam file Beam and returns a string containing
|
||||
the pretty-printed corresponding erlang source code.</td></tr>
|
||||
<tr><td valign="top"><a href="#pp_beam-2">pp_beam/2</a></td><td>
|
||||
Reads debug_info from the beam file Beam and pretty-prints it as
|
||||
Erlang source code, storing it in the file Out.</td></tr>
|
||||
<tr><td valign="top"><a href="#pp_src-2">pp_src/2</a></td><td>Pretty-prints the erlang source code corresponding to Forms into Out.</td></tr>
|
||||
<tr><td valign="top"><a href="#revert-1">revert/1</a></td><td>Reverts back from Syntax Tools format to Erlang forms.</td></tr>
|
||||
<tr><td valign="top"><a href="#top-3">top/3</a></td><td></td></tr>
|
||||
<tr><td valign="top"><a href="#transform-4">transform/4</a></td><td>
|
||||
@ -98,8 +106,9 @@
|
||||
|
||||
<h3 class="function"><a name="function_exists-3">function_exists/3</a></h3>
|
||||
<div class="spec">
|
||||
<p><tt>function_exists(Fname, Arity, Forms) -> any()</tt></p>
|
||||
</div>
|
||||
<p><tt>function_exists(Fname::atom(), Arity::integer(), Forms) -> <a href="#type-boolean">boolean()</a></tt></p>
|
||||
</div><p>
|
||||
Checks whether the given function is defined in Forms.</p>
|
||||
|
||||
<h3 class="function"><a name="get_attribute-2">get_attribute/2</a></h3>
|
||||
<div class="spec">
|
||||
@ -132,8 +141,10 @@
|
||||
|
||||
<h3 class="function"><a name="get_pos-1">get_pos/1</a></h3>
|
||||
<div class="spec">
|
||||
<p><tt>get_pos(I) -> any()</tt></p>
|
||||
</div>
|
||||
<p><tt>get_pos(I::list()) -> integer()</tt></p>
|
||||
</div><p>
|
||||
Tries to retrieve the line number from an erl_syntax form. Returns a
|
||||
(very high) dummy number if not successful.</p>
|
||||
|
||||
<h3 class="function"><a name="initial_context-2">initial_context/2</a></h3>
|
||||
<div class="spec">
|
||||
@ -157,10 +168,25 @@
|
||||
<p><tt>optionally_pretty_print(Result, Options, Context) -> any()</tt></p>
|
||||
</div>
|
||||
|
||||
<h3 class="function"><a name="pp_beam-1">pp_beam/1</a></h3>
|
||||
<div class="spec">
|
||||
<p><tt>pp_beam(Beam::<a href="#type-filename">filename()</a>) -> string() | {error, Reason}</tt></p>
|
||||
</div><p>
|
||||
Reads debug_info from the beam file Beam and returns a string containing
|
||||
the pretty-printed corresponding erlang source code.</p>
|
||||
|
||||
<h3 class="function"><a name="pp_beam-2">pp_beam/2</a></h3>
|
||||
<div class="spec">
|
||||
<p><tt>pp_beam(Beam::<a href="#type-filename">filename()</a>, Out::<a href="#type-filename">filename()</a>) -> ok | {error, Reason}</tt></p>
|
||||
</div><p>
|
||||
Reads debug_info from the beam file Beam and pretty-prints it as
|
||||
Erlang source code, storing it in the file Out.</p>
|
||||
|
||||
<h3 class="function"><a name="pp_src-2">pp_src/2</a></h3>
|
||||
<div class="spec">
|
||||
<p><tt>pp_src(Res, F) -> any()</tt></p>
|
||||
</div>
|
||||
<p><tt>pp_src(Res::Forms, Out::<a href="#type-filename">filename()</a>) -> ok</tt></p>
|
||||
</div><p>Pretty-prints the erlang source code corresponding to Forms into Out
|
||||
</p>
|
||||
|
||||
<h3 class="function"><a name="revert-1">revert/1</a></h3>
|
||||
<div class="spec">
|
||||
@ -186,6 +212,6 @@
|
||||
<hr>
|
||||
|
||||
<div class="navbar"><a name="#navbar_bottom"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
|
||||
<p><i>Generated by EDoc, Jan 10 2010, 19:56:29.</i></p>
|
||||
<p><i>Generated by EDoc, Feb 25 2010, 07:32:25.</i></p>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -4,7 +4,7 @@ BEAMS := $(patsubst %.erl,%.beam,$(SOURCES))
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(BEAMS)
|
||||
all: $(BEAMS)
|
||||
|
||||
test: $(APPLICATION) $(TEST_BEAMS) ../util/run_test.beam
|
||||
@echo Running tests
|
||||
@ -16,9 +16,12 @@ test: $(APPLICATION) $(TEST_BEAMS) ../util/run_test.beam
|
||||
|
||||
test.beam: test_pt.beam
|
||||
|
||||
test_exprecs.erl: ../ebin/exprecs.beam
|
||||
|
||||
clean:
|
||||
@echo Cleaning
|
||||
@rm -f *.beam
|
||||
@rm -f *~
|
||||
@rm -f *.xfm
|
||||
@rm -f *.xforms
|
||||
|
||||
|
13
examples/test_exprecs.erl
Normal file
13
examples/test_exprecs.erl
Normal file
@ -0,0 +1,13 @@
|
||||
-module(test_exprecs).
|
||||
|
||||
-export([f/0]).
|
||||
|
||||
-compile({parse_transform, exprecs}).
|
||||
|
||||
-record(r, {a, b, c}).
|
||||
|
||||
-export_records([r]).
|
||||
|
||||
|
||||
f() ->
|
||||
{new, '#new-r'([])}.
|
25
include/exprecs.hrl
Normal file
25
include/exprecs.hrl
Normal file
@ -0,0 +1,25 @@
|
||||
%% The contents of this file are subject to the Erlang Public License,
|
||||
%% Version 1.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.erlang.org/license/EPL1_0.txt
|
||||
%%
|
||||
%% 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) 2010 Erlang Solutions Ltd.
|
||||
%%
|
||||
%% Contributor(s): ______________________________________.
|
||||
|
||||
%%-------------------------------------------------------------------
|
||||
%% File : exprecs.hrl
|
||||
%% @author : Ulf Wiger <ulf.wiger@erlang-solutions.com>
|
||||
%% @end
|
||||
%% Description :
|
||||
%%
|
||||
%% Created : 25 Feb 2010 by Ulf Wiger <ulf.wiger@erlang-solutions.com>
|
||||
%%-------------------------------------------------------------------
|
||||
-compile({parse_transform, exprecs}).
|
337
src/exprecs.erl
337
src/exprecs.erl
@ -1,92 +1,124 @@
|
||||
%%% The contents of this file are subject to the Erlang Public License,
|
||||
%%% Version 1.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.erlang.org/license/EPL1_0.txt
|
||||
%%%
|
||||
%%% 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): ______________________________________.
|
||||
%% The contents of this file are subject to the Erlang Public License,
|
||||
%% Version 1.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.erlang.org/license/EPL1_0.txt
|
||||
%%
|
||||
%% 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) 2010 Erlang Solutions Ltd.
|
||||
%% 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
|
||||
%%% @author : Ulf Wiger <ulf.wiger@ericsson.com>
|
||||
%%% @end
|
||||
%%% Description :
|
||||
%%%
|
||||
%%% Created : 13 Feb 2006 by Ulf Wiger <ulf.wiger@ericsson.com>
|
||||
%%%-------------------------------------------------------------------
|
||||
%%-------------------------------------------------------------------
|
||||
%% File : exprecs.erl
|
||||
%% @author : Ulf Wiger <ulf.wiger@ericsson.com>
|
||||
%% @end
|
||||
%% Description :
|
||||
%%
|
||||
%% Created : 13 Feb 2006 by Ulf Wiger <ulf.wiger@ericsson.com>
|
||||
%% Rewritten: Jan-Feb 2010 by Ulf Wiger <ulf.wiger@elang-solutions.com>
|
||||
%%-------------------------------------------------------------------
|
||||
|
||||
%%% @doc Parse transform for generating record access functions.
|
||||
%%% <p>This parse transform can be used to reduce compile-time
|
||||
%%% dependencies in large systems.</p>
|
||||
%%% <p>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.</p>
|
||||
%%% <p>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.</p>
|
||||
%%% <p>Whenever record definitions need to be exported from a module,
|
||||
%%% inserting a compiler attribute,
|
||||
%%% <code>export_records([RecName|...])</code> causes this transform
|
||||
%%% to lay out access functions for the exported records:</p>
|
||||
%%%
|
||||
%%% <pre>
|
||||
%%% -module(foo)
|
||||
%%% -compile({parse_transform, dia_exprecs}).
|
||||
%%%
|
||||
%%% -record(a, {a, b, c}).
|
||||
%%% -export_records([a]).
|
||||
%%% -export(['#info-'/1, '#info-'/2,
|
||||
%%% '#get-'/2, '#set-'/2,
|
||||
%%% '#new-a'/0, '#new-a'/1,
|
||||
%%% '#get-a'/2, '#set-a'/2,
|
||||
%%% '#info-a'/1]).
|
||||
%%%
|
||||
%%% '#info-'(Rec) ->
|
||||
%%% '#info-'(Rec, fields).
|
||||
%%%
|
||||
%%% '#info-'(a, Info) ->
|
||||
%%% '#info-a'(Info).
|
||||
%%%
|
||||
%%% '#new-a'() -> #a{}.
|
||||
%%% '#new-a'(Vals) -> '#set-a'(Vals, #a{}).
|
||||
%%%
|
||||
%%% '#get-'(Attrs, Rec) when is_record(Rec, a) ->
|
||||
%%% '#get-a'(Attrs, Rec).
|
||||
%%%
|
||||
%%% '#get-a'(Attrs, R) when is_list(Attrs) ->
|
||||
%%% ['#get-a'(A, R) || A <- Attrs];
|
||||
%%% '#get-a'(a, R) -> R#a.a;
|
||||
%%% '#get-a'(b, R) -> R#a.b;
|
||||
%%% '#get-a'(c, R) -> R#a.c.
|
||||
%%%
|
||||
%%% '#set-'(Vals, Rec) when is_record(Rec, a) ->
|
||||
%%% '#set-a'(Vals, Rec).
|
||||
%%%
|
||||
%%% '#set-a'(Vals, Rec) ->
|
||||
%%% F = fun ([], R, _F1) -> R;
|
||||
%%% ([{a, V} | T], R, F1) -> F1(T, R#a{a = V}, F1);
|
||||
%%% ([{b, V} | T], R, F1) -> F1(T, R#a{b = V}, F1);
|
||||
%%% ([{c, V} | T], R, F1) -> F1(T, R#a{c = V}, F1)
|
||||
%%% end,
|
||||
%%% F(Vals, Rec, F).
|
||||
%%%
|
||||
%%% '#info-a'(fields) -> record_info(fields, a);
|
||||
%%% '#info-a'(size) -> record_info(size, a).
|
||||
%%% </pre>
|
||||
%%% @end
|
||||
%% @doc Parse transform for generating record access functions.
|
||||
%% <p>This parse transform can be used to reduce compile-time
|
||||
%% dependencies in large systems.</p>
|
||||
%% <p>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.</p>
|
||||
%% <p>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.</p>
|
||||
%% <p>Whenever record definitions need to be exported from a module,
|
||||
%% inserting a compiler attribute,
|
||||
%% <code>export_records([RecName|...])</code> causes this transform
|
||||
%% to lay out access functions for the exported records:</p>
|
||||
%%
|
||||
%% <pre>
|
||||
%% -module(test_exprecs).
|
||||
%%
|
||||
%% -record(r, {a, b, c}).
|
||||
%% -export_records([r]).
|
||||
%%
|
||||
%% -export(['#new-'/1, '#info-'/1, '#info-'/2, '#pos-'/2,
|
||||
%% '#is_record-'/2, '#get-'/2, '#set-'/2, '#fromlist-'/2,
|
||||
%% '#new-r'/0, '#new-r'/1, '#get-r'/2, '#set-r'/2,
|
||||
%% '#pos-r'/1, '#fromlist-r'/2, '#info-r'/1]).
|
||||
%%
|
||||
%% '#new-'(r) -> '#new-r'().
|
||||
%%
|
||||
%% '#info-'(RecName) -> '#info-'(RecName, fields).
|
||||
%%
|
||||
%% '#info-'(r, Info) -> '#info-r'(Info).
|
||||
%%
|
||||
%% '#pos-'(r, Attr) -> '#pos-r'(Attr).
|
||||
%%
|
||||
%% '#is_record-'(r, Rec)
|
||||
%% when tuple_size(Rec) == 3, element(1, Rec) == r ->
|
||||
%% true;
|
||||
%% '#is_record-'(_, _) -> false.
|
||||
%%
|
||||
%% '#get-'(Attrs, Rec) when is_record(Rec, r) ->
|
||||
%% '#get-r'(Attrs, Rec).
|
||||
%%
|
||||
%% '#set-'(Vals, Rec) when is_record(Rec, r) ->
|
||||
%% '#set-r'(Vals, Rec).
|
||||
%%
|
||||
%% '#fromlist-'(Vals, Rec) when is_record(Rec, r) ->
|
||||
%% '#fromlist-r'(Vals, Rec).
|
||||
%%
|
||||
%% '#new-r'() -> #r{}.
|
||||
%%
|
||||
%% '#new-r'(Vals) -> '#set-r'(Vals, #r{}).
|
||||
%%
|
||||
%% '#get-r'(Attrs, R) when is_list(Attrs) ->
|
||||
%% ['#get-r'(A, R) || A <- Attrs];
|
||||
%% '#get-r'(a, R) -> R#r.a;
|
||||
%% '#get-r'(b, R) -> R#r.b;
|
||||
%% '#get-r'(c, R) -> R#r.c;
|
||||
%% '#get-r'(Attr, R) ->
|
||||
%% erlang:error(bad_record_op, ['#get-r', Attr, R]).
|
||||
%%
|
||||
%% '#set-r'(Vals, Rec) ->
|
||||
%% F = fun ([], R, _F1) -> R;
|
||||
%% ([{a, V} | T], R, F1) -> F1(T, R#r{a = V}, F1);
|
||||
%% ([{b, V} | T], R, F1) -> F1(T, R#r{b = V}, F1);
|
||||
%% ([{c, V} | T], R, F1) -> F1(T, R#r{c = V}, F1);
|
||||
%% (Vs, R, _) ->
|
||||
%% erlang:error(bad_record_op, ['#set-r', Vs, R])
|
||||
%% end,
|
||||
%% F(Vals, Rec, F).
|
||||
%%
|
||||
%% '#fromlist-r'(Vals, Rec) ->
|
||||
%% AttrNames = [{a, 2}, {b, 3}, {c, 4}],
|
||||
%% F = fun ([], R, _F1) -> R;
|
||||
%% ([{H, Pos} | T], R, F1) ->
|
||||
%% case lists:keyfind(H, 1, Vals) of
|
||||
%% false -> F1(T, R, F1);
|
||||
%% {_, Val} -> F1(T, setelement(Pos, R, Val), F1)
|
||||
%% end
|
||||
%% end,
|
||||
%% F(AttrNames, Rec, F).
|
||||
%%
|
||||
%% '#pos-r'(a) -> 2;
|
||||
%% '#pos-r'(b) -> 3;
|
||||
%% '#pos-r'(c) -> 4;
|
||||
%% '#pos-r'(_) -> 0.
|
||||
%%
|
||||
%% '#info-r'(fields) -> record_info(fields, r);
|
||||
%% '#info-r'(size) -> record_info(size, r).
|
||||
%% </pre>
|
||||
%% @end
|
||||
|
||||
-module(exprecs).
|
||||
|
||||
@ -163,6 +195,7 @@ generate_f(attribute, {attribute,L,export_records,_} = Form, _Ctxt,
|
||||
Exports = [{fname(new), 1},
|
||||
{fname(info), 1},
|
||||
{fname(info), 2},
|
||||
{fname(pos), 2},
|
||||
{fname(isrec), 2},
|
||||
{fname(get), 2},
|
||||
{fname(set), 2},
|
||||
@ -174,6 +207,7 @@ generate_f(attribute, {attribute,L,export_records,_} = Form, _Ctxt,
|
||||
[{FNew, 0}, {FNew,1},
|
||||
{fname(get, RecS), 2},
|
||||
{fname(set, RecS), 2},
|
||||
{fname(pos, RecS), 1},
|
||||
{fname(fromlist, RecS), 2},
|
||||
{fname(info, RecS), 1}]
|
||||
end, Es)] ++ version_exports(Vsns),
|
||||
@ -271,6 +305,7 @@ generate_accessors(L, Acc) ->
|
||||
[f_new_(Acc, L),
|
||||
f_info(Acc, L),
|
||||
f_info_2(Acc, L),
|
||||
f_pos_2(Acc, L),
|
||||
f_isrec(Acc, L),
|
||||
f_get(Acc, L),
|
||||
f_set(Acc, L),
|
||||
@ -284,6 +319,7 @@ generate_accessors(L, Acc) ->
|
||||
f_get_2(Rname, Fields, L),
|
||||
f_set_2(Rname, Fields, L),
|
||||
f_fromlist_2(Rname, Fields, L),
|
||||
f_pos_1(Rname, Fields, L),
|
||||
f_info_1(Rname, L)]
|
||||
end, Acc#pass1.exports))] ++ version_accessors(L, Acc).
|
||||
|
||||
@ -302,9 +338,10 @@ fname_prefix(Op) ->
|
||||
get -> "#get-";
|
||||
set -> "#set-";
|
||||
fromlist -> "#fromlist-";
|
||||
info -> "#info-";
|
||||
isrec -> "#is_record-";
|
||||
convert -> "#convert-"
|
||||
info -> "#info-";
|
||||
pos -> "#pos-";
|
||||
isrec -> "#is_record-";
|
||||
convert -> "#convert-"
|
||||
end.
|
||||
|
||||
fname_prefix(Op, Rname) ->
|
||||
@ -390,10 +427,23 @@ bad_record_op(L, Fname, Val, R) ->
|
||||
{nil, L}}}}]}.
|
||||
|
||||
|
||||
|
||||
f_pos_1(Rname, Flds, L) ->
|
||||
Fname = fname(pos, Rname),
|
||||
FieldList = lists:zip(Flds, lists:seq(2, length(Flds)+1)),
|
||||
{function, L, Fname, 1,
|
||||
[{clause, L,
|
||||
[{atom, L, FldName}],
|
||||
[],
|
||||
[{integer, L, Pos}]} || {FldName, Pos} <- FieldList] ++
|
||||
[{clause, L,
|
||||
[{var, L, '_'}], [], [{integer, L, 0}]}]
|
||||
}.
|
||||
|
||||
|
||||
f_fromlist_2(Rname, Flds, L) ->
|
||||
Fname = fname(fromlist, Rname),
|
||||
FldList = erl_parse:abstract(
|
||||
lists:zip(Flds, lists:seq(2, length(Flds)+1))),
|
||||
FldList = field_list(Flds),
|
||||
{function, L, Fname, 2,
|
||||
[{clause, L, [{var, L, 'Vals'}, {var, L, 'Rec'}], [],
|
||||
[{match, L, {var, L, 'AttrNames'}, FldList},
|
||||
@ -434,6 +484,11 @@ f_fromlist_2(Rname, Flds, L) ->
|
||||
]}
|
||||
]}.
|
||||
|
||||
field_list(Flds) ->
|
||||
erl_parse:abstract(
|
||||
lists:zip(Flds, lists:seq(2, length(Flds)+1))).
|
||||
|
||||
|
||||
|
||||
f_get_2(Rname, Flds, L) ->
|
||||
FName = fname(get, Rname),
|
||||
@ -512,6 +567,16 @@ f_info_3(Versions, L) ->
|
||||
%% io:fwrite("F = ~p~n", [F]),
|
||||
F.
|
||||
|
||||
f_pos_2(Acc, L) ->
|
||||
Fname = list_to_atom(fname_prefix(pos)),
|
||||
{function, L, Fname, 2,
|
||||
[{clause, L,
|
||||
[{atom, L, R},
|
||||
{var, L, 'Attr'}],
|
||||
[],
|
||||
[{call, L, {atom, L, fname(pos, R)}, [{var, L, 'Attr'}]}]} ||
|
||||
R <- Acc#pass1.exports]}.
|
||||
|
||||
|
||||
f_get(Acc, L) ->
|
||||
Fname = list_to_atom(fname_prefix(get)),
|
||||
@ -526,6 +591,7 @@ f_get(Acc, L) ->
|
||||
{var, L, 'Rec'}]}]} ||
|
||||
R <- Acc#pass1.exports]}.
|
||||
|
||||
|
||||
f_set(Acc, L) ->
|
||||
Fname = list_to_atom(fname_prefix(set)),
|
||||
{function, L, Fname, 2,
|
||||
@ -630,96 +696,12 @@ f_convert(_Vsns, L) ->
|
||||
|
||||
-spec context(atom(), #context{}) ->
|
||||
term().
|
||||
%% @hidden
|
||||
context(module, #context{module = M} ) -> M;
|
||||
context(function, #context{function = F}) -> F;
|
||||
context(arity, #context{arity = A} ) -> A.
|
||||
|
||||
|
||||
% transform(Forms, F, Acc) ->
|
||||
% case [{L,M} || {attribute, L, module, M} <- Forms] of
|
||||
% [{_,Module}] ->
|
||||
% transform(Forms, F, #context{module = Module}, Acc);
|
||||
% [] ->
|
||||
% ?ERROR(missing_module_attribute, ?HERE, []);
|
||||
% [_|_] = Multiple ->
|
||||
% ?ERROR(multiple_module_attributes, ?HERE,
|
||||
% [{L,{module,M}} || {L,M} <- Multiple])
|
||||
% end.
|
||||
|
||||
% transform(Forms, F, Context, Acc) ->
|
||||
% F1 =
|
||||
% fun(Form, Acc0) ->
|
||||
% Type = erl_syntax:type(Form),
|
||||
% {Before1, Form1, After1, Recurse, Acc1} =
|
||||
% try F(Type, Form, Context, Acc0) of
|
||||
% {F1, Rec1, A1} ->
|
||||
% {[], F1, [], Rec1, A1};
|
||||
% {_Be1, _F1, _Af1, _Rec1, _Ac1} = Res1 ->
|
||||
% Res1
|
||||
% catch
|
||||
% error:Reason ->
|
||||
% ?ERROR(Reason,
|
||||
% ?HERE,
|
||||
% [{type, Type},
|
||||
% {context, Context},
|
||||
% {acc, Acc},
|
||||
% {form, Form}])
|
||||
% end,
|
||||
% if Recurse == true ->
|
||||
% case erl_syntax:subtrees(Form1) of
|
||||
% [] ->
|
||||
% {Before1, Form1, After1, Acc1};
|
||||
% ListOfLists ->
|
||||
% {NewListOfLists, NewAcc} =
|
||||
% mapfoldl(
|
||||
% fun(L, AccX) ->
|
||||
% transform(
|
||||
% L, F,
|
||||
% new_context(
|
||||
% Form1, Context), AccX)
|
||||
% end, Acc1, ListOfLists),
|
||||
% NewForm =
|
||||
% erl_syntax:update_tree(
|
||||
% Form, NewListOfLists),
|
||||
% {Before1, NewForm, After1, NewAcc}
|
||||
% end;
|
||||
% true ->
|
||||
% {Before1, Form1, After1, Acc1}
|
||||
% end
|
||||
% end,
|
||||
% mapfoldl(F1, Acc, Forms).
|
||||
|
||||
|
||||
% new_context(Form, Context0) ->
|
||||
% case erl_syntax:type(Form) of
|
||||
% function ->
|
||||
% {Fun, Arity} =
|
||||
% erl_syntax_lib:analyze_function(Form),
|
||||
% Context0#context{function = Fun,
|
||||
% arity = Arity};
|
||||
% _ ->
|
||||
% Context0
|
||||
% end.
|
||||
|
||||
|
||||
|
||||
|
||||
%%% Slightly modified version of lists:mapfoldl/3
|
||||
%%% Here, F/2 is able to insert forms before and after the form
|
||||
%%% 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,
|
||||
% {Rs, Accu2} = mapfoldl(F, Accu1, Tail),
|
||||
% {Before ++ [Res| After ++ Rs], Accu2};
|
||||
% mapfoldl(F, Accu, []) when is_function(F, 2) -> {[], Accu}.
|
||||
|
||||
|
||||
|
||||
rpt_error(Reason, Fun, Info) ->
|
||||
Fmt = lists:flatten(
|
||||
@ -736,5 +718,6 @@ rpt_error(Reason, Fun, Info) ->
|
||||
|
||||
-spec format_error({atom(), term()}) ->
|
||||
iolist().
|
||||
%% @hidden
|
||||
format_error({_Cat, Error}) ->
|
||||
Error.
|
||||
|
@ -64,7 +64,8 @@
|
||||
get_orig_syntax_tree/1,
|
||||
function_exists/3,
|
||||
optionally_pretty_print/3,
|
||||
pp_src/2
|
||||
pp_src/2,
|
||||
pp_beam/1, pp_beam/2
|
||||
]).
|
||||
|
||||
-import(erl_syntax, [atom_value/1,
|
||||
@ -117,6 +118,13 @@ error(R, F, I) ->
|
||||
throw({error,get_pos(I),{unknown,R}}).
|
||||
|
||||
|
||||
%% @spec (list()) -> integer()
|
||||
%%
|
||||
%% @doc
|
||||
%% Tries to retrieve the line number from an erl_syntax form. Returns a
|
||||
%% (very high) dummy number if not successful.
|
||||
%% @end
|
||||
%%
|
||||
-spec get_pos(list()) ->
|
||||
integer().
|
||||
get_pos(I) when is_list(I) ->
|
||||
@ -180,6 +188,12 @@ find_attribute(A, [F|Forms]) ->
|
||||
find_attribute(_, []) ->
|
||||
false.
|
||||
|
||||
%% @spec (Fname::atom(), Arity::integer(), Forms) -> boolean()
|
||||
%%
|
||||
%% @doc
|
||||
%% Checks whether the given function is defined in Forms.
|
||||
%% @end
|
||||
%%
|
||||
-spec function_exists(atom(), integer(), forms()) ->
|
||||
boolean().
|
||||
function_exists(Fname, Arity, Forms) ->
|
||||
@ -342,6 +356,10 @@ ext(pp) -> ".xfm";
|
||||
ext(forms) -> ".xforms".
|
||||
|
||||
|
||||
%% @spec (Forms, Out::filename()) -> ok
|
||||
%%
|
||||
%% @doc Pretty-prints the erlang source code corresponding to Forms into Out
|
||||
%%
|
||||
-spec pp_src(forms(), string()) ->
|
||||
ok.
|
||||
pp_src(Res, F) ->
|
||||
@ -350,6 +368,46 @@ pp_src(Res, F) ->
|
||||
Fm <- revert(Res)])])],
|
||||
file:write_file(F, list_to_binary(Str)).
|
||||
|
||||
%% @spec (Beam::filename()) -> string() | {error, Reason}
|
||||
%%
|
||||
%% @doc
|
||||
%% Reads debug_info from the beam file Beam and returns a string containing
|
||||
%% the pretty-printed corresponding erlang source code.
|
||||
%% @end
|
||||
pp_beam(Beam) ->
|
||||
case pp_beam_to_str(Beam) of
|
||||
{ok, Str} ->
|
||||
io:put_chars(Str);
|
||||
Other ->
|
||||
Other
|
||||
end.
|
||||
|
||||
%% @spec (Beam::filename(), Out::filename()) -> ok | {error, Reason}
|
||||
%%
|
||||
%% @doc
|
||||
%% Reads debug_info from the beam file Beam and pretty-prints it as
|
||||
%% Erlang source code, storing it in the file Out.
|
||||
%% @end
|
||||
%%
|
||||
pp_beam(F, Out) ->
|
||||
case pp_beam_to_str(F) of
|
||||
{ok, Str} ->
|
||||
file:write_file(Out, list_to_binary(Str));
|
||||
Other ->
|
||||
Other
|
||||
end.
|
||||
|
||||
pp_beam_to_str(F) ->
|
||||
case beam_lib:chunks(F, [abstract_code]) of
|
||||
{ok, {_, [{abstract_code,{_,AC}}]}} ->
|
||||
{ok, lists:flatten(
|
||||
io_lib:fwrite("~s~n", [erl_prettypr:format(
|
||||
erl_syntax:form_list(AC))])
|
||||
)};
|
||||
Other ->
|
||||
{error, Other}
|
||||
end.
|
||||
|
||||
%% pp_debug_info(Mod) when is_atom(Mod) ->
|
||||
%% case code:which(Mod) of
|
||||
%% F when is_list(F) ->
|
||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user