120 lines
4.4 KiB
Scheme
120 lines
4.4 KiB
Scheme
;;; Here documents in Scheme for scsh scripts.
|
|
;;; These are like "here documents" for sh and csh shell scripts
|
|
;;; (i.e., the <<EOF redirection).
|
|
;;; Copyright (c) 1995 by Olin Shivers.
|
|
|
|
;;; There are two kinds of here string, both introduced by the #< read macro.
|
|
;;;
|
|
;;; Character-delimited here strings:
|
|
;;; A *character-delimited* here string has the form
|
|
;;; #<x...stuff...x
|
|
;;; where x is any single character (except <, see below), which is used
|
|
;;; to delimit the string bounds.
|
|
;;; Here are some examples:
|
|
;;;
|
|
;;; Here string syntax Ordinary syntax
|
|
;;; ------------------ ---------------
|
|
;;; #<|Hello, world.| "Hello, world."
|
|
;;; #<!"Ouch," he said.! "\"Ouch,\" he said."
|
|
;;;
|
|
;;; There is *no* interpretation of characters within the here string;
|
|
;;; characters are all copied verbatim.
|
|
;;;
|
|
;;; Line-delimited here strings:
|
|
;;; If the macro begins "#<<" then it introduces a *line-delimited* here
|
|
;;; string. These are similar to the "here documents" of sh and
|
|
;;; csh. Line-delimited here strings are delimited by the line of text coming
|
|
;;; after the "#<<" sequence. For example:
|
|
;;;
|
|
;;; #<FOO
|
|
;;; Hello, there.
|
|
;;; This is read by Scheme as a string,
|
|
;;; terminated by the first occurrence
|
|
;;; of newline-F-O-O-newline or newline-F-O-O-eof.
|
|
;;; FOO
|
|
;;;
|
|
;;; Thus,
|
|
;;; #<foo
|
|
;;; Hello, world.
|
|
;;; foo
|
|
;;; is the same thing as
|
|
;;; "Hello, world."
|
|
;;;
|
|
;;; These are useful for writing down long, constant strings -- such
|
|
;;; as long, multi-line FORMAT strings, or arguments to Unix programs, e.g.
|
|
;;; ;; Free up some disk space for my netnews files.
|
|
;;; (run (csh -c #<EOF
|
|
;;; cd /urops
|
|
;;; rm -rf *
|
|
;;; echo All done.
|
|
;;;
|
|
;;; EOF
|
|
;;; ))
|
|
;;;
|
|
;;; The advantage they have over the double-quote syntax -- "Hello, world." --
|
|
;;; is that there is no need to backslash-quote special characters internal
|
|
;;; to the string, such as the double-quote or backslash characters.
|
|
|
|
;;; Line-delimited here-string syntax:
|
|
;;; The characters "#<<" introduce the here-string. The characters
|
|
;;; between the "#<<" and the next newline are the *delimiter line*. *All* chars
|
|
;;; between the "#<<" and the next newline comprise the delimiter line --
|
|
;;; including any white space. The newline char separates the delimiter line
|
|
;;; from the body of the string. The string body is terminated by a line of
|
|
;;; text which exactly matches the delimiter line. This terminating line can
|
|
;;; be ended by either a newline or end-of-file. Absolutely *no*
|
|
;;; interpretation is done on the input string. Control chars, white space,
|
|
;;; quotes, backslash chars -- everything is copied as-is. The newline
|
|
;;; immediately preceding the terminating delimiter line is *not* included in
|
|
;;; the result string (leave an extra blank line if you need to put a final
|
|
;;; newline in the here string -- see the example above). If EOF is
|
|
;;; encountered before reading the end of the here string, an error is
|
|
;;; signalled.
|
|
|
|
(define (read-here-string port)
|
|
(make-immutable!
|
|
(let ((delim-char (read-char port)))
|
|
(cond ((eof-object? delim-char)
|
|
(reading-error port "EOF while reading #< here-string delimiter."))
|
|
|
|
((char=? delim-char #\<) ; It's a #<<EOF long here-string.
|
|
(read-line-delimited-here-string port))
|
|
|
|
;; It's short: #<|Here's the string.|
|
|
(else (receive (str terminator)
|
|
(read-delimited (char-set delim-char) port 'split)
|
|
(if (eof-object? terminator)
|
|
(reading-error port "EOF while reading #< here-string.")
|
|
str)))))))
|
|
|
|
|
|
(define (read-line-delimited-here-string port)
|
|
;; First, read in the delimiter line.
|
|
(let ((delim (read-line port)))
|
|
(cond ((eof-object? delim)
|
|
(reading-error port
|
|
"EOF while reading #<< here-string delimiter line."))
|
|
((= 1 (string-length delim))
|
|
(reading-error port "#<< here-string empty delimiter line"))
|
|
|
|
(else
|
|
(let lp ((text '()))
|
|
(receive (line terminator) (read-line port 'split)
|
|
(cond ((equal? line delim)
|
|
;; We're done. The last newline must be stripped
|
|
;; off (with the CDR application).
|
|
(if (pair? text)
|
|
(apply string-append (reverse (cdr text)))
|
|
""))
|
|
|
|
((eof-object? terminator)
|
|
(reading-error port "EOF while reading #<< here-string."))
|
|
|
|
(else (lp `(,(string terminator) ,line . ,text))))))))))
|
|
|
|
|
|
(define-sharp-macro #\<
|
|
(lambda (c port)
|
|
(read-char port) ; Snarf the first < char.
|
|
(read-here-string port)))
|