Minor evaluator tweaks
[bpt/guile.git] / module / ice-9 / eval.scm
CommitLineData
5161a3c0
AW
1;;; -*- mode: scheme; coding: utf-8; -*-
2
1487367e 3;;;; Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 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 45(eval-when (compile)
cfc28c80
AW
46 (define-syntax env-toplevel
47 (syntax-rules ()
48 ((_ env)
49 (let lp ((e env))
50 (if (vector? e)
51 (lp (vector-ref e 0))
52 e)))))
53
54 (define-syntax make-env
55 (syntax-rules ()
56 ((_ n init next)
57 (let ((v (make-vector (1+ n) init)))
58 (vector-set! v 0 next)
59 v))))
60
61 (define-syntax make-env*
62 (syntax-rules ()
63 ((_ next init ...)
64 (vector next init ...))))
65
66 (define-syntax env-ref
67 (syntax-rules ()
68 ((_ env depth width)
69 (let lp ((e env) (d depth))
70 (if (zero? d)
71 (vector-ref e (1+ width))
72 (lp (vector-ref e 0) (1- d)))))))
73
74 (define-syntax env-set!
75 (syntax-rules ()
76 ((_ env depth width val)
77 (let lp ((e env) (d depth))
78 (if (zero? d)
79 (vector-set! e (1+ width) val)
80 (lp (vector-ref e 0) (1- d)))))))
81
be6e40a1
AW
82 ;; For evaluating the initializers in a "let" expression. We have to
83 ;; evaluate the initializers before creating the environment rib, to
84 ;; prevent continuation-related shenanigans; see
85 ;; http://wingolog.org/archives/2013/11/02/scheme-quiz-time for a
86 ;; deeper discussion.
87 ;;
88 ;; This macro will inline evaluation of the first N initializers.
89 ;; That number N is indicated by the number of template arguments
90 ;; passed to the macro. It's a bit nasty but it's flexible and
91 ;; optimizes well.
92 (define-syntax let-env-evaluator
93 (syntax-rules ()
94 ((eval-and-make-env eval env (template ...))
95 (let ()
96 (define-syntax eval-and-make-env
97 (syntax-rules ()
98 ((eval-and-make-env inits width (template ...) k)
99 (let lp ((n (length '(template ...))) (vals '()))
100 (if (eqv? n width)
101 (let ((env (make-env n #f env)))
102 (let lp ((n (1- n)) (vals vals))
103 (if (null? vals)
104 (k env)
105 (begin
106 (env-set! env 0 n (car vals))
107 (lp (1- n) (cdr vals))))))
108 (lp (1+ n)
109 (cons (eval (vector-ref inits n) env) vals)))))
110 ((eval-and-make-env inits width (var (... ...)) k)
111 (let ((n (length '(var (... ...)))))
112 (if (eqv? n width)
113 (k (make-env n #f env))
114 (let* ((x (eval (vector-ref inits n) env))
115 (k (lambda (env)
116 (env-set! env 0 n x)
117 (k env))))
118 (eval-and-make-env inits width (x var (... ...)) k)))))))
119 (lambda (inits)
120 (let ((width (vector-length inits))
121 (k (lambda (env) env)))
122 (eval-and-make-env inits width () k)))))))
123
d8a071fc
AW
124 ;; Fast case for procedures with fixed arities.
125 (define-syntax make-fixed-closure
4abb824c 126 (lambda (x)
9331f91c 127 (define *max-static-argument-count* 8)
4abb824c
AW
128 (define (make-formals n)
129 (map (lambda (i)
130 (datum->syntax
c438cd71 131 x
4abb824c
AW
132 (string->symbol
133 (string (integer->char (+ (char->integer #\a) i))))))
134 (iota n)))
135 (syntax-case x ()
d8a071fc 136 ((_ eval nreq body env) (not (identifier? #'env))
4abb824c 137 #'(let ((e env))
d8a071fc
AW
138 (make-fixed-closure eval nreq body e)))
139 ((_ eval nreq body env)
4abb824c
AW
140 #`(case nreq
141 #,@(map (lambda (nreq)
142 (let ((formals (make-formals nreq)))
143 #`((#,nreq)
d8a071fc
AW
144 (lambda (#,@formals)
145 (eval body
cfc28c80 146 (make-env* env #,@formals))))))
4abb824c
AW
147 (iota *max-static-argument-count*))
148 (else
149 #,(let ((formals (make-formals *max-static-argument-count*)))
150 #`(lambda (#,@formals . more)
cfc28c80
AW
151 (let ((env (make-env nreq #f env)))
152 #,@(map (lambda (formal n)
153 #`(env-set! env 0 #,n #,formal))
154 formals (iota (length formals)))
155 (let lp ((i #,*max-static-argument-count*)
156 (args more))
157 (cond
158 ((= i nreq)
4abb824c 159 (eval body
d8a071fc 160 (if (null? args)
cfc28c80 161 env
d8a071fc
AW
162 (scm-error 'wrong-number-of-args
163 "eval" "Wrong number of arguments"
cfc28c80
AW
164 '() #f))))
165 ((null? args)
166 (scm-error 'wrong-number-of-args
167 "eval" "Wrong number of arguments"
168 '() #f))
169 (else
170 (env-set! env 0 i (car args))
171 (lp (1+ i) (cdr args))))))))))))))
4abb824c 172
a4b64fa2
AW
173 ;; Fast case for procedures with fixed arities and a rest argument.
174 (define-syntax make-rest-closure
175 (lambda (x)
176 (define *max-static-argument-count* 3)
177 (define (make-formals n)
178 (map (lambda (i)
179 (datum->syntax
180 x
181 (string->symbol
182 (string (integer->char (+ (char->integer #\a) i))))))
183 (iota n)))
184 (syntax-case x ()
185 ((_ eval nreq body env) (not (identifier? #'env))
186 #'(let ((e env))
187 (make-rest-closure eval nreq body e)))
188 ((_ eval nreq body env)
189 #`(case nreq
190 #,@(map (lambda (nreq)
191 (let ((formals (make-formals nreq)))
192 #`((#,nreq)
193 (lambda (#,@formals . rest)
194 (eval body
cfc28c80 195 (make-env* env #,@formals rest))))))
a4b64fa2
AW
196 (iota *max-static-argument-count*))
197 (else
198 #,(let ((formals (make-formals *max-static-argument-count*)))
199 #`(lambda (#,@formals . more)
cfc28c80
AW
200 (let ((env (make-env (1+ nreq) #f env)))
201 #,@(map (lambda (formal n)
202 #`(env-set! env 0 #,n #,formal))
203 formals (iota (length formals)))
204 (let lp ((i #,*max-static-argument-count*)
205 (args more))
206 (cond
207 ((= i nreq)
208 (env-set! env 0 nreq args)
209 (eval body env))
210 ((null? args)
211 (scm-error 'wrong-number-of-args
212 "eval" "Wrong number of arguments"
213 '() #f))
214 (else
215 (env-set! env 0 i (car args))
216 (lp (1+ i) (cdr args))))))))))))))
a4b64fa2 217
9331f91c
AW
218 (define-syntax call
219 (lambda (x)
220 (define *max-static-call-count* 4)
221 (syntax-case x ()
222 ((_ eval proc nargs args env) (identifier? #'env)
223 #`(case nargs
224 #,@(map (lambda (nargs)
225 #`((#,nargs)
226 (proc
227 #,@(map
228 (lambda (n)
229 (let lp ((n n) (args #'args))
230 (if (zero? n)
231 #`(eval (car #,args) env)
232 (lp (1- n) #`(cdr #,args)))))
233 (iota nargs)))))
234 (iota *max-static-call-count*))
235 (else
236 (apply proc
237 #,@(map
238 (lambda (n)
239 (let lp ((n n) (args #'args))
240 (if (zero? n)
241 #`(eval (car #,args) env)
242 (lp (1- n) #`(cdr #,args)))))
243 (iota *max-static-call-count*))
244 (let lp ((exps #,(let lp ((n *max-static-call-count*)
245 (args #'args))
246 (if (zero? n)
247 args
248 (lp (1- n) #`(cdr #,args)))))
249 (args '()))
250 (if (null? exps)
251 (reverse args)
252 (lp (cdr exps)
253 (cons (eval (car exps) env) args)))))))))))
254
b2b554ef
AW
255 ;; This macro could be more straightforward if the compiler had better
256 ;; copy propagation. As it is we do some copy propagation by hand.
5161a3c0
AW
257 (define-syntax mx-bind
258 (lambda (x)
259 (syntax-case x ()
260 ((_ data () body)
261 #'body)
262 ((_ data (a . b) body) (and (identifier? #'a) (identifier? #'b))
263 #'(let ((a (car data))
264 (b (cdr data)))
265 body))
266 ((_ data (a . b) body) (identifier? #'a)
267 #'(let ((a (car data))
268 (xb (cdr data)))
269 (mx-bind xb b body)))
270 ((_ data (a . b) body)
271 #'(let ((xa (car data))
272 (xb (cdr data)))
273 (mx-bind xa a (mx-bind xb b body))))
274 ((_ data v body) (identifier? #'v)
275 #'(let ((v data))
276 body)))))
277
b2b554ef
AW
278 ;; The resulting nested if statements will be an O(n) dispatch. Once
279 ;; we compile `case' effectively, this situation will improve.
5161a3c0
AW
280 (define-syntax mx-match
281 (lambda (x)
5bfc0653 282 (syntax-case x (quote else)
5161a3c0
AW
283 ((_ mx data tag)
284 #'(error "what" mx))
5bfc0653
AW
285 ((_ mx data tag (else body))
286 #'body)
5161a3c0
AW
287 ((_ mx data tag (('type pat) body) c* ...)
288 #`(if (eqv? tag #,(or (memoized-typecode (syntax->datum #'type))
289 (error "not a typecode" #'type)))
290 (mx-bind data pat body)
291 (mx-match mx data tag c* ...))))))
292
293 (define-syntax memoized-expression-case
294 (lambda (x)
295 (syntax-case x ()
296 ((_ mx c ...)
0720f70e
AW
297 #'(let ((tag (car mx))
298 (data (cdr mx)))
5161a3c0
AW
299 (mx-match mx data tag c ...)))))))
300
301
21ec0bd9
AW
302;;;
303;;; On 18 Feb 2010, I did a profile of how often the various memoized expression
304;;; types occur when getting to a prompt on a fresh build. Here are the numbers
305;;; I got:
306;;;
307;;; lexical-ref: 32933054
308;;; call: 20281547
309;;; toplevel-ref: 13228724
310;;; if: 9156156
311;;; quote: 6610137
312;;; let: 2619707
313;;; lambda: 1010921
314;;; begin: 948945
315;;; lexical-set: 509862
316;;; call-with-values: 139668
317;;; apply: 49402
318;;; module-ref: 14468
319;;; define: 1259
320;;; toplevel-set: 328
21ec0bd9
AW
321;;; call/cc: 0
322;;; module-set: 0
323;;;
324;;; So until we compile `case' into a computed goto, we'll order the clauses in
325;;; `eval' in this order, to put the most frequent cases first.
326;;;
327
5161a3c0
AW
328(define primitive-eval
329 (let ()
a4b64fa2
AW
330 ;; We pre-generate procedures with fixed arities, up to some number
331 ;; of arguments, and some rest arities; see make-fixed-closure and
332 ;; make-rest-closure above.
d8a071fc 333
7572ee52
AW
334 ;; Procedures with rest, optional, or keyword arguments, potentially with
335 ;; multiple arities, as with case-lambda.
cfdc8416
AW
336 (define (make-general-closure env body nreq rest? nopt kw ninits unbound
337 alt)
7572ee52 338 (define alt-proc
27ecfd36 339 (and alt ; (body meta nreq ...)
dc3e203e 340 (let* ((body (car alt))
c438cd71
LC
341 (spec (cddr alt))
342 (nreq (car spec))
343 (rest (if (null? (cdr spec)) #f (cadr spec)))
344 (tail (and (pair? (cdr spec)) (pair? (cddr spec)) (cddr spec)))
dc3e203e
AW
345 (nopt (if tail (car tail) 0))
346 (kw (and tail (cadr tail)))
cfdc8416
AW
347 (ninits (if tail (caddr tail) 0))
348 (unbound (and tail (cadddr tail)))
349 (alt (and tail (car (cddddr tail)))))
350 (make-general-closure env body nreq rest nopt kw ninits unbound
351 alt))))
f3cf9421
AW
352 (define (set-procedure-arity! proc)
353 (let lp ((alt alt) (nreq nreq) (nopt nopt) (rest? rest?))
354 (if (not alt)
fc835b1b
AW
355 (begin
356 (set-procedure-property! proc 'arglist
357 (list nreq
358 nopt
359 (if kw (cdr kw) '())
360 (and kw (car kw))
361 (and rest? '_)))
362 (set-procedure-minimum-arity! proc nreq nopt rest?))
c438cd71
LC
363 (let* ((spec (cddr alt))
364 (nreq* (car spec))
365 (rest?* (if (null? (cdr spec)) #f (cadr spec)))
366 (tail (and (pair? (cdr spec)) (pair? (cddr spec)) (cddr spec)))
f3cf9421 367 (nopt* (if tail (car tail) 0))
cfdc8416 368 (alt* (and tail (car (cddddr tail)))))
f3cf9421
AW
369 (if (or (< nreq* nreq)
370 (and (= nreq* nreq)
371 (if rest?
372 (and rest?* (> nopt* nopt))
373 (or rest?* (> nopt* nopt)))))
374 (lp alt* nreq* nopt* rest?*)
375 (lp alt* nreq nopt rest?)))))
376 proc)
377 (set-procedure-arity!
378 (lambda %args
cfc28c80
AW
379 (define (npositional args)
380 (let lp ((n 0) (args args))
381 (if (or (null? args)
382 (and (>= n nreq) (keyword? (car args))))
383 n
384 (lp (1+ n) (cdr args)))))
385 (let ((nargs (length %args)))
386 (cond
387 ((or (< nargs nreq)
388 (and (not kw) (not rest?) (> nargs (+ nreq nopt)))
6a59420a 389 (and alt kw (not rest?) (> (npositional %args) (+ nreq nopt))))
cfc28c80
AW
390 (if alt
391 (apply alt-proc %args)
392 ((scm-error 'wrong-number-of-args
393 "eval" "Wrong number of arguments"
394 '() #f))))
395 (else
cfdc8416
AW
396 (let* ((nvals (+ nreq (if rest? 1 0) ninits))
397 (env (make-env nvals unbound env)))
cfc28c80
AW
398 (let lp ((i 0) (args %args))
399 (cond
400 ((< i nreq)
401 ;; Bind required arguments.
402 (env-set! env 0 i (car args))
403 (lp (1+ i) (cdr args)))
404 ((not kw)
405 ;; Optional args (possibly), but no keyword args.
cfdc8416 406 (let lp ((i i) (args args))
581f410f 407 (cond
cfdc8416
AW
408 ((and (< i (+ nreq nopt)) (< i nargs))
409 (env-set! env 0 i (car args))
410 (lp (1+ i) (cdr args)))
581f410f 411 (else
cfc28c80 412 (when rest?
cfdc8416 413 (env-set! env 0 (+ nreq nopt) args))
cfc28c80
AW
414 (eval body env)))))
415 (else
416 ;; Optional args. As before, but stop at the first
417 ;; keyword.
cfdc8416 418 (let lp ((i i) (args args))
cfc28c80 419 (cond
cfdc8416
AW
420 ((and (< i (+ nreq nopt))
421 (< i nargs)
422 (not (keyword? (car args))))
423 (env-set! env 0 i (car args))
424 (lp (1+ i) (cdr args)))
cfc28c80
AW
425 (else
426 (when rest?
cfdc8416 427 (env-set! env 0 (+ nreq nopt) args))
cfc28c80 428 (let ((aok (car kw))
cfdc8416 429 (kw (cdr kw)))
581f410f
AW
430 ;; Now scan args for keywords.
431 (let lp ((args args))
cfc28c80
AW
432 (cond
433 ((and (pair? args) (pair? (cdr args))
434 (keyword? (car args)))
435 (let ((kw-pair (assq (car args) kw))
436 (v (cadr args)))
437 (if kw-pair
438 ;; Found a known keyword; set its value.
439 (env-set! env 0 (cdr kw-pair) v)
440 ;; Unknown keyword.
441 (if (not aok)
442 ((scm-error
443 'keyword-argument-error
444 "eval" "Unrecognized keyword"
445 '() (list (car args))))))
446 (lp (cddr args))))
447 ((pair? args)
448 (if rest?
449 ;; Be lenient parsing rest args.
450 (lp (cdr args))
451 ((scm-error 'keyword-argument-error
452 "eval" "Invalid keyword"
453 '() (list (car args))))))
454 (else
cfdc8416
AW
455 ;; Finally, eval the body.
456 (eval body env))))))))))))))))))
d8a071fc 457
b2b554ef 458 ;; The "engine". EXP is a memoized expression.
5161a3c0
AW
459 (define (eval exp env)
460 (memoized-expression-case exp
cfc28c80
AW
461 (('lexical-ref (depth . width))
462 (env-ref env depth width))
bb0c8157 463
21ec0bd9
AW
464 (('call (f nargs . args))
465 (let ((proc (eval f env)))
466 (call eval proc nargs args env)))
467
e6a42e67 468 (('box-ref box)
5bfc0653
AW
469 (memoized-expression-case box
470 ;; Accelerate common cases.
471 (('resolve var-or-loc)
472 (if (variable? var-or-loc)
473 (variable-ref var-or-loc)
474 (variable-ref (eval box env))))
475 (('lexical-ref (depth . width))
476 (variable-ref (env-ref env depth width)))
477 (else
478 (variable-ref (eval box env)))))
e6a42e67
AW
479
480 (('resolve var-or-loc)
481 (if (variable? var-or-loc)
482 var-or-loc
483 (let ((var (%resolve-variable var-or-loc (env-toplevel env))))
484 (set-cdr! exp var)
485 var)))
21ec0bd9 486
5161a3c0
AW
487 (('if (test consequent . alternate))
488 (if (eval test env)
489 (eval consequent env)
490 (eval alternate env)))
491
21ec0bd9
AW
492 (('quote x)
493 x)
494
5161a3c0 495 (('let (inits . body))
be6e40a1 496 (eval body ((let-env-evaluator eval env (_ _ _ _)) inits)))
c438cd71 497
27ecfd36 498 (('lambda (body meta nreq . tail))
c438cd71
LC
499 (let ((proc
500 (if (null? tail)
ef47c422 501 (make-fixed-closure eval nreq body env)
d0d8a552
AW
502 (mx-bind
503 tail (rest? . tail)
504 (if (null? tail)
505 (make-rest-closure eval nreq body env)
506 (mx-bind
cfdc8416 507 tail (nopt kw ninits unbound alt)
d0d8a552 508 (make-general-closure env body nreq rest?
cfdc8416
AW
509 nopt kw ninits unbound
510 alt)))))))
27ecfd36
AW
511 (let lp ((meta meta))
512 (unless (null? meta)
513 (set-procedure-property! proc (caar meta) (cdar meta))
514 (lp (cdr meta))))
c438cd71 515 proc))
d8a071fc 516
99fb07e1
AW
517 (('capture-env (locs . body))
518 (let* ((len (vector-length locs))
519 (new-env (make-env len #f (env-toplevel env))))
520 (let lp ((n 0))
521 (when (< n len)
522 (mx-bind
523 (vector-ref locs n) (depth . width)
524 (env-set! new-env 0 n (env-ref env depth width)))
525 (lp (1+ n))))
526 (eval body new-env)))
527
6fc3eae4
AW
528 (('seq (head . tail))
529 (begin
530 (eval head env)
531 (eval tail env)))
532
e6a42e67
AW
533 (('box-set! (box . val))
534 (variable-set! (eval box env) (eval val env)))
535
cfc28c80
AW
536 (('lexical-set! ((depth . width) . x))
537 (env-set! env depth width (eval x env)))
5161a3c0 538
21ec0bd9
AW
539 (('call-with-values (producer . consumer))
540 (call-with-values (eval producer env)
541 (eval consumer env)))
542
543 (('apply (f args))
544 (apply (eval f env) (eval args env)))
545
ef47c422
AW
546 (('capture-module x)
547 (eval x (current-module)))
548
1773bc7d
AW
549 (('call-with-prompt (tag thunk . handler))
550 (call-with-prompt
551 (eval tag env)
552 (eval thunk env)
553 (eval handler env)))
747022e4 554
21ec0bd9 555 (('call/cc proc)
e6a42e67 556 (call/cc (eval proc env)))))
5161a3c0 557
b2b554ef 558 ;; primitive-eval
5161a3c0 559 (lambda (exp)
b2b554ef 560 "Evaluate @var{exp} in the current module."
5161a3c0 561 (eval
a310a1d1
AW
562 (memoize-expression
563 (if (macroexpanded? exp)
564 exp
565 ((module-transformer (current-module)) exp)))
ef47c422 566 #f))))