infinate recursion...
This commit is contained in:
parent
bec0c3c5d1
commit
7e60283d71
|
@ -4,21 +4,6 @@ defmodule Farmbot.CeleryScript.AST do
|
|||
Ast nodes.
|
||||
"""
|
||||
|
||||
defmodule Meta do
|
||||
@moduledoc "Metadata about an AST node."
|
||||
defstruct [precompiled: false, encoded: nil]
|
||||
@type t :: %__MODULE__{
|
||||
precompiled: boolean,
|
||||
encoded: binary
|
||||
}
|
||||
|
||||
def new(ast) do
|
||||
bin = Map.from_struct(ast) |> Map.delete(:__meta__) |> Poison.encode!
|
||||
encoded = :crypto.hash(:md5, bin) |> Base.encode16()
|
||||
struct(__MODULE__, encoded: encoded)
|
||||
end
|
||||
end
|
||||
|
||||
alias Farmbot.CeleryScript.Error
|
||||
|
||||
@typedoc """
|
||||
|
@ -30,8 +15,7 @@ defmodule Farmbot.CeleryScript.AST do
|
|||
Type for CeleryScript Ast's.
|
||||
"""
|
||||
@type t :: %__MODULE__{
|
||||
__meta__: Meta.t,
|
||||
uid: binary,
|
||||
compile_meta: map | nil,
|
||||
args: args,
|
||||
body: [t, ...],
|
||||
kind: String.t(),
|
||||
|
@ -39,14 +23,11 @@ defmodule Farmbot.CeleryScript.AST do
|
|||
}
|
||||
|
||||
@enforce_keys [:args, :body, :kind]
|
||||
defstruct [
|
||||
kind: nil,
|
||||
uid: nil,
|
||||
args: %{},
|
||||
body: [],
|
||||
comment: nil,
|
||||
__meta__: nil
|
||||
]
|
||||
defstruct kind: nil,
|
||||
compile_meta: nil,
|
||||
args: %{},
|
||||
body: [],
|
||||
comment: nil
|
||||
|
||||
@doc """
|
||||
Parses json and traverses the tree and turns everything can
|
||||
|
@ -58,10 +39,7 @@ defmodule Farmbot.CeleryScript.AST do
|
|||
def parse(%{"kind" => kind, "args" => args} = thing) do
|
||||
body = thing["body"] || []
|
||||
comment = thing["comment"]
|
||||
uid = thing["uuid"] || generate_uid()
|
||||
before_meta = %__MODULE__{kind: kind, args: parse_args(args), body: parse(body), comment: comment, uid: uid}
|
||||
meta = thing["__meta__"] || Meta.new(before_meta)
|
||||
%{before_meta | __meta__: meta}
|
||||
%__MODULE__{kind: kind, args: parse_args(args), body: parse(body), comment: comment}
|
||||
end
|
||||
|
||||
def parse(%{__struct__: _} = thing) do
|
||||
|
@ -71,10 +49,7 @@ defmodule Farmbot.CeleryScript.AST do
|
|||
def parse(%{kind: kind, args: args} = thing) do
|
||||
body = thing[:body] || []
|
||||
comment = thing[:comment]
|
||||
uid = thing[:uid] || generate_uid()
|
||||
before_meta = %__MODULE__{kind: kind, body: parse(body), args: parse_args(args), comment: comment, uid: uid}
|
||||
meta = thing[:__meta__] || Meta.new(before_meta)
|
||||
%{before_meta | __meta__: meta}
|
||||
%__MODULE__{kind: kind, body: parse(body), args: parse_args(args), comment: comment}
|
||||
end
|
||||
|
||||
# You can give a list of nodes.
|
||||
|
@ -102,16 +77,4 @@ defmodule Farmbot.CeleryScript.AST do
|
|||
end
|
||||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a new AST node. No validation is preformed on this other than making
|
||||
sure its syntax is valid.
|
||||
"""
|
||||
def create(kind, args, body) when is_map(args) and is_list(body) do
|
||||
%__MODULE__{kind: kind, args: args, body: body}
|
||||
end
|
||||
|
||||
defp generate_uid do
|
||||
UUID.uuid1 |> String.split("-") |> List.first
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
defmodule Farmbot.CeleryScript.AST.CompileError do
|
||||
defmodule Farmbot.CeleryScript.AST.Compiler.CompileError do
|
||||
defexception [:message]
|
||||
|
||||
def exception(message) do
|
|
@ -1,87 +1,82 @@
|
|||
defmodule Farmbot.CeleryScript.AST.Compiler do
|
||||
require Logger
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.AST.CompileError
|
||||
alias Farmbot.CeleryScript.AST.Compiler.CompileError
|
||||
alias Farmbot.CeleryScript.VirtualMachine.InstructionSet
|
||||
import Farmbot.CeleryScript.AST.Compiler.Utils
|
||||
|
||||
def compile(ast, instrs) do
|
||||
compiler_debug_log(
|
||||
"#{Farmbot.DebugLog.color(:YELLOW)}BEGIN COMPILE: #{inspect(ast)}#{
|
||||
Farmbot.DebugLog.color(:NC)
|
||||
}"
|
||||
)
|
||||
compiler_debug_log_begin_step(ast, "ensure_implementation")
|
||||
ast = ensure_implementation!(ast, instrs)
|
||||
compiler_debug_log_complete_step(ast, "ensure_implementation")
|
||||
compiler_debug_log_begin_step(ast, "precompile")
|
||||
{state, ast} = precompile!(ast, instrs)
|
||||
compiler_debug_log(Map.keys(state) |> Enum.join("\n\n"))
|
||||
compiler_debug_log_complete_step(ast, "precompile")
|
||||
ast
|
||||
def compile(ast, %InstructionSet{} = instruction_set) do
|
||||
res = ast
|
||||
|> tag(:precompile)
|
||||
|> ensure_impl(instruction_set)
|
||||
|> precompile(instruction_set, [])
|
||||
require IEx; IEx.pry
|
||||
end
|
||||
|
||||
def ensure_implementation!(%AST{kind: kind, body: []} = ast, instrs) do
|
||||
:ok = do_ensure(kind, instrs)
|
||||
ast
|
||||
def tag(ast, step) do
|
||||
%{ast | compile_meta: Map.put(ast.compile_meta || %{}, :step, step), body: tag_body(ast.body, step)}
|
||||
end
|
||||
|
||||
def ensure_implementation!(%AST{kind: kind, body: body} = ast, instrs) do
|
||||
:ok = do_ensure(kind, instrs)
|
||||
ensure_implementation!(body, instrs)
|
||||
ast
|
||||
defp tag_body(body, step, acc \\ [])
|
||||
|
||||
defp tag_body([ast | rest], step, acc) do
|
||||
tag_body(rest, step, [tag(ast, step) | acc])
|
||||
end
|
||||
|
||||
def ensure_implementation!([%AST{kind: kind, body: body} | next], instrs) do
|
||||
:ok = do_ensure(kind, instrs)
|
||||
ensure_implementation!(body, instrs)
|
||||
ensure_implementation!(next, instrs)
|
||||
end
|
||||
defp tag_body([], _step, acc), do: Enum.reverse(acc)
|
||||
|
||||
def ensure_implementation!([], _), do: :ok
|
||||
|
||||
defp do_ensure(kind, instrs) do
|
||||
compiler_debug_log(kind, "ensure implementation")
|
||||
impl = instrs[kind] || raise CompileError, "No implementation for #{kind}"
|
||||
if Code.ensure_loaded?(impl) do
|
||||
:ok
|
||||
else
|
||||
raise CompileError, "Implementation module: #{impl} is not loaded."
|
||||
def ensure_impl(ast, %InstructionSet{} = instruction_set) do
|
||||
unless instruction_set[ast.kind] do
|
||||
raise CompileError, "#{ast.kind} has no implementation."
|
||||
end
|
||||
|
||||
unless Code.ensure_loaded?(instruction_set[ast.kind]) do
|
||||
raise CompileError, "#{ast.kind} implementation could not be loaded."
|
||||
end
|
||||
|
||||
%{ast | body: ensure_impl_body(ast.body, instruction_set)}
|
||||
end
|
||||
|
||||
# sequence
|
||||
# -> execute_sequence
|
||||
# -> move_abs
|
||||
def ensure_impl_body(body, instruction_set, acc \\ [])
|
||||
|
||||
def precompile!(ast, instrs, state \\ %{}) do
|
||||
impl = instrs[ast.kind]
|
||||
if state[ast.__meta__.encoded] do
|
||||
compiler_debug_log("#{inspect ast} compiled")
|
||||
{state, ast}
|
||||
def ensure_impl_body([ast | rest], %InstructionSet{} = instruction_set, acc) do
|
||||
ensure_impl_body(rest, instruction_set, [ensure_impl(ast, instruction_set) | acc])
|
||||
end
|
||||
|
||||
def ensure_impl_body([], %InstructionSet{} = _instruction_set, acc), do: Enum.reverse(acc)
|
||||
|
||||
def precompile(ast, %InstructionSet{} = instruction_set, cache) do
|
||||
md5 = :crypto.hash(:md5, Poison.encode!(ast)) |> Base.encode16
|
||||
IO.puts "precompiling: #{inspect ast}: #{inspect ast.compile_meta} => #{md5}"
|
||||
IO.inspect cache
|
||||
if md5 in cache do
|
||||
ast
|
||||
else
|
||||
case impl.precompile(ast) do
|
||||
{:ok, res} ->
|
||||
res = %{res | __meta__: %{res.__meta__ | precompiled: true}}
|
||||
state = Map.put(state, res.__meta__.encoded, res)
|
||||
{state, ast} = precompile!(res, instrs, state)
|
||||
{state, body} = precompile_body(ast.body, instrs, state)
|
||||
%{ast | body: body} |> precompile!(instrs, state)
|
||||
cache = [md5 | cache]
|
||||
case instruction_set[ast.kind].precompile(ast) do
|
||||
{:error, reason} -> raise CompileError, reason
|
||||
{:ok, precompiled} ->
|
||||
precompiled = %{precompiled | body: precompile_body(precompiled.body, instruction_set, cache)}
|
||||
IO.inspect precompiled.compile_meta
|
||||
case precompiled.compile_meta do
|
||||
nil ->
|
||||
precompiled
|
||||
|> tag(:precompile)
|
||||
|> ensure_impl(instruction_set)
|
||||
|> precompile(instruction_set, cache)
|
||||
|> tag(:compile)
|
||||
%{step: :precompile} -> tag(precompiled, :compile)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
defp precompile_body(body, instrs, state, acc \\ [])
|
||||
def precompile_body(body, instruction_set, cache, acc \\ [])
|
||||
|
||||
defp precompile_body([ast | rest], instrs, state, acc) do
|
||||
{state, ast} = precompile!(ast, instrs, state)
|
||||
acc = [ast | acc]
|
||||
precompile_body(rest, instrs, state, acc)
|
||||
def precompile_body([ast | rest], %InstructionSet{} = instruction_set, cache, acc) do
|
||||
precompile_body(rest, instruction_set, cache, [precompile(ast, instruction_set, cache) | acc])
|
||||
end
|
||||
|
||||
defp precompile_body([], instrs, state, acc) do
|
||||
{state, Enum.reverse(acc)}
|
||||
end
|
||||
def precompile_body([], %InstructionSet{} = _instruction_set, _cache, acc), do: Enum.reverse(acc)
|
||||
end
|
||||
|
||||
# Farmbot.HTTP.get!("/api/sequences/2") |> Map.get(:body) |> Poison.decode! |> Farmbot.CeleryScript.AST.parse |> Farmbot.CeleryScript.VirtualMachine.execute
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
defmodule Farmbot.CeleryScript.AST.Compiler.Utils do
|
||||
alias Farmbot.CeleryScript.VirtualMachine.InstructionSet
|
||||
def compiler_debug_log(kind, msg) do
|
||||
msg =
|
||||
"#{format_kind(kind)}" <>
|
||||
"#{Farmbot.DebugLog.color(:LIGHT_GREEN)}[ " <> msg <> " ]#{Farmbot.DebugLog.color(:NC)}"
|
||||
|
||||
compiler_debug_log(msg)
|
||||
end
|
||||
|
||||
def compiler_debug_log_begin_step(ast, step) do
|
||||
compiler_debug_log(
|
||||
"#{Farmbot.DebugLog.color(:YELLOW)}[ #{inspect ast} ] " <> # "#{ast.__meta__.encoded} " <>
|
||||
"#{Farmbot.DebugLog.color(:LIGHT_GREEN)}begin step: #{Farmbot.DebugLog.color(:YELLOW)}[ #{step} ]#{
|
||||
Farmbot.DebugLog.color(:NC)
|
||||
}"
|
||||
)
|
||||
end
|
||||
|
||||
def compiler_debug_log_complete_step(ast, step) do
|
||||
compiler_debug_log(
|
||||
"#{Farmbot.DebugLog.color(:YELLOW)}[ #{inspect ast} ] " <># "#{ast.__meta__.encoded} " <>
|
||||
"#{Farmbot.DebugLog.color(:LIGHT_GREEN)}complete step: #{Farmbot.DebugLog.color(:YELLOW)}[ #{step} ]#{
|
||||
Farmbot.DebugLog.color(:NC)
|
||||
}"
|
||||
)
|
||||
end
|
||||
|
||||
def compiler_debug_log(msg) do
|
||||
IO.puts(msg)
|
||||
end
|
||||
|
||||
kinds = Map.keys(struct(InstructionSet)) -- [:__struct__]
|
||||
|
||||
max_chars =
|
||||
Enum.reduce(kinds, 0, fn kind, acc ->
|
||||
num_chars = to_charlist(kind) |> Enum.count()
|
||||
|
||||
if num_chars > acc do
|
||||
num_chars
|
||||
else
|
||||
acc
|
||||
end
|
||||
end)
|
||||
|
||||
for kind <- kinds do
|
||||
num_chars = to_charlist(kind) |> Enum.count()
|
||||
pad = max_chars - num_chars
|
||||
# "#{String.duplicate(" ", pad)} - " <>
|
||||
res =
|
||||
"#{Farmbot.DebugLog.color(:LIGHT_CYAN)}" <>
|
||||
"[ " <>
|
||||
"#{Farmbot.DebugLog.color(:CYAN)}#{kind}" <>
|
||||
"#{String.duplicate(" ", pad)}" <>
|
||||
"#{Farmbot.DebugLog.color(:LIGHT_CYAN)} ]" <> "#{Farmbot.DebugLog.color(:NC)} - "
|
||||
|
||||
def format_kind(unquote(kind |> to_string())), do: unquote(res)
|
||||
end
|
||||
end
|
|
@ -1,5 +1,5 @@
|
|||
defimpl Inspect, for: Farmbot.CeleryScript.AST do
|
||||
def inspect(ast, _opts) do
|
||||
"#CeleryScript[#{ast.uid}]<#{ast.kind}: #{inspect(Map.keys(ast.args))}>"
|
||||
"#CeleryScript<#{ast.kind}: #{inspect(Map.keys(ast.args))}>"
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue