Sha256: 2e06f9b9a6488d54a26bc1650b1952853027699916fc5644d9a3d9210e209c11

Contents?: true

Size: 1.75 KB

Versions: 69

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

69 entries across 69 versions & 1 rubygems

Version Path
trackler-2.2.1.180 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.179 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.178 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.177 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.176 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.175 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.174 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.173 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.172 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.171 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.170 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.169 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.167 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.166 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.165 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.164 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.163 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.162 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.161 tracks/elixir/exercises/dot-dsl/example.exs
trackler-2.2.1.160 tracks/elixir/exercises/dot-dsl/example.exs