# `Lockstep.ErlangRewriter`
[🔗](https://github.com/b-erdem/lockstep/blob/v0.1.0/lib/lockstep/erlang_rewriter.ex#L1)

Rewrites Erlang source (`.erl`) so that vanilla OTP calls
(`gen_server:call`, `erlang:spawn`, `Pid ! Msg`, bare `receive`)
go through Lockstep's controller. Mirrors `Lockstep.Rewriter` but
on Erlang's abstract format instead of Elixir's macro AST.

## Why

Most of the BEAM ecosystem's foundational libraries are pure Erlang:
`:pg`, `gen_stage`, `gen_statem`, `:dets`, `sleeplocks`, the Erlang
internals of `:gen_server`, `:supervisor`, etc. Without an Erlang
rewriter, libraries that depend on these (Phoenix.PubSub, GenStage,
Cachex's transaction layer, libcluster, ...) fall outside Lockstep.
This module is the bridge.

## Mappings

    vanilla Erlang                    rewritten
    -------                           ---------
    gen_server:call(S, M)             'Elixir.Lockstep.GenServer':call(S, M)
    gen_server:cast(S, M)             'Elixir.Lockstep.GenServer':cast(S, M)
    gen_server:start_link(M, A, O)    'Elixir.Lockstep.GenServer':start_link(M, A, O)
    gen_server:reply(F, R)            'Elixir.Lockstep.GenServer':reply(F, R)
    gen_server:stop(S, R, T)          'Elixir.Lockstep.GenServer':stop(S, R, T)
    erlang:spawn(F)                   'Elixir.Lockstep':spawn(F)
    erlang:spawn_link(F)              'Elixir.Lockstep':spawn_link(F)
    erlang:send(D, M)                 'Elixir.Lockstep':send(D, M)
    erlang:monitor(process, P)        'Elixir.Lockstep':monitor(P)
    erlang:demonitor(R)               'Elixir.Lockstep':demonitor(R, [])
    erlang:demonitor(R, O)            'Elixir.Lockstep':demonitor(R, O)
    erlang:link(P)                    'Elixir.Lockstep':link(P)
    erlang:unlink(P)                  'Elixir.Lockstep':unlink(P)
    erlang:process_flag(F, V)         'Elixir.Lockstep':flag(F, V)
    erlang:is_process_alive(P)        'Elixir.Lockstep':'alive?'(P)
    erlang:send_after(T, D, M)        'Elixir.Lockstep':send_after(D, M, T)   ** arg reorder! **
    erlang:cancel_timer(R)            'Elixir.Lockstep':cancel_timer(R)
    Pid ! Msg                         'Elixir.Lockstep':send(Pid, Msg)
    receive Cls end                   case 'Elixir.Lockstep':recv_first(matcher) of Cls end
    receive Cls after T -> Body end   timer + recv_first dispatch (see below)

Also handles bare BIFs (`spawn(F)` without `erlang:` prefix), which
are the auto-imported BIFs from the Erlang module.

## Limitations (v0.1)

  * `gen_server:start_link({local, Name}, M, A, O)` 4-arg form not
    yet supported. Use the 3-arg form (no name) and pass the pid
    around explicitly.
  * Pre-bound variables in receive patterns lose their "pin"
    semantics (the matcher does structural match only). Most code
    doesn't rely on this; use guards explicitly if you do.
  * `gen_statem`, `supervisor`, `:pg` are not yet wrapper-rewritten
    individually. They could be added by extending the call mappings.

# `rewrite_and_compile`

```elixir
@spec rewrite_and_compile(
  Path.t(),
  keyword()
) :: {:ok, atom(), binary()} | {:error, list(), list()}
```

Rewrite a `.erl` file's forms and compile directly to a `.beam`
binary. Skips the `.erl` round-trip. Returns `{:ok, module, binary}`
or `{:error, errors, warnings}`.

# `rewrite_file`

```elixir
@spec rewrite_file(Path.t(), Path.t(), keyword()) ::
  {:ok, Path.t()} | {:error, term()}
```

Read `.erl` file, parse, rewrite, write back to `output_path`.
Returns `{:ok, output_path}` on success.

# `rewrite_forms`

```elixir
@spec rewrite_forms([tuple()]) :: [tuple()]
```

Walk a list of Erlang forms and rewrite. Returns the new forms.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
