ocypode_lang/runtime/builtins/
functions.rs

1use crate::{
2    ast::*,
3    errors::{Error as OYError, ErrorKind as OYErrorKind, Result as OYResult},
4};
5
6/// Format builtin function. It takes a string as first argument and a list of arguments to format.
7///
8/// If the number of arguments is not the same as the number of `{}` in the string, it will return an error.
9///
10/// # Example
11/// ```oy
12/// println<format<"Hello {}"><["Awiteb"]>>;
13/// // Hello Awiteb
14/// ```
15pub fn format(args: Vec<ObjectExpression>, call_span: Span) -> OYResult<ObjectExpression> {
16    // Safety: The number of arguments is checked in the interpreter before calling this function.
17    let format = &args[0];
18    let args = &args[1];
19    let format = match format {
20        ObjectExpression::String(format, _) => format,
21        _ => {
22            return Err(OYError::new(
23                OYErrorKind::UnexpectedType("String".to_owned(), format.type_name().to_owned()),
24                format.span(),
25            ))
26        }
27    };
28    let args: Vec<_> = match args {
29        ObjectExpression::Array(args, _) => args
30            .iter()
31            .map(|arg| match arg {
32                ExpressionStatement::Value(ValueExpression::Object(obj)) => obj,
33                _ => unreachable!(
34                    "The interpreter should have checked that the arguments are objects"
35                ),
36            })
37            .collect(),
38        _ => {
39            return Err(OYError::new(
40                OYErrorKind::UnexpectedType("Array".to_owned(), args.type_name().to_owned()),
41                args.span(),
42            ))
43        }
44    };
45    let mut result = String::new();
46    let mut arg_index = 0;
47    let mut placeholder = false;
48    let mut close_placeholder = false;
49    let mut placeholder_number = String::new();
50
51    let placeholder_count = regex::Regex::new(r"\{(\d+)?\}")
52        .unwrap()
53        .find_iter(format)
54        .count();
55    let placeholder_count_without_index = regex::Regex::new(r"\{\}")
56        .unwrap()
57        .find_iter(format)
58        .count();
59
60    if args.len() > placeholder_count {
61        return Err(OYError::new(
62            OYErrorKind::FormatError(
63                format!(
64                    "Too many arguments for format string, expected {}, got {}",
65                    placeholder_count, args.len()
66                ),
67                format!("The format string has {} placeholders, remove the extra arguments or add more placeholders", placeholder_count),
68            ),
69            call_span,
70        ));
71    }
72    if placeholder_count_without_index > args.len() {
73        return Err(OYError::new(
74            OYErrorKind::FormatError(
75                    "Too many placeholders without index for format string".to_owned()
76                ,
77                format!("| The format string has {} placeholders without index\n| and there are only {} arguments, you can add more\n| arguments or remove the extra placeholders", 
78                    placeholder_count_without_index, args.len()
79                ),
80            ),
81            call_span,
82        ));
83    }
84
85    for c in format.chars() {
86        if placeholder {
87            if c == '}' {
88                if placeholder_number.is_empty() {
89                    if arg_index < args.len() {
90                        result.push_str(&args[arg_index].to_string());
91                        arg_index += 1;
92                    }
93                } else {
94                    let placeholder_number: usize = placeholder_number.parse().map_err(|_| {
95                        OYError::new(
96                            OYErrorKind::FormatError(
97                                format!("Invalid placeholder index `{}`", placeholder_number),
98                                "The placeholder index must be a number".to_owned(),
99                            ),
100                            call_span,
101                        )
102                    })?;
103                    if placeholder_number < args.len() {
104                        result.push_str(&args[placeholder_number].to_string());
105                    } else {
106                        return Err(OYError::new(
107                            OYErrorKind::FormatError(
108                                format!(
109                                    "The placeholder index `{}` is out of range.",
110                                    placeholder_number
111                                ),
112                                format!("The maximum index is {}", placeholder_count),
113                            ),
114                            call_span,
115                        ));
116                    }
117                }
118                placeholder = false;
119                placeholder_number.clear();
120            } else if c == '{' {
121                result.push('{');
122                placeholder = false;
123            } else if c.is_numeric() {
124                placeholder_number.push(c);
125            } else {
126                return Err(OYError::new(
127                    OYErrorKind::FormatError(
128                        format!("Invalid placeholder `{}`", c),
129                        "The placeholder must contain the index or nothing".to_owned(),
130                    ),
131                    call_span,
132                ));
133            }
134        } else if c == '{' {
135            placeholder = true;
136        } else if c == '}' && !close_placeholder {
137            close_placeholder = true;
138        } else if c == '}' && close_placeholder {
139            result.push('}');
140            close_placeholder = false;
141        } else if close_placeholder {
142            // Unclosed placeholder
143            return Err(OYError::new(
144                OYErrorKind::FormatError(
145                    "Unclosed placeholder".to_owned(),
146                    "The placeholder must be closed with `}`".to_owned(),
147                ),
148                call_span,
149            ));
150        } else {
151            result.push(c);
152        }
153    }
154    Ok(ObjectExpression::String(result, call_span))
155}
156
157/// Print builtin function. It takes a array of objects as argument and print them.
158///
159/// Note: The function will not print a newline at the end of the value. To print a newline, use `println`.
160pub fn print(args: Vec<ObjectExpression>, call_span: Span) -> OYResult<ObjectExpression> {
161    print!("{}", print_result(args)?);
162    Ok(ObjectExpression::Nil(call_span))
163}
164
165/// Print builtin function. It takes a array of objects as argument and print them with a newline.
166pub fn println(args: Vec<ObjectExpression>, call_span: Span) -> OYResult<ObjectExpression> {
167    println!("{}", print_result(args)?);
168    Ok(ObjectExpression::Nil(call_span))
169}
170
171/// Input builtin function. It takes a prompt as argument and read a line from stdin.
172/// The function returns a string.
173pub fn input(args: Vec<ObjectExpression>, call_span: Span) -> OYResult<ObjectExpression> {
174    let prompt = match &args[0] {
175        ObjectExpression::String(prompt, _) => prompt,
176        _ => {
177            return Err(OYError::new(
178                OYErrorKind::UnexpectedType("String".to_owned(), args[0].type_name().to_owned()),
179                args[0].span(),
180            ))
181        }
182    };
183    let input = rustyline::DefaultEditor::new()
184        .map_err(|_| {
185            OYError::new(
186                OYErrorKind::Runtime("Failed to read a line from stdin".to_owned()),
187                call_span,
188            )
189        })?
190        .readline(prompt)
191        .map_err(|_| {
192            OYError::new(
193                OYErrorKind::Runtime("Failed to read a line from stdin".to_owned()),
194                call_span,
195            )
196        })?;
197    Ok(ObjectExpression::String(input, call_span))
198}
199
200/// Len builtin function. It takes a array/string as argument and returns the length of the array/string.
201pub fn len(args: Vec<ObjectExpression>, call_span: Span) -> OYResult<ObjectExpression> {
202    let len = match &args[0] {
203        ObjectExpression::Array(array, _) => array.len().to_string(),
204        ObjectExpression::String(string, _) => string.chars().count().to_string(),
205        _ => {
206            return Err(OYError::new(
207                OYErrorKind::UnexpectedType(
208                    "Array or String".to_owned(),
209                    args[0].type_name().to_owned(),
210                ),
211                args[0].span(),
212            ))
213        }
214    };
215    Ok(ObjectExpression::Int(
216        len.parse().map_err(|err| {
217            OYError::new(
218                OYErrorKind::Runtime(format!(
219                    "Failed to parse the length of the array/string: {}",
220                    err
221                )),
222                call_span,
223            )
224        })?,
225        call_span,
226    ))
227}
228
229/// Push builtin function. It takes a array and an object as argument and returns the array with the object pushed.
230pub fn push(mut args: Vec<ObjectExpression>, call_span: Span) -> OYResult<ObjectExpression> {
231    let object = args.pop().unwrap();
232    let array = args.pop().unwrap();
233    let mut array: Vec<_> = match array {
234        ObjectExpression::Array(array, _) => array,
235        _ => {
236            return Err(OYError::new(
237                OYErrorKind::UnexpectedType("Array".to_owned(), args[0].type_name().to_owned()),
238                args[0].span(),
239            ))
240        }
241    };
242    array.push(ExpressionStatement::Value(ValueExpression::Object(object)));
243    Ok(ObjectExpression::Array(array, call_span))
244}
245
246/// Pop builtin function. It takes a array as argument and returns the array with the last element popped
247pub fn pop(mut args: Vec<ObjectExpression>, call_span: Span) -> OYResult<ObjectExpression> {
248    let array = args.pop().unwrap();
249    let mut array: Vec<_> = match array {
250        ObjectExpression::Array(array, _) => array,
251        _ => {
252            return Err(OYError::new(
253                OYErrorKind::UnexpectedType("Array".to_owned(), args[0].type_name().to_owned()),
254                args[0].span(),
255            ))
256        }
257    };
258    array.pop();
259    Ok(ObjectExpression::Array(array, call_span))
260}
261
262fn print_result(mut args: Vec<ObjectExpression>) -> OYResult<String> {
263    let args = args
264        .pop()
265        .expect("The interpreter should have checked that the arguments are not empty");
266    Ok(match args {
267        ObjectExpression::Array(args, _) => args
268            .into_iter()
269            .map(|arg| match arg {
270                ExpressionStatement::Value(ValueExpression::Object(obj)) => obj.to_string(),
271                _ => unreachable!(
272                    "The interpreter should have checked that the arguments are objects, {:?}",
273                    arg
274                ),
275            })
276            .collect(),
277        _ => {
278            return Err(OYError::new(
279                OYErrorKind::UnexpectedType("Array".to_owned(), args.type_name().to_owned()),
280                args.span(),
281            ))
282        }
283    })
284}