Skip to main content

java_ast_parser/
lib.rs

1use lalrpop_util::lalrpop_mod;
2use ownable::{IntoOwned, traits::IntoOwned};
3use std::borrow::Cow;
4
5use crate::lexer::{LexicalError, Token};
6
7pub mod ast;
8mod lexer;
9
10lalrpop_mod!(
11    #[allow(clippy::ptr_arg)]
12    #[rustfmt::skip]
13    java
14);
15
16#[derive(Debug, Clone, PartialEq, IntoOwned)]
17pub struct ErrorLocation {
18    raw: usize,
19
20    row: usize,
21    column: usize,
22}
23
24impl ErrorLocation {
25    pub fn new(str: &str, loc: usize) -> Self {
26        let row = str[..loc].chars().filter(|&ch| ch == '\n').count() + 1;
27        let column = loc - str[..loc].rfind("\n").unwrap_or(0);
28
29        ErrorLocation {
30            raw: loc,
31            row,
32            column,
33        }
34    }
35}
36
37#[derive(Debug, Clone)]
38pub enum Error<'a> {
39    InvalidToken {
40        location: ErrorLocation,
41    },
42
43    UnrecognizedEof {
44        location: ErrorLocation,
45        expected: Vec<String>,
46    },
47
48    UnrecognizedToken {
49        token: (ErrorLocation, Token<'a>, ErrorLocation),
50        expected: Vec<String>,
51    },
52
53    ExtraToken {
54        token: (ErrorLocation, Token<'a>, ErrorLocation),
55    },
56
57    User {
58        error: LexicalError,
59    },
60}
61
62impl<'a> IntoOwned for Error<'a> {
63    type Owned = Error<'static>;
64
65    fn into_owned(self) -> Self::Owned {
66        match self {
67            Error::UnrecognizedToken {
68                token: (l, t, r),
69                expected,
70            } => Error::UnrecognizedToken {
71                token: (l, t.into_owned(), r),
72                expected,
73            },
74            Error::ExtraToken { token: (l, t, r) } => Error::ExtraToken {
75                token: (l, t.into_owned(), r),
76            },
77            Error::InvalidToken { location } => Error::InvalidToken { location },
78            Error::UnrecognizedEof { location, expected } => {
79                Error::UnrecognizedEof { location, expected }
80            }
81            Error::User { error } => Error::User { error },
82        }
83    }
84}
85
86#[derive(Clone, IntoOwned)]
87pub struct ErrorCell<'a> {
88    pub owner: Cow<'a, str>,
89    pub inner: Error<'a>,
90}
91
92impl<'a> ErrorCell<'a> {
93    fn new(
94        owner: &'a str,
95        error: lalrpop_util::ParseError<usize, Token<'a>, LexicalError>,
96    ) -> Self {
97        Self {
98            owner: Cow::from(owner),
99            inner: match error {
100                lalrpop_util::ParseError::InvalidToken { location } => Error::InvalidToken {
101                    location: ErrorLocation::new(owner, location),
102                },
103                lalrpop_util::ParseError::UnrecognizedEof { location, expected } => {
104                    Error::UnrecognizedEof {
105                        location: ErrorLocation::new(owner, location),
106                        expected,
107                    }
108                }
109                lalrpop_util::ParseError::UnrecognizedToken {
110                    token: (l, t, r),
111                    expected,
112                } => Error::UnrecognizedToken {
113                    token: (
114                        ErrorLocation::new(owner, l),
115                        t,
116                        ErrorLocation::new(owner, r),
117                    ),
118                    expected,
119                },
120                lalrpop_util::ParseError::ExtraToken { token: (l, t, r) } => Error::ExtraToken {
121                    token: (
122                        ErrorLocation::new(owner, l),
123                        t,
124                        ErrorLocation::new(owner, r),
125                    ),
126                },
127                lalrpop_util::ParseError::User { error } => Error::User { error },
128            },
129        }
130    }
131}
132
133impl<'a> std::fmt::Display for ErrorCell<'a> {
134    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
135        match &self.inner {
136            Error::InvalidToken { location } => {
137                write!(f, "Invalid token at {}:{}", location.row, location.column)?
138            }
139            Error::UnrecognizedEof { location, expected } => write!(
140                f,
141                "Unrecognized end of file at {}:{}, expected '{}'",
142                location.row,
143                location.column,
144                expected.join("', '")
145            )?,
146            Error::UnrecognizedToken { token, expected } => write!(
147                f,
148                "Unrecognized token '{}' from {}:{} to {}:{}, expected '{}'",
149                token.1,
150                token.0.row,
151                token.0.column,
152                token.2.row,
153                token.2.column,
154                expected.join("', '")
155            )?,
156            Error::ExtraToken { token } => write!(
157                f,
158                "Unexpected token '{}' at the end of file from {}:{} to {}:{}",
159                token.1, token.0.row, token.0.column, token.2.row, token.2.column
160            )?,
161            Error::User { error } => {
162                let start = ErrorLocation::new(self.owner.as_ref(), error.span.start);
163                let end = ErrorLocation::new(self.owner.as_ref(), error.span.end);
164
165                match &error.kind {
166                    lexer::LexicalErrorKind::InvalidToken => write!(
167                        f,
168                        "Invalid token '{}' from {}:{} to {}:{}",
169                        &self.owner[error.span.start..error.span.end],
170                        start.row,
171                        start.column,
172                        end.row,
173                        end.column
174                    )?,
175                    lexer::LexicalErrorKind::InvalidInteger(parse_int_error) => write!(
176                        f,
177                        "Invalid integer '{}' from {}:{} to {}:{}: {}",
178                        &self.owner[error.span.start..error.span.end],
179                        start.row,
180                        start.column,
181                        end.row,
182                        end.column,
183                        &parse_int_error
184                    )?,
185                    lexer::LexicalErrorKind::InvalidFloat(parse_float_error) => write!(
186                        f,
187                        "Invalid float '{}' from {}:{} to {}:{}: {}",
188                        &self.owner[error.span.start..error.span.end],
189                        start.row,
190                        start.column,
191                        end.row,
192                        end.column,
193                        &parse_float_error
194                    )?,
195                }
196            }
197        };
198
199        Ok(())
200    }
201}
202
203pub fn parse<'a>(data: &'a str) -> Result<ast::Root, Box<ErrorCell<'a>>> {
204    let lexer = lexer::Lexer::new(data);
205    let parser = java::RootParser::new();
206
207    parser
208        .parse(data, lexer)
209        .map_err(|e| Box::new(ErrorCell::new(data, e)))
210}