Commit | Line | Data |
---|---|---|
c7228382 KN |
1 | ;;; Guile Scheme specification |
2 | ||
3 | ;; Copyright (C) 2001 Free Software Foundation, Inc. | |
4 | ||
5 | ;; This program is free software; you can redistribute it and/or modify | |
6 | ;; it under the terms of the GNU General Public License as published by | |
7 | ;; the Free Software Foundation; either version 2, or (at your option) | |
8 | ;; any later version. | |
2335fb97 | 9 | ;; |
c7228382 KN |
10 | ;; This program is distributed in the hope that it will be useful, |
11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | ;; GNU General Public License for more details. | |
2335fb97 | 14 | ;; |
c7228382 KN |
15 | ;; You should have received a copy of the GNU General Public License |
16 | ;; along with this program; see the file COPYING. If not, write to | |
17 | ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
18 | ;; Boston, MA 02111-1307, USA. | |
19 | ||
20 | ;;; Code: | |
21 | ||
22 | (define-module (language scheme translate) | |
f245e62c | 23 | :use-module (system base pmatch) |
c7228382 KN |
24 | :use-module (system base language) |
25 | :use-module (system il ghil) | |
c7228382 | 26 | :use-module (ice-9 receive) |
2335fb97 | 27 | :use-module (srfi srfi-39) |
48302624 | 28 | :use-module ((system base compile) :select (syntax-error)) |
c7228382 KN |
29 | :export (translate)) |
30 | ||
2335fb97 | 31 | |
b89fc215 LC |
32 | ;; Module in which compile-time code (macros) is evaluated. |
33 | (define &compile-time-module (make-parameter #f)) | |
34 | ||
35 | (define (eval-at-compile-time exp) | |
36 | "Evaluate @var{exp} in the current compile-time module." | |
37 | (catch #t | |
38 | (lambda () | |
39 | (save-module-excursion | |
40 | (lambda () | |
41 | (eval exp (&compile-time-module))))) | |
42 | (lambda (key . args) | |
43 | (syntax-error #f | |
44 | (format #f "~a: compile-time evaluation failed" exp) | |
45 | (cons key args))))) | |
9dbbe4bb | 46 | |
c7228382 | 47 | (define (translate x e) |
b89fc215 | 48 | (parameterize ((&compile-time-module (make-module))) |
9dbbe4bb LC |
49 | |
50 | ;; Import only core bindings in the macro module. | |
b89fc215 | 51 | (module-use! (&compile-time-module) the-root-module) |
9dbbe4bb | 52 | |
2335fb97 LC |
53 | (call-with-ghil-environment (make-ghil-mod e) '() |
54 | (lambda (env vars) | |
849cefac | 55 | (make-ghil-lambda env #f vars #f (trans env #f x)))))) |
2335fb97 LC |
56 | |
57 | \f | |
58 | ;;; | |
59 | ;;; Macro tricks | |
60 | ;;; | |
61 | ||
62 | (define (expand-macro e) | |
63 | ;; Similar to `macroexpand' in `boot-9.scm' except that it does not expand | |
64 | ;; `define-macro' and `defmacro'. | |
65 | (cond | |
66 | ((pair? e) | |
67 | (let* ((head (car e)) | |
9dbbe4bb LC |
68 | (val (and (symbol? head) |
69 | (false-if-exception | |
b89fc215 | 70 | (module-ref (&compile-time-module) head))))) |
2335fb97 LC |
71 | (case head |
72 | ((defmacro define-macro) | |
73 | ;; Normally, these are expanded as `defmacro:transformer' but we | |
b89fc215 LC |
74 | ;; don't want it to happen since they are handled by `trans-pair'. |
75 | e) | |
76 | ||
77 | ((use-syntax) | |
78 | ;; `use-syntax' is used to express a compile-time dependency | |
79 | ;; (because we use a macro from that module, or because one of our | |
80 | ;; macros uses bindings from that module). Thus, we arrange to get | |
81 | ;; the current compile-time module to use it. | |
82 | (let* ((module-name (cadr e)) | |
83 | (module (false-if-exception (resolve-module module-name)))) | |
84 | (if (module? module) | |
85 | (let ((public-if (module-public-interface module))) | |
86 | (module-use! (&compile-time-module) public-if)) | |
87 | (syntax-error #f "invalid `use-syntax' form" e))) | |
88 | '(void)) | |
89 | ||
90 | ((begin let let* letrec lambda quote quasiquote if and or | |
91 | set! cond case eval-case define do) | |
92 | ;; All these built-in macros should not be expanded. | |
2335fb97 | 93 | e) |
b89fc215 | 94 | |
2335fb97 | 95 | (else |
b89fc215 LC |
96 | ;; Look for a macro. |
97 | (let ((ref (false-if-exception | |
98 | (module-ref (&compile-time-module) head)))) | |
99 | (if (macro? ref) | |
100 | (expand-macro | |
101 | (save-module-excursion | |
102 | (lambda () | |
103 | (let ((transformer (macro-transformer ref)) | |
104 | (syntax-error syntax-error)) | |
105 | (set-current-module (&compile-time-module)) | |
106 | (catch #t | |
107 | (lambda () | |
108 | (transformer (copy-tree e) (current-module))) | |
109 | (lambda (key . args) | |
110 | (syntax-error #f | |
111 | (format #f "~a: macro transformer failed" | |
112 | head) | |
113 | (cons key args)))))))) | |
114 | e)))))) | |
115 | ||
2335fb97 | 116 | (#t e))) |
c7228382 KN |
117 | |
118 | \f | |
119 | ;;; | |
120 | ;;; Translator | |
121 | ;;; | |
122 | ||
2335fb97 | 123 | (define %scheme-primitives |
8f5cfc81 KN |
124 | '(not null? eq? eqv? equal? pair? list? cons car cdr set-car! set-cdr!)) |
125 | ||
2335fb97 LC |
126 | (define %forbidden-primitives |
127 | ;; Guile's `procedure->macro' family is evil because it crosses the | |
128 | ;; compilation boundary. One solution might be to evaluate calls to | |
129 | ;; `procedure->memoizing-macro' at compilation time, but it may be more | |
130 | ;; compicated than that. | |
131 | '(procedure->syntax procedure->macro procedure->memoizing-macro)) | |
132 | ||
c7228382 KN |
133 | (define (trans e l x) |
134 | (cond ((pair? x) | |
b89fc215 LC |
135 | (let ((y (expand-macro x))) |
136 | (if (eq? x y) | |
137 | (trans-pair e (or (location x) l) (car x) (cdr x)) | |
138 | (trans e l y)))) | |
f21dfea6 | 139 | ((symbol? x) |
ac99cb0c KN |
140 | (let ((y (symbol-expand x))) |
141 | (if (symbol? y) | |
849cefac | 142 | (make-ghil-ref e l (ghil-lookup e y)) |
b6368dbb | 143 | (trans e l y)))) |
849cefac | 144 | (else (make-ghil-quote e l x)))) |
c7228382 | 145 | |
ac99cb0c | 146 | (define (symbol-expand x) |
f21dfea6 KN |
147 | (let loop ((s (symbol->string x))) |
148 | (let ((i (string-rindex s #\.))) | |
149 | (if i | |
ac99cb0c KN |
150 | (let ((sym (string->symbol (substring s (1+ i))))) |
151 | `(slot ,(loop (substring s 0 i)) (quote ,sym))) | |
152 | (string->symbol s))))) | |
f21dfea6 | 153 | |
f245e62c AW |
154 | (define (valid-bindings? bindings . it-is-for-do) |
155 | (define (valid-binding? b) | |
156 | (pmatch b | |
157 | ((,sym ,var) (guard (symbol? sym)) #t) | |
158 | ((,sym ,var ,update) (guard (pair? it-is-for-do) (symbol? sym)) #t) | |
159 | (else #f))) | |
160 | (and (list? bindings) (and-map valid-binding? bindings))) | |
161 | ||
c7228382 KN |
162 | (define (trans-pair e l head tail) |
163 | (define (trans:x x) (trans e l x)) | |
164 | (define (trans:pair x) (trans-pair e l (car x) (cdr x))) | |
165 | (define (trans:body body) (trans-body e l body)) | |
849cefac | 166 | (define (make:void) (make-ghil-void e l)) |
c7228382 KN |
167 | (define (bad-syntax) |
168 | (syntax-error l (format #f "bad ~A" head) (cons head tail))) | |
f245e62c AW |
169 | ;; have to use a case first, because pmatch treats e.g. (quote foo) |
170 | ;; and (unquote foo) specially | |
c7228382 KN |
171 | (case head |
172 | ;; (void) | |
173 | ((void) | |
f245e62c | 174 | (pmatch tail |
c7228382 KN |
175 | (() (make:void)) |
176 | (else (bad-syntax)))) | |
177 | ||
178 | ;; (quote OBJ) | |
179 | ((quote) | |
f245e62c AW |
180 | (pmatch tail |
181 | ((,obj) (make-ghil-quote e l obj)) | |
c7228382 KN |
182 | (else (bad-syntax)))) |
183 | ||
184 | ;; (quasiquote OBJ) | |
185 | ((quasiquote) | |
f245e62c AW |
186 | (pmatch tail |
187 | ((,obj) (make-ghil-quasiquote e l (trans-quasiquote e l obj))) | |
c7228382 KN |
188 | (else (bad-syntax)))) |
189 | ||
fbbc50ca | 190 | ((define) |
f245e62c | 191 | (pmatch tail |
c7228382 | 192 | ;; (define NAME VAL) |
9f8ec6eb | 193 | ((,name ,val) (guard (symbol? name)) |
f245e62c | 194 | (make-ghil-define e l (ghil-lookup e name) (trans:x val))) |
c7228382 KN |
195 | |
196 | ;; (define (NAME FORMALS...) BODY...) | |
f245e62c AW |
197 | (((,name . ,formals) . ,body) (guard (symbol? name)) |
198 | ;; -> (define NAME (lambda FORMALS BODY...)) | |
199 | (let ((val (trans:x `(lambda ,formals ,@body)))) | |
200 | (make-ghil-define e l (ghil-lookup e name) val))) | |
c7228382 KN |
201 | |
202 | (else (bad-syntax)))) | |
203 | ||
2335fb97 LC |
204 | ;; simple macros |
205 | ((defmacro define-macro) | |
b89fc215 LC |
206 | ;; Evaluate the macro definition in the current compile-time module. |
207 | (eval-at-compile-time (cons head tail)) | |
208 | ||
209 | ;; FIXME: We need to evaluate them in the runtime module as well. | |
210 | (make:void)) | |
2335fb97 | 211 | |
c7228382 | 212 | ((set!) |
f245e62c | 213 | (pmatch tail |
c7228382 | 214 | ;; (set! NAME VAL) |
f245e62c AW |
215 | ((,name ,val) (guard (symbol? name)) |
216 | (make-ghil-set e l (ghil-lookup e name) (trans:x val))) | |
c7228382 KN |
217 | |
218 | ;; (set! (NAME ARGS...) VAL) | |
f245e62c AW |
219 | (((,name . ,args) ,val) (guard (symbol? name)) |
220 | ;; -> ((setter NAME) ARGS... VAL) | |
221 | (trans:pair `((setter ,name) . (,@args ,val)))) | |
c7228382 KN |
222 | |
223 | (else (bad-syntax)))) | |
224 | ||
225 | ;; (if TEST THEN [ELSE]) | |
226 | ((if) | |
f245e62c AW |
227 | (pmatch tail |
228 | ((,test ,then) | |
229 | (make-ghil-if e l (trans:x test) (trans:x then) (make:void))) | |
230 | ((,test ,then ,else) | |
231 | (make-ghil-if e l (trans:x test) (trans:x then) (trans:x else))) | |
c7228382 KN |
232 | (else (bad-syntax)))) |
233 | ||
234 | ;; (and EXPS...) | |
235 | ((and) | |
849cefac | 236 | (make-ghil-and e l (map trans:x tail))) |
c7228382 KN |
237 | |
238 | ;; (or EXPS...) | |
239 | ((or) | |
849cefac | 240 | (make-ghil-or e l (map trans:x tail))) |
c7228382 KN |
241 | |
242 | ;; (begin EXPS...) | |
243 | ((begin) | |
849cefac | 244 | (make-ghil-begin e l (map trans:x tail))) |
c7228382 KN |
245 | |
246 | ((let) | |
f245e62c | 247 | (pmatch tail |
c7228382 | 248 | ;; (let NAME ((SYM VAL) ...) BODY...) |
f245e62c AW |
249 | ((,name ,bindings . ,body) (guard (symbol? name) |
250 | (valid-bindings? bindings)) | |
251 | ;; -> (letrec ((NAME (lambda (SYM...) BODY...))) (NAME VAL...)) | |
252 | (trans:pair `(letrec ((,name (lambda ,(map car bindings) ,@body))) | |
253 | (,name ,@(map cadr bindings))))) | |
c7228382 KN |
254 | |
255 | ;; (let () BODY...) | |
f245e62c AW |
256 | ((() . ,body) |
257 | ;; Note: this differs from `begin' | |
258 | (make-ghil-begin e l (list (trans:body body)))) | |
c7228382 KN |
259 | |
260 | ;; (let ((SYM VAL) ...) BODY...) | |
f245e62c | 261 | ((,bindings . ,body) (guard (valid-bindings? bindings)) |
9f8ec6eb AW |
262 | (let ((vals (map trans:x (map cadr bindings)))) |
263 | (call-with-ghil-bindings e (map car bindings) | |
f245e62c AW |
264 | (lambda (vars) |
265 | (make-ghil-bind e l vars vals (trans:body body)))))) | |
c7228382 KN |
266 | (else (bad-syntax)))) |
267 | ||
268 | ;; (let* ((SYM VAL) ...) BODY...) | |
269 | ((let*) | |
f245e62c AW |
270 | (pmatch tail |
271 | ((() . ,body) | |
272 | (trans:pair `(let () ,@body))) | |
273 | ((((,sym ,val) . ,rest) . ,body) (guard (symbol? sym)) | |
274 | (trans:pair `(let ((,sym ,val)) (let* ,rest ,@body)))) | |
c7228382 KN |
275 | (else (bad-syntax)))) |
276 | ||
277 | ;; (letrec ((SYM VAL) ...) BODY...) | |
278 | ((letrec) | |
f245e62c AW |
279 | (pmatch tail |
280 | ((,bindings . ,body) (guard (valid-bindings? bindings)) | |
281 | (call-with-ghil-bindings e (map car bindings) | |
282 | (lambda (vars) | |
283 | (let ((vals (map trans:x (map cadr bindings)))) | |
284 | (make-ghil-bind e l vars vals (trans:body body)))))) | |
c7228382 KN |
285 | (else (bad-syntax)))) |
286 | ||
287 | ;; (cond (CLAUSE BODY...) ...) | |
288 | ((cond) | |
f245e62c | 289 | (pmatch tail |
c7228382 | 290 | (() (make:void)) |
f245e62c AW |
291 | (((else . ,body)) (trans:body body)) |
292 | (((,test) . ,rest) (trans:pair `(or ,test (cond ,@rest)))) | |
293 | (((,test => ,proc) . ,rest) | |
294 | ;; FIXME hygiene! | |
295 | (trans:pair `(let ((_t ,test)) (if _t (,proc _t) (cond ,@rest))))) | |
296 | (((,test . ,body) . ,rest) | |
297 | (trans:pair `(if ,test (begin ,@body) (cond ,@rest)))) | |
c7228382 KN |
298 | (else (bad-syntax)))) |
299 | ||
300 | ;; (case EXP ((KEY...) BODY...) ...) | |
301 | ((case) | |
f245e62c AW |
302 | (pmatch tail |
303 | ((,exp . ,clauses) | |
304 | (trans:pair | |
305 | ;; FIXME hygiene! | |
306 | `(let ((_t ,exp)) | |
307 | ,(let loop ((ls clauses)) | |
308 | (cond ((null? ls) '(void)) | |
309 | ((eq? (caar ls) 'else) `(begin ,@(cdar ls))) | |
310 | (else `(if (memv _t ',(caar ls)) | |
311 | (begin ,@(cdar ls)) | |
312 | ,(loop (cdr ls))))))))) | |
c7228382 KN |
313 | (else (bad-syntax)))) |
314 | ||
315 | ;; (do ((SYM VAL [UPDATE]) ...) (TEST RESULT...) BODY...) | |
316 | ((do) | |
f245e62c AW |
317 | (pmatch tail |
318 | ((,bindings (,test . ,result) . ,body) | |
319 | (let ((sym (map car bindings)) | |
320 | (val (map cadr bindings)) | |
321 | (update (map cddr bindings))) | |
322 | (define (next s x) (if (pair? x) (car x) s)) | |
323 | (trans:pair | |
324 | ;; FIXME hygiene! | |
325 | `(letrec ((_l (lambda ,sym | |
326 | (if ,test | |
327 | (let () (void) ,@result) | |
328 | (let () (void) ,@body | |
329 | (_l ,@(map next sym update))))))) | |
9f8ec6eb | 330 | (_l ,@val))))) |
f245e62c | 331 | (else (bad-syntax)))) |
c7228382 KN |
332 | |
333 | ;; (lambda FORMALS BODY...) | |
334 | ((lambda) | |
f245e62c AW |
335 | (pmatch tail |
336 | ((,formals . ,body) | |
337 | (receive (syms rest) (parse-formals formals) | |
338 | (call-with-ghil-environment e syms | |
339 | (lambda (env vars) | |
340 | (make-ghil-lambda env l vars rest (trans-body env l body)))))) | |
c7228382 KN |
341 | (else (bad-syntax)))) |
342 | ||
343 | ((eval-case) | |
344 | (let loop ((x tail)) | |
f245e62c AW |
345 | (pmatch x |
346 | (() (make:void)) | |
347 | (((else . ,body)) (trans:pair `(begin ,@body))) | |
348 | (((,keys . ,body) . ,rest) (guard (list? keys) (and-map symbol? keys)) | |
349 | (if (memq 'load-toplevel keys) | |
350 | (begin | |
351 | (primitive-eval `(begin ,@(copy-tree body))) | |
352 | (trans:pair `(begin ,@body))) | |
353 | (loop rest))) | |
354 | (else (bad-syntax))))) | |
c7228382 KN |
355 | |
356 | (else | |
2335fb97 | 357 | (if (memq head %scheme-primitives) |
f245e62c AW |
358 | (make-ghil-inline e l head (map trans:x tail)) |
359 | (if (memq head %forbidden-primitives) | |
360 | (syntax-error l (format #f "`~a' is forbidden" head) | |
361 | (cons head tail)) | |
362 | (make-ghil-call e l (trans:x head) (map trans:x tail))))))) | |
c7228382 KN |
363 | |
364 | (define (trans-quasiquote e l x) | |
365 | (cond ((not (pair? x)) x) | |
366 | ((memq (car x) '(unquote unquote-splicing)) | |
367 | (let ((l (location x))) | |
f245e62c AW |
368 | (pmatch (cdr x) |
369 | ((,obj) | |
c7228382 | 370 | (if (eq? (car x) 'unquote) |
849cefac AW |
371 | (make-ghil-unquote e l (trans e l obj)) |
372 | (make-ghil-unquote-splicing e l (trans e l obj)))) | |
c7228382 KN |
373 | (else (syntax-error l (format #f "bad ~A" (car x)) x))))) |
374 | (else (cons (trans-quasiquote e l (car x)) | |
375 | (trans-quasiquote e l (cdr x)))))) | |
376 | ||
377 | (define (trans-body e l body) | |
378 | (define (define->binding df) | |
f245e62c AW |
379 | (pmatch (cdr df) |
380 | ((,name ,val) (guard (symbol? name)) (list name val)) | |
381 | (((,name . ,formals) . ,body) (guard (symbol? name)) | |
c7228382 KN |
382 | (list name `(lambda ,formals ,@body))) |
383 | (else (syntax-error (location df) "bad define" df)))) | |
384 | ;; main | |
385 | (let loop ((ls body) (ds '())) | |
be852e52 AW |
386 | (pmatch ls |
387 | (() (syntax-error l "bad body" body)) | |
388 | (((define . _) . _) | |
389 | (loop (cdr ls) (cons (car ls) ds))) | |
390 | (else | |
391 | (if (null? ds) | |
392 | (trans-pair e l 'begin ls) | |
393 | (trans-pair e l 'letrec (cons (map define->binding ds) ls))))))) | |
c7228382 KN |
394 | |
395 | (define (parse-formals formals) | |
396 | (cond | |
397 | ;; (lambda x ...) | |
398 | ((symbol? formals) (values (list formals) #t)) | |
399 | ;; (lambda (x y z) ...) | |
400 | ((list? formals) (values formals #f)) | |
401 | ;; (lambda (x y . z) ...) | |
402 | ((pair? formals) | |
403 | (let loop ((l formals) (v '())) | |
404 | (if (pair? l) | |
405 | (loop (cdr l) (cons (car l) v)) | |
406 | (values (reverse! (cons l v)) #t)))) | |
407 | (else (syntax-error (location formals) "bad formals" formals)))) | |
408 | ||
409 | (define (location x) | |
410 | (and (pair? x) | |
411 | (let ((props (source-properties x))) | |
412 | (and (not (null? props)) | |
413 | (cons (assq-ref props 'line) (assq-ref props 'column)))))) |