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(crate) use crate::runtime::eval_builtin_fs::*;
pub(crate) use crate::runtime::eval_builtin_json_ops::*;
pub(crate) use crate::runtime::eval_builtin_path::*;
pub(crate) use crate::runtime::eval_builtin_platform::*;
pub fn eval_builtin_function(
name: &str,
args: &[Value],
) -> Result<Option<Value>, InterpreterError> {
type Handler = fn(&str, &[Value]) -> Result<Option<Value>, InterpreterError>;
let handlers: &[Handler] = &[
try_eval_io_function,
try_eval_math_function,
try_eval_utility_function,
try_eval_collection_function,
try_eval_conversion_function,
try_eval_time_function,
try_eval_dataframe_function,
try_eval_environment_function,
try_eval_fs_function,
try_eval_stdlib003,
try_eval_stdlib005,
try_eval_path_function,
try_eval_json_function,
try_eval_file_function,
try_eval_string_function,
];
for handler in handlers {
if let Some(result) = handler(name, args)? {
return Ok(Some(result));
}
}
#[cfg(not(target_arch = "wasm32"))]
{
let platform_handlers: &[Handler] = &[
try_eval_http_function,
try_eval_html_function,
try_eval_process_function,
];
for handler in platform_handlers {
if let Some(result) = handler(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()))
}
#[cfg(test)]
#[path = "eval_builtin_unit_tests.rs"]
mod tests;
#[cfg(test)]
#[allow(clippy::expect_used)]
#[path = "eval_builtin_prop_tests.rs"]
mod property_tests_builtin;