Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
453 changes: 453 additions & 0 deletions dev/interpreter/architecture/INTERPRETER_ERROR_REPORTING.md

Large diffs are not rendered by default.

205 changes: 205 additions & 0 deletions dev/interpreter/tests/error_handling_comprehensive.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;

# Comprehensive test for interpreter error handling and introspection
# Tests: die, warn, eval block, error variable, caller

print "# Testing die, warn, eval, error var, and caller in interpreter\n";

# Test 1: Basic die and eval
{
my $result = eval {
die "Test error\n";
return "Should not reach here";
};
ok(!$result, 'eval block caught die');
like($@, qr/Test error/, 'die message captured');
print "# Test 1 complete\n";
}

# Test 2: Die without newline (adds location)
{
eval {
die "Error without newline";
};
like($@, qr/Error without newline at/, 'die without newline adds location');
like($@, qr/line \d+/, 'location includes line number');
print "# Test 2 complete\n";
}

# Test 3: Nested eval blocks
{
my $outer;
eval {
eval {
die "Inner error\n";
};
$outer = $@;
die "Outer error\n";
};
like($outer, qr/Inner error/, 'inner error captured');
like($@, qr/Outer error/, 'outer error captured');
print "# Test 3 complete\n";
}

# Test 4: Eval returns undef on die
{
my $result = eval {
die "Test\n";
};
ok(!defined($result), 'eval returns undef when die occurs');
print "# Test 4 complete\n";
}

# Test 5: Warn functionality
{
my $warning;
local $SIG{__WARN__} = sub { $warning = shift };

warn "Test warning\n";

like($warning, qr/Test warning/, 'warn message captured');
print "# Test 5 complete\n";
}

# Test 6: Warn without newline adds location
{
my $warning;
local $SIG{__WARN__} = sub { $warning = shift };

warn "Warning without newline";

like($warning, qr/Warning without newline at/, 'warn adds location');
like($warning, qr/line \d+/, 'warn location has line number');
print "# Test 6 complete\n";
}

# Test 7: Caller in subroutine (0 levels)
{
sub test_caller {
my ($package, $filename, $line) = caller(0);
return ($package, $filename, $line);
}

my ($pkg, $file, $line) = test_caller();

is($pkg, 'main', 'caller(0) returns main package');
ok(defined($file), 'caller(0) returns filename');
ok($line > 0, 'caller(0) returns line number');

print "# Test 7 - caller(0): package=$pkg, file=$file, line=$line\n";
}

# Test 8: Caller with nested calls
{
sub inner_func {
my ($package, $filename, $line, $subroutine) = caller(1);
return ($package, $filename, $line, $subroutine);
}

sub outer_func {
return inner_func();
}

my ($pkg, $file, $line, $sub) = outer_func();

is($pkg, 'main', 'caller(1) returns correct package');
ok(defined($file), 'caller(1) returns filename');
ok($line > 0, 'caller(1) returns line number');
print "# Test 8 - caller(1): package=$pkg, file=$file, line=$line\n";
}

# Test 9: Caller returns false when no caller
{
my @caller = caller(10); # Way too deep
ok(!@caller, 'caller returns empty list when no caller');
print "# Test 9 complete\n";
}

# Test 10: Die inside subroutine with stack trace
{
sub level2 {
die "Error in level2\n";
}

sub level1 {
level2();
}

eval {
level1();
};

like($@, qr/Error in level2/, 'die message from nested call');
print "# Test 10 complete\n";
}

# Test 11: Error variable cleared on successful eval
{
$@ = "Previous error\n";
eval {
1 + 1; # Successful code
};
is($@, '', 'error variable cleared on successful eval');
print "# Test 11 complete\n";
}

# Test 12: Eval can return values
{
my $result = eval {
my $x = 10;
my $y = 20;
$x + $y;
};
is($result, 30, 'eval returns last expression');
is($@, '', 'no error on successful eval');
print "# Test 12 - eval returned: $result\n";
}

# Test 13: Die with saved error
{
eval {
die "First error\n";
};
my $saved = $@;

eval {
die $saved;
};

is($@, $saved, 'die preserves error object');
print "# Test 13 complete\n";
}

# Test 14: Bare die behavior
{
my $result;

eval {
$@ = "Inner saved\n";
die; # Bare die
};

like($@, qr/Inner saved|Died/, 'bare die propagates error');
print "# Test 14 complete\n";
}

