write.scm 7.9 KB

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