much revised code, matching doc.txt and tests
svn: r6830 original commit: 11feb6010e5a5ef159d767292f8f2851c0308729
This commit is contained in:
parent
edb25384b7
commit
04836721c3
|
@ -1,32 +1,4 @@
|
|||
The _Scribble_ Collection
|
||||
=========================
|
||||
|
||||
The Scribble collection contains a few libraries that can be used to
|
||||
create documents from Scheme. It is made of independently usable
|
||||
parts. For example, the reader can be used in any situation that
|
||||
requires lots of free-form text, or you can use the rendering portion
|
||||
directly to generate documents.
|
||||
|
||||
|
||||
Running Scribble
|
||||
----------------
|
||||
|
||||
To process a Scribble document, use the `scribble' command-line utility
|
||||
(use `scribble -h' to see usage information). This is implemented by
|
||||
the "run-scribble.ss" module, which can be used directly:
|
||||
|
||||
> (render-file input output format)
|
||||
|
||||
Renders the given `input' file to the `output' file using the given
|
||||
format specification. The input and output files are used as is (no
|
||||
suffixes are added). The `output' argument can be #f, which will render
|
||||
the input to the current output port. `format' is a symbol that
|
||||
specifies the kind of output rendering (use the `scribble' commad to
|
||||
find the list of available formatters).
|
||||
|
||||
A Scribble document is a MzScheme module file, which provides a
|
||||
`content' binding.
|
||||
|
||||
(The rest of the Scribble documentation is now a separate manual.)
|
||||
|
||||
The Scribble Reader
|
||||
-------------------
|
||||
|
@ -71,12 +43,16 @@ Informally, the concrete syntax of @-forms is:
|
|||
|
||||
where all three parts after "@" are optional, but at least one should
|
||||
be present. (Note that spaces are not allowed between the three
|
||||
parts.) @litchar["@"] is set as a non-terminating reader macro, so it
|
||||
can be used as usual in Scheme identifiers unless you want to use it
|
||||
as a first character of an identifier; in this case you need to quote
|
||||
with a backslash (`\@foo@') or quote the whole identifier with bars
|
||||
(`|@foo|'). Of course, "@" is not treated specially in Scheme
|
||||
strings, character constants, etc.
|
||||
parts.) "@" is set as a non-terminating reader macro, so it can be
|
||||
used as usual in Scheme identifiers unless you want to use it as a
|
||||
first character of an identifier; in this case you need to quote with
|
||||
a backslash (`\@foo@') or quote the whole identifier with bars
|
||||
(`|@foo|').
|
||||
|
||||
(define |@foo| '\@bar@baz)
|
||||
|
||||
Of course, "@" is not treated specially in Scheme strings, character
|
||||
constants, etc.
|
||||
|
||||
Roughly, a form matching the above grammar is read as
|
||||
|
||||
|
@ -87,322 +63,541 @@ input. Thus, the initial <cmd> determines the Scheme code that the
|
|||
input is translated into. The common case is when <cmd> is a Scheme
|
||||
identifier, which generates a plain Scheme form.
|
||||
|
||||
A <text-body> is made of character sequences, newlines, and nested
|
||||
@-forms. Note that the syntax for @-forms is the same in a
|
||||
<text-body> context as in a Scheme context. A <text-body> that isn't
|
||||
an @-form is converted to a string expression for its <parsed-body>,
|
||||
and newlines are converted to "\n" expressions:
|
||||
A <text-body> is made of text, newlines, and nested @-forms. Note
|
||||
that the syntax for @-forms is the same in a <text-body> context as in
|
||||
a Scheme context. A <text-body> that isn't an @-form is converted to
|
||||
a string expression for its <parsed-body>, and newlines are converted
|
||||
to "\n" expressions:
|
||||
|
||||
@foo{bar baz
|
||||
blah}
|
||||
--is-read-as-->
|
||||
--reads-as-->
|
||||
(foo "bar baz" "\n" "blah")
|
||||
|
||||
@foo{bar @baz[3]
|
||||
blah}
|
||||
--is-read-as-->
|
||||
(foo (baz 3) "\n" "blah")
|
||||
--reads-as-->
|
||||
(foo "bar " (baz 3) "\n" "blah")
|
||||
|
||||
@foo{bar @baz{3}
|
||||
blah}
|
||||
--is-read-as-->
|
||||
--reads-as-->
|
||||
(foo "bar " (baz "3") "\n" "blah")
|
||||
|
||||
@foo{bar @baz[2 3]{4 5}
|
||||
blah}
|
||||
--is-read-as-->
|
||||
--reads-as-->
|
||||
(foo "bar " (baz 2 3 "4 5") "\n" "blah")
|
||||
|
||||
Note that spaces are not allowed before a "[" or a "{", or they will
|
||||
be part of the following text (or Scheme code). (More on using braces
|
||||
in body texts below.)
|
||||
|
||||
@foo{bar @baz[2 3] {4 5}}
|
||||
--reads-as-->
|
||||
(foo "bar " (baz 2 3) " {4 5}")
|
||||
|
||||
When the above @-forms appear in a Scheme expression context, the
|
||||
context must provide bindings for `foo' (as a procedure or macro). To
|
||||
just see the read result for an @-form, you can always a Scheme
|
||||
`quote':
|
||||
lexical environment must provide bindings for `foo' (as a procedure or
|
||||
a macro).
|
||||
|
||||
(let* ([formatter (lambda (fmt)
|
||||
(lambda args (format fmt (apply string-append args))))]
|
||||
[bf (formatter "*~a*")]
|
||||
[it (formatter "/~a/")]
|
||||
[ul (formatter "_~a_")]
|
||||
[text string-append])
|
||||
@text{@it{Note}: @bf{This is @ul{not} a pipe}.})
|
||||
--> "/Note/: *This is _not_ a pipe*."
|
||||
|
||||
If you want to see the expression that is actually being read, you can
|
||||
use Scheme's `quote'.
|
||||
|
||||
'@foo{bar}
|
||||
|
||||
** Concrete Syntax: The Command Part
|
||||
|
||||
Besides being a Scheme identifier, the <cmd> part of an @-form can be
|
||||
any Scheme expression. The expression can have Scheme punctuation
|
||||
prefixes, which will end up wrapping the *whole* expression.
|
||||
Besides being a Scheme identifier, the <cmd> part of an @-form can
|
||||
have Scheme punctuation prefixes, which will end up wrapping the
|
||||
*whole* expression.
|
||||
|
||||
@`',@foo{blah}
|
||||
|
||||
--is-read-as-->
|
||||
|
||||
`',@(foo "blah")
|
||||
@`',@foo{blah} --reads-as--> `',@(foo "blah")
|
||||
|
||||
When writing Scheme code, this means that @`',@foo{blah} is exactly
|
||||
the same as `@',@foo{blah} and `',@@foo{blah}, but unlike the latter
|
||||
two, the first construct can appear in body texts with the same
|
||||
meaning, whereas the other two would not work (see below).
|
||||
|
||||
As mentioned above, the command itself is not limited to a Scheme
|
||||
identifier; it can be any Scheme expression.
|
||||
|
||||
@(lambda (x) x){blah}
|
||||
|
||||
--is-read-as-->
|
||||
|
||||
((lambda (x) x) "blah")
|
||||
|
||||
|
||||
|
||||
|
||||
After the optional punctuation prefix, the <cmd> itself is not limited
|
||||
to identifiers -- it can be *any* Scheme expression.
|
||||
|
||||
@(lambda (x) x){blah} --reads-as--> ((lambda (x) x) "blah")
|
||||
@`(unquote foo){blah} --reads-as--> `(,foo "blah")
|
||||
|
||||
In addition, the command can be omitted altogether, which will omit it
|
||||
from the translation, resulting in an s-expression that usually contains
|
||||
just strings:
|
||||
from the translation, resulting in an S-expression that usually
|
||||
contains, say, just strings:
|
||||
|
||||
@{foo bar --is-read-as--> ("foo bar" "\n" "baz")
|
||||
@{foo bar --reads-as--> ("foo bar" "\n" "baz")
|
||||
baz}
|
||||
|
||||
@'{foo bar --is-read-as--> '("foo bar" "\n" "baz")
|
||||
@'{foo bar --reads-as--> '("foo bar" "\n" "baz")
|
||||
baz}
|
||||
|
||||
If the command part begins with a ";" (with no newline between the "@"
|
||||
and the ";"), then the construct is a comment. There are two comment
|
||||
forms, one for arbitrary-text and possibly nested comments, and another
|
||||
one for a -to-the-end-of-the-line comment:
|
||||
forms, one for arbitrary-text and possibly nested comments, and
|
||||
another one for a line comments:
|
||||
|
||||
@;{ ...any-text-including-tested-scribble-pieces... }
|
||||
@;{<anything> ...}
|
||||
|
||||
@; <anything-that-doesn't-begin-with-a-brace-to-the-end-of-the-line>
|
||||
@;<anything-else-without-newline>
|
||||
|
||||
Note that the first form is analogous to a "#;" comment: the commented
|
||||
body must still parse correctly. Also note that in the second form all
|
||||
text from the "@;" to the end of the line and all following
|
||||
(non-newline) whitespaces are part of the comment (as with TeX `%'
|
||||
comments). For example:
|
||||
In the first form, the commented body must still parse correctly; see
|
||||
the description of the body syntax below. In the second form, all
|
||||
text from the "@;" to the end of the line *and* all following spaces
|
||||
(or tabs) are part of the comment (similar to "%" comments in TeX).
|
||||
|
||||
@foo{bar @; comment --is-read-as--> (foo "bar baz")
|
||||
@foo{bar @; comment --reads-as--> (foo "bar baz")
|
||||
baz}
|
||||
|
||||
Tip: if you're editing in a Scheme-aware editor, it is useful to comment
|
||||
out blocks like this:
|
||||
|
||||
@;
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
or
|
||||
Tip: if you're editing in a Scheme-aware editor (like DrScheme or
|
||||
Emacs), it is useful to comment out blocks like this:
|
||||
|
||||
@;{
|
||||
...
|
||||
;}
|
||||
|
||||
otherwise you will probably confuse the editor into treating the file as
|
||||
having imbalanced parenthesis.
|
||||
so the editor does not treat the file as having unbalanced
|
||||
parenthesis.
|
||||
|
||||
If only the command part is specified, then the result is the command
|
||||
part only, without an extra set of parenthesis. This makes it suitable
|
||||
for Scheme escapes in body texts. More below, in the description of the
|
||||
body part.
|
||||
If only the <cmd> part of an @-form is specified, then the result is
|
||||
the command part only, without an extra set of parenthesis. This
|
||||
makes it suitable for Scheme escapes in body texts. (More on this
|
||||
below, in the description of the body part.)
|
||||
|
||||
Finally, note that there are no special rules for using "@" in the
|
||||
command itself, which can lead to things like:
|
||||
@foo{x @y z} --reads-as--> (foo "x " y " z")
|
||||
@foo{x @(* y 2) z} --reads-as--> (foo "x " (* y 2) " z")
|
||||
@{@foo bar} --reads-as--> (foo " bar")
|
||||
|
||||
@@foo{bar}{baz} --is-read-as--> ((foo "bar") "baz")
|
||||
Finally, note that there are currently no special rules for using "@"
|
||||
in the command itself, which can lead to things like:
|
||||
|
||||
To use "@" as in plain Scheme code, you need to quote it as you would
|
||||
quote other characters, for example:
|
||||
@@foo{bar}{baz} --reads-as--> ((foo "bar") "baz")
|
||||
|
||||
(define |@foo| '\@bar)
|
||||
** Concrete Syntax: The Datum Part
|
||||
|
||||
** Concrete Syntax: the body part
|
||||
The datum part can contains arbitrary Scheme expressions, which are
|
||||
simply stacked before the body text arguments:
|
||||
|
||||
The syntax of the body part is intended to be as convenient as possible
|
||||
for writing free text. It can contain almost any text -- the only
|
||||
character with special meaning is "@", in addition, braces, "|", and
|
||||
backslash can have special meanings but only in a few contexts. As
|
||||
described above, the text turns to a sequence of string arguments for
|
||||
the resulting form. Spaces at the beginning of lines are discarded (but
|
||||
see the information about indentation below), and newlines turn to
|
||||
individual "\n" strings. (Spaces are preserved on a single-line text.)
|
||||
As part of trying to do the `right thing', an empty line at the
|
||||
beginning and at the end are discarded, so
|
||||
@foo[1 (* 2 3)]{bar} --reads-as--> (foo 1 (* 2 3) "bar")
|
||||
@foo[@bar{...}]{blah} --reads-as--> (foo (bar "...") "blah")
|
||||
|
||||
The body part can still be omitted, which is essentially an
|
||||
alternative syntax for plain (non-textual) S-expressions:
|
||||
|
||||
@foo[bar] --reads-as--> (foo bar)
|
||||
@foo{bar @f[x] baz} --reads-as--> (foo "bar " (f x) " baz")
|
||||
|
||||
The datum part can be empty, which makes no difference, except when
|
||||
the body is omitted. It is more common, however, to use an empty body
|
||||
for the same purpose.
|
||||
|
||||
@foo[]{bar} --reads-as--> (foo "bar")
|
||||
@foo[] --reads-as--> (foo)
|
||||
@foo --reads-as--> foo
|
||||
@foo{} --reads-as--> (foo)
|
||||
|
||||
The most common use of the datum part is for Scheme forms that expect
|
||||
keyword-value arguments that precede the body of text arguments.
|
||||
|
||||
@foo[#:style 'big]{bar} --reads-as--> (foo #:style 'big "bar")
|
||||
|
||||
** Concrete Syntax: The Body Part
|
||||
|
||||
The syntax of the body part is intended to be as convenient as
|
||||
possible for free text. It can contain almost any text -- the only
|
||||
characters with special meaning is "@" for sub forms, and "}" for the
|
||||
end of the text. In addition, a "{" is allowed as part of the text,
|
||||
and it makes the matching "}" be part of the text too -- so balanced
|
||||
braces are valid text.
|
||||
|
||||
@foo{f{o}o} --reads-as--> (foo "f{o}o")
|
||||
@foo{{{}}{}} --reads-as--> (foo "{{}}{}")
|
||||
|
||||
As described above, the text turns to a sequence of string arguments
|
||||
for the resulting form. Spaces at the beginning and end of lines are
|
||||
discarded, and newlines turn to individual "\n" strings (i.e., they
|
||||
are not merged with other body parts). (See also the information
|
||||
about newlines and indentation below.) Spaces are *not* discarded if
|
||||
they appear after the open "{" (before the closing "}") when there is
|
||||
also text that follows (precedes) it; specifically, they are preserved
|
||||
in a single-line body.
|
||||
|
||||
@foo{bar} --reads-as--> (foo "bar")
|
||||
@foo{ bar } --reads-as--> (foo " bar ")
|
||||
@foo[1]{ bar } --reads-as--> (foo 1 " bar ")
|
||||
|
||||
If "@" appears in a body, then it is interpreted as Scheme code, which
|
||||
means that the @-reader is applied recursively, and the resulting
|
||||
syntax appears as part of the S-expression, among other string
|
||||
contents.
|
||||
|
||||
@foo{a @bar{b} c} --reads-as--> (foo "a " (bar "b") " c")
|
||||
|
||||
If the nested "@" construct has only a command -- no body or datum
|
||||
parts -- it will not appear in a subform. Given that the command part
|
||||
can be any Scheme expression, this makes "@" a general escape to
|
||||
arbitrary Scheme code.
|
||||
|
||||
@foo{a @bar c} --reads-as--> (foo "a " bar " c")
|
||||
@foo{a @(bar 2) c} --reads-as--> (foo "a " (bar 2) " c")
|
||||
|
||||
This is particularly useful with strings, which can be used to include
|
||||
arbitrary text.
|
||||
|
||||
@foo{This @"}" is a closing brace}
|
||||
--reads-as-->
|
||||
(foo "This } is a closing brace")
|
||||
|
||||
Note that the escaped string is (intentionally) merged with the rest
|
||||
of the text. This works for "@" too:
|
||||
|
||||
@foo{The command prefix is @"@".}
|
||||
--reads-as-->
|
||||
(foo "The command prefix is @.")
|
||||
|
||||
@foo{@"@foo{bar}" reads as (foo "bar")}
|
||||
--reads-as-->
|
||||
(foo "@foo{bar} reads as (foo \"bar\")")
|
||||
|
||||
* Concrete Syntax: Alternative Body Syntax
|
||||
|
||||
In addition, there is an alternative syntax for the body, one that
|
||||
specifies a new marker for its end: use "|{" for the opening marker to
|
||||
have the text terminated by a "}|".
|
||||
|
||||
@foo|{...}|
|
||||
--reads-as-->
|
||||
(foo "...")
|
||||
|
||||
@foo|{close with "}", open with "{"}|
|
||||
--reads-as-->
|
||||
(foo "close with \"}\", open with \"{\"")
|
||||
|
||||
@foo|{Nesting |{is}| ok}|
|
||||
--reads-as-->
|
||||
(foo "Nesting |{is}| ok")
|
||||
|
||||
This applies to sub-@-forms too -- the "@" must be prefixed with a
|
||||
"|":
|
||||
|
||||
@foo|{Maze
|
||||
|@bar{is}
|
||||
Life!}|
|
||||
--reads-as-->
|
||||
(foo "Maze" "\n" (bar "is") "Life!")
|
||||
|
||||
@foo|{Works for |@bar|{subforms}| too}|
|
||||
--reads-as-->
|
||||
(foo "Works for " (bar "subforms") " too")
|
||||
|
||||
Note that the subform uses its own delimiters, "{...}" or "|{...}|".
|
||||
This means that you can copy and paste Scribble text with @-forms
|
||||
freely, just prefix the "@" if the immediate surrounding text has a
|
||||
prefix.
|
||||
|
||||
For even better control, you can add characters in the opening
|
||||
delimiter, between the "|" and the "{". Characters that are put there
|
||||
(non alphanumeric ASCII characters only, excluding "{" and "@") should
|
||||
also be used for sub-@-forms, and the end-of-body marker should have
|
||||
these characters in reverse order with paren-like characters ("(",
|
||||
"[", "<") mirrored.
|
||||
|
||||
@foo|<<<{Some @x{more} |@{text}|.}>>>|
|
||||
--reads-as-->
|
||||
(foo "Some @x{more} |@{text}|.")
|
||||
|
||||
@foo|!!{Blah |!!@bold{blah}...}!!|
|
||||
--reads-as-->
|
||||
(foo "Blah " (bold "blah") "...")
|
||||
|
||||
Finally, remember that you can use an expression escape with a Scheme
|
||||
string for confusing situations. This works well when you only need
|
||||
to quote short pieces, and the above works well when you have larger
|
||||
multi-line body texts.
|
||||
|
||||
* Concrete Syntax: Scheme Expression Escapes
|
||||
|
||||
In some cases, you may want to use a Scheme identifier (or a number or
|
||||
a boolean etc.) in a position that touches the following text; in
|
||||
these situations you should surround the escaped Scheme expression by
|
||||
a pair of "|" characters. The text inside the bars is parsed as a
|
||||
Scheme expression.
|
||||
|
||||
@foo{foo@bar.} --reads-as--> (foo "foo" bar.)
|
||||
@foo{foo@|bar|.} --reads-as--> (foo "foo" bar ".")
|
||||
@foo{foo@3.} --reads-as--> (foo "foo" 3.0)
|
||||
@foo{foo@|3|.} --reads-as--> (foo "foo" 3 ".")
|
||||
|
||||
This form is a generic Scheme expression escape, there is no body text
|
||||
or datum part when you use this form.
|
||||
|
||||
@foo{foo@|(f 1)|{bar}} --reads-as--> (foo "foo" (f 1) "{bar}")
|
||||
@foo{foo@|bar|[1]{baz}} --reads-as--> (foo "foo" bar "[1]{baz}")
|
||||
|
||||
This works for string expressions too, but not that unlike the above,
|
||||
the string is (intentionally) not merged with the rest of the text:
|
||||
|
||||
@foo{x@"y"z} --reads-as--> (foo "xyz")
|
||||
@foo{x@|"y"|z} --reads-as--> (foo "x" "y" "z")
|
||||
|
||||
Expression escapes also work with *any* number of expressions,
|
||||
|
||||
@foo{x@|1 (+ 2 3) 4|y} --reads-as--> (foo "x" 1 (+ 2 3) 4 "y")
|
||||
|
||||
@foo{x@|* --reads-as--> (foo "x" * * "y")
|
||||
*|y}
|
||||
|
||||
It seems that "@||" has no purpose -- but remember that these escapes
|
||||
are never merged with the surrounding text, which can be useful when
|
||||
you want to control the sub expressions in the form.
|
||||
|
||||
@foo{Alice@||Bob@| --reads-as--> (foo "Alice" "Bob" "Carol")
|
||||
|Carol}
|
||||
|
||||
Note that "@|{...}|" can be parsed as either an escape expression or
|
||||
as a no-command @-form. The latter is used in this case (since there
|
||||
is little point in Scheme code that uses braces.
|
||||
|
||||
@|{blah}| --is-read-as--> ("blah")
|
||||
|
||||
* Concrete Syntax: Comments
|
||||
|
||||
As noted above, there are two kinds of Scribble comments: "@;{...}" is
|
||||
a (nestable) comment for a whole body of text (following the same
|
||||
rules for @-forms), and "@;..." is a line-comment.
|
||||
|
||||
@foo{First line@;{there is still a
|
||||
newline at this point;}
|
||||
Second line}
|
||||
--reads-as-->
|
||||
(foo "First line" "\n" "Second line")
|
||||
|
||||
One useful property of line-comments is that they continue to the end
|
||||
of the line *and* all following spaces (or tabs). Using this, you can
|
||||
get further control of the subforms.
|
||||
|
||||
@foo{This is @;
|
||||
a pretty long @;
|
||||
single string-@;
|
||||
argument.}
|
||||
--reads-as-->
|
||||
(foo "This is a pretty long single string-argument.")
|
||||
|
||||
Note how this is different from using "@||"s in that strings around it
|
||||
are not merged.
|
||||
|
||||
* Concrete Syntax: Spaces, Newlines, and Indentation
|
||||
|
||||
The Scribble syntax treats spaces and newlines in a special way is
|
||||
meant to be sensible for dealing with text. As mentioned above,
|
||||
spaces at the beginning and end of body lines are discarded, except
|
||||
for spaces between a "{" and text, or between text and a "}".
|
||||
|
||||
@foo{bar} --reads-as--> (foo "bar")
|
||||
|
||||
@foo{ bar } --reads-as--> (foo " bar ")
|
||||
|
||||
@foo{ bar --reads-as--> (foo " bar" "\n" "baz ")
|
||||
baz }
|
||||
|
||||
A single newline that follows an open brace or precedes a closing brace is
|
||||
discarded, unless there are only newlines in the body; other newlines
|
||||
are read as a "\n" string
|
||||
|
||||
@foo{bar --reads-as--> (foo "bar")
|
||||
}
|
||||
|
||||
@foo{
|
||||
bar --is-read-as--> (foo "bar") <--is-read-as-- @foo{bar}
|
||||
bar --reads-as--> (foo "bar")
|
||||
}
|
||||
|
||||
@foo{ bar } --is-read-as--> (foo " bar ")
|
||||
@foo{
|
||||
|
||||
If an "@" appears in the input, then it is interpreted as Scheme code,
|
||||
which means that the at-reader will be applied recursively, and the
|
||||
resulting syntax will appear as an argument, among other string
|
||||
contents. For example:
|
||||
bar --reads-as--> (foo "\n" "bar" "\n")
|
||||
|
||||
@foo{a @bar{b} c} --is-read-as--> (foo "a " (bar "b") " c")
|
||||
}
|
||||
|
||||
If the nested "@" construct has only a command -- no body part, then it
|
||||
does not appear in a subform. Given that the command part can be any
|
||||
Scheme expression, this makes "@" a general escape to arbitrary Scheme
|
||||
code:
|
||||
@foo{
|
||||
bar
|
||||
--reads-as--> (foo "bar" "\n" "\n" "baz")
|
||||
baz
|
||||
}
|
||||
|
||||
@foo{a @bar c} --is-read-as--> (foo "a " bar " c")
|
||||
@foo{ --reads-as--> (foo "\n")
|
||||
}
|
||||
|
||||
@foo{a @(bar 2) c} --is-read-as--> (foo "a " (bar 2) " c")
|
||||
@foo{
|
||||
--reads-as--> (foo "\n" "\n")
|
||||
}
|
||||
|
||||
In some cases, you may want to use a Scheme identifier (or a number or a
|
||||
boolean) in a position that touches other text that can make an
|
||||
identifier -- in these situations you should surround the Scheme
|
||||
identifier (/number/boolean) by a pair of bar characters. The text
|
||||
inside the bars is parsed as a Scheme expression, but if that fails, it
|
||||
is used as a quoted identifier -- do not rely on this behavior, and
|
||||
avoid using whitespace inside the bars. Also, if bars are used, then no
|
||||
body text is used even if they are followed by braces (see the next
|
||||
paragraph). Examples:
|
||||
@foo{ bar --reads-as--> (foo " bar" "\n" "baz ")
|
||||
baz }
|
||||
|
||||
@foo{foo @bar foo} --is-read-as--> (foo "foo " bar " foo")
|
||||
In the parsed S-expression syntax, a single newline string is used for
|
||||
all newlines; you can use `eq?' to identify this line. This can be
|
||||
used to identify newlines in the original <text-body>.
|
||||
|
||||
@foo{foo@bar.} --is-read-as--> (foo "foo" bar.)
|
||||
(let ([nl (car @'{
|
||||
})])
|
||||
(for-each (lambda (x) (display (if (eq? x nl) "\n... " x)))
|
||||
@`{foo
|
||||
@,@(list "bar" "\n" "baz")
|
||||
blah})
|
||||
(newline))
|
||||
--prints-->
|
||||
foo
|
||||
... bar
|
||||
baz
|
||||
... blah
|
||||
|
||||
@foo{foo@|bar|.} --is-read-as--> (foo "foo" bar ".")
|
||||
Spaces at the beginning of body lines do not appear in the resulting
|
||||
S-expressions, but the column of each line is noticed, and all-space
|
||||
indentation strings are added so the result has the same indentation.
|
||||
A indentation string is added to each line according to its distance
|
||||
from the leftmost syntax object (except for empty lines). (Note: if
|
||||
you try these examples on a mzscheme REPL, you should be aware that
|
||||
the reader does not know about the "> " prompt.)
|
||||
|
||||
@foo{foo@3.} --is-read-as--> (foo "foo" 3.0)
|
||||
@foo{ --reads-as--> (foo "bar" "\n"
|
||||
bar "baz" "\n"
|
||||
baz "blah")
|
||||
blah
|
||||
}
|
||||
|
||||
@foo{foo@|3|.} --is-read-as--> (foo "foo" 3 ".")
|
||||
@foo{ --reads-as--> (foo "begin" "\n"
|
||||
begin " " "x++;" "\n"
|
||||
x++; "end")
|
||||
end}
|
||||
|
||||
@foo{foo@|(f 1)|{bar}.} --is-read-as--> (foo "foo" (f 1) "{bar}.")
|
||||
@foo{ --reads-as--> (foo " " "a" "\n"
|
||||
a " " "b" "\n"
|
||||
b "c")
|
||||
c}
|
||||
|
||||
Braces are only problematic because a "}" is used to mark the end of the
|
||||
text. They are therefore allowed, as long as they are balanced. For
|
||||
example:
|
||||
If the first string came from the openning "{" line, it is not
|
||||
prepended with an indentation (but it can affect the leftmost syntax
|
||||
object used for indentation). This makes sense when formatting
|
||||
structured code as well as text (see the last example in the following
|
||||
block).
|
||||
|
||||
@foo{f{o}o} --is-read-as--> (foo "f{o}o")
|
||||
|
||||
There is also an alternative syntax for the body, one that specifies a
|
||||
new marker for the end. To do this, use "|{" for the openning marker,
|
||||
optionally with additional characters between them (excluding "{",
|
||||
whitespace, and alphanumerics) -- the matching closing marker should be
|
||||
the mirrored form of the openning marker (reverse the characters and
|
||||
swap round, square, curly, and angle parentheses). For example:
|
||||
|
||||
@foo|{...}| --is-read-as--> (foo "...")
|
||||
|
||||
@foo|{foo{{{bar}| --is-read-as--> (foo "foo{{{bar")
|
||||
|
||||
@foo|<{{foo{{{bar}}>| --is-read-as--> (foo "{foo{{{bar}")
|
||||
|
||||
* Concrete Syntax: quoting in body texts
|
||||
|
||||
To quote braces or "@", precede them with a backslash. Note that this
|
||||
is an irregular use of backslash quoting! To use "\@" in your text,
|
||||
simply precede it with a backslash. The general rule is that to use N
|
||||
backslashes-and-a-special-character, you should precede it with one
|
||||
extra backslash. Any other use of a backslash (one that is not followed
|
||||
by more back-slashes and a special character) is preserved in the text
|
||||
as usual. Examples:
|
||||
|
||||
@foo{b\@ar} --is-read-as--> (foo "b@ar")
|
||||
@foo{b\\@ar} --is-read-as--> (foo "b\\@ar")
|
||||
@foo{b\\\@ar} --is-read-as--> (foo "b\\\\@ar")
|
||||
@foo{b\{\@\@ar} --is-read-as--> (foo "b{@@ar")
|
||||
@foo{b\ar} --is-read-as--> (foo "b\\ar")
|
||||
@foo{b\\ar} --is-read-as--> (foo "b\\\\ar")
|
||||
|
||||
* Concrete Syntax: newlines and indentation
|
||||
|
||||
When indentation is used, all-space indentation string syntaxes are
|
||||
perpended to the beginning of each line. The rule for adding these
|
||||
string is:
|
||||
- A spaces-string is added to each line according to its distance from
|
||||
the leftmost syntax object;
|
||||
- The first string is not prepended with indentation if it appears on
|
||||
the first line of output.
|
||||
Examples:
|
||||
|
||||
@foo{ --is-read-as--> (foo "bar" "\n"
|
||||
bar " " "baz" "\n"
|
||||
baz "bbb")
|
||||
bbb}
|
||||
|
||||
@foo{bar --is-read-as--> (foo "bar" "\n"
|
||||
@foo{bar --reads-as--> (foo "bar" "\n"
|
||||
baz " " "baz" "\n"
|
||||
bbb} "bbb")
|
||||
|
||||
@foo{ bar --is-read-as--> (foo " bar" "\n"
|
||||
@foo{ bar --reads-as--> (foo " bar" "\n"
|
||||
baz " " "baz" "\n"
|
||||
bbb} " " "bbb")
|
||||
|
||||
@foo{bar --is-read-as--> (foo "bar" "\n"
|
||||
@foo{bar --reads-as--> (foo "bar" "\n"
|
||||
baz "baz" "\n"
|
||||
bbb} "bbb")
|
||||
|
||||
@foo{ bar --is-read-as--> (foo " bar" "\n"
|
||||
@foo{ bar --reads-as--> (foo " bar" "\n"
|
||||
baz "baz" "\n"
|
||||
bbb} "bbb")
|
||||
|
||||
@foo{ bar --is-read-as--> (foo " bar" "\n"
|
||||
@foo{ bar --reads-as--> (foo " bar" "\n"
|
||||
baz "baz" "\n"
|
||||
bbb} " " "bbb")
|
||||
|
||||
Additional notes:
|
||||
- You can identify indentation strings at the syntax level by the fact
|
||||
that they have the same location information as the following syntax
|
||||
object;
|
||||
- This mechanism depends on line and column number information
|
||||
(`use-at-readtable' turns them on for the current input port);
|
||||
- To use this mechanism with nested commands that should preserve
|
||||
indentation, you will need to do some additional work since the nested
|
||||
use will have only its own indentation;
|
||||
- When using it on a command-line, you note that the reader is not aware
|
||||
of the "> " prompt, which might lead to confusing results.
|
||||
@text{Some text@footnote{And a
|
||||
footnote comment.}. More text.}
|
||||
--reads-as-->
|
||||
(text "Some text"
|
||||
(footnote "And a" "\n" "footnote comment.")
|
||||
". More text.")
|
||||
|
||||
[The following is likely to change.]
|
||||
|
||||
For situations where spaces at the beginning of lines matter (various
|
||||
verbatim environments), you should begin a line with a "|". It has no
|
||||
other special meaning -- so to use a "|" as the first character in the
|
||||
text, simply use another before it.
|
||||
Note that each @-form is parsed to an S-expression that has its own
|
||||
indentation. This means that Scribble source can be indented like
|
||||
code, but if indentation matters then you may need to apply
|
||||
indentation of the outer item to all lines of the inner one. For
|
||||
example, in
|
||||
|
||||
@code{
|
||||
|(define (foo x) --is-read-as--> (code "(define (foo x)" "\n"
|
||||
| |error|) " |error|)")
|
||||
begin
|
||||
i = 1, r = 1
|
||||
@bold{while i < n do
|
||||
r *= i++
|
||||
done}
|
||||
end
|
||||
}
|
||||
--reads-as-->
|
||||
(code "begin" "\n"
|
||||
" " "i = 1, r = 1" "\n"
|
||||
" " (bold "while i < n do" "\n"
|
||||
" " "r *= i++" "\n"
|
||||
"done") "\n"
|
||||
"end")
|
||||
|
||||
In other situations, newlines matter -- you might want to avoid a
|
||||
newline token in some place. To avoid a newline and still break the
|
||||
source line, use a line comment. As in TeX, these will consume text
|
||||
up-to and including the end of the line and all following whitespace.
|
||||
Example:
|
||||
a formatter will need to apply the 2-space indentation to the
|
||||
rendering of the `bold' body.
|
||||
|
||||
@foo{bar @;
|
||||
baz@; --is-read-as--> (foo "bar baz.")
|
||||
.}
|
||||
Note that to get a first-line text to be counted as a leftmost line,
|
||||
line and column accounting should be on for the input port
|
||||
(`use-at-readtable' turns them on for the current input port).
|
||||
Without this,
|
||||
|
||||
A "|" that follows this is still used for marking the beginning of the
|
||||
text:
|
||||
@foo{x1
|
||||
x2
|
||||
x3}
|
||||
|
||||
@foo{bar @;
|
||||
baz@; --is-read-as--> (foo "bar baz .")
|
||||
| .}
|
||||
will not have 2-space indentations in the parsed S-expression if
|
||||
source accounting is not on, but
|
||||
|
||||
** Concrete Syntax: the keyword-value part
|
||||
@foo{x1
|
||||
x2
|
||||
x3}
|
||||
|
||||
The keyword-value part can contain arbitrary Scheme expressions, which
|
||||
are simply stacked before the body text arguments:
|
||||
will (due to the last line).
|
||||
|
||||
@foo[1 (* 2 3)]{bar} --is-read-as--> (foo 1 (* 2 3) "bar")
|
||||
@foo[@bar{...}]{blah} --is-read-as--> (foo (bar "...") "blah")
|
||||
For rare situations where spaces at the beginning (or end) of lines
|
||||
matter, you can begin (or end) a line with a "@||".
|
||||
|
||||
But there is one change that makes it easy to use for keyword/values:
|
||||
(a) "=" is a terminating character in the textual scope, (b) if there is
|
||||
a "<identifier>=<expr>" sequence (spaces optional), then it is converted
|
||||
to "#:identifier <expr>":
|
||||
@foo{
|
||||
@|| bar @|| --reads-as--> (foo " bar " "\n" " baz")
|
||||
@|| baz}
|
||||
|
||||
@foo[(* 2 3) a=b]{bar} --is-read-as--> (foo (* 2 3) #:a b "bar")
|
||||
Finally, you might be need a verbatim-like environment, where the
|
||||
parsed body matches exactly the textual source. To make this
|
||||
possible, the @-parser uses syntax properties on the resulting syntax
|
||||
values. All items that are not physically in the Scribble source --
|
||||
newlines and indentation-spaces -- have a 'scribble property. An
|
||||
indentation string will have 'indentation as the value of this
|
||||
property, and a newline will have a '(newline S) value where S is the
|
||||
original newline string including spaces that precede and follow it
|
||||
(which includes the indentation for the following item). To implement
|
||||
a verbatim environment you need to drop indentation strings, and use
|
||||
the original newline strings instead of the single-newline string.
|
||||
Here is an example of this.
|
||||
|
||||
*** How should this be used?
|
||||
|
||||
This facility can be used in any way you want. All you need is to use
|
||||
function names that you bind. You can even use quasi-quotes, skipping
|
||||
the need for functions, for example:
|
||||
|
||||
> (define (important . text) @`b{@u{@big{@,@text}}})
|
||||
> (important @`p{This is an important announcement!
|
||||
Read it!})
|
||||
(b (u (big (p "This is an important announcement!" "\n" "Read it!"))))
|
||||
(define-syntax (verb stx)
|
||||
(syntax-case stx ()
|
||||
[(_ cmd item ...)
|
||||
#`(cmd .
|
||||
#,(let loop ([items (syntax->list #'(item ...))])
|
||||
(if (null? items)
|
||||
'()
|
||||
(let* ([fst (car items)]
|
||||
[prop (syntax-property fst 'scribble)]
|
||||
[rst (loop (cdr items))])
|
||||
(cond [(not prop) (cons fst rst)]
|
||||
[(eq? prop 'indentation) rst]
|
||||
[else (cons (datum->syntax-object
|
||||
fst (cadr prop) fst)
|
||||
rst)])))))]))
|
||||
@verb[string-append]{
|
||||
foo
|
||||
bar
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user