empty records broke exprecs in #lens-

This commit is contained in:
Ulf Wiger 2013-12-12 14:20:47 +01:00
parent b3e5372047
commit f89567ec01
14 changed files with 1716 additions and 6 deletions

73
doc/README.md Normal file
View File

@ -0,0 +1,73 @@
#The parse_trans application#
__Authors:__ Ulf Wiger ([`ulf@feuerlabs.com`](mailto:ulf@feuerlabs.com)).
A generic parse transform library
This library is intended to simplify the task of writing parse transform
modules for Erlang.
#Introduction to parse transforms#
##The simplest transform##
The very simplest transform we can make is one that doesn't
change a thing. For convenience, we will at least print the forms.
This will enlighten us as to what the forms actually look like.<pre>
-module(test_pt).
-export([parse_transform/2]).
parse_transform(Forms, _Options) ->
io:fwrite("Forms = ~p~n", [Forms]),
Forms.
</pre>
Trying this with a very simple module:<pre>
-module(ex1).
-export([add/2]).
add(X,Y) ->
X + Y.
</pre><pre>
1> c(ex1, [{parse_transform,test_pt}]).
Forms = [{attribute,1,file,{"./ex1.erl",1}},
{attribute,1,module,ex1},
{attribute,2,export,[{add,2}]},
{function,4,add,2,
[{clause,4,
[{var,4,'X'},{var,4,'Y'}],
[],
[{op,5,'+',{var,5,'X'},{var,5,'Y'}}]}]},
{eof,6}]
{ok,ex1}
</pre>
##`transform/4`##
...
#Current limitations#
...
##Modules##
<table width="100%" border="0" summary="list of modules">
<tr><td><a href="ct_expand.md" class="module">ct_expand</a></td></tr>
<tr><td><a href="exprecs.md" class="module">exprecs</a></td></tr>
<tr><td><a href="parse_trans.md" class="module">parse_trans</a></td></tr>
<tr><td><a href="parse_trans_codegen.md" class="module">parse_trans_codegen</a></td></tr>
<tr><td><a href="parse_trans_mod.md" class="module">parse_trans_mod</a></td></tr>
<tr><td><a href="parse_trans_pp.md" class="module">parse_trans_pp</a></td></tr></table>

106
doc/ct_expand.md Normal file
View File

