Text lines (lines that are not program code) begin with semicolon. Let's
write a function to distinguish such lines. It will accept a string s
and return #t if it's a text line.
(define (text-line? s)
(and (> (string-length s) 0)
(char=? (string-ref s 0) #\;)))
To output text lines, we should strip semicolon and maybe a space from it.
The following function does exactly this, and outputs the result:
(define (output-text-line s)
(display
(substring s
(if (and (>= (string-length s) 2)
(char=? (string-ref s 1) #\ ))
2 ; cut two characters, semicolon and space
1) ; cut one character, semicolon
(string-length s))))
Outputting code lines is easier, we just have to keep in mind that code blocks
must be indented by two spaces for reStructuredText to format them correctly.
(define (output-code-line s)
(display (string-append " " s)))
To distinguish program code from other pre blocks in output, we'll assign
special classes to pre blocks with code.
This functions outputs a special markup to let reStructuredText know that it
needs to add program and language-scheme to the classes of pre
element that wraps the program code.
(define (start-code-block)
(display "\n.. class :: program language-scheme\n\n::\n\n "))
Default styles shipped with reStructuredText tools don't know about such
classes, so we need to add our own style. Instead of creating templates, let's
just put this quick hack, which only works with rst2html, but not
rst2latex and other tools -- a CSS style block that makes program code
appear in navy color and adds a thick vertical border to the left.
We'll write this directly, so that it appears before any other output:
(display ".. raw:: html
<style>
pre.program {
color: navy;
border-left: 8px solid #eee;
padding-left: 16px;
margin-left: 0;
}
</style>
")
When we write a variable, a procedure or a macro name in the text, we'd like
them to appear as literal text. It would also be helpful to assign classes to
their markup. Let's output directives for reStructuredText:
(display
"
.. role:: proc(literal)
:class: procedure
.. role:: var(literal)
:class: variable
.. role:: value(literal)
:class: value
.. role:: macro(literal)
:class: macro
.. role:: module(literal)
:class: module
")
Finally, the read-write loop. We'll read lines from the standard input until
we reach the end of file, and process them one-by-one.
We'll use read-line function from rdelim module and R6RS
control structures, so let's import these modules:
(use-modules (ice-9 rdelim)
(rnrs control))
In the loop we read a line from input into variable s, and also
remember the last line read in the variable prev. We need prev
in order to know where to start a code block (if the previous line was a text
line, and the current one is not, then we should start a code block).
(let loop ((s (read-line)) (prev ""))
(unless (eof-object? s)
(cond
((text-line? s) (output-text-line s))
(else
(when (text-line? prev) (start-code-block))
(output-code-line s)))
(newline)
(loop (read-line) s)))
That's it!
Download source code
and run it with Guile.