primitive-eval.scm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. ;;; Eval
  2. ;;; Copyright (C) 2024 Igalia, S.L.
  3. ;;;
  4. ;;; Licensed under the Apache License, Version 2.0 (the "License");
  5. ;;; you may not use this file except in compliance with the License.
  6. ;;; You may obtain a copy of the License at
  7. ;;;
  8. ;;; http://www.apache.org/licenses/LICENSE-2.0
  9. ;;;
  10. ;;; Unless required by applicable law or agreed to in writing, software
  11. ;;; distributed under the License is distributed on an "AS IS" BASIS,
  12. ;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. ;;; See the License for the specific language governing permissions and
  14. ;;; limitations under the License.
  15. ;;; Commentary:
  16. ;;;
  17. ;;; Eval. Derived from Andy Wingo's work on Guile's ice-9/eval.scm.
  18. ;;;
  19. ;;; Code:
  20. (library (hoot primitive-eval)
  21. (export primitive-eval primitive-expression?)
  22. (import (hoot apply)
  23. (hoot debug)
  24. (hoot eq)
  25. (hoot errors)
  26. (hoot exceptions)
  27. (hoot lists)
  28. (hoot modules)
  29. (hoot not)
  30. (hoot pairs)
  31. (hoot syntax)
  32. (hoot tree-il)
  33. (hoot values)
  34. (hoot vectors)
  35. (ice-9 match)
  36. (only (hoot assoc) memv assq-ref)
  37. (only (hoot keywords) keyword?)
  38. (only (hoot numbers) 1+ 1- + zero? < <= exact-integer?))
  39. (define (primitive-expression? exp) (tree-il? exp))
  40. (define (primitive-eval exp toplevel-env)
  41. (check-type toplevel-env module? 'primitive-eval)
  42. (define (lookup-lexical var cenv)
  43. (let outer ((depth 0) (cenv cenv))
  44. (match cenv
  45. ((vars . cenv)
  46. (let inner ((idx 1) (vars vars))
  47. (match vars
  48. (() (outer (1+ depth) cenv))
  49. ((v . vars)
  50. (if (eq? v var)
  51. (values depth idx)
  52. (inner (1+ idx) vars)))))))))
  53. (define (lookup-toplevel mod name bound?)
  54. ;; Unlike Guile, we don't have a state where a identifier can be
  55. ;; defined but unbound.
  56. (let ((mod (resolve-module toplevel-env mod)))
  57. (module-variable mod name (lambda (var mod name) var))))
  58. (define (toplevel-define! mod name)
  59. (let ((mod (resolve-module toplevel-env mod)))
  60. (or (module-local-variable mod name)
  61. (module-define! mod name #f))))
  62. (define (compile-const exp)
  63. (lambda (env) exp))
  64. (define (compile-lexical-ref name var cenv)
  65. (call-with-values (lambda () (lookup-lexical var cenv))
  66. (lambda (depth idx)
  67. (lambda (env)
  68. (let lp ((depth depth) (env env))
  69. (if (zero? depth)
  70. (vector-ref env idx)
  71. (lp (1- depth) (vector-ref env 0))))))))
  72. (define (compile-lexical-set name var val cenv)
  73. (let ((val (compile val cenv)))
  74. (call-with-values (lambda () (lookup-lexical var cenv))
  75. (lambda (depth idx)
  76. (lambda (env)
  77. (let ((val (val env)))
  78. (let lp ((depth depth) (env env))
  79. (if (zero? depth)
  80. (vector-set! env idx val)
  81. (lp (1- depth) (vector-ref env 0))))))))))
  82. (define (compile-toplevel-ref mod name cenv)
  83. (let ((getter #f))
  84. (lambda (env)
  85. (unless getter
  86. (set! getter (lookup-toplevel mod name #t)))
  87. (getter))))
  88. (define (compile-toplevel-set mod name val cenv)
  89. (let ((setter #f)
  90. (val (compile val cenv)))
  91. (lambda (env)
  92. (unless setter
  93. (set! setter (lookup-toplevel mod name #f)))
  94. (setter (val env)))))
  95. (define (compile-toplevel-define mod name val cenv)
  96. (let ((setter #f)
  97. (val (compile val cenv)))
  98. (lambda (env)
  99. (unless setter
  100. (set! setter (toplevel-define! mod name)))
  101. (setter (val env)))))
  102. (define (compile-if test consequent alternate cenv)
  103. (let ((test (compile test cenv))
  104. (consequent (compile consequent cenv))
  105. (alternate (compile alternate cenv)))
  106. (lambda (env)
  107. (if (test env)
  108. (consequent env)
  109. (alternate env)))))
  110. (define (compile-call f args cenv)
  111. (let ((f (compile f cenv)))
  112. (match args
  113. (()
  114. (lambda (env) ((f env))))
  115. ((a)
  116. (let ((a (compile a cenv)))
  117. (lambda (env) ((f env) (a env)))))
  118. ((a b)
  119. (let ((a (compile a cenv))
  120. (b (compile b cenv)))
  121. (lambda (env) ((f env) (a env) (b env)))))
  122. ((a b c)
  123. (let ((a (compile a cenv))
  124. (b (compile b cenv))
  125. (c (compile c cenv)))
  126. (lambda (env) ((f env) (a env) (b env) (c env)))))
  127. ((a b c . d*)
  128. (let ((a (compile a cenv))
  129. (b (compile b cenv))
  130. (c (compile c cenv))
  131. (d* (map (lambda (exp) (compile exp cenv)) d*)))
  132. (lambda (env)
  133. (apply (f env) (a env) (b env) (c env)
  134. (map (lambda (exp) (exp env)) d*))))))))
  135. ;; If present, primitive-ref and primcall are produced by the
  136. ;; expander. We just need to handle the set of primcalls that the
  137. ;; expander produces, which until the full expander lands, is just
  138. ;; memv in call position.
  139. (define (compile-primitive-ref prim cenv)
  140. ;; These essentially end up here via syntax-case output.
  141. (error "unexpected primitive-ref" prim))
  142. (define (compile-primcall prim args cenv)
  143. (match (cons prim args)
  144. (('memv k l)
  145. (let ((k (compile k cenv))
  146. (l (compile l cenv)))
  147. (lambda (env)
  148. (memv (k env) (l env)))))
  149. (_ (error "unexpected primcall" prim))))
  150. (define (compile-seq head tail cenv)
  151. (let ((head (compile head cenv))
  152. (tail (compile tail cenv)))
  153. (lambda (env)
  154. (head env)
  155. (tail env))))
  156. (define (compile-fixed-lambda req syms body cenv)
  157. (let* ((cenv (cons syms cenv))
  158. (body (compile body cenv)))
  159. (match syms
  160. (() (lambda (env) (lambda () (body (vector env)))))
  161. ((a) (lambda (env) (lambda (a) (body (vector env a)))))
  162. ((a b) (lambda (env) (lambda (a b) (body (vector env a b)))))
  163. ((a b c) (lambda (env) (lambda (a b c) (body (vector env a b c)))))
  164. ((a b c . d)
  165. (let ((nreq (length syms)))
  166. (lambda (env)
  167. (lambda (a b c . d)
  168. (let ((env (make-vector (1+ nreq) env)))
  169. (vector-set! env 1 a)
  170. (vector-set! env 2 b)
  171. (vector-set! env 3 c)
  172. (let lp ((i 4) (rest d))
  173. (when (<= i nreq)
  174. (vector-set! env i (car rest))
  175. (lp (1+ i) (cdr rest))))
  176. (body env)))))))))
  177. (define (compile-rest-lambda req rest syms body cenv)
  178. (let* ((cenv (cons syms cenv))
  179. (body (compile body cenv)))
  180. (match syms
  181. ((a)
  182. (lambda (env) (lambda a (body (vector env a)))))
  183. ((a b)
  184. (lambda (env) (lambda (a . b) (body (vector env a b)))))
  185. ((a b c)
  186. (lambda (env) (lambda (a b . c) (body (vector env a b c)))))
  187. ((a b c . d)
  188. (let ((nreq+rest (length syms)))
  189. (lambda (env)
  190. (lambda (a b c . d)
  191. (let ((env (make-vector nreq+rest env)))
  192. (vector-set! env 1 a)
  193. (vector-set! env 2 b)
  194. (vector-set! env 3 c)
  195. (let lp ((i 4) (rest d))
  196. (cond
  197. ((< i nreq+rest)
  198. (vector-set! env i (car rest))
  199. (lp (1+ i) (cdr rest)))
  200. (else
  201. (vector-set! env i rest)
  202. (body env))))))))))))
  203. (define (compile-general-lambda req opt rest kw inits syms body alt cenv)
  204. (let* ((cenv (cons syms cenv))
  205. (body (compile body cenv))
  206. (inits (map (lambda (exp) (compile exp cenv)) inits)))
  207. (define nreq (length req))
  208. (define nopt (length opt))
  209. (define nvars (length syms))
  210. (define unbound (list 'unbound))
  211. (define rest-idx (and rest (+ nreq nopt 1)))
  212. (define allow-other-keys? (match kw (#f #f) ((aok? . _) aok?)))
  213. (define kw-indices
  214. (match kw
  215. (#f '())
  216. ((aok? (key name sym) ...)
  217. (let lp ((kw* key) (idx (+ nreq nopt (if rest 2 1))) )
  218. (match kw*
  219. (() '())
  220. ((kw . kw*) (acons kw idx (lp kw* (1+ idx)))))))))
  221. (lambda (env)
  222. (lambda args
  223. (define (next-case)
  224. (apply (if alt
  225. (alt env)
  226. (lambda args
  227. (raise (make-arity-error args 'apply))))
  228. args))
  229. (let ((env (let ((env* (make-vector (1+ nvars) unbound)))
  230. (vector-set! env* 0 env)
  231. env*)))
  232. (define (parse-req idx remaining args)
  233. (if (zero? remaining)
  234. (parse-opt idx nopt args)
  235. (match args
  236. (() (next-case))
  237. ((arg . args)
  238. (vector-set! env idx arg)
  239. (parse-req (1+ idx) (1- remaining) args)))))
  240. (define (parse-opt idx remaining args)
  241. (cond
  242. ((zero? remaining)
  243. (parse-rest args))
  244. (else
  245. (match args
  246. (() (parse-rest '()))
  247. ((arg . args)
  248. (vector-set! env idx arg)
  249. (parse-opt (1+ idx) (1- nreq) args))))))
  250. (define (parse-rest args)
  251. (cond
  252. (rest-idx
  253. (vector-set! env rest-idx args)
  254. (if kw
  255. (parse-kw args)
  256. (finish)))
  257. ((null? args)
  258. (finish))
  259. ((or (not kw)
  260. (and alt (pair? args) (not (keyword? (car args)))))
  261. ;; Too many positional arguments for this case.
  262. (next-case))
  263. (else
  264. (parse-kw args))))
  265. (define (parse-kw args)
  266. (match args
  267. (()
  268. (finish))
  269. ((k . args)
  270. (match (assq-ref kw-indices k)
  271. (#f
  272. (cond
  273. ((not (keyword? k))
  274. (if rest-idx
  275. (parse-kw args)
  276. (raise (make-invalid-keyword-error k))))
  277. (allow-other-keys?
  278. (match args
  279. (() (finish))
  280. ((v . args) (parse-kw args))))
  281. (else
  282. (raise (make-unrecognized-keyword-error k)))))
  283. (idx
  284. (match args
  285. (() (raise (make-missing-keyword-argument-error k)))
  286. ((v . args)
  287. (vector-set! env idx v)
  288. (parse-kw args))))))))
  289. (define (finish)
  290. (let lp ((idx (+ nreq 1)) (inits inits))
  291. (if (eq? idx rest-idx)
  292. (lp (1+ idx) inits)
  293. (match inits
  294. (() (body env))
  295. ((init . inits)
  296. (when (eq? (vector-ref env idx) unbound)
  297. (vector-set! env idx (init env)))
  298. (lp (1+ idx) inits))))))
  299. (parse-req 1 nreq args))))))
  300. (define (compile-lambda-case body cenv)
  301. (match body
  302. (#f (lambda (env)
  303. (lambda args
  304. (raise (make-arity-error args 'apply)))))
  305. (($ <lambda-case> src req (or #f ()) #f #f () syms body #f)
  306. (compile-fixed-lambda req syms body cenv))
  307. (($ <lambda-case> src req (or #f ()) rest #f () syms body #f)
  308. (compile-rest-lambda req rest syms body cenv))
  309. (($ <lambda-case> src req opt rest kw inits syms body alt)
  310. (compile-general-lambda
  311. req (or opt '()) rest kw inits syms body
  312. (and alt (compile-lambda-case alt cenv))
  313. cenv))))
  314. (define (compile-lambda meta body cenv)
  315. (compile-lambda-case body cenv))
  316. (define (compile-let names vars inits body cenv)
  317. (let* ((inits (map (lambda (exp) (compile exp cenv)) inits))
  318. (cenv (cons vars cenv))
  319. (len (length inits))
  320. (body (compile body cenv)))
  321. (lambda (env)
  322. (let ((env* (make-vector (1+ len))))
  323. (vector-set! env* 0 env)
  324. (let lp ((i 0) (inits inits))
  325. (when (< i len)
  326. (vector-set! env* (1+ i) ((car inits) env))
  327. (lp (1+ i) (cdr inits))))
  328. (body env*)))))
  329. (define (compile-letrec in-order? names vars inits body cenv)
  330. (let* ((len (length inits))
  331. (cenv (cons vars cenv))
  332. (inits (map (lambda (exp) (compile exp cenv)) inits))
  333. (body (compile body cenv)))
  334. (lambda (env)
  335. (let ((env* (make-vector (1+ len))))
  336. (vector-set! env* 0 env)
  337. (let lp ((i 0) (inits inits))
  338. (when (< i len)
  339. (vector-set! env* (1+ i) ((car inits) env*))
  340. (lp (1+ i) (cdr inits))))
  341. (body env*)))))
  342. (define (compile exp cenv)
  343. (match exp
  344. (($ <void> src)
  345. (compile-const (if #f #f)))
  346. (($ <const> src exp)
  347. (compile-const exp))
  348. (($ <primitive-ref> src name)
  349. (compile-primitive-ref name cenv))
  350. (($ <lexical-ref> src name var)
  351. (compile-lexical-ref name var cenv))
  352. (($ <lexical-set> src name var val)
  353. (compile-lexical-set name var val cenv))
  354. (($ <toplevel-ref> src mod name)
  355. (compile-toplevel-ref mod name cenv))
  356. (($ <module-ref> src mod name #f)
  357. (compile-toplevel-ref mod name cenv))
  358. (($ <toplevel-set> src mod name val)
  359. (compile-toplevel-set mod name val cenv))
  360. (($ <module-set> src mod name #f val)
  361. (compile-toplevel-set mod name val cenv))
  362. (($ <toplevel-define> src mod name val)
  363. (compile-toplevel-define mod name val cenv))
  364. (($ <conditional> src test consequent alternate)
  365. (compile-if test consequent alternate cenv))
  366. (($ <call> src f args)
  367. (compile-call f args cenv))
  368. (($ <primcall> src prim args)
  369. (compile-primcall prim args cenv))
  370. (($ <seq> src head tail)
  371. (compile-seq head tail cenv))
  372. (($ <lambda> src meta body)
  373. (compile-lambda meta body cenv))
  374. (($ <let> src names vars inits body)
  375. (compile-let names vars inits body cenv))
  376. (($ <letrec> src in-order? names vars inits body)
  377. (compile-letrec in-order? names vars inits body cenv))))
  378. ((compile exp 'compile-env) 'lexical-env)))