feat: 添加三元运算符支持,更新解析器和解释器以处理条件表达式
This commit is contained in:
@@ -73,6 +73,13 @@ pub enum Expr {
|
|||||||
right: Box<Expr>,
|
right: Box<Expr>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// 三元运算:condition ? then_branch : else_branch
|
||||||
|
Ternary {
|
||||||
|
condition: Box<Expr>,
|
||||||
|
then_branch: Box<Expr>,
|
||||||
|
else_branch: Box<Expr>,
|
||||||
|
},
|
||||||
|
|
||||||
/// 函数调用
|
/// 函数调用
|
||||||
Call {
|
Call {
|
||||||
callee: Box<Expr>,
|
callee: Box<Expr>,
|
||||||
|
|||||||
@@ -361,6 +361,14 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Expr::Ternary { condition, then_branch, else_branch } => {
|
||||||
|
let cond = self.evaluate(*condition)?;
|
||||||
|
if self.is_truthy(&cond) {
|
||||||
|
self.evaluate(*then_branch)
|
||||||
|
} else {
|
||||||
|
self.evaluate(*else_branch)
|
||||||
|
}
|
||||||
|
}
|
||||||
Expr::Call { callee, arguments } => {
|
Expr::Call { callee, arguments } => {
|
||||||
let func = self.evaluate(*callee)?;
|
let func = self.evaluate(*callee)?;
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
@@ -730,6 +738,57 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Ternary
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_ternary_true_condition() {
|
||||||
|
assert_num(&eval_expr("true ? 1 : 2"), 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_ternary_false_condition() {
|
||||||
|
assert_num(&eval_expr("false ? 1 : 2"), 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_ternary_truthy_condition() {
|
||||||
|
// Non-nil, non-false values are truthy
|
||||||
|
assert_num(&eval_expr("1 ? 42 : 99"), 42.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_ternary_nil_condition() {
|
||||||
|
assert_num(&eval_expr("nil ? 1 : 2"), 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_ternary_nested() {
|
||||||
|
let interp = run("let x = 5; let r = x > 3 ? (x > 10 ? 100 : 50) : 0;");
|
||||||
|
assert_num(&get_var(&interp, "r"), 50.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_ternary_with_computed_branches() {
|
||||||
|
let interp = run("let x = 10; let r = x > 5 ? x * 2 : x / 2;");
|
||||||
|
assert_num(&get_var(&interp, "r"), 20.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_ternary_short_circuit_then() {
|
||||||
|
// When condition is false, then_branch is not evaluated
|
||||||
|
let interp = run("let x = 0; let r = false ? (x = 999) : (x = 42);");
|
||||||
|
assert_num(&get_var(&interp, "x"), 42.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_ternary_short_circuit_else() {
|
||||||
|
// When condition is true, else_branch is not evaluated
|
||||||
|
let interp = run("let x = 0; let r = true ? (x = 42) : (x = 999);");
|
||||||
|
assert_num(&get_var(&interp, "x"), 42.0);
|
||||||
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Unary
|
// Unary
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ impl Lexer {
|
|||||||
}
|
}
|
||||||
'.' => TokenKind::Dot,
|
'.' => TokenKind::Dot,
|
||||||
':' => TokenKind::Colon,
|
':' => TokenKind::Colon,
|
||||||
|
'?' => TokenKind::Question,
|
||||||
'[' => TokenKind::LeftBracket,
|
'[' => TokenKind::LeftBracket,
|
||||||
']' => TokenKind::RightBracket,
|
']' => TokenKind::RightBracket,
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ pub enum TokenKind {
|
|||||||
LeftBracket, RightBracket,
|
LeftBracket, RightBracket,
|
||||||
Comma, Semicolon, Dot,
|
Comma, Semicolon, Dot,
|
||||||
Colon,
|
Colon,
|
||||||
|
Question,
|
||||||
|
|
||||||
// 运算符
|
// 运算符
|
||||||
Plus, Minus, Star, Slash, Percent,
|
Plus, Minus, Star, Slash, Percent,
|
||||||
|
|||||||
+55
-1
@@ -208,8 +208,24 @@ impl Parser {
|
|||||||
self.assignment()
|
self.assignment()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assignment(&mut self) -> Result<Expr, RuntimeError> {
|
fn ternary(&mut self) -> Result<Expr, RuntimeError> {
|
||||||
let expr = self.logical_or()?;
|
let expr = self.logical_or()?;
|
||||||
|
if self.match_kind(&[TokenKind::Question]) {
|
||||||
|
let then_branch = self.assignment()?;
|
||||||
|
self.consume(TokenKind::Colon, "Expected ':' after then branch of ternary.")?;
|
||||||
|
let else_branch = self.assignment()?;
|
||||||
|
Ok(Expr::Ternary {
|
||||||
|
condition: Box::new(expr),
|
||||||
|
then_branch: Box::new(then_branch),
|
||||||
|
else_branch: Box::new(else_branch),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assignment(&mut self) -> Result<Expr, RuntimeError> {
|
||||||
|
let expr = self.ternary()?;
|
||||||
|
|
||||||
let op = if self.match_kind(&[TokenKind::Equal]) {
|
let op = if self.match_kind(&[TokenKind::Equal]) {
|
||||||
Some(AssignOp::Equal)
|
Some(AssignOp::Equal)
|
||||||
@@ -814,6 +830,44 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----- ternary -----
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_ternary_simple() {
|
||||||
|
match parse_expr("a ? 1 : 2") {
|
||||||
|
Expr::Ternary { condition, then_branch, else_branch } => {
|
||||||
|
assert!(matches!(*condition, Expr::Variable(_)));
|
||||||
|
assert!(matches!(*then_branch, Expr::Literal(Literal::Number(1.0))));
|
||||||
|
assert!(matches!(*else_branch, Expr::Literal(Literal::Number(2.0))));
|
||||||
|
}
|
||||||
|
e => panic!("Expected Ternary, got {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_ternary_nested_right_assoc() {
|
||||||
|
// a ? b : c ? d : e → a ? b : (c ? d : e)
|
||||||
|
match parse_expr("a ? b : c ? d : e") {
|
||||||
|
Expr::Ternary { condition, then_branch, else_branch } => {
|
||||||
|
assert!(matches!(*condition, Expr::Variable(_)));
|
||||||
|
assert!(matches!(*then_branch, Expr::Variable(_)));
|
||||||
|
assert!(matches!(*else_branch, Expr::Ternary { .. }));
|
||||||
|
}
|
||||||
|
e => panic!("Expected Ternary with nested Ternary else, got {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_ternary_with_assignment_in_else() {
|
||||||
|
// a ? b : c = d → a ? b : (c = d)
|
||||||
|
match parse_expr("a ? b : c = 5") {
|
||||||
|
Expr::Ternary { else_branch, .. } => {
|
||||||
|
assert!(matches!(*else_branch, Expr::Assign { .. }));
|
||||||
|
}
|
||||||
|
e => panic!("Expected Ternary with Assign else, got {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----- assignment -----
|
// ----- assignment -----
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user