Skip to main content

litcheck_filecheck/parse/
mod.rs

1#![expect(unused_assignments)]
2
3mod lexer;
4mod parser;
5#[cfg(test)]
6mod tests;
7
8use crate::common::*;
9
10pub use self::lexer::{Lexed, Lexer, LexerError, Token};
11pub use self::parser::CheckFileParser;
12
13pub type ParseError<'a> = lalrpop_util::ParseError<usize, Token<'a>, ParserError>;
14
15#[derive(Diagnostic, Debug, thiserror::Error)]
16pub enum ParserError {
17    #[error(transparent)]
18    #[diagnostic(transparent)]
19    Lexer(#[from] LexerError),
20    #[error(transparent)]
21    #[diagnostic(transparent)]
22    Expr(#[from] crate::expr::ExprError),
23    #[error("invalid token")]
24    #[diagnostic()]
25    InvalidToken {
26        #[label("occurs here")]
27        span: SourceSpan,
28    },
29    #[error("unrecognized token")]
30    #[diagnostic(help("expected one of: {}", expected.as_slice().join(", ")))]
31    UnrecognizedToken {
32        #[label("lexed a {token} here")]
33        span: SourceSpan,
34        token: String,
35        expected: Vec<String>,
36    },
37    #[error("unexpected trailing tokens")]
38    #[diagnostic()]
39    ExtraToken {
40        #[label("{token} was found here, but was not expected")]
41        span: SourceSpan,
42        token: String,
43    },
44    #[error("unexpected end of file")]
45    #[diagnostic(help("expected one of: {}", expected.as_slice().join(", ")))]
46    UnrecognizedEof {
47        #[label("reached end of file here")]
48        span: SourceSpan,
49        expected: Vec<String>,
50    },
51    #[error("invalid check type")]
52    #[diagnostic()]
53    InvalidCheckType {
54        #[label("this is not a valid check type")]
55        span: SourceSpan,
56    },
57    #[error("invalid pattern")]
58    #[diagnostic(help("CHECK-EMPTY should be used to check for empty lines"))]
59    EmptyPattern {
60        #[label("expected a non-empty pattern")]
61        span: SourceSpan,
62    },
63    #[error("invalid pattern")]
64    #[diagnostic(help("CHECK-EMPTY must be immediately followed by a newline"))]
65    ExpectedEmptyPattern {
66        #[label("found non-empty pattern for CHECK-EMPTY check")]
67        span: SourceSpan,
68    },
69    #[error("invalid check modifier")]
70    #[diagnostic()]
71    InvalidCheckModifier {
72        #[label("this modifier is not recognized, valid modifiers are: LITERAL")]
73        span: SourceSpan,
74    },
75    #[error("invalid check modifier")]
76    #[diagnostic(help("count must be non-zero, and in decimal format"))]
77    InvalidCheckCount {
78        #[label("invalid count in -COUNT specification")]
79        span: SourceSpan,
80    },
81    #[error("unclosed substitution block")]
82    #[diagnostic()]
83    UnclosedSubstitution {
84        #[label("no closing ']]' found for this block")]
85        span: SourceSpan,
86    },
87    #[error("unclosed regex block")]
88    #[diagnostic()]
89    UnclosedRegex {
90        #[label("no closing '}}' found for this block")]
91        span: SourceSpan,
92    },
93    #[error("no check strings found with prefix(es) {}", text::DisplayCommaSeparated(.0.as_slice()))]
94    #[diagnostic()]
95    UnusedCheckPrefixes(Vec<Symbol>),
96}
97
98pub type ParseResult<T> = Result<T, ParserError>;
99
100pub trait Parser<'parser> {
101    type Value<'a>
102    where
103        'a: 'parser;
104
105    fn parse<'a: 'parser, S>(&mut self, code: &'a S) -> ParseResult<Self::Value<'a>>
106    where
107        S: ?Sized + AsRef<str> + 'a;
108}