Current section

Files

Jump to
orb lib orb control.ex
Raw

lib/orb/control.ex

defmodule Orb.Control do
import Orb.DSL, only: [__expand_identifier: 2, __get_block_items: 1]
@doc """
Declare a block, useful for structured control flow.
```elixir
defmodule Example do
use Orb
defw example do
Control.block Validate do
Validate.break(if: i < 0)
# Do something with i
end
end
end
```
"""
defmacro block(identifier, result_type \\ nil, do: block) do
identifier = __expand_identifier(identifier, __CALLER__)
result_type = Macro.expand_literals(result_type, __CALLER__)
block_items = __get_block_items(block)
# TODO: is there some way to do this using normal Elixir modules?
# I think we can define a module inline.
block_items =
Macro.prewalk(block_items, fn
{{:., _, [{:__aliases__, _, [identifier]}, :break]}, _, []} ->
quote do: %Orb.Block.Branch{identifier: unquote(identifier)}
{{:., _, [{:__aliases__, _, [identifier]}, :break]}, _, [[if: condition]]} ->
quote do: %Orb.Block.Branch{
identifier: unquote(identifier),
if: unquote(condition)
}
other ->
other
end)
quote do
%Orb.Block{
identifier: unquote(identifier),
push_type: unquote(result_type),
body: Orb.InstructionSequence.new(unquote(block_items))
}
end
end
@doc """
Break from a block.
```elixir
defmodule Example do
use Orb
defw example do
Control.block Validate do
# Other code…
Control.break(Validate)
end
end
end
```
"""
def break(identifier),
do: %Orb.Block.Branch{identifier: __expand_identifier(identifier, __ENV__)}
@doc """
Break from a block if a condition is true.
```elixir
defmodule Example do
use Orb
defw example do
Control.block Validate do
Validate.break(if: i < 0)
# Do something with i
end
end
end
```
"""
def break(identifier, if: condition),
do: %Orb.Block.Branch{identifier: __expand_identifier(identifier, __ENV__), if: condition}
def return(), do: Orb.Control.Return.new()
end