plymio_fontais v0.2.0 Plymio.Fontais.Form View Source

Functions for Quoted Forms (Asts)

See Plymio.Fontais for overview and documentation terms.

Link to this section Summary

Functions

form_validate/1 calls Macro.validate/1 on the argument (the expected form) and if the result is :ok returns {:ok, form}, else {:error, error}

forms_edit/1 takes zero, one or more form and an optional opts, normalises the forms and then applies the edit verbs given in the opts, returning {:ok, forms}.

forms_normalise/1 takes zero, one or more form and normalises them to a forms returning {:ok, forms}

forms_reduce/1 takes a zero, one or more form, normalises them, and reduces the forms to a single form using Kernel.SpecialForms.unquote_splicing/1

forms_validate/1 validates the forms using form_validate/1 on each form, returning {:ok, forms} if all are valid, else {:error, error}

opts_forms_normalise/2 takes an opts, a key and an optional default, gets all the key’s values, or uses the default if none, and calls forms_normalise/1 on the values returning {:ok, forms}

opts_forms_reduce/2 takes an opts and a key, gets all the key’s values and calls forms_reduce/1 on the values returning {:ok, forms}

Link to this section Types

Link to this section Functions

Link to this function form_validate(form) View Source
form_validate(any()) :: {:ok, form()} | {:error, error()}

form_validate/1 calls Macro.validate/1 on the argument (the expected form) and if the result is :ok returns {:ok, form}, else {:error, error}.

Examples

iex> 1 |> form_validate
{:ok, 1}

iex> nil |> form_validate # nil is a valid ast
{:ok, nil}

iex> [:x, :y] |> form_validate
{:ok, [:x, :y]}

iex> form = {:x, :y} # this 2tuple is a valid form without escaping
...> form |> form_validate
{:ok, {:x, :y}}

iex> {:error, error} = {:x, :y, :z} |> form_validate
...> error |> Exception.message
"form invalid, got: {:x, :y, :z}"

iex> {:error, error} = %{a: 1, b: 2, c: 3} |> form_validate # map not a valid form
...> error |> Exception.message
"form invalid, got: %{a: 1, b: 2, c: 3}"

iex> form = %{a: 1, b: 2, c: 3} |> Macro.escape # escaped map is a valid form
...> form |> form_validate
{:ok,  %{a: 1, b: 2, c: 3} |> Macro.escape}
Link to this function forms_edit(forms, opts \\ []) View Source
forms_edit(any(), any()) :: {:ok, forms()} | {:error, error()}

forms_edit/1 takes zero, one or more form and an optional opts, normalises the forms and then applies the edit verbs given in the opts, returning {:ok, forms}.

Examples

No opts just returns the normalise forms.

iex> {:ok, forms} = [quote(do: x = x + 1), quote(do: x = x * x), quote(do: x = x - 1)]
...> |> forms_edit
...> forms |> Enum.map(&Macro.to_string/1)
["x = x + 1", "x = x * x", "x = x - 1"]

The forms can be mapped by an arbitrary function using the :transform verb:

iex> {:ok, forms} = [quote(do: x = x + 1), quote(do: x = x * x), quote(do: x = x - 1)]
...> |> forms_edit(transform: fn _ -> 42 end)
...> forms |> Enum.map(&Macro.to_string/1)
["42", "42", "42"]

The forms can be mapped by Macro.postwalk/2 using the :postwalk verb, here renaming the x var to a:

iex> {:ok, forms} = [quote(do: x = x + 1), quote(do: x = x * x), quote(do: x = x - 1)]
...> |> forms_edit(postwalk: fn
...>   {:x, ctx, mod} when is_atom(mod) -> {:a, ctx, mod}
...>   v -> v
...> end)
...> forms |> Enum.map(&Macro.to_string/1)
["a = a + 1", "a = a * a", "a = a - 1"]

For convenvenience, a :replace_vars verb is supported that packages up the postwalk in the previous example:

iex> {:ok, forms} = [quote(do: x = x + 1), quote(do: x = x * x), quote(do: x = x - 1)]
...> |> forms_edit(replace_vars: [
...>   x: Macro.var(:y, nil)
...> ])
...> forms |> Enum.map(&Macro.to_string/1)
["y = y + 1", "y = y * y", "y = y - 1"]

Similarly, the :rename_vars verb takes a Keyword where the values are atoms and renames any vars found in the Keyword:

iex> {:ok, forms} = [quote(do: x = x + 1), quote(do: x = x * x), quote(do: x = x - 1)]
...> |> forms_edit(rename_vars: [
...>   x: :z,
...>   p: :q
...> ])
...> forms |> Enum.map(&Macro.to_string/1)
["z = z + 1", "z = z * z", "z = z - 1"]

The :rename_atoms verb takes a Keyword where the values are the replacement atoms:

iex> {:ok, forms} = [quote(do: x = :x_add_1), quote(do: x = :x_mul_x), quote(do: x = :x_sub_1)]
...> |> forms_edit(rename_atoms: [
...>   x: :z,
...>   x_add_1: :add_1_to_x,
...>   x_sub_1: :takeaway_1_from_x
...> ])
...> forms |> Enum.map(&Macro.to_string/1)
["x = :add_1_to_x", "x = :x_mul_x", "x = :takeaway_1_from_x"]

The replace_atoms verb takes a Keyword where the values are the replacement term:

iex> {:ok, forms} = [quote(do: x = :x_add_1), quote(do: x = :x_mul_x), quote(do: x = :x_sub_1)]
...> |> forms_edit(replace_atoms: [
...>   x_add_1: quote(do: x + 1),
...>   x_mul_x: quote(do: x * x),
...>   x_sub_1: quote(do: x - 1),
...> ])
...> forms |> Enum.map(&Macro.to_string/1)
["x = x + 1", "x = x * x", "x = x - 1"]

The :rename_funs verb takes a Keyword where the values are the replacement fun names:

iex> {:ok, forms} = [
...>    quote do
...>      def x_add_1(x), do: x + 1
...>    end,
...>    quote do
...>      def x_mul_x(x), do: x * x
...>    end,
...>    quote do
...>      def x_sub_1(x), do: x - 1
...>    end
...> ] |> forms_edit(rename_funs: [
...>   x_mul_x: :times_x,
...>   x_sub_1: :decr_x
...> ])
...> forms |> Enum.map(&Macro.to_string/1)
["def(x_add_1(x)) do\n  x + 1\nend",
 "def(times_x(x)) do\n  x * x\nend",
 "def(decr_x(x)) do\n  x - 1\nend"]
Link to this function forms_normalise(forms \\ []) View Source
forms_normalise(any()) :: {:ok, forms()} | {:error, error()}

forms_normalise/1 takes zero, one or more form and normalises them to a forms returning {:ok, forms}.

The list is first flattened and any nils or empty blocks are removed.

Examples

iex> {:ok, forms} = quote(do: a = x + y) |> forms_normalise
...> forms |> hd |> Macro.to_string
"a = x + y"

iex> {:ok, forms} = [
...>  quote(do: a = x + y),
...>  quote(do: a * c)
...> ] |> forms_normalise
...> forms |> Macro.to_string
"[a = x + y, a * c]"

iex> nil |> forms_normalise
{:ok, []}

iex> {:ok, form} = [
...>  quote(do: a = x + y),
...>  nil,
...>  [
...>   quote(do: b = a / c),
...>   nil,
...>   quote(do: d = b * b),
...>  ],
...>  quote(do: e = a + d),
...> ] |> forms_normalise
...> form |> Macro.to_string
"[a = x + y, b = a / c, d = b * b, e = a + d]"
Link to this function forms_reduce(asts \\ []) View Source
forms_reduce(any()) :: {:ok, form()} | {:error, error()}

forms_reduce/1 takes a zero, one or more form, normalises them, and reduces the forms to a single form using Kernel.SpecialForms.unquote_splicing/1.

Top level block (:__block__) forms are “deblocked” into their constituent statements (the args).

If the reduction suceeds, {:ok, reduced_form} is returned, else {:error, error}.

An empty list reduces to {:ok, nil}.

Examples

iex> {:ok, reduced_form} = quote(do: a = x + y) |> forms_reduce
...> reduced_form |> Macro.to_string
"a = x + y"

iex> {:ok, reduced_form} = quote do
...>   x = x + 1
...>   x = x * x
...>   x = x - 1
...> end |> forms_reduce
...> reduced_form |> Macro.to_string
"(\n  x = x + 1\n  x = x * x\n  x = x - 1\n)"

iex> {:ok, reduced_form} = [
...>  quote(do: a = x + y),
...>  quote(do: a * c)
...> ] |> forms_reduce
...> reduced_form |> Macro.to_string
"(\n  a = x + y\n  a * c\n)"

iex> {:ok, form} = nil |> forms_reduce
...> form |> Macro.to_string
"nil"

