better parser errors

This commit is contained in:
Masashi 2025-12-15 21:26:39 +05:30
commit dde1da8656
7 changed files with 93 additions and 113 deletions

View file

@ -1,47 +1,50 @@
# Bracket and parenthesis mismatch errors
# Missing closing parenthesis in function call
fn test() -> int
fn test() -> int do
let f = lambda(x) x
f(5
42
end
# Missing closing bracket in array access
fn test() -> int
fn test() -> int do
let arr = [1, 2, 3]
arr[0
42
end
struct Point
x: int,
y: int
end
# Missing closing brace in struct literal
fn test() -> int
struct Point
x: int
y: int
end
fn test() -> int do
let p = Point { x: 5, y: 10
p.x
end
# Missing closing parenthesis in tuple
fn test() -> int
fn test() -> int do
let t = (1, 2, 3
42
end
# Missing closing angle bracket in generic
fn test() -> int
fn test() -> int do
let v = Vec<int
42
end
# Extra closing parenthesis
fn test() -> int
fn test() -> int do
let x = (5))
x
# Mismatched brackets
fn test() -> int
let x = [1, 2, 3}
x[0]
end
# Missing opening parenthesis in expression
fn test() -> int
fn test() -> int do
let x = 5 + 3)
x
x
end

View file

@ -17,19 +17,16 @@ fn test() -> int
fn test() -> int
for in [1, 2, 3]
item
0
# Missing 'in' keyword in for loop
fn test() -> int
for item [1, 2, 3]
item
0
# Missing iterable in for loop
fn test() -> int
for item in
item
0
# Invalid while syntax
fn test() -> int
@ -40,10 +37,8 @@ fn test() -> int
fn test() -> int
let f = lambda
42
f()
# Missing closing parenthesis in lambda
fn test() -> int
let f = lambda(x
x
f(5)

View file

@ -1,29 +0,0 @@
# Enum syntax errors
# Missing enum name
enum
Red
Green
end
# Missing variant name
enum Color
Red
Green
end
# Invalid variant syntax (missing parentheses for tuple variant)
enum Result
Ok int
Err string
end
# Missing comma between variants
enum Color
Red Green
end
# Missing end keyword
enum Color
Red
Green

View file

@ -1,43 +0,0 @@
# Expression and statement errors
# Missing variable name in let
fn test() -> int
let = 5
42
# Missing equals in assignment
fn test() -> int
let x 5
x
# Missing expression after operator
fn test() -> int
5 +
# Invalid array syntax (missing comma)
fn test() -> int
let arr = [1 2 3]
arr[0]
# Missing closing bracket in array
fn test() -> int
let arr = [1, 2, 3
arr[0]
# Invalid struct literal (missing field value)
fn test() -> int
struct Point
x: int
y: int
end
let p = Point { x: 5, y }
p.x
# Missing colon in struct literal
fn test() -> int
struct Point
x: int
y: int
end
let p = Point { x 5, y: 10 }
p.x

View file

@ -1,5 +1,14 @@
# Pattern matching errors
struct Point
x: int,
y: int,
end
enum Result
Ok(int)
Err(string)
end
# Invalid pattern in match
fn test() -> int
match 42
@ -7,26 +16,20 @@ fn test() -> int
end
# Missing colon in struct pattern
fn test() -> int
struct Point
x: int
y: int
end
let p = Point { x: 5, y: 10 }
fn test() -> int do
let p = Point { x: 5, y: 10 }
match p
Point { x 5, y: 10 } => 1
end
end
# Invalid enum pattern
fn test() -> int
enum Result
Ok(int)
Err(string)
end
let r = Result::Ok(42)
fn test() -> int do
let r = Result::Ok(42)
match r
Result::Ok value => value
end
end
# Missing pattern in match arm
fn test() -> int
@ -38,4 +41,4 @@ fn test() -> int
fn test() -> int
match 42
x 0
end
end

View file

@ -2,7 +2,7 @@
# Missing struct name
struct
x: int
x: int,
y: int
end
@ -14,17 +14,17 @@ end
# Missing colon in field
struct Point
x int
x int,
y: int
end
# Missing end keyword
struct Point
x: int
y: int
x: int,
y: int,
# Invalid field separator (semicolon instead of comma)
struct Point
x: int;
y: int
end
end

View file

@ -44,6 +44,57 @@ fn run_test_suite() {
}
}
fn format_parse_error(source: &str, error: &suicmez::parser::ParseError) -> String {
// Find the line containing the error
let lines: Vec<&str> = source.lines().collect();
let mut current_pos = 0;
for (line_idx, line) in lines.iter().enumerate() {
let line_start = current_pos;
let line_end = current_pos + line.len();
// Check if the error span intersects with this line
if error.span.start < line_end && error.span.end > line_start {
let mut result = String::new();
// Print the error message
result.push_str(&format!("Parse error: {}\n", error.message));
// Print the line number and content
result.push_str(&format!("{} | {}\n", line_idx + 1, line));
// Calculate column positions within the line
let line_start_col = error.span.start.saturating_sub(line_start);
let line_end_col = (error.span.end - line_start).min(line.len());
// Print spaces and squiggly line for the span
result.push_str(&format!("{} | ", " ".repeat((line_idx + 1).to_string().len())));
for _ in 0..line_start_col {
result.push(' ');
}
for _ in line_start_col..line_end_col {
result.push('~');
}
result.push('\n');
// Print caret at the start position
result.push_str(&format!("{} | ", " ".repeat((line_idx + 1).to_string().len())));
for _ in 0..line_start_col {
result.push(' ');
}
result.push('^');
return result;
}
current_pos = line_end + 1; // +1 for the newline character
}
// Fallback if we can't find the line
format!("Parse error: {} (at byte {})", error.message, error.span.start)
}
fn run_file(filename: &str) -> Result<(), String> {
// Read the source file
let source = fs::read_to_string(filename)
@ -69,7 +120,7 @@ fn run_file(filename: &str) -> Result<(), String> {
let mut parser = Parser::new(filename.to_string(), tokens);
let ast_nodes = parser
.parse()
.map_err(|e| format!("Parse error: {}", e.message))?;
.map_err(|e| format_parse_error(&source, &e))?;
println!("Parsed {} AST nodes successfully", ast_nodes.len());