79 lines
2.2 KiB
Elixir
79 lines
2.2 KiB
Elixir
defmodule Farmbot.CeleryScript.AST.Unslicer do
|
|
@moduledoc """
|
|
Turn an AST Heap back into an AST.
|
|
"""
|
|
alias Farmbot.CeleryScript.AST
|
|
alias Farmbot.CeleryScript.AST.Heap
|
|
|
|
@link Heap.link()
|
|
@parent Heap.parent()
|
|
@body Heap.body()
|
|
@next Heap.next()
|
|
@kind Heap.kind()
|
|
|
|
@typedoc "Ast with String Keys"
|
|
@type pre_ast :: map
|
|
|
|
@doc "Unslices a Heap struct back to cannonical celeryscript."
|
|
@spec run(Heap.t(), Address.t()) :: AST.t()
|
|
def run(%Heap{} = heap, %Address{} = addr) do
|
|
heap
|
|
|> unslice(addr)
|
|
|> AST.decode()
|
|
end
|
|
|
|
@spec unslice(Heap.t(), Address.t()) :: pre_ast
|
|
defp unslice(heap, addr) do
|
|
here_cell = heap[addr] || raise "No cell at address: #{inspect(addr)}"
|
|
|
|
Enum.reduce(here_cell, %{"args" => %{}}, fn {key, value}, acc ->
|
|
if is_link?(key) do
|
|
do_unslice(heap, key, value, acc)
|
|
else
|
|
%{acc | "args" => Map.put(acc["args"], to_string(key), value)}
|
|
end
|
|
end)
|
|
end
|
|
|
|
@spec do_unslice(Heap.t(), Heap.link(), any, acc :: map) :: acc :: map
|
|
defp do_unslice(_heap, @parent, _value, acc), do: acc
|
|
defp do_unslice(_heap, @next, _value, acc), do: acc
|
|
|
|
defp do_unslice(_heap, @kind, value, acc),
|
|
do: Map.put(acc, "kind", to_string(value))
|
|
|
|
defp do_unslice(heap, @body, value, acc) do
|
|
if heap[value][@kind] == :nothing do
|
|
acc
|
|
else
|
|
next_addr = value
|
|
n = heap[next_addr]
|
|
body = reduce_body(n, next_addr, heap, [])
|
|
Map.put(acc, "body", body)
|
|
end
|
|
end
|
|
|
|
defp do_unslice(heap, key, value, acc) do
|
|
key = String.replace(to_string(key), @link, "")
|
|
args = Map.put(acc["args"], key, unslice(heap, value))
|
|
%{acc | "args" => args}
|
|
end
|
|
|
|
@spec reduce_body(Heap.cell(), Address.t(), Heap.t(), [pre_ast]) :: [pre_ast]
|
|
defp reduce_body(%{__kind: :nothing}, _next_addr, _heap, acc),
|
|
do: acc
|
|
|
|
defp reduce_body(%{} = cell, %Address{} = next_addr, heap, acc) do
|
|
item = unslice(heap, next_addr)
|
|
new_acc = acc ++ [item]
|
|
next_addr = cell[@next]
|
|
next_cell = heap[next_addr]
|
|
reduce_body(next_cell, next_addr, heap, new_acc)
|
|
end
|
|
|
|
@spec is_link?(atom) :: boolean()
|
|
defp is_link?(key) do
|
|
String.starts_with?(to_string(key), @link)
|
|
end
|
|
end
|