157 lines
6.1 KiB
Racket
157 lines
6.1 KiB
Racket
#lang at-exp s-exp "shared.rkt"
|
|
|
|
(require scheme/list scheme/string scheme/dict scheme/promise scheme/function)
|
|
|
|
;; bib values are hash tables mapping field names (symbols) to strings.
|
|
;; Keywords can also be used for the field names, which makes them meta-fields
|
|
;; that are not included in the usual bib printout. Two of them are required:
|
|
;; - #:type the type of the entry (a symbol: 'article, 'techreport, etc)
|
|
;; - #:key the label for the entry
|
|
|
|
(define bib-fields
|
|
'(author editor title booktitle journal
|
|
edition volume number series
|
|
chapter pages
|
|
type
|
|
school institution organization
|
|
publisher howpublished
|
|
address
|
|
month year
|
|
key
|
|
crossref
|
|
url
|
|
note
|
|
eprint))
|
|
|
|
(define meta-field? keyword?)
|
|
|
|
(define key->number
|
|
(let ([t (for/hash ([k bib-fields] [i (in-naturals)]) (values k i))])
|
|
(lambda (key)
|
|
(hash-ref t key (lambda ()
|
|
(error 'key->number "unknown field name: ~e" key))))))
|
|
|
|
;; converts the hash to an alist with the order specified by bib-fields
|
|
(define (bib->alist bib)
|
|
(sort (filter-not (compose meta-field? car) (hash-map bib cons))
|
|
< #:key (compose key->number car) #:cache-keys? #t))
|
|
|
|
(define (display* . xs)
|
|
(for-each display xs))
|
|
|
|
(define (display-attr attr)
|
|
(let* ([prefix (format " ~a = {" (car attr))]
|
|
[sep (delay (string-append "\n" (make-string (string-length prefix)
|
|
#\space)))])
|
|
(display* prefix
|
|
(if (regexp-match? #rx"\n" (cdr attr))
|
|
(regexp-replace* #rx"\n" (cdr attr) (force sep))
|
|
(cdr attr))
|
|
"}")))
|
|
|
|
(provide display-bib)
|
|
(define (display-bib bib)
|
|
(display* "@" (hash-ref bib '#:type) "{" (hash-ref bib '#:key))
|
|
(for ([attr (bib->alist bib)]) (display* ",\n") (display-attr attr))
|
|
(display* "\n}\n"))
|
|
|
|
(provide with-braces without-braces)
|
|
(define (with-braces str) (regexp-replace* #px"\\b\\w+[A-Z]\\w*" str "{\\0}"))
|
|
(define (without-braces str) (regexp-replace* #rx"[{}]+" str ""))
|
|
|
|
(provide bib-author)
|
|
(define (bib-author bib)
|
|
(let ([authors (regexp-split #rx"[ \t\n]+and[ \t\n]+"
|
|
(hash-ref bib 'author))])
|
|
(case (length authors)
|
|
[(0) "???"]
|
|
[(1) (car authors)]
|
|
[(2) (apply format "~a and ~a" authors)]
|
|
[(3) (apply format "~a, ~a, and ~a" authors)]
|
|
[else (format "~a et al" (car authors))])))
|
|
|
|
;; ----------------------------------------------------------------------------
|
|
|
|
;; processes the (key val ...) alist to a hash of (key . val) by combining the
|
|
;; possibly multiple values for each key (each value becomes a line)
|
|
(provide bib)
|
|
(define (bib type key attrs)
|
|
(define t (make-hasheq))
|
|
(hash-set! t '#:type type)
|
|
(hash-set! t '#:key key)
|
|
(for ([a attrs])
|
|
(define (err) (error 'make-bib "bad attribute: ~e" a))
|
|
(unless (and (pair? a) (pair? (cdr a)) (list? (cdr a))) (err))
|
|
(let ([key (car a)])
|
|
(unless (hash-ref t key #f) ; previous keys take precedence
|
|
(cond [(symbol? key)
|
|
;; turn non-strings to strings, join multiple strings, normalize
|
|
;; spaces
|
|
(let* ([val (cdr a)]
|
|
[val (map (lambda (x) (if (string? x) x (format "~a" x)))
|
|
val)]
|
|
[val (string-append* (add-between val "\n"))]
|
|
[val (regexp-replace* #rx"\t" val " ")]
|
|
[val (regexp-replace* #rx" +" val " ")]
|
|
[val (regexp-replace #rx"^ +" val "")]
|
|
[val (regexp-replace #rx" +$" val "")]
|
|
[val (regexp-replace* #rx"(?: *\r?\n *)+" val "\n")])
|
|
(hash-set! t key val))]
|
|
[(and (meta-field? key) (null? (cddr a)))
|
|
(hash-set! t key (cadr a))]
|
|
[else (err)]))))
|
|
t)
|
|
|
|
;; ----------------------------------------------------------------------------
|
|
|
|
#|
|
|
In the future, it might be good to do some field verification, etc. From the
|
|
bibtext thing:
|
|
article: An article from a journal or magazine.
|
|
req: author, title, journal, year
|
|
opt: volume, number, pages, month, note
|
|
book: A book with an explicit publisher.
|
|
req: author or editor, title, publisher, year
|
|
opt: volume or number, series, address, edition, month, note
|
|
booklet: A work that is printed and bound, but without a named publisher or
|
|
sponsoring institution.
|
|
req: title
|
|
opt: author, howpublished, address, month, year, note
|
|
conference: The same as `inproceedings', included for Scribe compatibility.
|
|
inbook: A part of a book, which may be a chapter (or section or whatever)
|
|
and/or a range of pages.
|
|
req: author or editor, title, chapter and/or pages, publisher, year
|
|
opt: volume or number, series, type, address, edition, month, note
|
|
incollection: A part of a book having its own title.
|
|
req: author, title, booktitle, publisher, year
|
|
opt: editor, volume or number, series, type, chapter, pages, address,
|
|
edition, month, note
|
|
inproceedings: An article in a conference proceedings.
|
|
req: author, title, booktitle, year
|
|
opt: editor, volume or number, series, pages, address, month, organization,
|
|
publisher, note
|
|
manual: Technical documentation.
|
|
req: title
|
|
opt: author, organization, address, edition, month, year, note
|
|
mastersthesis: A Master's thesis.
|
|
req: author, title, school, year
|
|
opt: type, address, month, note
|
|
misc: Use this type when nothing else fits.
|
|
opt: author, title, howpublished, month, year, note
|
|
phdthesis: A PhD thesis.
|
|
req: author, title, school, year
|
|
opt: type, address, month, note
|
|
proceedings: The proceedings of a conference.
|
|
req: title, year
|
|
opt: editor, volume or number, series, address, month, organization,
|
|
publisher, note
|
|
techreport: A report published by a school or other institution, usually
|
|
numbered within a series.
|
|
req: author, title, institution, year
|
|
opt: type, number, address, month, note
|
|
unpublished: A document having an author and title, but not formally
|
|
published.
|
|
req: author, title, note
|
|
opt: month, year
|
|
|#
|