Skip to content

Commit 4fd4b46

Browse files
committed
feat: Implement try-catch-finally exception handling and throw statements.
1 parent 7330de7 commit 4fd4b46

14 files changed

+1140
-2
lines changed

TRY_CATCH.md

Lines changed: 560 additions & 0 deletions
Large diffs are not rendered by default.

demo_codes/test_try_catch_basic.ch

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Basic try-catch test
2+
try {
3+
throw "Something went wrong!"
4+
} catch error {
5+
println("Caught error: " + error)
6+
}
7+
8+
println("Program continues...")
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Try-catch with finally
2+
let cleanup_called = false
3+
4+
try {
5+
println("In try block")
6+
throw "Error occurred"
7+
} catch error {
8+
println("In catch block: " + error)
9+
} finally {
10+
println("In finally block")
11+
cleanup_called = true
12+
}
13+
14+
println("Cleanup called: " + cleanup_called)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Function with exception
2+
def divide(a, b) {
3+
if b == 0 {
4+
throw "Division by zero"
5+
}
6+
a / b
7+
}
8+
9+
try {
10+
let result = divide(10, 2)
11+
println("Result: " + result)
12+
13+
let bad_result = divide(10, 0)
14+
println("This should not print")
15+
} catch error {
16+
println("Caught: " + error)
17+
}
18+
19+
println("Program completed")
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Nested try-catch
2+
try {
3+
println("Outer try")
4+
5+
try {
6+
println("Inner try")
7+
throw "Inner error"
8+
} catch inner_error {
9+
println("Inner catch: " + inner_error)
10+
throw "Outer error"
11+
}
12+
13+
println("This should not print")
14+
} catch outer_error {
15+
println("Outer catch: " + outer_error)
16+
}
17+
18+
println("Done")

docs/pkg/chen_lang_bg.wasm

-169 KB
Binary file not shown.

src/chen.pest

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,15 @@ TRUE = @{ "true" ~ !(ASCII_ALPHANUMERIC | "_") }
1717
FALSE = @{ "false" ~ !(ASCII_ALPHANUMERIC | "_") }
1818
BREAK = @{ "break" ~ !(ASCII_ALPHANUMERIC | "_") }
1919
CONTINUE = @{ "continue" ~ !(ASCII_ALPHANUMERIC | "_") }
20+
TRY = @{ "try" ~ !(ASCII_ALPHANUMERIC | "_") }
21+
CATCH = @{ "catch" ~ !(ASCII_ALPHANUMERIC | "_") }
22+
FINALLY = @{ "finally" ~ !(ASCII_ALPHANUMERIC | "_") }
23+
THROW = @{ "throw" ~ !(ASCII_ALPHANUMERIC | "_") }
2024
// NULL removed
2125

2226
// 标识符 (不能是关键字)
2327
identifier = @{ !keyword ~ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* }
24-
keyword = { LET | FOR | DEF | RETURN | IF | ELSE | TRUE | FALSE | BREAK | CONTINUE }
28+
keyword = { LET | FOR | DEF | RETURN | IF | ELSE | TRUE | FALSE | BREAK | CONTINUE | TRY | CATCH | FINALLY | THROW }
2529

2630
// 字面量
2731
integer = @{ "-"? ~ ASCII_DIGIT+ }
@@ -94,6 +98,8 @@ statement = {
9498
return_stmt |
9599
break_stmt |
96100
continue_stmt |
101+
try_catch |
102+
throw_stmt |
97103
assignment |
98104
expression
99105
}
@@ -107,6 +113,8 @@ function_def = { DEF ~ identifier? ~ "(" ~ NEWLINE* ~ parameters? ~ NEWLINE* ~ "
107113
return_stmt = { RETURN ~ NEWLINE* ~ expression }
108114
break_stmt = { BREAK }
109115
continue_stmt = { CONTINUE }
116+
try_catch = { TRY ~ block ~ CATCH ~ identifier? ~ block ~ (FINALLY ~ block)? }
117+
throw_stmt = { THROW ~ NEWLINE* ~ expression }
110118

111119
// 复合结构
112120
block = { "{" ~ NEWLINE* ~ (statement ~ stmt_sep)* ~ statement? ~ NEWLINE* ~ "}" }

src/compiler.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,11 @@ impl<'a> Compiler<'a> {
233233
self.compile_expression(value);
234234
self.emit(Instruction::SetIndex, line);
235235
}
236+
Statement::TryCatch(tc) => self.compile_try_catch(tc),
237+
Statement::Throw { value, line } => {
238+
self.compile_expression(value);
239+
self.emit(Instruction::Throw, line);
240+
}
236241
}
237242
}
238243

