comparison lisp/w3/dsssl.el @ 14:9ee227acff29 r19-15b90

Import from CVS: tag r19-15b90
author cvs
date Mon, 13 Aug 2007 08:48:42 +0200
parents
children 0293115a14e9
comparison
equal deleted inserted replaced
13:13c6d0aaafe5 14:9ee227acff29
1 ;;; dsssl.el --- DSSSL parser
2 ;; Author: wmperry
3 ;; Created: 1996/12/18 21:10:58
4 ;; Version: 1.11
5 ;; Keywords:
6
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;; Copyright (c) 1996 by William M. Perry (wmperry@cs.indiana.edu)
9 ;;;
10 ;;; This file is part of GNU Emacs.
11 ;;;
12 ;;; GNU Emacs is free software; you can redistribute it and/or modify
13 ;;; it under the terms of the GNU General Public License as published by
14 ;;; the Free Software Foundation; either version 2, or (at your option)
15 ;;; any later version.
16 ;;;
17 ;;; GNU Emacs is distributed in the hope that it will be useful,
18 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;;; GNU General Public License for more details.
21 ;;;
22 ;;; You should have received a copy of the GNU General Public License
23 ;;; along with GNU Emacs; see the file COPYING. If not, write to the
24 ;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;;; Boston, MA 02111-1307, USA.
26 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
27
28 (require 'cl)
29
30 (if (not (fboundp 'cl-copy-hashtable))
31 (defun cl-copy-hashtable (h)
32 (let ((new (make-hash-table)))
33 (cl-maphash (function (lambda (k v) (cl-puthash k v new))) h)
34 new)))
35
36 ;; We need to have this up at the top to avoid compilation warnings in
37 ;; 'make' in dsssl-eval. Call me anal.
38 (defstruct flow-object
39 (name 'unknown :read-only t) ; Name of this flow object
40 (properties nil)
41 (children nil)
42 (parent nil)
43 )
44
45 (defconst dsssl-builtin-functions
46 '(not boolean\? case equal\? null\? list\? list length append
47 reverse list-tail list-ref member symbol\? keyword\? quantity\?
48 number\? real\? integer\? = < > <= >= + * - / max min abs quotient
49 modulo remainder floor ceiling truncate round number->string
50 string->number char\? char=\? char-property string\? string
51 string-length string-ref string=\? substring string-append
52 procedure\? apply external-procedure make time time->string quote
53 char-downcase indentity error let)
54 "A list of all the builtin DSSSL functions that we support.")
55
56 (defsubst dsssl-check-args (args expected)
57 ;; Signal an error if we don't have the expected # of arguments
58 (or (= (length args) expected)
59 (error "Wrong # arguments (expected %d): %d" expected (length args))))
60
61 (defsubst dsssl-min-args (args min)
62 (or (>= (length args) min)
63 (error "Wrong # arguments (expected at least %d): %d" min
64 (length args))))
65
66 (defun dsssl-call-function (func args)
67 (declare (special defines units))
68 (let ((old-defines nil)
69 (old-units nil)
70 (func-args (nth 1 func))
71 (real-func (nth 2 func))
72 (retval nil))
73 ;; Make sure we got the right # of arguments
74 (dsssl-check-args args (length func-args))
75
76 ;; make sure we evaluate all the arguments in the old environment
77 (setq args (mapcar 'dsssl-eval args))
78
79 ;; Save the old environment
80 (setq old-defines (cl-copy-hashtable defines)
81 old-units (cl-copy-hashtable units))
82
83 ;; Create the function's environment
84 (while func-args
85 (cl-puthash (car func-args) (car args) defines)
86 (setq func-args (cdr func-args)
87 args (cdr args)))
88
89 ;; Now evaluate the function body, returning the value of the last one
90 (while real-func
91 (setq retval (dsssl-eval (car real-func))
92 real-func (cdr real-func)))
93
94 ;; Restore the previous environment
95 (setq defines old-defines
96 units old-units)
97
98 ;; And we are out of here baby!
99 retval))
100
101 (defun dsssl-eval (form)
102 ;; We expect to have a 'defines' and 'units' hashtable floating around
103 ;; from higher up the call stack.
104 (declare (special defines units))
105 (cond
106 ((consp form) ; A function call
107 (let ((func (car form))
108 (args (cdr form)))
109 (case func
110 (cons
111 (dsssl-check-args args 2)
112 (cons (dsssl-eval (pop args)) (dsssl-eval (pop args))))
113 (cdr
114 (dsssl-check-args args 1)
115 (cdr (dsssl-eval (pop args))))
116 (car
117 (dsssl-check-args args 1)
118 (car (dsssl-eval (pop args))))
119 (not
120 (dsssl-check-args args 1)
121 (not (dsssl-eval (car args))))
122 (boolean\?
123 (dsssl-check-args args 1)
124 (and (symbolp (car args))
125 (memq (car args) '(\#f \#t))))
126 (if
127 (dsssl-min-args args 2)
128 (let ((val (dsssl-eval (pop args))))
129 (if val
130 (dsssl-eval (nth 0 args))
131 (if (nth 1 args)
132 (dsssl-eval (nth 1 args))))))
133 (let ; FIXME
134 )
135 (case
136 (dsssl-min-args args 2)
137 (let* ((val (dsssl-eval (pop args)))
138 (conditions args)
139 (done nil)
140 (possibles nil)
141 (cur nil))
142 (while (and conditions (not done))
143 (setq cur (pop conditions)
144 possibles (nth 0 cur))
145 (if (or (and (listp possibles)
146 (member val possibles))
147 (equal val possibles)
148 (memq possibles '(default otherwise)))
149 (setq done (dsssl-eval (nth 1 cur)))))
150 done))
151 (equal\?
152 (dsssl-check-args args 2)
153 (equal (dsssl-eval (car args)) (dsssl-eval (cadr args))))
154 (null\?
155 (dsssl-check-args args 1)
156 (null (dsssl-eval (car args))))
157 (list\?
158 (dsssl-check-args args 1)
159 (listp (dsssl-eval (car args))))
160 (list
161 (mapcar 'dsssl-eval args))
162 (length
163 (dsssl-check-args args 1)
164 (length (dsssl-eval (car args))))
165 (append
166 (apply 'append (mapcar 'dsssl-eval args)))
167 (reverse
168 (dsssl-check-args args 1)
169 (reverse (dsssl-eval (car args))))
170 (list-tail
171 (dsssl-check-args args 2)
172 (nthcdr (dsssl-eval (car args)) (dsssl-eval (cadr args))))
173 (list-ref
174 (dsssl-check-args args 2)
175 (nth (dsssl-eval (car args)) (dsssl-eval (cadr args))))
176 (member
177 (dsssl-check-args args 2)
178 (member (dsssl-eval (car args)) (dsssl-eval (cadr args))))
179 (symbol\?
180 (dsssl-check-args args 1)
181 (symbolp (dsssl-eval (car args))))
182 (keyword\?
183 (dsssl-check-args args 1)
184 (keywordp (dsssl-eval (car args))))
185 (quantity\?
186 (dsssl-check-args args 1)
187 (error "%s not implemented yet." func))
188 (number\?
189 (dsssl-check-args args 1)
190 (numberp (dsssl-eval (car args))))
191 (real\?
192 (dsssl-check-args args 1)
193 (let ((rval (dsssl-eval (car args))))
194 (and (numberp rval)
195 (/= (truncate rval) rval))))
196 (integer\?
197 (dsssl-check-args args 1)
198 (let ((rval (dsssl-eval (car args))))
199 (and (numberp rval)
200 (= (truncate rval) rval))))
201 ((= < > <= >=)
202 (dsssl-min-args args 2)
203 (let ((not-done t)
204 (initial (dsssl-eval (car args)))
205 (next nil))
206 (setq args (cdr args))
207 (while (and args not-done)
208 (setq next (dsssl-eval (car args))
209 args (cdr args)
210 not-done (funcall func initial next)
211 initial next))
212 not-done))
213 ((+ *)
214 (dsssl-min-args args 2)
215 (let ((acc (dsssl-eval (car args))))
216 (setq args (cdr args))
217 (while args
218 (setq acc (funcall func acc (dsssl-eval (car args)))
219 args (cdr args)))
220 acc))
221 (-
222 (dsssl-min-args args 1)
223 (apply func (mapcar 'dsssl-eval args)))
224 (/
225 (dsssl-min-args args 1)
226 (if (= (length args) 1)
227 (/ 1 (dsssl-eval (car args)))
228 (apply func (mapcar 'dsssl-eval args))))
229 ((max min)
230 (apply func (mapcar 'dsssl-eval args)))
231 (abs
232 (dsssl-check-args args 1)
233 (abs (dsssl-eval (car args))))
234 (quotient ; FIXME
235 (error "`%s' not implemented yet!" func))
236 (modulo
237 (dsssl-check-args args 2)
238 (mod (dsssl-eval (car args)) (dsssl-eval (cadr args))))
239 (remainder
240 (dsssl-check-args args 2)
241 (% (dsssl-eval (car args)) (dsssl-eval (cadr args))))
242 ((floor ceiling truncate round)
243 (dsssl-check-args args 1)
244 (funcall func (dsssl-eval (car args))))
245 (number->string
246 (dsssl-min-args args 1)
247 (if (= (length args) 1)
248 (number-to-string (dsssl-eval (car args)))
249 (if (= (length args) 2) ; They gave us a radix
250 (error "Radix arg not supported yet.")
251 (dsssl-check-args args 1))))
252 (string->number
253 (dsssl-min-args args 1)
254 (if (= (length args) 1)
255 (string-to-number (dsssl-eval (car args)))
256 (if (= (length args) 2) ; They gave us a radix
257 (error "Radix arg not supported yet.")
258 (dsssl-check-args args 1))))
259 (char\?
260 (dsssl-check-args args 1)
261 (characterp (dsssl-eval (car args))))
262 (char=\?
263 (dsssl-check-args args 2)
264 (char-equal (dsssl-eval (car args)) (dsssl-eval (cadr args))))
265 (char-downcase
266 (dsssl-check-args args 1)
267 (downcase (dsssl-eval (car args))))
268 (char-property ; FIXME
269 (error "`%s' not implemented yet!" func))
270 (string\?
271 (dsssl-check-args args 1)
272 (stringp (dsssl-eval (car args))))
273 (string
274 (dsssl-min-args args 1)
275 (mapconcat 'char-to-string (mapcar 'dsssl-eval args) ""))
276 (string-length
277 (dsssl-check-args args 1)
278 (length (dsssl-eval (car args))))
279 (string-ref
280 (dsssl-check-args args 2)
281 (aref (dsssl-eval (car args)) (dsssl-eval (cadr args))))
282 (string=\?
283 (dsssl-check-args args 2)
284 (string= (dsssl-eval (car args)) (dsssl-eval (cadr args))))
285 (substring
286 (substring (dsssl-eval (pop args))
287 (dsssl-eval (pop args))
288 (dsssl-eval (pop args))))
289 (string-append
290 (let ((rval ""))
291 (while args
292 (setq rval (concat rval (dsssl-eval (pop args)))))
293 rval))
294 (procedure\?
295 (dsssl-check-args args 1)
296 (let* ((sym (dsssl-eval (car args)))
297 (def (cl-gethash sym defines)))
298 (or (memq sym dsssl-builtin-functions)
299 (and def (listp def) (eq (car def) 'lambda)))))
300 (apply ; FIXME
301 )
302 (external-procedure ; FIXME
303 )
304 (make
305 (let* ((type (dsssl-eval (pop args)))
306 (symname nil)
307 (props nil)
308 (tail nil)
309 (children nil)
310 (temp nil)
311 )
312 ;; Massage :children into the last slot
313 (setq props (mapcar 'dsssl-eval args)
314 tail (last props)
315 children (car tail))
316 (if (consp tail)
317 (setcar tail nil))
318 (if (not (car props))
319 (setq props nil))
320 (setq temp (- (length props) 1))
321 ;; Not sure if we should really bother with this or not, but
322 ;; it does at least make it look more common-lispy keywordish
323 ;; and such. DSSSL keywords look like font-weight:, this makes
324 ;; it :font-weight
325 (while (>= temp 0)
326 (setq symname (symbol-name (nth temp props)))
327 (if (string-match "^\\(.*\\):$" symname)
328 (setf (nth temp props)
329 (intern (concat ":" (match-string 1 symname)))))
330 (setq temp (- temp 2)))
331
332 ;; Create the actual flow object
333 (make-flow-object :name type
334 :children children
335 :properties props)
336 )
337 )
338 (time
339 (mapconcat 'int-to-string (current-time) ":"))
340 (time->string
341 (dsssl-check-args args 1)
342 (current-time-string
343 (mapcar 'string-to-int
344 (split-string (dsssl-eval (car args)) ":"))))
345 (quote
346 (dsssl-check-args args 1)
347 (car args))
348 (identity
349 (dsssl-check-args args 1)
350 (dsssl-eval (car args)))
351 (error
352 (apply 'error (mapcar 'dsssl-eval args)))
353 (otherwise
354 ;; A non-built-in function - look it up
355 (let ((def (cl-gethash func defines)))
356 (if (and def (listp def) (eq (car def) 'lambda))
357 (dsssl-call-function def args)
358 (error "Symbol's function definition is void: %s" func))))
359 )
360 )
361 )
362 ((symbolp form) ; A variable
363 ;; A DSSSL keyword!
364 (if (string-match ":$" (symbol-name form))
365 form
366 (let ((val (cl-gethash form defines 'ThIS-Is_A_BOgUs-VariuhhBBLE)))
367 (if (not (eq val 'ThIS-Is_A_BOgUs-VariuhhBBLE))
368 val
369 ;; Ok, we got a bogus variable, but maybe it is really a UNIT
370 ;; dereference. Check.
371 (let ((name (symbol-name form))
372 (the-units nil)
373 (number nil)
374 (conversion nil))
375 (if (not (string-match "^\\([0-9.]+\\)\\([a-zA-Z]+\\)$" name))
376 (error "Symbol's value as variable is void: %s" form)
377 (setq number (string-to-int (match-string 1 name))
378 the-units (intern (downcase (match-string 2 name)))
379 conversion (cl-gethash the-units units))
380 (if (or (not conversion) (not (numberp conversion)))
381 (error "Symbol's value as variable is void: %s" form)
382 (* number conversion))))))))
383 (t
384 form)
385 )
386 )
387
388 (defsubst dsssl-predeclared ()
389 (declare (special defines units))
390 (cl-puthash '\#f nil defines)
391 (cl-puthash 'nil nil defines)
392 (cl-puthash '\#t t defines)
393 ;; NOTE: All units are stored internally as points.
394 (cl-puthash 'in (float 72) units)
395 (cl-puthash 'mm (float (* 72 25.4)) units)
396 (cl-puthash 'cm (float (* 72 2.54)) units)
397 )
398
399 (defun dsssl-parse (buf)
400 ;; Return the full representation of the DSSSL stylesheet as a series
401 ;; of LISP objects.
402 (let ((defines (make-hash-table :size 13))
403 (units (make-hash-table :size 13))
404 (buf-contents nil))
405 (dsssl-predeclared)
406 (save-excursion
407 (setq buf-contents (if (or (bufferp buf) (get-buffer buf))
408 (progn
409 (set-buffer buf)
410 (buffer-string))
411 buf))
412 (set-buffer (generate-new-buffer " *dsssl-style*"))
413 (insert buf-contents)
414 (goto-char (point-min))
415 (skip-chars-forward " \t\n\r")
416 (if (looking-at "<!") ; DOCTYPE present
417 (progn
418 ;; This should _DEFINITELY_ be smarter
419 (search-forward ">" nil t)
420 ))
421 (let ((result nil)
422 (temp nil)
423 (save-pos nil))
424 (while (not (eobp))
425 (condition-case ()
426 (setq save-pos (point)
427 temp (read (current-buffer)))
428 (invalid-read-syntax
429 ;; This disgusting hack is in here so that we can basically
430 ;; extend the lisp reader to gracefully deal with converting
431 ;; DSSSL #\A to Emacs-Lisp ?A notation. If you know of a
432 ;; better way, please feel free to send me some email.
433 (setq temp nil)
434 (backward-char 1)
435 (if (looking-at "#\\\\")
436 (replace-match "?")
437 (insert "\\"))
438 (goto-char save-pos))
439 (error nil))
440 (cond
441 ((null temp)
442 nil)
443 ((listp temp)
444 (case (car temp)
445 (define-unit
446 (cl-puthash (cadr temp) (dsssl-eval (caddr temp))
447 units))
448 (define
449 (if (listp (cadr temp))
450 ;; A function
451 (cl-puthash (caadr temp)
452 (list 'lambda
453 (cdadr temp)
454 (cddr temp)) defines)
455 ;; A normal define
456 (cl-puthash (cadr temp)
457 (dsssl-eval (caddr temp)) defines)))
458 (otherwise
459 (setq result (cons temp result)))))
460 (t
461 (setq result (cons temp result))))
462 (skip-chars-forward " \t\n\r"))
463 (kill-buffer (current-buffer))
464 (list defines units (nreverse result))))))
465
466 (defun dsssl-test (x)
467 (let* ((result (dsssl-parse x))
468 (defines (nth 0 result))
469 (units (nth 1 result))
470 (forms (nth 2 result)))
471 (mapcar 'dsssl-eval forms)))
472
473
474 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
475 ;;; The flow object classes.
476 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
477 (defmacro flow-object-property (obj prop &optional default)
478 "Return property PROP of the DSSSL flow object OBJ.
479 OBJ can be any flow object class, as long as it was properly derived
480 from the base `flow-object' class."
481 (` (plist-get (flow-object-properties (, obj)) (, prop) (, default))))
482
483 ;; Now for specific types of flow objects
484 ;; Still to do:
485 ;;; display-group
486 ;;; paragraph
487 ;;; sequence
488 ;;; line-field
489 ;;; paragraph-break
490 ;;; simple-page-sequence
491 ;;; score
492 ;;; table
493 ;;; table-row
494 ;;; table-cell
495 ;;; rule
496 ;;; external-graphic
497
498
499 (provide 'dsssl)