Evaluator uses two-dimensional environment
[bpt/guile.git] / module / ice-9 / eval.scm
CommitLineData
5161a3c0
AW
1;;; -*- mode: scheme; coding: utf-8; -*-
2
747bd534 3;;;; Copyright (C) 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
5161a3c0
AW
4;;;;
5;;;; This library is free software; you can redistribute it and/or
6;;;; modify it under the terms of the GNU Lesser General Public
7;;;; License as published by the Free Software Foundation; either
8;;;; version 3 of the License, or (at your option) any later version.
9;;;;
10;;;; This library 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 GNU
13;;;; Lesser General Public License for more details.
14;;;;
15;;;; You should have received a copy of the GNU Lesser General Public
16;;;; License along with this library; if not, write to the Free Software
17;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18;;;;
19
20\f
21
22;;; Commentary:
23
b2b554ef
AW
24;;; Scheme eval, written in Scheme.
25;;;
26;;; Expressions are first expanded, by the syntax expander (i.e.
27;;; psyntax), then memoized into internal forms. The evaluator itself
28;;; only operates on the internal forms ("memoized expressions").
29;;;
30;;; Environments are represented as linked lists of the form (VAL ... .
31;;; MOD). If MOD is #f, it means the environment was captured before
32;;; modules were booted. If MOD is the literal value '(), we are
33;;; evaluating at the top level, and so should track changes to the
34;;; current module.
35;;;
36;;; Evaluate this in Emacs to make code indentation work right:
37;;;
38;;; (put 'memoized-expression-case 'scheme-indent-function 1)
5161a3c0
AW
39;;;
40
41;;; Code:
42
43\f
44
5161a3c0
AW
45(eval-when (compile)
46 (define-syntax capture-env
47 (syntax-rules ()
bb0c8157
AW
48 ((_ (exp ...))
49 (let ((env (exp ...)))
50 (capture-env env)))
5161a3c0
AW
51 ((_ env)
52 (if (null? env)
53 (current-module)
54 (if (not env)
b2b554ef
AW
55 ;; the and current-module checks that modules are booted,
56 ;; and thus the-root-module is defined
5f161164 57 (and (current-module) the-root-module)
5161a3c0
AW
58 env)))))
59
cfc28c80
AW
60 (define-syntax env-toplevel
61 (syntax-rules ()
62 ((_ env)
63 (let lp ((e env))
64 (if (vector? e)
65 (lp (vector-ref e 0))
66 e)))))
67
68 (define-syntax make-env
69 (syntax-rules ()
70 ((_ n init next)
71 (let ((v (make-vector (1+ n) init)))
72 (vector-set! v 0 next)
73 v))))
74
75 (define-syntax make-env*
76 (syntax-rules ()
77 ((_ next init ...)
78 (vector next init ...))))
79
80 (define-syntax env-ref
81 (syntax-rules ()
82 ((_ env depth width)
83 (let lp ((e env) (d depth))
84 (if (zero? d)
85 (vector-ref e (1+ width))
86 (lp (vector-ref e 0) (1- d)))))))
87
88 (define-syntax env-set!
89 (syntax-rules ()
90 ((_ env depth width val)
91 (let lp ((e env) (d depth))
92 (if (zero? d)
93 (vector-set! e (1+ width) val)
94 (lp (vector-ref e 0) (1- d)))))))
95
d8a071fc
AW
96 ;; Fast case for procedures with fixed arities.
97 (define-syntax make-fixed-closure
4abb824c 98 (lambda (x)
9331f91c 99 (define *max-static-argument-count* 8)
4abb824c
AW
100 (define (make-formals n)
101 (map (lambda (i)
102 (datum->syntax
c438cd71 103 x
4abb824c
AW
104 (string->symbol
105 (string (integer->char (+ (char->integer #\a) i))))))
106 (iota n)))
107 (syntax-case x ()
d8a071fc 108 ((_ eval nreq body env) (not (identifier? #'env))
4abb824c 109 #'(let ((e env))
d8a071fc
AW
110 (make-fixed-closure eval nreq body e)))
111 ((_ eval nreq body env)
4abb824c
AW
112 #`(case nreq
113 #,@(map (lambda (nreq)
114 (let ((formals (make-formals nreq)))
115 #`((#,nreq)
d8a071fc
AW
116 (lambda (#,@formals)
117 (eval body
cfc28c80 118 (make-env* env #,@formals))))))
4abb824c
AW
119 (iota *max-static-argument-count*))
120 (else
121 #,(let ((formals (make-formals *max-static-argument-count*)))
122 #`(lambda (#,@formals . more)
cfc28c80
AW
123 (let ((env (make-env nreq #f env)))
124 #,@(map (lambda (formal n)
125 #`(env-set! env 0 #,n #,formal))
126 formals (iota (length formals)))
127 (let lp ((i #,*max-static-argument-count*)
128 (args more))
129 (cond
130 ((= i nreq)
4abb824c 131 (eval body
d8a071fc 132 (if (null? args)
cfc28c80 133 env
d8a071fc
AW
134 (scm-error 'wrong-number-of-args
135 "eval" "Wrong number of arguments"
cfc28c80
AW
136 '() #f))))
137 ((null? args)
138 (scm-error 'wrong-number-of-args
139 "eval" "Wrong number of arguments"
140 '() #f))
141 (else
142 (env-set! env 0 i (car args))
143 (lp (1+ i) (cdr args))))))))))))))
4abb824c 144
a4b64fa2
AW
145 ;; Fast case for procedures with fixed arities and a rest argument.
146 (define-syntax make-rest-closure
147 (lambda (x)
148 (define *max-static-argument-count* 3)
149 (define (make-formals n)
150 (map (lambda (i)
151 (datum->syntax
152 x
153 (string->symbol
154 (string (integer->char (+ (char->integer #\a) i))))))
155 (iota n)))
156 (syntax-case x ()
157 ((_ eval nreq body env) (not (identifier? #'env))
158 #'(let ((e env))
159 (make-rest-closure eval nreq body e)))
160 ((_ eval nreq body env)
161 #`(case nreq
162 #,@(map (lambda (nreq)
163 (let ((formals (make-formals nreq)))
164 #`((#,nreq)
165 (lambda (#,@formals . rest)
166 (eval body
cfc28c80 167 (make-env* env #,@formals rest))))))
a4b64fa2
AW
168 (iota *max-static-argument-count*))
169 (else
170 #,(let ((formals (make-formals *max-static-argument-count*)))
171 #`(lambda (#,@formals . more)
cfc28c80
AW
172 (let ((env (make-env (1+ nreq) #f env)))
173 #,@(map (lambda (formal n)
174 #`(env-set! env 0 #,n #,formal))
175 formals (iota (length formals)))
176 (let lp ((i #,*max-static-argument-count*)
177 (args more))
178 (cond
179 ((= i nreq)
180 (env-set! env 0 nreq args)
181 (eval body env))
182 ((null? args)
183 (scm-error 'wrong-number-of-args
184 "eval" "Wrong number of arguments"
185 '() #f))
186 (else
187 (env-set! env 0 i (car args))
188 (lp (1+ i) (cdr args))))))))))))))
a4b64fa2 189
9331f91c
AW
190 (define-syntax call
191 (lambda (x)
192 (define *max-static-call-count* 4)
193 (syntax-case x ()
194 ((_ eval proc nargs args env) (identifier? #'env)
195 #`(case nargs
196 #,@(map (lambda (nargs)
197 #`((#,nargs)
198 (proc
199 #,@(map
200 (lambda (n)
201 (let lp ((n n) (args #'args))
202 (if (zero? n)
203 #`(eval (car #,args) env)
204 (lp (1- n) #`(cdr #,args)))))
205 (iota nargs)))))
206 (iota *max-static-call-count*))
207 (else
208 (apply proc
209 #,@(map
210 (lambda (n)
211 (let lp ((n n) (args #'args))
212 (if (zero? n)
213 #`(eval (car #,args) env)
214 (lp (1- n) #`(cdr #,args)))))
215 (iota *max-static-call-count*))
216 (let lp ((exps #,(let lp ((n *max-static-call-count*)
217 (args #'args))
218 (if (zero? n)
219 args
220 (lp (1- n) #`(cdr #,args)))))
221 (args '()))
222 (if (null? exps)
223 (reverse args)
224 (lp (cdr exps)
225 (cons (eval (car exps) env) args)))))))))))
226
b2b554ef
AW
227 ;; This macro could be more straightforward if the compiler had better
228 ;; copy propagation. As it is we do some copy propagation by hand.
5161a3c0
AW
229 (define-syntax mx-bind
230 (lambda (x)
231 (syntax-case x ()
232 ((_ data () body)
233 #'body)
234 ((_ data (a . b) body) (and (identifier? #'a) (identifier? #'b))
235 #'(let ((a (car data))
236 (b (cdr data)))
237 body))
238 ((_ data (a . b) body) (identifier? #'a)
239 #'(let ((a (car data))
240 (xb (cdr data)))
241 (mx-bind xb b body)))
242 ((_ data (a . b) body)
243 #'(let ((xa (car data))
244 (xb (cdr data)))
245 (mx-bind xa a (mx-bind xb b body))))
246 ((_ data v body) (identifier? #'v)
247 #'(let ((v data))
248 body)))))
249
b2b554ef
AW
250 ;; The resulting nested if statements will be an O(n) dispatch. Once
251 ;; we compile `case' effectively, this situation will improve.
5161a3c0
AW
252 (define-syntax mx-match
253 (lambda (x)
254 (syntax-case x (quote)
255 ((_ mx data tag)
256 #'(error "what" mx))
257 ((_ mx data tag (('type pat) body) c* ...)
258 #`(if (eqv? tag #,(or (memoized-typecode (syntax->datum #'type))
259 (error "not a typecode" #'type)))
260 (mx-bind data pat body)
261 (mx-match mx data tag c* ...))))))
262
263 (define-syntax memoized-expression-case
264 (lambda (x)
265 (syntax-case x ()
266 ((_ mx c ...)
267 #'(let ((tag (memoized-expression-typecode mx))
268 (data (memoized-expression-data mx)))
269 (mx-match mx data tag c ...)))))))
270
271
21ec0bd9
AW
272;;;
273;;; On 18 Feb 2010, I did a profile of how often the various memoized expression
274;;; types occur when getting to a prompt on a fresh build. Here are the numbers
275;;; I got:
276;;;
277;;; lexical-ref: 32933054
278;;; call: 20281547
279;;; toplevel-ref: 13228724
280;;; if: 9156156
281;;; quote: 6610137
282;;; let: 2619707
283;;; lambda: 1010921
284;;; begin: 948945
285;;; lexical-set: 509862
286;;; call-with-values: 139668
287;;; apply: 49402
288;;; module-ref: 14468
289;;; define: 1259
290;;; toplevel-set: 328
21ec0bd9
AW
291;;; call/cc: 0
292;;; module-set: 0
293;;;
294;;; So until we compile `case' into a computed goto, we'll order the clauses in
295;;; `eval' in this order, to put the most frequent cases first.
296;;;
297
5161a3c0
AW
298(define primitive-eval
299 (let ()
a4b64fa2
AW
300 ;; We pre-generate procedures with fixed arities, up to some number
301 ;; of arguments, and some rest arities; see make-fixed-closure and
302 ;; make-rest-closure above.
d8a071fc
AW
303
304 ;; A unique marker for unbound keywords.
305 (define unbound-arg (list 'unbound-arg))
306
7572ee52
AW
307 ;; Procedures with rest, optional, or keyword arguments, potentially with
308 ;; multiple arities, as with case-lambda.
d8a071fc 309 (define (make-general-closure env body nreq rest? nopt kw inits alt)
7572ee52 310 (define alt-proc
a4b64fa2 311 (and alt ; (body docstring nreq ...)
dc3e203e 312 (let* ((body (car alt))
c438cd71
LC
313 (spec (cddr alt))
314 (nreq (car spec))
315 (rest (if (null? (cdr spec)) #f (cadr spec)))
316 (tail (and (pair? (cdr spec)) (pair? (cddr spec)) (cddr spec)))
dc3e203e
AW
317 (nopt (if tail (car tail) 0))
318 (kw (and tail (cadr tail)))
319 (inits (if tail (caddr tail) '()))
320 (alt (and tail (cadddr tail))))
321 (make-general-closure env body nreq rest nopt kw inits alt))))
f3cf9421
AW
322 (define (set-procedure-arity! proc)
323 (let lp ((alt alt) (nreq nreq) (nopt nopt) (rest? rest?))
324 (if (not alt)
fc835b1b
AW
325 (begin
326 (set-procedure-property! proc 'arglist
327 (list nreq
328 nopt
329 (if kw (cdr kw) '())
330 (and kw (car kw))
331 (and rest? '_)))
332 (set-procedure-minimum-arity! proc nreq nopt rest?))
c438cd71
LC
333 (let* ((spec (cddr alt))
334 (nreq* (car spec))
335 (rest?* (if (null? (cdr spec)) #f (cadr spec)))
336 (tail (and (pair? (cdr spec)) (pair? (cddr spec)) (cddr spec)))
f3cf9421
AW
337 (nopt* (if tail (car tail) 0))
338 (alt* (and tail (cadddr tail))))
339 (if (or (< nreq* nreq)
340 (and (= nreq* nreq)
341 (if rest?
342 (and rest?* (> nopt* nopt))
343 (or rest?* (> nopt* nopt)))))
344 (lp alt* nreq* nopt* rest?*)
345 (lp alt* nreq nopt rest?)))))
346 proc)
347 (set-procedure-arity!
348 (lambda %args
cfc28c80
AW
349 (define (npositional args)
350 (let lp ((n 0) (args args))
351 (if (or (null? args)
352 (and (>= n nreq) (keyword? (car args))))
353 n
354 (lp (1+ n) (cdr args)))))
355 (let ((nargs (length %args)))
356 (cond
357 ((or (< nargs nreq)
358 (and (not kw) (not rest?) (> nargs (+ nreq nopt)))
359 (and kw (not rest?) (> (npositional %args) (+ nreq nopt))))
360 (if alt
361 (apply alt-proc %args)
362 ((scm-error 'wrong-number-of-args
363 "eval" "Wrong number of arguments"
364 '() #f))))
365 (else
366 (let* ((nvals (+ nreq (if rest? 1 0) (length inits)))
367 (env (make-env nvals unbound-arg env)))
368 (let lp ((i 0) (args %args))
369 (cond
370 ((< i nreq)
371 ;; Bind required arguments.
372 (env-set! env 0 i (car args))
373 (lp (1+ i) (cdr args)))
374 ((not kw)
375 ;; Optional args (possibly), but no keyword args.
376 (let lp ((i i) (args args) (inits inits))
581f410f 377 (cond
cfc28c80
AW
378 ((< i (+ nreq nopt))
379 (cond
380 ((< i nargs)
381 (env-set! env 0 i (car args))
382 (lp (1+ i) (cdr args) (cdr inits)))
383 (else
384 (env-set! env 0 i (eval (car inits) env))
385 (lp (1+ i) args (cdr inits)))))
581f410f 386 (else
cfc28c80
AW
387 (when rest?
388 (env-set! env 0 i args))
389 (eval body env)))))
390 (else
391 ;; Optional args. As before, but stop at the first
392 ;; keyword.
393 (let lp ((i i) (args args) (inits inits))
394 (cond
395 ((< i (+ nreq nopt))
396 (cond
397 ((and (< i nargs) (not (keyword? (car args))))
398 (env-set! env 0 i (car args))
399 (lp (1+ i) (cdr args) (cdr inits)))
400 (else
401 (env-set! env 0 i (eval (car inits) env))
402 (lp (1+ i) args (cdr inits)))))
403 (else
404 (when rest?
405 (env-set! env 0 i args))
406 (let ((aok (car kw))
407 (kw (cdr kw))
408 (kw-base (if rest? (1+ i) i)))
581f410f
AW
409 ;; Now scan args for keywords.
410 (let lp ((args args))
cfc28c80
AW
411 (cond
412 ((and (pair? args) (pair? (cdr args))
413 (keyword? (car args)))
414 (let ((kw-pair (assq (car args) kw))
415 (v (cadr args)))
416 (if kw-pair
417 ;; Found a known keyword; set its value.
418 (env-set! env 0 (cdr kw-pair) v)
419 ;; Unknown keyword.
420 (if (not aok)
421 ((scm-error
422 'keyword-argument-error
423 "eval" "Unrecognized keyword"
424 '() (list (car args))))))
425 (lp (cddr args))))
426 ((pair? args)
427 (if rest?
428 ;; Be lenient parsing rest args.
429 (lp (cdr args))
430 ((scm-error 'keyword-argument-error
431 "eval" "Invalid keyword"
432 '() (list (car args))))))
433 (else
434 ;; Finished parsing keywords. Fill in
435 ;; uninitialized kwargs by evalling init
436 ;; expressions in their appropriate
437 ;; environment.
438 (let lp ((i kw-base) (inits inits))
439 (cond
440 ((pair? inits)
441 (when (eq? (env-ref env 0 i) unbound-arg)
442 (env-set! env 0 i (eval (car inits) env)))
443 (lp (1+ i) (cdr inits)))
444 (else
445 ;; Finally, eval the body.
446 (eval body env)))))))))))))))))))))
d8a071fc 447
b2b554ef 448 ;; The "engine". EXP is a memoized expression.
5161a3c0
AW
449 (define (eval exp env)
450 (memoized-expression-case exp
cfc28c80
AW
451 (('lexical-ref (depth . width))
452 (env-ref env depth width))
bb0c8157 453
21ec0bd9
AW
454 (('call (f nargs . args))
455 (let ((proc (eval f env)))
456 (call eval proc nargs args env)))
457
458 (('toplevel-ref var-or-sym)
459 (variable-ref
460 (if (variable? var-or-sym)
461 var-or-sym
bb0c8157 462 (memoize-variable-access! exp
cfc28c80 463 (capture-env (env-toplevel env))))))
21ec0bd9 464
5161a3c0
AW
465 (('if (test consequent . alternate))
466 (if (eval test env)
467 (eval consequent env)
468 (eval alternate env)))
469
21ec0bd9
AW
470 (('quote x)
471 x)
472
5161a3c0 473 (('let (inits . body))
cfc28c80
AW
474 (let* ((width (vector-length inits))
475 (new-env (make-env width #f (capture-env env))))
476 (let lp ((i 0))
477 (when (< i width)
478 (env-set! new-env 0 i (eval (vector-ref inits i) env))
479 (lp (1+ i))))
480 (eval body new-env)))
c438cd71
LC
481
482 (('lambda (body docstring nreq . tail))
483 (let ((proc
484 (if (null? tail)
485 (make-fixed-closure eval nreq body (capture-env env))
486 (if (null? (cdr tail))
a4b64fa2 487 (make-rest-closure eval nreq body (capture-env env))
c438cd71
LC
488 (apply make-general-closure (capture-env env)
489 body nreq tail)))))
490 (when docstring
491 (set-procedure-property! proc 'documentation docstring))
492 proc))
d8a071fc 493
6fc3eae4
AW
494 (('seq (head . tail))
495 (begin
496 (eval head env)
497 (eval tail env)))
498
cfc28c80
AW
499 (('lexical-set! ((depth . width) . x))
500 (env-set! env depth width (eval x env)))
5161a3c0 501
21ec0bd9
AW
502 (('call-with-values (producer . consumer))
503 (call-with-values (eval producer env)
504 (eval consumer env)))
505
506 (('apply (f args))
507 (apply (eval f env) (eval args env)))
508
509 (('module-ref var-or-spec)
5161a3c0 510 (variable-ref
21ec0bd9
AW
511 (if (variable? var-or-spec)
512 var-or-spec
513 (memoize-variable-access! exp #f))))
5161a3c0 514
21ec0bd9 515 (('define (name . x))
ee15aa46
AW
516 (let ((x (eval x env)))
517 (if (and (procedure? x) (not (procedure-property x 'name)))
518 (set-procedure-property! x 'name name))
adb8054c
MW
519 (define! name x)
520 (if #f #f)))
21ec0bd9 521
5161a3c0
AW
522 (('toplevel-set! (var-or-sym . x))
523 (variable-set!
524 (if (variable? var-or-sym)
525 var-or-sym
bb0c8157 526 (memoize-variable-access! exp
cfc28c80 527 (capture-env (env-toplevel env))))
5161a3c0
AW
528 (eval x env)))
529
1773bc7d
AW
530 (('call-with-prompt (tag thunk . handler))
531 (call-with-prompt
532 (eval tag env)
533 (eval thunk env)
534 (eval handler env)))
747022e4 535
21ec0bd9
AW
536 (('call/cc proc)
537 (call/cc (eval proc env)))
5161a3c0
AW
538
539 (('module-set! (x . var-or-spec))
540 (variable-set!
541 (if (variable? var-or-spec)
542 var-or-spec
543 (memoize-variable-access! exp #f))
544 (eval x env)))))
545
b2b554ef 546 ;; primitive-eval
5161a3c0 547 (lambda (exp)
b2b554ef 548 "Evaluate @var{exp} in the current module."
5161a3c0 549 (eval
a310a1d1
AW
550 (memoize-expression
551 (if (macroexpanded? exp)
552 exp
553 ((module-transformer (current-module)) exp)))
5161a3c0 554 '()))))