Note that this document doesn't explain Erlang macros and directives themselves. So if you want to know the detail of them, please refer to Erlang Reference Manual - 9. Preprocessor.
A unique point of efmt
among various Erlang formatters is that it can handle macros correctly.
For example, efmt
can format the following code containing unusual macros without errors.
-module(weird_macro).
-define(FOO, /).
-define(BAR, :format().
-define(baz(A), A).
-define(qux, -> [1, 2, 3], [).
-define(quux, )], [2,).
-define(a(A, B), A).
-export([?baz(?baz(main))?FOO 0]).
hello(A)->io?BAR "hello ~p\n",[A]).
main()?a(?qux a, b), c],[1, hello(world?quux 3].
$ efmt weird_macro.erl
-module(weird_macro).
-define(FOO, /).
-define(BAR, :format().
-define(baz(A), A).
-define(qux, -> [1, 2, 3], [).
-define(quux, )], [2,).
-define(a(A, B), A).
-export([?baz(?baz(main))?FOO 0]).
hello(A) ->
io?BAR "hello ~p\n", [A]).
main() ?a(?qux a, b)
, c],
[1, hello(world?quux
3].
To make it possible, during the parse phase, efmt
collects macro definitions (i.e., -define
directives) and expands macro calls (i.e., ?MACRO_NAME
) to build an abstract syntax tree-like structure from the input text.
Then, during the format phase, efmt
traverses the tree and emits the formatted text representing each tree node.
When it visits tree nodes expanded from a macro, the formatted text of the original macro call is emitted instead.
efmt
doesn't process -include
and -include_lib
directives. The macros defined in those include files are expanded to an atom.
efmt
doesn't recognize the following directives relating to flow control:
-undef(Macro)
-ifdef(Macro)
-ifndef(Macro)
-else
-endif
-if(Condition)
-elif(Condigion)
Those are treated as ordinal attributes.
That is, both the "then" and "else" branch forms are always processed.
In many cases, this behavior doesn't cause a problem.
However, if either of the branches contains corrupted code, efmt
would fail to format the file (see the example code below).
-define(FOO, foo).
-ifdef(FOO).
%% As the `FOO` macro is defined in the same file, the Erlang compiler always evaluates this branch.
foo() -> ok.
-else.
%% On the other hand, this branch is always removed by the preprocessor.
%% But, `efmt` tries to parse this branch. Then it fails as the following function declaration is invalid.
foo()
-endif.