write.scm 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. ;;; (hoot write) library
  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. ;;; R7RS (scheme write) implementation, plus number->string
  18. ;;;
  19. ;;; Code:
  20. (library (hoot write)
  21. (export number->string
  22. display write write-shared write-simple)
  23. (import (hoot syntax)
  24. (hoot errors)
  25. (hoot inline-wasm)
  26. (hoot bitwise)
  27. (hoot bitvectors)
  28. (hoot bytevectors)
  29. (hoot char)
  30. (hoot keywords)
  31. (hoot procedures)
  32. (hoot strings)
  33. (hoot symbols)
  34. (hoot numbers)
  35. (hoot vectors)
  36. (hoot ports)
  37. (only (hoot records)
  38. record? write-record)
  39. (hoot syntax-objects)
  40. (hoot syntax-transformers)
  41. (hoot pairs)
  42. (hoot eq)
  43. (hoot match))
  44. (define* (number->string n #:optional (radix 10))
  45. (cond
  46. ((exact-integer? n)
  47. (if (zero? n)
  48. "0"
  49. (let* ((mag (if (< n 0) (- n) n))
  50. (digits
  51. (case radix
  52. ((2) (let lp ((mag mag) (out '()))
  53. (if (zero? mag)
  54. out
  55. (lp (ash mag -1)
  56. (cons (integer->char
  57. (+ (char->integer #\0)
  58. (logand mag 1)))
  59. out)))))
  60. ((8) (let lp ((mag mag) (out '()))
  61. (if (zero? mag)
  62. out
  63. (lp (ash mag -3)
  64. (cons (integer->char
  65. (+ (char->integer #\0)
  66. (logand mag 7)))
  67. out)))))
  68. ((10) (let lp ((mag mag) (out '()))
  69. (if (zero? mag)
  70. out
  71. (lp (quotient mag 10)
  72. (cons (integer->char
  73. (+ (char->integer #\0)
  74. (remainder mag 10)))
  75. out)))))
  76. ((16) (let lp ((mag mag) (out '()))
  77. (if (zero? mag)
  78. out
  79. (lp (ash mag -4)
  80. (cons (integer->char
  81. (let ((digit (logand mag 15)))
  82. (+ (if (< digit 10)
  83. (char->integer #\0)
  84. (char->integer #\a))
  85. digit)))
  86. out))))))))
  87. (list->string (if (negative? n) (cons #\- digits) digits)))))
  88. ((exact? n)
  89. (string-append (number->string (numerator n) radix)
  90. "/"
  91. (number->string (denominator n) radix)))
  92. ((real? n)
  93. (assert (eqv? radix 10) 'number->string)
  94. (%inline-wasm
  95. '(func (param $n f64)
  96. (result (ref eq))
  97. (struct.new $string
  98. (i32.const 0)
  99. (call $flonum->string (local.get $n))))
  100. n))
  101. (else
  102. (string-append (number->string (real-part n) radix)
  103. "/"
  104. (number->string (imag-part n) radix)
  105. "i"))))
  106. (define* (%write-datum port x #:optional quote-strings?)
  107. (define (recur x) (%write-datum port x quote-strings?))
  108. (cond
  109. ((eq? x #f) (write-string "#f" port))
  110. ((eq? x #t) (write-string "#t" port))
  111. ((eq? x #nil) (write-string "#nil" port))
  112. ((eq? x '()) (write-string "()" port))
  113. ((eq? x (if #f #f)) (write-string "#<unspecified>" port))
  114. ((eof-object? x) (write-string "#<eof>" port))
  115. ((number? x) (write-string (number->string x) port))
  116. ((char? x)
  117. (if quote-strings?
  118. (case x
  119. ((#\alarm) (write-string "#\\alarm" port))
  120. ((#\backspace) (write-string "#\\backspace" port))
  121. ((#\delete) (write-string "#\\delete" port))
  122. ((#\escape) (write-string "#\\escape" port))
  123. ((#\newline) (write-string "#\\newline" port))
  124. ((#\null) (write-string "#\\null" port))
  125. ((#\return) (write-string "#\\return" port))
  126. ((#\space) (write-string "#\\space" port))
  127. ((#\tab) (write-string "#\\tab" port))
  128. ((#\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m
  129. #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z
  130. #\A #\B #\C #\D #\E #\F #\G #\H #\I #\J #\K #\L #\M
  131. #\N #\O #\P #\Q #\R #\S #\T #\U #\V #\W #\X #\Y #\Z
  132. #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9
  133. #\` #\~ #\! #\@ #\# #\$ #\% #\^ #\& #\* #\( #\) #\- #\_ #\= #\+
  134. #\[ #\] #\{ #\} #\\ #\| #\; #\: #\' #\" #\< #\> #\, #\. #\/ #\?)
  135. (write-char #\# port)
  136. (write-char #\\ port)
  137. (write-char x port))
  138. (else
  139. (write-char #\# port)
  140. (write-char #\\ port)
  141. (write-char #\x port)
  142. (write-string (number->string (char->integer x) 16) port)))
  143. (write-char x port)))
  144. ((pair? x)
  145. (write-char #\( port)
  146. (recur (car x))
  147. (let lp ((tail (cdr x)))
  148. (cond
  149. ((null? tail)
  150. (write-char #\) port))
  151. ((pair? tail)
  152. (write-char #\space port)
  153. (recur (car tail))
  154. (lp (cdr tail)))
  155. (else
  156. (write-string " . " port)
  157. (recur tail)
  158. (write-char #\) port)))))
  159. ((string? x)
  160. (cond
  161. (quote-strings?
  162. (write-char #\" port)
  163. (string-for-each (lambda (ch)
  164. (case ch
  165. ((#\newline)
  166. (write-char #\\ port)
  167. (write-char #\n port))
  168. ((#\\ #\")
  169. (write-char #\\ port)
  170. (write-char ch port))
  171. (else
  172. (write-char ch port))))
  173. x)
  174. (write-char #\" port))
  175. (else
  176. (write-string x port))))
  177. ((symbol? x)
  178. (%write-datum port (symbol->string x) #f))
  179. ((vector? x)
  180. (write-char #\# port)
  181. (recur (vector->list x)))
  182. ((bytevector? x)
  183. (write-string "#vu8(" port)
  184. (let lp ((i 0))
  185. (when (< i (bytevector-length x))
  186. (unless (zero? i)
  187. (write-char #\space port))
  188. (write-string (number->string (bytevector-u8-ref x i)) port)
  189. (lp (1+ i))))
  190. (write-char #\) port))
  191. ((bitvector? x)
  192. (write-string "#*" port)
  193. (let lp ((i 0))
  194. (when (< i (bitvector-length x))
  195. (write-char (if (bitvector-ref x i) #\1 #\0) port)
  196. (lp (1+ i)))))
  197. ;; Test for records before procedures because applicable structs
  198. ;; are considered procedures.
  199. ((record? x)
  200. (write-record x port write))
  201. ((procedure? x)
  202. (match (procedure-name x)
  203. (#f
  204. (write-string "#<procedure>" port))
  205. (name
  206. (write-string "#<procedure " port)
  207. (write-string name port)
  208. (write-char #\> port))))
  209. ((keyword? x)
  210. (write-string "#:" port)
  211. (write-string (symbol->string (keyword->symbol x)) port))
  212. ((port? x)
  213. (write-string "#<port>" port))
  214. ((syntax? x)
  215. (write-string "#<syntax" port)
  216. (let ((expr (syntax-expression x))
  217. (src (syntax-sourcev x)))
  218. (when src
  219. (let ((file (vector-ref src 0))
  220. (line (1+ (vector-ref src 1)))
  221. (col (vector-ref src 2)))
  222. (write-char #\: port)
  223. (write-string (or file "unknown file") port)
  224. (write-char #\: port)
  225. (write-string (number->string line) port)
  226. (write-char #\: port)
  227. (write-string (number->string col) port)))
  228. (write-char #\space port)
  229. (write expr port)
  230. (write-char #\> port)))
  231. ((syntax-transformer? x)
  232. (write-string "#<syntax-transformer " port)
  233. (write (syntax-transformer-type x) port)
  234. (write-char #\space port)
  235. (write (syntax-transformer-value x) port)
  236. (write-char #\> port))
  237. (else
  238. (recur "unhandled object :("))))
  239. (define* (display datum #:optional (port (current-output-port)))
  240. (%write-datum port datum #f))
  241. (define* (write datum #:optional (port (current-output-port)))
  242. (%write-datum port datum #t))
  243. (define* (write-shared datum #:optional (port (current-output-port)))
  244. (raise (make-unimplemented-error 'write-shared)))
  245. (define* (write-simple datum #:optional (port (current-output-port)))
  246. (write datum port))
  247. )