---- title: BIBTEX module – parse .bib files SPDX-FileCopyrightText: 2024 Norman Gray SPDX-License-Identifier: BSD-2-Clause This module contains functions * to parse BibTeX files (`.bib`) and examine the results; * to write such a parsed database in a couple of different formats; and * to format entries for output. Parsing ------- The core function is [`parse-bibtex-file`](#fn-parse-bibtex-file), alongside its companion [`parse-bibtex-string`](#fn-parse-bibtex-string). This reads a `.bib` file, and produces a list of [`entry?`](#fn-entry?) structures. These structures can be examined and processed using the various `entry-*` functions described below. This produces a list of `entry?` structures, which might be usefully filtered with [`filter-entries`](#fn-filter-entries). Entries can have ‘local’ variables attached to them during processing. These are set and retrieved using [`entry-set-local-variable!`](#fn-entry-set-local-variable!) and [`entry-get-local-variable`](#fn-entry-get-local-variable) respectively. Writing ------- The three `write-bibtex/FMT!` functions take an entry list and write it out in one or other suitable formats. For example, the following program will read an aux file, and write out a new `.bib` file containing only the entries cited. (define auxfile-name "path/to/foo.aux") ;; ...includes \bibdata{...} (module 'bibtex 'aux) (let ((aux-info (parse-aux-file auxfile-name))) ;; aux-info is ((key key ...) (bibfile bibfile...) stylefile) (let ((citations (car aux-info)) (bibfile (caadr aux-info))) ;first bibfile ;(printf "citations: ~s~%bibfile=~s~%" citations bibfile) (let ((entries (filter-entries (parse-bibtex-file bibfile) citations))) (write-bibtex/bib! entries)))) See `examples/extract-bib.scm` for a more elaborate version of the same thing. Formatting entries ------------------ The overall structure of a beastie BibTeX parser is to parse a `.bib` file using [`parse-bibtex-file`](#fn-parse-bibtex-file), and then extract from it the entries required (possibly using `parse-aux-file` and `filter-entries` as above). This gives a list of `entry?` objects. It is frdequently convenient to then map these to a list of [`citation?`](#fn-citation?) objects before writing out the results. The object created by [`make-citation`](#fn-make-citation) conveniently bundles together information about the citation, accessible via [`citation-entry`](#fn-citation-entry), [`citation-html`](#fn-citation-html), [`citation-key`](#fn-citation-key), [`citation-reference`](#fn-citation-reference). This is the approach taken by the `examples/plain.scm` style file in the distribution, but if it's not useful to use that style, then this bit of structure can be omitted. When formatting an entry into an output, it's useful to use the [`with-fields-from-entry`](#fn-with-fields-from-entry) function to extract and validate entry contents, and the [`maybe-list`](#fn-maybe-list), [`maybe-list/qq`](#fn-maybe-list/qq), [`maybe-sprintf`](#fn-maybe-sprintf) functions to conditionally create output content, and the [`sentence`](#fn-sentence) macro to add or omit full stops as necessary. For example, `plain.scm` contains a function `format-entry-article`, which looks like: (define (format-entry-article e) (with-fields-from-entry e (author title journal year) (volume number pages doi note url) (let ((al (parse-author-list author))) (make-citation e (sprintf "~a, ~a" (format-authorlist/ref al) (or year "n.d.")) `(li ,@(sentence (a ((name ,(symbol->string (entry-key e)))) ,(format-authorlist/text al)) ,(sprintf " (~a), " year) "‘" ,(titlecase-string/bst title) "’, " (em ,journal) ,(or (maybe-sprintf " (~a)" number) ", ") ,(maybe-list 'strong (stringify/true volume) ", ") ;alternatively ,@(maybe-list `(strong ,(stringify/true volume))) ,@(maybe-list/qq " pp." nbsp ,(en-dashify pages))) ,@(format-links doi url) ,@(sentence ,note)))))) Within the `with-fields-from-entry` form, the fields for author, title, journal and year are filled with the corresponding data, and if they are absent, then beastie prints a warning and defines them to have some dummy value (ie, they are guaranteed to be non-empty); and the other fields are filled with entry data, or are set to `#f`. The function returns a `citation?` object containing the entry, a citation reference, and an xexpr for a bibliography entry (see the `xexpr` module). This `(li...)` list, corresponding to a `
  • ` element, is written using standard Scheme quasiquotation, with the non-fixed parts of the list filled in as usual. Here, the [`(maybe-sprintf " (~a)" number)`](#fn-maybe-sprintf) will format the journal number containing the article. If `number` is present, then this will act as the function `sprintf`, but `number` is `#f` then this will expand to `#f` without error. The function `stringify/true` (from the `utils` module) will stringify its argument, unless the argument is `#f`, when it will expand without error to `#f`. The form [`(maybe-list 'strong (stringify/true volume) ", ")`](#fn-maybe-list) will act list `(list ...)`, unless any of its arguments are `#f`, when it will expand to a empty list ([`maybe-list/qq`](#fn-maybe-list/qq) does the same, but quasi-quotes its arguments). Finally, the [`(sentence ...)` macro](#fn-sentence) will expand to its contents as a list, except with a full stop appended. If this would be an empty sentence, then it expands to `()`.