leifg
leifg

Reputation: 9028

Pattern match on end of a string/binary argument

I'm currently writing a little test runner in Elixir. I want to use pattern matching to evaluate if a file is in the spec format (ending in "_spec.exs"). There are numerous tutorials on how to pattern match on the beginning of a string, but that somehow won't work on string endings:

defp filter_spec(file <> "_spec.exs") do
  run_spec(file)
end

defp run_spec(file) do
  ...
end

This always ends up in the compilation error:

== Compilation error on file lib/monitor.ex ==
** (CompileError) lib/monitor.ex:13: a binary field without size is only allowed at the end of a binary pattern
    (stdlib) lists.erl:1337: :lists.foreach/2
    (stdlib) erl_eval.erl:669: :erl_eval.do_apply/6

Is there any solution for that?

Upvotes: 12

Views: 5154

Answers (5)

Tallak Tveide
Tallak Tveide

Reputation: 369

If you precalculate the length of the binary you would like to match, matching at the end is possible. Something like this:

    file = "..."
    postfix = "_spec.exs"
    skip_chars = byte_size(file) - bytes_size(postfix)
    <<_ :: binary-size(skip_chars), post :: little-16>> = file

You could put this in a function, but not in a pattern matching clause I guess. I believe you could quite easily extend this to using utf8 instead of binaries as well

Upvotes: 0

Sam Houston
Sam Houston

Reputation: 3661

Check for match:

String.ends_with? filename, "_spec.exs"

Extract file:

file = String.trim_trailing filename, "_spec.exs"

Upvotes: 3

user2353097
user2353097

Reputation:

using a more conventional definition of "pattern match":

String.match?(filename, ~r"_spec\.exs$")

Upvotes: 3

Steve B
Steve B

Reputation: 644

As other answers have mentioned this is not possible in elixir/erlang. Another solution however would be to approach the problem using the Path module so for your use case you should be able to do something like the following:

dir_path
  |> Path.join( "**/*_spec.exs" )
  |> Path.wildcard

Upvotes: 1

milch
milch

Reputation: 1046

Looking at this link in the Elixir getting started guide, it seems that it is impossible. The relevant section states:

However, we can match on the rest of the binary modifier:

iex> <<0, 1, x :: binary>> = <<0, 1, 2, 3>>
<<0, 1, 2, 3>>
iex> x
<<2, 3>>

The pattern above only works if the binary is at the end of <<>>. Similar results can be achieved with the string concatenation operator <>

iex> "he" <> rest = "hello"
"hello"
iex> rest
"llo"

Since Strings are binaries under the hood in Elixir, matching a suffix should be impossible for them as well.

Upvotes: 9

Related Questions