error if given an unrewindable partial continuation
[bpt/guile.git] / module / ice-9 / eval.scm
CommitLineData
5161a3c0
AW
1;;; -*- mode: scheme; coding: utf-8; -*-
2
d69531e2 3;;;; Copyright (C) 2009, 2010
5161a3c0
AW
4;;;; Free Software Foundation, Inc.
5;;;;
6;;;; This library is free software; you can redistribute it and/or
7;;;; modify it under the terms of the GNU Lesser General Public
8;;;; License as published by the Free Software Foundation; either
9;;;; version 3 of the License, or (at your option) any later version.
10;;;;
11;;;; This library is distributed in the hope that it will be useful,
12;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14;;;; Lesser General Public License for more details.
15;;;;
16;;;; You should have received a copy of the GNU Lesser General Public
17;;;; License along with this library; if not, write to the Free Software
18;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19;;;;
20
21\f
22
23;;; Commentary:
24
b2b554ef
AW
25;;; Scheme eval, written in Scheme.
26;;;
27;;; Expressions are first expanded, by the syntax expander (i.e.
28;;; psyntax), then memoized into internal forms. The evaluator itself
29;;; only operates on the internal forms ("memoized expressions").
30;;;
31;;; Environments are represented as linked lists of the form (VAL ... .
32;;; MOD). If MOD is #f, it means the environment was captured before
33;;; modules were booted. If MOD is the literal value '(), we are
34;;; evaluating at the top level, and so should track changes to the
35;;; current module.
36;;;
37;;; Evaluate this in Emacs to make code indentation work right:
38;;;
39;;; (put 'memoized-expression-case 'scheme-indent-function 1)
5161a3c0
AW
40;;;
41
42;;; Code:
43
44\f
45
5161a3c0
AW
46(eval-when (compile)
47 (define-syntax capture-env
48 (syntax-rules ()
49 ((_ env)
50 (if (null? env)
51 (current-module)
52 (if (not env)
b2b554ef
AW
53 ;; the and current-module checks that modules are booted,
54 ;; and thus the-root-module is defined
5f161164 55 (and (current-module) the-root-module)
5161a3c0
AW
56 env)))))
57
4abb824c
AW
58 (define-syntax make-closure
59 (lambda (x)
9331f91c 60 (define *max-static-argument-count* 8)
4abb824c
AW
61 (define (make-formals n)
62 (map (lambda (i)
63 (datum->syntax
64 x
65 (string->symbol
66 (string (integer->char (+ (char->integer #\a) i))))))
67 (iota n)))
68 (syntax-case x ()
69 ((_ eval nreq rest? body env) (not (identifier? #'env))
70 #'(let ((e env))
71 (make-closure eval nreq rest? body e)))
72 ((_ eval nreq rest? body env)
73 #`(case nreq
74 #,@(map (lambda (nreq)
75 (let ((formals (make-formals nreq)))
76 #`((#,nreq)
77 (if rest?
78 (lambda (#,@formals . rest)
79 (eval body
80 (cons* rest #,@(reverse formals)
81 env)))
82 (lambda (#,@formals)
83 (eval body
84 (cons* #,@(reverse formals) env)))))))
85 (iota *max-static-argument-count*))
86 (else
87 #,(let ((formals (make-formals *max-static-argument-count*)))
88 #`(lambda (#,@formals . more)
89 (let lp ((new-env (cons* #,@(reverse formals) env))
90 (nreq (- nreq #,*max-static-argument-count*))
91 (args more))
92 (if (zero? nreq)
93 (eval body
94 (if rest?
95 (cons args new-env)
96 (if (not (null? args))
97 (scm-error 'wrong-number-of-args
98 "eval" "Wrong number of arguments"
99 '() #f)
100 new-env)))
101 (if (null? args)
102 (scm-error 'wrong-number-of-args
103 "eval" "Wrong number of arguments"
104 '() #f)
105 (lp (cons (car args) new-env)
106 (1- nreq)
107 (cdr args)))))))))))))
108
9331f91c
AW
109 (define-syntax call
110 (lambda (x)
111 (define *max-static-call-count* 4)
112 (syntax-case x ()
113 ((_ eval proc nargs args env) (identifier? #'env)
114 #`(case nargs
115 #,@(map (lambda (nargs)
116 #`((#,nargs)
117 (proc
118 #,@(map
119 (lambda (n)
120 (let lp ((n n) (args #'args))
121 (if (zero? n)
122 #`(eval (car #,args) env)
123 (lp (1- n) #`(cdr #,args)))))
124 (iota nargs)))))
125 (iota *max-static-call-count*))
126 (else
127 (apply proc
128 #,@(map
129 (lambda (n)
130 (let lp ((n n) (args #'args))
131 (if (zero? n)
132 #`(eval (car #,args) env)
133 (lp (1- n) #`(cdr #,args)))))
134 (iota *max-static-call-count*))
135 (let lp ((exps #,(let lp ((n *max-static-call-count*)
136 (args #'args))
137 (if (zero? n)
138 args
139 (lp (1- n) #`(cdr #,args)))))
140 (args '()))
141 (if (null? exps)
142 (reverse args)
143 (lp (cdr exps)
144 (cons (eval (car exps) env) args)))))))))))
145
b2b554ef
AW
146 ;; This macro could be more straightforward if the compiler had better
147 ;; copy propagation. As it is we do some copy propagation by hand.
5161a3c0
AW
148 (define-syntax mx-bind
149 (lambda (x)
150 (syntax-case x ()
151 ((_ data () body)
152 #'body)
153 ((_ data (a . b) body) (and (identifier? #'a) (identifier? #'b))
154 #'(let ((a (car data))
155 (b (cdr data)))
156 body))
157 ((_ data (a . b) body) (identifier? #'a)
158 #'(let ((a (car data))
159 (xb (cdr data)))
160 (mx-bind xb b body)))
161 ((_ data (a . b) body)
162 #'(let ((xa (car data))
163 (xb (cdr data)))
164 (mx-bind xa a (mx-bind xb b body))))
165 ((_ data v body) (identifier? #'v)
166 #'(let ((v data))
167 body)))))
168
b2b554ef
AW
169 ;; The resulting nested if statements will be an O(n) dispatch. Once
170 ;; we compile `case' effectively, this situation will improve.
5161a3c0
AW
171 (define-syntax mx-match
172 (lambda (x)
173 (syntax-case x (quote)
174 ((_ mx data tag)
175 #'(error "what" mx))
176 ((_ mx data tag (('type pat) body) c* ...)
177 #`(if (eqv? tag #,(or (memoized-typecode (syntax->datum #'type))
178 (error "not a typecode" #'type)))
179 (mx-bind data pat body)
180 (mx-match mx data tag c* ...))))))
181
182 (define-syntax memoized-expression-case
183 (lambda (x)
184 (syntax-case x ()
185 ((_ mx c ...)
186 #'(let ((tag (memoized-expression-typecode mx))
187 (data (memoized-expression-data mx)))
188 (mx-match mx data tag c ...)))))))
189
190
21ec0bd9
AW
191;;;
192;;; On 18 Feb 2010, I did a profile of how often the various memoized expression
193;;; types occur when getting to a prompt on a fresh build. Here are the numbers
194;;; I got:
195;;;
196;;; lexical-ref: 32933054
197;;; call: 20281547
198;;; toplevel-ref: 13228724
199;;; if: 9156156
200;;; quote: 6610137
201;;; let: 2619707
202;;; lambda: 1010921
203;;; begin: 948945
204;;; lexical-set: 509862
205;;; call-with-values: 139668
206;;; apply: 49402
207;;; module-ref: 14468
208;;; define: 1259
209;;; toplevel-set: 328
210;;; dynwind: 162
211;;; with-fluids: 0
212;;; call/cc: 0
213;;; module-set: 0
214;;;
215;;; So until we compile `case' into a computed goto, we'll order the clauses in
216;;; `eval' in this order, to put the most frequent cases first.
217;;;
218
5161a3c0
AW
219(define primitive-eval
220 (let ()
b2b554ef 221 ;; The "engine". EXP is a memoized expression.
5161a3c0
AW
222 (define (eval exp env)
223 (memoized-expression-case exp
21ec0bd9
AW
224 (('lexical-ref n)
225 (let lp ((n n) (env env))
226 (if (zero? n)
227 (car env)
228 (lp (1- n) (cdr env)))))
5161a3c0 229
21ec0bd9
AW
230 (('call (f nargs . args))
231 (let ((proc (eval f env)))
232 (call eval proc nargs args env)))
233
234 (('toplevel-ref var-or-sym)
235 (variable-ref
236 (if (variable? var-or-sym)
237 var-or-sym
238 (let lp ((env env))
239 (if (pair? env)
240 (lp (cdr env))
241 (memoize-variable-access! exp (capture-env env)))))))
242
5161a3c0
AW
243 (('if (test consequent . alternate))
244 (if (eval test env)
245 (eval consequent env)
246 (eval alternate env)))
247
21ec0bd9
AW
248 (('quote x)
249 x)
250
5161a3c0
AW
251 (('let (inits . body))
252 (let lp ((inits inits) (new-env (capture-env env)))
253 (if (null? inits)
254 (eval body new-env)
255 (lp (cdr inits)
256 (cons (eval (car inits) env) new-env)))))
257
258 (('lambda (nreq rest? . body))
4abb824c
AW
259 (make-closure eval nreq rest? body (capture-env env)))
260
21ec0bd9
AW
261 (('begin (first . rest))
262 (let lp ((first first) (rest rest))
263 (if (null? rest)
264 (eval first env)
265 (begin
266 (eval first env)
267 (lp (car rest) (cdr rest))))))
5161a3c0
AW
268
269 (('lexical-set! (n . x))
270 (let ((val (eval x env)))
271 (let lp ((n n) (env env))
272 (if (zero? n)
273 (set-car! env val)
274 (lp (1- n) (cdr env))))))
275
21ec0bd9
AW
276 (('call-with-values (producer . consumer))
277 (call-with-values (eval producer env)
278 (eval consumer env)))
279
280 (('apply (f args))
281 (apply (eval f env) (eval args env)))
282
283 (('module-ref var-or-spec)
5161a3c0 284 (variable-ref
21ec0bd9
AW
285 (if (variable? var-or-spec)
286 var-or-spec
287 (memoize-variable-access! exp #f))))
5161a3c0 288
21ec0bd9
AW
289 (('define (name . x))
290 (define! name (eval x env)))
291
5161a3c0
AW
292 (('toplevel-set! (var-or-sym . x))
293 (variable-set!
294 (if (variable? var-or-sym)
295 var-or-sym
296 (let lp ((env env))
297 (if (pair? env)
298 (lp (cdr env))
299 (memoize-variable-access! exp (capture-env env)))))
300 (eval x env)))
301
21ec0bd9
AW
302 (('dynwind (in exp . out))
303 (dynamic-wind (eval in env)
304 (lambda () (eval exp env))
305 (eval out env)))
306
bb0229b5
AW
307 (('with-fluids (fluids vals . exp))
308 (let* ((fluids (map (lambda (x) (eval x env)) fluids))
309 (vals (map (lambda (x) (eval x env)) vals)))
310 (with-fluids* fluids vals (lambda () (eval exp env)))))
311
747022e4
AW
312 (('prompt (tag exp . handler))
313 (@prompt (eval tag env)
314 (eval exp env)
315 (eval handler env)))
316
21ec0bd9
AW
317 (('call/cc proc)
318 (call/cc (eval proc env)))
5161a3c0
AW
319
320 (('module-set! (x . var-or-spec))
321 (variable-set!
322 (if (variable? var-or-spec)
323 var-or-spec
324 (memoize-variable-access! exp #f))
325 (eval x env)))))
326
b2b554ef 327 ;; primitive-eval
5161a3c0 328 (lambda (exp)
b2b554ef 329 "Evaluate @var{exp} in the current module."
5161a3c0 330 (eval
5f161164
AW
331 (memoize-expression ((or (module-transformer (current-module))
332 (lambda (x) x))
5161a3c0
AW
333 exp))
334 '()))))
335