You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

334 lines
9.5 KiB

  1. #include "UserScript.h"
  2. #include <algorithm>
  3. #include <span>
  4. #include <utility>
  5. #include <utility>
  6. using interpreter = decltype(scripting::prepare_interpreter({}));
  7. using namespace scripting;
  8. struct fn_array final : public scripting::function_impl {
  9. const int32_t size_limit;
  10. const bool can_contain_arrays;
  11. fn_array(int32_t _size_limit, bool _can_contain_arrays)
  12. : size_limit(_size_limit)
  13. , can_contain_arrays(_can_contain_arrays)
  14. {}
  15. std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
  16. array ary;
  17. if(not can_contain_arrays) {
  18. if(std::any_of(n.begin(), n.end(), [&](argument& arg) {
  19. if(std::holds_alternative<scripting::script_value>(arg)) {
  20. return true;
  21. } else {
  22. const auto& val = self->resolve(std::get<scripting::script_variable>(arg).name);
  23. if(not can_contain_arrays && std::holds_alternative<array>(val)) {
  24. return true;
  25. }
  26. }
  27. return false;
  28. })) {
  29. error = script_error{
  30. .message = "/array: arrays cannot contain other arrays"
  31. };
  32. return std::nullopt;
  33. }
  34. }
  35. if(n.size() > std::max<int32_t>(0, size_limit)) {
  36. error = script_error{
  37. .message = "/array: arrays cannot contain other arrays"
  38. };
  39. return std::nullopt;
  40. }
  41. std::transform(n.rbegin(), n.rend(), std::back_inserter(ary.value), [&](argument& arg){
  42. if(std::holds_alternative<scripting::script_value>(arg)) {
  43. return std::get<scripting::script_value>(arg);
  44. } else {
  45. return self->resolve(std::get<scripting::script_variable>(arg).name);
  46. }
  47. });
  48. return ary;
  49. }
  50. ~fn_array() final = default;
  51. };
  52. struct fn_array_reverse final : public scripting::function_impl {
  53. fn_array_reverse() = default;
  54. std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
  55. if(n.size() != 1) {
  56. error = script_error{
  57. .message = "/array_reverse takes exactly 1 argument of type array"
  58. };
  59. return std::nullopt;
  60. }
  61. auto& arg = n.front();
  62. script_value target;
  63. if(std::holds_alternative<scripting::script_value>(arg)) {
  64. target = std::get<scripting::script_value>(arg);
  65. } else {
  66. target = self->resolve(std::get<scripting::script_variable>(arg).name);
  67. }
  68. if(not std::holds_alternative<array>(target)) {
  69. error = script_error{
  70. .message = "/array_reverse takes exactly 1 argument of type array"
  71. };
  72. return std::nullopt;
  73. }
  74. auto& ary = std::get<array>(target);
  75. std::reverse(ary.value.begin(), ary.value.end());
  76. return target;
  77. }
  78. ~fn_array_reverse() final = default;
  79. };
  80. struct fn_array_append final : public scripting::function_impl {
  81. const std::string name;
  82. const int32_t size_limit;
  83. const bool can_contain_arrays;
  84. fn_array_append(std::string _name, int32_t _size_limit, bool _can_contain_arrays)
  85. : name(std::move(_name))
  86. , size_limit(_size_limit)
  87. , can_contain_arrays(_can_contain_arrays)
  88. {}
  89. std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
  90. if(n.size() < 1) {
  91. error = script_error{
  92. .message = "/array_append: must provide an array variable as first argument"
  93. };
  94. return std::nullopt;
  95. }
  96. if(std::holds_alternative<script_value>(n.front())) {
  97. error = script_error{
  98. .message = "/array_append: must provide an array variable as first argument"
  99. };
  100. return std::nullopt;
  101. }
  102. auto target = self->getValue(std::get<script_variable>(n.back()).name);
  103. n.pop_back();
  104. if(not target) {
  105. error = script_error{
  106. .message = "/array_append: provided variable name is undefined"
  107. };
  108. return std::nullopt;
  109. }
  110. if(not std::holds_alternative<array>(target.value().get())) {
  111. error = script_error{
  112. .message = "/array_append: provided variable is not an array"
  113. };
  114. return std::nullopt;
  115. }
  116. auto& ary = std::get<array>(target.value().get()).value;
  117. if(not can_contain_arrays) {
  118. if(std::any_of(n.begin(), n.end(), [&](argument& arg) {
  119. if(std::holds_alternative<scripting::script_value>(arg)) {
  120. return true;
  121. } else {
  122. const auto& val = self->resolve(std::get<scripting::script_variable>(arg).name);
  123. if(not can_contain_arrays && std::holds_alternative<array>(val)) {
  124. return true;
  125. }
  126. }
  127. return false;
  128. })) {
  129. error = script_error{
  130. .message = "/array_append: arrays cannot contain other arrays"
  131. };
  132. return std::nullopt;
  133. }
  134. }
  135. if(n.size() + ary.size() > std::max<int32_t>(0, size_limit)) {
  136. error = script_error{
  137. .message = "/array_append: array would exceed size limit"
  138. };
  139. return script_value{0};
  140. }
  141. std::transform(n.rbegin(), n.rend(), std::back_inserter(ary), [&](argument& arg){
  142. if(std::holds_alternative<scripting::script_value>(arg)) {
  143. return std::get<scripting::script_value>(arg);
  144. } else {
  145. return self->resolve(std::get<scripting::script_variable>(arg).name);
  146. }
  147. });
  148. return script_value{1};
  149. }
  150. ~fn_array_append() final = default;
  151. };
  152. struct fn_array_pop final : public scripting::function_impl {
  153. fn_array_pop() = default;
  154. std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
  155. if(n.size() != 1) {
  156. error = script_error{
  157. .message = "/array_pop takes exactly 1 argument of type array"
  158. };
  159. return std::nullopt;
  160. }
  161. auto& arg = n.front();
  162. script_value target;
  163. if(std::holds_alternative<scripting::script_value>(arg)) {
  164. target = std::get<scripting::script_value>(arg);
  165. } else {
  166. target = self->resolve(std::get<scripting::script_variable>(arg).name);
  167. }
  168. if(not std::holds_alternative<array>(target)) {
  169. error = script_error{
  170. .message = "/array_pop takes exactly 1 argument of type array"
  171. };
  172. return std::nullopt;
  173. }
  174. auto& ary = std::get<array>(target);
  175. if(ary.value.empty()) {
  176. return {null{}};
  177. }
  178. auto value = ary.value.back();
  179. ary.value.pop_back();
  180. return value;
  181. }
  182. ~fn_array_pop() final = default;
  183. };
  184. struct fn_array_size final : public scripting::function_impl {
  185. fn_array_size() = default;
  186. std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
  187. if(n.size() != 1) {
  188. error = script_error{
  189. .message = "/array_size takes exactly 1 argument of type array"
  190. };
  191. return std::nullopt;
  192. }
  193. auto& arg = n.front();
  194. script_value target;
  195. if(std::holds_alternative<scripting::script_value>(arg)) {
  196. target = std::get<scripting::script_value>(arg);
  197. } else {
  198. target = self->resolve(std::get<scripting::script_variable>(arg).name);
  199. }
  200. if(not std::holds_alternative<array>(target)) {
  201. error = script_error{
  202. .message = "/array_size takes exactly 1 argument of type array"
  203. };
  204. return std::nullopt;
  205. }
  206. return static_cast<int32_t>(std::get<array>(target).value.size());
  207. }
  208. ~fn_array_size() final = default;
  209. };
  210. struct fn_array_index final : public scripting::function_impl {
  211. fn_array_index() = default;
  212. std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
  213. if(n.size() != 2) {
  214. error = script_error{
  215. .message = "/array_index takes exactly 1 argument of type array followed by an integer, provided a different amount"
  216. };
  217. return std::nullopt;
  218. }
  219. auto& arg = n.back();
  220. script_value target;
  221. if(std::holds_alternative<scripting::script_value>(arg)) {
  222. target = std::get<scripting::script_value>(arg);
  223. } else {
  224. target = self->resolve(std::get<scripting::script_variable>(arg).name);
  225. }
  226. if(not std::holds_alternative<array>(target)) {
  227. error = script_error{
  228. .message = "/array_index takes exactly 1 argument of type array followed by an integer, argument 1 is not an array"
  229. };
  230. return std::nullopt;
  231. }
  232. auto& idx_arg = n.front();
  233. script_value idx;
  234. if(std::holds_alternative<scripting::script_value>(idx_arg)) {
  235. idx = std::get<scripting::script_value>(idx_arg);
  236. } else {
  237. idx = self->resolve(std::get<scripting::script_variable>(idx_arg).name);
  238. }
  239. if(not std::holds_alternative<int32_t>(idx)) {
  240. error = script_error{
  241. .message = "/array_index takes exactly 1 argument of type array followed by an integer, argument 2 is not an integer"
  242. };
  243. return std::nullopt;
  244. }
  245. if(static_cast<int32_t>(std::get<array>(target).value.size()) <= std::get<int32_t>(idx)) {
  246. error = script_error{
  247. .message = "/array_index index must be smaller that the array size, the first element of the array has index 0"
  248. };
  249. return std::nullopt;
  250. }
  251. if(std::get<int32_t>(idx) < 0) {
  252. error = script_error{
  253. .message = "/array_index index must be 0 or more"
  254. };
  255. return std::nullopt;
  256. }
  257. return std::get<array>(target).value.at(static_cast<size_t>(std::get<int32_t>(idx)));
  258. }
  259. ~fn_array_index() final = default;
  260. };
  261. namespace scripting {
  262. interpreter register_array_lib(interpreter target, bool recursive_arrays, int32_t size_limit) {
  263. target->registerFunction("array", std::make_unique<fn_array>(size_limit, recursive_arrays));
  264. target->registerFunction("array_reverse", std::make_unique<fn_array_reverse>());
  265. target->registerFunction("array_append", std::make_unique<fn_array_append>("array_append", size_limit, recursive_arrays));
  266. target->registerFunction("array_push", std::make_unique<fn_array_append>("array_push", size_limit, recursive_arrays));
  267. target->registerFunction("array_pop", std::make_unique<fn_array_pop>());
  268. target->registerFunction("array_size", std::make_unique<fn_array_size>());
  269. target->registerFunction("array_index", std::make_unique<fn_array_index>());
  270. return std::move(target);
  271. }
  272. }