ericmj / ex2ms

:ets.fun2ms for Elixir, translate functions to match specifications
195 stars 27 forks source link

Error when matching against tuples #24

Closed lud closed 5 years ago

lud commented 5 years ago

Hi,

I try to match the keys against 3-tuples

This is my first attempt:

   key_start = {2019, 6, 10}
   key_end = {2019, 6, 12}

    match_fun =
      fun do
        {key, day} = record
        when key >= ^key_start and
               key <= ^key_end ->
          day
      end

And this is the result:

[
  {{:"$1", :"$2"},
   [{:andalso, {:>=, :"$1", {2019, 6, 10}}, {:"=<", :"$1", {2019, 6, 12}}}],
   [:"$2"]}
]

It's wrong because the 3-tuples should be wrapped in 1-tuples like this:

[
  {{:"$1", :"$2"},
   [
     {:andalso, {:>=, :"$1", {{2019, 6, 10}}},
      {:"=<", :"$1", {{2019, 6, 12}}}}
   ], [:"$2"]}
]

I tried this:

    match_fun =
      fun do
        {key, day} = record
        when key >= {^key_start} and
               key <= {^key_end} ->
          day
      end

But there is now an extra level of wrapping, which is expected I guess:

[
  {{:"$1", :"$2"},
   [
     {:andalso, {:>=, :"$1", {{{2019, 6, 10}}}},
      {:"=<", :"$1", {{{2019, 6, 12}}}}}
   ], [:"$2"]}
]

Is there a way to get the expected result, or is it a bug ?

Thank you

ericmj commented 5 years ago

It's a bug. We need to escape all interpolated composite terms or wrap them in {:const, ...}.

lud commented 5 years ago

The basic trick would be to do that for guard clauses:

  defp translate_cond({:^, _, [var]}, _state) do
    {{:unquote, [], [var]}}
  end

But it fails a gproc test. We must be able to know by state if we already are inside a constant or defining one. Or use :const then walk throught the final result and unwrap all nested consts.

ericmj commented 5 years ago

I don't think there would be any nested consts since we don't use :const at all today in ex2ms.

lud commented 5 years ago

You use 1-tuples according to the results I could see. I mean the unwrapping method would be the same, but using :const give less chances of false positives. I'd be gald to work on this but I would not be able to guess what the better solution is.

ericmj commented 5 years ago

I think this would work:

  defp translate_cond({:^, _, [var]}, _state) do
    {:const, {:unquote, [], [var]}}
  end
lud commented 5 years ago

Ok so I did a quick experiment and added a test with ets:test_ms(Tuple, MatchSpec) and everything seems fine.

Althoug I think testing against a real ETS table and a gproc instance could be a good thing.