Scriptum a handful of reader macros to allow for S-expressions that contain many strings.
Load Scriptum and activate the scriptum:syntax readtable:
CL-USER> (named-readtables:in-readtable scrawl:syntax)
CL-USER> '@a[:href "https://quil-lang.github.io"]{C o a l t o n}
(A :HREF "https://coalton-lang.github.io" "C o a l t o n")It's incredibly important to recognize that Scriptum is just an alternative S-expression syntax, optimized for a different use-case. That means expressions must be appropriately quoted lest you want evaluation to occur..
Scriptum provides an extension to the standard readtable which gives special treatment to the @ [ ] { } characters. The general form of a Scriptum expression is
'@' <op> <args>? <body>?
where
<op>is any Lisp expression,<args>is an optional square-bracketed list of Lisp expressions, e.g.[1 2 3], and<body>is an optional sequence of (mostly) unescaped text, surrounded by braces, e.g.{ foo frob }.
Note that the above is sensitive to spaces, so @foo [1 2 3] is different from @foo[1 2 3].
The body of a Scriptum expression consists of text, possibly containing further Scriptum expressions. This is read recursively as a sequence of strings and Scriptum expressions. The result of reading
@<op>[<arg1> ... <argN>]{ <body1> ... <bodyM> }
is
(<op> <arg1> ... <argN> <body1> ... <bodyM>)
where <body> contained a total of M text-segments (possibly containing whitespace) and Scriptum expressions.
Here's an extended example:
@div[:id "my-div"]{
@h1{
Hello World!
}
@p{
The quick @b{brown}
fox jumps over
the lazy dog.
}
}
is read as
(DIV :ID "my-div" (H1 "Hello World!")
(P "The quick " (B "brown") "
fox jumps over
the lazy dog."))
There are a few choices we have made.
-
Whitespace is trimmed from the start of the first string and the end of the last, e.g.
@foo{ bar @baz frob }results in(FOO "bar " BAZ " frob") -
Whitespace-only strings are ignored, e.g.
@foo{ }results in(FOO). -
Escaping within the body of a Scriptum expression is accomplished via
@, e.g.@foo{ @"@" }yields(FOO "@") -
The easiest way force inclusion of whitespace is to escape it:
@foo{@" "bar}yields'(FOO " " "bar") -
Nested braces are fine if they are balanced:
@foo{ { } }yields(FOO "{ }") -
Unbalanced braces must be escaped, e.g.
@foo{ @"{" }yields(FOO "{")
Scriptum comingles with the ordinary Lisp reader, which may lead to inscrutible parse errors. The variable scriptum:*debug-stream* can be set to a stream (e.g., *standard-output*) and Scriptum will print its progress in processing Scriptum data.
By default, Scriptum assembles a list for a Scriptum expression, but this behavior can be customized. A form handler is a function with lambda list
(operator &key options body)where
operatorrepresents the expression after@optionsrepresents the list of options in[brackets]bodyrepresents the list of character and nested data.
One may bind a new form handler to the variable scriptum:*form-handler*. The default form handler is #'scriptum:default-form-handler.
One use-case is to produce CLOS objects instead of lists, by dispatching off of the operator and building an object.
Strings within the body of a Scriptum expression may also be customized with a string handler function bound to scrawl:*string-handler*. A string handler must take a string and return an object (which may not be a string). The default string handler is #'identity, and thus strings remain unprocessed.
Scriptum was forked from Scrawl, which is a pedagogical clone of Scribble.
Eli Barzilay's The Scribble Reader was also inspiration in the design decisions around Scriptum.