text:
Input and Output
11.1 Ports
By definition, ports in MzScheme produce and consume bytes. When a
port is provided to a character-based operation, such as
read, the port's bytes are read and interpreted as a UTF-8
encoding of characters (see also section 1.2.3). Thus, reading a
single character may require reading multiple bytes, and a procedure
char-ready? may need to peek several bytes into the
stream to determine whether a character is available. In the case of
a byte stream that does not correspond to a valid UTF-8 encoding,
functions such as
read-char may need to peek one byte ahead
in the stream to discover that the stream is not a valid encoding.
When an input port produces a sequence of bytes that is not a valid UTF-8 encoding in a character-reading context, then bytes that constitute an invalid sequence are converted to the character ``?''. Specifically, bytes 255 and 254 are always converted to ``?'', bytes in the range 192 to 253 produce ``?'' when they are not followed by bytes that form a valid UTF-8 encoding, and bytes in the range 128 to 191 are converted to ``?'' when they are not part of a valid encoding that was started by a preceding byte in the range 192 to 253. To put it another way, when reading a sequence of bytes as characters, a minimal set of bytes are changed to 6327 so that the entire sequence of bytes is a valid UTF-8 encoding.
See section 3.6 for procedures that facilitate conversions
using UTF-8 or other encodings. See also
reencode-input-port and
in Chapter 32
in PLT MzLib: Libraries Manual for obtaining a UTF-8-based port from one
that uses a different encoding of characters.
) returns
#t if either
( or
input-port? v)
( is
output-port? v)
#f otherwise.
) returns
#t if the given port is
a file-stream port (see section 11.1.6,
#f otherwise.
) returns
#t if the given port is
attached to an interactive terminal,
#f otherwise.
11.1.1 End-of-File Constant
The global variable
eof is bound to the end-of-file
value. The standard Scheme predicate
eof-object? returns
#t only when applied to this value.
Reading from a port produces an end-of-file result when the port has no more data, but some ports may also return end-of-file mid-stream. For example, a port connected to a Unix terminal returns an end-of-file when the user types control-d; if the user provides more input, the port returns additional bytes after the end-of-file.
11.1.2 Current Ports
The standard Scheme procedures
current-input-port and
current-output-port are implemented as parameters in
MzScheme. See section 188.8.131.52 for more information.
11.1.3 Opening File Ports
open-input-file and
procedures accept an optional flag argument after the filename that
specifies a mode for the file:
'binary-- bytes are returned from the port exactly as they are read from the file. Binary mode is the default mode.
'text-- return and linefeed bytes (10 and 13) are written to and read from the file are filtered by the port in a platform specific manner:
Unix and Mac OS X: no filtering occurs.
Windows reading: a return-linefeed combination from a file is returned by the port as a single linefeed; no filtering occurs for return bytes that are not followed by a linefeed, or for a linefeed that is not preceded by a return.
Windows writing: a linefeed written to the port is translated into a return-linefeed combination in the file; no filtering occurs for returns.
In Windows,
'textmode works only with regular files; attempting to use
'textwith other kinds of files triggers an
open-output-file procedure can also take a flag
argument that specifies how to proceed when a file with the specified
name already exists:
'truncate/replace-- try
'truncate; if it fails, try
'append-- append to the end of the file under Unix and Mac OS X; under Windows,
'appendis equivalent to
'update, except that the file position is immediately set to the end of the file after opening it
'update-- open an existing file without truncating it; if the file does not exist, the
exn:fail:filesystemexception is raised
open-input-output-file procedure takes the same
arguments as
, but it produces two values:
an input port and an output port. The two ports are connected in that
they share the underlying file device. This procedure is intended for
use with special devices that can be opened by only one process, such
as COM1 in Windows. For regular files, sharing the device can
be confusing. For example, using one port does not automatically
flush the other port's buffer (see section 11.1.6 for more
information about buffers), and reading or writing in one port moves
the file position (if any) for the other port. For regular files, use
open-input-file and
open-output-file calls
to avoid confusion.
Extra flag arguments are passed to
in any
order. Appropriate flag arguments can also be passed as the last
argument(s) to
with-output-to-file. When conflicting flag arguments
(e.g., both
'error and
'replace) are provided to
with-output-to-file, or
call-with-output-file, the
exn:fail:contract exception is raised.
with-input-from-file and
with-output-to-file close the port they create if control
jumps out of the supplied thunk (either through a continuation or an
exception), and the port remains closed if control jumps back into
the thunk. The current input or output port is installed and restored
parameterize (see section 7.9.2).
See section 11.1.6 for more information on file ports. When an input or output file-stream port is created, it is placed into the management of the current custodian (see section 9.2).
11.1.4 Pipes
(make-pipe [
limit-k input-name-v output-name-v]
) returns two port
values (see section 2.2): the first port is an input port and the
second is an output port. Data written to the output port is read
from the input port. The ports do not need to be explicitly closed.
The optional
limit-k argument can be
#f or a positive
exact integer. If
limit-k is omitted or
#f, the new
pipe holds an unlimited number of unread bytes (i.e., limited only by
the available memory). If
limit-k is a positive number, then
the pipe will hold at most
limit-k unread/unpeeked bytes;
writing to the pipe's output port thereafter will block until a read
or peek from the input port makes more space available. (Peeks
effectively extend the port's capacity until the peeked bytes are
The optional
input-name-v and
output-name-v are used as
the names for the returned input and out ports, respectively, if they
are supplied. Otherwise, the name of each port is
) returns the number of bytes
contained in a pipe, where
pipe-port is either of the pipe's
ports produced by
make-pipe. The pipe's content length
counts all bytes that have been written to the pipe and not yet read
(though possibly peeked).
11.1.5 String Ports
Scheme input and output can be read from or collected into a string or byte string:
)creates an input port that reads characters from
bytes(see section 3.6). Modifying
bytesafterward does not affect the byte stream produced by the port. The optional
name-vargument is used as the name for the returned port; the default is
)creates an input port that reads bytes from the UTF-8 encoding (see section 1.2.3) of
string. The optional
name-vargument is used as the name for the returned port; the default is
)creates an output port that accumulates the output into a byte string. The optional
name-vargument is used as the name for the returned port; the default is
)creates an output port that accumulates the output into a byte string. This procedure is the same as
)returns the bytes accumulated in
string-output-portso far in a freshly-allocated byte string. The bytes also remain in the port for further accumulation or for later calls to
(; see also section 3.6.
get-output-bytesstring-output-port) #\?)
String input and output ports do not need to be explicitly closed. The
procedure, described in section 11.1.6,
works for string ports in position-setting mode.
(define i (
open-input-string"hello world")) (define o (
open-output-string)) (
readi) o) (
get-output-stringo) ; =>
11.1.6 File-Stream Ports
A port created by
, and related functions is a file-stream
port. The initial input, output, and error ports in stand-alone
MzScheme are also file-stream ports. The
predicate recognizes file-stream ports.
An input port is block buffered by default, which means that on any
read, the buffer is filled with immediately-available bytes to speed
up future reads. Thus, if a file is modified between a pair of reads
to the file, the second read can produce stale data. Calling
to set an input port's file position flushes
its buffer.
Most output ports are block buffered by default, but a terminal output port is line buffered, and the error output port is unbuffered. An output buffer is filled with a sequence of written bytes to be committed as a group, either when the buffer is full (in block mode) or when a newline is written (in line mode).
A port's buffering can be changed via
(described below). The two ports produced by
open-input-output-file have independent buffers.
The following procedures work primarily on file-stream ports:
)forces all buffered data in the given output port to be physically written. If
output-portis omitted, then the current output port is flushed. Only file-stream ports and custom ports (see section 11.1.7) use buffers; when called on a port without a buffer,
has no effect.
By default, a file-stream port is block-buffered, but this behavior can be modified with
file-stream-buffer-mode. In addition, the initial current output and error ports are automatically flushed when
, etc. are performed on the initial standard input port.
)gets or sets the buffer mode for
port, if possible. All file-stream ports support setting the buffer mode, TCP ports (see section 11.4) support setting and getting the buffer mode, and custom ports (see section 11.1.7) may support getting and setting buffer modes.
mode-symbolis provided, it must be one of
'line(output only), or
'block, and the port's buffering is set accordingly. If the port does not support setting the mode, the
exn:failexception is raised.
mode-symbolis not provided, the current mode is returned, or
#fis returned if the mode cannot be determined. If
file-stream-portis an input port and
'line, the
exn:fail:contractexception is raised.
For an input port, peeking always places peeked bytes into the port's buffer, even when the port's buffer mode is
'none; furthermore, on some platforms, testing the port for input (via
sync) may be implemented with a peek. If an input port's buffer mode is
'none, then at most one byte is read for
, or
; if any bytes are buffered in the port (e.g., to satisfy a previous peek), the procedures may access multiple buffered bytes, but no further bytes are read.
)returns the current read/write position of
port. For file-stream and string ports,
port k-or-eof
)sets the read/write position to
k-or-eofrelative to the beginning of the file/string if
k-or-eofis a number, or to the current end of the file/string if
eof. In position-setting mode,
file-positionraises the
exn:fail:contractexception for port kinds other than file-stream and string ports. Calling
without a position on a non-file/non-string input port returns the number of bytes that have been read from that port if the position is known (see section 22.214.171.124), otherwise the
exn:fail:filesystemexception is raised.
(file-positionsets the position
kbeyond the current size of an output file or string, the file/string is enlarged to size
kand the new region is filled with
#\nul. If
kis beyond the end of an input file or string, then reading thereafter returns
without changing the port's position.
Not all file-stream ports support setting the position. If
is called with a position argument on such a file-stream port, the
exn:fail:filesystemexception is raised.
When changing the file position for an output port, the port is first flushed if its buffer is not empty. Similarly, setting the position for an input port clears the port's buffer (even if the new position is the same as the old position). However, although input and output ports produced by
open-input-output-fileshare the file position, setting the position via one port does not flush the other port's buffer.
)returns an exact positive integer that represents the identity of the device and file read or written by
file-stream-port. For two ports whose open times overlap, the result of
port-file-identityis the same for both ports if and only if the ports access the same device and file. For ports whose open times do not overlap, no guarantee is provided for the port identities (even if the ports actually access the same file) -- except as can be inferred through relationships with other ports. If
file-stream-portis closed, the
exn:failexception is raised. Under Windows 95, 98, and Me, if
file-stream-portis connected to a pipe instead of a file, the
exn:fail:filesystemexception is raised.
11.1.7 Custom Ports
create custom ports with arbitrary control procedures. Correctly
implementing a custom port can be tricky, because it amounts to
implementing a device driver. Custom ports are mainly useful to
obtain fine control over the action of committing bytes as read or
Many simple port variations can be implemented using threads and
pipes. For example, if
get-next-char is a function that
produces either a character or
, it can be turned into an
input port as follows
(let-values ([(r w) (
make-pipe4096)]) ;; Create a thread to move chars from
get-next-charto the pipe (
thread(lambda () (let loop () (let ([v (get-next-char)]) (if (eof-object? v) (
close-output-portw) (begin (
write-charv w) (loop))))))) ;; Return the read end of the pipe r)
The port.ss in MzLib provides several other port constructors; see Chapter 32 in PLT MzLib: Libraries Manual.
126.96.36.199 Custom Input
name-v read-proc optional-peek-proc close-proc [
optional-progress-evt-proc optional-commit-proc optional-location-proc count-lines!-proc init-position optional-buffer-mode-proc]
creates an input port. The port is immediately open for reading. If
close-proc procedure has no side effects, then the port need
not be explicitly closed.
name-v-- the name for the input port, which is reported by
object-name(see section 6.2.4).
read-proc-- a procedure that takes a single argument: a mutable byte string to receive read bytes. The procedure's result is one of the following:
the number of bytes read, as an exact, non-negative integer;
a procedure of arity four (representing a ``special'' result, as discussed further below) and optionally of arity two, but a procedure result is allowed only when
optional-peek-procis not
#f; or
a synchronizable event (see section 7.7) that becomes ready when the read is complete (roughly): the event's value can one of the above three results or another event like itself; in the last case, a reading process loops with
until it gets a non-event result.
read-procprocedure must not block indefinitely. If no bytes are immediately available for reading, the
read-procmust return
0or an event, and preferably an event (to avoid busy waits). The
read-procshould not return
0(or an event whose value is
0) when data is available in the port, otherwise polling the port will behave incorrectly. An event result from an event can also break polling.
If the result of a
read-proccall is not one of the above values, the
exn:fail:contractexception is raised. If a returned integer is larger than the supplied byte string's length, the
exn:fail:contractexception is raised. If
#fand a procedure for a special result is returned, the
exn:fail:contractexception is raised.
read-procprocedure can report an error by raising an exception, but only if no bytes are read. Similarly, no bytes should be read if
, an event, or a procedure is returned. In other words, no bytes should be lost due to spurious exceptions or non-byte data.
A port's reading procedure may be called in multiple threads simultaneously (if the port is accessible in multiple threads), and the port is responsible for its own internal synchronization. Note that improper implementation of such synchronization mechanisms might cause a non-blocking read procedure to block indefinitely.
optional-progress-evt-proc, and
optional-commit-procare all provided and non-
#f, then the following is an acceptable implementation of
(lambda (bstr) (let* ([progress-evt (progress-evt-proc)] [v (peek-proc bstr 0 progress-evt)]) (cond [(
sync/timeout0 progress-evt) 0] ; try again [(
evt?v) (wrap-evt v (lambda (x) 0))] ; sync, then try again [(and (
number?v) (
zero?v)) 0] ; try again [else (if (optional-commit-proc (if (
number?v) v 1) progress-evt always-evt) v ; got a result 0)]))) ; try again
An implementor may choose not to implement the
optional-procedures, however, and even an implementor who does supply
optional-procedures may provide a different
read-procthat uses a fast path for non-blocking reads.
optional-peek-proc-- either
#for a procedure that takes three arguments:
a mutable byte string to receive peeked bytes;
a non-negative number of bytes (or specials) to skip before peeking; and
#for a progress event produced by
The results and conventions for
optional-peek-procare mostly the same as for
read-proc. The main difference is in the handling of the progress event, if it is not
#f. If the given progress event becomes ready, the
optional-peek-procmust abort any skip attempts and not peek any values. In particular,
optional-peek-procmust not peek any values if the progress event is initially ready.
optional-peek-procshould produce
#f(or an event whose value is
#f) if no bytes were peeked because the progress event became ready. Like
read-proc, a
0result indicates that another attempt is likely to succeed, so
0is inappropriate when the progress event is ready. Also like
optional-peek-procmust not block indefinitely.
The skip count provided to
optional-peek-procis a number of bytes (or specials) that must remain present in the port -- in addition to the peek results -- when the peek results are reported. If a progress event is supplied, then the peek is effectively canceled when another process reads data before the given number can be skipped. If a progress event is not supplied and data is read, then the peek must effectively restart with the original skip count.
The system does not check that multiple peeks return consistent results, or that peeking and reading produce consistent results.
#f, then peeking for the port is implemented automatically in terms of reads, but with several limitations. First, the automatic implementation is not thread-safe. Second, the automatic implementation cannot handle special results (non-byte and non-eof), so
read-proccannot return a procedure for a special when
#f. Finally, the automatic peek implementation is incompatible with progress events, so if
#f, then
optional-commit-procmust be
#f. See also
make-input-port/peek-to-readin Chapter 32 in PLT MzLib: Libraries Manual.
close-proc-- a procedure of zero arguments that is called to close the port. The port is not considered closed until the closing procedure returns. The port's procedures will never be used again via the port after it is closed. However, the closing procedure can be called simultaneously in multiple threads (if the port is accessible in multiple threads), and it may be called during a call to the other procedures in another thread; in the latter case, any outstanding reads and peeks should be terminated with an error.
optional-progress-evt-proc-- either
#f(the default), or a procedure that takes no arguments and returns an event. The event must become ready only after data is next read from the port or the port is closed. After the event becomes ready, it must remain so. (See also
semaphore-peek-evtin section 7.4.)
#f, then
port-provides-progress-evts?applied to the port will produce
#f, and the port will not be a valid argument to
optional-commit-proc-- either
#f(the default), or a procedure that takes three arguments:
an exact, positive integer kr;
a progress event produced by
an event,
done-evt, that is either a channel-put event, channel, semaphore, semaphore-peek event, always event, or never event.
A commit corresponds to removing data from the stream that was previously peeked, but only if no other process removed data first. (The removed data does not need to be reported, because it has been peeked already.) More precisely, assuming that kp bytes, specials, and mid-stream
s have been previously peeked or skipped at the start of the port's stream,
optional-commit-procmust satisfy the following constraints:
It must return only when the commit is complete or when the given progress event becomes ready.
It must commit only if kp is positive.
If it commits, then it must do so with either kr items or kp items, whichever is smaller, and only if kp is positive.
It must never choose
done-evtin a synchronization after the given progress event is ready, or after
done-evthas been synchronized once.
It must not treat any data as read from the port unless
done-evtis chosen in a synchronization.
It must not block indefinitely if
done-evtis ready; it must return soon after the read completes or soon after the given progress event is ready, whichever is first.
It can report an error by raising an exception, but only if no data is committed. In other words, no data should be lost due to an exception, including a break exception.
It must return a true value if data is committed,
#fotherwise. When it returns a value, the given progress event must be ready (perhaps because data was just committed).
It must raise an exception if no data (including
) has been peeked from the beginning of the port's stream, or if it would have to block indefinitely to wait for the given progress event to become ready.
A call to
parameterize-breaked to disable breaks.
optional-location-proc-- either
#f(the default), or a procedure that takes no arguments and returns three values: the line number for the next item in the port's stream (a positive number or
#f), the column number for the next item in the port's stream (a non-negative number or
#f), and the position for the next item in the port's stream (a positive number or
#f). See also section 18.104.22.168.
This procedure is only called if line counting is enabled for the port via
port-count-lines!(in which case
count-lines!-procis called). The
read-honu, and
read-honu-syntaxprocedures assume that reading a non-whitespace character increments the column and position by one.
count-lines!-proc-- a procedure of no arguments that is called if and when line counting is enabled for the port. The default procedure is
init-position-- an exact, positive integer that determines the position of the port's first item, used when line counting is not enabled for the port. The default is
optional-buffer-mode-proc-- either
#f(the default) or a procedure that accepts zero or one arguments. If
#f, then the resulting port does not support a buffer-mode setting. Otherwise, the procedure is called with one symbol argument (
'none) to set the buffer mode, and it is called with zero arguments to get the current buffer mode. In the latter case, the result must be
'none, or
#f(unknown). See section 11.1.6 for more information on buffer modes.
read-proc or
optional-peek-proc (or an event
produced by one of these) returns a procedure, and the procedure is
used to obtain a non-byte result.29 The procedure is called by
peek-byte-or-special, or
peek-char-or-special. The
special-value procedure can return an arbitrary value, and it will be
called zero or one times (not necessarily before further reads or
peeks from the port). See section 11.2.9 for more details on
the procedure's arguments and result.
read-proc or
optional-peek-proc returns a special
procedure when called by any reading procedure other than
, or
, then the
exn:fail:contract exception is raised.
;; A port with no input... ;; Easy:
(;; Hard: (define /dev/null-in (
null(lambda (s)
eof) (lambda (skip s progress-evt)
void(lambda () never-evt) (lambda (k progress-evt done-evt) (
error"no successful peeks!")))) (
read-char/dev/null-in) ; =>
peek-char/dev/null-in) ; =>
read-byte-or-special/dev/null-in) ; =>
peek-byte-or-special/dev/null-in 100) ; =>
;; A port that produces a stream of 1s: (define infinite-ones (
make-input-port'ones (lambda (s) (
bytes-set!s 0 (
char->integer#\1)) 1) #f
void)) (
read-string5 infinite-ones) ; =>
"11111";; But we can't peek ahead arbitrarily far, because the ;; automatic peek must record the skipped bytes: (
peek-string5 (
expt2 5000) infinite-ones) ; => error: out of memory ;; An infinite stream of 1s with a specific peek procedure: (define infinite-ones (let ([one! (lambda (s) (
bytes-set!s 0 (
char->integer#\1)) 1)]) (
make-input-port'ones one! (lambda (s skip progress-evt) (one! s))
void))) (
read-string5 infinite-ones) ; =>
"11111";; Now we can peek ahead arbitrarily far: (
peek-string5 (
expt2 5000) infinite-ones) ; =>
"11111";; The port doesn't supply procedures to implement progress events: (
port-provides-progress-evts?infinite-ones) ; =>
port-progress-evtinfinite-ones) ; error: no progress events ;; Non-byte port results: (define infinite-voids (
make-input-port'voids (lambda (s) (lambda args '
void)) (lambda (skip s) (lambda args '
void)) (
read-charinfinite-voids) ; => error: non-char in an unsupported context (
read-char-or-specialinfinite-voids) ; =>
';; This port produces 0, 1, 2, 0, 1, 2, etc., but it is not ;; thread-safe, because multiple threads might read and change
n. (define mod3-cycle/one-thread (let* ([n 2] [mod! (lambda (s delta) (
bytes-set!s 0 (+ 48 (
modulo(+ n delta) 3))) 1)]) (
make-input-port'mod3-cycle/not-thread-safe (lambda (s) (set! n (
add1n) 3)) (mod! s 0)) (lambda (s skip) (mod! s skip))
void))) (
read-string5 mod3-cycle/one-thread) ; =>
peek-string5 (
expt2 5000) mod3-cycle/one-thread) ; =>
"20120";; Same thing, but thread-safe and kill-safe, and with progress ;; events. Only the server thread touches the stateful part ;; directly. (See the output port examples for a simpler thread-safe ;; example, but this one is more general.) (define (make-mod3-cycle) (define read-req-ch (
make-channel)) (define peek-req-ch (
make-channel)) (define progress-req-ch (
make-channel)) (define commit-req-ch (
make-channel)) (define close-req-ch (
make-channel)) (define closed? #f) (define n 0) (define progress-sema #f) (define (mod! s delta) (
bytes-set!s 0 (+ 48 (
modulo(+ n delta) 3))) 1) ;; ---------------------------------------- ;; The server has a list of outstanding commit requests, ;; and it also must service each port operation (read, ;; progress-evt, etc.) (define (serve commit-reqs response-evts) (
sync(handle-evt read-req-ch (handle-read commit-reqs response-evts)) (handle-evt progress-req-ch (handle-progress commit-reqs response-evts)) (handle-evt commit-req-ch (add-commit commit-reqs response-evts)) (handle-evt close-req-ch (handle-close commit-reqs response-evts)) (
map(make-handle-response commit-reqs response-evts) response-evts) (
map(make-handle-commit commit-reqs response-evts) commit-reqs)))) ;; Read/peek request: fill in the string and commit (define ((handle-read commit-reqs response-evts) r) (let ([s (
carr)] [skip (
cadrr)] [ch (
caddrr)] [nack (
cadddrr)] [peek? (
cddddrr)]) (unless closed? (mod! s skip) (unless peek? (commit! 1))) ;; Add an event to respond: (serve commit-reqs (
choice-evtnack (
channel-put-evtch (if closed? 0 1))) response-evts)))) ;; Progress request: send a peek evt for the current ;; progress-sema (define ((handle-progress commit-reqs response-evts) r) (let ([ch (
carr)] [nack (
cdrr)]) (unless progress-sema (set! progress-sema (
make-semaphore(if closed? 1 0)))) ;; Add an event to respond: (serve commit-reqs (
choice-evtnack (
channel-put-evtch (semaphore-peek-evt progress-sema))) response-evts)))) ;; Commit request: add the request to the list (define ((add-commit commit-reqs response-evts) r) (serve (
consr commit-reqs) response-evts)) ;; Commit handling: watch out for progress, in which case ;; the response is a commit failure; otherwise, try ;; to sync for a commit. In either event, remove the ;; request from the list (define ((make-handle-commit commit-reqs response-evts) r) (let ([k (
carr)] [progress-evt (
cadrr)] [done-evt (
caddrr)] [ch (
cadddrr)] [nack (
cddddrr)]) ;; Note: we don't check that k is < the sum of ;; previous peeks, because the entire stream is actually ;; known, but we could send an exception in that case. (
choice-evt(handle-evt progress-evt (lambda (x) (
syncnack (
channel-put-evtch #f)) (serve (
remqr commit-reqs) response-evts))) ;; Only create an event to satisfy done-evt if progress-evt ;; isn't already ready. ;; Afterward, if progress-evt becomes ready, then this ;; event-making function will be called again, because ;; the server controls all posts to progress-evt. (if (
sync/timeout0 progress-evt) never-evt (handle-evt done-evt (lambda (v) (commit! k) (
syncnack (
channel-put-evtch #t)) (serve (
remqr commit-reqs) response-evts))))))) ;; Response handling: as soon as the respondee listens, ;; remove the response (define ((make-handle-response commit-reqs response-evts) evt) (handle-evt evt (lambda (x) (serve commit-reqs (
remqevt response-evts))))) ;; Close handling: post the progress sema, if any, and set ;; the
closed?flag (define ((handle-close commit-reqs response-evts) r) (let ([ch (
carr)] [nack (
cdrr)]) (set! closed? #t) (when progress-sema (
semaphore-postprogress-sema)) (serve commit-reqs (
choice-evtnack (
channel-put-evtch (
void))) response-evts)))) ;; Helper for reads and post-peek commits: (define (commit! k) (when progress-sema (
semaphore-postprogress-sema) (set! progress-sema #f)) (set! n (+ n k))) ;; Start the server thread: (define server-thread (
thread(lambda () (serve
null)))) ;; ---------------------------------------- ;; Client-side helpers: (define (req-evt f) (
nack-guard-evt(lambda (nack) ;; Be sure that the server thread is running: (
thread-resumeserver-thread (
current-thread)) ;; Create a channel to hold the reply: (let ([ch (
make-channel)]) (f ch nack) ch)))) (define (read-or-peek-evt s skip peek?) (req-evt (lambda (ch nack) (
channel-putread-req-ch (
list*s skip ch nack peek?))))) ;; Make the port: (
make-input-port'mod3-cycle ;; Each handler for the port just sends ;; a request to the server (lambda (s) (read-or-peek-evt s 0 #f)) (lambda (s skip) (read-or-peek-evt s skip #t)) (lambda () ; close (
sync(req-evt (lambda (ch nack) (
channel-putprogress-req-ch (
list*ch nack)))))) (lambda () ; progress-evt (
sync(req-evt (lambda (ch nack) (
channel-putprogress-req-ch (
list*ch nack)))))) (lambda (k progress-evt done-evt) ; commit (
sync(req-evt (lambda (ch nack) (
channel-putcommit-req-ch (
list*k progress-evt done-evt ch nack)))))))) (let ([mod3-cycle (make-mod3-cycle)]) (let ([result1 #f] [result2 #f]) (let ([t1 (
thread(lambda () (set! result1 (
read-string5 mod3-cycle))))] [t2 (
thread(lambda () (set! result2 (
read-string5 mod3-cycle))))]) (
thread-waitt1) (
thread-waitt2) (
string-appendresult1 "," result2))) ; =>
"02120,10201", maybe (let ([s (
make-bytes1)] [progress-evt (
port-progress-evtmod3-cycle)]) (
peek-bytes-avail!s 0 progress-evt mod3-cycle) ; =>
1s ; =>
port-commit-peeked1 progress-evt (
make-semaphore1) mod3-cycle) ; =>
sync/timeout0 progress-evt) ; =>
peek-bytes-avail!s 0 progress-evt mod3-cycle) ; =>
port-commit-peeked1 progress-evt (
make-semaphore1) mod3-cycle)) ; =>
22.214.171.124 Custom Output
name-v evt write-proc close-proc [
optional-write-special-proc optional-write-evt-proc optional-special-evt-proc optional-location-proc count-lines!-proc init-position optional-buffer-mode-proc]
creates an output port. The port is immediately open for writing. If
close-proc procedure has no side effects, then the port need
not be explicitly closed. The port can buffer data within its
write-proc and
optional-write-special-proc procedures.
name-v-- the name for the output port, which is reported by
object-name(see section 6.2.4).
evt-- a synchronization event (see section 7.7; e.g., a semaphore or another port). The event is used in place of the output port when the port is supplied to synchronization procedures like
sync. Thus, the event should be unblocked when the port is ready for writing at least one byte without blocking, or ready to make progress in flushing an internal buffer without blocking. The event must not unblock unless the port is ready for writing; otherwise, the guarantees of
will be broken for the output port. Use
always-evtif writes to the port always succeed without blocking.
write-proc-- a procedure of five arguments:
an immutable byte string containing bytes to write;
a non-negative exact integer for a starting offset (inclusive) into the byte string;
a non-negative exact integer for an ending offset (exclusive) into the byte string;
a boolean;
#findicates that the port is allowed to keep the written bytes in a buffer, and that it is allowed to block indefinitely;
#tindicates that the write should not block, and that the port should attempt to flush its buffer and completely write new bytes instead of buffering them;
a boolean;
#tindicates that if the port blocks for a write, then it should enable breaks while blocking (e.g., using
sync/enable-break; this argument is always
#fif the fourth argument is
The procedure returns one of the following:
a non-negative exact integer representing the number of bytes written or buffered;
#fif no bytes could be written, perhaps because the internal buffer could not be completely flushed;
a synchronizable event (see section 7.7) that acts like the result of
write-bytes-avail-evtto complete the write.
write-proccan produce an event, an acceptable implementation of
write-procis to pass its first three arguments to the port's
optional-write-evt-proc. Some port implementors, however, may choose not to provide
optional-write-evt-proc(perhaps because writes cannot be made atomic), or may implement
write-procto enable a fast path for non-blocking writes or to enable buffering.
From a user's perspective, the difference between buffered and completely written data is (1) buffered data can be lost in the future due to a failed write, and (2)
flush-outputforces all buffered data to be completely written. Under no circumstances is buffering required.
If the start and end indices are the same, then the fourth argument to
write-procwill be
#f, and the write request is actually a flush request for the port's buffer (if any), and the result should be
0for a successful flush (or if there is no buffer).
The result should never be
0if the start and end indices are different, otherwise the
exn:fail:contractexception is raised. If a returned integer is larger than the supplied byte-string range, the
exn:fail:contractexception is raised.
#fresult should be avoided, unless the next write attempt is likely to work. Otherwise, if data cannot be written, return an event instead.
An event returned by
write-proccan return
#for another event like itself, in contrast to events produced by
optional-write-evt-proc. A writing process loops with
until it obtains a non-event result.
write-procprocedure is always called with breaks disabled, independent of whether breaks were enabled when the write was requested by a client of the port. If breaks were enabled for a blocking operation, then the fifth argument to
write-procwill be
#t, which indicates that
write-procshould re-enable breaks while blocking.
If the writing procedure raises an exception, due either to write or commit operations, it must not have committed any bytes (though it may have committed previously buffered bytes).
A port's writing procedure may be called in multiple threads simultaneously (if the port is accessible in multiple threads). The port is responsible for its own internal synchronization. Note that improper implementation of such synchronization mechanisms might cause a non-blocking write procedure to block.
close-proc-- a procedure of zero arguments that is called to close the port. The port is not considered closed until the closing procedure returns. The port's procedures will never be used again via the port after it is closed. However, the closing procedure can be called simultaneously in multiple threads (if the port is accessible in multiple threads), and it may be called during a call to the other procedures in another thread; in the latter case, any outstanding writes or flushes should be terminated immediately with an error.
optional-write-special-proc-- either
#f(the default), or a procedure to handle
write-specialcalls for the port. If
#f, then the port does not support special output, and
port-writes-special?will return
#fwhen applied to the port.
If a procedure is supplied, it takes three arguments: the special value to write, a boolean that is
#fif the procedure can buffer the special value and block indefinitely, and a boolean that is
#tif the procedure should enable breaks while blocking. The result is one of the following:
a non-event true value, which indicates that the special is written;
#fif the special could not be written, perhaps because an internal buffer could not be completely flushed;
a synchronizable event (see section 7.7) that acts like the result of
write-special-evtto complete the write.
optional-write-special-proccan return an event, passing the first argument to an implementation of
option-write-special-evt-procis acceptable as an
As for
write-proc, the
#fresult is discouraged, since it can lead to busy waiting. Also as for
write-proc, an event produced by
optional-write-special-procis allowed to produce
#for another event like itself. The
optional-write-special-procprocedure is always called with breaks disabled, independent of whether breaks were enabled when the write was requested by a client of the port.
optional-write-evt-proc-- either
#f(the default) or a procedure of three arguments:
an immutable byte string containing bytes to write;
a non-negative exact integer for a starting offset (inclusive) into the byte string, and
a non-negative exact integer for an ending offset (exclusive) into the byte string.
The result is a synchronizable event (see section 7.7) to act as the result of
write-bytes-avail-evtfor the port (i.e., to complete a write or flush), which becomes available only as data is committed to the port's underlying device, and whose result is the number of bytes written.
#f, then
port-writes-atomic?will produce
#fwith applied to the port, and the port will not be a valid argument to procedures such as
Otherwise, an event returned by
optional-write-evt-procmust not cause data to be written to the port unless the event is chosen in a synchronization, and it must write to the port if the event is chosen (i.e., the write must appear atomic with respect to the synchronization).
If the event's result integer is larger than the supplied byte-string range, the
exn:fail:contractexception is raised by a wrapper on the event. If the start and end indices are the same (i.e., no bytes are to be written), then the event should produce
0when the buffer is completely flushed. (If the port has no buffer, then it is effectively always flushed.)
If the event raises an exception, due either to write or commit operations, it must not have committed any new bytes (though it may have committed previously buffered bytes).
Naturally, a port's events may be used in multiple threads simultaneously (if the port is accessible in multiple threads). The port is responsible for its own internal synchronization.
optional-write-special-evt-proc-- either
#f(the default), or a procedure to handle
write-special-evtcalls for the port. This argument must be
#fif either
#f, and it must be a procedure if both of those arguments are procedures.
If it is a procedure, it takes one argument: the special value to write. The resulting event (with its constraints) is analogous to the result of
If the event raises an exception, due either to write or commit operations, it must not have committed the special value (though it may have committed previously buffered bytes and values).
optional-location-proc-- either
#f(the default), or a procedure that takes no arguments and returns three values: the line number for the next item written to the port's stream (a positive number or
#f), the column number for the next item written to port's stream (a non-negative number or
#f), and the position for the next item written to port's stream (a positive number or
#f). See also section 184.108.40.206.
This procedure is only called if line counting is enabled for the port via
port-count-lines!(in which case
count-lines!-procis called).
count-lines!-proc-- a procedure of no arguments that is called if and when line counting is enabled for the port. The default procedure is
init-position-- an exact, positive integer that determines the position of the port's first output item, used when line counting is not enabled for the port. The default is
optional-buffer-mode-proc-- either
#f(the default) or a procedure that accepts zero or one arguments. If
#f, then the resulting port does not support a buffer-mode setting. Otherwise, the procedure is called with one symbol argument (
'line, or
'none) to set the buffer mode, and it is called with zero arguments to get the current buffer mode. In the latter case, the result must be
'none, or
#f(unknown). See section 11.1.6 for more information on buffer modes.
;; A port that writes anything to nowhere: (define /dev/null-out (
nullalways-evt (lambda (s start end non-block? breakable?) (- end start))
void(lambda (special non-block? breakable?) #t) (lambda (s start end) (wrap-evt always-evt (lambda (x) (- end start)))) (lambda (special) always-evt))) (
display"hello" /dev/null-out) ; => void (write-bytes-avail #"hello" /dev/null-out) ; =>
5(write-special 'hello /dev/null-out) ; =>
sync(write-bytes-avail-evt #"hello" /dev/null-out)) ; =>
5;; A part that accumulates bytes as characters in a list, ;; but not in a thread-safe way: (define accum-list
null) (define accumulator/not-thread-safe (
make-output-port'accum/not-thread-safe always-evt (lambda (s start end non-block? breakable?) (set! accum-list (
appendaccum-list (
integer->char(bytes->list (subbytes s start end))))) (- end start))
void)) (
display"hello" accumulator/not-thread-safe) accum-list ; =>
'(#\h #\e #\l #\l #\o);; Same as before, but with simple thread-safety: (define accum-list
null) (define accumulator (let* ([lock (
make-semaphore1)] [lock-peek-evt (semaphore-peek-evt lock)]) (
make-output-port'accum lock-peek-evt (lambda (s start end non-block? breakable?) (if (
semaphore-try-wait?lock) (begin (set! accum-list (
appendaccum-list (
integer->char(bytes->list (subbytes s start end))))) (
semaphore-postlock) (- end start)) ;; Cheap strategy: block until the list is unlocked, ;; then return 0, so we get called again (wrap-evt lock-peek (lambda (x) 0))))
void))) (
display"hello" accumulator) accum-list ; =>
'(#\h #\e #\l #\l #\o);; A port that transforms data before sending it on ;; to another port. Atomic writes exploit the ;; underlying port's ability for atomic writes. (define (make-latin-1-capitalize port) (define (byte-upcase s start end) (list->bytes (
map(lambda (b) (
integer->charb)))) (bytes->list (subbytes s start end))))) (
make-output-port'byte-upcase ;; This port is ready when the original is ready: port ;; Writing procedure: (lambda (s start end non-block? breakable?) (let ([s (byte-upcase s start end)]) (if non-block? (write-bytes-avail* s port) (begin (
displays port) (
bytes-lengths))))) ;; Close procedure --- close original port: (lambda () (
close-output-portport)) #f ;; Write event: (and (port-writes-atomic? port) (lambda (s start end) (write-bytes-avail-evt (byte-upcase s start end) port))))) (define orig-port (
open-output-string)) (define cap-port (make-latin-1-capitalize orig-port)) (
display"Hello" cap-port) (
get-output-stringorig-port) ; =>
sync(write-bytes-avail-evt #"Bye" cap-port)) ; =>
get-output-stringorig-port) ; =>
11.2 Reading and Writing
MzScheme's support for reading and writing includes many extensions compared to R5RS, both at the level of individual bytes and characters and at the level of S-expressions.
11.2.1 Reading Bytes, Characters, and Strings
In addition to the standard reading procedures, MzScheme provides
byte-reading procedure, block-reading procedures such as
, and more.
input-port mode-symbol]
)returns a string containing the next line of bytes from
input-port. If
input-portis omitted, the current input port is used.
Characters are read from
input-portuntil a line separator or an end-of-file is read. The line separator is not included in the result string (but it is removed from the port's stream). If no characters are read before an end-of-file is encountered,
is returned.
mode-symbolargument determines the line separator(s). It must be one of the following symbols:
'linefeedbreaks lines on linefeed characters; this is the default.
'returnbreaks lines on return characters.
'return-linefeedbreaks lines on return-linefeed combinations. If a return character is not followed by a linefeed character, it is included in the result string; similarly, a linefeed that is not preceded by a return is included in the result string.
'anybreaks lines on any of a return character, linefeed character, or return-linefeed combination. If a return character is followed by a linefeed character, the two are treated as a combination.
'any-onebreaks lines on either a return or linefeed character, without recognizing return-linefeed combinations.
Return and linefeed characters are detected after the conversions that are automatically performed when reading a file in text mode. For example, reading a file in text mode under Windows automatically changes return-linefeed combinations to a linefeed. Thus, when a file is opened in text mode,
'linefeedis usually the appropriate
input-port mode-symbol]
)is analogous to
read-line, but it reads bytes and produces a byte string.
)returns a string containing the next
kcharacters from
input-port. The default value of
input-portis the current input port.
0, then the empty string is returned. Otherwise, if fewer than
kcharacters are available before an end-of-file is encountered, then the returned string will contain only those characters before the end-of-file (i.e., the returned string's length will be less than
k). 31 If no characters are available before an end-of-file, then
is returned.
If an error occurs during reading, some characters may be lost (i.e., if
successfully reads some characters before encountering an error, the characters are dropped.)
)is analogous to
read-string, but it reads bytes and produces a byte string.
input-port start-k end-k]
)reads characters from
read-string, but puts them into
stringstarting from index
start-k(inclusive) up to
end-k(exclusive). The default value of
input-portis the current input port. The default value of
0. The default value of
end-kis the length of the
string. Like
, the
exn:fail:contractexception is raised if
end-kis out-of-range for
If the difference between
0, then
0is returned and
bytesis not modified. If no bytes are available before an end-of-file, then
is returned. Otherwise, the return value is the number of bytes read. If
mbytes are read and
start-k, then
bytesis not modified at indices
input-port start-k end-k]
)is analogous to
read-string!, but it reads bytes and puts them into a byte string.
input-port start-k end-k]
)is like
read-bytes!, but returns without blocking after reading immediately-available bytes, and it may return a procedure for a ``special'' result. The
procedure blocks only if no bytes (or specials) are yet available. Also unlike
never drops bytes; if
successfully reads some bytes and then encounters an error, it suppresses the error (treating it roughly like an end-of-file) and returns the read bytes. (The error will be triggered by future reads.) If an error is encountered before any bytes have been read, an exception is raised.
input-portproduces a special value, as described in section 11.1.7, the result is a procedure of four arguments. The four arguments correspond to the location of the special value within the port, as described in section 11.1.7. If the procedure is called more than once with valid arguments, the
exn:fail:contractexception is raised. If
read-bytes-availreturns a special-producing procedure, then it does not place characters in
bytes. Similarly,
read-bytes-availplaces only as many bytes into
bytesas are available before a special value in the port's stream.
input-port start-k end-k]
)is like
, except that it returns
0immediately if no bytes (or specials) are available for reading and the end-of-file is not reached.
input-port start-k end-k]
)is like
, except that breaks are enabled during the read (see also section 6.6). If breaking is disabled when
is called, and if the
exn:breakexception is raised as a result of the call, then no bytes will have been read from
k skip-k[
)is similar to
read-string, except that the returned characters are preserved in the port for future reads. (More precisely, undecoded bytes are left for future reads.) The
skip-kargument indicates a number of bytes (not characters) in the input stream to skip before collecting characters to return; thus, in total, the next
skip-kbytes plus
kcharacters are inspected.
For most kinds of ports, inspecting
skip-kbytes and
kcharacters requires at least
kbytes of memory overhead associated with the port, at least until the bytes/characters are read. No such overhead is required when peeking into a string port (see section 11.1.5), a pipe port (see section 11.1.4), or a custom port with a specific peek procedure (depending on how the peek procedure is implemented; see section 11.1.7).
If a port produces
mid-stream, peek skips beyond the
always produce
until the
is read.
k skip-k[
)is analogous to
peek-string, but it peeks bytes and produces a byte string.
string skip-k[
input-port start-k end-k]
)is like
read-string!, but for peeking, and with a
skip-kargument like
bytes skip-k[
input-port start-k end-k]
)is analogous to
peek-string!, but it peeks bytes and puts them into a byte string.
bytes skip-k[
progress-evt input-port start-k end-k]
)is like
, but for peeking, and with two extra arguments. The
skip-kargument is as in
. The
progress-evtargument must be either
#f(the default) or an event produced by
To peek,
blocks until finding an end-of-file, at least one byte (or special) past the skipped bytes, or until a non-
progress-evtbecomes ready. Furthermore, if
progress-evtis ready before bytes are peeked, no bytes are peeked or skipped, and
progress-evtmay cut short the skipping process if it becomes available during the peek attempt.
The result of
0only in the case that
progress-evtbecomes ready before bytes are peeked.
bytes skip-k[
progress-evt input-port start-k end-k]
)is like
, but for peeking, and with
progress-evtarguments like
. Since this procedure never blocks, it may return before even
skip-kbytes are available from the port.
bytes skip-k[
progress-evt input-port start-k end-k]
)is the peeking version of
, with
progress-evtarguments like
)is analogous to
, but it reads and returns a byte (or
eof) instead of a character.
)is the same as
, except that if the input port returns a non-byte value (through a value-generating procedure in a custom port; see section 11.1.7 and section 18.104.22.168 for details), the non-byte value is returned.
)is analogous to
, but it reads and returns a byte instead of a character.
input-port skip-k]
)extends the standard
with an optional argument (defaulting to
0) that represents the number of bytes (not characters) to skip.
input-port skip-k]
)is analogous to
, but it reads and returns a byte instead of a character.
input-port skip-k]
)is the same as
, except that if the input port returns a non-byte value after
skip-kbyte positions, it is returned.
input-port skip-k progress-evt]
)is analogous to
, but it reads and returns a byte instead of a character, and it supports a
progress-evtargument (which is
#fby default) like
)returns an event that becomes ready after any subsequent read from
input-port, or after
input-portis closed. After the event becomes ready, it remains ready. If progress events are unavailable for
input-port(as reported by
port-provides-progress-evts?) the
exn:fail:contractexception is raised.
port-progress-evtcan return an event for
input-port. All built-in kinds of ports support progress events, but ports created with
make-input-port(see section 11.1.7) may not.
k progress-evt evt[
)attempts to commit as read the first
kpreviously peeked bytes, non-byte specials, and
s from
input-port, or the first
or special value peeked from
input-port.32 The read commits only if
progress-evtdoes not become ready first (i.e., if no other process reads from
input-portfirst), and only if
evtis chosen by a
port-commit-peeked(in which case the event result is ignored); the
evtmust be either a channel-put event, channel, semaphore, semaphore-peek event, always event, or never event. Suspending the thread that calls
port-commit-peekedmay or may not prevent the commit from proceeding. The result from
#tif data is committed, and
If no data has been peeked from
progress-evtis not ready, then
exn:fail:contractexception is raised. If fewer than
kitems have been peeked at the current start of
input-port's stream, then only the peeked items are committed as read. If
input-port's stream currently starts at an
or a non-byte special value, then only the
or special value is committed as read.
progress-evtis not a result of
port-progress-evtapplied to
input-port, then
exn:fail:contractexception is raised.
188.8.131.52 Counting Positions, Lines, and Columns
By default, MzScheme keeps track of the position in a port
as the number of bytes that have been read from or written to any
port (independent of the read/write position, which is accessed or
changed with
). Optionally, however, MzScheme
can track the position in terms of characters (after UTF-8 decoding),
instead of bytes, and it can track line locations and
column locations; this optional tracking must be
specifically enabled for a port via
parameter (see
section 188.8.131.52). Position, line, and column
locations for a port are used by
section 12.2 for more information) and
read-honu-syntax. Position and line locations are numbered
from 1; column locations are numbered from 0.
)turns on line and column counting for a port. Counting can be turned on at any time, though generally it is turned on before any data is read from or written to a port. When a port is created, if the value of the
port-count-lines-enabledparameter is true (see section 18.104.22.168), then line counting is automatically enabled for the port. Line counting cannot be disabled for a port after it is enabled.
When counting lines, MzScheme treats linefeed, return, and return-linefeed combinations as a line terminator and as a single position (on all platforms). Each tab advances the column count to one before the next multiple of 8. When a sequence of bytes in the range 128 to 253 forms a UTF-8 encoding of a character, the position/column is incremented is incremented once for each byte, and then decremented appropriately when a complete encoding sequence is discovered. See also section 11.1 for more information on UTF-8 decoding for ports.
A position is known for any port as long as its value can be expressed as a fixnum (which is more than enough tracking for realistic applications in, say, syntax-error reporting). If the position for a port exceeds the value of the largest fixnum, then the position for the port becomes unknown, and line and column tacking is disabled. Return-linefeed combinations are treated as a single character position only when line and column counting is enabled.
)returns three values: a positive exact integer or
#ffor the line number of the next read/written item, a non-negative exact integer or
#ffor the next item's column, and a positive exact integer or
#ffor the next item's position. The next column and position normally increases as bytes are read from or written to the port, but if line/character counting is enabled for
port, the column and position results can decrease after reading or writing a byte that ends a UTF-8 encoding sequence.
Certain kinds of exceptions (see section 6.1) encapsulate
source-location information using a
srcloc structure, which
has five fields:
source-- An arbitrary value identifying the source, often a path (see section 11.3.1).
line-- The line number, a positive exact integer (counts from 1) or
column-- The column number, a non-negative exact integer (counts from 0) or
position-- The starting position, a positive exact integer (counts from 1) or
span-- The number of covered positions, a non-negative exact integer (counts from 0) or
The fields of a
srcloc structure are immutable, so no
field-mutator procedures are defined for
srcloc. The
srcloc structure type is transparent to all inspectors (see
section 4.5).
11.2.2 Writing Bytes, Characters, and Strings
In addition to the standard printing procedures, MzScheme provides
byte-writing procedures, block-writing procedures such as
write-string, and more.
output-port start-k end-k]
)write characters to
stringstarting from index
start-k(inclusive) up to
end-k(exclusive). The default value of
output-portis the current output port. The default value of
0. The default value of
end-kis the length of the
string. Like
, the
exn:fail:contractexception is raised if
end-kis out-of-range for
The result is the number of characters written to
output-port, which is always
(- end-k start-k).
output-port start-k end-k]
)is analogous to
write-string, but it writes a byte string.
output-port start-k end-k]
)is like
write-bytes, but it returns without blocking after writing as many bytes as it can immediately flush. It blocks only if no bytes can be flushed immediately. The result is the number of bytes written and flushed to
output-port; if
start-kis the same as
end-k, then the result can be
0(indicating a successful flush of any buffered data), otherwise the result is at least
1but possibly less than
(- end-k start-k).
write-bytes-availprocedure never drops bytes; if
write-bytes-availsuccessfully writes some bytes and then encounters an error, it suppresses the error and returns the number of written bytes. (The error will be triggered by future writes.) If an error is encountered before any bytes have been written, an exception is raised.
output-port start-k end-k]
)is like
write-bytes-avail, except that it never blocks, it returns
#fif the port contains buffered data that cannot be written immediately, and it returns
0if the port's internal buffer (if any) is flushed but no additional bytes can be written immediately.
input-port start-k end-k]
)is like
write-bytes-avail, except that breaks are enabled during the write. The procedure provides a guarantee about the interaction of writing and breaks: if breaking is disabled when
write-bytes-avail/enable-breakis called, and if the
exn:breakexception is raised as a result of the call, then no bytes will have been written to
output-port. See also section 6.6.
)is analogous to
write-char, but for writing a byte instead of a character.
vdirectly to
output-portif it supports special writes, or raises
exn:fail:contractif the port does not support special write. The result is always
#t, indicating that the write succeeded.
)is like
write-special, but without blocking. If
vcannot be written immediately, the result is
#fwithout writing
v, otherwise the result is
vis written.
output-port start-k end-k]
)is similar to
write-bytes-avail, but instead of writing bytes immediately, it returns a synchronizable event (see section 7.7). The
output-portmust support atomic writes, as indicated by
Synchronizing on the object starts a write from
bytes, and the event becomes ready when bytes are written (unbuffered) to the port. If
end-kare the same, then the synchronization result is
0when the port's internal buffer (if any) is flushed, otherwise the result is a positive exact integer. If the event is not selected in a synchronization, then no bytes will have been written to
)is similar to
write-special, but instead of writing the special value immediately, it returns a synchronizable event (see section 7.7). The
output-portmust support atomic writes, as indicated by
Synchronizing on the object starts a write of the special value, and the event becomes ready when the value is written (unbuffered) to the port. If the event is not selected in a synchronization, then no value will have been written to
write-bytes-avail/enable-breakcan provide an exclusive-or guarantee (break or write, but not both) for
output-port, and if the port can be used with procedures like
write-bytes-avail-evt. MzScheme's file-stream ports, pipes, string ports, and TCP ports all support atomic writes; ports created with
make-output-port(see section 11.1.7) may support atomic writes.
#tif procedures like
write-specialcan write arbitrary values to the port. MzScheme's file-stream ports, pipes, string ports, and TCP ports all reject special values, but ports created with
make-output-port(see section 11.1.7) may support them.
11.2.3 Writing Structured Data
procedure is used to print Scheme values in a
context where a programmer expects to see a value:
output-port. The default value of
output-portis the current output port.
The rationale for providing
is that
both have standard output conventions, and this
standardization restricts the ways that an environment can change the
behavior of these procedures. No output conventions should be assumed
so that environments are free to modify the actual
output generated by
in any way. Unlike the port
display and write handlers, a global port print handler can be
installed through the
global-port-print-handler parameter
(see section 18.104.22.168).
, and
create formatted output:
output-port format-string v···
)prints formatted output to
output-port, where
format-stringis a string that is printed;
format-stringcan contain special formatting tags:
~n or
~% prints a newline
~a or
s the next argument among the
~s or
s the next argument among the
~v or
s the next argument among the
~e or
~E outputs the next argument among the
vs using the current error value conversion handler (see section 220.127.116.11) and current error printing width
~c or
s the next argument in
vs; if the next argument is not a character, the
exn:fail:contractexception is raised
~b or
~B prints the next argument among the
vs in binary; if the next argument is not an exact number, the
exn:fail:contractexception is raised
~o or
~O prints the next argument among the
vs in octal; if the next argument is not an exact number, the
exn:fail:contractexception is raised
~x or
~X prints the next argument among the
vs in hexadecimal; if the next argument is not an exact number, the
exn:fail:contractexception is raised
~~ prints a tilde (~)
w, where
wis a whitespace character, skips characters in
format-stringuntil a non-whitespace character is encountered or until a second end-of-line is encountered (whichever happens first). An end-of-line is either
#\newline, or
#\returnfollowed immediately by
#\newline(on all platforms).
The return value is void.
format-string v···
)same as
with the current output port.
format-string v···
)same as
with a string output port where the final string is returned as the result.
When an illegal format string is supplied to one of these procedures,
exn:fail:contract exception is raised. When the format string requires more
additional arguments than are supplied, the
exn:fail:contract exception is raised. When more additional arguments are supplied
than are used by the format string, the
exn:fail:contract exception is raised.
For example,
fprintf port "~a as a string is ~s.~n" '(3 4) "(3 4)")
prints this message to
(3 4) as a string is "(3 4)".
followed by a newline.
11.2.4 Default Reader
MzScheme's input parser obeys the following non-standard rules. See also section 11.2.8 for information on configuring the input parser through a readtable.
Square brackets (``['' and ``]'') and curly braces (``{'' and ``}'') can be used in place of parentheses. An open square bracket must be closed by a closing square bracket and an open curly brace must be closed by a closing curly brace. Whether square brackets are treated as parentheses is controlled by the
read-square-bracket-as-parenparameter (see section 184.108.40.206). Similarly, the parsing of curly braces is controlled with the
read-curly-brace-as-parenparameter. When square brackets and curly braces are not treated as parentheses, they are disallowed as input. By default, square brackets and curly braces are treated as parentheses.
Vector constants can be unquoted, and a vector size can be specified with a decimal integer between the
#and opening parenthesis. If the specified size is larger than the number of vector elements that are provided, the last specified element is used to fill the remaining vector slots. For example,
#4(1 2)is equivalent to
#(1 2 2 2). If no vector elements are specified, the vector is filled with
0. If a vector size is provided and it is smaller than the number of elements provided, the
exn:fail:readexception is raised.
Boxed constants can be created using
#&. The datum following
#&is treated as a quoted constant and put into the new box. (Space and comments following the
#&are ignored.) Box reading is controlled with the
read-accept-boxboolean parameter (see section 184.108.40.206). Box reading is enabled by default. When box reading is disabled and
#&is provided as input, the
exn:fail:readexception is raised.
Expressions beginning with
#'are wrapped with
syntaxin the same way that expressions starting with
'are wrapped with
quote. Similarly,
unsyntax, and
unsyntax-splicing. See also section 18.104.22.168.
The following character constants are recognized:
#\is followed by at least two alphabetic characters, characters are read from the input port until the next non-alphabetic character is returned. If the resulting string of letters does not match one of the above constants (case-insensitively), the
exn:fail:readexception is raised.
Character constants can also be specified through direct Unicode values in octal notation (up to 255):
n1is in the range [
3] and
n3are in the range [
7]. Whenever
#\is followed by at least two characters in the range [
7], the next character must also be in this range, and the resulting octal number must be in the range 0008 to 3778.
Finally, character constants can be specified through direct Unicode values in hexadecimal notation:
#\U, where each
niis a hexadecimal digit (0-9, a-f, or A-F), and k is no more than 4 for
#\uor 6 for
#\U. Whenever
#\is followed by a
Uand one hexadecimal digit, the character constant is terminated by either the first non-hexadecimal character in the stream, or the fourth/sixth hexadecimal character, whichever comes first. The resulting hexadecimal number must be a valid argument to
integer->char, otherwise the
exn:fail:readexception is raised.
Unless otherwise specified above, character-constants are terminated after the character following
#\. For example, if
#\is followed by an alphabetic character other than
uand then a non-alphabetic character, then the character constant is terminated. If
#\is followed by a
9, then the constant is terminated. If
#\is followed by a non-alphabetic, non-decimal-digit character then the constant is terminated.
Within string constants, the following escape sequences are recognized in addition to \" and \\:
\': quote (i.e., the backslash has no effect)
o, \
oo, or \
ooo: Unicode for octal
oo, or
ooo, where each
6, or
7. The \
oooform takes precedence over the \
ooform, and \
ootakes precedence over \
hor \x
hh: Unicode for hexadecimal
hh, where each
f, or
F. The \x
hhform takes precedence over the \x
h, \u
hh, \u
hhh, or \u
hhhh: like \x, but with up to four hexadecimal digits (longer sequences take precedence). The resulting hexadecimal number must be a valid argument to
integer->char, otherwise the
exn:fail:readexception is raised.
h, \U
hh, \U
hhh, \U
hhhh, \U
hhhhh, \U
hhhhhh, \U
hhhhhhh, or \U
hhhhhhhh: like \x, but with up to eight hexadecimal digits (longer sequences take precedence). The resulting hexadecimal number must be a valid argument to
integer->char, otherwise the
exn:fail:readexception is raised.
Furthermore, a backslash followed by a linefeed, carriage return or return-linefeed combination is elided, allowing string constants to span lines. Any other use of backslash within a string constant is an error.
A string constant preceded by
#is a byte-string constant. Byte string constants support the same escape sequences as character strings except \u and \U.
The sequence
#<<starts a here string. The characters following
#<<until a newline character define a terminator for the string. The content of the string includes all characters between the
#<<line and a line whose only content is the specified terminator. More precisely, the content of the string starts after a newline following
#<<, and it ends before a newline that is followed by the terminator, where the terminator is itself followed by either a newline or end-of-file. No escape sequences are recognized between the starting and terminating lines; all characters are included in the string (and terminator) literally. A return character is not treated as a line separator in this context. If no characters appear between
#<<and a newline or end-of-file, or if an end-of-file is encountered before a terminating line, the
exn:fail:readexception is raised.
The syntax for numbers is extended as described in section 3.3. Numbers containing a decimal point or exponent (e.g.,
2e78) are normally read as inexact. If the
read-decimal-as-inexactparameter is set to
#f, then such numbers are instead read as exact. The parameter does not affect the parsing of numbers with an explicit exactness tag (
A parenthesized sequence containing two delimited dots (``
.'') triggers infix parsing. A single
datummust appear between the dots, and one or more
datums must appear before the first dot and after the last dot:The resulting list consists of the
(left-datum ···1 . first-datum . right-datum ···1)
datumbetween the dots, followed by the remaining
datums in order:Consequently, the input expression
(first-datum left-datum ···1 right-datum ···1)
(1 . < . 2)produces
#t, and
(1 2 . + . 3 4 5)produces
When the
read-accept-dotparameter is set to
#f, then a delimited dot (``
.'') is disallowed in input. When the
read-accept-quasiquoteparameter is set to
#f, then a backquote or comma is disallowed in input. These modes simplify Scheme's input model for students.
MzScheme's identifier and symbol syntax is considerably more liberal than the syntax specified by R5RS. When input is scanned for tokens, the following characters delimit an identifier in addition to whitespace:
" , ' ` ; ( ) [ ] { }
In addition, an identifier cannot start with a hash mark (``
#'') unless the hash mark is immediately followed by a percent sign (``
%''). The only other special characters are backslash (``\'') and quoting vertical bars (``|''); any other character is used as part of an identifier.
Symbols containing special characters (including delimiters) are expressed using an escaping backslash (``\'') or quoting vertical bars (``|''):
A backslash preceding any character includes that character in the symbol literally; double backslashes produce a single backslash in the symbol.
Characters between a pair of vertical bars are included in the symbol literally. Quoting bars can be used for any part of a symbol, or the whole symbol can be quoted. Backslashes and quoting bars can be mixed within a symbol, but a backslash is not a special character within a pair of quoting bars.
Characters quoted with a backslash or a vertical bar always preserve their case, even when identifiers are read case-insensitively.
An input token constructed in this way is an identifier when it is not a numerical constant (following the extended number syntax described in section 3.3). A token containing a backslash or vertical bars is never treated as a numerical constant.
(produces the same symbol as
(produces the same symbol as
(when identifiers are read without case-sensitivity.
quotea\ b)
(, and
quote|a b|)
(all produce the same symbol as
quotea| |b)
string->symbol"a b")
(is the same as
(, which produces the same symbol as
(is the number 10, but
(produces the same symbol as
Whether a vertical bar is used as a special or normal symbol character is controlled with the
read-accept-bar-quoteboolean parameter (see section 184.108.40.206). Vertical bar quotes are enabled by default. Quoting backslashes cannot be disabled.
By default, symbols are read case-sensitively. Case sensitivity for reading can be controlled in three ways:
Quoting part of a symbol with an escaping backslash (``\'') or quoting vertical bar (``|'') always preserves the case of the quoted portion, as described above.
The sequence
#cscan be used as a prefix for any expression to make reading symbols within the expression case-sensitive. A
#ciprefix similarly makes reading symbols in an expression case-insensitive. Whitespace can appear between a
#ciprefix and its expression, and prefixes can be nested. Backslash and vertical-bar quotes override a
When the
read-case-sensitiveparameter (see section 22.214.171.124) is set to
#t, then case is preserved when reading symbols. The default is
#t, and it is set to
#twhile loading a module (see section 5.8). A
#ciprefix overrides the parameter setting, as does backslash or vertical-bar quoting.
Symbol case conversions are not sensitive to the current locale (see section 1.2.2).
A symbol-like expression that starts with an unquoted hash and colon (``#:'') is parsed as a keyword constant. After the leading colon, backslashes, vertical bars, and case sensitivity are handled as for symbols, except that a keyword expression can never be interpreted as a number.
Expressions of the form
#rxare literal regexp values (see section 10) where
stringis a string constant. The regexp produced by
#rxis the same as produced by
(regexp. If
stringis not a valid pattern, the
exn:fail:readexception is raised.
Expressions of the form
#rx#are similarly literal byte-regexp values. The regexp produced by
#rx#is the same as produced by
(byte-regexp #.
Expressions of the form
#hash((are literal immutable hash tables. The hash table maps each
val-datum) ···)
key-datumto its
val-datum, comparing keys with
. The table is constructed by adding each
key-datummapping from left to right, so later mappings can hide earlier mappings if the
key-datums are
. An expression of the form
#hasheq((produces an immutable hash table with keys compared using
val-datum) ···)
. If the value of
read-square-bracket-as-parenparameter (see section 220.127.116.11) is true, matching parentheses in a
#hasheqconstant can be replaced by matching square brackets. Similarly, matching curly braces can be used if
read-curly-brace-as-parenis true.
Values with shared structure are expressed using
#, where
nis a decimal integer. See section 184.108.40.206.
Expressions of the form
#%are symbols, where
xcan be a symbol or a number.
Expressions beginning with
#~are interpreted as compiled MzScheme code. See section 14.3.
Multi-line comments are started with
#|and terminated with
|#. Comments of this form can be nested arbitrarily.
#;comments out the next datum. Whitespace and comments (including
#;comments) may appear between the
#;and the commented-out datum. Graph-structure annotations with
#work within the comment as if the datum were not commented out (e.g., bindings can be introduced with
#for use in parts of the datum that are not commented out). When
#;appears at the beginning of a top-level datum, however, graph-structure bindings are discarded (along with the first following datum) before reading the second following datum.
If the first line of a
ed file begins with
#!, it is ignored by the default load handler. If an ignored line ends with a backslash (``\''), then the next line is also ignored. (The
#!convention is for shell scripts; see Chapter 18 for details.)
#hxshifts the reader into H-expression mode (see section 19) for one H-expression. A
#sxhas no effect in normal mode, but in H-expression mode, it shifts the reader back to (normal) S-expression mode. The
read-honu-syntaxprocedures read as if the stream starts with
#honushifts the reader into H-expression mode (see section 19) and reads repeatedly until an end-of-file is encountered. The H-expression results are wrapped in a module-formed S-expression, as described in section 19.
#readermust be followed by a datum. The datum is passed to the procedure that is the value of the
current-reader-guardparameter (see section 18.104.22.168), and the result is used as a module path. The module path is passed to
dynamic-require(see section 5.5) with either
'read-syntax(depending on whether parsing started with
read-syntax). The resulting procedure should accept the same arguments as
read-syntax(with all optional arguments as required). The procedure is given the port whose stream contained
#reader, and it should produce a datum result. If the result is a syntax object in
readmode it is converted to a datum using
syntax-object->datum; if the result is not a syntax object in
read-syntaxmode, it is converted to one using
datum->syntax-object. See also section 184.108.40.206 and section 184.108.40.206 for information on special-comment results and recursive reads. If the
read-accept-readerparameter is set to
#f, then
#readeris disallowed as input.
Reading from a custom port can produce arbitrary values generated by
the port; see section 11.1.7 for details. If the port generates
a non-character value in a position where a character is required
(e.g., within a string), the
exn:fail:read:non-char exception is raised.
11.2.5 Default Printer
MzScheme's printer obeys the following non-standard rules (though the
rules for
do not apply when the
parameter is set to
#t; see section 188.8.131.52).
A vector can be printed by
using the shorthand described in section 11.2.4, where the vector's length is printed between the leading
#and the opening parenthesis and repeated tail elements are omitted. For example,
#(1 2 2 2)is printed as
#4(1 2). The
procedure does not output vectors using this shorthand. Shorthand vector printing is controlled with the
print-vector-lengthboolean parameter (see section 18.104.22.168). Shorthand vector printing is enabled by default.
Boxes (see section 3.11) can be printed with the
#¬ation (see section 11.2.4). When box printing is disabled, all boxes are printed un
ably as
#<box>. Box printing is controlled with the
print-boxboolean parameter (see section 220.127.116.11). Box printing is enabled by default.
Structures (see Chapter 4) can be printed using either a custom-write procedure or vector notation. See section 11.2.10 for information on custom-write procedures; the following information applies only when no custom-write procedure is specified. In the vector form of output, the first item is a symbol of the form
struct:-- where
sis the name of the structure -- and the remaining elements are the elements of the structure, but the vector exposes only as much information about the structure as the current inspector can access (see section 4.5). When structure printing is disabled, or when no part of the structure is accessible to the current inspector, a structure is printed un
ably as
#<struct:. Structure printing is controlled with the
print-structboolean parameter (see section 188.8.131.52). Structure printing is disabled by default.
Symbols containing spaces or special characters
using escaping backslashes and quoting vertical bars. When the
read-case-sensitiveparameter is set to
#f, then symbols containing uppercase characters also use escaping backslashes or quoting vertical bars. In addition, symbols are quoted with vertical bars or a leading backslash when they would otherwise print the same as a numerical constant. If the value of the
read-accept-bar-quoteboolean parameter is
#f(see section 184.108.40.206), then backslashes are always used to escape special characters instead of quoting them with vertical bars, and a vertical bar is not treated as a special character. Otherwise, quoting bars are used in printing when bar at the beginning and one at the end suffices to correctly print the symbol. See section 11.2.4 for more information about symbol parsing. Symbols
without escaping or quoting special characters.
displaythe same as symbols, except with a leading hash and colon, and without special handing when the printed form matches a number (since the leading
#:distinguishes the keyword).
Characters with the special names described in section 11.2.4
using the same name. (Some characters have multiple names; the
#\nulnames are used instead of
#\null). Other graphic characters (according to
char-graphic?; see section 3.4)
#\followed by the single character, and all others characters are written in
#\unotation with four digits or
#\Unotation with eight digits (using the latter only if the character value it does not fit in four digits). All characters
as a single character.
Strings containing non-graphic, non-blank characters (according to
char-blank?; see section 3.4)
using the escape sequences described in section 11.2.4, using \a, \b, \t, \n, \v, \f, \r, or \e if possible, otherwise using \u with four hexadecimal digits or \U with eight hexadecimal digits (using the latter only if the character value does not fit into four digits). All strings
as their literal character sequences.
Byte strings
#", where each byte in the string content is written using the corresponding ASCII decoding if the byte is between 0 and 127 and the character is graphic or blank (according to
char-blank?; see section 3.4). Otherwise, the byte is written using \a, \b, \t, \n, \v, \f, \r, or \e if possible, otherwise using \o with one to three octal digits (only as many as necessary). All strings
as their literal byte sequence; this byte sequence may not be a valid UTF-8 encoding, so it may not correspond to a sequence of characters.
Regexp values print using the form
#rx, where
stringis the
form of the regexp's source character string or byte string. Similarly, byte-regexp values print starting with
like other un
able values using
#<path:...>, but a path
s like the result of
path->stringapplied to the path.
When the
print-hash-tableparameter is set to true (see section 188.8.131.52), hash tables print using the form
val) ···)
#hasheq((for tables using
val) ···)
key comparisons, respectively. If the
print-hash-tableparameter's value is
#f, hash tables print un
ably as
#<hash-table>. Hash tables with weakly held keys always print un
ably as
Values with shared structure can be printed using
#, where
nis a decimal integer. See section 18.104.22.168.
A value with no
able format prints as
#<...>, but only when the
print-unreadableparameter is set to
#t(the default; see also section 18.104.22.168). When the parameter's value is
#f, attempting to print an un
able value raises
184.108.40.206 Sharing Structure in Input and Output
MzScheme can read and print Common LISP-style graphs, values
with shared structure (including cycles). Graphs are described by
tagging the shared structure once with
# (using
some decimal integer
n with no more than eight digits) and then
referencing it later with
# (using the same number
n). For example, the following datum represents the infinite
list of ones:
#0=(1 . #0#)
If this graph is entered into MzScheme's
ones to the
infinite list of ones, using
quote to hide the infinite list
from the compiler:
(define ones (quote #0=(1 . #0#)))
A tagged structure can be referenced multiple
times. Here,
v is defined to be a vector containing the same
cell in all three slots:
(define v #(#1=(
cons 1 2) #1# #1#))
A tag
# must appear to the left of all references
#, and all references must appear in the same
top-level datum as the tag. By default, MzScheme's printer will
display a value without showing the shared structure:
#((1 . 2) (1 . 2) (1 . 2))
Graph reading and printing are controlled with the
read-accept-graph and
print-graph boolean
parameters (see section 22.214.171.124). Graph reading is enabled by
default, and graph printing is disabled by default. However, when the
printer encounters a graph containing a cycle, graph
printing is automatically enabled, temporarily. (For this reason, the
write, and
exn:fail:read exception is raised.
If the
n in a
# form or a
# form
contains more than eight digits, the
exn:fail:read exception is raised. If a
# form is not preceded by a
# form
using the same
n, the
exn:fail:read exception is raised. If two
# forms are in the same expression for the same
n, the
exn:fail:read exception is raised.
11.2.6 Replacing the Reader
Each input port has its own port read handler. This handler
is invoked to read from the port when the built-in
procedure is applied to the port.34 A port read handler is applied to
either one argument or two arguments:
A single argument is supplied when the port is used with
; the argument is the port being read. The return value is the value that was read from the port.
Two arguments are supplied when the port is used with
; the first argument is the port being read, and the second argument is a value indicating the source. The return value is a syntax object that was read from the port.
A port's read handler is configured with
)returns the current port read handler for
input-port proc
)sets the handler for
The default port read handler reads standard Scheme expressions with MzScheme's built-in parser (see section 11.2.4). It handles a special result from a custom input port (see section 126.96.36.199) by treating it as a single expression, except that special-comment values (see section 126.96.36.199) are treated as whitespace.
read and
read-syntax procedures themselves can
be customized through a readtable; see section 11.2.8 for more
11.2.7 Replacing the Printer
Each output port has its own port display handler,
port write handler, and port print
handler. These handlers are invoked to output to the port when the
is applied to the port. A port display/write/print handler takes a
two arguments: the value to be printed and the destination port. The
handler's return value is ignored.
)returns the current port display handler for
output-port proc
)sets the display handler for
)returns the current port write handler for
output-port proc
)sets the write handler for
)returns the current port print handler for
output-port proc
)sets the print handler for
The default port display and write handlers print Scheme expressions
with MzScheme's built-in printer (see section 11.2.5). The default
print handler calls the global port print handler (the value of the
global-port-print-handler parameter; see
section 220.127.116.11); the default global port print
handler is the same as the default write handler.
11.2.8 Customizing the Reader through Readtables
A readtable configures MzScheme's built-in reader by adjusting the way that individual characters are parsed. MzScheme readtables are just like readtables in Common LISP, except that an individual readtable is immutable, and the procedures for creating and inspecting readtables are somewhat different than the Common LISP procedures.
In many contexts,
#f identifies the default readtable for
MzScheme. In particular,
#f is the initial value for the
current-readtable parameter (see
section 184.108.40.206), which causes the reader to behave as
described in section 11.2.4. Adjust MzScheme's default reader by
setting the
current-readtable parameter to a readtable
created with
readtable [
char-or-false symbol-or-char
readtable-or-proc ···1]
) creates a new readtable that is like
readtable (which can be
#f), except that the reader's
behavior is modified for each
char according to the given
symbol-or-char and
readtable-or-proc. The ···1 for
make-readtable applies to all three of
symbol-or-char, and
readtable-or-proc; in other words,
the total number of arguments to
make-readtable must be one
modulo three.
The possible combinations for
symbol-or-char, and
readtable-or-proc are as follows:
proc-- causes
charto be parsed as a delimiter, and an unquoted/uncommented
charin the input string triggers a call to the reader macro
proc; the activity of
procis described further below. Conceptually, characters like semi-colon (``;'') and parentheses are mapped to terminating reader macros in the default readtable.
proc-- like the
'terminating-macrovariant, but
charis not treated as a delimiter, so it can be used in the middle of an identifier. Conceptually, hash (``#'') is mapped to a non-terminating macro in the default readtable.
proc-- like the
'non-terminating-macrovariant, but
charonly when it follows a hash (``#'') -- or, more precisely, when the character follows one that has been mapped to the behavior of hash in the default readtable.
readtable-- causes
charto be parsed in the same way that
like-charis parsed in
readtable, where
readtablecan be
#fto indicate the default readtable. Mapping a character to the same actions as vertical bar (``|'') in the default reader means that the character starts quoting for symbols, and the same character terminates the quote; in contrast, mapping a character to the same action as a parenthesis or double quote means that the character starts a list or string, but the list or string is still terminated with a closing parenthesis or double quote. Finally, mapping a character to an action in the default readtable means that the character's behavior is sensitive to parameters that affect the original character; for example, mapping a character to the same action is a curly brace (``{'') in the default readtable means that the character is disallowed when the
read-curly-brace-as-parenparameter is set to
proc-- replaces the macro used to parse characters with no specific mapping: i.e., characters (other than hash or vertical bar) that can start a symbol or number with the default readtable.
If multiple
'dispatch-macro mappings are provided for a
char-or-false, all but the last one are
ignored. Similarly, if multiple non-
'dispatch-macro mappings
are provided for a single
char-or-false, all but the last one
are ignored.
A reader macro
proc must accept six arguments, and it can
optionally accept two arguments. See section 11.2.9 for
information on the procedure's arguments and results.
A reader macro normally reads characters from the given input port to
produce a value to be used as the ``reader macro-expansion'' of the
consumed characters. The reader macro might produce a special-comment
value to cause the consumed character to be treated as whitespace,
and it might use
read/recursive or
read-syntax/recursive; see section 22.214.171.124 and
section 22.214.171.124 for more information on these topics.
readtable char
), where
readtable is not
#f, produces information about the mappings in
readtable for
char. The result is three values:
either a character (mapping is to same behavior as the character in the default readtable),
'terminating-macro, or
'non-terminating-macro; this result reports the main (i.e., non-
'dispatch-macro) mapping for
char. When the result is a character, then
charis mapped to the same behavior as the returned character in the default readtable.
#for a reader-macro procedure; the result is a procedure when the first result is
#for a reader-macro procedure; the result is a procedure when the character has a
'dispatch-macromapping in
readtableto override the default dispatch behavior.
Note that reader-macro procedures for the default readtable are not
directly accessible. To invoke default behaviors, use
read/recursive or
read-syntax/recursive (see
section 18.104.22.168) with a character and the
Extended example:
;; Provides
raise-read-eof-error(require (lib "readerr.ss" "syntax")) (define (skip-whitespace port) ;; Skips whitespace characters, sensitive to the current ;; readtable's definition of whitespace (let ([ch (
peek-charport)]) (unless (eof-object? ch) ;; Consult current readtable: (let-values ([(like-ch/sym proc dispatch-proc) (readtable-mapping (
current-readtable) ch)]) ;; If like-ch/sym is whitespace, then ch is whitespace (when (and (char? like-ch/sym) (
char-whitespace?like-ch/sym)) (
read-charport) (skip-whitespace port)))))) (define (skip-comments read-one port src) ;; Recursive read, but skip comments and detect EOF (let loop () (let ([v (read-one)]) (cond [(special-comment? v) (loop)] [(eof-object? v) (let-values ([(l c p) (
port-next-locationport)]) (raise-read-eof-error "unexpected EOF in tuple" src l c p 1))] [else v])))) (define (parse port read-one src) ;; First, check for empty tuple (skip-whitespace port) (if (
eq?#\> (
null(let ([elem (read-one)]) (if (special-comment? elem) ;; Found a comment, so look for > again (parse port read-one src) ;; Non-empty tuple: (
conselem (parse-nonempty port read-one src)))))) (define (parse-nonempty port read-one src) ;; Need a comma or closer (skip-whitespace port) (case (
peek-charport) [(#\>) (
read-charport) ;; Done
null] [(#\,) (
read-charport) ;; Read next element and recur (
cons(skip-comments read-one port src) (parse-nonempty port read-one src))] [else ;; Either a comment or an error; grab location (in case ;; of error) and read recursively to detect comments (let-values ([(l c p) (
port-next-locationport)] [(v) (read-one)]) (cond [(special-comment? v) ;; It was a comment, so try again (parse-nonempty port read-one src)] [else ;; Wasn't a comment, comma, or closer; error ((if (eof-object? v) raise-read-eof-error raise-read-error) "expected `,' or `>'" src l c p 1)]))])) (define (make-delims-table) ;; Table to use for recursive reads to disallow delimiters ;; (except those in sub-expressions) (letrec ([misplaced-delimiter (case-lambda [(ch port) (unexpected-delimiter ch port #f #f #f #f)] [(ch port src line col pos) (raise-read-error (
format"misplaced `~a' in tuple" ch) src line col pos 1)])]) (
current-readtable) #\, 'terminating-macro misplaced-delimiter #\> 'terminating-macro misplaced-delimiter))) (define (wrap l) `(make-tuple (
list,@l))) (define parse-open-tuple (case-lambda [(ch port) ;; `read' mode (wrap (parse port (lambda () (
read/recursiveport #f (make-delims-table))) (
object-nameport)))] [(ch port src line col pos) ;; `read-syntax' mode (
datum->syntax-object#f (wrap (parse port (lambda () (
read-syntax/recursivesrc port #f (make-delims-table))) src)) (let-values ([(l c p) (
port-next-locationport)]) (
listsrc line col pos (and pos (- p pos)))))])) (define tuple-readtable (
make-readtable#f #\< 'terminating-macro parse-open-tuple)) (parameterize ([
current-readtabletuple-readtable]) (
open-input-string"<1 , 2 , \"a\">"))) ;; =>
'(make-tuple ((parameterize ([
list1 2 "a"))
current-readtabletuple-readtable]) (
open-input-string"< #||# 1 #||# , #||# 2 #||# , #||# \"a\" #||# >"))) ;; =>
'(make-tuple ((define tuple-readtable+ (
list1 2 "a"))
make-readtabletuple-readtable #\* 'terminating-macro (lambda a (make-special-comment #f)) #\_ #\space #f)) (parameterize ([
current-readtabletuple-readtable+]) (
open-input-string"< * 1 __,__ 2 __,__ * \"a\" * >"))) ;; =>
'(make-tuple (
list1 2 "a"))
11.2.9 Reader-Extension Procedures
MzScheme's reader can be extended in three ways: through a
reader-macro procedure in a readtable (see section 11.2.8),
through a
#reader form (see section 11.2.4), or through a
custom-port byte reader that returns a ``special'' result procedure
(see section 220.127.116.11). All three kinds of procedures accept
similar arguments, and their results are treated in the same way by
read and
read-syntax (or, more precisely, by the
default read handler; see section 11.2.6).
Calls to these reader-extension procedures can be triggered through
read-syntax, or
read-honu-syntax. In addition, a special-read procedure can
be triggered by calls to
or by the context of
read-bytes-avail!, and
Optional arities for reader-macro and special-result procedures allow
them to distinguish reads via
read, etc. from reads via
read-syntax, etc. in the case that the source value is
#f and no other location information is available.
Procedure arguments
A reader-macro procedure must accept six arguments, and it can
optionally accept two arguments. The first two arguments are always
the character that triggered the reader macro and the input port for
reading. When the reader macro is triggered by
read-syntax/recursive), the procedure is passed four
additional arguments that represent a source location. When the
reader macro is triggered by
read (or
read/recursive), the procedure is passed only two arguments
if it accepts two arguments, otherwise it is passed six arguments
where the last four are all
#reader-loaded procedure accepts the same arguments as
read or
read-syntax, depending on whether
the procedure was loaded through
read, etc. or through
read-syntax, etc.
A special-result procedure must accept four arguments, and it can
optionally accept zero arguments. When the special read is triggered
read-syntax (or
read-syntax/recursive, etc.), the procedure is passed four
arguments that represent a source location. When the special read is
triggered by
read (or
read/syntax, etc.), the procedure is
passed no arguments if it accepts zero arguments, otherwise it is
passed four arguments that are all
Procedure result
When a reader-extension procedure is called in syntax-reading mode
read-syntax, etc.), it should generally return a syntax
object that has no lexical context (e.g., a syntax object created
#f as the first
argument and with the given location information as the third
argument). Another possible result is a special-comment value (see
section 22.214.171.124). If the procedure's result is not a syntax
object and not a special-comment value, it is converted to one using
When a reader-extension procedure is called in non-syntax-reading
modes, it should generally not return a syntax object. If a syntax
object is returned, it is converted to a plain value using
In either context, when the result from a reader-extension procedure
is a special-comment value (see section 22.214.171.124), then
read-syntax, etc. treat the value as a
delimiting comment and otherwise ignore it.
Also in either context, the result may be copied to prevent mutation to pairs, vectors, or boxes before the read result is completed, and to support the construction of graphs with cycles. Mutable pairs, boxes, and vectors are copied, along with any pairs, boxes, or vectors that lead to such mutable values, to placeholders produced by a recursive read (see section 126.96.36.199), or to references of a shared value. Graph structure (including cycles) is preserved in the copy.
184.108.40.206 Special Comments
) creates a special-comment value that
v. The
read-syntax, etc.
procedures treat values constructed with
make-special-comment as delimiting whitespace when returned
by a reader-extension procedure (see section 11.2.9).
) returns
#t if
v is the result of
) returns the value encapsulated by the
special-comment value
sc. This value is never used directly by
a reader, but it might be used by the context of a
read-char-or-special, etc. call that detects a special
22.214.171.124 Recursive Reads
(read/recursive [
input-port char-or-false readtable]
) is similar
to calling
, but it is normally used during the dynamic
extent of
read within a reader-extension procedure (see
section 11.2.9). The main effect of using
read/recursive instead of
read is that
graph-structure annotations (see section 188.8.131.52) in the nested
read are considered part of the overall read. Since the result is wrapped in a
placeholder, however, it is not directly inspectable.
char-or-false is provided and not
#f, it is
effectively prefixed to the beginning of
input-port's stream
for the read. (To prefix multiple characters, use
input-port-append from MzLib's port library; see
Chapter 32
in PLT MzLib: Libraries Manual.)
readtable argument, which defaults to
(current-readtable), is used for top-level parsing to
satisfy the read request; recursive parsing within the read (e.g., to
read the elements of a list) instead uses the current readtable as
determined by the
current-readtable parameter. A reader
macro might call
read/recursive with a character and
readtable to effectively invoke the readtable's behavior for the
character. If
readtable is
#f, the default readtable
is used for top-level parsing.
When called within the dynamic extent of
read, the
read/recursive procedure produces either an opaque
placeholder value, a special-comment value, or an end-of-file. The
result is a special-comment value (see section 220.127.116.11)
when the input stream's first non-whitespace content parses as a
comment. The result is end-of-file when
encounters an end-of-file. Otherwise, the result is a placeholder
that protects graph references that are not yet resolved. When this
placeholder is returned within an S-expression that is produced by
any reader-extension procedure (see section 11.2.9) for the
same outermost
, it will be replaced with the actual
read value before the outermost
read returns.
(read-syntax/recursive [
source-name-v input-port char-or-false readtable]
is analogous to calling
read/recursive, but the resulting
value encapsulates S-expression structure with source-location
information. As with
read/recursive, when
read-syntax/recursive is used within the dynamic extent of
read-syntax, the result of from
read-syntax/recursive is either a special-comment value,
end-of-file, or opaque graph-structure
placeholder (not a syntax object). The placeholder can be embedded
in an S-expression or syntax object returned by a reader macro, etc.,
and it will be replaced with the actual syntax object before the
read-syntax returns.
read/recursive within the dynamic extent of
read-syntax does not allow graph structure for reading to be
included in the outer
read-syntax parsing, and neither does
read-syntax/recursive within the dynamic extent of
read. In those cases,
read/recursive and
read-syntax/recursive produce results like
read and
See section 11.2.8 for an extended example that uses
read/recursive and
11.2.10 Customizing the Printer through Custom-Write Procedures
The built-in
structure type property associates a procedures to a structure
type. The procedure is used by the default printer to
display or
write (or
See section 4.4 for general information on structure type properties.
The procedure for a
prop:custom-write value takes three
arguments: the structure to be printed, the target port, and a
boolean that is
#t for
write mode and
display mode. The procedure should print the value to
the given port using
write-special, etc.
The write handler, display handler, and print handler are specially
configured for a port given to a custom-write procedure. Printing to
the port through
write, or
write-special. To recursively print but to a port other
than the one given to the custom-write procedure, copy the given
port's write handler, display handler, and print handler to the other
The port given to a custom-write handler is not necessarily the actual target port. In particular, to detect cycles and sharing, the printer invokes a custom-write procedure with a port that records recursive prints, and does not retain any other output.
Recursive print operations may trigger an escape from the call to the custom-write procedure (e.g., for pretty-printing where a tentative print attempt overflows the line, or for printing error output of a limited width).
The following example definition of a
tuple type includes
custom-write procedures that print the tuple's list content using
angle brackets in
mode and no brackets in
mode. Elements of the tuple are printed recursively,
so that graph and cycle structure can be represented.
(define (tuple-print tuple port write?) (when write? (write-string "<" port)) (let ([l (tuple-ref tuple 0)]) (unless (
null?l) ((if write?
display) (
carl) port) (
for-each(lambda (
e) (write-string ", " port) ((if write?
eport)) (
cdrl)))) (when write? (write-string ">" port))) (define-values (s:tuple make-tuple tuple? tuple-ref tuple-set!) (
make-struct-type'tuple #f 1 0 #f (
consprop:custom-write tuple-print)))) (
display(make-tuple '(1 2 "a"))) ; prints
1, 2, a(let ([t (make-tuple (
list1 2 "a"))]) (
set-car!(tuple-ref t 0) t) (
writet)) ; prints
#0=<#0#, 2, "a">
11.3 Filesystem Utilities
MzScheme provides many operations for accessing and modifying filesystems in a (mostly) platform-independent manner. Additional filesystem utilities are in MzLib; see also Chapter 17 in PLT MzLib: Libraries Manual.
11.3.1 Paths
The format of a filesystem path varies across platforms. For example,
under Unix, directories are separated by ``/'' while Windows uses
both ``/'' and ``\''. (See section 20 for more
information on Windows paths in MzScheme.) Furthermore, for most Unix
filesystems, the true name of a file is a byte string, but users
prefer to see the bytes decoded in a locale-specific way when the
filename is printed. MzScheme therefore provides a
datatype for managing filesystem paths, and procedures such
bytes->path for manipulating paths. Two paths
when their byte-string representations
When a MzScheme procedure takes a filesystem path as an argument, the
path can be provided either as a string or as an instance of the
path datatype. If a string is provided, it is converted to a
path using
string->path. A MzScheme procedure that generates
a filesystem path always generates a
path value.
Most MzScheme primitives that take path perform an expansion on the path before using it. (Procedures that build paths or merely check the form of a path do not perform this expansion.) Under Unix and Mac OS X, a user directory specification using ``~'' is expanded and multiple adjacent slashes are replaced with a single slash.35 Under Windows, paths that start \\?\, certain redundant backslashes are removed; see section 20 for more information. Under Windows for other paths, multiple slashes are converted to single slashes (except at the beginning of a shared folder name), a slash is inserted after the colon in a drive specification if it is missing, and trailing spaces and periods are removed from the last path element unless that element consists of only spaces and periods.
A path string (or byte string) cannot be empty, and it cannot contain a nul
character or byte. When an empty string or a string containing nul is
provided as a path to any procedure except
, or
exn:fail:contract exception is raised.
The basic path utilities are as follows:
vis a path value (not a string),
vis either a path value or a non-empty string without nul characters,
)produces a path whose byte-string name is
(; see section 3.6 for more information on
string->bytes/localestring (
. Beware that the current locale might not encode every string, in which case
string->pathcan produce the same path for different
)produces a path whose byte-string name is
)produces a string that represents
pathby decoding
path's byte-string name using the current locale's encoding; ``?'' is used in the result string where encoding fails, and if the encoding result is the empty string, then the result is
"?". In addition, under Windows, if the path is relative and the byte-string version of the path starts with \\?\REL, this prefix and the immediately following backslashes (one or two) are removed from the resulting string. The resulting string is suitable for displaying to a user, string-ordering comparisons, etc., but it is not suitable for re-creating the path through
string->path, since decoding and re-encoding the path's byte string may lose information.
path's byte string representation. No information is lost in this translation, so that
(always produces a path is that is
path. For any reasonable locale, consecutive ASCII characters in the printed form of
pathare mapped to consecutive byte values in
paththat match each character's code-point value, and a leading or trailing ASCII character is mapped to a leading or trailing byte, respectively.
base-path sub-path···
)creates a path given a base path and any number of sub-path extensions. If
base-pathis an absolute path, the result is an absolute path; if
baseis a relative path, the result is a relative path. Each
sub-pathmust be either a relative path, a directory name, the symbol
'up(indicating the relative parent directory), or the symbol
'same(indicating the relative current directory). Under Windows, if
base-pathis a drive specification (with or without a trailing slash) the first
sub-pathcan be an absolute (driveless) path. The last
sub-pathcan be a filename.
Under Windows, trailing spaces and periods are removed from the last element of
base-pathand all but the last
sub-path(unless the element consists of only spaces and peroids), except for those that start with \\?\. If
base-pathstarts \\?\, then after each non-\\?\REL\
sub-pathis added, all slashes in the addition are converted to backslashes, multiple consecutive backslashes are converted to a single backslash, added . elements are removed, and added .. elements are removed along with the preceding element; these conversions are not performed on the original
base-pathpart of the result or on any \\?\REL\
sub-path. In other cases under Windows, a backslash may be added or removed before combining paths to avoid changing the root meaning of the path (e.g., combining //x and y produces /x/y, because //x/y would be a UNC path instead of a drive-relative path).
base-pathcan optionally end in a directory separator. If the last
sub-pathends in a separator, it is included in the resulting path.
sub-pathis an illegal path string (because it is empty or contains a nul character), the
exn:fail:contractexception is raised.
procedure builds a path without checking the validity of the path or accessing the filesystem.
The following examples assume that the current directory is /home/joeuser for Unix examples and C:\Joe's Files for Windows examples.
(define p1 (
current-directory) "src" "scheme")) ; Unix: p1 =>
"/home/joeuser/src/scheme"; Windows: p1 =>
"C:\Joe's Files\src\scheme"(define p2 (
build-path'up 'up "docs" "MzScheme")) ; Unix: p2 =>
"../../docs/MzScheme"; Windows: p2 =>
build-pathp2 p1) ; Unix and Windows: raises
p1is absolute (
build-pathp1 p2) ; Unix: =>
"/home/joeuser/src/scheme/../../docs/MzScheme"; Windows: =>
"C:\Joe's Files\src\scheme\..\..\docs\MzScheme"
pathis an absolute path,
#fotherwise. If
pathis not a legal path string (e.g., it contains a nul character),
#fis returned. This procedure does not access the filesystem.
pathis a relative path,
#fotherwise. If
pathis not a legal path string (e.g., it contains a nul character),
#fis returned. This procedure does not access the filesystem.
pathis a completely determined path (not relative to a directory or drive),
#fotherwise. Note that under Windows, an absolute path can omit the drive specification, in which case the path is neither relative nor complete. If
pathis not a legal path string (e.g., it contains a nul character),
#fis returned. This procedure does not access the filesystem.
pathas a complete path. If
pathis already a complete path, it is returned as the result. Otherwise,
pathis resolved with respect to the complete path
base-path. If
base-pathis omitted,
pathis resolved with respect to the current directory. If
base-pathis provided and it is not a complete path, the
exn:fail:contractexception is raised. This procedure does not access the filesystem.
pathand returns a path that references the same file or directory as
path. Under Unix and Mac OS X, if
pathis a soft link to another path, then the referenced path is returned (this may be a relative path with respect to the directory owning
path) otherwise
pathis returned (after expansion).
)returns the expanded version of
path(as described at the beginning of this section). The filesystem might be accessed, but the source or expanded path might be a non-existent path.
)eliminates up-directory (``..'') and same-directory (``.'') indicators in
path, such that the result accesses the same file or directory (if it exists) as
path; under Windows, if
pathstarts \\?\, no conversion is performed. If no indicators are in
path, then
pathis returned. Otherwise, when
use-filesystem?is true (the default), a complete path is returned; if
pathis relative, it is resolved with respect to the current directory, and up-directory indicators are removed taking into account soft links (so that the resulting path refers to the same directory as before). When
#f, then up-directory indicators are removed by deleting a preceding path element, and the result can be a relative path with up-directory indicators remaining at the beginning of the path; similarly, the result can be the same as
(if eliminating up-directory indicators leaves only same-directory indicators. For a complete path, up-directory indicators are dropped when they refer to the parent of a root directory. The filesystem might be accessed when
use-filesystem?is true, but the source or expanded path might be a non-existent path. If
pathcannot be simplified due to a cycle of links, the
exn:fail:filesystemexception is raised (but a successfully simplified path may still involve a cycle of links if the cycle did not inhibit the simplification).
pathwith ``normalized'' case letters. Under Unix and Mac OS X and Mac OS X, this procedure always returns the input path, because filesystems for these platforms can be case-sensitive. Under Windows, if the path does not start \\?\, the resulting string uses only lowercase letters, based on the current locale. In addition, under Windows when the path does not start \\?\, all forward slashes (``/'') are converted to backward slashes (``\''), and trailing spaces and periods are removed. This procedure does not access the filesystem.
pathinto a smaller path and an immediate directory or file name. Three values are returned (see section 2.2):
baseis either
a path,
pathis an immediate relative directory or filename, or
pathis a root directory.
nameis either
a directory-name path,
a filename,
'upif the last part of
pathspecifies the parent directory of the preceding path (e.g., ``..'' under Unix), or
'sameif the last part of
pathspecifies the same directory as the preceding path (e.g., ``.'' under Unix).
pathexplicitly specifies a directory (e.g., with a trailing separator),
#fotherwise. Note that
must-be-dir?does not specify whether
nameis actually a directory or not, but whether
pathsyntactically specified a directory.
#f, then
namecannot be
'same. This procedure does not access the filesystem.
Under Windows, splitting a path that does not start with \\?\ can produce parts that start with \\?\. For example, splitting C:/x /aux/ produces \\?\C:\x \ and \\?\REL\\aux; the \\?\ is needed in these cases to preserve a trailing space after x and to avoid referring to the AUX devide instead of an aux file.
path string-or-bytes
)returns a path that is the same as
path, except that the suffix is changed to
string-or-bytes. If
pathas no suffix, then
string-or-bytesis added to the path. A suffix is defined as a period followed by any number of non-period characters/bytes at the end of the pathname. If
pathrepresents a root, the
exn:fail:contractexception is raised.
11.3.2 Locating Paths
find-system-path and
procedures locate useful files and directories:
)returns a machine-specific path for a standard type of path specified by
kind-symbol, which must be one of the following:
'home-dir-- the current user's home directory.
Under Unix and Mac OS X, this directory is determined by expanding the path ~, which is expanded by first checking for a HOME environment variable. If none is defined, the USER and LOGNAME environment variables are consulted (in that order) to find a user name, and then system files are consulted to locate the user's home directory.
Under Windows, the user's home directory is the user-specific profile directory as determined by the Windows registry. If the registry cannot provide a directory for some reason, the value of the USERPROFILE environment variable is used instead, as long as it refers to a directory that exists. If USERPROFILE also fails, the directory is the one specified by the HOMEDRIVE and HOMEPATH environment variables. If those environment variables are not defined, or if the indicated directory still does not exist, the directory containing the MzScheme executable is used as the home directory.
'pref-dir-- the standard directory for storing the current user's preferences. Under Unix, the directory is .plt-scheme in the user's home directory. Under Windows, it is PLT Scheme in the user's application-data folder as specified by the Windows registry; the application-data folder is usually Application Data in the user's profile directory. Under Mac OS X, it is Library/Preferences in the user's home directory. This directory might not exist.
'pref-file-- a file that contains a symbol-keyed association list of preference values. The file's directory path always matches the result returned for
'pref-dir. The file name is plt-prefs.ss under Unix and Windows, and it is org.plt-scheme.prefs.ss under Mac OS X. The file's directory might not exist. See also
get-preferencein Chapter 17 in PLT MzLib: Libraries Manual.
'temp-dir-- the standard directory for storing temporary files. Under Unix and Mac OS X, this is the directory specified by the TMPDIR environment variable, if it is defined.
'init-dir-- the directory containing the initialization file used by stand-alone MzScheme application. It is the same as the current user's home directory.
'init-file-- the file loaded at start-up by the stand-alone MzScheme application. The directory part of the path is the same path as returned for
'init-dir. The file name is platform-specific:
'addon-dir-- a directory for installing PLT Scheme extensions. It's the same as
'pref-dir, except under Mac OS X, where it's Library/PLT Scheme in the user's home directory. This directory might not exist.
'doc-dir-- the standard directory for storing the current user's documents. It's the same as
'home-dirunder Unix and Mac OS X. Under Windows, it is the user's documents folder as specified by the Windows registry; the documents folder is usually My Documents in the user's home directory.
'desk-dir-- the directory for the current user's desktop. Under Unix, it's the same as
'home-dir. Under Windows, it is the user's desktop folder as specified by the Windows registry; the documents folder is usually Desktop in the user's home directory. Under Mac OS X, it is the desktop directory, which is specifically ~/Desktop under Mac OS X.
'sys-dir-- the directory containing the operating system for Windows. Under Unix and Mac OS X, the result is
'exec-file-- the path of the MzScheme executable as provided by the operating system for the current invocation.36
'run-file-- the path of the current executable; this may be different from result for
'exec-filebecause an alternate path was provided through a --name or -N command-line flag to stand-alone MzScheme (or MrEd), or because an embedding executable installed an alternate path. In particular a ``launcher'' script created by
make-mzscheme-launchersets this path to the script's path. In the stand-alone MzScheme application, this path is also bound initially to
'collects-dir-- a path to the main collection of libraries (see section 16). If this path is relative, it's relative to the directory of
(. This path is normally embedded in a stand-alone MzScheme executable, but it can be overridden by the --collects or -X command-line flag.
'orig-dir-- the current directory at start-up, which can be useful in converting a relative-path result from
(to a complete path.
string default-path-list
)parses a string or byte string containing a list of paths, and returns a list of path strings. Under Unix and Mac OS X, paths in a path list are separated by a colon (``:''); under Windows, paths are separated by a semi-colon (``;''). Whenever the path list contains an empty path, the list
default-path-listis spliced into the returned list of paths. Parts of
stringthat do not form a valid path are not included in the returned list.
program-sub-path related-sub-path
)finds a path for the executable
program-sub-path, returning
#fif the path cannot be found.
related-sub-pathis not
#f, then it must be a relative path string, and the path found for
program-sub-pathmust be such that the file or directory
related-sub-pathexists in the same directory as the executable. The result is then the full path for the found
related-sub-path, instead of the path for the executable.
This procedure is used by MzScheme (as a stand-alone executable) to find the standard library collection directory (see Chapter 16). In this case,
programis the name used to start MzScheme and
"collects". The
related-sub-pathargument is used because, under Unix and Mac OS X,
program-sub-pathmay involve to a sequence of soft links; in this case,
related-sub-pathdetermines which link in the chain is relevant.
program-sub-pathhas a directory path, exists as a file or link to a file, and
related-sub-pathis not
determines whether
related-sub-pathexists relative to the directory of
program-sub-path. If so, the complete path for
program-sub-pathis returned. Otherwise, if
program-sub-pathis a link to another file path, the destination directory of the link is checked for
related-sub-path. Further links are inspected until
related-sub-pathis found or the end of the chain of links is reached.
program-sub-pathis a pathless name,
gets the value of the PATH environment variable; if this environment variable is defined,
tries each path in PATH as a prefix for
program-sub-pathusing the search algorithm described above for path-containing
program-sub-paths. If the PATH environment variable is not defined,
program-sub-pathis prefixed with the current directory and used in the search algorithm above. (Under Windows, the current directory is always implicitly the first item in PATH, so
checks the current directory first under Windows.)
11.3.3 Files
The file management utilities are:
#tif a file (not a directory)
#tif a link
pathexists (Unix and Mac OS X),
#fotherwise. Note that the predicates
work on the final destination of a link or series of links, while
only follows links to resolve the base part of
path(i.e., everything except the last name in the path). This procedure never raises the
)deletes the file with path
pathif it exists, returning void if a file was deleted successfully, otherwise the
exn:fail:filesystemexception is raised. If
pathis a link, the link is deleted rather than the destination of the link.
old-path new-path[
)renames the file or directory with path
old-path-- if it exists -- to the path
new-path. If the file or directory is renamed successfully, void is returned, otherwise the
exn:fail:filesystemexception is raised.
This procedure can be used to move a file/directory to a different directory (on the same disk) as well as rename a file/directory within a directory. Unless
exists-ok?is provided as a true value,
new-pathcannot refer to an existing file or directory. Even if
exists-ok?is true,
new-pathcannot refer to an existing file when
old-pathis a directory, and vice versa. (If
new-pathexists and is replaced, the replacement is atomic in the filesystem, except under Windows 95, 98, or Me. However, the check for existence is not included in the atomic action, which means that race conditions are possible when
exists-ok?is false or not supplied.)
old-pathis a link, the link is renamed rather than the destination of the link, and it counts as a file for replacing any existing
secs-n fail-thunk]
)returns the file or directory's last modification date as platform-specific seconds (see also section 15.1) when
secs-nis not provided or is
#f.38 If
secs-nis provided and not
#f, the access and modification times of
pathare set to the given time. On error (e.g., if no such file exists),
fail-thunkis called if it is provided, otherwise the
exn:fail:filesystemexception is raised
)returns a list containing
'write, and/or
'executefor the given file or directory path. On error (e.g., if no such file exists), the
exn:fail:filesystemexception is raised. Under Unix and Mac OS X, permissions are checked for the current effective user instead of the real user.
)returns the (logical) size of the specified file in bytes. (Under Mac OS X, this size excludes the resource-fork size.) On error (e.g., if no such file exists), the
exn:fail:filesystemexception is raised.
src-path dest-path
)creates the file
dest-pathas a copy of
src-path. If the file is successfully copied, void is returned, otherwise the
exn:fail:filesystemexception is raised. If
dest-pathalready exists, the copy will fail. File permissions are preserved in the copy. Under Mac OS X, the resource fork is also preserved in the copy. If
src-pathrefers to a link, the target of the link is copied, rather than the link itself.
to-path path
)creates a link
to-pathunder Unix and Mac OS X. The creation will fail if
pathalready exists. The
to-pathneed not refer to an existing file or directory. If the link is created successfully, void is returned, otherwise the
exn:fail:filesystemexception is raised. Under Windows, the
exn:fail:unsupportedexception is raised always.
11.3.4 Directories
The directory management utilities are:
(returns the current directory and
(current-directorysets the current directory to
path. This procedure is actually a parameter, as described in section 18.104.22.168.
)returns the current drive name under Windows. For other platforms, the
exn:fail:unsupportedexception is raised. The current drive is always the drive of the current directory.
pathrefers to a directory,
)creates a new directory with the path
path. If the directory is created successfully, void is returned, otherwise the
exn:fail:filesystemexception is raised.
)deletes an existing directory with the path
path. If the directory is deleted successfully, void is returned, otherwise the
exn:fail:filesystemexception is raised.
(, as described in the previous section, renames directories.
(, as described in the previous section, gets directory dates.
(, as described in the previous section, gets directory permissions.
)returns a list of all files and directories in the directory specified by
path. If
pathis omitted, a list of files and directories in the current directory is returned.
)returns a list of all current root directories. Obtaining this list can be particularly slow under Windows.
11.4 Networking
MzScheme supports networking with the TCP and UDP protocols.
For information about TCP in general, see TCP/IP Illustrated, Volume 1 by W. Richard Stevens.
max-allow-wait-k reuse? hostname-string-or-false]
)creates a ``listening'' server on the local machine at the specified port number (where
port-kis an exact integer between
65535inclusive). The
max-allow-wait-kargument determines the maximum number of client connections that can be waiting for acceptance. (When
max-allow-wait-kclients are waiting acceptance, no new client connections can be made.) The default value for
max-allow-wait-kargument is
If the
reuse?argument is true, then
will create a listener even if the port is involved in a TIME_WAIT state. Such a use of
reuse?defeats certain guarantees of the TCP protocol; see Stevens's book for details. Furthermore, on many modern platforms, a true value for
reuse?overrides TIME_WAIT only if the listener was previously created with a true value for
reuse?. The default for
#f(the default), then the listener accepts connections to all of the listening machine's addresses.39 Otherwise, the listener accepts connections only at the interface(s) associated with the given hostname. For example, providing
hostname-string-or-falsecreates a listener that accepts only connections to
"127.0.0.1"(the loopback interface) from the local machine.
The return value of
is a TCP listener value. This value can be used in future calls to
, and
. Each new TCP listener value is placed into the management of the current custodian (see section 9.2).
If the server cannot be started by
, the
exn:fail:networkexception is raised.
hostname-string port-k[
local-hostname-string-or-false local-port-k-or-false]
)attempts to connect as a client to a listening server. The
hostname-stringargument is the server host's Internet address name40 (e.g.,
"www.plt-scheme.org"), and
port-k(an exact integer between
65535) is the port where the server is listening.
The optional
local-port-k-or-falsespecify the client's address and port. If both are
#f(the default), the client's address and port are selected automatically. If
local-hostname-string-or-falseis not
#f, then
local-port-k-or-falsemust be non-
#f. If
local-port-k-or-falseis non-
#f, then the given port is used but the address is selected automatically.
Two values (see section 2.2) are returned by
: an input port and an output port. Data can be received from the server through the input port and sent to the server through the output port. If the server is a MzScheme process, it can obtain ports to communicate to the client with
. These ports are placed into the management of the current custodian (see section 9.2); if a custodian shuts down the connection, it must abort the connection.
Initially, the returned input port is block-buffered, and the returned output port is block-buffered. Change the buffer mode using
file-stream-buffer-mode(see section 11.1.6).
Both of the returned ports must be closed to terminate the TCP connection. When both ports are still open, closing the output port with
close-output-portsends a TCP close to the server (which is seen as an end-of-file if the server reads the connection through a port). In contrast,
tcp-abandon-port(see below) closes the output port, but does not send a TCP close until the input port is also closed.
If a connection cannot be established by
, the
exn:fail:networkexception is raised.
hostname-string port-k[
local-hostname-string local-port-k]
)is like
, but breaking is enabled (see section 6.6) while trying to connect. If breaking is disabled when
is called, then either ports are returned or the
exn:breakexception is raised, but not both.
)accepts a client connection for the server associated with
tcp-listener. The
tcp-listenerargument is a TCP listener value returned by
. If no client connection is waiting on the listening port, the call to
will block. (See also
, below.)
Two values (see section 2.2) are returned by
: an input port and an output port. Data can be received from the client through the input port and sent to the client through the output port. These ports are placed into the management of the current custodian (see section 9.2).
Initially, the returned input port is block-buffered, and the returned output port is block-buffered. Change the buffer mode using
file-stream-buffer-mode(see section 11.1.6).
Both of the returned ports must be closed to terminate the connection. When both ports are still open, closing the output port with
close-output-portsends a TCP close to the client (which is seen as an end-of-file if the client reads the connection through a port). In contrast,
tcp-abandon-port(see below) closes the output port, but does not send a TCP close until the input port is also closed.
If a connection cannot be accepted by
, or if the listener has been closed, the
exn:fail:networkexception is raised.
)tests whether an unaccepted client has connected to the server associated with
tcp-listener. The
tcp-listenerargument is a TCP listener value returned by
. If a client is waiting, the return value is
#t, otherwise it is
#f. A client is accepted with the
procedure, which returns ports for communicating with the client and removes the client from the list of unaccepted clients.
If the listener has been closed, the
exn:fail:networkexception is raised.
)is like
, but breaking is enabled (see section 6.6) while trying to accept a connection. If breaking is disabled when
is called, then either ports are returned or the
exn:breakexception is raised, but not both.
)shuts down the server associated with
tcp-listener. The
tcp-listenerargument is a TCP listener value returned by
. All unaccepted clients receive an end-of-file from the server; connections to accepted clients are unaffected.
If the listener has already been closed, the
exn:fail:networkexception is raised.
The listener's port number may not become immediately available for new listeners (with the default
reuse?argument of
). For further information, see Stevens's explanation of the TIME_WAIT TCP state.
vis a TCP listener value created by
)returns a synchronizable event (see section 7.7) that is in a blocking state when
tcp-listenerwould block. If the event is chosen in a synchronization, the result is a list of two items, which correspond to the two results of
tcp-accept. (If the event is not chosen, no connections are accepted.)
)is like
close-input-port(depending on whether
tcp-portis an input or output port), but if
tcp-portis an output port and its associated input port is not yet closed, then the other end of the TCP connection does not receive a TCP close message until the input port is also closed.41
)returns two strings. The first string is the Internet address for the local machine a viewed by the given TCP port's connection.42 The second string is the Internet address for the other end of the connection.
If the given port has been closed, the
exn:fail:networkexception is raised.
vis a port returned by
tcp-accept/enable-break, or
For information about UDP in general, see TCP/IP Illustrated, Volume 1 by W. Richard Stevens (which discusses UDP in addition to TCP).
family-hostname-string-or-false family-port-k-or-false]
)creates and returns a UDP socket to send and receive datagrams (broadcasting is allowed). Initially, the socket is not bound or connected to any address or port.
family-port-k-or-falseis provided and not
#f, then the socket's protocol family is determined from these arguments. The socket is not bound to the hostname or port number. For example, the arguments might be the hostname and port to which messages will be sent through the socket, which ensures that the socket's protocol family is consistent with the destination. Alternately, the arguments might be the same as for a future call to
udp-bind!, which ensures that the socket's protocol family is consistent with the binding. If neither
family-port-k-or-falseis provided as non-
#f, then the socket's protocol family is IPv4.
udp-socket hostname-string-or-false port-k
)binds an unbound
udp-socketto the local port number
port-k(an exact integer between
65535). The result is always void.
#f, then the socket accepts connections to all of the listening machine's IP addresses at
port-k. Otherwise, the socket accepts connections only at the IP address associated with the given name. For example, providing
hostname-string-or-falsetypically creates a listener that accepts only connections to
"127.0.0.1"from the local machine.
A socket cannot receive datagrams until it is bound to a local address and port. If a socket is not bound before it is used with a sending procedure
udp-send-to, etc., the sending procedure binds the socket to a random local port. Similarly, if an event from
udp-send-to-evtis chosen for a synchronization (see section 7.7), the socket is bound; if the event is not chosen, the socket may or may not become bound. The binding of a bound socket cannot be changed.
udp-socketis already bound or closed, the
exn:fail:networkexception is raised.
udp-socket hostname-string-or-false port-k-or-false
)connects the socket to the indicated remote address and port if
hostname-string-or-falseis a string and
port-k-or-falseis an exact integer between
65535. The result is always void.
#f, then
port-k-or-falsealso must be
#f, and the port is disconnected (if connected). If one of
#fand the other is not, the
exn:fail:contractexception is raised.
A connected socket can be used with
udp-send-to), and it accepts datagrams only from the connected address and port. A socket need not be connected to receive datagrams. A socket can be connected, re-connected, and disconnected any number of times.
udp-socketis closed, the
exn:fail:networkexception is raised.
udp-socket hostname-address port-k bytes[
start-k end-k]
(subbytesas a datagram from the unconnected
udp-socketto the socket at the remote machine
hostname-addresson the port
port-k. The
udp-socketneed not be bound or connected; if it is not bound,
udp-send-tobinds it to a random local port. If the socket's outgoing datagram queue is too full to support the send,
udp-send-toblocks until the datagram can be queued. The result is always void.
The optional
start-kargument defaults to
0, and the optional
end-kargument defaults to the length of
bytes. If
start-kis greater than the length of
bytes, or if
end-kis less than
start-kor greater than the length of
bytes, the
exn:fail:contractexception is raised.
udp-socketis closed or connected, the
exn:fail:networkexception is raised.
udp-socket bytes[
start-k end-k]
)is like
udp-send-to, except that
udp-socketmust be connected, and the datagram goes to the connection target. If
udp-socketis closed or unconnected, the
exn:fail:networkexception is raised.
udp-socket hostname-address port-k bytes[
start-k end-k]
)is like
udp-send-to, except that it never blocks; if the socket's outgoing queue is too full to support the send,
#fis returned, otherwise the datagram is queued and the result is
udp-socket bytes[
start-k end-k]
)is like
udp-send, except that (like
udp-send-to) it never blocks and returns
udp-socket hostname-address port-k bytes[
start-k end-k]
)is like
udp-send-to, but breaking is enabled (see section 6.6) while trying to send the datagram. If breaking is disabled when
udp-send-to/enable-breakis called, then either the datagram is sent or the
exn:breakexception is raised, but not both.
udp-socket bytes[
start-k end-k]
)is like
udp-send, except that breaks are enabled like
udp-socket mutable-bytes[
start-k end-k]
)accepts up to
start-kbytes of
udp-socket's next incoming datagram into
mutable-bytes, writing the datagram bytes starting at position
mutable-bytes. The
udp-socketmust be bound to a local address and port (but need not be connected). If no incoming datagram is immediately available,
udp-receive!blocks until one is available.
Three values are returned: an exact integer that indicates the number of received bytes (between
start-k), a hostname string indicating the source address of the datagram, and an exact integer between
65535indicating the source port of the datagram. If the received datagram is longer than
start-kbytes, the remainder is discarded.
The optional
start-kargument defaults to
0, and the optional
end-kargument defaults to the length of
mutable-bytes. If
start-kis greater than the length of
mutable-bytes, or if
end-kis less than
start-kor greater than the length of
mutable-bytes, the
exn:fail:contractexception is raised.
udp-socket mutable-bytes[
start-k end-k]
)is like
udp-receive!, except that it never blocks. If no datagram is available, the three result values are all
udp-socket mutable-bytes[
start-k end-k]
)is like
udp-receive!, but breaking is enabled (see section 6.6) while trying to receive the datagram. If breaking is disabled when
udp-receive!/enable-breakis called, then either a datagram is received or the
exn:breakexception is raised, but not both.
udp-socket, discarding unreceived datagrams. If the socket is already closed, the
exn:fail:networkexception is raised.
vis a socket created by
udp-socketis bound to a local address and port,
udp-socketis connected to a remote address and port,
)returns a synchronizable event (see section 7.7) that is in a blocking state when
udp-socketwould block.
)returns a synchronizable event (see section 7.7) that is in a blocking state when
udp-socketwould block.
udp-socket hostname-address port-k bytes[
start-k end-k]
)returns a synchronizable event. The event is in a blocking state when
udp-socketwould block. Otherwise, if the event is chosen in a synchronization, data is sent as for
(udp-send-to, and the synchronization result is void. (No bytes are sent if the event is not chosen.)
udp-socket bytes[
start-k end-k]
)is like
udp-send-to-evt, except that
udp-socketmust be connected when the event is synchronized, and if the event is chosen in a synchronization, the datagram goes to the connection target. If
udp-socketis closed or unconnected, the
exn:fail:networkexception is raised during a synchronization attempt.
udp-socket bytes[
start-k end-k]
)returns a synchronizable event. The event is in a blocking state when
udp-socketwould block. Otherwise, if the event is chosen in a synchronization, data is receive into
bytesas for
(udp-receive!, and the synchronization result is a list of three values, corresponding to the three results from
udp-receive!. (No bytes are received and the
bytescontent is not modified if the event is not chosen.)
27 63 is
the same as
char->integer #\?)
28 Flushing is performed by the default port
read handler (see section 11.2.6) rather than by
29 This non-byte result is
not intended to return a character or
; in
read-char raises an exception if it encounters a
non-byte from a port.
30 More precisely, the procedure is used by the default port read handler; see also section 11.2.6.
temporary string of size
k is allocated while reading the
input, even if the size of the result is less than
32 Only mid-stream
s can be
committed. A
when the port is exhausted does not
correspond to data in the stream.
33 Assuming that the current port display and write handlers are the default ones; see section 11.2.7 for more information.
34 The
port read handler is not used for
read/recursive or
35 Under Mac OS X, Finder aliases are zero-length files.
36 For MrEd, the executable path is the name of a MrEd executable.
37 Under Windows,
#t for all variations of the special filenames (e.g.,
38 For FAT filesystems under Windows, directories do not have modification dates. Therefore, the creation date is returned for a directory (but the modification date is returned for a file).
39 MzScheme implements a listener with multiple
sockets, if necessary, to accomodate multiple addresses with
different protocol families. Under Linux,
hostname-string-or-false maps to both IPv4 and IPv6
addresses, then the behavior depends on whether IPv6 is supported and
IPv6 sockets can be configured to listen to only IPv6 connections: if
IPv6 is not supported or IPv6 sockets are not configurable, then the
IPv6 addresses are ignored; otherwise, each IPv6 listener accepts
only IPv6 connections.
40 If
hostname-string is associated with
multiple addresses, they are tried one at a time until a connection
succeeds. The name
"localhost" generally specifies the local
41 The TCP protocol does not include a ``no longer
reading'' state on connections, so
tcp-abandon-port is
equivalent to
close-input-port on input TCP ports.
42 For most machines, the answer corresponds to the current machine's only Internet address. But when a machine serves multiple addresses, the result is connection-specific.