98 lines
3.1 KiB
Crystal
98 lines
3.1 KiB
Crystal
require "./RuntimeCall/*"
|
|
|
|
module RuntimeCall
|
|
class UndefinedCall < Exception
|
|
end
|
|
|
|
# Defines a list of functions that are used to find operator, and defines `termine`.
|
|
#
|
|
# It makes the `termine` able to call the function by associating their name with a proc.
|
|
macro extended
|
|
alias RuntimeCallArg = String
|
|
alias RuntimeCallArgsContainer = Array
|
|
alias RuntimeCallArgs = RuntimeCallArgsContainer(RuntimeCallArg)
|
|
|
|
RUNTIME_FUNCTIONS__ = [] of String
|
|
|
|
# Reads into the list of operators functions to call the right one.
|
|
def runtime_call(call : String, values : RuntimeCallArgs)
|
|
call_proc = RUNTIME_CALLS__[call]?
|
|
raise RuntimeCall::UndefinedCall.new %(No runtime call "#{call}" in (#{self.class})) if call_proc.nil?
|
|
call_proc.call(self, values)
|
|
end
|
|
|
|
def runtime_call(call : String, *values)
|
|
call_proc = RUNTIME_CALLS__[call]?
|
|
raise RuntimeCall::UndefinedCall.new %(No runtime call "#{call}" in (#{self.class})) if call_proc.nil?
|
|
if values.size == 0
|
|
call_proc.call self, [] of String
|
|
else
|
|
call_proc.call self, Array(String).new(values.size) { |i| values[i].to_s }
|
|
end
|
|
end
|
|
|
|
# Defines an operator compatible with __set_operators. It also checks the type of the arguments.
|
|
macro define_runtime_call(_call, *_types, &_block)
|
|
def _rt_\{{_call.id}}(values : RuntimeCallArgs)
|
|
\{% if _types.empty? %}
|
|
RuntimeCall.__require_no_arguments(\{{_call}}, values)
|
|
\{{yield}}
|
|
\{% else %}
|
|
args = RuntimeCall.__require_arguments(\{{_call}}, values, \{{*_types}})
|
|
\{{yield args}}
|
|
\{% end %}
|
|
end
|
|
\{{RUNTIME_FUNCTIONS__ << _call}}
|
|
end
|
|
|
|
# Defines getter with an unused parameter so it is compatible with the prototype used by __set_operators
|
|
macro getter_runtime_call(*_calls)
|
|
\{% for _call in _calls %}
|
|
define_runtime_call(\{{_call}}) do
|
|
@\{{_call.id}}
|
|
end
|
|
\{% end %}
|
|
end
|
|
|
|
macro finish_runtime_call()
|
|
RUNTIME_CALLS__ = {
|
|
\{% for _call in RUNTIME_FUNCTIONS__ %}
|
|
\{{_call}} => -> (on : self, args : RuntimeCallArgs) { on._rt_\{{_call.id}}(args) },
|
|
\{% end %}
|
|
}
|
|
end
|
|
end
|
|
|
|
# ```
|
|
# arg1, arg2 = __require_arguments("my_function", UInt32, String)
|
|
# ```
|
|
macro __require_arguments(context, values, *_types)
|
|
raise "Invalid argument count in (#{{{context}}}) with arguments (#{{{values}}})" if {{values}}.size != {{_types.size}}
|
|
{
|
|
{% for _i in 0..._types.size %}
|
|
(
|
|
begin
|
|
{% if _types[_i].stringify == "String" %}
|
|
{{_types[_i]}}.new({{values}}[{{_i}}].to_slice)
|
|
{% else %}
|
|
{{_types[_i]}}.new({{values}}[{{_i}}])
|
|
{% end %}
|
|
rescue err
|
|
raise "Invalid (#{{{_i}}}th) argument (#{{{values}}[{{_i}}]}) in (#{{{context}}})"
|
|
end
|
|
),
|
|
{% end %}
|
|
}
|
|
end
|
|
|
|
macro __require_no_arguments(context, values)
|
|
raise "Invalid argument count in (#{{{context}}}) with arguments (#{{{values}}})" unless {{values}}.size == 0
|
|
end
|
|
|
|
macro extends(&block)
|
|
extend RuntimeCall
|
|
{{yield}}
|
|
finish_runtime_call
|
|
end
|
|
end
|