-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
options.lisp
84 lines (74 loc) · 3.46 KB
/
options.lisp
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
;; Copyright (c) 2022, "the Phoeron" Colin J.E. Lupton <[email protected]>
;; Released under the MIT License. See baphomet/LICENSE for more information.
(in-package :baphomet)
;; DEFINER OPTIONS
(defun ensure-boolean-option (definer keyword value)
(unless (typep value 'boolean)
(error "Expecting a boolean instead of ~s for ~s in definer ~s of type ~s."
value keyword (name-of definer) (definer-type definer)))
value)
(defun ensure-string-option (definer keyword value)
(unless (stringp value)
(error "Expecting a string instead of ~s for ~s in definer ~s of type ~s."
value keyword (name-of definer) (definer-type definer)))
value)
(defun ensure-function-option (definer keyword value)
(declare (ignore definer keyword))
(if (and (consp value) (member (car value) '(function lambda)))
value
`(fdefinition ,value)))
(defun oerror (fmt options definer &rest rest)
(apply #'error fmt options (name-of definer)
(definer-type definer) rest))
(defun validate-definer-options (definer &optional extra-options-writer)
"Validates definer options of function like definers."
(destructuring-bind (options extra-options)
;; Extract definer options.
(let* ((options (options-of definer))
(probable-options (coerce (symbol-name (car options)) 'list))
(available-options (available-definer-options definer)))
(if (set-difference
probable-options available-options :test #'char-equal)
(list nil options)
(list probable-options (rest options))))
;; Check extra options.
(when (and extra-options (not extra-options-writer))
(oerror "Invalid definer options ~s in definer ~s of type ~s."
extra-options definer))
;; Check restricted options.
(dolist (restricted-combination (restricted-definer-options definer))
(when (every (lambda (option) (member option options :test #'char-equal))
restricted-combination)
(oerror "Ambiguous definer options ~s in definer ~s of type ~s. ~
(Cannot use ~s definer options at the same.)"
options definer restricted-combination)))
;; Update validated slot values.
(setf (options-of definer) options)
(when extra-options-writer
(funcall extra-options-writer definer extra-options))))
(defun combine-option-writers (option-writers)
(lambda (definer options)
;; Apply each OPTION-WRITER-FUNCTION, if found appropriate keyword in the
;; options of related definer.
(loop with no-value-p = (gensym)
for (keyword writer-function) on option-writers by #'cddr
for option = (getf options keyword no-value-p)
unless (eql option no-value-p)
do (progn
(funcall writer-function definer keyword option)
(remf options keyword)))
;; Check whether we processed all options.
(unless (null options)
(oerror "Invalid options ~s for definer ~s of type ~s."
options definer))))
(defmacro make-option-writer (slot-writer &optional validator)
(with-unique-names (definer keyword value)
`(lambda (,definer ,keyword ,value)
,@(unless validator
`((declare (ignore ,keyword))))
(setf (,slot-writer ,definer)
,(if validator
`(,validator ,definer ,keyword ,value)
value)))))
(defun has-option-p (definer option)
(member option (options-of definer) :test #'char-equal))