docs update
parent
7dc3ba800b
commit
201890d2d9
@ -0,0 +1,757 @@
|
|||||||
|
#lang scribble/manual
|
||||||
|
@(require scribble/bnf scribble/eval "utils.rkt"
|
||||||
|
(for-syntax racket/base)
|
||||||
|
(for-label (only-in scribble/reader
|
||||||
|
use-at-readtable) pollen/world))
|
||||||
|
|
||||||
|
@(define read-eval (make-base-eval))
|
||||||
|
@(interaction-eval #:eval read-eval (require (for-syntax racket/base)))
|
||||||
|
|
||||||
|
@(define (at-exp-racket)
|
||||||
|
@racket[#, @hash-lang[] #, @racketmodname[at-exp] #, @racketidfont{racket}])
|
||||||
|
|
||||||
|
@title[#:tag "reader"]{Pollen ◊ command notation}
|
||||||
|
|
||||||
|
@italic{Pollen is a dialect of Scribble, so I've adapted this section from Matthew Flatt and Eli Barzilay's excellent documentation for Scribble. If you think this section is good, it's because of them. If you don't, it's because of me.}
|
||||||
|
|
||||||
|
Pollen's command notation is designed to be a convenient system for embedding Pollen commands within free-form text.
|
||||||
|
|
||||||
|
@section{The golden rule}
|
||||||
|
|
||||||
|
Pollen uses a special character — the @italic{lozenge}, which looks like this: ◊ — as its command character. Meaning, when you put the ◊ in front of a word or expression, it will it be interpreted as a Pollen command.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Typically, ◊ notation is enabled through
|
||||||
|
@racketmodname[scribble/base] or similar languages, but you can also
|
||||||
|
add ◊ notation to an S-expression-based language using the
|
||||||
|
@racketmodname[at-exp] meta-language. For example,
|
||||||
|
|
||||||
|
@verbatim[#:indent 2]|{
|
||||||
|
#lang at-exp racket
|
||||||
|
(define v '@op{str})
|
||||||
|
}|
|
||||||
|
|
||||||
|
is equivalent to
|
||||||
|
|
||||||
|
@racketmod[
|
||||||
|
racket
|
||||||
|
(define v '(op "str"))
|
||||||
|
]
|
||||||
|
|
||||||
|
Using @at-exp-racket[] is probably the easiest way to try the examples
|
||||||
|
in this chapter.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@section{About that lozenge}
|
||||||
|
|
||||||
|
I picked the lozenge glyph as the delimiter — meaning this thing: ◊ — because a) it appears in almost every font, b) it's barely used in ordinary typesetting, and c) its shape and color allow it to stand out easily in code without being distracting.
|
||||||
|
|
||||||
|
Scribble itself uses the @"@" sign as a delimiter. Not a bad choice if all you do is Racket. But as you use Pollen to work on other kinds of text-based files that commonly contain @"@" signs — you know, like things with email addresses — a small headache starts to form. That's why I changed it.
|
||||||
|
|
||||||
|
Still, if you don't want to use the lozenge as your delimiter, you can use something else. Set Pollen's @racket[world:expression-delimiter] value to whatever character you want. But don't knock the lozenge till you try it.
|
||||||
|
|
||||||
|
@;--------------------------------------------------------------------
|
||||||
|
@section{The Pollen Syntax at a Glance}
|
||||||
|
|
||||||
|
To review @secref["how-to:reader"], the concrete syntax of @deftech{◊-forms}
|
||||||
|
is roughly
|
||||||
|
|
||||||
|
@racketblock[
|
||||||
|
@#,BNF-seq[@litchar["@"]
|
||||||
|
@nonterm{cmd}
|
||||||
|
@litchar{[} @kleenestar{@nonterm{datum}} @litchar{]}
|
||||||
|
@litchar["{"] @kleenestar{@nonterm{text-body}} @litchar["}"]]
|
||||||
|
]
|
||||||
|
|
||||||
|
where all three parts after @litchar["@"] are optional, but at least
|
||||||
|
one should be present. (Spaces are not allowed between the
|
||||||
|
three parts.) Roughly, a form matching the above grammar is read as
|
||||||
|
|
||||||
|
@racketblock[
|
||||||
|
(@#,nonterm{cmd} @#,kleenestar{@nonterm{datum}} @#,kleenestar{@nonterm{parsed-body}})
|
||||||
|
]
|
||||||
|
|
||||||
|
where @nonterm{parsed-body} is the translation of each
|
||||||
|
@nonterm{text-body} in the input. Thus, the initial @nonterm{cmd}
|
||||||
|
determines the Racket code that the input is translated into. The
|
||||||
|
common case is when @nonterm{cmd} is a Racket identifier, which reads
|
||||||
|
as a plain Racket form, with datum arguments and/or string arguments.
|
||||||
|
|
||||||
|
Here is one example:
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{blah blah blah}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
The example shows how an input syntax is read as Racket syntax, not
|
||||||
|
what it evaluates to. If you want to see the translation of an example
|
||||||
|
into S-expression form, add a quote in front of it in a
|
||||||
|
@at-exp-racket[] module. For example, running
|
||||||
|
|
||||||
|
@verbatim[#:indent 2]|{
|
||||||
|
#lang at-exp racket
|
||||||
|
'@foo{blah blah blah}
|
||||||
|
}|
|
||||||
|
|
||||||
|
in DrRacket prints the output
|
||||||
|
|
||||||
|
@nested[#:style 'inset]{@racketresult[(foo "blah blah blah")]}
|
||||||
|
|
||||||
|
while omitting the quote
|
||||||
|
|
||||||
|
@verbatim[#:indent 2]|{
|
||||||
|
#lang at-exp racket
|
||||||
|
@foo{blah blah blah}
|
||||||
|
}|
|
||||||
|
|
||||||
|
triggers a syntax error because @racket[foo] is not bound, and
|
||||||
|
|
||||||
|
@verbatim[#:indent 2]|{
|
||||||
|
#lang at-exp racket
|
||||||
|
(define (foo str) (printf "He wrote ~s.\n" str))
|
||||||
|
@foo{blah blah blah}
|
||||||
|
}|
|
||||||
|
|
||||||
|
prints the output
|
||||||
|
|
||||||
|
@nested[#:style 'inset]{@racketoutput{He wrote "blah blah blah".}}
|
||||||
|
|
||||||
|
Here are more examples of @tech{◊-forms}:
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{blah "blah" (`blah'?)}
|
||||||
|
@foo[1 2]{3 4}
|
||||||
|
@foo[1 2 3 4]
|
||||||
|
@foo[#:width 2]{blah blah}
|
||||||
|
@foo{blah blah
|
||||||
|
yada yada}
|
||||||
|
@foo{
|
||||||
|
blah blah
|
||||||
|
yada yada
|
||||||
|
}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
As seen in the last example, multiple lines and the newlines that
|
||||||
|
separate them are parsed to multiple Racket strings. More generally,
|
||||||
|
a @nonterm{text-body} is made of text, newlines, and nested
|
||||||
|
@tech{◊-forms}, where the syntax for @tech{◊-forms} is the same whether it's
|
||||||
|
in a @nonterm{text-body} context as in a Racket context. A
|
||||||
|
@nonterm{text-body} that isn't an @tech{◊-form} is converted to a string
|
||||||
|
expression for its @nonterm{parsed-body}; newlines and following
|
||||||
|
indentations are converted to @racket["\n"] and all-space string
|
||||||
|
expressions.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{bar @baz{3}
|
||||||
|
blah}
|
||||||
|
@foo{@b{@u[3] @u{4}}
|
||||||
|
blah}
|
||||||
|
@C{while (*(p++))
|
||||||
|
*p = '\n';}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
The command part of an @tech{◊-form} is optional as well. In that case,
|
||||||
|
the @tech{◊-form} is read as a list, which usually counts as a function
|
||||||
|
application, but it also useful when quoted with the usual Racket
|
||||||
|
@racket[quote]:
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@{blah blah}
|
||||||
|
@{blah @[3]}
|
||||||
|
'@{foo
|
||||||
|
bar
|
||||||
|
baz}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
Finally, we can also drop the datum and text parts, which leaves us with
|
||||||
|
only the command---which is read as is, not within a parenthesized
|
||||||
|
form. This is not useful when reading Racket code, but it can be used
|
||||||
|
inside a text block to escape a Racket identifier. A vertical bar
|
||||||
|
(@litchar{|}) can be used to delimit the escaped identifier when
|
||||||
|
needed.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo
|
||||||
|
@{blah @foo blah}
|
||||||
|
@{blah @foo: blah}
|
||||||
|
@{blah @|foo|: blah}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
Actually, the command part can be any Racket expression (that does not
|
||||||
|
start with @litchar["["], @litchar["{"], or @litchar["|"]), which is
|
||||||
|
particularly useful with such escapes since they can be used with any
|
||||||
|
expression.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{(+ 1 2) -> @(+ 1 2)!}
|
||||||
|
@foo{A @"string" escape}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
Note that an escaped Racket string is merged with the surrounding text
|
||||||
|
as a special case. This is useful if you want to use the special
|
||||||
|
characters in your string, but escaping braces are not necessary if
|
||||||
|
they are balanced.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{eli@"@"barzilay.org}
|
||||||
|
@foo{A @"{" begins a block}
|
||||||
|
@C{while (*(p++)) {
|
||||||
|
*p = '\n';
|
||||||
|
}}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
In some cases, a text contains many literal ◊s, which can be
|
||||||
|
cumbersome to quote individually. For such case, braces have an
|
||||||
|
alternative syntax: A block of text can begin with a
|
||||||
|
``@litchar["|{"]'' and terminated accordingly with a
|
||||||
|
``@litchar["}|"]''. Furthermore, any nested @tech{◊-forms} must begin
|
||||||
|
with a ``@litchar["|@"]''.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo|{bar}@{baz}|
|
||||||
|
@foo|{bar |@x{X} baz}|
|
||||||
|
@foo|{bar |@x|{@}| baz}|
|
||||||
|
}===|
|
||||||
|
|
||||||
|
In cases when even this is not convenient enough, punctuation
|
||||||
|
characters can be added between the @litchar{|} and the braces and the
|
||||||
|
◊ in nested forms. (The punctuation is mirrored for parentheses
|
||||||
|
and @litchar{<>}s.) With this extension, Pollen syntax can be used as a
|
||||||
|
``here string'' replacement.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo|--{bar}@|{baz}--|
|
||||||
|
@foo|<<{bar}@|{baz}>>|
|
||||||
|
}===|
|
||||||
|
|
||||||
|
On the flip side of this is, how can an ◊ sign be used in Racket
|
||||||
|
code? This is almost never an issue, because Racket strings and
|
||||||
|
characters are still read the same, and @litchar["@"] is set as a
|
||||||
|
non-terminating reader macro so it can be used in Racket identifiers
|
||||||
|
anywhere except in the first character of an identifier. When
|
||||||
|
@litchar["@"] must appear as the first character of an identifier, you
|
||||||
|
must quote the identifier just like other non-standard characters in
|
||||||
|
normal S-expression syntax: with a backslash or with vertical bars.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
(define \@email "foo@bar.com")
|
||||||
|
(define |@atchar| #\@)
|
||||||
|
}===|
|
||||||
|
|
||||||
|
Note that spaces are not allowed before a @litchar{[} or a
|
||||||
|
@litchar["{"], or they will be part of the following text (or Racket
|
||||||
|
code). (More on using braces in body texts below.)
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{bar @baz[2 3] {4 5}}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
Finally, remember that the Pollen is just an alternate for
|
||||||
|
S-expressions. Identifiers still get their meaning, as in any
|
||||||
|
Racket code, through the lexical context in which they appear.
|
||||||
|
Specifically, when the above @tech{◊-form} appears in a Racket expression
|
||||||
|
context, the lexical environment must provide bindings for
|
||||||
|
@racket[foo] as a procedure or a macro; it can be defined, required,
|
||||||
|
or bound locally (with @racket[let], for example).
|
||||||
|
|
||||||
|
@; FIXME: unfortunate code duplication
|
||||||
|
@interaction[
|
||||||
|
(eval:alts
|
||||||
|
(let* ([formatter (lambda (fmt)
|
||||||
|
(lambda args (format fmt (apply string-append args))))]
|
||||||
|
[bf (formatter "*~a*")]
|
||||||
|
[it (formatter "/~a/")]
|
||||||
|
[ul (formatter "_~a_")]
|
||||||
|
[text string-append])
|
||||||
|
#,(tt "@text{@it{Note}: @bf{This is @ul{not} a pipe}.}"))
|
||||||
|
(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}.}))
|
||||||
|
]
|
||||||
|
|
||||||
|
@;--------------------------------------------------------------------
|
||||||
|
@section{The Command Part}
|
||||||
|
|
||||||
|
Besides being a Racket identifier, the @nonterm{cmd} part of an
|
||||||
|
@tech{◊-form} can have Racket punctuation prefixes, which will end up
|
||||||
|
wrapping the @italic{whole} expression.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@`',@foo{blah}
|
||||||
|
@#`#'#,@foo{blah}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
When writing Racket code, this means that @litchar|{@`',@foo{blah}}|
|
||||||
|
is exactly the same as @litchar|{`@',@foo{blah}}| and
|
||||||
|
@litchar|{`',@@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).
|
||||||
|
|
||||||
|
After the optional punctuation prefix, the @nonterm{cmd} itself is not
|
||||||
|
limited to identifiers; it can be @italic{any} Racket expression.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@(lambda (x) x){blah}
|
||||||
|
@`(unquote 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, say, just strings:
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@{foo bar
|
||||||
|
baz}
|
||||||
|
@'{foo bar
|
||||||
|
baz}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
If the command part begins with a @litchar{;} (with no newline between
|
||||||
|
the @litchar["@"] and the @litchar{;}), then the construct is a
|
||||||
|
comment. There are two comment forms, one for arbitrary-text and
|
||||||
|
possibly nested comments, and another one for line comments:
|
||||||
|
|
||||||
|
@racketblock[
|
||||||
|
@#,BNF-seq[@litchar["@;{"] @kleenestar{@nonterm{any}} @litchar["}"]]
|
||||||
|
|
||||||
|
@#,BNF-seq[@litchar["@;"] @kleenestar{@nonterm{anything-else-without-newline}}]
|
||||||
|
]
|
||||||
|
|
||||||
|
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 @litchar["@;"] to the end of the line @italic{and} all
|
||||||
|
following spaces (or tabs) are part of the comment (similar to
|
||||||
|
@litchar{%} comments in TeX).
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{bar @; comment
|
||||||
|
baz@;
|
||||||
|
blah}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
Tip: if you use an editor in some Scheme mode without support for
|
||||||
|
@tech{◊-forms}, balanced comments can be confusing, since the open brace
|
||||||
|
looks commented out, and the closing one isn't. In such cases it is
|
||||||
|
useful to ``comment'' out the closing brace too:
|
||||||
|
|
||||||
|
@verbatim[#:indent 2]|==={
|
||||||
|
@;{
|
||||||
|
...
|
||||||
|
;}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
so the editor does not treat the file as having unbalanced
|
||||||
|
parentheses.
|
||||||
|
|
||||||
|
If only the @nonterm{cmd} part of an @tech{◊-form} is specified, then the
|
||||||
|
result is the command part only, without an extra set of parenthesis.
|
||||||
|
This makes it suitable for Racket escapes in body texts. (More on this
|
||||||
|
below, in the description of the body part.)
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{x @y z}
|
||||||
|
@foo{x @(* y 2) z}
|
||||||
|
@{@foo bar}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
Finally, note that there are currently no special rules for using
|
||||||
|
@litchar["@"] in the command itself, which can lead to things like:
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@@foo{bar}{baz}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
@;--------------------------------------------------------------------
|
||||||
|
@section{The Datum Part}
|
||||||
|
|
||||||
|
The datum part can contains arbitrary Racket expressions, which
|
||||||
|
are simply stacked before the body text arguments:
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo[1 (* 2 3)]{bar}
|
||||||
|
@foo[@bar{...}]{blah}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
The body part can still be omitted, which is essentially an
|
||||||
|
alternative syntax for plain (non-textual) S-expressions:
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo[bar]
|
||||||
|
@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.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo[]{bar}
|
||||||
|
@foo[]
|
||||||
|
@foo
|
||||||
|
@foo{}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
The most common use of the datum part is for Racket forms that expect
|
||||||
|
keyword-value arguments that precede the body of text arguments.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo[#:style 'big]{bar}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
@;--------------------------------------------------------------------
|
||||||
|
@section{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 @litchar["@"] for sub-@tech{◊-forms},
|
||||||
|
and @litchar["}"] for the end of the text. In addition, a
|
||||||
|
@litchar["{"] is allowed as part of the text, and it makes the
|
||||||
|
matching @litchar["}"] be part of the text too---so balanced braces
|
||||||
|
are valid text.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{f{o}o}
|
||||||
|
@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 @racket["\n"] strings
|
||||||
|
(i.e., they are not merged with other body parts); see also the
|
||||||
|
information about newlines and indentation below. Spaces are
|
||||||
|
@italic{not} discarded if they appear after the open @litchar["{"]
|
||||||
|
(before the closing @litchar["}"]) when there is also text that
|
||||||
|
follows (precedes) it; specifically, they are preserved in a
|
||||||
|
single-line body.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{bar}
|
||||||
|
@foo{ bar }
|
||||||
|
@foo[1]{ bar }
|
||||||
|
}===|
|
||||||
|
|
||||||
|
If @litchar["@"] appears in a body, then it is interpreted as Racket
|
||||||
|
code, which means that the ◊-reader is applied recursively, and the
|
||||||
|
resulting syntax appears as part of the S-expression, among other
|
||||||
|
string contents.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@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 Racket expression, this makes ◊ a general escape to
|
||||||
|
arbitrary Racket code.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{a @bar c}
|
||||||
|
@foo{a @(bar 2) c}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
This is particularly useful with strings, which can be used to include
|
||||||
|
arbitrary text.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{A @"}" marks the end}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
Note that the escaped string is (intentionally) merged with the rest
|
||||||
|
of the text. This works for @litchar["@"] too:
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{The prefix: @"@".}
|
||||||
|
@foo{@"@x{y}" --> (x "y")}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
@;--------------------------------------------------------------------
|
||||||
|
@subsection[#:tag "alt-body-syntax"]{Alternative Body Syntax}
|
||||||
|
|
||||||
|
In addition to the above, there is an alternative syntax for the body,
|
||||||
|
one that specifies a new marker for its end: use @litchar["|{"] for
|
||||||
|
the opening marker to have the text terminated by a @litchar["}|"].
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo|{...}|
|
||||||
|
@foo|{"}" follows "{"}|
|
||||||
|
@foo|{Nesting |{is}| ok}|
|
||||||
|
}===|
|
||||||
|
|
||||||
|
This applies to sub-@tech{◊-forms} too---the @litchar["@"] must be
|
||||||
|
prefixed with a @litchar{|}:
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo|{Maze
|
||||||
|
|@bar{is}
|
||||||
|
Life!}|
|
||||||
|
@t|{In |@i|{sub|@"@"s}| too}|
|
||||||
|
}===|
|
||||||
|
|
||||||
|
Note that the subform uses its own delimiters, @litchar{{...}} or
|
||||||
|
@litchar{|{...}|}. This means that you can copy and paste Pollen
|
||||||
|
text with @tech{◊-forms} freely, just prefix the @litchar["@"] if the
|
||||||
|
immediate surrounding text has a prefix.
|
||||||
|
|
||||||
|
For even better control, you can add characters in the opening
|
||||||
|
delimiter, between the @litchar{|} and the @litchar["{"].
|
||||||
|
Characters that are put there (non alphanumeric ASCII characters only,
|
||||||
|
excluding @litchar["{"] and @litchar["@"]) should also be used for
|
||||||
|
sub-@tech{◊-forms}, and the end-of-body marker should have these characters
|
||||||
|
in reverse order with paren-like characters (@litchar{(},
|
||||||
|
@litchar{[}, @litchar{<}) mirrored.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo|<<<{@x{foo} |@{bar}|.}>>>|
|
||||||
|
@foo|!!{X |!!@b{Y}...}!!|
|
||||||
|
}===|
|
||||||
|
|
||||||
|
Finally, remember that you can use an expression escape with a Racket
|
||||||
|
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.
|
||||||
|
|
||||||
|
@;--------------------------------------------------------------------
|
||||||
|
@subsection{Racket Expression Escapes}
|
||||||
|
|
||||||
|
In some cases, you may want to use a Racket identifier (or a number or
|
||||||
|
a boolean etc.) in a position that touches the following text; in
|
||||||
|
these situations you should surround the escaped Racket expression by
|
||||||
|
a pair of @litchar{|} characters. The text inside the bars is
|
||||||
|
parsed as a Racket expression.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{foo@bar.}
|
||||||
|
@foo{foo@|bar|.}
|
||||||
|
@foo{foo@3.}
|
||||||
|
@foo{foo@|3|.}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
This form is a generic Racket expression escape, there is no body text
|
||||||
|
or datum part when you use this form.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{foo@|(f 1)|{bar}}
|
||||||
|
@foo{foo@|bar|[1]{baz}}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
This works for string expressions too, but note that unlike the above,
|
||||||
|
the string is (intentionally) not merged with the rest of the text:
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{x@"y"z}
|
||||||
|
@foo{x@|"y"|z}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
Expression escapes also work with @italic{any} number of expressions,
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{x@|1 (+ 2 3) 4|y}
|
||||||
|
@foo{x@|*
|
||||||
|
*|y}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
It seems that @litchar["@||"] 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.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{Alice@||Bob@|
|
||||||
|
|Carol}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
Note that @litchar["@|{...}|"] can be parsed as either an escape expression or
|
||||||
|
as the Racket command part of an @tech{◊-form}. The latter is used in this case
|
||||||
|
(since there is little point in Racket code that uses braces.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@|{blah}|
|
||||||
|
}===|
|
||||||
|
|
||||||
|
@;--------------------------------------------------------------------
|
||||||
|
@subsection{Comments}
|
||||||
|
|
||||||
|
As noted above, there are two kinds of Pollen comments: @litchar|{@;{...}}| is
|
||||||
|
a (nestable) comment for a whole body of text (following the same
|
||||||
|
rules for @tech{◊-forms}), and @litchar|{@;...}| is a line-comment.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{First line@;{there is still a
|
||||||
|
newline here;}
|
||||||
|
Second line}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
One useful property of line-comments is that they continue to the end
|
||||||
|
of the line @italic{and} all following spaces (or tabs). Using this,
|
||||||
|
you can get further control of the subforms.
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{A long @;
|
||||||
|
single-@;
|
||||||
|
string arg.}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
Note how this is different from using @litchar["@||"]s in that strings
|
||||||
|
around it are not merged.
|
||||||
|
|
||||||
|
@;--------------------------------------------------------------------
|
||||||
|
@subsection{Spaces, Newlines, and Indentation}
|
||||||
|
|
||||||
|
The Pollen 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 @litchar["{"] and text, or between text and a
|
||||||
|
@litchar["}"].
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{bar}
|
||||||
|
@foo{ bar }
|
||||||
|
@foo{ bar
|
||||||
|
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 @racket["\n"] string
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{bar
|
||||||
|
}
|
||||||
|
@foo{
|
||||||
|
bar
|
||||||
|
}
|
||||||
|
@foo{
|
||||||
|
|
||||||
|
bar
|
||||||
|
|
||||||
|
}
|
||||||
|
@foo{
|
||||||
|
bar
|
||||||
|
|
||||||
|
baz
|
||||||
|
}
|
||||||
|
@foo{
|
||||||
|
}
|
||||||
|
@foo{
|
||||||
|
|
||||||
|
}
|
||||||
|
@foo{ bar
|
||||||
|
baz }
|
||||||
|
}===|
|
||||||
|
|
||||||
|
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 Racket REPL, you should be aware that
|
||||||
|
the reader does not know about the ``@litchar{> }'' prompt.)
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{
|
||||||
|
bar
|
||||||
|
baz
|
||||||
|
blah
|
||||||
|
}
|
||||||
|
@foo{
|
||||||
|
begin
|
||||||
|
x++;
|
||||||
|
end}
|
||||||
|
@foo{
|
||||||
|
a
|
||||||
|
b
|
||||||
|
c}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
If the first string came from the opening @litchar["{"] 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).
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{bar
|
||||||
|
baz
|
||||||
|
bbb}
|
||||||
|
@foo{ bar
|
||||||
|
baz
|
||||||
|
bbb}
|
||||||
|
@foo{bar
|
||||||
|
baz
|
||||||
|
bbb}
|
||||||
|
@foo{ bar
|
||||||
|
baz
|
||||||
|
bbb}
|
||||||
|
@foo{ bar
|
||||||
|
baz
|
||||||
|
bbb}
|
||||||
|
@text{Some @b{bold
|
||||||
|
text}, and
|
||||||
|
more text.}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
Note that each ◊-form is parsed to an S-expression that has its own
|
||||||
|
indentation. This means that Pollen 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
|
||||||
|
|
||||||
|
@litchar/lines|==={
|
||||||
|
@code{
|
||||||
|
begin
|
||||||
|
i = 1, r = 1
|
||||||
|
@bold{while i < n do
|
||||||
|
r *= i++
|
||||||
|
done}
|
||||||
|
end
|
||||||
|
}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
a formatter will need to apply the 2-space indentation to the
|
||||||
|
rendering of the @racket[bold] body.
|
||||||
|
|
||||||
|
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
|
||||||
|
(@racket[use-at-readtable] turns them on for the current input port).
|
||||||
|
Without this,
|
||||||
|
|
||||||
|
@litchar/lines|==={
|
||||||
|
@foo{x1
|
||||||
|
x2
|
||||||
|
x3}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
will not have 2-space indentations in the parsed S-expression if
|
||||||
|
source accounting is not on, but
|
||||||
|
|
||||||
|
@litchar/lines|==={
|
||||||
|
@foo{x1
|
||||||
|
x2
|
||||||
|
x3}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
will (due to the last line). Pay attention to this, as it can be a
|
||||||
|
problem with Racket code, for example:
|
||||||
|
|
||||||
|
@litchar/lines|==={
|
||||||
|
@code{(define (foo x)
|
||||||
|
(+ x 1))}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
For rare situations where spaces at the beginning (or end) of lines
|
||||||
|
matter, you can begin (or end) a line with a @litchar["@||"].
|
||||||
|
|
||||||
|
@scribble-examples|==={
|
||||||
|
@foo{
|
||||||
|
@|| bar @||
|
||||||
|
@|| baz}
|
||||||
|
}===|
|
||||||
|
|
||||||
|
@; --------------------------------------------------
|
||||||
|
@(close-eval read-eval)
|
||||||
|
|
@ -0,0 +1,230 @@
|
|||||||
|
#lang racket/base
|
||||||
|
|
||||||
|
(require scribble/core
|
||||||
|
scribble/html-properties
|
||||||
|
scribble/manual
|
||||||
|
(prefix-in racket: scribble/racket)
|
||||||
|
(prefix-in scribble: scribble/reader))
|
||||||
|
|
||||||
|
(define-syntax bounce-for-label
|
||||||
|
(syntax-rules (all-except)
|
||||||
|
[(_ (all-except mod (id ...) (id2 ...)))
|
||||||
|
(begin (require (for-label (except-in mod id ...)))
|
||||||
|
(provide (for-label (except-out (all-from-out mod) id2 ...))))]
|
||||||
|
[(_ mod) (begin (require (for-label mod))
|
||||||
|
(provide (for-label (all-from-out mod))))]
|
||||||
|
[(_ mod ...) (begin (bounce-for-label mod) ...)]))
|
||||||
|
|
||||||
|
(bounce-for-label (all-except racket (abstract link) ())
|
||||||
|
scribble/core
|
||||||
|
scribble/base-render
|
||||||
|
scribble/decode
|
||||||
|
scribble/manual
|
||||||
|
scribble/racket
|
||||||
|
scribble/html-properties
|
||||||
|
scribble/latex-properties
|
||||||
|
scribble/eval
|
||||||
|
scribble/bnf)
|
||||||
|
|
||||||
|
(provide scribble-examples litchar/lines doc-render-examples)
|
||||||
|
|
||||||
|
(define (as-flow e)
|
||||||
|
(if (block? e) e (make-paragraph plain (list e))))
|
||||||
|
|
||||||
|
(define (litchar/lines . strs)
|
||||||
|
(let ([strs (regexp-split #rx"\n" (apply string-append strs))])
|
||||||
|
(if (= 1 (length strs))
|
||||||
|
(litchar (car strs))
|
||||||
|
(make-table
|
||||||
|
plain
|
||||||
|
(map (lambda (s) ; the nbsp is needed for IE
|
||||||
|
(list (as-flow (if (string=? s "") 'nbsp (litchar s)))))
|
||||||
|
strs)))))
|
||||||
|
|
||||||
|
(define spacer (hspace 2))
|
||||||
|
|
||||||
|
(define ((norm-spacing base) p)
|
||||||
|
(cond [(and (syntax->list p) (not (null? (syntax-e p))))
|
||||||
|
(let loop ([e (syntax->list p)]
|
||||||
|
[line (syntax-line (car (syntax-e p)))]
|
||||||
|
[pos base]
|
||||||
|
[second #f]
|
||||||
|
[accum null])
|
||||||
|
(if (null? e)
|
||||||
|
(datum->syntax
|
||||||
|
p (reverse accum)
|
||||||
|
(list (syntax-source p) (syntax-line p) base (add1 base)
|
||||||
|
(- pos base))
|
||||||
|
p)
|
||||||
|
(let* ([v ((norm-spacing (if (= line (syntax-line (car e)))
|
||||||
|
pos
|
||||||
|
(or second pos)))
|
||||||
|
(car e))]
|
||||||
|
[next-pos (+ (syntax-column v) (syntax-span v) 1)])
|
||||||
|
(loop (cdr e)
|
||||||
|
(syntax-line v)
|
||||||
|
next-pos
|
||||||
|
(or second next-pos)
|
||||||
|
(cons v accum)))))]
|
||||||
|
[else (datum->syntax
|
||||||
|
p (syntax-e p)
|
||||||
|
(list (syntax-source p) (syntax-line p) base (add1 base) 1)
|
||||||
|
p)]))
|
||||||
|
|
||||||
|
(define (scribble-examples . lines)
|
||||||
|
(define reads-as (make-paragraph plain (list spacer "reads as" spacer)))
|
||||||
|
(let* ([lines (apply string-append lines)]
|
||||||
|
[p (open-input-string lines)])
|
||||||
|
(port-count-lines! p)
|
||||||
|
(let loop ([r '()] [newlines? #f])
|
||||||
|
(regexp-match? #px#"^[[:space:]]*" p)
|
||||||
|
(let* ([p1 (file-position p)]
|
||||||
|
[stx (scribble:read-syntax #f p)]
|
||||||
|
[p2 (file-position p)])
|
||||||
|
(if (not (eof-object? stx))
|
||||||
|
(let ([str (substring lines p1 p2)])
|
||||||
|
(loop (cons (list str stx) r)
|
||||||
|
(or newlines? (regexp-match? #rx#"\n" str))))
|
||||||
|
(let* ([r (reverse r)]
|
||||||
|
[r (if newlines?
|
||||||
|
(cdr (apply append (map (lambda (x) (list #f x)) r)))
|
||||||
|
r)])
|
||||||
|
(make-table
|
||||||
|
plain
|
||||||
|
(map (lambda (x)
|
||||||
|
(let ([@expr (if x (litchar/lines (car x)) "")]
|
||||||
|
[sexpr (if x
|
||||||
|
(racket:to-paragraph
|
||||||
|
((norm-spacing 0) (cadr x)))
|
||||||
|
"")]
|
||||||
|
[reads-as (if x reads-as "")])
|
||||||
|
(map as-flow (list spacer @expr reads-as sexpr))))
|
||||||
|
r))))))))
|
||||||
|
|
||||||
|
;; stuff for the scribble/text examples
|
||||||
|
|
||||||
|
(require racket/list (for-syntax racket/base racket/list))
|
||||||
|
|
||||||
|
(define max-textsample-width 45)
|
||||||
|
|
||||||
|
(define (textsample-verbatim-boxes line in-text out-text more)
|
||||||
|
(define (split str) (regexp-split #rx"\n" str))
|
||||||
|
(define strs1 (split in-text))
|
||||||
|
(define strs2 (split out-text))
|
||||||
|
(define strsm (map (compose split cdr) more))
|
||||||
|
(define (str->elts str)
|
||||||
|
(let ([spaces (regexp-match-positions #rx"(?:^| ) +" str)])
|
||||||
|
(if spaces
|
||||||
|
(list* (str->elts (substring str 0 (caar spaces)))
|
||||||
|
(smaller (hspace (- (cdar spaces) (caar spaces))))
|
||||||
|
(str->elts (substring str (cdar spaces))))
|
||||||
|
(list (smaller (make-element 'tt str))))))
|
||||||
|
(define (make-line str)
|
||||||
|
(list (as-flow (if (equal? str "")
|
||||||
|
(smaller (hspace 1))
|
||||||
|
(str->elts str)))))
|
||||||
|
(define (make-box strs [file #f])
|
||||||
|
(nested #:style 'code-inset
|
||||||
|
(let ([t (make-table plain (map make-line strs))])
|
||||||
|
(if file
|
||||||
|
(filebox file t)
|
||||||
|
t))))
|
||||||
|
(define filenames (map car more))
|
||||||
|
(define indent (let ([d (- max-textsample-width
|
||||||
|
(for*/fold ([m 0])
|
||||||
|
([s (in-list (cons strs1 strsm))]
|
||||||
|
[s (in-list s)])
|
||||||
|
(max m (string-length s))))])
|
||||||
|
(if (negative? d)
|
||||||
|
(error 'textsample-verbatim-boxes
|
||||||
|
"left box too wide for sample at line ~s" line)
|
||||||
|
(make-element 'tt (list (hspace d))))))
|
||||||
|
;; Note: the font-size property is reset for every table, so we need it
|
||||||
|
;; everywhere there's text, and they don't accumulate for nested tables
|
||||||
|
(values
|
||||||
|
(make-table
|
||||||
|
(make-style #f
|
||||||
|
(list (make-table-columns (list (make-style #f '(left top))))))
|
||||||
|
(cons (list (as-flow (make-box strs1)))
|
||||||
|
(map (lambda (file strs)
|
||||||
|
(list (as-flow (make-box strs file))))
|
||||||
|
filenames strsm)))
|
||||||
|
(make-box strs2)))
|
||||||
|
|
||||||
|
(define (textsample line in-text out-text more)
|
||||||
|
(define-values (box1 box2)
|
||||||
|
(textsample-verbatim-boxes line in-text out-text more))
|
||||||
|
(make-table
|
||||||
|
(make-style #f (list (make-table-columns (list (make-style #f '(left vcenter))
|
||||||
|
(make-style "Short" '(left vcenter))
|
||||||
|
(make-style #f '(left vcenter))))))
|
||||||
|
(list (map as-flow (list box1 (make-paragraph plain '(nbsp rarr nbsp)) box2)))))
|
||||||
|
|
||||||
|
(define-for-syntax tests-ids #f)
|
||||||
|
|
||||||
|
(provide initialize-tests)
|
||||||
|
(define-syntax (initialize-tests stx)
|
||||||
|
(set! tests-ids (map (lambda (x) (datum->syntax stx x stx))
|
||||||
|
'(tests add-to-tests)))
|
||||||
|
(with-syntax ([(tests add-to-tests) tests-ids])
|
||||||
|
#'(begin (provide tests)
|
||||||
|
(define-values (tests add-to-tests)
|
||||||
|
(let ([l '()])
|
||||||
|
(values (lambda () (reverse l))
|
||||||
|
(lambda (x) (set! l (cons x l)))))))))
|
||||||
|
|
||||||
|
(provide example)
|
||||||
|
(define-syntax (example stx)
|
||||||
|
(define sep-rx #px"^---[*]{3}---(?: +(.*))?$")
|
||||||
|
(define file-rx #rx"^[a-z0-9_.+-]+$")
|
||||||
|
(define-values (body hidden?)
|
||||||
|
(syntax-case stx ()
|
||||||
|
[(_ #:hidden x ...) (values #'(x ...) #t)]
|
||||||
|
[(_ x ...) (values #'(x ...) #f)]))
|
||||||
|
(let loop ([xs body] [text '(#f)] [texts '()])
|
||||||
|
(syntax-case xs ()
|
||||||
|
[("\n" sep "\n" . xs)
|
||||||
|
(and (string? (syntax-e #'sep)) (regexp-match? sep-rx (syntax-e #'sep)))
|
||||||
|
(let ([m (cond [(regexp-match sep-rx (syntax-e #'sep)) => cadr]
|
||||||
|
[else #f])])
|
||||||
|
(if (and m (not (regexp-match? file-rx m)))
|
||||||
|
(raise-syntax-error #f "bad filename specified" stx #'sep)
|
||||||
|
(loop #'xs
|
||||||
|
(list (and m (datum->syntax #'sep m #'sep #'sep)))
|
||||||
|
(cons (reverse text) texts))))]
|
||||||
|
[(x . xs) (loop #'xs (cons #'x text) texts)]
|
||||||
|
[() (let ([texts (reverse (cons (reverse text) texts))]
|
||||||
|
[line (syntax-line stx)])
|
||||||
|
(define-values (files i/o) (partition car texts))
|
||||||
|
(unless ((length i/o) . = . 2)
|
||||||
|
(raise-syntax-error
|
||||||
|
'example "need at least an input and an output block" stx))
|
||||||
|
(with-syntax ([line line]
|
||||||
|
[((in ...) (out ...)) (map cdr i/o)]
|
||||||
|
[((file text ...) ...) files]
|
||||||
|
[add-to-tests (cadr tests-ids)])
|
||||||
|
(quasisyntax/loc stx
|
||||||
|
(let* ([in-text (string-append in ...)]
|
||||||
|
[out-text (string-append out ...)]
|
||||||
|
[more (list (cons file (string-append text ...)) ...)])
|
||||||
|
(add-to-tests (list line in-text out-text more))
|
||||||
|
#,(if hidden? #'""
|
||||||
|
#'(textsample line in-text out-text more))))))]
|
||||||
|
[_ (raise-syntax-error #f "no separator found in example text")])))
|
||||||
|
|
||||||
|
(provide ltx ltxe ltxd)
|
||||||
|
(define (ltx s) (tt "\\" s)) ; command
|
||||||
|
(define (ltxe s) (tt s)) ; enviornment
|
||||||
|
(define (ltxd n s)
|
||||||
|
(make-element #f (cons (index (list s) (ltx s))
|
||||||
|
(for/list ([i (in-range n)]) (tt "{}")))))
|
||||||
|
|
||||||
|
;; Utility to render examples of scribble documentation forms
|
||||||
|
;; Note: it would be nice if this abstracted over the codeblock
|
||||||
|
;; that usually comes along with this too, but that's hard
|
||||||
|
;; because there's a read-time distinction between [...]
|
||||||
|
;; and |{...}|.
|
||||||
|
(define-syntax-rule (doc-render-examples e ...)
|
||||||
|
(nested "Renders like:\n"
|
||||||
|
(nested #:style 'inset (nested #:style 'inset e ...))))
|
||||||
|
|
Loading…
Reference in New Issue