-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtrivalent.lisp
96 lines (88 loc) · 3.14 KB
/
trivalent.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
85
86
87
88
89
90
91
92
93
94
95
96
(in-package #:sandalphon.types)
(defmacro tri/if (condition ontrue onfalse onunknown)
(let ((truth (gensym "TRUTH"))
(certainty (gensym "CERTAINTY")))
`(multiple-value-bind (,truth ,certainty) ,condition
(if ,certainty
(if ,truth ,ontrue ,onfalse)
,onunknown))))
(defmacro tri/combine (&rest forms)
"Evaluate FORMS from left to right until one is certain, and return that."
(cond ((endp forms) (values nil nil)) ; whatever
;; (the t ...) is to force nontoplevelness.
((endp (rest forms)) `(the t ,(first forms)))
(t (with-gensyms (result certainty)
`(multiple-value-bind (,result ,certainty) ,(first forms)
(if ,certainty
(values ,result ,certainty)
(tri/combine ,@(rest forms))))))))
(defun tri/every (pred first-seq &rest more-seqs)
"Like CL:EVERY but with trivalent values."
(let ((overall-certainty t))
(flet ((map-me (&rest rest)
(multiple-value-bind (value certainty)
(apply pred rest)
(unless value
(return-from tri/every (values nil t)))
(unless certainty (setf overall-certainty nil)))))
(apply #'map nil #'map-me first-seq more-seqs)
(if overall-certainty
(values t t)
(values nil nil)))))
(defun tri/notany (pred first-seq &rest more-seqs)
"Like CL:NOTANY, but with trivalent values."
(let ((overall-certainty t))
(flet ((map-me (&rest rest)
(multiple-value-bind (value certainty)
(apply pred rest)
(unless certainty (setf overall-certainty nil))
(when value
(return-from tri/notany (values nil t))))))
(declare (inline map-me))
(apply #'map nil #'map-me first-seq more-seqs)
(if overall-certainty
(values t t)
(values nil nil)))))
(defun tri/notevery (pred first-seq &rest more-seqs)
"Like CL:NOTEVERY, but with trivalent values."
(let ((overall-certainty t))
(flet ((map-me (&rest rest)
(multiple-value-bind (value certainty)
(apply pred rest)
(if certainty
(unless value
(return-from tri/notevery (values t t)))
(setf overall-certainty nil)))))
(declare (inline map-me))
(apply #'map nil #'map-me first-seq more-seqs)
(values nil overall-certainty))))
(defmacro tri/not (form)
(with-gensyms (result certainty)
`(multiple-value-bind (,result ,certainty) ,form
(if ,certainty
(values (not ,result) ,certainty)
(values nil nil)))))
(defmacro tri/or (&rest forms)
;; true | unknown = true, false | unknown = unknown
(cond ((endp forms) (values nil nil))
((endp (rest forms))
`(the t ,(first forms)))
(t (let ((result (gensym "RESULT"))
(certainty (gensym "CERTAINTY")))
`(multiple-value-bind (,result ,certainty)
,(first forms)
(if ,result
(values ,result t)
(tri/or ,@(rest forms))))))))
(defmacro tri/and (&rest forms)
;; true & unknown = unknown, false & unknown = false
(cond ((endp forms) (values nil nil))
((endp (rest forms))
`(the t ,(first forms)))
(t (let ((result (gensym "RESULT"))
(certainty (gensym "CERTAINTY")))
`(multiple-value-bind (,result ,certainty)
,(first forms)
(if (and ,certainty (not ,result)) ; false
(values ,certainty ,result)
(tri/and ,@(rest forms))))))))