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>;