|
|
- #include "UserScript.h"
- #include <algorithm>
- #include <span>
- #include <utility>
- #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) {
- if(std::any_of(n.begin(), n.end(), [&](argument& arg) {
- if(std::holds_alternative<scripting::script_value>(arg)) {
- return true;
- } else {
- const auto& val = self->resolve(std::get<scripting::script_variable>(arg).name);
- if(not can_contain_arrays && std::holds_alternative<array>(val)) {
- return true;
- }
- }
- return false;
- })) {
- 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 cannot contain other arrays"
- };
- 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 {
- 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<scripting::script_value>(arg)) {
- target = std::get<scripting::script_value>(arg);
- } else {
- target = self->resolve(std::get<scripting::script_variable>(arg).name);
- }
-
- if(not std::holds_alternative<array>(target)) {
- error = script_error{
- .message = "/array_reverse takes exactly 1 argument of type array"
- };
- return std::nullopt;
- }
-
- 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 {
- 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<script_value>(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<script_variable>(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<array>(target.value().get())) {
- error = script_error{
- .message = "/array_append: provided variable is not an array"
- };
- return std::nullopt;
- }
-
- auto& ary = std::get<array>(target.value().get()).value;
-
- if(not can_contain_arrays) {
- if(std::any_of(n.begin(), n.end(), [&](argument& arg) {
- if(std::holds_alternative<scripting::script_value>(arg)) {
- return true;
- } else {
- const auto& val = self->resolve(std::get<scripting::script_variable>(arg).name);
- if(not can_contain_arrays && std::holds_alternative<array>(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<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_pop final : public scripting::function_impl {
- fn_array_pop() = default;
-
- std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& 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<scripting::script_value>(arg)) {
- target = std::get<scripting::script_value>(arg);
- } else {
- target = self->resolve(std::get<scripting::script_variable>(arg).name);
- }
-
- if(not std::holds_alternative<array>(target)) {
- error = script_error{
- .message = "/array_pop takes exactly 1 argument of type array"
- };
- return std::nullopt;
- }
-
- 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 {
- 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<scripting::script_value>(arg)) {
- target = std::get<scripting::script_value>(arg);
- } else {
- target = self->resolve(std::get<scripting::script_variable>(arg).name);
- }
-
- if(not std::holds_alternative<array>(target)) {
- error = script_error{
- .message = "/array_size takes exactly 1 argument of type array"
- };
- return std::nullopt;
- }
-
- 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_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<scripting::script_value>(arg)) {
- target = std::get<scripting::script_value>(arg);
- } else {
- target = self->resolve(std::get<scripting::script_variable>(arg).name);
- }
-
- if(not std::holds_alternative<array>(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<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_index 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>(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;
- };
-
- 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>());
- target->registerFunction("array_size", std::make_unique<fn_array_size>());
- target->registerFunction("array_index", std::make_unique<fn_array_index>());
- return std::move(target);
- }
-
- }
|