added pos functions to exprecs

This commit is contained in:
Ulf Wiger 2010-02-25 07:34:17 -05:00
parent 275690db26
commit 6b9510fa74
11 changed files with 392 additions and 266 deletions

View File

@ -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

View File

@ -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>

View File

@ -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) -&gt;
'#info-'(Rec, fields).
'#info-'(a, Info) -&gt;
'#info-a'(Info).
'#new-a'() -&gt; #a{}.
'#new-a'(Vals) -&gt; '#set-a'(Vals, #a{}).
'#get-'(Attrs, Rec) when is_record(Rec, a) -&gt;
'#get-a'(Attrs, Rec).
'#get-a'(Attrs, R) when is_list(Attrs) -&gt;
['#get-a'(A, R) || A &lt;- Attrs];
'#get-a'(a, R) -&gt; R#a.a;
'#get-a'(b, R) -&gt; R#a.b;
'#get-a'(c, R) -&gt; R#a.c.
'#set-'(Vals, Rec) when is_record(Rec, a) -&gt;
'#set-a'(Vals, Rec).
'#set-a'(Vals, Rec) -&gt;
F = fun ([], R, _F1) -&gt; R;
([{a, V} | T], R, F1) -&gt; F1(T, R#a{a = V}, F1);
([{b, V} | T], R, F1) -&gt; F1(T, R#a{b = V}, F1);
([{c, V} | T], R, F1) -&gt; F1(T, R#a{c = V}, F1)
end,
F(Vals, Rec, F).
'#info-a'(fields) -&gt; record_info(fields, a);
'#info-a'(size) -&gt; 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) -&gt; '#new-r'().
'#info-'(RecName) -&gt; '#info-'(RecName, fields).
'#info-'(r, Info) -&gt; '#info-r'(Info).
'#pos-'(r, Attr) -&gt; '#pos-r'(Attr).
'#is_record-'(r, Rec)
when tuple_size(Rec) == 3, element(1, Rec) == r -&gt;
true;
'#is_record-'(_, _) -&gt; false.
'#get-'(Attrs, Rec) when is_record(Rec, r) -&gt;
'#get-r'(Attrs, Rec).
'#set-'(Vals, Rec) when is_record(Rec, r) -&gt;
'#set-r'(Vals, Rec).
'#fromlist-'(Vals, Rec) when is_record(Rec, r) -&gt;
'#fromlist-r'(Vals, Rec).
'#new-r'() -&gt; #r{}.
'#new-r'(Vals) -&gt; '#set-r'(Vals, #r{}).
'#get-r'(Attrs, R) when is_list(Attrs) -&gt;
['#get-r'(A, R) || A &lt;- Attrs];
'#get-r'(a, R) -&gt; R#r.a;
'#get-r'(b, R) -&gt; R#r.b;
'#get-r'(c, R) -&gt; R#r.c;
'#get-r'(Attr, R) -&gt;
erlang:error(bad_record_op, ['#get-r', Attr, R]).
'#set-r'(Vals, Rec) -&gt;
F = fun ([], R, _F1) -&gt; R;
([{a, V} | T], R, F1) -&gt; F1(T, R#r{a = V}, F1);
([{b, V} | T], R, F1) -&gt; F1(T, R#r{b = V}, F1);
([{c, V} | T], R, F1) -&gt; F1(T, R#r{c = V}, F1);
(Vs, R, _) -&gt;
erlang:error(bad_record_op, ['#set-r', Vs, R])
end,
F(Vals, Rec, F).
'#fromlist-r'(Vals, Rec) -&gt;
AttrNames = [{a, 2}, {b, 3}, {c, 4}],
F = fun ([], R, _F1) -&gt; R;
([{H, Pos} | T], R, F1) -&gt;
case lists:keyfind(H, 1, Vals) of
false -&gt; F1(T, R, F1);
{_, Val} -&gt; F1(T, setelement(Pos, R, Val), F1)
end
end,
F(AttrNames, Rec, F).
'#pos-r'(a) -&gt; 2;
'#pos-r'(b) -&gt; 3;
'#pos-r'(c) -&gt; 4;
'#pos-r'(_) -&gt; 0.
'#info-r'(fields) -&gt; record_info(fields, r);
'#info-r'(size) -&gt; 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) -&gt; 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) -&gt; 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) -&gt; 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>

View File

@ -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>

View File

@ -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) -&gt; any()</tt></p>
</div>
<p><tt>function_exists(Fname::atom(), Arity::integer(), Forms) -&gt; <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) -&gt; any()</tt></p>
</div>
<p><tt>get_pos(I::list()) -&gt; 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) -&gt; 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>) -&gt; 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>) -&gt; 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) -&gt; any()</tt></p>
</div>
<p><tt>pp_src(Res::Forms, Out::<a href="#type-filename">filename()</a>) -&gt; 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>

