Erlang Central

Format Code

Revision as of 01:57, 22 October 2006 by Ayrnieu (Talk | contribs)

Problem

You have some Erlang code that you want to format readably.

Solution

The erl_pp module provides this functionality. The only complicated thing is that it expects to deal with intermediate Erlang forms, such as those produced by the erl_parse module. Of course, the erl_parse module wants to deal with lexically-scanned tokens, such as those produced by the erl_scan module. Consequently, taking a chunk of badly-formatted Erlang code and generating beautiful output is generally a three-step process.

1> Badly_formatted = "fib(0) -> 1;fib(1) -> 1;fib(N) -> fib(N-2) + fib(N-1).".
"fib(0) -> 1;fib(1) -> 1;fib(N) -> fib(N-2) + fib(N-1)."
2> {ScanStatus, Scanned_Badly_Formatted, ScanCount} = 
2>     erl_scan:string(Badly_formatted).
{ok,[{atom,1,fib},
     {'(',1},
     {integer,1,0},
     {')',1},
     {'->',1},
     {integer,1,1},
     {';',1},
     {atom,1,fib},
     {'(',1},
     {integer,1,1},
     {')',1},
     {'->',1},
     {integer,1,1},
     {';',1},
     {atom,1,fib},
     {'(',1},
     {var,1,'N'},
     {')',1},
     {'->',1},
     {atom,1,fib},
     {'(',1},
     {var,1,'N'},
     {'-',1},
     {integer,1,2},
     {')',1},
     {'+'|...},
     {...}|...],
    1}
3> {ParseStatus,Parsed_Badly_Formatted} =
3>    erl_parse:parse_form(Scanned_Badly_Formatted).
{ok,{function,1,
              fib,
              1,
              [{clause,1,[{integer,1,0}],[],[{integer,1,1}]},
               {clause,1,[{integer,1,1}],[],[{integer,1,1}]},
               {clause,1,
                       [{var,1,'N'}],
                       [],
                       [{op,1,
                            '+',
                            {call,1,{atom,1,fib},[{op|...}]},
                            {call,1,{atom,1|...},[{...}]}}]}]}}
4> Pretty_Form = erl_pp:form(Parsed_Badly_Formatted).
[[[["fib","(",["0"],")"],[],[" ->","\n    ","1"]],
  [";\n",[["fib","(",["1"],")"],[],[" ->","\n    ","1"]]],
  [";\n",
   [["fib","(",[["N"]],")"],
    [],
    [" ->",
     "\n    ",
     [["fib","(",[[["N"]," - ","2"]],")"],
      " + ",
      ["fib","(",[[["N"]," - ","1"]],")"]]]]]],
 ".\n"]
5> io:fwrite("~s", [Pretty_Form]).
fib(0) ->
    1;
fib(1) ->
    1;
fib(N) ->
    fib(N - 2) + fib(N - 1).

Whew! That's a lot of work to make the code a bit easier to read. However, you generally would not use this as shown in the example. We would wrap everything up in a nice set of functions to produce a single output:

pretty_print(BadStr) ->
    {ScanStatus, Scanned_Badly_Formatted, ScanCount} = 
        erl_scan:string(Badly_formatted),
    {ParseStatus,Parsed_Badly_Formatted} =
        erl_parse:parse_form(Scanned_Badly_Formatted),
    erl_pp:form(Parsed_Badly_Formatted).

1> io:fwrite("~s", [pretty_print(
    "fib(0) -> 1;fib(1) -> 1;fib(N) -> fib(N-2) + fib(N-1).")]).
fib(0) ->
    1;
fib(1) ->
    1;
fib(N) ->
    fib(N - 2) + fib(N - 1).
ok

To be truly useful, we should probably create a full-featured pretty_print function that would allow us to control formatting, such as the desired columns width, perhaps a maximum level to parse, etc.

In fact, it appears someone has already done so. Richard Carlsson (a member of the HIPE Project) has written a number of tools for parsing and manipulating Erlang text. His edoc package appears to support everything you could possibly want with respect to pretty printing.