Skip to content

Commit f577a7d

Browse files
claude[bot]tomhrr
andauthored
Fix try-catch error handling framework
- Add semicolons to try forms in tests - Expand try_mode scope beyond single instructions - Add proper error continuation in try mode - Update arithmetic operations to handle errors in try mode - Add division by zero checks Co-authored-by: tomhrr <tomhrr@users.noreply.github.com>
1 parent 7a69468 commit f577a7d

3 files changed

Lines changed: 121 additions & 63 deletions

File tree

src/vm.rs

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ pub struct VM {
8585
try_next: bool,
8686
/// Captured error message when in try mode.
8787
captured_error: Option<String>,
88+
/// Stack depth when try mode started (to know when form ends).
89+
try_stack_depth: usize,
8890
/// The local variable stack.
8991
local_var_stack: Rc<RefCell<Vec<Value>>>,
9092
/// The scopes.
@@ -447,6 +449,7 @@ impl VM {
447449
try_mode: false,
448450
try_next: false,
449451
captured_error: None,
452+
try_stack_depth: 0,
450453
scopes: vec![global_vars],
451454
global_functions: global_functions,
452455
call_stack_chunks: Vec::new(),
@@ -531,6 +534,17 @@ impl VM {
531534
}
532535
}
533536

537+
/// Helper function to handle errors appropriately based on try mode.
538+
/// Returns 1 in try mode (continue execution), 0 otherwise (terminate).
539+
pub fn handle_error(&mut self, error: &str) -> usize {
540+
self.print_error(error);
541+
if self.try_mode {
542+
1 // Continue execution in try mode
543+
} else {
544+
0 // Terminate execution
545+
}
546+
}
547+
534548
/// Toggles whether the stack is printed and cleared on command
535549
/// execution when running interactively.
536550
pub fn opcode_togglemode(&mut self) -> i32 {
@@ -1194,8 +1208,13 @@ impl VM {
11941208
let value_rr = new_string_value(s.to_string());
11951209
self.stack.push(value_rr);
11961210
} else {
1197-
self.print_error("function not found");
1198-
return false;
1211+
if self.try_mode {
1212+
self.print_error("function not found");
1213+
return true; // Continue in try mode
1214+
} else {
1215+
self.print_error("function not found");
1216+
return false;
1217+
}
11991218
}
12001219

12011220
true
@@ -1294,8 +1313,13 @@ impl VM {
12941313
if is_implicit {
12951314
self.stack.push(function_rr.clone());
12961315
} else {
1297-
self.print_error("function not found");
1298-
return false;
1316+
if self.try_mode {
1317+
self.print_error("function not found");
1318+
return true; // Continue in try mode
1319+
} else {
1320+
self.print_error("function not found");
1321+
return false;
1322+
}
12991323
}
13001324
}
13011325
}
@@ -1376,8 +1400,7 @@ impl VM {
13761400
}
13771401
let len = self.stack.len();
13781402
if len == 0 {
1379-
self.print_error("+ requires two arguments");
1380-
return 0;
1403+
return self.handle_error("+ requires two arguments");
13811404
}
13821405
let v1_rr = self.stack.get_mut(len - 1).unwrap();
13831406
if let Value::Int(ref mut n1) = v1_rr {
@@ -1432,8 +1455,7 @@ impl VM {
14321455
}
14331456
let len = self.stack.len();
14341457
if len == 0 {
1435-
self.print_error("- requires two arguments");
1436-
return 0;
1458+
return self.handle_error("- requires two arguments");
14371459
}
14381460
let v1_rr = self.stack.get_mut(len - 1).unwrap();
14391461
if let Value::Int(ref mut n1) = v1_rr {
@@ -1488,8 +1510,7 @@ impl VM {
14881510
}
14891511
let len = self.stack.len();
14901512
if len == 0 {
1491-
self.print_error("* requires two arguments");
1492-
return 0;
1513+
return self.handle_error("* requires two arguments");
14931514
}
14941515
let v1_rr = self.stack.get_mut(len - 1).unwrap();
14951516
if let Value::Int(ref mut n1) = v1_rr {
@@ -1544,17 +1565,15 @@ impl VM {
15441565
}
15451566
let len = self.stack.len();
15461567
if len == 0 {
1547-
self.print_error("/ requires two arguments");
1548-
return 0;
1568+
return self.handle_error("/ requires two arguments");
15491569
}
15501570
let v1_rr = self.stack.get_mut(len - 1).unwrap();
15511571
if let Value::Int(ref mut n1) = v1_rr {
15521572
if self.debug {
15531573
eprintln!(" > Got integer from stack: {}", *n1);
15541574
}
15551575
if n == 0 {
1556-
self.print_error("/ requires two non-zero numbers");
1557-
return 0;
1576+
return self.handle_error("/ requires two non-zero numbers");
15581577
}
15591578
*n1 /= n;
15601579
done = true;
@@ -2457,9 +2476,10 @@ impl VM {
24572476
}
24582477
}
24592478
OpCode::Try => {
2460-
// Enable try mode for the next instruction
2479+
// Enable try mode for the next form
24612480
self.try_next = true;
24622481
self.captured_error = None;
2482+
self.try_stack_depth = self.stack.len();
24632483
}
24642484
OpCode::EndFn => {
24652485
if !chunk.borrow().is_generator && chunk.borrow().has_vars {
@@ -2479,26 +2499,32 @@ impl VM {
24792499
}
24802500
}
24812501

2482-
// Check if we should enable try mode for the next instruction
2502+
// Check if we should enable try mode for the next form
24832503
if self.try_next {
24842504
self.try_next = false;
24852505
self.try_mode = true;
24862506
} else if self.try_mode {
2487-
// We just finished executing an instruction in try mode
2488-
self.try_mode = false;
2489-
match &self.captured_error {
2490-
Some(error_msg) => {
2491-
// Error was captured, push false and error message
2492-
self.stack.push(new_string_value("f".to_string()));
2493-
self.stack.push(new_string_value(error_msg.clone()));
2494-
}
2495-
None => {
2496-
// No error, push true and empty string
2497-
self.stack.push(new_string_value("t".to_string()));
2498-
self.stack.push(new_string_value("".to_string()));
2507+
// Check if we should end try mode
2508+
let should_end_try = self.captured_error.is_some() ||
2509+
(self.stack.len() > self.try_stack_depth);
2510+
2511+
if should_end_try {
2512+
// We finished executing the form in try mode
2513+
self.try_mode = false;
2514+
match &self.captured_error {
2515+
Some(error_msg) => {
2516+
// Error was captured, push false and error message
2517+
self.stack.push(new_string_value(".f".to_string()));
2518+
self.stack.push(new_string_value(error_msg.clone()));
2519+
}
2520+
None => {
2521+
// No error, push true and empty string
2522+
self.stack.push(new_string_value(".t".to_string()));
2523+
self.stack.push(new_string_value("".to_string()));
2524+
}
24992525
}
2526+
self.captured_error = None;
25002527
}
2501-
self.captured_error = None;
25022528
}
25032529

25042530
i += 1;

src/vm/vm_arithmetic.rs

Lines changed: 59 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,15 @@ fn multiply_ints(n1: i32, n2: i32) -> Value {
5555

5656
/// Divide one integer by another and return the result value. Promote
5757
/// to bigint if the value cannot be stored in an i32.
58-
fn divide_ints(n1: i32, n2: i32) -> Value {
58+
fn divide_ints(n1: i32, n2: i32) -> Option<Value> {
59+
if n1 == 0 {
60+
return None; // Division by zero
61+
}
5962
match n2.checked_div(n1) {
60-
Some(n3) => Value::Int(n3),
63+
Some(n3) => Some(Value::Int(n3)),
6164
None => {
6265
let n2_bigint = BigInt::from_i32(n2).unwrap();
63-
Value::BigInt(n2_bigint / n1)
66+
Some(Value::BigInt(n2_bigint / n1))
6467
}
6568
}
6669
}
@@ -80,6 +83,16 @@ fn remainder_ints(n1: i32, n2: i32) -> Value {
8083
}
8184

8285
impl VM {
86+
/// Helper function to handle errors appropriately based on try mode.
87+
/// Returns 1 in try mode (continue execution), 0 otherwise (terminate).
88+
fn handle_arithmetic_error(&mut self, error: &str) -> i32 {
89+
self.print_error(error);
90+
if self.try_mode {
91+
1 // Continue execution in try mode
92+
} else {
93+
0 // Terminate execution
94+
}
95+
}
8396
/// Helper function for adding two values together and placing the
8497
/// result onto the stack. Returns an integer indicating whether
8598
/// the values were able to be added together.
@@ -131,8 +144,7 @@ impl VM {
131144
pub fn opcode_add(&mut self) -> i32 {
132145
let len = self.stack.len();
133146
if len < 2 {
134-
self.print_error("+ requires two arguments");
135-
return 0;
147+
return self.handle_arithmetic_error("+ requires two arguments");
136148
}
137149

138150
let v1_rr = self.stack.pop().unwrap();
@@ -152,8 +164,7 @@ impl VM {
152164

153165
let res = self.opcode_add_inner(&v1_rr, &v2_rr);
154166
if res == 0 {
155-
self.print_error("+ requires two numbers");
156-
return 0;
167+
return self.handle_arithmetic_error("+ requires two numbers");
157168
}
158169
}
159170

@@ -215,8 +226,7 @@ impl VM {
215226
pub fn opcode_subtract(&mut self) -> i32 {
216227
let len = self.stack.len();
217228
if len < 2 {
218-
self.print_error("- requires two arguments");
219-
return 0;
229+
return self.handle_arithmetic_error("- requires two arguments");
220230
}
221231

222232
let v1_rr = self.stack.pop().unwrap();
@@ -236,8 +246,7 @@ impl VM {
236246

237247
let res = self.opcode_subtract_inner(&v1_rr, &v2_rr);
238248
if res == 0 {
239-
self.print_error("- requires two numbers");
240-
return 0;
249+
return self.handle_arithmetic_error("- requires two numbers");
241250
}
242251
}
243252

@@ -300,8 +309,7 @@ impl VM {
300309
pub fn opcode_multiply(&mut self) -> i32 {
301310
let len = self.stack.len();
302311
if len < 2 {
303-
self.print_error("* requires two arguments");
304-
return 0;
312+
return self.handle_arithmetic_error("* requires two arguments");
305313
}
306314

307315
let v1_rr = self.stack.pop().unwrap();
@@ -321,8 +329,7 @@ impl VM {
321329

322330
let res = self.opcode_multiply_inner(&v1_rr, &v2_rr);
323331
if res == 0 {
324-
self.print_error("* requires two numbers");
325-
return 0;
332+
return self.handle_arithmetic_error("* requires two numbers");
326333
}
327334
}
328335

@@ -335,17 +342,28 @@ impl VM {
335342
fn opcode_divide_inner(&mut self, v1: &Value, v2: &Value) -> i32 {
336343
match (v1, v2) {
337344
(Value::BigInt(n1), Value::BigInt(n2)) => {
345+
if *n1 == BigInt::from_i32(0).unwrap() {
346+
return -1; // Division by zero
347+
}
338348
let n3 = Value::BigInt(n2 / n1);
339349
self.stack.push(n3);
340350
1
341351
}
342352
(Value::BigInt(_), Value::Int(n2)) => self.opcode_divide_inner(v1, &int_to_bigint(*n2)),
343353
(Value::Int(n1), Value::BigInt(_)) => self.opcode_divide_inner(&int_to_bigint(*n1), v2),
344354
(Value::Int(n1), Value::Int(n2)) => {
345-
self.stack.push(divide_ints(*n1, *n2));
346-
1
355+
match divide_ints(*n1, *n2) {
356+
Some(result) => {
357+
self.stack.push(result);
358+
1
359+
}
360+
None => -1 // Division by zero
361+
}
347362
}
348363
(Value::Float(n1), Value::Float(n2)) => {
364+
if *n1 == 0.0 {
365+
return -1; // Division by zero
366+
}
349367
self.stack.push(Value::Float(n2 / n1));
350368
1
351369
}
@@ -355,8 +373,13 @@ impl VM {
355373
let n1_opt = v1.to_int();
356374
let n2_opt = v2.to_int();
357375
if let (Some(n1), Some(n2)) = (n1_opt, n2_opt) {
358-
self.stack.push(divide_ints(n1, n2));
359-
return 1;
376+
match divide_ints(n1, n2) {
377+
Some(result) => {
378+
self.stack.push(result);
379+
return 1;
380+
}
381+
None => return -1 // Division by zero
382+
}
360383
}
361384
let n1_opt = v1.to_bigint();
362385
let n2_opt = v2.to_bigint();
@@ -367,6 +390,9 @@ impl VM {
367390
let n1_opt = v1.to_float();
368391
let n2_opt = v2.to_float();
369392
if let (Some(n1), Some(n2)) = (n1_opt, n2_opt) {
393+
if n1 == 0.0 {
394+
return -1; // Division by zero
395+
}
370396
self.stack.push(Value::Float(n2 / n1));
371397
return 1;
372398
}
@@ -380,8 +406,7 @@ impl VM {
380406
pub fn opcode_divide(&mut self) -> i32 {
381407
let len = self.stack.len();
382408
if len < 2 {
383-
self.print_error("/ requires two arguments");
384-
return 0;
409+
return self.handle_arithmetic_error("/ requires two arguments");
385410
}
386411

387412
let v1_rr = self.stack.pop().unwrap();
@@ -390,10 +415,16 @@ impl VM {
390415
if let (Value::Int(n1), Value::Int(ref mut n2)) =
391416
(&v1_rr, self.stack.get_mut(len - 2).unwrap())
392417
{
393-
let v3 = divide_ints(*n1, *n2);
394-
if let Value::Int(n3) = v3 {
395-
*n2 = n3;
396-
done = true;
418+
match divide_ints(*n1, *n2) {
419+
Some(v3) => {
420+
if let Value::Int(n3) = v3 {
421+
*n2 = n3;
422+
done = true;
423+
}
424+
}
425+
None => {
426+
return self.handle_arithmetic_error("/ requires two non-zero numbers");
427+
}
397428
}
398429
}
399430

@@ -402,8 +433,9 @@ impl VM {
402433

403434
let res = self.opcode_divide_inner(&v1_rr, &v2_rr);
404435
if res == 0 {
405-
self.print_error("/ requires two numbers");
406-
return 0;
436+
return self.handle_arithmetic_error("/ requires two numbers");
437+
} else if res == -1 {
438+
return self.handle_arithmetic_error("/ requires two non-zero numbers");
407439
}
408440
}
409441

0 commit comments

Comments
 (0)