litcheck_filecheck/parse/
mod.rs1#![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}