farmbot_os/test/support/celery_script/test_sys_calls.ex

284 lines
5.0 KiB
Elixir
Raw Normal View History

2019-03-26 14:49:43 -06:00
defmodule Farmbot.TestSupport.CeleryScript.TestSysCalls do
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
@moduledoc """
Stub implementation of CeleryScript SysCalls
"""
2019-03-05 10:14:01 -07:00
@behaviour FarmbotCeleryScript.SysCalls
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
use GenServer
def checkout do
case GenServer.start_link(__MODULE__, [], name: __MODULE__) do
{:error, {:already_started, pid}} ->
:ok = GenServer.call(pid, :checkout)
{:ok, pid}
{:ok, pid} ->
:ok = GenServer.call(pid, :checkout)
{:ok, pid}
end
end
def handle(pid, fun) when is_function(fun, 2) do
GenServer.call(pid, {:handle, fun})
end
@impl true
def init([]) do
{:ok, %{checked_out: nil, handler: nil}}
end
@impl true
def handle_call(:checkout, {pid, _}, state) do
{:reply, :ok, %{state | checked_out: pid}}
end
def handle_call({:handle, fun}, {pid, _}, %{checked_out: pid} = state) do
{:reply, :ok, %{state | handler: fun}}
end
def handle_call({kind, args}, _from, %{handler: handler} = state)
when is_function(handler, 2) do
{:reply, {handler, kind, args}, state}
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
end
2019-07-03 14:04:53 -06:00
@impl true
def log(_message, _force) do
2019-07-03 14:04:53 -06:00
:ok
end
@impl true
def sequence_init_log(_message) do
:ok
end
@impl true
def sequence_complete_log(_message) do
:ok
end
@impl true
def coordinate(x, y, z) do
%{x: x, y: y, z: z}
end
@impl true
def nothing() do
call({:nothing, []})
end
@impl true
def set_servo_angle(pin_number, angle) do
call({:set_servo_angle, [pin_number, angle]})
end
2019-08-08 10:15:57 -06:00
@impl true
def set_pin_io_mode(pin_number, mode) do
call({:set_pin_io_mode, [pin_number, mode]})
end
@impl true
def install_first_party_farmware() do
call({:install_first_party_farmware, []})
end
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
@impl true
def point(type, id) do
call({:point, [type, id]})
end
@impl true
def get_point_group(type_or_id) do
call({:get_point_group, [type_or_id]})
end
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
@impl true
def move_absolute(x, y, z, speed) do
call({:move_absolute, [x, y, z, speed]})
end
@impl true
def get_current_x do
call({:get_current_x, []})
end
@impl true
def get_current_y do
call({:get_current_y, []})
end
@impl true
def get_current_z do
call({:get_current_z, []})
end
@impl true
def get_cached_x do
call({:get_cached_x, []})
end
@impl true
def get_cached_y do
call({:get_cached_y, []})
end
@impl true
def get_cached_z do
call({:get_cached_z, []})
end
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
@impl true
def write_pin(pin_number, mode, value) do
call({:write_pin, [pin_number, mode, value]})
end
@impl true
def named_pin(type, id) do
call({:named_pin, [type, id]})
end
@impl true
def read_pin(number, mode) do
call({:read_pin, [number, mode]})
end
@impl true
def read_cached_pin(number) do
call({:read_cached_pin, [number]})
end
@impl true
def toggle_pin(number) do
call({:toggle_pin, [number]})
end
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
@impl true
def wait(millis) do
call({:wait, [millis]})
end
@impl true
def send_message(level, message, channels) do
call({:send_message, [level, message, channels]})
end
@impl true
def find_home(axis) do
call({:find_home, [axis]})
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
end
@impl true
def get_sequence(id) do
call({:get_sequence, [id]})
end
@impl true
def execute_script(name, args) do
call({:execute_script, [name, args]})
end
2019-08-19 10:47:19 -06:00
@impl true
def update_farmware(name) do
call({:update_farmware, [name]})
end
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
@impl true
def read_status do
call({:read_status, []})
end
2019-03-26 14:49:43 -06:00
@impl true
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
def set_user_env(key, val) do
call({:set_user_env, [key, val]})
end
@impl true
def sync do
call({:sync, []})
end
2019-03-26 14:49:43 -06:00
@impl true
def calibrate(axis) do
call({:calibrate, [axis]})
end
@impl true
def flash_firmware(package) do
call({:flash_firmware, [package]})
end
2019-04-15 16:36:39 -06:00
@impl true
2019-05-06 10:43:57 -06:00
def change_ownership(email, secret, server) do
call({:change_ownership, [email, secret, server]})
2019-04-15 16:36:39 -06:00
end
@impl true
def dump_info() do
call({:dump_info, []})
end
@impl true
def factory_reset(package) do
call({:factory_reset, [package]})
2019-04-15 16:36:39 -06:00
end
@impl true
def firmware_reboot do
call({:firmware_reboot, []})
end
@impl true
def power_off do
call({:power_off, []})
end
@impl true
def reboot do
call({:reboot, []})
end
@impl true
def resource_update(kind, id, params) do
call({:resource_update, [kind, id, params]})
end
2019-05-06 10:43:57 -06:00
@impl true
def check_update() do
call({:check_update, []})
end
@impl true
def emergency_lock() do
call({:emergency_lock, []})
end
@impl true
def emergency_unlock() do
call({:emergency_unlock, []})
end
@impl true
def get_toolslot_for_tool(id) do
call({:get_toolslot_for_tool, [id]})
end
@impl true
def home(axis, speed) do
call({:home, [axis, speed]})
end
@impl true
def zero(axis) do
call({:zero, [axis]})
end
@impl true
def eval_assertion(comment, expression) do
call({:eval_assertion, [comment, expression]})
end
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
defp call(data) do
{handler, kind, args} = GenServer.call(__MODULE__, data, :infinity)
handler.(kind, args)
Implement new CeleryScript Runtime environment. This is obviously a rather large change warranting an essay describing it. A Brief overview Basically the old implementation had quite a few down sides preventing it from really working as intended, especially with the addition of the variables feature. Here is the shortlist of things that needed addressing: * No scoping between sequences. What this essentially means is that a sequence that executes another sequence is unable to add data to the calle. This is important for using Variables. * Error recovery certain nodes have a high likelyhood of failing such as anything that interfaces the firmware. Much focus was spent ensuring that errors would be recoverable when desired. * Complexity of control flow asts versus action asts. Nodes such as `if` will always work in the same way regardless of the state of the rest of the system meaning there is no reason for it to have a special implementation per environment. on the other hand `move_absolute` is bound to a specific part of the system. Seperating these concerns allows for better testing of each piece independently. A More In Depth overview The core of this change resolves around 1 really big change resulting in many more small changes. This change is the CeleryScript `compiler`. The TLDR of this system is that now CeleryScript ASTs are deterministicly compiled to Elixir's AST and executed. Doing this has some big benifits as described below. 1) CeleryScript "runtime" environment is now much simpiler in favor of a somewhat complex "compile time" environment. Basically instead of EVERY single CeleryScript AST having a custom runtime implementation, only a subset of ASTs that require external services such as the Firmware, Database, HTTP, etc require having a runtime implementation. This subset of ASTs are called `SysCalls`. Also the runtime implementations are compiled to a single function call that can be implemented instead of needing to have a contextual environment and making decisions at runtime to evaluate variables and the like. 2) Static analysis is now possible. This means an incorrectly crafted sequence can be validated at compile time rather than getting half way through a sequence before finding the error. 3) Having the "external services" separated leads to better plugability. There is now a behaviour to be implemented for the subset of syscalls that are system specific.
2019-02-20 12:57:45 -07:00
end
end