#include "UserScript.h" #include #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) { if(std::any_of(n.begin(), n.end(), [&](argument& arg) { if(std::holds_alternative(arg)) { return true; } else { const auto& val = self->resolve(std::get(arg).name); if(not can_contain_arrays && std::holds_alternative(val)) { return true; } } return false; })) { 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 cannot contain other arrays" }; 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 { if(n.size() != 1) { 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); } if(not std::holds_alternative(target)) { error = script_error{ .message = "/array_reverse takes exactly 1 argument of type array" }; return std::nullopt; } 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 { if(n.size() < 1) { error = script_error{ .message = "/array_append: must provide an array variable as first argument" }; return std::nullopt; } if(std::holds_alternative(n.front())) { 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(); if(not target) { error = script_error{ .message = "/array_append: provided variable name is undefined" }; return std::nullopt; } if(not std::holds_alternative(target.value().get())) { error = script_error{ .message = "/array_append: provided variable is not an array" }; return std::nullopt; } auto& ary = std::get(target.value().get()).value; if(not can_contain_arrays) { if(std::any_of(n.begin(), n.end(), [&](argument& arg) { if(std::holds_alternative(arg)) { return true; } else { const auto& val = self->resolve(std::get(arg).name); if(not can_contain_arrays && std::holds_alternative(val)) { return true; } } return false; })) { error = script_error{ .message = "/array_append: arrays cannot contain other arrays" }; return std::nullopt; } } 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_pop final : public scripting::function_impl { fn_array_pop() = default; std::optional apply(UserScript* self, std::vector n, std::optional& error) final { if(n.size() != 1) { error = script_error{ .message = "/array_pop 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); } if(not std::holds_alternative(target)) { error = script_error{ .message = "/array_pop takes exactly 1 argument of type array" }; return std::nullopt; } 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 { if(n.size() != 1) { 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); } if(not std::holds_alternative(target)) { error = script_error{ .message = "/array_size takes exactly 1 argument of type array" }; return std::nullopt; } 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_index takes exactly 1 argument of type array followed by an integer, provided a different amount" }; 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); } if(not std::holds_alternative(target)) { error = script_error{ .message = "/array_index takes exactly 1 argument of type array followed by an integer, argument 1 is not an array" }; return std::nullopt; } 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(not std::holds_alternative(idx)) { error = script_error{ .message = "/array_index takes exactly 1 argument of type array followed by an integer, argument 2 is not an integer" }; return std::nullopt; } 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; }; 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()); target->registerFunction("array_size", std::make_unique()); target->registerFunction("array_index", std::make_unique()); return std::move(target); } }