dynwind is now a part of guile's primitive language
[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
191(define primitive-eval
192 (let ()
b2b554ef 193 ;; The "engine". EXP is a memoized expression.
5161a3c0
AW
194 (define (eval exp env)
195 (memoized-expression-case exp
196 (('begin (first . rest))
197 (let lp ((first first) (rest rest))
198 (if (null? rest)
199 (eval first env)
200 (begin
201 (eval first env)
202 (lp (car rest) (cdr rest))))))
203
204 (('if (test consequent . alternate))
205 (if (eval test env)
206 (eval consequent env)
207 (eval alternate env)))
208
209 (('let (inits . body))
210 (let lp ((inits inits) (new-env (capture-env env)))
211 (if (null? inits)
212 (eval body new-env)
213 (lp (cdr inits)
214 (cons (eval (car inits) env) new-env)))))
215
216 (('lambda (nreq rest? . body))
4abb824c
AW
217 (make-closure eval nreq rest? body (capture-env env)))
218
5161a3c0
AW
219 (('quote x)
220 x)
221
222 (('define (name . x))
223 (define! name (eval x env)))
224
d69531e2
AW
225 (('dynwind (in exp . out))
226 (dynamic-wind (eval in env)
227 (lambda () (eval exp env))
228 (eval out env)))
229
5161a3c0
AW
230 (('apply (f args))
231 (apply (eval f env) (eval args env)))
232
9331f91c 233 (('call (f nargs . args))
5161a3c0 234 (let ((proc (eval f env)))
9331f91c
AW
235 (call eval proc nargs args env)))
236
5161a3c0
AW
237 (('call/cc proc)
238 (call/cc (eval proc env)))
239
240 (('call-with-values (producer . consumer))
241 (call-with-values (eval producer env)
242 (eval consumer env)))
243
244 (('lexical-ref n)
245 (let lp ((n n) (env env))
246 (if (zero? n)
247 (car env)
248 (lp (1- n) (cdr env)))))
249
250 (('lexical-set! (n . x))
251 (let ((val (eval x env)))
252 (let lp ((n n) (env env))
253 (if (zero? n)
254 (set-car! env val)
255 (lp (1- n) (cdr env))))))
256
257 (('toplevel-ref var-or-sym)
258 (variable-ref
259 (if (variable? var-or-sym)
260 var-or-sym
261 (let lp ((env env))
262 (if (pair? env)
263 (lp (cdr env))
264 (memoize-variable-access! exp (capture-env env)))))))
265
266 (('toplevel-set! (var-or-sym . x))
267 (variable-set!
268 (if (variable? var-or-sym)
269 var-or-sym
270 (let lp ((env env))
271 (if (pair? env)
272 (lp (cdr env))
273 (memoize-variable-access! exp (capture-env env)))))
274 (eval x env)))
275
276 (('module-ref var-or-spec)
277 (variable-ref
278 (if (variable? var-or-spec)
279 var-or-spec
280 (memoize-variable-access! exp #f))))
281
282 (('module-set! (x . var-or-spec))
283 (variable-set!
284 (if (variable? var-or-spec)
285 var-or-spec
286 (memoize-variable-access! exp #f))
287 (eval x env)))))
288
b2b554ef 289 ;; primitive-eval
5161a3c0 290 (lambda (exp)
b2b554ef 291 "Evaluate @var{exp} in the current module."
5161a3c0 292 (eval
5f161164
AW
293 (memoize-expression ((or (module-transformer (current-module))
294 (lambda (x) x))
5161a3c0
AW
295 exp))
296 '()))))
297