@@ -603,4 +608,102 @@ impl<'a> Compiler<'a> {
603608
},
604609
);
605610
}
611+
612+
fn compile_try_catch(&mut self, tc: TryCatch) {
613+
let line = tc.line;
614+
let unique_id = self.unique_id();
615+
let catch_label = format!("catch_{}", unique_id);
616+
let finally_label = format!("finally_{}", unique_id);
617+
let end_label = format!("end_try_{}", unique_id);
618+
619+
// Set up exception handler
620+
self.emit(Instruction::PushExceptionHandler(catch_label.clone()), line);
621+
622+
// Compile try block
623+
self.begin_scope();
624+
for stmt in tc.try_body {
625+
self.compile_statement(stmt);
626+
}
627+
self.end_scope();
628+
629+
// Pop exception handler if no exception occurred
630+
self.emit(Instruction::PopExceptionHandler, line);
631+
632+
// Jump to finally or end
633+
if tc.finally_body.is_some() {
634+
self.emit(Instruction::Jump(finally_label.clone()), line);
635+
} else {
636+
self.emit(Instruction::Jump(end_label.clone()), line);
637+
}
638+
639+
// Catch block
640+
self.program.syms.insert(
641+
catch_label.clone(),
642+
Symbol {
643+
location: self.program.instructions.len() as i32,
644+
narguments: 0,
645+
nlocals: 0,
646+
},
647+
);
648+
649+
self.begin_scope();
650+
651+
// Define error variable if provided
652+
if let Some(error_name) = tc.error_name {
653+
let var_location = self.define_variable(error_name);
654+
match var_location {
655+
VarLocation::Local(offset) => {
656+
self.emit(Instruction::MovePlusFP(offset as usize), line);
657+
}
658+
VarLocation::Global(name) => {
659+
self.emit(Instruction::Store(name), line);
660+
}
661+
}
662+
} else {
663+
// Pop the error value if no variable to store it
664+
self.emit(Instruction::Pop, line);
665+
}
666+
667+
// Compile catch block
668+
for stmt in tc.catch_body {
669+
self.compile_statement(stmt);
670+
}
671+
672+
self.end_scope();
673+
674+
// Jump to finally or end after catch
675+
if tc.finally_body.is_some() {
676+
self.emit(Instruction::Jump(finally_label.clone()), line);
677+
} else {
678+
self.emit(Instruction::Jump(end_label.clone()), line);
679+
}
680+
681+
// Finally block (if present)
682+
if let Some(finally_body) = tc.finally_body {
683+
self.program.syms.insert(
684+
finally_label.clone(),
685+
Symbol {
686+
location: self.program.instructions.len() as i32,
687+
narguments: 0,
688+
nlocals: 0,
689+
},
690+
);
691+
692+
self.begin_scope();
693+
for stmt in finally_body {
694+
self.compile_statement(stmt);
695+
}
696+
self.end_scope();
697+
}
698+
699+
// End label
700+
self.program.syms.insert(
701+
end_label,
702+
Symbol {
703+
location: self.program.instructions.len() as i32,
704+
narguments: 0,
705+
nlocals: 0,
706+
},
707+
);
708+
}
606709
}

src/expression.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ pub enum Statement {
106106
},
107107
Break(u32),
108108
Continue(u32),
109+
/// Try-Catch-Finally 异常处理
110+
TryCatch(TryCatch),
111+
/// Throw 抛出异常
112+
Throw {
113+
value: Expression,
114+
line: u32,
115+
},
109116
}
110117