@ -0,0 +1,106 @@
#Module ct_expand#
* [Description](#description)
* [Data Types](#types)
* [Function Index](#index)
* [Function Details](#functions)
Compile-time expansion utility.
__Authors:__ : Ulf Wiger ([`ulf@feuerlabs.com`](mailto:ulf@feuerlabs.com)).<a name="description"></a>
##Description##
This module serves as an example of parse_trans-based transforms,
but might also be a useful utility in its own right.
The transform searches for calls to the pseudo-function
`ct_expand:term(Expr)`, and then replaces the call site with the
result of evaluating `Expr` at compile-time.
For example, the line
`ct_expand:term(lists:sort([3,5,2,1,4]))`
would be expanded at compile-time to `[1,2,3,4,5]`.
ct_expand has now been extended to also evaluate calls to local functions.
See examples/ct_expand_test.erl for some examples.
A debugging facility exists: passing the option {ct_expand_trace, Flags} as an option,
or adding a compiler attribute -ct_expand_trace(Flags) will enable a form of call trace.`Flags` can be `[]` (no trace) or `[F]`, where `F` is `c` (call trace),
`r` (return trace), or `x` (exception trace)'.
<a name="types"></a>
##Data Types##
###<a name="type-form">form()</a>##
<pre>form() = any()</pre>
###<a name="type-forms">forms()</a>##
<pre>forms() = [<a href="#type-form">form()</a>]</pre>
###<a name="type-options">options()</a>##
<pre>options() = [{atom(), any()}]</pre>
<a name="index"></a>
##Function Index##
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#extract_fun-3">extract_fun/3</a></td><td></td></tr><tr><td valign="top"><a href="#lfun_rewrite-2">lfun_rewrite/2</a></td><td></td></tr><tr><td valign="top"><a href="#parse_transform-2">parse_transform/2</a></td><td></td></tr></table>
<a name="functions"></a>
##Function Details##
<a name="extract_fun-3"></a>
###extract_fun/3##
`extract_fun(Name, Arity, Forms) -> any()`
<a name="lfun_rewrite-2"></a>
###lfun_rewrite/2##
`lfun_rewrite(Exprs, Forms) -> any()`
<a name="parse_transform-2"></a>
###parse_transform/2##
<pre>parse_transform(Forms::<a href="#type-forms">forms()</a>, Options::<a href="#type-options">options()</a>) -> <a href="#type-forms">forms()</a></pre>
<br></br>

506
doc/exprecs.md Normal file
View File

@ -0,0 +1,506 @@
#Module exprecs#
* [Description](#description)
* [Data Types](#types)
* [Function Index](#index)
* [Function Details](#functions)
Parse transform for generating record access functions.
__Authors:__ : Ulf Wiger ([`ulf.wiger@ericsson.com`](mailto:ulf.wiger@ericsson.com)).<a name="description"></a>
##Description##
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:
<pre>
-module(test_exprecs).
-export([f/0]).
-compile({parse_transform, exprecs}).
-record(r, {a = 0 :: integer(),
b = 0 :: integer(),
c = 0 :: integer()}).
-record(s,{a}).
-export_records([r,s]).
f() ->
{new,'#new-r'([])}.</pre>
Compiling this (assuming exprecs is in the path) will produce the
following code.<pre>
-module(test_exprecs).
-compile({pt_pp_src,true}).
-export([f/0]).
-record(r,{a = 0 :: integer(),b = 0 :: integer(),c = 0 :: integer()}).
-record(s,{a}).
-export_records([r,s]).
-export(['#exported_records-'/0,
'#new-'/1,
'#info-'/1,
'#info-'/2,
'#pos-'/2,
'#is_record-'/1,
'#is_record-'/2,
'#get-'/2,
'#set-'/2,
'#fromlist-'/2,
'#lens-'/2,
'#new-r'/0,
'#new-r'/1,
'#get-r'/2,
'#set-r'/2,
'#pos-r'/1,
'#fromlist-r'/1,
'#fromlist-r'/2,
'#info-r'/1,
'#lens-r'/1,
'#new-s'/0,
'#new-s'/1,
'#get-s'/2,
'#set-s'/2,
'#pos-s'/1,
'#fromlist-s'/1,
'#fromlist-s'/2,
'#info-s'/1,
'#lens-s'/1]).
-type '#prop-r'() :: {a, integer()} | {b, integer()} | {c, integer()}.
-type '#attr-r'() :: a | b | c.
-type '#prop-s'() :: {a, any()}.
-type '#attr-s'() :: a.
-spec '#exported_records-'() -> [r | s].
'#exported_records-'() ->
[r,s].
-spec '#new-'(r) -> #r{};
(s) -> #s{}.
'#new-'(r) ->
'#new-r'();
'#new-'(s) ->
'#new-s'().
-spec '#info-'(r) -> [a | b | c];
(s) -> [a].
'#info-'(RecName) ->
'#info-'(RecName, fields).
-spec '#info-'(r, size) -> 4;
(r, fields) -> [a | b | c];
(s, size) -> 2;
(s, fields) -> [a].
'#info-'(r, Info) ->
'#info-r'(Info);
'#info-'(s, Info) ->
'#info-s'(Info).
-spec '#pos-'(r, a) -> 1;
(r, b) -> 2;
(r, c) -> 3;
(s, a) -> 1.
'#pos-'(r, Attr) ->
'#pos-r'(Attr);
'#pos-'(s, Attr) ->
'#pos-s'(Attr).
-spec '#is_record-'(#r{}) -> true;
(#s{}) -> true;
(any()) -> false.
'#is_record-'(X) ->
if
is_record(X, r) ->
true;
is_record(X, s) ->
true;
true ->
false
end.
-spec '#is_record-'(r, #r{}) -> true;
(s, #s{}) -> true;
(any(), any()) -> false.
'#is_record-'(s, Rec) when tuple_size(Rec) == 2, element(1, Rec) == s ->
true;
'#is_record-'(r, Rec) when tuple_size(Rec) == 4, element(1, Rec) == r ->
true;
'#is_record-'(_, _) ->
false.
-spec '#get-'(a, #r{}) -> integer();
(b, #r{}) -> integer();
(c, #r{}) -> integer();
(a, #s{}) -> any();
(['#attr-r'()], #r{}) -> [integer()];
(['#attr-s'()], #s{}) -> [any()].
'#get-'(Attrs, Rec) when is_record(Rec, r) ->
'#get-r'(Attrs, Rec);
'#get-'(Attrs, Rec) when is_record(Rec, s) ->
'#get-s'(Attrs, Rec).
-spec '#set-'(['#prop-r'()], #r{}) -> #r{};
(['#prop-s'()], #s{}) -> #s{}.
'#set-'(Vals, Rec) when is_record(Rec, r) ->
'#set-r'(Vals, Rec);
'#set-'(Vals, Rec) when is_record(Rec, s) ->
'#set-s'(Vals, Rec).
-spec '#fromlist-'(['#prop-r'()], #r{}) -> #r{};
(['#prop-s'()], #s{}) -> #s{}.
'#fromlist-'(Vals, Rec) when is_record(Rec, r) ->
'#fromlist-r'(Vals, Rec);
'#fromlist-'(Vals, Rec) when is_record(Rec, s) ->
'#fromlist-s'(Vals, Rec).
-spec '#lens-'('#prop-r'(), r) ->
{fun((#r{}) -> any()), fun((any(), #r{}) -> #r{})};
('#prop-s'(), s) ->
{fun((#s{}) -> any()), fun((any(), #s{}) -> #s{})}.
'#lens-'(Attr, r) ->
'#lens-r'(Attr);
'#lens-'(Attr, s) ->
'#lens-s'(Attr).
-spec '#new-r'() -> #r{}.
'#new-r'() ->
#r{}.
-spec '#new-r'(['#prop-r'()]) -> #r{}.
'#new-r'(Vals) ->
'#set-r'(Vals, #r{}).
-spec '#get-r'(a, #r{}) -> integer();
(b, #r{}) -> integer();
(c, #r{}) -> integer();
(['#attr-r'()], #r{}) -> [integer()].
'#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) ->
error(bad_record_op, ['#get-r',Attr,R]).
-spec '#set-r'(['#prop-r'()], #r{}) -> #r{}.
'#set-r'(Vals, Rec) ->
F = fun([], R, _F1) ->
R;
([{a,V}|T], R, F1) when is_list(T) ->
F1(T, R#r{a = V}, F1);
([{b,V}|T], R, F1) when is_list(T) ->
F1(T, R#r{b = V}, F1);
([{c,V}|T], R, F1) when is_list(T) ->
F1(T, R#r{c = V}, F1);
(Vs, R, _) ->
error(bad_record_op, ['#set-r',Vs,R])
end,
F(Vals, Rec, F).
-spec '#fromlist-r'(['#prop-r'()]) -> #r{}.
'#fromlist-r'(Vals) when is_list(Vals) ->
'#fromlist-r'(Vals, '#new-r'()).
-spec '#fromlist-r'(['#prop-r'()], #r{}) -> #r{}.
'#fromlist-r'(Vals, Rec) ->
AttrNames = [{a,2},{b,3},{c,4}],
F = fun([], R, _F1) ->
R;
([{H,Pos}|T], R, F1) when is_list(T) ->
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).
-spec '#pos-r'('#attr-r'() | atom()) -> integer().
'#pos-r'(a) ->
2;
'#pos-r'(b) ->
3;
'#pos-r'(c) ->
4;
'#pos-r'(A) when is_atom(A) ->
0.
-spec '#info-r'(fields) -> [a | b | c];
(size) -> 3.
'#info-r'(fields) ->
record_info(fields, r);
'#info-r'(size) ->
record_info(size, r).
-spec '#lens-r'('#prop-r'()) ->
{fun((#r{}) -> any()), fun((any(), #r{}) -> #r{})}.
'#lens-r'(a) ->
{fun(R) ->
'#get-r'(a, R)
end,
fun(X, R) ->
'#set-r'([{a,X}], R)
end};
'#lens-r'(b) ->
{fun(R) ->
'#get-r'(b, R)
end,
fun(X, R) ->
'#set-r'([{b,X}], R)
end};
'#lens-r'(c) ->
{fun(R) ->
'#get-r'(c, R)
end,
fun(X, R) ->
'#set-r'([{c,X}], R)
end};
'#lens-r'(Attr) ->
error(bad_record_op, ['#lens-r',Attr]).
-spec '#new-s'() -> #s{}.
'#new-s'() ->
#s{}.
-spec '#new-s'(['#prop-s'()]) -> #s{}.
'#new-s'(Vals) ->
'#set-s'(Vals, #s{}).
-spec '#get-s'(a, #s{}) -> any();
(['#attr-s'()], #s{}) -> [any()].
'#get-s'(Attrs, R) when is_list(Attrs) ->
[
'#get-s'(A, R) ||
A <- Attrs
];
'#get-s'(a, R) ->
R#s.a;
'#get-s'(Attr, R) ->
error(bad_record_op, ['#get-s',Attr,R]).
-spec '#set-s'(['#prop-s'()], #s{}) -> #s{}.
'#set-s'(Vals, Rec) ->
F = fun([], R, _F1) ->
R;
([{a,V}|T], R, F1) when is_list(T) ->
F1(T, R#s{a = V}, F1);
(Vs, R, _) ->
error(bad_record_op, ['#set-s',Vs,R])
end,
F(Vals, Rec, F).
-spec '#fromlist-s'(['#prop-s'()]) -> #s{}.
'#fromlist-s'(Vals) when is_list(Vals) ->
'#fromlist-s'(Vals, '#new-s'()).
-spec '#fromlist-s'(['#prop-s'()], #s{}) -> #s{}.
'#fromlist-s'(Vals, Rec) ->
AttrNames = [{a,2}],
F = fun([], R, _F1) ->
R;
([{H,Pos}|T], R, F1) when is_list(T) ->
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).
-spec '#pos-s'('#attr-s'() | atom()) -> integer().
'#pos-s'(a) ->
2;
'#pos-s'(A) when is_atom(A) ->
0.
-spec '#info-s'(fields) -> [a];
(size) -> 1.
'#info-s'(fields) ->
record_info(fields, s);
'#info-s'(size) ->
record_info(size, s).
-spec '#lens-s'('#prop-s'()) ->
{fun((#s{}) -> any()), fun((any(), #s{}) -> #s{})}.
'#lens-s'(a) ->
{fun(R) ->
'#get-s'(a, R)
end,
fun(X, R) ->
'#set-s'([{a,X}], R)
end};
'#lens-s'(Attr) ->
error(bad_record_op, ['#lens-s', Attr]).
f() ->
{new,'#new-r'([])}.</pre>
It is possible to modify the naming rules of exprecs, through the use
of the following attributes (example reflecting the current rules):<pre>
-exprecs_prefix(["#", operation, "-"]).
-exprecs_fname([prefix, record]).
-exprecs_vfname([fname, "__", version]).</pre>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:
<dt><code>new</code></dt>
<dd>Creates a new record</dd>
<dt><code>get</code></dt>
<dd>Retrieves given attribute values from a record</dd>
<dt><code>set</code></dt>
<dd>Sets given attribute values in a record</dd>
<dt><code>fromlist</code></dt>
<dd>Creates a record from a key-value list</dd>
<dt><code>info</code></dt>
<dd>Equivalent to record_info/2</dd>
<dt><code>pos</code></dt>
<dd>Returns the position of a given attribute</dd>
<dt><code>is_record</code></dt>
<dd>Tests if a value is a specific record</dd>
<dt><code>convert</code></dt>
<dd>Converts an old record to the current version</dd>
<dt><code>prop</code></dt>
<dd>Used only in type specs</dd>
<dt><code>attr</code></dt>
<dd>Used only in type specs</dd>
<dt><code>lens</code></dt>
<dd>Returns a 'lens' (an accessor pair) as described in
<a href="http://github.com/jlouis/erl-lenses" target="_top"><tt>http://github.com/jlouis/erl-lenses</tt></a></dd>
<a name="types"></a>
##Data Types##
###<a name="type-form">form()</a>##
<pre>form() = any()</pre>
###<a name="type-forms">forms()</a>##
<pre>forms() = [<a href="#type-form">form()</a>]</pre>
###<a name="type-options">options()</a>##
<pre>options() = [{atom(), any()}]</pre>
<a name="index"></a>
##Function Index##
<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>
<a name="functions"></a>
##Function Details##
<a name="parse_transform-2"></a>
###parse_transform/2##
<pre>parse_transform(Forms::<a href="#type-forms">forms()</a>, Options::<a href="#type-options">options()</a>) -> <a href="#type-forms">forms()</a></pre>
<br></br>

574
doc/parse_trans.md Normal file
View File

@ -0,0 +1,574 @@
#Module parse_trans#
* [Description](#description)
* [Data Types](#types)
* [Function Index](#index)
* [Function Details](#functions)
Generic parse transform library for Erlang.
__Authors:__ : Ulf Wiger ([`ulf.wiger@feuerlabs.com`](mailto:ulf.wiger@feuerlabs.com)).<a name="description"></a>
##Description##
...
<a name="types"></a>
##Data Types##
###<a name="type-form">form()</a>##
<pre>form() = any()</pre>
###<a name="type-forms">forms()</a>##
<pre>forms() = [<a href="#type-form">form()</a>]</pre>
###<a name="type-insp_f">insp_f()</a>##
<pre>insp_f() = fun((<a href="#type-type">type()</a>, <a href="#type-form">form()</a>, #context{}, A) -> {boolean(), A})</pre>
###<a name="type-options">options()</a>##
<pre>options() = [{atom(), any()}]</pre>
###<a name="type-type">type()</a>##
<pre>type() = atom()</pre>
###<a name="type-xform_f_df">xform_f_df()</a>##
<pre>xform_f_df() = fun((<a href="#type-type">type()</a>, <a href="#type-form">form()</a>, #context{}, Acc) -> {<a href="#type-form">form()</a>, Acc} | {<a href="#type-forms">forms()</a>, <a href="#type-form">form()</a>, <a href="#type-forms">forms()</a>, Acc})</pre>
###<a name="type-xform_f_rec">xform_f_rec()</a>##
<pre>xform_f_rec() = fun((<a href="#type-type">type()</a>, <a href="#type-form">form()</a>, #context{}, Acc) -> {<a href="#type-form">form()</a>, boolean(), Acc} | {<a href="#type-forms">forms()</a>, <a href="#type-form">form()</a>, <a href="#type-forms">forms()</a>, boolean(), Acc})</pre>
<a name="index"></a>
##Function Index##
<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>
Accessor function for the Context record.</td></tr><tr><td valign="top"><a href="#depth_first-4">depth_first/4</a></td><td></td></tr><tr><td valign="top"><a href="#do_depth_first-4">do_depth_first/4</a></td><td></td></tr><tr><td valign="top"><a href="#do_insert_forms-4">do_insert_forms/4</a></td><td></td></tr><tr><td valign="top"><a href="#do_inspect-4">do_inspect/4</a></td><td></td></tr><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="#export_function-3">export_function/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="#format_exception-2">format_exception/2</a></td><td>Equivalent to <a href="#format_exception-3"><tt>format_exception(Class, Reason, 4)</tt></a>.</td></tr><tr><td valign="top"><a href="#format_exception-3">format_exception/3</a></td><td>Produces a few lines of user-friendly formatting of exception info.</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_attribute-3">get_attribute/3</a></td><td></td></tr><tr><td valign="top"><a href="#get_file-1">get_file/1</a></td><td>
Returns the name of the file being compiled.</td></tr><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>
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="#plain_transform-2">plain_transform/2</a></td><td>
Performs a transform of <code>Forms</code> using the fun <code>Fun(Form)</code>.</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="#replace_function-4">replace_function/4</a></td><td></td></tr><tr><td valign="top"><a href="#return-2">return/2</a></td><td>Checks the transformed result for errors and warnings.</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="#revert_form-1">revert_form/1</a></td><td>Reverts a single form 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>
Makes one pass.</td></tr></table>
<a name="functions"></a>
##Function Details##
<a name="context-2"></a>
###context/2##
<pre>context(X1::Attr, Context) -&gt; any()</pre>
<ul class="definitions"><li><pre>Attr = module | function | arity | options</pre></li></ul>
Accessor function for the Context record.<a name="depth_first-4"></a>
###depth_first/4##
<pre>depth_first(Fun::<a href="#type-xform_f_df">xform_f_df()</a>, Acc, Forms::<a href="#type-forms">forms()</a>, Options::<a href="#type-options">options()</a>) -> {<a href="#type-forms">forms()</a>, Acc} | {error, list()}</pre>
<br></br>
<a name="do_depth_first-4"></a>
###do_depth_first/4##
<pre>do_depth_first(F::<a href="#type-xform_f_df">xform_f_df()</a>, Acc::term(), Forms::<a href="#type-forms">forms()</a>, Context::#context{}) -> {<a href="#type-forms">forms()</a>, term()}</pre>
<br></br>
<a name="do_insert_forms-4"></a>
###do_insert_forms/4##
<pre>do_insert_forms(X1::above | below, Insert::<a href="#type-forms">forms()</a>, Forms::<a href="#type-forms">forms()</a>, Context::#context{}) -> <a href="#type-forms">forms()</a></pre>
<br></br>
<a name="do_inspect-4"></a>
###do_inspect/4##
<pre>do_inspect(F::<a href="#type-insp_f">insp_f()</a>, Acc::term(), Forms::<a href="#type-forms">forms()</a>, Context::#context{}) -> term()</pre>
<br></br>
<a name="do_transform-4"></a>
###do_transform/4##
<pre>do_transform(F::<a href="#type-xform_f_rec">xform_f_rec()</a>, Acc::term(), Forms::<a href="#type-forms">forms()</a>, Context::#context{}) -> {<a href="#type-forms">forms()</a>, term()}</pre>
<br></br>
<a name="error-3"></a>
###error/3##
<pre>error(R::Reason, F::Form, I::Info) -> <a href="#type-throw">throw()</a></pre>
<ul class="definitions"><li><pre>Info = [{Key, Value}]</pre></li></ul>
Used to report errors detected during the parse transform.<a name="export_function-3"></a>
###export_function/3##
`export_function(F, Arity, Forms) -> any()`
<a name="format_error-1"></a>
###format_error/1##
<pre>format_error(Error::{atom(), term()}) -&gt; iolist()</pre>
<br></br>
<a name="format_exception-2"></a>
###format_exception/2##
<pre>format_exception(Class, Reason) -&gt; String</pre>
<br></br>
Equivalent to [`format_exception(Class, Reason, 4)`](#format_exception-3).<a name="format_exception-3"></a>
###format_exception/3##
<pre>format_exception(Class, Reason, Lines) -&gt; String</pre>
<ul class="definitions"><li><pre>Class = error | throw | exit</pre></li><li><pre>Reason = term()</pre></li><li><pre>Lines = integer() | infinity</pre></li></ul>
Produces a few lines of user-friendly formatting of exception info
This function is very similar to the exception pretty-printing in the shell,
but returns a string that can be used as error info e.g. by error forms
handled by [`return/2`](#return-2). By default, the first 4 lines of the
pretty-printed exception info are returned, but this can be controlled
with the `Lines` parameter.Note that a stacktrace is generated inside this function.<a name="function_exists-3"></a>
###function_exists/3##
<pre>function_exists(Fname::atom(), Arity::integer(), Forms) -&gt; boolean()</pre>
<br></br>
Checks whether the given function is defined in Forms.<a name="get_attribute-2"></a>
###get_attribute/2##
<pre>get_attribute(A, Forms) -&gt; any()</pre>
<ul class="definitions"><li><pre>A = atom()</pre></li></ul>
Returns the value of the first occurence of attribute A.<a name="get_attribute-3"></a>
###get_attribute/3##
`get_attribute(A, Forms, Undef) -> any()`
<a name="get_file-1"></a>
###get_file/1##
<pre>get_file(Forms) -&gt; string()</pre>
<br></br>
Returns the name of the file being compiled.<a name="get_module-1"></a>
###get_module/1##
<pre>get_module(Forms) -&gt; atom()</pre>
<br></br>
Returns the name of the module being compiled.<a name="get_orig_syntax_tree-1"></a>
###get_orig_syntax_tree/1##
<pre>get_orig_syntax_tree(File) -&gt; Forms</pre>
<br></br>
Fetches a Syntax Tree representing the code before pre-processing,
that is, including record and macro definitions. Note that macro
definitions must be syntactically complete forms (this function
uses epp_dodger).<a name="get_pos-1"></a>
###get_pos/1##
<pre>get_pos(I::list()) -&gt; integer()</pre>
<br></br>
Tries to retrieve the line number from an erl_syntax form. Returns a
(very high) dummy number if not successful.<a name="initial_context-2"></a>
###initial_context/2##
<pre>initial_context(Forms, Options) -&gt; #context{}</pre>
<br></br>
Initializes a context record. When traversing through the form
list, the context is updated to reflect the current function and
arity. Static elements in the context are the file name, the module
name and the options passed to the transform function.<a name="inspect-4"></a>
###inspect/4##
<pre>inspect(F::Fun, Acc::Forms, Forms::Acc, Options) -&gt; NewAcc</pre>
<ul class="definitions"><li><pre>Fun = function()</pre></li></ul>
Equvalent to do_inspect(Fun,Acc,Forms,initial_context(Forms,Options)).<a name="optionally_pretty_print-3"></a>
###optionally_pretty_print/3##
<pre>optionally_pretty_print(Result::<a href="#type-forms">forms()</a>, Options::<a href="#type-options">options()</a>, Context::#context{}) -> ok</pre>
<br></br>
<a name="plain_transform-2"></a>
###plain_transform/2##
<pre>plain_transform(Fun, Forms) -> <a href="#type-forms">forms()</a></pre>
<ul class="definitions"><li><pre>Fun = function()</pre></li><li><pre>Forms = <a href="#type-forms">forms()</a></pre></li></ul>
Performs a transform of `Forms` using the fun `Fun(Form)`. `Form` is always
an Erlang abstract form, i.e. it is not converted to syntax_tools
representation. The intention of this transform is for the fun to have a
catch-all clause returning `continue`. This will ensure that it stays robust
against additions to the language.
`Fun(Form)` must return either of the following:
* `NewForm` - any valid form
* `continue` - dig into the sub-expressions of the form
* `{done, NewForm}` - Replace `Form` with `NewForm`; return all following
forms unchanged
* `{error, Reason}` - Abort transformation with an error message.Example - This transform fun would convert all instances of `P ! Msg` to
`gproc:send(P, Msg)`:
<pre>
parse_transform(Forms, _Options) ->
parse_trans:plain_transform(fun do_transform/1, Forms).
do_transform({'op', L, '!', Lhs, Rhs}) ->
[NewLhs] = parse_trans:plain_transform(fun do_transform/1, [Lhs]),
[NewRhs] = parse_trans:plain_transform(fun do_transform/1, [Rhs]),
{call, L, {remote, L, {atom, L, gproc}, {atom, L, send}},
[NewLhs, NewRhs]};
do_transform(_) ->
continue.</pre><a name="pp_beam-1"></a>
###pp_beam/1##
<pre>pp_beam(Beam::<a href="file.md#type-filename">file:filename()</a>) -> string() | {error, Reason}</pre>
<br></br>
Reads debug_info from the beam file Beam and returns a string containing
the pretty-printed corresponding erlang source code.<a name="pp_beam-2"></a>
###pp_beam/2##
<pre>pp_beam(Beam::<a href="#type-filename">filename()</a>, Out::<a href="#type-filename">filename()</a>) -> ok | {error, Reason}</pre>
<br></br>
Reads debug_info from the beam file Beam and pretty-prints it as
Erlang source code, storing it in the file Out.<a name="pp_src-2"></a>
###pp_src/2##
<pre>pp_src(Res::Forms, Out::<a href="#type-filename">filename()</a>) -> ok</pre>
<br></br>
Pretty-prints the erlang source code corresponding to Forms into Out
<a name="replace_function-4"></a>
###replace_function/4##
`replace_function(F, Arity, NewForm, Forms) -> any()`
<a name="return-2"></a>
###return/2##
<pre>return(Forms, Context) -&gt; Forms | {error, Es, Ws} | {warnings, Forms, Ws}</pre>
<br></br>
Checks the transformed result for errors and warnings
Errors and warnings can be produced from inside a parse transform, with
a bit of care. The easiest way is to simply produce an `{error, Err}` or
`{warning, Warn}` form in place. This function finds such forms, and
removes them from the form list (otherwise, the linter will crash), and
produces a return value that the compiler can work with.The format of the `error` and `warning` "forms" must be
`{Tag, {Pos, Module, Info}}`, where:
* `Tag :: error | warning`
* `Pos :: LineNumber | {LineNumber, ColumnNumber}`
* `Module` is a module that exports a corresponding
`Module:format_error(Info)`
* `Info :: term()`
If the error is in the form of a caught exception, `Info` may be produced
using the function [`format_exception/2`](#format_exception-2).<a name="revert-1"></a>
###revert/1##
<pre>revert(Tree) -&gt; Forms</pre>
<br></br>
Reverts back from Syntax Tools format to Erlang forms.
Note that the Erlang forms are a subset of the Syntax Tools
syntax tree, so this function is safe to call even on a list of
regular Erlang forms.<a name="revert_form-1"></a>
###revert_form/1##
<pre>revert_form(F::Tree) -&gt; Form</pre>
<br></br>
Reverts a single form back from Syntax Tools format to Erlang forms.
`erl_syntax:revert/1` has had a long-standing bug where it doesn't
completely revert attribute forms. This function deals properly with those
cases.
Note that the Erlang forms are a subset of the Syntax Tools
syntax tree, so this function is safe to call even on a regular Erlang
form.<a name="top-3"></a>
###top/3##
<pre>top(F::function(), Forms::<a href="#type-forms">forms()</a>, Options::list()) -> <a href="#type-forms">forms()</a> | {error, term()}</pre>
<br></br>
<a name="transform-4"></a>
###transform/4##
<pre>transform(Fun, Acc, Forms, Options) -&gt; {TransformedForms, NewAcc}</pre>
<ul class="definitions"><li><pre>Fun = function()</pre></li><li><pre>Options = [{Key, Value}]</pre></li></ul>
Makes one pass

160
doc/parse_trans_codegen.md Normal file
View File

@ -0,0 +1,160 @@
#Module parse_trans_codegen#
* [Description](#description)
* [Function Index](#index)
* [Function Details](#functions)
Parse transform for code generation pseduo functions.
__Authors:__ : Ulf Wiger ([`ulf@feuerlabs.com`](mailto:ulf@feuerlabs.com)).<a name="description"></a>
##Description##
...
<a name="index"></a>
##Function Index##
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><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>
Searches for calls to pseudo functions in the module <code>codegen</code>,
and converts the corresponding erlang code to a data structure
representing the abstract form of that code.</td></tr></table>
<a name="functions"></a>
##Function Details##
<a name="format_error-1"></a>
###format_error/1##
`format_error(E) -> any()`
<a name="parse_transform-2"></a>
###parse_transform/2##
<pre>parse_transform(Forms, Options) -&gt; NewForms</pre>
<br></br>
Searches for calls to pseudo functions in the module `codegen`,
and converts the corresponding erlang code to a data structure
representing the abstract form of that code.
The purpose of these functions is to let the programmer write
the actual code that is to be generated, rather than manually
writing abstract forms, which is more error prone and cannot be
checked by the compiler until the generated module is compiled.
Supported functions:
##gen_function/2##
Usage: `codegen:gen_function(Name, Fun)`
Substitutes the abstract code for a function with name `Name`
and the same behaviour as `Fun`.`Fun` can either be a anonymous `fun`, which is then converted to
a named function, or it can be an `implicit fun`, e.g.
`fun is_member/2`. In the latter case, the referenced function is fetched
and converted to an abstract form representation. It is also renamed
so that the generated function has the name `Name`.
Another alternative is to wrap a fun inside a list comprehension, e.g.
<pre>
f(Name, L) ->
codegen:gen_function(
Name,
[ fun({'$var',X}) ->
{'$var', Y}
end || {X, Y} &lt;- L ]).</pre>
Calling the above with `f(foo, [{1,a},{2,b},{3,c}])` will result in
generated code corresponding to:
<pre>
foo(1) -> a;
foo(2) -> b;
foo(3) -> c.</pre>
##gen_functions/1##
Takes a list of `{Name, Fun}` tuples and produces a list of abstract
data objects, just as if one had written
`[codegen:gen_function(N1,F1),codegen:gen_function(N2,F2),...]`.
##exprs/1##
Usage: `codegen:exprs(Fun)`
`Fun` is either an anonymous function, or an implicit fun with only one
function clause. This "function" takes the body of the fun and produces
a data type representing the abstract form of the list of expressions in
the body. The arguments of the function clause are ignored, but can be
used to ensure that all necessary variables are known to the compiler.
##gen_module/3##
Generates abstract forms for a complete module definition.
Usage: `codegen:gen_module(ModuleName, Exports, Functions)`
`ModuleName` is either an atom or a `{'$var', V}` reference.
`Exports` is a list of `{Function, Arity}` tuples.
`Functions` is a list of `{Name, Fun}` tuples analogous to that for
`gen_functions/1`.
##Variable substitution##
It is possible to do some limited expansion (importing a value
bound at compile-time), using the construct `{'$var', V}`, where
`V` is a bound variable in the scope of the call to `gen_function/2`.Example:
<pre>
gen(Name, X) ->
codegen:gen_function(Name, fun(L) -> lists:member({'$var',X}, L) end).</pre>After transformation, calling `gen(contains_17, 17)` will yield the
abstract form corresponding to:
<pre>
contains_17(L) ->
lists:member(17, L).</pre>
##Form substitution##
It is possible to inject abstract forms, using the construct
`{'$form', F}`, where `F` is bound to a parsed form in
the scope of the call to `gen_function/2`.Example:
<pre>
gen(Name, F) ->
codegen:gen_function(Name, fun(X) -> X =:= {'$form',F} end).</pre>After transformation, calling `gen(is_foo, {atom,0,foo})` will yield the
abstract form corresponding to:
<pre>
is_foo(X) ->
X =:= foo.</pre>

118
doc/parse_trans_mod.md Normal file
View File

@ -0,0 +1,118 @@
#Module parse_trans_mod#
* [Data Types](#types)
* [Function Index](#index)
* [Function Details](#functions)
<a name="types"></a>
##Data Types##
###<a name="type-compile_options">compile_options()</a>##
<pre>compile_options() = [term()]</pre>
###<a name="type-erlang_form">erlang_form()</a>##
<pre>erlang_form() = term()</pre>
<a name="index"></a>
##Function Index##
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#abstract_code-1">abstract_code/1</a></td><td></td></tr><tr><td valign="top"><a href="#beam_file-1">beam_file/1</a></td><td></td></tr><tr><td valign="top"><a href="#compile_and_load_forms-1">compile_and_load_forms/1</a></td><td></td></tr><tr><td valign="top"><a href="#compile_and_load_forms-2">compile_and_load_forms/2</a></td><td></td></tr><tr><td valign="top"><a href="#compile_options-1">compile_options/1</a></td><td></td></tr><tr><td valign="top"><a href="#rename_module-2">rename_module/2</a></td><td></td></tr><tr><td valign="top"><a href="#transform_module-3">transform_module/3</a></td><td></td></tr></table>
<a name="functions"></a>
##Function Details##
<a name="abstract_code-1"></a>
###abstract_code/1##
<pre>abstract_code(BeamFile::binary()) -> <a href="#type-erlang_form">erlang_form()</a></pre>
<br></br>
<a name="beam_file-1"></a>
###beam_file/1##
<pre>beam_file(Module::module()) -&gt; binary()</pre>
<br></br>
<a name="compile_and_load_forms-1"></a>
###compile_and_load_forms/1##
<pre>compile_and_load_forms(AbsCode::<a href="#type-erlang_form">erlang_form()</a>) -> ok</pre>
<br></br>
<a name="compile_and_load_forms-2"></a>
###compile_and_load_forms/2##
<pre>compile_and_load_forms(AbsCode::<a href="#type-erlang_form">erlang_form()</a>, Opts::<a href="#type-compile_options">compile_options()</a>) -> ok</pre>
<br></br>
<a name="compile_options-1"></a>
###compile_options/1##
<pre>compile_options(BeamFile::binary() | module()) -> <a href="#type-compile_options">compile_options()</a></pre>
<br></br>
<a name="rename_module-2"></a>
###rename_module/2##
<pre>rename_module(T::<a href="#type-erlang_form">erlang_form()</a>, NewName::module()) -> <a href="#type-erlang_form">erlang_form()</a></pre>
<br></br>
<a name="transform_module-3"></a>
###transform_module/3##
`transform_module(Mod, PT, Options) -> any()`

96
doc/parse_trans_pp.md Normal file
View File

@ -0,0 +1,96 @@
#Module parse_trans_pp#
* [Description](#description)
* [Function Index](#index)
* [Function Details](#functions)
Generic parse transform library for Erlang.
__Authors:__ : Ulf Wiger ([`ulf@feuerlabs.com`](mailto:ulf@feuerlabs.com)).<a name="description"></a>
##Description##
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:
<pre>
alias pp='escript $PARSE_TRANS_ROOT/ebin/parse_trans_pp.beam'</pre>
a file could be pretty-printed using the following command:`$ pp ex_codegen.beam | less`<a name="index"></a>
##Function Index##
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#main-1">main/1</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></table>
<a name="functions"></a>
##Function Details##
<a name="main-1"></a>
###main/1##
<pre>main(X1::[string()]) -&gt; any()</pre>
<br></br>
<a name="pp_beam-1"></a>
###pp_beam/1##
<pre>pp_beam(Beam::<a href="#type-filename">filename()</a>) -> string() | {error, Reason}</pre>
<br></br>
Reads debug_info from the beam file Beam and returns a string containing
the pretty-printed corresponding erlang source code.<a name="pp_beam-2"></a>
###pp_beam/2##
<pre>pp_beam(Beam::<a href="#type-filename">filename()</a>, Out::<a href="#type-filename">filename()</a>) -> ok | {error, Reason}</pre>
<br></br>
Reads debug_info from the beam file Beam and pretty-prints it as
Erlang source code, storing it in the file Out.<a name="pp_src-2"></a>
###pp_src/2##
<pre>pp_src(Forms0::Forms, Out::<a href="#type-filename">filename()</a>) -> ok</pre>
<br></br>
Pretty-prints the erlang source code corresponding to Forms into Out

View File

@ -0,0 +1,13 @@
-module(ex_gen_module).
-compile(export_all).
-compile({parse_transform, parse_trans_codegen}).
f() ->
codegen:gen_module(test, [{render,0}, {source, 0}],
[
{render, fun() ->
x end},
{source, fun() ->
ok end}
]).

7
examples/lc.erl Normal file
View File

@ -0,0 +1,7 @@
-module(lc).
-export([f/1]).
f(X) ->
[fun(_) ->
erlang:now()
end || {Y1,Y2} <- [{1,a},{2,b}]].

7
examples/t.erl Normal file
View File

@ -0,0 +1,7 @@
-module(t).
-export([f/1]).
f(X) ->
(fun(X1) ->
X1
end)(X).

15
examples/t_ex.erl Normal file
View File

@ -0,0 +1,15 @@
-module(t_ex).
-record(r, {a,b}).
-export(['sel-r'/1]).
-spec 'sel-r'(a | b) ->
{fun((#r{}) -> any()), fun((any(), #r{}) -> #r{})}.
'sel-r'(a) ->
{fun(#r{a = A}) ->
A
end,
fun(X, #r{} = R) ->
R#r{a = X}
end}.

View File

@ -9,8 +9,9 @@
-record(r, {a = 0 :: integer(), b = 0 :: integer(), c = 0 :: integer()}). -record(r, {a = 0 :: integer(), b = 0 :: integer(), c = 0 :: integer()}).
-record(s, {a}). -record(s, {a}).
-record(t, {}).
-export_records([r, s]). -export_records([r, s, t]).
f() -> f() ->

View File

@ -52,6 +52,9 @@
-module(ct_expand). -module(ct_expand).
-export([parse_transform/2]). -export([parse_transform/2]).
-export([extract_fun/3,
lfun_rewrite/2]).
-type form() :: any(). -type form() :: any().
-type forms() :: [form()]. -type forms() :: [form()].
-type options() :: [{atom(), any()}]. -type options() :: [{atom(), any()}].

View File

@ -298,7 +298,9 @@
%% end, %% end,
%% fun(X, R) -&gt; %% fun(X, R) -&gt;
%% '#set-r'([{c,X}], R) %% '#set-r'([{c,X}], R)
%% end}. %% end};
%% '#lens-r'(Attr) -&gt;
%% error(bad_record_op, ['#lens-r',Attr]).
%% %%
%% -spec '#new-s'() -&gt; #s{}. %% -spec '#new-s'() -&gt; #s{}.
%% '#new-s'() -&gt; %% '#new-s'() -&gt;
@ -371,7 +373,9 @@
%% end, %% end,
%% fun(X, R) -&gt; %% fun(X, R) -&gt;
%% '#set-s'([{a,X}], R) %% '#set-s'([{a,X}], R)
%% end}. %% end};
%% '#lens-s'(Attr) -&gt;
%% error(bad_record_op, ['#lens-s', Attr]).
%% %%
%% f() -&gt; %% f() -&gt;
%% {new,'#new-r'([])}. %% {new,'#new-r'([])}.
@ -649,7 +653,13 @@ generate_specs(L, Specs, Acc) ->
{fname(attr, R, Acc), {fname(attr, R, Acc),
{type, L, union, {type, L, union,
[{atom, L, A} || {A,_} <- Attrs]}, []}} [{atom, L, A} || {A,_} <- Attrs]}, []}}
] || {R, Attrs} <- Specs]. ] || {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) -> generate_accessors(L, Acc) ->
@ -792,7 +802,20 @@ funspec(L, Fname, [{H,_} | _] = Alts) ->
{attribute, L, spec, {attribute, L, spec,
{{Fname, Arity}, {{Fname, Arity},
[{type, L, 'fun', [{type, L, product, Head}, Ret]} || [{type, L, 'fun', [{type, L, product, Head}, Ret]} ||
{Head, Ret} <- Alts]}}. {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) -> funspec(L, Fname, Head, Returns) ->
Arity = length(Head), Arity = length(Head),
@ -851,6 +874,12 @@ f_set_2(Rname, Flds, L, Acc) ->
{var, L, 'Rec'}, {var, L, 'Rec'},
{var, L, 'F'}]}]}]}]. {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) -> bad_record_op(L, Fname, Val, R) ->
{call, L, {remote, L, {atom,L,erlang}, {atom,L,error}}, {call, L, {remote, L, {atom,L,erlang}, {atom,L,error}},
[{atom,L,bad_record_op}, {cons, L, {atom, L, Fname}, [{atom,L,bad_record_op}, {cons, L, {atom, L, Fname},
@ -1256,7 +1285,9 @@ f_lens_1(Rname, Flds, L, Acc) ->
{var, L, 'X'}]}, {nil,L}}, {var, L, 'X'}]}, {nil,L}},
{var, L, 'R'}]}] {var, L, 'R'}]}]
}]}} }]}}
]}]} || Attr <- Flds] ]}]} || Attr <- Flds] ++
[{clause, L, [{var, L, 'Attr'}], [],
[bad_record_op(L, Fname, 'Attr')]}]
}]. }].
%%% ========== generic parse_transform stuff ============== %%% ========== generic parse_transform stuff ==============