1708 lines
58 KiB
Racket
1708 lines
58 KiB
Racket
#lang scribble/doc
|
||
@(require scribble/manual
|
||
scribble/bnf
|
||
scribble/eval
|
||
(for-syntax scheme/base)
|
||
(for-label scheme/base
|
||
scheme/gui
|
||
scheme/pretty
|
||
scheme/contract
|
||
(only-in slideshow/pict pict? text dc-for-text-size)
|
||
redex))
|
||
|
||
@(define-syntax (defpattech stx)
|
||
(syntax-case stx ()
|
||
[(_ arg)
|
||
(identifier? #'arg)
|
||
(let ([as (symbol->string (syntax-e #'arg))])
|
||
#`(index '("Redex Pattern" #,as) (deftech #,as)))]))
|
||
|
||
@(define-syntax (pattech stx)
|
||
(syntax-case stx ()
|
||
[(_ arg)
|
||
(identifier? #'arg)
|
||
#`(tech #,(symbol->string (syntax-e #'arg)))]))
|
||
|
||
@(define-syntax (ttpattern stx)
|
||
(syntax-case stx ()
|
||
[(_ args ...)
|
||
#'((tech (tt "pattern")) args ...)]
|
||
[x (identifier? #'x) #'(tech (tt "pattern"))]))
|
||
|
||
@(define-syntax (pattern stx)
|
||
(syntax-case stx ()
|
||
[(_ args ...)
|
||
#'((tech "pattern") args ...)]
|
||
[x (identifier? #'x) #'(tech "pattern")]))
|
||
|
||
@(define-syntax (tttterm stx)
|
||
(syntax-case stx ()
|
||
[(_ args ...)
|
||
#'((tech (tt "term")) args ...)]
|
||
[x (identifier? #'x) #'(tech (tt "term"))]))
|
||
|
||
@(define-syntax (tterm stx)
|
||
(syntax-case stx ()
|
||
[(_ args ...)
|
||
#'((tech "term") args ...)]
|
||
[x (identifier? #'x) #'(tech "term")]))
|
||
|
||
@title{@bold{Redex}: Debugging Operational Semantics}
|
||
|
||
@author["Robert Bruce Findler"]
|
||
|
||
PLT Redex consists of a domain-specific language for specifying
|
||
reduction semantics, plus a suite of tools for working with the
|
||
semantics.
|
||
|
||
This is a reference manual for Redex. See
|
||
@link["http://redex.plt-scheme.org/"]{@tt{http://redex.plt-scheme.org/}}
|
||
for a gentler overview. (See also the @tt{examples} subdirectory in
|
||
the @tt{redex} collection.)
|
||
|
||
To load Redex use: @defmodule[redex] which provides all of
|
||
the names documented in this library.
|
||
|
||
The module @schememodname[redex/reduction-semantics]
|
||
provides only the non-GUI portions of what is described in
|
||
this manual (everything except the last two sections),
|
||
making it suitable for use with @tt{mzscheme} scripts.
|
||
|
||
@table-of-contents[]
|
||
|
||
@section{Patterns}
|
||
|
||
@defmodule[redex/reduction-semantics]
|
||
|
||
All of the exports in this section are provided both by
|
||
@schememodname[redex/reduction-semantics] (which includes
|
||
all non-GUI portions of Redex) and also exported by
|
||
@schememodname[redex] (which includes all of Redex).
|
||
|
||
This section covers Redex's @deftech{pattern} language, used
|
||
in various ways:
|
||
|
||
@(schemegrammar* #:literals (any number string variable variable-except variable-prefix variable-not-otherwise-mentioned hole name in-hole side-condition cross)
|
||
[pattern any
|
||
number
|
||
string
|
||
variable
|
||
(variable-except symbol ...)
|
||
(variable-prefix symbol)
|
||
variable-not-otherwise-mentioned
|
||
hole
|
||
symbol
|
||
(name symbol pattern)
|
||
(in-hole pattern pattern)
|
||
(hide-hole pattern)
|
||
(side-condition pattern guard)
|
||
(cross symbol)
|
||
(pattern-sequence ...)
|
||
scheme-constant]
|
||
[pattern-sequence
|
||
pattern
|
||
(code:line ... (code:comment "literal ellipsis"))
|
||
..._id])
|
||
|
||
@itemize{
|
||
|
||
@item{The @defpattech[any] @pattern matches any sepxression.
|
||
This @pattern may also be suffixed with an underscore and another
|
||
identifier, in which case they bind the full name (as if it
|
||
were an implicit @pattech[name] @pattern) and match the portion
|
||
before the underscore.
|
||
}
|
||
|
||
@item{The @defpattech[number] @pattern matches any number.
|
||
This @pattern may also be suffixed with an underscore and another
|
||
identifier, in which case they bind the full name (as if it
|
||
were an implicit @pattech[name] @pattern) and match the portion
|
||
before the underscore.
|
||
}
|
||
|
||
@item{The @defpattech[string] @pattern matches any string.
|
||
This @pattern may also be suffixed with an underscore and another
|
||
identifier, in which case they bind the full name (as if it
|
||
were an implicit @pattech[name] @pattern) and match the portion
|
||
before the underscore.
|
||
}
|
||
|
||
@item{The @defpattech[variable] @pattern matches any symbol.
|
||
This @pattern may also be suffixed with an underscore and another
|
||
identifier, in which case they bind the full name (as if it
|
||
were an implicit @pattech[name] @pattern) and match the portion
|
||
before the underscore.
|
||
}
|
||
|
||
@item{The @defpattech[variable-except] @pattern matches any symbol except those
|
||
listed in its argument. This is useful for ensuring that
|
||
keywords in the language are not accidentally captured by
|
||
variables.
|
||
}
|
||
|
||
@item{ The @defpattech[variable-prefix] @pattern matches any symbol
|
||
that begins with the given prefix. }
|
||
|
||
@item{The @defpattech[variable-not-otherwise-mentioned] @pattern matches any
|
||
symbol except those that are used as literals elsewhere in
|
||
the language.
|
||
}
|
||
|
||
@item{The @defpattech[hole] @pattern matches anything when inside a matching
|
||
the first argument to an @pattech[in-hole] @|pattern|. Otherwise,
|
||
it matches only the hole.
|
||
}
|
||
|
||
@item{The @defpattech[symbol] @pattern stands for a literal symbol that must
|
||
match exactly, unless it is the name of a non-terminal in a
|
||
relevant language or contains an underscore.
|
||
|
||
If it is a non-terminal, it matches any of the right-hand
|
||
sides of that non-terminal.
|
||
|
||
If the symbol is a non-terminal followed by an underscore,
|
||
for example @tt{e_1}, it is implicitly the same as a name @pattern
|
||
that matches only the non-terminal, @tt{(@pattech[name] e_1 e)} for the
|
||
example. Accordingly, repeated uses of the same name are
|
||
constrainted to match the same expression.
|
||
|
||
If the symbol is a non-terminal followed by @tt{_!_}, for example
|
||
@tt{e_!_1}, it is also treated as a @|pattern|, but repeated uses of
|
||
the same @pattern are constrained to be different. For
|
||
example, this @|pattern|:
|
||
|
||
@schemeblock[(e_!_1 e_!_1 e_!_1)]
|
||
|
||
matches lists of three @tt{e}s, but where all three of them are
|
||
distinct.
|
||
|
||
Unlike a @tt{_} @|pattern|, the @tt{_!_} @|pattern|s do not bind names.
|
||
|
||
If @tt{_} names and @tt{_!_} are mixed, they are treated as
|
||
separate. That is, this @pattern @tt{(e_1 e_!_1)} matches just the
|
||
same things as @tt{(e e)}, but the second doesn't bind any
|
||
variables.
|
||
|
||
If the symbol otherwise has an underscore, it is an error.
|
||
}
|
||
|
||
@item{The @pattern @tt{(@defpattech[name] symbol @ttpattern)}
|
||
matches @tt{@pattern} and binds using it to the name @tt{symbol}.
|
||
}
|
||
|
||
@item{The @tt{(@defpattech[in-hole] @ttpattern @ttpattern)} @pattern
|
||
matches the first @|pattern|. This match must include exactly one match
|
||
against the second @|pattern|. If there are zero matches or more
|
||
than one match, an exception is raised.
|
||
|
||
When matching the first argument of in-hole, the `hole' @pattern
|
||
matches any sexpression. Then, the sexpression that matched the hole
|
||
@pattern is used to match against the second @|pattern|.
|
||
}
|
||
|
||
@item{The @tt{(@defpattech[hide-hole] @ttpattern)} @pattern matches what
|
||
the embedded @pattern matches but if the @pattern matcher is
|
||
looking for a decomposition, it ignores any holes found in
|
||
that @|pattern|.
|
||
}
|
||
|
||
@item{The @tt{(@defpattech[side-condition] @ttpattern guard)} @pattern matches
|
||
what the embedded @pattern matches, and then the guard expression is
|
||
evaluated. If it returns @scheme[#f], the @pattern fails to match, and if it
|
||
returns anything else, the @pattern matches. In addition, any
|
||
occurrences of `name' in the @pattern are bound using @scheme[term-let]
|
||
in the guard.
|
||
}
|
||
|
||
@item{The @tt{(@defpattech[cross] symbol)} @pattern is used for the compatible
|
||
closure functions. If the language contains a non-terminal with the
|
||
same name as <symbol>, the @pattern @tt{(cross symbol)} matches the
|
||
context that corresponds to the compatible closure of that
|
||
non-terminal.
|
||
}
|
||
|
||
@item{The @tt{(@defpattech[pattern-sequence] ...)}
|
||
@pattern matches a sexpression
|
||
list, where each pattern-sequence element matches an element
|
||
of the list. In addition, if a list @pattern contains an
|
||
ellipsis, the ellipsis is not treated as a literal, instead
|
||
it matches any number of duplications of the @pattern that
|
||
came before the ellipses (including 0). Furthermore, each
|
||
@tt{(@pattech[name] symbol @ttpattern)} in the duplicated @pattern binds a
|
||
list of matches to @tt{symbol}, instead of a single match. (A
|
||
nested duplicated @pattern creates a list of list matches,
|
||
etc.) Ellipses may be placed anywhere inside the row of
|
||
@|pattern|s, except in the first position or immediately after
|
||
another ellipses.
|
||
|
||
Multiple ellipses are allowed. For example, this @|pattern|:
|
||
|
||
@schemeblock[((name x a) ... (name y a) ...)]
|
||
|
||
matches this sexpression:
|
||
|
||
@schemeblock[(#, @|tttterm| (a a))]
|
||
|
||
three different ways. One where the first @tt{a} in the @pattern
|
||
matches nothing, and the second matches both of the
|
||
occurrences of @tt{a}, one where each named @pattern matches a
|
||
single @tt{a} and one where the first matches both and the
|
||
second matches nothing.
|
||
|
||
If the ellipses is named (ie, has an underscore and a name
|
||
following it, like a variable may), the @pattern matcher
|
||
records the length of the list and ensures that any other
|
||
occurrences of the same named ellipses must have the same
|
||
length.
|
||
|
||
As an example, this @|pattern|:
|
||
|
||
@schemeblock[((name x a) ..._1 (name y a) ..._1)]
|
||
|
||
only matches this sexpression:
|
||
|
||
@schemeblock[(#, @|tttterm| (a a))]
|
||
|
||
one way, with each named @pattern matching a single a. Unlike
|
||
the above, the two @|pattern|s with mismatched lengths is ruled
|
||
out, due to the underscores following the ellipses.
|
||
|
||
Also, like underscore @|pattern|s above, if an underscore
|
||
@pattern begins with @tt{..._!_}, then the lengths must be
|
||
different.
|
||
|
||
Thus, with the @|pattern|:
|
||
|
||
@schemeblock[((name x a) ..._!_1 (name y a) ..._!_1)]
|
||
|
||
and the expression
|
||
|
||
@schemeblock[(#, @|tttterm| (a a))]
|
||
|
||
two matches occur, one where @tt{x} is bound to @scheme['()] and
|
||
@tt{y} is bound to @scheme['(a a)] and one where @tt{x} is bound to
|
||
@scheme['(a a)] and @tt{y} is
|
||
bound to @scheme['()].
|
||
|
||
}
|
||
}
|
||
|
||
@defform*[[(redex-match lang #, @|ttpattern| any)
|
||
(redex-match lang #, @|ttpattern|)]]{
|
||
|
||
If @scheme[redex-match] receives three arguments, it
|
||
matches the pattern (in the language) against its third
|
||
argument. If it matches, this returns a list of match
|
||
structures describing the matches. If it fails, it returns
|
||
@scheme[#f].
|
||
|
||
If @scheme[redex-match] receives only two arguments, it
|
||
builds a procedure for efficiently testing if expressions
|
||
match the pattern, using the language @scheme[lang]. The
|
||
procedures accepts a single expression and if the expresion
|
||
matches, it returns a list of match structures describing the
|
||
matches. If the match fails, the procedure returns @scheme[#f].
|
||
}
|
||
|
||
@defproc[(match? [val any/c]) boolean?]{
|
||
|
||
Determines if a value is a @tt{match} structure.
|
||
}
|
||
|
||
@defproc[(match-bindings [m match?]) (listof bind?)]{
|
||
|
||
This returns a bindings structure (see below) that
|
||
binds the pattern variables in this match.
|
||
}
|
||
|
||
@defstruct[bind ([name symbol?] [exp any?])]{
|
||
|
||
Instances of this struct are returned by @scheme[redex-match].
|
||
Each @scheme[bind] associates a name with an s-expression from the
|
||
language, or a list of such s-expressions, if the @tt{(@pattech[name] ...)}
|
||
clause is followed by an ellipsis. Nested ellipses produce
|
||
nested lists.
|
||
}
|
||
|
||
@defproc[(set-cache-size! [size (or/c false/c positive-integer?)]) void?]{
|
||
|
||
Changes the cache size; a #f disables the cache
|
||
entirely. The default size is 350.
|
||
|
||
The cache is per-pattern (ie, each pattern has a cache of
|
||
size at most 350 (by default)) and is a simple table that
|
||
maps expressions to how they matched the pattern. When the
|
||
cache gets full, it is thrown away and a new cache is
|
||
started.
|
||
}
|
||
|
||
@section{Terms}
|
||
|
||
All of the exports in this section are provided both by
|
||
@schememodname[redex/reduction-semantics] (which includes
|
||
all non-GUI portions of Redex) and also exported by
|
||
@schememodname[redex] (which includes all of Redex).
|
||
|
||
Object langauge expressions in Redex are written using
|
||
@scheme[term]. It is similar to Scheme's @scheme[quote] (in
|
||
many cases it is identical) in that it constructs lists as
|
||
the visible representation of terms.
|
||
|
||
The grammar of @deftech{term}s is (note that an ellipsis
|
||
stands for repetition unless otherwise indicated):
|
||
|
||
@(schemegrammar* #:literals (in-hole hole)
|
||
[term identifier
|
||
(term-sequence ...)
|
||
,scheme-expression
|
||
(in-hole term term)
|
||
hole
|
||
#t #f
|
||
string]
|
||
[term-sequence
|
||
term
|
||
,@scheme-expression
|
||
(code:line ... (code:comment "literal ellipsis"))])
|
||
|
||
@itemize{
|
||
|
||
@item{A term written @tt{identifier} is equivalent to the
|
||
corresponding symbol, unless the identifier is bound by
|
||
@scheme[term-let] (or in a @|pattern| elsewhere) or is
|
||
@tt{hole} (as below). }
|
||
|
||
@item{A term written @tt{(term-sequence ...)} constructs a list of
|
||
the terms constructed by the sequence elements.}
|
||
|
||
@item{A term written @scheme[,scheme-expression] evaluates the
|
||
@scheme[scheme-expression] and substitutes its value into the term at
|
||
that point.}
|
||
|
||
@item{A term written @scheme[,@scheme-expression] evaluates the
|
||
@scheme[scheme-expression], which must produce a list. It then splices
|
||
the contents of the list into the expression at that point in the sequence.}
|
||
|
||
@item{A term written @tt{(in-hole @|tttterm| @|tttterm|)}
|
||
is the dual to the @pattern `in-hole' -- it accepts
|
||
a context and an expression and uses @scheme[plug] to combine
|
||
them.}
|
||
|
||
@item{A term written @tt{hole} produces a hole.}
|
||
|
||
@item{A term written as a literal boolean or a string
|
||
produces the boolean or the string.}
|
||
}
|
||
|
||
@defform[(term #, @|tttterm|)]{
|
||
|
||
This form is used for construction of a term.
|
||
|
||
in
|
||
the right-hand sides of reductions. It behaves similarly to
|
||
quasiquote except for a few special forms that are
|
||
recognized (listed below) and that names bound by @scheme[term-let] are
|
||
implicitly substituted with the values that those names were
|
||
bound to, expanding ellipses as in-place sublists (in the
|
||
same manner as syntax-case patterns).
|
||
|
||
For example,
|
||
|
||
@schemeblock[
|
||
(term-let ([body '(+ x 1)]
|
||
[(expr ...) '(+ - (values * /))]
|
||
[((id ...) ...) '((a) (b) (c d))])
|
||
(term (let-values ([(id ...) expr] ...) body)))
|
||
]
|
||
|
||
evaluates to
|
||
|
||
@schemeblock[
|
||
'(let-values ([(a) +]
|
||
[(b) -]
|
||
[(c d) (values * /)])
|
||
(+ x 1))
|
||
]
|
||
|
||
It is an error for a term variable to appear in an
|
||
expression with an ellipsis-depth different from the depth
|
||
with which it was bound by @scheme[term-let]. It is also an error
|
||
for two @scheme[term-let]-bound identifiers bound to lists of
|
||
different lengths to appear together inside an ellipsis.
|
||
}
|
||
|
||
@defform/subs[(term-let ([tl-pat expr] ...) body)
|
||
([tl-pat identifier (tl-pat-ele ...)]
|
||
[tl-pat-ele tl-pat (code:line tl-pat ... (code:comment "a literal ellipsis"))])]{
|
||
|
||
Matches each given id pattern to the value yielded by
|
||
evaluating the corresponding expr and binds each variable in
|
||
the id pattern to the appropriate value (described
|
||
below). These bindings are then accessible to the `term'
|
||
syntactic form.
|
||
|
||
Note that each @scheme[ellipsis] should be the literal
|
||
symbol consisting of three dots (and the ... elsewhere
|
||
indicates repetition as usual). If @scheme[tl-pat] is an identifier,
|
||
it matches any value and binds it to the identifier, for use
|
||
inside @scheme[term]. If it is a list, it matches only if the value
|
||
being matched is a list value and only if every subpattern
|
||
recursively matches the corresponding list element. There
|
||
may be a single ellipsis in any list pattern; if one is
|
||
present, the pattern before the ellipses may match multiple
|
||
adjacent elements in the list value (possibly none).
|
||
}
|
||
|
||
@defform[(term-match language [#, @|ttpattern| expression] ...)]{
|
||
|
||
This produces a procedure that accepts term (or quoted)
|
||
expressions and checks them against each pattern. The
|
||
function returns a list of the values of the expression
|
||
where the pattern matches. If one of the patterns matches
|
||
multiple times, the expression is evaluated multiple times,
|
||
once with the bindings in the pattern for each match.
|
||
}
|
||
|
||
@defform[(term-match/single language [#, @|ttpattern| expression] ...)]{
|
||
|
||
This produces a procedure that accepts term (or quoted)
|
||
expressions and checks them against each pattern. The
|
||
function returns the expression behind the first sucessful
|
||
match. If that pattern produces multiple matches, an error
|
||
is signaled. If no patterns match, an error is signaled.
|
||
|
||
Raises an exception recognized by @scheme[exn:fail:redex?] if
|
||
no clauses match or if one of the clauses matches multiple ways.
|
||
}
|
||
|
||
@defproc[(plug [context any?] [expression any?]) any]{
|
||
|
||
The first argument to this function is an sexpression to
|
||
plug into. The second argument is the sexpression to replace
|
||
in the first argument. It returns the replaced term. This is
|
||
also used when a @scheme[term] sub-expression contains @tt{in-hole}.
|
||
}
|
||
|
||
@defproc[(variable-not-in [t any?] [var symbol?]) symbol?]{
|
||
|
||
This helper function accepts an sexpression and a
|
||
variable. It returns a variable not in the sexpression with
|
||
a prefix the same as the second argument.
|
||
|
||
}
|
||
|
||
@defproc[(variables-not-in [t any?] [vars (listof symbol?)]) (listof symbol?)]{
|
||
|
||
This function, like variable-not-in, makes variables that do
|
||
no occur in its first argument, but it returns a list of
|
||
such variables, one for each variable in its second
|
||
argument.
|
||
|
||
Does not expect the input symbols to be distinct, but does
|
||
produce variables that are always distinct.
|
||
}
|
||
|
||
@defproc[(exn:fail:redex? [v any/c]) boolean?]{
|
||
Returns @scheme[#t] if its argument is a Redex exception record, and
|
||
@scheme[#f] otherwise.
|
||
}
|
||
|
||
@section{Languages}
|
||
|
||
All of the exports in this section are provided both by
|
||
@schememodname[redex/reduction-semantics] (which includes
|
||
all non-GUI portions of Redex) and also exported by
|
||
@schememodname[redex] (which includes all of Redex).
|
||
|
||
@defform/subs[(define-language lang-name
|
||
(non-terminal-spec #, @|ttpattern| ...)
|
||
...)
|
||
([non-terminal-spec symbol (symbol ...)])]{
|
||
|
||
This form defines the grammar of a language. It allows the
|
||
definition of recursive @|pattern|s, much like a BNF, but for
|
||
regular-tree grammars. It goes beyond their expressive
|
||
power, however, because repeated `name' @|pattern|s and
|
||
side-conditions can restrict matches in a context-sensitive
|
||
way.
|
||
|
||
The non-terminal-spec can either by a symbol, indicating a
|
||
single name for this non-terminal, or a sequence of symbols,
|
||
indicating that all of the symbols refer to these
|
||
productions.
|
||
|
||
As a simple example of a grammar, this is the lambda
|
||
calculus:
|
||
|
||
@schemeblock[
|
||
(define-language lc-lang
|
||
(e (e e ...)
|
||
x
|
||
v)
|
||
(c (v ... c e ...)
|
||
hole)
|
||
(v (lambda (x ...) e))
|
||
(x variable-not-otherwise-mentioned))
|
||
]
|
||
|
||
with non-terminals @scheme[e] for the expression language, @scheme[x] for
|
||
variables, @scheme[c] for the evaluation contexts and @scheme[v] for values.
|
||
}
|
||
|
||
@defform[(define-extended-language language language
|
||
(non-terminal #, @|ttpattern| ...)
|
||
...)]{
|
||
|
||
This form extends a language with some new, replaced, or
|
||
extended non-terminals. For example, this language:
|
||
|
||
@schemeblock[
|
||
(define-extended-language lc-num-lang
|
||
lc-lang
|
||
(e .... (code:comment "extend the previous `e' non-terminal")
|
||
+
|
||
number)
|
||
(v ....
|
||
+
|
||
number)
|
||
(x (variable-except lambda +)))
|
||
]
|
||
|
||
extends lc-lang with two new alternatives for both the @scheme[e]
|
||
and @scheme[v] nonterminal, replaces the @scheme[x] non-terminal with a
|
||
new one, and carries the @scheme[c] non-terminal forward.
|
||
|
||
The four-period ellipses indicates that the new language's
|
||
non-terminal has all of the alternatives from the original
|
||
language's non-terminal, as well as any new ones. If a
|
||
non-terminal occurs in both the base language and the
|
||
extension, the extension's non-terminal replaces the
|
||
originals. If a non-terminal only occurs in either the base
|
||
language, then it is carried forward into the
|
||
extension. And, of course, extend-language lets you add new
|
||
non-terminals to the language.
|
||
|
||
If a language is has a group of multiple non-terminals
|
||
defined together, extending any one of those non-terminals
|
||
extends all of them.
|
||
}
|
||
|
||
@defproc[(language-nts [lang compiled-lang?]) (listof symbol?)]{
|
||
|
||
Returns the list of non-terminals (as symbols) that are
|
||
defined by this language.
|
||
}
|
||
|
||
@defproc[(compiled-lang? [l any/c]) boolean?]{
|
||
|
||
Returns #t if its argument was produced by `language', #f
|
||
otherwise.
|
||
}
|
||
|
||
@section{Reduction Relations}
|
||
|
||
All of the exports in this section are provided both by
|
||
@schememodname[redex/reduction-semantics] (which includes
|
||
all non-GUI portions of Redex) and also exported by
|
||
@schememodname[redex] (which includes all of Redex).
|
||
|
||
@defform/subs[#:literals (--> fresh side-condition where)
|
||
(reduction-relation language reduction-case ...)
|
||
([reduction-case (--> #, @|ttpattern| #, @|tttterm| extras ...)]
|
||
[extras name
|
||
(fresh fresh-clause ...)
|
||
(side-condition scheme-expression ...)
|
||
(where tl-pat #, @|tttterm|)]
|
||
[fresh-clause var ((var1 ...) (var2 ...))]
|
||
[tl-pat identifier (tl-pat-ele ...)]
|
||
[tl-pat-ele tl-pat (code:line tl-pat ... (code:comment "a literal ellipsis"))])]{
|
||
|
||
Defines a reduction relation casewise, one case for each of the
|
||
clauses beginning with @scheme[-->]. Each of the @scheme[pattern]s
|
||
refers to the @scheme[language], and binds variables in the
|
||
@|tttterm|.
|
||
|
||
Following the @|pattern| and @|tterm| can be the name of the
|
||
reduction rule, declarations of some fresh variables, and/or
|
||
some side-conditions. The name can either be a literal
|
||
name (identifier), or a literal string.
|
||
|
||
The fresh variables clause generates variables that do not
|
||
occur in the term being matched. If the @scheme[fresh-clause] is a
|
||
variable, that variable is used both as a binding in the
|
||
rhs-exp and as the prefix for the freshly generated
|
||
variable.
|
||
|
||
The second case of a @scheme[fresh-clause] is used when you want to
|
||
generate a sequence of variables. In that case, the ellipses
|
||
are literal ellipses; that is, you must actually write
|
||
ellipses in your rule. The variable var1 is like the
|
||
variable in first case of a @scheme[fresh-clause], namely it is
|
||
used to determine the prefix of the generated variables and
|
||
it is bound in the right-hand side of the reduction rule,
|
||
but unlike the single-variable fresh clause, it is bound to
|
||
a sequence of variables. The variable var2 is used to
|
||
determine the number of variables generated and var2 must be
|
||
bound by the left-hand side of the rule.
|
||
|
||
The side-conditions are expected to all hold, and have the
|
||
format of the second argument to the side-condition pattern,
|
||
described above.
|
||
|
||
Each @scheme[where] clauses binds a variable and the side-conditions
|
||
(and @scheme[where] clauses) that follow the where declaration are in
|
||
scope of the where declaration. The bindings are the same as
|
||
bindings in a @scheme[term-let] expression.
|
||
|
||
As an example, this
|
||
|
||
@schemeblock[
|
||
(reduction-relation
|
||
lc-lang
|
||
(--> (in-hole c_1 ((lambda (variable_i ...) e_body) v_i ...))
|
||
(in-hole c_1 ,(foldl lc-subst
|
||
(term e_body)
|
||
(term (v_i ...))
|
||
(term (variable_i ...))))
|
||
beta-v))
|
||
]
|
||
|
||
defines a reduction relation for the lambda-calculus above.
|
||
}
|
||
|
||
@defform/none[#:literals (with reduction-relation)
|
||
(reduction-relation
|
||
language
|
||
(arrow-var #, @|ttpattern| #, @|tttterm|) ...
|
||
with
|
||
[(arrow #, @|ttpattern| #, @|tttterm|)
|
||
(arrow-var var var)] ...)]{
|
||
|
||
Defines a reduction relation with shortcuts. As above, the
|
||
first section defines clauses of the reduction relation, but
|
||
instead of using -->, those clauses can use any identifier
|
||
for an arrow, as long as the identifier is bound after the
|
||
`with' clause.
|
||
|
||
Each of the clauses after the `with' define new relations
|
||
in terms of other definitions after the `with' clause or in
|
||
terms of the main --> relation.
|
||
|
||
@scheme[fresh] is always fresh with respect to the entire
|
||
term, not just with respect to the part that matches the
|
||
right-hand-side of the newly defined arrow.
|
||
|
||
As an example, this
|
||
|
||
@schemeblock[
|
||
(reduction-relation
|
||
lc-num-lang
|
||
(==> ((lambda (variable_i ...) e_body) v_i ...)
|
||
,(foldl lc-subst
|
||
(term e_body)
|
||
(term (v_i ...))
|
||
(term (variable_i ...))))
|
||
(==> (+ number_1 ...)
|
||
,(apply + (term (number_1 ...))))
|
||
|
||
with
|
||
[(--> (in-hole c_1 a) (in-hole c_1 b))
|
||
(==> a b)])
|
||
]
|
||
|
||
defines reductions for the lambda calculus with numbers,
|
||
where the @tt{==>} relation is defined by reducing in the context
|
||
@tt{c}.
|
||
}
|
||
|
||
@defform[(extend-reduction-relation reduction-relation language more ...)]{
|
||
|
||
This form extends the reduction relation in its first
|
||
argument with the rules specified in <more>. They should
|
||
have the same shape as the the rules (including the `with'
|
||
clause) in an ordinary @scheme[reduction-relation].
|
||
|
||
If the original reduction-relation has a rule with the same
|
||
name as one of the rules specified in the extension, the old
|
||
rule is removed.
|
||
|
||
In addition to adding the rules specified to the existing
|
||
relation, this form also reinterprets the rules in the
|
||
original reduction, using the new language.
|
||
}
|
||
@defproc[(union-reduction-relations [r reduction-relation?] ...)
|
||
reduction-relation?]{
|
||
|
||
Combines all of the argument reduction relations into a
|
||
single reduction relation that steps when any of the
|
||
arguments would have stepped.
|
||
}
|
||
|
||
@defproc[(reduction-relation->rule-names [r reduction-relation?])
|
||
(listof (union false/c symbol?))]{
|
||
|
||
Returns the names of all of the reduction relation's clauses
|
||
(or false if there is no name for a given clause).
|
||
}
|
||
|
||
@defform[(compatible-closure reduction-relation lang non-terminal)]{
|
||
|
||
This accepts a reduction, a language, the name of a
|
||
non-terminal in the language and returns the compatible
|
||
closure of the reduction for the specified non-terminal.
|
||
}
|
||
|
||
@defform[(context-closure reduction-relation lang pattern)]{
|
||
|
||
This accepts a reduction, a language, a pattern representing
|
||
a context (ie, that can be used as the first argument to
|
||
`in-hole'; often just a non-terminal) in the language and
|
||
returns the closure of the reduction in that context.
|
||
}
|
||
|
||
@defproc[(reduction-relation? [v any/c]) boolean?]{
|
||
Returns @scheme[#t] if its argument is a reduction-relation, and
|
||
@scheme[#f] otherwise.
|
||
}
|
||
|
||
@defproc[(apply-reduction-relation [r reduction-relation?] [t any?]) (listof any?)]{
|
||
|
||
This accepts reduction relation, a term, and returns a
|
||
list of terms that the term reduces to.
|
||
}
|
||
|
||
@defproc[(apply-reduction-relation/tag-with-names
|
||
[r reduction-relation?]
|
||
[t any/c])
|
||
(listof (list/c (union false/c string?) any/c))]{
|
||
|
||
Like @scheme[apply-reduction-relation], but the result indicates the
|
||
names of the reductions that were used.
|
||
}
|
||
|
||
@defproc[(apply-reduction-relation*
|
||
[r reduction-relation?]
|
||
[t any?])
|
||
(listof (listof any?))]{
|
||
|
||
apply-reduction-relation* accepts a list of reductions and a
|
||
term. It returns the results of following every reduction
|
||
path from the term. If there are infinite reduction
|
||
sequences starting at the term, this function will not
|
||
terminate.
|
||
}
|
||
|
||
@defidform[-->]{ Recognized specially within
|
||
@scheme[reduction-relation]. A @scheme[-->] form is an
|
||
error elsewhere. }
|
||
|
||
@defidform[fresh]{ Recognized specially within
|
||
@scheme[reduction-relation]. A @scheme[-->] form is an
|
||
error elsewhere. }
|
||
|
||
@defidform[with]{ Recognized specially within
|
||
@scheme[reduction-relation]. A @scheme[with] form is an
|
||
error elsewhere. }
|
||
|
||
@section{Metafunctions}
|
||
|
||
All of the exports in this section are provided both by
|
||
@schememodname[redex/reduction-semantics] (which includes
|
||
all non-GUI portions of Redex) and also exported by
|
||
@schememodname[redex] (which includes all of Redex).
|
||
|
||
@defform/subs[#:literals (: ->)
|
||
(define-metafunction language-exp
|
||
contract
|
||
[(name #, @|ttpattern| ...) #, @|tttterm| extras ...]
|
||
...)
|
||
([contract (code:line)
|
||
(code:line id : #, @|ttpattern| ... -> #, @|ttpattern|)]
|
||
[extras (side-condition scheme-expression)
|
||
(where tl-pat #, @|tttterm|)]
|
||
[tl-pat identifier (tl-pat-ele ...)]
|
||
[tl-pat-ele tl-pat (code:line tl-pat ... (code:comment "a literal ellipsis"))])]{
|
||
|
||
The @scheme[define-metafunction] form builds a function on
|
||
sexpressions according to the pattern and right-hand-side
|
||
expressions. The first argument indicates the language used
|
||
to resolve non-terminals in the pattern expressions. Each of
|
||
the rhs-expressions is implicitly wrapped in `term'. In
|
||
addition, recursive calls in the right-hand side of the
|
||
metafunction clauses should appear inside `term'.
|
||
|
||
If specified, the side-conditions are collected with
|
||
@scheme[and] and used as guards on the case being matched. The
|
||
argument to each side-condition should be a Scheme
|
||
expression, and the pattern variables in the <pattern> are
|
||
bound in that expression.
|
||
|
||
Raises an exception recognized by @scheme[exn:fail:redex?] if
|
||
no clauses match, if one of the clauses matches multiple ways, or
|
||
if the contract is violated.
|
||
|
||
As an example, these metafunctions finds the free variables in
|
||
an expression in the lc-lang above:
|
||
|
||
@schemeblock[
|
||
(define-metafunction lc-lang
|
||
free-vars : e -> (x ...)
|
||
[(free-vars (e_1 e_2 ...))
|
||
(∪ (free-vars e_1) (free-vars e_2) ...)]
|
||
[(free-vars x) (x)]
|
||
[(free-vars (lambda (x ...) e))
|
||
(- (free-vars e) (x ...))])
|
||
]
|
||
|
||
The first argument to define-metafunction is the grammar
|
||
(defined above). Following that are three cases, one for
|
||
each variation of expressions (e in lc-lang). The right-hand
|
||
side of each clause begins with a comma, since they are
|
||
implicitly wrapped in `term'. The free variables of an
|
||
application are the free variables of each of the subterms;
|
||
the free variables of a variable is just the variable
|
||
itself, and the free variables of a lambda expression are
|
||
the free variables of the body, minus the bound parameters.
|
||
|
||
Here are the helper metafunctions used above.
|
||
|
||
@schemeblock[
|
||
(define-metafunction lc-lang
|
||
∪ : (x ...) ... -> (x ...)
|
||
[(∪ (x_1 ...) (x_2 ...) (x_3 ...) ...)
|
||
(∪ (x_1 ... x_2 ...) (x_3 ...) ...)]
|
||
[(∪ (x_1 ...))
|
||
(x_1 ...)]
|
||
[(∪) ()])
|
||
|
||
(define-metafunction lc-lang
|
||
- : (x ...) (x ...) -> (x ...)
|
||
[(- (x ...) ()) (x ...)]
|
||
[(- (x_1 ... x_2 x_3 ...) (x_2 x_4 ...))
|
||
(- (x_1 ... x_3 ...) (x_2 x_4 ...))
|
||
(side-condition (not (memq (term x_2) (term (x_3 ...)))))]
|
||
[(- (x_1 ...) (x_2 x_3 ...))
|
||
(- (x_1 ...) (x_3 ...))])
|
||
]
|
||
|
||
Note the side-condition in the second case of @scheme[-]. It
|
||
ensures that there is a unique match for that case. Without
|
||
it, @scheme[(term (- (x x) x))] would lead to an ambiguous
|
||
match.
|
||
|
||
}
|
||
|
||
@defform[(define-metafunction/extension extending-name language-exp
|
||
contract
|
||
[(name #, @|ttpattern| ...) #, @|tttterm| (side-condition scheme-expression) ...]
|
||
...)]{
|
||
|
||
This defines a metafunction as an extension of an existing
|
||
one. The extended metafunction behaves as if the original
|
||
patterns were in this definitions, with the name of the
|
||
function fixed up to be @scheme[extending-name].
|
||
}
|
||
|
||
@defform[(in-domain? (metafunction-name #, @|tttterm| ...))]{
|
||
Returns @scheme[#t] if the inputs specified to @scheme[metafunction-name] are
|
||
legtimate inputs according to @scheme[metafunction-name]'s contract,
|
||
and @scheme[#f] otherwise.
|
||
}
|
||
|
||
@section{Testing}
|
||
|
||
All of the exports in this section are provided both by
|
||
@schememodname[redex/reduction-semantics] (which includes
|
||
all non-GUI portions of Redex) and also exported by
|
||
@schememodname[redex] (which includes all of Redex).
|
||
|
||
@defform[(test-equal e1 e2)]{
|
||
|
||
Tests to see if @scheme[e1] is equal to @scheme[e2].
|
||
}
|
||
|
||
@defform[(test--> reduction-relation e1 e2 ...)]{
|
||
|
||
Tests to see if the value of @scheme[e1] (which should be a term),
|
||
reduces to the @scheme[e2]s under @scheme[reduction-relation].
|
||
}
|
||
|
||
@defform[(test-predicate p? e)]{
|
||
Tests to see if the value of @scheme[e] matches the predicate @scheme[p?].
|
||
}
|
||
|
||
@defproc[(test-results) void?]{
|
||
Prints out how many tests passed and failed, and resets the
|
||
counters so that next time this function is called, it
|
||
prints the test results for the next round of tests.
|
||
}
|
||
|
||
@deftech{Debugging PLT Redex Programs}
|
||
|
||
It is easy to write grammars and reduction rules that are
|
||
subtly wrong and typically such mistakes result in examples
|
||
that just get stuck when viewed in a `traces' window.
|
||
|
||
The best way to debug such programs is to find an expression
|
||
that looks like it should reduce but doesn't and try to find
|
||
out what pattern is failing to match. To do so, use the
|
||
redex-match special form, described above.
|
||
|
||
In particular, first ceck to see if the term matches the
|
||
main non-terminal for your system (typically the expression
|
||
or program nonterminal). If it does not, try to narrow down
|
||
the expression to find which part of the term is failing to
|
||
match and this will hopefully help you find the problem. If
|
||
it does match, figure out which reduction rule should have
|
||
matched, presumably by inspecting the term. Once you have
|
||
that, extract a pattern from the left-hand side of the
|
||
reduction rule and do the same procedure until you find a
|
||
small example that shoudl work but doesn't (but this time
|
||
you might also try simplifying the pattern as well as
|
||
simplifying the expression).
|
||
|
||
|
||
@section{GUI}
|
||
@defmodule[redex/gui]
|
||
|
||
This section describes the GUI tools that Redex provides for
|
||
exploring reduction sequences.
|
||
|
||
@defproc[(traces [reductions reduction-relation?]
|
||
[expr (or/c any/c (listof any/c))]
|
||
[#:multiple? multiple? boolean? #f]
|
||
[#:pred pred
|
||
(or/c (sexp -> any) (sexp term-node? any))
|
||
(lambda (x) #t)]
|
||
[#:pp pp
|
||
(or/c (any -> string)
|
||
(any output-port number (is-a?/c text%) -> void))
|
||
default-pretty-printer]
|
||
[#:colors colors (listof (list string string)) '()])
|
||
void?]{
|
||
|
||
This function opens a new window and inserts each expression
|
||
in expr (if @scheme[multiple?] is #t -- if
|
||
@scheme[multiple?] is #f, then expr is treated as a single
|
||
expression). Then, it reduces the terms until at least
|
||
@scheme[reduction-steps-cutoff] (see below) different terms are
|
||
found, or no more reductions can occur. It inserts each new
|
||
term into the gui. Clicking the @onscreen{reduce} button reduces
|
||
until reduction-steps-cutoff more terms are found.
|
||
|
||
The pred function indicates if a term has a particular
|
||
property. If it returns @scheme[#f], the term is displayed with a
|
||
pink background. If it returns a string or a @scheme[color%] object,
|
||
the term is displayed with a background of that color (using
|
||
@scheme[the-color-database] to map the string to a color). If it
|
||
returns any other value, the term is displayed normally. If
|
||
the pred function accepts two arguments, a term-node
|
||
corresponding to the term is passed to the predicate. This
|
||
lets the predicate function explore the (names of the)
|
||
reductions that led to this term, using term-node-children,
|
||
term-node-parents, and term-node-labels.
|
||
|
||
The @scheme[pred] function may be called more than once per node. In
|
||
particular, it is called each time an edge is added to a
|
||
node. The latest value returned determines the color.
|
||
|
||
The @scheme[pp] function is used to specially print expressions. It
|
||
must either accept one or four arguments. If it accepts one
|
||
argument, it will be passed each term and is expected to
|
||
return a string to display the term.
|
||
|
||
If the @scheme[pp] function takes four arguments, it should render
|
||
its first argument into the port (its second argument) with
|
||
width at most given by the number (its third argument). The
|
||
final argument is the text where the port is connected --
|
||
characters written to the port go to the end of the editor.
|
||
|
||
The @scheme[colors] argument, if provided, specifies a list of
|
||
reduction-name/color-string pairs. The traces gui will color
|
||
arrows drawn because of the given reduction name with the
|
||
given color instead of using the default color.
|
||
|
||
You can save the contents of the window as a postscript file
|
||
from the menus.
|
||
}
|
||
|
||
@defproc[(stepper [reductions reduction-relation?]
|
||
[t any/c]
|
||
[pp (or/c (any -> string)
|
||
(any output-port number (is-a?/c text%) -> void))
|
||
default-pretty-printer])
|
||
void?]{
|
||
|
||
This function opens a stepper window for exploring the
|
||
behavior of its third argument in the reduction system
|
||
described by its first two arguments.
|
||
|
||
The @scheme[pp] argument is the same as to the
|
||
@scheme[traces] functions (above).
|
||
}
|
||
|
||
@defproc[(stepper/seed [reductions reduction-relation?]
|
||
[seed (cons/c any/c (listof any/c))]
|
||
[pp (or/c (any -> string)
|
||
(any output-port number (is-a?/c text%) -> void))
|
||
default-pretty-printer])
|
||
void?]{
|
||
|
||
Like @scheme[stepper], this function opens a stepper window, but it
|
||
seeds it with the reduction-sequence supplied in @scheme[seed].
|
||
}
|
||
|
||
@defproc[(term-node-children [tn term-node?]) (listof term-node?)]{
|
||
|
||
Returns a list of the children (ie, terms that this term
|
||
reduces to) of the given node.
|
||
|
||
Note that this function does not return all terms that this
|
||
term reduces to -- only those that are currently in the
|
||
graph.
|
||
}
|
||
|
||
@defproc[(term-node-parents [tn term-node?]) (listof term-node?)]{
|
||
|
||
Returns a list of the parents (ie, terms that reduced to the
|
||
current term) of the given node.
|
||
|
||
Note that this function does not return all terms that
|
||
reduce to this one -- only those that are currently in the
|
||
graph.
|
||
}
|
||
@defproc[(term-node-labels [tn term-node]) (listof (or/c false/c string?))]{
|
||
|
||
Returns a list of the names of the reductions that led to
|
||
the given node, in the same order as the result of
|
||
term-node-parents. If the list contains @scheme[#f], that means that
|
||
the corresponding step does not have a label.
|
||
}
|
||
|
||
@defproc[(term-node-set-color! [tn term-node?] [color (or/c string? (is-a?/c color%) false/c)]) void?]{
|
||
|
||
Changes the highlighting of the node; if its second argument
|
||
is @scheme[#f], the coloring is removed, otherwise the color is set
|
||
to the specified @scheme[color%] object or the color named by the
|
||
string. The @scheme[color-database<%>] is used to convert the string
|
||
to a @scheme[color%] object.
|
||
}
|
||
|
||
@defproc[(term-node-set-red! [tn term-node?] [red? boolean?]) void?]{
|
||
|
||
Changes the highlighting of the node; if its second argument
|
||
is @scheme[#t], the term is colored pink, if it is @scheme[#f], the term is
|
||
not colored specially.
|
||
|
||
}
|
||
|
||
@defproc[(term-node-expr [tn term-node?]) any]{
|
||
|
||
Returns the expression in this node.
|
||
}
|
||
|
||
@defproc[(term-node? [v any/c]) boolean?]{
|
||
|
||
Recognizes term nodes.
|
||
}
|
||
|
||
@defparam[reduction-steps-cutoff cutoff number?]{
|
||
|
||
A parameter that controls how many steps the @scheme[traces] function
|
||
takes before stopping.
|
||
}
|
||
|
||
@defparam[initial-font-size size number?]{
|
||
|
||
A parameter that controls the initial font size for the terms shown
|
||
in the GUI window.
|
||
}
|
||
|
||
@defparam[initial-char-width width number?]{
|
||
|
||
A parameter that determines the initial width of the boxes
|
||
where terms are displayed (measured in characters) for both
|
||
the stepper and traces.
|
||
}
|
||
|
||
@deftogether[[
|
||
@defparam[dark-pen-color color (or/c string? (is-a?/c color<%>))]{}
|
||
@defparam[dark-brush-color color (or/c string? (is-a?/c color<%>))]{}
|
||
@defparam[light-pen-color color (or/c string? (is-a?/c color<%>))]{}
|
||
@defparam[light-brush-color color (or/c string? (is-a?/c color<%>))]{}]]{
|
||
|
||
These four parameters control the color of the edges in the graph.
|
||
}
|
||
|
||
@defproc[(default-pretty-printer [v any] [port output-port] [width number] [text (is-a?/c text%)]) void?]{
|
||
|
||
This is the default value of @scheme[pp] used by @scheme[traces] and
|
||
@scheme[stepper] and it uses
|
||
@scheme[pretty-print].
|
||
|
||
It sets the @scheme[pretty-print-columns] parameter to
|
||
@scheme[width], and it sets @scheme[pretty-print-size-hook]
|
||
and @scheme[pretty-print-print-hook] to print holes and the
|
||
symbol @scheme['hole] to match the way they are input in a
|
||
@scheme[term] expression.
|
||
|
||
}
|
||
|
||
@section{Typesetting}
|
||
|
||
@defmodule[redex/pict]
|
||
|
||
The @schememodname[redex/pict] library provides functions
|
||
designed to automatically typeset grammars, reduction
|
||
relations, and metafunction written with plt redex.
|
||
|
||
Each grammar, reduction relation, and metafunction can be
|
||
saved in a .ps file (as encapsulated postscript), or can be
|
||
turned into a pict for viewing in the REPL or using with
|
||
Slideshow (see
|
||
@other-manual['(lib "scribblings/slideshow/slideshow.scrbl")]).
|
||
|
||
@subsection{Picts & PostScript}
|
||
|
||
This section documents two classes of operations, one for
|
||
direct use of creating postscript figures for use in papers
|
||
and for use in DrScheme to easily adjust the typesetting:
|
||
@scheme[render-language],
|
||
@scheme[render-reduction-relation],
|
||
@scheme[render-metafunction], and
|
||
@scheme[render-lw],
|
||
and one
|
||
for use in combination with other libraries that operate on picts
|
||
@scheme[language->pict],
|
||
@scheme[reduction-relation->pict],
|
||
@scheme[metafunction->pict], and
|
||
@scheme[lw->pict].
|
||
The primary difference between these functions is that the former list
|
||
sets @scheme[dc-for-text-size] and the latter does not.
|
||
|
||
@defproc[(render-language [lang compiled-lang?]
|
||
[file (or/c false/c path-string?) #f]
|
||
[#:nts nts (or/c false/c (listof (or/c string? symbol?)))
|
||
(render-language-nts)])
|
||
(if file void? pict?)]{
|
||
|
||
Renders a language. If @scheme[file] is @scheme[#f],
|
||
it produces a pict; if @scheme[file] is a path, it saves
|
||
Encapsulated PostScript in the provided filename. See
|
||
@scheme[render-language-nts] for information on the
|
||
@scheme[nts] argument.
|
||
|
||
This function parameterizes @scheme[dc-for-text-size] to install a
|
||
relevant dc: a @scheme[bitmap-dc%] or a @scheme[post-script-dc%], depending on
|
||
whether @scheme[file] is a path.
|
||
|
||
See @scheme[language->pict] if you are using Slideshow or
|
||
are otherwise setting @scheme[dc-for-text-size]. }
|
||
|
||
@defproc[(language->pict (lang compiled-lang?)
|
||
[#:nts nts (or/c false/c (listof (or/c string? symbol?)))
|
||
(render-language-nts)])
|
||
pict?]{
|
||
|
||
Produce a pict like @scheme[render-language], but without
|
||
adjust @scheme[dc-for-text-size].
|
||
|
||
This function is primarily designed to be used with
|
||
Slideshow or with other tools that combine picts together.
|
||
}
|
||
|
||
@defproc[(render-reduction-relation [rel reduction-relation?]
|
||
[file (or/c false/c path-string?) #f]
|
||
[#:style style reduction-rule-style/c (rule-pict-style)])
|
||
(if file void? pict?)]{
|
||
|
||
Renders a reduction relation. If @scheme[file] is @scheme[#f],
|
||
it produces a pict; if @scheme[file] is a path, it saves
|
||
Encapsulated PostScript in the provided filename. See
|
||
@scheme[rule-pict-style] for information on the
|
||
@scheme[style] argument.
|
||
|
||
This function parameterizes @scheme[dc-for-text-size] to install a
|
||
relevant dc: a @scheme[bitmap-dc%] or a @scheme[post-script-dc%], depending on
|
||
whether @scheme[file] is a path. See also
|
||
@scheme[reduction-relation->pict].
|
||
|
||
}
|
||
|
||
@defproc[(reduction-relation->pict (r reduction-relation?)
|
||
[#:style style reduction-rule-style/c (rule-pict-style)])
|
||
pict?]{
|
||
|
||
Produces a pict like @scheme[render-reduction-relation], but
|
||
without setting @scheme[dc-for-text-size].
|
||
|
||
This function is
|
||
primarily designed to be used with Slideshow or with
|
||
other tools that combine picts together.
|
||
}
|
||
|
||
@deftogether[[
|
||
@defform[(render-metafunction metafunction-name)]{}
|
||
@defform/none[#:literals (render-metafunction)
|
||
(render-metafunction metafunction-name filename)]{}]]{
|
||
|
||
If provided with one argument, @scheme[render-metafunction]
|
||
produces a pict that renders properly in the definitions
|
||
window in DrScheme. If given two argument, it writes
|
||
postscript into the file named by @scheme[filename] (which
|
||
may be either a string or bytes).
|
||
|
||
This function sets @scheme[dc-for-text-size]. See also
|
||
@scheme[metafunction->pict].
|
||
}
|
||
|
||
@defform[(metafunction->pict metafunction-name)]{
|
||
This produces a pict, but without setting @scheme[dc-for-text-size].
|
||
It is suitable for use in Slideshow or other libraries that combine
|
||
picts.
|
||
}
|
||
|
||
@subsection{Customization}
|
||
|
||
@defparam[render-language-nts nts (or/c false/c (listof symbol?))]{
|
||
The value of this parameter controls which non-terminals
|
||
@scheme[render-language] and @scheme[language->pict] render by default. If it
|
||
is @scheme[#f] (the default), all non-terminals are rendered.
|
||
If it is a list of symbols, only the listed symbols are rendered.
|
||
|
||
See also @scheme[language-nts].
|
||
}
|
||
|
||
@defparam[extend-language-show-union show? boolean?]{
|
||
|
||
If this is #t, then a language constructed with
|
||
extend-language is shown as if the language had been
|
||
constructed directly with `language'. If it is #f, then only
|
||
the last extension to the language is shown (with
|
||
four-period ellipses, just like in the concrete syntax).
|
||
|
||
Defaultly @scheme[#f].
|
||
|
||
Note that the #t variant can look a little bit strange if
|
||
@scheme[....] are used and the original version of the language has
|
||
multi-line right-hand sides.
|
||
}
|
||
|
||
@defparam[render-reduction-relation-rules rules (or/c false/c (listof (or/c symbol? string?)))]{
|
||
This parameter controls which rules in a reduction relation
|
||
will be rendered.
|
||
}
|
||
|
||
@defparam[rule-pict-style style reduction-rule-style/c]{
|
||
|
||
This parameter controls the style used by default for the reduction
|
||
relation. It can be @scheme['horizontal], where the left and
|
||
right-hand sides of the reduction rule are beside each other
|
||
or @scheme['vertical], where the left and right-hand sides of the
|
||
reduction rule are above each other.
|
||
The @scheme['compact-vertical] style moves the reduction arrow
|
||
to the second line and uses less space between lines.
|
||
Finally, in the @scheme['vertical-overlapping-side-conditions] variant, the side-conditions don't contribute to
|
||
the width of the pict, but are just overlaid on the second
|
||
line of each rule.
|
||
}
|
||
|
||
@defthing[reduction-rule-style/c flat-contract?]{
|
||
|
||
A contract equivalent to
|
||
|
||
@schemeblock[
|
||
(symbols 'vertical
|
||
'compact-vertical
|
||
'vertical-overlapping-side-conditions
|
||
'horizontal)
|
||
]}
|
||
|
||
@defparam[arrow-space space natural-number/c]{
|
||
|
||
This parameter controls the amount of extra horizontal space
|
||
around the reduction relation arrow. Defaults to 0.
|
||
}
|
||
|
||
@defparam[horizontal-label-space space natural-number/c]{
|
||
|
||
This parameter controls the amount of extra space before the
|
||
label on each rule, but only in horizontal mode. Defaults to
|
||
0.
|
||
}
|
||
|
||
@defparam[metafunction-pict-style style (parameter/c (symbols 'left-right 'up-down))]{
|
||
|
||
This parameter controls the style used for typesetting
|
||
metafunctions. The 'left-right style means that the
|
||
results of calling the metafunction are displayed to the
|
||
right of the arguments and the 'up-down style means that
|
||
the results are displayed below the arguments.
|
||
}
|
||
|
||
@deftogether[[
|
||
@defparam[label-style style text-style/c]{}
|
||
@defparam[literal-style style text-style/c]{}
|
||
@defparam[metafunction-style style text-style/c]{}
|
||
@defparam[non-terminal-style style text-style/c]{}
|
||
@defparam[non-terminal-subscript-style style text-style/c]{}
|
||
@defparam[default-style style text-style/c]{}]]{
|
||
|
||
These parameters determine the font used for various text in
|
||
the picts. See `text' in the texpict collection for
|
||
documentation explaining text-style/c. One of the more
|
||
useful things it can be is one of the symbols 'roman,
|
||
'swiss, or 'modern, which are a serif, sans-serif, and
|
||
monospaced font, respectively. (It can also encode style
|
||
information, too.)
|
||
|
||
The label-style is used for the reduction rule label
|
||
names. The literal-style is used for names that aren't
|
||
non-terminals that appear in patterns. The
|
||
metafunction-style is used for the names of
|
||
metafunctions. The non-terminal-style is for non-terminals
|
||
and non-terminal-subscript-style is used for the portion
|
||
after the underscore in non-terminal references.
|
||
|
||
The default-style is used for parenthesis, the dot in dotted
|
||
lists, spaces, the separator words in the grammar, the
|
||
"where" and "fresh" in side-conditions, and other places
|
||
where the other parameters aren't used.
|
||
}
|
||
|
||
@deftogether[[
|
||
@defparam[label-font-size size (and/c (between/c 1 255) integer?)]{}
|
||
@defparam[metafunction-font-size size (and/c (between/c 1 255) integer?)]{}
|
||
@defparam[default-font-size size (and/c (between/c 1 255) integer?)]{}]]{
|
||
|
||
These parameters control the various font sizes. The
|
||
default-font-size is used for all of the font sizes except
|
||
labels and metafunctions.
|
||
}
|
||
|
||
@defparam[reduction-relation-rule-separation sep (parameter/c (and/c integer? positive? exact?))]{
|
||
|
||
Controls the amount of space between clauses in a reduction
|
||
relation. Defaults to 4.
|
||
}
|
||
|
||
@defparam[curly-quotes-for-strings on? boolean?]{
|
||
|
||
Controls if the open and close quotes for strings are turned
|
||
into “ and ” or are left as merely ".
|
||
|
||
Defaults to #t.
|
||
}
|
||
|
||
@defparam[current-text proc (-> string? text-style/c number? pict?)]{
|
||
|
||
This parameter's function is called whenever Redex typesets
|
||
some part of a grammar, reduction relation, or
|
||
metafunction. It defaults to slideshow's @scheme[text] function.
|
||
}
|
||
|
||
@defparam[set-arrow-pict! proc (-> symbol? (-> pict?) void?)]{
|
||
|
||
This functions sets the pict for a given reduction-relation
|
||
symbol. When typesetting a reduction relation that uses the
|
||
symbol, the thunk will be invoked to get a pict to render
|
||
it. The thunk may be invoked multiple times when rendering a
|
||
single reduction relation.
|
||
}
|
||
|
||
@defparam[white-bracket-sizing proc (-> string? number? (values number? number? number? number?))]{
|
||
|
||
This parameter is used when typesetting metafunctions to
|
||
determine how to create the @"\u301a\u301b"
|
||
characters. Rather than using those characters directory
|
||
(since glyphs tend not to be available in PostScript
|
||
fonts), they are created by combining two ‘[’ characters
|
||
or two ‘]’ characters together.
|
||
|
||
The procedure accepts a string that is either @scheme["["]
|
||
or @scheme["]"], and returns four numbers. The first two
|
||
numbers determine the offset (from the left and from the
|
||
right respectively) for the second square bracket, and the
|
||
second two two numbers determine the extra space added (to
|
||
the left and to the right respectively).
|
||
|
||
The default value of the parameter is: @schemeblock[
|
||
(λ (str size)
|
||
(let ([inset-amt (floor/even (max 4 (* size 1/2)))])
|
||
(cond
|
||
[(equal? str "[")
|
||
(values inset-amt
|
||
0
|
||
0
|
||
(/ inset-amt 2))]
|
||
[else
|
||
(values 0
|
||
inset-amt
|
||
(/ inset-amt 2)
|
||
0)])))]
|
||
|
||
where @scheme[floor/even] returns the nearest even number
|
||
below its argument. This means that for sizes 9, 10, and
|
||
11, @scheme[inset-amt] will be 4, and for 12, 13, 14, and
|
||
15, @scheme[inset-amt] will be 6.
|
||
|
||
}
|
||
|
||
@deftech{Removing the pink background from PLT Redex rendered picts and ps files}
|
||
|
||
When reduction rules, a metafunction, or a grammar contains
|
||
unquoted Scheme code or side-conditions, they are rendered
|
||
with a pink background as a guide to help find them and
|
||
provide alternative typesettings for them. In general, a
|
||
good goal for a PLT Redex program that you intend to typeset
|
||
is to only include such things when they correspond to
|
||
standard mathematical operations, and the Scheme code is an
|
||
implementation of those operations.
|
||
|
||
To replace the pink code, use:
|
||
|
||
@defform[(with-unquote-rewriter proc expression)]{
|
||
|
||
It installs @scheme[proc] the current unqoute rewriter and
|
||
evaluates expression. If that expression computes any picts,
|
||
the unquote rewriter specified is used to remap them.
|
||
|
||
The @scheme[proc] should be a function of one argument. It receives
|
||
a lw struct as an argument and should return
|
||
another lw that contains a rewritten version of the
|
||
code.
|
||
}
|
||
|
||
@defform[(with-atomic-rewriter name-symbol string-or-thunk-returning-pict expression)]{
|
||
|
||
This extends the current set of atomic-rewriters with one
|
||
new one that rewrites the value of name-symbol to
|
||
string-or-pict-returning-thunk (applied, in the case of a
|
||
thunk), during the evaluation of expression.
|
||
|
||
@scheme[name-symbol] is expected to evaluate to a symbol. The value
|
||
of string-or-thunk-returning-pict is used whever the symbol
|
||
appears in a pattern.
|
||
}
|
||
|
||
@defform[(with-compound-rewriter name-symbol proc expression)]{
|
||
|
||
This extends the current set of compound-rewriters with one
|
||
new one that rewrites the value of name-symbol via proc,
|
||
during the evaluation of expression.
|
||
|
||
@scheme[name-symbol] is expected to evaluate to a symbol. The value
|
||
of proc is called with a (listof lw) -- see below
|
||
for details on the shape of lw, and is expected to
|
||
return a new (listof (union lw string pict)),
|
||
rewritten appropriately.
|
||
|
||
The list passed to the rewriter corresponds to the
|
||
lw for the sequence that has name-symbol's value at
|
||
its head.
|
||
|
||
The result list is constrained to have at most 2 adjacent
|
||
non-lws. That list is then transformed by adding
|
||
lw structs for each of the non-lws in the
|
||
list (see the description of lw below for an
|
||
explanation of logical-space):
|
||
|
||
@itemize{
|
||
@item{
|
||
If there are two adjacent lws, then the logical
|
||
space between them is filled with whitespace.}
|
||
|
||
@item{
|
||
If there is a pair of lws with just a single
|
||
non-lw between them, a lw will be
|
||
created (containing the non-lw) that uses all
|
||
of the available logical space between the lws.
|
||
}
|
||
|
||
@item{
|
||
If there are two adjacent non-lws between two
|
||
lws, the first non-lw is rendered
|
||
right after the first lw with a logical space
|
||
of zero, and the second is rendered right before the
|
||
last lw also with a logical space of zero, and
|
||
the logical space between the two lws is
|
||
absorbed by a new lw that renders using no
|
||
actual space in the typeset version.
|
||
}}
|
||
}
|
||
|
||
|
||
@subsection{LW}
|
||
|
||
@deftogether[[
|
||
@defproc[(build-lw [e (or/c string?
|
||
symbol?
|
||
pict?
|
||
(listof (or/c (symbols 'spring) lw?)))]
|
||
[line exact-positive-integer?]
|
||
[line-span exact-positive-integer?]
|
||
[column exact-positive-integer?]
|
||
[column-span exact-positive-integer?])
|
||
lw?]{}
|
||
@defproc[(lw-e (lw lw?)) (or/c string?
|
||
symbol?
|
||
pict?
|
||
(listof (or/c (symbols 'spring) lw?)))]{}
|
||
@defproc[(lw-line (lw lw?)) exact-positive-integer?]{}
|
||
@defproc[(lw-line-span (lw lw?)) exact-positive-integer?]{}
|
||
@defproc[(lw-column (lw lw?)) exact-positive-integer?]{}
|
||
@defproc[(lw-column-span (lw lw?)) exact-positive-integer?]{}
|
||
@defproc[(lw? (v any/c)) boolean?]{}
|
||
@defidform[lw]{}]]{
|
||
|
||
The lw data structure corresponds represents a pattern or a Scheme
|
||
expression that is to be typeset. The functions listed above
|
||
construct @scheme[lw] structs, select fields out of them, and
|
||
recognize them. The @scheme[lw] binding can be used with
|
||
@scheme[copy-struct].
|
||
}
|
||
|
||
@defform[(to-lw arg)]{
|
||
|
||
This form turns its argument into lw structs that
|
||
contain all of the spacing information just as it would appear
|
||
when being used to typeset.
|
||
|
||
Each sub-expression corresponds to its own lw, and
|
||
the element indicates what kind of subexpression it is. If
|
||
the element is a list, then the lw corresponds to a
|
||
parenthesized sequence, and the list contains a lw
|
||
for the open paren, one lw for each component of the
|
||
sequence and then a lw for the close
|
||
parenthesis. In the case of a dotted list, there will also
|
||
be a lw in the third-to-last position for the dot.
|
||
|
||
For example, this expression:
|
||
|
||
@schemeblock[(a)]
|
||
|
||
becomes this lw (assuming the above expression
|
||
appears as the first thing in the file):
|
||
|
||
@schemeblock[
|
||
(build-lw (list (build-lw "(" 0 0 0 1)
|
||
(build-lw 'a 0 0 1 1)
|
||
(build-lw ")" 0 0 2 1))
|
||
0 0 0 3)
|
||
]
|
||
|
||
If there is some whitespace in the sequence, like this one:
|
||
|
||
@schemeblock[
|
||
(a b)
|
||
]
|
||
|
||
then there is no lw that corresponds to that
|
||
whitespace; instead there is a logical gap between the
|
||
lws.
|
||
|
||
@schemeblock[
|
||
(build-lw (list (build-lw "(" 0 0 0 1)
|
||
(build-lw 'a 0 0 1 1)
|
||
(build-lw 'b 0 0 3 1)
|
||
(build-lw ")" 0 0 4 1))
|
||
0 0 0 5)
|
||
]
|
||
|
||
In general, identifiers are represented with symbols and
|
||
parenthesis are represented with strings and picts can be
|
||
inserted to render arbitrary pictures.
|
||
|
||
The line, line-span, column, and column-span correspond to
|
||
the logical spacing for the redex program, not the actual
|
||
spacing that will be used when they are rendered. The
|
||
logical spacing is only used when determining where to place
|
||
typeset portions of the program. In the absense of any
|
||
rewriters, these numbers correspond to the line and column
|
||
numbers in the original program.
|
||
|
||
The line and column are absolute numbers from the beginning
|
||
of the file containing the expression. The column number is
|
||
not necessarily the column of the open parenthesis in a
|
||
sequence -- it is the leftmost column that is occupied by
|
||
anything in the sequence. The line-span is the number of
|
||
lines, and the column span is the number of columns on the
|
||
last line (not the total width).
|
||
|
||
When there are multiple lines, lines are aligned based on
|
||
the logical space (ie, the line/column &
|
||
line-span/column-span) fields of the lws. As an
|
||
example, if this is the original pattern:
|
||
|
||
@schemeblock[
|
||
(all good boys
|
||
deserve fudge)
|
||
]
|
||
|
||
then the leftmost edges of the words "good" and "deserve"
|
||
will be lined up underneath each other, but the relative
|
||
positions of "boys" and "fudge" will be determined by the
|
||
natural size of the words as they rendered in the
|
||
appropriate font.
|
||
|
||
When @scheme['spring] appears in the list in the @scheme[e]
|
||
field of a @scheme[lw] struct, then it absorbs all of the
|
||
space around it. It is also used by @scheme[to-lw] when
|
||
constructing the picts for unquoted strings. For example, this expression
|
||
|
||
@schemeblock[,x]
|
||
|
||
corresponds to these structs:
|
||
|
||
@schemeblock[(build-lw (list (build-lw "" 1 0 9 0)
|
||
'spring
|
||
(build-lw x 1 0 10 1))
|
||
1 0 9 2)]
|
||
|
||
and the @scheme['spring] causes there to be no space between
|
||
the empty string and the @scheme[x] in the typeset output.
|
||
|
||
}
|
||
|
||
@defproc[(render-lw (language/nts (or/c (listof symbol?) compiled-lang?))
|
||
(lw lw?)) pict?]{
|
||
|
||
Produces a pict that corresponds to the @scheme[lw] object
|
||
argument, using @scheme[language/nts] to determine which
|
||
of the identifiers in the @scheme[lw] argument are
|
||
non-terminals.
|
||
|
||
This function sets @scheme[dc-for-text-size]. See also
|
||
@scheme[lw->pict].
|
||
}
|
||
|
||
@defproc[(lw->pict (language/ntw (or/c (listof symbol?) compiled-lang?))
|
||
(lw lw?)) pict?]{
|
||
|
||
Produces a pict that corresponds to the @scheme[lw] object
|
||
argument, using @scheme[language/nts] to determine which
|
||
of the identifiers in the @scheme[lw] argument are
|
||
non-terminals.
|
||
|
||
This does not set the @scheme[dc-for-text-size] parameter. See also
|
||
@scheme[render-lw].
|
||
}
|
||
|
||
@deftogether[[
|
||
@defproc[(just-before [stuff (or/c pict? string? symbol?)]
|
||
[lw lw?])
|
||
lw?]{}
|
||
@defproc[(just-after [stuff (or/c pict? string? symbol?)]
|
||
[lw lw?])
|
||
lw?]{}]]{
|
||
|
||
These two helper functions build new lws whose contents are
|
||
the first argument, and whose line and column are based on
|
||
the second argument, making the new loc wrapper be either
|
||
just before or just after that argument. The line-span and
|
||
column-span of the new lw is always zero.
|
||
}
|
||
|
||
|
||
@index-section[]
|
||
|