kbvm 0.1.5

An implementation of the XKB specification
Documentation
use crate::xkb::{
    code::Code,
    interner::{Interned, Interner},
    kccgst::{
        lexer::{
            Lexer,
            LexerError::{
                self, InvalidIntegerLiteral, UnexpectedByte, UnterminatedKeyName,
                UnterminatedString,
            },
        },
        token::{
            Punctuation,
            Token::{self, Float, Ident, Integer, KeyName, String},
        },
    },
    span::{SpanExt, Spanned},
};

fn l_(interner: &mut Interner, input: &str) -> Result<Vec<Spanned<Token>>, Spanned<LexerError>> {
    let mut output = vec![];
    let code = Code::new(&input.as_bytes().to_vec().into());
    Lexer::new(None, &code, 0).lex_item(interner, &mut output)?;
    Ok(output)
}

fn l(interner: &mut Interner, input: &str) -> Vec<Spanned<Token>> {
    l_(interner, input).unwrap()
}

fn q(input: &str) -> Vec<Spanned<Token>> {
    let mut interner = Interner::default();
    l(&mut interner, input)
}

fn e(input: &str) -> Spanned<LexerError> {
    let mut interner = Interner::default();
    l_(&mut interner, input).unwrap_err()
}

fn i(interner: &mut Interner, s: &str) -> Interned {
    let code = Code::new(&s.as_bytes().to_vec().into());
    let code = code.to_slice();
    interner.intern(&code)
}

#[test]
fn two_single_char() {
    let chars = [
        (';', token![;]),
        ('{', Punctuation::Obrace.into()),
        ('}', Punctuation::Cbrace.into()),
        ('=', token![=]),
        ('[', Punctuation::Obracket.into()),
        (']', Punctuation::Cbracket.into()),
        ('(', Punctuation::Oparen.into()),
        (')', Punctuation::Cparen.into()),
        ('.', token![.]),
        (',', token![,]),
        ('+', token![+]),
        ('-', token![-]),
        ('*', token![*]),
        ('/', token![/]),
        ('!', token![!]),
        ('~', token![~]),
    ];
    for (c1, t1) in chars {
        if t1 == token![;] {
            continue;
        }
        for (c2, t2) in chars {
            if (t1, t2) == (token![/], token![/]) {
                continue;
            }
            let input = format!("{c1}{c2}");
            let expected = [t1.spanned(0, 1), t2.spanned(1, 2)];
            assert_eq!(q(&input), expected);
        }
    }
}

#[test]
fn number() {
    let mut interner = Interner::default();

    let input = "123";
    let interned = i(&mut interner, input);
    assert_eq!(
        l(&mut interner, input),
        [Integer(interned, 123).spanned(0, 3)]
    );

    let input = "123.456";
    let interned = i(&mut interner, input);
    assert_eq!(
        l(&mut interner, input),
        [Float(interned, 123.456).spanned(0, 7)]
    );

    let input = "123.456.789";
    let interned1 = i(&mut interner, "123.456");
    let interned2 = i(&mut interner, "789");
    assert_eq!(
        l(&mut interner, input),
        [
            Float(interned1, 123.456).spanned(0, 7),
            token![.].spanned(7, 8),
            Integer(interned2, 789).spanned(8, 11),
        ]
    );

    let input = "0x123fg";
    let interned1 = i(&mut interner, "0x123f");
    let interned2 = i(&mut interner, "g");
    assert_eq!(
        l(&mut interner, input),
        [
            Integer(interned1, 0x123f).spanned(0, 6),
            Ident(interned2).spanned(6, 7),
        ]
    );

    let input = "0x123a.456";
    let interned1 = i(&mut interner, "0x123a");
    let interned2 = i(&mut interner, "456");
    assert_eq!(
        l(&mut interner, input),
        [
            Integer(interned1, 0x123a).spanned(0, 6),
            token![.].spanned(6, 7),
            Integer(interned2, 456).spanned(7, 10),
        ]
    );

    let input = "1111111111111111111111111111111111111";
    assert!(matches!(e(input).val, InvalidIntegerLiteral));
}

#[test]
fn comment() {
    let mut interner = Interner::default();
    let input = "abc // comment\ndef # comment\nghi";
    let abc = i(&mut interner, "abc");
    let def = i(&mut interner, "def");
    let ghi = i(&mut interner, "ghi");
    assert_eq!(
        l(&mut interner, input),
        [
            Ident(abc).spanned(0, 3),
            Ident(def).spanned(15, 18),
            Ident(ghi).spanned(29, 32),
        ],
    );
}

#[test]
fn ident() {
    let mut interner = Interner::default();

    let input = "abc";
    let interned = i(&mut interner, "abc");
    assert_eq!(l(&mut interner, input), [Ident(interned).spanned(0, 3),],);

    let input = "abc.";
    let interned = i(&mut interner, "abc");
    assert_eq!(
        l(&mut interner, input),
        [Ident(interned).spanned(0, 3), token![.].spanned(3, 4),],
    );

    let input = "Abc";
    let interned = i(&mut interner, "Abc");
    assert_eq!(l(&mut interner, input), [Ident(interned).spanned(0, 3),],);

    let input = "_aBc";
    let interned = i(&mut interner, "_aBc");
    assert_eq!(l(&mut interner, input), [Ident(interned).spanned(0, 4),],);

    let input = "_1aBc";
    let interned = i(&mut interner, "_1aBc");
    assert_eq!(l(&mut interner, input), [Ident(interned).spanned(0, 5),],);

    let input = "_1abc";
    let interned = i(&mut interner, "_1abc");
    assert_eq!(l(&mut interner, input), [Ident(interned).spanned(0, 5),],);

    let input = "1abc";
    let interned1 = i(&mut interner, "1");
    let interned2 = i(&mut interner, "abc");
    assert_eq!(
        l(&mut interner, input),
        [
            Integer(interned1, 1).spanned(0, 1),
            Ident(interned2).spanned(1, 4),
        ],
    );
}

#[test]
fn string() {
    let mut interner = Interner::default();

    let input = "\"abc\"";
    let interned = i(&mut interner, "abc");
    assert_eq!(l(&mut interner, input), [String(interned).spanned(0, 5),],);

    let input = r#"   "abc\""  "#;
    let interned = i(&mut interner, "abc\\\"");
    assert_eq!(
        l(&mut interner, input),
        [String(interned).spanned(3, 10),],
        "{:?}",
        interner,
    );

    let input = r#"   "abc\"   "#;
    assert_eq!(e(input), UnterminatedString.spanned(3, 12),);
}

#[test]
fn key_name() {
    let mut interner = Interner::default();

    let input = "<abc>";
    let interned = i(&mut interner, "abc");
    assert_eq!(l(&mut interner, input), [KeyName(interned).spanned(0, 5),],);

    let input = r#"<abc "#;
    assert_eq!(e(input), UnterminatedKeyName.spanned(0, 4),);

    let input = r#"<!{}#>"#;
    let interned = i(&mut interner, "!{}#");
    assert_eq!(l(&mut interner, input), [KeyName(interned).spanned(0, 6),],);
}

#[test]
fn unexpected_byte() {
    let input = "{\0";
    assert_eq!(e(input), UnexpectedByte(0).spanned(1, 2));
}