;; Functions supporting aux-file parsing.
;;
;; This file is part of Beastie <https://purl.org/nxg/dist/beastie>
;; SPDX-FileCopyrightText: 2024 Norman Gray <https://nxg.me.uk>
;; SPDX-License-Identifier: BSD-2-Clause

(define *requires-implementation-functions*
  '(parse-aux-source**))

(define-macro (%module-verbosity-flag%) 8)

;; A helper function, tidying up after parse-aux.y.
;;
;; The argument is a list of the contents of the aux file; the order doesn't matter,
;; This comprises a list (("command" "arg" ...) ...).
;; We ignore all of the commands apart from "citation", "bibstyle" and "bibdata".
;; We return a list of (list-of-strings list-of-strings string)
;; which are in order the list of citations, or 'all if we see \citation{*},
;; the list of .bib databases to consult,
;; and the style-file to use.
;; The databases and style-file elements are returned as #f if they're absent
(define (preen-auxparse-output auxfile-elements)
  (let loop ((aux auxfile-elements)
             (citations '())
             (databases '())
             (style-file #f))
    (if (null? aux)
        (list citations
              (if (null? databases) #f databases)
              style-file)
        (let ((cmd (caar aux))
              (arg (if (null? (cdar aux)) #f (cadar aux))))
          (cond ((string=? cmd "citation")
                 (if (string=? arg "*")
                     (loop (cdr aux)
                           'all
                           databases
                           style-file)
                     (loop (cdr aux)
                           (if (list? citations) ;citation is either '("key"...) or 'all
                               (append (map string->symbol (string-split arg #\,))
                                       citations)
                               citations)
                           databases
                           style-file)))
                ((string=? cmd "bibdata")
                 (loop (cdr aux)
                       citations
                       (append databases (string-split arg #\,))
                       style-file))
                ((string=? cmd "bibstyle")
                 (when style-file
                   ;; we are processing these in the reverse order to the order
                   ;; they appear in the .aux file, so if we override this here,
                   ;; we are ignoring 'later' ones.
                   (print-warning
                    "Duplicate style file command in aux; later ones ignored"))
                 (loop (cdr aux)
                       citations
                       databases
                       arg))
                (else
                 (loop (cdr aux) citations databases style-file)))))))

(define/provide (parse-aux-file fn)
  #"""`parse-aux-file : string? -> (list (listof symbol?) (listof string?) string?` :
  In `(parse-aux-file filename)`
  parse the aux file `filename`, and return relevant contents.

  We return a list `(list-of-symbols list-of-strings string)` which are
  in order the list of citations, or `'all` if we see `\citation{*}`;
  the list of .bib databases to consult; and the style-file to use.  The
  databases and style-file elements are returned as `#f` if they're
  absent.

  We recognise the following commands in the `.aux` file:

    * `\citation{foo}` : cite an object with BibTeX key `foo`.
    * `\citation{*}` : include _all_ of the entries in the database.
    * `\bibdata{bib}` : names the BibTeX database to consult; this command
      can appear more than once, the argument can be a single file or a list
      separated by commas, and the `.bib` extension is optional.
    * `\bibstyle{plain}` : the style file to be used.

  If the [kpathsea](https://tug.org/kpathsea/) library was available when
  `beastie` was built (it usually is) then it will be used to look up
  the values of the `\bibdata` and `\bibstyle` commands.

  If the `fn` is passed as `#f`, then parse stdin.

  Returns `#f` if the file can't be parsed as an aux-file."""
  (cond ((parse-aux-source** #t fn) => preen-auxparse-output)
        (else #f)))
(define/provide (parse-aux-string str)
  "Like parse-aux-file, mostly for debugging."
  (cond ((parse-aux-source** #f str) => preen-auxparse-output)
        (else #f)))

(define* (call-with-aux-file fn processor (parse-aux-file parse-aux-file))
  #"""`call-with-aux-file : string? ((listof symbol?) (listof string?) string? [:parse-aux-file fn] -> unspecified`:
  In `(call-with-aux-file fn processor)`, the aux-file `fn` is opened, parsed,
  and the contents processed with the function `processor`,
  called with the results from `parse-aux-file`.

  The given processor function is called with three arguments:

    * citations: a list of citation keys, as symbols, or the special symbol `'all`
    * bibdata: a list of paths to the .bib files to read from, as indicated by `\bibdata`
    * bibstyle: a style file indicated by the `\bibstyle` command

  If the `fn` is passed as `#f`, then parse stdin.

  If the aux file can't be opened, or it can't be parsed, then return `#f`.

  For a suitable processor, see the function `process-bibs/bst` in the `bst` module.

  If the keyword argument `:parse-aux-file` is present, then that
  function is used, instead of the default `parse-aux-file` function,
  to parse the file.  A suitable alternative function might be, for example,

      (define (parse-aux-file/nobst fn)
        (let ((result (parse-aux-file fn)))
          (list (car result) (cadr result) #f)))

  will have the same effect as `parse-aux-file`, but discarding the
  style-file found there."""
  (let ((aux (parse-aux-file fn)))
    (if (null? aux)
        #f
        (let ((citations (if (eqv? (car aux) 'all)
                             'all
                             (car aux)))
              (bibdata (and (cadr aux)
                            (map (lambda (f)
                                   (resolve-file f ".bib"))
                                 (cadr aux))))
              (bibstyle (and (caddr aux)
                             (resolve-file (caddr aux) ".bst"))))
          (processor citations bibdata bibstyle)))))
(module-provide call-with-aux-file)
