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
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}
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"]
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]"
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)"
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]"
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"]
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)"