Skip to content

Commit

Permalink
Merge pull request #9 from hrzndhrn/issue/8
Browse files Browse the repository at this point in the history
Comprehensions fix
  • Loading branch information
NickNeck authored Jun 15, 2023
2 parents 7a890f8 + 252e8f0 commit b13ec25
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 12 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
# Changelog

## 0.5.1 - 2023/06/15

+ Fix comprehensions
+ Add `:%{}` to `@none_unquotes`

## 0.5.0 - 2023/05/05

+ Require Elixir 1.13
+ Add `BeamFile.elixir_quoted/2`
+ `BeamFile.elixir_code/2` uses now `docs: false` as default opts.
+ `BeamFile.elixir_code/2` uses now `docs: false` as default opts
+ Refactor code to fix a bunch of errors

## 0.4.2 - 2023/04/03
Expand Down
6 changes: 4 additions & 2 deletions lib/beam_file.ex
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ defmodule BeamFile do
:bin_element,
0,
{:string, 0,
[240, 105, 247, 119, 22, 50, 219, 207, 90, 95, 127, 92, 159, 46, 131, 169]},
[166, 117, 1, 22, 146, 56, 30, 199, 203, 141, 158, 223, 3, 11, 225, 190]},
:default,
:default
}
Expand Down Expand Up @@ -632,7 +632,9 @@ defmodule BeamFile do
true
"""
@spec erl_code(input()) :: {:ok, String.t()} | {:error, any()}
def erl_code({:module, _name, bin, _context}), do: erl_code(bin)
def erl_code({:module, _name, bin, _context}) do
erl_code(bin)
end

def erl_code(input) when is_atom(input) do
with {:ok, path} <- path(input) do
Expand Down
12 changes: 12 additions & 0 deletions lib/beam_file/normalizer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule BeamFile.Normalizer do

@chars Enum.to_list(?a..?z) ++ Enum.to_list(?A..?Z) ++ Enum.to_list(?0..?9) ++ [??, ?!, ?_]
@none_unquotes [
:%{},
:{},
:->,
:<<>>,
Expand Down Expand Up @@ -92,6 +93,17 @@ defmodule BeamFile.Normalizer do
{{:., meta1, [:erlang, :binary_to_atom]}, meta2, [arg, {:__block__, [], [:utf8]}]}
end

def normalize({:for, meta, args}) when is_list(args) do
{args, [last]} = Enum.split(args, -1)

# put the :do to the end of the keyword list
block = Keyword.fetch!(last, :do)
last = last |> Keyword.delete(:do) |> Enum.concat([{:do, block}])

args = normalize(args ++ [last])
{:for, meta, args}
end

def normalize({expr, meta, args}) do
if unquote?(expr) do
{{:unquote, [], [expr]}, meta, normalize(args)}
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule BeamFile.MixProject do
def project do
[
app: :beam_file,
version: "0.5.0",
version: "0.5.1",
elixir: "~> 1.13",
description: "An interface to the BEAM file format and a decompiler",
start_permanent: Mix.env() == :prod,
Expand Down
16 changes: 11 additions & 5 deletions test/beam_file_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ defmodule BeamFileTest do

alias BeamFile.Error

if TestSupport.version?("~> 1.14") and TestSupport.otp_release?(25) do
if TestSupport.version?("~> 1.14") and TestSupport.otp_release?(26) do
doctest(BeamFile)
end

@math_beam_path "_build/test/lib/beam_file/ebin/Elixir.Math.beam"
@math_abstract_code TestSupport.fixture_version("math_abstract_code.exs")
@math_debug_info TestSupport.fixture_version("math_debug_info.exs")
@math_docs TestSupport.fixture_version("math_docs.exs")
@math_abstract_code TestSupport.fixture_version("math_abstract_code.exs", eval: true)
@math_debug_info TestSupport.fixture_version("math_debug_info.exs", eval: true)
@math_docs TestSupport.fixture_version("math_docs.exs", eval: true)

@elixir_modules :elixir
|> Application.spec(:modules)
Expand Down Expand Up @@ -471,6 +471,11 @@ defmodule BeamFileTest do
assert code <> "\n" == File.read!("test/fixtures/doc_doc.exs")
end

test "returns elixir code for the Comps module" do
assert {:ok, code} = BeamFile.elixir_code(Comps)
assert code <> "\n" == TestSupport.fixture_version("comps.exs")
end

test "returns an error for invalid binary" do
assert BeamFile.elixir_code(<<0, 0, 7>>) == {:error, {:not_a_beam_file, <<0, 0, 7>>}}
end
Expand Down Expand Up @@ -515,7 +520,8 @@ defmodule BeamFileTest do
end
end

if TestSupport.otp_release?(25) do
if (!TestSupport.version?("~> 1.14") && !TestSupport.otp_release?(22)) ||
(TestSupport.version?("~> 1.14") && TestSupport.otp_release?(:latest)) do
@math_erl_code TestSupport.fixture_version("math.erl")

describe "erl_code/1" do
Expand Down
48 changes: 48 additions & 0 deletions test/fixtures/1.13.4/comps.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
defmodule Elixir.Comps do
def one do
for n <- [1, 2, 3] do
:erlang.*(n, n)
end
end

def two do
for n <- [1, 2, 3], into: [], do: :erlang.*(n, n)
end

def three do
for n <- [1, 2, 3], into: %{}, do: {n, :erlang.*(n, n)}
end

def four do
for i <- [1, 2, 3], n <- [1, 2, 3], into: %{}, do: {i, n}
end

def five do
for <<(<<x>> <- "abcabc")>>, uniq: true, into: "", do: <<:erlang.-(x, 32)>>
end

def six do
for <<(<<x>> <- "AbCabCABc")>>,
:erlang.andalso(
:erlang.is_integer(x),
:erlang.andalso(
:erlang.>=(
x,
97
),
:erlang."=<"(
x,
122
)
)
),
reduce: %{},
do: (acc -> Map.update(acc, <<x>>, 1, fn x1 -> :erlang.+(x1, 1) end))
end

def users(users) do
for {type, name} when :erlang."/="(type, :guest) <- users do
String.upcase(name)
end
end
end
35 changes: 35 additions & 0 deletions test/fixtures/1.14.5/comps.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
defmodule Elixir.Comps do
def one do
for n <- [1, 2, 3], into: [], do: :erlang.*(n, n)
end

def two do
for n <- [1, 2, 3], into: [], do: :erlang.*(n, n)
end

def three do
for n <- [1, 2, 3], into: %{}, do: {n, :erlang.*(n, n)}
end

def four do
for i <- [1, 2, 3], n <- [1, 2, 3], into: %{}, do: {i, n}
end

def five do
for <<(<<x>> <- "abcabc")>>, uniq: true, into: "", do: <<:erlang.-(x, 32)>>
end

def six do
for <<(<<x>> <- "AbCabCABc")>>,
:erlang.andalso(
:erlang.is_integer(x),
:erlang.andalso(:erlang.>=(x, 97), :erlang."=<"(x, 122))
),
reduce: %{},
do: (acc -> Map.update(acc, <<x>>, 1, fn x1 -> :erlang.+(x1, 1) end))
end

def users(users) do
for {type, name} when :erlang."/="(type, :guest) <- users, into: [], do: String.upcase(name)
end
end
2 changes: 1 addition & 1 deletion test/fixtures/1.14.5/math.erl
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
'__info__'(macros) -> [{biggest, 2}];
'__info__'(struct) -> nil;
'__info__'(exports_md5) ->
<<"\023f\225¯-?\002Ñô\025ÿ\t\210Mò:">>;
<<"ùÓË\233Ëùï\202$.­o»¸¨\030">>;
'__info__'(Key = attributes) ->
erlang:get_module_info('Elixir.Math', Key);
'__info__'(Key = compile) ->
Expand Down
36 changes: 36 additions & 0 deletions test/fixtures/comps.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
defmodule Comps do
def one do
for n <- [1, 2, 3], do: n * n
end

def two do
for n <- [1, 2, 3], into: [], do: n * n
end

def three do
for n <- [1, 2, 3], into: %{}, do: {n, n * n}
end

def four do
for i <- [1, 2, 3],
n <- [1, 2, 3],
into: %{},
do: {i, n}
end

def five do
for <<x <- "abcabc">>, uniq: true, into: "", do: <<x - 32>>
end

def six do
for <<x <- "AbCabCABc">>, x in ?a..?z, reduce: %{} do
acc -> Map.update(acc, <<x>>, 1, &(&1 + 1))
end
end

def users(users) do
for {type, name} when type != :guest <- users do
String.upcase(name)
end
end
end
10 changes: 8 additions & 2 deletions test/test_support.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule TestSupport do
@moduledoc false

@latest_otp_release 26

def system_version do
{:ok, version} = Version.parse(System.version())

Expand All @@ -12,10 +14,10 @@ defmodule TestSupport do
end
end

def fixture_version(file) do
def fixture_version(file, opts \\ []) do
path = "test/fixtures/#{system_version()}/#{file}"

if Path.extname(file) == ".exs" do
if Keyword.get(opts, :eval, false) do
path |> Code.eval_file() |> elem(0)
else
File.read!(path)
Expand All @@ -27,6 +29,10 @@ defmodule TestSupport do
Version.match?(version, require)
end

def otp_release?(:latest) do
otp_release?(@latest_otp_release)
end

def otp_release?(release) when is_integer(release) do
:erlang.list_to_integer(:erlang.system_info(:otp_release)) == release
end
Expand Down

0 comments on commit b13ec25

Please sign in to comment.