kbvm 0.1.5

An implementation of the XKB specification
Documentation
use {
    crate::xkb::{
        code_slice::CodeSlice,
        diagnostic::DiagnosticKind,
        interner::Interned,
        rmlvo::{
            parser::Parser,
            token::{Punctuation, Token},
        },
        span::{Span, SpanExt, SpanUnit, Spanned},
    },
    bstr::ByteSlice,
    debug_fn::debug_fn,
    std::fmt::{self, Formatter},
    thiserror::Error,
};

#[derive(Debug, Clone, Error)]
pub(crate) enum ParserError {
    #[error(
        "expected {}, but encountered EOF",
        debug_fn(|f| write_expected(f, .0.expected)),
    )]
    ExpectedButEof(ExpectedButEof),
    #[error(
        "expected {}, but found {}",
        debug_fn(|f| write_expected(f, .0.expected)),
        debug_fn(|f| write_actual(f, &.0.actual)),
    )]
    UnexpectedToken(UnexpectedToken),
    #[error(
        "expected end of line, but found {}",
        debug_fn(|f| write_actual(f, .0)),
    )]
    ExpectedEol(ActualToken),
    #[error(
        "expected `[` but found `{}`",
        *.0 as char,
    )]
    IndexStart(u8),
    #[error(
        "expected `]` but found `{}`",
        *.0 as char,
    )]
    IndexEnd(u8),
    #[error(
        "expected `single`, `first`, `later`, `any`, or a group index, but found `{}`",
        .0.as_bytes().as_bstr()
    )]
    Index(CodeSlice<'static>),
}

impl ParserError {
    pub(crate) fn diagnostic_kind(&self) -> DiagnosticKind {
        match self {
            ParserError::ExpectedButEof(_) => DiagnosticKind::UnexpectedEof,
            ParserError::UnexpectedToken(_) => DiagnosticKind::UnexpectedToken,
            ParserError::ExpectedEol(_) => DiagnosticKind::ExpectedEol,
            ParserError::IndexStart(_) => DiagnosticKind::ExpectedIndexStart,
            ParserError::IndexEnd(_) => DiagnosticKind::ExpectedIndexEnd,
            ParserError::Index(_) => DiagnosticKind::InvalidMatcherIndex,
        }
    }
}

fn write_actual(f: &mut Formatter<'_>, actual: &ActualToken) -> fmt::Result {
    match actual {
        ActualToken::Ident(i) => write!(f, "`{}`", i.as_bytes().as_bstr()),
        ActualToken::GroupName(i) => write!(f, "`${}`", i.as_bytes().as_bstr()),
        ActualToken::Token(t) => match t {
            Token::Ident(_) => f.write_str("an identifier"),
            Token::GroupName(_) => f.write_str("a group name"),
            Token::Punctuation(p) => write!(f, "`{}`", punctuation_string(*p)),
        },
    }
}

fn write_expected(f: &mut Formatter<'_>, expected: &[Expected]) -> fmt::Result {
    if let Some(e) = get_unique_expected(expected) {
        return write_single_expected(f, e, false);
    }
    write_expected_(f, expected, true)
}

fn write_expected_(f: &mut Formatter<'_>, expected: &[Expected], or_prefix: bool) -> fmt::Result {
    let last = expected.len() - 1;
    for (idx, e) in expected.iter().enumerate() {
        if idx > 0 {
            f.write_str(", ")?;
        }
        if let Expected::Nested(n) = e {
            if idx == last {
                write_expected_(f, n, or_prefix)?;
                continue;
            }
        } else {
            if or_prefix && idx == last {
                f.write_str("or ")?;
            }
        }
        write_single_expected(f, e, false)?;
    }
    Ok(())
}

fn get_unique_expected(expected: &[Expected]) -> Option<&Expected> {
    if expected.len() != 1 {
        return None;
    }
    if let Expected::Nested(e) = &expected[0] {
        return get_unique_expected(e);
    }
    Some(&expected[0])
}

fn write_single_expected(
    f: &mut Formatter<'_>,
    expected: &Expected,
    or_prefix: bool,
) -> fmt::Result {
    match expected {
        Expected::Nested(n) => write_expected_(f, n, or_prefix),
        Expected::Ident(i) => write!(f, "`{}`", i),
        Expected::AnyIdent => f.write_str("an identifier"),
        Expected::AnyGroupName => f.write_str("a group name"),
        Expected::Punctuation(p) => {
            write!(f, "`{}`", punctuation_string(*p))
        }
    }
}

