~ chicken-core (chicken-5) /expand.scm
Trap1;;;; expand.scm - The HI/LO expander
2;
3; Copyright (c) 2008-2022, The CHICKEN Team
4; All rights reserved.
5;
6; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
7; conditions are met:
8;
9; Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10; disclaimer.
11; Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
12; disclaimer in the documentation and/or other materials provided with the distribution.
13; Neither the name of the author nor the names of its contributors may be used to endorse or promote
14; products derived from this software without specific prior written permission.
15;
16; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
17; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24; POSSIBILITY OF SUCH DAMAGE.
25
26
27;; this unit needs the "modules" unit, but must be initialized first, so it doesn't
28;; declare "modules" as used - if you use "-explicit-use", take care of this.
29
30(declare
31 (unit expand)
32 (uses internal)
33 (disable-interrupts)
34 (fixnum)
35 (not inline ##sys#syntax-error-hook ##sys#compiler-syntax-hook))
36
37(module chicken.syntax
38 (expand
39 get-line-number
40 read-with-source-info
41 strip-syntax
42 syntax-error
43 er-macro-transformer
44 ir-macro-transformer)
45
46(import scheme
47 chicken.base
48 chicken.condition
49 chicken.fixnum
50 chicken.internal
51 chicken.keyword
52 chicken.platform
53 chicken.string)
54
55(include "common-declarations.scm")
56(include "mini-srfi-1.scm")
57
58(define-syntax d (syntax-rules () ((_ . _) (void))))
59;(define-syntax d (syntax-rules () ((_ args ...) (print args ...))))
60
61;; Macro to avoid "unused variable map-se" when "d" is disabled
62(define-syntax map-se
63 (syntax-rules ()
64 ((_ ?se)
65 (map (lambda (a)
66 (cons (car a) (if (symbol? (cdr a)) (cdr a) '<macro>)))
67 ?se))))
68
69(define-alias dd d)
70(define-alias dm d)
71(define-alias dx d)
72
73(define-inline (getp sym prop)
74 (##core#inline "C_i_getprop" sym prop #f))
75
76(define-inline (putp sym prop val)
77 (##core#inline_allocate ("C_a_i_putprop" 8) sym prop val))
78
79(define-inline (namespaced-symbol? sym)
80 (##core#inline "C_u_i_namespaced_symbolp" sym))
81
82;;; Source file tracking
83
84(define ##sys#current-source-filename #f)
85
86;;; Syntactic environments
87
88(define ##sys#current-environment (make-parameter '()))
89(define ##sys#current-meta-environment (make-parameter '()))
90
91(define (lookup id se)
92 (cond ((##core#inline "C_u_i_assq" id se) => cdr)
93 ((getp id '##core#macro-alias))
94 (else #f)))
95
96(define (macro-alias var se)
97 (if (or (keyword? var) (namespaced-symbol? var))
98 var
99 (let* ((alias (gensym var))
100 (ua (or (lookup var se) var))
101 (rn (or (getp var '##core#real-name) var)))
102 (putp alias '##core#macro-alias ua)
103 (putp alias '##core#real-name rn)
104 (dd "aliasing " alias " (real: " var ") to "
105 (if (pair? ua)
106 '<macro>
107 ua))
108 alias) ) )
109
110(define (strip-syntax exp)
111 (let ((seen '()))
112 (let walk ((x exp))
113 (cond ((assq x seen) => cdr)
114 ((keyword? x) x)
115 ((symbol? x)
116 (let ((x2 (getp x '##core#macro-alias) ) )
117 (cond ((getp x '##core#real-name))
118 ((not x2) x)
119 ((pair? x2) x)
120 (else x2))))
121 ((pair? x)
122 (let ((cell (cons #f #f)))
123 (set! seen (cons (cons x cell) seen))
124 (set-car! cell (walk (car x)))
125 (set-cdr! cell (walk (cdr x)))
126 cell))
127 ((vector? x)
128 (let* ((len (##sys#size x))
129 (vec (make-vector len)))
130 (set! seen (cons (cons x vec) seen))
131 (do ((i 0 (fx+ i 1)))
132 ((fx>= i len) vec)
133 (##sys#setslot vec i (walk (##sys#slot x i))))))
134 (else x)))))
135
136(define (##sys#extend-se se vars #!optional (aliases (map gensym vars)))
137 (for-each
138 (lambda (alias sym)
139 (let ((original-real-name (getp sym '##core#real-name)))
140 (putp alias '##core#real-name (or original-real-name sym))))
141 aliases vars)
142 (append (map (lambda (x y) (cons x y)) vars aliases) se)) ; inline cons
143
144
145;;; Macro handling
146
147(define ##sys#macro-environment (make-parameter '()))
148
149(define ##sys#scheme-macro-environment '()) ; reassigned below
150;; These are all re-assigned by chicken-syntax.scm:
151(define ##sys#chicken-ffi-macro-environment '()) ; used later in foreign.import.scm
152(define ##sys#chicken.condition-macro-environment '()) ; used later in chicken.condition.import.scm
153(define ##sys#chicken.time-macro-environment '()) ; used later in chicken.time.import.scm
154(define ##sys#chicken.type-macro-environment '()) ; used later in chicken.type.import.scm
155(define ##sys#chicken.syntax-macro-environment '()) ; used later in chicken.syntax.import.scm
156(define ##sys#chicken.base-macro-environment '()) ; used later in chicken.base.import.scm
157
158(define (##sys#ensure-transformer t #!optional loc)
159 (if (##sys#structure? t 'transformer)
160 (##sys#slot t 1)
161 (##sys#error loc "expected syntax-transformer, but got" t)))
162
163(define (##sys#extend-macro-environment name se transformer)
164 (let ((me (##sys#macro-environment))
165 (handler (##sys#ensure-transformer transformer name)))
166 (cond ((lookup name me) =>
167 (lambda (a)
168 (set-car! a se)
169 (set-car! (cdr a) handler)
170 a))
171 (else
172 (let ((data (list se handler)))
173 (##sys#macro-environment
174 (cons (cons name data) me))
175 data)))))
176
177(define (##sys#macro? sym #!optional (senv (##sys#current-environment)))
178 (or (let ((l (lookup sym senv)))
179 (pair? l))
180 (and-let* ((l (lookup sym (##sys#macro-environment))))
181 (pair? l))))
182
183(define (##sys#undefine-macro! name)
184 (##sys#macro-environment
185 ;; this builds up stack, but isn't used often anyway...
186 (let loop ((me (##sys#macro-environment)))
187 (cond ((null? me) '())
188 ((eq? name (caar me)) (cdr me))
189 (else (cons (car me) (loop (cdr me))))))))
190
191;; The basic macro-expander
192
193(define (##sys#expand-0 exp dse cs?)
194 (define (call-handler name handler exp se cs)
195 (dd "invoking macro: " name)
196 (dd `(STATIC-SE: ,@(map-se se)))
197 (handle-exceptions ex
198 ;; modify error message in condition object to include
199 ;; currently expanded macro-name
200 (abort
201 (if (and (##sys#structure? ex 'condition)
202 (memv 'exn (##sys#slot ex 1)) )
203 (##sys#make-structure
204 'condition
205 (##sys#slot ex 1)
206 (let copy ([ps (##sys#slot ex 2)])
207 (if (null? ps)
208 '()
209 (let ([p (car ps)]
210 [r (cdr ps)])
211 (if (and (equal? '(exn . message) p)
212 (pair? r)
213 (string? (car r)) )
214 (cons
215 '(exn . message)
216 (cons (string-append
217 "during expansion of ("
218 (##sys#slot name 1)
219 " ...) - "
220 (car r) )
221 (cdr r) ) )
222 (copy r) ) ) ) ) )
223 ex) )
224 (let ((exp2
225 (if cs
226 ;; compiler-syntax may "fall through"
227 (fluid-let ((chicken.internal.syntax-rules#syntax-rules-mismatch
228 (lambda (input) exp))) ; a bit of a hack
229 (handler exp se dse))
230 (handler exp se dse))) )
231 (when (and (not cs) (eq? exp exp2))
232 (##sys#syntax-error-hook
233 (string-append
234 "syntax transformer for `" (symbol->string name)
235 "' returns original form, which would result in endless expansion")
236 exp))
237 (dx `(,name ~~> ,exp2))
238 (expansion-result-hook exp exp2) ) ) )
239 (define (expand head exp mdef)
240 (dd `(EXPAND:
241 ,head
242 ,(cond ((getp head '##core#macro-alias) =>
243 (lambda (a) (if (symbol? a) a '<macro>)) )
244 (else '_))
245 ,exp
246 ,(if (pair? mdef)
247 `(SE: ,@(map-se (car mdef)))
248 mdef)))
249 (cond ((not (list? exp))
250 (##sys#syntax-error-hook "invalid syntax in macro form" exp) )
251 ((pair? mdef)
252 (values
253 ;; force ref. opaqueness by passing dynamic se [what does this comment mean? I forgot ...]
254 (call-handler head (cadr mdef) exp (car mdef) #f)
255 #t))
256 (else (values exp #f)) ) )
257 (let loop ((exp exp))
258 (if (pair? exp)
259 (let ((head (car exp))
260 (body (cdr exp)) )
261 (if (symbol? head)
262 (let ((head2 (or (lookup head dse) head)))
263 (unless (pair? head2)
264 (set! head2 (or (lookup head2 (##sys#macro-environment)) head2)) )
265 (cond ((and (pair? head2)
266 (eq? (##sys#get head '##sys#override) 'value))
267 (values exp #f))
268 ((eq? head2 '##core#let)
269 (##sys#check-syntax 'let body '#(_ 2) #f dse)
270 (let ((bindings (car body)))
271 (cond ((symbol? bindings) ; expand named let
272 (##sys#check-syntax 'let body '(_ #((variable _) 0) . #(_ 1)) #f dse)
273 (let ([bs (cadr body)])
274 (values
275 `(##core#app
276 (##core#letrec*
277 ([,bindings
278 (##core#loop-lambda
279 ,(map (lambda (b) (car b)) bs) ,@(cddr body))])
280 ,bindings)
281 ,@(##sys#map cadr bs) )
282 #t) ) )
283 (else (values exp #f)) ) ) )
284 ((and cs? (symbol? head2) (getp head2 '##compiler#compiler-syntax)) =>
285 (lambda (cs)
286 (let ((result (call-handler head (car cs) exp (cdr cs) #t)))
287 (cond ((eq? result exp) (expand head exp head2))
288 (else
289 (when ##sys#compiler-syntax-hook
290 (##sys#compiler-syntax-hook head2 result))
291 (loop result))))))
292 (else (expand head exp head2)) ) )
293 (values exp #f) ) )
294 (values exp #f) ) ) )
295
296(define ##sys#compiler-syntax-hook #f)
297(define ##sys#enable-runtime-macros #f)
298(define expansion-result-hook (lambda (input output) output))
299
300
301;;; User-level macroexpansion
302
303(define (expand exp #!optional (se (##sys#current-environment)) cs?)
304 (let loop ((exp exp))
305 (let-values (((exp2 m) (##sys#expand-0 exp se cs?)))
306 (if m
307 (loop exp2)
308 exp2) ) ) )
309
310
311;;; Extended (DSSSL-style) lambda lists
312;
313; Assumptions:
314;
315; 1) #!rest must come before #!key
316; 2) default values may refer to earlier variables
317; 3) optional/key args may be either variable or (variable default)
318; 4) an argument marker may not be specified more than once
319; 5) no special handling of extra keywords (no error)
320; 6) default value of optional/key args is #f
321; 7) mixing with dotted list syntax is allowed
322
323(define (##sys#extended-lambda-list? llist)
324 (let loop ([llist llist])
325 (and (pair? llist)
326 (case (##sys#slot llist 0)
327 [(#!rest #!optional #!key) #t]
328 [else (loop (cdr llist))] ) ) ) )
329
330(define ##sys#expand-extended-lambda-list
331 (let ((reverse reverse))
332 (lambda (llist0 body errh se)
333 (define (err msg) (errh msg llist0))
334 (define (->keyword s) (string->keyword (##sys#slot s 1)))
335 (let ((rvar #f)
336 (hasrest #f)
337 ;; These might not exist in se, use default or chicken env:
338 (%let* (macro-alias 'let* ##sys#default-macro-environment))
339 (%lambda '##core#lambda)
340 (%opt (macro-alias 'optional ##sys#chicken.base-macro-environment))
341 (%let-optionals* (macro-alias 'let-optionals* ##sys#chicken.base-macro-environment))
342 (%let '##core#let))
343 (let loop ([mode 0] ; req=0, opt=1, rest=2, key=3, end=4
344 [req '()]
345 [opt '()]
346 [key '()]
347 [llist llist0] )
348 (cond [(null? llist)
349 (values
350 (if rvar (##sys#append (reverse req) rvar) (reverse req))
351 (let ([body
352 (if (null? key)
353 body
354 `((,%let*
355 ,(map (lambda (k)
356 (let ([s (car k)])
357 `(,s (##sys#get-keyword
358 (##core#quote ,(->keyword (strip-syntax s))) ,(or hasrest rvar)
359 ,@(if (pair? (cdr k))
360 `((,%lambda () ,@(cdr k)))
361 '())))))
362 (reverse key) )
363 ,@body) ) ) ] )
364 (cond [(null? opt) body]
365 [(and (not hasrest) (null? key) (null? (cdr opt)))
366 `((,%let
367 ([,(caar opt) (,%opt ,rvar ,(cadar opt))])
368 ,@body) ) ]
369 [(and (not hasrest) (null? key))
370 `((,%let-optionals*
371 ,rvar ,(reverse opt) ,@body))]
372 [else
373 `((,%let-optionals*
374 ,rvar ,(##sys#append (reverse opt) (list (or hasrest rvar)))
375 ,@body))] ) ) ) ]
376 [(symbol? llist)
377 (if (fx> mode 2)
378 (err "rest argument list specified more than once")
379 (begin
380 (unless rvar (set! rvar llist))
381 (set! hasrest llist)
382 (loop 4 req opt '() '()) ) ) ]
383 [(not (pair? llist))
384 (err "invalid lambda list syntax") ]
385 [else
386 (let* ((var (car llist))
387 (x (or (and (symbol? var) (not (eq? 3 mode)) (lookup var se)) var))
388 (r (cdr llist)))
389 (case x
390 [(#!optional)
391 (unless rvar (set! rvar (macro-alias 'rest se)))
392 (if (eq? mode 0)
393 (loop 1 req '() '() r)
394 (err "`#!optional' argument marker in wrong context") ) ]
395 [(#!rest)
396 (if (fx<= mode 1)
397 (if (and (pair? r) (symbol? (car r)))
398 (begin
399 (if (not rvar) (set! rvar (car r)))
400 (set! hasrest (car r))
401 (loop 2 req opt '() (cdr r)) )
402 (err "invalid syntax of `#!rest' argument") )
403 (err "`#!rest' argument marker in wrong context") ) ]
404 [(#!key)
405 (if (not rvar) (set! rvar (macro-alias 'rest se)))
406 (if (fx<= mode 2)
407 (loop 3 req opt '() r)
408 (err "`#!key' argument marker in wrong context") ) ]
409 [else
410 (cond [(symbol? var)
411 (case mode
412 [(0) (loop 0 (cons var req) '() '() r)]
413 [(1) (loop 1 req (cons (list var #f) opt) '() r)]
414 [(2) (err "invalid lambda list syntax after `#!rest' marker")]
415 [else (loop 3 req opt (cons (list var) key) r)] ) ]
416 [(and (list? var) (eq? 2 (length var)) (symbol? (car var)))
417 (case mode
418 [(0) (err "invalid required argument syntax")]
419 [(1) (loop 1 req (cons var opt) '() r)]
420 [(2) (err "invalid lambda list syntax after `#!rest' marker")]
421 [else (loop 3 req opt (cons var key) r)] ) ]
422 [else (err "invalid lambda list syntax")] ) ] ) ) ] ) ) ) ) ) )
423
424
425;;; Error message for redefinition of currently used defining form
426;
427; (i.e.`"(define define ...)")
428
429(define (defjam-error form)
430 (##sys#syntax-error-hook
431 "redefinition of currently used defining form" ; help me find something better
432 form))
433
434;;; Expansion of multiple values assignments.
435;
436; Given a lambda list and a multi-valued expression, returns a form that
437; will `set!` each variable to its corresponding value in order.
438
439(define (##sys#expand-multiple-values-assignment formals expr)
440 (##sys#decompose-lambda-list
441 formals
442 (lambda (vars argc rest)
443 (let ((aliases (if (symbol? formals) '() (map gensym formals)))
444 (rest-alias (if (not rest) '() (gensym rest))))
445 `(##sys#call-with-values
446 (##core#lambda () ,expr)
447 (##core#lambda
448 ,(append aliases rest-alias)
449 ,@(map (lambda (v a) `(##core#set! ,v ,a)) vars aliases)
450 ,@(cond
451 ((null? formals) '((##core#undefined)))
452 ((null? rest-alias) '())
453 (else `((##core#set! ,rest ,rest-alias))))))))))
454
455;;; Expansion of bodies (and internal definitions)
456;
457; This code is disgustingly complex.
458
459(define define-definition)
460(define define-syntax-definition)
461(define define-values-definition)
462(define import-definition)
463
464(define ##sys#canonicalize-body
465 (lambda (body #!optional (se (##sys#current-environment)) cs?)
466 (define (comp s id)
467 (let ((f (or (lookup id se)
468 (lookup id (##sys#macro-environment)))))
469 (and (or (not (symbol? f))
470 (not (eq? (##sys#get id '##sys#override) 'value)))
471 (or (eq? f s) (eq? s id)))))
472 (define (comp-def def)
473 (lambda (id)
474 (let repeat ((id id))
475 (let ((f (or (lookup id se)
476 (lookup id (##sys#macro-environment)))))
477 (and (or (not (symbol? f))
478 (not (eq? (##sys#get id '##sys#override) 'value)))
479 (or (eq? f def)
480 (and (symbol? f)
481 (not (eq? f id))
482 (repeat f))))))))
483 (define comp-define (comp-def define-definition))
484 (define comp-define-syntax (comp-def define-syntax-definition))
485 (define comp-define-values (comp-def define-values-definition))
486 (define comp-import (comp-def import-definition))
487 (define (fini vars vals mvars body)
488 (if (and (null? vars) (null? mvars))
489 ;; Macro-expand body, and restart when defines are found.
490 (let loop ((body body) (exps '()))
491 (if (not (pair? body))
492 (cons
493 '##core#begin
494 (reverse exps)) ; no more defines, otherwise we would have called `expand'
495 (let loop2 ((body body))
496 (let ((x (car body))
497 (rest (cdr body)))
498 (if (and (pair? x)
499 (let ((d (car x)))
500 (and (symbol? d)
501 (or (comp '##core#begin d)
502 (comp-define d)
503 (comp-define-values d)
504 (comp-define-syntax d)
505 (comp-import d)))))
506 ;; Stupid hack to avoid expanding imports
507 (if (comp-import (car x))
508 (loop rest (cons x exps))
509 (cons
510 '##core#begin
511 (##sys#append (reverse exps) (list (expand body)))))
512 (let ((x2 (##sys#expand-0 x se cs?)))
513 (if (eq? x x2)
514 ;; Modules and includes must be processed before
515 ;; we can continue with other forms, so hand
516 ;; control back to the compiler
517 (if (and (pair? x)
518 (symbol? (car x))
519 (or (comp '##core#module (car x))
520 (comp '##core#include (car x))))
521 `(##core#begin
522 ,@(reverse exps)
523 ,@(if (comp '##core#module (car x))
524 (if (null? rest)
525 `(,x)
526 `(,x (##core#let () ,@rest)))
527 `((##core#include ,@(cdr x) ,rest))))
528 (loop rest (cons x exps)))
529 (loop2 (cons x2 rest)) )) ))) ))
530 ;; We saw defines. Translate to letrec, and let compiler
531 ;; call us again for the remaining body by wrapping the
532 ;; remaining body forms in a ##core#let.
533 (let* ((result
534 `(##core#let
535 ,(##sys#map
536 (lambda (v) (##sys#list v '(##core#undefined)))
537 ;; vars are all normalised to lambda-lists: flatten them
538 (foldl (lambda (l v)
539 (##sys#append l (##sys#decompose-lambda-list
540 v (lambda (a _ _) a))))
541 '()
542 (reverse vars))) ; not strictly necessary...
543 ,@(map (lambda (var val is-mvar?)
544 ;; Non-mvars should expand to set! for
545 ;; efficiency, but also because they must be
546 ;; implicit multi-value continuations.
547 (if is-mvar?
548 (##sys#expand-multiple-values-assignment var val)
549 `(##core#set! ,(car var) ,val)))
550 (reverse vars)
551 (reverse vals)
552 (reverse mvars))
553 ,@body) ) )
554 (dd `(BODY: ,result))
555 result)))
556 (define (fini/syntax vars vals mvars body)
557 (fini
558 vars vals mvars
559 (let loop ((body body) (defs '()) (done #f))
560 (cond (done `((##core#letrec-syntax
561 ,(map cdr (reverse defs)) ,@body) ))
562 ((not (pair? body)) (loop body defs #t))
563 ((and (list? (car body))
564 (>= 3 (length (car body)))
565 (symbol? (caar body))
566 (comp-define-syntax (caar body)))
567 (let ((def (car body)))
568 ;; This check is insufficient, if introduced by
569 ;; different expansions, but better than nothing:
570 (when (eq? (car def) (cadr def))
571 (defjam-error def))
572 (loop (cdr body) (cons def defs) #f)))
573 (else (loop body defs #t))))))
574 ;; Expand a run of defines or define-syntaxes into letrec. As
575 ;; soon as we encounter something else, finish up.
576 (define (expand body)
577 ;; Each #t in "mvars" indicates an MV-capable "var". Non-MV
578 ;; vars (#f in mvars) are 1-element lambda-lists for simplicity.
579 (let loop ((body body) (vars '()) (vals '()) (mvars '()))
580 (d "BODY: " body)
581 (if (not (pair? body))
582 (fini vars vals mvars body)
583 (let* ((x (car body))
584 (rest (cdr body))
585 (exp1 (and (pair? x) (car x)))
586 (head (and exp1 (symbol? exp1) exp1)))
587 (if (not (symbol? head))
588 (fini vars vals mvars body)
589 (cond
590 ((comp-define head)
591 (##sys#check-syntax 'define x '(_ _ . #(_ 0)) #f se)
592 (let loop2 ((x x))
593 (let ((head (cadr x)))
594 (cond ((not (pair? head))
595 (##sys#check-syntax 'define x '(_ variable . #(_ 0)) #f se)
596 (when (eq? (car x) head) ; see above
597 (defjam-error x))
598 (loop rest (cons (list head) vars)
599 (cons (if (pair? (cddr x))
600 (caddr x)
601 '(##core#undefined) )
602 vals)
603 (cons #f mvars)))
604 ((pair? (car head))
605 (##sys#check-syntax
606 'define x '(_ (_ . lambda-list) . #(_ 1)) #f se)
607 (loop2
608 (chicken.syntax#expand-curried-define head (cddr x) se)))
609 (else
610 (##sys#check-syntax
611 'define x
612 '(_ (variable . lambda-list) . #(_ 1)) #f se)
613 (loop rest
614 (cons (list (car head)) vars)
615 (cons `(##core#lambda ,(cdr head) ,@(cddr x)) vals)
616 (cons #f mvars)))))))
617 ((comp-define-syntax head)
618 (##sys#check-syntax 'define-syntax x '(_ _ . #(_ 1)) se)
619 (fini/syntax vars vals mvars body))
620 ((comp-define-values head)
621 ;;XXX check for any of the variables being `define-values'
622 (##sys#check-syntax 'define-values x '(_ lambda-list _) #f se)
623 (loop rest (cons (cadr x) vars) (cons (caddr x) vals) (cons #t mvars)))
624 ((comp '##core#begin head)
625 (loop (##sys#append (cdr x) rest) vars vals mvars))
626 (else
627 ;; Do not macro-expand local definitions we are
628 ;; in the process of introducing.
629 (if (member (list head) vars)
630 (fini vars vals mvars body)
631 (let ((x2 (##sys#expand-0 x se cs?)))
632 (if (eq? x x2)
633 (fini vars vals mvars body)
634 (loop (cons x2 rest) vars vals mvars)))))))))))
635 (expand body) ) )
636
637
638;;; A simple expression matcher
639
640;; Used by "quasiquote", below
641(define chicken.syntax#match-expression
642 (lambda (exp pat vars)
643 (let ((env '()))
644 (define (mwalk x p)
645 (cond ((not (pair? p))
646 (cond ((assq p env) => (lambda (a) (equal? x (cdr a))))
647 ((memq p vars)
648 (set! env (cons (cons p x) env))
649 #t)
650 (else (eq? x p)) ) )
651 ((pair? x)
652 (and (mwalk (car x) (car p))
653 (mwalk (cdr x) (cdr p)) ) )
654 (else #f) ) )
655 (and (mwalk exp pat) env) ) ) )
656
657
658;;; Expand "curried" lambda-list syntax for `define'
659
660;; Used by "define", below
661(define (chicken.syntax#expand-curried-define head body se)
662 (let ((name #f))
663 (define (loop head body)
664 (if (symbol? (car head))
665 (begin
666 (set! name (car head))
667 `(##core#lambda ,(cdr head) ,@body) )
668 (loop (car head) `((##core#lambda ,(cdr head) ,@body)) ) ))
669 (let ([exp (loop head body)])
670 (list 'define name exp) ) ) )
671
672
673;;; Line-number database management:
674
675(define ##sys#line-number-database #f)
676
677;;; General syntax checking routine:
678
679(define ##sys#syntax-error-culprit #f)
680(define ##sys#syntax-context '())
681
682(define (syntax-error . args)
683 (apply ##sys#signal-hook #:syntax-error
684 (strip-syntax args)))
685
686(define ##sys#syntax-error-hook syntax-error)
687
688(define ##sys#syntax-error/context
689 (lambda (msg arg)
690 (define (syntax-imports sym)
691 (let loop ((defs (or (##sys#get (strip-syntax sym) '##core#db) '())))
692 (cond ((null? defs) '())
693 ((eq? 'syntax (caar defs))
694 (cons (cadar defs) (loop (cdr defs))))
695 (else (loop (cdr defs))))))
696 (if (null? ##sys#syntax-context)
697 (##sys#syntax-error-hook msg arg)
698 (let ((out (open-output-string)))
699 (define (outstr str)
700 (##sys#print str #f out))
701 (let loop ((cx ##sys#syntax-context))
702 (cond ((null? cx) ; no unimported syntax found
703 (outstr msg)
704 (outstr ": ")
705 (##sys#print arg #t out)
706 (outstr "\ninside expression `(")
707 (##sys#print (strip-syntax (car ##sys#syntax-context)) #t out)
708 (outstr " ...)'"))
709 (else
710 (let* ((sym (strip-syntax (car cx)))
711 (us (syntax-imports sym)))
712 (cond ((pair? us)
713 (outstr msg)
714 (outstr ": ")
715 (##sys#print arg #t out)
716 (outstr "\n\n Perhaps you intended to use the syntax `(")
717 (##sys#print sym #t out)
718 (outstr " ...)' without importing it first.\n")
719 (if (fx= 1 (length us))
720 (outstr
721 (string-append
722 " Suggesting: `(import "
723 (symbol->string (car us))
724 ")'"))
725 (outstr
726 (string-append
727 " Suggesting one of:\n"
728 (let loop ((lst us))
729 (if (null? lst)
730 ""
731 (string-append
732 "\n (import " (symbol->string (car lst)) ")'"
733 (loop (cdr lst)))))))))
734 (else (loop (cdr cx))))))))
735 (##sys#syntax-error-hook (get-output-string out))))))
736
737;;; Hook for source information
738
739(define (alist-weak-cons k v lst)
740 (cons (##core#inline_allocate ("C_a_i_weak_cons" 3) k v) lst))
741
742(define (assq/drop-bwp! x lst)
743 (let lp ((lst lst)
744 (prev #f))
745 (cond ((null? lst) #f)
746 ((eq? x (caar lst)) (car lst))
747 ((and prev
748 (##core#inline "C_bwpp" (caar lst)))
749 (set-cdr! prev (cdr lst))
750 (lp (cdr lst) prev))
751 (else (lp (cdr lst) lst)))))
752
753(define (read-with-source-info-hook class data val)
754 (when (and (eq? 'list-info class) (symbol? (car data)))
755 (let ((old-value (or (hash-table-ref ##sys#line-number-database (car data)) '())))
756 (assq/drop-bwp! (car data) old-value) ;; Hack to clean out garbage values
757 (hash-table-set!
758 ##sys#line-number-database
759 (car data)
760 (alist-weak-cons
761 data (conc (or ##sys#current-source-filename "<stdin>") ":" val)
762 old-value ) )) )
763 data)
764
765(define-constant line-number-database-size 997) ; Copied from core.scm
766
767(define (read-with-source-info #!optional (in ##sys#standard-input))
768 ;; Initialize line number db on first use
769 (unless ##sys#line-number-database
770 (set! ##sys#line-number-database (make-vector line-number-database-size '())))
771 (##sys#check-input-port in #t 'read-with-source-info)
772 (##sys#read in read-with-source-info-hook) )
773
774
775(define (get-line-number sexp)
776 (and ##sys#line-number-database
777 (pair? sexp)
778 (let ([head (car sexp)])
779 (and (symbol? head)
780 (cond ((hash-table-ref ##sys#line-number-database head)
781 => (lambda (pl)
782 (let ((a (assq/drop-bwp! sexp pl)))
783 (and a (cdr a)))))
784 (else #f))))))
785
786;; TODO: Needs a better name - it extracts the name(?) and the source expression
787(define (##sys#get-line-2 exp)
788 (let* ((name (car exp))
789 (lst (hash-table-ref ##sys#line-number-database name)))
790 (cond ((and lst (assq/drop-bwp! exp (cdr lst)))
791 => (lambda (a) (values (car lst) (cdr a))) )
792 (else (values name #f)) ) ) )
793
794(define (##sys#display-line-number-database)
795 (hash-table-for-each
796 (lambda (key val)
797 (when val
798 (let ((port (current-output-port)))
799 (##sys#print key #t port)
800 (##sys#print " " #f port)
801 (##sys#print (map cdr val) #t port)
802 (##sys#print "\n" #f port))) )
803 ##sys#line-number-database) )
804
805;;; Traverse expression and update line-number db with all contained calls:
806
807(define (##sys#update-line-number-database! exp ln)
808 (define (mapupdate xs)
809 (let loop ((xs xs))
810 (when (pair? xs)
811 (walk (car xs))
812 (loop (cdr xs)) ) ) )
813 (define (walk x)
814 (cond ((not (pair? x)))
815 ((symbol? (car x))
816 (let* ((name (car x))
817 (old (or (hash-table-ref ##sys#line-number-database name) '())))
818 (unless (assq x old)
819 (hash-table-set! ##sys#line-number-database name (alist-cons x ln old)))
820 (mapupdate (cdr x)) ) )
821 (else (mapupdate x)) ) )
822 (walk exp) )
823
824
825(define-constant +default-argument-count-limit+ 99999)
826
827(define ##sys#check-syntax
828 (lambda (id exp pat #!optional culprit (se (##sys#current-environment)))
829
830 (define (test x pred msg)
831 (unless (pred x) (err msg)) )
832
833 (define (err msg)
834 (let* ([sexp ##sys#syntax-error-culprit]
835 [ln (get-line-number sexp)] )
836 (##sys#syntax-error-hook
837 (if ln
838 (string-append "(" ln ") in `" (symbol->string id) "' - " msg)
839 (string-append "in `" (symbol->string id) "' - " msg) )
840 exp) ) )
841
842 (define (lambda-list? x)
843 (or (##sys#extended-lambda-list? x)
844 (let loop ((x x))
845 (cond ((null? x))
846 ((symbol? x))
847 ((pair? x)
848 (let ((s (car x)))
849 (and (symbol? s)
850 (loop (cdr x)) ) ) )
851 (else #f) ) ) ) )
852
853 (define (variable? v)
854 (symbol? v))
855
856 (define (proper-list? x)
857 (let loop ((x x))
858 (cond ((eq? x '()))
859 ((pair? x) (loop (cdr x)))
860 (else #f) ) ) )
861
862 (when culprit (set! ##sys#syntax-error-culprit culprit))
863 (let walk ((x exp) (p pat))
864 (cond ((vector? p)
865 (let* ((p2 (vector-ref p 0))
866 (vlen (##sys#size p))
867 (min (if (fx> vlen 1)
868 (vector-ref p 1)
869 0) )
870 (max (cond ((eq? vlen 1) 1)
871 ((fx> vlen 2) (vector-ref p 2))
872 (else +default-argument-count-limit+) ) ) )
873 (do ((x x (cdr x))
874 (n 0 (fx+ n 1)) )
875 ((eq? x '())
876 (if (fx< n min)
877 (err "not enough arguments") ) )
878 (cond ((fx>= n max)
879 (err "too many arguments") )
880 ((not (pair? x))
881 (err "not a proper list") )
882 (else (walk (car x) p2) ) ) ) ) )
883 ((##sys#immediate? p)
884 (if (not (eq? p x)) (err "unexpected object")) )
885 ((symbol? p)
886 (case p
887 ((_) #t)
888 ((pair) (test x pair? "pair expected"))
889 ((variable) (test x variable? "identifier expected"))
890 ((symbol) (test x symbol? "symbol expected"))
891 ((list) (test x proper-list? "proper list expected"))
892 ((number) (test x number? "number expected"))
893 ((string) (test x string? "string expected"))
894 ((lambda-list) (test x lambda-list? "lambda-list expected"))
895 (else
896 (test
897 x
898 (lambda (y)
899 (let ((y2 (and (symbol? y) (lookup y se))))
900 (eq? (if (symbol? y2) y2 y) p)))
901 "missing keyword")) ) )
902 ((not (pair? p))
903 (err "incomplete form") )
904 ((not (pair? x)) (err "pair expected"))
905 (else
906 (walk (car x) (car p))
907 (walk (cdr x) (cdr p)) ) ) ) ) )
908
909
910;;; explicit/implicit-renaming transformer
911
912(define (make-er/ir-transformer handler explicit-renaming?)
913 (##sys#make-structure
914 'transformer
915 (lambda (form se dse)
916 (let ((renv '())) ; keep rename-environment for this expansion
917 (define (inherit-pair-line-numbers old new)
918 (and-let* ((name (car new))
919 ((symbol? name))
920 (ln (get-line-number old))
921 (cur (or (hash-table-ref ##sys#line-number-database name) '())) )
922 (unless (assq new cur)
923 (hash-table-set! ##sys#line-number-database name
924 (alist-weak-cons new ln cur))))
925 new)
926 (assert (list? se) "not a list" se) ;XXX remove later
927 (define (rename sym)
928 (cond ((pair? sym)
929 (inherit-pair-line-numbers sym (cons (rename (car sym)) (rename (cdr sym)))))
930 ((vector? sym)
931 (list->vector (rename (vector->list sym))))
932 ((not (symbol? sym)) sym)
933 ((assq sym renv) =>
934 (lambda (a)
935 (dd `(RENAME/RENV: ,sym --> ,(cdr a)))
936 (cdr a)))
937 (else
938 (let ((a (macro-alias sym se)))
939 (dd `(RENAME: ,sym --> ,a))
940 (set! renv (cons (cons sym a) renv))
941 a))))
942 (define (compare s1 s2)
943 (let ((result
944 (cond ((pair? s1)
945 (and (pair? s2)
946 (compare (car s1) (car s2))
947 (compare (cdr s1) (cdr s2))))
948 ((vector? s1)
949 (and (vector? s2)
950 (let ((len (vector-length s1)))
951 (and (fx= len (vector-length s2))
952 (do ((i 0 (fx+ i 1))
953 (f #t (compare (vector-ref s1 i) (vector-ref s2 i))))
954 ((or (fx>= i len) (not f)) f))))))
955 ((and (symbol? s1)
956 (symbol? s2))
957 (let ((ss1 (or (getp s1 '##core#macro-alias)
958 (lookup2 1 s1 dse)
959 s1) )
960 (ss2 (or (getp s2 '##core#macro-alias)
961 (lookup2 2 s2 dse)
962 s2) ) )
963 (cond ((symbol? ss1)
964 (cond ((symbol? ss2) (eq? ss1 ss2))
965 ((assq ss1 (##sys#macro-environment)) =>
966 (lambda (a) (eq? (cdr a) ss2)))
967 (else #f) ) )
968 ((symbol? ss2)
969 (cond ((assq ss2 (##sys#macro-environment)) =>
970 (lambda (a) (eq? ss1 (cdr a))))
971 (else #f)))
972 (else (eq? ss1 ss2)))))
973 (else (eq? s1 s2))) ) )
974 (dd `(COMPARE: ,s1 ,s2 --> ,result))
975 result))
976 (define (lookup2 n sym dse)
977 (let ((r (lookup sym dse)))
978 (dd " (lookup/DSE " (list n) ": " sym " --> "
979 (if (and r (pair? r))
980 '<macro>
981 r)
982 ")")
983 r))
984 (define (assq-reverse s l)
985 (cond
986 ((null? l) #f)
987 ((eq? (cdar l) s) (car l))
988 (else (assq-reverse s (cdr l)))))
989 (define (mirror-rename sym)
990 (cond ((pair? sym)
991 (inherit-pair-line-numbers
992 sym (cons (mirror-rename (car sym)) (mirror-rename (cdr sym)))))
993 ((vector? sym)
994 (list->vector (mirror-rename (vector->list sym))))
995 ((not (symbol? sym)) sym)
996 (else ; Code stolen from strip-syntax
997 (let ((renamed (lookup sym se) ) )
998 (cond ((assq-reverse sym renv) =>
999 (lambda (a)
1000 (dd "REVERSING RENAME: " sym " --> " (car a)) (car a)))
1001 ((not renamed)
1002 (dd "IMPLICITLY RENAMED: " sym) (rename sym))
1003 ((pair? renamed)
1004 (dd "MACRO: " sym) (rename sym))
1005 ((getp sym '##core#real-name) =>
1006 (lambda (name)
1007 (dd "STRIP SYNTAX ON " sym " ---> " name)
1008 name))
1009 ;; Rename builtin aliases so strip-syntax can still
1010 ;; access symbols as entered by the user
1011 (else (let ((implicitly-renamed (rename sym)))
1012 (dd "BUILTIN ALIAS: " sym " as " renamed
1013 " --> " implicitly-renamed)
1014 implicitly-renamed)))))))
1015 (if explicit-renaming?
1016 ;; Let the user handle renaming
1017 (handler form rename compare)
1018 ;; Implicit renaming:
1019 ;; Rename everything in the input first, feed it to the transformer
1020 ;; and then swap out all renamed identifiers by their non-renamed
1021 ;; versions, and vice versa. User can decide when to inject code
1022 ;; unhygienically this way.
1023 (mirror-rename (handler (rename form) rename compare)) ) ) )))
1024
1025(define (er-macro-transformer handler) (make-er/ir-transformer handler #t))
1026(define (ir-macro-transformer handler) (make-er/ir-transformer handler #f))
1027
1028(define ##sys#er-transformer er-macro-transformer)
1029(define ##sys#ir-transformer ir-macro-transformer)
1030
1031
1032;; Expose some internals for use in core.scm and chicken-syntax.scm:
1033
1034(define chicken.syntax#define-definition define-definition)
1035(define chicken.syntax#define-syntax-definition define-syntax-definition)
1036(define chicken.syntax#define-values-definition define-values-definition)
1037(define chicken.syntax#expansion-result-hook expansion-result-hook)
1038
1039) ; chicken.syntax module
1040
1041(import scheme chicken.base chicken.blob chicken.fixnum)
1042(import chicken.syntax chicken.internal chicken.platform)
1043
1044;;; Macro definitions:
1045
1046(##sys#extend-macro-environment
1047 'import-syntax '()
1048 (##sys#er-transformer
1049 (cut ##sys#expand-import <> <> <>
1050 ##sys#current-environment ##sys#macro-environment
1051 #f #f 'import-syntax)))
1052
1053(##sys#extend-macro-environment
1054 'import-syntax-for-syntax '()
1055 (##sys#er-transformer
1056 (cut ##sys#expand-import <> <> <>
1057 ##sys#current-meta-environment ##sys#meta-macro-environment
1058 #t #f 'import-syntax-for-syntax)))
1059
1060(set! chicken.syntax#import-definition
1061 (##sys#extend-macro-environment
1062 'import '()
1063 (##sys#er-transformer
1064 (lambda (x r c)
1065 `(##core#begin
1066 ,@(map (lambda (x)
1067 (let-values (((name lib spec v s i) (##sys#decompose-import x r c 'import))
1068 ((mod) (##sys#current-module)))
1069 (when (and mod (eq? name (##sys#module-name mod)))
1070 (##sys#syntax-error-hook
1071 'import "cannot import from module currently being defined" name))
1072 (if (not spec)
1073 (##sys#syntax-error-hook
1074 'import "cannot import from undefined module" name)
1075 (##sys#import
1076 spec v s i
1077 ##sys#current-environment ##sys#macro-environment #f #f 'import))
1078 (if (not lib)
1079 '(##core#undefined)
1080 `(##core#require ,lib ,name))))
1081 (cdr x)))))))
1082
1083(##sys#extend-macro-environment
1084 'import-for-syntax '()
1085 (##sys#er-transformer
1086 (lambda (x r c)
1087 (##sys#register-meta-expression `(,(r 'import) ,@(cdr x)))
1088 `(##core#elaborationtimeonly (,(r 'import) ,@(cdr x))))))
1089
1090
1091(##sys#extend-macro-environment
1092 'cond-expand
1093 '()
1094 (##sys#er-transformer
1095 (lambda (form r c)
1096 (let ((clauses (cdr form)))
1097 (define (err x)
1098 (##sys#error "syntax error in `cond-expand' form"
1099 x
1100 (cons 'cond-expand clauses)))
1101 (define (test fx)
1102 (cond ((symbol? fx) (feature? (strip-syntax fx)))
1103 ((not (pair? fx)) (err fx))
1104 (else
1105 (let ((head (car fx))
1106 (rest (cdr fx)))
1107 (case (strip-syntax head)
1108 ((and)
1109 (or (eq? rest '())
1110 (if (pair? rest)
1111 (and (test (car rest))
1112 (test `(and ,@(cdr rest))))
1113 (err fx))))
1114 ((or)
1115 (and (not (eq? rest '()))
1116 (if (pair? rest)
1117 (or (test (car rest))
1118 (test `(or ,@(cdr rest))))
1119 (err fx))))
1120 ((not) (not (test (cadr fx))))
1121 (else (err fx)))))))
1122 (let expand ((cls clauses))
1123 (cond ((eq? cls '())
1124 (##sys#apply
1125 ##sys#error "no matching clause in `cond-expand' form"
1126 (map (lambda (x) (car x)) clauses)))
1127 ((not (pair? cls)) (err cls))
1128 (else
1129 (let ((clause (car cls))
1130 (rclauses (cdr cls)))
1131 (if (not (pair? clause))
1132 (err clause)
1133 (let ((id (car clause)))
1134 (cond ((eq? (strip-syntax id) 'else)
1135 (let ((rest (cdr clause)))
1136 (if (eq? rest '())
1137 '(##core#undefined)
1138 `(##core#begin ,@rest))))
1139 ((test id) `(##core#begin ,@(cdr clause)))
1140 (else (expand rclauses)))))))))))))
1141
1142;; The "initial" macro environment, containing only import forms and
1143;; cond-expand. TODO: Eventually, cond-expand should move to the
1144;; (chicken base) module to match r7rs. Keeping it in the initial env
1145;; makes it a whole lot easier to write portable CHICKEN 4 & 5 code.
1146(define ##sys#initial-macro-environment (##sys#macro-environment))
1147
1148(##sys#extend-macro-environment
1149 'module '()
1150 (##sys#er-transformer
1151 (lambda (x r c)
1152 (##sys#check-syntax 'module x '(_ _ _ . #(_ 0)))
1153 (let ((len (length x))
1154 (name (library-id (cadr x))))
1155 ;; We strip syntax here instead of doing a hygienic comparison
1156 ;; to "=". This is a tradeoff; either we do this, or we must
1157 ;; include a mapping of (= . scheme#=) in our syntax env. In
1158 ;; the initial environment, = is bound to scheme#=, but when
1159 ;; using -explicit-use that's not the case. Doing an unhygienic
1160 ;; comparison ensures module will work in both cases.
1161 (cond ((and (fx>= len 4) (eq? '= (strip-syntax (caddr x))))
1162 (let* ((x (strip-syntax x))
1163 (app (cadddr x)))
1164 (cond ((fx> len 4)
1165 ;; feature suggested by syn:
1166 ;;
1167 ;; (module NAME = FUNCTORNAME BODY ...)
1168 ;; ~>
1169 ;; (begin
1170 ;; (module _NAME * BODY ...)
1171 ;; (module NAME = (FUNCTORNAME _NAME)))
1172 ;;
1173 ;; - the use of "_NAME" is a bit stupid, but it must be
1174 ;; externally visible to generate an import library from
1175 ;; and compiling "NAME" separately may need an import-lib
1176 ;; for stuff in "BODY" (say, syntax needed by syntax exported
1177 ;; from the functor, or something like this...)
1178 (let ((mtmp (string->symbol
1179 (##sys#string-append
1180 "_"
1181 (symbol->string name))))
1182 (%module (r 'module)))
1183 `(##core#begin
1184 (,%module ,mtmp * ,@(cddddr x))
1185 (,%module ,name = (,app ,mtmp)))))
1186 (else
1187 (##sys#check-syntax
1188 'module x '(_ _ _ (_ . #(_ 0))))
1189 (##sys#instantiate-functor
1190 name
1191 (library-id (car app))
1192 (cdr app)))))) ; functor arguments
1193 (else
1194 ;;XXX use module name in "loc" argument?
1195 (let ((exports (##sys#validate-exports (strip-syntax (caddr x)) 'module)))
1196 `(##core#module
1197 ,name
1198 ,(if (eq? '* exports)
1199 #t
1200 exports)
1201 ,@(let ((body (cdddr x)))
1202 (if (and (pair? body)
1203 (null? (cdr body))
1204 (string? (car body)))
1205 `((##core#include ,(car body) ,##sys#current-source-filename))
1206 body))))))))))
1207
1208(##sys#extend-macro-environment
1209 'export '()
1210 (##sys#er-transformer
1211 (lambda (x r c)
1212 (let ((exps (##sys#validate-exports (strip-syntax (cdr x)) 'export))
1213 (mod (##sys#current-module)))
1214 (when mod
1215 (##sys#add-to-export-list mod exps))
1216 '(##core#undefined)))))
1217
1218(##sys#extend-macro-environment
1219 'export/rename '()
1220 (##sys#er-transformer
1221 (lambda (x r c)
1222 (let ((exps (map (lambda (ren)
1223 (if (and (pair? ren)
1224 (symbol? (car ren))
1225 (pair? (cdr ren))
1226 (symbol? (cadr ren))
1227 (null? (cddr ren)))
1228 (cons (car ren) (cadr ren))
1229 (##sys#syntax-error-hook "invalid item in export rename list"
1230 ren)))
1231 (strip-syntax (cdr x))))
1232 (mod (##sys#current-module)))
1233 (when mod
1234 (##sys#add-to-export/rename-list mod exps))
1235 '(##core#undefined)))))
1236
1237(##sys#extend-macro-environment
1238 'reexport '()
1239 (##sys#er-transformer
1240 (cut ##sys#expand-import <> <> <>
1241 ##sys#current-environment ##sys#macro-environment
1242 #f #t 'reexport)))
1243
1244;;; functor definition
1245
1246(##sys#extend-macro-environment
1247 'functor '()
1248 (##sys#er-transformer
1249 (lambda (x r c)
1250 (##sys#check-syntax 'functor x '(_ (_ . #((_ _) 0)) _ . _))
1251 (let* ((x (strip-syntax x))
1252 (head (cadr x))
1253 (name (car head))
1254 (args (cdr head))
1255 (exps (caddr x))
1256 (body (cdddr x))
1257 (registration
1258 `(##sys#register-functor
1259 (##core#quote ,(library-id name))
1260 (##core#quote
1261 ,(map (lambda (arg)
1262 (let ((argname (car arg))
1263 (exps (##sys#validate-exports (cadr arg) 'functor)))
1264 (unless (or (symbol? argname)
1265 (and (list? argname)
1266 (= 2 (length argname))
1267 (symbol? (car argname))
1268 (valid-library-specifier? (cadr argname))))
1269 (##sys#syntax-error-hook "invalid functor argument" name arg))
1270 (cons argname exps)))
1271 args))
1272 (##core#quote ,(##sys#validate-exports exps 'functor))
1273 (##core#quote ,body))))
1274 `(##core#module ,(library-id name)
1275 #t
1276 (import scheme chicken.syntax) ;; TODO: Is this correct?
1277 (begin-for-syntax ,registration))))))
1278
1279;;; interface definition
1280
1281(##sys#extend-macro-environment
1282 'define-interface '()
1283 (##sys#er-transformer
1284 (lambda (x r c)
1285 (##sys#check-syntax 'define-interface x '(_ variable _))
1286 (let ((name (strip-syntax (cadr x))))
1287 (when (eq? '* name)
1288 (syntax-error-hook
1289 'define-interface "`*' is not allowed as a name for an interface"))
1290 `(##core#elaborationtimeonly
1291 (##sys#put/restore!
1292 (##core#quote ,name)
1293 (##core#quote ##core#interface)
1294 (##core#quote
1295 ,(let ((exps (strip-syntax (caddr x))))
1296 (cond ((eq? '* exps) '*)
1297 ((symbol? exps) `(#:interface ,exps))
1298 ((list? exps)
1299 (##sys#validate-exports exps 'define-interface))
1300 (else
1301 (syntax-error-hook
1302 'define-interface "invalid exports" (caddr x))))))))))))
1303
1304(##sys#extend-macro-environment
1305 'current-module '()
1306 (##sys#er-transformer
1307 (lambda (x r c)
1308 (##sys#check-syntax 'current-module x '(_))
1309 (and-let* ((mod (##sys#current-module)))
1310 `(##core#quote ,(##sys#module-name mod))))))
1311
1312;; The chicken.module syntax environment
1313(define ##sys#chicken.module-macro-environment (##sys#macro-environment))
1314
1315(set! ##sys#scheme-macro-environment
1316 (let ((me0 (##sys#macro-environment)))
1317
1318(##sys#extend-macro-environment
1319 'lambda
1320 '()
1321 (##sys#er-transformer
1322 (lambda (x r c)
1323 (##sys#check-syntax 'lambda x '(_ lambda-list . #(_ 1)))
1324 `(##core#lambda ,@(cdr x)))))
1325
1326(##sys#extend-macro-environment
1327 'quote
1328 '()
1329 (##sys#er-transformer
1330 (lambda (x r c)
1331 (##sys#check-syntax 'quote x '(_ _))
1332 `(##core#quote ,(cadr x)))))
1333
1334(##sys#extend-macro-environment
1335 'if
1336 '()
1337 (##sys#er-transformer
1338 (lambda (x r c)
1339 (##sys#check-syntax 'if x '(_ _ _ . #(_)))
1340 `(##core#if ,@(cdr x)))))
1341
1342(##sys#extend-macro-environment
1343 'begin
1344 '()
1345 (##sys#er-transformer
1346 (lambda (x r c)
1347 (##sys#check-syntax 'begin x '(_ . #(_ 0)))
1348 `(##core#begin ,@(cdr x)))))
1349
1350(set! chicken.syntax#define-definition
1351 (##sys#extend-macro-environment
1352 'define
1353 '()
1354 (##sys#er-transformer
1355 (lambda (x r c)
1356 (##sys#check-syntax 'define x '(_ . #(_ 1)))
1357 (let loop ((form x))
1358 (let ((head (cadr form))
1359 (body (cddr form)) )
1360 (cond ((not (pair? head))
1361 (##sys#check-syntax 'define form '(_ variable . #(_ 0 1)))
1362 (let ((name (or (getp head '##core#macro-alias) head)))
1363 (##sys#register-export name (##sys#current-module)))
1364 (when (c (r 'define) head)
1365 (chicken.syntax#defjam-error x))
1366 `(##core#begin
1367 (##core#ensure-toplevel-definition ,head)
1368 (##core#set!
1369 ,head
1370 ,(if (pair? body) (car body) '(##core#undefined)))))
1371 ((pair? (car head))
1372 (##sys#check-syntax 'define form '(_ (_ . lambda-list) . #(_ 1)))
1373 (loop (chicken.syntax#expand-curried-define head body '()))) ;XXX '() should be se
1374 (else
1375 (##sys#check-syntax 'define form '(_ (variable . lambda-list) . #(_ 1)))
1376 (loop (list (car x) (car head) `(##core#lambda ,(cdr head) ,@body)))))))))))
1377
1378(set! chicken.syntax#define-syntax-definition
1379 (##sys#extend-macro-environment
1380 'define-syntax
1381 '()
1382 (##sys#er-transformer
1383 (lambda (form r c)
1384 (##sys#check-syntax 'define-syntax form '(_ variable _))
1385 (let ((head (cadr form))
1386 (body (caddr form)))
1387 (let ((name (or (getp head '##core#macro-alias) head)))
1388 (##sys#register-export name (##sys#current-module)))
1389 (when (c (r 'define-syntax) head)
1390 (chicken.syntax#defjam-error form))
1391 `(##core#define-syntax ,head ,body))))))
1392
1393(##sys#extend-macro-environment
1394 'let
1395 '()
1396 (##sys#er-transformer
1397 (lambda (x r c)
1398 (cond ((and (pair? (cdr x)) (symbol? (cadr x)))
1399 (##sys#check-syntax 'let x '(_ variable #((variable _) 0) . #(_ 1)))
1400 (check-for-multiple-bindings (caddr x) x "let"))
1401 (else
1402 (##sys#check-syntax 'let x '(_ #((variable _) 0) . #(_ 1)))
1403 (check-for-multiple-bindings (cadr x) x "let")))
1404 `(##core#let ,@(cdr x)))))
1405
1406(##sys#extend-macro-environment
1407 'letrec
1408 '()
1409 (##sys#er-transformer
1410 (lambda (x r c)
1411 (##sys#check-syntax 'letrec x '(_ #((variable _) 0) . #(_ 1)))
1412 (check-for-multiple-bindings (cadr x) x "letrec")
1413 `(##core#letrec ,@(cdr x)))))
1414
1415(##sys#extend-macro-environment
1416 'let-syntax
1417 '()
1418 (##sys#er-transformer
1419 (lambda (x r c)
1420 (##sys#check-syntax 'let-syntax x '(_ #((variable _) 0) . #(_ 1)))
1421 (check-for-multiple-bindings (cadr x) x "let-syntax")
1422 `(##core#let-syntax ,@(cdr x)))))
1423
1424(##sys#extend-macro-environment
1425 'letrec-syntax
1426 '()
1427 (##sys#er-transformer
1428 (lambda (x r c)
1429 (##sys#check-syntax 'letrec-syntax x '(_ #((variable _) 0) . #(_ 1)))
1430 (check-for-multiple-bindings (cadr x) x "letrec-syntax")
1431 `(##core#letrec-syntax ,@(cdr x)))))
1432
1433(##sys#extend-macro-environment
1434 'set!
1435 '()
1436 (##sys#er-transformer
1437 (lambda (x r c)
1438 (##sys#check-syntax 'set! x '(_ _ _))
1439 (let ((dest (cadr x))
1440 (val (caddr x)))
1441 (cond ((pair? dest)
1442 `((##sys#setter ,(car dest)) ,@(cdr dest) ,val))
1443 (else `(##core#set! ,dest ,val)))))))
1444
1445(##sys#extend-macro-environment
1446 'and
1447 '()
1448 (##sys#er-transformer
1449 (lambda (form r c)
1450 (let ((body (cdr form)))
1451 (if (null? body)
1452 #t
1453 (let ((rbody (cdr body))
1454 (hbody (car body)) )
1455 (if (null? rbody)
1456 hbody
1457 `(##core#if ,hbody (,(r 'and) ,@rbody) #f) ) ) ) ) ) ) )
1458
1459(##sys#extend-macro-environment
1460 'or
1461 '()
1462 (##sys#er-transformer
1463 (lambda (form r c)
1464 (let ((body (cdr form)))
1465 (if (null? body)
1466 #f
1467 (let ((rbody (cdr body))
1468 (hbody (car body)))
1469 (if (null? rbody)
1470 hbody
1471 (let ((tmp (r 'tmp)))
1472 `(##core#let ((,tmp ,hbody))
1473 (##core#if ,tmp ,tmp (,(r 'or) ,@rbody)) ) ) ) ) ) ) ) ) )
1474
1475(##sys#extend-macro-environment
1476 'cond
1477 '()
1478 (##sys#er-transformer
1479 (lambda (form r c)
1480 (let ((body (cdr form))
1481 (%=> (r '=>))
1482 (%or (r 'or))
1483 (%else (r 'else)))
1484 (let expand ((clauses body) (else? #f))
1485 (if (not (pair? clauses))
1486 '(##core#undefined)
1487 (let ((clause (car clauses))
1488 (rclauses (cdr clauses)) )
1489 (##sys#check-syntax 'cond clause '#(_ 1))
1490 (cond (else?
1491 (##sys#warn
1492 (chicken.format#sprintf "clause following `~S' clause in `cond'" else?)
1493 (strip-syntax clause))
1494 (expand rclauses else?)
1495 '(##core#begin))
1496 ((or (c %else (car clause))
1497 (eq? #t (car clause))
1498 ;; Like "constant?" from support.scm
1499 (number? (car clause))
1500 (char? (car clause))
1501 (string? (car clause))
1502 (eof-object? (car clause))
1503 ;; TODO: Remove once we have a bootstrapping libchicken with bwp-object?
1504 (##core#inline "C_bwpp" (car clause))
1505 #;(bwp-object? (car clause))
1506 (blob? (car clause))
1507 (vector? (car clause))
1508 (##sys#srfi-4-vector? (car clause))
1509 (and (pair? (car clause))
1510 (c (r 'quote) (caar clause))))
1511 (expand rclauses (strip-syntax (car clause)))
1512 (cond ((and (fx= (length clause) 3)
1513 (c %=> (cadr clause)))
1514 `(,(caddr clause) ,(car clause)))
1515 ((pair? (cdr clause))
1516 `(##core#begin ,@(cdr clause)))
1517 ((c %else (car clause))
1518 `(##core#undefined))
1519 (else (car clause))))
1520 ((null? (cdr clause))
1521 `(,%or ,(car clause) ,(expand rclauses #f)))
1522 ((and (fx= (length clause) 3)
1523 (c %=> (cadr clause)))
1524 (let ((tmp (r 'tmp)))
1525 `(##core#let ((,tmp ,(car clause)))
1526 (##core#if ,tmp
1527 (,(caddr clause) ,tmp)
1528 ,(expand rclauses #f) ) ) ) )
1529 ((and (fx= (length clause) 4)
1530 (c %=> (caddr clause)))
1531 (let ((tmp (r 'tmp)))
1532 `(##sys#call-with-values
1533 (##core#lambda () ,(car clause))
1534 (##core#lambda
1535 ,tmp
1536 (if (##sys#apply ,(cadr clause) ,tmp)
1537 (##sys#apply ,(cadddr clause) ,tmp)
1538 ,(expand rclauses #f) ) ) ) ) )
1539 (else `(##core#if ,(car clause)
1540 (##core#begin ,@(cdr clause))
1541 ,(expand rclauses #f) ) ) ) ) ) ) ) ) ) )
1542
1543(##sys#extend-macro-environment
1544 'case
1545 '((eqv? . scheme#eqv?))
1546 (##sys#er-transformer
1547 (lambda (form r c)
1548 (##sys#check-syntax 'case form '(_ _ . #(_ 0)))
1549 (let ((exp (cadr form))
1550 (body (cddr form)) )
1551 (let ((tmp (r 'tmp))
1552 (%or (r 'or))
1553 (%=> (r '=>))
1554 (%eqv? (r 'eqv?))
1555 (%else (r 'else)))
1556 `(let ((,tmp ,exp))
1557 ,(let expand ((clauses body) (else? #f))
1558 (if (not (pair? clauses))
1559 '(##core#undefined)
1560 (let ((clause (car clauses))
1561 (rclauses (cdr clauses)) )
1562 (##sys#check-syntax 'case clause '#(_ 1))
1563 (cond (else?
1564 (##sys#warn
1565 "clause following `else' clause in `case'"
1566 (strip-syntax clause))
1567 (expand rclauses #t)
1568 '(##core#begin))
1569 ((c %else (car clause))
1570 (expand rclauses #t)
1571 (cond ((null? (cdr clause))
1572 `(##core#undefined))
1573 ((and (fx= (length clause) 3) ; (else => expr)
1574 (c %=> (cadr clause)))
1575 `(,(caddr clause) ,tmp))
1576 (else
1577 `(##core#begin ,@(cdr clause)))))
1578 (else
1579 `(##core#if (,%or ,@(##sys#map
1580 (lambda (x) `(,%eqv? ,tmp ',x))
1581 (car clause)))
1582 ,(if (and (fx= (length clause) 3) ; ((...) => expr)
1583 (c %=> (cadr clause)))
1584 `(,(caddr clause) ,tmp)
1585 `(##core#begin ,@(cdr clause)))
1586 ,(expand rclauses #f) ) ) ) ) ) ) ) ) ) ) ) )
1587
1588(##sys#extend-macro-environment
1589 'let*
1590 '()
1591 (##sys#er-transformer
1592 (lambda (form r c)
1593 (##sys#check-syntax 'let* form '(_ #((variable _) 0) . #(_ 1)))
1594 (let ((bindings (cadr form))
1595 (body (cddr form)) )
1596 (let expand ((bs bindings))
1597 (if (eq? bs '())
1598 `(##core#let () ,@body)
1599 `(##core#let (,(car bs)) ,(expand (cdr bs))) ) ) ) ) ) )
1600
1601(##sys#extend-macro-environment
1602 'do
1603 '()
1604 (##sys#er-transformer
1605 (lambda (form r c)
1606 (##sys#check-syntax 'do form '(_ #((variable _ . #(_)) 0) . #(_ 1)))
1607 (let ((bindings (cadr form))
1608 (test (caddr form))
1609 (body (cdddr form))
1610 (dovar (r 'doloop)))
1611 `(##core#let
1612 ,dovar
1613 ,(##sys#map (lambda (b) (list (car b) (car (cdr b)))) bindings)
1614 (##core#if ,(car test)
1615 ,(let ((tbody (cdr test)))
1616 (if (eq? tbody '())
1617 '(##core#undefined)
1618 `(##core#begin ,@tbody) ) )
1619 (##core#begin
1620 ,(if (eq? body '())
1621 '(##core#undefined)
1622 `(##core#let () ,@body) )
1623 (##core#app
1624 ,dovar ,@(##sys#map (lambda (b)
1625 (if (eq? (cdr (cdr b)) '())
1626 (car b)
1627 (car (cdr (cdr b))) ) )
1628 bindings) ) ) ) ) ) ) ) )
1629
1630(##sys#extend-macro-environment
1631 'quasiquote
1632 '()
1633 (##sys#er-transformer
1634 (lambda (form r c)
1635 (let ((%quasiquote (r 'quasiquote))
1636 (%unquote (r 'unquote))
1637 (%unquote-splicing (r 'unquote-splicing)))
1638 (define (walk x n) (simplify (walk1 x n)))
1639 (define (walk1 x n)
1640 (cond ((vector? x)
1641 `(##sys#list->vector ,(walk (vector->list x) n)) )
1642 ((not (pair? x)) `(##core#quote ,x))
1643 (else
1644 (let ((head (car x))
1645 (tail (cdr x)))
1646 (cond ((c %unquote head)
1647 (cond ((eq? n 0)
1648 (##sys#check-syntax 'unquote x '(_ _))
1649 (car tail))
1650 (else (list '##sys#cons `(##core#quote ,%unquote)
1651 (walk tail (fx- n 1)) ) )))
1652 ((c %quasiquote head)
1653 (list '##sys#cons `(##core#quote ,%quasiquote)
1654 (walk tail (fx+ n 1)) ) )
1655 ((and (pair? head) (c %unquote-splicing (car head)))
1656 (cond ((eq? n 0)
1657 (##sys#check-syntax 'unquote-splicing head '(_ _))
1658 `(##sys#append ,(cadr head) ,(walk tail n)))
1659 (else
1660 `(##sys#cons
1661 (##sys#cons (##core#quote ,%unquote-splicing)
1662 ,(walk (cdr head) (fx- n 1)) )
1663 ,(walk tail n)))))
1664 (else
1665 `(##sys#cons ,(walk head n) ,(walk tail n)) ) ) ) ) ) )
1666 (define (simplify x)
1667 (cond ((chicken.syntax#match-expression x '(##sys#cons a (##core#quote ())) '(a))
1668 => (lambda (env) (simplify `(##sys#list ,(cdr (assq 'a env))))) )
1669 ((chicken.syntax#match-expression x '(##sys#cons a (##sys#list . b)) '(a b))
1670 => (lambda (env)
1671 (let ((bxs (assq 'b env)))
1672 (if (fx< (length bxs) 32)
1673 (simplify `(##sys#list ,(cdr (assq 'a env))
1674 ,@(cdr bxs) ) )
1675 x) ) ) )
1676 ((chicken.syntax#match-expression x '(##sys#append a (##core#quote ())) '(a))
1677 => (lambda (env) (cdr (assq 'a env))) )
1678 (else x) ) )
1679 (##sys#check-syntax 'quasiquote form '(_ _))
1680 (walk (cadr form) 0) ) ) ) )
1681
1682(##sys#extend-macro-environment
1683 'delay
1684 '()
1685 (##sys#er-transformer
1686 (lambda (form r c)
1687 (##sys#check-syntax 'delay form '(_ _))
1688 `(,(r 'delay-force)
1689 (##sys#make-promise
1690 (##sys#call-with-values (##core#lambda () ,(cadr form)) ##sys#list))))))
1691
1692;;; syntax-rules
1693
1694(include "synrules.scm")
1695
1696(macro-subset me0)))
1697
1698;;; the base macro environment (the old "scheme", essentially)
1699;;; TODO: Remove this
1700
1701(define ##sys#default-macro-environment
1702 (fixup-macro-environment (##sys#macro-environment)))
1703
1704(define ##sys#meta-macro-environment (make-parameter (##sys#macro-environment)))
1705
1706;; register features
1707
1708(register-feature! 'srfi-0 'srfi-46 'srfi-61 'srfi-87)