How to collect data with the accumulator in postwalk?

Hi,

How do I have to change fun to collect data in Postwalk?

ast = quote do
        if ( a < b ) and (c < d ) -> x = z
        end

fun = fn node, acc -> 
         if is_tuple(node), do: IO.inspect(elem(node,0))
         {node, acc}
         end

This is the result:

iex(1)>  ast |> Macro.postwalk([], fun) |> Enum.reverse
:a
:b
:<
:c
:d
:<
:and
:x
:z 
:=
:then
:if
** (Protocol.UndefinedError) protocol Enumerable not implemented for {{:if, [context: Elixir, import: Kernel], [{:and, [context: Elixir, import: Kernel], [{:<, [context: Elixir, import: Kernel], [{:a, [], Elixir}, {:b, [], Elixir}]}, {:<, [context: Elixir, import: Kernel], [{:c, [], Elixir}, {:d, [], Elixir}]}]}, {:then, [], [{:=, [], [{:x, [], Elixir}, {:z, [], Elixir}]}]}]}, []}
    (elixir) lib/enum.ex:1: Enumerable.impl_for!/1
    (elixir) lib/enum.ex:116: Enumerable.reduce/3
    (elixir) lib/enum.ex:1636: Enum.reduce/3

Thanks in advance,

Thiel

I don’t understand. What are you trying to do? You aren’t actually using the accumulator at the moment?

Hi Ben,

The problem is I don’t know how to use the accumulator to get the result.

The result of what? What are you trying to do?

1 Like

postwalk is going to return a tuple with the AST and the accumulator. You cannot reverse a tuple. Try this:

{ast, acc} = ast |> Macro.postwalk([], fun)
Enum.reverse(acc)

But it is not clear what you want to achieve. So if you provide more information, we will be able to further help you.

1 Like

Hi Jose,

I need to change my function fun to receive the list from ast.

[:a, :b, :<, :c, :d, :<, :and, :x, :z, :=, :then, :if]

The steps I tried (in fun) were:

  • check if node in fun is a tuple
  • take first element (zero based) of that tuple and transform it into a list term v
  • v ++ acc
1 Like
ast = quote do
  if ( a < b ) and (c < d ) -> x = z
end

{_ast, acc} = Macro.postwalk(ast, [], fn
  node, acc when is_tuple(node) ->
    {node, [elem(node, 0) | acc]}
  node, acc ->
    {node, acc}
end)
acc |> Enum.reverse

This gets you:

[:a, :b, :<, :c, :d, :<, :and, :if, :x, :z, :=, :->]
2 Likes

Hi Ben (and Jose),

Many thanks for your answers!

I struggled with the application of the accumulator concept. As far as digits were concerned I understood the meaning of the accumulator and I could use it. But as soon as I was confronted with something different I had a problem how to apply the accumulator.
Your solution made it clear to me why and how I did the wrong solution.

Best regards,

Thiel

2 Likes