diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 7a30282..5563c0e 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -15,8 +15,8 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - elixir: [1.15.8, 1.16.3] - otp: [25.3.2.12, 26.2.5] + elixir: [1.17.1] + otp: [26.2.5.1, 27.0] steps: - name: Cancel Previous Runs uses: styfle/cancel-workflow-action@0.12.1 @@ -60,8 +60,8 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - elixir: [1.15.8, 1.16.3] - otp: [25.3.2.12, 26.2.5] + elixir: [1.17.1] + otp: [26.2.5.1, 27.0] steps: - name: Cancel Previous Runs uses: styfle/cancel-workflow-action@0.12.1 @@ -125,8 +125,8 @@ jobs: strategy: fail-fast: false matrix: - elixir: [1.15.8, 1.16.3] - otp: [25.3.2.12, 26.2.5] + elixir: [1.17.1] + otp: [26.2.5.1, 27.0] steps: - name: Cancel Previous Runs uses: styfle/cancel-workflow-action@0.12.1 diff --git a/.tool-versions b/.tool-versions index f41c57f..65587b0 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -elixir 1.16.3 -erlang 26.2.5 +elixir 1.17.1 +erlang 27.0 diff --git a/lib/umwelt.ex b/lib/umwelt.ex index 7543e79..76e72ea 100644 --- a/lib/umwelt.ex +++ b/lib/umwelt.ex @@ -1,5 +1,5 @@ defmodule Umwelt do @moduledoc """ - Documentation for `Umwelt`. + Root of `Umwelt`. """ end diff --git a/lib/umwelt/parser.ex b/lib/umwelt/parser.ex index 277267e..100a8d5 100644 --- a/lib/umwelt/parser.ex +++ b/lib/umwelt/parser.ex @@ -5,6 +5,16 @@ defmodule Umwelt.Parser do alias Umwelt.{Files, Parser} + def parse_raw(code) do + case read_ast({:ok, code}) do + {:ok, ast} -> + parse_ast({:ok, ast}) + + {:error, message} -> + {:error, message} + end + end + def parse_source(project) do Map.merge( parse_root_source(project), @@ -12,28 +22,16 @@ defmodule Umwelt.Parser do ) end + def read_ast({:ok, code}), do: Code.string_to_quoted(code) def read_ast({:error, msg}), do: {:error, msg} - - def read_ast({:ok, code}), - do: Code.string_to_quoted(code) - - def maybe_list_parse(ast, aliases) when is_list(ast), - do: parse_list(ast, aliases) - - def maybe_list_parse(ast, aliases), - do: parse(ast, aliases) - - def parse_list(ast, aliases) when is_list(ast), - do: Enum.map(ast, &parse(&1, aliases)) - - def parse({:ok, ast}), - do: ast |> parse([]) |> index() - - def parse({:error, _}), do: %{[] => %{}} + def read_ast(code) when is_binary(code), do: read_ast({:ok, code}) def parse_root({:ok, ast}), do: ast |> Parser.Root.parse() |> index() + def parse_ast({:ok, ast}), do: ast |> parse([]) |> index() + def parse_ast({:error, _}), do: %{[] => %{}} + def parse(ast, aliases) when is_macro(ast), do: Parser.Macro.parse(ast, aliases) @@ -50,6 +48,15 @@ defmodule Umwelt.Parser do def parse(ast, _aliases), do: Parser.Literal.parse(ast) + def maybe_list_parse(ast, aliases) when is_list(ast), + do: parse_list(ast, aliases) + + def maybe_list_parse(ast, aliases), + do: parse(ast, aliases) + + def parse_list(ast, aliases) when is_list(ast), + do: Enum.map(ast, &parse(&1, aliases)) + defp index(parsed) do parsed |> inner_modules() @@ -89,7 +96,7 @@ defmodule Umwelt.Parser do defp parse_other_sources(project) do project |> Files.list_root_dir() - |> Enum.map(&(&1 |> File.read() |> Parser.read_ast() |> Parser.parse())) + |> Enum.map(&(&1 |> File.read() |> read_ast() |> parse_ast())) |> Enum.reduce(&Map.merge/2) end end diff --git a/mix.exs b/mix.exs index 0d3d292..b61479a 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Umwelt.MixProject do def project do [ app: :umwelt, - version: "0.1.4", + version: "0.1.5", elixir: "~> 1.15", compilers: [:leex, :yecc] ++ Mix.compilers(), start_permanent: Mix.env() == :prod, diff --git a/mix.lock b/mix.lock index 6ee4140..0736b5c 100644 --- a/mix.lock +++ b/mix.lock @@ -1,10 +1,10 @@ %{ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "credo": {:hex, :credo, "1.7.6", "b8f14011a5443f2839b04def0b252300842ce7388f3af177157c86da18dfbeea", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "146f347fb9f8cbc5f7e39e3f22f70acbef51d441baa6d10169dd604bfbc55296"}, + "credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"}, "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, - "ex_doc": {:hex, :ex_doc, "0.34.0", "ab95e0775db3df71d30cf8d78728dd9261c355c81382bcd4cefdc74610bef13e", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "60734fb4c1353f270c3286df4a0d51e65a2c1d9fba66af3940847cc65a8066d7"}, + "ex_doc": {:hex, :ex_doc, "0.34.1", "9751a0419bc15bc7580c73fde506b17b07f6402a1e5243be9e0f05a68c723368", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "d441f1a86a235f59088978eff870de2e815e290e44a8bd976fe5d64470a4c9d2"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, diff --git a/test/umwelt/parser_test.exs b/test/umwelt/parser_test.exs index 5776a6a..a14c5c5 100644 --- a/test/umwelt/parser_test.exs +++ b/test/umwelt/parser_test.exs @@ -40,6 +40,193 @@ defmodule Umwelt.ParserTest do {:ok, code: code} end + describe "parse_raw" do + test "just a function" do + code = """ + defmodule Foobar do + def foo(bar, baz) + end + """ + + assert %{ + ["Foobar"] => %{ + functions: [ + %{ + arguments: [ + %{type: %{type: :anything, kind: :Literal}, body: "bar", kind: :Variable}, + %{type: %{type: :anything, kind: :Literal}, body: "baz", kind: :Variable} + ], + body: "foo", + kind: :Function + } + ], + context: ["Foobar"], + body: "Foobar", + kind: :Concept, + guards: [], + types: [], + attrs: [], + calls: [] + } + } == Parser.parse_raw(code) + end + + test "attr and function" do + code = """ + defmodule Context do + @fizz :buzz + def foo(bar, baz) + end + """ + + assert %{ + ["Context"] => %{ + functions: [ + %{ + arguments: [ + %{type: %{type: :anything, kind: :Literal}, body: "bar", kind: :Variable}, + %{type: %{type: :anything, kind: :Literal}, body: "baz", kind: :Variable} + ], + body: "foo", + kind: :Function + } + ], + context: ["Context"], + body: "Context", + kind: :Concept, + guards: [], + types: [], + attrs: [ + %{ + value: %{type: %{type: :atom, kind: :Literal}, body: "buzz", kind: :Value}, + body: "fizz", + kind: :Attr + } + ], + calls: [] + } + } == Parser.parse_raw(code) + end + + test "several modules", %{code: code} do + assert %{ + ["Root"] => %{ + functions: [ + %{ + arguments: [ + %{type: %{type: :anything, kind: :Literal}, body: "once", kind: :Variable} + ], + body: "root_one", + kind: :Function + }, + %{ + arguments: [ + %{type: %{type: :anything, kind: :Literal}, body: "twice", kind: :Variable} + ], + body: "root_two", + kind: :Function + } + ], + context: ["Root"], + body: "Root", + kind: :Concept, + guards: [], + types: [], + attrs: [ + %{ + value: %{ + type: %{type: :atom, kind: :Literal}, + body: "root_attribute", + kind: :Value + }, + body: "root_attr", + kind: :Attr + } + ], + calls: [], + note: "Root description" + }, + ["Root", "Foo"] => %{ + functions: [ + %{ + arguments: [ + %{type: %{type: :anything, kind: :Literal}, body: "bar", kind: :Variable} + ], + body: "foo", + kind: :Function + } + ], + context: ["Root", "Foo"], + body: "Foo", + kind: :Concept, + guards: [], + types: [], + attrs: [ + %{ + value: %{ + type: %{type: :atom, kind: :Literal}, + body: "baz_attribute", + kind: :Value + }, + body: "baz_attr", + kind: :Attr + }, + %{ + value: %{ + type: %{type: :atom, kind: :Literal}, + body: "bar_attribute", + kind: :Value + }, + body: "bar_attr", + kind: :Attr + } + ], + calls: [], + note: "Foo description" + }, + ["Root", "Foo", "Bar"] => %{ + functions: [ + %{ + arguments: [ + %{type: %{type: :anything, kind: :Literal}, body: "baz", kind: :Variable} + ], + body: "bar", + kind: :Function + } + ], + context: ["Root", "Foo", "Bar"], + body: "Bar", + kind: :Concept, + guards: [], + types: [], + attrs: [], + calls: [], + note: "Bar description" + }, + ["Root", "Foo", "Baz"] => %{ + functions: [ + %{ + arguments: [ + %{type: %{type: :anything, kind: :Literal}, body: "foo", kind: :Variable} + ], + body: "baz", + kind: :Function + } + ], + context: ["Root", "Foo", "Baz"], + body: "Baz", + kind: :Concept, + guards: [], + types: [], + attrs: [], + calls: [], + note: "Baz description" + } + } == + Parser.parse_raw(code) + end + end + describe "reading ast" do test "read_ast when error" do assert {:error, "read failed"} ==