From ae3060708d6ef6776bd2c825538118f89d5dd12d Mon Sep 17 00:00:00 2001 From: 0264408 Date: Wed, 10 Jun 2026 17:37:12 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0break=E5=92=8Ccontinu?= =?UTF-8?q?e=E8=AF=AD=E5=8F=A5=E7=9A=84=E6=94=AF=E6=8C=81=EF=BC=8C?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=A7=A3=E6=9E=90=E5=99=A8=E3=80=81=E8=A7=A3?= =?UTF-8?q?=E9=87=8A=E5=99=A8=E5=92=8C=E8=AF=8D=E6=B3=95=E5=88=86=E6=9E=90?= =?UTF-8?q?=E5=99=A8=E4=BB=A5=E5=A4=84=E7=90=86=E7=9B=B8=E5=85=B3=E8=AF=AD?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/break_test.ast | 27 +++++++++++++ src/ast/stmt.rs | 6 +++ src/interpreter/interpreter.rs | 72 ++++++++++++++++++++++++---------- src/interpreter/mod.rs | 7 ++++ src/lexer/lexer.rs | 2 + src/lexer/token.rs | 2 + src/parser/parser.rs | 14 +++++++ 7 files changed, 109 insertions(+), 21 deletions(-) create mode 100644 examples/break_test.ast diff --git a/examples/break_test.ast b/examples/break_test.ast new file mode 100644 index 0000000..f15d47e --- /dev/null +++ b/examples/break_test.ast @@ -0,0 +1,27 @@ +// test break +let i = 0 +while (i < 10) { + if (i == 5) { break } + i = i + 1 +} +print("break (expect 5):", i) + +// test continue +let sum = 0 +let j = 0 +while (j < 10) { + j = j + 1 + if (j == 5) { continue } + sum = sum + j +} +print("continue (expect 50):", sum) + +// test for + break +let found = -1 +for (let k = 0; k < 100; k = k + 1) { + if (k * k > 50) { + found = k + break + } +} +print("for break (expect 8):", found) diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 9693cd1..bd249da 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -44,4 +44,10 @@ pub enum Stmt { /// return expr?; Return(Option), + + /// break + Break, + + /// continue + Continue, } diff --git a/src/interpreter/interpreter.rs b/src/interpreter/interpreter.rs index 2dad083..eff1c7f 100644 --- a/src/interpreter/interpreter.rs +++ b/src/interpreter/interpreter.rs @@ -1,5 +1,6 @@ use crate::ast::*; use crate::error::RuntimeError; +use crate::interpreter::Signal; use super::{Value, Env, Function}; use std::collections::HashMap; use std::rc::Rc; @@ -23,26 +24,30 @@ impl Interpreter { Ok(()) } - fn execute(&mut self, stmt: Stmt) -> Result, RuntimeError> { + fn execute(&mut self, stmt: Stmt) -> Result { match stmt { Stmt::Let { name, initializer } => { let val = self.evaluate(initializer)?; self.env.borrow_mut().define(name, val); - Ok(None) + Ok(Signal::None) } Stmt::ExprStmt(expr) => { - Ok(Some(self.evaluate(expr)?)) + self.evaluate(expr)?; + Ok(Signal::None) } Stmt::Block(stmts) => { let previous = Rc::clone(&self.env); self.env = Rc::new(RefCell::new(Env::new(Some(previous)))); - let mut result = None; + let mut signal = Signal::None; for s in stmts { - result = self.execute(s)?; + signal = self.execute(s)?; + if !matches!(signal, Signal::None) { + break; + } } let parent = self.env.borrow().parent.as_ref().unwrap().clone(); self.env = parent; - Ok(result) + Ok(signal) } Stmt::If { condition, then_branch, else_branch } => { let cond_val = self.evaluate(condition)?; @@ -51,7 +56,7 @@ impl Interpreter { } else if let Some(else_branch) = else_branch { self.execute(*else_branch) } else { - Ok(None) + Ok(Signal::None) } } Stmt::While { condition, body } => { @@ -60,25 +65,42 @@ impl Interpreter { if !self.is_truthy(&cond_val) { break; } - self.execute(*body.clone())?; + match self.execute(*body.clone())? { + Signal::Break => break, + Signal::Continue => continue, + sig @ Signal::Return(_) => return Ok(sig), + Signal::None => {} + } } - Ok(None) + Ok(Signal::None) } Stmt::For { initializer, condition, step, body } => { if let Some(init) = initializer { self.execute(*init)?; } - while let Some(cond) = &condition { - let cond_val = self.evaluate(cond.clone())?; - if !self.is_truthy(&cond_val) { - break; + loop { + if let Some(cond) = &condition { + let cond_val = self.evaluate(cond.clone())?; + if !self.is_truthy(&cond_val) { + break; + } + } + match self.execute(*body.clone())? { + Signal::Break => break, + Signal::Continue => { + if let Some(step) = &step { + self.evaluate(step.clone())?; + } + continue; + } + sig @ Signal::Return(_) => return Ok(sig), + Signal::None => {} } - self.execute(*body.clone())?; if let Some(step) = &step { self.evaluate(step.clone())?; } } - Ok(None) + Ok(Signal::None) } Stmt::Function { name, params, body } => { let func = Value::Function(Rc::new(Function { @@ -88,15 +110,17 @@ impl Interpreter { name: Some(name.clone()), })); self.env.borrow_mut().define(name, func); - Ok(None) + Ok(Signal::None) } Stmt::Return(expr_opt) => { if let Some(expr) = expr_opt { - Ok(Some(self.evaluate(expr)?)) + Ok(Signal::Return(self.evaluate(expr)?)) } else { - Ok(Some(Value::Nil)) + Ok(Signal::Return(Value::Nil)) } } + Stmt::Break => Ok(Signal::Break), + Stmt::Continue => Ok(Signal::Continue), } } @@ -327,9 +351,15 @@ impl Interpreter { self.env = env; let mut ret = Value::Nil; for stmt in &f.body { - if let Some(val) = self.execute(stmt.clone())? { - ret = val; - break; + match self.execute(stmt.clone())? { + Signal::Return(val) => { ret = val; break; } + Signal::None => {} + Signal::Break | Signal::Continue => { + return Err(RuntimeError::RuntimeError { + message: "break/continue outside of loop".to_string(), + token: None, + }); + } } } self.env = previous; diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 0f47eb0..56c3123 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -24,6 +24,13 @@ pub enum Value { NativeFunction(NativeFn), } +pub enum Signal { + None, // 正常执行 + Return(Value), // return 语句携带的返回值 + Break, // break 信号 + Continue, // continue 信号 +} + impl std::fmt::Debug for Value { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { diff --git a/src/lexer/lexer.rs b/src/lexer/lexer.rs index ce94db3..05c8746 100644 --- a/src/lexer/lexer.rs +++ b/src/lexer/lexer.rs @@ -246,6 +246,8 @@ impl Lexer { "else" => TokenKind::Else, "while" => TokenKind::While, "for" => TokenKind::For, + "break" => TokenKind::Break, + "continue" => TokenKind::Continue, "return" => TokenKind::Return, "true" => TokenKind::True, "false" => TokenKind::False, diff --git a/src/lexer/token.rs b/src/lexer/token.rs index 6640a53..4690664 100644 --- a/src/lexer/token.rs +++ b/src/lexer/token.rs @@ -29,6 +29,8 @@ pub enum TokenKind { Else, While, For, + Break, + Continue, Return, True, False, diff --git a/src/parser/parser.rs b/src/parser/parser.rs index d107039..301d1b2 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -39,6 +39,10 @@ impl Parser { self.while_statement() } else if self.match_kind(&[TokenKind::For]) { self.for_statement() + } else if self.match_kind(&[TokenKind::Break]) { + self.break_statement() + } else if self.match_kind(&[TokenKind::Continue]) { + self.continue_statement() } else if self.match_kind(&[TokenKind::Return]) { self.return_statement() } else if self.match_kind(&[TokenKind::LeftBrace]) { @@ -153,6 +157,16 @@ impl Parser { } } + fn break_statement(&mut self) -> Stmt { + self.match_kind(&[TokenKind::Semicolon]); // 分号可选 + Stmt::Break + } + + fn continue_statement(&mut self) -> Stmt { + self.match_kind(&[TokenKind::Semicolon]); // 分号可选 + Stmt::Continue + } + fn return_statement(&mut self) -> Stmt { let value = if !self.check(&TokenKind::Semicolon) && !self.check(&TokenKind::RightBrace) { Some(self.expression())