-
Notifications
You must be signed in to change notification settings - Fork 0
/
chez-init.ss
371 lines (342 loc) · 14.8 KB
/
chez-init.ss
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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
;; save the nicer chez behavior
(define chez-printf printf)
(define chez-pretty-print pretty-print)
;; use the nicer chez behavior for these
(define sllgen:pretty-print chez-pretty-print)
(define eopl:pretty-print chez-pretty-print)
(define define-datatype:pretty-print chez-pretty-print)
;;I do not want to get into the debugger:
(define eopl:error-stop (lambda () '()))
(define eopl:error errorf)
;-------------------------- from this other file:
;; define-datatype.scm
;; this line must be within 8 lines of the top of the file
'(let ((time-stamp "Time-stamp: <2001-06-08 10:36:53 dfried>"))
(display (string-append
"define-datatype.scm version J3 "
(substring time-stamp 13 29)
(string #\newline))))
;;; This is an r5rs-compliant datatype system.
;;; exports define-datatype, isa, cases, list-of?, always?
;;; test with (define-datatype:test-all)
;; new error reporting system added by mw Mon Apr 24 14:49:03 2000.
(define define-datatype:report-error eopl:error)
; (lambda (symbol format . data)
; ;; print the message
; (eopl:printf "Error in ~s: " symbol)
; (apply eopl:printf (cons format data))
; (newline)
; (eopl:error-stop)))
(define define-datatype:reset-registries 'ignored)
(define define-datatype:is-a-type? 'ignored)
(define define-datatype:datatype-checker®istry-updater 'ignored)
(define define-datatype:case-checker 'ignored)
(let ((define-datatype:type-registry '())
(define-datatype:variant-registry '()))
(set! define-datatype:reset-registries
(lambda ()
(set! define-datatype:type-registry '())
(set! define-datatype:variant-registry '())
#t))
(set! define-datatype:is-a-type?
(lambda (type-name)
(memq type-name define-datatype:type-registry)))
(set! define-datatype:datatype-checker®istry-updater
(letrec ((set?
(lambda (s)
(if (null? s) #t
(and (not (memq (car s) (cdr s))) (set? (cdr s)))))))
(lambda (Type-name Variants)
(if (not (symbol? Type-name))
(define-datatype:report-error 'define-datatype
" The data type name ~s is not an identifier."
Type-name))
(for-each
(lambda (variant)
(if (not (symbol? (car variant)))
(define-datatype:report-error 'define-datatype
(string-append
"(While defining the ~a datatype) "
" The variant-name ~s is not an identifier.")
Type-name (car variant))))
Variants)
(let ((variant-names (map car Variants)))
(if (not (set? variant-names))
(define-datatype:report-error 'define-datatype
(string-append
"(While defining the ~a datatype) "
" Some of the variant-names are repeated: ~s.")
Type-name variant-names))
(for-each
(lambda (v)
(cond ;;; This assq cannot be changed.
((assq v define-datatype:variant-registry) =>
(lambda (pair)
(if (not (eq? (cdr pair) Type-name))
(define-datatype:report-error 'define-datatype
(string-append
"(While defining the ~a data type) "
" The variant-name ~s has already been "
" used as a variant name in ~s.")
Type-name v (cdr pair)))))))
variant-names)
(cond ;;; This assq could be a memq over variant names, only.
;;; but would reqire a third local registry.
((assq Type-name define-datatype:variant-registry) =>
(lambda (pair)
(define-datatype:report-error 'define-datatype
(string-append
"(While defining the ~a data type) "
" The type name ~s has already been "
" used as a variant name ~s in the "
" data type ~s.")
Type-name Type-name (car pair) (cdr pair))))
((memq Type-name variant-names)
(define-datatype:report-error 'define-datatype
(string-append
"(While defining the ~a data type) "
" Variant name is the same as the data type name.")
Type-name)))
(for-each
(lambda (variant-name)
(cond
((memq variant-name define-datatype:type-registry)
(define-datatype:report-error 'define-datatype
(string-append
"(While defining the ~a data type) "
" The variant name ~s has already been "
" used as a type name.")
Type-name variant-name))))
variant-names)
(set! define-datatype:variant-registry
(append
(map (lambda (v) (cons v Type-name)) variant-names)
define-datatype:variant-registry))
(cond
((memq Type-name define-datatype:type-registry) =>
(lambda (pair)
(set-car! pair Type-name)))
(else
(set! define-datatype:type-registry
(cons Type-name define-datatype:type-registry))))))))
(set! define-datatype:case-checker
(let ((remq-or-false
(lambda (sym ls)
(call-with-current-continuation
(lambda (k)
(let f ((ls ls))
(cond ((null? ls) (k #f))
((eq? (car ls) sym) (cdr ls))
(else (cons (car ls) (f (cdr ls)))))))))))
(lambda (Type-value Type-name Expression clauses)
(if (eq? Type-name Expression)
(begin
(define-datatype:report-error 'cases
(string-append
"The data type ~s should not be the same "
" as a lexical variable.")
Type-name))
(let ((variant-table (cdr Type-value)))
(let f ((clauses* clauses)
(unused-variants (map car variant-table)))
(if (null? clauses*)
(if (not (null? unused-variants))
(begin
(define-datatype:report-error 'cases "Missing variant clauses for ~s."
unused-variants)))
(let* ((head-clause (car clauses*))
(tail-clauses (cdr clauses*))
(purported-variant (car head-clause)))
(if (eq? purported-variant Expression)
(begin
(define-datatype:report-error 'cases
(string-append
"The variant name ~s should not be the same "
" as a lexical variable.")
Expression))
(cond
((and (null? tail-clauses) (eq? purported-variant 'else))
; do nothing, we're fine
)
((assq purported-variant variant-table)
=>
(lambda (p)
(let ((fields (cdr p))
(purported-fields (cadr head-clause))
(new-unused-variants-or-false
(remq-or-false
purported-variant
unused-variants)))
(if (not (=
(length fields)
(length purported-fields)))
(begin
(define-datatype:report-error 'cases "Bad fields in ~s." head-clause)))
(if (not new-unused-variants-or-false)
(begin
(define-datatype:report-error 'cases "Duplicate variant clause: ~s."
head-clause)))
(f tail-clauses new-unused-variants-or-false))))
(else
(define-datatype:report-error 'cases
"Bad clause: ~s."
head-clause)))))))))))))
(define-syntax isa
(syntax-rules ()
((_)
(define-datatype:report-error 'isa "isa expects 1 argument, not 0."))
((_ type-name)
(if (symbol? 'type-name)
(lambda args
(if (null? args)
(define-datatype:report-error 'isa "(isa ~s) expects 1 argument, not 0." 'type-name)
(if (null? (cdr args))
(let ((variant (car args)))
(let ((type-info type-name))
(if (and (pair? type-info) (list? (car type-info)))
(and (pair? variant)
(memq (car variant) (car type-info)) #t)
(define-datatype:report-error 'isa
(string-append
"(isa ~s) did not get a data type bound to an "
" appropriate structure: ~s. "
" This tends to happen when the type name is "
" bound to a lexical variable.")
'type-name type-info))))
(define-datatype:report-error 'isa
(string-append
"(isa ~s) expects 1 argument, not ~s. "
" With argument list = ~s.")
'type-name (length args) args))))
(define-datatype:report-error 'isa "Type name is not a symbol: ~s." 'type-name)))
((_ type-name other ...)
(define-datatype:report-error 'isa "(isa ~s) expects 1 argument, not ~s with ~s."
'type-name (add1 (length '(other ...)))
(cons 'isa '(type-name other ...))))))
(define-syntax define-datatype
(syntax-rules ()
((_ Type-name)
(define-datatype:report-error 'define-datatype
(string-append
" There are no variants: ~s.")
'(define-datatype Type-name)))
((_ Type-name Type-name?)
(define-datatype:report-error 'define-datatype
(string-append
" There are no variants: ~s.")
'(define-datatype Type-name Type-name?)))
((_ Type-name Type-name?
(Variant-name (Field-name Pred?) ...)
...)
(begin
;[wdc]
(define ignored
(define-datatype:datatype-checker®istry-updater
'Type-name
'((Variant-name (Field-name Pred?) ...)
...)))
;[\wdc]
(define Type-name
(cons '(Variant-name ...)
'((Variant-name Field-name ...) ...)))
(define Type-name?
(if (symbol? 'Type-name)
(lambda args
(if (null? args)
(define-datatype:report-error 'Type-name? "expects 1 argument, not 0.")
(if (null? (cdr args))
(let ((variant (car args)))
(let ((type-info Type-name))
(if (and (pair? type-info) (list? (car type-info)))
(and (pair? variant)
(memq (car variant) (car type-info)) #t)
(define-datatype:report-error 'Type-name?
(string-append
"did not get a data type bound to an "
" appropriate structure: ~s. "
" This tends to happen when the type name is "
" bound to a lexical variable.")
'type-name type-info))))
(define-datatype:report-error 'Type-name?
(string-append
"expects 1 argument, not ~s. "
" With argument list = ~s.")
(length args) args))))
(define-datatype:report-error 'Type-name "Type name is not a symbol: ~s." 'type-name)))
(define Variant-name
(let ((expected-length (length '(Field-name ...)))
(field-names '(Field-name ...))
(pred-names '(Pred? ...))
(preds (list (lambda (x) (Pred? x)) ...)))
(lambda args
(if (not (= (length args) expected-length))
(define-datatype:report-error 'Variant-name
(string-append
"Expected ~s arguments but got ~s arguments."
" Fields are: ~s Args are: ~s.")
expected-length (length args) '(Field-name ...) args))
(for-each
(lambda (a f p pname)
(if (not (p a))
(define-datatype:report-error 'Variant-name " Bad ~a field (~s ~s) => #f."
f pname a)))
args
field-names
preds
pred-names)
(cons 'Variant-name args))))
...))))
(define-syntax cases
(syntax-rules ()
((_ Type-name Expression . Clauses)
(let ((type-predicate? (isa Type-name)))
(define-datatype:case-checker
Type-name
'Type-name
'Expression
'Clauses)
(let ((x Expression))
(if (type-predicate? x)
(define-datatype:case-helper x . Clauses)
(begin
(define-datatype:report-error 'cases
" Not a ~a variant: ~s." 'Type-name x))))))))
;;; this only works because no-variant datatypes are invalid.
(define-syntax define-datatype:case-helper
(syntax-rules (else)
((_ Variant (else Body0 Body1 ...))
(begin Body0 Body1 ...))
((_ Variant (Purported-variant-name (Purported-field-name ...)
Body0 Body1 ...))
(apply (lambda (Purported-field-name ...) Body0 Body1 ...)
(cdr Variant)))
((_ Variant (Purported-variant-name (Purported-field-name ...)
Body0 Body1 ...)
Clause ...)
(if (eq? (car Variant) 'Purported-variant-name)
(apply (lambda (Purported-field-name ...) Body0 Body1 ...)
(cdr Variant))
(define-datatype:case-helper Variant Clause ...)))
((_ Variant Neither-an-else-nor-clause ...)
(define-datatype:report-error 'cases
" Not a ~a clause: ~s." 'Type-name
(list Neither-an-else-nor-clause ...)))))
;;; ------------------------------
;;; general helpers
(define always?
(lambda (x) #t))
(define list-of
(lambda (pred . l)
(let ((all-preds (cons pred l)))
(lambda (obj)
(let loop ((obj obj) (preds '()))
(or
;; if list is empty, preds should be, too
(and (null? obj) (null? preds))
(if (null? preds)
;; if preds is empty, but list isn't, then recycle
(loop obj all-preds)
;; otherwise check and element and recur.
(and (pair? obj)
((car preds) (car obj))
(loop (cdr obj) (cdr preds))))))))))