Current section
Files
Jump to
Current section
Files
lib/samples.ex
defmodule Samples do
def extract(contents, type) do
contents
|> extract_table_parts
|> (fn {vars, _type, keyword_lists} -> {vars, type, keyword_lists} end).()
|> process_table_parts
end
def extract(contents) do
contents
|> extract_table_parts
|> process_table_parts
end
defp extract_table_parts(contents) do
{vars, type, fields, fields_values} =
contents
|> normalize_contents
|> contents_to_table
|> slice_table
keyword_lists = zip_fields_and_values(fields, fields_values)
{vars, type, keyword_lists}
end
defp process_table_parts({[], type, keyword_lists}) do
to_typed_list(keyword_lists, type)
end
defp process_table_parts({vars, type, keyword_lists}) do
to_assignments(vars, type, keyword_lists)
end
defp slice_table(table) do
[header|rows] = table
{type, fields} = extract_type_and_fields(header)
{vars, fields_values} = extract_vars_and_fields_values(type, rows)
{vars, type, fields, fields_values}
end
defp to_assignments(vars, type, keyword_lists) do
vars
|> Enum.zip(keyword_lists)
|> Enum.map fn {var_name, value} ->
var = Macro.var(var_name, nil)
quote do
unquote(var) = unquote(replace_value(type, value))
end
end
end
defp to_typed_list(contents, nil) do
to_typed_list(contents, {:%{}, [], []})
end
defp to_typed_list(contents, type) do
Enum.map contents, fn item ->
replace_value(type, item)
end
end
def extract_type_and_fields([type = {atom, _, []}|fields]) when atom == :%{} do
{type, fields}
end
def extract_type_and_fields(fields = [{field, [_], _}|_]) when is_atom(field) do
{nil, Enum.map(fields, fn {field, [_], _} -> field end)}
end
def extract_type_and_fields(fields = [field|_]) when is_atom(field) do
{nil, fields}
end
def extract_type_and_fields([type|fields]) do
{type, fields}
end
def extract_vars_and_fields_values(nil, rows) do
{[], rows}
end
def extract_vars_and_fields_values(_type, rows) do
rows
|> Enum.map(fn [{var,[line: _line],_}|fields_values] -> {var, fields_values} end)
|> :lists.unzip
end
defp zip_fields_and_values(fields, rows) do
Enum.map rows, fn row ->
Enum.zip(fields, row)
end
end
# As structs by module name
defp replace_value({:__aliases__, [counter: _, line: _], [module]}, value) do
{:%, [], [{:__aliases__, [], [module]}, {:%{}, [], value}]}
end
# As structs
defp replace_value({:%, meta, [lhs, {:%{}, _, _value}]}, value) do
{:%, meta, [lhs, {:%{}, [], value}]}
end
# As maps
defp replace_value({:%{}, meta, []}, value) do
{:%{}, meta, value}
end
# As keyword list
defp replace_value([], value) do
value
end
defp contents_to_table(contents) do
case contents do
[do: nil] -> []
nil -> []
_ -> extract_rows(contents)
end
end
defp extract_rows(contents) do
contents |> Enum.map &extract_row(&1)
end
defp extract_row([row]) do
row |> extract_row
end
defp extract_row(row) do
row |> extract_cells([]) |> Enum.reverse
end
defp extract_cells({:|, _, [lhs, rhs]}, values) do
rhs |> extract_cells([lhs|values])
end
defp extract_cells(value, values) do
[value|values]
end
defp normalize_contents(contents) do
case contents do
[do: {:__block__, _, code}] -> code
[do: code] -> [code]
end
end
end