# Test 15: Caller inside eval
{
sub caller_in_eval {
my ($pkg, $file, $line) = caller(0);
return ($pkg, $file, $line);
}

my ($pkg, $file, $line) = eval {
caller_in_eval();
};

is($pkg, 'main', 'caller works inside eval');
ok(defined($file), 'caller returns file inside eval');
print "# Test 15 - Caller in eval: $pkg at $file:$line\n";
}

done_testing();
109 changes: 109 additions & 0 deletions dev/interpreter/tests/interpreter_errors.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use strict;
use warnings;
use Test::More;

# Test interpreter error reporting

# Test 1: Die shows correct location
{
my $error;
my $code = '
sub foo {
die "Error in foo";
}
foo();
';
eval $code;
$error = $@;

# Check that error message includes location
like($error, qr/Error in foo/, 'die message preserved');
like($error, qr/at \(eval \d+\) line/, 'die shows eval location');

print "# Error message: $error";
}

# Test 2: Stack trace with nested calls
{
my $error;
my $code = '
sub bar {
die "Error in bar";
}
sub foo {
bar();
}
foo();
';
eval $code;
$error = $@;

# Check that stack trace includes both functions
like($error, qr/Error in bar/, 'nested die message preserved');
like($error, qr/at \(eval \d+\)/, 'nested die shows location');

print "# Nested error message: $error";
}

# Test 3: Multiple levels of nesting
{
my $error;
my $code = '
sub level3 {
die "Deep error";
}
sub level2 {
level3();
}
sub level1 {
level2();
}
level1();
';
eval $code;
$error = $@;

like($error, qr/Deep error/, 'multi-level die message preserved');
like($error, qr/at \(eval \d+\)/, 'multi-level die shows location');

print "# Multi-level error: $error";
}

# Test 4: Die without explicit message
{
my $error;
my $code = '
$@ = "Previous error\n";
sub test_bare_die {
die;
}
test_bare_die();
';
eval $code;
$error = $@;

# Bare die should propagate $@
like($error, qr/Previous error|Died at/, 'bare die behavior');

print "# Bare die error: $error";
}

# Test 5: Verify line numbers are accurate
{
my $error;
my $code = '# Line 1
sub test_line_numbers { # Line 2
die "Line number test"; # Line 3
} # Line 4
test_line_numbers(); # Line 5
';
eval $code;
$error = $@;

# Should report line 3 (where die is)
like($error, qr/at \(eval \d+\) line 3/, 'die reports correct line number');

print "# Line number error: $error";
}

done_testing();
71 changes: 71 additions & 0 deletions dev/interpreter/tests/line_numbers_comprehensive.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;

# Test 1: Simple die - line 7
eval { die "Line7" };
like($@, qr/Line7 at .* line 7/, 'die on line 7');

# Test 2: Die with blank lines
eval {


die "Line14";

};
like($@, qr/Line14 at .* line 14/, 'die with blank lines shows line 14');

# Test 3: Die in subroutine
sub level1 {
die "In level1";
}
eval { level1(); };
like($@, qr/In level1 at/, 'die in subroutine');

# Test 4: Multi-level subroutines
sub level3 { die "Level3" }
sub level2 { level3() }
sub level1_nested { level2() }
eval { level1_nested(); };
like($@, qr/Level3 at/, 'die in nested subroutines');

# Test 5: caller() with no nesting
sub test_caller_0 {
my ($pkg, $file, $line) = caller(0);
return ($pkg, $file, $line);
}
my ($p, $f, $l) = test_caller_0();
is($p, 'main', 'caller(0) package');
ok($l > 0, 'caller(0) line number');

# Test 6: caller() with nesting
sub inner_caller {
my ($pkg, $file, $line) = caller(1);
return ($pkg, $file, $line);
}
sub outer_caller {
return inner_caller();
}
my ($p2, $f2, $l2) = outer_caller();
is($p2, 'main', 'caller(1) package');
ok($l2 > 0, 'caller(1) line number');

# Test 7: caller() with blank lines


sub with_blanks {


my ($pkg, $file, $line) = caller(0);


return $line;
}


my $line_number = with_blanks();
# This call is on line ~68, with_blanks caller(0) should report this line
ok($line_number > 60 && $line_number < 75, 'caller with blank lines');

done_testing();
Loading