fix: 修复字符串转义、对象相等比较、除零处理

- 词法分析器:字符串字面量中支持 \n \t \r " \ 转义序列,未知转义序列报告错误并跳过剩余字符串
- 解释器:对象相等比较新增深度比较(键值对逐一比对)
- 解释器:除法和取模运算在除数为零时返回 RuntimeError,避免产生 inf/NaN

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
0264408
2026-06-16 17:02:30 +08:00
parent 983a7d8b34
commit 284a29a4be
2 changed files with 139 additions and 1 deletions
+70
View File
@@ -308,6 +308,12 @@ impl Interpreter {
}),
},
crate::ast::expr::BinaryOp::Div => match (l,r) {
(Value::Number(_), Value::Number(b)) if b == 0.0 => {
Err(RuntimeError::RuntimeError {
message: "Division by zero".to_string(),
token: None,
})
}
(Value::Number(a), Value::Number(b)) => Ok(Value::Number(a/b)),
_ => Err(RuntimeError::RuntimeError {
message: "Invalid '/' operands".to_string(),
@@ -315,6 +321,12 @@ impl Interpreter {
}),
},
crate::ast::expr::BinaryOp::Mod => match (l,r) {
(Value::Number(_), Value::Number(b)) if b == 0.0 => {
Err(RuntimeError::RuntimeError {
message: "Modulo by zero".to_string(),
token: None,
})
}
(Value::Number(a), Value::Number(b)) => Ok(Value::Number(a%b)),
_ => Err(RuntimeError::RuntimeError {
message: "Invalid '%' operands".to_string(),
@@ -423,6 +435,14 @@ impl Interpreter {
if x.len() != y.len() { return false; }
x.iter().zip(y.iter()).all(|(a,b)| self.is_equal(a,b))
}
(Value::Object(x), Value::Object(y)) => {
let x = x.borrow();
let y = y.borrow();
if x.len() != y.len() { return false; }
x.iter().all(|(k, v)| {
y.get(k).map_or(false, |yv| self.is_equal(v, yv))
})
}
_ => false,
}
}
@@ -1003,6 +1023,40 @@ mod tests {
assert_bool(&eval_expr("[1] == [1, 2]"), false);
}
// =========================================================================
// Object equality
// =========================================================================
#[test]
fn eval_object_equality_same() {
let interp = run("let a = {x: 1, y: 2}; let b = {x: 1, y: 2}; let r = a == b;");
assert_bool(&get_var(&interp, "r"), true);
}
#[test]
fn eval_object_equality_different_values() {
let interp = run("let a = {x: 1}; let b = {x: 2}; let r = a == b;");
assert_bool(&get_var(&interp, "r"), false);
}
#[test]
fn eval_object_equality_different_keys() {
let interp = run("let a = {x: 1}; let b = {y: 1}; let r = a == b;");
assert_bool(&get_var(&interp, "r"), false);
}
#[test]
fn eval_object_equality_different_size() {
let interp = run("let a = {x: 1}; let b = {x: 1, y: 2}; let r = a == b;");
assert_bool(&get_var(&interp, "r"), false);
}
#[test]
fn eval_object_equality_empty() {
let interp = run("let a = {}; let b = {}; let r = a == b;");
assert_bool(&get_var(&interp, "r"), true);
}
// =========================================================================
// Strings as indexable
// =========================================================================
@@ -1119,6 +1173,22 @@ mod tests {
assert!(result.is_err(), "Expected error for undefined variable");
}
#[test]
fn error_division_by_zero() {
let result = std::panic::catch_unwind(|| {
eval_expr("1 / 0");
});
assert!(result.is_err(), "Expected error for division by zero");
}
#[test]
fn error_modulo_by_zero() {
let result = std::panic::catch_unwind(|| {
eval_expr("1 % 0");
});
assert!(result.is_err(), "Expected error for modulo by zero");
}
#[test]
fn error_type_mismatch_negate_string() {
let result = std::panic::catch_unwind(|| {