1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
mod lexer;
mod parser;
#[cfg(test)]
mod tests;

use crate::common::*;

pub use self::lexer::{Lexed, Lexer, LexerError, Token};
pub use self::parser::CheckFileParser;

pub type ParseError<'a> = lalrpop_util::ParseError<usize, Token<'a>, ParserError>;

#[derive(Diagnostic, Debug, thiserror::Error)]
pub enum ParserError {
    #[error(transparent)]
    #[diagnostic(transparent)]
    Lexer(#[from] LexerError),
    #[error(transparent)]
    #[diagnostic(transparent)]
    Expr(#[from] crate::expr::ExprError),
    #[error("invalid token")]
    #[diagnostic()]
    InvalidToken {
        #[label("occurs here")]
        span: SourceSpan,
    },
    #[error("unrecognized token")]
    #[diagnostic(help("expected one of: {}", expected.as_slice().join(", ")))]
    UnrecognizedToken {
        #[label("lexed a {token} here")]
        span: SourceSpan,
        token: String,
        expected: Vec<String>,
    },
    #[error("unexpected trailing tokens")]
    #[diagnostic()]
    ExtraToken {
        #[label("{token} was found here, but was not expected")]
        span: SourceSpan,
        token: String,
    },
    #[error("unexpected end of file")]
    #[diagnostic(help("expected one of: {}", expected.as_slice().join(", ")))]
    UnrecognizedEof {
        #[label("reached end of file here")]
        span: SourceSpan,
        expected: Vec<String>,
    },
    #[error("invalid check type")]
    #[diagnostic()]
    InvalidCheckType {
        #[label("this is not a valid check type")]
        span: SourceSpan,
    },
    #[error("invalid pattern")]
    #[diagnostic(help("CHECK-EMPTY should be used to check for empty lines"))]
    EmptyPattern {
        #[label("expected a non-empty pattern")]
        span: SourceSpan,
    },
    #[error("invalid check modifier")]
    #[diagnostic()]
    InvalidCheckModifier {
        #[label("this modifier is not recognized, valid modifiers are: LITERAL")]
        span: SourceSpan,
    },
    #[error("unclosed substitution block")]
    #[diagnostic()]
    UnclosedSubstitution {
        #[label("no closing ']]' found for this block")]
        span: SourceSpan,
    },
    #[error("unclosed regex block")]
    #[diagnostic()]
    UnclosedRegex {
        #[label("no closing '}}' found for this block")]
        span: SourceSpan,
    },
    #[error("no check strings found with prefix(es) {}", text::DisplayCommaSeparated(.0.as_slice()))]
    #[diagnostic()]
    UnusedCheckPrefixes(Vec<Arc<str>>),
}

pub type ParseResult<T> = Result<T, ParserError>;

pub trait Parser<'parser> {
    type Value<'a>
    where
        'a: 'parser;

    fn parse<'a: 'parser, S>(&mut self, code: &'a S) -> ParseResult<Self::Value<'a>>
    where
        S: SourceFile + ?Sized + 'a;
}