feat: try-catch-finally 异常处理

语法: try { ... } catch (e) { ... } finally { ... }
catch 和 finally 都是可选的,但至少需要一个。throw expr 抛出任意值。

- Lexer: 新增 try, catch, finally, throw 关键字
- AST: Stmt::Try { body, catch_var, catch_body, finally_body } + Stmt::Throw(Expr)
- Parser: try/catch/finally/throw 语句解析,更新 synchronize()
- Opcode: Throw = 46,FunctionProto 新增 exception_handlers 表
- ExceptionHandler: { try_start, try_end, catch_ip, catch_slot, finally_ip }
- Compiler: compile_try 将 finally 在成功/异常两条路径各内联一次
- VM: unwind() 栈展开 + find_handler() 处理器搜索
- RuntimeError(除零、未定义变量等)在 try 块内自动转为可捕获异常
- LSP: 补全关键字列表追加

已知限制: return/break/continue 在 try-finally 内部不会先执行 finally

16 个新测试,全部 343 个测试通过。

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
0264408
2026-06-26 14:37:38 +08:00
parent deab894864
commit a70e5fbc30
11 changed files with 595 additions and 38 deletions
+40 -1
View File
@@ -54,6 +54,10 @@ impl Parser {
self.continue_statement()
} else if self.match_kind(&[TokenKind::Return]) {
self.return_statement()
} else if self.match_kind(&[TokenKind::Try]) {
self.try_statement()
} else if self.match_kind(&[TokenKind::Throw]) {
self.throw_statement()
} else if self.match_kind(&[TokenKind::LeftBrace]) {
Ok(Stmt::Block(self.block()?))
} else {
@@ -201,6 +205,39 @@ impl Parser {
Ok(Stmt::Return(value))
}
fn try_statement(&mut self) -> Result<Stmt, RuntimeError> {
self.consume(TokenKind::LeftBrace, "Expected '{' after 'try'.")?;
let body = self.block()?;
let mut catch_var = None;
let mut catch_body = None;
if self.match_kind(&[TokenKind::Catch]) {
self.consume(TokenKind::LeftParen, "Expected '(' after 'catch'.")?;
catch_var = Some(self.consume_ident("Expected exception variable.")?);
self.consume(TokenKind::RightParen, "Expected ')' after catch variable.")?;
self.consume(TokenKind::LeftBrace, "Expected '{' before catch body.")?;
catch_body = Some(self.block()?);
}
let mut finally_body = None;
if self.match_kind(&[TokenKind::Finally]) {
self.consume(TokenKind::LeftBrace, "Expected '{' after 'finally'.")?;
finally_body = Some(self.block()?);
}
if catch_var.is_none() && finally_body.is_none() {
return Err(RuntimeError::parse("Expected 'catch' or 'finally' after 'try'.", self.peek().clone()));
}
Ok(Stmt::Try { body, catch_var, catch_body, finally_body })
}
fn throw_statement(&mut self) -> Result<Stmt, RuntimeError> {
let expr = self.expression()?;
self.match_kind(&[TokenKind::Semicolon]); // 分号可选
Ok(Stmt::Throw(expr))
}
fn block(&mut self) -> Result<Vec<Stmt>, RuntimeError> {
let mut statements = Vec::new();
@@ -616,7 +653,9 @@ impl Parser {
| TokenKind::For
| TokenKind::Return
| TokenKind::Break
| TokenKind::Continue => return,
| TokenKind::Continue
| TokenKind::Try
| TokenKind::Throw => return,
_ => {
self.advance();
}