much revised code, matching doc.txt and tests
svn: r6830
This commit is contained in:
parent
52409e3d15
commit
11feb6010e
|
@ -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")
|
||||
@foo{bar --reads-as--> (foo "bar" "\n"
|
||||
baz " " "baz" "\n"
|
||||
bbb} "bbb")
|
||||
|
||||
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{ bar --reads-as--> (foo " bar" "\n"
|
||||
baz " " "baz" "\n"
|
||||
bbb} " " "bbb")
|
||||
|
||||
@foo|{...}| --is-read-as--> (foo "...")
|
||||
@foo{bar --reads-as--> (foo "bar" "\n"
|
||||
baz "baz" "\n"
|
||||
bbb} "bbb")
|
||||
|
||||
@foo|{foo{{{bar}| --is-read-as--> (foo "foo{{{bar")
|
||||
@foo{ bar --reads-as--> (foo " bar" "\n"
|
||||
baz "baz" "\n"
|
||||
bbb} "bbb")
|
||||
|
||||
@foo|<{{foo{{{bar}}>| --is-read-as--> (foo "{foo{{{bar}")
|
||||
@foo{ bar --reads-as--> (foo " bar" "\n"
|
||||
baz "baz" "\n"
|
||||
bbb} " " "bbb")
|
||||
|
||||
* Concrete Syntax: quoting in body texts
|
||||
@text{Some text@footnote{And a
|
||||
footnote comment.}. More text.}
|
||||
--reads-as-->
|
||||
(text "Some text"
|
||||
(footnote "And a" "\n" "footnote comment.")
|
||||
". More text.")
|
||||
|
||||
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"
|
||||
baz " " "baz" "\n"
|
||||
bbb} "bbb")
|
||||
|
||||
@foo{ bar --is-read-as--> (foo " bar" "\n"
|
||||
baz " " "baz" "\n"
|
||||
bbb} " " "bbb")
|
||||
|
||||
@foo{bar --is-read-as--> (foo "bar" "\n"
|
||||
baz "baz" "\n"
|
||||
bbb} "bbb")
|
||||
|
||||
@foo{ bar --is-read-as--> (foo " bar" "\n"
|
||||
baz "baz" "\n"
|
||||
bbb} "bbb")
|
||||
|
||||
@foo{ bar --is-read-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.
|
||||
|
||||
[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
|
||||
}
|
||||
|
|
|
@ -129,25 +129,6 @@
|
|||
(unless (eof-object? ch)
|
||||
(when (whitespace? ch rt) (read-char port) (loop)))))))))
|
||||
|
||||
;; Wrappers for placeholders, to keep source information for them. (MzScheme
|
||||
;; provides nothing for them -- there's not even a predicate. Hopefully, if
|
||||
;; something is added it will use the same name, so there's a compiler error
|
||||
;; here and this code is adapted.)
|
||||
;; (Note: used to wrap special comment values too.)
|
||||
(define-struct placeholder (p loc))
|
||||
(define (syntax/placeholder-line sp)
|
||||
(if (placeholder? sp) (cadr (placeholder-loc sp)) (syntax-line sp)))
|
||||
(define (syntax/placeholder-column sp)
|
||||
(if (placeholder? sp) (caddr (placeholder-loc sp)) (syntax-column sp)))
|
||||
(define (syntax/placeholder-strip sp)
|
||||
(if (placeholder? sp) (placeholder-p sp) sp))
|
||||
(define (datum->syntax-object/placeholder sp d)
|
||||
(if (placeholder? sp)
|
||||
;; using the syntax for lexical context is not possible for placeholders,
|
||||
;; but we don't need it since we're a reader
|
||||
(datum->syntax-object #f d (placeholder-loc sp))
|
||||
(datum->syntax-object sp d sp)))
|
||||
|
||||
;; make n spaces, cached for n
|
||||
(define make-spaces
|
||||
(let ([t (make-hash-table)])
|
||||
|
@ -157,6 +138,17 @@
|
|||
(let ([s (make-string n #\space)])
|
||||
(hash-table-put! t n s) s))))))
|
||||
|
||||
(define (bytes-width bs start)
|
||||
(let ([len (bytes-length bs)])
|
||||
(if (regexp-match? #rx"^ *$" bs start)
|
||||
(- (bytes-length bs) start)
|
||||
(let loop ([i start] [w 0])
|
||||
(if (= i len)
|
||||
w
|
||||
(loop (add1 i) (+ w (if (eq? 9 (bytes-ref bs i))
|
||||
(- 8 (modulo w 8))
|
||||
1))))))))
|
||||
|
||||
;; a unique eol string
|
||||
(define eol-token "\n")
|
||||
(define (eol-syntax? x) (and (syntax? x) (eq? eol-token (syntax-e x))))
|
||||
|
@ -191,7 +183,6 @@
|
|||
(define (read-stx/rt rt) (read-syntax/recursive source-name inp #f rt))
|
||||
;; use this to avoid placeholders so we have source location information
|
||||
(define (read-stx*)
|
||||
;; the following should not return placeholders, but it does
|
||||
;; (read-syntax/recursive source-name inp #f (current-readtable) #f)
|
||||
(read-syntax source-name inp))
|
||||
|
||||
|
@ -201,127 +192,71 @@
|
|||
(define (*skip rx) (*regexp-match1 rx inp))
|
||||
(define (*peek rx) (*regexp-match-peek-positions rx inp))
|
||||
|
||||
(define (cur-pos)
|
||||
(let-values ([(line col pos) (port-next-location inp)])
|
||||
pos))
|
||||
|
||||
(define (span-from start)
|
||||
(and start (- (cur-pos) start)))
|
||||
(and start (let-values ([(line col pos) (port-next-location inp)])
|
||||
(- pos start))))
|
||||
|
||||
(define (read-delimited-list begin-re end-re end-ch tweak-locations)
|
||||
;; when `tweak-locations' is not #f, it should be (src line col pos) for
|
||||
;; the whole thing -- and we need to adjust the first item so it appears
|
||||
;; from its beginning, and the last so it appears to go to its end (used
|
||||
;; to make escape sequences not have bogus indentation added)
|
||||
(define (read-delimited-list begin-re end-re end-ch)
|
||||
(and (*skip begin-re)
|
||||
(let ([reader (if tweak-locations
|
||||
;; should always be `read-syntax/recursive', but
|
||||
;; then we don't get location information
|
||||
read-stx* read-stx)])
|
||||
(let loop ([r '()])
|
||||
(skip-whitespace inp)
|
||||
(if (*skip end-re)
|
||||
(cond [(null? r) r]
|
||||
[(not tweak-locations) (reverse! r)]
|
||||
[(null? (cdr r))
|
||||
;; make the single syntax span the whole thing
|
||||
(list (datum->syntax-object (car r) (syntax-e (car r))
|
||||
`(,@tweak-locations
|
||||
,(span-from (cadddr tweak-locations)))))]
|
||||
[else
|
||||
(let* (;; make the last one span to the end
|
||||
[last (car r)]
|
||||
[last (datum->syntax-object last (syntax-e last)
|
||||
(list (syntax-source last)
|
||||
(syntax-line last)
|
||||
(syntax-column last)
|
||||
(syntax-position last)
|
||||
(span-from
|
||||
(syntax-position last))))]
|
||||
[r (reverse! (cons last (cdr r)))]
|
||||
;; make the first go from the beginning
|
||||
[fst (car r)]
|
||||
[fst (datum->syntax-object fst (syntax-e fst)
|
||||
`(,@tweak-locations
|
||||
,(let ([tw-pos (cadddr tweak-locations)]
|
||||
[1pos (syntax-position fst)]
|
||||
[1span (syntax-span fst)])
|
||||
(and tw-pos 1pos 1span
|
||||
(+ (- 1pos tw-pos) 1span)))))])
|
||||
(cons fst (cdr r)))])
|
||||
(let ([x (reader)])
|
||||
(if (eof-object? x)
|
||||
(read-error 'eof "expected a '~a'" end-ch)
|
||||
(loop (if (special-comment? x) r (cons x r))))))))))
|
||||
(let loop ([r '()])
|
||||
(skip-whitespace inp)
|
||||
(if (*skip end-re)
|
||||
(reverse! r)
|
||||
(let ([x (read-stx)])
|
||||
(if (eof-object? x)
|
||||
(read-error 'eof "expected a '~a'" end-ch)
|
||||
(loop (if (special-comment? x) r (cons x r)))))))))
|
||||
|
||||
;; adds indentation (as new syntaxes, not merged); if the first line was
|
||||
;; not empty, then it is treated specially. called with at least two items
|
||||
;; (see below).
|
||||
(define (add-indents stxs 1st-eol?)
|
||||
#; ; the reader always turns on line counting
|
||||
(unless (andmap (lambda (x)
|
||||
(and (or (syntax? x) (placeholder? x))
|
||||
(syntax/placeholder-column x)
|
||||
(syntax/placeholder-line x)))
|
||||
stxs)
|
||||
(read-error "internal error [add-indents] ~s" stxs))
|
||||
(let* ([mincol
|
||||
(let loop ([min #f] [stxs (if 1st-eol? stxs (cdr stxs))])
|
||||
(if (null? stxs)
|
||||
(or min (error "internal error [add-indents]"))
|
||||
(loop (if (eol-syntax? (car stxs))
|
||||
min
|
||||
(let ([c (syntax/placeholder-column (car stxs))])
|
||||
(if (or (not min) (< c min)) c min)))
|
||||
(cdr stxs))))]
|
||||
[mincol (if 1st-eol?
|
||||
mincol
|
||||
(min mincol (syntax/placeholder-column (car stxs))))])
|
||||
(let loop (;; no indentation for text on the first '{' line
|
||||
[newline? 1st-eol?] [curline -1] [stxs stxs] [r '()])
|
||||
(if (null? stxs)
|
||||
(reverse! r)
|
||||
(let* ([stx (car stxs)]
|
||||
[line (syntax/placeholder-line stx)])
|
||||
(loop (eol-syntax? stx) line (cdr stxs)
|
||||
(let* ([stxcol (syntax/placeholder-column stx)]
|
||||
[stx* (syntax/placeholder-strip stx)]
|
||||
;; add spaces
|
||||
[r (if (and newline?
|
||||
(< curline line)
|
||||
(< mincol stxcol))
|
||||
(cons (syntax-property
|
||||
(datum->syntax-object/placeholder stx
|
||||
(make-spaces (- stxcol mincol)))
|
||||
'scribble 'indentation)
|
||||
r)
|
||||
r)]
|
||||
;; remove special-comments
|
||||
[r (if (special-comment? stx*) r (cons stx* r))])
|
||||
r)))))))
|
||||
|
||||
;; gets an accumulated (reversed) list of syntaxes, sorts things out
|
||||
;; (remove prefix and suffix newlines, adds indentation if needed)
|
||||
(define (done-lines rlines)
|
||||
(cond
|
||||
[(andmap eol-syntax? rlines)
|
||||
;; nothing to do (includes null, so the code below can assume a pair)
|
||||
(reverse! rlines)]
|
||||
[start-inside?
|
||||
;; no newlines removed
|
||||
(add-indents (reverse! rlines) #t)] ; don't ignore the 1st line
|
||||
[else
|
||||
;; strip off leading and trailing newlines (must have at least one
|
||||
;; non-newline item)
|
||||
(let* ([rlines (if (eol-syntax? (car rlines)) (cdr rlines) rlines)]
|
||||
[lines (reverse! rlines)]
|
||||
[1st-eol? (eol-syntax? (car lines))]
|
||||
[lines (if 1st-eol? (cdr lines) lines)])
|
||||
(if (null? (cdr lines)) ; common case: one string
|
||||
(let ([line (syntax/placeholder-strip (car lines))])
|
||||
;; note: we can get comment values
|
||||
(if (special-comment? line) '() (list line)))
|
||||
(add-indents lines 1st-eol?)))]))
|
||||
;; gets an accumulated (reversed) list of syntaxes and column markers, and
|
||||
;; sorts things out (remove prefix and suffix newlines, adds indentation if
|
||||
;; needed)
|
||||
(define (done-items xs)
|
||||
;; a column marker is either a non-negative integer N (saying the the
|
||||
;; following code came from at column N), or a negative integer -N
|
||||
;; (saying that the following code came from column N but no need to add
|
||||
;; indentation at this point because it is at the openning of a {...});
|
||||
;; `get-lines*' is careful not to include column markers before a newline
|
||||
;; or the end of the text, and a -N marker can only come from the
|
||||
;; beginning of the text (and it's never there if the text began with a
|
||||
;; newline)
|
||||
(if (andmap eol-syntax? xs)
|
||||
;; nothing to do
|
||||
(reverse! xs)
|
||||
(let ([mincol (let loop ([xs xs] [m #f])
|
||||
(if (null? xs)
|
||||
m
|
||||
(let ([x (car xs)])
|
||||
(loop (cdr xs)
|
||||
(if (integer? x)
|
||||
(let ([x (abs x)]) (if (and m (< m x)) m x))
|
||||
m)))))])
|
||||
(let loop ([xs (if (and (not start-inside?) (eol-syntax? (car xs)))
|
||||
(cdr xs) ; trim last eol
|
||||
xs)]
|
||||
[r '()])
|
||||
(if (or (null? xs)
|
||||
(and (not start-inside?)
|
||||
;; trim first eol
|
||||
(null? (cdr xs)) (eol-syntax? (car xs))))
|
||||
r
|
||||
(loop
|
||||
(cdr xs)
|
||||
(let ([x (car xs)])
|
||||
(cond [(integer? x)
|
||||
(if (or (< x 0) (= x mincol))
|
||||
r ; no indentation marker, or zero indentation
|
||||
(let ([eol (cadr xs)]
|
||||
[spaces (make-spaces (- x mincol))])
|
||||
;; markers always follow end-of-lines
|
||||
(unless (eol-syntax? eol)
|
||||
(error 'reader "internal error [done-items]"))
|
||||
(cons (syntax-property
|
||||
(datum->syntax-object eol spaces eol)
|
||||
'scribble 'indentation)
|
||||
r)))]
|
||||
;; can have special comment values from "@||"
|
||||
[(special-comment? x) r]
|
||||
[else (cons x r)]))))))))
|
||||
|
||||
;; cons stx (new syntax) to the list of stxs, merging it if both are
|
||||
;; strings, except for newline markers
|
||||
|
@ -342,84 +277,87 @@
|
|||
(cdr stxs))
|
||||
(cons stx stxs))))
|
||||
|
||||
;; helper for `get-lines*' drop a column marker if the previous item was
|
||||
;; also a newline (or the beginning)
|
||||
(define (maybe-drop-marker r)
|
||||
(if (and (pair? r) (integer? (car r))
|
||||
(or (null? (cdr r)) (eol-syntax? (cadr r))))
|
||||
(cdr r)
|
||||
r))
|
||||
|
||||
(define (get-lines* re:begin re:end re:cmd-pfx re:item end-token)
|
||||
;; re:begin, re:end, end-token can be false if start-inside? is #t
|
||||
(let loop ([lvl 0] [r '()])
|
||||
(let-values ([(line col pos) (port-next-location inp)])
|
||||
(define (make-stx sexpr)
|
||||
(datum->syntax-object #f
|
||||
(if (bytes? sexpr) (bytes->string/utf-8 sexpr) sexpr)
|
||||
(list source-name line col pos (span-from pos))))
|
||||
(cond
|
||||
[(and re:begin (*match1 re:begin))
|
||||
=> (lambda (m) (loop (add1 lvl) (maybe-merge (make-stx m) r)))]
|
||||
[(and re:end (*match1 re:end))
|
||||
=> (lambda (m)
|
||||
(if (and (zero? lvl) (not start-inside?))
|
||||
(done-lines r)
|
||||
(loop (sub1 lvl) (maybe-merge (make-stx m) r))))]
|
||||
[(*match1 re:end-of-line)
|
||||
=> (lambda (m)
|
||||
(loop lvl (cons ; no merge needed
|
||||
(syntax-property (make-stx eol-token)
|
||||
'scribble `(newline ,m))
|
||||
r)))]
|
||||
[(if re:cmd-pfx
|
||||
(and (*skip re:cmd-pfx) (*peek re:command))
|
||||
(*peek re:command))
|
||||
;; read the next value, include comment objs, keep source location
|
||||
;; manually (see above)
|
||||
=> (lambda (m)
|
||||
(let ([x (cond
|
||||
[(cadr m)
|
||||
;; the command is a string escape, use
|
||||
;; `read-stx*' to not get a placeholder, so we
|
||||
;; can merge the string to others, and adjust
|
||||
;; source location to avoid bogus indentation
|
||||
(make-stx (syntax-e (read-stx*)))]
|
||||
[(caddr m)
|
||||
;; it's an expression escape, get multiple
|
||||
;; expressions and put them all here
|
||||
(read-bytes (caaddr m) inp)
|
||||
(get-escape-expr #f line col pos)]
|
||||
[else (read-stx)])]) ; otherwise: a plain sub-read
|
||||
(loop
|
||||
lvl
|
||||
(cond
|
||||
[(eof-object? x) (read-error 'eof "missing command")]
|
||||
[(syntax? x) (maybe-merge x r)]
|
||||
;; escaped expressions (not empty: @||)
|
||||
[(pair? x) (append! (reverse x) r)]
|
||||
;; a comment in the middle of a line disappears so
|
||||
;; strings next to it are merged
|
||||
[(and (special-comment? x)
|
||||
(not (and (pair? r) (eol-syntax? (car r)))))
|
||||
r]
|
||||
;; otherwise it's a either null (@||) a comment (at the
|
||||
;; beginning of a line) or a placeholder: wrap to get
|
||||
;; source info for proper indentation; @|| is turned to
|
||||
;; a comment, which can be used to separate strings, or
|
||||
;; to make spaces meaningful
|
||||
[else (let ([x (if (null? x)
|
||||
(make-special-comment #f)
|
||||
x)])
|
||||
(cons (make-placeholder x ; no merge
|
||||
(list source-name line col pos
|
||||
(span-from pos)))
|
||||
r))]))))]
|
||||
;; must be last, since it will always succeed with 1 char
|
||||
[(*peek re:item) ; don't read: regexp grabs the following text
|
||||
=> (lambda (m)
|
||||
(loop lvl (maybe-merge (make-stx (read-bytes (cdadr m) inp))
|
||||
r)))]
|
||||
[(*peek #rx#"^$")
|
||||
(if end-token
|
||||
(read-error 'eof "missing closing `~a'~a" end-token
|
||||
(if (and line-num col-num)
|
||||
(format " for command at ~a:~a" line-num col-num)
|
||||
""))
|
||||
(done-lines r))]
|
||||
[else (read-error "internal error [get-lines*]")]))))
|
||||
;; re:begin, re:end, end-token can be false if start-inside? is #t;
|
||||
;; re:cmd-pfx is a regexp when we do sub-@-reads only after a prefix
|
||||
(let loop ([lvl 0]
|
||||
[r (let-values ([(l c p) (port-next-location inp)])
|
||||
;; marker for the beginning of the text
|
||||
(if c (list (- c)) '()))])
|
||||
;; this loop collects lines etc for the body, and also puts in column
|
||||
;; markers (integers) after newlines -- the result is handed off to
|
||||
;; `done-items' to finish the job
|
||||
(define make-stx
|
||||
(let-values ([(line col pos) (port-next-location inp)])
|
||||
(lambda (sexpr)
|
||||
(datum->syntax-object #f
|
||||
(if (bytes? sexpr) (bytes->string/utf-8 sexpr) sexpr)
|
||||
(list source-name line col pos (span-from pos))))))
|
||||
(cond
|
||||
[(and re:begin (*match1 re:begin))
|
||||
=> (lambda (m) (loop (add1 lvl) (maybe-merge (make-stx m) r)))]
|
||||
[(and re:end (*match1 re:end))
|
||||
=> (lambda (m)
|
||||
(if (and (zero? lvl) (not start-inside?))
|
||||
;; drop a marker if it's after a last eol item
|
||||
(done-items (maybe-drop-marker r))
|
||||
(loop (sub1 lvl) (maybe-merge (make-stx m) r))))]
|
||||
[(*match1 re:end-of-line)
|
||||
=> (lambda (m)
|
||||
(let ([n (car (regexp-match-positions #rx#"\n" m))])
|
||||
(loop lvl (list* ; no merge needed
|
||||
(bytes-width m (cdr n))
|
||||
(syntax-property
|
||||
(make-stx eol-token)
|
||||
'scribble `(newline ,(bytes->string/utf-8 m)))
|
||||
(maybe-drop-marker r)))))]
|
||||
[(if re:cmd-pfx
|
||||
(and (*skip re:cmd-pfx) (*peek re:command))
|
||||
(*peek re:command))
|
||||
;; read the next value
|
||||
=> (lambda (m)
|
||||
(let ([x (cond
|
||||
[(cadr m)
|
||||
;; the command is a string escape, use `read-stx*'
|
||||
;; to not get a placeholder, so we can merge the
|
||||
;; string to others, and adjust source location to
|
||||
;; avoid bogus indentation
|
||||
(read-stx*)]
|
||||
[(caddr m)
|
||||
;; it's an expression escape, get multiple
|
||||
;; expressions and put them all here
|
||||
(read-bytes (caaddr m) inp)
|
||||
(get-escape-expr #f)]
|
||||
[else (read-stx)])]) ; otherwise: a plain sub-read
|
||||
(loop lvl (cond [(eof-object? x)
|
||||
(read-error 'eof "missing command")]
|
||||
;; throw away comments
|
||||
[(special-comment? x) r]
|
||||
;; escaped expressions: no merge
|
||||
[(pair? x) (append! (reverse x) r)]
|
||||
[(null? x) (cons (make-special-comment #f) r)]
|
||||
[else (maybe-merge x r)]))))]
|
||||
;; must be last, since it will always succeed with 1 char
|
||||
[(*peek re:item) ; don't read: regexp grabs the following text
|
||||
=> (lambda (m)
|
||||
(loop lvl
|
||||
(maybe-merge (make-stx (read-bytes (cdadr m) inp)) r)))]
|
||||
[(*peek #rx#"^$")
|
||||
(if end-token
|
||||
(read-error 'eof "missing closing `~a'~a" end-token
|
||||
(if (and line-num col-num)
|
||||
(format " for command at ~a:~a" line-num col-num)
|
||||
""))
|
||||
(done-items r))]
|
||||
[else (read-error "internal error [get-lines*]")])))
|
||||
|
||||
(define (get-lines)
|
||||
(cond [(*skip re:lines-begin) (get-lines* re:lines-begin re:lines-end #f
|
||||
|
@ -438,22 +376,25 @@
|
|||
[else #f]))
|
||||
|
||||
(define (get-attrs)
|
||||
(read-delimited-list re:attrs-begin re:attrs-end ch:attrs-end #f))
|
||||
(read-delimited-list re:attrs-begin re:attrs-end ch:attrs-end))
|
||||
|
||||
(define (get-escape-expr single? line col pos)
|
||||
(define (get-escape-expr single?)
|
||||
;; single? means expect just one expression (or none, which is returned
|
||||
;; as a special-comment)
|
||||
(let ([xs (parameterize ([current-readtable command-readtable])
|
||||
;; tweak source information to avoid bad indentation
|
||||
(read-delimited-list
|
||||
re:expr-escape re:expr-escape ch:expr-escape
|
||||
(list source-name line col pos)))])
|
||||
(cond [(not xs) xs]
|
||||
[(not single?) xs]
|
||||
[(null? xs) (make-special-comment #f)]
|
||||
[(null? (cdr xs)) (car xs)]
|
||||
[else (read-error* line col pos (span-from pos)
|
||||
"too many escape expressions")])))
|
||||
(let ([get (lambda ()
|
||||
(parameterize ([current-readtable command-readtable])
|
||||
;; tweak source information to avoid bad indentation
|
||||
(read-delimited-list re:expr-escape re:expr-escape
|
||||
ch:expr-escape)))])
|
||||
(if single?
|
||||
(let*-values ([(line col pos) (port-next-location inp)]
|
||||
[(xs) (get)])
|
||||
(cond [(not xs) xs]
|
||||
[(null? xs) (make-special-comment #f)]
|
||||
[(null? (cdr xs)) (car xs)]
|
||||
[else (read-error* line col pos (span-from pos)
|
||||
"too many escape expressions")]))
|
||||
(get))))
|
||||
|
||||
;; called only when we must see a command in the input
|
||||
(define (get-command)
|
||||
|
@ -504,8 +445,7 @@
|
|||
;; simple expression escape, same for get-attrs
|
||||
[(get-lines) => (lambda (lines) (values #f #f lines))]
|
||||
[(get-attrs) => (lambda (attrs) (values #f attrs (get-lines)))]
|
||||
[(get-escape-expr #t line-num col-num position)
|
||||
=> (lambda (expr) (values expr #f #f))]
|
||||
[(get-escape-expr #t) => (lambda (expr) (values expr #f #f))]
|
||||
[else (values (get-command) (get-attrs) (get-lines))])]
|
||||
[(stx) (and (or attrs lines)
|
||||
(append (or attrs '()) (or lines '())))]
|
||||
|
@ -559,18 +499,15 @@
|
|||
(if (eq? src default-src) (object-name port) src))
|
||||
|
||||
(define/kw (*read #:optional [inp (current-input-port)])
|
||||
(port-count-lines! inp)
|
||||
(parameterize ([current-readtable at-readtable])
|
||||
(read inp)))
|
||||
|
||||
(define/kw (*read-syntax #:optional [src default-src]
|
||||
[inp (current-input-port)])
|
||||
(port-count-lines! inp)
|
||||
(parameterize ([current-readtable at-readtable])
|
||||
(read-syntax (src-name src inp) inp)))
|
||||
|
||||
(define/kw (read-inside #:optional [inp (current-input-port)])
|
||||
(port-count-lines! inp)
|
||||
(let-values ([(line col pos) (port-next-location inp)])
|
||||
(parameterize ([current-readtable at-readtable])
|
||||
(syntax-object->datum
|
||||
|
@ -578,7 +515,6 @@
|
|||
|
||||
(define/kw (read-inside-syntax #:optional [src default-src]
|
||||
[inp (current-input-port)])
|
||||
(port-count-lines! inp)
|
||||
(let-values ([(line col pos) (port-next-location inp)])
|
||||
(parameterize ([current-readtable at-readtable])
|
||||
((dispatcher #t) #f inp (src-name src inp) line col pos))))
|
||||
|
|
|
@ -21,9 +21,21 @@ exec mzscheme -r "$0" "$@"
|
|||
[@foo[]{}
|
||||
(foo)]
|
||||
|
||||
[foo@
|
||||
,(string->symbol "foo@")]
|
||||
|
||||
[fo@o
|
||||
,(string->symbol "fo@o")]
|
||||
|
||||
[\@foo
|
||||
,(string->symbol "@foo")]
|
||||
|
||||
[|@foo|
|
||||
,(string->symbol "@foo")]
|
||||
|
||||
[(define |@foo| '\@bar@baz)
|
||||
,(read-from-string "(define @foo '@bar@baz)")]
|
||||
|
||||
[@foo{bar}
|
||||
(foo "bar")]
|
||||
|
||||
|
@ -31,6 +43,31 @@ exec mzscheme -r "$0" "$@"
|
|||
blah}
|
||||
(foo "bar baz" "\n" "blah")]
|
||||
|
||||
[@foo{bar @baz[3]
|
||||
blah}
|
||||
(foo "bar " (baz 3) "\n" "blah")]
|
||||
|
||||
[@foo{bar @baz{3}
|
||||
blah}
|
||||
(foo "bar " (baz "3") "\n" "blah")]
|
||||
|
||||
[@foo{bar @baz[2 3]{4 5}
|
||||
blah}
|
||||
(foo "bar " (baz 2 3 "4 5") "\n" "blah")]
|
||||
|
||||
[@foo{bar @baz[2 3] {4 5}}
|
||||
(foo "bar " (baz 2 3) " {4 5}")]
|
||||
|
||||
[,(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*."]
|
||||
|
||||
['@foo{bar}
|
||||
'(foo "bar")]
|
||||
|
||||
|
@ -56,6 +93,9 @@ exec mzscheme -r "$0" "$@"
|
|||
[@(lambda (x) x){blah}
|
||||
((lambda (x) x) "blah")]
|
||||
|
||||
[@`(unquote foo){blah}
|
||||
`(,foo "blah")]
|
||||
|
||||
[@{foo bar
|
||||
baz}
|
||||
("foo bar" "\n" "baz")]
|
||||
|
@ -68,10 +108,6 @@ exec mzscheme -r "$0" "$@"
|
|||
3 4)
|
||||
(1 2 3 4)]
|
||||
|
||||
[(1 2@; comment, 2 touches 3 but there is a comment syntax between them
|
||||
3 4)
|
||||
(1 2 3 4)]
|
||||
|
||||
[@foo{bar @; comment, note the extra space
|
||||
baz}
|
||||
(foo "bar baz")]
|
||||
|
@ -80,33 +116,64 @@ exec mzscheme -r "$0" "$@"
|
|||
baz}
|
||||
(foo "barbaz")]
|
||||
|
||||
#; ;!!!
|
||||
[@foo{bar @; comment, with space and newline
|
||||
|
||||
baz}
|
||||
(foo "bar " "\n" "baz")]
|
||||
|
||||
[@foo{x @y z}
|
||||
(foo "x " y " z")]
|
||||
|
||||
[@foo{x @(* y 2) z}
|
||||
(foo "x " (* y 2) " z")]
|
||||
|
||||
[@{@foo bar}
|
||||
(foo " bar")]
|
||||
|
||||
[@@foo{bar}{baz}
|
||||
((foo "bar") "baz")]
|
||||
|
||||
[(define |@foo| '\@bar)
|
||||
,(read-from-string "(define @foo '@bar)")]
|
||||
[@foo[1 (* 2 3)]{bar}
|
||||
(foo 1 (* 2 3) "bar")]
|
||||
|
||||
[@foo{
|
||||
bar
|
||||
}
|
||||
@foo{bar}]
|
||||
[@foo[@bar{...}]{blah}
|
||||
(foo (bar "...") "blah")]
|
||||
|
||||
[@foo[bar]
|
||||
(foo bar)]
|
||||
|
||||
[@foo{bar @f[x] baz}
|
||||
(foo "bar " (f x) " baz")]
|
||||
|
||||
[@foo[]{bar}
|
||||
(foo "bar")]
|
||||
|
||||
[@foo[]
|
||||
(foo)]
|
||||
|
||||
[@foo
|
||||
foo]
|
||||
|
||||
[@foo{}
|
||||
(foo)]
|
||||
|
||||
[@foo[#:style 'big]{bar}
|
||||
(foo #:style 'big "bar")]
|
||||
|
||||
[@foo{f{o}o}
|
||||
(foo "f{o}o")]
|
||||
|
||||
[@foo{{{}}{}}
|
||||
(foo "{{}}{}")]
|
||||
|
||||
[@foo{bar}
|
||||
(foo "bar")]
|
||||
|
||||
[@foo{ bar }
|
||||
(foo " bar ")]
|
||||
|
||||
[@foo{ bar
|
||||
}
|
||||
(foo " bar")]
|
||||
|
||||
[@foo{
|
||||
bar }
|
||||
(foo "bar ")]
|
||||
[@foo[1]{ bar }
|
||||
(foo 1 " bar ")]
|
||||
|
||||
[@foo{a @bar{b} c}
|
||||
(foo "a " (bar "b") " c")]
|
||||
|
@ -117,6 +184,238 @@ exec mzscheme -r "$0" "$@"
|
|||
[@foo{a @(bar 2) c}
|
||||
(foo "a " (bar 2) " c")]
|
||||
|
||||
[@foo{This @"}" is a closing brace}
|
||||
(foo "This } is a closing brace")]
|
||||
|
||||
[@foo{The command prefix is @"@".}
|
||||
(foo "The command prefix is @.")]
|
||||
|
||||
[@foo{@"@foo{bar}" reads as (foo "bar")}
|
||||
(foo "@foo{bar} reads as (foo \"bar\")")]
|
||||
|
||||
[@foo|{...}|
|
||||
(foo "...")]
|
||||
|
||||
[@foo|{close with "}", open with "{"}|
|
||||
(foo "close with \"}\", open with \"{\"")]
|
||||
|
||||
[@foo|{Nesting |{is}| ok}|
|
||||
(foo "Nesting |{is}| ok")]
|
||||
|
||||
[@foo|{Maze
|
||||
|@bar{is}
|
||||
Life!}|
|
||||
(foo "Maze" "\n" (bar "is") "\n" "Life!")]
|
||||
|
||||
[@foo|{Works for |@bar|{subforms}| too}|
|
||||
(foo "Works for " (bar "subforms") " too")]
|
||||
|
||||
[@foo|<<<{Some @x{more} |@{text}|.}>>>|
|
||||
(foo "Some @x{more} |@{text}|.")]
|
||||
|
||||
[@foo|!!{Blah |!!@bold{blah}...}!!|
|
||||
(foo "Blah " (bold "blah") "...")]
|
||||
|
||||
[@foo{foo@bar.}
|
||||
(foo "foo" bar.)]
|
||||
|
||||
[@foo{foo@|bar|.}
|
||||
(foo "foo" bar ".")]
|
||||
|
||||
[@foo{foo@3.}
|
||||
(foo "foo" 3.0)]
|
||||
|
||||
[@foo{foo@|3|.}
|
||||
(foo "foo" 3 ".")]
|
||||
|
||||
[@foo{foo@|(f 1)|{bar}}
|
||||
(foo "foo" (f 1) "{bar}")]
|
||||
|
||||
[@foo{foo@|bar|[1]{baz}}
|
||||
(foo "foo" bar "[1]{baz}")]
|
||||
|
||||
[@foo{x@"y"z}
|
||||
(foo "xyz")]
|
||||
|
||||
[@foo{x@|"y"|z}
|
||||
(foo "x" "y" "z")]
|
||||
|
||||
[@foo{x@|1 (+ 2 3) 4|y}
|
||||
(foo "x" 1 (+ 2 3) 4 "y")]
|
||||
|
||||
[@foo{x@|1 (+ 2 3) 4|y}
|
||||
(foo "x" 1 (+ 2 3) 4 "y")]
|
||||
|
||||
[@foo{x@|*
|
||||
*|y}
|
||||
(foo "x" * * "y")]
|
||||
|
||||
[@foo{Alice@||Bob@|
|
||||
|Carol}
|
||||
(foo "Alice" "Bob" "Carol")]
|
||||
|
||||
[@|{blah}|
|
||||
("blah")]
|
||||
|
||||
[@foo{First line@;{there is still a
|
||||
newline at this point;}
|
||||
Second line}
|
||||
(foo "First line" "\n" "Second line")]
|
||||
|
||||
[@foo{This is @;
|
||||
a pretty long @;
|
||||
single string-@;
|
||||
argument.}
|
||||
(foo "This is a pretty long single string-argument.")]
|
||||
|
||||
[@foo{bar}
|
||||
(foo "bar")]
|
||||
|
||||
[@foo{ bar }
|
||||
(foo " bar ")]
|
||||
|
||||
|
||||
[@foo{ bar
|
||||
baz }
|
||||
(foo " bar" "\n" "baz ")]
|
||||
|
||||
[@foo{bar
|
||||
}
|
||||
(foo "bar")]
|
||||
|
||||
[@foo{
|
||||
bar
|
||||
}
|
||||
(foo "bar")]
|
||||
|
||||
[@foo{
|
||||
|
||||
bar
|
||||
|
||||
}
|
||||
(foo "\n" "bar" "\n")]
|
||||
|
||||
[@foo{
|
||||
bar
|
||||
|
||||
baz
|
||||
}
|
||||
(foo "bar" "\n" "\n" "baz")]
|
||||
|
||||
[@foo{
|
||||
}
|
||||
(foo "\n")]
|
||||
|
||||
[@foo{
|
||||
|
||||
}
|
||||
(foo "\n" "\n")]
|
||||
|
||||
[@foo{
|
||||
|
||||
|
||||
}
|
||||
(foo "\n" "\n" "\n")]
|
||||
|
||||
[,(let ([nl (car @'{
|
||||
})]
|
||||
[o (open-output-string)])
|
||||
(for-each (lambda (x) (display (if (eq? x nl) "\n... " x) o))
|
||||
@`{foo
|
||||
@,@(list "bar" "\n" "baz")
|
||||
blah})
|
||||
(newline o)
|
||||
(get-output-string o))
|
||||
"foo\n... bar\nbaz\n... blah\n"]
|
||||
|
||||
[@foo{
|
||||
bar
|
||||
baz
|
||||
blah
|
||||
}
|
||||
(foo "bar" "\n" "baz" "\n" "blah")]
|
||||
|
||||
[@foo{
|
||||
begin
|
||||
x++;
|
||||
end}
|
||||
(foo "begin" "\n" " " "x++;" "\n" "end")]
|
||||
|
||||
[@foo{
|
||||
a
|
||||
b
|
||||
c}
|
||||
(foo " " "a" "\n" " " "b" "\n" "c")]
|
||||
|
||||
[@foo{bar
|
||||
baz
|
||||
bbb}
|
||||
(foo "bar" "\n" " " "baz" "\n" "bbb")]
|
||||
|
||||
[@foo{ bar
|
||||
baz
|
||||
bbb}
|
||||
(foo " bar" "\n" " " "baz" "\n" " " "bbb")]
|
||||
|
||||
[@foo{bar
|
||||
baz
|
||||
bbb}
|
||||
(foo "bar" "\n" "baz" "\n" "bbb")]
|
||||
|
||||
[@foo{ bar
|
||||
baz
|
||||
bbb}
|
||||
(foo " bar" "\n" "baz" "\n" "bbb")]
|
||||
|
||||
[@foo{ bar
|
||||
baz
|
||||
bbb}
|
||||
(foo " bar" "\n" "baz" "\n" " " "bbb")]
|
||||
|
||||
[@text{Some text@footnote{And a
|
||||
footnote comment.}. More text.}
|
||||
(text "Some text"
|
||||
(footnote "And a" "\n" "footnote comment.")
|
||||
". More text.")]
|
||||
|
||||
[@code{
|
||||
begin
|
||||
i = 1, r = 1
|
||||
@bold{while i < n do
|
||||
r *= i++
|
||||
done}
|
||||
end
|
||||
}
|
||||
(code "begin" "\n"
|
||||
" " "i = 1, r = 1" "\n"
|
||||
" " (bold "while i < n do" "\n"
|
||||
" " "r *= i++" "\n"
|
||||
"done") "\n"
|
||||
"end")]
|
||||
|
||||
[@foo{
|
||||
@|| bar @||
|
||||
@|| baz}
|
||||
(foo " bar " "\n" " baz")]
|
||||
|
||||
[@foo{bar
|
||||
@|baz| bbb
|
||||
@|x1 x2| x3 x4
|
||||
@|| waaaah
|
||||
}
|
||||
(foo "bar" "\n" baz " bbb" "\n" x1 x2 " x3 x4" "\n" " waaaah")]
|
||||
|
||||
[@foo{x1
|
||||
x2@;
|
||||
y2
|
||||
x3@;{
|
||||
;}y3
|
||||
x4@|
|
||||
|y4
|
||||
x5}
|
||||
(foo "x1" "\n" "x2y2" "\n" "x3y3" "\n" "x4" "y4" "\n" "x5")]
|
||||
|
||||
|
||||
))
|
||||
|
||||
(define failures 0)
|
||||
|
|
Loading…
Reference in New Issue
Block a user