1use rust_decimal::prelude::ToPrimitive;
2
3use crate::error::{Result, Span};
4#[cfg(feature = "no_std_io")]
5use crate::io::Write;
6#[cfg(not(feature = "no_std_io"))]
7use std::io::Write;
8use std::rc::Rc;
9
10use super::value::{types, Value, WrappedValue};
11
12#[macro_export]
13macro_rules! expect_len {
14 ($args:ident, $num:literal, $name:literal, $span:ident) => {
15 if $args.len() != $num {
16 error!(
17 TypeError,
18 *$span,
19 concat!(
20 "Function '", $name, "' takes ", $num, " argument", expect_len!(@plural $num),
21 ", however {} were supplied"
22 ),
23 $args.len(),
24 );
25 }
26 };
27 (@plural 1) => { "" };
28 (@plural $num:literal) => { "s" };
29}
30
31#[cfg(not(feature = "no_std_io"))]
32pub fn print<'tree>(
33 args: Vec<WrappedValue<'tree>>,
34 stdout: &mut impl Write,
35 span: &Span,
36 newline: bool,
37) -> Result<WrappedValue<'tree>> {
38 let args: Vec<_> = args.iter().map(|arg| arg.borrow().to_string()).collect();
39 if let Err(e) = write!(
40 stdout,
41 "{}{}",
42 args.join(" "),
43 if newline { "\n" } else { "" }
44 ) {
45 error!(SystemError, *span, "Failed to write to stdout: {}", e,);
46 }
47 Ok(Value::Null.wrapped())
48}
49
50#[cfg(feature = "no_std_io")]
51pub fn print<'tree>(
52 args: Vec<WrappedValue<'tree>>,
53 stdout: &mut impl Write,
54 _span: &Span,
55 newline: bool,
56) -> Result<WrappedValue<'tree>> {
57 let args: Vec<_> = args.iter().map(|arg| arg.borrow().to_string()).collect();
58 stdout.write(format!(
59 "{}{}",
60 args.join(" "),
61 if newline { "\n" } else { "" }
62 ));
63 Ok(Value::Null.wrapped())
64}
65
66pub fn exit<'tree>(
67 args: Vec<WrappedValue<'tree>>,
68 callback: impl FnOnce(i32),
69 span: &Span,
70) -> Result<WrappedValue<'tree>> {
71 expect_len!(args, 1, "exit", span);
72 if let Value::Number(num) = &*args[0].borrow() {
73 if !num.fract().is_zero() {
74 error!(ValueError, *span, "Exit code has to be an integer");
75 }
76 if let Some(num) = num.to_i32() {
77 callback(num)
78 } else {
79 error!(ValueError, *span, "Exit code is too high or too low");
80 }
81 } else {
82 error!(
83 TypeError,
84 *span, "First argument of function 'exit' has to be of type 'number'",
85 );
86 }
87 Ok(Value::Null.wrapped())
88}
89
90pub fn type_of<'tree>(args: Vec<WrappedValue<'tree>>, span: &Span) -> Result<WrappedValue<'tree>> {
91 expect_len!(args, 1, "typeOf", span);
92 Ok(Value::String(types::type_of(&args[0].borrow()).to_string()).wrapped())
93}
94
95pub fn assert<'tree>(args: Vec<WrappedValue<'tree>>, span: &Span) -> Result<WrappedValue<'tree>> {
96 expect_len!(args, 1, "assert", span);
97 if args[0].borrow().is_false() {
98 error!(RuntimeError, *span, "Assertion failed");
99 }
100 Ok(Value::Null.wrapped())
101}
102
103pub fn throw<'tree>(args: Vec<WrappedValue<'tree>>, span: &Span) -> Result<WrappedValue<'tree>> {
104 expect_len!(args, 1, "throw", span);
105 let borrow = args[0].borrow();
106 let str = match &*borrow {
107 Value::String(str) => str,
108 _ => error!(
109 TypeError,
110 *span, "First argument of function 'throw' has to be of type 'string'",
111 ),
112 };
113 error!(RuntimeError, *span, "{str}",)
114}
115
116#[cfg(not(feature = "no_std_io"))]
117pub fn debug<'tree>(
118 args: Vec<WrappedValue<'tree>>,
119 stderr: &mut impl Write,
120 span: &Span,
121) -> Result<WrappedValue<'tree>> {
122 let arg_strings: Vec<_> = args
123 .iter()
124 .map(|arg| format!("{:?}", arg.borrow()))
125 .collect();
126 if let Err(e) = writeln!(stderr, "{}", arg_strings.join(", ")) {
127 error!(SystemError, *span, "Failed to write to stdout: {}", e,);
128 }
129 if args.len() == 1 {
130 Ok(Rc::clone(&args[0]))
131 } else {
132 Ok(Value::List(args).wrapped())
133 }
134}
135
136#[cfg(feature = "no_std_io")]
137pub fn debug<'tree>(
138 args: Vec<WrappedValue<'tree>>,
139 stderr: &mut impl Write,
140 _span: &Span,
141) -> Result<WrappedValue<'tree>> {
142 let arg_strings: Vec<_> = args
143 .iter()
144 .map(|arg| format!("{:?}", arg.borrow()))
145 .collect();
146 stderr.write(format!("{}\n", arg_strings.join(", ")));
147 if args.len() == 1 {
148 Ok(Rc::clone(&args[0]))
149 } else {
150 Ok(Value::List(args).wrapped())
151 }
152}