diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c8f59f12c..6aa8246a0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ instead - Fix handling of large literal indexes - `unicode:characters_to_list`: fixed bogus out_of_memory error on some platforms such as ESP32 - Fix crash in Elixir library when doing `inspect(:atom)` +- General inspect() compliance with Elixir behavior (but there are still some minor differences) ## [0.6.4] - 2024-08-18 diff --git a/libs/exavmlib/lib/Kernel.ex b/libs/exavmlib/lib/Kernel.ex index b7b2418752..fe450d4e5a 100644 --- a/libs/exavmlib/lib/Kernel.ex +++ b/libs/exavmlib/lib/Kernel.ex @@ -48,8 +48,13 @@ defmodule Kernel do :erlang.integer_to_binary(t) t when is_list(t) -> - # TODO: escape unprintable lists - :erlang.list_to_binary(t) + if is_printable_list(t) do + str = :erlang.list_to_binary(t) + <> + else + [?[ | t |> inspect_join(?])] + |> :erlang.list_to_binary() + end t when is_pid(t) -> :erlang.pid_to_list(t) @@ -65,14 +70,14 @@ defmodule Kernel do t when is_binary(t) -> # TODO: escape unprintable binaries - t + <> t when is_reference(t) -> :erlang.ref_to_list(t) |> :erlang.list_to_binary() t when is_float(t) -> - :erlang.float_to_binary(t) + :erlang.float_to_binary(term, [{:decimals, 17}, :compact]) t when is_map(t) -> [?%, ?{ | t |> inspect_kv() |> join(?})] @@ -88,6 +93,10 @@ defmodule Kernel do [inspect(e), last] end + defp inspect_join([h | e], last) when not is_list(e) do + [inspect(h), " | ", inspect(e), last] + end + defp inspect_join([h | t], last) do [inspect(h), ?,, ?\s | inspect_join(t, last)] end @@ -140,6 +149,26 @@ defmodule Kernel do end end + defp is_printable_list([]), do: false + + defp is_printable_list([char]) do + is_printable_ascii(char) + end + + defp is_printable_list([char | t]) do + if is_printable_ascii(char) do + is_printable_list(t) + else + false + end + end + + defp is_printable_list(_any), do: false + + defp is_printable_ascii(char) do + is_integer(char) and char >= 32 and char < 127 and char != ?' + end + @doc """ Returns the biggest of the two given terms according to Erlang's term ordering. diff --git a/tests/libs/exavmlib/Tests.ex b/tests/libs/exavmlib/Tests.ex index 782da05d15..ba6cd21cc6 100644 --- a/tests/libs/exavmlib/Tests.ex +++ b/tests/libs/exavmlib/Tests.ex @@ -19,6 +19,12 @@ # defmodule Tests do + +# defstruct [ +# :field1, +# field2: 42 +# ] + @compile {:no_warn_undefined, :undef} def start() do @@ -244,10 +250,48 @@ defmodule Tests do ":アトム" = inspect(:アトム) "Test" = inspect(Test) + "5" = inspect(5) + "5.0" = inspect(5.0) + + ~s["hello"] = inspect("hello") + ~s["アトム"] = inspect("アトム") + + "[]" = inspect([]) + "[0]" = inspect([0]) + "[9, 10]" = inspect([9, 10]) + ~s'["test"]' = inspect(["test"]) + "'hello'" = inspect('hello') + "[127]" = inspect([127]) + "[104, 101, 108, 108, 248]" = inspect('hellø') + + ~s([5 | "hello"]) = inspect([5 | "hello"]) + + "{}" = inspect({}) + "{1, 2}" = inspect({1, 2}) + "{:test, 1}" = inspect({:test, 1}) + + "%{}" = inspect(%{}) + either("%{a: 1, b: 2}", "%{b: 2, a: 1}", inspect(%{a: 1, b: 2})) + either(~s[%{"a" => 1, "b" => 2}], ~s[%{"b" => 2, "a" => 1}], inspect(%{"a" => 1, "b" => 2})) + + # TODO: structs are not yet supported + # either( + # ~s[%#{__MODULE__}{field1: nil, field2: 42}], + # ~s[%#{__MODULE__}{field2: 42, field1: nil}], + # inspect(%__MODULE__{}) + # ) + :ok end defp fact(n) when n < 0, do: :test defp fact(0), do: 1 defp fact(n), do: fact(n - 1) * n + + def either(a, b, value) do + case value do + ^a -> a + ^b -> b + end + end end