Skip to main content

badness_parser/bib/
syntax.rs

1//! `SyntaxKind` — the kinds of CST tokens and nodes — and the rowan `Language`
2//! binding for badness's BibTeX/BibLaTeX surface CST.
3//!
4//! BibTeX is a distinct grammar from LaTeX, so it carries its own kind set and
5//! [`Language`] marker rather than reusing [`crate::syntax`]. The structure
6//! deliberately mirrors that module (`#[repr(u16)]`, tokens first, nodes after,
7//! `ROOT` last + `COUNT`) so the two stay easy to compare.
8
9use rowan::Language;
10
11/// Kinds of tokens (terminals, from the lexer) and nodes (composites, from the
12/// parser) in the BibTeX CST.
13///
14/// Token kinds come first, node kinds after; `ROOT` is kept **last** so
15/// [`BibLang::kind_from_raw`] can bounds-check the raw discriminant with a single
16/// comparison. Do not add variants after `ROOT`.
17#[allow(non_camel_case_types)]
18#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
19#[repr(u16)]
20pub enum SyntaxKind {
21    // --- Tokens (terminals, produced by the lexer) ---
22    AT,         // @
23    L_BRACE,    // {
24    R_BRACE,    // }
25    L_PAREN,    // (
26    R_PAREN,    // )
27    COMMA,      // ,
28    EQ,         // =
29    HASH,       // #  (value concatenation)
30    QUOTE,      // "
31    WORD,       // a run of identifier / value characters
32    NUMBER,     // a pure-digit run (kept distinct from a macro name)
33    WHITESPACE, // spaces / tabs
34    NEWLINE,    // `\n`, `\r\n`, or `\r`
35    ERROR,      // lexer fallback; the lexer is total, so this is unused today
36
37    // --- Nodes (composites, produced by the parser) ---
38    JUNK,           // free text between entries (BibTeX ignores it)
39    ENTRY,          // a regular bibliographic entry: @type{ key, fields }
40    STRING_ENTRY,   // @string{ name = value }
41    PREAMBLE_ENTRY, // @preamble{ value }
42    COMMENT_ENTRY,  // @comment{ … }
43    ENTRY_TYPE,     // the type word following `@`
44    KEY,            // the cite key
45    FIELD,          // name = value
46    FIELD_NAME,     // the field / macro name on the left of `=`
47    VALUE,          // the right-hand side; value pieces separated by `#`
48    BRACE_GROUP,    // { … }  (recursive: may nest BRACE_GROUP)
49    QUOTED,         // " … "  (may contain nested BRACE_GROUP)
50    LITERAL,        // a bare word / number value piece (macro ref or number)
51    ROOT,           // the file root  (keep LAST)
52}
53
54impl SyntaxKind {
55    /// The number of `SyntaxKind` variants. Sound because the enum is
56    /// `#[repr(u16)]` with contiguous discriminants `0..=ROOT` and `ROOT` is kept
57    /// last; used to size kind-indexed tables.
58    pub const COUNT: usize = SyntaxKind::ROOT as usize + 1;
59}
60
61impl From<SyntaxKind> for rowan::SyntaxKind {
62    fn from(kind: SyntaxKind) -> Self {
63        Self(kind as u16)
64    }
65}
66
67/// The rowan language marker for badness's BibTeX CST.
68#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
69pub enum BibLang {}
70
71impl Language for BibLang {
72    type Kind = SyntaxKind;
73
74    fn kind_from_raw(raw: rowan::SyntaxKind) -> SyntaxKind {
75        assert!(
76            raw.0 <= SyntaxKind::ROOT as u16,
77            "invalid bib SyntaxKind discriminant: {}",
78            raw.0
79        );
80        // SAFETY: `SyntaxKind` is `#[repr(u16)]` with contiguous discriminants
81        // `0..=ROOT`, and the assert above bounds `raw.0` into that range.
82        unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
83    }
84
85    fn kind_to_raw(kind: SyntaxKind) -> rowan::SyntaxKind {
86        kind.into()
87    }
88}
89
90pub type SyntaxNode = rowan::SyntaxNode<BibLang>;
91pub type SyntaxToken = rowan::SyntaxToken<BibLang>;
92pub type SyntaxElement = rowan::SyntaxElement<BibLang>;