Sha256: afe9ace6ec6b0394e62a5fe26ba77b89df5720fec73aa981e18941c86a22d916

Contents?: true

Size: 1.75 KB

Versions: 327

Compression:

Stored size: 1.75 KB

Contents

defmodule Graph do
  defstruct attrs: [], nodes: [], edges: []
end

defmodule Dot do
  # Normally matching on keywords is a bad idea as keyword lists can have
  # several orders (i.e. `[a: 1, b: 2]` and `[b: 2, a: 1]`). But in this case
  # only one keyword is allowed, so it's safe.
  defmacro graph([do: ast]) do
    g = do_graph(ast)
    Macro.escape(
      %Graph{attrs: Enum.sort(g.attrs),
             nodes: Enum.sort(g.nodes),
             edges: Enum.sort(g.edges)})
  end

  defp do_graph(nil) do
    %Graph{}
  end
  defp do_graph({:__block__, _, stmts}) do
    Enum.reduce(stmts, %Graph{}, &do_stmt/2)
  end
  defp do_graph(stmt) do
    do_stmt(stmt, %Graph{})
  end

  defp do_stmt(stmt = {:graph, _, [kws]}, g) when is_list(kws) do
    if Keyword.keyword?(kws) do
      %{g | attrs: kws ++ g.attrs}
    else
      raise_invalid_stmt(stmt)
    end
  end
  defp do_stmt({atom, _, nil}, g) when is_atom(atom) and atom != :-- do
    %{g | nodes: [{atom, []} | g.nodes]}
  end
  defp do_stmt(stmt = {atom, _, [kws]}, g)
    when is_atom(atom) and atom != :-- and is_list(kws) do
    if Keyword.keyword?(kws) do
      %{g | nodes: [{atom, kws} | g.nodes]}
    else
      raise_invalid_stmt(stmt)
    end
  end
  defp do_stmt({:--, _, [{a, _, nil}, {b, _, nil}]}, g)
    when is_atom(a) and is_atom(b) do
    %{g | edges: [{a, b, []} | g.edges]}
  end
  defp do_stmt(stmt = {:--, _, [{a, _, nil}, {b, _, [kws]}]}, g)
    when is_atom(a) and is_atom(b) and is_list(kws) do
    if Keyword.keyword?(kws) do
      %{g | edges: [{a, b, kws} | g.edges]}
    else
      raise_invalid_stmt(stmt)
    end
  end
  defp do_stmt(stmt, _) do
    raise_invalid_stmt(stmt)
  end

  defp raise_invalid_stmt(stmt) do
    raise ArgumentError, message: "Invalid statement: #{inspect stmt}"
  end

end

Version data entries

327 entries across 327 versions & 1 rubygems

Version Path
trackler-2.2.1.109 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.108 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.107 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.106 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.105 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.104 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.103 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.102 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.101 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.100 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.99 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.98 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.97 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.96 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.95 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.94 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.93 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.92 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.91 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.90 tracks/elixir/exercises/dot-dsl/example.exs