[From Matthias Clasen (Institut für Mathematik, Albert-Ludwigs-Universitaet Freiburg). Created as a DSSSL-learning project. The package contains a DTD, DSSSL stylesheets, SGML source, etc. Available via FTP: ftp://peano.mathematik.uni-freiburg.de/pub/tutorial.tgz]
To learn more about DSSSL, I posed myself the following problem:
I wanted to achieve roughly the functionality of LaTeX + Bibtex. This includes:
You can find all mentioned files, including this document, in the gezipped tar file bib.tgz.
This is only a vastly simplified model. Bibtex knows 13 different types of references having many more fields than our examples book and article.
<!element doc1 - - ( bibliography, p* ) > <!element doc2 - - ( p*, references? ) > <!element p - o ( #PCDATA ) +( bibref, bibnoref ) > <!element ( bibliography, references ) - - ( article | book )* > <!element ( article, book ) - - ( author & title ) > <!attlist ( article, book ) id ID #REQUIRED > <!element ( author, title ) - - ( #PCDATA ) > <!element bibref - - EMPTY > <!attlist bibref cite IDREF #REQUIRED nocite (nocite) #IMPLIED > |
As you can see, this contains two document types: users will normally
create <doc1> documents with no explicit list of
references an the bibliographical data in the
<bibliography> element. A first
transformation step will convert this into a
<doc2> document containing an explicit list of
references in the <references> element. Users could
also create a <doc2> document manually (this was
one of the goals).
The intermediate document will then be subject to a formatting process.
A typical document as entered by a user thus looks like:
<!doctype doc1 system "bib.dtd" [ <!entity bibdata system "bibdata.sgm" > ]> <doc1> <bibliography>&bibdata;</bibliography> <p>This is just some example text. There are probably better examples in <bibref cite="handbook">. <bibref nocite cite="boo"> <bibref nocite cite="bar"> <bibref nocite cite="baz"> <bibref nocite cite="foo"> <p>Now let's cite the DSSSL standard <bibref cite="dsssl">! And never forget to have the handbook <bibref cite="handbook"> under your pillow. <p>And now I even cite a faked article <bibref cite="myarticle"> of my own! </doc1> |
<article id="myarticle"> <author>Matthias Clasen</author> <title>Wish I had one</title> </article> <book id="dsssl"> <title>ISO/IEC 10179:1996</title> <author>Joint Technical Commitiee ISO/IEC JTC1, Information technology</author> </book> <book id="handbook"> <author>Charles F. Goldfarb</author> <title>The SGML Handbook</title> </book> <article id="boo"> <author>D</author> <title>Article by D</title> </article> <article id="foo"> <author>A</author> <title>Article by A</title> </article> <article id="baz"> <author>C</author> <title>Article by C</title> </article> <article id="bar"> <author>B</author> <title>Article by B</title> </article> <article id="uncited1"> <author>E</author> <title>Article by E</title> </article> <article id="uncited2"> <author>F</author> <title>Article by F</title> </article> |
This example shows how the bibliographical information can be kept
separately from the document instance. The approach taken here might
become problematic with very large databases, since the
ID's come all from the same name space. Using subdoc
entities instead of text entities would avoid that problem but at the
cost of abandoning the simple ID-IDREF
mechanism completely.
In the absence of an implementation of the transformation language of DSSSL, I had to use the nonstandard flow object classes of Jade to do the transformation with the style language. I intend to translate this to the transformation language when implementations become available.
The missing-standard-procedures entity contains some
functions which are part of the style langugage, but are not yet
implemented in Jade: map, node-list-reduce,
node-list-contains?,
node-list-remove-duplicates?,
node-list-filter and node-list->list. I have
simply copied the sample implementations from the standard.
The copy-attributes procedure is from James Clark,
the identity transformation in the default construction rule is from
W. Eliot Kimbers dovalueref.dsl and the reference sorting is lifted
from the index sorting functions in Mark Burtons dbtohtml.dsl.
It would be nice to make the sorting function customizable, e.g. by
introducing a class or role attribute on the
<bibref> elements and create multiple lists of
references according to that.
Another nice thing to implement would be an index of the citations
<!DOCTYPE style-sheet PUBLIC "-//James Clark//DTD DSSSL Style Sheet//EN" [
<!ENTITY missing-standard-procedures system "std-proc.dsl">
]>
<style-specification>
(declare-flow-object-class element
"UNREGISTERED::James Clark//Flow Object Class::element")
(declare-flow-object-class empty-element
"UNREGISTERED::James Clark//Flow Object Class::empty-element")
(declare-flow-object-class document-type
"UNREGISTERED::James Clark//Flow Object Class::document-type")
(declare-flow-object-class entity
"UNREGISTERED::James Clark//Flow Object Class::entity")
&missing-standard-procedures;
; Suspiciously missing counterpart of node-list->list.
(define (list->node-list l)
(apply node-list l))
(define (copy-attributes #!optional (nd (current-node)))
(let loop ((atts (named-node-list-names (attributes nd))))
(if (null? atts)
'()
(let* ((name (car atts))
(value (attribute-string name nd)))
(if value
(cons (list name value)
(loop (cdr atts)))
(loop (cdr atts)))))))
(define (string-ci>? s1 s2)
(let ((len1 (string-length s1))
(len2 (string-length s2)))
(let loop ((i 0))
(cond ((= i len1) #f)
((= i len2) #t)
(#t (let ((c1 (index-char-val (string-ref s1 i)))
(c2 (index-char-val (string-ref s2 i))))
(cond
((= c1 c2) (loop (+ i 1)))
(#t (> c1 c2)))))))))
(define (index-char-val ch)
(case ch
((#\A #\a) 65)
((#\B #\b) 66)
((#\C #\c) 67)
((#\D #\d) 68)
((#\E #\e) 69)
((#\F #\f) 70)
((#\G #\g) 71)
((#\H #\h) 72)
((#\I #\i) 73)
((#\J #\j) 74)
((#\K #\k) 75)
((#\L #\l) 76)
((#\M #\m) 77)
((#\N #\n) 78)
((#\O #\o) 79)
((#\P #\p) 80)
((#\Q #\q) 81)
((#\R #\r) 82)
((#\S #\s) 83)
((#\T #\t) 84)
((#\U #\u) 85)
((#\V #\v) 86)
((#\W #\w) 87)
((#\X #\x) 88)
((#\Y #\y) 89)
((#\Z #\z) 90)
((#\ ) 32)
((#\0) 48)
((#\1) 49)
((#\2) 50)
((#\3) 51)
((#\4) 52)
((#\5) 53)
((#\6) 54)
((#\7) 55)
((#\8) 56)
((#\9) 57)
(else 0)))
; Sort an alist. Each member of al should be a pair whose car
; must be a string which is used as sort key.
(define (sort-alist al)
(letrec ((list-head (lambda (l n)
(if (> n 0)
(cons (car l) (list-head (cdr l) (- n 1)))
'())))
(merge (lambda (al1 al2)
(cond ((null? al1) al2)
((null? al2) al1)
((string-ci>? (car (car al2)) (car (car al1)))
(cons (car al2) (merge al1 (cdr al2))))
(#t (cons (car al1) (merge (cdr al1) al2)))))))
(let* ((ll (length al))
(ldiv2 (quotient ll 2)))
(if (> 2 ll)
al
(merge (sort-alist (list-head al ldiv2))
(sort-alist (list-tail al ldiv2)))))))
; Turn a node list into an alist with the help of proc which must
; be a function turning a singleton node list into a pair.
(define (node-list->alist proc nl)
(map proc (node-list->list nl)))
; Turn an alist into a node list.
(define (alist->node-list al)
(list->node-list (map cdr al)))
; Return a node list containing all nodes whose id's occur as the value
; of a bibref element in the subtree below the current node. The node
; list is sorted alphabetically by author.
(define (references)
(alist->node-list
(sort-alist
(node-list->alist
(lambda (snl) (cons (data (select-elements (descendants snl) "AUTHOR")) snl))
(node-list-remove-duplicates
(node-list-map
(lambda (snl) (element-with-id (attribute-string "CITE" snl)))
(select-elements (descendants (current-node)) "BIBREF")))))))
; parameters
(define instance-sysid "bibdoc.sgm2")
(define dtd-sysid "bib.dtd")
; construction rules
(root
(case (gi (node-property 'docelem (current-node)))
(("DOC1") (make entity
system-id: instance-sysid
(make document-type
name: "DOC2"
system-id: dtd-sysid)
(process-children)))
(("DOC2") (error "I can only work on documents of type DOC1."))))
(default
(cond
((node-list-empty? (node-property 'content (current-node)))
(make empty-element
attributes: (copy-attributes)))
(else
(make element
attributes: (copy-attributes)))))
(element BIBLIOGRAPHY
(empty-sosofo))
(element DOC1
(make element
gi: "DOC2"
attributes: (copy-attributes)
(process-children)
(make element
gi: "REFERENCES"
(process-node-list (references)))))
</style-specification>
|
You should take a look at the result of running Jade's SGML backend on the example document with this style sheet.
The transformation process has already done the
most for us, the only remaining task is to create the keys for the
references. This is done in the procedure bibkey. There
are three parameters involved:
bibkey-open the character used for the opening bracketbibkey-close the character used for the closing bracketbibkey-style"full-id", "short-id" and
"numeric". It would certainly be nice to have styles like
"author-year" and so on.
<!DOCTYPE style-sheet PUBLIC "-//James Clark//DTD DSSSL Style Sheet//EN" [
<!ENTITY missing-standard-procedures system "std-proc.dsl">
]>
<style-specification>
; parameters
(define bibkey-style "numeric")
(define bibkey-open "[")
(define bibkey-close "]")
(define (bibkey snl)
(string-append
bibkey-open
(case bibkey-style
(("full-id") (attribute-string "ID" snl))
(("short-id") (substring (attribute-string "ID" snl) 0 3))
(("numeric") (number->string (+ 1 (node-list-length (preced snl))))))
bibkey-close))
; construction rules
(root
(case (gi (node-property 'docelem (current-node)))
(("DOC1") (error "I can only work on documents of type DOC2."))
(("DOC2") (process-children))))
(element DOC2
(make simple-page-sequence
left-margin: 1in
right-margin: 1in
top-margin: 1in
bottom-margin: 1in
(process-children)))
(element P
(make paragraph
(process-children)))
(element BIBREF
(if (attribute-string "NOCITE") (empty-sosofo)
(make sequence
(literal (bibkey (element-with-id (attribute-string "CITE")))))))
(element REFERENCES
(make sequence
(make paragraph
font-size: 12pt
font-weight: 'bold
space-before: 0.5cm
space-after: 0.2cm
(literal "References"))
(process-children)))
(element BOOK
(make paragraph
(literal (bibkey (current-node)))
(literal " ")
(process-matching-children 'author)
(literal ", ")
(make sequence
font-posture: 'italic
(process-matching-children 'title))))
(element ARTICLE
(make paragraph
(literal (bibkey (current-node)))
(literal " ")
(process-matching-children 'author)
(literal ", ")
(make sequence
font-posture: 'upright
(process-matching-children 'title))))
</style-specification>
|
You can have a look at the result of applying this style sheet to the result of the first pass on the example document. It is a dvi file created by Jade's TeX backend and Sebastian Rahtz's Jadetex macros.
I would appreciate comments on the DSSSL code.
Matthias Clasen mclasen@sun2.mathematik.uni-freiburg.de