parse_trans/doc/exprecs.md
2011-10-23 13:52:05 +02:00

4.3 KiB

#Module exprecs#

Parse transform for generating record access functions.

Authors: : Ulf Wiger (ulf.wiger@ericsson.com).

##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:

  -module(test_exprecs).
 
  -record(r, {a = 0 :: integer(),
              b = 0 :: integer(),
              c = 0 :: integer()}).
  -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'/1, '#fromlist-r'/2, '#info-r'/1]).
 
  -type '#prop-r'() :: {a, integer()} | {b, integer()} | {c, integer()}.
 
  '#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).
 
  -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-'(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]).
 
  -spec '#set-r'(['#prop-r'()], #r{}) -> #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).
 
  -spec '#pos-r'('#attr-r'()) -> integer().
  '#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).
  

##Data Types##

###form()##

form() = any()

###forms()##

forms() = [[form()](#type-form)]

###options()##

options() = [{atom(), any()}]

##Function Index##

parse_transform/2

##Function Details##

###parse_transform/2##

parse_transform(Forms::[forms()](#type-forms), Options::[options()](#type-options)) -> [forms()](#type-forms)