Packages

plymio_ast_vorm: Managing a Collection of Quoted Forms (Asts)

Current section

Files

Jump to
plymio_ast_vorm lib ast vorm utility pipeline.ex
Raw

lib/ast/vorm/utility/pipeline.ex

defmodule Plymio.Ast.Vorm.Utility.Pipeline do
@moduledoc false
alias Plymio.Option.Utility, as: POU
alias Plymio.Ast.Vorm, as: PAV
import Plymio.Ast.Vorm.Utility, only: [
new_error_result: 1,
]
use Plymio.Ast.Vorm.Attribute
@type items :: list
@type t :: %PAV{}
@type error :: struct
@pav_pipeline_verbs_dict_aliases @pav_pipeline_verb_aliases
|> POU.opts_create_aliases_dict
def opts_canon_keys(opts, dict \\ @pav_pipeline_verbs_dict_aliases) do
opts |> POU.opts_canonical_keys(dict)
end
defp canonical_verb(verb, dict \\ @pav_pipeline_verbs_dict_aliases) do
verb |> POU.canonical_key(dict)
end
defp pipeline_normalise_item_verb(verb)
defp pipeline_normalise_item_verb(verb) do
verb |> canonical_verb
end
defp pipeline_normalise_item_args(kv)
defp pipeline_normalise_item_args({verb,args})
when verb in [:add] do
{:ok, {verb, [args |> List.wrap]}}
end
defp pipeline_normalise_item_args({verb,args})
when verb in [:insert, :replace]
and is_list(args)
and length(args) >= 2 do
# slurp up all the args after the first (form_range) into new_forms
{:ok, {verb, [args |> hd, args |> tl |> List.wrap]}}
end
defp pipeline_normalise_item_args({verb,args}) when is_list(args) do
{:ok, {verb, args}}
end
defp pipeline_normalise_item_args({verb,args}) when is_nil(args) do
{:ok, {verb, [nil]}}
end
defp pipeline_normalise_item_args({verb,args}) do
{:ok, {verb, [args]}}
end
defp pipeline_normalise_item(item)
defp pipeline_normalise_item({k,v}) do
with {:ok, verb} <- k |> pipeline_normalise_item_verb,
{:ok, {_k,v}} = result when is_list(v) <- {verb, v} |> pipeline_normalise_item_args do
result
else
{:error, %KeyError{} = error} -> {:error, error |> struct!(term: {k,v})}
{:error, _} = result -> result
end
end
defp pipeline_normalise_item(v) when is_tuple(v) do
{[verb], args} = v
|> Tuple.to_list |> Enum.split(1)
# if a known verb convert. otherwise must be an ast
with {:ok, verb} <- verb |> canonical_verb do
{verb, args} |> pipeline_normalise_item
else
_ ->
v
|> Macro.validate
|> case do
:ok -> {:ok, {@pav_pipeline_verb_add, [v]}}
_ -> new_error_result(m: "form invalid", v: v)
end
end
end
defp pipeline_normalise_item(v) when is_list(v) do
case v |> Keyword.keyword? do
true ->
v
|> pipeline_normalise_items
_ ->
v
|> Enum.all?(fn v -> :ok = Macro.validate(v) end)
|> case do
true -> {:ok, {@pav_pipeline_verb_add, [v]}}
_ -> new_error_result(m: "invalid item", v: v)
end
end
end
defp pipeline_normalise_items(items)
defp pipeline_normalise_items(items) when is_list(items) do
items
|> Enum.reduce_while([], fn v, tuples ->
v
|> pipeline_normalise_item
|> case do
{:error, _} = result -> {:halt, result}
{:ok, values} -> {:cont, [values | tuples]}
{k,v} -> {:cont, [{k,v} | tuples]}
end
end)
|> case do
{:error, _} = result -> result
items -> {:ok, items |> Enum.reverse |> List.flatten}
end
end
defp pipeline_apply_items(state, items)
defp pipeline_apply_items(%PAV{} = state, items) when is_list(items) do
items
|> Enum.reduce_while(state,
fn {verb,args}, vorm ->
apply(PAV, verb, [vorm | args])
|> case do
{:error, _} = result -> {:halt, result}
{:ok, %PAV{} = vorm} -> {:cont, vorm}
end
end)
|> case do
{:error, _} = result -> result
%PAV{} = vorm -> {:ok, vorm}
end
end
@doc false
@spec pipeline(t, items) :: {:ok, t} | {:error, error}
def pipeline(vorm, items \\ [])
def pipeline(%PAV{} = state, []) do
{:ok, state}
end
def pipeline(%PAV{} = state, items) when is_list(items) do
with {:ok, tuples} <- items |> pipeline_normalise_items,
{:ok, %PAV{}} = result <- state |> pipeline_apply_items(tuples) do
result
else
{:error, _} = result -> result
end
end
end