//! 标准库:string(split, trim, substring, replace, contains, upper, lower, starts_with, ends_with) use crate::error::RuntimeError; use crate::interpreter::{Runtime, Value}; use std::cell::RefCell; use std::rc::Rc; fn require_string(val: &Value, arg_name: &str) -> Result { match val { Value::String(s) => Ok(s.clone()), _ => Err(RuntimeError::RuntimeError { message: format!("{} must be a string", arg_name), token: None, }), } } // ============================================================================ // split(str, delim) → array of substrings // ============================================================================ pub fn split(_interp: &mut dyn Runtime, args: Vec) -> Result { if args.len() < 2 { return Err(RuntimeError::RuntimeError { message: "split() expects 2 arguments (string, delimiter)".into(), token: None, }); } let s = require_string(&args[0], "First argument")?; let delim = require_string(&args[1], "Second argument (delimiter)")?; let parts: Vec = if delim.is_empty() { // Split by empty string → characters s.chars().map(|c| Value::String(c.to_string())).collect() } else { s.split(&delim).map(|p| Value::String(p.to_string())).collect() }; Ok(Value::Array(Rc::new(RefCell::new(parts)))) } // ============================================================================ // trim(str) → string with leading/trailing whitespace removed // ============================================================================ pub fn trim(_interp: &mut dyn Runtime, args: Vec) -> Result { if args.is_empty() { return Err(RuntimeError::RuntimeError { message: "trim() expects 1 argument (string)".into(), token: None, }); } let s = require_string(&args[0], "Argument")?; Ok(Value::String(s.trim().to_string())) } // ============================================================================ // substring(str, start, len?) → substring // ============================================================================ pub fn substring(_interp: &mut dyn Runtime, args: Vec) -> Result { if args.is_empty() { return Err(RuntimeError::RuntimeError { message: "substring() expects at least 2 arguments (string, start, len?)".into(), token: None, }); } let s = require_string(&args[0], "First argument")?; let start = as_usize(&args[1], "Second argument (start)")?; let chars: Vec = s.chars().collect(); let start = start.min(chars.len()); let end = if args.len() >= 3 { (start + as_usize(&args[2], "Third argument (length)")?).min(chars.len()) } else { chars.len() }; Ok(Value::String(chars[start..end].iter().collect())) } // ============================================================================ // replace(str, old, new) → string with all occurrences replaced // ============================================================================ pub fn replace(_interp: &mut dyn Runtime, args: Vec) -> Result { if args.len() < 3 { return Err(RuntimeError::RuntimeError { message: "replace() expects 3 arguments (string, old, new)".into(), token: None, }); } let s = require_string(&args[0], "First argument")?; let old = require_string(&args[1], "Second argument (old)")?; let new = require_string(&args[2], "Third argument (new)")?; if old.is_empty() { return Err(RuntimeError::RuntimeError { message: "replace(): 'old' must not be empty".into(), token: None, }); } Ok(Value::String(s.replace(&old, &new))) } // ============================================================================ // contains(str, needle) → bool // ============================================================================ pub fn contains(_interp: &mut dyn Runtime, args: Vec) -> Result { if args.len() < 2 { return Err(RuntimeError::RuntimeError { message: "contains() expects 2 arguments (string, needle)".into(), token: None, }); } let s = require_string(&args[0], "First argument")?; let needle = require_string(&args[1], "Second argument (needle)")?; Ok(Value::Bool(s.contains(&needle))) } // ============================================================================ // upper(str) → uppercase (ASCII) // ============================================================================ pub fn upper(_interp: &mut dyn Runtime, args: Vec) -> Result { if args.is_empty() { return Err(RuntimeError::RuntimeError { message: "upper() expects 1 argument (string)".into(), token: None, }); } let s = require_string(&args[0], "Argument")?; Ok(Value::String(s.to_ascii_uppercase())) } // ============================================================================ // lower(str) → lowercase (ASCII) // ============================================================================ pub fn lower(_interp: &mut dyn Runtime, args: Vec) -> Result { if args.is_empty() { return Err(RuntimeError::RuntimeError { message: "lower() expects 1 argument (string)".into(), token: None, }); } let s = require_string(&args[0], "Argument")?; Ok(Value::String(s.to_ascii_lowercase())) } // ============================================================================ // starts_with(str, prefix) → bool // ============================================================================ pub fn starts_with(_interp: &mut dyn Runtime, args: Vec) -> Result { if args.len() < 2 { return Err(RuntimeError::RuntimeError { message: "starts_with() expects 2 arguments (string, prefix)".into(), token: None, }); } let s = require_string(&args[0], "First argument")?; let prefix = require_string(&args[1], "Second argument (prefix)")?; Ok(Value::Bool(s.starts_with(&prefix))) } // ============================================================================ // ends_with(str, suffix) → bool // ============================================================================ pub fn ends_with(_interp: &mut dyn Runtime, args: Vec) -> Result { if args.len() < 2 { return Err(RuntimeError::RuntimeError { message: "ends_with() expects 2 arguments (string, suffix)".into(), token: None, }); } let s = require_string(&args[0], "First argument")?; let suffix = require_string(&args[1], "Second argument (suffix)")?; Ok(Value::Bool(s.ends_with(&suffix))) } // ============================================================================ // Helper // ============================================================================ fn as_usize(val: &Value, arg_name: &str) -> Result { match val { Value::Number(n) => { if *n < 0.0 || n.fract() != 0.0 { return Err(RuntimeError::RuntimeError { message: format!("{} must be a non-negative integer, got {}", arg_name, n), token: None, }); } Ok(*n as usize) } _ => Err(RuntimeError::RuntimeError { message: format!("{} must be a number", arg_name), token: None, }), } }