From db541c315b6ff374523d96544f832f969b94f16a Mon Sep 17 00:00:00 2001 From: 0264408 Date: Tue, 16 Jun 2026 18:41:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=B8=89=E5=85=83?= =?UTF-8?q?=E8=BF=90=E7=AE=97=E7=AC=A6=E6=94=AF=E6=8C=81=EF=BC=8C=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E8=A7=A3=E6=9E=90=E5=99=A8=E5=92=8C=E8=A7=A3=E9=87=8A?= =?UTF-8?q?=E5=99=A8=E4=BB=A5=E5=A4=84=E7=90=86=E6=9D=A1=E4=BB=B6=E8=A1=A8?= =?UTF-8?q?=E8=BE=BE=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ast/expr.rs | 7 ++++ src/interpreter/interpreter.rs | 59 ++++++++++++++++++++++++++++++++++ src/lexer/lexer.rs | 1 + src/lexer/token.rs | 1 + src/parser/parser.rs | 56 +++++++++++++++++++++++++++++++- 5 files changed, 123 insertions(+), 1 deletion(-) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index d04b882..b2e79c4 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -73,6 +73,13 @@ pub enum Expr { right: Box, }, + /// 三元运算:condition ? then_branch : else_branch + Ternary { + condition: Box, + then_branch: Box, + else_branch: Box, + }, + /// 函数调用 Call { callee: Box, diff --git a/src/interpreter/interpreter.rs b/src/interpreter/interpreter.rs index ba607ee..e50193e 100644 --- a/src/interpreter/interpreter.rs +++ b/src/interpreter/interpreter.rs @@ -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 } => { let func = self.evaluate(*callee)?; 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 // ========================================================================= diff --git a/src/lexer/lexer.rs b/src/lexer/lexer.rs index 4ae1bb9..71f9215 100644 --- a/src/lexer/lexer.rs +++ b/src/lexer/lexer.rs @@ -81,6 +81,7 @@ impl Lexer { } '.' => TokenKind::Dot, ':' => TokenKind::Colon, + '?' => TokenKind::Question, '[' => TokenKind::LeftBracket, ']' => TokenKind::RightBracket, diff --git a/src/lexer/token.rs b/src/lexer/token.rs index 7c90a9a..38f0995 100644 --- a/src/lexer/token.rs +++ b/src/lexer/token.rs @@ -7,6 +7,7 @@ pub enum TokenKind { LeftBracket, RightBracket, Comma, Semicolon, Dot, Colon, + Question, // 运算符 Plus, Minus, Star, Slash, Percent, diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 9bf8b2e..ab05d89 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -208,8 +208,24 @@ impl Parser { self.assignment() } - fn assignment(&mut self) -> Result { + fn ternary(&mut self) -> Result { 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 { + let expr = self.ternary()?; let op = if self.match_kind(&[TokenKind::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 ----- #[test]