1 ;;; dsssl.el --- DSSSL parser
3 ;; Created: 1998/12/18 02:19:24
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;; Copyright (c) 1996, 1997 by William M. Perry <wmperry@cs.indiana.edu>
9 ;;; Copyright (c) 1997 - 1999 by Free Software Foundation, Inc.
11 ;;; This file is part of GNU Emacs.
13 ;;; GNU Emacs is free software; you can redistribute it and/or modify
14 ;;; it under the terms of the GNU General Public License as published by
15 ;;; the Free Software Foundation; either version 2, or (at your option)
16 ;;; any later version.
18 ;;; GNU Emacs is distributed in the hope that it will be useful,
19 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;;; GNU General Public License for more details.
23 ;;; You should have received a copy of the GNU General Public License
24 ;;; along with GNU Emacs; see the file COPYING. If not, write to the
25 ;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 ;;; Boston, MA 02111-1307, USA.
27 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
32 (defconst dsssl-builtin-functions
33 '(not boolean\? case equal\? null\? list\? list length append
34 reverse list-tail list-ref member symbol\? keyword\? quantity\?
35 number\? real\? integer\? = < > <= >= + * - / max min abs quotient
36 modulo remainder floor ceiling truncate round number->string
37 string->number char\? char=\? char-property string\? string
38 string-length string-ref string=\? substring string-append
39 procedure\? apply external-procedure make time time->string quote
40 char-downcase identity error let)
41 "A list of all the builtin DSSSL functions that we support.")
43 (defsubst dsssl-check-args (args expected)
44 ;; Signal an error if we don't have the expected # of arguments
45 (or (= (length args) expected)
46 (error "Wrong # arguments (expected %d): %d" expected (length args))))
48 (defsubst dsssl-min-args (args min)
49 (or (>= (length args) min)
50 (error "Wrong # arguments (expected at least %d): %d" min
53 (defun dsssl-call-function (func args)
54 (declare (special defines units))
55 (let ((old-defines nil)
57 (func-args (nth 1 func))
58 (real-func (nth 2 func))
60 ;; Make sure we got the right # of arguments
61 (dsssl-check-args args (length func-args))
63 ;; make sure we evaluate all the arguments in the old environment
64 (setq args (mapcar 'dsssl-eval args))
66 ;; Save the old environment
67 (setq old-defines (copy-hash-table defines)
68 old-units (copy-hash-table units))
70 ;; Create the function's environment
72 (puthash (car func-args) (car args) defines)
73 (setq func-args (cdr func-args)
76 ;; Now evaluate the function body, returning the value of the last one
78 (setq retval (dsssl-eval (car real-func))
79 real-func (cdr real-func)))
81 ;; Restore the previous environment
82 (setq defines old-defines
85 ;; And we are out of here baby!
88 (defun dsssl-eval (form)
89 ;; We expect to have a 'defines' and 'units' hashtable floating around
90 ;; from higher up the call stack.
91 (declare (special defines units))
93 ((consp form) ; A function call
94 (let ((func (car form))
98 (dsssl-check-args args 2)
99 (cons (dsssl-eval (pop args)) (dsssl-eval (pop args))))
101 (dsssl-check-args args 1)
102 (cdr (dsssl-eval (pop args))))
104 (dsssl-check-args args 1)
105 (car (dsssl-eval (pop args))))
107 (dsssl-check-args args 1)
108 (not (dsssl-eval (car args))))
110 (dsssl-check-args args 1)
111 (and (symbolp (car args))
112 (memq (car args) '(\#f \#t))))
114 (dsssl-min-args args 2)
115 (let ((val (dsssl-eval (pop args))))
117 (dsssl-eval (nth 0 args))
119 (dsssl-eval (nth 1 args))))))
123 (dsssl-min-args args 2)
124 (let* ((val (dsssl-eval (pop args)))
129 (while (and conditions (not done))
130 (setq cur (pop conditions)
131 possibles (nth 0 cur))
132 (if (or (and (listp possibles)
133 (member val possibles))
134 (equal val possibles)
135 (memq possibles '(default otherwise)))
136 (setq done (dsssl-eval (nth 1 cur)))))
139 (dsssl-check-args args 2)
140 (equal (dsssl-eval (car args)) (dsssl-eval (cadr args))))
142 (dsssl-check-args args 1)
143 (null (dsssl-eval (car args))))
145 (dsssl-check-args args 1)
146 (listp (dsssl-eval (car args))))
148 (mapcar 'dsssl-eval args))
150 (dsssl-check-args args 1)
151 (length (dsssl-eval (car args))))
153 (apply 'append (mapcar 'dsssl-eval args)))
155 (dsssl-check-args args 1)
156 (reverse (dsssl-eval (car args))))
158 (dsssl-check-args args 2)
159 (nthcdr (dsssl-eval (car args)) (dsssl-eval (cadr args))))
161 (dsssl-check-args args 2)
162 (nth (dsssl-eval (car args)) (dsssl-eval (cadr args))))
164 (dsssl-check-args args 2)
165 (member (dsssl-eval (car args)) (dsssl-eval (cadr args))))
167 (dsssl-check-args args 1)
168 (symbolp (dsssl-eval (car args))))
170 (dsssl-check-args args 1)
171 (keywordp (dsssl-eval (car args))))
173 (dsssl-check-args args 1)
174 (error "%s not implemented yet." func))
176 (dsssl-check-args args 1)
177 (numberp (dsssl-eval (car args))))
179 (dsssl-check-args args 1)
180 (let ((rval (dsssl-eval (car args))))
182 (/= (truncate rval) rval))))
184 (dsssl-check-args args 1)
185 (let ((rval (dsssl-eval (car args))))
187 (= (truncate rval) rval))))
189 (dsssl-min-args args 2)
191 (initial (dsssl-eval (car args)))
193 (setq args (cdr args))
194 (while (and args not-done)
195 (setq next (dsssl-eval (car args))
197 not-done (funcall func initial next)
201 (dsssl-min-args args 2)
202 (let ((acc (dsssl-eval (car args))))
203 (setq args (cdr args))
205 (setq acc (funcall func acc (dsssl-eval (car args)))
209 (dsssl-min-args args 1)
210 (apply func (mapcar 'dsssl-eval args)))
212 (dsssl-min-args args 1)
213 (if (= (length args) 1)
214 (/ 1 (dsssl-eval (car args)))
215 (apply func (mapcar 'dsssl-eval args))))
217 (apply func (mapcar 'dsssl-eval args)))
219 (dsssl-check-args args 1)
220 (abs (dsssl-eval (car args))))
222 (error "`%s' not implemented yet!" func))
224 (dsssl-check-args args 2)
225 (mod (dsssl-eval (car args)) (dsssl-eval (cadr args))))
227 (dsssl-check-args args 2)
228 (% (dsssl-eval (car args)) (dsssl-eval (cadr args))))
229 ((floor ceiling truncate round)
230 (dsssl-check-args args 1)
231 (funcall func (dsssl-eval (car args))))
233 (dsssl-min-args args 1)
234 (if (= (length args) 1)
235 (number-to-string (dsssl-eval (car args)))
236 (if (= (length args) 2) ; They gave us a radix
237 (error "Radix arg not supported yet.")
238 (dsssl-check-args args 1))))
240 (dsssl-min-args args 1)
241 (if (= (length args) 1)
242 (string-to-number (dsssl-eval (car args)))
243 (if (= (length args) 2) ; They gave us a radix
244 (error "Radix arg not supported yet.")
245 (dsssl-check-args args 1))))
247 (dsssl-check-args args 1)
248 (characterp (dsssl-eval (car args))))
250 (dsssl-check-args args 2)
251 (char-equal (dsssl-eval (car args)) (dsssl-eval (cadr args))))
253 (dsssl-check-args args 1)
254 (downcase (dsssl-eval (car args))))
255 (char-property ; FIXME
256 (error "`%s' not implemented yet!" func))
258 (dsssl-check-args args 1)
259 (stringp (dsssl-eval (car args))))
261 (dsssl-min-args args 1)
262 (mapconcat 'char-to-string (mapcar 'dsssl-eval args) ""))
264 (dsssl-check-args args 1)
265 (length (dsssl-eval (car args))))
267 (dsssl-check-args args 2)
268 (aref (dsssl-eval (car args)) (dsssl-eval (cadr args))))
270 (dsssl-check-args args 2)
271 (string= (dsssl-eval (car args)) (dsssl-eval (cadr args))))
273 (substring (dsssl-eval (pop args))
274 (dsssl-eval (pop args))
275 (dsssl-eval (pop args))))
279 (setq rval (concat rval (dsssl-eval (pop args)))))
282 (dsssl-check-args args 1)
283 (let* ((sym (dsssl-eval (car args)))
284 (def (gethash sym defines)))
285 (or (memq sym dsssl-builtin-functions)
286 (and def (listp def) (eq (car def) 'lambda)))))
289 (external-procedure ; FIXME
292 (let* ((type (dsssl-eval (pop args)))
299 ;; Massage :children into the last slot
300 (setq props (mapcar 'dsssl-eval args)
305 (if (not (car props))
307 (setq temp (- (length props) 1))
308 ;; Not sure if we should really bother with this or not, but
309 ;; it does at least make it look more common-lispy keywordish
310 ;; and such. DSSSL keywords look like font-weight:, this makes
313 (setq symname (symbol-name (nth temp props)))
314 (if (string-match "^\\(.*\\):$" symname)
315 (setf (nth temp props)
316 (intern (concat ":" (match-string 1 symname)))))
317 (setq temp (- temp 2)))
319 ;; Create the actual flow object
320 (make-flow-object :type type
326 (mapconcat 'int-to-string (current-time) ":"))
328 (dsssl-check-args args 1)
330 (mapcar 'string-to-int
331 (split-string (dsssl-eval (car args)) ":"))))
333 (dsssl-check-args args 1)
336 (dsssl-check-args args 1)
337 (dsssl-eval (car args)))
339 (apply 'error (mapcar 'dsssl-eval args)))
341 ;; A non-built-in function - look it up
342 (let ((def (gethash func defines)))
343 (if (and def (listp def) (eq (car def) 'lambda))
344 (dsssl-call-function def args)
345 (error "Symbol's function definition is void: %s" func))))
349 ((symbolp form) ; A variable
351 (if (string-match ":$" (symbol-name form))
353 (let ((val (gethash form defines 'ThIS-Is_A_BOgUs-VariuhhBBLE)))
354 (if (not (eq val 'ThIS-Is_A_BOgUs-VariuhhBBLE))
356 ;; Ok, we got a bogus variable, but maybe it is really a UNIT
357 ;; dereference. Check.
358 (let ((name (symbol-name form))
362 (if (not (string-match "^\\([0-9.]+\\)\\([a-zA-Z]+\\)$" name))
363 (error "Symbol's value as variable is void: %s" form)
364 (setq number (string-to-int (match-string 1 name))
365 the-units (intern (downcase (match-string 2 name)))
366 conversion (gethash the-units units))
367 (if (or (not conversion) (not (numberp conversion)))
368 (error "Symbol's value as variable is void: %s" form)
369 (* number conversion))))))))
375 (defsubst dsssl-predeclared ()
376 (declare (special defines units))
377 (puthash '\#f nil defines)
378 (puthash 'nil nil defines)
379 (puthash '\#t t defines)
380 ;; NOTE: All units are stored internally as points.
381 (puthash 'in (float 72) units)
382 (puthash 'mm (float (* 72 25.4)) units)
383 (puthash 'cm (float (* 72 2.54)) units)
386 (defun dsssl-parse (buf)
387 ;; Return the full representation of the DSSSL stylesheet as a series
389 (let ((defines (make-hash-table :size 13))
390 (units (make-hash-table :size 13))
394 (setq buf-contents (if (or (bufferp buf) (get-buffer buf))
399 (set-buffer (generate-new-buffer " *dsssl-style*"))
400 (insert buf-contents)
401 (goto-char (point-min))
402 (skip-chars-forward " \t\n\r")
403 (if (looking-at "<!") ; DOCTYPE present
405 ;; This should _DEFINITELY_ be smarter
406 (search-forward ">" nil t)
413 (setq save-pos (point)
414 temp (read (current-buffer)))
416 ;; This disgusting hack is in here so that we can basically
417 ;; extend the lisp reader to gracefully deal with converting
418 ;; DSSSL #\A to Emacs-Lisp ?A notation. If you know of a
419 ;; better way, please feel free to send me some email.
422 (if (looking-at "#\\\\")
425 (goto-char save-pos))
433 (puthash (cadr temp) (dsssl-eval (caddr temp))
436 (if (listp (cadr temp))
438 (puthash (caadr temp)
441 (cddr temp)) defines)
444 (dsssl-eval (caddr temp)) defines)))
446 (setq result (cons temp result)))))
448 (setq result (cons temp result))))
449 (skip-chars-forward " \t\n\r"))
450 (kill-buffer (current-buffer))
451 (list defines units (nreverse result))))))
453 (defun dsssl-test (x)
454 (let* ((result (dsssl-parse x))
455 (defines (nth 0 result))
456 (units (nth 1 result))
457 (forms (nth 2 result)))
458 (declare (special defines units))
459 (mapcar 'dsssl-eval forms)))
462 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
463 ;;; The flow object classes.
464 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
465 (defmacro flow-object-property (obj prop &optional default)
466 "Return property PROP of the DSSSL flow object OBJ.
467 OBJ can be any flow object class, as long as it was properly derived
468 from the base `flow-object' class."
469 `(plist-get (flow-object-properties ,obj) ,prop ,default))
471 ;; Now for specific types of flow objects
478 ;;; simple-page-sequence