Packages
igniter
0.1.4
0.8.2
0.8.1
0.8.0
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.30
0.6.29
0.6.28
0.6.27
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.52
0.5.51
0.5.50
0.5.49
0.5.48
0.5.47
0.5.46
0.5.45
0.5.44
0.5.43
0.5.42
0.5.41
0.5.40
0.5.39
0.5.38
0.5.37
0.5.36
0.5.35
0.5.34
0.5.33
0.5.32
0.5.31
0.5.30
0.5.29
0.5.28
0.5.27
0.5.26
0.5.25
0.5.24
0.5.23
0.5.22
0.5.21
0.5.20
0.5.19
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.78
0.3.77
0.3.76
0.3.75
0.3.74
0.3.73
0.3.72
0.3.71
0.3.70
0.3.69
0.3.68
0.3.67
0.3.66
0.3.65
0.3.64
0.3.63
0.3.62
0.3.61
0.3.60
0.3.59
0.3.58
0.3.57
0.3.56
0.3.55
0.3.54
0.3.53
0.3.52
0.3.51
0.3.50
0.3.49
0.3.48
0.3.47
0.3.46
0.3.45
0.3.44
0.3.43
0.3.42
0.3.41
0.3.40
0.3.39
0.3.38
0.3.37
0.3.36
0.3.35
0.3.34
0.3.33
0.3.32
0.3.31
0.3.30
0.3.29
0.3.28
0.3.27
0.3.26
0.3.25
0.3.24
0.3.23
0.3.22
0.3.21
0.3.20
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
A code generation and project patching framework
Current section
Files
Jump to
Current section
Files
lib/code/function.ex
defmodule Igniter.Code.Function do
@moduledoc """
Utilities for working with functions.
"""
alias Igniter.Code.Common
alias Sourceror.Zipper
@doc "Returns `true` if the argument at the provided index exists and matches the provided pattern"
defmacro argument_matches_pattern?(zipper, index, pattern) do
quote do
Igniter.Code.Function.argument_matches_predicate?(
unquote(zipper),
unquote(index),
fn zipper ->
code_at_node =
zipper
|> Sourceror.Zipper.subtree()
|> Sourceror.Zipper.root()
match?(unquote(pattern), code_at_node)
end
)
end
end
@doc "Moves to a function call by the given name and arity, matching the given predicate, in the current scope"
@spec move_to_function_call_in_current_scope(Zipper.t(), atom, non_neg_integer()) ::
{:ok, Zipper.t()} | :error
def move_to_function_call_in_current_scope(zipper, name, arity, predicate \\ fn _ -> true end)
def move_to_function_call_in_current_scope(zipper, name, [arity | arities], predicate)
when is_list(arities) do
case move_to_function_call_in_current_scope(zipper, name, arity, predicate) do
:error ->
move_to_function_call_in_current_scope(zipper, name, arities, predicate)
{:ok, zipper} ->
{:ok, zipper}
end
end
def move_to_function_call_in_current_scope(_, _, [], _) do
:error
end
def move_to_function_call_in_current_scope(%Zipper{} = zipper, name, arity, predicate) do
zipper
|> Common.maybe_move_to_block()
|> Common.move_right(fn zipper ->
function_call?(zipper, name, arity) && predicate.(zipper)
end)
end
@doc "Returns `true` if the node is a function call of the given name and arity"
@spec function_call?(Zipper.t(), atom, non_neg_integer()) :: boolean()
def function_call?(%Zipper{} = zipper, name, arity) do
zipper
|> Common.maybe_move_to_block()
|> Zipper.subtree()
|> Zipper.root()
|> case do
{^name, _, args} ->
Enum.count(args) == arity
{{^name, _, context}, _, args} when is_atom(context) ->
Enum.count(args) == arity
{:|>, _, [{^name, _, context} | rest]} when is_atom(context) ->
Enum.count(rest) == arity - 1
{:|>, _, [^name | rest]} ->
Enum.count(rest) == arity - 1
_ ->
false
end
end
@doc "Returns `true` if the node is a function call of the given name"
@spec function_call?(Zipper.t(), atom) :: boolean()
def function_call?(%Zipper{} = zipper, name) do
zipper
|> Common.maybe_move_to_block()
|> Zipper.subtree()
|> Zipper.root()
|> case do
{^name, _, _} ->
true
{{^name, _, context}, _, _} when is_atom(context) ->
true
{:|>, _, [{^name, _, context} | _rest]} when is_atom(context) ->
true
{:|>, _, [^name | _rest]} ->
true
_ ->
false
end
end
@doc "Returns `true` if the node is a function call"
@spec function_call?(Zipper.t()) :: boolean()
def function_call?(%Zipper{} = zipper) do
zipper
|> Common.maybe_move_to_block()
|> Zipper.subtree()
|> Zipper.root()
|> case do
{:|>, _, [{name, _, context} | _rest]} when is_atom(context) and is_atom(name) ->
true
{:|>, _, [name | _rest]} when is_atom(name) ->
true
{name, _, _} when is_atom(name) ->
true
{{name, _, context}, _, _} when is_atom(context) and is_atom(name) ->
true
_ ->
false
end
end
def update_nth_argument(zipper, index, func) do
if pipeline?(zipper) do
if index == 0 do
zipper
|> Zipper.down()
|> case do
nil ->
:error
zipper ->
func.(zipper)
end
else
zipper
|> Zipper.down()
|> case do
nil ->
:error
zipper ->
zipper
|> Zipper.rightmost()
|> Zipper.down()
|> case do
nil ->
:error
zipper ->
zipper
|> Common.nth_right(index)
|> case do
:error ->
:error
{:ok, nth} ->
func.(nth)
end
end
end
end
else
zipper
|> Zipper.down()
|> case do
nil ->
:error
zipper ->
zipper
|> Common.nth_right(index)
|> case do
:error ->
:error
{:ok, nth} ->
func.(nth)
end
end
end
end
def move_to_nth_argument(zipper, index) do
if pipeline?(zipper) do
if index == 0 do
zipper
|> Zipper.down()
|> case do
nil ->
:error
zipper ->
{:ok, zipper}
end
else
zipper
|> Zipper.down()
|> case do
nil ->
:error
zipper ->
zipper
|> Zipper.rightmost()
|> Zipper.down()
|> case do
nil ->
:error
zipper ->
zipper
|> Common.nth_right(index)
|> case do
:error ->
:error
{:ok, nth} ->
{:ok, nth}
end
end
end
end
else
zipper
|> Zipper.down()
|> case do
nil ->
:error
zipper ->
zipper
|> Common.nth_right(index)
|> case do
:error ->
:error
{:ok, nth} ->
{:ok, nth}
end
end
end
end
def append_argument(zipper, value) do
zipper
|> Zipper.append_child(value)
end
def argument_matches_predicate?(zipper, index, func) do
if pipeline?(zipper) do
if index == 0 do
zipper
|> Zipper.down()
|> case do
nil -> nil
zipper -> func.(zipper)
end
else
zipper
|> Zipper.down()
|> case do
nil ->
nil
zipper ->
zipper
|> Zipper.rightmost()
|> Zipper.down()
|> case do
nil ->
nil
zipper ->
zipper
|> Common.nth_right(index - 1)
|> case do
:error ->
false
{:ok, zipper} ->
zipper
|> Common.maybe_move_to_block()
|> func.()
end
end
end
end
else
zipper
|> Zipper.down()
|> case do
nil ->
false
zipper ->
zipper
|> Common.nth_right(index)
|> case do
:error ->
false
{:ok, zipper} ->
zipper
|> Common.maybe_move_to_block()
|> func.()
end
end
end
end
def pipeline?(zipper) do
zipper
|> Zipper.subtree()
|> Zipper.root()
|> case do
{:|>, _, _} -> true
_ -> false
end
end
end