How Macros and Directives are Handled ===================================== 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](https://www.erlang.org/doc/reference_manual/macros.html). Macros ------ 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. ### Original code ```erlang -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]. ``` ### Formatted code `$ efmt weird_macro.erl` ```erlang -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. `-include` and `-include_lib` Directives ---------------------------------------- `efmt` doesn't process `-include` and `-include_lib` directives. The macros defined in those include files are expanded to an atom. Flow Control Directives ----------------------- `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). ```erlang -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. ```