summary refs log tree commit diff
path: root/src/guile/skribilo/lib.scm
diff options
context:
space:
mode:
Diffstat (limited to 'src/guile/skribilo/lib.scm')
-rw-r--r--src/guile/skribilo/lib.scm137
1 files changed, 92 insertions, 45 deletions
diff --git a/src/guile/skribilo/lib.scm b/src/guile/skribilo/lib.scm
index feb5c8a..96bf483 100644
--- a/src/guile/skribilo/lib.scm
+++ b/src/guile/skribilo/lib.scm
@@ -1,6 +1,6 @@
 ;;; lib.scm -- Utilities.                 -*- coding: iso-8859-1 -*-
 ;;;
-;;; Copyright 2005, 2007, 2009  Ludovic Courtès <ludo@gnu.org>
+;;; Copyright 2005, 2007, 2009, 2012  Ludovic Courtès <ludo@gnu.org>
 ;;; Copyright 2003, 2004  Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
 ;;;
 ;;;
@@ -65,50 +65,97 @@
 ;;;
 ;;; DEFINE-MARKUP
 ;;;
-(define-macro (define-markup bindings . body)
-  ;; This is just an `(ice-9 optargs)' kind of `lambda*', with DSSSL
-  ;; keyword-style conversion enabled.  However, using `(ice-9 optargs)', the
-  ;; `:rest' argument can only appear last, which is not what Skribe/DSSSL
-  ;; expect, hence `fix-rest-arg'.  In addition, all keyword arguments are
-  ;; allowed (hence `:allow-other-keys'); they are then checked by `verify'.
-  (define (fix-rest-arg args)
-    (let loop ((args          args)
-	       (result        '())
-	       (rest-arg      '())
-               (has-keywords? #f))
-      (cond ((null? args)
-             (let ((result (if has-keywords?
-                               (cons :allow-other-keys result)
-                               result)))
-               (append! (reverse! result) rest-arg)))
-
-	    ((list? args)
-	     (let ((is-rest-arg? (eq? (car args) :rest))
-                   (is-keyword?  (eq? (car args) :key)))
-               (if is-rest-arg?
-                   (loop (cddr args)
-                         result
-                         (list (car args) (cadr args))
-                         (or has-keywords? is-keyword?))
-                   (loop (cdr args)
-                         (cons (car args) result)
-                         rest-arg
-                         (or has-keywords? is-keyword?)))))
-
-	    ((pair? args)
-	     (loop '()
-		   (cons (car args) result)
-		   (list #:rest (cdr args))
-                   has-keywords?)))))
-
-  (let ((name (car bindings))
-	(opts (cdr bindings)))
-    `(define*-public ,(cons name (fix-rest-arg opts))
-       ;; Memorize the invocation location.  Note: the invocation depth
-       ;; passed to `invocation-location' was determined experimentally and
-       ;; may change as Guile changes (XXX).
-       (let ((&invocation-location (invocation-location 3)))
-         ,@body))))
+
+(define (dsssl->guile-formals args)
+  ;; When using `(ice-9 optargs)', the `:rest' argument can only appear last,
+  ;; which is not what Skribe/DSSSL expect'.  In addition, all keyword
+  ;; arguments are allowed (hence `:allow-other-keys'); they are then checked
+  ;; by `verify'.  This procedure shuffles ARGS accordingly.
+
+  (let loop ((args          args)
+             (result        '())
+             (rest-arg      '())
+             (has-keywords? #f))
+    (cond ((null? args)
+           (let ((result (if has-keywords?
+                             (cons :allow-other-keys result)
+                             result)))
+             (append (reverse result) rest-arg)))
+
+          ((list? args)
+           (let ((is-rest-arg? (eq? (car args) :rest))
+                 (is-keyword?  (eq? (car args) :key)))
+             (if is-rest-arg?
+                 (loop (cddr args)
+                       result
+                       (list (car args) (cadr args))
+                       (or has-keywords? is-keyword?))
+                 (loop (cdr args)
+                       (cons (car args) result)
+                       rest-arg
+                       (or has-keywords? is-keyword?)))))
+
+          ((pair? args)
+           (loop '()
+                 (cons (car args) result)
+                 (list :rest (cdr args))
+                 has-keywords?)))))
+
+;; `define-markup' is similar to Guile's `lambda*', with DSSSL
+;; keyword style, and a couple other differences handled by
+;; `dsssl->guile-formals'.
+
+(cond-expand
+ (guile-2
+  ;; On Guile 2.0, `define-markup' generates a macro for the markup, such
+  ;; that the macro captures its invocation source location using
+  ;; `current-source-location'.
+
+  (define-syntax define-markup
+    (lambda (s)
+      (syntax-case s ()
+        ;; Note: Use a dotted pair for formals, to allow for dotted forms
+        ;; like: `(define-markup (foo x . rest) ...)'.
+        ((_ (name . formals) body ...)
+         (let ((formals  (map (lambda (s)
+                                (datum->syntax #'formals s))
+                              (dsssl->guile-formals (syntax->datum #'formals))))
+               (internal (symbol-append '% (syntax->datum #'name)
+                                        '-internal)))
+           (with-syntax ((internal/loc (datum->syntax #'name internal)))
+             #`(begin
+                 (define* (internal/loc loc #,@formals)
+                   (syntax-parameterize ((&invocation-location
+                                          (identifier-syntax loc)))
+                     body ...))
+                 (define-syntax name
+                   (lambda (s)
+                     (syntax-case s ()
+                       ((_ . args)
+                        #'(let ((loc (source-properties->location
+                                      (current-source-location))))
+                            (internal/loc loc . args)))
+                       (_
+                        #'(lambda args
+                            (let ((loc (source-properties->location
+                                        (current-source-location))))
+                              (apply internal/loc loc args)))))))
+                 internal/loc                     ; mark it as used
+                 (export name)))))))))
+
+ (else                                            ; Guile 1.8
+  ;; On Guile 1.8, a markup is a procedure.  Its invocation source location
+  ;; is captured by walking the stack, which is fragile.
+
+  (define-macro (define-markup bindings . body)
+    (let ((name (car bindings))
+          (opts (cdr bindings)))
+      `(define*-public ,(cons name (dsssl->guile-formals opts))
+         ;; Memorize the invocation location.  Note: the invocation depth
+         ;; passed to `invocation-location' was determined experimentally and
+         ;; may change as Guile changes (XXX).
+         (let ((&invocation-location (invocation-location 3)))
+           ,@body))))))
 
 
 ;;;