View Source Math expression to french

The module MathToFrench can read simple integer math expressions, like "1+2", "(42-2)*12 / 5", and rewrite them in ... french.

iex> MathToFrench.parse("(42-2)*12 / 5")
{:ok, "(42 moins 2) multiplié par 12 divisé par 5"}

Here follows its declaration.

defmodule MathToFrench do
  use Grammar

  # This rule wille be the entry point.
  # Note that we could get rid of it, as it add no real value to
  # the output.
  rule start(:expression) do
    [program] = params
    "#{program}"
  end

  # a math expression is a composed of "terms" joined by
  # symbol + or -.
  # This ensure precedence of other operators over the them.
  rule expression(:term, :expression_cont) do
    join_non_nil(params)
  end

  rule? expression_cont("+", :term, :expression_cont) do
    [_, term, cont] = params
    join_non_nil(["plus", term, cont])
  end

  rule? expression_cont("-", :term, :expression_cont) do
    [_, term, cont] = params
    join_non_nil(["moins", term, cont])
  end

  # A term is composed of elements joined by operators * or /.
  # Those elements are reduced by rule `factor`.
  rule term(:factor, :term_cont) do
    join_non_nil(params)
  end

  rule? term_cont("*", :factor, :term_cont) do
    [_, factor, cont] = params
    join_non_nil(["multiplié par", factor, cont])
  end

  rule? term_cont("/", :factor, :term_cont) do
    [_, factor, cont] = params
    join_non_nil(["divisé par", factor, cont])
  end

  # Eventually a factor is either a integer number or
  # a parenthesized expression, as in rule `expression`.
  rule factor(:number) do
    [number] = params
    number
  end

  rule factor("(", :expression, ")") do
    [_, expression, _] = params
    "(#{expression})"
  end

  rule number(~r/[0-9]+/) do
    [number] = params
    number
  end

  defp join_non_nil(params) when is_list(params) do
    params |> Enum.reject(&is_nil/1) |> Enum.join(" ")
  end
end