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.