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}