infinate recursion..
This commit is contained in:
parent
7bde7fb8c8
commit
bec0c3c5d1
|
@ -59,11 +59,9 @@ defmodule Farmbot.BotState.Transport.GenMQTT.Client do
|
|||
|
||||
def on_publish(["bot", _bot, "from_clients"], msg, state) do
|
||||
Logger.warn("not implemented yet: #{msg}")
|
||||
|
||||
if state.cache do
|
||||
GenMQTT.publish(self(), status_topic(state.device), state.cache, 0, false)
|
||||
end
|
||||
|
||||
msg
|
||||
|> Farmbot.CeleryScript.AST.parse()
|
||||
|> Farmbot.CeleryScript.VirtualMachine.execute()
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
|
|
|
@ -1,17 +1,26 @@
|
|||
defmodule Farmbot.CeleryScript.Ast do
|
||||
defmodule Farmbot.CeleryScript.AST do
|
||||
@moduledoc """
|
||||
Handy functions for turning various data types into Farbot Celery Script
|
||||
Ast nodes.
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.Error
|
||||
defmodule Meta do
|
||||
@moduledoc "Metadata about an AST node."
|
||||
defstruct [precompiled: false, encoded: nil]
|
||||
@type t :: %__MODULE__{
|
||||
precompiled: boolean,
|
||||
encoded: binary
|
||||
}
|
||||
|
||||
defimpl Inspect, for: __MODULE__ do
|
||||
def inspect(thing, _) do
|
||||
"#CeleryScript<#{thing.kind}: #{inspect(Map.keys(thing.args))}>"
|
||||
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 """
|
||||
CeleryScript args.
|
||||
"""
|
||||
|
@ -21,6 +30,8 @@ defmodule Farmbot.CeleryScript.Ast do
|
|||
Type for CeleryScript Ast's.
|
||||
"""
|
||||
@type t :: %__MODULE__{
|
||||
__meta__: Meta.t,
|
||||
uid: binary,
|
||||
args: args,
|
||||
body: [t, ...],
|
||||
kind: String.t(),
|
||||
|
@ -28,19 +39,29 @@ defmodule Farmbot.CeleryScript.Ast do
|
|||
}
|
||||
|
||||
@enforce_keys [:args, :body, :kind]
|
||||
defstruct [:args, :body, :kind, :comment]
|
||||
defstruct [
|
||||
kind: nil,
|
||||
uid: nil,
|
||||
args: %{},
|
||||
body: [],
|
||||
comment: nil,
|
||||
__meta__: nil
|
||||
]
|
||||
|
||||
@doc """
|
||||
Parses json and traverses the tree and turns everything can
|
||||
possibly be parsed.
|
||||
"""
|
||||
@spec parse({:ok, map} | map | [map, ...]) :: t
|
||||
@spec parse(map | [map, ...]) :: t
|
||||
def parse(map_or_json_map)
|
||||
|
||||
def parse(%{"kind" => kind, "args" => args} = thing) do
|
||||
body = thing["body"] || []
|
||||
comment = thing["comment"]
|
||||
%__MODULE__{kind: kind, args: parse_args(args), body: parse(body), comment: 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}
|
||||
end
|
||||
|
||||
def parse(%{__struct__: _} = thing) do
|
||||
|
@ -50,7 +71,10 @@ defmodule Farmbot.CeleryScript.Ast do
|
|||
def parse(%{kind: kind, args: args} = thing) do
|
||||
body = thing[:body] || []
|
||||
comment = thing[:comment]
|
||||
%__MODULE__{kind: kind, body: parse(body), args: parse_args(args), comment: 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}
|
||||
end
|
||||
|
||||
# You can give a list of nodes.
|
||||
|
@ -86,4 +110,8 @@ defmodule Farmbot.CeleryScript.Ast do
|
|||
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
|
7
lib/farmbot/celery_script/ast/compile_error.ex
Normal file
7
lib/farmbot/celery_script/ast/compile_error.ex
Normal file
|
@ -0,0 +1,7 @@
|
|||
defmodule Farmbot.CeleryScript.AST.CompileError do
|
||||
defexception [:message]
|
||||
|
||||
def exception(message) do
|
||||
%__MODULE__{message: message}
|
||||
end
|
||||
end
|
87
lib/farmbot/celery_script/ast/compiler/compiler.ex
Normal file
87
lib/farmbot/celery_script/ast/compiler/compiler.ex
Normal file
|
@ -0,0 +1,87 @@
|
|||
defmodule Farmbot.CeleryScript.AST.Compiler do
|
||||
require Logger
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.AST.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
|
||||
end
|
||||
|
||||
def ensure_implementation!(%AST{kind: kind, body: []} = ast, instrs) do
|
||||
:ok = do_ensure(kind, instrs)
|
||||
ast
|
||||
end
|
||||
|
||||
def ensure_implementation!(%AST{kind: kind, body: body} = ast, instrs) do
|
||||
:ok = do_ensure(kind, instrs)
|
||||
ensure_implementation!(body, instrs)
|
||||
ast
|
||||
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
|
||||
|
||||
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."
|
||||
end
|
||||
end
|
||||
|
||||
# sequence
|
||||
# -> execute_sequence
|
||||
# -> move_abs
|
||||
|
||||
def precompile!(ast, instrs, state \\ %{}) do
|
||||
impl = instrs[ast.kind]
|
||||
if state[ast.__meta__.encoded] do
|
||||
compiler_debug_log("#{inspect ast} compiled")
|
||||
{state, 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)
|
||||
{:error, reason} -> raise CompileError, reason
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp precompile_body(body, instrs, state, 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)
|
||||
end
|
||||
|
||||
defp precompile_body([], instrs, state, acc) do
|
||||
{state, Enum.reverse(acc)}
|
||||
end
|
||||
end
|
||||
# Farmbot.HTTP.get!("/api/sequences/2") |> Map.get(:body) |> Poison.decode! |> Farmbot.CeleryScript.AST.parse |> Farmbot.CeleryScript.VirtualMachine.execute
|
59
lib/farmbot/celery_script/ast/compiler/utils.ex
Normal file
59
lib/farmbot/celery_script/ast/compiler/utils.ex
Normal file
|
@ -0,0 +1,59 @@
|
|||
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
|
5
lib/farmbot/celery_script/ast/inspect.ex
Normal file
5
lib/farmbot/celery_script/ast/inspect.ex
Normal file
|
@ -0,0 +1,5 @@
|
|||
defimpl Inspect, for: Farmbot.CeleryScript.AST do
|
||||
def inspect(ast, _opts) do
|
||||
"#CeleryScript[#{ast.uid}]<#{ast.kind}: #{inspect(Map.keys(ast.args))}>"
|
||||
end
|
||||
end
|
5
lib/farmbot/celery_script/celery_script.ex
Normal file
5
lib/farmbot/celery_script/celery_script.ex
Normal file
|
@ -0,0 +1,5 @@
|
|||
defmodule Farmbot.CeleryScript do
|
||||
@moduledoc """
|
||||
CeleryScript is the scripting language that Farmbot OS understands.
|
||||
"""
|
||||
end
|
|
@ -1,4 +0,0 @@
|
|||
defmodule Farmbot.CeleryScript.Error do
|
||||
@moduledoc "Implementme.sh"
|
||||
defexception [:message]
|
||||
end
|
|
@ -1,100 +0,0 @@
|
|||
defmodule Farmbot.CeleryScript.Types do
|
||||
@moduledoc """
|
||||
Common types for shared CeleryScript Nodes.
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.Ast
|
||||
|
||||
@typedoc """
|
||||
usually either `farmbot_os` | `arduino_firmware` but will also be things
|
||||
like:
|
||||
|
||||
* Farmware Names
|
||||
* Sync resources.
|
||||
"""
|
||||
@type package :: binary
|
||||
|
||||
@typedoc "X | Y | Z"
|
||||
@type axis :: coord_x_bin | coord_y_bin | coord_z_bin
|
||||
|
||||
@typedoc "The literal string `X`"
|
||||
@type coord_x_bin :: binary
|
||||
|
||||
@typedoc "The literal string `Y`"
|
||||
@type coord_y_bin :: binary
|
||||
|
||||
@typedoc "The literal string `Z`"
|
||||
@type coord_z_bin :: binary
|
||||
|
||||
@typedoc "Integer representing an X coord."
|
||||
@type coord_x :: integer
|
||||
|
||||
@typedoc "Integer representing an Y coord."
|
||||
@type coord_y :: integer
|
||||
|
||||
@typedoc "Integer representing an X coord."
|
||||
@type coord_z :: integer
|
||||
|
||||
@typedoc """
|
||||
Ast in the shape of:
|
||||
```
|
||||
%Ast{
|
||||
kind: "pair",
|
||||
args: %{label: binary, value: any},
|
||||
body: []
|
||||
}
|
||||
```
|
||||
"""
|
||||
@type pair_ast :: ast
|
||||
|
||||
@typedoc false
|
||||
@type pairs_ast :: [pair_ast]
|
||||
|
||||
@typep coord_args :: %{x: coord_x, y: coord_y, z: coord_z}
|
||||
@typedoc """
|
||||
Ast in the shaps of:
|
||||
```
|
||||
%Ast{
|
||||
kind: "coordinate",
|
||||
args: %{x: int, y: int, z: int},
|
||||
body: []
|
||||
}
|
||||
```
|
||||
"""
|
||||
@type coord_ast :: %Ast{kind: binary, args: coord_args, body: []}
|
||||
|
||||
@typedoc """
|
||||
Ast in the shape of:
|
||||
```
|
||||
%Ast{
|
||||
kind: "nothing",
|
||||
args: %{},
|
||||
body: []
|
||||
}
|
||||
```
|
||||
"""
|
||||
@type nothing_ast :: %Ast{kind: binary, args: %{}, body: []}
|
||||
|
||||
@typedoc """
|
||||
Ast in the shape of:
|
||||
```
|
||||
%Ast{
|
||||
kind: "explanation",
|
||||
args: %{message: binary},
|
||||
body: []
|
||||
}
|
||||
```
|
||||
"""
|
||||
|
||||
@type expl_ast_args :: %{message: binary}
|
||||
@type explanation_ast :: %Ast{kind: binary, args: expl_ast_args, body: []}
|
||||
|
||||
@typedoc "Integer representing a pin on the arduino."
|
||||
@type pin_number :: 0..69
|
||||
|
||||
@typedoc "Integer representing digital (0) or pwm (1)"
|
||||
@type pin_mode :: 0 | 1
|
||||
|
||||
@typedoc false
|
||||
@type ast :: Ast.t()
|
||||
end
|
|
@ -1,66 +0,0 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine do
|
||||
@moduledoc "Virtual Machine."
|
||||
|
||||
alias Farmbot.CeleryScript.Ast
|
||||
alias Farmbot.CeleryScript.VirtualMachine.{InstructionSet, StackFrame}
|
||||
require Logger
|
||||
alias Farmbot.CeleryScript.VirtualMachine.RuntimeError, as: VmError
|
||||
|
||||
@typep instruction_set :: InstructionSet.t()
|
||||
@typep ast :: Ast.t()
|
||||
@typep stack_frame :: StackFrame.t()
|
||||
|
||||
defstruct instruction_set: %InstructionSet{},
|
||||
call_stack: [],
|
||||
program: [],
|
||||
pc: -1,
|
||||
running: true
|
||||
|
||||
@typedoc "State of a virtual machine."
|
||||
@type t :: %__MODULE__{
|
||||
instruction_set: instruction_set,
|
||||
call_stack: [stack_frame],
|
||||
program: [ast],
|
||||
pc: integer,
|
||||
running: boolean
|
||||
}
|
||||
|
||||
# increment the program counter by one.
|
||||
defp inc_pc(%__MODULE__{pc: pc} = vm), do: %{vm | pc: pc + 1}
|
||||
|
||||
def step(%__MODULE__{running: true} = vm) do
|
||||
vm
|
||||
|> inc_pc()
|
||||
|> do_step()
|
||||
end
|
||||
|
||||
def step(%__MODULE__{running: false} = vm), do: vm
|
||||
|
||||
defp do_step(%__MODULE__{} = vm) do
|
||||
case vm.program |> Enum.at(vm.pc) do
|
||||
%Ast{kind: kind, args: args, body: body} = ast ->
|
||||
Logger.info("Doing #{inspect(ast)}")
|
||||
|
||||
# Turn kind into an instruction
|
||||
instruction = Module.concat([kind])
|
||||
|
||||
# Lookup the implementation. This could raise.
|
||||
impl = vm.instruction_set[instruction]
|
||||
|
||||
# Build a new stack frame and put it on the stack.
|
||||
sf = %StackFrame{return_address: vm.pc, args: args, body: body}
|
||||
vm = %{vm | call_stack: [sf | vm.call_stack]}
|
||||
|
||||
try do
|
||||
# Execute the implementation.
|
||||
impl.eval(vm)
|
||||
rescue
|
||||
ex in VmError -> reraise(ex, System.stacktrace())
|
||||
ex -> raise VmError, machine: vm, exception: ex
|
||||
end
|
||||
|
||||
nil ->
|
||||
%{vm | running: false}
|
||||
end
|
||||
end
|
||||
end
|
8
lib/farmbot/celery_script/virtual_machine/instruction.ex
Normal file
8
lib/farmbot/celery_script/virtual_machine/instruction.ex
Normal file
|
@ -0,0 +1,8 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction do
|
||||
@moduledoc "Behaviour for implementing CeleryScript nodes."
|
||||
alias Farmbot.CeleryScript.AST
|
||||
|
||||
@callback precompile(AST.t) :: {:ok, AST.t} | {:error, term}
|
||||
|
||||
@callback execute() :: {:ok, Ast.t} | {:error, term}
|
||||
end
|
17
lib/farmbot/celery_script/virtual_machine/instruction/_if.ex
Normal file
17
lib/farmbot/celery_script/virtual_machine/instruction/_if.ex
Normal file
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.If do
|
||||
@moduledoc """
|
||||
_if
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.Calibrate do
|
||||
@moduledoc """
|
||||
calibrate
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.CheckUpdates do
|
||||
@moduledoc """
|
||||
check_updates
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.ConfigUpdate do
|
||||
@moduledoc """
|
||||
config_update
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.Coordinate do
|
||||
@moduledoc """
|
||||
coordinate
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.DataUpdate do
|
||||
@moduledoc """
|
||||
data_update
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.EmergencyLock do
|
||||
@moduledoc """
|
||||
emergency_lock
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.EmergencyUnlock do
|
||||
@moduledoc """
|
||||
emergency_unlock
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.Execute do
|
||||
@moduledoc """
|
||||
execute
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
# require IEx; IEx.pry
|
||||
res = Farmbot.HTTP.get!("/api/sequences/#{ast.args.sequence_id}") |> Map.get(:body) |> Poison.decode! |> Farmbot.CeleryScript.AST.parse
|
||||
{:ok, res}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.ExecuteScript do
|
||||
@moduledoc """
|
||||
execute_script
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.Explanation do
|
||||
@moduledoc """
|
||||
explanation
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.FactoryReset do
|
||||
@moduledoc """
|
||||
factory_reset
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.FindHome do
|
||||
@moduledoc """
|
||||
find_home
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.Home do
|
||||
@moduledoc """
|
||||
home
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.InstallFarmware do
|
||||
@moduledoc """
|
||||
install_farmware
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.InstallFirstPartyFarmware do
|
||||
@moduledoc """
|
||||
install_first_party_farmware
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.MoveAbsolute do
|
||||
@moduledoc """
|
||||
move_absolute
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.MoveRelative do
|
||||
@moduledoc """
|
||||
move_relative
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.Nothing do
|
||||
@moduledoc """
|
||||
nothing
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.Pair do
|
||||
@moduledoc """
|
||||
pair
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.PowerOff do
|
||||
@moduledoc """
|
||||
power_off
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.ReadAllParams do
|
||||
@moduledoc """
|
||||
read_all_params
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.ReadParam do
|
||||
@moduledoc """
|
||||
read_param
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.ReadPin do
|
||||
@moduledoc """
|
||||
read_pin
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.ReadStatus do
|
||||
@moduledoc """
|
||||
read_status
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.Reboot do
|
||||
@moduledoc """
|
||||
reboot
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.RemoveFarmware do
|
||||
@moduledoc """
|
||||
remove_farmware
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule RPCError do
|
||||
@moduledoc """
|
||||
rpc_error
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule RPCOk do
|
||||
@moduledoc """
|
||||
rpc_ok
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule RPCRequest do
|
||||
@moduledoc """
|
||||
rpc_request
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.SendMessage do
|
||||
@moduledoc """
|
||||
send_message
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.Sequence do
|
||||
@moduledoc """
|
||||
sequence
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.SetUserEnv do
|
||||
@moduledoc """
|
||||
set_user_env
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.Sync do
|
||||
@moduledoc """
|
||||
sync
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.TakePhoto do
|
||||
@moduledoc """
|
||||
take_photo
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.TogglePin do
|
||||
@moduledoc """
|
||||
toggle_pin
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.UpdateFarmware do
|
||||
@moduledoc """
|
||||
update_farmware
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.Wait do
|
||||
@moduledoc """
|
||||
wait
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.WritePin do
|
||||
@moduledoc """
|
||||
write_pin
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.Instruction.Zero do
|
||||
@moduledoc """
|
||||
zero
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
|
@ -1,62 +1,103 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.InstructionSet do
|
||||
@moduledoc """
|
||||
Instruction Set for a virtual machine.
|
||||
This will allow swapping of instructions between machine executions.
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.VirtualMachine.UndefinedInstructionError
|
||||
|
||||
defstruct instructions: %{
|
||||
# TODO(Connor) add back all the default modules here.
|
||||
}
|
||||
|
||||
@typedoc "Instruction Set type."
|
||||
@type t :: %__MODULE__{
|
||||
instructions: %{optional(module) => module}
|
||||
@moduledoc "Map of CeleryScript `kind` to implementation module."
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction.{
|
||||
Execute,
|
||||
WritePin,
|
||||
Nothing,
|
||||
InstallFarmware,
|
||||
Calibrate,
|
||||
SetUserEnv,
|
||||
MoveRelative,
|
||||
RpcOk,
|
||||
EmergencyUnlock,
|
||||
MoveAbsolute,
|
||||
ReadAllParams,
|
||||
ReadParam,
|
||||
InstallFirstPartyFarmware,
|
||||
If,
|
||||
Reboot,
|
||||
RpcRequest,
|
||||
Wait,
|
||||
EmergencyLock,
|
||||
TogglePin,
|
||||
Zero,
|
||||
ConfigUpdate,
|
||||
RemoveFarmware,
|
||||
ExecuteScript,
|
||||
Sync,
|
||||
TakePhoto,
|
||||
RpcError,
|
||||
Coordinate,
|
||||
Pair,
|
||||
Home,
|
||||
UpdateFarmware,
|
||||
Sequence,
|
||||
PowerOff,
|
||||
DataUpdate,
|
||||
FactoryReset,
|
||||
SendMessage,
|
||||
Explanation,
|
||||
FindHome,
|
||||
ReadStatus,
|
||||
CheckUpdates,
|
||||
ReadPin
|
||||
}
|
||||
|
||||
@doc false
|
||||
def fetch(%__MODULE__{instructions: instrs}, instr) do
|
||||
impl = instrs[instr] || raise UndefinedInstructionError, instr
|
||||
{:ok, impl}
|
||||
end
|
||||
defstruct _if: If,
|
||||
calibrate: Calibrate,
|
||||
check_updates: CheckUpdates,
|
||||
config_update: ConfigUpdate,
|
||||
coordinate: Coordinate,
|
||||
data_update: DataUpdate,
|
||||
emergency_lock: EmergencyLock,
|
||||
emergency_unlock: EmergencyUnlock,
|
||||
execute: Execute,
|
||||
execute_script: ExecuteScript,
|
||||
explanation: Explanation,
|
||||
factory_reset: FactoryReset,
|
||||
find_home: FindHome,
|
||||
home: Home,
|
||||
install_farmware: InstallFarmware,
|
||||
install_first_party_farmware: InstallFirstPartyFarmware,
|
||||
move_absolute: MoveAbsolute,
|
||||
move_relative: MoveRelative,
|
||||
nothing: Nothing,
|
||||
pair: Pair,
|
||||
power_off: PowerOff,
|
||||
read_all_params: ReadAllParams,
|
||||
read_param: ReadParam,
|
||||
read_pin: ReadPin,
|
||||
read_status: ReadStatus,
|
||||
reboot: Reboot,
|
||||
remove_farmware: RemoveFarmware,
|
||||
rpc_error: RPCError,
|
||||
rpc_ok: RPCOk,
|
||||
rpc_request: RPCRequest,
|
||||
send_message: SendMessage,
|
||||
sequence: Sequence,
|
||||
set_user_env: SetUserEnv,
|
||||
sync: Sync,
|
||||
take_photo: TakePhoto,
|
||||
toggle_pin: TogglePin,
|
||||
update_farmware: UpdateFarmware,
|
||||
wait: Wait,
|
||||
write_pin: WritePin,
|
||||
zero: Zero
|
||||
|
||||
@doc "Builds a new InstructionSet"
|
||||
@spec new :: t
|
||||
def new do
|
||||
# don't use the default implementation.
|
||||
%__MODULE__{instructions: %{}}
|
||||
end
|
||||
def fetch(instrs, instr) when is_binary(instr) do
|
||||
valid_keys = Enum.map(Map.keys(instrs), &Atom.to_string(&1))
|
||||
|
||||
@doc "Implement an instruction. "
|
||||
@spec impl(t, module, module) :: t
|
||||
def impl(%__MODULE__{} = set, instruction, implementation)
|
||||
when is_atom(instruction) and is_atom(implementation) do
|
||||
implementation
|
||||
|> ensure_loaded!
|
||||
|> ensure_implemented!
|
||||
|
||||
instrs = Map.put(set.instructions, instruction, implementation)
|
||||
%{set | instructions: instrs}
|
||||
end
|
||||
|
||||
defp ensure_loaded!(impl) do
|
||||
case Code.ensure_loaded(impl) do
|
||||
{:module, _} ->
|
||||
impl
|
||||
|
||||
{:error, _} ->
|
||||
name = Macro.underscore(impl)
|
||||
raise CompileError, description: "Failed to load implementation: #{name}."
|
||||
if instr in valid_keys do
|
||||
Map.fetch(instrs, :"#{instr}")
|
||||
else
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
defp ensure_implemented!(impl) do
|
||||
unless function_exported?(impl, :eval, 1) do
|
||||
name = Macro.underscore(impl)
|
||||
raise CompileError, description: "#{name} does not implement CeleryScript."
|
||||
def fetch(instrs, instr) do
|
||||
case Map.get(instrs, instr) do
|
||||
nil -> :error
|
||||
mod -> {:ok, mod}
|
||||
end
|
||||
|
||||
impl
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,31 +1,10 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.RuntimeError do
|
||||
@moduledoc "Runtime Error of the Virtual Machine"
|
||||
defexception [:message, :cs_stack_trace]
|
||||
|
||||
alias Farmbot.CeleryScript.VirtualMachine
|
||||
defexception [:exception, :machine]
|
||||
|
||||
@doc "Requires a VirtualMachine state, and a message."
|
||||
def exception(opts) do
|
||||
machine =
|
||||
case Keyword.get(opts, :machine) do
|
||||
%VirtualMachine{} = machine -> machine
|
||||
_ -> raise ArgumentError, "Machine state was not supplied to #{__MODULE__}."
|
||||
end
|
||||
|
||||
exception =
|
||||
Keyword.get(opts, :exception) ||
|
||||
raise ArgumentError, "Exception was not supplied to #{__MODULE__}."
|
||||
|
||||
%__MODULE__{machine: machine, exception: exception}
|
||||
message = Keyword.fetch!(opts, :message)
|
||||
cs_stack_trace = Keyword.fetch!(opts, :cs_stack_trace)
|
||||
%__MODULE__{message: message, cs_stack_trace: cs_stack_trace}
|
||||
end
|
||||
|
||||
@doc false
|
||||
def message(%__MODULE__{exception: ex}) do
|
||||
Exception.message(ex)
|
||||
end
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
exception: Exception.t(),
|
||||
machine: Farmbot.CeleryScript.VirtualMachine.t()
|
||||
}
|
||||
end
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.StackFrame do
|
||||
@moduledoc "Frame of the callstack"
|
||||
|
||||
alias Farmbot.CeleryScript.Ast
|
||||
@enforce_keys [:args, :return_address, :body]
|
||||
defstruct [:args, :return_address, :body]
|
||||
|
||||
@typedoc "Part of a call stack."
|
||||
@type t :: %__MODULE__{
|
||||
args: Ast.args(),
|
||||
body: Ast.body(),
|
||||
return_address: integer
|
||||
}
|
||||
end
|
|
@ -1,11 +0,0 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine.UndefinedInstructionError do
|
||||
@moduledoc "Undefined Instruction. Usually means something is not implemented."
|
||||
|
||||
defexception [:message, :instruction]
|
||||
|
||||
@doc false
|
||||
def exception(instruction) do
|
||||
instr = Macro.underscore(instruction)
|
||||
%__MODULE__{message: "Undefined instruction: #{instr}", instruction: instruction}
|
||||
end
|
||||
end
|
19
lib/farmbot/celery_script/virtual_machine/virtual_machine.ex
Normal file
19
lib/farmbot/celery_script/virtual_machine/virtual_machine.ex
Normal file
|
@ -0,0 +1,19 @@
|
|||
defmodule Farmbot.CeleryScript.VirtualMachine do
|
||||
@moduledoc "Executes CeleryScript"
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias AST.Compiler
|
||||
alias Farmbot.CeleryScript.VirtualMachine.{InstructionSet, RuntimeError}
|
||||
|
||||
defmodule State do
|
||||
@moduledoc false
|
||||
|
||||
defstruct instruction_set: struct(InstructionSet)
|
||||
end
|
||||
|
||||
def execute(ast, state \\ %State{})
|
||||
|
||||
def execute(%AST{} = ast, state) do
|
||||
ast |> Compiler.compile(state.instruction_set)
|
||||
end
|
||||
end
|
7
new_instruction.exs
Normal file
7
new_instruction.exs
Normal file
|
@ -0,0 +1,7 @@
|
|||
Enum.each(struct(Farmbot.CeleryScript.VirtualMachine.InstructionSet) |> Map.from_struct(), fn({snake, camel}) ->
|
||||
camel = Module.split(camel)
|
||||
camel = Enum.join(camel, ".")
|
||||
res = "#{:code.priv_dir(:farmbot)}/instruction.ex.eex" |> EEx.eval_file(camel_instruction: camel, snake_instruction: snake)
|
||||
File.write!("lib/farmbot/celery_script/virtual_machine/instruction/#{snake}.ex", res)
|
||||
end
|
||||
)
|
17
priv/instruction.ex.eex
Normal file
17
priv/instruction.ex.eex
Normal file
|
@ -0,0 +1,17 @@
|
|||
defmodule <%= camel_instruction %> do
|
||||
@moduledoc """
|
||||
<%= snake_instruction %>
|
||||
"""
|
||||
|
||||
alias Farmbot.CeleryScript.AST
|
||||
alias Farmbot.CeleryScript.VirtualMachine.Instruction
|
||||
@behaviour Instruction
|
||||
|
||||
def precompile(%AST{} = ast) do
|
||||
{:ok, ast}
|
||||
end
|
||||
|
||||
def execute(args, body) do
|
||||
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue