Files
overleaf-cep/services/web/frontend/js/features/source-editor/lezer-bibtex/bibtex.grammar
T
Eric Mc Sween 0f22a44d1a Merge pull request #37005 from overleaf/em-bibtex-grammar-fixes
[web] match biber in three bibtex parsing edge cases

GitOrigin-RevId: da7e2c0b9f137e7debfe26f09c531a61a135e40e
2026-09-17 12:08:25 +00:00

152 lines
4.0 KiB
Plaintext

@tokens {
whiteSpace { $[\t\n\r ]+ }
// Anything outside of an entry at the top level is a BibTeX free-form comment.
// A word may contain a "%" but may not start with one, so a "%" at a token
// boundary still opens a comment: btparse parses the entry in
// "Junk% @article{a}" but treats "Junk % @article{a}" as a comment.
junkWord { ![@ \t\n\r%] ![@ \t\n\r]* }
Junk { junkWord (whiteSpace junkWord)* }
// BibLaTeX supports comments starting with a % and extending to the end of
// the line
Comment { "%" ![\n\r]* }
// btparse derives a name by "removing the magic 10 from printable ASCII, then
// further excluding @, \ and ~", the magic 10 being " # % ' ( ) , = { } and
// space. Stating that by exclusion rather than by listing the allowed
// punctuation also admits non-ASCII: btparse's own lexer is ASCII-only here,
// but biber accepts UTF-8 throughout, including in citation keys.
// See https://metacpan.org/dist/Text-BibTeX/view/btparse/doc/bt_language.pod
identifierChar { ![\u{0}-\u{20}\u{7f}"#%'(),=@\\{}~] }
// Identifiers cannot start with a digit, so that a value like 1995 is a
// NumberLiteral rather than a macro name. Citation keys can.
identifierStart { ![\u{0}-\u{20}\u{7f}"#%'(),=@\\{}~0-9] }
identifier { identifierStart identifierChar* }
EntryType { identifier }
CitationKey { identifierChar+ }
NumberLiteral { @digit+ }
"#" "="
}
@external specialize {EntryType} specializeEntryType from "./tokens.mjs" {
stringKeyword,
preambleKeyword,
commentKeyword
}
@skip { whiteSpace | Comment }
@top Bibliography {
(Junk | Entry | CommentEntry | PreambleEntry | StringEntry)*
}
StringEntry { StringCommand StringBody }
PreambleEntry { PreambleCommand PreambleBody }
CommentEntry { CommentCommand CommentBody }
Entry { EntryCommand EntryBody }
EntryCommand { "@" EntryType }
EntryBody { commandBody<entryContents> }
StringCommand { "@" stringKeyword }
StringBody { commandBody<fields> }
PreambleCommand { "@" preambleKeyword }
PreambleBody { commandBody<Value> }
CommentCommand { "@" commentKeyword }
commandBody<contents> {
(
BodyOpen[closedBy="BodyClose"] { "{" }
contents
BodyClose[openedBy="BodyOpen"] { "}" }
) | (
BodyOpen[closedBy="BodyClose"] { "(" }
contents
BodyClose[openedBy="BodyOpen"] { ")" }
)
}
entryContents { CitationKey ("," fields)? }
fields { (Field ("," Field)* ","?)? }
Field { FieldName "=" Value }
FieldName { identifier }
Value { simpleValue ("#" simpleValue)* }
simpleValue { StringLiteral | NumberLiteral | StringName }
StringName { identifier }
@local tokens {
strOpenBrace { "{" }
strCloseBrace { "}" }
strOpenParen { "(" }
strCloseParen { ")" }
strQuote { "\"" }
@else strOther
}
@skip {} {
StringLiteral {
(
StringOpen[closedBy="StringClose"] { "\"" }
StringContents { quotedStringContents }
StringClose[openedBy="StringOpen"] { strQuote }
) | (
StringOpen[closedBy="StringClose"] { "{" }
StringContents { bracedStringContents }
StringClose[openedBy="StringOpen"] { strCloseBrace }
)
}
CommentBody {
(
BodyOpen[closedBy="BodyClose"] { "{" }
CommentContents { bracedStringContents }
BodyClose[openedBy="BodyOpen"] { strCloseBrace }
) | (
BodyOpen[closedBy="BodyClose"] { "(" }
CommentContents { parenthesizedStringContents }
BodyClose[openedBy="BodyOpen"] { strCloseParen }
)
}
quotedStringContents {
(
strOpenBrace bracedStringContents strCloseBrace |
strOpenParen |
strCloseParen |
strCloseBrace |
strOther
)*
}
parenthesizedStringContents {
(
strOpenBrace bracedStringContents strCloseBrace |
strQuote |
strOpenParen |
strCloseBrace |
strOther
)*
}
bracedStringContents {
(
strOpenBrace bracedStringContents strCloseBrace |
strQuote |
strOpenParen |
strCloseParen |
strOther
)*
}
}
@external propSource highlighting from "./highlight.mjs"