macroscript/stdlib.rs
1/*!
2Contains the standard library for macroscript.
3
4If you want to see the documentation for all macros at once, see [`DocumentationHelper`].
5
6*/
7
8
9#![allow(
10 clippy::cast_sign_loss,
11 clippy::float_cmp,
12 clippy::cast_possible_truncation,
13 clippy::cast_possible_wrap
14)]
15
16use itertools::Itertools;
17use std::{
18 collections::HashMap,
19 str::FromStr,
20 hash::{Hasher, BuildHasher}
21};
22use rand_pcg::Pcg32;
23use rand::{Rng, SeedableRng};
24use seahash::SeaHasher;
25use regex::Regex;
26
27use crate::{execution::{Macro, MacroError, MacroErrorKind}, parsing::unescape, TextMacro};
28
29macro_rules! count {
30 ($tt: tt $($tts: tt)*) => {
31 1 + count!($($tts)*)
32 };
33 () => {0}
34}
35
36macro_rules! builtin_macros {
37 ($($(#[$attr: meta])* macro $id: ident as $name: literal {$inner: item})*) => {$(
38 #[derive(Debug, Copy, Clone, PartialEq, Eq, Default, Hash)]
39 #[doc = concat!("See the documentation on [`DocumentationHelper`] for documentation on this struct.")]
40 pub struct $id;
41
42 impl Macro for $id {
43 $inner
44 }
45 )*
46
47 /// Item purely for documentation purposes of the standard library.
48 /// Dynamically made for easier browsing.
49 /**
50# Core macros
51Even without the standard library, there are a few core macros that are always included. They are as follows:
52
53## `try`
54Executes some escaped macroscript, and returns a boolean value and output.
55
56- If the inner script errors, then the boolean is `false` and the output is the error message.
57- If the inner script succeeds, then the boolean is `true` and the output is the result of the inner script.
58
59This is reminiscent of Lua's `pcall` function.
60
61### Examples
62```
63# use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
64[try/\[add\/5\/5\]] -> true/10
65[try/\[shl\/5\/100\]] -> false/shift amount of 100 is too large
66# "#)}
67```
68
69## `load`
70Loads a variable's value and returns it. Errors if the variable doesn't exist.
71
72### Examples
73```
74# use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
75[load/x] -> error: variable "x" does not currently exist
76[store/x/5][load/x] -> 5
77# "#)}
78```
79
80## `store`
81Stores a value into a variable and returns nothing.
82
83The variable table is global to the `apply_macros` function.
84
85### Example
86```
87# use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
88[store/x/5] -> <no output>
89# "#)}
90```
91
92## `drop`
93Deletes a variable.
94
95### Example
96```
97# use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
98[store/x/5][drop/x][load/x] -> error: variable "x" does not currently exist
99# "#)}
100```
101
102## `get`
103Gets the value of a variable, storing a supplied default and returning it if the variable doesn't exist.
104
105### Example
106```
107# use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
108[get/x/5],[load/x] -> 5,5
109# "#)}
110```
111
112## `is_stored`
113Returns whether a variable currently exists.
114
115### Examples
116```
117# use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
118[is_stored/x] -> false
119[store/x/5][is_stored/x] -> true
120# "#)}
121```
122 */
123 /// ---
124 /// # Standard library
125 ///
126 /// These macros need to be included using [`crate::add_stdlib`].
127 ///
128 $(
129 /// ---
130 ///
131 #[doc = concat!("# [`", $name, "`](struct@", stringify!($id), ")")]
132 $(#[$attr])*
133 ///
134 )*
135 pub enum DocumentationHelper {}
136
137 /// Adds the standard library's builtin macros to a map of macro names.
138 pub fn add(macros: &mut HashMap<String, Box<dyn Macro>, impl BuildHasher>) {
139 $(
140 macros.insert($name.into(), Box::new($id));
141 )*
142 }
143 }
144}
145
146macro_rules! get_args {
147 ($name: literal, $arguments: ident; $($ids: ident),+) => {{
148 let mut args = $arguments.iter();
149 let c = count!($($ids)*);
150 get_args!{_recur $name args; c $($ids)* | }
151 }};
152 (_recur $name: literal $args: ident; $amount: ident $id: ident $($ids: ident)* | $($leftover: ident)*) => {
153 let Some($id) = $args.next() else {
154 return Err(MacroError::new(
155 $name.into(),
156 MacroErrorKind::not_enough_args($amount, count!($($leftover)*))
157 ));
158 };
159 get_args!{ _recur $name $args; $amount $($ids)* | $($leftover)* $id }
160 };
161 (_recur $name: literal $args: ident; $amount: ident | $($leftover: ident)*) => {
162 ($($leftover,)*)
163 }
164}
165
166macro_rules! convert_to_number {
167 ($name: literal; at $idx: expr => $arg: expr) => {
168 convert_to_number!($name; <f64> at $idx => $arg)
169 };
170 ($name: literal; <$ty: ty> at $idx: expr => $arg: expr) => {{
171 let arg = $arg;
172 <$ty>::from_str(arg).map_err(|_| {
173 MacroError::new(
174 $name.into(),
175 MacroErrorKind::user(
176 format!("could not convert argument {} \"{arg}\" to {}", $idx, stringify!($ty))
177 )
178 )
179 })?
180 }}
181}
182
183fn truthy(string: impl AsRef<str>) -> bool {
184 match string.as_ref() {
185 "true" | "True" => true,
186 v if f64::from_str(v).is_ok_and(|v| v > 0. && !v.is_nan()) => true,
187 _ => false
188 }
189}
190
191builtin_macros! {
192 /// Comment. Returns nothing.
193 /// ### Example
194 /// ```
195 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
196 /// [/comment!] -> <no output>
197 /// # "#)}
198 /// ```
199 macro Comment as "" {
200 fn apply(&self, _arguments: Vec<&str>) -> Result<String, MacroError> {
201 Ok(String::new())
202 }
203 }
204
205 /// Reverses the given inputs.
206 /// ### Example
207 /// ```
208 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
209 /// [reverse/one/tw\/o/thr\\ee] -> thr\\ee/tw\/o/one
210 /// # "#)}
211 /// ```
212 macro Reverse as "reverse" {
213 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
214 Ok(
215 arguments
216 .into_iter()
217 .rev()
218 .join("/")
219 )
220 }
221 }
222
223 /// Addition. Takes 0 or more numeric arguments and returns their sum.
224 /// ### Examples
225 /// ```
226 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
227 /// [add/3/2/3/5/3] -> 16
228 /// [add/5] -> 5
229 /// [add] -> 0
230 /// [add/a/b] -> error: could not convert argument 1 "a" to f64
231 /// # "#)}
232 /// ```
233 macro Add as "add" {
234 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
235 arguments
236 .iter()
237 .enumerate()
238 .map(|(idx, arg)| {
239 Ok(convert_to_number!("add"; at idx+1 => arg))
240 })
241 .process_results(|iter| iter.sum())
242 .map(|sum: f64| sum.to_string())
243 }
244 }
245
246 /// Multiplicaton. Takes 0 or more numeric arguments and returns their product.
247 /// ### Examples
248 /// ```
249 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
250 /// [multiply/1/2/3/4/5] -> 120
251 /// [multiply/5] -> 5
252 /// [multiply] -> 1
253 /// # "#)}
254 /// ```
255 macro Multiply as "multiply" {
256 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
257 arguments
258 .iter()
259 .enumerate()
260 .map(|(idx, arg)| {
261 Ok(convert_to_number!("multiply"; at idx+1 => arg))
262 })
263 .process_results(|iter| iter.product())
264 .map(|product: f64| product.to_string())
265 }
266 }
267
268 /// Unescapes its input.
269 /// ### Examples
270 /// ```
271 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
272 /// [unescape/among\/us] -> among/us
273 /// [unescape/[if/true/\[add\/1\/1\]/\[add\/2\/1\]]] -> 2
274 /// # "#)}
275 /// ```
276 macro Unescape as "unescape" {
277 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
278 let (first_arg, ) = get_args!("unescape", arguments; first_arg);
279 Ok(unescape(first_arg).to_string())
280 }
281 }
282
283 /// Basic alternation. Chooses between all even arguments with the condition of the odd ones,
284 /// with the last as a base case.
285 /// ### Examples
286 /// ```
287 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
288 /// [if/true/a/true/b/c] -> a
289 /// [if/false/a/true/b/c] -> b
290 /// [if/false/a/false/b/c] -> c
291 /// [if/false/a/false/b] -> error: all conditions exhausted
292 /// [if/c] -> c
293 /// # "#)}
294 /// ```
295 macro If as "if" {
296 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
297 let mut chunks = arguments.chunks_exact(2);
298 // Technically refutable pattern
299 while let Some([condition, value]) = chunks.next() {
300 if truthy(condition) {
301 return Ok((*value).to_string());
302 }
303 }
304 if let [end] = chunks.remainder() {
305 Ok((*end).to_string())
306 } else {
307 Err(MacroError {
308 name: "if".into(),
309 error_type: MacroErrorKind::User {
310 message: "all conditions exhausted".into()
311 }
312 })
313 }
314 }
315 }
316
317 /// Returns whether a string is "truthy", i.e. whether it converts to true or false.
318 /// Truthy strings have to be either "True", "true", or a number greater than 0.
319 /// ### Examples
320 /// ```
321 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
322 /// [truthy/1] -> true
323 /// [truthy/0] -> false
324 /// [truthy/ture] -> false
325 /// [truthy/among us] -> false
326 /// [truthy/True/true] -> true/true
327 /// # "#)}
328 /// ```
329 macro Truthy as "truthy" {
330 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
331 Ok(arguments.into_iter().map(truthy).join("/"))
332 }
333 }
334
335 /// Returns whether a string can be converted to a number.
336 /// ### Examples
337 /// ```
338 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
339 /// [is_number/1] -> true
340 /// [is_number/abc/2] -> false/true
341 /// # "#)}
342 /// ```
343 macro IsNumber as "is_number" {
344 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
345 Ok(arguments.into_iter().map(|v| f64::from_str(v).is_ok()).join("/"))
346 }
347 }
348
349 /// Raises a number to the power of another.
350 /// ### Examples
351 /// ```
352 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
353 /// [pow/7/2] -> 49
354 /// # "#)}
355 /// ```
356 macro Pow as "pow" {
357 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
358 let (base, exp) = get_args!("pow", arguments; base, exp);
359 let base = convert_to_number!("pow"; at 1 => base);
360 let exp = convert_to_number!("pow"; at 2 => exp);
361 Ok(base.powf(exp).to_string())
362 }
363 }
364
365 /// Subtracts a number from another.
366 /// ### Examples
367 /// ```
368 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
369 /// [subtract/7/2] -> 5
370 /// [subtract/3/5] -> -2
371 /// # "#)}
372 /// ```
373 macro Sub as "subtract" {
374 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
375 let (lhs, rhs) = get_args!("subtract", arguments; a, b);
376 let lhs = convert_to_number!("subtract"; at 1 => lhs);
377 let rhs = convert_to_number!("subtract"; at 2 => rhs);
378 Ok((lhs - rhs).to_string())
379 }
380 }
381
382 /// Divides a number by another.
383 /// ### Examples
384 /// ```
385 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
386 /// [divide/5/2] -> 2.5
387 /// [divide/3/5] -> 0.6
388 /// [divide/1/0] -> inf
389 /// [divide/-1/0] -> -inf
390 /// [divide/0/0] -> NaN
391 /// # "#)}
392 /// ```
393 macro Div as "divide" {
394 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
395 let (lhs, rhs) = get_args!("divide", arguments; a, b);
396 let lhs = convert_to_number!("divide"; at 1 => lhs);
397 let rhs = convert_to_number!("divide"; at 2 => rhs);
398 Ok((lhs / rhs).to_string())
399 }
400 }
401
402 /// Takes the modulus of one number with respect to another.
403 /// ### Examples
404 /// ```
405 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
406 /// [mod/5/2] -> 1
407 /// [mod/-3/5] -> 2
408 /// # "#)}
409 /// ```
410 macro Modulus as "mod" {
411 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
412 let (lhs, rhs) = get_args!("mod", arguments; a, b);
413 let lhs = convert_to_number!("mod"; at 1 => lhs);
414 let rhs = convert_to_number!("mod"; at 2 => rhs);
415 Ok(lhs.rem_euclid(rhs).to_string())
416 }
417 }
418
419
420 /// Takes the logarithm of a number. The base is optional, and defaults to [`std::f64::consts::E`].
421 /// ### Examples
422 /// ```
423 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
424 /// [log/5] -> 1.6094379124341003
425 /// [log/16/2] -> 4
426 /// # "#)}
427 /// ```
428 macro Log as "log" {
429 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
430 let (value, ) = get_args!("log", arguments; value);
431 let value = convert_to_number!("log"; at 1 => value);
432 let base = if let Some(base) = arguments.get(1) {
433 convert_to_number!("log"; at 2 => base)
434 } else {
435 std::f64::consts::E
436 };
437 Ok(value.log(base).to_string())
438 }
439 }
440
441 /// Gets a random number on the range [0, 1).
442 /// A seed can optionally be supplied.
443 /// ### Examples
444 /// ```
445 /// # /*
446 /// [rand] -> ?
447 /// # */
448 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
449 /// [rand/among us] -> 0.22694492387911513
450 /// # "#)}
451 /// ```
452 macro Rand as "rand" {
453 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
454 let value: f64 = if let Some(seed) = arguments.first() {
455 let mut hasher = SeaHasher::new();
456 hasher.write(seed.as_bytes());
457 let mut rand = Pcg32::seed_from_u64(hasher.finish());
458 rand.gen()
459 } else {
460 rand::random()
461 };
462 Ok(value.to_string())
463 }
464 }
465
466 /// Hashes many values, returning 64-bit integers.
467 /// ### Examples
468 /// ```
469 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
470 /// [hash/rain world/brain rot] -> -4983183619591677382/-1860790453662518022
471 /// # "#)}
472 /// ```
473 macro Hash as "hash" {
474 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
475 Ok(
476 arguments.iter().map(|value| {
477 let mut hasher = SeaHasher::new();
478 hasher.write(value.as_bytes());
479 hasher.finish() as i64
480 }).join("/")
481 )
482 }
483 }
484
485 /// Replaces all matches of a regular expression with a pattern.
486 /// Both the pattern and replacement are unescaped.
487 /// ### Examples
488 /// ```
489 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
490 /// [replace/vaporeon/(\[aeiou\])/$1$1] -> vaapooreeoon
491 /// [replace/porygon/\[o/e] -> error: unclosed character class
492 /// # "#)}
493 /// ```
494 macro Replace as "replace" {
495 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
496 let (haystack, pattern, replacement) = get_args!("hash", arguments; a, b, c);
497 let pattern = unescape(pattern);
498 let replacement = unescape(replacement);
499 let regex = Regex::new(&pattern).map_err(|err| {
500 let disp = match err {
501 regex::Error::Syntax(err) => {
502 let err_string = err.to_string();
503 let last_line = err_string.lines().last().unwrap();
504 last_line[7..].to_string()
505 },
506 regex::Error::CompiledTooBig(limit) =>
507 format!("compiled regex exceeds size limit of {limit} bytes"),
508 _ => err.to_string()
509 };
510 MacroError::new("replace".into(), MacroErrorKind::user(disp))
511 })?;
512 let res = regex.replace_all(haystack, replacement);
513 Ok(res.into_owned())
514 }
515 }
516
517 /// Converts the input to an integer, with an optional base to convert from.
518 /// ### Examples
519 /// ```
520 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
521 /// [int/54.2] -> 54
522 /// [int/-101/2] -> -5
523 /// [int/E621/16] -> 58913
524 /// # "#)}
525 /// ```
526 macro Int as "int" {
527 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
528 let (value, ) = get_args!("int", arguments; value);
529 if let Some(base) = arguments.get(1) {
530 let base = convert_to_number!("int"; <u32> at 2 => base);
531 if !(2 ..= 36).contains(&base) {
532 return Err(MacroError::new("int".into(), MacroErrorKind::user(
533 format!("invalid base {base} (must be between 2 and 36, inclusive)")
534 )));
535 }
536 i64::from_str_radix(value, base)
537 .map(|v| v.to_string())
538 .map_err(|_| MacroError::new("int".into(), MacroErrorKind::user(
539 format!("failed to convert {value} to a number with base {base}")
540 )))
541 } else {
542 let value = convert_to_number!("int"; at 1 => value) as i64;
543 Ok(value.to_string())
544 }
545 }
546 }
547
548 /// Converts the input to a hexadecimal integer.
549 /// ### Examples
550 /// ```
551 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
552 /// [hex/16] -> 10
553 /// [hex/255/5] -> FF/5
554 /// # "#)}
555 /// ```
556 macro Hex as "hex" {
557 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
558 arguments.iter().enumerate()
559 .map(|(idx, value)|
560 Ok(format!("{:X}", convert_to_number!("hex"; <i64> at idx + 1 => value)))
561 ).process_results(|mut iter| iter.join("/"))
562 }
563 }
564
565
566 /// Converts the input to a binary integer.
567 /// ### Examples
568 /// ```
569 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
570 /// [bin/5] -> 101
571 /// [bin/7/8] -> 111/1000
572 /// # "#)}
573 /// ```
574 macro Bin as "bin" {
575 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
576 arguments.iter().enumerate()
577 .map(|(idx, value)|
578 Ok(format!("{:b}", convert_to_number!("bin"; <i64> at idx + 1 => value)))
579 ).process_results(|mut iter| iter.join("/"))
580 }
581 }
582
583
584 /// Converts the input to an octal integer.
585 /// ### Examples
586 /// ```
587 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
588 /// [oct/59] -> 73
589 /// [oct/1777/755] -> 3361/1363
590 /// # "#)}
591 /// ```
592 macro Oct as "oct" {
593 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
594 arguments.iter().enumerate()
595 .map(|(idx, value)|
596 Ok(format!("{:o}", convert_to_number!("oct"; <i64> at idx + 1 => value)))
597 ).process_results(|mut iter| iter.join("/"))
598 }
599 }
600
601 /// Converts a unicode codepoint to a character.
602 /// Note that this will error for invalid codepoints!
603 /// ### Examples
604 /// ```
605 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
606 /// [chr/55296] -> error: invalid codepoint at argument 1
607 /// [chr/65] -> A
608 /// [chr/65/109/111/110/103/32/85/115] -> Among Us
609 /// # "#)}
610 /// ```
611 macro Chr as "chr" {
612 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
613 arguments
614 .iter().enumerate()
615 .map(|(idx, chr)| {
616 let ord = convert_to_number!("chr"; <u32> at idx + 1 => *chr);
617 char::from_u32(ord).ok_or_else(|| MacroError::new("chr".into(), MacroErrorKind::user(
618 format!("invalid codepoint at argument {}", idx + 1)
619 )))
620 }).collect()
621 }
622 }
623
624 /// Converts characters into their unicode codepoints.
625 /// ### Examples
626 /// ```
627 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
628 /// [ord/] -> <no output>
629 /// [ord/A] -> 65
630 /// [ord/Among Us] -> 65/109/111/110/103/32/85/115
631 /// # "#)}
632 /// ```
633 macro Ord as "ord" {
634 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
635 let (value, ) = get_args!("ord", arguments; value);
636 Ok(value.chars()
637 .map(|c| (c as u32).to_string())
638 .join("/"))
639 }
640 }
641
642 /// Gets the length of the inputs.
643 /// ### Examples
644 /// ```
645 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
646 /// [len/] -> 0
647 /// [len/abc] -> 3
648 /// [len/abc/de] -> 3/2
649 /// # "#)}
650 /// ```
651 macro Length as "len" {
652 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
653 Ok(arguments.into_iter().map(|c| c.chars().count().to_string()).join("/"))
654 }
655 }
656
657 /// Splits the first input delimited by the second,
658 /// then returns the section at the third argument.
659 /// ### Example
660 /// ```
661 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
662 /// [split/a,b,c/,/1] -> b
663 /// # "#)}
664 /// ```
665 macro Split as "split" {
666 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
667 let (haystack, delimiter, index) = get_args!("split", arguments; a, b, c);
668 let index = convert_to_number!("split"; <usize> at 1 => index);
669 haystack.split(&**delimiter).nth(index)
670 .map(ToString::to_string)
671 .ok_or_else(|| MacroError::new(
672 "split".into(), MacroErrorKind::user(
673 format!("index {index} is out of bounds")
674 )
675 ))
676 }
677 }
678
679 /// Selects one of the arguments based on an index on the first.
680 /// If the index is `#`, returns the number of arguments, minus 1 for the `#`.
681 /// ### Examples
682 /// ```
683 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
684 /// [select/1/a/b/c] -> a
685 /// [select/#/one/two/three] -> 3
686 /// [select/0/it works, but why would you do this?] -> 0
687 /// [select/5/a/b] -> error: index 5 is out of bounds
688 /// [select/-1/nope, this isn't python] -> error: could not convert argument 1 "-1" to usize
689 /// # "#)}
690 /// ```
691 macro Select as "select" {
692 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
693 let (index, ) = get_args!("select", arguments; a);
694 if *index == "#" {
695 return Ok((arguments.len() - 1).to_string());
696 }
697 let index = convert_to_number!("select"; <usize> at 1 => index);
698 arguments.get(index)
699 .map(ToString::to_string)
700 .ok_or_else(|| MacroError::new(
701 "select".into(), MacroErrorKind::user(
702 format!("index {index} is out of bounds")
703 )
704 ))
705 }
706 }
707
708 /// Returns whether many strings are equal.
709 /// ### Examples
710 /// ```
711 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
712 /// [equal/one/one] -> true
713 /// [equal/one/two/three] -> false
714 /// [equal/1/1] -> true
715 /// [equal/1/1.0] -> false
716 /// # "#)}
717 /// ```
718 macro Equal as "equal" {
719 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
720 Ok(arguments.windows(2).all(|w| *w[0] == *w[1]).to_string()) // ** to convert &Cow<str> to str
721 }
722 }
723
724 /// Returns whether a number is equal to another.
725 /// ### Examples
726 /// ```
727 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
728 /// [#equal/1/1.0] -> true
729 /// [#equal/0.3/[add/0.1/0.2]] -> false
730 /// [#equal/nan/nan] -> false
731 /// # "#)}
732 /// ```
733 macro NumEqual as "#equal" {
734 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
735 let (lhs, rhs) = get_args!("#equal", arguments; a, b);
736 let lhs = convert_to_number!("#equal"; at 1 => lhs);
737 let rhs = convert_to_number!("#equal"; at 2 => rhs);
738 Ok((lhs == rhs).to_string())
739 }
740 }
741
742 /// Returns whether a number is greater than another.
743 /// ### Examples
744 /// ```
745 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
746 /// [greater/1/1] -> false
747 /// [greater/0.2/0.1] -> true
748 /// [greater/nan/nan] -> false
749 /// # "#)}
750 /// ```
751 macro Greater as "greater" {
752 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
753 let (lhs, rhs) = get_args!("greater", arguments; a, b);
754 let lhs = convert_to_number!("greater"; at 1 => lhs);
755 let rhs = convert_to_number!("greater"; at 2 => rhs);
756 Ok((lhs > rhs).to_string())
757 }
758 }
759
760 /// Returns whether a number is less than another.
761 /// ### Examples
762 /// ```
763 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
764 /// [less/1/1] -> false
765 /// [less/0.1/0.2] -> true
766 /// [less/nan/nan] -> false
767 /// # "#)}
768 /// ```
769 macro Less as "less" {
770 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
771 let (lhs, rhs) = get_args!("less", arguments; a, b);
772 let lhs = convert_to_number!("less"; at 1 => lhs);
773 let rhs = convert_to_number!("less"; at 2 => rhs);
774 Ok((lhs < rhs).to_string())
775 }
776 }
777
778 /// Negates many boolean inputs.
779 /// ### Examples
780 /// ```
781 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
782 /// [not/1.0] -> false
783 /// [not/true/false/3.0/-5.9] -> false/true/false/true
784 /// # "#)}
785 /// ```
786 macro Not as "not" {
787 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
788 Ok(
789 arguments.iter()
790 .map(truthy)
791 .map(|v| !v)
792 .map(|v| v.to_string())
793 .join("/")
794 )
795 }
796 }
797
798 /// Takes the logical AND of an arbitrary number of boolean inputs.
799 /// ### Examples
800 /// ```
801 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
802 /// [and/true/true] -> true
803 /// [and/false/true/true] -> false
804 /// # "#)}
805 /// ```
806 macro And as "and" {
807 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
808 Ok(
809 arguments.iter()
810 .map(truthy)
811 .reduce(|a, b| a && b)
812 .unwrap_or(false)
813 .to_string()
814 )
815 }
816 }
817
818 /// Takes the logical OR of an arbitrary number of boolean inputs.
819 /// ### Example
820 /// ```
821 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
822 /// [or/false/true] -> true
823 /// [or/false/true/true] -> true
824 /// # "#)}
825 /// ```
826 macro Or as "or" {
827 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
828 Ok(
829 arguments.iter()
830 .map(truthy)
831 .reduce(|a, b| a || b)
832 .unwrap_or(false)
833 .to_string()
834 )
835 }
836 }
837
838
839 /// Takes the logical XOR of an arbitrary number of boolean inputs.
840 /// ### Examples
841 /// ```
842 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
843 /// [xor/false/true] -> true
844 /// [xor/false/true/true] -> false
845 /// # "#)}
846 /// ```
847 macro Xor as "xor" {
848 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
849 Ok(
850 arguments.iter()
851 .map(truthy)
852 .reduce(|a, b| a ^ b)
853 .unwrap_or(false)
854 .to_string()
855 )
856 }
857 }
858
859 /// Takes the bitwise NOT of many 64-bit signed integer inputs.
860 /// ### Example
861 /// ```
862 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
863 /// [#not/0] -> -1 (0b00...0 -> 0b11...1)
864 /// [#not/5/-4] -> -6/3
865 /// # "#)}
866 /// ```
867 macro BitNot as "#not" {
868 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
869 arguments.iter().enumerate()
870 .map(|(idx, value)|
871 Ok(!convert_to_number!("abs"; <i64> at idx + 1 => value))
872 ).process_results(|mut iter| iter.join("/"))
873 }
874 }
875
876 /// Takes the bitwise AND of many 64-bit signed integer inputs.
877 /// ### Examples
878 /// ```
879 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
880 /// [#and/11/5] -> 1 (0b1011 & 0b0101)
881 /// [#and/8/13/7] -> 0 (0b1000 & 0b1101 & 0b0111)
882 /// # "#)}
883 /// ```
884 macro BitAnd as "#and" {
885 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
886 arguments.iter().enumerate()
887 .map(|(idx, value)| Ok(convert_to_number!("#and"; <i64> at idx + 1 => value)))
888 .process_results(|iter| iter.reduce(|a, b| a & b).unwrap_or(0).to_string())
889 }
890 }
891
892 /// Takes the bitwise OR of many 64-bit signed integer inputs.
893 /// ### Examples
894 /// ```
895 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
896 /// [#or/5/3] -> 7 (0b0101 | 0b0011)
897 /// [#or/9/5/2] -> 15 (0b1001 | 0b0101 | 0b0010)
898 /// # "#)}
899 /// ```
900 macro BitOr as "#or" {
901 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
902 arguments.iter().enumerate()
903 .map(|(idx, value)| Ok(convert_to_number!("#or"; <i64> at idx + 1 => value)))
904 .process_results(|iter| iter.reduce(|a, b| a | b).unwrap_or(0).to_string())
905 }
906 }
907
908
909 /// Takes the bitwise XOR of two 64-bit signed integer inputs.
910 /// ### Examples
911 /// ```
912 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
913 /// [#xor/5/3] -> 6 (0b0101 ^ 0b0011)
914 /// [#xor/8/11/5] -> 6 (0b1000 ^ 0b1011 ^ 0b0101)
915 /// # "#)}
916 /// ```
917 macro BitXor as "#xor" {
918 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
919 arguments.iter().enumerate()
920 .map(|(idx, value)| Ok(convert_to_number!("#xor"; <i64> at idx + 1 => value)))
921 .process_results(|iter| iter.reduce(|a, b| a ^ b).unwrap_or(0).to_string())
922 }
923 }
924
925 /// Shifts the first argument's bits to the left by the second argument.
926 /// The second argument may not be greater than 63.
927 /// ### Examples
928 /// ```
929 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
930 /// [shl/5/2] -> 20 (0b101 -> 0b10100)
931 /// [shl/-9223372036854775808/1] -> 0 (0b100...0 -> 0b00...0)
932 /// # "#)}
933 /// ```
934 macro ShiftLeft as "shl" {
935 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
936 let (lhs, rhs) = get_args!("shl", arguments; a, b);
937 let lhs = convert_to_number!("shl"; <i64> at 1 => lhs) as u64;
938 let rhs = convert_to_number!("shl"; <u32> at 2 => rhs);
939 lhs.checked_shl(rhs)
940 .map(|v| (v as i64).to_string())
941 .ok_or_else(|| MacroError::new(
942 "shl".into(),
943 MacroErrorKind::user(format!("shift amount of {rhs} is too large"))
944 ))
945 }
946 }
947
948
949 /// Shifts the first argument's bits to the right by the second argument.
950 /// The second argument may not be greater than 63.
951 /// ### Example
952 /// ```
953 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
954 /// [shr/-9223372036854775808/1] -> 4611686018427387904 (0b100...0 -> 0b0100...0)
955 /// # "#)}
956 /// ```
957 macro ShiftRight as "shr" {
958 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
959 let (lhs, rhs) = get_args!("shr", arguments; a, b);
960 let lhs = convert_to_number!("shr"; <i64> at 1 => lhs) as u64;
961 let rhs = convert_to_number!("shr"; <u32> at 2 => rhs);
962 lhs.checked_shr(rhs)
963 .map(|v| (v as i64).to_string())
964 .ok_or_else(|| MacroError::new(
965 "shr".into(),
966 MacroErrorKind::user(format!("shift amount of {rhs} is too large"))
967 ))
968 }
969 }
970
971
972 /// Shifts the first argument's bits to the right by the second argument, keeping the sign bit.
973 /// The second argument may not be greater than 63.
974 /// ### Example
975 /// ```
976 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
977 /// [#shr/-9223372036854775808/1] -> -4611686018427387904 (0b100...0 -> 0b1100...0)
978 /// # "#)}
979 /// ```
980 macro ArithmeticShiftRight as "#shr" {
981 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
982 let (lhs, rhs) = get_args!("#shr", arguments; a, b);
983 let lhs = convert_to_number!("#shr"; <i64> at 1 => lhs);
984 let rhs = convert_to_number!("#shr"; <u32> at 2 => rhs);
985 lhs.checked_shr(rhs)
986 .map(|v| v.to_string())
987 .ok_or_else(|| MacroError::new(
988 "#shr".into(),
989 MacroErrorKind::user(format!("shift amount of {rhs} is too large"))
990 ))
991 }
992 }
993
994 /// Gets the absolute value of many numbers.
995 /// ### Examples
996 /// ```
997 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
998 /// [abs/-5] -> 5
999 /// [abs/NaN/-inf] -> NaN/inf
1000 /// # "#)}
1001 /// ```
1002 macro Abs as "abs" {
1003 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1004 arguments.iter().enumerate()
1005 .map(|(idx, value)|
1006 Ok(convert_to_number!("abs"; at idx + 1 => value).abs().to_string())
1007 ).process_results(|mut iter| iter.join("/"))
1008 }
1009 }
1010
1011 /// Gets the sine of many numbers.
1012 /// ### Example
1013 /// ```
1014 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1015 /// [int/[sin/3.14159]] -> 0
1016 /// # "#)}
1017 /// ```
1018 macro Sine as "sin" {
1019 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1020 arguments.iter().enumerate()
1021 .map(|(idx, value)|
1022 Ok(convert_to_number!("sin"; at idx + 1 => value).sin().to_string())
1023 ).process_results(|mut iter| iter.join("/"))
1024 }
1025 }
1026
1027 /// Gets the cosine of many numbers.
1028 /// ### Example
1029 /// ```
1030 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1031 /// [int/[add/-0.01/[cos/3.14159]]] -> -1
1032 /// # "#)}
1033 /// ```
1034 macro Cosine as "cos" {
1035 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1036 arguments.iter().enumerate()
1037 .map(|(idx, value)|
1038 Ok(convert_to_number!("cos"; at idx + 1 => value).cos().to_string())
1039 ).process_results(|mut iter| iter.join("/"))
1040 }
1041 }
1042
1043 /// Gets the tangent of many numbers.
1044 /// ### Example
1045 /// ```
1046 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1047 /// [int/[multiply/2/[tan/1]]] -> 3
1048 /// # "#)}
1049 /// ```
1050 macro Tangent as "tan" {
1051 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1052 arguments.iter().enumerate()
1053 .map(|(idx, value)|
1054 Ok(convert_to_number!("tan"; at idx + 1 => value).tan().to_string())
1055 ).process_results(|mut iter| iter.join("/"))
1056 }
1057 }
1058
1059
1060 /// Gets the inverse sine of many numbers.
1061 /// ### Example
1062 /// ```
1063 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1064 /// [asin/0/1] -> 0/1.5707963267948966
1065 /// # "#)}
1066 /// ```
1067 macro InvSine as "asin" {
1068 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1069 arguments.iter().enumerate()
1070 .map(|(idx, value)|
1071 Ok(convert_to_number!("asin"; at idx + 1 => value).asin().to_string())
1072 ).process_results(|mut iter| iter.join("/"))
1073 }
1074 }
1075
1076 /// Gets the inverse cosine of many numbers.
1077 /// ### Example
1078 /// ```
1079 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1080 /// [acos/1/0] -> 0/1.5707963267948966
1081 /// # "#)}
1082 /// ```
1083 macro InvCosine as "acos" {
1084 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1085 arguments.iter().enumerate()
1086 .map(|(idx, value)|
1087 Ok(convert_to_number!("sin"; at idx + 1 => value).acos().to_string())
1088 ).process_results(|mut iter| iter.join("/"))
1089 }
1090 }
1091
1092 /// Gets the inverse tangent of a number.
1093 /// ### Example
1094 /// ```
1095 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1096 /// [int/[atan/1.5708]] -> 1
1097 /// # "#)}
1098 /// ```
1099 macro InvTangent as "atan" {
1100 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1101 arguments.iter().enumerate()
1102 .map(|(idx, value)|
1103 Ok(convert_to_number!("atan"; at idx + 1 => value).atan().to_string())
1104 ).process_results(|mut iter| iter.join("/"))
1105 }
1106 }
1107
1108 /// Immediately raises an error. The error message is unescaped.
1109 /// ### Example
1110 /// ```
1111 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1112 /// [error/oh no!] -> error: oh no!
1113 /// # "#)}
1114 /// ```
1115 macro Error as "error" {
1116 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1117 Err(MacroError::new("error".into(), MacroErrorKind::user(
1118 arguments.first().map_or(String::from("no reason given"), ToString::to_string)
1119 )))
1120 }
1121 }
1122
1123 /// Raises an error if the first argument is not truthy.
1124 /// ### Examples
1125 /// ```
1126 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1127 /// [assert/1/all good] -> <no output>
1128 /// [assert/false/yikes] -> error: yikes
1129 /// # "#)}
1130 /// ```
1131 macro Assert as "assert" {
1132 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1133 let (condition, ) = get_args!("assert", arguments; a);
1134 if truthy(condition) {
1135 Ok(String::new())
1136 } else {
1137 Err(MacroError::new("assert".into(), MacroErrorKind::user(
1138 arguments.get(1).map_or(String::from("no reason given"), ToString::to_string)
1139 )))
1140 }
1141 }
1142 }
1143
1144 /// Slices a string.
1145 /// The first argument is the start, the next is the end, and optionally, the last is the step size.
1146 /// This works similarly to Python's string slicing rules (and is in fact carried over from it).
1147 /// ### Examples
1148 /// ```
1149 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1150 /// [slice/abcdefg/1/4] -> bcd
1151 /// [slice/abcde/1/] -> bcde
1152 /// [slice/1,2,30,45///2] -> 123,5
1153 /// [slice/kcab///-1] -> back
1154 /// # "#)}
1155 /// ```
1156 macro Slice as "slice" {
1157 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1158 let (haystack, start, end) = get_args!("slice", arguments; a, b, c);
1159 let start = (!start.is_empty())
1160 .then(|| Ok(convert_to_number!("slice"; <usize> at 2 => start)))
1161 .transpose()?;
1162 let end = (!end.is_empty())
1163 .then(|| Ok(convert_to_number!("slice"; <usize> at 3 => end)))
1164 .transpose()?;
1165 let step = arguments.get(3)
1166 .map(|v| Ok(convert_to_number!("slice"; <isize> at 4 => v)))
1167 .transpose()?
1168 .unwrap_or(1);
1169 if step == 0 {
1170 return Err(MacroError::new("slice".into(), MacroErrorKind::user(
1171 "cannot have a step length of 0"
1172 )))
1173 }
1174 let Some(slice) = (match (start, end) {
1175 (None, None) => Some(&haystack[..]),
1176 (Some(s), None) => haystack.char_indices().nth(s).and_then(|(s, _)| haystack.get(s..)),
1177 (None, Some(e)) => haystack.char_indices().nth(e).and_then(|(e, _)| haystack.get(..e)),
1178 (Some(s), Some(e)) => haystack.char_indices().nth(s)
1179 .and_then(|(s, _)| Some((s, haystack.char_indices().nth(e)?)))
1180 .and_then(|(s, (e, _))| haystack.get(s..e))
1181 }) else {
1182 return Err(MacroError::new("slice".into(), MacroErrorKind::user(
1183 format!(
1184 "part of range \"{}..{}\" is out of bounds for string of length {}",
1185 start.map(|v| v.to_string()).unwrap_or_default(),
1186 end.map(|v| v.to_string()).unwrap_or_default(),
1187 haystack.chars().count()
1188 )
1189 )))
1190 };
1191 if step == 1 {
1192 // Fast path
1193 Ok(slice.to_string())
1194 } else {
1195 // Slow path
1196 Ok(
1197 if step < 0 {
1198 slice.chars().rev().step_by((-step) as usize).collect()
1199 } else {
1200 slice.chars().step_by(step as usize).collect()
1201 }
1202 )
1203 }
1204 }
1205 }
1206
1207 /// Returns the start location of the second argument in the first.
1208 /// Returns -1 if it couldn't be found.
1209 /// ### Examples
1210 /// ```
1211 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1212 /// [find/homeowner/meow] -> 2
1213 /// [find/clubstep monster/end] -> -1
1214 /// # "#)}
1215 /// ```
1216 macro Find as "find" {
1217 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1218 let (haystack, needle) = get_args!("find", arguments; a, b);
1219
1220 Ok(haystack.find(&**needle).map_or(-1, |v| {
1221 haystack[..v].chars().count() as isize
1222 }).to_string())
1223 }
1224 }
1225
1226 /// Returns the number of disjoint occurrences of the second argument in the first.
1227 /// Returns 0 if none were found.
1228 /// ### Examples
1229 /// ```
1230 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1231 /// [count/Pacific Ocean/c] -> 3
1232 /// [count/hellololo/lol] -> 1
1233 /// # "#)}
1234 /// ```
1235 macro Count as "count" {
1236 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1237 let (haystack, needle) = get_args!("count", arguments; a, b);
1238 Ok(haystack.matches(&**needle).count().to_string())
1239 }
1240 }
1241
1242 /// Joins all arguments with the unescaped first argument.
1243 /// ### Examples
1244 /// ```
1245 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1246 /// [join/:/red/left/sleep] -> red:left:sleep
1247 /// [join/\/\//dou/ble] -> dou//ble
1248 /// # "#)}
1249 /// ```
1250 macro Join as "join" {
1251 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1252 let (delimiter, ) = get_args!("join", arguments; a);
1253 Ok(arguments.iter().skip(1).join(&unescape(delimiter)))
1254 }
1255 }
1256
1257 /// Escapes the first argument.
1258 ///
1259 /// ### Example
1260 /// ```
1261 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1262 /// [escape/add/5/3] -> add\/5\/3
1263 /// # "#)}
1264 /// ```
1265 macro Escape as "escape" {
1266 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1267 let raw = arguments.join("/");
1268 Ok(raw.replace('\\', r"\\").replace('/', r"\/").replace('[', r"\[").replace(']', r"\]"))
1269 }
1270 }
1271
1272 /// Repeats the first argument N times, where N is the second argument, optionally joined by the third argument.
1273 /// ### Examples
1274 /// ```
1275 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1276 /// [repeat/5/5/:] -> 5:5:5:5:5
1277 /// [store/x/0][unescape/[repeat/\[store\/x\/\[add\/\[load\/x\]\/1\]\]\[load\/x\]/5]] -> 12345
1278 /// # "#)}
1279 /// ```
1280 macro Repeat as "repeat" {
1281 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1282 let (target, count) = get_args!("repeat", arguments; a, b);
1283 let count = convert_to_number!("repeat"; <usize> at 2 => count);
1284 Ok(std::iter::repeat(target).take(count).join(arguments.get(2).map_or("", |v| &**v)))
1285 }
1286 }
1287
1288 /// Turns the input into lowercase.
1289 /// ### Examples
1290 /// ```
1291 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1292 /// [lower/VVVVVV/GO PLAY IT] -> vvvvvv/go play it
1293 /// [lower/ὈΔΥΣΣΕΎΣ] -> ὀδυσσεύς
1294 /// # "#)}
1295 /// ```
1296 macro Lower as "lower" {
1297 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1298 Ok(arguments.into_iter().map(str::to_lowercase).join("/"))
1299 }
1300 }
1301
1302 /// Turns the input into uppercase.
1303 /// ### Examples
1304 /// ```
1305 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1306 /// [upper/vvvvvv/go play it] -> VVVVVV/GO PLAY IT
1307 /// [upper/tschüß] -> TSCHÜSS
1308 /// # "#)}
1309 /// ```
1310 macro Upper as "upper" {
1311 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1312 Ok(arguments.into_iter().map(str::to_uppercase).join("/"))
1313 }
1314 }
1315
1316 /// Maps an escaped text macro over all of the inputs, returning the results as outputs.
1317 /// # Example
1318 /// ```
1319 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1320 /// [map/\[multiply\/$1\/2\]/1/2/3] -> 2/4/6
1321 /// # "#)}
1322 /// ```
1323 macro Map as "map" {
1324 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1325 if arguments.len() == 1 { return Ok(String::new()) }
1326 let (mac, ) = get_args!("map", arguments; a);
1327 let mac = TextMacro::new(unescape(mac));
1328 arguments
1329 .iter()
1330 .skip(1)
1331 .map(|v| mac.apply(vec![v]))
1332 .process_results(|mut v| v.join("/"))
1333 }
1334 }
1335
1336 /// Performs a fold with an escaped text macro over all of the inputs, taking the first as a base case.
1337 /// # Example
1338 /// ```
1339 /// # use macroscript::test::test_output; fn main() -> Result<(), Box<dyn std::error::Error>> { test_output(r#"
1340 /// [fold/\[add\/$1\/$2\]/0/1/2/3] -> 6
1341 /// # "#)}
1342 /// ```
1343 macro Fold as "fold" {
1344 fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
1345 let (mac, base) = get_args!("map", arguments; a, b);
1346 let mac = TextMacro::new(unescape(mac));
1347 arguments
1348 .iter()
1349 .skip(2)
1350 .try_fold((*base).to_string(), |a, b| mac.apply(vec![&a, *b]))
1351 }
1352 }
1353}