View File

@ -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
View 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
View 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}).

View File

@ -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) -&gt;
%%% '#info-'(Rec, fields).
%%%
%%% '#info-'(a, Info) -&gt;
%%% '#info-a'(Info).
%%%
%%% '#new-a'() -&gt; #a{}.
%%% '#new-a'(Vals) -&gt; '#set-a'(Vals, #a{}).
%%%
%%% '#get-'(Attrs, Rec) when is_record(Rec, a) -&gt;
%%% '#get-a'(Attrs, Rec).
%%%
%%% '#get-a'(Attrs, R) when is_list(Attrs) -&gt;
%%% ['#get-a'(A, R) || A &lt;- Attrs];
%%% '#get-a'(a, R) -&gt; R#a.a;
%%% '#get-a'(b, R) -&gt; R#a.b;
%%% '#get-a'(c, R) -&gt; R#a.c.
%%%
%%% '#set-'(Vals, Rec) when is_record(Rec, a) -&gt;
%%% '#set-a'(Vals, Rec).
%%%
%%% '#set-a'(Vals, Rec) -&gt;
%%% F = fun ([], R, _F1) -> R;
%%% ([{a, V} | T], R, F1) -&gt; F1(T, R#a{a = V}, F1);
%%% ([{b, V} | T], R, F1) -&gt; F1(T, R#a{b = V}, F1);
%%% ([{c, V} | T], R, F1) -&gt; F1(T, R#a{c = V}, F1)
%%% end,
%%% F(Vals, Rec, F).
%%%
%%% '#info-a'(fields) -&gt; record_info(fields, a);
%%% '#info-a'(size) -&gt; 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) -&gt; '#new-r'().
%%
%% '#info-'(RecName) -&gt; '#info-'(RecName, fields).
%%
%% '#info-'(r, Info) -&gt; '#info-r'(Info).
%%
%% '#pos-'(r, Attr) -&gt; '#pos-r'(Attr).
%%
%% '#is_record-'(r, Rec)
%% when tuple_size(Rec) == 3, element(1, Rec) == r -&gt;
%% true;
%% '#is_record-'(_, _) -&gt; false.
%%
%% '#get-'(Attrs, Rec) when is_record(Rec, r) -&gt;
%% '#get-r'(Attrs, Rec).
%%
%% '#set-'(Vals, Rec) when is_record(Rec, r) -&gt;
%% '#set-r'(Vals, Rec).
%%
%% '#fromlist-'(Vals, Rec) when is_record(Rec, r) -&gt;
%% '#fromlist-r'(Vals, Rec).
%%
%% '#new-r'() -&gt; #r{}.
%%
%% '#new-r'(Vals) -&gt; '#set-r'(Vals, #r{}).
%%
%% '#get-r'(Attrs, R) when is_list(Attrs) -&gt;
%% ['#get-r'(A, R) || A &lt;- Attrs];
%% '#get-r'(a, R) -&gt; R#r.a;
%% '#get-r'(b, R) -&gt; R#r.b;
%% '#get-r'(c, R) -&gt; R#r.c;
%% '#get-r'(Attr, R) -&gt;
%% erlang:error(bad_record_op, ['#get-r', Attr, R]).
%%
%% '#set-r'(Vals, Rec) -&gt;
%% F = fun ([], R, _F1) -&gt; R;
%% ([{a, V} | T], R, F1) -&gt; F1(T, R#r{a = V}, F1);
%% ([{b, V} | T], R, F1) -&gt; F1(T, R#r{b = V}, F1);
%% ([{c, V} | T], R, F1) -&gt; F1(T, R#r{c = V}, F1);
%% (Vs, R, _) -&gt;
%% erlang:error(bad_record_op, ['#set-r', Vs, R])
%% end,
%% F(Vals, Rec, F).
%%
%% '#fromlist-r'(Vals, Rec) -&gt;
%% AttrNames = [{a, 2}, {b, 3}, {c, 4}],
%% F = fun ([], R, _F1) -&gt; R;
%% ([{H, Pos} | T], R, F1) -&gt;
%% case lists:keyfind(H, 1, Vals) of
%% false -&gt; F1(T, R, F1);
%% {_, Val} -&gt; F1(T, setelement(Pos, R, Val), F1)
%% end
%% end,
%% F(AttrNames, Rec, F).
%%
%% '#pos-r'(a) -&gt; 2;
%% '#pos-r'(b) -&gt; 3;
%% '#pos-r'(c) -&gt; 4;
%% '#pos-r'(_) -&gt; 0.
%%
%% '#info-r'(fields) -&gt; record_info(fields, r);
%% '#info-r'(size) -&gt; 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.

View File

@ -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.