Commit | Line | Data |
---|---|---|
74fa635b | 1 | (defpackage :types |
23a2c88b IA |
2 | (:use :common-lisp) |
3 | (:export :mal-value= | |
4 | ;; Accessors | |
5 | :mal-value | |
6 | :mal-type | |
7 | :mal-meta | |
1161823b | 8 | :mal-attrs |
23a2c88b IA |
9 | ;; Mal values |
10 | :number | |
11 | :boolean | |
12 | :nil | |
13 | :string | |
14 | :symbol | |
15 | :keyword | |
16 | :list | |
17 | :vector | |
18 | :hash-map | |
b9de97b6 | 19 | :atom |
579201bb | 20 | :fn |
3747635d | 21 | :builtin-fn |
a85bb5e2 | 22 | :any |
e844f5c8 IA |
23 | :mal-exception |
24 | ;; User exceptions | |
25 | :mal-user-exception | |
26 | ;; Exceptions raised by the runtime itself | |
27 | :mal-runtime-exception | |
aa333009 IA |
28 | ;; Error |
29 | :mal-error | |
23a2c88b | 30 | ;; Helpers |
623c5d2f | 31 | :wrap-value |
23a2c88b | 32 | :apply-unwrapped-values |
d584319a | 33 | :apply-unwrapped-values-prefer-bool |
362360df | 34 | :switch-mal-type)) |
74fa635b IA |
35 | |
36 | (in-package :types) | |
37 | ||
aa333009 IA |
38 | (define-condition mal-error (error) |
39 | nil) | |
40 | ||
e844f5c8 IA |
41 | (define-condition mal-exception (error) |
42 | nil) | |
43 | ||
44 | (define-condition mal-runtime-exception (mal-exception) | |
45 | nil) | |
46 | ||
47 | (define-condition mal-user-exception (mal-exception) | |
48 | ((data :accessor mal-exception-data :initarg :data))) | |
49 | ||
74fa635b IA |
50 | (defclass mal-type () |
51 | ((value :accessor mal-value :initarg :value) | |
dc3f4a5d | 52 | (meta :accessor mal-meta :initarg :meta :initform nil) |
1161823b IA |
53 | (type :accessor mal-type :initarg :type) |
54 | (attrs :accessor mal-attrs :initarg :attrs))) | |
74fa635b | 55 | |
1e9ac59c | 56 | (defmethod print-object ((obj mal-type) out) |
1161823b IA |
57 | (with-slots (value type meta attrs) obj |
58 | (format out "#<mal ~a: ~a (~a, ~a)>" type value meta attrs))) | |
33537539 | 59 | |
74fa635b IA |
60 | (defmacro define-mal-type (type) |
61 | ;; Create a class for given type and a convenience constructor and also export | |
62 | ;; them | |
63 | (let ((name (intern (string-upcase (concatenate 'string | |
64 | "mal-" | |
65 | (symbol-name type))))) | |
66 | (constructor (intern (string-upcase (concatenate 'string | |
67 | "make-mal-" | |
24c07f4c IA |
68 | (symbol-name type))))) |
69 | (predicate (intern (string-upcase (concatenate 'string | |
70 | "mal-" | |
71 | (symbol-name type) | |
72 | "-p"))))) | |
74fa635b IA |
73 | `(progn (defclass ,name (mal-type) |
74 | ((type :accessor mal-type | |
75 | :initarg :type | |
76 | :initform ',type))) | |
77 | ||
1161823b | 78 | (defun ,constructor (value &key meta attrs) |
74fa635b | 79 | (make-instance ',name |
33537539 | 80 | :value value |
1161823b IA |
81 | :meta meta |
82 | :attrs attrs)) | |
24c07f4c | 83 | (defun ,predicate (value) |
579201bb IA |
84 | (when (typep value 'mal-type) |
85 | (equal (mal-type value) ',type))) | |
74fa635b IA |
86 | |
87 | (export ',name) | |
24c07f4c IA |
88 | (export ',constructor) |
89 | (export ',predicate)))) | |
74fa635b | 90 | |
74fa635b IA |
91 | (define-mal-type number) |
92 | (define-mal-type symbol) | |
3ccd8eaa | 93 | (define-mal-type keyword) |
74fa635b | 94 | (define-mal-type string) |
e4d7c6ac | 95 | ;; TODO true, false and nil should ideally be singleton |
74fa635b | 96 | (define-mal-type boolean) |
e4d7c6ac IA |
97 | (define-mal-type nil) |
98 | ||
23a2c88b IA |
99 | (define-mal-type list) |
100 | (define-mal-type vector) | |
101 | (define-mal-type hash-map) | |
e4d7c6ac | 102 | |
b9de97b6 IA |
103 | (define-mal-type atom) |
104 | ||
e4d7c6ac | 105 | (define-mal-type fn) |
3747635d | 106 | (define-mal-type builtin-fn) |
0baefce0 | 107 | |
a85bb5e2 | 108 | ;; Generic type |
49b184f0 | 109 | (defvar any) |
a85bb5e2 | 110 | |
0baefce0 | 111 | (defmacro switch-mal-type (ast &body forms) |
26451863 | 112 | `(let ((type (types:mal-type ,ast))) |
0baefce0 IA |
113 | (cond |
114 | ,@(mapcar (lambda (form) | |
a85bb5e2 IA |
115 | (list (if (or (equal (car form) t) |
116 | (equal (car form) 'any)) | |
117 | t | |
118 | (list 'equal (list 'quote (car form)) 'type)) | |
0baefce0 IA |
119 | (cadr form))) |
120 | forms)))) | |
a85bb5e2 | 121 | |
de8ef209 IA |
122 | (defun mal-symbol= (value1 value2) |
123 | (string= (symbol-name (mal-value value1)) | |
124 | (symbol-name (mal-value value2)))) | |
125 | ||
126 | (defun mal-sequence= (value1 value2) | |
127 | (let ((sequence1 (map 'list #'identity (mal-value value1))) | |
128 | (sequence2 (map 'list #'identity (mal-value value2)))) | |
129 | (when (= (length sequence1) (length sequence2)) | |
130 | (every #'identity | |
131 | (loop | |
132 | for x in sequence1 | |
133 | for y in sequence2 | |
134 | collect (mal-value= x y)))))) | |
135 | ||
136 | (defun mal-hash-map= (value1 value2) | |
137 | (let ((map1 (mal-value value1)) | |
138 | (map2 (mal-value value2))) | |
139 | (when (= (hash-table-count map1) (hash-table-count map2)) | |
140 | (every #'identity | |
141 | (loop | |
142 | for key being the hash-keys of map1 | |
143 | collect (mal-value= (gethash key map1) | |
144 | (gethash key map2))))))) | |
145 | ||
146 | (defun mal-value= (value1 value2) | |
fd53ac3c IA |
147 | (when (and (typep value1 'mal-type) |
148 | (typep value2 'mal-type)) | |
149 | (if (equal (mal-type value1) (mal-type value2)) | |
dc1a9c47 IA |
150 | (switch-mal-type value1 |
151 | (number (= (mal-value value1) (mal-value value2))) | |
152 | (boolean (equal (mal-value value1) (mal-value value2))) | |
153 | (nil (equal (mal-value value1) (mal-value value2))) | |
154 | (string (string= (mal-value value1) (mal-value value2))) | |
155 | (symbol (mal-symbol= value1 value2)) | |
156 | (keyword (mal-symbol= value1 value2)) | |
157 | (list (mal-sequence= value1 value2)) | |
158 | (vector (mal-sequence= value1 value2)) | |
159 | (hash-map (mal-hash-map= value1 value2)) | |
160 | (any nil)) | |
161 | (when (or (and (mal-list-p value1) (mal-vector-p value2)) | |
162 | (and (mal-list-p value2) (mal-vector-p value1))) | |
fd53ac3c | 163 | (mal-sequence= value1 value2))))) |
de8ef209 IA |
164 | |
165 | (defun hash-mal-value (value) | |
166 | (sxhash (mal-value value))) | |
167 | ||
168 | #+sbcl (sb-ext:define-hash-table-test mal-value= hash-mal-value) | |
169 | #+clisp (ext:define-hash-table-test mal-value= mal-value= hash-mal-value) | |
170 | ||
130e1c94 IA |
171 | (defun wrap-hash-value (value) |
172 | (let ((new-hash-table (make-hash-table :test 'mal-value=))) | |
173 | (loop | |
174 | for key being the hash-keys of value | |
175 | do (setf (gethash (wrap-value key) new-hash-table) | |
176 | (wrap-value (gethash key value)))) | |
177 | new-hash-table)) | |
178 | ||
179 | (defun wrap-value (value &key booleanp listp) | |
180 | (typecase value | |
181 | (number (make-mal-number value)) | |
182 | ;; This needs to before symbol since nil is a symbol | |
183 | (null (funcall (cond | |
184 | (booleanp #'make-mal-boolean) | |
185 | (listp #'make-mal-list) | |
186 | (t #'make-mal-nil)) | |
187 | value)) | |
188 | ;; This needs to before symbol since t, nil are symbols | |
189 | (boolean (make-mal-boolean value)) | |
190 | (symbol (make-mal-symbol value)) | |
191 | (keyword (make-mal-keyword value)) | |
192 | (string (make-mal-string value)) | |
193 | (list (make-mal-list (map 'list #'wrap-value value))) | |
194 | (vector (make-mal-vector (map 'vector #'wrap-value value))) | |
195 | (hash-table (make-mal-hash-map (wrap-hash-value value))) | |
196 | (null (make-mal-nil value)))) | |
623c5d2f | 197 | |
cc9b97ef IA |
198 | (defun unwrap-value (value) |
199 | (switch-mal-type value | |
200 | (list (mapcar #'unwrap-value (mal-value value))) | |
201 | (vector (map 'vector #'unwrap-value (mal-value value))) | |
202 | (hash-map (let ((hash-table (make-hash-table)) | |
203 | (hash-map-value (mal-value value))) | |
204 | (loop | |
205 | for key being the hash-keys of hash-map-value | |
206 | do (setf (gethash (mal-value key) hash-table) | |
207 | (mal-value (gethash key hash-map-value)))) | |
208 | hash-table)) | |
209 | (any (mal-value value)))) | |
210 | ||
24c07f4c | 211 | (defun apply-unwrapped-values (op &rest values) |
cc9b97ef | 212 | (wrap-value (apply op (mapcar #'unwrap-value values)))) |
d584319a IA |
213 | |
214 | (defun apply-unwrapped-values-prefer-bool (op &rest values) | |
cc9b97ef | 215 | (wrap-value (apply op (mapcar #'unwrap-value values)) :booleanp t)) |