feat: 添加内置函数支持(len, typeof, push, pop),更新相关错误处理和测试用例
This commit is contained in:
@@ -397,7 +397,7 @@ impl Interpreter {
|
||||
fn call_function(&mut self, func_val: Value, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
match func_val {
|
||||
Value::NativeFunction(native_fn) => {
|
||||
Ok(native_fn(args))
|
||||
native_fn(args)
|
||||
}
|
||||
Value::Function(f) => {
|
||||
let env = Rc::new(RefCell::new(Env::new(Some(Rc::clone(&f.env)))));
|
||||
@@ -1421,5 +1421,196 @@ mod tests {
|
||||
});
|
||||
assert!(result.is_err(), "Expected error for index on non-array");
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Builtins: len
|
||||
// =========================================================================
|
||||
|
||||
#[test]
|
||||
fn eval_len_string() {
|
||||
assert_num(&eval_expr("len(\"hello\")"), 5.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_len_empty_string() {
|
||||
assert_num(&eval_expr("len(\"\")"), 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_len_array() {
|
||||
assert_num(&eval_expr("len([1, 2, 3])"), 3.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_len_empty_array() {
|
||||
assert_num(&eval_expr("len([])"), 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_len_object() {
|
||||
assert_num(&eval_expr("len({a: 1, b: 2})"), 2.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_len_empty_object() {
|
||||
assert_num(&eval_expr("len({})"), 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_len_no_args() {
|
||||
let result = std::panic::catch_unwind(|| {
|
||||
eval_expr("len()");
|
||||
});
|
||||
assert!(result.is_err(), "Expected error for len() with no args");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_len_number() {
|
||||
let result = std::panic::catch_unwind(|| {
|
||||
eval_expr("len(42)");
|
||||
});
|
||||
assert!(result.is_err(), "Expected error for len(42)");
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Builtins: typeof
|
||||
// =========================================================================
|
||||
|
||||
#[test]
|
||||
fn eval_typeof_number() {
|
||||
match eval_expr("typeof(42)") {
|
||||
Value::String(s) => assert_eq!(s, "number"),
|
||||
v => panic!("Expected String, got {:?}", v),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_typeof_string() {
|
||||
match eval_expr("typeof(\"hello\")") {
|
||||
Value::String(s) => assert_eq!(s, "string"),
|
||||
v => panic!("Expected String, got {:?}", v),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_typeof_bool() {
|
||||
match eval_expr("typeof(true)") {
|
||||
Value::String(s) => assert_eq!(s, "bool"),
|
||||
v => panic!("Expected String, got {:?}", v),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_typeof_nil() {
|
||||
match eval_expr("typeof(nil)") {
|
||||
Value::String(s) => assert_eq!(s, "nil"),
|
||||
v => panic!("Expected String, got {:?}", v),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_typeof_object() {
|
||||
match eval_expr("typeof({a: 1})") {
|
||||
Value::String(s) => assert_eq!(s, "object"),
|
||||
v => panic!("Expected String, got {:?}", v),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_typeof_array() {
|
||||
match eval_expr("typeof([1, 2])") {
|
||||
Value::String(s) => assert_eq!(s, "array"),
|
||||
v => panic!("Expected String, got {:?}", v),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_typeof_function() {
|
||||
let interp = run("fn f() {}");
|
||||
// f is a variable, not an expression — use get_var
|
||||
match get_var(&interp, "f") {
|
||||
Value::Function(_) => {} // just verify it's a function
|
||||
v => panic!("Expected Function, got {:?}", v),
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Builtins: push / pop
|
||||
// =========================================================================
|
||||
|
||||
#[test]
|
||||
fn eval_push_returns_value() {
|
||||
assert_num(&eval_expr("push([1, 2], 3)"), 3.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_push_modifies_array() {
|
||||
let interp = run("let arr = [1, 2]; push(arr, 3);");
|
||||
match get_var(&interp, "arr") {
|
||||
Value::Array(arr) => {
|
||||
let arr = arr.borrow();
|
||||
assert_eq!(arr.len(), 3);
|
||||
assert_num(&arr[0], 1.0);
|
||||
assert_num(&arr[1], 2.0);
|
||||
assert_num(&arr[2], 3.0);
|
||||
}
|
||||
v => panic!("Expected Array, got {:?}", v),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_push_string_to_array() {
|
||||
let interp = run("let arr = [\"a\"]; push(arr, \"b\");");
|
||||
match get_var(&interp, "arr") {
|
||||
Value::Array(arr) => {
|
||||
let arr = arr.borrow();
|
||||
assert_eq!(arr.len(), 2);
|
||||
}
|
||||
v => panic!("Expected Array, got {:?}", v),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_pop_returns_last() {
|
||||
assert_num(&eval_expr("pop([1, 2, 3])"), 3.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_pop_modifies_array() {
|
||||
let interp = run("let arr = [1, 2, 3]; pop(arr);");
|
||||
match get_var(&interp, "arr") {
|
||||
Value::Array(arr) => {
|
||||
let arr = arr.borrow();
|
||||
assert_eq!(arr.len(), 2);
|
||||
assert_num(&arr[0], 1.0);
|
||||
assert_num(&arr[1], 2.0);
|
||||
}
|
||||
v => panic!("Expected Array, got {:?}", v),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_pop_empty_array() {
|
||||
let result = std::panic::catch_unwind(|| {
|
||||
eval_expr("pop([])");
|
||||
});
|
||||
assert!(result.is_err(), "Expected error for pop([])");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_push_non_array() {
|
||||
let result = std::panic::catch_unwind(|| {
|
||||
eval_expr("push(42, 1)");
|
||||
});
|
||||
assert!(result.is_err(), "Expected error for push on non-array");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_pop_non_array() {
|
||||
let result = std::panic::catch_unwind(|| {
|
||||
eval_expr("pop(42)");
|
||||
});
|
||||
assert!(result.is_err(), "Expected error for pop on non-array");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user