Extraction

The Exploration page describes a "Hello World" example, drawn from Programming Erlang: Software for a Concurrent World, 2e. It presents Erlang and Elixir versions of the example, in both the source code and AST forms.

Here is the Erlang AST from that page, rendered as an Elixir data structure:

iex> {:ok, ast_erl} = :epp.parse_file('hello_0.erl', []); ast_erl
[{:attribute,  1, :file,   {'hello_0.erl', 1}},
 {:attribute,  1, :module, :hello},
 {:attribute,  2, :export, [start: 0]},
 {:function,   4, :start, 0,
  [{:clause, 4, [], [],
    [{:call, 5, {:remote, 5, {:atom, 5, :io}, {:atom, 5, :format}},
      [{:string, 5, 'Hello world~n'}]}]}]},
 {:eof,        6}]

The top level of the AST is a list of tuples, e.g.:

  • an atom (:attribute, :eof, :function, ...)
  • the relevant line number in the source code
  • zero or more arguments, which may also be tuples

The Erlang and Elixir ASTs differ in both order and structure, making a direct transformation somewhat awkward. By creating an intermediate data structure, we can decouple data extraction from AST generation, simplifying both tasks.

Data Structures

A pair of simpler data structures should cover our needs quite nicely. Basically, we'll store the :attribute entries in a map and the :function entries in a list:

erl_map = %{                   # attribute map
  export:   [ start: 0 ],
  file:     { 'hello_0.erl', 1 },
  module:   :hello
}

erl_list = [                   # function list
  { 4, :start, 0,
    [ { :clause, 4, [], [],
        [ { :call, 5,
            { :remote, 5,
              { :atom, 5, :io },
              { :atom, 5, :format } },
            [ { :string, 5, 'Hello world~n' } ]
  } ] } ] }
]

The extraction can be performed by the following sort of code:

defp do_tuple(tuple, { attrs, funs }) do
# Handle a single tuple from the Erlang Abstract Format.

  case tuple do
    {:attribute, _, key, val} ->
      attrs = Dict.put(attrs, key, val)

    {:eof, _} -> nil

    {:function, line, name, arity, body} ->
      funs = [ { line, name, arity, body } | funs ]

    _ ->
      IO.puts "!! unrecognized tuple (#{ inspect(tuple) })"
  end
  { attrs, funs }
end

...
new_acc  = { %{}, [] }
ast_info = Enum.reduce(ast_erl, new_acc, &do_tuple/2)
{ attrs, funs } = ast_info
...

Approach

The Abstract Format definition is complicated enough that I don't feel confident about using it as the sole basis for my design. So, I plan to use a large set of test cases, starting with the source code for Joe Armstrong's examples. This should detect a variety of problems in my extraction (etc) code.

At the moment, I'm able to:

  • scan a directory of examples
  • get an AST for each example
  • extract the data structures

To be continued...


This wiki page is maintained by Rich Morin, an independent consultant specializing in software design, development, and documentation. Please feel free to email comments, inquiries, suggestions, etc!

Topic revision: r9 - 23 Jun 2015, RichMorin
This site is powered by Foswiki Copyright © by the contributing authors. All material on this wiki is the property of the contributing authors.
Foswiki version v2.1.6, Release Foswiki-2.1.6, Plugin API version 2.4
Ideas, requests, problems regarding CFCL Wiki? Send us email