~ chicken-core (master) /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 er-macro-transformer
43 ir-macro-transformer)
44
45(import scheme
46 chicken.base
47 chicken.condition
48 chicken.fixnum
49 chicken.internal
50 chicken.keyword
51 chicken.platform
52 chicken.string)
53(import (only (scheme base) make-parameter open-output-string get-output-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#symbol->string/shared name)
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
233 (string-append
234 "syntax transformer for `" (##sys#symbol->string/shared 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 (if (pair? mdef)
250 (values
251 ;; force ref. opaqueness by passing dynamic se [what does this comment mean? I forgot ...]
252 (call-handler head (cadr mdef) exp (car mdef) #f)
253 #t)
254 (values exp #f)) )
255 (let loop ((exp exp))
256 (if (pair? exp)
257 (let ((head (car exp))
258 (body (cdr exp)) )
259 (if (symbol? head)
260 (let ((head2 (or (lookup head dse) head)))
261 (unless (pair? head2)
262 (set! head2 (or (lookup head2 (##sys#macro-environment)) head2)) )
263 (cond ((and (pair? head2)
264 (eq? (##sys#get head '##sys#override) 'value))
265 (values exp #f))
266 ((eq? head2 '##core#let)
267 (##sys#check-syntax 'let body '#(_ 2) #f dse)
268 (let ((bindings (car body)))
269 (cond ((symbol? bindings) ; expand named let
270 (##sys#check-syntax 'let body '(_ #((variable _) 0) . #(_ 1)) #f dse)
271 (let ([bs (cadr body)])
272 (values
273 `(##core#app
274 (##core#letrec*
275 ([,bindings
276 (##core#loop-lambda
277 ,(map (lambda (b) (car b)) bs) ,@(cddr body))])
278 ,bindings)
279 ,@(##sys#map cadr bs) )
280 #t) ) )
281 (else (values exp #f)) ) ) )
282 ((and cs? (symbol? head2) (getp head2 '##compiler#compiler-syntax)) =>
283 (lambda (cs)
284 (let ((result (call-handler head (car cs) exp (cdr cs) #t)))
285 (cond ((eq? result exp) (expand head exp head2))
286 (else
287 (when ##sys#compiler-syntax-hook
288 (##sys#compiler-syntax-hook head2 result))
289 (loop result))))))
290 (else (expand head exp head2)) ) )
291 (values exp #f) ) )
292 (values exp #f) ) ) )
293
294(define ##sys#compiler-syntax-hook #f)
295(define ##sys#enable-runtime-macros #f)
296(define expansion-result-hook (lambda (input output) output))
297
298
299;;; User-level macroexpansion
300
301(define (expand exp #!optional (se (##sys#current-environment)) cs?)
302 (let loop ((exp exp))
303 (let-values (((exp2 m) (##sys#expand-0 exp se cs?)))
304 (if m
305 (loop exp2)
306 exp2) ) ) )
307
308
309;;; Extended (DSSSL-style) lambda lists
310;
311; Assumptions:
312;
313; 1) #!rest must come before #!key
314; 2) default values may refer to earlier variables
315; 3) optional/key args may be either variable or (variable default)
316; 4) an argument marker may not be specified more than once
317; 5) no special handling of extra keywords (no error)
318; 6) default value of optional/key args is #f
319; 7) mixing with dotted list syntax is allowed
320
321(define (##sys#extended-lambda-list? llist)
322 (let loop ([llist llist])
323 (and (pair? llist)
324 (case (##sys#slot llist 0)
325 [(#!rest #!optional #!key) #t]
326 [else (loop (cdr llist))] ) ) ) )
327
328(define ##sys#expand-extended-lambda-list
329 (let ((reverse reverse))
330 (lambda (llist0 body errh se)
331 (define (err msg) (errh msg llist0))
332 (define (->keyword s) (string->keyword (##sys#symbol->string/shared s)))
333 (let ((rvar #f)
334 (hasrest #f)
335 ;; These might not exist in se, use default or chicken env:
336 (%let* (macro-alias 'let* ##sys#default-macro-environment))
337 (%lambda '##core#lambda)
338 (%opt (macro-alias 'optional ##sys#chicken.base-macro-environment))
339 (%let-optionals* (macro-alias 'let-optionals* ##sys#chicken.base-macro-environment))
340 (%let '##core#let))
341 (let loop ([mode 0] ; req=0, opt=1, rest=2, key=3, end=4
342 [req '()]
343 [opt '()]
344 [key '()]
345 [llist llist0] )
346 (cond [(null? llist)
347 (values
348 (if rvar (##sys#append (reverse req) rvar) (reverse req))
349 (let ([body
350 (if (null? key)
351 body
352 `((,%let*
353 ,(map (lambda (k)
354 (let ((s (car k)))
355 `(,s (##sys#get-keyword
356 (##core#quote ,(->keyword (strip-syntax s))) ,(or hasrest rvar)
357 ,@(if (pair? (cdr k))
358 `((,%lambda () ,@(cdr k)))
359 '())))))
360 (reverse key) )
361 ,@body) ) ) ] )
362 (cond [(null? opt) body]
363 [(and (not hasrest) (null? key) (null? (cdr opt)))
364 `((,%let
365 ([,(caar opt) (,%opt ,rvar ,(cadar opt))])
366 ,@body) ) ]
367 [(and (not hasrest) (null? key))
368 `((,%let-optionals*
369 ,rvar ,(reverse opt) ,@body))]
370 [else
371 `((,%let-optionals*
372 ,rvar ,(##sys#append (reverse opt) (list (or hasrest rvar)))
373 ,@body))] ) ) ) ]
374 [(symbol? llist)
375 (if (fx> mode 2)
376 (err "rest argument list specified more than once")
377 (begin
378 (unless rvar (set! rvar llist))
379 (set! hasrest llist)
380 (loop 4 req opt '() '()) ) ) ]
381 [(not (pair? llist))
382 (err "invalid lambda list syntax") ]
383 [else
384 (let* ((var (car llist))
385 (x (or (and (symbol? var) (not (eq? 3 mode)) (lookup var se)) var))
386 (r (cdr llist)))
387 (case x
388 [(#!optional)
389 (unless rvar (set! rvar (macro-alias 'rest se)))
390 (if (eq? mode 0)
391 (loop 1 req '() '() r)
392 (err "`#!optional' argument marker in wrong context") ) ]
393 [(#!rest)
394 (if (fx<= mode 1)
395 (if (and (pair? r) (symbol? (car r)))
396 (begin
397 (if (not rvar) (set! rvar (car r)))
398 (set! hasrest (car r))
399 (loop 2 req opt '() (cdr r)) )
400 (err "invalid syntax of `#!rest' argument") )
401 (err "`#!rest' argument marker in wrong context") ) ]
402 [(#!key)
403 (if (not rvar) (set! rvar (macro-alias 'rest se)))
404 (if (fx<= mode 2)
405 (loop 3 req opt '() r)
406 (err "`#!key' argument marker in wrong context") ) ]
407 [else
408 (cond [(symbol? var)
409 (case mode
410 [(0) (loop 0 (cons var req) '() '() r)]
411 [(1) (loop 1 req (cons (list var #f) opt) '() r)]
412 [(2) (err "invalid lambda list syntax after `#!rest' marker")]
413 [else (loop 3 req opt (cons (list var) key) r)] ) ]
414 [(and (list? var) (eq? 2 (length var)) (symbol? (car var)))
415 (case mode
416 [(0) (err "invalid required argument syntax")]
417 [(1) (loop 1 req (cons var opt) '() r)]
418 [(2) (err "invalid lambda list syntax after `#!rest' marker")]
419 [else (loop 3 req opt (cons var key) r)] ) ]
420 [else (err "invalid lambda list syntax")] ) ] ) ) ] ) ) ) ) ) )
421
422
423;;; Error message for redefinition of currently used defining form
424;
425; (i.e.`"(define define ...)")
426
427(define (defjam-error form)
428 (##sys#syntax-error
429 "redefinition of currently used defining form" ; help me find something better
430 form))
431
432;;; Expansion of multiple values assignments.
433;
434; Given a lambda list and a multi-valued expression, returns a form that
435; will `set!` each variable to its corresponding value in order.
436
437(define (##sys#expand-multiple-values-assignment formals expr)
438 (##sys#decompose-lambda-list
439 formals
440 (lambda (vars argc rest)
441 (let ((aliases (if (symbol? formals) '() (map gensym formals)))
442 (rest-alias (if (not rest) '() (gensym rest))))
443 `(##sys#call-with-values
444 (##core#lambda () ,expr)
445 (##core#lambda
446 ,(append aliases rest-alias)
447 ,@(map (lambda (v a) `(##core#set! ,v ,a)) vars aliases)
448 ,@(cond
449 ((null? formals) '((##core#undefined)))
450 ((null? rest-alias) '())
451 (else `((##core#set! ,rest ,rest-alias))))))))))
452
453;;; Expansion of bodies (and internal definitions)
454;
455; This code is disgustingly complex.
456
457(define define-definition)
458(define define-syntax-definition)
459(define define-values-definition)
460(define import-definition)
461
462(define ##sys#canonicalize-body
463 (lambda (body #!optional (se (##sys#current-environment)) cs?)
464 (define (comp s id)
465 (let ((f (or (lookup id se)
466 (lookup id (##sys#macro-environment)))))
467 (and (or (not (symbol? f))
468 (not (eq? (##sys#get id '##sys#override) 'value)))
469 (or (eq? f s) (eq? s id)))))
470 (define (comp-def def)
471 (lambda (id)
472 (let repeat ((id id))
473 (let ((f (or (lookup id se)
474 (lookup id (##sys#macro-environment)))))
475 (and (or (not (symbol? f))
476 (not (eq? (##sys#get id '##sys#override) 'value)))
477 (or (eq? f def)
478 (and (symbol? f)
479 (not (eq? f id))
480 (repeat f))))))))
481 (define comp-define (comp-def define-definition))
482 (define comp-define-syntax (comp-def define-syntax-definition))
483 (define comp-define-values (comp-def define-values-definition))
484 (define comp-import (comp-def import-definition))
485 (define (fini vars vals mvars body)
486 (if (and (null? vars) (null? mvars))
487 ;; Macro-expand body, and restart when defines are found.
488 (let loop ((body body) (exps '()))
489 (if (not (pair? body))
490 (cons
491 '##core#begin
492 (reverse exps)) ; no more defines, otherwise we would have called `expand'
493 (let loop2 ((body body))
494 (let ((x (car body))
495 (rest (cdr body)))
496 (if (and (pair? x)
497 (let ((d (car x)))
498 (and (symbol? d)
499 (or (comp '##core#begin d)
500 (comp-define d)
501 (comp-define-values d)
502 (comp-define-syntax d)
503 (comp-import d)))))
504 ;; Stupid hack to avoid expanding imports
505 (if (comp-import (car x))
506 (loop rest (cons x exps))
507 (cons
508 '##core#begin
509 (##sys#append (reverse exps) (list (expand body)))))
510 (let ((x2 (##sys#expand-0 x se cs?)))
511 (if (eq? x x2)
512 ;; Modules and includes must be processed before
513 ;; we can continue with other forms, so hand
514 ;; control back to the compiler
515 (if (and (pair? x)
516 (symbol? (car x))
517 (or (comp '##core#module (car x))
518 (comp '##core#include (car x))))
519 `(##core#begin
520 ,@(reverse exps)
521 ,@(if (comp '##core#module (car x))
522 (if (null? rest)
523 `(,x)
524 `(,x (##core#let () ,@rest)))
525 `((##core#include ,@(cdr x) ,rest))))
526 (loop rest (cons x exps)))
527 (loop2 (cons x2 rest)) )) ))) ))
528 ;; We saw defines. Translate to letrec, and let compiler
529 ;; call us again for the remaining body by wrapping the
530 ;; remaining body forms in a ##core#let.
531 (let* ((result
532 `(##core#let
533 ,(##sys#map
534 (lambda (v) (##sys#list v '(##core#undefined)))
535 ;; vars are all normalised to lambda-lists: flatten them
536 (foldl (lambda (l v)
537 (##sys#append l (##sys#decompose-lambda-list
538 v (lambda (a _ _) a))))
539 '()
540 (reverse vars))) ; not strictly necessary...
541 ,@(map (lambda (var val is-mvar?)
542 ;; Non-mvars should expand to set! for
543 ;; efficiency, but also because they must be
544 ;; implicit multi-value continuations.
545 (if is-mvar?
546 (##sys#expand-multiple-values-assignment var val)
547 `(##core#set! ,(car var) ,val)))
548 (reverse vars)
549 (reverse vals)
550 (reverse mvars))
551 ,@body) ) )
552 (dd `(BODY: ,result))
553 result)))
554 (define (fini/syntax vars vals mvars body)
555 (fini
556 vars vals mvars
557 (let loop ((body body) (defs '()) (done #f))
558 (cond (done `((##core#letrec-syntax
559 ,(map cdr (reverse defs)) ,@body) ))
560 ((not (pair? body)) (loop body defs #t))
561 ((and (list? (car body))
562 (>= 3 (length (car body)))
563 (symbol? (caar body))
564 (comp-define-syntax (caar body)))
565 (let ((def (car body)))
566 ;; This check is insufficient, if introduced by
567 ;; different expansions, but better than nothing:
568 (when (eq? (car def) (cadr def))
569 (defjam-error def))
570 (loop (cdr body) (cons def defs) #f)))
571 (else (loop body defs #t))))))
572 ;; Expand a run of defines or define-syntaxes into letrec. As
573 ;; soon as we encounter something else, finish up.
574 (define (expand body)
575 ;; Each #t in "mvars" indicates an MV-capable "var". Non-MV
576 ;; vars (#f in mvars) are 1-element lambda-lists for simplicity.
577 (let loop ((body body) (vars '()) (vals '()) (mvars '()))
578 (d "BODY: " body)
579 (if (not (pair? body))
580 (fini vars vals mvars body)
581 (let* ((x (car body))
582 (rest (cdr body))
583 (exp1 (and (pair? x) (car x)))
584 (head (and exp1 (symbol? exp1) exp1)))
585 (if (not (symbol? head))
586 (fini vars vals mvars body)
587 (cond
588 ((comp-define head)
589 (##sys#check-syntax 'define x '(_ _ . #(_ 0)) #f se)
590 (let loop2 ((x x))
591 (let ((head (cadr x)))
592 (cond ((not (pair? head))
593 (##sys#check-syntax 'define x '(_ variable . #(_ 0)) #f se)
594 (when (eq? (car x) head) ; see above
595 (defjam-error x))
596 (loop rest (cons (list head) vars)
597 (cons (if (pair? (cddr x))
598 (caddr x)
599 '(##core#undefined) )
600 vals)
601 (cons #f mvars)))
602 ((pair? (car head))
603 (##sys#check-syntax
604 'define x '(_ (_ . lambda-list) . #(_ 1)) #f se)
605 (loop2
606 (chicken.syntax#expand-curried-define head (cddr x) se)))
607 (else
608 (##sys#check-syntax
609 'define x
610 '(_ (variable . lambda-list) . #(_ 1)) #f se)
611 (loop rest
612 (cons (list (car head)) vars)
613 (cons `(##core#lambda ,(cdr head) ,@(cddr x)) vals)
614 (cons #f mvars)))))))
615 ((comp-define-syntax head)
616 (##sys#check-syntax 'define-syntax x '(_ _ . #(_ 1)) se)
617 (fini/syntax vars vals mvars body))
618 ((comp-define-values head)
619 ;;XXX check for any of the variables being `define-values'
620 (##sys#check-syntax 'define-values x '(_ lambda-list _) #f se)
621 (loop rest (cons (cadr x) vars) (cons (caddr x) vals) (cons #t mvars)))
622 ((comp '##core#begin head)
623 (loop (##sys#append (cdr x) rest) vars vals mvars))
624 (else
625 ;; Do not macro-expand local definitions we are
626 ;; in the process of introducing.
627 (if (member (list head) vars)
628 (fini vars vals mvars body)
629 (let ((x2 (##sys#expand-0 x se cs?)))
630 (if (eq? x x2)
631 (fini vars vals mvars body)
632 (loop (cons x2 rest) vars vals mvars)))))))))))
633 (expand body) ) )
634
635
636;;; A simple expression matcher
637
638;; Used by "quasiquote", below
639(define chicken.syntax#match-expression
640 (lambda (exp pat vars)
641 (let ((env '()))
642 (define (mwalk x p)
643 (cond ((not (pair? p))
644 (cond ((assq p env) => (lambda (a) (equal? x (cdr a))))
645 ((memq p vars)
646 (set! env (cons (cons p x) env))
647 #t)
648 (else (eq? x p)) ) )
649 ((pair? x)
650 (and (mwalk (car x) (car p))
651 (mwalk (cdr x) (cdr p)) ) )
652 (else #f) ) )
653 (and (mwalk exp pat) env) ) ) )
654
655
656;;; Expand "curried" lambda-list syntax for `define'
657
658;; Used by "define", below
659(define (chicken.syntax#expand-curried-define head body se)
660 (let ((name #f))
661 (define (loop head body)
662 (if (symbol? (car head))
663 (begin
664 (set! name (car head))
665 `(##core#lambda ,(cdr head) ,@body) )
666 (loop (car head) `((##core#lambda ,(cdr head) ,@body)) ) ))
667 (let ([exp (loop head body)])
668 (list 'define name exp) ) ) )
669
670
671;;; Line-number database management:
672
673(define ##sys#line-number-database #f)
674
675
676;;; General syntax checking routine:
677
678(define ##sys#syntax-error-culprit #f)
679(define ##sys#syntax-context '())
680
681(define (##sys#syntax-error-hook . args)
682 (apply ##sys#signal-hook #:syntax-error
683 (strip-syntax args)))
684
685(define (##sys#syntax-error . args)
686 (apply ##sys#syntax-error-hook args))
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 (when (list? x) (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
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.bytevector chicken.fixnum)
1042(import chicken.syntax chicken.internal chicken.platform)
1043(import (only (scheme base) make-parameter))
1044
1045;;; Macro definitions:
1046
1047(##sys#extend-macro-environment
1048 'import-syntax '()
1049 (##sys#er-transformer
1050 (cut ##sys#expand-import <> <> <>
1051 ##sys#current-environment ##sys#macro-environment
1052 #f #f 'import-syntax)))
1053
1054(##sys#extend-macro-environment
1055 'import-syntax-for-syntax '()
1056 (##sys#er-transformer
1057 (cut ##sys#expand-import <> <> <>
1058 ##sys#current-meta-environment ##sys#meta-macro-environment
1059 #t #f 'import-syntax-for-syntax)))
1060
1061(set! chicken.syntax#import-definition
1062 (##sys#extend-macro-environment
1063 'import '()
1064 (##sys#er-transformer
1065 (lambda (x r c)
1066 `(##core#begin
1067 ,@(map (lambda (x)
1068 (let-values (((name lib spec v s i) (##sys#decompose-import x r c 'import))
1069 ((mod) (##sys#current-module)))
1070 (when (and mod (eq? name (##sys#module-name mod)))
1071 (##sys#syntax-error
1072 'import "cannot import from module currently being defined" name))
1073 (if (not spec)
1074 (##sys#syntax-error
1075 'import "cannot import from undefined module" name)
1076 (##sys#import
1077 spec v s i
1078 ##sys#current-environment ##sys#macro-environment #f #f 'import))
1079 (if (not lib)
1080 '(##core#undefined)
1081 `(##core#require ,lib ,name))))
1082 (cdr x)))))))
1083
1084(##sys#extend-macro-environment
1085 'import-for-syntax '()
1086 (##sys#er-transformer
1087 (lambda (x r c)
1088 (##sys#register-meta-expression `(,(r 'import) ,@(cdr x)))
1089 `(##core#elaborationtimeonly (,(r 'import) ,@(cdr x))))))
1090
1091(define (process-cond-expand clauses)
1092 (define (err x)
1093 (##sys#syntax-error "syntax error in `cond-expand' form"
1094 x
1095 (cons 'cond-expand clauses)))
1096 (define (file-exists? fname)
1097 (##sys#file-exists? fname #f #f 'cond-expand))
1098 (define (locate-library name)
1099 (let* ((name2 (library-id name))
1100 (sname2 (symbol->string name2)))
1101 (or (##sys#find-module name2 #f)
1102 (let loop ((rp (repository-path)))
1103 (and (pair? rp)
1104 (let ((p (car rp)))
1105 (or (file-exists? (string-append p "/" sname2 ".import.so"))
1106 (file-exists? (string-append p "/" sname2 ".import.scm"))
1107 (loop (cdr rp)))))))))
1108 (define (test fx)
1109 (cond ((symbol? fx) (feature? (strip-syntax fx)))
1110 ((not (pair? fx)) (err fx))
1111 (else
1112 (let ((head (car fx))
1113 (rest (cdr fx)))
1114 (case (strip-syntax head)
1115 ((and)
1116 (or (eq? rest '())
1117 (if (pair? rest)
1118 (and (test (car rest))
1119 (test `(and ,@(cdr rest))))
1120 (err fx))))
1121 ((or)
1122 (and (not (eq? rest '()))
1123 (if (pair? rest)
1124 (or (test (car rest))
1125 (test `(or ,@(cdr rest))))
1126 (err fx))))
1127 ((not) (not (test (cadr fx))))
1128 ((library)
1129 (if (and (pair? rest)
1130 (null? (cdr rest)))
1131 (locate-library (strip-syntax (car rest)))
1132 (err fx)))
1133 (else (err fx)))))))
1134 (let expand ((cls clauses))
1135 (cond ((eq? cls '())
1136 (##sys#apply
1137 ##sys#error "no matching clause in `cond-expand' form"
1138 (map (lambda (x) (car x)) clauses)))
1139 ((not (pair? cls)) (err cls))
1140 (else
1141 (let ((clause (car cls))
1142 (rclauses (cdr cls)))
1143 (if (not (pair? clause))
1144 (err clause)
1145 (let ((id (car clause)))
1146 (cond ((eq? (strip-syntax id) 'else)
1147 (let ((rest (cdr clause)))
1148 (if (eq? rest '())
1149 '(##core#undefined)
1150 `(##core#begin ,@rest))))
1151 ((test id) `(##core#begin ,@(cdr clause)))
1152 (else (expand rclauses))))))))))
1153
1154(##sys#extend-macro-environment
1155 'cond-expand
1156 '()
1157 (##sys#er-transformer
1158 (lambda (form r c)
1159 (process-cond-expand (cdr form)))))
1160
1161;; The "initial" macro environment, containing only import forms and
1162;; cond-expand. TODO: Eventually, cond-expand should move to the
1163;; (chicken base) module to match r7rs. Keeping it in the initial env
1164;; makes it a whole lot easier to write portable CHICKEN 4 & 5 code.
1165(define ##sys#initial-macro-environment (##sys#macro-environment))
1166
1167(##sys#extend-macro-environment
1168 'module '()
1169 (##sys#er-transformer
1170 (lambda (x r c)
1171 (##sys#check-syntax 'module x '(_ _ _ . #(_ 0)))
1172 (let ((len (length x))
1173 (name (library-id (cadr x))))
1174 ;; We strip syntax here instead of doing a hygienic comparison
1175 ;; to "=". This is a tradeoff; either we do this, or we must
1176 ;; include a mapping of (= . scheme#=) in our syntax env. In
1177 ;; the initial environment, = is bound to scheme#=, but when
1178 ;; using -explicit-use that's not the case. Doing an unhygienic
1179 ;; comparison ensures module will work in both cases.
1180 (cond ((and (fx>= len 4) (eq? '= (strip-syntax (caddr x))))
1181 (let* ((x (strip-syntax x))
1182 (app (cadddr x)))
1183 (cond ((fx> len 4)
1184 ;; feature suggested by syn:
1185 ;;
1186 ;; (module NAME = FUNCTORNAME BODY ...)
1187 ;; ~>
1188 ;; (begin
1189 ;; (module _NAME * BODY ...)
1190 ;; (module NAME = (FUNCTORNAME _NAME)))
1191 ;;
1192 ;; - the use of "_NAME" is a bit stupid, but it must be
1193 ;; externally visible to generate an import library from
1194 ;; and compiling "NAME" separately may need an import-lib
1195 ;; for stuff in "BODY" (say, syntax needed by syntax exported
1196 ;; from the functor, or something like this...)
1197 (let ((mtmp (string->symbol
1198 (##sys#string-append
1199 "_"
1200 (symbol->string name))))
1201 (%module (r 'module)))
1202 `(##core#begin
1203 (,%module ,mtmp * ,@(cddddr x))
1204 (,%module ,name = (,app ,mtmp)))))
1205 (else
1206 (##sys#check-syntax
1207 'module x '(_ _ _ (_ . #(_ 0))))
1208 (##sys#instantiate-functor
1209 name
1210 (library-id (car app))
1211 (cdr app)))))) ; functor arguments
1212 (else
1213 ;;XXX use module name in "loc" argument?
1214 (let ((exports (##sys#validate-exports (strip-syntax (caddr x)) 'module)))
1215 `(##core#module
1216 ,name
1217 ,(if (eq? '* exports)
1218 #t
1219 exports)
1220 ,@(let ((body (cdddr x)))
1221 (if (and (pair? body)
1222 (null? (cdr body))
1223 (string? (car body)))
1224 `((##core#include ,(car body) ,##sys#current-source-filename))
1225 body))))))))))
1226
1227;;; R7RS define-library
1228
1229(##sys#extend-macro-environment
1230 'define-library '()
1231 (##sys#er-transformer
1232 (lambda (x r c)
1233 (define (register-r7rs-module name)
1234 (let ((dummy (string->symbol (string-append (string #\x04) "r7rs" (symbol->string name)))))
1235 (##sys#put! name '##r7rs#module dummy)
1236 dummy))
1237 (define implicit-r7rs-library-bindings
1238 '(begin
1239 cond-expand
1240 export
1241 import
1242 import-for-syntax
1243 include
1244 include-ci
1245 syntax-rules))
1246 (##sys#check-syntax 'define-library x '(_ . #(_ 0)))
1247 (let* ((x (strip-syntax x))
1248 (name (cadr x))
1249 (real-name (library-id name))
1250 (decls (cddr x))
1251 (dummy (register-r7rs-module real-name)))
1252 (define (parse-exports specs)
1253 (map (lambda (spec)
1254 (cond ((and (list? spec)
1255 (= 3 (length spec))
1256 (eq? 'rename (car spec)))
1257 `(export/rename ,(cdr spec)))
1258 ((symbol? spec) `(export ,spec))
1259 (else
1260 (##sys#syntax-error 'define-library "invalid export specifier" spec name))))
1261 specs))
1262 (define (parse-imports specs)
1263 ;; XXX TODO: Should be import-for-syntax'ed as well?
1264 `(import ,@specs))
1265 (define (process-includes fnames ci?)
1266 `(##core#begin
1267 ,@(map (lambda (fname)
1268 (if (string? fname)
1269 `(##core#begin ,@(read-forms fname ci?)))
1270 (fname (##sys#syntax-error 'include "invalid include-filename" fname)))
1271 fnames)))
1272 (define (expand/begin e)
1273 (let ((e2 (expand e '())))
1274 (if (and (pair? e2) (eq? '##core#begin (car e2)))
1275 (cons '##core#begin (map expand/begin (cdr e2)))
1276 e2)))
1277 (define (read-forms filename ci? #!optional (proc (lambda (x) (map expand/begin x))))
1278 (fluid-let ((##sys#default-read-info-hook
1279 (let ((name 'chicken.compiler.support#read-info-hook))
1280 (and (feature? 'compiling)
1281 (##sys#symbol-has-toplevel-binding? name)
1282 (##sys#slot name 0)))))
1283 (##sys#include-forms-from-file
1284 filename
1285 ##sys#current-source-filename ci?
1286 (lambda (forms path) (proc forms)))))
1287 (define (process-include-decls fnames)
1288 (parse-decls
1289 (let loop ((fnames fnames) (all '()))
1290 (if (null? fnames)
1291 (reverse all)
1292 (let ((forms (read-forms (car fnames) #t (lambda (x) x))))
1293 (loop (cdr fnames)
1294 (append (reverse forms) all)))))))
1295 (define (fail spec)
1296 (##sys#syntax-error 'define-library "invalid library declaration" spec))
1297 (define (parse-decls decls)
1298 (cond ((null? decls) '(##core#begin))
1299 ((and (pair? decls) (pair? (car decls)))
1300 (let ((spec (car decls))
1301 (more (cdr decls)))
1302 (case (car spec)
1303 ((export)
1304 (##sys#check-syntax 'export spec '(_ . #(_ 0)))
1305 `(##core#begin ,@(parse-exports (cdr spec))
1306 ,(parse-decls more)))
1307 ((import)
1308 (##sys#check-syntax 'import spec '(_ . #(_ 0)))
1309 `(##core#begin ,(parse-imports (cdr spec))
1310 ,(parse-decls more)))
1311 ((include)
1312 (##sys#check-syntax 'include spec '(_ . #(_ 0)))
1313 `(##core#begin ,(process-includes (cdr spec) #f)
1314 ,(parse-decls more)))
1315 ((include-ci)
1316 (##sys#check-syntax 'include-ci spec '(_ . #(_ 0)))
1317 `(##core#begin ,(process-includes (cdr spec) #t)
1318 ,(parse-decls more)))
1319 ((include-library-declarations)
1320 `(##core#begin ,(process-include-decls (cdr spec))
1321 ,(parse-decls more)))
1322 ((cond-expand)
1323 (parse-decls (append (list (process-cond-expand (cdr spec)))
1324 more)))
1325 ((##core#begin)
1326 (parse-decls (cdr spec)))
1327 ((begin)
1328 `(##core#begin ,@(cdr spec)
1329 ,(parse-decls more)))
1330 (else (fail spec)))))
1331 (else (fail (car decls)))))
1332 `(##core#module ,real-name ((,dummy))
1333 ;; gruesome hack: we add a dummy export for adding indirect exports
1334 (##core#define-syntax ,dummy
1335 (##sys#er-transformer (##core#lambda (x r c) (##core#undefined))))
1336 ;; Set up an R7RS environment for the module's body.
1337 (import-for-syntax (only scheme.base ,@implicit-r7rs-library-bindings))
1338 (import (only scheme.base ,@implicit-r7rs-library-bindings)
1339 (only chicken.module export/rename))
1340 ;; Now process all toplevel library declarations
1341 ,(parse-decls decls))))))
1342
1343(##sys#extend-macro-environment
1344 'export '()
1345 (##sys#er-transformer
1346 (lambda (x r c)
1347 (let ((exps (##sys#validate-exports (strip-syntax (cdr x)) 'export))
1348 (mod (##sys#current-module)))
1349 (when mod
1350 (##sys#add-to-export-list mod exps))
1351 '(##core#undefined)))))
1352
1353(##sys#extend-macro-environment
1354 'export/rename '()
1355 (##sys#er-transformer
1356 (lambda (x r c)
1357 (let ((exps (map (lambda (ren)
1358 (if (and (pair? ren)
1359 (symbol? (car ren))
1360 (pair? (cdr ren))
1361 (symbol? (cadr ren))
1362 (null? (cddr ren)))
1363 (cons (car ren) (cadr ren))
1364 (##sys#syntax-error "invalid item in export rename list"
1365 ren)))
1366 (strip-syntax (cdr x))))
1367 (mod (##sys#current-module)))
1368 (when mod
1369 (##sys#add-to-export/rename-list mod exps))
1370 '(##core#undefined)))))
1371
1372(##sys#extend-macro-environment
1373 'reexport '()
1374 (##sys#er-transformer
1375 (cut ##sys#expand-import <> <> <>
1376 ##sys#current-environment ##sys#macro-environment
1377 #f #t 'reexport)))
1378
1379;;; functor definition
1380
1381(##sys#extend-macro-environment
1382 'functor '()
1383 (##sys#er-transformer
1384 (lambda (x r c)
1385 (##sys#check-syntax 'functor x '(_ (_ . #((_ _) 0)) _ . _))
1386 (let* ((x (strip-syntax x))
1387 (head (cadr x))
1388 (name (car head))
1389 (args (cdr head))
1390 (exps (caddr x))
1391 (body (cdddr x))
1392 (registration
1393 `(##sys#register-functor
1394 (##core#quote ,(library-id name))
1395 (##core#quote
1396 ,(map (lambda (arg)
1397 (let ((argname (car arg))
1398 (exps (##sys#validate-exports (cadr arg) 'functor)))
1399 (unless (or (symbol? argname)
1400 (and (list? argname)
1401 (= 2 (length argname))
1402 (symbol? (car argname))
1403 (valid-library-specifier? (cadr argname))))
1404 (##sys#syntax-error "invalid functor argument" name arg))
1405 (cons argname exps)))
1406 args))
1407 (##core#quote ,(##sys#validate-exports exps 'functor))
1408 (##core#quote ,body))))
1409 `(##core#module ,(library-id name)
1410 #t
1411 (import scheme chicken.syntax) ;; TODO: Is this correct?
1412 (begin-for-syntax ,registration))))))
1413
1414;;; interface definition
1415
1416(##sys#extend-macro-environment
1417 'define-interface '()
1418 (##sys#er-transformer
1419 (lambda (x r c)
1420 (##sys#check-syntax 'define-interface x '(_ variable _))
1421 (let ((name (strip-syntax (cadr x))))
1422 (when (eq? '* name)
1423 (##sys#syntax-error
1424 'define-interface "`*' is not allowed as a name for an interface"))
1425 `(##core#elaborationtimeonly
1426 (##sys#put/restore!
1427 (##core#quote ,name)
1428 (##core#quote ##core#interface)
1429 (##core#quote
1430 ,(let ((exps (strip-syntax (caddr x))))
1431 (cond ((eq? '* exps) '*)
1432 ((symbol? exps) `(#:interface ,exps))
1433 ((list? exps)
1434 (##sys#validate-exports exps 'define-interface))
1435 (else
1436 (##sys#syntax-error
1437 'define-interface "invalid exports" (caddr x))))))))))))
1438
1439(##sys#extend-macro-environment
1440 'current-module '()
1441 (##sys#er-transformer
1442 (lambda (x r c)
1443 (##sys#check-syntax 'current-module x '(_))
1444 (and-let* ((mod (##sys#current-module)))
1445 `(##core#quote ,(##sys#module-name mod))))))
1446
1447;; The chicken.module syntax environment
1448(define ##sys#chicken.module-macro-environment (##sys#macro-environment))
1449
1450(set! ##sys#scheme-macro-environment
1451 (let ((me0 (##sys#macro-environment)))
1452
1453(##sys#extend-macro-environment
1454 'lambda
1455 '()
1456 (##sys#er-transformer
1457 (lambda (x r c)
1458 (##sys#check-syntax 'lambda x '(_ lambda-list . #(_ 1)))
1459 `(##core#lambda ,@(cdr x)))))
1460
1461(##sys#extend-macro-environment
1462 'quote
1463 '()
1464 (##sys#er-transformer
1465 (lambda (x r c)
1466 (##sys#check-syntax 'quote x '(_ _))
1467 `(##core#quote ,(cadr x)))))
1468
1469(##sys#extend-macro-environment
1470 'if
1471 '()
1472 (##sys#er-transformer
1473 (lambda (x r c)
1474 (##sys#check-syntax 'if x '(_ _ _ . #(_)))
1475 `(##core#if ,@(cdr x)))))
1476
1477(##sys#extend-macro-environment
1478 'begin
1479 '()
1480 (##sys#er-transformer
1481 (lambda (x r c)
1482 (##sys#check-syntax 'begin x '(_ . #(_ 0)))
1483 `(##core#begin ,@(cdr x)))))
1484
1485(set! chicken.syntax#define-definition
1486 (##sys#extend-macro-environment
1487 'define
1488 '()
1489 (##sys#er-transformer
1490 (lambda (x r c)
1491 (##sys#check-syntax 'define x '(_ . #(_ 1)))
1492 (let loop ((form x))
1493 (let ((head (cadr form))
1494 (body (cddr form)) )
1495 (cond ((not (pair? head))
1496 (##sys#check-syntax 'define form '(_ variable . #(_ 0 1)))
1497 (let ((name (or (getp head '##core#macro-alias) head)))
1498 (##sys#register-export name (##sys#current-module)))
1499 (when (c (r 'define) head)
1500 (chicken.syntax#defjam-error x))
1501 `(##core#begin
1502 (##core#ensure-toplevel-definition ,head)
1503 (##core#set!
1504 ,head
1505 ,(if (pair? body) (car body) '(##core#undefined)))))
1506 ((pair? (car head))
1507 (##sys#check-syntax 'define form '(_ (_ . lambda-list) . #(_ 1)))
1508 (loop (chicken.syntax#expand-curried-define head body '()))) ;XXX '() should be se
1509 (else
1510 (##sys#check-syntax 'define form '(_ (variable . lambda-list) . #(_ 1)))
1511 (loop (list (car x) (car head) `(##core#lambda ,(cdr head) ,@body)))))))))))
1512
1513(set! chicken.syntax#define-syntax-definition
1514 (##sys#extend-macro-environment
1515 'define-syntax
1516 '()
1517 (##sys#er-transformer
1518 (lambda (form r c)
1519 (##sys#check-syntax 'define-syntax form '(_ variable _))
1520 (let ((head (cadr form))
1521 (body (caddr form)))
1522 (let ((name (or (getp head '##core#macro-alias) head)))
1523 (##sys#register-export name (##sys#current-module)))
1524 (when (c (r 'define-syntax) head)
1525 (chicken.syntax#defjam-error form))
1526 `(##core#define-syntax ,head ,body))))))
1527
1528(##sys#extend-macro-environment
1529 'let
1530 '()
1531 (##sys#er-transformer
1532 (lambda (x r c)
1533 (cond ((and (pair? (cdr x)) (symbol? (cadr x)))
1534 (##sys#check-syntax 'let x '(_ variable #((variable _) 0) . #(_ 1)))
1535 (check-for-multiple-bindings (caddr x) x "let"))
1536 (else
1537 (##sys#check-syntax 'let x '(_ #((variable _) 0) . #(_ 1)))
1538 (check-for-multiple-bindings (cadr x) x "let")))
1539 `(##core#let ,@(cdr x)))))
1540
1541(##sys#extend-macro-environment
1542 'letrec
1543 '()
1544 (##sys#er-transformer
1545 (lambda (x r c)
1546 (##sys#check-syntax 'letrec x '(_ #((variable _) 0) . #(_ 1)))
1547 (check-for-multiple-bindings (cadr x) x "letrec")
1548 `(##core#letrec ,@(cdr x)))))
1549
1550(##sys#extend-macro-environment
1551 'let-syntax
1552 '()
1553 (##sys#er-transformer
1554 (lambda (x r c)
1555 (##sys#check-syntax 'let-syntax x '(_ #((variable _) 0) . #(_ 1)))
1556 (check-for-multiple-bindings (cadr x) x "let-syntax")
1557 `(##core#let-syntax ,@(cdr x)))))
1558
1559(##sys#extend-macro-environment
1560 'letrec-syntax
1561 '()
1562 (##sys#er-transformer
1563 (lambda (x r c)
1564 (##sys#check-syntax 'letrec-syntax x '(_ #((variable _) 0) . #(_ 1)))
1565 (check-for-multiple-bindings (cadr x) x "letrec-syntax")
1566 `(##core#letrec-syntax ,@(cdr x)))))
1567
1568(##sys#extend-macro-environment
1569 'set!
1570 '()
1571 (##sys#er-transformer
1572 (lambda (x r c)
1573 (##sys#check-syntax 'set! x '(_ _ _))
1574 (let ((dest (cadr x))
1575 (val (caddr x)))
1576 (cond ((pair? dest)
1577 `((##sys#setter ,(car dest)) ,@(cdr dest) ,val))
1578 (else `(##core#set! ,dest ,val)))))))
1579
1580(##sys#extend-macro-environment
1581 'and
1582 '()
1583 (##sys#er-transformer
1584 (lambda (form r c)
1585 (let ((body (cdr form)))
1586 (if (null? body)
1587 #t
1588 (let ((rbody (cdr body))
1589 (hbody (car body)) )
1590 (if (null? rbody)
1591 hbody
1592 `(##core#if ,hbody (,(r 'and) ,@rbody) #f) ) ) ) ) ) ) )
1593
1594(##sys#extend-macro-environment
1595 'or
1596 '()
1597 (##sys#er-transformer
1598 (lambda (form r c)
1599 (let ((body (cdr form)))
1600 (if (null? body)
1601 #f
1602 (let ((rbody (cdr body))
1603 (hbody (car body)))
1604 (if (null? rbody)
1605 hbody
1606 (let ((tmp (r 'tmp)))
1607 `(##core#let ((,tmp ,hbody))
1608 (##core#if ,tmp ,tmp (,(r 'or) ,@rbody)) ) ) ) ) ) ) ) ) )
1609
1610(##sys#extend-macro-environment
1611 'cond
1612 '()
1613 (##sys#er-transformer
1614 (lambda (form r c)
1615 (let ((body (cdr form))
1616 (%=> (r '=>))
1617 (%or (r 'or))
1618 (%else (r 'else)))
1619 (let expand ((clauses body) (else? #f))
1620 (if (not (pair? clauses))
1621 '(##core#undefined)
1622 (let ((clause (car clauses))
1623 (rclauses (cdr clauses)) )
1624 (##sys#check-syntax 'cond clause '#(_ 1))
1625 (cond (else?
1626 (##sys#warn
1627 (chicken.format#sprintf "clause following `~S' clause in `cond'" else?)
1628 (strip-syntax clause))
1629 (expand rclauses else?)
1630 '(##core#begin))
1631 ((or (c %else (car clause))
1632 (eq? #t (car clause))
1633 ;; Like "constant?" from support.scm
1634 (number? (car clause))
1635 (char? (car clause))
1636 (string? (car clause))
1637 (eof-object? (car clause))
1638 (bytevector? (car clause))
1639 (bwp-object? (car clause))
1640 (vector? (car clause))
1641 (##sys#srfi-4-vector? (car clause))
1642 (and (pair? (car clause))
1643 (c (r 'quote) (caar clause))))
1644 (expand rclauses (strip-syntax (car clause)))
1645 (cond ((and (fx= (length clause) 3)
1646 (c %=> (cadr clause)))
1647 `(,(caddr clause) ,(car clause)))
1648 ((pair? (cdr clause))
1649 `(##core#begin ,@(cdr clause)))
1650 ((c %else (car clause))
1651 `(##core#undefined))
1652 (else (car clause))))
1653 ((null? (cdr clause))
1654 `(,%or ,(car clause) ,(expand rclauses #f)))
1655 ((and (fx= (length clause) 3)
1656 (c %=> (cadr clause)))
1657 (let ((tmp (r 'tmp)))
1658 `(##core#let ((,tmp ,(car clause)))
1659 (##core#if ,tmp
1660 (,(caddr clause) ,tmp)
1661 ,(expand rclauses #f) ) ) ) )
1662 ((and (fx= (length clause) 4)
1663 (c %=> (caddr clause)))
1664 (let ((tmp (r 'tmp)))
1665 `(##sys#call-with-values
1666 (##core#lambda () ,(car clause))
1667 (##core#lambda
1668 ,tmp
1669 (if (##sys#apply ,(cadr clause) ,tmp)
1670 (##sys#apply ,(cadddr clause) ,tmp)
1671 ,(expand rclauses #f) ) ) ) ) )
1672 (else `(##core#if ,(car clause)
1673 (##core#begin ,@(cdr clause))
1674 ,(expand rclauses #f) ) ) ) ) ) ) ) ) ) )
1675
1676(##sys#extend-macro-environment
1677 'case
1678 '((eqv? . scheme#eqv?))
1679 (##sys#er-transformer
1680 (lambda (form r c)
1681 (##sys#check-syntax 'case form '(_ _ . #(_ 0)))
1682 (let ((exp (cadr form))
1683 (body (cddr form)) )
1684 (let ((tmp (r 'tmp))
1685 (%or (r 'or))
1686 (%=> (r '=>))
1687 (%eqv? (r 'eqv?))
1688 (%else (r 'else)))
1689 `(let ((,tmp ,exp))
1690 ,(let expand ((clauses body) (else? #f))
1691 (if (not (pair? clauses))
1692 '(##core#undefined)
1693 (let ((clause (car clauses))
1694 (rclauses (cdr clauses)) )
1695 (##sys#check-syntax 'case clause '#(_ 1))
1696 (cond (else?
1697 (##sys#warn
1698 "clause following `else' clause in `case'"
1699 (strip-syntax clause))
1700 (expand rclauses #t)
1701 '(##core#begin))
1702 ((c %else (car clause))
1703 (expand rclauses #t)
1704 (cond ((null? (cdr clause))
1705 `(##core#undefined))
1706 ((and (fx= (length clause) 3) ; (else => expr)
1707 (c %=> (cadr clause)))
1708 `(,(caddr clause) ,tmp))
1709 (else
1710 `(##core#begin ,@(cdr clause)))))
1711 (else
1712 `(##core#if (,%or ,@(##sys#map
1713 (lambda (x) `(,%eqv? ,tmp ',x))
1714 (car clause)))
1715 ,(if (and (fx= (length clause) 3) ; ((...) => expr)
1716 (c %=> (cadr clause)))
1717 `(,(caddr clause) ,tmp)
1718 `(##core#begin ,@(cdr clause)))
1719 ,(expand rclauses #f) ) ) ) ) ) ) ) ) ) ) ) )
1720
1721(##sys#extend-macro-environment
1722 'let*
1723 '()
1724 (##sys#er-transformer
1725 (lambda (form r c)
1726 (##sys#check-syntax 'let* form '(_ #((variable _) 0) . #(_ 1)))
1727 (let ((bindings (cadr form))
1728 (body (cddr form)) )
1729 (let expand ((bs bindings))
1730 (if (eq? bs '())
1731 `(##core#let () ,@body)
1732 `(##core#let (,(car bs)) ,(expand (cdr bs))) ) ) ) ) ) )
1733
1734(##sys#extend-macro-environment
1735 'do
1736 '()
1737 (##sys#er-transformer
1738 (lambda (form r c)
1739 (##sys#check-syntax 'do form '(_ #((variable _ . #(_)) 0) . #(_ 1)))
1740 (let ((bindings (cadr form))
1741 (test (caddr form))
1742 (body (cdddr form))
1743 (dovar (r 'doloop)))
1744 `(##core#let
1745 ,dovar
1746 ,(##sys#map (lambda (b) (list (car b) (car (cdr b)))) bindings)
1747 (##core#if ,(car test)
1748 ,(let ((tbody (cdr test)))
1749 (if (eq? tbody '())
1750 '(##core#undefined)
1751 `(##core#begin ,@tbody) ) )
1752 (##core#begin
1753 ,(if (eq? body '())
1754 '(##core#undefined)
1755 `(##core#let () ,@body) )
1756 (##core#app
1757 ,dovar ,@(##sys#map (lambda (b)
1758 (if (eq? (cdr (cdr b)) '())
1759 (car b)
1760 (car (cdr (cdr b))) ) )
1761 bindings) ) ) ) ) ) ) ) )
1762
1763(##sys#extend-macro-environment
1764 'quasiquote
1765 '()
1766 (##sys#er-transformer
1767 (lambda (form r c)
1768 (let ((%quasiquote (r 'quasiquote))
1769 (%unquote (r 'unquote))
1770 (%unquote-splicing (r 'unquote-splicing)))
1771 (define (walk x n) (simplify (walk1 x n)))
1772 (define (walk1 x n)
1773 (cond ((vector? x)
1774 `(##sys#list->vector ,(walk (vector->list x) n)) )
1775 ((not (pair? x)) `(##core#quote ,x))
1776 (else
1777 (let ((head (car x))
1778 (tail (cdr x)))
1779 (cond ((c %unquote head)
1780 (cond ((eq? n 0)
1781 (##sys#check-syntax 'unquote x '(_ _))
1782 (car tail))
1783 (else (list '##sys#cons `(##core#quote ,%unquote)
1784 (walk tail (fx- n 1)) ) )))
1785 ((c %quasiquote head)
1786 (list '##sys#cons `(##core#quote ,%quasiquote)
1787 (walk tail (fx+ n 1)) ) )
1788 ((and (pair? head) (c %unquote-splicing (car head)))
1789 (cond ((eq? n 0)
1790 (##sys#check-syntax 'unquote-splicing head '(_ _))
1791 `(##sys#append ,(cadr head) ,(walk tail n)))
1792 (else
1793 `(##sys#cons
1794 (##sys#cons (##core#quote ,%unquote-splicing)
1795 ,(walk (cdr head) (fx- n 1)) )
1796 ,(walk tail n)))))
1797 (else
1798 `(##sys#cons ,(walk head n) ,(walk tail n)) ) ) ) ) ) )
1799 (define (simplify x)
1800 (cond ((chicken.syntax#match-expression x '(##sys#cons a (##core#quote ())) '(a))
1801 => (lambda (env) (simplify `(##sys#list ,(cdr (assq 'a env))))) )
1802 ((chicken.syntax#match-expression x '(##sys#cons a (##sys#list . b)) '(a b))
1803 => (lambda (env)
1804 (let ((bxs (assq 'b env)))
1805 (if (fx< (length bxs) 32)
1806 (simplify `(##sys#list ,(cdr (assq 'a env))
1807 ,@(cdr bxs) ) )
1808 x) ) ) )
1809 ((chicken.syntax#match-expression x '(##sys#append a (##core#quote ())) '(a))
1810 => (lambda (env) (cdr (assq 'a env))) )
1811 (else x) ) )
1812 (##sys#check-syntax 'quasiquote form '(_ _))
1813 (walk (cadr form) 0) ) ) ) )
1814
1815(##sys#extend-macro-environment
1816 'delay
1817 '()
1818 (##sys#er-transformer
1819 (lambda (form r c)
1820 (##sys#check-syntax 'delay form '(_ _))
1821 `(,(r 'delay-force)
1822 (##sys#make-promise
1823 (##sys#call-with-values (##core#lambda () ,(cadr form)) ##sys#list))))))
1824
1825(##sys#extend-macro-environment
1826 'syntax-error
1827 '()
1828 (##sys#er-transformer
1829 (lambda (form r c)
1830 (##sys#check-syntax 'syntax-error form '(_ string . #(_ 0)))
1831 (apply ##sys#syntax-error (cadr form) (cddr form)))))
1832
1833;;; syntax-rules
1834
1835(include "synrules.scm")
1836
1837(macro-subset me0)))
1838
1839;;; the base macro environment (the old "scheme", essentially)
1840;;; TODO: Remove this
1841
1842(define ##sys#default-macro-environment
1843 (fixup-macro-environment (##sys#macro-environment)))
1844
1845(define ##sys#meta-macro-environment (make-parameter (##sys#macro-environment)))
1846
1847;; register features
1848
1849(register-feature! 'srfi-0 'srfi-46 'srfi-61 'srfi-87)