111118
pub type Ast = Vec<Statement>;
@@ -137,3 +144,17 @@ pub struct Loop {
137144
pub body: Vec<Statement>,
138145
pub line: u32,
139146
}
147+
148+
/// Try-Catch-Finally 异常处理
149+
#[derive(Debug, PartialEq, Clone)]
150+
pub struct TryCatch {
151+
/// try 块中的语句
152+
pub try_body: Vec<Statement>,
153+
/// catch 块中的错误变量名
154+
pub error_name: Option<String>,
155+
/// catch 块中的语句
156+
pub catch_body: Vec<Statement>,
157+
/// finally 块中的语句(可选)
158+
pub finally_body: Option<Vec<Statement>>,
159+
pub line: u32,
160+
}

src/parser/handwritten.rs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use thiserror::Error;
44

55
use crate::expression::{
66
Assign, Ast, BinaryOperation, Expression, FunctionCall, FunctionDeclaration, If, Literal, Local, Loop, Return,
7-
Statement, Unary,
7+
Statement, TryCatch, Unary,
88
};
99
use crate::token::Keyword;
1010
use crate::token::Operator;
@@ -162,6 +162,12 @@ impl Parser {
162162
if self.match_token(&Token::Keyword(Keyword::CONTINUE)) {
163163
return Ok(Statement::Continue(start_line));
164164
}
165+
if self.match_token(&Token::Keyword(Keyword::TRY)) {
166+
return self.parse_try_catch();
167+
}
168+
if self.match_token(&Token::Keyword(Keyword::THROW)) {
169+
return self.parse_throw();
170+
}
165171

166172
// Assignment or Expression
167173
let expr = self.parse_expression_logic()?;
@@ -617,6 +623,65 @@ impl Parser {
617623
self.consume(&Token::RSquare, "Expected ']' after array elements")?;
618624
Ok(Expression::ArrayLiteral(elements, start_line))
619625
}
626+
627+
fn parse_try_catch(&mut self) -> Result<Statement, ParseError> {
628+
let start_line = self.line;
629+
630+
// Parse try block
631+
self.skip_newlines();
632+
self.consume(&Token::LBig, "Expected '{' after 'try'")?;
633+
let try_body = self.parse_block()?;
634+
self.consume(&Token::RBig, "Expected '}' after try block")?;
635+
636+
// Parse catch
637+
self.skip_newlines();
638+
self.consume(&Token::Keyword(Keyword::CATCH), "Expected 'catch' after try block")?;
639+
640+
// Optional error variable name
641+
let error_name = if let Some(Token::Identifier(name)) = self.peek() {
642+
let n = name.clone();
643+
self.advance();
644+
Some(n)
645+
} else {
646+
None
647+
};
648+
649+
// Parse catch block
650+
self.skip_newlines();
651+
self.consume(&Token::LBig, "Expected '{' after 'catch'")?;
652+
let catch_body = self.parse_block()?;
653+
self.consume(&Token::RBig, "Expected '}' after catch block")?;
654+
655+
// Optional finally block
656+
self.skip_newlines();
657+
let finally_body = if self.match_token(&Token::Keyword(Keyword::FINALLY)) {
658+
self.skip_newlines();
659+
self.consume(&Token::LBig, "Expected '{' after 'finally'")?;
660+
let body = self.parse_block()?;
661+
self.consume(&Token::RBig, "Expected '}' after finally block")?;
662+
Some(body)
663+
} else {
664+
None
665+
};
666+
667+
Ok(Statement::TryCatch(TryCatch {
668+
try_body,
669+
error_name,
670+
catch_body,
671+
finally_body,
672+
line: start_line,
673+
}))
674+
}
675+
676+
fn parse_throw(&mut self) -> Result<Statement, ParseError> {
677+
let start_line = self.line;
678+
let value = self.parse_expression_logic()?;
679+
680+
Ok(Statement::Throw {
681+
value,
682+
line: start_line,
683+
})
684+
}
620685
}
621686

622687
pub fn parse(tokens: Vec<Token>) -> Result<Ast, ParseError> {

0 commit comments

Comments
 (0)