1  Writing Mistie formats

A typical intent of a format file is to cause certain characters in the input document to trigger non-trivial changes in the output document. E.g., if the output is to be HTML, we'd like the characters <, >, &, and " in the input to come out as &lt;, &gt;, &amp;, and &quot;, respectively. The Mistie procedure mistie-def-char can be used for this:

(mistie-def-char #\< 
  (lambda ()
    (display "&lt;")))

(mistie-def-char #\> 
  (lambda ()
    (display "&gt;")))

(mistie-def-char #\& 
  (lambda ()
    (display "&amp;")))

(mistie-def-char #\" 
  (lambda ()
    (display "&quot;")))

mistie-def-char takes two arguments: The first is the character that is defined, and the second is the procedure associated with it. Here, the procedure writes the HTML encoded version of the character.

Suppose we want a contiguous sequence of blank lines to be come out as the paragraph separator, <p>. We could mistie-def-char the newline character as follows:

(mistie-def-char #\newline
  (lambda ()
    (newline)
    (let* ((s (h-read-whitespace))
           (n (h-number-of-newlines s)))
      (if (> n 0)
          (begin (display "<p>")
            (newline) (newline))
          (display s)))))

This will cause newline to read up all the following whitespace, and then check to see how many further newlines it picked up. If there was at least one, it outputs the paragraph separator, viz., <p> followed by two newlines (added for human readability). Otherwise, it merely prints the picked up whitespace as is. The help procedures h-read-whitespace and h-number-of-newlines are ordinary Scheme procedures:

(define h-read-whitespace
  (lambda ()
    (let loop ((r '()))
      (let ((c (peek-char)))
        (if (or (eof-object? c) (not (char-whitespace? c)))
            (list->string (reverse r))
            (loop (cons (read-char) r)))))))

(define h-number-of-newlines
  (lambda (ws)
    (let ((n (string-length ws)))
      (let loop ((i 0) (k 0))
        (if (>= i n) k
            (loop (+ i 1)
              (if (char=? (string-ref ws i) #\newline)
                  (+ k 1) k)))))))

1.1  Control sequences

The Mistie procedure mistie-def-ctl-seq defines control sequences. A control sequence is a sequence of letters (alphabetic characters), and is invoked in the input document by prefixing the sequence with an escape character. (The case of the letters is insignificant.) mistie-def-ctl-seq associates a procedure with a control sequence -- when the control sequence occurs in the input document, it causes the procedure to be applied. The following defines the control sequence br, which emits the HTML tag <br>:

(mistie-def-ctl-seq 'br
  (lambda ()
    (display "<br>")))

Before a control sequence can be used, we must fix the escape character. The following sets it to backslash:

(set! mistie-escape-char #\\)

We can now invoke the br control sequence as \br.

1.2  Frames

However, we can do better and get automatic line breaks with a more powerful control sequence. Let's say text between \obeylines and \endobeylines should have automatic line breaks. We define the control sequences obeylines and endobeylines as follows:

(mistie-def-ctl-seq 'obeylines
  (lambda ()
    (mistie-push-frame)
    (mistie-def-char #\newline
      (lambda ()
        (display "<br>")
        (newline)))
    (mistie-def-ctl-seq 'endobeylines
      (lambda ()
        (mistie-pop-frame)))))

The obeylines control sequence first pushes a new frame on to the Mistie environment, using the Mistie procedure mistie-push-frame. What this means is that any definitions (whether mistie-def-char or mistie-def-ctl-seq) will shadow existing definitions. The Mistie procedure mistie-pop-frame exits the frame, causing the older definitions to take effect again.

In this case, we create a shadowing mistie-def-char for newline, so that it will emit <br> instead of performing its default action (which, as we described above, was to look for paragraph separation). We also define a control sequence endobeylines which will pop the frame pushed by obeylines. With this definition in place, any text sandwiched between \obeylines and \endobeylines (assuming \ is the escape character) will be output with a <br> at the end of each of its lines.

1.3  Calling Scheme from within the document

We can define a control sequence eval that will allow the input document to explicitly evaluate Scheme expressions, without having to put them all in a format file.

(mistie-def-ctl-seq 'eval
  (lambda ()
    (eval (read))))

This will cause \eval followed by a Scheme expression to evaluate that Scheme expression. E.g.,

\eval (display (+ 21 21))

will cause 42 to be printed at the point where the \eval statement is placed. Of course, once you have arbitrary access to Scheme within your document, the amount of kooky intertextual stuff you can do is limited only by your imagination. A mundane use for \eval is to reset the escape character at arbitrary locations in the document, should the existing character be needed (temporarily or permanently) for something else.