oxur_ast/
error.rs

1use std::fmt;
2use std::path::PathBuf;
3
4/// Position in source text
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct Position {
7    pub offset: usize, // Byte offset
8    pub line: usize,   // Line number (1-based)
9    pub column: usize, // Column number (1-based)
10}
11
12impl Position {
13    pub fn new(offset: usize, line: usize, column: usize) -> Self {
14        Self { offset, line, column }
15    }
16}
17
18impl fmt::Display for Position {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        write!(f, "line {}, column {}", self.line, self.column)
21    }
22}
23
24/// Lexer errors
25#[derive(Debug, thiserror::Error)]
26pub enum LexError {
27    #[error("Unexpected character '{ch}' at {pos}")]
28    UnexpectedChar { ch: char, pos: Position },
29
30    #[error("Unterminated string at {pos}")]
31    UnterminatedString { pos: Position },
32
33    #[error("Invalid escape sequence '\\{ch}' at {pos}")]
34    InvalidEscape { ch: char, pos: Position },
35
36    #[error("Unexpected end of input")]
37    UnexpectedEof,
38}
39
40/// Parser errors
41#[derive(Debug, thiserror::Error)]
42pub enum ParseError {
43    #[error("Unexpected token {token:?} at {pos}")]
44    UnexpectedToken { token: String, pos: Position },
45
46    #[error("Expected {expected}, found {found} at {pos}")]
47    Expected { expected: String, found: String, pos: Position },
48
49    #[error("Unterminated list at {pos}")]
50    UnterminatedList { pos: Position },
51
52    #[error("Unexpected closing parenthesis at {pos}")]
53    UnexpectedCloseParen { pos: Position },
54
55    #[error("Empty input")]
56    EmptyInput,
57
58    #[error("Failed to read file {path:?}: {source}")]
59    FileReadError {
60        path: PathBuf,
61        #[source]
62        source: std::io::Error,
63    },
64
65    #[error("Lexer error: {0}")]
66    LexError(#[from] LexError),
67}
68
69pub type Result<T> = std::result::Result<T, ParseError>;