eval.scm 20 KB

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