library-group.scm 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. ;;; Library-group expander
  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. ;;; Parser, linker, and expander for `library-group` form.
  18. ;;;
  19. ;;; Code:
  20. (define-module (hoot library-group)
  21. #:use-module (ice-9 match)
  22. #:use-module (language tree-il)
  23. #:use-module (language tree-il primitives)
  24. #:use-module ((srfi srfi-1) #:select (append-map partition fold))
  25. #:use-module (srfi srfi-9)
  26. #:use-module ((system syntax internal) #:select (syntax? syntax-sourcev))
  27. #:export (library-group?
  28. parse-r6rs-library
  29. parse-library-group
  30. link-library-group
  31. expand-library-group))
  32. (define-record-type <iset-library>
  33. (make-iset-library name version)
  34. iset-library?
  35. (name iset-library-name)
  36. (version iset-library-version))
  37. (define-record-type <iset-only>
  38. (make-iset-only iset ids)
  39. only?
  40. (iset iset-only-iset)
  41. (ids iset-only-ids))
  42. (define-record-type <iset-except>
  43. (make-iset-except iset ids)
  44. iset-except?
  45. (iset iset-except-iset)
  46. (ids iset-except-ids))
  47. (define-record-type <iset-rename>
  48. (make-iset-rename iset renamings)
  49. iset-rename?
  50. (iset iset-rename-iset)
  51. (renamings iset-rename-renamings))
  52. (define-record-type <iset-prefix>
  53. (make-iset-prefix iset prefix)
  54. iset-prefix?
  55. (iset iset-prefix-iset)
  56. (prefix iset-prefix-prefix))
  57. (define-record-type <library>
  58. (make-library src name version trusted? exports isets body)
  59. library?
  60. (src library-src)
  61. (name library-name)
  62. (version library-version)
  63. (trusted? library-trusted?)
  64. (exports library-exports)
  65. (isets library-isets)
  66. (body library-body))
  67. (define-record-type <program>
  68. (make-program src trusted? isets body)
  69. program?
  70. (src program-src)
  71. (trusted? program-trusted?)
  72. (isets program-isets)
  73. (body program-body))
  74. (define-record-type <library-group>
  75. (make-library-group src libraries program)
  76. library-group?
  77. (src library-group-src)
  78. (libraries library-group-libraries)
  79. (program library-group-program))
  80. (define imported-library-name
  81. (match-lambda
  82. (($ <iset-only> iset select) (imported-library-name iset))
  83. (($ <iset-except> iset hide) (imported-library-name iset))
  84. (($ <iset-rename> iset renamings) (imported-library-name iset))
  85. (($ <iset-prefix> iset prefix) (imported-library-name iset))
  86. (($ <iset-library> name version) name)))
  87. (define (id? x) (symbol? x))
  88. (define (name-component? x) (id? x))
  89. (define (version-component? x) (and (exact-integer? x) (not (negative? x))))
  90. (define (name-matches? stx sym)
  91. (eq? (syntax->datum stx) sym))
  92. (define-syntax-rule (symbolic-match? name)
  93. (name-matches? #'name 'name))
  94. (define parse-name+version
  95. (match-lambda
  96. (((? name-component? name) ... ((? version-component? version) ...))
  97. (values name version))
  98. (((? name-component? name) ...)
  99. (values name '()))))
  100. (define (includes-forbidden filename)
  101. (error "library-group include clause forbidden" filename))
  102. (define (parse-imports import-sets)
  103. (define parse-import-set
  104. (match-lambda
  105. ((head . tail)
  106. (match head
  107. ('only
  108. (match tail
  109. ((iset (? id? select) ...)
  110. (make-iset-only (parse-import-set iset) select))))
  111. ('except
  112. (match tail
  113. ((iset (? id? hide) ...)
  114. (make-iset-except (parse-import-set iset) hide))))
  115. ('prefix
  116. (match tail
  117. ((iset (? id? prefix))
  118. (make-iset-prefix (parse-import-set iset) prefix))))
  119. ('rename
  120. (match tail
  121. ((iset ((? id? from) (? id? to)) ...)
  122. (make-iset-rename (parse-import-set iset) (map cons from to)))))
  123. ('library
  124. (match tail
  125. ((name+version)
  126. (call-with-values (lambda ()
  127. (parse-name+version name+version))
  128. (lambda (name version)
  129. (make-iset-library name version))))))
  130. (_
  131. (parse-import-set `(library (,head . ,tail))))))))
  132. (map (match-lambda
  133. ;; Strip level.
  134. (('for iset level ...) (parse-import-set iset))
  135. (iset (parse-import-set iset)))
  136. import-sets))
  137. (define (parse-r6rs-library form trusted?)
  138. "Given the R6RS library @var{form}, as a syntax object, parse out the
  139. imports and exports to a @code{library}."
  140. (define (parse-exports exports)
  141. ;; -> ((local . public) ...)
  142. (map (match-lambda
  143. ((? id? id) (cons id id))
  144. (('rename (? id? from) (? id? to)) (cons from to)))
  145. exports))
  146. (syntax-case form ()
  147. ((library (name ...)
  148. (export export-spec ...)
  149. (import import-spec ...)
  150. body ...)
  151. (and (symbolic-match? library)
  152. (symbolic-match? export)
  153. (symbolic-match? import))
  154. (let ()
  155. (define src
  156. (and (syntax? #'library) (syntax-sourcev #'library)))
  157. (define-values (modname version)
  158. (parse-name+version (syntax->datum #'(name ...))))
  159. (define exports
  160. (parse-exports (syntax->datum #'(export-spec ...))))
  161. (define imports
  162. (parse-imports (syntax->datum #'(import-spec ...))))
  163. (make-library src modname version trusted? exports imports
  164. #'(body ...))))))
  165. (define* (parse-library-group form #:key (include-file includes-forbidden))
  166. "Parse a @code{library-group} form to a @code{<library-group>} record,
  167. processing includes. No other expansion or analysis is performed beyond
  168. syntactic validity."
  169. (define* (parse forms libraries #:key (trusted? #f))
  170. "For each form in @var{forms}, which should be a list of syntax objects,
  171. process any includes, collecting the prefix of @code{<library>} forms
  172. and then parsing the tail @code{<program>}, or @code{#f} if there is no
  173. program."
  174. (syntax-case forms ()
  175. (() (values (reverse libraries) #f))
  176. ((form . forms)
  177. (syntax-case #'form ()
  178. (#:untrusted
  179. (parse #'forms libraries #:trusted? #f))
  180. ((library . _)
  181. (symbolic-match? library)
  182. (parse #'forms (cons (parse-r6rs-library #'form trusted?) libraries)
  183. #:trusted? trusted?))
  184. ((define-library . _)
  185. (symbolic-match? define-library)
  186. (error "R7RS libraries not yet supported"))
  187. ((include filename)
  188. (symbolic-match? include)
  189. (parse (append (include-file (syntax->datum #'filename)) #'forms)
  190. libraries #:trusted? trusted?))
  191. ((import import-spec ...)
  192. (symbolic-match? import)
  193. (values (reverse libraries)
  194. (make-program #f trusted?
  195. (parse-imports
  196. (syntax->datum #'(import-spec ...)))
  197. #'forms)))))))
  198. (syntax-case form ()
  199. ((library-group form ...)
  200. (symbolic-match? library-group)
  201. (let ((src (and (syntax? #'library-group)
  202. (syntax-sourcev #'library-group))))
  203. (call-with-values (lambda () (parse #'(form ...) '() #:trusted? #t))
  204. (lambda (libraries program)
  205. (make-library-group src libraries program)))))
  206. (_
  207. (error "invalid library-group" form))))
  208. (define* (link-library-group group #:key
  209. (load-library (lambda (name) #f))
  210. (allow-dangling-import? (lambda (name) #f)))
  211. (define linked '()) ;; List of libraries.
  212. (define by-name (make-hash-table))
  213. (define (link-library! library)
  214. (let ((name (library-name library)))
  215. (when (hash-ref by-name name)
  216. (error "duplicate library definition" name))
  217. (hash-set! by-name name 'linking)
  218. (for-each link-import! (library-isets library))
  219. (set! linked (cons library linked))
  220. (hash-set! by-name name 'linked)))
  221. (define (link-import! iset)
  222. (let ((name (imported-library-name iset)))
  223. (match (hash-ref by-name name 'unvisited)
  224. ('linked (values))
  225. ('linking (error "cycle in module graph" name))
  226. ('unvisited
  227. (cond
  228. ((load-library name) => link-library!)
  229. ((allow-dangling-import? name) (values))
  230. (else (error "module not found" name)))))))
  231. (match group
  232. (($ <library-group> src libraries program)
  233. (for-each link-library! libraries)
  234. (when program (for-each link-import! (program-isets program)))
  235. (make-library-group src (reverse linked) program))))
  236. (define-record-type <import>
  237. (make-import modname exported-name imported-name)
  238. import?
  239. (modname import-modname)
  240. (exported-name exported-name)
  241. (imported-name imported-name))
  242. (define-record-type <lexical>
  243. (make-lexical sym)
  244. lexical?
  245. (sym lexical-sym))
  246. (define-record-type <primitive>
  247. (make-primitive name)
  248. primitive?
  249. (name primitive-name))
  250. (define-record-type <expand-time-value>
  251. (make-expand-time-value)
  252. expand-time-value?)
  253. ;; <value> := <lexical>
  254. ;; | <primitive>
  255. ;; | <expand-time-value>
  256. (define-record-type <module-definitions>
  257. (make-module-definitions private public)
  258. module-definitions?
  259. ;; Hash table of symbol -> <value>.
  260. (private module-private-definitions)
  261. ;; Hash table of symbol -> <value>.
  262. (public module-public-definitions))
  263. (define-record-type <definition>
  264. (make-definition name sym val)
  265. definition?
  266. (name definition-name)
  267. (sym definition-sym)
  268. (val definition-val))
  269. (define-record-type <statement>
  270. (make-statement exp)
  271. statement?
  272. (exp statement-exp))
  273. ;; FIXME: Get this exported from (language tree-il primitives).
  274. (define (primitive-for-variable box)
  275. (hashq-ref (@@ (language tree-il primitives) *interesting-primitive-vars*)
  276. box))
  277. (define (expand-library call-with-target mod form)
  278. "Expand the syntax object @var{form} in the module @var{mod}.
  279. The term will be expanded twice: once to create the expand-time module,
  280. which will then be evaluated directly, and once to residualize a Tree-IL
  281. term for the compilation unit.
  282. Syntax transformers (macros) will be evaluated at expansion-time, and
  283. not residualized into the compilation unit."
  284. (save-module-excursion
  285. (lambda ()
  286. (set-current-module mod)
  287. (primitive-eval (macroexpand form 'e '(expand eval)))
  288. (call-with-target
  289. (lambda () (macroexpand form 'c '()))))))
  290. (define (expand-program call-with-target mod form)
  291. "Expand the syntax object @var{form} in the module @var{mod}.
  292. Syntax transformers (macros) will be evaluated at expansion-time, and
  293. not residualized into the compilation unit."
  294. (save-module-excursion
  295. (lambda ()
  296. (set-current-module mod)
  297. (call-with-target
  298. (lambda () (macroexpand form 'c '(expand)))))))
  299. (define* (expand-library-group group #:key
  300. (call-with-target (lambda (f) (f)))
  301. (primitives #f))
  302. "Take a @code{<library-group>} record and expand it to a big
  303. @code{letrec*}.
  304. The libraries in the group are expanded one-by-one. Expanding a library
  305. residualises a Tree-IL AST node as part of the compilation unit, and
  306. additionally populates a compile-time host module with definitions. If
  307. expanding a module needs compile-time values from another module, it
  308. uses the bindings in the host module.
  309. All definitions and expressions in the expanded libraries are then
  310. rewritten to be part of a big @code{letrec*}, and top-level and module
  311. references in those definitions and expressions are rewritten to use
  312. lexical references.
  313. The final program in the @code{<library-group>} is given the same
  314. treatment, except that its final expression (if any) is evaluated in
  315. tail position."
  316. ;; A mapping from module,name,public? tuple to <binding> record, for
  317. ;; all modules in the library group.
  318. (define module-definitions (make-hash-table))
  319. (define (add-module-definitions! modname)
  320. (when (hash-ref module-definitions modname)
  321. (error "duplicate module" modname))
  322. (define defs
  323. (make-module-definitions (make-hash-table) (make-hash-table)))
  324. (hash-set! module-definitions modname defs)
  325. defs)
  326. (define (lookup-module-definitions modname)
  327. (or (hash-ref module-definitions modname)
  328. (error "unknown module" modname)))
  329. (define (add-definition! defs name public? value)
  330. (match defs
  331. (($ <module-definitions> private public)
  332. (let ((t (if public? public private)))
  333. (when (hashq-ref t name)
  334. (error "duplicate definition" name))
  335. (hashq-set! t name value)))))
  336. (define (lookup-definition defs name public?)
  337. (match defs
  338. (($ <module-definitions> private public)
  339. (hashq-ref (if public? public private) name))))
  340. ;; Add definitions from primitive module.
  341. (when primitives
  342. (let ((defs (add-module-definitions! primitives)))
  343. (module-for-each
  344. (lambda (name box)
  345. (add-definition! defs name #t
  346. (match (primitive-for-variable box)
  347. (#f (make-expand-time-value))
  348. (name (make-primitive name)))))
  349. (resolve-interface primitives))))
  350. (define (parse-isets isets trusted?)
  351. (define parse-iset
  352. (match-lambda
  353. (($ <iset-only> iset select)
  354. (filter (match-lambda
  355. (($ <import> mod-name exported imported)
  356. (memq imported select)))
  357. (parse-iset iset)))
  358. (($ <iset-except> iset hide)
  359. (filter (match-lambda
  360. (($ <import> mod-name exported imported)
  361. (not (memq imported hide))))
  362. (parse-iset iset)))
  363. (($ <iset-prefix> iset prefix)
  364. (map (match-lambda
  365. (($ <import> mod-name exported imported)
  366. (let ((renamed (symbol-append prefix imported)))
  367. (make-import mod-name exported renamed))))
  368. (parse-iset iset)))
  369. (($ <iset-rename> iset renamings)
  370. (map (match-lambda
  371. (($ <import> mod-name exported imported)
  372. (define renamed
  373. (or (assq-ref renamings imported) imported))
  374. (make-import mod-name exported renamed)))
  375. (parse-iset iset)))
  376. (($ <iset-library> modname version)
  377. (unless (null? version)
  378. (error "version references unsupported"))
  379. (when (equal? modname primitives)
  380. (unless trusted?
  381. (error "untrusted module cannot import primitives")))
  382. (let ((exports (module-public-definitions
  383. (lookup-module-definitions modname))))
  384. (define (id<? a b)
  385. (string<? (symbol->string a) (symbol->string b)))
  386. (define (import<? a b)
  387. (id<? (exported-name a) (exported-name b)))
  388. (sort (hash-map->list (lambda (name binding)
  389. (make-import modname name name))
  390. exports)
  391. import<?)))))
  392. (append-map parse-iset isets))
  393. ;; Because each invocation of expand-library-group gets its own
  394. ;; namespace, we don't have to deal with lingering definitions from
  395. ;; any previous expansion; all modules defined by this compilation
  396. ;; unit are fresh. This also allows expansion to happen in parallel.
  397. (define namespace (gensym "%library-group"))
  398. (define (host-modname? modname)
  399. (match modname
  400. (() #f)
  401. ((head . tail)
  402. (not (eq? namespace head)))))
  403. (define (annotate-modname modname)
  404. (if (equal? modname primitives)
  405. modname
  406. (cons namespace modname)))
  407. (define (strip-modname modname)
  408. (match modname
  409. (((? (lambda (x) (eq? x namespace))) . modname) modname)
  410. (_
  411. (unless (equal? modname primitives)
  412. (error "unexpected modname" modname))
  413. modname)))
  414. (define (make-expand-time-module modname filename version imports exports)
  415. "Create the host module in which to store compile-time library
  416. definitions. The module may import other host libraries."
  417. (define imports-by-module (make-hash-table))
  418. (define (add-import! modname exported imported)
  419. (define tail (hash-ref imports-by-module modname '()))
  420. (define entry (cons exported imported))
  421. (hash-set! imports-by-module modname (cons entry tail)))
  422. (for-each (match-lambda
  423. (($ <import> modname exported imported)
  424. (add-import! modname exported imported)))
  425. imports)
  426. (define (id<? a b)
  427. (string<? (symbol->string a) (symbol->string b)))
  428. (define (modname<? a b)
  429. (match a
  430. (() #t)
  431. ((a . a*) (match b
  432. (() #f)
  433. ((b . b*) (and (id<? a b) (modname<? a* b*)))))))
  434. (define module-import-decls
  435. (sort (hash-map->list (lambda (modname entries)
  436. (list (annotate-modname modname)
  437. #:select
  438. (sort entries
  439. (lambda (a b)
  440. (id<? (car a) (car b))))))
  441. imports-by-module)
  442. (lambda (a b)
  443. (modname<? (car a) (car b)))))
  444. (define-values (module-export-decls module-re-export-decls)
  445. (let ()
  446. (define imports-by-name (make-hash-table))
  447. (for-each (match-lambda
  448. ((and import ($ <import> _ _ imported))
  449. (match (hashq-ref imports-by-name imported)
  450. (#f (hashq-set! imports-by-name imported import))
  451. (existing
  452. (error "duplicate imports" existing import)))))
  453. imports)
  454. (partition (match-lambda
  455. ((local . public) (not (hashq-ref imports-by-name local))))
  456. exports)))
  457. (define-module* (annotate-modname modname)
  458. #:filename filename
  459. #:pure #t
  460. #:version version
  461. #:imports module-import-decls
  462. #:exports module-export-decls
  463. #:re-exports module-re-export-decls
  464. #:declarative? #t))
  465. (define (tree-il->reversed-bindings exp modname imports exports bindings)
  466. "Given the expanded library @var{exp}, as a Tree-IL node, transform it to
  467. a sequence of definitions and expressions, as @code{<binding>} nodes.
  468. Rewrite references to other top-level bindings to refer to primitive or
  469. lexical definitions. Append those @code{<binding>} nodes to
  470. @var{bindings}, in reverse order."
  471. ;; Make defs for module.
  472. (define defs (add-module-definitions! modname))
  473. (define (has-expand-time-value? name)
  474. (module-variable (resolve-module (annotate-modname modname)) name))
  475. ;; Add definitions for imports.
  476. (for-each (match-lambda
  477. (($ <import> imod exported imported)
  478. (match (lookup-definition (lookup-module-definitions imod)
  479. exported #t)
  480. (#f (error "unknown import?" imod exported))
  481. (value (add-definition! defs imported #f value)))))
  482. imports)
  483. (define (tree-il-for-each f exp)
  484. (define fold (make-tree-il-folder))
  485. (fold exp (lambda (exp) (values)) f))
  486. ;; Prohibit set! to imports. Check module on expanded toplevel defs
  487. ;; and uses.
  488. (tree-il-for-each (match-lambda
  489. (($ <toplevel-define> src mod name val)
  490. (unless (equal? (strip-modname mod) modname)
  491. (error "unexpected mod" exp mod modname))
  492. (values))
  493. (($ <toplevel-ref> src mod name)
  494. (unless (equal? (strip-modname mod) modname)
  495. (error "unexpected mod" exp mod modname))
  496. (values))
  497. (($ <toplevel-set> src mod name val)
  498. (unless (equal? (strip-modname mod) modname)
  499. (error "unexpected mod" exp mod modname))
  500. (when (lookup-definition defs name #f)
  501. (error "set! to imported binding" src name))
  502. (values))
  503. (_ (values)))
  504. exp)
  505. ;; Record local definitions and allocate lexicals for them.
  506. (tree-il-for-each (match-lambda
  507. (($ <toplevel-define> src mod name exp)
  508. (when (lookup-definition defs name #f)
  509. (error "duplicate definition" modname name))
  510. (add-definition! defs name #f (make-lexical (gensym "top")))
  511. (values))
  512. (_ (values)))
  513. exp)
  514. ;; Check for unbound top-levels.
  515. (tree-il-for-each (match-lambda
  516. (($ <toplevel-ref> src mod name)
  517. (unless (lookup-definition defs name #f)
  518. (error "unbound top-level" src name))
  519. (values))
  520. (($ <toplevel-set> src mod name val)
  521. (unless (lookup-definition defs name #f)
  522. (error "unbound top-level" src name))
  523. (values))
  524. (($ <module-ref> src mod name public?)
  525. (unless (or (host-modname? mod)
  526. (let ((defs (lookup-module-definitions
  527. (strip-modname mod))))
  528. (lookup-definition defs name public?)))
  529. (error "unbound macro-introduced top-level for module"
  530. src (strip-modname mod) name))
  531. (values))
  532. (($ <module-set> src mod name public? val)
  533. (unless (let ((defs (lookup-module-definitions
  534. (strip-modname mod))))
  535. (lookup-definition defs name public?))
  536. (error "unbound macro-introduced top-level for module"
  537. src (strip-modname mod) name))
  538. (values))
  539. (_ (values)))
  540. exp)
  541. ;; Find local definitions for exports.
  542. (for-each (match-lambda
  543. ((local . exported)
  544. (match (lookup-definition defs local #f)
  545. (#f
  546. ;; An export without a binding in the compilation
  547. ;; unit. Perhaps it is an expansion-time binding.
  548. (unless (has-expand-time-value? local)
  549. (error "missing definition for export"
  550. modname local exported))
  551. (let ((val (make-expand-time-value)))
  552. (add-definition! defs local #f val)
  553. (add-definition! defs exported #t val)))
  554. (val (add-definition! defs exported #t val)))))
  555. exports)
  556. ;; Resolve references to local definitions and residualized
  557. ;; module-private definitions to lexical-ref or primitive-ref.
  558. (define (visit-expr exp)
  559. (post-order
  560. (lambda (exp)
  561. (match exp
  562. (($ <toplevel-ref> src mod name)
  563. (match (lookup-definition defs name #f)
  564. (($ <lexical> sym) (make-lexical-ref src name sym))
  565. (($ <primitive> name) (make-primitive-ref src name))
  566. (($ <expand-time-value>)
  567. (error "reference to expansion-time value in generated code"
  568. src modname name))))
  569. (($ <toplevel-set> src mod name val)
  570. (match (lookup-definition defs name #f)
  571. (($ <lexical> sym) (make-lexical-set src name sym val))
  572. (($ <expand-time-value>)
  573. (error "reference to expansion-time value in generated code"
  574. src modname name))))
  575. (($ <module-ref> src (? host-modname? mod) name #f)
  576. ;; A primitive reference introduced by a primitive syntax
  577. ;; expander.
  578. (match (primitive-for-variable
  579. (module-variable (resolve-module mod) name))
  580. (#f (error "can't find name for primitive reference" mod name))
  581. (name (make-primitive-ref src name))))
  582. (($ <module-ref> src mod name public?)
  583. (let ((defs (lookup-module-definitions (strip-modname mod))))
  584. (match (lookup-definition defs name public?)
  585. (($ <lexical> sym) (make-lexical-ref src name sym))
  586. (($ <primitive> name) (make-primitive-ref src name))
  587. (($ <expand-time-value>)
  588. (error "reference to expansion-time value in generated code"
  589. src mod name)))))
  590. (($ <module-set> src mod name public? val)
  591. (let ((defs (lookup-module-definitions (strip-modname mod))))
  592. (match (lookup-definition defs name public?)
  593. (($ <lexical> sym) (make-lexical-set src name sym val))
  594. (($ <expand-time-value>)
  595. (error "reference to expansion-time value in generated code"
  596. src mod name)))))
  597. (($ <toplevel-define>)
  598. (error "unexpected nested toplevel define" exp))
  599. (($ <call> src ($ <primitive-ref> _ name) args)
  600. (expand-primcall (make-primcall src name args)))
  601. (_ exp)))
  602. exp))
  603. ;; Walk the chain of <seq> and <toplevel-define> to extract
  604. ;; definitions and statements.
  605. (define (visit-top-level exp bindings)
  606. (match exp
  607. (($ <toplevel-define> src mod name val)
  608. (match (lookup-definition defs name #f)
  609. (($ <lexical> sym)
  610. (cons (make-definition name sym (visit-expr val))
  611. bindings))))
  612. (($ <seq> src head tail)
  613. (visit-top-level tail (visit-top-level head bindings)))
  614. ;; Could fold in let and letrec* bindings. Dunno.
  615. (_ (cons (make-statement (visit-expr exp)) bindings))))
  616. (visit-top-level exp bindings))
  617. (define (srcv-filename srcv)
  618. (match srcv
  619. (#f #f)
  620. (#(filename line column) filename)))
  621. (define (library->reversed-bindings library bindings)
  622. "Given the R6RS library @var{form}, as a syntax object, parse out the
  623. imports and exports, create a compile-time module, and expand the body
  624. of the library within that module. Add the residual definitions and
  625. expressions from the module to @var{bindings}, as in
  626. @code{tree-il->reversed-bindings}."
  627. (match library
  628. (($ <library> src modname version trusted? exports isets body)
  629. (define filename (srcv-filename src))
  630. (define imports (parse-isets isets trusted?))
  631. (define ctmod
  632. (make-expand-time-module modname filename version imports exports))
  633. (define expanded
  634. (expand-library call-with-target ctmod #`(begin . #,body)))
  635. (tree-il->reversed-bindings expanded modname imports exports
  636. bindings))))
  637. (define (program->reversed-bindings program bindings)
  638. "Same as @code{r6rs-library->reversed-bindings}, but for a program.
  639. @var{imports} is already parsed, as a list of @code{<import>}. A new
  640. module with a fresh name will be defined for the purposes of expanding "
  641. (match program
  642. (#f (cons (make-statement (make-void #f)) bindings))
  643. (($ <program> src trusted? isets body)
  644. (define modname (list (gensym "library-group-program")))
  645. (define filename (srcv-filename src))
  646. (define imports (parse-isets isets trusted?))
  647. (define ctmod
  648. (make-expand-time-module modname filename '() imports '()))
  649. (define expanded
  650. (expand-program call-with-target ctmod #`(begin . #,body)))
  651. (tree-il->reversed-bindings expanded modname imports '() bindings))))
  652. (define reversed-bindings
  653. (match group
  654. (($ <library-group> src libraries program)
  655. (program->reversed-bindings
  656. program
  657. (fold library->reversed-bindings '() libraries)))))
  658. (match reversed-bindings
  659. ((($ <statement> tail) . bindings)
  660. (let ((bindings (reverse bindings)))
  661. (make-letrec (library-group-src group)
  662. #t ; in-order?
  663. (map (match-lambda
  664. (($ <definition> name sym val) name)
  665. (($ <statement> exp) '_))
  666. bindings)
  667. (map (match-lambda
  668. (($ <definition> name sym val) sym)
  669. (($ <statement> exp) (gensym "_")))
  670. bindings)
  671. (map (match-lambda
  672. (($ <definition> name sym val) val)
  673. (($ <statement> exp)
  674. (if (void? exp)
  675. exp
  676. (make-seq #f exp (make-void #f)))))
  677. bindings)
  678. tail)))))