File: macros.lisp

package info (click to toggle)
cl-markdown 20101006-2.1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 556 kB
  • sloc: lisp: 5,863; makefile: 11
file content (92 lines) | stat: -rw-r--r-- 3,424 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
(in-package #:cl-markdown)

(defmacro defsimple-extension (name &body body)
  "Create an extension (a function named `name`) with no arguments that 
does not depend on the markdown phase and which does not use the result.
These are handy for simple text substitutions."
  (with-gensyms (phase arguments result)
  `(progn
     (pushnew (list ',name t) *extensions* :key #'car)
     (defun ,name (,phase ,arguments ,result)
       (declare (ignore ,phase ,arguments ,result))
       ,@body)
     ,@(%import/export-symbol name))))

(defun %validate-defextension-arguments (arguments)
  (loop for argument in (ensure-list arguments) do
       (cond ((atom argument)
	      (when (eq (symbol-package argument) #.(find-package :keyword))
		  (error "Argument names may not be keywords and ~s is not"
			 argument)))
	     (t 
	      (unless (every (lambda (facet)
			       (member facet '(:required :keyword :whole)))
			     (rest argument))
		(error "Invalid argument facets in ~s" (rest argument)))))))

(defun %collect-arguments (arguments kind)
  (loop for argument in (ensure-list arguments) 
     when (and (consp argument)
	       (member kind (rest argument))) collect
     (first argument)))

(defun %collect-positionals (arguments)
  (loop for argument in (ensure-list arguments) 
       when (or (atom argument)
		(and (consp argument)
		     (not (member :keyword (rest argument))))) collect
     (first (ensure-list argument))))

(defparameter *extensions* nil)

(defmacro defextension ((name &key arguments (insertp nil) (exportp t))
			&body body)
  (%validate-defextension-arguments arguments)
  (bind ((keywords (%collect-arguments arguments :keyword))
	 (requires (%collect-arguments arguments :required))
	 (whole  (%collect-arguments arguments :whole))
	 (positionals (%collect-positionals arguments)))
    (assert (<= (length whole) 1)
	    nil "At most one :whole argument is allowed.")
    (assert (null (intersection whole keywords))
	    nil "Keyword arguments cannot be wholes")
    `(progn
       (setf *extensions* (remove ',name *extensions* :key #'first))
       (push (list ',name ,insertp) *extensions*)
       (defun ,name (phase args result)
	 (declare (ignorable phase args result))
	 (bind (,@(loop for positional in positionals
		     unless (member positional whole) collect
		       `(,positional (pop args)))
		  ,@(loop for keyword in keywords collect
			 `(,keyword 
			   (getf args ,(intern (symbol-name keyword) :keyword)
				 nil)))
		  ,@(when whole
			  `((,(first whole)
			      ;; remove keywords from args
			      (progn
				,@(loop for keyword in keywords collect
				       `(,keyword 
					 (remf args
					       ,(intern (symbol-name keyword) :keyword))))
				(if (length-1-list-p args) (first args) args))))))
	   ,@(loop for require in requires collect
		  `(assert ,require nil ,(format nil "~s is required" require)))
	   ,@body
	   ,@(unless insertp nil)))
       ,@(when exportp
	       (%import/export-symbol name)))))

(defun %import/export-symbol (name)
  `((eval-when (:compile-toplevel :load-toplevel :execute)
      (import ',name ,(find-package :cl-markdown-user))
      (export ',name ,(find-package :cl-markdown-user)))))
 
(defmacro aand+ (&rest args)
  "Anaphoric nested AND.

Binds the symbol `it' to the value of the preceding `arg.'"
  (cond ((null args) t)
        ((null (cdr args)) (car args))
        (t `(aif ,(car args) (aand ,@(cdr args))))))