Custom operators in EEx.Engine

I am playing with EEx and I am trying to create a custom engine. I am trying to override the handle_assign/1 function from the EEx.Engine. The problem is I don’t understand what are the valid atoms. Here is my code

def handle_assign({:"??", meta, [{name, _, atom}]}) when is_atom(name) and is_atom(atom) do
    line = meta[:line] || 0
    quote line: line, do: Clouseau.TemplateEngine.assign_valid?(var!(assigns), unquote(name))
  end


def assign_valid?(assigns, key) do
  case  Map.get(assigns, key) do
    val when val in ["", nil, false] -> false
    _ -> true
  end

end

I want to use it like <%= if ??module, do: ":" %>.

This crashes on compilation with

iex(17)> recompile
Compiling 3 files (.ex)
{:<>, [line: 12],
 [{{:., [line: 12], [{:__aliases__, [line: 12], [:IO, :ANSI]}, :blue]},
   [line: 12], []},
  {:<>, [line: 12], [{:@, [line: 12], [{:file, [line: 12], nil}]}, "\n"]}]}
{:<>, [line: 14],
 [{{:., [line: 14], [{:__aliases__, [line: 14], [:IO, :ANSI]}, :green]},
   [line: 14], []}, {:@, [line: 14], [{:module, [line: 14], nil}]}]}

== Compilation error in file lib/render.ex ==
** (SyntaxError) lib/render.ex:14: syntax error before: module
    lib/eex/compiler.ex:37: EEx.Compiler.generate_buffer/4
    lib/render.ex:12: (module)
    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
** (exit) shutdown: 1
    (elixir) lib/kernel/parallel_compiler.ex:325: Kernel.ParallelCompiler.terminate/1
    (elixir) lib/kernel/parallel_compiler.ex:65: Kernel.ParallelCompiler.spawn_compilers/3
    (mix) lib/mix/compilers/elixir.ex:159: Mix.Compilers.Elixir.compile_manifest/8
    (mix) lib/mix/compilers/elixir.ex:86: Mix.Compilers.Elixir.compile/6
    (mix) lib/mix/tasks/compile.elixir.ex:68: Mix.Tasks.Compile.Elixir.run/1
    (mix) lib/mix/task.ex:301: Mix.Task.run_task/3
    (elixir) lib/enum.ex:1270: Enum."-map/2-lists^map/1-0-"/2
    (elixir) lib/enum.ex:1270: Enum."-map/2-lists^map/1-0-"/2
iex(17)>

When I replace the :"??" with :??, :?, :"?" and doing the appropriate changes in the template, still crashes. And generally it crashes with everything I tried except - and @.

If I use :- in the code above it works OK.

  def handle_assign({:-, meta, [{name, _, atom}]}) when is_atom(name) and is_atom(atom) do
    line = meta[:line] || 0
    quote line: line, do: Clouseau.TemplateEngine.assign_valid?(var!(assigns), unquote(name))
  end

Why is that and how can I work around it?

P.S.: what does the line quote line: line, do: do in the code above? I have been reading about macros but I haven’t seen that syntax with arbitrary keys in the options before.

Everything inside <% %>, <%= %> and friends needs to be valid Elixir syntax, you can’t change that.

3 Likes

I see. So if I understand right by using @ and - I was essentially overloading functions.

@voger yes, exactly! It is likely -1 didn’t even work as expected anymore.

2 Likes