enum.scm 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. ;; This file is part of scheme-GNUnet, a partial Scheme port of GNUnet
  2. ;; scheme-GNUnet contains scheme-extractor.
  3. ;; scheme-extractor is a partial Scheme port of libextractor.
  4. ;; Copyright (C) 2020, 2021 GNUnet e.V.
  5. ;; SPDX-License-Identifier: GPL-3.0-or-later
  6. ;;
  7. ;; libextractor is free software; you can redistribute it and/or modify
  8. ;; it under the terms of the GNU General Public License as published
  9. ;; by the Free Software Foundation; either version 3, or (at your
  10. ;; option) any later version.
  11. ;;
  12. ;; libextractor is distributed in the hope that it will be useful, but
  13. ;; WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. ;; General Public License for more details.
  16. ;;
  17. ;; You should have received a copy of the GNU General Public License
  18. ;; along with libextractor; see the file COPYING. If not, write to the
  19. ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  20. ;; Boston, MA 02110-1301, USA.
  21. ;; Brief: typed C-like enums
  22. ;; Features:
  23. ;; * typed
  24. ;; * integer and symbol conversion
  25. ;; * source line information (bug: isn't registered for some reason)
  26. ;; * docstrings
  27. ;; * enum values can be compared with eq?
  28. ;; (unless they aren't defined, in which
  29. ;; one must compare the indices directly,
  30. ;; or with value=?)
  31. (define-library (gnu extractor enum)
  32. (export value->index value->symbol value-dynamic?
  33. value-documentation value-source
  34. value-enum
  35. enum-name enum-max-value enum-predefined-values
  36. enum-source enum-docstring
  37. integer->value symbol->value symbol-value
  38. value enumeration define-enumeration
  39. value=?)
  40. (import (only (guile)
  41. write newline display
  42. syntax-source assq-ref compose
  43. resolve-module module-ref
  44. raise-exception)
  45. (only (system syntax) syntax-local-binding)
  46. (system vm program)
  47. (ice-9 format)
  48. (only (srfi srfi-9 gnu)
  49. set-record-type-printer!)
  50. (except (srfi srfi-1) map)
  51. (srfi srfi-26)
  52. (except (srfi srfi-43) vector-map)
  53. (rnrs base)
  54. ;;map vector-map)
  55. (rnrs control)
  56. (rnrs syntax-case)
  57. (rnrs records syntactic))
  58. (begin
  59. (define-record-type (<value> %make-value value?)
  60. ;; Numeric value
  61. (fields (immutable index value->index)
  62. ;; Symbolic name (or #f)
  63. (immutable symbol value->symbol)
  64. ;; Is this predefined (so eq? can be used),
  65. ;; or dynamically generated (so equal? must be used)?
  66. (immutable dynamic? value-dynamic?)
  67. ;; Docstring (or #f)
  68. (immutable docstring value-documentation)
  69. ;; thunked <enum>
  70. (immutable part-of value-enum-thunk)
  71. ;; Source location (or #f)
  72. (immutable source value-source))
  73. (sealed #t)
  74. (opaque #t))
  75. (define (value=? x y)
  76. "Compare two values of the same enumeration."
  77. (assert (eq? ((value-enum-thunk x))
  78. ((value-enum-thunk y))))
  79. (= (value->index x)
  80. (value->index y)))
  81. (define (value-enum enum)
  82. "To which enumeration does @var{enum} belong?"
  83. (let ((t (value-enum-thunk enum)))
  84. (if t (t) #f)))
  85. ;; FIXME variant if enum is sparse
  86. (define-record-type (<enum> %make-enum enum?)
  87. (fields (immutable max enum-max-value)
  88. (immutable symbol enum-name)
  89. (immutable values enum-predefined-values)
  90. (immutable source enum-source)
  91. (immutable docstring enum-docstring))
  92. (sealed #t)
  93. (opaque #t))
  94. ;; Make sure record printing terminates.
  95. ;; Also include line numbers, and remove
  96. ;; uninteresting data (and data that takes
  97. ;; too much space).
  98. (set-record-type-printer!
  99. <value>
  100. (lambda (record port)
  101. (let ((sources (value-source record)))
  102. (if sources
  103. ;; TODO source:[...] + syntax-source isn't correct,
  104. ;; at least on Guile 3.0.7, though no exception will result.
  105. (format port "#<value (~a ~a) index: ~a at ~a:~a:~a>"
  106. (enum-name ((value-enum-thunk record)))
  107. (value->symbol record)
  108. (value->index record)
  109. (source:file sources)
  110. (source:line sources)
  111. (source:column sources))
  112. (format port "#<value (~a ~a) index: ~a>"
  113. (enum-name ((value-enum-thunk record)))
  114. (value->symbol record)
  115. (value->index record))))))
  116. (set-record-type-printer!
  117. <enum>
  118. (lambda (record port)
  119. (let ((sources (enum-source record)))
  120. (if sources
  121. (format port "#<enum ~a (max: ~a) at ~a:~a:~a>"
  122. (enum-name record)
  123. (enum-max-value record)
  124. (source:file sources)
  125. (source:line sources)
  126. (source:column sources))
  127. (format port "<enum ~a (max: ~a)>"
  128. (enum-name record)
  129. (enum-max-value record))))))
  130. (define (%make-enum/fix max symbol values-proc source docstring)
  131. (letrec ((e (%make-enum max symbol
  132. (vector-map (lambda (vproc)
  133. (vproc (lambda () e)))
  134. values-proc)
  135. source docstring)))
  136. e))
  137. (define (integer->value enum i)
  138. (assert (and (exact? i) (integer? i)))
  139. (assert (<= 0 i))
  140. (assert (<= i (enum-max-value enum)))
  141. (let ((predef (enum-predefined-values enum)))
  142. (if (< i (vector-length predef))
  143. (vector-ref predef i)
  144. (%make-value i #f #t #f (lambda () enum) #f))))
  145. ;; Slow
  146. (define (symbol->value enum s)
  147. "Return the enum value in @var{enum} with symbol @var{s},
  148. or #f it doesn't exist."
  149. (let ((i (vector-index (compose (cute eq? s <>) value->symbol)
  150. (enum-predefined-values enum))))
  151. (and i (vector-ref (enum-predefined-values enum) i))))
  152. ;; Returned code is fast.
  153. (define-syntax symbol-value
  154. (lambda (x)
  155. "Takes a (name of) a enumeration @var{enum} and literal symbol
  156. @var{s} in that, and expands to an expression returning the enumeration
  157. value. Due to technical reasons, @var{enum} must be a binding from a
  158. module, and @var{enum} must be defined the same in the build and host."
  159. (syntax-case x ()
  160. ((_ enum s)
  161. (let-values (((type info) (syntax-local-binding #'enum)))
  162. (case type
  163. ((global)
  164. (let* ((module (resolve-module (cdr info)))
  165. (enum@host (module-ref module (car info)))
  166. (value@host (symbol->value enum@host
  167. (syntax->datum #'s)))
  168. (index (value->index value@host)))
  169. #`(vector-ref (enum-predefined-values enum) #,index)))
  170. (else (raise-exception
  171. (syntax-violation 'symbol-value
  172. "@var{enum} is not a global variable"
  173. x
  174. #'enum)))))))))
  175. (define (syntax->list s)
  176. (syntax-case s ()
  177. (() '())
  178. ((x . rest)
  179. (cons #'x (syntax->list #'rest)))))
  180. (define-syntax value
  181. (lambda (s)
  182. (syntax-case s ()
  183. ((_ (x y) ...)
  184. (let* ((key-value
  185. (zip (map syntax->datum (syntax->list #'(x ...)))
  186. (syntax->list #'(y ...))))
  187. (index/syntax (assq-ref key-value 'index))
  188. (index (car (syntax->datum index/syntax)))
  189. (symbol/syntax (assq-ref key-value 'symbol))
  190. (symbol (if symbol/syntax
  191. (car (syntax->datum symbol/syntax))
  192. #f))
  193. (docstring/syntax
  194. (assq-ref key-value 'documentation))
  195. (docstring (if docstring/syntax
  196. (car (syntax->datum docstring/syntax))
  197. #f)))
  198. (assert (and (exact? index) (integer? index)))
  199. (when symbol
  200. (assert (symbol? symbol)))
  201. (when docstring
  202. (assert (string? docstring)))
  203. #`(lambda (thunk)
  204. (%make-value #,index
  205. '#,(datum->syntax s symbol)
  206. #f
  207. #,docstring
  208. thunk
  209. '#,(datum->syntax #f (syntax-source s)))))))))
  210. ;; TODO verify indices are correct
  211. (define-syntax enumeration
  212. (lambda (s)
  213. (syntax-case s ()
  214. ((_ (name)
  215. (#:documentation doc)
  216. (#:max maximum)
  217. (#:known entry ...))
  218. #`(%make-enum/fix 'maximum
  219. 'name
  220. (vector entry ...)
  221. '#,(datum->syntax #f (syntax-source s))
  222. doc)))))
  223. (define-syntax define-enumeration
  224. (syntax-rules ()
  225. ((_ (name enum-value?)
  226. (#:documentation doc)
  227. (#:max maximum)
  228. (#:known entry ...))
  229. (begin
  230. (define name
  231. (enumeration (name)
  232. (#:documentation doc)
  233. (#:max maximum)
  234. (#:known entry ...)))
  235. (define (enum-value? o)
  236. (and (value? o)
  237. (eq? name ((value-enum-thunk o)))))))))))