#include "UserScript.h" #include "UserScriptRequire.h" #include #include #include using interpreter = decltype(scripting::prepare_interpreter({})); using namespace scripting; struct fn_array final : public scripting::function_impl { const int32_t size_limit; const bool can_contain_arrays; fn_array(int32_t _size_limit, bool _can_contain_arrays) : size_limit(_size_limit) , can_contain_arrays(_can_contain_arrays) {} std::optional apply(UserScript* self, std::vector n, std::optional& error) final { array ary; if(not can_contain_arrays && details::NoArrays<0>{}.verify(self, n)) { error = script_error{ .message = "/array: arrays cannot contain other arrays" }; return std::nullopt; } if(n.size() > std::max(0, size_limit)) { error = script_error{ .message = "/array: arrays exceeds max size" }; return std::nullopt; } std::transform(n.rbegin(), n.rend(), std::back_inserter(ary.value), [&](argument& arg){ if(std::holds_alternative(arg)) { return std::get(arg); } else { return self->resolve(std::get(arg).name); } }); return ary; } ~fn_array() final = default; }; struct fn_array_reverse final : public scripting::function_impl { fn_array_reverse() = default; std::optional apply(UserScript* self, std::vector n, std::optional& error) final { using verifier = Verify>; if(verifier{}.verify(self, n)) { error = script_error{ .message = "/array_reverse takes exactly 1 argument of type array" }; return std::nullopt; } auto& arg = n.front(); script_value target; if(std::holds_alternative(arg)) { target = std::get(arg); } else { target = self->resolve(std::get(arg).name); } auto& ary = std::get(target); std::reverse(ary.value.begin(), ary.value.end()); return target; } ~fn_array_reverse() final = default; }; struct fn_array_append final : public scripting::function_impl { const std::string name; const int32_t size_limit; const bool can_contain_arrays; fn_array_append(std::string _name, int32_t _size_limit, bool _can_contain_arrays) : name(std::move(_name)) , size_limit(_size_limit) , can_contain_arrays(_can_contain_arrays) {} std::optional apply(UserScript* self, std::vector n, std::optional& error) final { using verifier = Verify< details::TypeVerifier<0, array>, details::VariableVerifier<0>, details::SizeAtLeast<1> >; if(verifier{}.verify(self, n) && can_contain_arrays || details::NoArrays<1>{}.verify(self, n)) { error = script_error{ .message = "/array_append: must provide an array variable as first argument" }; return std::nullopt; } auto target = self->getValue(std::get(n.back()).name); n.pop_back(); auto& ary = std::get(target.value().get()).value; if(n.size() + ary.size() > std::max(0, size_limit)) { error = script_error{ .message = "/array_append: array would exceed size limit" }; return script_value{0}; } std::transform(n.rbegin(), n.rend(), std::back_inserter(ary), [&](argument& arg){ if(std::holds_alternative(arg)) { return std::get(arg); } else { return self->resolve(std::get(arg).name); } }); return script_value{1}; } ~fn_array_append() final = default; }; struct fn_array_prepend final : public scripting::function_impl { const std::string name; const int32_t size_limit; const bool can_contain_arrays; fn_array_prepend(std::string _name, int32_t _size_limit, bool _can_contain_arrays) : name(std::move(_name)) , size_limit(_size_limit) , can_contain_arrays(_can_contain_arrays) {} std::optional apply(UserScript* self, std::vector n, std::optional& error) final { using verifier = Verify< details::TypeVerifier<0, array>, details::VariableVerifier<0>, details::SizeEquals<2> >; if((verifier{}.verify(self, n)) && (can_contain_arrays || details::NoArrays<1>{}.verify(self, n))) { error = script_error{ .message = "/array_append: must provide an array variable as first argument" }; return std::nullopt; } auto target = self->getValue(std::get(n.back()).name); n.pop_back(); auto& ary = std::get(target.value().get()).value; if(n.size() + ary.size() > std::max(0, size_limit)) { error = script_error{ .message = name + ": array would exceed size limit" }; return script_value{0}; } if(std::holds_alternative(n.front())) { ary.push_front(std::get(n.front())); } else { ary.push_front(self->resolve(std::get(n.front()).name)); } return script_value{1}; } ~fn_array_prepend() final = default; }; struct fn_array_pop final : public scripting::function_impl { std::string name; fn_array_pop(std::string _name) : name(std::move(_name)) {} std::optional apply(UserScript* self, std::vector n, std::optional& error) final { using verifier = Verify< details::TypeVerifier<0, array>, details::VariableVerifier<0>, details::SizeEquals<1> >; if(verifier{}.verify(self, n)) { error = script_error{ .message = name + " takes exactly 1 argument of type array" }; return std::nullopt; } auto& arg = n.front(); script_value target; if(std::holds_alternative(arg)) { target = std::get(arg); } else { target = self->resolve(std::get(arg).name); } auto& ary = std::get(target); if(ary.value.empty()) { return {null{}}; } auto value = ary.value.back(); ary.value.pop_back(); return value; } ~fn_array_pop() final = default; }; struct fn_array_size final : public scripting::function_impl { fn_array_size() = default; std::optional apply(UserScript* self, std::vector n, std::optional& error) final {using verifier = Verify< details::TypeVerifier<0, array>, details::VariableVerifier<0>, details::SizeEquals<1> >; if(verifier{}.verify(self, n)) { error = script_error{ .message = "/array_size takes exactly 1 argument of type array" }; return std::nullopt; } auto& arg = n.front(); script_value target; if(std::holds_alternative(arg)) { target = std::get(arg); } else { target = self->resolve(std::get(arg).name); } return static_cast(std::get(target).value.size()); } ~fn_array_size() final = default; }; struct fn_array_index final : public scripting::function_impl { fn_array_index() = default; std::optional apply(UserScript* self, std::vector n, std::optional& error) final { if(n.size() != 2) { error = script_error{ .message = "/array_size takes exactly 2 argument of type (array, integer)" }; return std::nullopt; } using verifier = Verify< details::TypeVerifier<0, array>, details::VariableVerifier<0>, details::TypeVerifier<1, int32_t> >; if(verifier{}.verify(self, n)) { error = script_error{ .message = "/array_index takes exactly 2 argument of type (array, integer)" }; return std::nullopt; } auto& arg = n.back(); script_value target; if(std::holds_alternative(arg)) { target = std::get(arg); } else { target = self->resolve(std::get(arg).name); } auto& idx_arg = n.front(); script_value idx; if(std::holds_alternative(idx_arg)) { idx = std::get(idx_arg); } else { idx = self->resolve(std::get(idx_arg).name); } if(static_cast(std::get(target).value.size()) <= std::get(idx)) { error = script_error{ .message = "/array_index index must be smaller that the array size, the first element of the array has index 0" }; return std::nullopt; } if(std::get(idx) < 0) { error = script_error{ .message = "/array_index index must be 0 or more" }; return std::nullopt; } return std::get(target).value.at(static_cast(std::get(idx))); } ~fn_array_index() final = default; }; struct fn_array_set final : public scripting::function_impl { fn_array_set() = default; std::optional apply(UserScript* self, std::vector n, std::optional& error) final { if(n.size() != 3) { error = script_error{ .message = "/array_set takes exactly 3 argument of type (array, integer, value), provided a different amount" }; return std::nullopt; } auto& arg = n.back(); if(std::holds_alternative(arg)) { error = script_error{ .message = "/array_set takes exactly 3 argument of type (array, integer, value), the array needs to be a variable" }; return std::nullopt; } auto target = self->getValue(std::get(arg).name); if(not target) { error = script_error{ .message = "/array_set takes exactly 3 argument of type (array, integer, value), provided array variable is undefined" }; return std::nullopt; } if(not std::holds_alternative(target.value().get())) { error = script_error{ .message = "/array_set takes exactly 1 argument of type array followed by an integer, argument 1 is not an array" }; return std::nullopt; } auto& concrete_target = std::get(target.value().get()); auto& idx_arg = n[1]; script_value idx; if(std::holds_alternative(idx_arg)) { idx = std::get(idx_arg); } else { idx = self->resolve(std::get(idx_arg).name); } if(not std::holds_alternative(idx)) { error = script_error{ .message = "/array_set takes exactly 1 argument of type array followed by an integer, argument 2 is not an integer" }; return std::nullopt; } if(static_cast(concrete_target.value.size()) <= std::get(idx)) { error = script_error{ .message = "/array_set index must be smaller that the array size, the first element of the array has index 0" }; return std::nullopt; } if(std::get(idx) < 0) { error = script_error{ .message = "/array_set index must be 0 or more" }; return std::nullopt; } auto& value_arg = n.front(); script_value value; if(std::holds_alternative(value_arg)) { value = std::get(value_arg); } else { value = self->resolve(std::get(value_arg).name); } concrete_target.value.at(std::get(idx)) = value; return concrete_target.value[std::get(idx)]; } ~fn_array_set() final = default; }; namespace scripting { interpreter register_array_lib(interpreter target, bool recursive_arrays, int32_t size_limit) { target->registerFunction("array", std::make_unique(size_limit, recursive_arrays)); target->registerFunction("array_reverse", std::make_unique()); target->registerFunction("array_append", std::make_unique("array_append", size_limit, recursive_arrays)); target->registerFunction("array_push", std::make_unique("array_push", size_limit, recursive_arrays)); target->registerFunction("array_pop", std::make_unique("array_pop")); target->registerFunction("array_size", std::make_unique()); target->registerFunction("array_index", std::make_unique()); target->registerFunction("array_set", std::make_unique()); target->registerFunction("queue_enqueue", std::make_unique("queue_enqueue", size_limit, recursive_arrays)); target->registerFunction("queue_dequeue", std::make_unique("queue_dequeue")); return std::move(target); } }