#[cfg(feature = "no_std")]
use alloc::{format, string::{String, ToString}, vec::Vec};
use crate::error::BopError;
use crate::memory::bop_would_exceed;
use crate::parser::{VariantDecl, VariantKind};
use crate::value::Value;
pub fn builtin_result_variants() -> Vec<VariantDecl> {
alloc_import::vec![
VariantDecl {
name: String::from("Ok"),
kind: VariantKind::Tuple(alloc_import::vec![String::from("value")]),
},
VariantDecl {
name: String::from("Err"),
kind: VariantKind::Tuple(alloc_import::vec![String::from("error")]),
},
]
}
pub fn builtin_runtime_error_fields() -> Vec<String> {
alloc_import::vec![String::from("message"), String::from("line")]
}
pub fn builtin_iter_variants() -> Vec<VariantDecl> {
alloc_import::vec![
VariantDecl {
name: String::from("Next"),
kind: VariantKind::Tuple(alloc_import::vec![String::from("value")]),
},
VariantDecl {
name: String::from("Done"),
kind: VariantKind::Unit,
},
]
}
pub fn make_iter_next(value: Value) -> Value {
let mut items: Vec<Value> = Vec::with_capacity(1);
items.push(value);
Value::new_enum_tuple(
String::from(crate::value::BUILTIN_MODULE_PATH),
String::from("Iter"),
String::from("Next"),
items,
)
}
pub fn make_iter_done() -> Value {
Value::new_enum_unit(
String::from(crate::value::BUILTIN_MODULE_PATH),
String::from("Iter"),
String::from("Done"),
)
}
#[cfg(not(feature = "no_std"))]
use std as alloc_import;
#[cfg(feature = "no_std")]
use alloc as alloc_import;
pub fn builtin_range(
args: &[Value],
line: u32,
rand_state: &mut u64,
) -> Result<Value, BopError> {
let _ = rand_state; let (start, end, step) = match args.len() {
1 => {
let n = expect_int("range", &args[0], line)?;
(0i64, n, 1i64)
}
2 => {
let start = expect_int("range", &args[0], line)?;
let end = expect_int("range", &args[1], line)?;
(start, end, if start <= end { 1 } else { -1 })
}
3 => {
let start = expect_int("range", &args[0], line)?;
let end = expect_int("range", &args[1], line)?;
let step = expect_int("range", &args[2], line)?;
if step == 0 {
return Err(error(line, "range step can't be 0"));
}
(start, end, step)
}
_ => return Err(error(line, "range takes 1, 2, or 3 arguments")),
};
let mut result = Vec::new();
let mut i = start;
let max_items = 10_000usize;
if step > 0 {
while i < end && result.len() < max_items {
result.push(Value::Int(i));
i = match i.checked_add(step) {
Some(v) => v,
None => break,
};
}
} else {
while i > end && result.len() < max_items {
result.push(Value::Int(i));
i = match i.checked_add(step) {
Some(v) => v,
None => break,
};
}
}
Ok(Value::new_array(result))
}
pub fn finite_to_int_or_number(n: f64) -> Value {
if n.is_finite() && n >= i64::MIN as f64 && n <= i64::MAX as f64 {
Value::Int(n as i64)
} else {
Value::Number(n)
}
}
pub fn builtin_panic(args: &[Value], line: u32) -> Result<Value, BopError> {
expect_args("panic", args, 1, line)?;
let message = match &args[0] {
Value::Str(s) => s.as_str().to_string(),
other => format!("{}", other),
};
Err(error(line, message))
}
pub fn builtin_rand(args: &[Value], line: u32, rand_state: &mut u64) -> Result<Value, BopError> {
expect_args("rand", args, 1, line)?;
let n = expect_int("rand", &args[0], line)?;
if n <= 0 {
return Err(error(line, "rand needs a positive number"));
}
*rand_state = rand_state
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
let value = (*rand_state >> 33) % (n as u64);
Ok(Value::Int(value as i64))
}
pub fn expect_args(
name: &str,
args: &[Value],
expected: usize,
line: u32,
) -> Result<(), BopError> {
if args.len() != expected {
Err(error(
line,
format!(
"`{}` expects {} argument{}, but got {}",
name,
expected,
if expected == 1 { "" } else { "s" },
args.len()
),
))
} else {
Ok(())
}
}
pub fn expect_number(
func_name: &str,
val: &Value,
line: u32,
) -> Result<f64, BopError> {
match val {
Value::Int(n) => Ok(*n as f64),
Value::Number(n) => Ok(*n),
_ => Err(error(
line,
format!(
"`{}` expects a number, but got {}",
func_name,
val.type_name()
),
)),
}
}
pub fn expect_int(
func_name: &str,
val: &Value,
line: u32,
) -> Result<i64, BopError> {
match val {
Value::Int(n) => Ok(*n),
_ => Err(error(
line,
format!(
"`{}` expects an int, but got {}",
func_name,
val.type_name()
),
)),
}
}
pub fn error(line: u32, message: impl Into<String>) -> BopError {
BopError {
line: Some(line),
column: None,
message: message.into(),
friendly_hint: None,
is_fatal: false,
is_try_return: false,
}
}
pub fn error_at(
line: u32,
column: Option<core::num::NonZeroU32>,
message: impl Into<String>,
) -> BopError {
BopError {
line: Some(line),
column: column.map(|c| c.get()),
message: message.into(),
friendly_hint: None,
is_fatal: false,
is_try_return: false,
}
}
pub fn error_with_hint(
line: u32,
message: impl Into<String>,
hint: impl Into<String>,
) -> BopError {
BopError {
line: Some(line),
column: None,
message: message.into(),
friendly_hint: Some(hint.into()),
is_fatal: false,
is_try_return: false,
}
}
pub fn error_with_hint_at(
line: u32,
column: Option<core::num::NonZeroU32>,
message: impl Into<String>,
hint: impl Into<String>,
) -> BopError {
BopError {
line: Some(line),
column: column.map(|c| c.get()),
message: message.into(),
friendly_hint: Some(hint.into()),
is_fatal: false,
is_try_return: false,
}
}
pub fn error_fatal_with_hint(
line: u32,
message: impl Into<String>,
hint: impl Into<String>,
) -> BopError {
BopError {
line: Some(line),
column: None,
message: message.into(),
friendly_hint: Some(hint.into()),
is_fatal: true,
is_try_return: false,
}
}
pub fn error_fatal(line: u32, message: impl Into<String>) -> BopError {
BopError {
line: Some(line),
column: None,
message: message.into(),
friendly_hint: None,
is_fatal: true,
is_try_return: false,
}
}
pub fn make_try_call_ok(value: Value) -> Value {
let mut items: Vec<Value> = Vec::with_capacity(1);
items.push(value);
Value::new_enum_tuple(
String::from(crate::value::BUILTIN_MODULE_PATH),
String::from("Result"),
String::from("Ok"),
items,
)
}
pub fn make_try_call_err(err: &BopError) -> Value {
let message = Value::new_str(err.message.clone());
let line = Value::Int(err.line.unwrap_or(0) as i64);
let mut fields: Vec<(String, Value)> = Vec::with_capacity(2);
fields.push((String::from("message"), message));
fields.push((String::from("line"), line));
let rt_err = Value::new_struct(
String::from(crate::value::BUILTIN_MODULE_PATH),
String::from("RuntimeError"),
fields,
);
let mut items: Vec<Value> = Vec::with_capacity(1);
items.push(rt_err);
Value::new_enum_tuple(
String::from(crate::value::BUILTIN_MODULE_PATH),
String::from("Result"),
String::from("Err"),
items,
)
}
pub fn check_string_repeat_memory(len: usize, count: usize, line: u32) -> Result<(), BopError> {
let result_len = len.saturating_mul(count);
if bop_would_exceed(result_len) {
Err(error_fatal_with_hint(
line,
"Memory limit exceeded",
"This string repeat would use too much memory.",
))
} else {
Ok(())
}
}
pub fn check_string_concat_memory(a_len: usize, b_len: usize, line: u32) -> Result<(), BopError> {
let result_len = a_len + b_len;
if bop_would_exceed(result_len) {
Err(error_fatal_with_hint(
line,
"Memory limit exceeded",
"This string concatenation would use too much memory.",
))
} else {
Ok(())
}
}
pub fn check_array_concat_memory(a_len: usize, b_len: usize, line: u32) -> Result<(), BopError> {
let result_bytes = (a_len + b_len) * core::mem::size_of::<Value>();
if bop_would_exceed(result_bytes) {
Err(error_fatal_with_hint(
line,
"Memory limit exceeded",
"This array concatenation would use too much memory.",
))
} else {
Ok(())
}
}