forked from jkitchin/scimax
-
Notifications
You must be signed in to change notification settings - Fork 0
/
scimax-macros.el
99 lines (94 loc) · 3.23 KB
/
scimax-macros.el
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
93
94
95
96
97
98
99
(defmacro defn (fname args &optional docstring &rest body)
"Macro to define a function with improved argument documentation.
FNAME is a symbol for the function name.
ARGS is a list of arguments. Each argument can be either a symbol
or a list of the form (arg-symbol arg-docstring options) where
options is a plist style of options that include:
:default value
:validate function (the function takes one argument, and should
return t if the argument is valid.
:rest (this indicates the rest of the arguments go into this
variable, and it has to be last)
The function docstring is built up from that information.
Default values will automatically be set in the body, and
validation code will be automatically generated if the option is
present.
DOCSTRING is an optional string for the overall purpose of the
function. The argument docstrings will be appended onto this.
BODY is a form for the function."
(if (not (stringp docstring))
(setq body docstring
docstring "No documentation provided."))
(let* (_ds
arg-options
;; build up the docstring.
(ds (concat
(or docstring "No docstring defined.")
"\n"
(mapconcat
'identity
(loop for arg in args
collect
(cond
((listp arg)
(setq arg-options (if (stringp (nth 1 arg))
(cddr arg)
(cdr arg)))
(format "%s : %s%s%s"
(upcase (symbol-name (car arg)))
(if (stringp (nth 1 arg)) (nth 1 arg) "No documentation")
(if (plist-get arg-options :default)
(format " (default = %s)"
(plist-get arg-options :default))
"")
(if (plist-get arg-options :validate)
(format " (valid = %s)"
(plist-get arg-options :validate))
"")))
;; this is a standalone symbol
(t
(format "%s : No documentation"
(upcase (symbol-name arg))))))
"\n")
"\n"))
;; These are the args to go in the function definition
(newargs (loop for arg in args
append
(cond
((listp arg)
(cond
((plist-get (cddr arg) :default)
`(&optional ,(car arg)))
((member :rest arg)
`(&rest ,(car arg)))
(t
(list (car arg)))))
(t
(list arg)))))
;; This is the code to set default values
(defaults (delq nil
(loop for arg in args
collect
(when (and (listp arg) (plist-get (cddr arg) :default))
`(when (null ,(car arg))
(setq ,(car arg) ,(plist-get (cddr arg) :default)))))))
;; This is the code to validate arguments
(validate (delq nil
(loop for i from 0 for arg in args
collect
(when (and (listp arg) (plist-get
(delq :rest (cddr arg)) :validate))
`(unless (funcall ',(plist-get
(delq :rest (cddr arg))
:validate)
,(car arg))
(error "In (%s %s) Expected %s to pass %S. Got %S"
,(symbol-name fname) ,(format "%s" newargs)
,(symbol-name (car arg))
',(plist-get (delq :rest (cddr arg)) :validate)
,(car arg)))))))
(f `(defun ,fname (,@newargs)
,(or ds "No docstring defined ;(."))))
(when defaults (setq f (append f `((progn ,@defaults)))))
(when validate (setq f (append f `((progn ,@validate)))))
(setq f (append f `,@body))))