|
|
@ -1,8 +1,8 @@ |
|
|
|
#include "UserScript.h"
|
|
|
|
#include "UserScriptRequire.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <span>
|
|
|
|
#include <utility>
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
using interpreter = decltype(scripting::prepare_interpreter({})); |
|
|
|
using namespace scripting; |
|
|
@ -18,28 +18,16 @@ struct fn_array final : public scripting::function_impl { |
|
|
|
|
|
|
|
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(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 cannot contain other arrays" |
|
|
|
.message = "/array: arrays exceeds max size" |
|
|
|
}; |
|
|
|
return std::nullopt; |
|
|
|
} |
|
|
@ -62,7 +50,9 @@ 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) { |
|
|
|
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" |
|
|
|
}; |
|
|
@ -78,13 +68,6 @@ struct fn_array_reverse final : public scripting::function_impl { |
|
|
|
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()); |
|
|
@ -101,20 +84,19 @@ struct fn_array_append final : public scripting::function_impl { |
|
|
|
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) |
|
|
|
: 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; |
|
|
|
} |
|
|
|
using verifier = Verify< |
|
|
|
details::TypeVerifier<0, array>, |
|
|
|
details::VariableVerifier<0>, |
|
|
|
details::SizeAtLeast<1> |
|
|
|
>; |
|
|
|
|
|
|
|
if(std::holds_alternative<script_value>(n.front())) { |
|
|
|
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" |
|
|
|
}; |
|
|
@ -124,41 +106,8 @@ struct fn_array_append final : public scripting::function_impl { |
|
|
|
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" |
|
|
@ -180,13 +129,70 @@ struct fn_array_append final : public scripting::function_impl { |
|
|
|
~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 { |
|
|
|
fn_array_pop() = default; |
|
|
|
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 { |
|
|
|
if(n.size() != 1) { |
|
|
|
using verifier = Verify< |
|
|
|
details::TypeVerifier<0, array>, |
|
|
|
details::VariableVerifier<0>, |
|
|
|
details::SizeEquals<1> |
|
|
|
>; |
|
|
|
|
|
|
|
if(verifier{}.verify(self, n)) { |
|
|
|
error = script_error{ |
|
|
|
.message = "/array_pop takes exactly 1 argument of type array" |
|
|
|
.message = n">name + " takes exactly 1 argument of type array" |
|
|
|
}; |
|
|
|
return std::nullopt; |
|
|
|
} |
|
|
@ -200,13 +206,6 @@ struct fn_array_pop final : public scripting::function_impl { |
|
|
|
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()) { |
|
|
@ -226,8 +225,13 @@ struct fn_array_pop final : public scripting::function_impl { |
|
|
|
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) { |
|
|
|
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" |
|
|
|
}; |
|
|
@ -243,13 +247,6 @@ struct fn_array_size final : public scripting::function_impl { |
|
|
|
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()); |
|
|
|
} |
|
|
|
|
|
|
@ -262,7 +259,20 @@ struct fn_array_index final : public scripting::function_impl { |
|
|
|
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" |
|
|
|
.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; |
|
|
|
} |
|
|
@ -276,13 +286,6 @@ struct fn_array_index final : public scripting::function_impl { |
|
|
|
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; |
|
|
|
|
|
|
@ -292,13 +295,6 @@ struct fn_array_index final : public scripting::function_impl { |
|
|
|
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" |
|
|
@ -406,17 +402,17 @@ struct fn_array_set final : public scripting::function_impl { |
|
|
|
}; |
|
|
|
|
|
|
|
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_pop", std::make_unique<fn_array_pop>(sa">"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); |
|
|
|
} |
|
|
|
|
|
|
|
} |