]> gitweb.factorcode.org Git - factor.git/blob - core/io/io-docs.factor
Implement and document stream-peek
[factor.git] / core / io / io-docs.factor
1 USING: help.markup help.syntax quotations hashtables kernel
2 classes strings continuations destructors math byte-arrays
3 alien ;
4 IN: io
5
6 ARTICLE: "stream-types" "Binary and text streams"
7 "A word which outputs the stream element type:"
8 { $subsections stream-element-type }
9 "Stream element types:"
10 { $subsections +byte+ +character+ }
11 "The stream element type is the data type read and written by " { $link stream-read1 } " and " { $link stream-write1 } "."
12 $nl
13 "Binary streams have an element type of " { $link +byte+ } ". Elements are integers in the range " { $snippet "[0,255]" } ", representing bytes. Reading a sequence of elements produces a " { $link byte-array } ". Any object implementing the " { $link >c-ptr } " and " { $link byte-length } " generic words can be written to a binary stream."
14 $nl
15 "Character streams have an element tye of " { $link +character+ } ". Elements are non-negative integers, representing Unicode code points. Only instances of the " { $link string } " class can be read or written on a character stream."
16 $nl
17 "Most external streams are binary streams, and can be wrapped in string streams once a suitable encoding has been provided; see " { $link "io.encodings" } "." ;
18
19 HELP: +byte+
20 { $description "A stream element type. See " { $link stream-element-type } " for explanation." } ;
21
22 HELP: +character+
23 { $description "A stream element type. See " { $link stream-element-type } " for explanation." } ;
24
25 HELP: stream-element-type
26 { $values { "stream" "a stream" } { "type" { $link +byte+ } " or " { $link +character+ } } }
27 { $contract "Outputs one of " { $link +byte+ } " or " { $link +character+ } "." } ;
28
29 HELP: stream-readln
30 { $values { "stream" "an input stream" } { "str/f" "a string or " { $link f } } }
31 { $contract "Reads a line of input from the stream. Outputs " { $link f } " on stream exhaustion." }
32 { $notes "Most code only works on one stream at a time and should instead use " { $link readln } "; see " { $link "stdio" } "." }
33 $io-error ;
34
35 HELP: stream-read1
36 { $values { "stream" "an input stream" } { "elt" "an element or " { $link f } } }
37 { $contract "Reads an element from the stream. Outputs " { $link f } " on stream exhaustion." }
38 { $notes "Most code only works on one stream at a time and should instead use " { $link read1 } "; see " { $link "stdio" } "." }
39 $io-error ;
40
41 HELP: stream-read
42 { $values { "n" "a non-negative integer" } { "stream" "an input stream" } { "seq" { $or byte-array string f } } }
43 { $contract "Reads " { $snippet "n" } " elements from the stream. Outputs a truncated string or " { $link f } " on stream exhaustion." }
44 { $notes "Most code only works on one stream at a time and should instead use " { $link read } "; see " { $link "stdio" } "." }
45 $io-error ;
46
47 HELP: stream-read-until
48 { $values { "seps" string } { "stream" "an input stream" } { "seq" { $or byte-array string f } } { "sep/f" "a character or " { $link f } } }
49 { $contract "Reads elements from the stream, until the first occurrence of a separator character, or stream exhaustion. In the former case, the separator is pushed on the stack, and is not part of the output string. In the latter case, the entire stream contents are output, along with " { $link f } "." }
50 { $notes "Most code only works on one stream at a time and should instead use " { $link read-until } "; see " { $link "stdio" } "." }
51 $io-error ;
52
53 HELP: stream-read-partial
54 { $values
55      { "n" "a non-negative integer" } { "stream" "an input stream" }
56      { "seq" { $or byte-array string f } } }
57 { $description "Reads at most " { $snippet "n" } " elements from a stream and returns up to that many characters without blocking. If no characters are available, blocks until some are and returns them." } ;
58
59 HELP: stream-write1
60 { $values { "elt" "an element" } { "stream" "an output stream" } }
61 { $contract "Writes an element to the stream. If the stream does buffering, output may not be performed immediately; use " { $link stream-flush } " to force output." }
62 { $notes "Most code only works on one stream at a time and should instead use " { $link write1 } "; see " { $link "stdio" } "." }
63 $io-error ;
64
65 HELP: stream-write
66 { $values { "data" "binary data or a string" } { "stream" "an output stream" } }
67 { $contract "Writes a piece of data to the stream. If the stream performs buffering, output may not be performed immediately; use " { $link stream-flush } " to force output." }
68 { $notes "Most code only works on one stream at a time and should instead use " { $link write } "; see " { $link "stdio" } "." }
69 $io-error ;
70
71 HELP: stream-flush
72 { $values { "stream" "an output stream" } }
73 { $contract "Waits for any pending output to complete." }
74 { $notes "With many output streams, written output is buffered and not sent to the underlying resource until either the buffer is full, or this word is called." }
75 { $notes "Most code only works on one stream at a time and should instead use " { $link flush } "; see " { $link "stdio" } "." }
76 $io-error ;
77
78 HELP: stream-nl
79 { $values { "stream" "an output stream" } }
80 { $contract "Writes a line terminator. If the stream does buffering, output may not be performed immediately; use " { $link stream-flush } " to force output." }
81 { $notes "Most code only works on one stream at a time and should instead use " { $link nl } "; see " { $link "stdio" } "." }
82 $io-error ;
83
84 HELP: stream-print
85 { $values { "str" string } { "stream" "an output stream" } }
86 { $description "Writes a newline-terminated string." }
87 { $notes "Most code only works on one stream at a time and should instead use " { $link print } "; see " { $link "stdio" } "." }
88 $io-error ;
89
90 HELP: stream-copy
91 { $values { "in" "an input stream" } { "out" "an output stream" } }
92 { $description "Copies the contents of one stream into another, closing both streams when done." } 
93 $io-error ;
94
95 HELP: stream-tell
96 { $values
97      { "stream" "a stream" } { "n" integer }
98 }
99 { $description "Returns the index of the stream pointer if the stream is seekable." }
100 { $notes "Stream seeking is not supported on streams that do not have a known length, e.g. TCP/IP streams." } ;
101
102
103 HELP: stream-seek
104 { $values
105      { "n" integer } { "seek-type" "a seek singleton" } { "stream" "a stream" }
106 }
107 { $description "Moves the pointer associated with a stream's handle to an offset " { $snippet "n" } " bytes from the seek type so that further reading or writing happens at the new location. For output streams, the buffer is flushed before seeking. Seeking past the end of an output stream will pad the difference with zeros once the stream is written to again." $nl
108     "Three methods of seeking are supported:"
109     { $list { $link seek-absolute } { $link seek-relative } { $link seek-end } }
110 }
111 { $notes "Stream seeking is not supported on streams that do not have a known length, e.g. TCP/IP streams." } ;
112
113 HELP: seek-absolute
114 { $values
115     
116      { "value" "a seek singleton" }
117 }
118 { $description "Seeks to an offset from the beginning of the stream." } ;
119
120 HELP: seek-end
121 { $values
122     
123      { "value" "a seek singleton" }
124 }
125 { $description "Seeks to an offset from the end of the stream. If the offset puts the stream pointer past the end of the data on an output stream, writing to it will pad the difference with zeros." } ;
126
127 HELP: seek-relative
128 { $values
129     
130      { "value" "a seek singleton" }
131 }
132 { $description "Seeks to an offset from the current position of the stream pointer." } ;
133
134 { seek-absolute seek-relative seek-end } related-words
135
136 HELP: seek-input
137 { $values
138      { "n" integer } { "seek-type" "a seek singleton" }
139 }
140 { $description "Calls " { $link stream-seek } " on the stream stored in " { $link input-stream } "." } ;
141
142 HELP: seek-output
143 { $values
144      { "n" integer } { "seek-type" "a seek singleton" }
145 }
146 { $description "Calls " { $link stream-seek } " on the stream stored in " { $link output-stream } "." } ;
147
148 HELP: input-stream
149 { $var-description "Holds an input stream for various implicit stream operations. Rebound using " { $link with-input-stream } " and " { $link with-input-stream* } "." } ;
150
151 HELP: output-stream
152 { $var-description "Holds an output stream for various implicit stream operations. Rebound using " { $link with-output-stream } " and " { $link with-output-stream* } "." } ;
153
154 HELP: error-stream
155 { $var-description "Holds an error stream." } ;
156
157 HELP: readln
158 { $values { "str/f" "a string or " { $link f } } }
159 { $description "Reads a line of input from " { $link input-stream } ". Outputs " { $link f } " on stream exhaustion." }
160 $io-error ;
161
162 HELP: read1
163 { $values { "elt" "an element or " { $link f } } }
164 { $description "Reads an element from " { $link input-stream } ". Outputs " { $link f } " on stream exhaustion." }
165 $io-error ;
166
167 HELP: read
168 { $values { "n" "a non-negative integer" } { "seq" { $or byte-array string f } } }
169 { $description "Reads " { $snippet "n" } " elements from " { $link input-stream } ". If there is no input available, outputs " { $link f } ". If there are less than " { $snippet "n" } " elements available, outputs a sequence shorter than " { $snippet "n" } " in length." }
170 $io-error ;
171
172 HELP: read-until
173 { $values { "seps" string } { "seq" { $or byte-array string f } } { "sep/f" "a character or " { $link f } } }
174 { $contract "Reads elements from " { $link input-stream } " until the first occurrence of a separator, or stream exhaustion. In the former case, the separator character is pushed on the stack, and is not part of the output. In the latter case, the entire stream contents are output, along with " { $link f } "." }
175 $io-error ;
176
177 HELP: read-partial
178 { $values { "n" integer } { "seq" { $or byte-array string f } } }
179 { $description "Reads at most " { $snippet "n" } " elements from " { $link input-stream } " and returns them in a sequence. This word should be used instead of " { $link read } " when processing the entire element a chunk at a time, since on some stream implementations it may be slightly faster." } ;
180
181 HELP: write1
182 { $values { "elt" "an element" } }
183 { $contract "Writes an element to " { $link output-stream } ". If the stream does buffering, output may not be performed immediately; use " { $link flush } " to force output." }
184 $io-error ;
185
186 HELP: write
187 { $values { "seq" { $or byte-array string f } } }
188 { $description "Writes a sequence of elements to " { $link output-stream } ". If the stream does buffering, output may not be performed immediately; use " { $link flush } " to force output." }
189 $io-error ;
190
191 HELP: flush
192 { $description "Waits for any pending output on " { $link output-stream } " to complete." }
193 $io-error ;
194
195 HELP: nl
196 { $description "Writes a line terminator to " { $link output-stream } ". If the stream does buffering, output may not be performed immediately; use " { $link flush } " to force output." }
197 $io-error ;
198
199 HELP: print
200 { $values { "str" string } }
201 { $description "Writes a newline-terminated string to " { $link output-stream } "." }
202 $io-error ;
203
204 HELP: with-input-stream
205 { $values { "stream" "an input stream" } { "quot" quotation } }
206 { $description "Calls the quotation in a new dynamic scope, with " { $link input-stream } " rebound to  " { $snippet "stream" } ". The stream is closed if the quotation returns or throws an error." } ;
207
208 HELP: with-output-stream
209 { $values { "stream" "an output stream" } { "quot" quotation } }
210 { $description "Calls the quotation in a new dynamic scope, with " { $link output-stream } " rebound to  " { $snippet "stream" } ". The stream is closed if the quotation returns or throws an error." } ;
211
212 HELP: with-streams
213 { $values { "input" "an input stream" } { "output" "an output stream" } { "quot" quotation } }
214 { $description "Calls the quotation in a new dynamic scope, with " { $link input-stream } " rebound to  " { $snippet "input" } " and " { $link output-stream } " rebound to  " { $snippet "output" } ". The stream is closed if the quotation returns or throws an error." } ;
215
216 HELP: with-streams*
217 { $values { "input" "an input stream" } { "output" "an output stream" } { "quot" quotation } }
218 { $description "Calls the quotation in a new dynamic scope, with " { $link input-stream } " rebound to  " { $snippet "input" } " and " { $link output-stream } " rebound to  " { $snippet "output" } "." }
219 { $notes "This word does not close the stream. Compare with " { $link with-streams } "." } ;
220
221 { with-input-stream with-input-stream* } related-words
222
223 { with-output-stream with-output-stream* } related-words
224
225 HELP: with-input-stream*
226 { $values { "stream" "an input stream" } { "quot" quotation } }
227 { $description "Calls the quotation in a new dynamic scope, with " { $link input-stream } " rebound to  " { $snippet "stream" } "." }
228 { $notes "This word does not close the stream. Compare with " { $link with-input-stream } "." } ;
229
230 HELP: with-output-stream*
231 { $values { "stream" "an output stream" } { "quot" quotation } }
232 { $description "Calls the quotation in a new dynamic scope, with " { $link output-stream } " rebound to  " { $snippet "stream" } "." }
233 { $notes "This word does not close the stream. Compare with " { $link with-output-stream } "." } ;
234
235 HELP: bl
236 { $description "Outputs a space character (" { $snippet "\" \"" } ") to " { $link output-stream } "." }
237 $io-error ;
238
239 HELP: stream-lines
240 { $values { "stream" "an input stream" } { "seq" "a sequence of strings" } }
241 { $description "Reads lines of text until the stream is exhausted, collecting them in a sequence of strings." } ;
242
243 HELP: lines
244 { $values { "seq" "a sequence of strings" } }
245 { $description "Reads lines of text until from the " { $link input-stream } " until it is exhausted, collecting them in a sequence of strings." } ;
246
247 HELP: each-line
248 { $values { "quot" { $quotation "( str -- )" } } }
249 { $description "Calls the quotation with successive lines of text, until the current " { $link input-stream } " is exhausted." } ;
250
251 HELP: each-block
252 { $values { "quot" { $quotation "( block -- )" } } }
253 { $description "Calls the quotation with successive blocks of data, until the current " { $link input-stream } " is exhausted." } ;
254
255 HELP: stream-contents
256 { $values { "stream" "an input stream" } { "seq" { $or string byte-array } } }
257 { $description "Reads all elements in the given stream until the stream is exhausted. The type of the sequence depends on the stream's element type." }
258 $io-error ;
259
260 HELP: contents
261 { $values { "seq" { $or string byte-array } } }
262 { $description "Reads all elements in the " { $link input-stream } " until the stream is exhausted. The type of the sequence depends on the stream's element type." }
263 $io-error ;
264
265 HELP: peek
266 { $values
267     { "n" integer }
268     { "seq/f" "a sequence or f" }
269 }
270 { $description "Reads the next " { $snippet "n" } " elements from the stream and seeks the stream to before the read." } ;
271
272 HELP: peek1
273 { $values
274         { "elt" "an element or f" }
275 }
276 { $description "Reads the next object from a stream and seeks the stream to before the read." } ;
277
278 HELP: stream-peek
279 { $values
280     { "n" integer } { "stream" "an input stream" }
281     { "seq/f" "a sequence or f" }
282 }
283 { $contract "Peeks " { $snippet "n" } " elements from the stream. Outputs " { $link f } " on stream exhaustion." }
284 { $notes "Most code only works on one stream at a time and should instead use " { $link peek } "; see " { $link "stdio" } "." }
285 $io-error ;
286
287 HELP: stream-peek1
288 { $values
289     { "stream" "an input stream" }
290     { "elt/f" "an element or f" }
291 }
292 { $contract "Peeks an element from the stream. Outputs " { $link f } " on stream exhaustion." }
293 { $notes "Most code only works on one stream at a time and should instead use " { $link peek1 } "; see " { $link "stdio" } "." }
294 $io-error ;
295
296 HELP: tell-input
297 { $values
298         { "n" integer }
299 }
300 { $description "Returns the index of the stream stored in " { $link input-stream } "." } ;
301
302 HELP: tell-output
303 { $values
304         { "n" integer }
305 }
306 { $description "Returns the index of the stream stored in " { $link output-stream } "." } ;
307
308 HELP: with-input-rewind
309 { $values
310     { "quot" quotation }    
311 }
312 { $description "Records the current seek position of the stream and calls the quotation. The position is then reset after the call." } ;
313
314 HELP: with-input-seek
315 { $values
316     { "n" integer } { "seek-type" "a seek singleton" } { "quot" quotation }    
317 }
318 { $description "Seeks the stream to a location, calls " { $snippet "quot" } ", and resets the input stream to where it was before the quotation was called." } ;
319
320 ARTICLE: "stream-protocol" "Stream protocol"
321 "The stream protocol consists of a large number of generic words, many of which are optional."
322 $nl
323 "Stream protocol words are rarely called directly, since code which only works with one stream at a time should be written to use " { $link "stdio" } " instead, wrapping I/O operations such as " { $link read } " and " { $link write } " in " { $link with-input-stream } " and " { $link with-output-stream } "."
324 $nl
325 "All streams must implement the " { $link dispose } " word in addition to the stream protocol."
326 { $subsections "stream-types" }
327 "These words are required for binary and string input streams:"
328 { $subsections
329     stream-peek1
330     stream-peek
331     stream-read1
332     stream-read
333     stream-read-until
334     stream-read-partial
335 }
336 "This word is only required for string input streams:"
337 { $subsections stream-readln }
338 "These words are required for binary and string output streams:"
339 { $subsections
340     stream-flush
341     stream-write1
342     stream-write
343 }
344 "This word is only required for string output streams:"
345 { $subsections stream-nl }
346 "These words are for seekable streams:"
347 { $subsections
348     stream-tell
349     stream-seek
350     tell-input
351     tell-output
352 }
353 { $see-also "io.timeouts" } ;
354
355 ARTICLE: "stdio-motivation" "Motivation for default streams"
356 "Most I/O code only operates on one stream at a time. The " { $link input-stream } " and " { $link output-stream } " variables are implicit parameters used by many I/O words. Using this idiom improves code in three ways:"
357 { $list
358     { "Code becomes simpler because there is no need to keep a stream around on the stack." }
359     { "Code becomes more robust because " { $link with-input-stream } " and " { $link with-output-stream } " automatically close the streams if there is an error." }
360     { "Code becomes more reusable because it can be written to not worry about which stream is being used, and instead the caller can use " { $link with-input-stream } " or " { $link with-output-stream } " to specify the source or destination for I/O operations." }
361 }
362 "For example, here is a program which reads the first line of a file, converts it to an integer, then reads that many characters, and splits them into groups of 16:"
363 { $code
364     "USING: continuations kernel io io.files math.parser splitting ;"
365     "\"data.txt\" utf8 <file-reader>"
366     "dup stream-readln string>number over stream-read 16 group"
367     "swap dispose"
368 }
369 "This code has two problems: it has some unnecessary stack shuffling, and if either " { $link stream-readln } " or " { $link stream-read } " throws an I/O error, the stream is not closed because " { $link dispose } " is never reached. So we can add a call to " { $link with-disposal } " to ensure the stream is always closed:"
370 { $code
371     "USING: continuations kernel io io.files math.parser splitting ;"
372     "\"data.txt\" utf8 <file-reader> ["
373     "    dup stream-readln string>number over stream-read"
374     "    16 group"
375     "] with-disposal"
376 }
377 "This code is robust, however it is more complex than it needs to be. This is where the default stream words come in; using them, the above can be rewritten as follows:"
378 { $code
379     "USING: continuations kernel io io.files math.parser splitting ;"
380     "\"data.txt\" utf8 <file-reader> ["
381     "    readln string>number read 16 group"
382     "] with-input-stream"
383 }
384 "An even better implementation that takes advantage of a utility word:"
385 { $code
386     "USING: continuations kernel io io.files math.parser splitting ;"
387     "\"data.txt\" utf8 ["
388     "    readln string>number read 16 group"
389     "] with-file-reader"
390 } ;
391
392 ARTICLE: "stdio" "Default input and output streams"
393 { $subsections "stdio-motivation" }
394 "The default input stream is stored in a dynamically-scoped variable:"
395 { $subsections input-stream }
396 "Unless rebound in a child namespace, this variable will be set to a console stream for reading input from the user."
397 $nl
398 "Words reading from the default input stream:"
399 { $subsections
400     peek1
401     peek
402     read1
403     read
404     read-until
405     read-partial
406 }
407 "If the default input stream is a character stream (" { $link stream-element-type } " outputs " { $link +character+ } "), lines of text can be read:"
408 { $subsections readln }
409 "Seeking on the default input stream:"
410 { $subsections seek-input }
411 "A pair of combinators for rebinding the " { $link input-stream } " variable:"
412 { $subsections
413     with-input-stream
414     with-input-stream*
415 }
416 "The default output stream is stored in a dynamically-scoped variable:"
417 { $subsections output-stream }
418 "Unless rebound in a child namespace, this variable will be set to a console stream for showing output to the user."
419 $nl
420 "Words writing to the default output stream:"
421 { $subsections
422     flush
423     write1
424     write
425 }
426 "If the default output stream is a character stream (" { $link stream-element-type } " outputs " { $link +character+ } "), lines of text can be written:"
427 { $subsections
428     print
429     nl
430     bl
431 }
432 "Seeking on the default output stream:"
433 { $subsections seek-output }
434 "Seeking descriptors:"
435 { $subsections
436     seek-absolute
437     seek-relative
438     seek-end
439 }
440 "Seeking combinators:"
441 { $subsections
442     with-input-seek
443     with-input-rewind
444 }
445 "A pair of combinators for rebinding the " { $link output-stream } " variable:"
446 { $subsections
447     with-output-stream
448     with-output-stream*
449 }
450 "A pair of combinators for rebinding both default streams at once:"
451 { $subsections
452     with-streams
453     with-streams*
454 } ;
455
456 ARTICLE: "stream-utils" "Stream utilities"
457 "There are a few useful stream-related words which are not generic, but merely built up from the stream protocol."
458 $nl
459 "First, a simple composition of " { $link stream-write } " and " { $link stream-nl } ":"
460 { $subsections stream-print }
461 "Processing lines one by one:"
462 { $subsections
463     stream-lines
464     lines
465     each-line
466 }
467 "Processing blocks of data:"
468 { $subsections
469     stream-contents
470     contents
471     each-block
472 }
473 "Copying the contents of one stream to another:"
474 { $subsections stream-copy } ;
475
476 ARTICLE: "stream-examples" "Stream example"
477 "Ask the user for their age, and print it back:"
478 { $code
479     "USING: io math.parser ;"
480     ""
481     ": ask-age ( -- ) \"How old are you?\" print ;"
482     ""
483     ": read-age ( -- n ) readln string>number ;"
484     ""
485     ": print-age ( n -- )"
486     "    \"You are \" write"
487     "    number>string write"
488     "    \" years old.\" print ;"
489     ": example ( -- ) ask-age read-age print-age ;"
490     ""
491     "example"
492 } ;
493
494 ARTICLE: "streams" "Streams"
495 "Input and output centers on the concept of a " { $emphasis "stream" } ", which is a source or sink of " { $emphasis "elements" } "."
496 { $subsections "stream-examples" }
497 "A stream can either be passed around on the stack or bound to a dynamic variable and used as one of the two implicit " { $emphasis "default streams" } "."
498 { $subsections
499     "stream-protocol"
500     "stdio"
501     "stream-utils"
502 }
503 { $see-also "io.streams.string" "io.streams.plain" "io.streams.duplex" } ;
504
505 ABOUT: "streams"