kbvm/xkb/compose/parser/
error.rs

1use {
2    crate::xkb::{
3        code_slice::CodeSlice,
4        compose::{
5            parser::Parser,
6            token::{Punctuation, Token},
7        },
8        diagnostic::DiagnosticKind,
9        span::{Span, SpanExt, Spanned},
10    },
11    bstr::ByteSlice,
12    debug_fn::debug_fn,
13    std::fmt::{self, Formatter},
14    thiserror::Error,
15};
16
17#[derive(Debug, Clone, Error)]
18pub(crate) enum ParserError {
19    #[error(
20        "expected {}, but encountered EOF",
21        debug_fn(|f| write_expected(f, .0.expected)),
22    )]
23    ExpectedButEof(ExpectedButEof),
24    #[error(
25        "expected {}, but found {}",
26        debug_fn(|f| write_expected(f, .0.expected)),
27        debug_fn(|f| write_actual(f, &.0.actual)),
28    )]
29    UnexpectedToken(UnexpectedToken),
30    #[error(
31        "expected end of line, but found {}",
32        debug_fn(|f| write_actual(f, .0)),
33    )]
34    ExpectedEol(ActualToken),
35    #[error(
36        "unknown keysym {}",
37        .0.as_bstr(),
38    )]
39    UnknownKeysym(CodeSlice<'static>),
40}
41
42impl ParserError {
43    pub(crate) fn diagnostic_kind(&self) -> DiagnosticKind {
44        match self {
45            ParserError::ExpectedButEof(_) => DiagnosticKind::UnexpectedEof,
46            ParserError::UnexpectedToken(_) => DiagnosticKind::UnexpectedToken,
47            ParserError::ExpectedEol(_) => DiagnosticKind::ExpectedEol,
48            ParserError::UnknownKeysym(_) => DiagnosticKind::UnknownKeysym,
49        }
50    }
51}
52
53fn write_actual(f: &mut Formatter<'_>, actual: &ActualToken) -> fmt::Result {
54    match actual {
55        ActualToken::Ident(i) => write!(f, "`{}`", i.as_bytes().as_bstr()),
56        ActualToken::String(i) => write!(f, "{:?}", i.as_bytes().as_bstr()),
57        ActualToken::Keysym(i) => write!(f, "<{}>", i.as_bytes().as_bstr()),
58        ActualToken::Token(t) => match t {
59            Token::Ident(_) => f.write_str("an identifier"),
60            Token::Punctuation(p) => write!(f, "`{}`", punctuation_string(*p)),
61            Token::String(_) => f.write_str("a string"),
62            Token::Keysym(_) => f.write_str("a keysym"),
63        },
64    }
65}
66
67fn write_expected(f: &mut Formatter<'_>, expected: &[Expected]) -> fmt::Result {
68    if let Some(e) = get_unique_expected(expected) {
69        return write_single_expected(f, e);
70    }
71    write_expected_(f, expected, true)
72}
73
74fn write_expected_(f: &mut Formatter<'_>, expected: &[Expected], or_prefix: bool) -> fmt::Result {
75    let last = expected.len() - 1;
76    for (idx, e) in expected.iter().enumerate() {
77        if idx > 0 {
78            f.write_str(", ")?;
79        }
80        if or_prefix && idx == last {
81            f.write_str("or ")?;
82        }
83        write_single_expected(f, e)?;
84    }
85    Ok(())
86}
87
88fn get_unique_expected(expected: &[Expected]) -> Option<&Expected> {
89    if expected.len() != 1 {
90        return None;
91    }
92    Some(&expected[0])
93}
94
95fn write_single_expected(f: &mut Formatter<'_>, expected: &Expected) -> fmt::Result {
96    match expected {
97        Expected::AnyIdent => f.write_str("an identifier"),
98        Expected::AnyString => f.write_str("a string"),
99        Expected::AnyModifier => f.write_str("a modifier"),
100        Expected::AnyKeysym => f.write_str("a keysym"),
101        Expected::Punctuation(p) => {
102            write!(f, "`{}`", punctuation_string(*p))
103        }
104    }
105}
106
107fn punctuation_string(p: Punctuation) -> &'static str {
108    match p {
109        Punctuation::Exclam => "!",
110        Punctuation::Tilde => "~",
111        Punctuation::Colon => ":",
112    }
113}
114
115#[derive(Debug, Clone)]
116pub(crate) struct ExpectedButEof {
117    expected: &'static [Expected],
118}
119
120#[derive(Debug, Clone)]
121pub(crate) struct UnexpectedToken {
122    expected: &'static [Expected],
123    actual: ActualToken,
124}
125
126#[derive(Debug)]
127pub(crate) enum Expected {
128    AnyIdent,
129    AnyString,
130    AnyModifier,
131    AnyKeysym,
132    Punctuation(Punctuation),
133}
134
135#[derive(Clone, Debug, PartialEq)]
136pub(crate) enum ActualToken {
137    Ident(CodeSlice<'static>),
138    String(CodeSlice<'static>),
139    Keysym(CodeSlice<'static>),
140    Token(Token),
141}
142
143impl Parser<'_, '_, '_> {
144    pub(super) fn expected_but_eof(
145        &self,
146        span: Span,
147        expected: &'static [Expected],
148    ) -> Spanned<ParserError> {
149        ParserError::ExpectedButEof(ExpectedButEof { expected }).spanned2(span)
150    }
151
152    fn token_to_actual(&self, token: Spanned<Token>) -> ActualToken {
153        match token.val {
154            Token::Ident(i) => ActualToken::Ident(self.interner.get(i).to_owned()),
155            Token::Keysym(i) => ActualToken::Keysym(self.interner.get(i).to_owned()),
156            Token::String(i) => ActualToken::String(self.interner.get(i).to_owned()),
157            _ => ActualToken::Token(token.val),
158        }
159    }
160
161    pub(super) fn expected_eol(&self, token: Spanned<Token>) -> Spanned<ParserError> {
162        let actual = self.token_to_actual(token);
163        ParserError::ExpectedEol(actual).spanned2(token.span)
164    }
165
166    pub(super) fn unexpected_token(
167        &self,
168        expected: &'static [Expected],
169        token: Spanned<Token>,
170    ) -> Spanned<ParserError> {
171        let actual = self.token_to_actual(token);
172        ParserError::UnexpectedToken(UnexpectedToken { expected, actual }).spanned2(token.span)
173    }
174
175    pub(super) fn unknown_keysym(&self, code: &CodeSlice<'_>, span: Span) -> Spanned<ParserError> {
176        ParserError::UnknownKeysym(code.to_owned()).spanned2(span)
177    }
178}
179
180pub(super) const LHS: &[Expected] = &[
181    Expected::Punctuation(punctuation![!]),
182    Expected::Punctuation(punctuation![~]),
183    Expected::Punctuation(punctuation![:]),
184    Expected::AnyKeysym,
185    Expected::AnyModifier,
186];