modules.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. ;;; Modules
  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. ;;; Run-time representation of module trees.
  18. ;;;
  19. ;;; Code:
  20. (library (hoot modules)
  21. (export module?
  22. make-empty-module
  23. module-name
  24. module-export!
  25. module-import!
  26. module-exported-names
  27. module-variable
  28. module-local-variable
  29. module-bound?
  30. module-ref
  31. module-set!
  32. module-define!
  33. submodule-ref
  34. submodule-define!
  35. module-public-interface
  36. resolve-module
  37. resolve-interface
  38. current-module)
  39. (import (hoot eq)
  40. (hoot errors)
  41. (hoot exceptions)
  42. (hoot hashtables)
  43. (hoot lists)
  44. (only (hoot numbers) exact-integer? negative?)
  45. (hoot not)
  46. (hoot pairs)
  47. (hoot records)
  48. (hoot symbols)
  49. (hoot syntax)
  50. (hoot syntax-objects)
  51. (hoot values)
  52. (hoot vectors)
  53. (ice-9 match)
  54. (only (hoot primitives)
  55. guile:syntax-module-bindings
  56. guile:syntax-module))
  57. (define-exception-type &binding-error &violation
  58. make-binding-error binding-error?
  59. (module binding-error-module)
  60. (name binding-error-name))
  61. (define-exception-type &undefined-variable &binding-error
  62. make-undefined-variable-error undefined-variable-error?)
  63. (define-exception-type &duplicate-definition &binding-error
  64. make-duplicate-definition-error duplicate-definition-error?)
  65. (define-exception-type &export-exists &binding-error
  66. make-export-exists-error export-exists-error?)
  67. (define-exception-type &immutable-import &binding-error
  68. make-immutable-import-error immutable-import-error?)
  69. (define-record-type <module>
  70. (%make-module name exports defs submodules state root)
  71. module?
  72. ;; list of name-component
  73. (name module-name)
  74. ;; public-name -> private-name | var
  75. (exports module-exports)
  76. ;; name -> #(var original-mod original-name)
  77. (defs module-defs)
  78. ;; name-component -> module
  79. (submodules module-submodules)
  80. ;; A module can be in four states, represented by the following
  81. ;; symbols:
  82. ;;
  83. ;; detached: The module is not referenced as a submodule of any
  84. ;; parent module. Any code that has a reference to the module
  85. ;; object can add new definitions.
  86. ;;
  87. ;; attached: The module is a submodule, but isn't yet open for
  88. ;; definitions.
  89. ;;
  90. ;; open: The module is not only attached, but it has formally been
  91. ;; created and is accumulating definitions.
  92. ;;
  93. ;; closed: The module is attached, complete, and does not accept
  94. ;; any more definitions.
  95. (state module-state set-module-state!)
  96. ;; #f | module
  97. (root module-root))
  98. (define* (make-module #:key
  99. (name '())
  100. (exports (make-eq-hashtable))
  101. (defs (make-eq-hashtable))
  102. (submodules (make-eq-hashtable))
  103. (state 'detached)
  104. (root #f))
  105. (%make-module name exports defs submodules state root))
  106. (define (name-component? x)
  107. (or (symbol? x)
  108. (and (exact-integer? x) (not (negative? x)))))
  109. (define (valid-module-name? name)
  110. (match name
  111. (((? name-component?) ...) name)
  112. (_ #f)))
  113. (define* (make-empty-module #:key (name '()) (root #f))
  114. (check-type name valid-module-name? 'make-empty-module)
  115. (when root (check-type root module? 'make-empty-module))
  116. (make-module #:name name #:root root))
  117. (define (module-exported-names mod)
  118. (check-type mod module? 'module-exported-names)
  119. (hashtable-keys (module-exports mod)))
  120. (define (module-local-variable mod name)
  121. (check-type mod module? 'module-local-variable)
  122. (check-type name symbol? 'module-local-variable)
  123. (match (hashtable-ref (module-defs mod) name)
  124. (#(var original-module original-name)
  125. (and (eq? mod original-module)
  126. var))
  127. (#f #f)))
  128. (define* (module-variable mod name kt #:optional
  129. (kf (lambda ()
  130. (raise
  131. (make-undefined-variable-error mod name)))))
  132. (match (hashtable-ref (module-defs mod) name)
  133. (#(var original-module original-name)
  134. (kt var original-module original-name))
  135. (#f
  136. (kf))))
  137. (define (module-bound? mod name)
  138. (module-variable mod name (lambda (var mod name) #t) (lambda () #f)))
  139. (define (module-attached? mod)
  140. (match (module-state mod)
  141. ('detached #f)
  142. ((or 'attached 'open 'closed) #t)))
  143. (define (module-accepts-definitions? mod)
  144. (match (module-state mod)
  145. ((or 'detached 'open) #t)
  146. ((or 'attached 'closed) #f)))
  147. (define* (module-export! mod name #:optional (src-name name))
  148. (check-type mod module? 'module-export!)
  149. (check-type mod module-accepts-definitions? 'module-export!)
  150. (check-type name symbol? 'module-export!)
  151. (check-type src-name symbol? 'module-export!)
  152. (when (hashtable-ref (module-exports mod) name)
  153. (raise (make-export-exists-error mod name)))
  154. ;; Lazily resolve the var, so as to allow for forward declaration of
  155. ;; exports.
  156. (hashtable-set! (module-exports mod) name src-name)
  157. (values))
  158. (define (resolve-export mod name)
  159. (match (hashtable-ref (module-exports mod) name)
  160. (#f (raise (make-undefined-variable-error mod name)))
  161. ((? symbol? private-name)
  162. (let ((v (hashtable-ref (module-defs mod) private-name)))
  163. (unless v
  164. (raise (make-undefined-variable-error mod name)))
  165. (hashtable-set! (module-exports mod) name v)
  166. v))
  167. (v v)))
  168. (define* (module-import! dst src name #:optional (src-name name))
  169. ;; Does not check that the module graph is acyclic.
  170. (check-type dst module? 'module-import!)
  171. (check-type dst module-accepts-definitions? 'module-import!)
  172. (check-type src module? 'module-import!)
  173. (check-type name symbol? 'module-import!)
  174. (check-type src-name symbol? 'module-import!)
  175. (when (module-bound? dst name)
  176. (raise (make-duplicate-definition-error dst name)))
  177. (match (resolve-export src src-name)
  178. ((and v #(var original-mod original-name))
  179. (hashtable-set! (module-defs dst) name v)
  180. var)))
  181. (define* (module-define! mod name value #:key (allow-redefinition? #f)
  182. (mutable? #t))
  183. (check-type mod module? 'module-define!)
  184. (check-type mod module-accepts-definitions? 'module-define!)
  185. (check-type name symbol? 'module-define!)
  186. (unless allow-redefinition?
  187. (when (module-bound? mod name)
  188. (raise (make-duplicate-definition-error mod name))))
  189. (let ((var (if mutable?
  190. (case-lambda
  191. (() value)
  192. ((new-value) (set! value new-value)))
  193. (case-lambda
  194. (() value)
  195. ((new-value)
  196. (raise (make-immutable-import-error mod name)))))))
  197. (hashtable-set! (module-defs mod) name (vector var mod name))
  198. var))
  199. (define (module-ref mod name)
  200. (module-variable mod name (lambda (var mod name) (var))))
  201. (define* (module-set! mod name value #:key (mutable-imports? #f))
  202. (check-type mod module? 'module-set!)
  203. (check-type name symbol? 'module-set!)
  204. (module-variable mod name
  205. (lambda (var original-mod original-name)
  206. (unless (or (eq? mod original-mod) mutable-imports?)
  207. (raise (make-immutable-import-error mod name)))
  208. (var value))))
  209. (define (submodule-ref mod name)
  210. (match name
  211. (()
  212. (match (module-state mod)
  213. ('attached #f)
  214. ((or 'detached 'open 'closed) mod)))
  215. ((name . name*)
  216. (match (hashtable-ref (module-submodules mod) name)
  217. (#f #f)
  218. (mod (submodule-ref mod name*))))))
  219. (define* (submodule-define! mod name)
  220. (match name
  221. (()
  222. (match (module-state mod)
  223. ('detached mod)
  224. ('attached (set-module-state! mod 'open) mod)
  225. ((or 'open 'closed)
  226. (raise (make-exception
  227. (make-assertion-violation)
  228. (make-exception-with-message "Module already registered")
  229. (make-exception-with-irritants (list mod)))))))
  230. ((name . name*)
  231. (submodule-define!
  232. (or (hashtable-ref (module-submodules mod) name)
  233. (let ((sub (make-module #:name (append (module-name mod) (list name))
  234. #:state 'attached)))
  235. (hashtable-set! (module-submodules mod) name sub)
  236. sub))
  237. name*))))
  238. (define (module-public-interface mod)
  239. (let ((iface (make-empty-module #:name (module-name mod)
  240. #:root (module-root mod))))
  241. (hashtable-for-each (lambda (name export)
  242. (module-import! iface mod name))
  243. (module-exports mod))
  244. iface))
  245. (define (resolve-module mod name)
  246. (check-type name valid-module-name? 'resolve-module)
  247. (define (find-relative-tail a b)
  248. (match a
  249. (() b)
  250. ((a . a*)
  251. (match b
  252. ((b . b*)
  253. (and (eq? a b) (find-relative-tail a* b*)))
  254. (() #f)))))
  255. (let ((tail (find-relative-tail (module-name mod) name)))
  256. (if tail
  257. (submodule-ref mod tail)
  258. (let ((root (module-root mod)))
  259. (and root (submodule-ref root name))))))
  260. (define (resolve-interface mod name)
  261. (let ((mod (resolve-module mod name)))
  262. (and mod (module-public-interface mod))))
  263. (define-syntax current-module
  264. (lambda (stx)
  265. (syntax-case stx ()
  266. ((current-module)
  267. #'(current-module current-module))
  268. ((_ id)
  269. (call-with-values (lambda () (guile:syntax-module-bindings #'id))
  270. (lambda (local imported)
  271. (with-syntax (((lname ...) local)
  272. ((iname ...) imported)
  273. ((_ modname ...) (datum->syntax
  274. #'id
  275. (guile:syntax-module #'id))))
  276. #`(let* ((bindings (make-eq-hashtable))
  277. (m (make-module #:name '(modname ...)
  278. #:defs bindings)))
  279. (hashtable-set! bindings 'lname
  280. (vector (case-lambda
  281. (() lname)
  282. ((v) (set! lname v)))
  283. m
  284. 'lname))
  285. ...
  286. (hashtable-set! bindings 'iname
  287. (vector (case-lambda
  288. (() iname)
  289. ((v) (raise (make-immutable-import-error #f 'iname))))
  290. m
  291. 'iname))
  292. ...
  293. m)))))))))