use crate::runtime::validation::validate_arg_count;
use crate::runtime::{InterpreterError, Value};
use std::collections::HashMap;
use std::io::Write;
use std::sync::Arc;
pub fn eval_builtin_function(
name: &str,
args: &[Value],
) -> Result<Option<Value>, InterpreterError> {
if let Some(result) = try_eval_io_function(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_math_function(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_utility_function(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_collection_function(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_conversion_function(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_time_function(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_dataframe_function(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_environment_function(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_fs_function(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_stdlib003(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_stdlib005(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_path_function(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_json_function(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_file_function(name, args)? {
return Ok(Some(result));
}
#[cfg(not(target_arch = "wasm32"))]
if let Some(result) = try_eval_http_function(name, args)? {
return Ok(Some(result));
}
#[cfg(not(target_arch = "wasm32"))]
if let Some(result) = try_eval_html_function(name, args)? {
return Ok(Some(result));
}
#[cfg(not(target_arch = "wasm32"))]
if let Some(result) = try_eval_process_function(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_string_function(name, args)? {
return Ok(Some(result));
}
Ok(None)
}
fn try_eval_io_function(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_println__" => Ok(Some(eval_println(args)?)),
"__builtin_print__" => Ok(Some(eval_print(args)?)),
"__builtin_dbg__" => Ok(Some(eval_dbg(args)?)),
_ => Ok(None),
}
}
fn try_eval_math_function(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
if let Some(result) = try_eval_basic_math(name, args)? {
return Ok(Some(result));
}
try_eval_advanced_math(name, args)
}
fn try_eval_basic_math_part1(
name: &str,
args: &[Value],
) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_sqrt__" => Ok(Some(eval_sqrt(args)?)),
"__builtin_pow__" => Ok(Some(eval_pow(args)?)),
"__builtin_abs__" => Ok(Some(eval_abs(args)?)),
_ => Ok(None),
}
}
fn try_eval_basic_math_part2(
name: &str,
args: &[Value],
) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_min__" => Ok(Some(eval_min(args)?)),
"__builtin_max__" => Ok(Some(eval_max(args)?)),
_ => Ok(None),
}
}
fn try_eval_basic_math(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
if let Some(result) = try_eval_basic_math_part1(name, args)? {
return Ok(Some(result));
}
try_eval_basic_math_part2(name, args)
}
fn try_eval_advanced_math_part1(
name: &str,
args: &[Value],
) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_floor__" => Ok(Some(eval_floor(args)?)),
"__builtin_ceil__" => Ok(Some(eval_ceil(args)?)),
"__builtin_round__" => Ok(Some(eval_round(args)?)),
_ => Ok(None),
}
}
fn try_eval_advanced_math_part2(
name: &str,
args: &[Value],
) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_sin__" => Ok(Some(eval_sin(args)?)),
"__builtin_cos__" => Ok(Some(eval_cos(args)?)),
"__builtin_tan__" => Ok(Some(eval_tan(args)?)),
_ => Ok(None),
}
}
fn try_eval_advanced_math_part3(
name: &str,
args: &[Value],
) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_log__" => Ok(Some(eval_log(args)?)),
"__builtin_log10__" => Ok(Some(eval_log10(args)?)),
"__builtin_exp__" => Ok(Some(eval_exp(args)?)),
"__builtin_random__" => Ok(Some(eval_random(args)?)),
_ => Ok(None),
}
}
fn try_eval_advanced_math(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
if let Some(result) = try_eval_advanced_math_part1(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_advanced_math_part2(name, args)? {
return Ok(Some(result));
}
try_eval_advanced_math_part3(name, args)
}
fn try_eval_utility_part1(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_len__" => Ok(Some(eval_len(args)?)),
"__builtin_range__" => Ok(Some(eval_range(args)?)),
_ => Ok(None),
}
}
fn try_eval_utility_part2(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_type__" => Ok(Some(eval_type(args)?)),
"__builtin_type_of__" => Ok(Some(eval_type_of(args)?)),
"__builtin_is_nil__" => Ok(Some(eval_is_nil(args)?)),
"__builtin_reverse__" => Ok(Some(eval_reverse(args)?)),
"__builtin_assert_eq__" => Ok(Some(eval_assert_eq(args)?)),
"__builtin_assert__" => Ok(Some(eval_assert(args)?)),
"__builtin_zip__" => Ok(Some(eval_zip(args)?)),
"__builtin_enumerate__" => Ok(Some(eval_enumerate(args)?)),
_ => Ok(None),
}
}
fn try_eval_collection_function(
name: &str,
args: &[Value],
) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_push__" => Ok(Some(eval_push(args)?)),
"__builtin_pop__" => Ok(Some(eval_pop(args)?)),
"__builtin_sort__" => Ok(Some(eval_sort(args)?)),
_ => Ok(None),
}
}
fn try_eval_utility_function(
name: &str,
args: &[Value],
) -> Result<Option<Value>, InterpreterError> {
if let Some(result) = try_eval_utility_part1(name, args)? {
return Ok(Some(result));
}
try_eval_utility_part2(name, args)
}
fn try_eval_conversion_function(
name: &str,
args: &[Value],
) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_str__" => Ok(Some(eval_str(args)?)),
"__builtin_to_string__" => Ok(Some(eval_to_string(args)?)),
"__builtin_int__" => Ok(Some(eval_int(args)?)),
"__builtin_float__" => Ok(Some(eval_float(args)?)),
"__builtin_bool__" => Ok(Some(eval_bool(args)?)),
"__builtin_parse_int__" => Ok(Some(eval_parse_int(args)?)),
"__builtin_parse_float__" => Ok(Some(eval_parse_float(args)?)),
_ => Ok(None),
}
}
fn try_eval_time_function(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_sleep__" => Ok(Some(eval_sleep(args)?)),
"__builtin_timestamp__" => Ok(Some(eval_timestamp(args)?)),
"__builtin_chrono_utc_now__" => Ok(Some(eval_chrono_utc_now(args)?)),
_ => Ok(None),
}
}
fn try_eval_dataframe_function(
name: &str,
args: &[Value],
) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_dataframe_new__" => Ok(Some(eval_dataframe_new(args)?)),
"__builtin_dataframe_from_csv_string__" => Ok(Some(eval_dataframe_from_csv_string(args)?)),
"__builtin_dataframe_from_json__" => Ok(Some(eval_dataframe_from_json(args)?)),
_ => Ok(None),
}
}
fn format_value_for_println(value: &Value) -> String {
match value {
Value::String(s) => s.to_string(),
other => format!("{other}"),
}
}
fn format_with_interpolation(fmt_str: &str, args: &[Value]) -> String {
let mut result = fmt_str.to_string();
for arg in args {
if let Some(pos) = result.find("{}") {
result.replace_range(pos..pos + 2, &format_value_for_println(arg));
}
}
result
}
fn join_values(args: &[Value]) -> String {
args.iter()
.map(format_value_for_println)
.collect::<Vec<_>>()
.join(" ")
}
fn format_println_output(args: &[Value]) -> String {
if args.is_empty() {
"\n".to_string()
} else if let Value::String(fmt_str) = &args[0] {
if fmt_str.contains("{}") {
format!("{}\n", format_with_interpolation(fmt_str, &args[1..]))
} else {
format!("{}\n", join_values(args))
}
} else {
format!("{}\n", join_values(args))
}
}
fn eval_println(args: &[Value]) -> Result<Value, InterpreterError> {
let output = format_println_output(args);
if let Ok(mut buf) = crate::runtime::builtins::OUTPUT_BUFFER.lock() {
buf.push_str(&output);
}
print!("{output}");
let _ = std::io::stdout().flush();
Ok(Value::Nil)
}
fn eval_print(args: &[Value]) -> Result<Value, InterpreterError> {
let output = args
.iter()
.map(|v| format!("{v}"))
.collect::<Vec<_>>()
.join(" ");
if let Ok(mut buf) = crate::runtime::builtins::OUTPUT_BUFFER.lock() {
buf.push_str(&output);
}
print!("{output}");
let _ = std::io::stdout().flush();
Ok(Value::Nil)
}
fn eval_dbg(args: &[Value]) -> Result<Value, InterpreterError> {
if args.len() == 1 {
println!("[DEBUG] {:?}", args[0]);
Ok(args[0].clone())
} else {
println!("[DEBUG] {args:?}");
Ok(Value::from_array(args.to_vec()))
}
}
fn eval_sqrt(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("sqrt", args, 1)?;
match &args[0] {
Value::Integer(n) => Ok(Value::Float((*n as f64).sqrt())),
Value::Float(f) => Ok(Value::Float(f.sqrt())),
_ => Err(InterpreterError::RuntimeError(
"sqrt() expects a number".to_string(),
)),
}
}
fn eval_pow(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("pow", args, 2)?;
match (&args[0], &args[1]) {
(Value::Integer(base), Value::Integer(exp)) => {
if *exp >= 0 {
Ok(Value::Integer(base.pow(*exp as u32)))
} else {
Ok(Value::Float((*base as f64).powf(*exp as f64)))
}
}
(Value::Float(base), Value::Integer(exp)) => Ok(Value::Float(base.powf(*exp as f64))),
(Value::Integer(base), Value::Float(exp)) => Ok(Value::Float((*base as f64).powf(*exp))),
(Value::Float(base), Value::Float(exp)) => Ok(Value::Float(base.powf(*exp))),
_ => Err(InterpreterError::RuntimeError(
"pow() expects two numbers".to_string(),
)),
}
}
fn eval_abs(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("abs", args, 1)?;
match &args[0] {
Value::Integer(n) => Ok(Value::Integer(n.abs())),
Value::Float(f) => Ok(Value::Float(f.abs())),
_ => Err(InterpreterError::RuntimeError(
"abs() expects a number".to_string(),
)),
}
}
fn eval_min(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("min", args, 2)?;
match (&args[0], &args[1]) {
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(*a.min(b))),
(Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.min(*b))),
(Value::Integer(a), Value::Float(b)) => Ok(Value::Float((*a as f64).min(*b))),
(Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a.min(*b as f64))),
_ => Err(InterpreterError::RuntimeError(
"min() expects two numbers".to_string(),
)),
}
}
fn eval_max(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("max", args, 2)?;
match (&args[0], &args[1]) {
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(*a.max(b))),
(Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.max(*b))),
(Value::Integer(a), Value::Float(b)) => Ok(Value::Float((*a as f64).max(*b))),
(Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a.max(*b as f64))),
_ => Err(InterpreterError::RuntimeError(
"max() expects two numbers".to_string(),
)),
}
}
fn eval_floor(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("floor", args, 1)?;
match &args[0] {
Value::Integer(n) => Ok(Value::Integer(*n)),
Value::Float(f) => Ok(Value::Integer(f.floor() as i64)),
_ => Err(InterpreterError::RuntimeError(
"floor() expects a number".to_string(),
)),
}
}
fn eval_ceil(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("ceil", args, 1)?;
match &args[0] {
Value::Integer(n) => Ok(Value::Integer(*n)),
Value::Float(f) => Ok(Value::Integer(f.ceil() as i64)),
_ => Err(InterpreterError::RuntimeError(
"ceil() expects a number".to_string(),
)),
}
}
fn eval_round(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("round", args, 1)?;
match &args[0] {
Value::Integer(n) => Ok(Value::Integer(*n)),
Value::Float(f) => Ok(Value::Integer(f.round() as i64)),
_ => Err(InterpreterError::RuntimeError(
"round() expects a number".to_string(),
)),
}
}
fn eval_sin(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("sin", args, 1)?;
match &args[0] {
Value::Integer(n) => Ok(Value::Float((*n as f64).sin())),
Value::Float(f) => Ok(Value::Float(f.sin())),
_ => Err(InterpreterError::RuntimeError(
"sin() expects a number".to_string(),
)),
}
}
fn eval_cos(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("cos", args, 1)?;
match &args[0] {
Value::Integer(n) => Ok(Value::Float((*n as f64).cos())),
Value::Float(f) => Ok(Value::Float(f.cos())),
_ => Err(InterpreterError::RuntimeError(
"cos() expects a number".to_string(),
)),
}
}
fn eval_tan(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("tan", args, 1)?;
match &args[0] {
Value::Integer(n) => Ok(Value::Float((*n as f64).tan())),
Value::Float(f) => Ok(Value::Float(f.tan())),
_ => Err(InterpreterError::RuntimeError(
"tan() expects a number".to_string(),
)),
}
}
fn eval_log(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("log", args, 1)?;
match &args[0] {
Value::Integer(n) => Ok(Value::Float((*n as f64).ln())), Value::Float(f) => Ok(Value::Float(f.ln())),
_ => Err(InterpreterError::RuntimeError(
"log() expects a number".to_string(),
)),
}
}
fn eval_log10(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("log10", args, 1)?;
match &args[0] {
Value::Integer(n) => Ok(Value::Float((*n as f64).log10())), Value::Float(f) => Ok(Value::Float(f.log10())),
_ => Err(InterpreterError::RuntimeError(
"log10() expects a number".to_string(),
)),
}
}
fn eval_exp(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("exp", args, 1)?;
match &args[0] {
Value::Integer(n) => Ok(Value::Float((*n as f64).exp())), Value::Float(f) => Ok(Value::Float(f.exp())),
_ => Err(InterpreterError::RuntimeError(
"exp() expects a number".to_string(),
)),
}
}
fn eval_random(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("random", args, 0)?;
use rand::Rng;
let mut rng = rand::thread_rng();
Ok(Value::Float(rng.gen::<f64>())) }
fn eval_len(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("len", args, 1)?;
match &args[0] {
Value::String(s) => Ok(Value::Integer(s.len() as i64)),
Value::Array(arr) => Ok(Value::Integer(arr.len() as i64)),
Value::Tuple(t) => Ok(Value::Integer(t.len() as i64)),
Value::DataFrame { columns } => {
if columns.is_empty() {
Ok(Value::Integer(0))
} else {
Ok(Value::Integer(columns[0].values.len() as i64))
}
}
_ => Err(InterpreterError::RuntimeError(
"len() expects a string, array, tuple, or dataframe".to_string(),
)),
}
}
fn eval_range(args: &[Value]) -> Result<Value, InterpreterError> {
match args.len() {
1 => eval_range_one_arg(&args[0]),
2 => eval_range_two_args(&args[0], &args[1]),
3 => eval_range_three_args(&args[0], &args[1], &args[2]),
_ => Err(InterpreterError::RuntimeError(
"range() expects 1, 2, or 3 arguments".to_string(),
)),
}
}
fn eval_range_one_arg(end_val: &Value) -> Result<Value, InterpreterError> {
match end_val {
Value::Integer(end) => {
let mut result = Vec::new();
for i in 0..*end {
result.push(Value::Integer(i));
}
Ok(Value::Array(result.into()))
}
_ => Err(InterpreterError::RuntimeError(
"range() expects integer arguments".to_string(),
)),
}
}
fn eval_range_two_args(start_val: &Value, end_val: &Value) -> Result<Value, InterpreterError> {
match (start_val, end_val) {
(Value::Integer(start), Value::Integer(end)) => {
let mut result = Vec::new();
for i in *start..*end {
result.push(Value::Integer(i));
}
Ok(Value::Array(result.into()))
}
_ => Err(InterpreterError::RuntimeError(
"range() expects integer arguments".to_string(),
)),
}
}
fn generate_range_forward(start: i64, end: i64, step: i64) -> Vec<Value> {
let mut result = Vec::new();
let mut i = start;
while i < end {
result.push(Value::Integer(i));
i += step;
}
result
}
fn generate_range_backward(start: i64, end: i64, step: i64) -> Vec<Value> {
let mut result = Vec::new();
let mut i = start;
while i > end {
result.push(Value::Integer(i));
i += step;
}
result
}
fn eval_range_three_args(
start_val: &Value,
end_val: &Value,
step_val: &Value,
) -> Result<Value, InterpreterError> {
match (start_val, end_val, step_val) {
(Value::Integer(start), Value::Integer(end), Value::Integer(step)) => {
if *step == 0 {
return Err(InterpreterError::RuntimeError(
"range() step cannot be zero".to_string(),
));
}
let result = if *step > 0 {
generate_range_forward(*start, *end, *step)
} else {
generate_range_backward(*start, *end, *step)
};
Ok(Value::Array(result.into()))
}
_ => Err(InterpreterError::RuntimeError(
"range() expects integer arguments".to_string(),
)),
}
}
fn eval_type(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("type", args, 1)?;
Ok(Value::from_string(args[0].type_name().to_string()))
}
fn eval_type_of(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("type_of", args, 1)?;
Ok(Value::from_string(args[0].type_name().to_string()))
}
fn eval_is_nil(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("is_nil", args, 1)?;
Ok(Value::Bool(matches!(args[0], Value::Nil)))
}
fn eval_reverse(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("reverse", args, 1)?;
match &args[0] {
Value::Array(arr) => {
let mut reversed = arr.to_vec();
reversed.reverse();
Ok(Value::from_array(reversed))
}
Value::String(s) => {
let reversed: String = s.chars().rev().collect();
Ok(Value::from_string(reversed))
}
_ => Err(InterpreterError::RuntimeError(
"reverse() expects an array or string".to_string(),
)),
}
}
fn eval_push(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("push", args, 2)?;
match &args[0] {
Value::Array(arr) => {
let mut new_arr = arr.to_vec();
new_arr.push(args[1].clone());
Ok(Value::from_array(new_arr))
}
_ => Err(InterpreterError::RuntimeError(
"push() expects an array as first argument".to_string(),
)),
}
}
fn eval_pop(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("pop", args, 1)?;
match &args[0] {
Value::Array(arr) => {
let mut new_arr = arr.to_vec();
if let Some(val) = new_arr.pop() {
Ok(val)
} else {
Ok(Value::nil())
}
}
_ => Err(InterpreterError::RuntimeError(
"pop() expects an array".to_string(),
)),
}
}
fn eval_sort(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("sort", args, 1)?;
match &args[0] {
Value::Array(arr) => {
let mut new_arr = arr.to_vec();
new_arr.sort_by(|a, b| match (a, b) {
(Value::Integer(x), Value::Integer(y)) => x.cmp(y),
(Value::Float(x), Value::Float(y)) => {
x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal)
}
(Value::String(x), Value::String(y)) => x.cmp(y),
_ => std::cmp::Ordering::Equal,
});
Ok(Value::from_array(new_arr))
}
_ => Err(InterpreterError::RuntimeError(
"sort() expects an array".to_string(),
)),
}
}
fn eval_zip(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("zip", args, 2)?;
match (&args[0], &args[1]) {
(Value::Array(a), Value::Array(b)) => {
let zipped: Vec<Value> = a
.iter()
.zip(b.iter())
.map(|(x, y)| Value::Tuple(Arc::from(vec![x.clone(), y.clone()].as_slice())))
.collect();
Ok(Value::from_array(zipped))
}
_ => Err(InterpreterError::RuntimeError(
"zip() expects two arrays".to_string(),
)),
}
}
fn eval_enumerate(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("enumerate", args, 1)?;
match &args[0] {
Value::Array(arr) => {
let enumerated: Vec<Value> = arr
.iter()
.enumerate()
.map(|(i, v)| {
Value::Tuple(Arc::from(
vec![Value::Integer(i as i64), v.clone()].as_slice(),
))
})
.collect();
Ok(Value::from_array(enumerated))
}
_ => Err(InterpreterError::RuntimeError(
"enumerate() expects an array".to_string(),
)),
}
}
fn eval_sleep(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("sleep", args, 1)?;
let millis = match &args[0] {
Value::Integer(n) => *n as u64,
Value::Float(f) => *f as u64,
_ => {
return Err(InterpreterError::RuntimeError(
"sleep() expects a numeric argument".to_string(),
))
}
};
std::thread::sleep(std::time::Duration::from_millis(millis));
Ok(Value::Nil)
}
fn eval_timestamp(args: &[Value]) -> Result<Value, InterpreterError> {
if !args.is_empty() {
return Err(InterpreterError::RuntimeError(
"timestamp() expects no arguments".to_string(),
));
}
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_err(|e| InterpreterError::RuntimeError(format!("System time error: {e}")))?;
Ok(Value::Integer(now.as_millis() as i64))
}
fn eval_chrono_utc_now(args: &[Value]) -> Result<Value, InterpreterError> {
if !args.is_empty() {
return Err(InterpreterError::RuntimeError(
"Utc::now() expects no arguments".to_string(),
));
}
let now = chrono::Utc::now();
let timestamp_str = now.to_rfc3339();
Ok(Value::from_string(timestamp_str))
}
fn eval_dataframe_new(args: &[Value]) -> Result<Value, InterpreterError> {
if !args.is_empty() {
return Err(InterpreterError::RuntimeError(
"DataFrame::new() takes no arguments".to_string(),
));
}
let mut builder = std::collections::HashMap::new();
builder.insert(
"__type".to_string(),
Value::from_string("DataFrameBuilder".to_string()),
);
builder.insert("__columns".to_string(), Value::from_array(vec![]));
Ok(Value::Object(std::sync::Arc::new(builder)))
}
fn eval_dataframe_from_csv_string(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("DataFrame::from_csv_string", args, 1)?;
let csv_string = match &args[0] {
Value::String(s) => s.as_ref(),
_ => {
return Err(InterpreterError::RuntimeError(
"DataFrame::from_csv_string() expects string argument".to_string(),
))
}
};
parse_csv_to_dataframe(csv_string)
}
fn eval_dataframe_from_json(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("DataFrame::from_json", args, 1)?;
let json_string = match &args[0] {
Value::String(s) => s.as_ref(),
_ => {
return Err(InterpreterError::RuntimeError(
"DataFrame::from_json() expects string argument".to_string(),
))
}
};
parse_json_to_dataframe(json_string)
}
fn parse_csv_to_dataframe(csv: &str) -> Result<Value, InterpreterError> {
let lines: Vec<&str> = csv.trim().lines().collect();
if lines.is_empty() {
return Ok(Value::DataFrame { columns: vec![] });
}
let headers: Vec<String> = lines[0].split(',').map(|s| s.trim().to_string()).collect();
let mut columns: Vec<crate::runtime::DataFrameColumn> = headers
.iter()
.map(|name| crate::runtime::DataFrameColumn {
name: name.clone(),
values: Vec::new(),
})
.collect();
for line in lines.iter().skip(1) {
let values: Vec<&str> = line.split(',').map(str::trim).collect();
for (col_idx, value_str) in values.iter().enumerate() {
if col_idx < columns.len() {
let value = infer_value_type(value_str);
columns[col_idx].values.push(value);
}
}
}
Ok(Value::DataFrame { columns })
}
fn parse_json_to_dataframe(json_str: &str) -> Result<Value, InterpreterError> {
let json_str = json_str.trim();
if json_str == "[]" {
return Ok(Value::DataFrame { columns: vec![] });
}
validate_json_array_format(json_str)?;
let objects = extract_json_objects(json_str)?;
if objects.is_empty() {
return Ok(Value::DataFrame { columns: vec![] });
}
build_dataframe_from_objects(&objects)
}
fn validate_json_array_format(json_str: &str) -> Result<(), InterpreterError> {
if !json_str.starts_with('[') || !json_str.ends_with(']') {
return Err(InterpreterError::RuntimeError(
"DataFrame::from_json() expects JSON array".to_string(),
));
}
Ok(())
}
fn build_dataframe_from_objects(objects: &[String]) -> Result<Value, InterpreterError> {
use std::collections::HashMap;
let column_names = extract_json_keys(&objects[0])?;
let mut columns_map: HashMap<String, Vec<Value>> = HashMap::new();
for name in &column_names {
columns_map.insert(name.clone(), Vec::new());
}
populate_columns_from_objects(objects, &mut columns_map)?;
convert_to_dataframe_columns(column_names, columns_map)
}
fn populate_columns_from_objects(
objects: &[String],
columns_map: &mut std::collections::HashMap<String, Vec<Value>>,
) -> Result<(), InterpreterError> {
for obj_str in objects {
let key_values = parse_json_object(obj_str)?;
for (key, value) in key_values {
if let Some(col_values) = columns_map.get_mut(&key) {
col_values.push(value);
}
}
}
Ok(())
}
fn convert_to_dataframe_columns(
column_names: Vec<String>,
mut columns_map: std::collections::HashMap<String, Vec<Value>>,
) -> Result<Value, InterpreterError> {
let mut columns = Vec::new();
for name in column_names {
if let Some(values) = columns_map.remove(&name) {
columns.push(crate::runtime::DataFrameColumn { name, values });
}
}
Ok(Value::DataFrame { columns })
}
fn infer_value_type(s: &str) -> Value {
if let Ok(i) = s.parse::<i64>() {
return Value::Integer(i);
}
if let Ok(f) = s.parse::<f64>() {
return Value::Float(f);
}
Value::from_string(s.to_string())
}
fn extract_json_objects(json_str: &str) -> Result<Vec<String>, InterpreterError> {
let inner = &json_str[1..json_str.len() - 1].trim();
if inner.is_empty() {
return Ok(vec![]);
}
let mut objects = Vec::new();
let mut state = JsonParserState::default();
for ch in inner.chars() {
process_json_char(ch, &mut state, &mut objects);
}
Ok(objects)
}
#[derive(Default)]
struct JsonParserState {
current: String,
brace_count: i32,
in_string: bool,
}
fn handle_opening_brace(state: &mut JsonParserState, ch: char) {
state.brace_count += 1;
state.current.push(ch);
}
fn handle_closing_brace(state: &mut JsonParserState, ch: char, objects: &mut Vec<String>) {
state.brace_count -= 1;
state.current.push(ch);
if state.brace_count == 0 {
objects.push(state.current.trim().to_string());
state.current.clear();
}
}
fn handle_json_default_char(ch: char, state: &mut JsonParserState) {
if state.brace_count > 0 {
state.current.push(ch);
}
}
fn process_json_char(ch: char, state: &mut JsonParserState, objects: &mut Vec<String>) {
match ch {
'"' => state.in_string = !state.in_string,
'{' if !state.in_string => handle_opening_brace(state, ch),
'}' if !state.in_string => handle_closing_brace(state, ch, objects),
',' if !state.in_string && state.brace_count == 0 => {}
_ => handle_json_default_char(ch, state),
}
}
fn extract_json_keys(obj_str: &str) -> Result<Vec<String>, InterpreterError> {
let mut keys = Vec::new();
let inner = obj_str.trim().trim_start_matches('{').trim_end_matches('}');
for pair in inner.split(',') {
if let Some(colon_pos) = pair.find(':') {
let key = pair[..colon_pos].trim().trim_matches('"');
keys.push(key.to_string());
}
}
Ok(keys)
}
fn parse_json_value(value_str: &str) -> Value {
if value_str.starts_with('"') {
let unquoted = value_str.trim_matches('"');
Value::from_string(unquoted.to_string())
} else if let Ok(i) = value_str.parse::<i64>() {
Value::Integer(i)
} else if let Ok(f) = value_str.parse::<f64>() {
Value::Float(f)
} else {
Value::from_string(value_str.to_string())
}
}
fn parse_json_object(obj_str: &str) -> Result<Vec<(String, Value)>, InterpreterError> {
let mut pairs = Vec::new();
let inner = obj_str.trim().trim_start_matches('{').trim_end_matches('}');
for pair in inner.split(',') {
if let Some(colon_pos) = pair.find(':') {
let key = pair[..colon_pos].trim().trim_matches('"').to_string();
let value_str = pair[colon_pos + 1..].trim();
let value = parse_json_value(value_str);
pairs.push((key, value));
}
}
Ok(pairs)
}
fn try_eval_env_part1(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_env_args__" => Ok(Some(eval_env_args(args)?)),
"__builtin_env_var__" => Ok(Some(eval_env_var(args)?)),
"__builtin_env_set_var__" => Ok(Some(eval_env_set_var(args)?)),
"__builtin_env_remove_var__" => Ok(Some(eval_env_remove_var(args)?)),
_ => Ok(None),
}
}
fn try_eval_env_part2(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_env_vars__" => Ok(Some(eval_env_vars(args)?)),
"__builtin_env_current_dir__" => Ok(Some(eval_env_current_dir(args)?)),
"__builtin_env_set_current_dir__" => Ok(Some(eval_env_set_current_dir(args)?)),
"__builtin_env_temp_dir__" => Ok(Some(eval_env_temp_dir(args)?)),
_ => Ok(None),
}
}
fn try_eval_environment_function(
name: &str,
args: &[Value],
) -> Result<Option<Value>, InterpreterError> {
if let Some(result) = try_eval_env_part1(name, args)? {
return Ok(Some(result));
}
try_eval_env_part2(name, args)
}
fn eval_env_args(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("env_args", args, 0)?;
let cmd_args: Vec<Value> = std::env::args().map(Value::from_string).collect();
Ok(Value::from_array(cmd_args))
}
fn eval_env_var(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("env_var", args, 1)?;
match &args[0] {
Value::String(key) => {
match std::env::var(key.as_ref()) {
Ok(val) => {
Ok(Value::EnumVariant {
enum_name: "Result".to_string(),
variant_name: "Ok".to_string(),
data: Some(vec![Value::from_string(val)]),
})
}
Err(_) => {
Ok(Value::EnumVariant {
enum_name: "Result".to_string(),
variant_name: "Err".to_string(),
data: Some(vec![Value::from_string("NotFound".to_string())]),
})
}
}
}
_ => Err(InterpreterError::RuntimeError(
"env_var() expects a string argument".to_string(),
)),
}
}
fn eval_env_set_var(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("env_set_var", args, 2)?;
match (&args[0], &args[1]) {
(Value::String(key), Value::String(value)) => {
std::env::set_var(key.as_ref(), value.as_ref());
Ok(Value::Nil)
}
_ => Err(InterpreterError::RuntimeError(
"env_set_var() expects two string arguments".to_string(),
)),
}
}
fn eval_env_remove_var(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("env_remove_var", args, 1)?;
match &args[0] {
Value::String(key) => {
std::env::remove_var(key.as_ref());
Ok(Value::Nil)
}
_ => Err(InterpreterError::RuntimeError(
"env_remove_var() expects a string argument".to_string(),
)),
}
}
fn eval_env_vars(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("env_vars", args, 0)?;
let vars: HashMap<String, Value> = std::env::vars()
.map(|(k, v)| (k, Value::from_string(v)))
.collect();
Ok(Value::Object(Arc::new(vars)))
}
fn eval_env_current_dir(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("env_current_dir", args, 0)?;
match std::env::current_dir() {
Ok(path) => Ok(Value::from_string(path.to_string_lossy().to_string())),
Err(e) => Err(InterpreterError::RuntimeError(format!(
"Failed to get current directory: {e}"
))),
}
}
fn eval_env_set_current_dir(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("env_set_current_dir", args, 1)?;
match &args[0] {
Value::String(path) => match std::env::set_current_dir(path.as_ref()) {
Ok(()) => Ok(Value::Nil),
Err(e) => Err(InterpreterError::RuntimeError(format!(
"Failed to set current directory: {e}"
))),
},
_ => Err(InterpreterError::RuntimeError(
"env_set_current_dir() expects a string argument".to_string(),
)),
}
}
fn eval_env_temp_dir(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("env_temp_dir", args, 0)?;
let temp = std::env::temp_dir();
Ok(Value::from_string(temp.to_string_lossy().to_string()))
}
fn eval_fs_read(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("fs_read", args, 1)?;
match &args[0] {
Value::String(path) => match std::fs::read_to_string(path.as_ref()) {
Ok(content) => Ok(Value::EnumVariant {
enum_name: "Result".to_string(),
variant_name: "Ok".to_string(),
data: Some(vec![Value::from_string(content)]),
}),
Err(e) => Ok(Value::EnumVariant {
enum_name: "Result".to_string(),
variant_name: "Err".to_string(),
data: Some(vec![Value::from_string(e.to_string())]),
}),
},
_ => Err(InterpreterError::RuntimeError(
"fs_read() expects a string argument".to_string(),
)),
}
}
fn eval_read_file_unwrapped(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("read_file", args, 1)?;
match &args[0] {
Value::String(path) => std::fs::read_to_string(path.as_ref())
.map(Value::from_string)
.map_err(|e| {
InterpreterError::RuntimeError(format!("Failed to read file '{path}': {e}"))
}),
_ => Err(InterpreterError::RuntimeError(
"read_file() expects a string argument".to_string(),
)),
}
}
fn eval_fs_write(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("fs_write", args, 2)?;
match (&args[0], &args[1]) {
(Value::String(path), Value::String(content)) => {
match std::fs::write(path.as_ref(), content.as_ref()) {
Ok(()) => Ok(Value::EnumVariant {
enum_name: "Result".to_string(),
variant_name: "Ok".to_string(),
data: Some(vec![Value::Nil]),
}),
Err(e) => Ok(Value::EnumVariant {
enum_name: "Result".to_string(),
variant_name: "Err".to_string(),
data: Some(vec![Value::from_string(e.to_string())]),
}),
}
}
_ => Err(InterpreterError::RuntimeError(
"fs_write() expects two string arguments".to_string(),
)),
}
}
fn eval_append_file(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("append_file", args, 2)?;
match (&args[0], &args[1]) {
(Value::String(path), Value::String(content)) => {
use std::fs::OpenOptions;
use std::io::Write;
match OpenOptions::new()
.create(true)
.append(true)
.open(path.as_ref())
{
Ok(mut file) => match file.write_all(content.as_bytes()) {
Ok(()) => Ok(Value::Nil),
Err(e) => Err(InterpreterError::RuntimeError(format!(
"Failed to append to file: {e}"
))),
},
Err(e) => Err(InterpreterError::RuntimeError(format!(
"Failed to open file for append: {e}"
))),
}
}
_ => Err(InterpreterError::RuntimeError(
"append_file() expects two string arguments".to_string(),
)),
}
}
fn eval_fs_exists(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("fs_exists", args, 1)?;
match &args[0] {
Value::String(path) => Ok(Value::Bool(std::path::Path::new(path.as_ref()).exists())),
_ => Err(InterpreterError::RuntimeError(
"fs_exists() expects a string argument".to_string(),
)),
}
}
fn eval_fs_create_dir(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("fs_create_dir", args, 1)?;
match &args[0] {
Value::String(path) => match std::fs::create_dir_all(path.as_ref()) {
Ok(()) => Ok(Value::EnumVariant {
enum_name: "Result".to_string(),
variant_name: "Ok".to_string(),
data: Some(vec![Value::Nil]),
}),
Err(e) => Ok(Value::EnumVariant {
enum_name: "Result".to_string(),
variant_name: "Err".to_string(),
data: Some(vec![Value::from_string(e.to_string())]),
}),
},
_ => Err(InterpreterError::RuntimeError(
"fs_create_dir() expects a string argument".to_string(),
)),
}
}
fn eval_fs_remove_file(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("fs_remove_file", args, 1)?;
match &args[0] {
Value::String(path) => match std::fs::remove_file(path.as_ref()) {
Ok(()) => Ok(Value::EnumVariant {
enum_name: "Result".to_string(),
variant_name: "Ok".to_string(),
data: Some(vec![Value::Nil]),
}),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(Value::EnumVariant {
enum_name: "Result".to_string(),
variant_name: "Ok".to_string(),
data: Some(vec![Value::Nil]),
}), Err(e) => Ok(Value::EnumVariant {
enum_name: "Result".to_string(),
variant_name: "Err".to_string(),
data: Some(vec![Value::from_string(e.to_string())]),
}),
},
_ => Err(InterpreterError::RuntimeError(
"fs_remove_file() expects a string argument".to_string(),
)),
}
}
fn eval_fs_remove_dir(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("fs_remove_dir", args, 1)?;
match &args[0] {
Value::String(path) => match std::fs::remove_dir(path.as_ref()) {
Ok(()) => Ok(Value::EnumVariant {
enum_name: "Result".to_string(),
variant_name: "Ok".to_string(),
data: Some(vec![Value::Nil]),
}),
Err(e) => Ok(Value::EnumVariant {
enum_name: "Result".to_string(),
variant_name: "Err".to_string(),
data: Some(vec![Value::from_string(e.to_string())]),
}),
},
_ => Err(InterpreterError::RuntimeError(
"fs_remove_dir() expects a string argument".to_string(),
)),
}
}
fn eval_walk(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("walk", args, 1)?;
match &args[0] {
Value::String(path) => {
use std::collections::HashMap;
use walkdir::WalkDir;
let entries: Vec<Value> = WalkDir::new(path.as_ref())
.into_iter()
.filter_map(std::result::Result::ok)
.map(|entry| {
let mut fields = HashMap::new();
fields.insert(
"path".to_string(),
Value::String(entry.path().display().to_string().into()),
);
fields.insert(
"name".to_string(),
Value::String(entry.file_name().to_string_lossy().to_string().into()),
);
fields.insert(
"is_file".to_string(),
Value::from_bool(entry.file_type().is_file()),
);
fields.insert(
"is_dir".to_string(),
Value::from_bool(entry.file_type().is_dir()),
);
fields.insert(
"is_symlink".to_string(),
Value::from_bool(entry.file_type().is_symlink()),
);
let size = entry.metadata().map(|m| m.len() as i64).unwrap_or(0);
fields.insert("size".to_string(), Value::Integer(size));
fields.insert("depth".to_string(), Value::Integer(entry.depth() as i64));
Value::Object(Arc::new(fields))
})
.collect();
Ok(Value::Array(entries.into()))
}
_ => Err(InterpreterError::RuntimeError(
"walk() expects a string path".to_string(),
)),
}
}
fn eval_glob(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("glob", args, 1)?;
match &args[0] {
Value::String(pattern) => {
use glob::glob;
match glob(pattern.as_ref()) {
Ok(paths) => {
let results: Vec<Value> = paths
.filter_map(std::result::Result::ok)
.map(|path| Value::String(path.display().to_string().into()))
.collect();
Ok(Value::Array(results.into()))
}
Err(e) => Err(InterpreterError::RuntimeError(format!(
"glob() pattern error: {e}"
))),
}
}
_ => Err(InterpreterError::RuntimeError(
"glob() expects a string pattern".to_string(),
)),
}
}
fn eval_search(args: &[Value]) -> Result<Value, InterpreterError> {
if args.len() < 2 || args.len() > 3 {
return Err(InterpreterError::RuntimeError(
"search() expects 2-3 arguments: (pattern, path, options?)".to_string(),
));
}
match (&args[0], &args[1]) {
(Value::String(pattern), Value::String(path)) => {
use regex::RegexBuilder;
use walkdir::WalkDir;
let case_insensitive = if args.len() == 3 {
if let Value::Object(opts) = &args[2] {
opts.get("case_insensitive")
.and_then(|v| match v {
Value::Bool(b) => Some(*b),
_ => None,
})
.unwrap_or(false)
} else {
false
}
} else {
false
};
let re = RegexBuilder::new(pattern.as_ref())
.case_insensitive(case_insensitive)
.build()
.map_err(|e| {
InterpreterError::RuntimeError(format!("search() regex error: {e}"))
})?;
let mut results = Vec::new();
for entry in WalkDir::new(path.as_ref())
.into_iter()
.filter_map(std::result::Result::ok)
.filter(|e| e.file_type().is_file())
{
if let Ok(contents) = std::fs::read_to_string(entry.path()) {
for (line_num, line) in contents.lines().enumerate() {
if re.is_match(line) {
let mut fields = HashMap::new();
fields.insert(
"path".to_string(),
Value::String(entry.path().display().to_string().into()),
);
fields.insert(
"line_num".to_string(),
Value::Integer(
(line_num + 1) as i64, ),
);
fields
.insert("line".to_string(), Value::String(line.to_string().into()));
results.push(Value::Object(Arc::new(fields)));
}
}
}
}
Ok(Value::Array(results.into()))
}
_ => Err(InterpreterError::RuntimeError(
"search() expects (string pattern, string path, object? options)".to_string(),
)),
}
}
fn eval_walk_with_options(args: &[Value]) -> Result<Value, InterpreterError> {
if args.len() != 2 {
return Err(InterpreterError::RuntimeError(
"walk_with_options() expects 2 arguments: (path, options)".to_string(),
));
}
match (&args[0], &args[1]) {
(Value::String(path), Value::Object(opts)) => {
use walkdir::WalkDir;
let mut walker = WalkDir::new(path.as_ref());
if let Some(Value::Integer(max)) = opts.get("max_depth") {
walker = walker.max_depth(*max as usize);
}
if let Some(Value::Integer(min)) = opts.get("min_depth") {
walker = walker.min_depth(*min as usize);
}
if let Some(Value::Bool(follow)) = opts.get("follow_links") {
walker = walker.follow_links(*follow);
}
let mut results = Vec::new();
for entry in walker.into_iter().filter_map(std::result::Result::ok) {
let mut fields = HashMap::new();
fields.insert(
"path".to_string(),
Value::String(entry.path().display().to_string().into()),
);
if let Some(name) = entry.file_name().to_str() {
fields.insert("name".to_string(), Value::String(name.to_string().into()));
}
let file_type = entry.file_type();
fields.insert("is_file".to_string(), Value::Bool(file_type.is_file()));
fields.insert("is_dir".to_string(), Value::Bool(file_type.is_dir()));
fields.insert(
"is_symlink".to_string(),
Value::Bool(file_type.is_symlink()),
);
let size = if file_type.is_file() {
entry.metadata().ok().map_or(0, |m| m.len())
} else {
0
};
fields.insert("size".to_string(), Value::Integer(size as i64));
fields.insert("depth".to_string(), Value::Integer(entry.depth() as i64));
results.push(Value::Object(Arc::new(fields)));
}
Ok(Value::Array(results.into()))
}
_ => Err(InterpreterError::RuntimeError(
"walk_with_options() expects (string path, object options)".to_string(),
)),
}
}
fn eval_walk_parallel(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("walk_parallel", args, 1)?;
match &args[0] {
Value::String(path) => {
use rayon::prelude::*;
use walkdir::WalkDir;
let entries: Vec<_> = WalkDir::new(path.as_ref())
.into_iter()
.filter_map(std::result::Result::ok)
.par_bridge() .map(|entry: walkdir::DirEntry| {
let path_str = entry.path().display().to_string();
let name_str = entry.file_name().to_string_lossy().to_string();
let file_type = entry.file_type();
let is_file = file_type.is_file();
let is_dir = file_type.is_dir();
let is_symlink = file_type.is_symlink();
let size = entry
.metadata()
.ok()
.map_or(0, |m: std::fs::Metadata| m.len());
let depth = entry.depth();
(path_str, name_str, is_file, is_dir, is_symlink, size, depth)
})
.collect();
let results: Vec<Value> = entries
.into_iter()
.map(
|(path_str, name_str, is_file, is_dir, is_symlink, size, depth)| {
let mut fields = HashMap::new();
fields.insert("path".to_string(), Value::String(path_str.into()));
fields.insert("name".to_string(), Value::String(name_str.into()));
fields.insert("is_file".to_string(), Value::Bool(is_file));
fields.insert("is_dir".to_string(), Value::Bool(is_dir));
fields.insert("is_symlink".to_string(), Value::Bool(is_symlink));
fields.insert("size".to_string(), Value::Integer(size as i64));
fields.insert("depth".to_string(), Value::Integer(depth as i64));
Value::Object(Arc::new(fields))
},
)
.collect();
Ok(Value::Array(results.into()))
}
_ => Err(InterpreterError::RuntimeError(
"walk_parallel() expects a string path argument".to_string(),
)),
}
}
fn eval_compute_hash(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("compute_hash", args, 1)?;
match &args[0] {
Value::String(path) => {
let content = std::fs::read(path.as_ref()).map_err(|e| {
InterpreterError::RuntimeError(format!("Failed to read file '{path}': {e}"))
})?;
let digest = md5::compute(&content);
let hash_string = format!("{digest:x}");
Ok(Value::String(hash_string.into()))
}
_ => Err(InterpreterError::RuntimeError(
"compute_hash() expects a string path argument".to_string(),
)),
}
}
fn eval_fs_copy(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("fs_copy", args, 2)?;
match (&args[0], &args[1]) {
(Value::String(from), Value::String(to)) => match std::fs::copy(from.as_ref(), to.as_ref())
{
Ok(_) => Ok(Value::Nil),
Err(e) => Err(InterpreterError::RuntimeError(format!(
"Failed to copy file: {e}"
))),
},
_ => Err(InterpreterError::RuntimeError(
"fs_copy() expects two string arguments".to_string(),
)),
}
}
fn eval_fs_rename(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("fs_rename", args, 2)?;
match (&args[0], &args[1]) {
(Value::String(from), Value::String(to)) => {
match std::fs::rename(from.as_ref(), to.as_ref()) {
Ok(()) => Ok(Value::Nil),
Err(e) => Err(InterpreterError::RuntimeError(format!(
"Failed to rename file: {e}"
))),
}
}
_ => Err(InterpreterError::RuntimeError(
"fs_rename() expects two string arguments".to_string(),
)),
}
}
fn eval_fs_metadata(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("fs_metadata", args, 1)?;
match &args[0] {
Value::String(path) => match std::fs::metadata(path.as_ref()) {
Ok(meta) => {
let mut map = HashMap::new();
map.insert("size".to_string(), Value::Integer(meta.len() as i64));
map.insert("is_dir".to_string(), Value::Bool(meta.is_dir()));
map.insert("is_file".to_string(), Value::Bool(meta.is_file()));
Ok(Value::Object(Arc::new(map)))
}
Err(e) => Err(InterpreterError::RuntimeError(format!(
"Failed to get metadata: {e}"
))),
},
_ => Err(InterpreterError::RuntimeError(
"fs_metadata() expects a string argument".to_string(),
)),
}
}
fn eval_fs_read_dir(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("fs_read_dir", args, 1)?;
match &args[0] {
Value::String(path) => match std::fs::read_dir(path.as_ref()) {
Ok(entries) => {
let paths: Vec<Value> = entries
.filter_map(std::result::Result::ok)
.map(|e| Value::from_string(e.path().display().to_string()))
.collect();
Ok(Value::Array(paths.into()))
}
Err(e) => Err(InterpreterError::RuntimeError(format!(
"Failed to read directory: {e}"
))),
},
_ => Err(InterpreterError::RuntimeError(
"fs_read_dir() expects a string argument".to_string(),
)),
}
}
fn eval_fs_canonicalize(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("fs_canonicalize", args, 1)?;
match &args[0] {
Value::String(path) => match std::fs::canonicalize(path.as_ref()) {
Ok(canonical) => Ok(Value::from_string(canonical.display().to_string())),
Err(e) => Err(InterpreterError::RuntimeError(format!(
"Failed to canonicalize path: {e}"
))),
},
_ => Err(InterpreterError::RuntimeError(
"fs_canonicalize() expects a string argument".to_string(),
)),
}
}
fn eval_fs_is_file(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("fs_is_file", args, 1)?;
match &args[0] {
Value::String(path) => Ok(Value::Bool(std::path::Path::new(path.as_ref()).is_file())),
_ => Err(InterpreterError::RuntimeError(
"fs_is_file() expects a string argument".to_string(),
)),
}
}
fn try_eval_fs_part1(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_fs_read__" => Ok(Some(eval_fs_read(args)?)),
"__builtin_fs_write__" => Ok(Some(eval_fs_write(args)?)),
"__builtin_fs_exists__" => Ok(Some(eval_fs_exists(args)?)),
"__builtin_fs_create_dir__" => Ok(Some(eval_fs_create_dir(args)?)),
_ => Ok(None),
}
}
fn try_eval_fs_part2(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_fs_remove_file__" => Ok(Some(eval_fs_remove_file(args)?)),
"__builtin_fs_remove_dir__" => Ok(Some(eval_fs_remove_dir(args)?)),
"__builtin_fs_copy__" => Ok(Some(eval_fs_copy(args)?)),
"__builtin_fs_rename__" => Ok(Some(eval_fs_rename(args)?)),
_ => Ok(None),
}
}
fn try_eval_fs_part3(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_fs_metadata__" => Ok(Some(eval_fs_metadata(args)?)),
"__builtin_fs_read_dir__" => Ok(Some(eval_fs_read_dir(args)?)),
"__builtin_fs_canonicalize__" => Ok(Some(eval_fs_canonicalize(args)?)),
"__builtin_fs_is_file__" => Ok(Some(eval_fs_is_file(args)?)),
_ => Ok(None),
}
}
fn try_eval_stdlib003(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_read_file__" | "read_file" => Ok(Some(eval_read_file_unwrapped(args)?)),
"__builtin_write_file__" | "write_file" => Ok(Some(eval_fs_write(args)?)),
"__builtin_file_exists__" | "file_exists" => Ok(Some(eval_fs_exists(args)?)),
"__builtin_delete_file__" | "delete_file" => Ok(Some(eval_fs_remove_file(args)?)),
"__builtin_append_file__" | "append_file" => Ok(Some(eval_append_file(args)?)),
_ => Ok(None),
}
}
fn try_eval_stdlib005(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_walk__" => Ok(Some(eval_walk(args)?)),
"__builtin_glob__" => Ok(Some(eval_glob(args)?)),
"__builtin_search__" => Ok(Some(eval_search(args)?)),
"__builtin_walk_with_options__" => Ok(Some(eval_walk_with_options(args)?)),
"__builtin_walk_parallel__" => Ok(Some(eval_walk_parallel(args)?)),
"__builtin_compute_hash__" => Ok(Some(eval_compute_hash(args)?)),
_ => Ok(None),
}
}
fn try_eval_fs_function(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
if let Some(result) = try_eval_fs_part1(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_fs_part2(name, args)? {
return Ok(Some(result));
}
if let Some(result) = try_eval_fs_part3(name, args)? {
return Ok(Some(result));
}
try_eval_stdlib003(name, args) }
fn eval_path_join(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("path_join", args, 2)?;
match (&args[0], &args[1]) {
(Value::String(base), Value::String(component)) => {
let path = std::path::Path::new(base.as_ref()).join(component.as_ref());
Ok(Value::from_string(path.to_string_lossy().to_string()))
}
_ => Err(InterpreterError::RuntimeError(
"path_join() expects two string arguments".to_string(),
)),
}
}
fn build_path_from_value_components(
components: &[Value],
) -> Result<std::path::PathBuf, InterpreterError> {
let mut path = std::path::PathBuf::new();
for component in components {
match component {
Value::String(s) => path.push(s.as_ref()),
_ => {
return Err(InterpreterError::RuntimeError(
"path_join_many() expects array of strings".to_string(),
))
}
}
}
Ok(path)
}
fn eval_path_join_many(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("path_join_many", args, 1)?;
match &args[0] {
Value::Array(components) => {
let path = build_path_from_value_components(components)?;
Ok(Value::from_string(path.to_string_lossy().to_string()))
}
_ => Err(InterpreterError::RuntimeError(
"path_join_many() expects an array argument".to_string(),
)),
}
}
fn eval_path_parent(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("path_parent", args, 1)?;
match &args[0] {
Value::String(path) => {
let p = std::path::Path::new(path.as_ref());
match p.parent() {
Some(parent) => Ok(Value::from_string(parent.to_string_lossy().to_string())),
None => Ok(Value::Nil),
}
}
_ => Err(InterpreterError::RuntimeError(
"path_parent() expects a string argument".to_string(),
)),
}
}
fn eval_path_file_name(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("path_file_name", args, 1)?;
match &args[0] {
Value::String(path) => {
let p = std::path::Path::new(path.as_ref());
match p.file_name() {
Some(name) => Ok(Value::from_string(name.to_string_lossy().to_string())),
None => Ok(Value::Nil),
}
}
_ => Err(InterpreterError::RuntimeError(
"path_file_name() expects a string argument".to_string(),
)),
}
}
fn eval_path_file_stem(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("path_file_stem", args, 1)?;
match &args[0] {
Value::String(path) => {
let p = std::path::Path::new(path.as_ref());
match p.file_stem() {
Some(stem) => Ok(Value::from_string(stem.to_string_lossy().to_string())),
None => Ok(Value::Nil),
}
}
_ => Err(InterpreterError::RuntimeError(
"path_file_stem() expects a string argument".to_string(),
)),
}
}
fn eval_path_extension(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("path_extension", args, 1)?;
match &args[0] {
Value::String(path) => {
let p = std::path::Path::new(path.as_ref());
match p.extension() {
Some(ext) => Ok(Value::from_string(ext.to_string_lossy().to_string())),
None => Ok(Value::Nil),
}
}
_ => Err(InterpreterError::RuntimeError(
"path_extension() expects a string argument".to_string(),
)),
}
}
fn eval_path_is_absolute(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("path_is_absolute", args, 1)?;
match &args[0] {
Value::String(path) => Ok(Value::Bool(
std::path::Path::new(path.as_ref()).is_absolute(),
)),
_ => Err(InterpreterError::RuntimeError(
"path_is_absolute() expects a string argument".to_string(),
)),
}
}
fn eval_path_is_relative(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("path_is_relative", args, 1)?;
match &args[0] {
Value::String(path) => Ok(Value::Bool(
std::path::Path::new(path.as_ref()).is_relative(),
)),
_ => Err(InterpreterError::RuntimeError(
"path_is_relative() expects a string argument".to_string(),
)),
}
}
fn eval_path_canonicalize(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("path_canonicalize", args, 1)?;
match &args[0] {
Value::String(path) => match std::fs::canonicalize(path.as_ref()) {
Ok(canonical) => Ok(Value::from_string(canonical.to_string_lossy().to_string())),
Err(e) => Err(InterpreterError::RuntimeError(format!(
"Failed to canonicalize path: {e}"
))),
},
_ => Err(InterpreterError::RuntimeError(
"path_canonicalize() expects a string argument".to_string(),
)),
}
}
fn eval_path_with_extension(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("path_with_extension", args, 2)?;
match (&args[0], &args[1]) {
(Value::String(path), Value::String(ext)) => {
let p = std::path::Path::new(path.as_ref()).with_extension(ext.as_ref());
Ok(Value::from_string(p.to_string_lossy().to_string()))
}
_ => Err(InterpreterError::RuntimeError(
"path_with_extension() expects two string arguments".to_string(),
)),
}
}
fn eval_path_with_file_name(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("path_with_file_name", args, 2)?;
match (&args[0], &args[1]) {
(Value::String(path), Value::String(name)) => {
let p = std::path::Path::new(path.as_ref()).with_file_name(name.as_ref());
Ok(Value::from_string(p.to_string_lossy().to_string()))
}
_ => Err(InterpreterError::RuntimeError(
"path_with_file_name() expects two string arguments".to_string(),
)),
}
}
fn eval_path_components(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("path_components", args, 1)?;
match &args[0] {
Value::String(path) => {
let p = std::path::Path::new(path.as_ref());
let components: Vec<Value> = p
.components()
.map(|c| Value::from_string(c.as_os_str().to_string_lossy().to_string()))
.collect();
Ok(Value::Array(components.into()))
}
_ => Err(InterpreterError::RuntimeError(
"path_components() expects a string argument".to_string(),
)),
}
}
fn eval_path_normalize(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("path_normalize", args, 1)?;
match &args[0] {
Value::String(path) => {
let p = std::path::Path::new(path.as_ref());
let mut normalized = std::path::PathBuf::new();
for component in p.components() {
match component {
std::path::Component::CurDir => {}
std::path::Component::ParentDir => {
normalized.pop();
}
_ => normalized.push(component),
}
}
Ok(Value::from_string(normalized.to_string_lossy().to_string()))
}
_ => Err(InterpreterError::RuntimeError(
"path_normalize() expects a string argument".to_string(),
)),
}
}
fn try_eval_path_part1(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_path_join__" => Ok(Some(eval_path_join(args)?)),
"__builtin_path_join_many__" => Ok(Some(eval_path_join_many(args)?)),
"__builtin_path_parent__" => Ok(Some(eval_path_parent(args)?)),
"__builtin_path_file_name__" => Ok(Some(eval_path_file_name(args)?)),
_ => Ok(None),
}
}
fn try_eval_path_part2(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_path_file_stem__" => Ok(Some(eval_path_file_stem(args)?)),
"__builtin_path_extension__" => Ok(Some(eval_path_extension(args)?)),
"__builtin_path_is_absolute__" => Ok(Some(eval_path_is_absolute(args)?)),
"__builtin_path_is_relative__" => Ok(Some(eval_path_is_relative(args)?)),
_ => Ok(None),
}
}
fn try_eval_path_part3a(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_path_canonicalize__" => Ok(Some(eval_path_canonicalize(args)?)),
"__builtin_path_with_extension__" => Ok(Some(eval_path_with_extension(args)?)),
"__builtin_path_with_file_name__" => Ok(Some(eval_path_with_file_name(args)?)),
_ => Ok(None),
}
}
fn try_eval_path_part3b(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_path_components__" => Ok(Some(eval_path_components(args)?)),
"__builtin_path_normalize__" => Ok(Some(eval_path_normalize(args)?)),
_ => Ok(None),
}
}
fn try_eval_path_function(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
let dispatchers: &[fn(&str, &[Value]) -> Result<Option<Value>, InterpreterError>] = &[
try_eval_path_part1,
try_eval_path_part2,
try_eval_path_part3a,
try_eval_path_part3b,
];
for dispatcher in dispatchers {
if let Some(result) = dispatcher(name, args)? {
return Ok(Some(result));
}
}
Ok(None)
}
fn eval_json_parse(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("json_parse", args, 1)?;
match &args[0] {
Value::String(s) => parse_json_string_to_value(s),
_ => Err(InterpreterError::RuntimeError(
"json_parse() expects a string argument".to_string(),
)),
}
}
fn parse_json_string_to_value(s: &str) -> Result<Value, InterpreterError> {
match serde_json::from_str::<serde_json::Value>(s) {
Ok(json) => Ok(json_to_ruchy_value(json)),
Err(e) => Err(InterpreterError::RuntimeError(format!(
"JSON parse error: {e}"
))),
}
}
fn json_to_ruchy_value(json: serde_json::Value) -> Value {
match json {
serde_json::Value::Null => Value::Nil,
serde_json::Value::Bool(b) => Value::Bool(b),
serde_json::Value::Number(n) => convert_json_number(n),
serde_json::Value::String(s) => Value::from_string(s),
serde_json::Value::Array(arr) => convert_json_array(arr),
serde_json::Value::Object(obj) => convert_json_object(obj),
}
}
fn convert_json_number(n: serde_json::Number) -> Value {
if let Some(i) = n.as_i64() {
Value::Integer(i)
} else if let Some(f) = n.as_f64() {
Value::Float(f)
} else {
Value::Nil
}
}
fn convert_json_array(arr: Vec<serde_json::Value>) -> Value {
let values: Vec<Value> = arr.into_iter().map(json_to_ruchy_value).collect();
Value::Array(values.into())
}
fn convert_json_object(obj: serde_json::Map<String, serde_json::Value>) -> Value {
let mut map = std::collections::HashMap::new();
for (k, v) in obj {
map.insert(k, json_to_ruchy_value(v));
}
Value::Object(std::sync::Arc::new(map))
}
fn value_to_json(value: &Value) -> Result<serde_json::Value, InterpreterError> {
match value {
Value::Nil => Ok(serde_json::Value::Null),
Value::Bool(b) => Ok(serde_json::Value::Bool(*b)),
Value::Integer(i) => Ok(serde_json::json!(*i)),
Value::Float(f) => Ok(serde_json::json!(*f)),
Value::String(s) => Ok(serde_json::Value::String(s.to_string())),
Value::Array(arr) => convert_ruchy_array_to_json(arr),
Value::Object(map) => convert_ruchy_object_to_json(map),
_ => Err(InterpreterError::RuntimeError(format!(
"Cannot convert {value:?} to JSON"
))),
}
}
fn convert_ruchy_array_to_json(arr: &[Value]) -> Result<serde_json::Value, InterpreterError> {
let json_arr: Result<Vec<serde_json::Value>, _> = arr.iter().map(value_to_json).collect();
Ok(serde_json::Value::Array(json_arr?))
}
fn convert_ruchy_object_to_json(
map: &std::collections::HashMap<String, Value>,
) -> Result<serde_json::Value, InterpreterError> {
let mut json_obj = serde_json::Map::new();
for (k, v) in map {
json_obj.insert(k.clone(), value_to_json(v)?);
}
Ok(serde_json::Value::Object(json_obj))
}
fn eval_json_stringify(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("json_stringify", args, 1)?;
let json = value_to_json(&args[0])?;
match serde_json::to_string(&json) {
Ok(s) => Ok(Value::from_string(s)),
Err(e) => Err(InterpreterError::RuntimeError(format!(
"JSON stringify error: {e}"
))),
}
}
fn eval_json_pretty(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("json_pretty", args, 1)?;
let json = value_to_json(&args[0])?;
match serde_json::to_string_pretty(&json) {
Ok(s) => Ok(Value::from_string(s)),
Err(e) => Err(InterpreterError::RuntimeError(format!(
"JSON pretty error: {e}"
))),
}
}
fn eval_json_read(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("json_read", args, 1)?;
match &args[0] {
Value::String(path) => {
let content = std::fs::read_to_string(path.as_ref())
.map_err(|e| InterpreterError::RuntimeError(format!("Failed to read file: {e}")))?;
eval_json_parse(&[Value::from_string(content)])
}
_ => Err(InterpreterError::RuntimeError(
"json_read() expects a string argument".to_string(),
)),
}
}
fn eval_json_write(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("json_write", args, 2)?;
match &args[0] {
Value::String(path) => {
let json = value_to_json(&args[1])?;
let content = serde_json::to_string_pretty(&json).map_err(|e| {
InterpreterError::RuntimeError(format!("JSON stringify error: {e}"))
})?;
std::fs::write(path.as_ref(), content).map_err(|e| {
InterpreterError::RuntimeError(format!("Failed to write file: {e}"))
})?;
Ok(Value::Bool(true))
}
_ => Err(InterpreterError::RuntimeError(
"json_write() expects first argument to be string".to_string(),
)),
}
}
fn eval_json_validate(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("json_validate", args, 1)?;
match &args[0] {
Value::String(s) => {
let is_valid = serde_json::from_str::<serde_json::Value>(s).is_ok();
Ok(Value::Bool(is_valid))
}
_ => Err(InterpreterError::RuntimeError(
"json_validate() expects a string argument".to_string(),
)),
}
}
fn eval_json_type(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("json_type", args, 1)?;
match &args[0] {
Value::String(s) => match serde_json::from_str::<serde_json::Value>(s) {
Ok(json) => {
let type_str = match json {
serde_json::Value::Null => "null",
serde_json::Value::Bool(_) => "boolean",
serde_json::Value::Number(_) => "number",
serde_json::Value::String(_) => "string",
serde_json::Value::Array(_) => "array",
serde_json::Value::Object(_) => "object",
};
Ok(Value::from_string(type_str.to_string()))
}
Err(e) => Err(InterpreterError::RuntimeError(format!(
"JSON parse error: {e}"
))),
},
_ => Err(InterpreterError::RuntimeError(
"json_type() expects a string argument".to_string(),
)),
}
}
fn eval_json_merge(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("json_merge", args, 2)?;
let json1 = value_to_json(&args[0])?;
let json2 = value_to_json(&args[1])?;
let merged = merge_json_values(json1, json2);
eval_json_parse(&[Value::from_string(merged.to_string())])
}
fn merge_json_values(a: serde_json::Value, b: serde_json::Value) -> serde_json::Value {
match (a, b) {
(serde_json::Value::Object(mut a_map), serde_json::Value::Object(b_map)) => {
merge_json_objects(&mut a_map, b_map);
serde_json::Value::Object(a_map)
}
(_, b_val) => b_val,
}
}
fn merge_json_objects(
a_map: &mut serde_json::Map<String, serde_json::Value>,
b_map: serde_json::Map<String, serde_json::Value>,
) {
for (k, v) in b_map {
if let Some(a_val) = a_map.get_mut(&k) {
*a_val = merge_json_values(a_val.clone(), v);
} else {
a_map.insert(k, v);
}
}
}
fn eval_json_get(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("json_get", args, 2)?;
let json = value_to_json(&args[0])?;
match &args[1] {
Value::String(path) => get_json_value_at_path(&json, path),
_ => Err(InterpreterError::RuntimeError(
"json_get() expects second argument to be string".to_string(),
)),
}
}
fn get_json_value_at_path(json: &serde_json::Value, path: &str) -> Result<Value, InterpreterError> {
let parts: Vec<&str> = path.split('.').collect();
match get_json_path_recursive(json, &parts) {
Some(val) => eval_json_parse(&[Value::from_string(val.to_string())]),
None => Ok(Value::Nil),
}
}
fn get_json_path_recursive<'a>(
json: &'a serde_json::Value,
path: &[&str],
) -> Option<&'a serde_json::Value> {
if path.is_empty() {
return Some(json);
}
match json {
serde_json::Value::Object(map) => map
.get(path[0])
.and_then(|v| get_json_path_recursive(v, &path[1..])),
_ => None,
}
}
fn eval_json_set(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("json_set", args, 3)?;
let mut json = value_to_json(&args[0])?;
let new_value = value_to_json(&args[2])?;
match &args[1] {
Value::String(path) => {
set_json_path_from_string(&mut json, path, new_value)?;
eval_json_parse(&[Value::from_string(json.to_string())])
}
_ => Err(InterpreterError::RuntimeError(
"json_set() expects second argument to be string".to_string(),
)),
}
}
fn set_json_path_from_string(
json: &mut serde_json::Value,
path: &str,
value: serde_json::Value,
) -> Result<(), InterpreterError> {
let parts: Vec<&str> = path.split('.').collect();
set_json_path_recursive(json, &parts, value);
Ok(())
}
fn set_json_path_recursive(json: &mut serde_json::Value, path: &[&str], value: serde_json::Value) {
if path.is_empty() {
*json = value;
return;
}
if path.len() == 1 {
set_json_single_key(json, path[0], value);
} else {
set_json_nested_path(json, path, value);
}
}
fn set_json_single_key(json: &mut serde_json::Value, key: &str, value: serde_json::Value) {
if let serde_json::Value::Object(map) = json {
map.insert(key.to_string(), value);
}
}
fn set_json_nested_path(json: &mut serde_json::Value, path: &[&str], value: serde_json::Value) {
if let serde_json::Value::Object(map) = json {
if let Some(next) = map.get_mut(path[0]) {
set_json_path_recursive(next, &path[1..], value);
}
}
}
fn try_eval_json_part1a(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_json_parse__" | "JSON_parse" | "parse_json" => Ok(Some(eval_json_parse(args)?)),
"__builtin_json_stringify__" | "JSON_stringify" | "stringify_json" => {
Ok(Some(eval_json_stringify(args)?))
}
"__builtin_json_pretty__" => Ok(Some(eval_json_pretty(args)?)),
_ => Ok(None),
}
}
fn try_eval_json_part1b(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_json_read__" => Ok(Some(eval_json_read(args)?)),
"__builtin_json_write__" => Ok(Some(eval_json_write(args)?)),
_ => Ok(None),
}
}
fn try_eval_json_part1(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
if let Some(result) = try_eval_json_part1a(name, args)? {
return Ok(Some(result));
}
try_eval_json_part1b(name, args)
}
fn try_eval_json_part2a(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_json_validate__" => Ok(Some(eval_json_validate(args)?)),
"__builtin_json_type__" => Ok(Some(eval_json_type(args)?)),
"__builtin_json_merge__" => Ok(Some(eval_json_merge(args)?)),
_ => Ok(None),
}
}
fn try_eval_json_part2b(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_json_get__" => Ok(Some(eval_json_get(args)?)),
"__builtin_json_set__" => Ok(Some(eval_json_set(args)?)),
_ => Ok(None),
}
}
fn try_eval_json_part2(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
if let Some(result) = try_eval_json_part2a(name, args)? {
return Ok(Some(result));
}
try_eval_json_part2b(name, args)
}
fn try_eval_json_function(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
let dispatchers: &[fn(&str, &[Value]) -> Result<Option<Value>, InterpreterError>] =
&[try_eval_json_part1, try_eval_json_part2];
for dispatcher in dispatchers {
if let Some(result) = dispatcher(name, args)? {
return Ok(Some(result));
}
}
Ok(None)
}
fn try_eval_file_function(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"File_open" => Ok(Some(eval_file_open(args)?)),
"__builtin_open__" => Ok(Some(eval_open(args)?)),
_ => Ok(None),
}
}
fn eval_file_open(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("File.open", args, 1)?;
match &args[0] {
Value::String(path) => {
let content = std::fs::read_to_string(path.as_ref()).map_err(|e| {
InterpreterError::RuntimeError(format!("Failed to open file '{path}': {e}"))
})?;
let lines: Vec<String> = content
.lines()
.map(std::string::ToString::to_string)
.collect();
let mut file_obj = std::collections::HashMap::new();
file_obj.insert("__type".to_string(), Value::from_string("File".to_string()));
file_obj.insert("path".to_string(), Value::from_string(path.to_string()));
file_obj.insert(
"lines".to_string(),
Value::Array(Arc::from(
lines
.into_iter()
.map(Value::from_string)
.collect::<Vec<_>>(),
)),
);
file_obj.insert("position".to_string(), Value::Integer(0));
file_obj.insert("closed".to_string(), Value::Bool(false));
Ok(Value::ObjectMut(Arc::new(std::sync::Mutex::new(file_obj))))
}
_ => Err(InterpreterError::RuntimeError(
"File.open() expects a string argument".to_string(),
)),
}
}
fn eval_open(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("open", args, 2)?;
let path = match &args[0] {
Value::String(s) => s.as_ref(),
_ => {
return Err(InterpreterError::RuntimeError(
"open() expects first argument to be a string (path)".to_string(),
))
}
};
let mode = match &args[1] {
Value::String(s) => s.as_ref(),
_ => {
return Err(InterpreterError::RuntimeError(
"open() expects second argument to be a string (mode)".to_string(),
))
}
};
if mode != "r" {
return Err(InterpreterError::RuntimeError(format!(
"open() mode '{mode}' not supported. Only 'r' (read) is currently supported."
)));
}
eval_file_open(&[Value::from_string(path.to_string())])
}
#[cfg(all(not(target_arch = "wasm32"), feature = "http-client"))]
fn try_eval_http_function(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"http_get" => Ok(Some(eval_http_get(args)?)),
"http_post" => Ok(Some(eval_http_post(args)?)),
"http_put" => Ok(Some(eval_http_put(args)?)),
"http_delete" => Ok(Some(eval_http_delete(args)?)),
_ => Ok(None),
}
}
#[cfg(not(all(not(target_arch = "wasm32"), feature = "http-client")))]
fn try_eval_http_function(_name: &str, _args: &[Value]) -> Result<Option<Value>, InterpreterError> {
Ok(None)
}
#[cfg(all(not(target_arch = "wasm32"), feature = "http-client"))]
fn eval_http_get(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("http_get", args, 1)?;
match &args[0] {
Value::String(url) => match crate::stdlib::http::get(url) {
Ok(response) => Ok(Value::from_string(response)),
Err(e) => Err(InterpreterError::RuntimeError(format!(
"HTTP GET failed: {e}"
))),
},
_ => Err(InterpreterError::RuntimeError(
"http_get() expects a string URL".to_string(),
)),
}
}
#[cfg(all(not(target_arch = "wasm32"), feature = "http-client"))]
fn eval_http_post(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("http_post", args, 2)?;
match (&args[0], &args[1]) {
(Value::String(url), Value::String(body)) => match crate::stdlib::http::post(url, body) {
Ok(response) => Ok(Value::from_string(response)),
Err(e) => Err(InterpreterError::RuntimeError(format!(
"HTTP POST failed: {e}"
))),
},
_ => Err(InterpreterError::RuntimeError(
"http_post() expects two string arguments".to_string(),
)),
}
}
#[cfg(all(not(target_arch = "wasm32"), feature = "http-client"))]
fn eval_http_put(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("http_put", args, 2)?;
match (&args[0], &args[1]) {
(Value::String(url), Value::String(body)) => match crate::stdlib::http::put(url, body) {
Ok(response) => Ok(Value::from_string(response)),
Err(e) => Err(InterpreterError::RuntimeError(format!(
"HTTP PUT failed: {e}"
))),
},
_ => Err(InterpreterError::RuntimeError(
"http_put() expects two string arguments".to_string(),
)),
}
}
#[cfg(all(not(target_arch = "wasm32"), feature = "http-client"))]
fn eval_http_delete(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("http_delete", args, 1)?;
match &args[0] {
Value::String(url) => match crate::stdlib::http::delete(url) {
Ok(response) => Ok(Value::from_string(response)),
Err(e) => Err(InterpreterError::RuntimeError(format!(
"HTTP DELETE failed: {e}"
))),
},
_ => Err(InterpreterError::RuntimeError(
"http_delete() expects a string URL".to_string(),
)),
}
}
#[cfg(not(target_arch = "wasm32"))]
fn try_eval_html_function(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"Html_parse" => Ok(Some(eval_html_parse(args)?)),
_ => Ok(None),
}
}
#[cfg(target_arch = "wasm32")]
fn try_eval_html_function(_name: &str, _args: &[Value]) -> Result<Option<Value>, InterpreterError> {
Ok(None)
}
#[cfg(not(target_arch = "wasm32"))]
fn eval_html_parse(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("Html.parse", args, 1)?;
match &args[0] {
Value::String(html) => {
let doc = crate::stdlib::html::HtmlDocument::parse(html);
Ok(Value::HtmlDocument(doc))
}
_ => Err(InterpreterError::RuntimeError(
"Html.parse() expects a string".to_string(),
)),
}
}
#[cfg(not(target_arch = "wasm32"))]
fn try_eval_process_function(
name: &str,
args: &[Value],
) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_command_new__" => Ok(Some(eval_command_new(args)?)),
_ => Ok(None),
}
}
#[cfg(target_arch = "wasm32")]
fn try_eval_process_function(
_name: &str,
_args: &[Value],
) -> Result<Option<Value>, InterpreterError> {
Ok(None)
}
#[cfg(not(target_arch = "wasm32"))]
fn eval_command_new(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("Command::new", args, 1)?;
match &args[0] {
Value::String(program) => {
let mut cmd_obj = HashMap::new();
cmd_obj.insert(
"__type".to_string(),
Value::from_string("Command".to_string()),
);
cmd_obj.insert(
"program".to_string(),
Value::from_string(program.to_string()),
);
cmd_obj.insert("args".to_string(), Value::Array(Arc::new([])));
Ok(Value::Object(Arc::new(cmd_obj)))
}
_ => Err(InterpreterError::RuntimeError(
"Command::new() expects a string program name".to_string(),
)),
}
}
fn try_eval_string_function(name: &str, args: &[Value]) -> Result<Option<Value>, InterpreterError> {
match name {
"__builtin_String_new__" => Ok(Some(eval_string_new(args)?)),
"__builtin_String_from__" => Ok(Some(eval_string_from(args)?)),
"__builtin_String_from_utf8__" => Ok(Some(eval_string_from_utf8(args)?)),
_ => Ok(None),
}
}
fn eval_string_new(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("String::new", args, 0)?;
Ok(Value::from_string(String::new()))
}
fn eval_string_from(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("String::from", args, 1)?;
match &args[0] {
Value::String(s) => Ok(Value::from_string(s.to_string())),
other => Ok(Value::from_string(format!("{other}"))),
}
}
fn eval_string_from_utf8(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("String::from_utf8", args, 1)?;
match &args[0] {
Value::Array(arr) => {
let mut bytes = Vec::with_capacity(arr.len());
for val in arr.iter() {
if let Value::Byte(b) = val {
bytes.push(*b);
} else {
return Err(InterpreterError::TypeError(
"String::from_utf8() requires an array of bytes".to_string(),
));
}
}
match String::from_utf8(bytes) {
Ok(s) => {
Ok(Value::EnumVariant {
enum_name: "Result".to_string(),
variant_name: "Ok".to_string(),
data: Some(vec![Value::from_string(s)]),
})
}
Err(e) => {
Ok(Value::EnumVariant {
enum_name: "Result".to_string(),
variant_name: "Err".to_string(),
data: Some(vec![Value::from_string(e.to_string())]),
})
}
}
}
_ => Err(InterpreterError::TypeError(
"String::from_utf8() requires an array argument".to_string(),
)),
}
}
fn eval_str(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("str", args, 1)?;
match &args[0] {
Value::String(s) => Ok(Value::String(s.clone())),
other => Ok(Value::from_string(format!("{other}"))),
}
}
fn eval_to_string(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("to_string", args, 1)?;
Ok(Value::from_string(format!("{}", args[0])))
}
fn eval_int(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("int", args, 1)?;
match &args[0] {
Value::Integer(n) => Ok(Value::Integer(*n)),
Value::Float(f) => Ok(Value::Integer(*f as i64)), Value::String(s) => {
s.parse::<i64>().map(Value::Integer).map_err(|_| {
InterpreterError::RuntimeError(format!("int() cannot parse string: '{s}'"))
})
}
Value::Bool(b) => Ok(Value::Integer(i64::from(*b))),
_ => Err(InterpreterError::RuntimeError(format!(
"int() does not support type: {}",
args[0]
))),
}
}
fn eval_float(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("float", args, 1)?;
match &args[0] {
Value::Float(f) => Ok(Value::Float(*f)),
Value::Integer(n) => Ok(Value::Float(*n as f64)), Value::String(s) => {
s.parse::<f64>().map(Value::Float).map_err(|_| {
InterpreterError::RuntimeError(format!("float() cannot parse string: '{s}'"))
})
}
Value::Bool(b) => Ok(Value::Float(if *b { 1.0 } else { 0.0 })),
_ => Err(InterpreterError::RuntimeError(format!(
"float() does not support type: {}",
args[0]
))),
}
}
fn eval_parse_int(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("parse_int", args, 1)?;
match &args[0] {
Value::String(s) => s.parse::<i64>().map(Value::Integer).map_err(|_| {
InterpreterError::RuntimeError(format!("parse_int() cannot parse string: '{s}'"))
}),
_ => Err(InterpreterError::RuntimeError(format!(
"parse_int() expects a string, got {}",
args[0].type_name()
))),
}
}
fn eval_parse_float(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("parse_float", args, 1)?;
match &args[0] {
Value::String(s) => s.parse::<f64>().map(Value::Float).map_err(|_| {
InterpreterError::RuntimeError(format!("parse_float() cannot parse string: '{s}'"))
}),
_ => Err(InterpreterError::RuntimeError(format!(
"parse_float() expects a string, got {}",
args[0].type_name()
))),
}
}
fn eval_bool(args: &[Value]) -> Result<Value, InterpreterError> {
validate_arg_count("bool", args, 1)?;
let result = match &args[0] {
Value::Bool(b) => *b,
Value::Integer(n) => *n != 0, Value::Float(f) => *f != 0.0, Value::String(s) => !s.is_empty(), Value::Nil => false,
Value::Array(arr) => !arr.is_empty(),
_ => true, };
Ok(Value::Bool(result))
}
fn eval_assert_eq(args: &[Value]) -> Result<Value, InterpreterError> {
if args.len() < 2 {
return Err(InterpreterError::RuntimeError(
"assert_eq() expects at least 2 arguments (expected, actual)".to_string(),
));
}
let expected = &args[0];
let actual = &args[1];
let message = if args.len() > 2 {
format!("{}", args[2])
} else {
format!("Assertion failed: expected {expected:?}, got {actual:?}")
};
if expected == actual {
Ok(Value::Nil)
} else {
Err(InterpreterError::AssertionFailed(message))
}
}
fn eval_assert(args: &[Value]) -> Result<Value, InterpreterError> {
if args.is_empty() {
return Err(InterpreterError::RuntimeError(
"assert() expects at least 1 argument (condition)".to_string(),
));
}
let condition = &args[0];
let message = if args.len() > 1 {
format!("{}", args[1])
} else {
"Assertion failed".to_string()
};
match condition {
Value::Bool(true) => Ok(Value::Nil),
Value::Bool(false) => Err(InterpreterError::AssertionFailed(message)),
_ => Err(InterpreterError::RuntimeError(
"assert() expects a boolean condition".to_string(),
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_eval_sqrt() {
let args = vec![Value::Integer(16)];
let result = eval_sqrt(&args).expect("eval_sqrt should succeed in test");
assert_eq!(result, Value::Float(4.0));
let args = vec![Value::Float(9.0)];
let result = eval_sqrt(&args).expect("eval_sqrt should succeed in test");
assert_eq!(result, Value::Float(3.0));
}
#[test]
fn test_eval_pow() {
let args = vec![Value::Integer(2), Value::Integer(3)];
let result = eval_pow(&args).expect("eval_pow should succeed in test");
assert_eq!(result, Value::Integer(8));
let args = vec![Value::Float(2.0), Value::Float(3.0)];
let result = eval_pow(&args).expect("eval_pow should succeed in test");
assert_eq!(result, Value::Float(8.0));
}
#[test]
fn test_eval_abs() {
let args = vec![Value::Integer(-42)];
let result = eval_abs(&args).expect("eval_abs should succeed in test");
assert_eq!(result, Value::Integer(42));
let args = vec![Value::Float(-3.15)];
let result = eval_abs(&args).expect("eval_abs should succeed in test");
assert_eq!(result, Value::Float(3.15));
}
#[test]
fn test_eval_min_max() {
let args = vec![Value::Integer(5), Value::Integer(3)];
let min_result = eval_min(&args).expect("eval_min should succeed in test");
assert_eq!(min_result, Value::Integer(3));
let max_result = eval_max(&args).expect("eval_max should succeed in test");
assert_eq!(max_result, Value::Integer(5));
}
#[test]
fn test_eval_len() {
let args = vec![Value::from_string("hello".to_string())];
let result = eval_len(&args).expect("eval_len should succeed in test");
assert_eq!(result, Value::Integer(5));
let args = vec![Value::Array(Arc::from(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]))];
let result = eval_len(&args).expect("eval_len should succeed in test");
assert_eq!(result, Value::Integer(3));
}
#[test]
fn test_eval_type() {
let args = vec![Value::Integer(42)];
let result = eval_type(&args).expect("eval_type should succeed in test");
assert_eq!(result, Value::from_string("integer".to_string()));
let args = vec![Value::Float(3.15)];
let result = eval_type(&args).expect("eval_type should succeed in test");
assert_eq!(result, Value::from_string("float".to_string()));
}
#[test]
fn test_eval_range() {
let args = vec![Value::Integer(3)];
let result = eval_range(&args).expect("eval_range should succeed in test");
if let Value::Array(arr) = result {
assert_eq!(arr.len(), 3);
assert_eq!(arr[0], Value::Integer(0));
assert_eq!(arr[1], Value::Integer(1));
assert_eq!(arr[2], Value::Integer(2));
} else {
panic!("Expected array result");
}
}
#[test]
fn test_eval_reverse() {
let args = vec![Value::Array(Arc::from(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]))];
let result = eval_reverse(&args).expect("eval_reverse should succeed in test");
if let Value::Array(arr) = result {
assert_eq!(arr[0], Value::Integer(3));
assert_eq!(arr[1], Value::Integer(2));
assert_eq!(arr[2], Value::Integer(1));
} else {
panic!("Expected array result");
}
let args = vec![Value::from_string("hello".to_string())];
let result = eval_reverse(&args).expect("eval_reverse should succeed in test");
assert_eq!(result, Value::from_string("olleh".to_string()));
}
#[test]
fn test_eval_floor() {
let args = vec![Value::Float(3.7)];
let result = eval_floor(&args).expect("eval_floor should succeed in test");
assert_eq!(result, Value::Integer(3));
let args = vec![Value::Float(-2.3)];
let result = eval_floor(&args).expect("eval_floor should succeed in test");
assert_eq!(result, Value::Integer(-3));
let args = vec![Value::Integer(5)];
let result = eval_floor(&args).expect("eval_floor should succeed in test");
assert_eq!(result, Value::Integer(5));
}
#[test]
fn test_eval_ceil() {
let args = vec![Value::Float(3.2)];
let result = eval_ceil(&args).expect("eval_ceil should succeed in test");
assert_eq!(result, Value::Integer(4));
let args = vec![Value::Float(-2.7)];
let result = eval_ceil(&args).expect("eval_ceil should succeed in test");
assert_eq!(result, Value::Integer(-2));
let args = vec![Value::Integer(5)];
let result = eval_ceil(&args).expect("eval_ceil should succeed in test");
assert_eq!(result, Value::Integer(5));
}
#[test]
fn test_eval_round() {
let args = vec![Value::Float(3.5)];
let result = eval_round(&args).expect("eval_round should succeed in test");
assert_eq!(result, Value::Integer(4));
let args = vec![Value::Float(3.4)];
let result = eval_round(&args).expect("eval_round should succeed in test");
assert_eq!(result, Value::Integer(3));
let args = vec![Value::Float(-2.5)];
let result = eval_round(&args).expect("eval_round should succeed in test");
assert_eq!(result, Value::Integer(-3));
let args = vec![Value::Integer(7)];
let result = eval_round(&args).expect("eval_round should succeed in test");
assert_eq!(result, Value::Integer(7));
}
#[test]
fn test_eval_sin() {
use std::f64::consts::PI;
let args = vec![Value::Float(0.0)];
let result = eval_sin(&args).expect("eval_sin should succeed in test");
if let Value::Float(v) = result {
assert!((v - 0.0).abs() < 1e-10, "sin(0) should be ~0");
} else {
panic!("Expected float result");
}
let args = vec![Value::Float(PI / 2.0)];
let result = eval_sin(&args).expect("eval_sin should succeed in test");
if let Value::Float(v) = result {
assert!((v - 1.0).abs() < 1e-10, "sin(Ï€/2) should be ~1");
} else {
panic!("Expected float result");
}
let args = vec![Value::Integer(0)];
let result = eval_sin(&args).expect("eval_sin should succeed in test");
if let Value::Float(v) = result {
assert!((v - 0.0).abs() < 1e-10);
} else {
panic!("Expected float result");
}
}
#[test]
fn test_eval_cos() {
use std::f64::consts::PI;
let args = vec![Value::Float(0.0)];
let result = eval_cos(&args).expect("eval_cos should succeed in test");
if let Value::Float(v) = result {
assert!((v - 1.0).abs() < 1e-10, "cos(0) should be ~1");
} else {
panic!("Expected float result");
}
let args = vec![Value::Float(PI)];
let result = eval_cos(&args).expect("eval_cos should succeed in test");
if let Value::Float(v) = result {
assert!((v + 1.0).abs() < 1e-10, "cos(Ï€) should be ~-1");
} else {
panic!("Expected float result");
}
let args = vec![Value::Integer(0)];
let result = eval_cos(&args).expect("eval_cos should succeed in test");
if let Value::Float(v) = result {
assert!((v - 1.0).abs() < 1e-10);
} else {
panic!("Expected float result");
}
}
#[test]
fn test_eval_tan() {
use std::f64::consts::PI;
let args = vec![Value::Float(0.0)];
let result = eval_tan(&args).expect("eval_tan should succeed in test");
if let Value::Float(v) = result {
assert!((v - 0.0).abs() < 1e-10, "tan(0) should be ~0");
} else {
panic!("Expected float result");
}
let args = vec![Value::Float(PI / 4.0)];
let result = eval_tan(&args).expect("eval_tan should succeed in test");
if let Value::Float(v) = result {
assert!((v - 1.0).abs() < 1e-10, "tan(Ï€/4) should be ~1");
} else {
panic!("Expected float result");
}
let args = vec![Value::Integer(0)];
let result = eval_tan(&args).expect("eval_tan should succeed in test");
if let Value::Float(v) = result {
assert!((v - 0.0).abs() < 1e-10);
} else {
panic!("Expected float result");
}
}
#[test]
fn test_eval_assert_true() {
let args = vec![Value::Bool(true)];
let result = eval_assert(&args);
assert!(result.is_ok(), "assert(true) should succeed");
assert_eq!(
result.expect("operation should succeed in test"),
Value::Nil
);
}
#[test]
fn test_eval_assert_false() {
let args = vec![Value::Bool(false)];
let result = eval_assert(&args);
assert!(result.is_err(), "assert(false) should fail");
}
#[test]
fn test_eval_assert_with_message() {
let args = vec![
Value::Bool(false),
Value::from_string("Custom error".to_string()),
];
let result = eval_assert(&args);
assert!(result.is_err(), "assert(false, msg) should fail");
if let Err(InterpreterError::AssertionFailed(msg)) = result {
assert!(
msg.contains("Custom error"),
"Should include custom message"
);
} else {
panic!("Expected AssertionFailed error");
}
}
#[test]
fn test_eval_assert_non_boolean() {
let args = vec![Value::Integer(1)];
let result = eval_assert(&args);
assert!(result.is_err(), "assert(non-bool) should fail");
}
#[test]
fn test_eval_assert_eq_equal() {
let args = vec![Value::Integer(42), Value::Integer(42)];
let result = eval_assert_eq(&args);
assert!(result.is_ok(), "assert_eq(42, 42) should succeed");
assert_eq!(
result.expect("operation should succeed in test"),
Value::Nil
);
}
#[test]
fn test_eval_assert_eq_not_equal() {
let args = vec![Value::Integer(42), Value::Integer(43)];
let result = eval_assert_eq(&args);
assert!(result.is_err(), "assert_eq(42, 43) should fail");
}
#[test]
fn test_eval_assert_eq_strings() {
let args = vec![
Value::from_string("hello".to_string()),
Value::from_string("hello".to_string()),
];
let result = eval_assert_eq(&args);
assert!(result.is_ok(), "assert_eq strings should succeed");
let args = vec![
Value::from_string("hello".to_string()),
Value::from_string("world".to_string()),
];
let result = eval_assert_eq(&args);
assert!(result.is_err(), "assert_eq different strings should fail");
}
#[test]
fn test_eval_println_basic() {
let args = vec![Value::from_string("Hello, World!".to_string())];
let result = eval_println(&args);
assert!(result.is_ok(), "println should not panic");
assert_eq!(
result.expect("operation should succeed in test"),
Value::Nil
);
}
#[test]
fn test_eval_println_multiple_args() {
let args = vec![
Value::from_string("Hello".to_string()),
Value::from_string("World".to_string()),
];
let result = eval_println(&args);
assert!(
result.is_ok(),
"println with multiple args should not panic"
);
}
#[test]
fn test_eval_println_no_args() {
let args = vec![];
let result = eval_println(&args);
assert!(result.is_ok(), "println with no args should print newline");
}
#[test]
fn test_eval_print_basic() {
let args = vec![Value::from_string("Test".to_string())];
let result = eval_print(&args);
assert!(result.is_ok(), "print should not panic");
assert_eq!(
result.expect("operation should succeed in test"),
Value::Nil
);
}
#[test]
fn test_eval_print_integers() {
let args = vec![Value::Integer(42)];
let result = eval_print(&args);
assert!(result.is_ok(), "print(42) should not panic");
}
#[test]
fn test_eval_dbg_basic() {
let args = vec![Value::Integer(123)];
let result = eval_dbg(&args);
assert!(result.is_ok(), "dbg should not panic");
assert_eq!(
result.expect("operation should succeed in test"),
Value::Integer(123)
);
}
#[test]
fn test_eval_dbg_string() {
let args = vec![Value::from_string("debug".to_string())];
let result = eval_dbg(&args);
assert!(result.is_ok(), "dbg should not panic");
assert_eq!(
result.expect("operation should succeed in test"),
Value::from_string("debug".to_string())
);
}
#[test]
fn prop_floor_ceil_relationship() {
let test_values = vec![3.1, 3.9, -2.3, -2.9, 0.0, 5.0];
for val in test_values {
let floor_result =
eval_floor(&[Value::Float(val)]).expect("eval_floor should succeed in test");
let ceil_result =
eval_ceil(&[Value::Float(val)]).expect("eval_ceil should succeed in test");
if let (Value::Integer(floor), Value::Integer(ceil)) = (floor_result, ceil_result) {
let floor_f = floor as f64;
let ceil_f = ceil as f64;
assert!(floor_f <= val, "floor({val}) should be <= {val}");
assert!(ceil_f >= val, "ceil({val}) should be >= {val}");
assert!(floor_f <= ceil_f, "floor({val}) <= ceil({val})");
}
}
}
#[test]
fn prop_trig_pythagorean_identity() {
use std::f64::consts::PI;
let test_angles = vec![0.0, PI / 6.0, PI / 4.0, PI / 3.0, PI / 2.0];
for angle in test_angles {
let sin_val =
eval_sin(&[Value::Float(angle)]).expect("eval_sin should succeed in test");
let cos_val =
eval_cos(&[Value::Float(angle)]).expect("eval_cos should succeed in test");
if let (Value::Float(s), Value::Float(c)) = (sin_val, cos_val) {
let identity = s * s + c * c;
assert!(
(identity - 1.0).abs() < 1e-10,
"sin²({angle}) + cos²({angle}) should = 1, got {identity}"
);
}
}
}
#[test]
fn prop_abs_non_negative() {
let test_values = vec![
Value::Integer(-100),
Value::Integer(0),
Value::Integer(100),
Value::Float(-3.15),
Value::Float(0.0),
Value::Float(2.71),
];
for val in test_values {
let result = eval_abs(&[val]).expect("eval_abs should succeed in test");
match result {
Value::Integer(i) => assert!(i >= 0, "abs should be non-negative"),
Value::Float(f) => assert!(f >= 0.0, "abs should be non-negative"),
_ => panic!("abs should return number"),
}
}
}
}
#[test]
fn test_println_string_no_quotes() {
let fmt = Value::from_string("Name: {}".to_string());
let arg = Value::from_string("Ruchy".to_string());
let output = format_println_output(&[fmt, arg]);
assert!(
!output.contains("\"Ruchy\""),
"println should not print quotes around strings, got: {output}"
);
assert!(
output.contains("Name: Ruchy"),
"Expected 'Name: Ruchy' without quotes, got: {output}"
);
}
#[cfg(test)]
#[allow(clippy::expect_used)]
mod property_tests_builtin {
use super::*;
use proptest::prelude::*;
proptest! {
#![proptest_config(ProptestConfig::with_cases(50))]
#[test]
fn prop_sqrt_non_negative(x in 0.0f64..1000.0) {
let result = eval_sqrt(&[Value::Float(x)]).expect("sqrt should succeed");
if let Value::Float(f) = result {
prop_assert!(f >= 0.0, "sqrt({}) = {} should be >= 0", x, f);
}
}
#[test]
fn prop_sqrt_square_inverse(x in 0.0f64..1000.0) {
let sqrt_result = eval_sqrt(&[Value::Float(x)]).expect("sqrt");
if let Value::Float(s) = sqrt_result {
let squared = s * s;
prop_assert!((squared - x).abs() < 1e-9, "sqrt({})² = {} should ≈ {}", x, squared, x);
}
}
#[test]
fn prop_abs_always_non_negative(x in -1000i64..1000) {
let result = eval_abs(&[Value::Integer(x)]).expect("abs should succeed");
if let Value::Integer(a) = result {
prop_assert!(a >= 0, "abs({}) = {} should be >= 0", x, a);
}
}
#[test]
fn prop_abs_symmetric(x in -1000i64..1000) {
let pos_result = eval_abs(&[Value::Integer(x)]).expect("abs(x)");
let neg_result = eval_abs(&[Value::Integer(-x)]).expect("abs(-x)");
prop_assert_eq!(pos_result, neg_result, "abs({}) should == abs({})", x, -x);
}
#[test]
fn prop_min_less_than_both(a in -1000i64..1000, b in -1000i64..1000) {
let result = eval_min(&[Value::Integer(a), Value::Integer(b)]).expect("min");
if let Value::Integer(m) = result {
prop_assert!(m <= a, "min({}, {}) = {} should be <= {}", a, b, m, a);
prop_assert!(m <= b, "min({}, {}) = {} should be <= {}", a, b, m, b);
}
}
#[test]
fn prop_max_greater_than_both(a in -1000i64..1000, b in -1000i64..1000) {
let result = eval_max(&[Value::Integer(a), Value::Integer(b)]).expect("max");
if let Value::Integer(m) = result {
prop_assert!(m >= a, "max({}, {}) = {} should be >= {}", a, b, m, a);
prop_assert!(m >= b, "max({}, {}) = {} should be >= {}", a, b, m, b);
}
}
#[test]
fn prop_min_same_value(a in -1000i64..1000) {
let result = eval_min(&[Value::Integer(a), Value::Integer(a)]).expect("min");
prop_assert_eq!(result, Value::Integer(a), "min({}, {}) should == {}", a, a, a);
}
#[test]
fn prop_max_same_value(a in -1000i64..1000) {
let result = eval_max(&[Value::Integer(a), Value::Integer(a)]).expect("max");
prop_assert_eq!(result, Value::Integer(a), "max({}, {}) should == {}", a, a, a);
}
#[test]
fn prop_floor_less_than_or_equal(x in -1000.0f64..1000.0) {
let result = eval_floor(&[Value::Float(x)]).expect("floor");
if let Value::Integer(f) = result {
let f_as_f64 = f as f64;
prop_assert!(f_as_f64 <= x, "floor({}) = {} should be <= {}", x, f, x);
}
}
#[test]
fn prop_ceil_greater_than_or_equal(x in -1000.0f64..1000.0) {
let result = eval_ceil(&[Value::Float(x)]).expect("ceil");
if let Value::Integer(c) = result {
let c_as_f64 = c as f64;
prop_assert!(c_as_f64 >= x, "ceil({}) = {} should be >= {}", x, c, x);
}
}
#[test]
fn prop_range_length(n in 0i64..100) {
let result = eval_range(&[Value::Integer(n)]).expect("range");
if let Value::Array(arr) = result {
prop_assert_eq!(arr.len() as i64, n, "range({}) should have length {}", n, n);
}
}
#[test]
fn prop_range_boundaries(n in 1i64..100) {
let result = eval_range(&[Value::Integer(n)]).expect("range");
if let Value::Array(arr) = result {
prop_assert_eq!(arr[0].clone(), Value::Integer(0), "range({}) should start at 0", n);
let last_idx = arr.len() - 1;
prop_assert_eq!(arr[last_idx].clone(), Value::Integer(n - 1), "range({}) should end at {}", n, n - 1);
}
}
#[test]
fn prop_len_string(s in "[a-zA-Z0-9]{0,50}") {
let result = eval_len(&[Value::from_string(s.clone())]).expect("len");
if let Value::Integer(len) = result {
prop_assert_eq!(len as usize, s.chars().count(), "len('{}') should be {}", s, s.chars().count());
}
}
#[test]
fn prop_type_non_empty(x in -1000i64..1000) {
let result = eval_type(&[Value::Integer(x)]).expect("type");
if let Value::String(s) = result {
prop_assert!(!s.is_empty(), "type should return non-empty string");
}
}
#[test]
fn prop_reverse_involutive(
a in -100i64..100,
b in -100i64..100,
c in -100i64..100
) {
let arr = Value::Array(Arc::from(vec![
Value::Integer(a),
Value::Integer(b),
Value::Integer(c),
]));
let reversed = eval_reverse(std::slice::from_ref(&arr)).expect("reverse once");
let double_reversed = eval_reverse(&[reversed]).expect("reverse twice");
prop_assert_eq!(double_reversed, arr, "reverse(reverse(arr)) should == arr");
}
#[test]
fn prop_pow_zero_exponent(x in 1i64..100) {
let result = eval_pow(&[Value::Integer(x), Value::Integer(0)]).expect("pow");
prop_assert_eq!(result, Value::Integer(1), "{}^0 should == 1", x);
}
#[test]
fn prop_pow_one_exponent(x in -100i64..100) {
let result = eval_pow(&[Value::Integer(x), Value::Integer(1)]).expect("pow");
prop_assert_eq!(result, Value::Integer(x), "{}^1 should == {}", x, x);
}
#[test]
fn prop_trig_identity(x in -10.0f64..10.0) {
let sin_result = eval_sin(&[Value::Float(x)]).expect("sin");
let cos_result = eval_cos(&[Value::Float(x)]).expect("cos");
if let (Value::Float(s), Value::Float(c)) = (sin_result, cos_result) {
let identity = s * s + c * c;
prop_assert!((identity - 1.0).abs() < 1e-10, "sin²({}) + cos²({}) = {} should ≈ 1", x, x, identity);
}
}
#[test]
fn prop_tan_definition(x in -1.0f64..1.0) {
let sin_result = eval_sin(&[Value::Float(x)]).expect("sin");
let cos_result = eval_cos(&[Value::Float(x)]).expect("cos");
let tan_result = eval_tan(&[Value::Float(x)]).expect("tan");
if let (Value::Float(s), Value::Float(c), Value::Float(t)) = (sin_result, cos_result, tan_result) {
if c.abs() > 0.01 {
let expected = s / c;
prop_assert!((t - expected).abs() < 1e-10, "tan({}) = {} should ≈ sin/cos = {}", x, t, expected);
}
}
}
#[test]
fn prop_log_exp_inverse(x in -10.0f64..10.0) {
let exp_result = eval_exp(&[Value::Float(x)]).expect("exp");
if let Value::Float(e) = exp_result {
if e > 0.0 && e.is_finite() {
let log_result = eval_log(&[Value::Float(e)]).expect("log");
if let Value::Float(l) = log_result {
prop_assert!((l - x).abs() < 1e-9, "log(exp({})) = {} should ≈ {}", x, l, x);
}
}
}
}
}
#[test]
fn test_eval_log() {
let args = vec![Value::Float(std::f64::consts::E)];
let result = eval_log(&args).expect("eval_log should succeed");
if let Value::Float(v) = result {
assert!((v - 1.0).abs() < 1e-10, "ln(e) should be ~1");
} else {
panic!("Expected float result");
}
let args = vec![Value::Integer(1)];
let result = eval_log(&args).expect("eval_log should succeed");
if let Value::Float(v) = result {
assert!((v - 0.0).abs() < 1e-10, "ln(1) should be ~0");
}
}
#[test]
fn test_eval_log10() {
let args = vec![Value::Float(100.0)];
let result = eval_log10(&args).expect("eval_log10 should succeed");
if let Value::Float(v) = result {
assert!((v - 2.0).abs() < 1e-10, "log10(100) should be ~2");
}
let args = vec![Value::Integer(10)];
let result = eval_log10(&args).expect("eval_log10 should succeed");
if let Value::Float(v) = result {
assert!((v - 1.0).abs() < 1e-10, "log10(10) should be ~1");
}
}
#[test]
fn test_eval_exp() {
let args = vec![Value::Float(0.0)];
let result = eval_exp(&args).expect("eval_exp should succeed");
if let Value::Float(v) = result {
assert!((v - 1.0).abs() < 1e-10, "exp(0) should be ~1");
}
let args = vec![Value::Integer(0)];
let result = eval_exp(&args).expect("eval_exp should succeed");
if let Value::Float(v) = result {
assert!((v - 1.0).abs() < 1e-10, "exp(0) should be ~1");
}
}
#[test]
fn test_eval_random() {
let args = vec![];
let result = eval_random(&args).expect("eval_random should succeed");
if let Value::Float(v) = result {
assert!(v >= 0.0 && v < 1.0, "random() should be in [0, 1)");
} else {
panic!("Expected float result");
}
}
#[test]
fn test_eval_type_of() {
let args = vec![Value::Integer(42)];
let result = eval_type_of(&args).expect("eval_type_of should succeed");
assert_eq!(result, Value::from_string("integer".to_string()));
let args = vec![Value::Nil];
let result = eval_type_of(&args).expect("eval_type_of should succeed");
assert_eq!(result, Value::from_string("nil".to_string()));
let args = vec![Value::Bool(true)];
let result = eval_type_of(&args).expect("eval_type_of should succeed");
assert_eq!(result, Value::from_string("boolean".to_string()));
}
#[test]
fn test_eval_is_nil() {
let args = vec![Value::Nil];
let result = eval_is_nil(&args).expect("eval_is_nil should succeed");
assert_eq!(result, Value::Bool(true));
let args = vec![Value::Integer(0)];
let result = eval_is_nil(&args).expect("eval_is_nil should succeed");
assert_eq!(result, Value::Bool(false));
let args = vec![Value::from_string("".to_string())];
let result = eval_is_nil(&args).expect("eval_is_nil should succeed");
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_eval_push() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
let args = vec![arr, Value::Integer(3)];
let result = eval_push(&args).expect("eval_push should succeed");
if let Value::Array(new_arr) = result {
assert_eq!(new_arr.len(), 3);
assert_eq!(new_arr[2], Value::Integer(3));
} else {
panic!("Expected array result");
}
}
#[test]
fn test_eval_pop() {
let arr = Value::Array(Arc::from(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]));
let args = vec![arr];
let result = eval_pop(&args).expect("eval_pop should succeed");
assert_eq!(result, Value::Integer(3));
}
#[test]
fn test_eval_sort() {
let arr = Value::Array(Arc::from(vec![
Value::Integer(3),
Value::Integer(1),
Value::Integer(2),
]));
let args = vec![arr];
let result = eval_sort(&args).expect("eval_sort should succeed");
if let Value::Array(sorted) = result {
assert_eq!(sorted[0], Value::Integer(1));
assert_eq!(sorted[1], Value::Integer(2));
assert_eq!(sorted[2], Value::Integer(3));
} else {
panic!("Expected array result");
}
}
#[test]
fn test_eval_zip() {
let arr1 = Value::Array(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
let arr2 = Value::Array(Arc::from(vec![
Value::from_string("a".to_string()),
Value::from_string("b".to_string()),
]));
let args = vec![arr1, arr2];
let result = eval_zip(&args).expect("eval_zip should succeed");
if let Value::Array(zipped) = result {
assert_eq!(zipped.len(), 2);
if let Value::Tuple(first) = &zipped[0] {
assert_eq!(first[0], Value::Integer(1));
}
} else {
panic!("Expected array result");
}
}
#[test]
fn test_eval_enumerate() {
let arr = Value::Array(Arc::from(vec![
Value::from_string("a".to_string()),
Value::from_string("b".to_string()),
]));
let args = vec![arr];
let result = eval_enumerate(&args).expect("eval_enumerate should succeed");
if let Value::Array(enumerated) = result {
assert_eq!(enumerated.len(), 2);
if let Value::Tuple(first) = &enumerated[0] {
assert_eq!(first[0], Value::Integer(0));
}
if let Value::Tuple(second) = &enumerated[1] {
assert_eq!(second[0], Value::Integer(1));
}
} else {
panic!("Expected array result");
}
}
#[test]
fn test_eval_assert_eq_success() {
let args = vec![Value::Integer(42), Value::Integer(42)];
let result = eval_assert_eq(&args);
assert!(result.is_ok());
}
#[test]
fn test_eval_assert_eq_failure() {
let args = vec![Value::Integer(42), Value::Integer(43)];
let result = eval_assert_eq(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_assert_success() {
let args = vec![Value::Bool(true)];
let result = eval_assert(&args);
assert!(result.is_ok());
}
#[test]
fn test_eval_assert_failure() {
let args = vec![Value::Bool(false)];
let result = eval_assert(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_assert_with_message() {
let args = vec![
Value::Bool(false),
Value::from_string("custom error".to_string()),
];
let result = eval_assert(&args);
assert!(result.is_err());
if let Err(InterpreterError::AssertionFailed(msg)) = result {
assert!(
msg.contains("custom error"),
"Message should contain custom error text"
);
}
}
#[test]
fn test_eval_range_two_args() {
let args = vec![Value::Integer(2), Value::Integer(5)];
let result = eval_range(&args).expect("eval_range should succeed");
if let Value::Array(arr) = result {
assert_eq!(arr.len(), 3);
assert_eq!(arr[0], Value::Integer(2));
assert_eq!(arr[1], Value::Integer(3));
assert_eq!(arr[2], Value::Integer(4));
} else {
panic!("Expected array result");
}
}
#[test]
fn test_eval_range_three_args() {
let args = vec![Value::Integer(0), Value::Integer(10), Value::Integer(2)];
let result = eval_range(&args).expect("eval_range should succeed");
if let Value::Array(arr) = result {
assert_eq!(arr.len(), 5);
assert_eq!(arr[0], Value::Integer(0));
assert_eq!(arr[1], Value::Integer(2));
assert_eq!(arr[4], Value::Integer(8));
} else {
panic!("Expected array result");
}
}
#[test]
fn test_eval_range_negative_step() {
let args = vec![Value::Integer(5), Value::Integer(0), Value::Integer(-1)];
let result = eval_range(&args).expect("eval_range should succeed");
if let Value::Array(arr) = result {
assert_eq!(arr.len(), 5);
assert_eq!(arr[0], Value::Integer(5));
assert_eq!(arr[4], Value::Integer(1));
} else {
panic!("Expected array result");
}
}
#[test]
fn test_eval_min_floats() {
let args = vec![Value::Float(5.5), Value::Float(3.3)];
let result = eval_min(&args).expect("eval_min should succeed");
assert_eq!(result, Value::Float(3.3));
}
#[test]
fn test_eval_max_floats() {
let args = vec![Value::Float(5.5), Value::Float(3.3)];
let result = eval_max(&args).expect("eval_max should succeed");
assert_eq!(result, Value::Float(5.5));
}
#[test]
fn test_eval_len_tuple() {
let tuple = Value::Tuple(Arc::from(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]));
let args = vec![tuple];
let result = eval_len(&args).expect("eval_len should succeed");
assert_eq!(result, Value::Integer(3));
}
#[test]
fn test_eval_type_string() {
let args = vec![Value::from_string("hello".to_string())];
let result = eval_type(&args).expect("eval_type should succeed");
assert_eq!(result, Value::from_string("string".to_string()));
}
#[test]
fn test_eval_type_array() {
let args = vec![Value::Array(Arc::from(vec![]))];
let result = eval_type(&args).expect("eval_type should succeed");
assert_eq!(result, Value::from_string("array".to_string()));
}
#[test]
fn test_eval_type_nil() {
let args = vec![Value::Nil];
let result = eval_type(&args).expect("eval_type should succeed");
assert_eq!(result, Value::from_string("nil".to_string()));
}
#[test]
fn test_eval_type_bool() {
let args = vec![Value::Bool(true)];
let result = eval_type(&args).expect("eval_type should succeed");
assert_eq!(result, Value::from_string("boolean".to_string()));
}
#[test]
fn test_eval_cos() {
let args = vec![Value::Float(0.0)];
let result = eval_cos(&args).expect("eval_cos should succeed");
if let Value::Float(v) = result {
assert!((v - 1.0).abs() < 1e-10, "cos(0) should be ~1");
}
let args = vec![Value::Integer(0)];
let result = eval_cos(&args).expect("eval_cos should succeed");
if let Value::Float(v) = result {
assert!((v - 1.0).abs() < 1e-10, "cos(0) should be ~1");
}
}
#[test]
fn test_eval_tan() {
let args = vec![Value::Float(0.0)];
let result = eval_tan(&args).expect("eval_tan should succeed");
if let Value::Float(v) = result {
assert!((v - 0.0).abs() < 1e-10, "tan(0) should be ~0");
}
let args = vec![Value::Integer(0)];
let result = eval_tan(&args).expect("eval_tan should succeed");
if let Value::Float(v) = result {
assert!((v - 0.0).abs() < 1e-10, "tan(0) should be ~0");
}
}
#[test]
fn test_eval_str() {
let args = vec![Value::Integer(42)];
let result = eval_str(&args).expect("eval_str should succeed");
assert_eq!(result, Value::from_string("42".to_string()));
let args = vec![Value::Float(3.14)];
let result = eval_str(&args).expect("eval_str should succeed");
if let Value::String(s) = result {
assert!(s.starts_with("3.14"));
}
let args = vec![Value::Bool(true)];
let result = eval_str(&args).expect("eval_str should succeed");
assert_eq!(result, Value::from_string("true".to_string()));
}
#[test]
fn test_eval_to_string() {
let args = vec![Value::Integer(42)];
let result = eval_to_string(&args).expect("eval_to_string should succeed");
assert_eq!(result, Value::from_string("42".to_string()));
}
#[test]
fn test_eval_int() {
let args = vec![Value::Float(3.9)];
let result = eval_int(&args).expect("eval_int should succeed");
assert_eq!(result, Value::Integer(3));
let args = vec![Value::from_string("42".to_string())];
let result = eval_int(&args).expect("eval_int should succeed");
assert_eq!(result, Value::Integer(42));
let args = vec![Value::Bool(true)];
let result = eval_int(&args).expect("eval_int should succeed");
assert_eq!(result, Value::Integer(1));
let args = vec![Value::Bool(false)];
let result = eval_int(&args).expect("eval_int should succeed");
assert_eq!(result, Value::Integer(0));
}
#[test]
fn test_eval_float() {
let args = vec![Value::Integer(42)];
let result = eval_float(&args).expect("eval_float should succeed");
assert_eq!(result, Value::Float(42.0));
let args = vec![Value::from_string("3.14".to_string())];
let result = eval_float(&args).expect("eval_float should succeed");
if let Value::Float(v) = result {
assert!((v - 3.14).abs() < 0.001);
}
}
#[test]
fn test_eval_bool() {
let args = vec![Value::Integer(0)];
let result = eval_bool(&args).expect("eval_bool should succeed");
assert_eq!(result, Value::Bool(false));
let args = vec![Value::Integer(1)];
let result = eval_bool(&args).expect("eval_bool should succeed");
assert_eq!(result, Value::Bool(true));
let args = vec![Value::from_string("".to_string())];
let result = eval_bool(&args).expect("eval_bool should succeed");
assert_eq!(result, Value::Bool(false));
let args = vec![Value::from_string("hello".to_string())];
let result = eval_bool(&args).expect("eval_bool should succeed");
assert_eq!(result, Value::Bool(true));
let args = vec![Value::Nil];
let result = eval_bool(&args).expect("eval_bool should succeed");
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_eval_parse_int() {
let args = vec![Value::from_string("42".to_string())];
let result = eval_parse_int(&args).expect("eval_parse_int should succeed");
assert_eq!(result, Value::Integer(42));
let args = vec![Value::from_string("-123".to_string())];
let result = eval_parse_int(&args).expect("eval_parse_int should succeed");
assert_eq!(result, Value::Integer(-123));
}
#[test]
fn test_eval_parse_float() {
let args = vec![Value::from_string("3.14".to_string())];
let result = eval_parse_float(&args).expect("eval_parse_float should succeed");
if let Value::Float(v) = result {
assert!((v - 3.14).abs() < 0.001);
}
}
#[test]
fn test_eval_timestamp() {
let args = vec![];
let result = eval_timestamp(&args).expect("eval_timestamp should succeed");
if let Value::Integer(ts) = result {
assert!(ts > 0, "timestamp should be positive");
} else {
panic!("Expected integer result");
}
}
#[test]
fn test_try_eval_io_function_unknown() {
let result = try_eval_io_function("unknown", &[]).expect("should not error");
assert!(result.is_none());
}
#[test]
fn test_try_eval_math_function_unknown() {
let result = try_eval_math_function("unknown", &[]).expect("should not error");
assert!(result.is_none());
}
#[test]
fn test_try_eval_utility_function_unknown() {
let result = try_eval_utility_function("unknown", &[]).expect("should not error");
assert!(result.is_none());
}
#[test]
fn test_try_eval_collection_function_unknown() {
let result = try_eval_collection_function("unknown", &[]).expect("should not error");
assert!(result.is_none());
}
#[test]
fn test_try_eval_conversion_function_unknown() {
let result = try_eval_conversion_function("unknown", &[]).expect("should not error");
assert!(result.is_none());
}
#[test]
fn test_try_eval_time_function_unknown() {
let result = try_eval_time_function("unknown", &[]).expect("should not error");
assert!(result.is_none());
}
#[test]
fn test_eval_builtin_function_unknown() {
let result =
eval_builtin_function("unknown_function", &[]).expect("should not error for unknown");
assert!(result.is_none());
}
#[test]
fn test_eval_builtin_function_println() {
let result = eval_builtin_function("__builtin_println__", &[Value::Integer(42)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_eval_builtin_function_sqrt() {
let result = eval_builtin_function("__builtin_sqrt__", &[Value::Integer(16)])
.expect("should succeed");
assert_eq!(result, Some(Value::Float(4.0)));
}
#[test]
fn test_eval_builtin_function_len() {
let result = eval_builtin_function(
"__builtin_len__",
&[Value::from_string("hello".to_string())],
)
.expect("should succeed");
assert_eq!(result, Some(Value::Integer(5)));
}
#[test]
fn test_eval_builtin_function_range() {
let result = eval_builtin_function("__builtin_range__", &[Value::Integer(3)])
.expect("should succeed");
assert!(result.is_some());
if let Some(Value::Array(arr)) = result {
assert_eq!(arr.len(), 3);
}
}
#[test]
fn test_eval_builtin_function_type() {
let result = eval_builtin_function("__builtin_type__", &[Value::Integer(42)])
.expect("should succeed");
assert_eq!(result, Some(Value::from_string("integer".to_string())));
}
#[test]
fn test_eval_builtin_function_str() {
let result = eval_builtin_function("__builtin_str__", &[Value::Integer(42)])
.expect("should succeed");
assert_eq!(result, Some(Value::from_string("42".to_string())));
}
#[test]
fn test_eval_builtin_function_int() {
let result =
eval_builtin_function("__builtin_int__", &[Value::Float(3.9)]).expect("should succeed");
assert_eq!(result, Some(Value::Integer(3)));
}
#[test]
fn test_eval_builtin_function_float() {
let result = eval_builtin_function("__builtin_float__", &[Value::Integer(42)])
.expect("should succeed");
assert_eq!(result, Some(Value::Float(42.0)));
}
#[test]
fn test_eval_builtin_function_bool() {
let result = eval_builtin_function("__builtin_bool__", &[Value::Integer(1)])
.expect("should succeed");
assert_eq!(result, Some(Value::Bool(true)));
}
#[test]
fn test_eval_sort_strings() {
let arr = Value::Array(Arc::from(vec![
Value::from_string("c".to_string()),
Value::from_string("a".to_string()),
Value::from_string("b".to_string()),
]));
let args = vec![arr];
let result = eval_sort(&args).expect("eval_sort should succeed");
if let Value::Array(sorted) = result {
assert_eq!(sorted[0], Value::from_string("a".to_string()));
assert_eq!(sorted[1], Value::from_string("b".to_string()));
assert_eq!(sorted[2], Value::from_string("c".to_string()));
}
}
#[test]
fn test_eval_sort_floats() {
let arr = Value::Array(Arc::from(vec![
Value::Float(3.3),
Value::Float(1.1),
Value::Float(2.2),
]));
let args = vec![arr];
let result = eval_sort(&args).expect("eval_sort should succeed");
if let Value::Array(sorted) = result {
assert_eq!(sorted[0], Value::Float(1.1));
assert_eq!(sorted[1], Value::Float(2.2));
assert_eq!(sorted[2], Value::Float(3.3));
}
}
#[test]
fn test_eval_sqrt_from_integer() {
let args = vec![Value::Integer(25)];
let result = eval_sqrt(&args).expect("eval_sqrt should succeed");
if let Value::Float(v) = result {
assert!((v - 5.0).abs() < 1e-10);
}
}
#[test]
fn test_eval_pow_with_floats() {
let args = vec![Value::Float(2.0), Value::Float(3.0)];
let result = eval_pow(&args).expect("eval_pow should succeed");
if let Value::Float(v) = result {
assert!((v - 8.0).abs() < 1e-10);
}
}
#[test]
fn test_eval_min_with_floats() {
let args = vec![Value::Float(5.5), Value::Float(3.3)];
let result = eval_min(&args).expect("eval_min should succeed");
assert_eq!(result, Value::Float(3.3));
}
#[test]
fn test_eval_max_with_floats() {
let args = vec![Value::Float(5.5), Value::Float(3.3)];
let result = eval_max(&args).expect("eval_max should succeed");
assert_eq!(result, Value::Float(5.5));
}
#[test]
fn test_eval_floor_with_negative() {
let args = vec![Value::Float(-3.2)];
let result = eval_floor(&args).expect("eval_floor should succeed");
assert_eq!(result, Value::Integer(-4));
}
#[test]
fn test_eval_ceil_with_negative() {
let args = vec![Value::Float(-3.7)];
let result = eval_ceil(&args).expect("eval_ceil should succeed");
assert_eq!(result, Value::Integer(-3));
}
#[test]
fn test_eval_round_down_case() {
let args = vec![Value::Float(3.4)];
let result = eval_round(&args).expect("eval_round should succeed");
assert_eq!(result, Value::Integer(3));
}
#[test]
fn test_eval_range_single_arg() {
let args = vec![Value::Integer(5)];
let result = eval_range(&args).expect("eval_range should succeed");
if let Value::Array(arr) = result {
assert_eq!(arr.len(), 5);
assert_eq!(arr[0], Value::Integer(0));
assert_eq!(arr[4], Value::Integer(4));
}
}
#[test]
fn test_eval_reverse_on_array() {
let arr = Value::Array(Arc::from(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]));
let args = vec![arr];
let result = eval_reverse(&args).expect("eval_reverse should succeed");
if let Value::Array(reversed) = result {
assert_eq!(reversed[0], Value::Integer(3));
assert_eq!(reversed[1], Value::Integer(2));
assert_eq!(reversed[2], Value::Integer(1));
}
}
#[test]
fn test_eval_reverse_on_string() {
let args = vec![Value::from_string("hello".to_string())];
let result = eval_reverse(&args).expect("eval_reverse should succeed");
assert_eq!(result, Value::from_string("olleh".to_string()));
}
#[test]
fn test_eval_is_nil_returns_true() {
let args = vec![Value::Nil];
let result = eval_is_nil(&args).expect("eval_is_nil should succeed");
assert_eq!(result, Value::Bool(true));
}
#[test]
fn test_eval_is_nil_returns_false() {
let args = vec![Value::Integer(0)];
let result = eval_is_nil(&args).expect("eval_is_nil should succeed");
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_eval_push_element_to_array() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1)]));
let args = vec![arr, Value::Integer(2)];
let result = eval_push(&args).expect("eval_push should succeed");
if let Value::Array(new_arr) = result {
assert_eq!(new_arr.len(), 2);
}
}
#[test]
fn test_eval_pop_element_from_array() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
let args = vec![arr];
let result = eval_pop(&args).expect("eval_pop should succeed");
assert_eq!(result, Value::Integer(2));
}
#[test]
fn test_eval_pop_from_empty_array() {
let arr = Value::Array(Arc::from(vec![]));
let args = vec![arr];
let result = eval_pop(&args).expect("eval_pop should succeed");
assert_eq!(result, Value::Nil);
}
#[test]
fn test_eval_type_for_integer_value() {
let args = vec![Value::Integer(42)];
let result = eval_type(&args).expect("eval_type should succeed");
assert_eq!(result, Value::from_string("integer".to_string()));
}
#[test]
fn test_eval_type_for_float_value() {
let args = vec![Value::Float(3.14)];
let result = eval_type(&args).expect("eval_type should succeed");
assert_eq!(result, Value::from_string("float".to_string()));
}
#[test]
fn test_eval_abs_on_negative_integer() {
let args = vec![Value::Integer(-42)];
let result = eval_abs(&args).expect("eval_abs should succeed");
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_abs_on_negative_float() {
let args = vec![Value::Float(-3.14)];
let result = eval_abs(&args).expect("eval_abs should succeed");
if let Value::Float(v) = result {
assert!((v - 3.14).abs() < 1e-10);
}
}
#[test]
fn test_eval_len_on_string() {
let args = vec![Value::from_string("hello".to_string())];
let result = eval_len(&args).expect("eval_len should succeed");
assert_eq!(result, Value::Integer(5));
}
#[test]
fn test_eval_len_on_array() {
let arr = Value::Array(Arc::from(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]));
let args = vec![arr];
let result = eval_len(&args).expect("eval_len should succeed");
assert_eq!(result, Value::Integer(3));
}
#[test]
fn test_eval_len_on_empty_string() {
let args = vec![Value::from_string("".to_string())];
let result = eval_len(&args).expect("eval_len should succeed");
assert_eq!(result, Value::Integer(0));
}
#[test]
fn test_eval_len_on_empty_array() {
let arr = Value::Array(Arc::from(vec![]));
let args = vec![arr];
let result = eval_len(&args).expect("eval_len should succeed");
assert_eq!(result, Value::Integer(0));
}
#[test]
fn test_eval_sqrt_error_on_string() {
let args = vec![Value::from_string("not a number".to_string())];
let result = eval_sqrt(&args);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("expects a number"));
}
#[test]
fn test_eval_sqrt_error_on_wrong_arg_count() {
let args = vec![];
let result = eval_sqrt(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_pow_error_on_string() {
let args = vec![Value::from_string("a".to_string()), Value::Integer(2)];
let result = eval_pow(&args);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("expects two numbers"));
}
#[test]
fn test_eval_pow_negative_exponent() {
let args = vec![Value::Integer(2), Value::Integer(-2)];
let result = eval_pow(&args).expect("should succeed");
if let Value::Float(v) = result {
assert!((v - 0.25).abs() < 1e-10);
} else {
panic!("Expected Float");
}
}
#[test]
fn test_eval_pow_mixed_int_float() {
let args = vec![Value::Integer(2), Value::Float(3.0)];
let result = eval_pow(&args).expect("should succeed");
assert_eq!(result, Value::Float(8.0));
}
#[test]
fn test_eval_pow_float_int() {
let args = vec![Value::Float(2.0), Value::Integer(3)];
let result = eval_pow(&args).expect("should succeed");
assert_eq!(result, Value::Float(8.0));
}
#[test]
fn test_eval_abs_error_on_string() {
let args = vec![Value::from_string("abc".to_string())];
let result = eval_abs(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_min_error_on_string() {
let args = vec![Value::from_string("a".to_string()), Value::Integer(1)];
let result = eval_min(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_min_mixed_int_float() {
let args = vec![Value::Integer(5), Value::Float(3.3)];
let result = eval_min(&args).expect("should succeed");
assert_eq!(result, Value::Float(3.3));
}
#[test]
fn test_eval_min_float_int() {
let args = vec![Value::Float(2.5), Value::Integer(3)];
let result = eval_min(&args).expect("should succeed");
assert_eq!(result, Value::Float(2.5));
}
#[test]
fn test_eval_max_error_on_string() {
let args = vec![Value::from_string("a".to_string()), Value::Integer(1)];
let result = eval_max(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_max_mixed_int_float() {
let args = vec![Value::Integer(5), Value::Float(3.3)];
let result = eval_max(&args).expect("should succeed");
assert_eq!(result, Value::Float(5.0));
}
#[test]
fn test_eval_max_float_int() {
let args = vec![Value::Float(2.5), Value::Integer(3)];
let result = eval_max(&args).expect("should succeed");
assert_eq!(result, Value::Float(3.0));
}
#[test]
fn test_eval_floor_error_on_string() {
let args = vec![Value::from_string("abc".to_string())];
let result = eval_floor(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_floor_on_integer() {
let args = vec![Value::Integer(42)];
let result = eval_floor(&args).expect("should succeed");
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_ceil_error_on_string() {
let args = vec![Value::from_string("abc".to_string())];
let result = eval_ceil(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_ceil_on_integer() {
let args = vec![Value::Integer(42)];
let result = eval_ceil(&args).expect("should succeed");
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_round_error_on_string() {
let args = vec![Value::from_string("abc".to_string())];
let result = eval_round(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_round_on_integer() {
let args = vec![Value::Integer(42)];
let result = eval_round(&args).expect("should succeed");
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_sin_on_integer() {
let args = vec![Value::Integer(0)];
let result = eval_sin(&args).expect("should succeed");
if let Value::Float(v) = result {
assert!(v.abs() < 1e-10);
}
}
#[test]
fn test_eval_sin_on_float() {
let args = vec![Value::Float(0.0)];
let result = eval_sin(&args).expect("should succeed");
if let Value::Float(v) = result {
assert!(v.abs() < 1e-10);
}
}
#[test]
fn test_eval_sin_error_on_string() {
let args = vec![Value::from_string("abc".to_string())];
let result = eval_sin(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_cos_on_integer() {
let args = vec![Value::Integer(0)];
let result = eval_cos(&args).expect("should succeed");
if let Value::Float(v) = result {
assert!((v - 1.0).abs() < 1e-10);
}
}
#[test]
fn test_eval_cos_on_float() {
let args = vec![Value::Float(0.0)];
let result = eval_cos(&args).expect("should succeed");
if let Value::Float(v) = result {
assert!((v - 1.0).abs() < 1e-10);
}
}
#[test]
fn test_eval_cos_error_on_string() {
let args = vec![Value::from_string("abc".to_string())];
let result = eval_cos(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_tan_on_integer() {
let args = vec![Value::Integer(0)];
let result = eval_tan(&args).expect("should succeed");
if let Value::Float(v) = result {
assert!(v.abs() < 1e-10);
}
}
#[test]
fn test_eval_tan_on_float() {
let args = vec![Value::Float(0.0)];
let result = eval_tan(&args).expect("should succeed");
if let Value::Float(v) = result {
assert!(v.abs() < 1e-10);
}
}
#[test]
fn test_eval_tan_error_on_string() {
let args = vec![Value::from_string("abc".to_string())];
let result = eval_tan(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_log_on_integer() {
let args = vec![Value::Integer(1)];
let result = eval_log(&args).expect("should succeed");
if let Value::Float(v) = result {
assert!(v.abs() < 1e-10); }
}
#[test]
fn test_eval_log_on_float() {
use std::f64::consts::E;
let args = vec![Value::Float(E)];
let result = eval_log(&args).expect("should succeed");
if let Value::Float(v) = result {
assert!((v - 1.0).abs() < 1e-10); }
}
#[test]
fn test_eval_log_error_on_string() {
let args = vec![Value::from_string("abc".to_string())];
let result = eval_log(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_log10_on_integer() {
let args = vec![Value::Integer(100)];
let result = eval_log10(&args).expect("should succeed");
if let Value::Float(v) = result {
assert!((v - 2.0).abs() < 1e-10);
}
}
#[test]
fn test_eval_log10_on_float() {
let args = vec![Value::Float(1000.0)];
let result = eval_log10(&args).expect("should succeed");
if let Value::Float(v) = result {
assert!((v - 3.0).abs() < 1e-10);
}
}
#[test]
fn test_eval_log10_error_on_string() {
let args = vec![Value::from_string("abc".to_string())];
let result = eval_log10(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_exp_on_integer() {
let args = vec![Value::Integer(0)];
let result = eval_exp(&args).expect("should succeed");
if let Value::Float(v) = result {
assert!((v - 1.0).abs() < 1e-10); }
}
#[test]
fn test_eval_exp_on_float() {
let args = vec![Value::Float(1.0)];
let result = eval_exp(&args).expect("should succeed");
if let Value::Float(v) = result {
assert!((v - std::f64::consts::E).abs() < 1e-10);
}
}
#[test]
fn test_eval_exp_error_on_string() {
let args = vec![Value::from_string("abc".to_string())];
let result = eval_exp(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_random_returns_float_in_range() {
let args = vec![];
let result = eval_random(&args).expect("should succeed");
if let Value::Float(v) = result {
assert!(v >= 0.0 && v < 1.0);
} else {
panic!("Expected Float");
}
}
#[test]
fn test_eval_random_error_on_args() {
let args = vec![Value::Integer(1)];
let result = eval_random(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_range_two_args_start_end() {
let args = vec![Value::Integer(2), Value::Integer(5)];
let result = eval_range(&args).expect("should succeed");
if let Value::Array(arr) = result {
assert_eq!(arr.len(), 3);
assert_eq!(arr[0], Value::Integer(2));
assert_eq!(arr[2], Value::Integer(4));
}
}
#[test]
fn test_eval_range_three_args_positive_step() {
let args = vec![Value::Integer(0), Value::Integer(10), Value::Integer(2)];
let result = eval_range(&args).expect("should succeed");
if let Value::Array(arr) = result {
assert_eq!(arr.len(), 5);
assert_eq!(arr[0], Value::Integer(0));
assert_eq!(arr[4], Value::Integer(8));
}
}
#[test]
fn test_eval_range_three_args_negative_step() {
let args = vec![Value::Integer(10), Value::Integer(0), Value::Integer(-2)];
let result = eval_range(&args).expect("should succeed");
if let Value::Array(arr) = result {
assert_eq!(arr.len(), 5);
assert_eq!(arr[0], Value::Integer(10));
assert_eq!(arr[4], Value::Integer(2));
}
}
#[test]
fn test_eval_range_zero_step_error() {
let args = vec![Value::Integer(0), Value::Integer(10), Value::Integer(0)];
let result = eval_range(&args);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("step cannot be zero"));
}
#[test]
fn test_eval_range_error_on_string() {
let args = vec![Value::from_string("abc".to_string())];
let result = eval_range(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_range_two_args_error_on_string() {
let args = vec![Value::from_string("a".to_string()), Value::Integer(5)];
let result = eval_range(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_range_three_args_error_on_string() {
let args = vec![
Value::Integer(0),
Value::from_string("x".to_string()),
Value::Integer(1),
];
let result = eval_range(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_range_invalid_arg_count() {
let args = vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
Value::Integer(4),
];
let result = eval_range(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_len_on_tuple() {
let tuple = Value::Tuple(Arc::from(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]));
let args = vec![tuple];
let result = eval_len(&args).expect("should succeed");
assert_eq!(result, Value::Integer(3));
}
#[test]
fn test_eval_len_error_on_integer() {
let args = vec![Value::Integer(42)];
let result = eval_len(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_reverse_error_on_integer() {
let args = vec![Value::Integer(42)];
let result = eval_reverse(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_push_error_on_non_array() {
let args = vec![Value::Integer(1), Value::Integer(2)];
let result = eval_push(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_pop_error_on_non_array() {
let args = vec![Value::Integer(1)];
let result = eval_pop(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_sort_error_on_non_array() {
let args = vec![Value::Integer(1)];
let result = eval_sort(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_sort_mixed_types() {
let arr = Value::Array(Arc::from(vec![
Value::Integer(3),
Value::from_string("b".to_string()),
Value::Integer(1),
]));
let args = vec![arr];
let result = eval_sort(&args);
assert!(result.is_ok());
}
#[test]
fn test_eval_zip_success() {
let a = Value::Array(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
let b = Value::Array(Arc::from(vec![
Value::from_string("a".to_string()),
Value::from_string("b".to_string()),
]));
let args = vec![a, b];
let result = eval_zip(&args).expect("should succeed");
if let Value::Array(arr) = result {
assert_eq!(arr.len(), 2);
if let Value::Tuple(t) = &arr[0] {
assert_eq!(t[0], Value::Integer(1));
}
}
}
#[test]
fn test_eval_zip_error_on_non_arrays() {
let args = vec![Value::Integer(1), Value::Integer(2)];
let result = eval_zip(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_enumerate_success() {
let arr = Value::Array(Arc::from(vec![
Value::from_string("a".to_string()),
Value::from_string("b".to_string()),
]));
let args = vec![arr];
let result = eval_enumerate(&args).expect("should succeed");
if let Value::Array(enumerated) = result {
assert_eq!(enumerated.len(), 2);
if let Value::Tuple(t) = &enumerated[0] {
assert_eq!(t[0], Value::Integer(0));
assert_eq!(t[1], Value::from_string("a".to_string()));
}
}
}
#[test]
fn test_eval_enumerate_error_on_non_array() {
let args = vec![Value::Integer(1)];
let result = eval_enumerate(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_type_of_integer() {
let args = vec![Value::Integer(42)];
let result = eval_type_of(&args).expect("should succeed");
assert_eq!(result, Value::from_string("integer".to_string()));
}
#[test]
fn test_eval_type_of_string() {
let args = vec![Value::from_string("hello".to_string())];
let result = eval_type_of(&args).expect("should succeed");
assert_eq!(result, Value::from_string("string".to_string()));
}
#[test]
fn test_eval_type_for_bool() {
let args = vec![Value::Bool(true)];
let result = eval_type(&args).expect("should succeed");
assert_eq!(result, Value::from_string("boolean".to_string()));
}
#[test]
fn test_eval_type_for_nil() {
let args = vec![Value::Nil];
let result = eval_type(&args).expect("should succeed");
assert_eq!(result, Value::from_string("nil".to_string()));
}
#[test]
fn test_eval_type_for_array() {
let arr = Value::Array(Arc::from(vec![]));
let args = vec![arr];
let result = eval_type(&args).expect("should succeed");
assert_eq!(result, Value::from_string("array".to_string()));
}
#[test]
fn test_eval_str_on_bool() {
let args = vec![Value::Bool(true)];
let result = eval_str(&args).expect("should succeed");
assert_eq!(result, Value::from_string("true".to_string()));
}
#[test]
fn test_eval_str_on_nil() {
let args = vec![Value::Nil];
let result = eval_str(&args).expect("should succeed");
assert_eq!(result, Value::from_string("nil".to_string()));
}
#[test]
fn test_eval_to_string_on_integer() {
let args = vec![Value::Integer(42)];
let result = eval_to_string(&args).expect("should succeed");
assert_eq!(result, Value::from_string("42".to_string()));
}
#[test]
fn test_eval_int_from_string() {
let args = vec![Value::from_string("42".to_string())];
let result = eval_int(&args).expect("should succeed");
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_int_from_bool() {
let args = vec![Value::Bool(true)];
let result = eval_int(&args).expect("should succeed");
assert_eq!(result, Value::Integer(1));
}
#[test]
fn test_eval_int_from_bool_false() {
let args = vec![Value::Bool(false)];
let result = eval_int(&args).expect("should succeed");
assert_eq!(result, Value::Integer(0));
}
#[test]
fn test_eval_int_error_on_invalid_string() {
let args = vec![Value::from_string("not a number".to_string())];
let result = eval_int(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_float_from_string() {
let args = vec![Value::from_string("3.14".to_string())];
let result = eval_float(&args).expect("should succeed");
if let Value::Float(v) = result {
assert!((v - 3.14).abs() < 1e-10);
}
}
#[test]
fn test_eval_float_from_bool() {
let args = vec![Value::Bool(true)];
let result = eval_float(&args).expect("should succeed");
assert_eq!(result, Value::Float(1.0));
}
#[test]
fn test_eval_float_error_on_invalid_string() {
let args = vec![Value::from_string("not a number".to_string())];
let result = eval_float(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_parse_int_success() {
let args = vec![Value::from_string("123".to_string())];
let result = eval_parse_int(&args).expect("should succeed");
assert_eq!(result, Value::Integer(123));
}
#[test]
fn test_eval_parse_int_error() {
let args = vec![Value::from_string("abc".to_string())];
let result = eval_parse_int(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_parse_int_on_non_string() {
let args = vec![Value::Integer(42)];
let result = eval_parse_int(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_parse_float_success() {
let args = vec![Value::from_string("3.14".to_string())];
let result = eval_parse_float(&args).expect("should succeed");
if let Value::Float(v) = result {
assert!((v - 3.14).abs() < 1e-10);
}
}
#[test]
fn test_eval_parse_float_error() {
let args = vec![Value::from_string("abc".to_string())];
let result = eval_parse_float(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_parse_float_on_non_string() {
let args = vec![Value::Integer(42)];
let result = eval_parse_float(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_bool_from_integer_zero() {
let args = vec![Value::Integer(0)];
let result = eval_bool(&args).expect("should succeed");
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_eval_bool_from_nil() {
let args = vec![Value::Nil];
let result = eval_bool(&args).expect("should succeed");
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_eval_bool_from_empty_string() {
let args = vec![Value::from_string("".to_string())];
let result = eval_bool(&args).expect("should succeed");
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_eval_bool_from_nonempty_string() {
let args = vec![Value::from_string("hello".to_string())];
let result = eval_bool(&args).expect("should succeed");
assert_eq!(result, Value::Bool(true));
}
#[test]
fn test_eval_bool_from_empty_array() {
let arr = Value::Array(Arc::from(vec![]));
let args = vec![arr];
let result = eval_bool(&args).expect("should succeed");
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_eval_print_empty_args() {
let args = vec![];
let result = eval_print(&args).expect("should succeed");
assert_eq!(result, Value::Nil);
}
#[test]
fn test_eval_println_empty_args() {
let args = vec![];
let result = eval_println(&args).expect("should succeed");
assert_eq!(result, Value::Nil);
}
#[test]
fn test_eval_println_format_interpolation() {
let args = vec![
Value::from_string("Hello, {}!".to_string()),
Value::from_string("World".to_string()),
];
let result = eval_println(&args).expect("should succeed");
assert_eq!(result, Value::Nil);
}
#[test]
fn test_eval_dbg_single_value() {
let args = vec![Value::Integer(42)];
let result = eval_dbg(&args).expect("should succeed");
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_dbg_multiple_values() {
let args = vec![Value::Integer(1), Value::Integer(2)];
let result = eval_dbg(&args).expect("should succeed");
if let Value::Array(arr) = result {
assert_eq!(arr.len(), 2);
} else {
panic!("Expected array");
}
}
#[test]
fn test_eval_timestamp_returns_integer() {
let args = vec![];
let result = eval_timestamp(&args).expect("should succeed");
if let Value::Integer(v) = result {
assert!(v > 0);
} else {
panic!("Expected integer");
}
}
#[test]
fn test_eval_timestamp_error_on_args() {
let args = vec![Value::Integer(1)];
let result = eval_timestamp(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_chrono_utc_now_returns_string() {
let args = vec![];
let result = eval_chrono_utc_now(&args).expect("should succeed");
if let Value::String(s) = result {
assert!(s.contains('T') || s.contains(':'));
} else {
panic!("Expected string");
}
}
#[test]
fn test_eval_chrono_utc_now_error_on_args() {
let args = vec![Value::Integer(1)];
let result = eval_chrono_utc_now(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_sleep_with_float() {
let args = vec![Value::Float(1.0)]; let result = eval_sleep(&args).expect("should succeed");
assert_eq!(result, Value::Nil);
}
#[test]
fn test_eval_sleep_error_on_string() {
let args = vec![Value::from_string("abc".to_string())];
let result = eval_sleep(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_assert_eq_equals_values() {
let args = vec![Value::Integer(1), Value::Integer(1)];
let result = eval_assert_eq(&args).expect("should succeed");
assert_eq!(result, Value::Nil);
}
#[test]
fn test_eval_assert_eq_not_equals_error() {
let args = vec![Value::Integer(1), Value::Integer(2)];
let result = eval_assert_eq(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_assert_truthy_passes() {
let args = vec![Value::Bool(true)];
let result = eval_assert(&args).expect("should succeed");
assert_eq!(result, Value::Nil);
}
#[test]
fn test_eval_assert_falsy_fails() {
let args = vec![Value::Bool(false)];
let result = eval_assert(&args);
assert!(result.is_err());
}
#[test]
fn test_eval_assert_custom_message_included() {
let args = vec![
Value::Bool(false),
Value::from_string("custom message".to_string()),
];
let result = eval_assert(&args);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("custom message"));
}
#[test]
fn test_format_value_for_println_string() {
let val = Value::from_string("hello".to_string());
let result = format_value_for_println(&val);
assert_eq!(result, "hello");
}
#[test]
fn test_format_value_for_println_integer() {
let val = Value::Integer(42);
let result = format_value_for_println(&val);
assert_eq!(result, "42");
}
#[test]
fn test_format_with_interpolation_multiple() {
let result = format_with_interpolation(
"{} + {} = {}",
&[Value::Integer(1), Value::Integer(2), Value::Integer(3)],
);
assert_eq!(result, "1 + 2 = 3");
}
#[test]
fn test_join_values_empty() {
let result = join_values(&[]);
assert_eq!(result, "");
}
#[test]
fn test_join_values_multiple() {
let result = join_values(&[Value::Integer(1), Value::Integer(2)]);
assert_eq!(result, "1 2");
}
#[test]
fn test_format_println_output_empty() {
let result = format_println_output(&[]);
assert_eq!(result, "\n");
}
#[test]
fn test_format_println_output_no_format() {
let result = format_println_output(&[Value::Integer(1), Value::Integer(2)]);
assert_eq!(result, "1 2\n");
}
#[test]
fn test_generate_range_forward() {
let result = generate_range_forward(0, 5, 2);
assert_eq!(result.len(), 3);
assert_eq!(result[0], Value::Integer(0));
assert_eq!(result[1], Value::Integer(2));
assert_eq!(result[2], Value::Integer(4));
}
#[test]
fn test_generate_range_backward() {
let result = generate_range_backward(10, 0, -2);
assert_eq!(result.len(), 5);
assert_eq!(result[0], Value::Integer(10));
assert_eq!(result[4], Value::Integer(2));
}
#[test]
fn test_try_eval_io_function_print() {
let result = try_eval_io_function("__builtin_print__", &[Value::Integer(42)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_io_function_dbg() {
let result =
try_eval_io_function("__builtin_dbg__", &[Value::Integer(42)]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_io_function_returns_none_for_unknown() {
let result = try_eval_io_function("unknown", &[]).expect("should succeed");
assert!(result.is_none());
}
#[test]
fn test_try_eval_basic_math_part1_sqrt() {
let result = try_eval_basic_math_part1("__builtin_sqrt__", &[Value::Integer(4)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_basic_math_part1_pow() {
let result =
try_eval_basic_math_part1("__builtin_pow__", &[Value::Integer(2), Value::Integer(3)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_basic_math_part2_min() {
let result =
try_eval_basic_math_part2("__builtin_min__", &[Value::Integer(1), Value::Integer(2)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_basic_math_part2_max() {
let result =
try_eval_basic_math_part2("__builtin_max__", &[Value::Integer(1), Value::Integer(2)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_advanced_math_part1_floor() {
let result = try_eval_advanced_math_part1("__builtin_floor__", &[Value::Float(3.5)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_advanced_math_part1_ceil() {
let result = try_eval_advanced_math_part1("__builtin_ceil__", &[Value::Float(3.5)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_advanced_math_part1_round() {
let result = try_eval_advanced_math_part1("__builtin_round__", &[Value::Float(3.5)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_advanced_math_part2_sin() {
let result = try_eval_advanced_math_part2("__builtin_sin__", &[Value::Integer(0)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_advanced_math_part2_cos() {
let result = try_eval_advanced_math_part2("__builtin_cos__", &[Value::Integer(0)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_advanced_math_part2_tan() {
let result = try_eval_advanced_math_part2("__builtin_tan__", &[Value::Integer(0)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_advanced_math_part3_log() {
let result = try_eval_advanced_math_part3("__builtin_log__", &[Value::Integer(1)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_advanced_math_part3_log10() {
let result = try_eval_advanced_math_part3("__builtin_log10__", &[Value::Integer(10)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_advanced_math_part3_exp() {
let result = try_eval_advanced_math_part3("__builtin_exp__", &[Value::Integer(0)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_advanced_math_part3_random() {
let result =
try_eval_advanced_math_part3("__builtin_random__", &[]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_utility_part1_len() {
let result = try_eval_utility_part1(
"__builtin_len__",
&[Value::from_string("hello".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_utility_part1_range() {
let result = try_eval_utility_part1("__builtin_range__", &[Value::Integer(5)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_utility_part2_type() {
let result = try_eval_utility_part2("__builtin_type__", &[Value::Integer(42)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_utility_part2_type_of() {
let result = try_eval_utility_part2("__builtin_type_of__", &[Value::Integer(42)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_utility_part2_is_nil() {
let result =
try_eval_utility_part2("__builtin_is_nil__", &[Value::Nil]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_utility_part2_reverse() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1)]));
let result = try_eval_utility_part2("__builtin_reverse__", &[arr]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_utility_part2_assert_eq() {
let result = try_eval_utility_part2(
"__builtin_assert_eq__",
&[Value::Integer(1), Value::Integer(1)],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_utility_part2_assert() {
let result = try_eval_utility_part2("__builtin_assert__", &[Value::Bool(true)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_utility_part2_zip() {
let a = Value::Array(Arc::from(vec![Value::Integer(1)]));
let b = Value::Array(Arc::from(vec![Value::Integer(2)]));
let result = try_eval_utility_part2("__builtin_zip__", &[a, b]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_utility_part2_enumerate() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1)]));
let result =
try_eval_utility_part2("__builtin_enumerate__", &[arr]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_collection_function_push() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1)]));
let result = try_eval_collection_function("__builtin_push__", &[arr, Value::Integer(2)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_collection_function_pop() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1)]));
let result =
try_eval_collection_function("__builtin_pop__", &[arr]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_collection_function_sort() {
let arr = Value::Array(Arc::from(vec![Value::Integer(2), Value::Integer(1)]));
let result =
try_eval_collection_function("__builtin_sort__", &[arr]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_conversion_function_str() {
let result = try_eval_conversion_function("__builtin_str__", &[Value::Integer(42)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_conversion_function_to_string() {
let result = try_eval_conversion_function("__builtin_to_string__", &[Value::Integer(42)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_conversion_function_int() {
let result = try_eval_conversion_function("__builtin_int__", &[Value::Float(3.5)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_conversion_function_float() {
let result = try_eval_conversion_function("__builtin_float__", &[Value::Integer(42)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_conversion_function_bool() {
let result = try_eval_conversion_function("__builtin_bool__", &[Value::Integer(1)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_conversion_function_parse_int() {
let result = try_eval_conversion_function(
"__builtin_parse_int__",
&[Value::from_string("42".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_conversion_function_parse_float() {
let result = try_eval_conversion_function(
"__builtin_parse_float__",
&[Value::from_string("3.14".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_time_function_sleep() {
let result = try_eval_time_function("__builtin_sleep__", &[Value::Integer(1)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_time_function_timestamp() {
let result = try_eval_time_function("__builtin_timestamp__", &[]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_time_function_chrono_utc_now() {
let result =
try_eval_time_function("__builtin_chrono_utc_now__", &[]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_eval_builtin_function_abs() {
let result = eval_builtin_function("__builtin_abs__", &[Value::Integer(-5)])
.expect("should succeed");
assert_eq!(result, Some(Value::Integer(5)));
}
#[test]
fn test_eval_builtin_function_min() {
let result =
eval_builtin_function("__builtin_min__", &[Value::Integer(3), Value::Integer(5)])
.expect("should succeed");
assert_eq!(result, Some(Value::Integer(3)));
}
#[test]
fn test_eval_builtin_function_max() {
let result =
eval_builtin_function("__builtin_max__", &[Value::Integer(3), Value::Integer(5)])
.expect("should succeed");
assert_eq!(result, Some(Value::Integer(5)));
}
#[test]
fn test_eval_builtin_function_floor() {
let result = eval_builtin_function("__builtin_floor__", &[Value::Float(3.7)])
.expect("should succeed");
assert_eq!(result, Some(Value::Integer(3)));
}
#[test]
fn test_eval_builtin_function_ceil() {
let result = eval_builtin_function("__builtin_ceil__", &[Value::Float(3.2)])
.expect("should succeed");
assert_eq!(result, Some(Value::Integer(4)));
}
#[test]
fn test_eval_builtin_function_round() {
let result = eval_builtin_function("__builtin_round__", &[Value::Float(3.5)])
.expect("should succeed");
assert_eq!(result, Some(Value::Integer(4)));
}
#[test]
fn test_eval_builtin_function_reverse() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
let result = eval_builtin_function("__builtin_reverse__", &[arr]).expect("should succeed");
if let Some(Value::Array(reversed)) = result {
assert_eq!(reversed[0], Value::Integer(2));
}
}
#[test]
fn test_eval_builtin_function_push() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1)]));
let result = eval_builtin_function("__builtin_push__", &[arr, Value::Integer(2)])
.expect("should succeed");
if let Some(Value::Array(pushed)) = result {
assert_eq!(pushed.len(), 2);
}
}
#[test]
fn test_eval_builtin_function_pop() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
let result = eval_builtin_function("__builtin_pop__", &[arr]).expect("should succeed");
assert_eq!(result, Some(Value::Integer(2)));
}
#[test]
fn test_eval_builtin_function_is_nil() {
let result =
eval_builtin_function("__builtin_is_nil__", &[Value::Nil]).expect("should succeed");
assert_eq!(result, Some(Value::Bool(true)));
}
#[test]
fn test_eval_builtin_function_type_of() {
let result = eval_builtin_function("__builtin_type_of__", &[Value::Float(3.14)])
.expect("should succeed");
assert_eq!(result, Some(Value::from_string("float".to_string())));
}
#[test]
fn test_eval_sqrt_wrong_arg_count() {
let result = eval_sqrt(&[]);
assert!(result.is_err());
if let Err(InterpreterError::RuntimeError(msg)) = result {
assert!(msg.contains("expects"));
}
}
#[test]
fn test_eval_sqrt_wrong_type() {
let result = eval_sqrt(&[Value::from_string("not a number".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_pow_wrong_arg_count() {
let result = eval_pow(&[Value::Integer(2)]);
assert!(result.is_err());
}
#[test]
fn test_eval_pow_wrong_type() {
let result = eval_pow(&[Value::from_string("a".to_string()), Value::Integer(2)]);
assert!(result.is_err());
}
#[test]
fn test_eval_abs_wrong_arg_count() {
let result = eval_abs(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_abs_wrong_type() {
let result = eval_abs(&[Value::from_string("abc".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_min_wrong_arg_count() {
let result = eval_min(&[Value::Integer(1)]);
assert!(result.is_err());
}
#[test]
fn test_eval_min_wrong_type() {
let result = eval_min(&[Value::from_string("a".to_string()), Value::Integer(1)]);
assert!(result.is_err());
}
#[test]
fn test_eval_max_wrong_arg_count() {
let result = eval_max(&[Value::Integer(1)]);
assert!(result.is_err());
}
#[test]
fn test_eval_max_wrong_type() {
let result = eval_max(&[Value::Bool(true), Value::Integer(1)]);
assert!(result.is_err());
}
#[test]
fn test_eval_floor_wrong_type() {
let result = eval_floor(&[Value::from_string("3.5".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_ceil_wrong_type() {
let result = eval_ceil(&[Value::from_string("3.5".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_round_wrong_type() {
let result = eval_round(&[Value::Bool(true)]);
assert!(result.is_err());
}
#[test]
fn test_eval_sin_wrong_type() {
let result = eval_sin(&[Value::from_string("0".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_cos_wrong_type() {
let result = eval_cos(&[Value::from_string("0".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_tan_wrong_type() {
let result = eval_tan(&[Value::Nil]);
assert!(result.is_err());
}
#[test]
fn test_eval_log_wrong_type() {
let result = eval_log(&[Value::from_string("e".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_log10_wrong_type() {
let result = eval_log10(&[Value::Bool(true)]);
assert!(result.is_err());
}
#[test]
fn test_eval_exp_wrong_type() {
let result = eval_exp(&[Value::from_string("0".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_random_wrong_arg_count() {
let result = eval_random(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_len_wrong_arg_count() {
let result = eval_len(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_len_wrong_type() {
let result = eval_len(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_len_dataframe_empty() {
let df = Value::DataFrame { columns: vec![] };
let result = eval_len(&[df]).expect("should succeed");
assert_eq!(result, Value::Integer(0));
}
#[test]
fn test_eval_len_dataframe_with_data() {
let col = crate::runtime::DataFrameColumn {
name: "col1".to_string(),
values: vec![Value::Integer(1), Value::Integer(2), Value::Integer(3)],
};
let df = Value::DataFrame { columns: vec![col] };
let result = eval_len(&[df]).expect("should succeed");
assert_eq!(result, Value::Integer(3));
}
#[test]
fn test_eval_range_wrong_arg_count() {
let result = eval_range(&[]);
assert!(result.is_err());
let result = eval_range(&[
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
Value::Integer(4),
]);
assert!(result.is_err());
}
#[test]
fn test_eval_range_wrong_type() {
let result = eval_range(&[Value::from_string("5".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_range_zero_step() {
let result = eval_range(&[Value::Integer(0), Value::Integer(10), Value::Integer(0)]);
assert!(result.is_err());
}
#[test]
fn test_eval_type_wrong_arg_count() {
let result = eval_type(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_type_of_wrong_arg_count() {
let result = eval_type_of(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_is_nil_wrong_arg_count() {
let result = eval_is_nil(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_reverse_wrong_arg_count() {
let result = eval_reverse(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_reverse_wrong_type() {
let result = eval_reverse(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_push_wrong_arg_count() {
let result = eval_push(&[Value::Array(Arc::from(vec![]))]);
assert!(result.is_err());
}
#[test]
fn test_eval_push_wrong_type() {
let result = eval_push(&[Value::Integer(1), Value::Integer(2)]);
assert!(result.is_err());
}
#[test]
fn test_eval_pop_wrong_arg_count() {
let result = eval_pop(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_pop_wrong_type() {
let result = eval_pop(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_pop_empty_array() {
let result = eval_pop(&[Value::Array(Arc::from(vec![]))]).expect("should succeed");
assert_eq!(result, Value::nil());
}
#[test]
fn test_eval_sort_wrong_arg_count() {
let result = eval_sort(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_sort_wrong_type() {
let result = eval_sort(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_zip_wrong_arg_count() {
let result = eval_zip(&[Value::Array(Arc::from(vec![]))]);
assert!(result.is_err());
}
#[test]
fn test_eval_zip_wrong_type() {
let result = eval_zip(&[Value::Integer(1), Value::Integer(2)]);
assert!(result.is_err());
}
#[test]
fn test_eval_enumerate_wrong_arg_count() {
let result = eval_enumerate(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_enumerate_wrong_type() {
let result = eval_enumerate(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_assert_eq_wrong_arg_count() {
let result = eval_assert_eq(&[Value::Integer(1)]);
assert!(result.is_err());
}
#[test]
fn test_eval_assert_wrong_arg_count() {
let result = eval_assert(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_str_wrong_arg_count() {
let result = eval_str(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_to_string_wrong_arg_count() {
let result = eval_to_string(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_int_wrong_arg_count() {
let result = eval_int(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_int_invalid_string() {
let result = eval_int(&[Value::from_string("not a number".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_int_already_int() {
let result = eval_int(&[Value::Integer(42)]).expect("should succeed");
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_float_wrong_arg_count() {
let result = eval_float(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_float_invalid_string() {
let result = eval_float(&[Value::from_string("not a number".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_float_already_float() {
let result = eval_float(&[Value::Float(3.14)]).expect("should succeed");
assert_eq!(result, Value::Float(3.14));
}
#[test]
fn test_eval_bool_wrong_arg_count() {
let result = eval_bool(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_bool_already_bool() {
let result = eval_bool(&[Value::Bool(true)]).expect("should succeed");
assert_eq!(result, Value::Bool(true));
}
#[test]
fn test_eval_bool_float_zero() {
let result = eval_bool(&[Value::Float(0.0)]).expect("should succeed");
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_eval_bool_float_nonzero() {
let result = eval_bool(&[Value::Float(1.5)]).expect("should succeed");
assert_eq!(result, Value::Bool(true));
}
#[test]
fn test_eval_parse_int_wrong_arg_count() {
let result = eval_parse_int(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_parse_int_wrong_type() {
let result = eval_parse_int(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_parse_int_invalid_string() {
let result = eval_parse_int(&[Value::from_string("not a number".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_parse_float_wrong_arg_count() {
let result = eval_parse_float(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_parse_float_wrong_type() {
let result = eval_parse_float(&[Value::Float(3.14)]);
assert!(result.is_err());
}
#[test]
fn test_eval_parse_float_invalid_string() {
let result = eval_parse_float(&[Value::from_string("not a number".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_sleep_wrong_arg_count() {
let result = eval_sleep(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_sleep_wrong_type() {
let result = eval_sleep(&[Value::from_string("100".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_sleep_float() {
let result = eval_sleep(&[Value::Float(1.0)]).expect("should succeed");
assert_eq!(result, Value::Nil);
}
#[test]
fn test_eval_timestamp_wrong_arg_count() {
let result = eval_timestamp(&[Value::Integer(1)]);
assert!(result.is_err());
}
#[test]
fn test_eval_chrono_utc_now() {
let result = eval_chrono_utc_now(&[]).expect("should succeed");
if let Value::String(s) = result {
assert!(s.contains("T"), "Should be RFC3339 format");
} else {
panic!("Expected string result");
}
}
#[test]
fn test_eval_chrono_utc_now_wrong_arg_count() {
let result = eval_chrono_utc_now(&[Value::Integer(1)]);
assert!(result.is_err());
}
#[test]
fn test_eval_dataframe_new() {
let result = eval_dataframe_new(&[]).expect("should succeed");
if let Value::Object(obj) = result {
assert!(obj.get("__type").is_some());
} else {
panic!("Expected object result");
}
}
#[test]
fn test_eval_dataframe_new_wrong_arg_count() {
let result = eval_dataframe_new(&[Value::Integer(1)]);
assert!(result.is_err());
}
#[test]
fn test_eval_dataframe_from_csv_string() {
let csv = "name,age\nAlice,30\nBob,25";
let result = eval_dataframe_from_csv_string(&[Value::from_string(csv.to_string())])
.expect("should succeed");
if let Value::DataFrame { columns } = result {
assert_eq!(columns.len(), 2);
assert_eq!(columns[0].name, "name");
assert_eq!(columns[1].name, "age");
} else {
panic!("Expected DataFrame result");
}
}
#[test]
fn test_eval_dataframe_from_csv_string_empty() {
let result = eval_dataframe_from_csv_string(&[Value::from_string("".to_string())])
.expect("should succeed");
if let Value::DataFrame { columns } = result {
assert!(columns.is_empty());
}
}
#[test]
fn test_eval_dataframe_from_csv_string_wrong_type() {
let result = eval_dataframe_from_csv_string(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_dataframe_from_json() {
let json = r#"[{"name": "Alice", "age": 30}]"#;
let result = eval_dataframe_from_json(&[Value::from_string(json.to_string())])
.expect("should succeed");
if let Value::DataFrame { columns } = result {
assert!(!columns.is_empty());
} else {
panic!("Expected DataFrame result");
}
}
#[test]
fn test_eval_dataframe_from_json_empty() {
let result = eval_dataframe_from_json(&[Value::from_string("[]".to_string())])
.expect("should succeed");
if let Value::DataFrame { columns } = result {
assert!(columns.is_empty());
}
}
#[test]
fn test_eval_dataframe_from_json_invalid() {
let result = eval_dataframe_from_json(&[Value::from_string("not json".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_dataframe_from_json_wrong_type() {
let result = eval_dataframe_from_json(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_env_args() {
let result = eval_env_args(&[]).expect("should succeed");
if let Value::Array(_) = result {
} else {
panic!("Expected array result");
}
}
#[test]
fn test_eval_env_args_wrong_arg_count() {
let result = eval_env_args(&[Value::Integer(1)]);
assert!(result.is_err());
}
#[test]
fn test_eval_env_var_not_found() {
let result = eval_env_var(&[Value::from_string("__NONEXISTENT_VAR_12345__".to_string())])
.expect("should succeed");
if let Value::EnumVariant { variant_name, .. } = result {
assert_eq!(variant_name, "Err");
} else {
panic!("Expected EnumVariant result");
}
}
#[test]
fn test_eval_env_var_wrong_type() {
let result = eval_env_var(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_env_set_var_and_get() {
let key = "__TEST_ENV_VAR_RUCHY__";
let value = "test_value";
eval_env_set_var(&[
Value::from_string(key.to_string()),
Value::from_string(value.to_string()),
])
.expect("set should succeed");
let result =
eval_env_var(&[Value::from_string(key.to_string())]).expect("get should succeed");
if let Value::EnumVariant {
variant_name, data, ..
} = result
{
assert_eq!(variant_name, "Ok");
if let Some(d) = data {
assert_eq!(d[0], Value::from_string(value.to_string()));
}
}
eval_env_remove_var(&[Value::from_string(key.to_string())]).expect("remove should succeed");
}
#[test]
fn test_eval_env_set_var_wrong_type() {
let result = eval_env_set_var(&[Value::Integer(1), Value::Integer(2)]);
assert!(result.is_err());
}
#[test]
fn test_eval_env_remove_var_wrong_type() {
let result = eval_env_remove_var(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_env_vars() {
let result = eval_env_vars(&[]).expect("should succeed");
if let Value::Object(_) = result {
} else {
panic!("Expected object result");
}
}
#[test]
fn test_eval_env_vars_wrong_arg_count() {
let result = eval_env_vars(&[Value::Integer(1)]);
assert!(result.is_err());
}
#[test]
fn test_eval_env_current_dir() {
let result = eval_env_current_dir(&[]).expect("should succeed");
if let Value::String(s) = result {
assert!(!s.is_empty());
} else {
panic!("Expected string result");
}
}
#[test]
fn test_eval_env_current_dir_wrong_arg_count() {
let result = eval_env_current_dir(&[Value::Integer(1)]);
assert!(result.is_err());
}
#[test]
fn test_eval_env_temp_dir() {
let result = eval_env_temp_dir(&[]).expect("should succeed");
if let Value::String(s) = result {
assert!(!s.is_empty());
} else {
panic!("Expected string result");
}
}
#[test]
fn test_eval_env_temp_dir_wrong_arg_count() {
let result = eval_env_temp_dir(&[Value::Integer(1)]);
assert!(result.is_err());
}
#[test]
fn test_eval_path_join() {
let result = eval_path_join(&[
Value::from_string("/home".to_string()),
Value::from_string("user".to_string()),
])
.expect("should succeed");
if let Value::String(s) = result {
assert!(s.contains("home") && s.contains("user"));
}
}
#[test]
fn test_eval_path_join_wrong_type() {
let result = eval_path_join(&[Value::Integer(1), Value::Integer(2)]);
assert!(result.is_err());
}
#[test]
fn test_eval_path_parent() {
let result = eval_path_parent(&[Value::from_string("/home/user/file.txt".to_string())])
.expect("should succeed");
if let Value::String(s) = result {
assert!(s.contains("user"));
}
}
#[test]
fn test_eval_path_parent_root() {
let result =
eval_path_parent(&[Value::from_string("/".to_string())]).expect("should succeed");
assert_eq!(result, Value::Nil);
}
#[test]
fn test_eval_path_parent_wrong_type() {
let result = eval_path_parent(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_path_file_name() {
let result = eval_path_file_name(&[Value::from_string("/home/user/file.txt".to_string())])
.expect("should succeed");
assert_eq!(result, Value::from_string("file.txt".to_string()));
}
#[test]
fn test_eval_path_file_name_no_file() {
let result =
eval_path_file_name(&[Value::from_string("/".to_string())]).expect("should succeed");
assert_eq!(result, Value::Nil);
}
#[test]
fn test_eval_path_file_stem() {
let result = eval_path_file_stem(&[Value::from_string("/home/user/file.txt".to_string())])
.expect("should succeed");
assert_eq!(result, Value::from_string("file".to_string()));
}
#[test]
fn test_eval_path_extension() {
let result = eval_path_extension(&[Value::from_string("/home/user/file.txt".to_string())])
.expect("should succeed");
assert_eq!(result, Value::from_string("txt".to_string()));
}
#[test]
fn test_eval_path_extension_none() {
let result = eval_path_extension(&[Value::from_string("/home/user/file".to_string())])
.expect("should succeed");
assert_eq!(result, Value::Nil);
}
#[test]
fn test_eval_path_is_absolute() {
let result = eval_path_is_absolute(&[Value::from_string("/home/user".to_string())])
.expect("should succeed");
assert_eq!(result, Value::Bool(true));
let result = eval_path_is_absolute(&[Value::from_string("relative/path".to_string())])
.expect("should succeed");
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_eval_path_is_relative() {
let result = eval_path_is_relative(&[Value::from_string("relative/path".to_string())])
.expect("should succeed");
assert_eq!(result, Value::Bool(true));
let result = eval_path_is_relative(&[Value::from_string("/absolute/path".to_string())])
.expect("should succeed");
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_eval_path_with_extension() {
let result = eval_path_with_extension(&[
Value::from_string("/home/file.txt".to_string()),
Value::from_string("md".to_string()),
])
.expect("should succeed");
if let Value::String(s) = result {
assert!(s.ends_with(".md"));
}
}
#[test]
fn test_eval_path_with_file_name() {
let result = eval_path_with_file_name(&[
Value::from_string("/home/old.txt".to_string()),
Value::from_string("new.txt".to_string()),
])
.expect("should succeed");
if let Value::String(s) = result {
assert!(s.ends_with("new.txt"));
}
}
#[test]
fn test_eval_path_components() {
let result = eval_path_components(&[Value::from_string("/home/user/file".to_string())])
.expect("should succeed");
if let Value::Array(arr) = result {
assert!(arr.len() >= 3);
}
}
#[test]
fn test_eval_path_normalize() {
let result =
eval_path_normalize(&[Value::from_string("/home/../home/user/./file".to_string())])
.expect("should succeed");
if let Value::String(s) = result {
assert!(!s.contains(".."));
assert!(!s.contains("./"));
}
}
#[test]
fn test_eval_path_join_many() {
let components = Value::Array(Arc::from(vec![
Value::from_string("/home".to_string()),
Value::from_string("user".to_string()),
Value::from_string("file.txt".to_string()),
]));
let result = eval_path_join_many(&[components]).expect("should succeed");
if let Value::String(s) = result {
assert!(s.contains("home") && s.contains("user") && s.contains("file.txt"));
}
}
#[test]
fn test_eval_path_join_many_wrong_type() {
let result = eval_path_join_many(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_path_join_many_invalid_component() {
let components = Value::Array(Arc::from(vec![
Value::from_string("/home".to_string()),
Value::Integer(42), ]));
let result = eval_path_join_many(&[components]);
assert!(result.is_err());
}
#[test]
fn test_eval_json_parse_object() {
let result = eval_json_parse(&[Value::from_string(r#"{"key": "value"}"#.to_string())])
.expect("should succeed");
if let Value::Object(obj) = result {
assert!(obj.get("key").is_some());
} else {
panic!("Expected object result");
}
}
#[test]
fn test_eval_json_parse_array() {
let result = eval_json_parse(&[Value::from_string("[1, 2, 3]".to_string())])
.expect("should succeed");
if let Value::Array(arr) = result {
assert_eq!(arr.len(), 3);
} else {
panic!("Expected array result");
}
}
#[test]
fn test_eval_json_parse_primitives() {
let result =
eval_json_parse(&[Value::from_string("null".to_string())]).expect("should succeed");
assert_eq!(result, Value::Nil);
let result =
eval_json_parse(&[Value::from_string("true".to_string())]).expect("should succeed");
assert_eq!(result, Value::Bool(true));
let result =
eval_json_parse(&[Value::from_string("42".to_string())]).expect("should succeed");
assert_eq!(result, Value::Integer(42));
let result =
eval_json_parse(&[Value::from_string("3.14".to_string())]).expect("should succeed");
if let Value::Float(f) = result {
assert!((f - 3.14).abs() < 0.001);
}
let result = eval_json_parse(&[Value::from_string(r#""hello""#.to_string())])
.expect("should succeed");
assert_eq!(result, Value::from_string("hello".to_string()));
}
#[test]
fn test_eval_json_parse_invalid() {
let result = eval_json_parse(&[Value::from_string("not valid json".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_json_parse_wrong_type() {
let result = eval_json_parse(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_json_stringify() {
let obj = Value::Object(Arc::new({
let mut map = HashMap::new();
map.insert("key".to_string(), Value::from_string("value".to_string()));
map
}));
let result = eval_json_stringify(&[obj]).expect("should succeed");
if let Value::String(s) = result {
assert!(s.contains("key") && s.contains("value"));
}
}
#[test]
fn test_eval_json_stringify_primitives() {
let result = eval_json_stringify(&[Value::Integer(42)]).expect("should succeed");
assert_eq!(result, Value::from_string("42".to_string()));
let result = eval_json_stringify(&[Value::Bool(true)]).expect("should succeed");
assert_eq!(result, Value::from_string("true".to_string()));
let result = eval_json_stringify(&[Value::Nil]).expect("should succeed");
assert_eq!(result, Value::from_string("null".to_string()));
}
#[test]
fn test_eval_json_pretty() {
let obj = Value::Object(Arc::new({
let mut map = HashMap::new();
map.insert("key".to_string(), Value::from_string("value".to_string()));
map
}));
let result = eval_json_pretty(&[obj]).expect("should succeed");
if let Value::String(s) = result {
assert!(s.contains('\n'), "Pretty print should have newlines");
}
}
#[test]
fn test_eval_json_validate_valid() {
let result = eval_json_validate(&[Value::from_string(r#"{"key": "value"}"#.to_string())])
.expect("should succeed");
assert_eq!(result, Value::Bool(true));
}
#[test]
fn test_eval_json_validate_invalid() {
let result = eval_json_validate(&[Value::from_string("not json".to_string())])
.expect("should succeed");
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_eval_json_validate_wrong_type() {
let result = eval_json_validate(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_json_type() {
let result =
eval_json_type(&[Value::from_string("null".to_string())]).expect("should succeed");
assert_eq!(result, Value::from_string("null".to_string()));
let result =
eval_json_type(&[Value::from_string("true".to_string())]).expect("should succeed");
assert_eq!(result, Value::from_string("boolean".to_string()));
let result =
eval_json_type(&[Value::from_string("42".to_string())]).expect("should succeed");
assert_eq!(result, Value::from_string("number".to_string()));
let result = eval_json_type(&[Value::from_string(r#""hello""#.to_string())])
.expect("should succeed");
assert_eq!(result, Value::from_string("string".to_string()));
let result =
eval_json_type(&[Value::from_string("[]".to_string())]).expect("should succeed");
assert_eq!(result, Value::from_string("array".to_string()));
let result =
eval_json_type(&[Value::from_string("{}".to_string())]).expect("should succeed");
assert_eq!(result, Value::from_string("object".to_string()));
}
#[test]
fn test_eval_json_type_invalid() {
let result = eval_json_type(&[Value::from_string("not json".to_string())]);
assert!(result.is_err());
}
#[test]
fn test_eval_json_merge() {
let obj1 = Value::Object(Arc::new({
let mut map = HashMap::new();
map.insert("a".to_string(), Value::Integer(1));
map
}));
let obj2 = Value::Object(Arc::new({
let mut map = HashMap::new();
map.insert("b".to_string(), Value::Integer(2));
map
}));
let result = eval_json_merge(&[obj1, obj2]).expect("should succeed");
if let Value::Object(obj) = result {
assert!(obj.get("a").is_some());
assert!(obj.get("b").is_some());
} else {
panic!("Expected object result");
}
}
#[test]
fn test_eval_json_get() {
let obj = Value::Object(Arc::new({
let mut map = HashMap::new();
let mut nested = HashMap::new();
nested.insert("inner".to_string(), Value::Integer(42));
map.insert("outer".to_string(), Value::Object(Arc::new(nested)));
map
}));
let result = eval_json_get(&[obj, Value::from_string("outer.inner".to_string())])
.expect("should succeed");
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_json_get_not_found() {
let obj = Value::Object(Arc::new(HashMap::new()));
let result = eval_json_get(&[obj, Value::from_string("nonexistent".to_string())])
.expect("should succeed");
assert_eq!(result, Value::Nil);
}
#[test]
fn test_eval_string_new() {
let result = eval_string_new(&[]).expect("should succeed");
assert_eq!(result, Value::from_string("".to_string()));
}
#[test]
fn test_eval_string_new_wrong_arg_count() {
let result = eval_string_new(&[Value::Integer(1)]);
assert!(result.is_err());
}
#[test]
fn test_eval_string_from() {
let result = eval_string_from(&[Value::Integer(42)]).expect("should succeed");
assert_eq!(result, Value::from_string("42".to_string()));
let result =
eval_string_from(&[Value::from_string("hello".to_string())]).expect("should succeed");
assert_eq!(result, Value::from_string("hello".to_string()));
}
#[test]
fn test_eval_string_from_wrong_arg_count() {
let result = eval_string_from(&[]);
assert!(result.is_err());
}
#[test]
fn test_eval_string_from_utf8_valid() {
let bytes = Value::Array(Arc::from(vec![
Value::Byte(72), Value::Byte(105), ]));
let result = eval_string_from_utf8(&[bytes]).expect("should succeed");
if let Value::EnumVariant {
variant_name, data, ..
} = result
{
assert_eq!(variant_name, "Ok");
if let Some(d) = data {
assert_eq!(d[0], Value::from_string("Hi".to_string()));
}
} else {
panic!("Expected EnumVariant result");
}
}
#[test]
fn test_eval_string_from_utf8_invalid() {
let bytes = Value::Array(Arc::from(vec![Value::Byte(0xFF), Value::Byte(0xFE)]));
let result = eval_string_from_utf8(&[bytes]).expect("should succeed");
if let Value::EnumVariant { variant_name, .. } = result {
assert_eq!(variant_name, "Err");
} else {
panic!("Expected EnumVariant result");
}
}
#[test]
fn test_eval_string_from_utf8_wrong_type() {
let result = eval_string_from_utf8(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_string_from_utf8_non_byte_array() {
let arr = Value::Array(Arc::from(vec![Value::Integer(72)])); let result = eval_string_from_utf8(&[arr]);
assert!(result.is_err());
}
#[test]
fn test_eval_fs_exists_true() {
let result =
eval_fs_exists(&[Value::from_string(".".to_string())]).expect("should succeed");
assert_eq!(result, Value::Bool(true));
}
#[test]
fn test_eval_fs_exists_false() {
let result =
eval_fs_exists(&[Value::from_string("__nonexistent_path_12345__".to_string())])
.expect("should succeed");
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_eval_fs_exists_wrong_type() {
let result = eval_fs_exists(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_fs_is_file() {
let result = eval_fs_is_file(&[Value::from_string("Cargo.toml".to_string())])
.expect("should succeed");
assert_eq!(result, Value::Bool(true));
let result =
eval_fs_is_file(&[Value::from_string(".".to_string())]).expect("should succeed");
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_eval_fs_is_file_wrong_type() {
let result = eval_fs_is_file(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_fs_read_nonexistent() {
let result = eval_fs_read(&[Value::from_string("__nonexistent_file__".to_string())])
.expect("should succeed");
if let Value::EnumVariant { variant_name, .. } = result {
assert_eq!(variant_name, "Err");
}
}
#[test]
fn test_eval_fs_read_wrong_type() {
let result = eval_fs_read(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_fs_write_wrong_type() {
let result = eval_fs_write(&[Value::Integer(1), Value::Integer(2)]);
assert!(result.is_err());
}
#[test]
fn test_eval_fs_create_dir_wrong_type() {
let result = eval_fs_create_dir(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_fs_remove_file_nonexistent() {
let result = eval_fs_remove_file(&[Value::from_string("__nonexistent_file__".to_string())])
.expect("should succeed");
if let Value::EnumVariant { variant_name, .. } = result {
assert_eq!(variant_name, "Ok");
}
}
#[test]
fn test_eval_fs_remove_file_wrong_type() {
let result = eval_fs_remove_file(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_fs_remove_dir_wrong_type() {
let result = eval_fs_remove_dir(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_fs_copy_wrong_type() {
let result = eval_fs_copy(&[Value::Integer(1), Value::Integer(2)]);
assert!(result.is_err());
}
#[test]
fn test_eval_fs_rename_wrong_type() {
let result = eval_fs_rename(&[Value::Integer(1), Value::Integer(2)]);
assert!(result.is_err());
}
#[test]
fn test_eval_fs_metadata_wrong_type() {
let result = eval_fs_metadata(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_fs_read_dir_wrong_type() {
let result = eval_fs_read_dir(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_eval_fs_canonicalize_wrong_type() {
let result = eval_fs_canonicalize(&[Value::Integer(42)]);
assert!(result.is_err());
}
#[test]
fn test_try_eval_dataframe_function_new() {
let result =
try_eval_dataframe_function("__builtin_dataframe_new__", &[]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_dataframe_function_from_csv() {
let result = try_eval_dataframe_function(
"__builtin_dataframe_from_csv_string__",
&[Value::from_string("a,b\n1,2".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_dataframe_function_from_json() {
let result = try_eval_dataframe_function(
"__builtin_dataframe_from_json__",
&[Value::from_string("[]".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_dataframe_function_unknown() {
let result = try_eval_dataframe_function("unknown", &[]).expect("should succeed");
assert!(result.is_none());
}
#[test]
fn test_try_eval_environment_function_args() {
let result =
try_eval_environment_function("__builtin_env_args__", &[]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_environment_function_vars() {
let result =
try_eval_environment_function("__builtin_env_vars__", &[]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_environment_function_current_dir() {
let result = try_eval_environment_function("__builtin_env_current_dir__", &[])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_environment_function_temp_dir() {
let result =
try_eval_environment_function("__builtin_env_temp_dir__", &[]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_environment_function_unknown() {
let result = try_eval_environment_function("unknown", &[]).expect("should succeed");
assert!(result.is_none());
}
#[test]
fn test_try_eval_fs_function_read() {
let result = try_eval_fs_function(
"__builtin_fs_read__",
&[Value::from_string("__nonexistent__".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_fs_function_exists() {
let result = try_eval_fs_function(
"__builtin_fs_exists__",
&[Value::from_string(".".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_fs_function_is_file() {
let result = try_eval_fs_function(
"__builtin_fs_is_file__",
&[Value::from_string("Cargo.toml".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_fs_function_unknown() {
let result = try_eval_fs_function("unknown", &[]).expect("should succeed");
assert!(result.is_none());
}
#[test]
fn test_try_eval_path_function_join() {
let result = try_eval_path_function(
"__builtin_path_join__",
&[
Value::from_string("/home".to_string()),
Value::from_string("user".to_string()),
],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_path_function_parent() {
let result = try_eval_path_function(
"__builtin_path_parent__",
&[Value::from_string("/home/user".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_path_function_file_name() {
let result = try_eval_path_function(
"__builtin_path_file_name__",
&[Value::from_string("/home/file.txt".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_path_function_extension() {
let result = try_eval_path_function(
"__builtin_path_extension__",
&[Value::from_string("file.txt".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_path_function_is_absolute() {
let result = try_eval_path_function(
"__builtin_path_is_absolute__",
&[Value::from_string("/home".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_path_function_components() {
let result = try_eval_path_function(
"__builtin_path_components__",
&[Value::from_string("/home/user".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_path_function_normalize() {
let result = try_eval_path_function(
"__builtin_path_normalize__",
&[Value::from_string("/home/../user".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_path_function_unknown() {
let result = try_eval_path_function("unknown", &[]).expect("should succeed");
assert!(result.is_none());
}
#[test]
fn test_try_eval_json_function_parse() {
let result = try_eval_json_function(
"__builtin_json_parse__",
&[Value::from_string("{}".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_json_function_stringify() {
let result = try_eval_json_function("__builtin_json_stringify__", &[Value::Integer(42)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_json_function_pretty() {
let result = try_eval_json_function("__builtin_json_pretty__", &[Value::Integer(42)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_json_function_validate() {
let result = try_eval_json_function(
"__builtin_json_validate__",
&[Value::from_string("{}".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_json_function_type() {
let result = try_eval_json_function(
"__builtin_json_type__",
&[Value::from_string("42".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_json_function_unknown() {
let result = try_eval_json_function("unknown", &[]).expect("should succeed");
assert!(result.is_none());
}
#[test]
fn test_try_eval_string_function_new() {
let result =
try_eval_string_function("__builtin_String_new__", &[]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_string_function_from() {
let result = try_eval_string_function("__builtin_String_from__", &[Value::Integer(42)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_try_eval_string_function_unknown() {
let result = try_eval_string_function("unknown", &[]).expect("should succeed");
assert!(result.is_none());
}
#[test]
fn test_try_eval_file_function_file_open() {
let result = try_eval_file_function(
"File_open",
&[Value::from_string("__nonexistent__".to_string())],
);
assert!(result.is_err()); }
#[test]
fn test_try_eval_file_function_unknown() {
let result = try_eval_file_function("unknown", &[]).expect("should succeed");
assert!(result.is_none());
}
#[test]
fn test_format_println_output_interpolation() {
let output = format_println_output(&[
Value::from_string("Hello {}!".to_string()),
Value::from_string("World".to_string()),
]);
assert_eq!(output, "Hello World!\n");
}
#[test]
fn test_format_println_output_multiple_placeholders() {
let output = format_println_output(&[
Value::from_string("{} + {} = {}".to_string()),
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]);
assert_eq!(output, "1 + 2 = 3\n");
}
#[test]
fn test_format_println_output_no_placeholders() {
let output = format_println_output(&[
Value::from_string("Hello".to_string()),
Value::from_string("World".to_string()),
]);
assert_eq!(output, "Hello World\n");
}
#[test]
fn test_format_println_output_non_string_first() {
let output = format_println_output(&[Value::Integer(42), Value::Integer(43)]);
assert_eq!(output, "42 43\n");
}
#[test]
fn test_format_value_for_println() {
assert_eq!(
format_value_for_println(&Value::from_string("hello".to_string())),
"hello"
);
assert_eq!(format_value_for_println(&Value::Integer(42)), "42");
assert_eq!(format_value_for_println(&Value::Float(3.14)), "3.14");
assert_eq!(format_value_for_println(&Value::Bool(true)), "true");
assert_eq!(format_value_for_println(&Value::Nil), "nil");
}
#[test]
fn test_infer_value_type_integer() {
let result = infer_value_type("42");
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_infer_value_type_float() {
let result = infer_value_type("3.14");
if let Value::Float(f) = result {
assert!((f - 3.14).abs() < 0.001);
} else {
panic!("Expected float");
}
}
#[test]
fn test_infer_value_type_string() {
let result = infer_value_type("hello");
assert_eq!(result, Value::from_string("hello".to_string()));
}
#[test]
fn test_eval_builtin_function_sort() {
let arr = Value::Array(Arc::from(vec![
Value::Integer(3),
Value::Integer(1),
Value::Integer(2),
]));
let result = eval_builtin_function("__builtin_sort__", &[arr]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_eval_builtin_function_zip() {
let a = Value::Array(Arc::from(vec![Value::Integer(1)]));
let b = Value::Array(Arc::from(vec![Value::Integer(2)]));
let result = eval_builtin_function("__builtin_zip__", &[a, b]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_eval_builtin_function_enumerate() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1)]));
let result =
eval_builtin_function("__builtin_enumerate__", &[arr]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_eval_builtin_function_sin() {
let result =
eval_builtin_function("__builtin_sin__", &[Value::Float(0.0)]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_eval_builtin_function_cos() {
let result =
eval_builtin_function("__builtin_cos__", &[Value::Float(0.0)]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_eval_builtin_function_tan() {
let result =
eval_builtin_function("__builtin_tan__", &[Value::Float(0.0)]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_eval_builtin_function_log() {
let result =
eval_builtin_function("__builtin_log__", &[Value::Float(1.0)]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_eval_builtin_function_log10() {
let result = eval_builtin_function("__builtin_log10__", &[Value::Float(10.0)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_eval_builtin_function_exp() {
let result =
eval_builtin_function("__builtin_exp__", &[Value::Float(0.0)]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_eval_builtin_function_random() {
let result = eval_builtin_function("__builtin_random__", &[]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_eval_builtin_function_timestamp() {
let result = eval_builtin_function("__builtin_timestamp__", &[]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_eval_builtin_function_chrono_utc_now() {
let result =
eval_builtin_function("__builtin_chrono_utc_now__", &[]).expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_eval_builtin_function_assert_eq() {
let result = eval_builtin_function(
"__builtin_assert_eq__",
&[Value::Integer(1), Value::Integer(1)],
)
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_eval_builtin_function_assert() {
let result = eval_builtin_function("__builtin_assert__", &[Value::Bool(true)])
.expect("should succeed");
assert!(result.is_some());
}
#[test]
fn test_eval_builtin_function_pow() {
let result =
eval_builtin_function("__builtin_pow__", &[Value::Integer(2), Value::Integer(3)])
.expect("should succeed");
assert_eq!(result, Some(Value::Integer(8)));
}
#[test]
fn test_eval_builtin_function_parse_int() {
let result = eval_builtin_function(
"__builtin_parse_int__",
&[Value::from_string("42".to_string())],
)
.expect("should succeed");
assert_eq!(result, Some(Value::Integer(42)));
}
#[test]
fn test_eval_builtin_function_parse_float() {
let result = eval_builtin_function(
"__builtin_parse_float__",
&[Value::from_string("3.14".to_string())],
)
.expect("should succeed");
assert!(result.is_some());
}
}