-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmacro-utils.lisp
More file actions
198 lines (173 loc) · 6.71 KB
/
macro-utils.lisp
File metadata and controls
198 lines (173 loc) · 6.71 KB
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
(in-package #:llvm-bitcode)
(cl-interpol:enable-interpol-syntax)
(quasiquote-2.0:enable-quasiquote-2.0)
(defmacro! define-enum (name &body specs)
`(defparameter ,name (let ((,g!-res nil) (,g!-i -1))
,@(mapcar (lambda (x)
(if (atom x)
`(push (cons ',x (incf ,g!-i)) ,g!-res)
`(push (cons ',(car x) (setf ,g!-i ,(cadr x))) ,g!-res)))
specs)
(nreverse ,g!-res))))
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun error-block-parser (form)
(llvm-read-error "Block ~a is unexpected in this context." (cdr (assoc 'block-id form))))
(defun error-record-parser (form &optional type)
(declare (ignore type))
(llvm-read-error "Record ~a is unexpected in this context." (cdr (assoc 'code form))))
(defun warn-block-parser (form)
(warn "Block ~a is unexpected in this context -- I do standard parsing." (cdr (assoc 'block-id form)))
(default-block-parser form))
(defun warn-record-parser (form &optional type)
(warn "Record ~a is unexpected in this context -- I do standard parsing." (cdr (assoc 'code form)))
(default-record-parser form type))
(defun skip-block-parser (form)
(declare (ignore form))
nil)
(defun skip-record-parser (form &optional type)
(declare (ignore form type))
nil)
(defparameter block-parser-stash (make-hash-table :test #'eq)))
(defun parse-string-field (lst)
(coerce (mapcar (lambda (x)
(if (characterp x)
x
(code-char x)))
lst)
'string))
(defun element-cons-parser-code (spec)
(or (case (car spec)
(function `(funcall ,spec (car cur)))
(parse-with-function `(funcall ,(cadr spec) (car cur)))
(parse-rest-with-function `(funcall ,(cadr spec) cur)))))
(defun element-atom-parser-code (spec)
(cond ((eq 'int spec) `(int<- (car cur)))
((eq 'bool spec) `(bool<- (car cur)))
((eq 'string spec) `(parse-string-field (car cur)))
(t (error "Don't know how to understand this atom record parser spec: ~a" spec))))
(defun element-atom-advancer-code (spec)
(declare (ignore spec))
`(setf cur (cdr cur)))
(defun element-parser-code (spec)
(if (atom spec)
(element-atom-parser-code spec)
(element-cons-parser-code spec)))
(defun element-advancer-code (spec)
(if (atom spec)
(element-atom-advancer-code spec)
(element-cons-advancer-code spec)))
(defun element-cons-advancer-code (spec)
(ecase (car spec)
(function `(setf cur (cdr cur)))
(parse-with-function `(setf cur (cdr cur)))
(parse-rest-with-function nil)))
(defun mk-element-parser-code (spec)
(declare (special side-effect))
(if (atom spec)
`(progn (push ,(element-atom-parser-code spec) res)
,(element-atom-advancer-code spec))
(if (eq :side-effect (car spec))
(progn (setf side-effect (cdr spec))
nil)
(let ((code (element-cons-parser-code spec)))
(if code
`(progn (push ,code res)
,(element-cons-advancer-code spec))
`(progn (push (cons ,(intern (string (car spec)) "KEYWORD")
,(element-parser-code (cadr spec)))
res)
,(element-advancer-code (cadr spec))))))))
(defun! make-record-parser (specs name common-side-effect)
(let (side-effect)
(declare (special side-effect))
(let ((expanded-specs (mapcar #'mk-element-parser-code specs)))
`(lambda (form &optional type)
(declare (ignorable type))
(let (res
(cur (cdr (assoc 'fields form))))
(declare (ignorable cur))
;; TODO : check that lengths match
,@expanded-specs
(setf res (nreverse res))
,@side-effect
(let ((it (cons ,(intern (string name) "KEYWORD") res)))
,@common-side-effect
it))))))
(defun find-cons-pos (tree sym)
"Find first cons in TREE, whose CAR is EQ to SYM"
(labels ((rec (x)
(if (consp x)
(if (eq sym (car x))
(return-from find-cons-pos x)
(progn (rec (car x))
(rec (cdr x)))))))
(rec tree)
(error "Was unable to find ~a in a tree ~a" sym tree)))
(defun if-around--wrap (specs body)
(let ((around (find-if (lambda (x)
(and (consp x)
(eq :around (car x))))
specs)))
(if (not around)
body
(let ((res (copy-tree (cdr around))))
(setf (car (find-cons-pos res 'sub-body)) body)
`(progn ,@res)))))
(defun! make-block-parser-wrap (keys specs)
(flet ((key (kwd) (getf keys kwd)))
(let ((res (if-around--wrap
specs
`(let ((block-parsers
(let ((it (make-hash-table :test #'equal)))
(setf (gethash 'default it)
,(if (key :on-undefined-blocks)
`#',(intern #?"$((key :on-undefined-blocks))-BLOCK-PARSER")
`#'default-block-parser))
,@(mapcar (lambda (x)
`(setf (gethash ,(or (cdr (assoc x block-ids))
(error "I don't know ID for block ~a: check BLOCK-ID assoc list." x))
it)
(or (gethash ',x block-parser-stash)
(progn (warn "No custom parser is yet defined for block ~a -- using default parser." ',x)
(lambda (form)
(with-vanilla-parsers
(default-block-parser form)))))))
(cdr (assoc 'blocks specs)))
it))
(record-parsers
(let ((it (make-hash-table :test #'equal)))
(setf (gethash 'default it) ,(if (key :on-undefined-records)
`#',(intern #?"$((key :on-undefined-records))-RECORD-PARSER")
`#'default-record-parser))
,@(let ((side-effect (cdr (assoc :side-effect (cdr (assoc 'records specs))))))
(iter (with i = 0)
(for rec-spec in (cdr (assoc 'records specs)))
(if (eq :side-effect (car rec-spec))
(next-iteration))
(destructuring-bind (name id) (if (atom (car rec-spec))
(list (car rec-spec) (incf i))
(list (caar rec-spec)
(setf i (cadar rec-spec))))
(collect `(setf (gethash ,id it)
,(make-record-parser (cdr rec-spec) name side-effect))))))
it)))
,g!-sub-body))))
(values res (find-cons-pos res g!-sub-body)))))
(defmacro! define-block (name (&rest keys) &body specs)
`(eval-when (:compile-toplevel :load-toplevel :execute)
(setf (gethash ',name block-parser-stash)
(lambda (form)
(let ((res ,(multiple-value-bind (form sub-body) (make-block-parser-wrap keys specs)
;; TODO : maybe there should be a room for side-effects at block level also?
(setf (car sub-body) `(default-block-parser form))
form)))
(cons ,(intern (string name) "KEYWORD")
(caddr res)))))))
(defmacro! define-toplevel-parser ((&rest keys) &body specs)
`(eval-when (:compile-toplevel :load-toplevel :execute)
(setf (gethash 'toplevel block-parser-stash)
(lambda ()
,(multiple-value-bind (form sub-body) (make-block-parser-wrap keys specs)
;; TODO : maybe there should be a room for side-effects at block level also?
(setf (car sub-body) `(default-toplevel-parser))
form)))))