more-port.scm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  2. ; Authors: Richard Kelsey, Jonathan Rees, Mike Sperber
  3. ; Additional port types
  4. ;----------------
  5. ; Ports which keep track of the current row and column.
  6. ;
  7. ; Note that we're only counting character access---single-byte access
  8. ; or block access are ignored.
  9. ;
  10. ; sub-port: port being tracked
  11. ; row, column: position of the next character
  12. (define-record-type port-location :port-location
  13. (really-make-port-location sub-port row column)
  14. port-location?
  15. (sub-port port-location-sub-port)
  16. (row port-location-row set-port-location-row!)
  17. (column port-location-column set-port-location-column!))
  18. (define (make-port-location sub-port)
  19. (really-make-port-location sub-port 0 0))
  20. (define (row-column-accessor accessor)
  21. (lambda (port)
  22. (let ((data (port-data port)))
  23. (if (port-location? data)
  24. (accessor data)
  25. #f))))
  26. (define current-row (row-column-accessor port-location-row))
  27. (define current-column (row-column-accessor port-location-column))
  28. (define (port-location-update! location ch)
  29. (if (char=? ch #\newline)
  30. (begin
  31. (set-port-location-row! location
  32. (+ 1 (port-location-row location)))
  33. (set-port-location-column! location 0))
  34. (set-port-location-column! location
  35. (+ 1 (port-location-column location)))))
  36. ; A codec that doesn't trigger the VM taking over
  37. (define-text-codec utf-8/diy "UTF-8/DIY"
  38. (lambda (char buffer start count)
  39. (char->utf (enum text-encoding-option utf-8) char buffer start count))
  40. (lambda (buffer start count)
  41. (utf->char (enum text-encoding-option utf-8) buffer start count)))
  42. ;----------------
  43. ; Input ports that keep track of the current row and column.
  44. (define (make-tracking-input-port port)
  45. (if (input-port? port)
  46. (let ((tracking
  47. (make-buffered-input-port tracking-input-port-handler
  48. (make-port-location port)
  49. (make-byte-vector (default-buffer-size) 0)
  50. 0
  51. 0)))
  52. ;; We need this because otherwise the VM won't give control
  53. ;; back to our handler for every character.
  54. (set-port-text-codec-spec! tracking utf-8/diy)
  55. tracking)
  56. (assertion-violation 'make-tracking-input-port "not an input port" port)))
  57. (define (make-tracking-one-char-input plain-one-char-input)
  58. (lambda (port mode)
  59. (let ((thing (plain-one-char-input port mode)))
  60. (cond
  61. (mode thing) ; PEEK or READY?
  62. ((eof-object? thing) thing)
  63. (else
  64. (port-location-update! (port-data port) thing)
  65. thing)))))
  66. (define tracking-input-port-handler
  67. (let ((plain-handler
  68. (make-buffered-input-port-handler
  69. (lambda (location)
  70. (list 'tracking-port (port-location-sub-port location)))
  71. (lambda (port) ; close
  72. (maybe-commit))
  73. (lambda (port wait?)
  74. (if (maybe-commit)
  75. (let ((got (read-block (port-buffer port)
  76. 0
  77. (byte-vector-length (port-buffer port))
  78. (port-location-sub-port (port-data port))
  79. wait?)))
  80. ;;(note-buffer-reuse! port)
  81. (if (eof-object? got)
  82. (set-port-pending-eof?! port #t)
  83. (begin
  84. (set-port-index! port 0)
  85. (set-port-limit! port got)))
  86. #t)
  87. #f))
  88. (lambda (port)
  89. (let ((ready? (byte-ready? (port-location-sub-port (port-data port)))))
  90. (if (maybe-commit)
  91. (values #t ready?)
  92. (values #f #f)))))))
  93. (make-port-handler
  94. (port-handler-discloser plain-handler)
  95. (port-handler-close plain-handler)
  96. (port-handler-byte plain-handler)
  97. (make-tracking-one-char-input (port-handler-char plain-handler))
  98. (port-handler-block plain-handler)
  99. (port-handler-ready? plain-handler)
  100. (port-handler-force plain-handler))))
  101. ;----------------
  102. ; Output ports that keep track of the current row and column.
  103. (define (make-tracking-one-char-output plain-one-char-output)
  104. (lambda (port ch)
  105. (plain-one-char-output port ch)
  106. (port-location-update! (port-data port) ch)))
  107. (define tracking-output-port-handler
  108. (let ((plain-handler
  109. (make-buffered-output-port-handler
  110. (lambda (location)
  111. (list 'tracking-port (port-location-sub-port location)))
  112. (lambda (port) ; close
  113. (maybe-commit))
  114. (lambda (port necessary?) ; we ignore necessary? and always write
  115. (if (maybe-commit) ; out whatever we have
  116. (begin
  117. (write-block (port-buffer port)
  118. 0
  119. (port-index port)
  120. (port-location-sub-port (port-data port)))
  121. ;;(note-buffer-reuse! port)
  122. (set-port-index! port 0)
  123. #t)
  124. #f))
  125. (lambda (port)
  126. (let ((ready? (output-port-ready?
  127. (port-location-sub-port (port-data port)))))
  128. (if (maybe-commit)
  129. (values #t ready?)
  130. (values #f #f)))))))
  131. (make-port-handler
  132. (port-handler-discloser plain-handler)
  133. (port-handler-close plain-handler)
  134. (port-handler-byte plain-handler)
  135. (make-tracking-one-char-output (port-handler-char plain-handler))
  136. (port-handler-block plain-handler)
  137. (port-handler-ready? plain-handler)
  138. (port-handler-force plain-handler))))
  139. (define (make-tracking-output-port port)
  140. (if (output-port? port)
  141. (let ((tracking
  142. (make-buffered-output-port tracking-output-port-handler
  143. (make-port-location port)
  144. (make-byte-vector (default-buffer-size) 0)
  145. 0
  146. (default-buffer-size))))
  147. ;; We need this because otherwise the VM won't give control
  148. ;; back to our handler for every character.
  149. (set-port-text-codec-spec! tracking utf-8/diy)
  150. tracking)
  151. (assertion-violation 'make-tracking-output-port "not an output port" port)))
  152. (define (fresh-line port)
  153. (let ((column (current-column port)))
  154. (if (and column (< 0 column))
  155. (newline port))))
  156. ;----------------
  157. ; String input ports
  158. ; All the work is done by the buffered-port code.
  159. (define string-input-port-handler
  160. (make-buffered-input-port-handler
  161. (lambda (ignore)
  162. (list 'string-input-port))
  163. (lambda (ignore)
  164. (maybe-commit))
  165. (lambda (port wait?)
  166. (set-port-pending-eof?! port #t)
  167. (maybe-commit))
  168. (lambda (port)
  169. (if (maybe-commit)
  170. (values #t #f)
  171. (values #f #f)))))
  172. ; We copy the input because it's mutable.
  173. (define (make-byte-vector-input-port bytes)
  174. (let* ((size (byte-vector-length bytes))
  175. (buffer (make-byte-vector size 0)))
  176. (copy-bytes! bytes 0 buffer 0 size)
  177. (make-byte-vector-input-port-internal buffer)))
  178. (define (make-byte-vector-input-port-internal buffer)
  179. (make-buffered-input-port string-input-port-handler
  180. #f ; no additional state needed
  181. buffer
  182. 0
  183. (byte-vector-length buffer)))
  184. (define (make-string-input-port string)
  185. (let* ((string-size (string-length string))
  186. (encoding-size (string-encoding-length/utf-8 string 0 string-size))
  187. (buffer (make-byte-vector encoding-size 0)))
  188. (encode-string/utf-8 string 0 string-size buffer 0 encoding-size)
  189. (let ((port (make-byte-vector-input-port-internal buffer)))
  190. (set-port-text-codec! port utf-8-codec)
  191. port)))
  192. ;----------------
  193. ; String output ports
  194. ; The cdr of the data field of the port is a list of buffers (the car is not
  195. ; used). When the output is wanted the buffers are concatenated together to
  196. ; get the final string.
  197. ;
  198. ; These are thread-safe for no particular reason.
  199. (define (make-byte-vector-output-port)
  200. (make-buffered-output-port string-output-port-handler
  201. (list #f)
  202. (make-byte-vector (default-buffer-size) 0)
  203. 0
  204. (default-buffer-size)))
  205. (define make-string-output-port make-byte-vector-output-port)
  206. ; Concatenates all of the buffers into single string.
  207. ; Could use a proposal...
  208. (define (byte-vector-output-port-output port)
  209. (ensure-atomicity
  210. (check-buffer-timestamp! port) ; makes the proposal check this
  211. (let* ((full (provisional-cdr (port-data port)))
  212. (last (port-buffer port))
  213. (index (provisional-port-index port))
  214. (out (make-byte-vector (apply +
  215. index
  216. (map byte-vector-length full))
  217. 0)))
  218. (let loop ((full (reverse full)) (i 0))
  219. (if (null? full)
  220. (copy-bytes! last 0 out i index)
  221. (let* ((buffer (car full))
  222. (count (byte-vector-length buffer)))
  223. (copy-bytes! buffer 0 out i count)
  224. (loop (cdr full) (+ i count)))))
  225. out)))
  226. (define (string-output-port-output port)
  227. (utf-8->string (byte-vector-output-port-output port) #\?))
  228. ; extract list of byte-vector chunks
  229. (define (byte-vector-output-port-chunks port)
  230. (ensure-atomicity
  231. (check-buffer-timestamp! port) ; makes the proposal check this
  232. (let* ((full (provisional-cdr (port-data port)))
  233. (last (port-buffer port))
  234. (index (provisional-port-index port))
  235. (last-copy (make-byte-vector index 0)))
  236. (copy-bytes! last 0 last-copy 0 index)
  237. (reverse (cons last-copy full)))))
  238. (define (write-byte-vector-output-port-output port other-port)
  239. (for-each (lambda (chunk)
  240. (write-block chunk
  241. 0
  242. (byte-vector-length chunk)
  243. other-port))
  244. (byte-vector-output-port-chunks port)))
  245. (define (write-string-output-port-output port other-port)
  246. (for-each (lambda (chunk)
  247. (display (utf-8->string chunk #\?) other-port))
  248. (byte-vector-output-port-chunks port)))
  249. (define string-output-port-handler
  250. (make-buffered-output-port-handler
  251. (lambda (port)
  252. '(string-output-port))
  253. (lambda (port) ; closer
  254. (maybe-commit))
  255. (lambda (port necessary?) ; we ignore necessary? and always write
  256. (provisional-set-cdr! ; out whatever we have
  257. (port-data port)
  258. (cons (let* ((size (provisional-port-index port))
  259. (new (make-byte-vector size 0)))
  260. (copy-bytes! (port-buffer port)
  261. 0
  262. new
  263. 0
  264. size)
  265. new)
  266. (provisional-cdr (port-data port))))
  267. (provisional-set-port-index! port 0)
  268. ;(note-buffer-reuse! port)
  269. (maybe-commit))
  270. (lambda (port)
  271. (if (maybe-commit)
  272. (values #t #f)
  273. (values #f #f)))))
  274. (define (call-with-string-output-port proc)
  275. (let ((port (make-string-output-port)))
  276. (proc port)
  277. (string-output-port-output port)))
  278. ;----------------
  279. ; Output ports from single byte consumers
  280. (define (byte-sink->output-port proc)
  281. (make-unbuffered-output-port byte-sink-output-port-handler
  282. proc))
  283. (define byte-sink-output-port-handler
  284. (make-unbuffered-output-port-handler
  285. (lambda (proc)
  286. (list 'byte-sink-output-port))
  287. make-output-port-closed!
  288. (lambda (port buffer start count)
  289. (let ((proc (port-data port)))
  290. (do ((i 0 (+ i 1)))
  291. ((= i count))
  292. (proc (byte-vector-ref buffer (+ start i)))))
  293. count)
  294. (lambda (port) ; ready?
  295. #t)))
  296. ; Output ports from single char consumers
  297. (define (char-sink->output-port proc)
  298. (make-unbuffered-output-port char-sink-output-port-handler
  299. proc))
  300. (define char-sink-output-port-handler
  301. (make-port-handler
  302. (lambda (proc)
  303. (list 'char-sink-output-port))
  304. make-output-port-closed!
  305. (lambda (port byte)
  306. (assertion-violation 'char-sink-output-port-handler
  307. "char port does not accept bytes"))
  308. (lambda (port ch)
  309. ((port-data port) ch))
  310. (lambda (port buffer start count)
  311. (assertion-violation 'char-sink-output-port-handler
  312. "char port does not accept bytes"))
  313. (lambda (port) ; ready?
  314. #t)
  315. (lambda (port error-if-closed?) ; output forcer
  316. (unspecific))))
  317. ; Call PROC on a port that will transfer COUNT bytes to PORT and
  318. ; then quit.
  319. (define (limit-output port count proc)
  320. (call-with-current-continuation
  321. (lambda (quit)
  322. (proc (byte-sink->output-port
  323. (lambda (byte)
  324. (write-byte byte port)
  325. (set! count (- count 1))
  326. (if (<= count 0)
  327. (quit #f))))))))
  328. ; Old name.
  329. (define write-one-line limit-output)
  330. ;----------------
  331. ; Input ports from single value producers
  332. ;
  333. ; ((make-source->input-port handler)
  334. ; <next-thunk>
  335. ; [<ready?-thunk>
  336. ; [<close-thunk>]])
  337. (define (make-source->input-port handler)
  338. (lambda (source . more)
  339. (make-buffered-input-port handler
  340. (make-source-data source
  341. (if (null? more)
  342. (lambda () #t)
  343. (car more))
  344. (if (or (null? more)
  345. (null? (cdr more)))
  346. values
  347. (cadr more)))
  348. (make-byte-vector 128 0)
  349. 0
  350. 0)))
  351. (define-record-type source-data :source-data
  352. (make-source-data source ready? close)
  353. source-data?
  354. (source source-data-source)
  355. (ready? source-data-ready?)
  356. (close source-data-close))
  357. (define (make-source-input-port-handler discloser-name encode-data)
  358. (make-buffered-input-port-handler
  359. (lambda (proc)
  360. (list discloser-name))
  361. (lambda (port)
  362. (make-input-port-closed! port)
  363. ((source-data-close (port-data port))))
  364. (lambda (port wait?)
  365. (let ((buffer (port-buffer port))
  366. (data (port-data port))
  367. (limit (provisional-port-limit port)))
  368. (let ((got
  369. (source-read-block encode-data
  370. port data
  371. buffer
  372. limit
  373. (- (byte-vector-length buffer) limit))))
  374. (provisional-set-port-limit! port (+ limit got))
  375. (maybe-commit))))
  376. (lambda (port)
  377. (if (port-pending-eof? port)
  378. #t
  379. ((source-data-ready? (port-data port)))))))
  380. (define (source-read-block encode-data port data buffer start count)
  381. (let loop ((i 0))
  382. (if (= i count)
  383. count
  384. (let ((next ((source-data-source data))))
  385. (if (eof-object? next)
  386. (begin
  387. (provisional-set-port-pending-eof?! port #t)
  388. i)
  389. (let ((got (encode-data next buffer (+ start i)))) ; we know the end is the limit
  390. (loop (+ i got))))))))
  391. (define (encode-byte thing buffer start)
  392. (byte-vector-set! buffer start thing)
  393. 1)
  394. (define byte-source-input-port-handler
  395. (make-source-input-port-handler 'byte-source-input-port
  396. encode-byte))
  397. (define byte-source->input-port
  398. (make-source->input-port byte-source-input-port-handler))
  399. (define char-source-input-port-handler
  400. (make-source-input-port-handler 'char-source-input-port
  401. encode-char/utf-8))
  402. (define char-source->input-port
  403. (let ((make (make-source->input-port char-source-input-port-handler)))
  404. (lambda (source . more)
  405. (let ((port (apply make source more)))
  406. (set-port-text-codec! port utf-8-codec)
  407. port))))