|
|
- #include "UserScript.h"
- #include "UserScriptRequire.h"
- #include <algorithm>
- #include <span>
- #include <utility>
-
- 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<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& 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<int32_t>(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<scripting::script_value>(arg)) {
- return std::get<scripting::script_value>(arg);
- } else {
- return self->resolve(std::get<scripting::script_variable>(arg).name);
- }
- });
-
- return ary;
- }
-
- ~fn_array() final = default;
- };
-
- struct fn_array_reverse final : public scripting::function_impl {
- fn_array_reverse() = default;
-
- std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
- using verifier = Verify<details::TypeVerifier<0, array>>;
-
- 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<scripting::script_value>(arg)) {
- target = std::get<scripting::script_value>(arg);
- } else {
- target = self->resolve(std::get<scripting::script_variable>(arg).name);
- }
-
- auto& ary = std::get<array>(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<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& 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<script_variable>(n.back()).name);
- n.pop_back();
-
- auto& ary = std::get<array>(target.value().get()).value;
-
- if(n.size() + ary.size() > std::max<int32_t>(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<scripting::script_value>(arg)) {
- return std::get<scripting::script_value>(arg);
- } else {
- return self->resolve(std::get<scripting::script_variable>(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<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& 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<script_variable>(n.back()).name);
- n.pop_back();
-
- auto& ary = std::get<array>(target.value().get()).value;
-
- if(n.size() + ary.size() > std::max<int32_t>(0, size_limit)) {
- error = script_error{
- .message = name + ": array would exceed size limit"
- };
- return script_value{0};
- }
-
- if(std::holds_alternative<scripting::script_value>(n.front())) {
- ary.push_front(std::get<scripting::script_value>(n.front()));
- } else {
- ary.push_front(self->resolve(std::get<scripting::script_variable>(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<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& 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<scripting::script_value>(arg)) {
- target = std::get<scripting::script_value>(arg);
- } else {
- target = self->resolve(std::get<scripting::script_variable>(arg).name);
- }
-
- auto& ary = std::get<array>(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<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& 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<scripting::script_value>(arg)) {
- target = std::get<scripting::script_value>(arg);
- } else {
- target = self->resolve(std::get<scripting::script_variable>(arg).name);
- }
-
- return static_cast<int32_t>(std::get<array>(target).value.size());
- }
-
- ~fn_array_size() final = default;
- };
-
- struct fn_array_index final : public scripting::function_impl {
- fn_array_index() = default;
-
- std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& 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<scripting::script_value>(arg)) {
- target = std::get<scripting::script_value>(arg);
- } else {
- target = self->resolve(std::get<scripting::script_variable>(arg).name);
- }
-
- auto& idx_arg = n.front();
- script_value idx;
-
- if(std::holds_alternative<scripting::script_value>(idx_arg)) {
- idx = std::get<scripting::script_value>(idx_arg);
- } else {
- idx = self->resolve(std::get<scripting::script_variable>(idx_arg).name);
- }
-
- if(static_cast<int32_t>(std::get<array>(target).value.size()) <= std::get<int32_t>(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<int32_t>(idx) < 0) {
- error = script_error{
- .message = "/array_index index must be 0 or more"
- };
- return std::nullopt;
- }
-
- return std::get<array>(target).value.at(static_cast<size_t>(std::get<int32_t>(idx)));
- }
-
- ~fn_array_index() final = default;
- };
-
- struct fn_array_set final : public scripting::function_impl {
- fn_array_set() = default;
-
- std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& 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<scripting::script_value>(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<scripting::script_variable>(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<array>(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<array>(target.value().get());
-
- auto& idx_arg = n[1];
- script_value idx;
-
- if(std::holds_alternative<scripting::script_value>(idx_arg)) {
- idx = std::get<scripting::script_value>(idx_arg);
- } else {
- idx = self->resolve(std::get<scripting::script_variable>(idx_arg).name);
- }
-
- if(not std::holds_alternative<int32_t>(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<int32_t>(concrete_target.value.size()) <= std::get<int32_t>(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<int32_t>(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<scripting::script_value>(value_arg)) {
- value = std::get<scripting::script_value>(value_arg);
- } else {
- value = self->resolve(std::get<scripting::script_variable>(value_arg).name);
- }
-
- concrete_target.value.at(std::get<int32_t>(idx)) = value;
-
- return concrete_target.value[std::get<int32_t>(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<fn_array>(size_limit, recursive_arrays));
- target->registerFunction("array_reverse", std::make_unique<fn_array_reverse>());
- target->registerFunction("array_append", std::make_unique<fn_array_append>("array_append", size_limit, recursive_arrays));
- target->registerFunction("array_push", std::make_unique<fn_array_append>("array_push", size_limit, recursive_arrays));
- target->registerFunction("array_pop", std::make_unique<fn_array_pop>("array_pop"));
- target->registerFunction("array_size", std::make_unique<fn_array_size>());
- target->registerFunction("array_index", std::make_unique<fn_array_index>());
- target->registerFunction("array_set", std::make_unique<fn_array_set>());
- target->registerFunction("queue_enqueue", std::make_unique<fn_array_prepend>("queue_enqueue", size_limit, recursive_arrays));
- target->registerFunction("queue_dequeue", std::make_unique<fn_array_pop>("queue_dequeue"));
- return std::move(target);
- }
- }
|