Skip to content

Latest commit

 

History

History
101 lines (71 loc) · 2.72 KB

MACRO_AND_DIRECTIVE.md

File metadata and controls

101 lines (71 loc) · 2.72 KB

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.

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

-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

-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).

-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.