Skip to main content

tenda_prelude/
lib.rs

1use tenda_runtime::*;
2
3macro_rules! global {
4    ($env:ident, $builtin:expr) => {{
5        let builtin = $builtin;
6
7        $env.set(builtin.0, ValueCell::Owned(builtin.1));
8    }};
9    ($stack:ident, $($builtin:expr),+) => {{
10        $(global!($stack, $builtin);)+
11    }};
12}
13
14macro_rules! def_value {
15    ($name:literal, $value:expr) => {
16        ($name.to_string(), $value)
17    };
18}
19
20macro_rules! builtin_assoc_array {
21    ($($name:literal => $value:expr),+ $(,)?) => {{
22        use std::cell::RefCell;
23        use std::rc::Rc;
24        use tenda_runtime::AssociativeArrayKey;
25
26        let mut map = indexmap::IndexMap::new();
27
28        $(
29            let key = AssociativeArrayKey::String($name.to_string());
30            map.insert(key, $value);
31        )+
32
33        Value::AssociativeArray(Rc::new(RefCell::new(map)))
34    }};
35}
36
37macro_rules! assoc_array_enum {
38    ($($name:literal),+ $(,)?) => {{
39        builtin_assoc_array!($($name => Value::String($name.to_string())),+)
40    }};
41}
42
43macro_rules! def_assoc_array {
44    ($assoc_array_name:literal, { $($name:literal => $value:expr),+ $(,)? }) => {
45        (
46            $assoc_array_name.to_string(),
47            builtin_assoc_array!($($name => $value),+)
48        )
49    };
50}
51
52macro_rules! builtin_fn {
53    ([$($param:expr),*], $body:expr) => {
54        Value::Function(Function::new_builtin(
55            params![$($param),*],
56            $body,
57        ))
58    };
59    ($body:expr) => {
60        Value::Function(Function::new_builtin(
61            Vec::new(),
62            $body,
63        ))
64    };
65}
66
67macro_rules! def_fn {
68    ($name:literal, [$($param:expr),*], $body:expr) => {
69        ($name.to_string(), builtin_fn!([$($param),*], $body))
70    };
71}
72
73macro_rules! args {
74    ($args:expr, $index:expr) => {
75        &$args[$index].1
76    };
77}
78
79macro_rules! error_object {
80    ($kind:literal) => {
81        builtin_assoc_array! {
82            "erro" => builtin_assoc_array! {
83                "tipo" => Value::String($kind.to_string()),
84            }
85        }
86    };
87}
88
89macro_rules! success_object {
90    ($value:expr) => {
91        builtin_assoc_array! {
92            "valor" => $value,
93        }
94    };
95}
96
97macro_rules! ensure {
98    ($val:expr, $variant:ident($binding:pat) => $body:expr) => {{
99        match $val {
100            Value::$variant($binding) => $body,
101            value => {
102                return Err(Box::new(RuntimeError::UnexpectedTypeError {
103                    expected: ValueType::$variant,
104                    found: value.kind(),
105                    span: None,
106                    message: None,
107                    stacktrace: vec![],
108                }))
109            }
110        }
111    }};
112}
113
114pub fn setup_runtime_prelude(env: &mut Environment) {
115    setup_io_prelude(env);
116    setup_list_prelude(env);
117    setup_math_prelude(env);
118    setup_string_prelude(env);
119    setup_file_prelude(env);
120    setup_program_prelude(env);
121    setup_date_prelude(env);
122}
123
124fn setup_io_prelude(env: &mut Environment) {
125    global!(
126        env,
127        def_fn!("exiba", ["texto"], |args, runtime, _| {
128            let text = match args!(args, 0) {
129                Value::String(value) => value.to_string(),
130                value => format!("{}", value),
131            };
132
133            runtime.get_platform().println(&text);
134
135            Ok(Value::Nil)
136        }),
137        def_fn!("entrada", [], |_, runtime, _| {
138            let input = runtime.get_platform().read_line();
139
140            Ok(Value::String(input))
141        }),
142        def_fn!("leia", ["texto"], |args, runtime, _| {
143            let prompt = match args!(args, 0) {
144                Value::String(value) => value.to_string(),
145                value => format!("{}", value),
146            };
147
148            runtime.get_platform().print(&prompt);
149
150            let input = runtime.get_platform().read_line();
151
152            Ok(Value::String(input))
153        }),
154        def_assoc_array!("Saída", {
155            "exiba" => builtin_fn!(["texto"], |args, runtime, _| {
156                let text = match args!(args, 0) {
157                    Value::String(value) => value.to_string(),
158                    value => format!("{}", value),
159                };
160
161                runtime.get_platform().println(&text);
162
163                Ok(Value::Nil)
164            }),
165            "escreva" => builtin_fn!(["texto"], |args, runtime, _| {
166                let text = match args!(args, 0) {
167                    Value::String(value) => value.to_string(),
168                    value => format!("{}", value),
169                };
170
171                runtime.get_platform().write(&text);
172
173                Ok(Value::Nil)
174            }),
175            "leia" => builtin_fn!(["texto"], |args, runtime, _| {
176                let prompt = match args!(args, 0) {
177                    Value::String(value) => value.to_string(),
178                    value => format!("{}", value),
179                };
180
181                runtime.get_platform().print(&prompt);
182
183                let input = runtime.get_platform().read_line();
184
185                Ok(Value::String(input))
186            }),
187            "entrada" => builtin_fn!([], |_, runtime, _| {
188                let input = runtime.get_platform().read_line();
189
190                Ok(Value::String(input))
191            })
192        })
193    );
194}
195
196fn setup_list_prelude(env: &mut Environment) {
197    global!(
198        env,
199        def_assoc_array!("Lista", {
200            "tamanho" => builtin_fn!(["lista"], |args, _, _| {
201                let list = ensure!(args!(args, 0), List(list) => list.borrow());
202
203                Ok(Value::Number(list.len() as f64))
204            }),
205            "insira" => builtin_fn!(["lista", "valor"], |args, _, _| {
206                let mut list = ensure!(args!(args, 0), List(list) => list.borrow_mut());
207
208                list.push(args!(args, 1).clone());
209
210                Ok(Value::Nil)
211            }),
212            "remova" => builtin_fn!(["lista", "valor"], |args, _, _| {
213                let mut list = ensure!(args!(args, 0), List(list) => list.borrow_mut());
214                let value = args!(args, 1);
215                let index = list.iter().position(|v| v == value);
216
217                if let Some(index) = index {
218                    return Ok(list.remove(index));
219                }
220
221                Ok(Value::Nil)
222            }),
223            "remova_todos" => builtin_fn!(["lista", "valor"], |args, _, _| {
224                let mut list = ensure!(args!(args, 0), List(list) => list.borrow_mut());
225                let value = args!(args, 1);
226
227                list.retain(|v| v != value);
228
229                Ok(Value::Nil)
230            }),
231            "remova_por_índice" => builtin_fn!(["lista", "índice"], |args, _, _| {
232                let mut list = ensure!(args!(args, 0), List(list) => list.borrow_mut());
233                let index = ensure!(args!(args, 1), Number(value) => *value as usize);
234
235                if list.len() <= index {
236                    return Err(Box::new(RuntimeError::IndexOutOfBounds {
237                        index,
238                        len: list.len(),
239                        span: None,
240                        help: vec![],
241                        stacktrace: vec![],
242                    }));
243                }
244
245                let element = list.remove(index);
246
247                Ok(element)
248            }),
249            "obtenha" => builtin_fn!(["lista", "índice"], |args, _, _| {
250                let list = ensure!(args!(args, 0), List(list) => list.borrow());
251                let index = ensure!(args!(args, 1), Number(value) => *value as usize);
252
253                if list.len() <= index {
254                    return Err(Box::new(RuntimeError::IndexOutOfBounds {
255                        index,
256                        len: list.len(),
257                        span: None,
258                        help: vec![],
259                        stacktrace: vec![],
260                    }));
261                }
262
263                Ok(list[index].clone())
264            }),
265            "índice_de" => builtin_fn!(["lista", "valor"], |args, _, _| {
266                let list = ensure!(args!(args, 0), List(list) => list.borrow());
267                let value = args!(args, 1);
268                let index = list.iter().position(|v| v == value).map(|i| i as f64);
269
270                Ok(index.map(Value::Number).unwrap_or(Value::Nil))
271            }),
272            "contém" => builtin_fn!(["lista", "valor"], |args, _, _| {
273                let list = ensure!(args!(args, 0), List(list) => list.borrow());
274                let value = args!(args, 1);
275
276                Ok(Value::Boolean(list.contains(value)))
277            }),
278            "vazio" => builtin_fn!(["lista"], |args, _, _| {
279                let list = ensure!(args!(args, 0), List(list) => list.borrow());
280
281                Ok(Value::Boolean(list.is_empty()))
282            }),
283            "limpa" => builtin_fn!(["lista"], |args, _, _| {
284                let mut list = ensure!(args!(args, 0), List(list) => list.borrow_mut());
285
286                list.clear();
287
288                Ok(Value::Nil)
289            }),
290            "fatia" => builtin_fn!(["lista", "início", "fim"], |args, _, _| {
291                let list = ensure!(args!(args, 0), List(list) => list.borrow());
292                let start = ensure!(args!(args, 1), Number(value) => *value as usize);
293                let end = ensure!(args!(args, 2), Number(value) => *value as usize);
294
295                if start > end {
296                    return Err(Box::new(RuntimeError::InvalidRangeBounds {
297                        bound: end as f64,
298                        span: None,
299                        stacktrace: vec![],
300                    }));
301                }
302
303                if end >= list.len() {
304                    return Err(Box::new(RuntimeError::IndexOutOfBounds {
305                        index: end,
306                        len: list.len(),
307                        span: None,
308                        help: vec![],
309                        stacktrace: vec![],
310                    }));
311                }
312
313                let extracted = list[start..=end].to_vec();
314
315                Ok(Value::List(Rc::new(RefCell::new(extracted))))
316            }),
317            "para_cada" => builtin_fn!(["lista", "função"], |args, runtime, _| {
318                let list = ensure!(args!(args, 0), List(list) => list.borrow());
319                let function = ensure!(args!(args, 1), Function(function) => function);
320
321                for (i, value) in list.iter().enumerate() {
322                    let i = Value::Number(i as f64);
323                    let args = vec![value.clone(), i];
324
325                    runtime.call_function(function.clone(), args, None)?;
326                }
327
328                Ok(Value::Nil)
329            }),
330            "de_intervalo" => builtin_fn!(["intervalo"], |args, _, _| {
331                let (from, to) = match args!(args, 0) {
332                    Value::Range(from, start) => (from, start),
333                    value => return Err(Box::new(RuntimeError::UnexpectedTypeError {
334                        expected: ValueType::Range,
335                        found: value.kind(),
336                        span: None,
337                        message: None,
338                        stacktrace: vec![],
339                    }))
340                };
341
342                let list = (*from as i64..=*to as i64)
343                    .map(|i| Value::Number(i as f64))
344                    .collect::<Vec<_>>();
345
346                Ok(Value::List(Rc::new(RefCell::new(list))))
347            }),
348            "de_texto" => builtin_fn!(["texto"], |args, _, _| {
349                let text = ensure!(args!(args, 0), String(value) => value);
350
351                let list = text
352                    .chars()
353                    .map(|c| Value::String(c.to_string()))
354                    .collect::<Vec<_>>();
355
356                Ok(Value::List(Rc::new(RefCell::new(list))))
357            }),
358            "transforma" => builtin_fn!(["lista", "função"], |args, runtime, _| {
359                let list = ensure!(args!(args, 0), List(list) => list.borrow());
360                let function = ensure!(args!(args, 1), Function(function) => function);
361
362                let mut new_list = vec![];
363
364                for value in list.iter() {
365                    let args = vec![value.clone()];
366                    let result = runtime.call_function(function.clone(), args, None)?;
367
368                    new_list.push(result);
369                }
370
371                Ok(Value::List(Rc::new(RefCell::new(new_list))))
372            })
373        })
374    );
375}
376
377fn setup_math_prelude(env: &mut Environment) {
378    global!(
379        env,
380        def_value!("infinito", Value::Number(f64::INFINITY)),
381        def_value!("NaN", Value::Number(f64::NAN)),
382        def_assoc_array!("Matemática", {
383            "maior_número" => Value::Number(f64::MAX),
384            "menor_número" => Value::Number(f64::MIN),
385            "pi" => Value::Number(std::f64::consts::PI),
386            "e" => Value::Number(std::f64::consts::E),
387            "absoluto" => builtin_fn!(["número"], |args, _, _| {
388                let number = ensure!(args!(args, 0), Number(value) => *value);
389
390                Ok(Value::Number(number.abs()))
391            }),
392            "arredonda" => builtin_fn!(["número"], |args, _, _| {
393                let number = ensure!(args!(args, 0), Number(value) => *value);
394
395                Ok(Value::Number(number.round()))
396            }),
397            "teto" => builtin_fn!(["número"], |args, _, _| {
398                let number = ensure!(args!(args, 0), Number(value) => *value);
399
400                Ok(Value::Number(number.ceil()))
401            }),
402            "piso" => builtin_fn!(["número"], |args, _, _| {
403                let number = ensure!(args!(args, 0), Number(value) => *value);
404
405                Ok(Value::Number(number.floor()))
406            }),
407            "raiz_quadrada" => builtin_fn!(["número"], |args, _, _| {
408                let number = ensure!(args!(args, 0), Number(value) => *value);
409
410                Ok(Value::Number(number.sqrt()))
411            }),
412            "seno" => builtin_fn!(["número"], |args, _, _| {
413                let number = ensure!(args!(args, 0), Number(value) => *value);
414
415                Ok(Value::Number(number.sin()))
416            }),
417            "cosseno" => builtin_fn!(["número"], |args, _, _| {
418                let number = ensure!(args!(args, 0), Number(value) => *value);
419
420                Ok(Value::Number(number.cos()))
421            }),
422            "tangente" => builtin_fn!(["número"], |args, _, _| {
423                let number = ensure!(args!(args, 0), Number(value) => *value);
424
425                Ok(Value::Number(number.tan()))
426            }),
427            "arco_seno" => builtin_fn!(["número"], |args, _, _| {
428                let number = ensure!(args!(args, 0), Number(value) => *value);
429
430                Ok(Value::Number(number.asin()))
431            }),
432            "arco_cosseno" => builtin_fn!(["número"], |args, _, _| {
433                let number = ensure!(args!(args, 0), Number(value) => *value);
434
435                Ok(Value::Number(number.acos()))
436            }),
437            "arco_tangente" => builtin_fn!(["número"], |args, _, _| {
438                let number = ensure!(args!(args, 0), Number(value) => *value);
439
440                Ok(Value::Number(number.atan()))
441            }),
442            "logaritmo" => builtin_fn!(["número", "base"], |args, _, _| {
443                let number = ensure!(args!(args, 0), Number(value) => *value);
444                let base = ensure!(args!(args, 1), Number(value) => *value);
445
446                Ok(Value::Number(number.log(base)))
447            }),
448            "logaritmo_natural" => builtin_fn!(["número"], |args, _, _| {
449                let number = ensure!(args!(args, 0), Number(value) => *value);
450
451                Ok(Value::Number(number.ln()))
452            }),
453            "logaritmo_10" => builtin_fn!(["número"], |args, _, _| {
454                let number = ensure!(args!(args, 0), Number(value) => *value);
455
456                Ok(Value::Number(number.log10()))
457            }),
458            "potência" => builtin_fn!(["base", "expoente"], |args, _, _| {
459                let base = ensure!(args!(args, 0), Number(value) => *value);
460                let exponent = ensure!(args!(args, 1), Number(value) => *value);
461
462                Ok(Value::Number(base.powf(exponent)))
463            }),
464            "máximo" => builtin_fn!(["número_1", "número_2"], |args, _, _| {
465                let number1 = ensure!(args!(args, 0), Number(value) => *value);
466                let number2 = ensure!(args!(args, 1), Number(value) => *value);
467
468                Ok(Value::Number(number1.max(number2)))
469            }),
470            "mínimo" => builtin_fn!(["número_1", "número_2"], |args, _, _| {
471                let number1 = ensure!(args!(args, 0), Number(value) => *value);
472                let number2 = ensure!(args!(args, 1), Number(value) => *value);
473
474                Ok(Value::Number(number1.min(number2)))
475            }),
476            "aleatório" => builtin_fn!(["mínimo", "máximo"], |args, runtime, _| {
477                let min = ensure!(args!(args, 0), Number(value) => *value);
478                let max = ensure!(args!(args, 1), Number(value) => *value);
479
480                let number = runtime.get_platform().rand();
481
482                Ok(Value::Number(number * (max - min) + min))
483            }),
484            "raiz_cúbica" => builtin_fn!(["número"], |args, _, _| {
485                let number = ensure!(args!(args, 0), Number(value) => *value);
486
487                Ok(Value::Number(number.cbrt()))
488            }),
489            "seno_hiperbólico" => builtin_fn!(["número"], |args, _, _| {
490                let number = ensure!(args!(args, 0), Number(value) => *value);
491
492                Ok(Value::Number(number.sinh()))
493            }),
494            "cosseno_hiperbólico" => builtin_fn!(["número"], |args, _, _| {
495                let number = ensure!(args!(args, 0), Number(value) => *value);
496
497                Ok(Value::Number(number.cosh()))
498            }),
499            "tangente_hiperbólica" => builtin_fn!(["número"], |args, _, _| {
500                let number = ensure!(args!(args, 0), Number(value) => *value);
501
502                Ok(Value::Number(number.tanh()))
503            }),
504            "arco_seno_hiperbólico" => builtin_fn!(["número"], |args, _, _| {
505                let number = ensure!(args!(args, 0), Number(value) => *value);
506
507                Ok(Value::Number(number.asinh()))
508            }),
509            "arco_cosseno_hiperbólico" => builtin_fn!(["número"], |args, _, _| {
510                let number = ensure!(args!(args, 0), Number(value) => *value);
511
512                Ok(Value::Number(number.acosh()))
513            }),
514            "arco_tangente_hiperbólica" => builtin_fn!(["número"], |args, _, _| {
515                let number = ensure!(args!(args, 0), Number(value) => *value);
516
517                Ok(Value::Number(number.atanh()))
518            }),
519            "graus_para_radianos" => builtin_fn!(["número"], |args, _, _| {
520                let degrees = ensure!(args!(args, 0), Number(value) => *value);
521
522                Ok(Value::Number(degrees.to_radians()))
523            }),
524            "radianos_para_graus" => builtin_fn!(["número"], |args, _, _| {
525                let radians = ensure!(args!(args, 0), Number(value) => *value);
526
527                Ok(Value::Number(radians.to_degrees()))
528            }),
529            "trunca" => builtin_fn!(["número"], |args, _, _| {
530                let number = ensure!(args!(args, 0), Number(value) => *value);
531
532                Ok(Value::Number(number.trunc()))
533            }),
534            "parte_fracionária" => builtin_fn!(["número"], |args, _, _| {
535                let number = ensure!(args!(args, 0), Number(value) => *value);
536
537                Ok(Value::Number(number.fract()))
538            }),
539            "arco_tangente2" => builtin_fn!(["y", "x"], |args, _, _| {
540                let y = ensure!(args!(args, 0), Number(value) => *value);
541                let x = ensure!(args!(args, 1), Number(value) => *value);
542
543                Ok(Value::Number(y.atan2(x)))
544            }),
545            "resto" => builtin_fn!(["número_1", "número_2"], |args, _, _| {
546                let n1 = ensure!(args!(args, 0), Number(value) => *value);
547                let n2 = ensure!(args!(args, 1), Number(value) => *value);
548
549                Ok(Value::Number(n1 % n2))
550            }),
551            "resto_euclidiano" => builtin_fn!(["número_1", "número_2"], |args, _, _| {
552                let n1 = ensure!(args!(args, 0), Number(value) => *value);
553                let n2 = ensure!(args!(args, 1), Number(value) => *value);
554
555                Ok(Value::Number(n1.rem_euclid(n2)))
556            }),
557            "copia_sinal" => builtin_fn!(["valor", "sinal"], |args, _, _| {
558                let valor = ensure!(args!(args, 0), Number(value) => *value);
559                let sinal = ensure!(args!(args, 1), Number(value) => *value);
560
561                Ok(Value::Number(valor.copysign(sinal)))
562            }),
563            "limita" => builtin_fn!(["número", "mínimo", "máximo"], |args, _, _| {
564                let x = ensure!(args!(args, 0), Number(value) => *value);
565                let min_val = ensure!(args!(args, 1), Number(value) => *value);
566                let max_val = ensure!(args!(args, 2), Number(value) => *value);
567
568                let clamped = if x < min_val {
569                    min_val
570                } else if x > max_val {
571                    max_val
572                } else {
573                    x
574                };
575
576                Ok(Value::Number(clamped))
577            }),
578            "sinal" => builtin_fn!(["número"], |args, _, _| {
579                let number = ensure!(args!(args, 0), Number(value) => *value);
580
581                Ok(Value::Number(number.signum()))
582            }),
583            "hipotenusa" => builtin_fn!(["número_1", "número_2"], |args, _, _| {
584                let x = ensure!(args!(args, 0), Number(value) => *value);
585                let y = ensure!(args!(args, 1), Number(value) => *value);
586
587                Ok(Value::Number(x.hypot(y)))
588            }),
589            "exponencial" => builtin_fn!(["número"], |args, _, _| {
590                let x = ensure!(args!(args, 0), Number(value) => *value);
591
592                Ok(Value::Number(x.exp()))
593            }),
594            "fatorial" => builtin_fn!(["número"], |args, _, _| {
595                let n = ensure!(args!(args, 0), Number(value) => *value);
596                let int_n = n as i64;
597
598                if n < 0.0 || (n - int_n as f64).abs() > f64::EPSILON {
599                    return Err(Box::new(RuntimeError::InvalidArgument {
600                        value: Value::Number(n),
601                        span: None,
602                        stacktrace: vec![],
603                    }));
604                }
605
606                let mut result = 1f64;
607
608                for i in 1..=int_n {
609                    result *= i as f64;
610                }
611
612                Ok(Value::Number(result))
613            })
614        })
615    );
616}
617
618fn setup_string_prelude(env: &mut Environment) {
619    global!(
620        env,
621        def_assoc_array!("Texto", {
622            "erros" => assoc_array_enum! {
623                "CONVERSÃO_INVÁLIDA",
624            },
625            "tamanho" => builtin_fn!(["texto"], |args, _, _| {
626                let text = ensure!(args!(args, 0), String(value) => value);
627
628                Ok(Value::Number(text.len() as f64))
629            }),
630            "vazio" => builtin_fn!(["texto"], |args, _, _| {
631                let text = ensure!(args!(args, 0), String(value) => value);
632
633                Ok(Value::Boolean(text.is_empty()))
634            }),
635            "subtexto" => builtin_fn!(["texto", "início", "tamanho"], |args, _, _| {
636                let text = ensure!(args!(args, 0), String(value) => value);
637                let start = ensure!(args!(args, 1), Number(value) => *value as usize);
638                let len = ensure!(args!(args, 2), Number(value) => *value as usize);
639
640                if start >= text.len() {
641                    return Err(Box::new(RuntimeError::IndexOutOfBounds {
642                        index: start,
643                        len: text.len(),
644                        span: None,
645                        help: vec![],
646                        stacktrace: vec![],
647                    }));
648                }
649
650                Ok(Value::String(text[start..start + len].to_string()))
651            }),
652            "para_lista" => builtin_fn!(["texto"], |args, _, _| {
653                let text = ensure!(args!(args, 0), String(value) => value);
654
655                Ok(Value::List(Rc::new(RefCell::new(
656                    text.chars().map(|c| Value::String(c.to_string())).collect(),
657                ))))
658            }),
659            "de_lista" => builtin_fn!(["lista"], |args, _, _| {
660                let list = args!(args, 0);
661
662                ensure!(list, List(_) => ());
663
664                Ok(Value::String(list.to_string()))
665            }),
666            "para_maiúsculas" => builtin_fn!(["texto"], |args, _, _| {
667                let text = ensure!(args!(args, 0), String(value) => value);
668
669                Ok(Value::String(text.to_uppercase()))
670            }),
671            "para_minúsculas" => builtin_fn!(["texto"], |args, _, _| {
672                let text = ensure!(args!(args, 0), String(value) => value);
673
674                Ok(Value::String(text.to_lowercase()))
675            }),
676            "contém" => builtin_fn!(["texto", "subtexto"], |args, _, _| {
677                let text = ensure!(args!(args, 0), String(value) => value);
678                let subtext = ensure!(args!(args, 1), String(value) => value);
679
680                Ok(Value::Boolean(text.contains(subtext)))
681            }),
682            "começa_com" => builtin_fn!(["texto", "prefixo"], |args, _, _| {
683                let text = ensure!(args!(args, 0),
684                    String(value) => value
685                );
686
687                let prefix = ensure!(args!(args, 1),
688                    String(value) => value
689                );
690
691                Ok(Value::Boolean(text.starts_with(prefix)))
692            }),
693            "termina_com" => builtin_fn!(["texto", "sufixo"], |args, _, _| {
694                let text = ensure!(args!(args, 0),
695                    String(value) => value
696                );
697
698                let suffix = ensure!(args!(args, 1),
699                    String(value) => value
700                );
701
702                Ok(Value::Boolean(text.ends_with(suffix)))
703            }),
704            "índice_de" => builtin_fn!(["texto", "subtexto"], |args, _, _| {
705                let text = ensure!(args!(args, 0), String(value) => value);
706                let subtext = ensure!(args!(args, 1), String(value) => value);
707                let index = text.find(subtext).map(|i| i as f64);
708
709                Ok(index.map(Value::Number).unwrap_or(Value::Nil))
710            }),
711            "repita" => builtin_fn!(["texto", "vezes"], |args, _, _| {
712                let text = ensure!(args!(args, 0), String(value) => value);
713                let times = ensure!(args!(args, 1), Number(value) => *value as usize);
714
715                Ok(Value::String(text.repeat(times)))
716            }),
717            "substitua" => builtin_fn!(["texto", "antigo", "novo"], |args, _, _| {
718                let text = ensure!(args!(args, 0), String(value) => value);
719                let old = ensure!(args!(args, 1), String(value) => value);
720                let new = ensure!(args!(args, 2), String(value) => value);
721
722                Ok(Value::String(text.replace(old, new)))
723            }),
724            "corta" => builtin_fn!(["texto", "início", "fim"], |args, _, _| {
725                let text = ensure!(args!(args, 0), String(value) => value);
726                let start = ensure!(args!(args, 1), Number(value) => *value as usize);
727                let end = ensure!(args!(args, 2), Number(value) => *value as usize);
728
729                if start > end {
730                    return Err(Box::new(RuntimeError::InvalidRangeBounds {
731                        bound: end as f64,
732                        span: None,
733                        stacktrace: vec![],
734                    }));
735                }
736
737                if end >= text.len() {
738                    return Err(Box::new(RuntimeError::IndexOutOfBounds {
739                        index: end,
740                        len: text.len(),
741                        span: None,
742                        help: vec![],
743                        stacktrace: vec![],
744                    }));
745                }
746
747                Ok(Value::String(text[start..=end].to_string()))
748            }),
749            "inverta" => builtin_fn!(["texto"], |args, _, _| {
750                let text = ensure!(args!(args, 0), String(value) => value);
751
752                Ok(Value::String(text.chars().rev().collect()))
753            }),
754            "remova" => builtin_fn!(["texto", "início", "tamanho"], |args, _, _| {
755                let text = ensure!(args!(args, 0), String(value) => value);
756                let start = ensure!(args!(args, 1), Number(value) => *value as usize);
757                let len = ensure!(args!(args, 2), Number(value) => *value as usize);
758
759                if start >= text.len() {
760                    return Err(Box::new(RuntimeError::IndexOutOfBounds {
761                        index: start,
762                        len: text.len(),
763                        span: None,
764                        help: vec![],
765                        stacktrace: vec![],
766                    }));
767                }
768
769                Ok(Value::String(
770                    text.chars()
771                        .enumerate()
772                        .filter_map(|(i, c)| {
773                            if i < start || i >= start + len {
774                                Some(c)
775                            } else {
776                                None
777                            }
778                        })
779                        .collect(),
780                ))
781            }),
782            "remova_prefixo" => builtin_fn!(["texto", "prefixo"], |args, _, _| {
783                let text = ensure!(args!(args, 0), String(value) => value);
784                let prefix = ensure!(args!(args, 1), String(value) => value);
785
786                if text.starts_with(prefix) {
787                    Ok(Value::String(text[prefix.len()..].to_string()))
788                } else {
789                    Ok(Value::String(text.to_string()))
790                }
791            }),
792            "remova_sufixo" => builtin_fn!(["texto", "sufixo"], |args, _, _| {
793                let text = ensure!(args!(args, 0), String(value) => value);
794                let suffix = ensure!(args!(args, 1), String(value) => value);
795
796                if text.ends_with(suffix) {
797                    Ok(Value::String(text[..text.len() - suffix.len()].to_string()))
798                } else {
799                    Ok(Value::String(text.to_string()))
800                }
801            }),
802            "remova_espaços" => builtin_fn!(["texto"], |args, _, _| {
803                let text = ensure!(args!(args, 0), String(value) => value);
804
805                Ok(Value::String(
806                    text.chars().filter(|c| !c.is_whitespace()).collect(),
807                ))
808            }),
809            "remova_espaços_início" => builtin_fn!(["texto"], |args, _, _| {
810                let text = ensure!(args!(args, 0), String(value) => value);
811
812                Ok(Value::String(text.trim_start().to_string()))
813            }),
814            "remova_espaços_fim" => builtin_fn!(["texto"], |args, _, _| {
815                let text = ensure!(args!(args, 0), String(value) => value);
816
817                Ok(Value::String(text.trim_end().to_string()))
818            }),
819            "remova_espaços_início_fim" => builtin_fn!(["texto"], |args, _, _| {
820                let text = ensure!(args!(args, 0), String(value) => value);
821
822                Ok(Value::String(text.trim().to_string()))
823            }),
824            "para_número" => builtin_fn!(["texto"], |args, _, _| {
825                let text = ensure!(args!(args, 0), String(value) => value);
826
827                match text.parse::<f64>() {
828                    Ok(number) => Ok(success_object!(Value::Number(number))),
829                    Err(_) => Ok(error_object!("CONVERSÃO_INVÁLIDA")),
830                }
831            })
832        })
833    );
834}
835
836fn setup_file_prelude(env: &mut Environment) {
837    global!(
838        env,
839        def_assoc_array!("Arquivo", {
840            "erros" => assoc_array_enum! {
841                "NÃO_ENCONTRADO",
842                "PERMISSÃO_NEGADA",
843                "JÁ_EXISTE",
844                "OUTRO",
845            },
846            "leia" => builtin_fn!(["caminho"], |args, runtime, _| {
847                let path = ensure!(args!(args, 0), String(value) => value);
848
849                match runtime.get_platform().read_file(path) {
850                    Ok(text) => Ok(success_object!(Value::String(text))),
851                    Err(kind) => Ok(io_error_to_error_object(kind)),
852                }
853            }),
854            "escreva" => builtin_fn!(["caminho", "conteúdo"], |args, runtime, _| {
855                let path = ensure!(args!(args, 0), String(value) => value);
856                let content = ensure!(args!(args, 1), String(value) => value);
857
858                match runtime.get_platform().write_file(path, content) {
859                    Ok(_) => Ok(success_object!(Value::Nil)),
860                    Err(kind) => Ok(io_error_to_error_object(kind)),
861                }
862            }),
863            "acrescenta" => builtin_fn!(["caminho", "conteúdo"], |args, runtime, _| {
864                let path = ensure!(args!(args, 0), String(value) => value);
865                let content = ensure!(args!(args, 1), String(value) => value);
866
867                match runtime.get_platform().file_append(path, content) {
868                    Ok(_) => Ok(success_object!(Value::Nil)),
869                    Err(kind) => Ok(io_error_to_error_object(kind)),
870                }
871            }),
872            "remova" => builtin_fn!(["caminho"], |args, runtime, _| {
873                let path = ensure!(args!(args, 0), String(value) => value);
874
875                match runtime.get_platform().remove_file(path) {
876                    Ok(_) => Ok(success_object!(Value::Nil)),
877                    Err(kind) => Ok(io_error_to_error_object(kind)),
878                }
879            }),
880            "lista" => builtin_fn!(["caminho"], |args, runtime, _| {
881                let path = ensure!(args!(args, 0), String(value) => value);
882
883                match runtime.get_platform().list_files(path) {
884                    Ok(files) => {
885                        let files = files.into_iter().map(Value::String).collect();
886                        let value = Value::List(Rc::new(RefCell::new(files)));
887
888                        Ok(success_object!(value))
889                    },
890                    Err(kind) => Ok(io_error_to_error_object(kind)),
891                }
892            }),
893            "cria_diretório" => builtin_fn!(["caminho"], |args, runtime, _| {
894                let path = ensure!(args!(args, 0), String(value) => value);
895
896                match runtime.get_platform().create_dir(path) {
897                    Ok(_) => Ok(success_object!(Value::Nil)),
898                    Err(kind) => Ok(io_error_to_error_object(kind)),
899                }
900            }),
901            "remova_diretório" => builtin_fn!(["caminho"], |args, runtime, _| {
902                let path = ensure!(args!(args, 0), String(value) => value);
903
904                match runtime.get_platform().remove_dir(path) {
905                    Ok(_) => Ok(success_object!(Value::Nil)),
906                    Err(kind) => Ok(io_error_to_error_object(kind)),
907                }
908            }),
909            "caminho_atual" => builtin_fn!(|_, runtime, _| {
910                match runtime.get_platform().current_dir() {
911                    Ok(path) => Ok(success_object!(Value::String(path))),
912                    Err(kind) => Ok(io_error_to_error_object(kind)),
913                }
914            }),
915        })
916    );
917}
918
919fn setup_program_prelude(env: &mut Environment) {
920    global!(
921        env,
922        def_assoc_array!("Programa", {
923            "argumentos" => builtin_fn!(|_, runtime, _| {
924                let args = runtime.get_platform().args();
925                let args = args.into_iter().map(Value::String).collect();
926                let value = Value::List(Rc::new(RefCell::new(args)));
927
928                Ok(value)
929            }),
930            "encerra" => builtin_fn!(["código"], |args, runtime, _| {
931                let code = ensure!(args!(args, 0), Number(value) => *value as i32);
932
933                runtime.get_platform().exit(code);
934
935                Ok(Value::Nil)
936            }),
937            "espera" => builtin_fn!(["segundos"], |args, runtime, _| {
938                let seconds = ensure!(args!(args, 0), Number(value) => *value);
939
940                runtime.get_platform().sleep(seconds);
941
942                Ok(Value::Nil)
943            }),
944        })
945    );
946}
947
948fn setup_date_prelude(env: &mut Environment) {
949    global!(
950        env,
951        def_assoc_array!("Data", {
952            "erros" => assoc_array_enum! {
953                "ISO_INVÁLIDA",
954                "TIMESTAMP_INVÁLIDO",
955                "FUSO_HORÁRIO_INVÁLIDO",
956            },
957            "agora" => builtin_fn!(|_, runtime, _| {
958                let now = runtime.get_platform().date_now();
959                let tz = runtime.get_platform().timezone_offset();
960
961                let value = Value::Date(Date::from_timestamp_millis(now, Some(tz)).unwrap());
962
963                Ok(value)
964            }),
965            "para_iso" => builtin_fn!(["data"], |args, _, _| {
966                let date = ensure!(args!(args, 0), Date(date) => date);
967
968                Ok(Value::String(date.to_iso_string()))
969            }),
970            "para_timestamp" => builtin_fn!(["data"], |args, _, _| {
971                let date = ensure!(args!(args, 0), Date(date) => date);
972
973                Ok(Value::Number(date.to_timestamp_millis() as f64))
974            }),
975            "de_iso" => builtin_fn!(["texto"], |args, _, _| {
976                let text = ensure!(args!(args, 0), String(value) => value);
977
978                 match Date::from_iso_string(text) {
979                    Ok(date) => Ok(Value::Date(date)),
980                    Err(kind) => Ok(date_error_to_error_object(*kind))
981                }
982            }),
983            "de_timestamp" => builtin_fn!(["número"], |args, _, _| {
984                let timestamp = ensure!(args!(args, 0), Number(value) => *value as i64);
985
986                match Date::from_timestamp_millis(timestamp, None) {
987                    Ok(date) => Ok(Value::Date(date)),
988                    Err(kind) => Ok(date_error_to_error_object(*kind))
989                }
990            }),
991            "com_região" => builtin_fn!(["data", "região"], |args, _, _| {
992                let date = ensure!(args!(args, 0), Date(date) => date);
993                let offset = ensure!(args!(args, 1), String(value) => value);
994
995                match date.with_named_timezone(offset) {
996                    Ok(date) => Ok(Value::Date(date)),
997                    Err(kind) => Ok(date_error_to_error_object(*kind))
998                }
999            }),
1000            "desvio_fuso_horário" => builtin_fn!(["data"], |args, _, _| {
1001                let date = ensure!(args!(args, 0), Date(date) => date);
1002
1003                Ok(Value::String(date.to_offset_string()))
1004            }),
1005            "ano" => builtin_fn!(["data"], |args, _, _| {
1006                let date = ensure!(args!(args, 0), Date(date) => date);
1007
1008                Ok(Value::Number(date.year() as f64))
1009            }),
1010            "mês" => builtin_fn!(["data"], |args, _, _| {
1011                let date = ensure!(args!(args, 0), Date(date) => date);
1012
1013                Ok(Value::Number(date.month() as f64))
1014            }),
1015            "dia" => builtin_fn!(["data"], |args, _, _| {
1016                let date = ensure!(args!(args, 0), Date(date) => date);
1017
1018                Ok(Value::Number(date.day() as f64))
1019            }),
1020            "hora" => builtin_fn!(["data"], |args, _, _| {
1021                let date = ensure!(args!(args, 0), Date(date) => date);
1022
1023                Ok(Value::Number(date.hour() as f64))
1024            }),
1025            "minuto" => builtin_fn!(["data"], |args, _, _| {
1026                let date = ensure!(args!(args, 0), Date(date) => date);
1027
1028                Ok(Value::Number(date.minute() as f64))
1029            }),
1030            "segundo" => builtin_fn!(["data"], |args, _, _| {
1031                let date = ensure!(args!(args, 0), Date(date) => date);
1032
1033                Ok(Value::Number(date.second() as f64))
1034            }),
1035            "dia_da_semana" => builtin_fn!(["data"], |args, _, _| {
1036                let date = ensure!(args!(args, 0), Date(date) => date);
1037
1038                Ok(Value::Number(date.weekday() as f64))
1039            }),
1040            "dia_do_ano" => builtin_fn!(["data"], |args, _, _| {
1041                let date = ensure!(args!(args, 0), Date(date) => date);
1042
1043                Ok(Value::Number(date.ordinal() as f64))
1044            }),
1045            "semana_do_ano" => builtin_fn!(["data"], |args, _, _| {
1046                let date = ensure!(args!(args, 0), Date(date) => date);
1047
1048                Ok(Value::Number(date.iso_week() as f64))
1049            }),
1050        })
1051    );
1052}
1053
1054fn io_error_to_error_object(kind: FileErrorKind) -> Value {
1055    use FileErrorKind::*;
1056
1057    match kind {
1058        NotFound => error_object!("NÃO_ENCONTRADO"),
1059        PermissionDenied => error_object!("PERMISSÃO_NEGADA"),
1060        AlreadyExists => error_object!("JÁ_EXISTE"),
1061        _ => error_object!("OUTRO"),
1062    }
1063}
1064
1065fn date_error_to_error_object(kind: RuntimeError) -> Value {
1066    use RuntimeError::*;
1067
1068    match kind {
1069        InvalidTimestamp { .. } => error_object!("TIMESTAMP_INVÁLIDO"),
1070        DateIsoParseError { .. } => error_object!("ISO_INVÁLIDA"),
1071        InvalidTimeZoneString { .. } => error_object!("FUSO_HORÁRIO_INVÁLIDO"),
1072        _ => panic!("unexpected date error kind: {:?}", kind),
1073    }
1074}