iex> {:ok, form} = [
...>  quote(do: a = x + y),
...>  nil,
...>  [
...>   quote(do: b = a / c),
...>   nil,
...>   quote(do: d = b * b),
...>  ],
...>  quote(do: e = a + d),
...> ] |> forms_reduce
...> form |> Macro.to_string
"(\n  a = x + y\n  b = a / c\n  d = b * b\n  e = a + d\n)"
Link to this function forms_validate(forms) View Source
forms_validate(any()) :: {:ok, forms()} | {:error, error()}

forms_validate/1 validates the forms using form_validate/1 on each form, returning {:ok, forms} if all are valid, else {:error, error}.

Examples

iex> [1, 2, 3] |> forms_validate
{:ok, [1, 2, 3]}

iex> [1, {2, 2}, :three] |> forms_validate
{:ok, [1, {2, 2}, :three]}

iex> {:error, error} = [1, {2, 2, 2}, %{c: 3}] |> forms_validate
...> error |> Exception.message
"forms invalid, got invalid indices: [1, 2]"
Link to this function opts_forms_normalise(opts, form_key, default \\ nil) View Source
opts_forms_normalise(any(), any(), any()) ::
  {:ok, forms()} | {:error, error()}

opts_forms_normalise/2 takes an opts, a key and an optional default, gets all the key’s values, or uses the default if none, and calls forms_normalise/1 on the values returning {:ok, forms}.

Examples

iex> opts = [form: quote(do: a = x + y) , form: quote(do: b = p * q)]
...> {:ok, forms} = opts |> opts_forms_normalise(:form)
...> forms |> Enum.map(&Macro.to_string/1)
["a = x + y", "b = p * q"]

iex> {:ok, forms} = [
...>  form1: quote(do: a = x + y),
...>  form2: quote(do: b = p * q),
...>  form1: quote(do: c = j - k),
...> ] |> opts_forms_normalise(:form1)
...> forms |> Enum.map(&Macro.to_string/1)
["a = x + y", "c = j - k"]

iex> opts = []
...> {:ok, forms} = opts |> opts_forms_normalise(:form)
...> forms |> Enum.map(&Macro.to_string/1)
[]

iex> opts = []
...> {:ok, forms} = opts |> opts_forms_normalise(:form, quote(do: a = x + y))
...> forms |> Enum.map(&Macro.to_string/1)
["a = x + y"]

iex> {:ok, forms} = [
...>  form: quote(do: a = x + y),
...>  form: nil,
...>  form: [
...>   quote(do: b = a / c),
...>   nil,
...>   quote(do: d = b * b),
...>  ],
...>  form: quote(do: e = a + d),
...> ] |> opts_forms_normalise(:form)
...> forms |> Enum.map(&Macro.to_string/1)
["a = x + y", "b = a / c", "d = b * b", "e = a + d"]
Link to this function opts_forms_reduce(opts, form_key, default \\ nil) View Source
opts_forms_reduce(any(), any(), any()) :: {:ok, forms()} | {:error, error()}

opts_forms_reduce/2 takes an opts and a key, gets all the key’s values and calls forms_reduce/1 on the values returning {:ok, forms}.

Examples

iex> opts = [form: quote(do: a = x + y) , form: quote(do: b = p * q)]
...> {:ok, form} = opts |> opts_forms_reduce(:form)
...> form |> Macro.to_string
"(\n  a = x + y\n  b = p * q\n)"

iex> {:ok, form} = [
...>  form1: quote(do: a = x + y),
...>  form2: quote(do: b = p * q),
...>  form1: quote(do: c = j - k),
...> ] |> opts_forms_reduce(:form1)
...> form |> Macro.to_string
"(\n  a = x + y\n  c = j - k\n)"

iex> opts = []
...> opts |> opts_forms_reduce(:form)
{:ok, nil}

iex> opts = []
...> {:ok, form} = opts |> opts_forms_reduce(:form, quote(do: a = x + y))
...> form |> Macro.to_string
"a = x + y"

iex> {:ok, form} = [
...>  form1: quote(do: a = x + y),
...>  form2: nil,
...>  form3: [
...>   quote(do: b = a / c),
...>   nil,
...>   quote(do: d = b * b),
...>  ],
...>  form4: quote(do: e = a + d),
...> ] |> opts_forms_reduce(:form3)
...> form |> Macro.to_string
"(\n  b = a / c\n  d = b * b\n)"