fn punctuation_string(p: Punctuation) -> &'static str {
    match p {
        Punctuation::Equals => "=",
        Punctuation::Times => "*",
        Punctuation::Exclam => "!",
    }
}

#[derive(Debug, Clone)]
pub(crate) struct ExpectedButEof {
    expected: &'static [Expected],
}

#[derive(Debug, Clone)]
pub(crate) struct UnexpectedToken {
    expected: &'static [Expected],
    actual: ActualToken,
}

#[derive(Debug)]
pub(crate) enum Expected {
    Nested(&'static [Expected]),
    Ident(&'static str),
    AnyIdent,
    AnyGroupName,
    Punctuation(Punctuation),
}

#[derive(Clone, Debug, PartialEq)]
pub(crate) enum ActualToken {
    Ident(CodeSlice<'static>),
    GroupName(CodeSlice<'static>),
    Token(Token),
}

impl Parser<'_, '_, '_> {
    pub(super) fn expected_but_eof(
        &self,
        span: Span,
        expected: &'static [Expected],
    ) -> Spanned<ParserError> {
        ParserError::ExpectedButEof(ExpectedButEof { expected }).spanned2(span)
    }

    fn token_to_actual(&self, token: Spanned<Token>) -> ActualToken {
        match token.val {
            Token::Ident(i) => ActualToken::Ident(self.interner.get(i).to_owned()),
            Token::GroupName(i) => ActualToken::GroupName(self.interner.get(i).to_owned()),
            _ => ActualToken::Token(token.val),
        }
    }

    pub(super) fn expected_eol(&self, token: Spanned<Token>) -> Spanned<ParserError> {
        let actual = self.token_to_actual(token);
        ParserError::ExpectedEol(actual).spanned2(token.span)
    }

    pub(super) fn unexpected_token(
        &self,
        expected: &'static [Expected],
        token: Spanned<Token>,
    ) -> Spanned<ParserError> {
        let actual = self.token_to_actual(token);
        ParserError::UnexpectedToken(UnexpectedToken { expected, actual }).spanned2(token.span)
    }

    pub(super) fn index_start(
        &self,
        ident: Spanned<Interned>,
        offset: usize,
    ) -> Spanned<ParserError> {
        let slice = self.interner.get(ident.val).to_owned();
        let lo = ident.span.lo + offset as SpanUnit;
        ParserError::IndexStart(slice[offset]).spanned(lo, lo + 1)
    }

    pub(super) fn index_end(&self, ident: Spanned<Interned>) -> Spanned<ParserError> {
        let slice = self.interner.get(ident.val).to_owned();
        let hi = ident.span.hi;
        ParserError::IndexEnd(*slice.last().unwrap()).spanned(hi - 1, hi)
    }

    pub(super) fn index(&self, ident: Spanned<Interned>, offset: usize) -> Spanned<ParserError> {
        let actual = self.interner.get(ident.val);
        let actual = actual.slice(offset + 1..actual.len() - 1);
        let lo = ident.span.lo + offset as SpanUnit + 1;
        let hi = ident.span.hi - 1;
        ParserError::Index(actual.to_owned()).spanned(lo, hi)
    }
}

pub(super) const START_OF_LINE: &[Expected] = &[
    Expected::Punctuation(punctuation![!]),
    Expected::Punctuation(punctuation![*]),
    Expected::AnyIdent,
];

pub(super) const AFTER_EXCLAM: &[Expected] = &[
    Expected::Ident("include"),
    Expected::Punctuation(punctuation![*]),
    Expected::Nested(MLVO),
];

pub(super) const RULE_KEY: &[Expected] = &[
    Expected::Punctuation(punctuation![*]),
    Expected::Punctuation(punctuation![=]),
    Expected::AnyIdent,
    Expected::AnyGroupName,
];

pub(super) const MLVO: &[Expected] = &[
    Expected::Ident("model"),
    Expected::Ident("layout"),
    Expected::Ident("variant"),
    Expected::Ident("option"),
];

pub(super) const MAPPING_KEY: &[Expected] = &[
    Expected::Nested(MLVO),
    Expected::Punctuation(punctuation![=]),
];

pub(super) const MAPPING_VALUE: &[Expected] = &[
    Expected::Ident("keycodes"),
    Expected::Ident("symbols"),
    Expected::Ident("types"),
    Expected::Ident("compat"),
    Expected::Ident("geometry"),
];