91 lines
2.3 KiB
Elixir
91 lines
2.3 KiB
Elixir
defmodule FarmbotCeleryScript.AST do
|
|
@moduledoc """
|
|
Handy functions for turning various data types into Farbot Celery Script
|
|
Ast nodes.
|
|
"""
|
|
alias FarmbotCeleryScript.AST
|
|
|
|
@typedoc "Arguments to a ast node."
|
|
@type args :: map
|
|
|
|
@typedoc "Body of a ast node."
|
|
@type body :: [t]
|
|
|
|
@typedoc "Kind of a ast node."
|
|
@type kind :: module
|
|
|
|
@typedoc "AST node."
|
|
@type t :: %__MODULE__{
|
|
kind: kind,
|
|
args: args,
|
|
body: body,
|
|
comment: binary,
|
|
meta: any()
|
|
}
|
|
|
|
defstruct [:args, :body, :kind, :comment, :meta]
|
|
|
|
@doc "Decode a base map into CeleryScript AST."
|
|
@spec decode(t() | map | [t() | map]) :: t()
|
|
def decode(map_or_list_of_maps)
|
|
|
|
def decode(list) when is_list(list) do
|
|
decode_body(list)
|
|
end
|
|
|
|
def decode(%{__struct__: _} = thing) do
|
|
thing |> Map.from_struct() |> decode()
|
|
end
|
|
|
|
def decode(%{} = thing) do
|
|
kind = thing["kind"] || thing[:kind] || raise("Bad ast: #{inspect(thing)}")
|
|
args = thing["args"] || thing[:args] || raise("Bad ast: #{inspect(thing)}")
|
|
body = thing["body"] || thing[:body] || []
|
|
comment = thing["comment"] || thing[:comment] || nil
|
|
meta = thing["meta"] || thing[:meta] || nil
|
|
|
|
%AST{
|
|
kind: String.to_atom(to_string(kind)),
|
|
args: decode_args(args),
|
|
body: decode_body(body),
|
|
comment: comment,
|
|
meta: meta
|
|
}
|
|
end
|
|
|
|
def decode(bad_ast), do: raise("Bad ast: #{inspect(bad_ast)}")
|
|
|
|
# You can give a list of nodes.
|
|
@spec decode_body([map]) :: [t()]
|
|
def decode_body(body) when is_list(body) do
|
|
Enum.map(body, fn itm ->
|
|
decode(itm)
|
|
end)
|
|
end
|
|
|
|
@spec decode_args(map) :: args
|
|
def decode_args(map) when is_map(map) do
|
|
Enum.reduce(map, %{}, fn {key, val}, acc ->
|
|
if is_map(val) do
|
|
# if it is a map, it could be another node so decode it too.
|
|
real_val = decode(val)
|
|
Map.put(acc, String.to_atom(to_string(key)), real_val)
|
|
else
|
|
Map.put(acc, String.to_atom(to_string(key)), val)
|
|
end
|
|
end)
|
|
end
|
|
|
|
@spec new(atom, map, [map]) :: t()
|
|
def new(kind, args, body, comment \\ nil, meta \\ nil) when is_map(args) and is_list(body) do
|
|
%AST{
|
|
kind: String.to_atom(to_string(kind)),
|
|
args: args,
|
|
body: body,
|
|
comment: comment,
|
|
meta: meta
|
|
}
|
|
|> decode()
|
|
end
|
|
end
|