Regexes are precompiled to binary values during code compilation.
These binary values may be incompatible between different PCRE versions
and OS endianness. This makes code less portable to achieve slight
performance improvement.
PCRE version and endianness are parts of Regex structure and may be
checked in runtime.
This commit adds this check and makes regex execution fall back
to binary matching if versions are incompatible.
This slightly reduces performance (around 5% for simple regexes) for
compatible versions because there is an additional version read.
Also for incompatible versions it's as fast as binary matching.
Let's see some use cases. First we configure IEx to use our custom
function:
iex> IEx.configure(inspect: [inspect_fun: &Pretty.inspect/2])
:ok
1. Pretty-printing integers
iex> :math.pow(2, 32) |> trunc()
4_294_967_296
2. Inspect tuples as records
iex> {:foo, :bar}
{:foo, :bar}
iex> {:person, "Alice", 30}
#person(name: "Alice", age: 30)
3. Overwrite struct's Inspect implementation
iex> URI.parse("https://elixir-lang.org")
#URI<https://elixir-lang.org>
---
Code for the `Pretty.inspect/2` function:
defmodule Pretty do
def inspect(integer, opts) when is_integer(integer) do
PrettyIntegers.inspect(integer, opts)
end
def inspect(tuple, opts) when is_tuple(tuple) do
records = %{
{:person, 2} => [:name, :age]
}
PrettyTuples.inspect(tuple, records, opts)
end
def inspect(%URI{} = uri, _opts) do
Inspect.Algebra.concat(["#URI<", URI.to_string(uri), ">"])
end
def inspect(term, opts) do
Inspect.inspect(term, opts)
end
end
defmodule PrettyIntegers do
def inspect(term, %Inspect.Opts{base: :decimal}) do
pretty_decimal(term, "_")
end
def inspect(term, %Inspect.Opts{base: base}) do
Integer.to_string(term, base_to_value(base))
|> prepend_prefix(base)
end
def pretty_decimal(n, separator) when n < 0 do
"-" <> pretty_decimal(abs(n), separator)
end
def pretty_decimal(n, separator) when n >= 1000 do
left = div(n, 1_000)
right = rem(n, 1_000)
right_str = :io_lib.format("~3..0B", [right]) |> IO.iodata_to_binary
pretty_decimal(left, separator) <> separator <> right_str
end
def pretty_decimal(n, _) do
Integer.to_string(n)
end
defp base_to_value(base) do
case base do
:binary -> 2
:octal -> 8
:hex -> 16
end
end
defp prepend_prefix(value, base) do
prefix = case base do
:binary -> "0b"
:octal -> "0o"
:hex -> "0x"
end
prefix <> value
end
end
defmodule PrettyTuples do
import Inspect.Algebra
def inspect({}, _records, opts), do: inspect_tuple([], opts)
def inspect(tuple, records, opts) do
list = Tuple.to_list(tuple)
[record_name | values] = list
case record_fields(records, record_name, tuple) do
{:ok, fields} ->
inspect_record(record_name, fields, values, opts)
_ ->
inspect_tuple(list, opts)
end
end
defp record_fields(records, record_name, tuple) do
fields_size = tuple_size(tuple) - 1
Map.fetch(records, {record_name, fields_size})
end
defp inspect_tuple(list, opts) do
inspect("{", list, "}", &to_doc/2, :flex, opts)
end
defp inspect_record(record_name, fields, values, opts) do
kwlist = Enum.zip(fields, values)
inspect("##{record_name}(", kwlist, ")", &Inspect.List.keyword/2, :strict, opts)
end
defp inspect(open, list, close, fun, break, opts) do
open = color(open, :tuple, opts)
sep = color(",", :tuple, opts)
close = color(close, :tuple, opts)
container_opts = [separator: sep, break: break]
container_doc(open, list, close, opts, fun, container_opts)
end
end
"Keyword.{take|drop|split}" don't offer support for an Enumerable of
keys to take/drop/split based on. However, the "Map" counterparts do.
With this commit, we hard-deprecate support for Enumerables of keys in
"Map.{take|drop|split}". Only lists of keys are allowed.
This fixes an issue where a registry's PIDPartition table would gain an
entry each time after the first call to Registry.register on a unique
registry.
We also guarantee unregistered works generally with "tricky keys".
Closes#8611.
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
The previous implementation was generating an unnecessary
intermediate function when using `&super/arity`.
In addition, capture expressions using `super` with sequential
value placeholders (`&n`) were not optimized like other functions.
For example, the capture expression `&foo(&1, &2, &3)` is
optimized to `&foo/3` because the value placeholders (`&n`) are
sequential `&1, &2, &3`.
The current approach discards the __aliases__ tuple and
do a simple check that allows some strange behavior like:
```elixir
iex(1)> alias :lists, as: :"Elixir.foobar"
:lists
iex(2)> :"Elixir.foobar"
:"Elixir.foobar"
iex(3)> alias :"Elixir.foobar", as: Baz
:"Elixir.foobar"
iex(3)> Baz
:lists
```
1) Keep order of elements in Kernel.in/2:
The order of elements was not being kept (not doing as it was said in the docs).
Now it is guaranteed that the elements are compared in the same order they were given.
previously:
iex> quote do 1 in [1, 2, 3] end |> Macro.expand(Map.put(__ENV__, :context, :guard)) |> Macro.to_string()
":erlang.orelse(:erlang.\"=:=\"(1, 2), :erlang.orelse(:erlang.\"=:=\"(1, 3), :erlang.\"=:=\"(1, 1)))"
now:
iex> quote do 1 in [1, 2, 3] end |> Macro.expand(Map.put(__ENV__, :context, :guard)) |> Macro.to_string()
":erlang.orelse(:erlang.orelse(:erlang.\"=:=\"(1, 1), :erlang.\"=:=\"(1, 2)), :erlang.\"=:=\"(1, 3))"
2) Error message improved from: invalid args for operator "in"...
to: invalid right argument for operator "in"...
3) More tests added to check for generated code
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
This is important when you may want to check if a module
is available but if that module is running a similar check
on you, you don't want the compiler to fail.
This change fixes inconsistencies when building structs: if the
struct was built using the map syntax the `:__struct__` key
was overridden by the given module name; however, if the struct
was built using `struct/1,2` the `:__struct__` key was kept.
For example:
defmodule X do
defstruct [:x]
end
defmodule Y do
defdelegate __struct__, to: X
defdelegate __struct__(args), to: X
end
iex> %Y{}
%Y{x: nil}
iex> struct(Y)
%X{x: nil}
Closes#8800
During the bitstring expansion, guard checks were avoided due to
a special case for inlining binaries during interpolation. These
changes ensure guards are checked.
The meaning of underscored variables in type specifications
seems to be the same as regular underscored variables: values
that are not meant to be used. This behavior can be seen in the
`singleton_typevar` error, in which underscored type variables
are ignored.
Type variables starting with an underscore (_foo) should not raise compile errors.
Underscore variables (_) cannot be used as type variables.
Fixes#8888