1#[cfg(feature = "no_std")]
8use alloc::{format, string::{String, ToString}, vec::Vec};
9
10use crate::error::BopError;
11use crate::memory::bop_would_exceed;
12use crate::parser::{VariantDecl, VariantKind};
13use crate::value::Value;
14
15pub fn builtin_result_variants() -> Vec<VariantDecl> {
35 alloc_import::vec![
36 VariantDecl {
37 name: String::from("Ok"),
38 kind: VariantKind::Tuple(alloc_import::vec![String::from("value")]),
39 },
40 VariantDecl {
41 name: String::from("Err"),
42 kind: VariantKind::Tuple(alloc_import::vec![String::from("error")]),
43 },
44 ]
45}
46
47pub fn builtin_runtime_error_fields() -> Vec<String> {
51 alloc_import::vec![String::from("message"), String::from("line")]
52}
53
54pub fn builtin_iter_variants() -> Vec<VariantDecl> {
60 alloc_import::vec![
61 VariantDecl {
62 name: String::from("Next"),
63 kind: VariantKind::Tuple(alloc_import::vec![String::from("value")]),
64 },
65 VariantDecl {
66 name: String::from("Done"),
67 kind: VariantKind::Unit,
68 },
69 ]
70}
71
72pub fn make_iter_next(value: Value) -> Value {
76 let mut items: Vec<Value> = Vec::with_capacity(1);
77 items.push(value);
78 Value::new_enum_tuple(
79 String::from(crate::value::BUILTIN_MODULE_PATH),
80 String::from("Iter"),
81 String::from("Next"),
82 items,
83 )
84}
85
86pub fn make_iter_done() -> Value {
89 Value::new_enum_unit(
90 String::from(crate::value::BUILTIN_MODULE_PATH),
91 String::from("Iter"),
92 String::from("Done"),
93 )
94}
95
96#[cfg(not(feature = "no_std"))]
101use std as alloc_import;
102#[cfg(feature = "no_std")]
103use alloc as alloc_import;
104
105pub fn builtin_range(
106 args: &[Value],
107 line: u32,
108 rand_state: &mut u64,
109) -> Result<Value, BopError> {
110 let _ = rand_state; let (start, end, step) = match args.len() {
114 1 => {
115 let n = expect_int("range", &args[0], line)?;
116 (0i64, n, 1i64)
117 }
118 2 => {
119 let start = expect_int("range", &args[0], line)?;
120 let end = expect_int("range", &args[1], line)?;
121 (start, end, if start <= end { 1 } else { -1 })
122 }
123 3 => {
124 let start = expect_int("range", &args[0], line)?;
125 let end = expect_int("range", &args[1], line)?;
126 let step = expect_int("range", &args[2], line)?;
127 if step == 0 {
128 return Err(error(line, "range step can't be 0"));
129 }
130 (start, end, step)
131 }
132 _ => return Err(error(line, "range takes 1, 2, or 3 arguments")),
133 };
134
135 let mut result = Vec::new();
136 let mut i = start;
137 let max_items = 10_000usize;
138 if step > 0 {
139 while i < end && result.len() < max_items {
140 result.push(Value::Int(i));
141 i = match i.checked_add(step) {
142 Some(v) => v,
143 None => break,
144 };
145 }
146 } else {
147 while i > end && result.len() < max_items {
148 result.push(Value::Int(i));
149 i = match i.checked_add(step) {
150 Some(v) => v,
151 None => break,
152 };
153 }
154 }
155 Ok(Value::new_array(result))
156}
157
158pub fn finite_to_int_or_number(n: f64) -> Value {
164 if n.is_finite() && n >= i64::MIN as f64 && n <= i64::MAX as f64 {
165 Value::Int(n as i64)
166 } else {
167 Value::Number(n)
168 }
169}
170
171pub fn builtin_panic(args: &[Value], line: u32) -> Result<Value, BopError> {
179 expect_args("panic", args, 1, line)?;
180 let message = match &args[0] {
181 Value::Str(s) => s.as_str().to_string(),
182 other => format!("{}", other),
187 };
188 Err(error(line, message))
189}
190
191pub fn builtin_rand(args: &[Value], line: u32, rand_state: &mut u64) -> Result<Value, BopError> {
192 expect_args("rand", args, 1, line)?;
193 let n = expect_int("rand", &args[0], line)?;
194 if n <= 0 {
195 return Err(error(line, "rand needs a positive number"));
196 }
197 *rand_state = rand_state
199 .wrapping_mul(6364136223846793005)
200 .wrapping_add(1442695040888963407);
201 let value = (*rand_state >> 33) % (n as u64);
202 Ok(Value::Int(value as i64))
203}
204
205pub fn expect_args(
208 name: &str,
209 args: &[Value],
210 expected: usize,
211 line: u32,
212) -> Result<(), BopError> {
213 if args.len() != expected {
214 Err(error(
215 line,
216 format!(
217 "`{}` expects {} argument{}, but got {}",
218 name,
219 expected,
220 if expected == 1 { "" } else { "s" },
221 args.len()
222 ),
223 ))
224 } else {
225 Ok(())
226 }
227}
228
229pub fn expect_number(
230 func_name: &str,
231 val: &Value,
232 line: u32,
233) -> Result<f64, BopError> {
234 match val {
235 Value::Int(n) => Ok(*n as f64),
236 Value::Number(n) => Ok(*n),
237 _ => Err(error(
238 line,
239 format!(
240 "`{}` expects a number, but got {}",
241 func_name,
242 val.type_name()
243 ),
244 )),
245 }
246}
247
248pub fn expect_int(
253 func_name: &str,
254 val: &Value,
255 line: u32,
256) -> Result<i64, BopError> {
257 match val {
258 Value::Int(n) => Ok(*n),
259 _ => Err(error(
260 line,
261 format!(
262 "`{}` expects an int, but got {}",
263 func_name,
264 val.type_name()
265 ),
266 )),
267 }
268}
269
270pub fn error(line: u32, message: impl Into<String>) -> BopError {
271 BopError {
272 line: Some(line),
273 column: None,
274 message: message.into(),
275 friendly_hint: None,
276 is_fatal: false,
277 is_try_return: false,
278 }
279}
280
281pub fn error_at(
286 line: u32,
287 column: Option<core::num::NonZeroU32>,
288 message: impl Into<String>,
289) -> BopError {
290 BopError {
291 line: Some(line),
292 column: column.map(|c| c.get()),
293 message: message.into(),
294 friendly_hint: None,
295 is_fatal: false,
296 is_try_return: false,
297 }
298}
299
300pub fn error_with_hint(
301 line: u32,
302 message: impl Into<String>,
303 hint: impl Into<String>,
304) -> BopError {
305 BopError {
306 line: Some(line),
307 column: None,
308 message: message.into(),
309 friendly_hint: Some(hint.into()),
310 is_fatal: false,
311 is_try_return: false,
312 }
313}
314
315pub fn error_with_hint_at(
319 line: u32,
320 column: Option<core::num::NonZeroU32>,
321 message: impl Into<String>,
322 hint: impl Into<String>,
323) -> BopError {
324 BopError {
325 line: Some(line),
326 column: column.map(|c| c.get()),
327 message: message.into(),
328 friendly_hint: Some(hint.into()),
329 is_fatal: false,
330 is_try_return: false,
331 }
332}
333
334pub fn error_fatal_with_hint(
340 line: u32,
341 message: impl Into<String>,
342 hint: impl Into<String>,
343) -> BopError {
344 BopError {
345 line: Some(line),
346 column: None,
347 message: message.into(),
348 friendly_hint: Some(hint.into()),
349 is_fatal: true,
350 is_try_return: false,
351 }
352}
353
354pub fn error_fatal(line: u32, message: impl Into<String>) -> BopError {
357 BopError {
358 line: Some(line),
359 column: None,
360 message: message.into(),
361 friendly_hint: None,
362 is_fatal: true,
363 is_try_return: false,
364 }
365}
366
367pub fn make_try_call_ok(value: Value) -> Value {
389 let mut items: Vec<Value> = Vec::with_capacity(1);
390 items.push(value);
391 Value::new_enum_tuple(
392 String::from(crate::value::BUILTIN_MODULE_PATH),
393 String::from("Result"),
394 String::from("Ok"),
395 items,
396 )
397}
398
399pub fn make_try_call_err(err: &BopError) -> Value {
404 let message = Value::new_str(err.message.clone());
405 let line = Value::Int(err.line.unwrap_or(0) as i64);
408 let mut fields: Vec<(String, Value)> = Vec::with_capacity(2);
409 fields.push((String::from("message"), message));
410 fields.push((String::from("line"), line));
411 let rt_err = Value::new_struct(
412 String::from(crate::value::BUILTIN_MODULE_PATH),
413 String::from("RuntimeError"),
414 fields,
415 );
416 let mut items: Vec<Value> = Vec::with_capacity(1);
417 items.push(rt_err);
418 Value::new_enum_tuple(
419 String::from(crate::value::BUILTIN_MODULE_PATH),
420 String::from("Result"),
421 String::from("Err"),
422 items,
423 )
424}
425
426pub fn check_string_repeat_memory(len: usize, count: usize, line: u32) -> Result<(), BopError> {
428 let result_len = len.saturating_mul(count);
429 if bop_would_exceed(result_len) {
430 Err(error_fatal_with_hint(
431 line,
432 "Memory limit exceeded",
433 "This string repeat would use too much memory.",
434 ))
435 } else {
436 Ok(())
437 }
438}
439
440pub fn check_string_concat_memory(a_len: usize, b_len: usize, line: u32) -> Result<(), BopError> {
442 let result_len = a_len + b_len;
443 if bop_would_exceed(result_len) {
444 Err(error_fatal_with_hint(
445 line,
446 "Memory limit exceeded",
447 "This string concatenation would use too much memory.",
448 ))
449 } else {
450 Ok(())
451 }
452}
453
454pub fn check_array_concat_memory(a_len: usize, b_len: usize, line: u32) -> Result<(), BopError> {
456 let result_bytes = (a_len + b_len) * core::mem::size_of::<Value>();
457 if bop_would_exceed(result_bytes) {
458 Err(error_fatal_with_hint(
459 line,
460 "Memory limit exceeded",
461 "This array concatenation would use too much memory.",
462 ))
463 } else {
464 Ok(())
465 }
466}