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

View file

@ -17,19 +17,16 @@ fn test() -> int
fn test() -> int fn test() -> int
for in [1, 2, 3] for in [1, 2, 3]
item item
0
# Missing 'in' keyword in for loop # Missing 'in' keyword in for loop
fn test() -> int fn test() -> int
for item [1, 2, 3] for item [1, 2, 3]
item item
0
# Missing iterable in for loop # Missing iterable in for loop
fn test() -> int fn test() -> int
for item in for item in
item item
0
# Invalid while syntax # Invalid while syntax
fn test() -> int fn test() -> int
@ -40,10 +37,8 @@ fn test() -> int
fn test() -> int fn test() -> int
let f = lambda let f = lambda
42 42
f()
# Missing closing parenthesis in lambda # Missing closing parenthesis in lambda
fn test() -> int fn test() -> int
let f = lambda(x let f = lambda(x
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 # Pattern matching errors
struct Point
x: int,
y: int,
end
enum Result
Ok(int)
Err(string)
end
# Invalid pattern in match # Invalid pattern in match
fn test() -> int fn test() -> int
match 42 match 42
@ -7,26 +16,20 @@ fn test() -> int
end end
# Missing colon in struct pattern # Missing colon in struct pattern
fn test() -> int fn test() -> int do
struct Point let p = Point { x: 5, y: 10 }
x: int
y: int
end
let p = Point { x: 5, y: 10 }
match p match p
Point { x 5, y: 10 } => 1 Point { x 5, y: 10 } => 1
end end
end
# Invalid enum pattern # Invalid enum pattern
fn test() -> int fn test() -> int do
enum Result let r = Result::Ok(42)
Ok(int)
Err(string)
end
let r = Result::Ok(42)
match r match r
Result::Ok value => value Result::Ok value => value
end end
end
# Missing pattern in match arm # Missing pattern in match arm
fn test() -> int fn test() -> int
@ -38,4 +41,4 @@ fn test() -> int
fn test() -> int fn test() -> int
match 42 match 42
x 0 x 0
end end

View file

@ -2,7 +2,7 @@
# Missing struct name # Missing struct name
struct struct
x: int x: int,
y: int y: int
end end
@ -14,17 +14,17 @@ end
# Missing colon in field # Missing colon in field
struct Point struct Point
x int x int,
y: int y: int
end end
# Missing end keyword # Missing end keyword
struct Point struct Point
x: int x: int,
y: int y: int,
# Invalid field separator (semicolon instead of comma) # Invalid field separator (semicolon instead of comma)
struct Point struct Point
x: int; x: int;
y: 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> { fn run_file(filename: &str) -> Result<(), String> {
// Read the source file // Read the source file
let source = fs::read_to_string(filename) 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 mut parser = Parser::new(filename.to_string(), tokens);
let ast_nodes = parser let ast_nodes = parser
.parse() .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()); println!("Parsed {} AST nodes successfully", ast_nodes.len());