
Added a second form of `generate-term' that produces a procedure. Improved the docs for `generate-term'. svn: r17853
2247 lines
83 KiB
Racket
2247 lines
83 KiB
Racket
#lang scribble/doc
|
||
@(require scribble/manual
|
||
scribble/bnf
|
||
scribble/struct
|
||
scribble/eval
|
||
(for-syntax scheme/base)
|
||
(for-label scheme/base
|
||
scheme/gui
|
||
scheme/pretty
|
||
scheme/contract
|
||
mrlib/graph
|
||
(only-in slideshow/pict pict? text dc-for-text-size text-style/c)
|
||
redex))
|
||
|
||
@(define-syntax (defpattech stx)
|
||
(syntax-case stx ()
|
||
[(_ arg)
|
||
(identifier? #'arg)
|
||
(let ([as (symbol->string (syntax-e #'arg))])
|
||
#`(index '("Redex Pattern" #,as) (deftech #:style? #f @scheme[arg])))]))
|
||
|
||
@(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 (schemevarfont "pattern")) args ...)]
|
||
[x (identifier? #'x) #'(tech (schemevarfont "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 (schemevarfont "term")) args ...)]
|
||
[x (identifier? #'x) #'(tech (schemevarfont "term"))]))
|
||
|
||
@(define-syntax (tterm stx)
|
||
(syntax-case stx ()
|
||
[(_ args ...)
|
||
#'((tech "term") args ...)]
|
||
[x (identifier? #'x) #'(tech "term")]))
|
||
|
||
@(define-syntax-rule (arrows a0 a ...)
|
||
(make-blockquote #f
|
||
(list (make-paragraph
|
||
(list (schemeidfont (make-element #f (list (symbol->string 'a0))))
|
||
(make-element #f (list " " (hspace 1) " " (schemeidfont (symbol->string 'a)))) ...)))))
|
||
|
||
@(define redex-eval (make-base-eval))
|
||
@(interaction-eval #:eval redex-eval (require redex/reduction-semantics))
|
||
|
||
@title{@bold{Redex}: Practical Semantics Engineering}
|
||
|
||
@author["Robert Bruce Findler" "Casey Klein"]
|
||
|
||
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 many
|
||
of Redex's forms.
|
||
|
||
Note that pattern matching is caching (including caching the results
|
||
of side-conditions). This means that once a pattern has matched a
|
||
given term, Redex assumes that it will always match that term.
|
||
|
||
This is the grammar for the Redex pattern language. Non-terminal
|
||
references are wrapped with angle brackets; otherwise identifiers
|
||
in the grammar are terminals.
|
||
|
||
@(schemegrammar* #;#:literals #;(any number string variable variable-except variable-prefix variable-not-otherwise-mentioned hole hide-hole name in-hole side-condition cross)
|
||
[pattern any
|
||
number
|
||
natural
|
||
integer
|
||
real
|
||
string
|
||
variable
|
||
(variable-except <id> ...)
|
||
(variable-prefix <id>)
|
||
variable-not-otherwise-mentioned
|
||
hole
|
||
symbol
|
||
(name <id> <pattern>)
|
||
(in-hole <pattern> <pattern>)
|
||
(hide-hole <pattern>)
|
||
(side-condition <pattern> guard)
|
||
(cross <id>)
|
||
(<pattern-sequence> ...)
|
||
<scheme-constant>]
|
||
[pattern-sequence
|
||
<pattern>
|
||
(code:line ... (code:comment "literal ellipsis"))
|
||
..._id])
|
||
|
||
@itemize[
|
||
|
||
@item{The @defpattech[any] @pattern matches any sexpression.
|
||
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[natural] @pattern matches any exact
|
||
non-negative integer.
|
||
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[integer] @pattern matches any exact integer.
|
||
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[real] @pattern matches any real 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
|
||
the first argument to an @pattech[in-hole] @|pattern|. Otherwise,
|
||
it matches only a 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 non-terminal appears
|
||
twice in a single pattern, then the match is constrained
|
||
to expressions that are the same, unless the pattern is part
|
||
of a grammar, in which case there is no constraint.
|
||
|
||
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 @ttpattern and binds using it to the name @tt{symbol}.
|
||
}
|
||
|
||
@item{The @tt{(@defpattech[in-hole] @ttpattern @ttpattern)} @pattern
|
||
matches the first @|ttpattern|. This match must include exactly one match
|
||
against the second @|ttpattern|. If there are zero matches or more
|
||
than one match, an exception is raised.
|
||
|
||
When matching the first argument of in-hole, the @scheme[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 @ttpattern matches but if the @pattern matcher is
|
||
looking for a decomposition, it ignores any holes found in
|
||
that @|ttpattern|.
|
||
}
|
||
|
||
@item{The @tt{(@defpattech[side-condition] @ttpattern guard)} @pattern
|
||
matches what the embedded @ttpattern 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. Any
|
||
occurrences of @scheme[name] in the @pattern (including those implicitly
|
||
there via @tt{_} pattersn) 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 @scheme[symbol], the @pattern @scheme[(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/c])]{
|
||
|
||
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.
|
||
}
|
||
|
||
@defparam[caching-enabled? on? boolean?]{
|
||
When this parameter is @scheme[#t] (the default), Redex caches the results of
|
||
pattern matching and metafunction evaluation. There is a separate cache for
|
||
each pattern and metafunction; when one fills (see @scheme[set-cache-size!]),
|
||
Redex evicts all of the entries in that cache.
|
||
|
||
Caching should be disabled when matching a pattern that depends on values
|
||
other than the in-scope pattern variables or evaluating a metafunction
|
||
that reads or writes mutable external state.
|
||
}
|
||
|
||
@defproc[(set-cache-size! [size positive-integer?]) void?]{
|
||
Changes the size of the per-pattern and per-metafunction caches. The default size is @scheme[350].
|
||
}
|
||
|
||
@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 unquote unquote-splicing)
|
||
[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 @scheme[_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 @scheme[(_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 @scheme[(in-hole @|tttterm| @|tttterm|)]
|
||
is the dual to the @pattern @scheme[in-hole] -- it accepts
|
||
a context and an expression and uses @scheme[plug] to combine
|
||
them.}
|
||
|
||
@item{A term written @scheme[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.
|
||
|
||
It behaves similarly to @scheme[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.
|
||
}
|
||
|
||
@defidform[hole]{ Recognized specially within
|
||
@scheme[term]. A @scheme[hole] form is an
|
||
error elsewhere. }
|
||
|
||
@defidform[in-hole]{ Recognized specially within
|
||
@scheme[reduction-relation]. An @scheme[in-hole] form is an
|
||
error elsewhere. }
|
||
|
||
@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 @|tttterm|
|
||
syntactic form.
|
||
|
||
Note that each 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).
|
||
|
||
This form is a lower-level form in Redex, and not really designed to
|
||
be used directly. If you want a @scheme[let]-like form that uses
|
||
Redex's full pattern matching facilities, see @scheme[term-match] and
|
||
@scheme[term-match/single].
|
||
|
||
}
|
||
|
||
@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.
|
||
|
||
When evaluating a @scheme[term-match] expression, the patterns are
|
||
compiled in an effort to speed up matching. Using the procedural
|
||
result multiple times to avoid compiling the patterns multiple times.
|
||
}
|
||
|
||
@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.
|
||
|
||
When evaluating a @scheme[term-match/single] expression, the patterns
|
||
are compiled in an effort to speed up matching. Using the procedural
|
||
result multiple times to avoid compiling the patterns multiple times.
|
||
}
|
||
|
||
@defproc[(plug [context any/c] [expression any/c]) 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/c] [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/c] [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 @scheme[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
|
||
(v .... (code:comment "extend the previous `v' non-terminal")
|
||
+
|
||
number)
|
||
(x (variable-except lambda +)))
|
||
]
|
||
|
||
extends lc-lang with two new alternatives for the @scheme[v]
|
||
non-terminal, carries forward the @scheme[e] and @scheme[c]
|
||
non-terminals, and replaces the @scheme[x] non-terminal with a
|
||
new one (which happens to be equivalent to the one that would
|
||
have been inherited).
|
||
|
||
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 @scheme[#t] if its argument was produced by @scheme[language], @scheme[#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 domain main-arrow reduction-case ...)
|
||
([domain (code:line) (code:line #:domain @#,ttpattern)]
|
||
[main-arrow (code:line) (code:line #:arrow arrow)]
|
||
[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[-->] (or with @scheme[arrow], if
|
||
specified). 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 variable does not have to be
|
||
a non-terminal in the language of the reduction relation.)
|
||
|
||
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 @scheme[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 @scheme[var2] is used to
|
||
determine the number of variables generated and @scheme[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 @pattech[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 @scheme[-->], those clauses can use any identifier
|
||
for an arrow, as long as the identifier is bound after the
|
||
@scheme[with] clause.
|
||
|
||
Each of the clauses after the @scheme[with] define new relations
|
||
in terms of other definitions after the @scheme[with] clause or in
|
||
terms of the main @scheme[-->] relation.
|
||
|
||
A @scheme[fresh] variable 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 @scheme[more]. They should
|
||
have the same shape as the rules (including the @scheme[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
|
||
@scheme[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/c]) (listof any/c)]{
|
||
|
||
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/c])
|
||
(listof any/c)]{
|
||
|
||
The function @scheme[apply-reduction-relation*] accepts a reduction relation and a
|
||
term. Starting from @scheme[t], it follows every reduction
|
||
path and returns all of the terms that do not reduce further.
|
||
If there are infinite reduction
|
||
sequences that do not repeat, this function will not
|
||
terminate (it does terminate if the only infinite reduction paths are cyclic).
|
||
}
|
||
|
||
@defidform[-->]{ Recognized specially within
|
||
@scheme[reduction-relation]. A @scheme[-->] form is an
|
||
error elsewhere. }
|
||
|
||
@defidform[fresh]{ Recognized specially within
|
||
@scheme[reduction-relation]. A @scheme[fresh] form is an
|
||
error elsewhere. }
|
||
|
||
@defidform[with]{ Recognized specially within
|
||
@scheme[reduction-relation]. A @scheme[with] form is an
|
||
error elsewhere. }
|
||
|
||
@section{Metafunctions and 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 (: ->)
|
||
(define-metafunction language
|
||
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 @|tttterm|.
|
||
|
||
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 @|ttpattern| 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
|
||
(and that leads to different results for the different matches),
|
||
or if the contract is violated.
|
||
|
||
Note that metafunctions are assumed to always return the same results
|
||
for the same inputs, and their results are cached, unless
|
||
@scheme[caching-enabled?] is set to @scheme[#f]. Accordingly, if a
|
||
metafunction is called with the same inputs twice, then its body is
|
||
only evaluated a single time.
|
||
|
||
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 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 f language
|
||
contract
|
||
[(g @#,ttpattern ...) @#,tttterm extras ...]
|
||
...)]{
|
||
|
||
Defines a metafunction @scheme[g] as an extension of an existing
|
||
metafunction @scheme[f]. The metafunction @scheme[g] behaves as
|
||
if @scheme[f]'s clauses were appended to its definition (with
|
||
occurrences of @scheme[f] changed to @scheme[g] in the inherited
|
||
clauses).
|
||
}
|
||
|
||
For example, @scheme[define-metafunction/extension] may be used to extend
|
||
the free-vars function above to the forms introduced by the language
|
||
lc-num-lang.
|
||
|
||
@schemeblock[
|
||
(define-metafunction/extension free-vars lc-num-lang
|
||
free-vars-num : e -> (x ...)
|
||
[(free-vars-num number)
|
||
()]
|
||
[(free-vars-num (+ e_1 e_2))
|
||
(∪ (free-vars-num e_1)
|
||
(free-vars-num e_2))])
|
||
]
|
||
|
||
@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.
|
||
}
|
||
|
||
@defform/subs[#:literals ()
|
||
(define-relation language
|
||
[(name @#,ttpattern ...) @#,tttterm ...] ...)
|
||
([tl-pat identifier (tl-pat-ele ...)]
|
||
[tl-pat-ele tl-pat (code:line tl-pat ... (code:comment "a literal ellipsis"))])]{
|
||
|
||
The @scheme[define-relation] form builds a relation 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 @|tttterm|.
|
||
|
||
Relations are like metafunctions in that they are called with
|
||
arguments and return results (unlike in, say, prolog, where a relation
|
||
definition would be able to synthesize some of the arguments based on
|
||
the values of others).
|
||
|
||
Unlike metafunctions, relations check all possible ways to match each
|
||
case, looking for a true result and if none of the clauses match, then
|
||
the result is @scheme[#f]. If there are multiple expressions on
|
||
the right-hand side of a relation, then all of them must be satisfied
|
||
in order for that clause of the relation to be satisfied.
|
||
|
||
Note that relations are assumed to always return the same results for
|
||
the same inputs, and their results are cached, unless
|
||
@scheme[caching-enable?] is set to @scheme[#f]. Accordingly, if a
|
||
relation is called with the same inputs twice, then its right-hand
|
||
sides are evaluated only once.
|
||
}
|
||
|
||
@defparam[current-traced-metafunctions traced-metafunctions (or/c 'all (listof symbol?))]{
|
||
|
||
Controls which metafunctions are currently being traced. If it is
|
||
@scheme['all], all of them are. Otherwise, the elements of the list
|
||
name the metafunctions to trace.
|
||
|
||
Defaults to @scheme['()].
|
||
|
||
}
|
||
|
||
@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/subs[(test-->> reduction-relation maybe-cycles e1 e2 ...)
|
||
([cycles (code:line) #:cycles-ok])]{
|
||
|
||
Tests to see if the value of @scheme[e1] (which should be a term),
|
||
reduces to the @scheme[e2]s under @scheme[reduction-relation]
|
||
(using @scheme[apply-reduction-relation*], so it may not terminate).
|
||
}
|
||
|
||
@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 in a single step, under @scheme[reduction-relation]
|
||
(using @scheme[apply-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.
|
||
}
|
||
|
||
@defform/subs[(make-coverage subject)
|
||
([subject (code:line metafunction)
|
||
(code:line relation-expr)])]{
|
||
Constructs a structure (recognized by @scheme[coverage?])
|
||
to contain per-case test coverage of the supplied metafunction
|
||
or reduction relation. Use with @scheme[relation-coverage] and
|
||
@scheme[covered-cases].
|
||
}
|
||
|
||
@defproc[(coverage? [v any/c]) boolean?]{
|
||
Returns @scheme[#t] for a value produced by @scheme[make-coverage]
|
||
and @scheme[#f] for any other.}
|
||
|
||
@defparam[relation-coverage tracked (listof coverage?)]{
|
||
Redex populates the coverage records in @scheme[tracked] (default @scheme[null]),
|
||
counting the times that tests exercise each case of the associated metafunction
|
||
and relations.}
|
||
|
||
@defproc[(covered-cases
|
||
[c coverage?])
|
||
(listof (cons/c string? natural-number/c))]{
|
||
Extracts the coverage information recorded in @scheme[c], producing
|
||
an association list mapping names (or source locations, in the case of
|
||
metafunctions or unnamed reduction-relation cases) to application counts.}
|
||
|
||
@examples[
|
||
#:eval redex-eval
|
||
(define-language empty-lang)
|
||
|
||
(define-metafunction empty-lang
|
||
[(plus number_1 number_2)
|
||
,(+ (term number_1) (term number_2))])
|
||
|
||
(define equals
|
||
(reduction-relation
|
||
empty-lang
|
||
(--> (+) 0 "zero")
|
||
(--> (+ number) number)
|
||
(--> (+ number_1 number_2 number ...)
|
||
(+ (plus number_1 number_2)
|
||
number ...)
|
||
"add")))
|
||
(let ([equals-coverage (make-coverage equals)]
|
||
[plus-coverage (make-coverage plus)])
|
||
(parameterize ([relation-coverage (list equals-coverage
|
||
plus-coverage)])
|
||
(apply-reduction-relation* equals (term (+ 1 2 3)))
|
||
(values (covered-cases equals-coverage)
|
||
(covered-cases plus-coverage))))]
|
||
|
||
@defform*/subs[[(generate-term language @#,ttpattern size-expr kw-args ...)
|
||
(generate-term language @#,ttpattern)]
|
||
([kw-args (code:line #:attempt-num attempts-expr)
|
||
(code:line #:retries retries-expr)])
|
||
#:contracts ([size-expr natural-number/c]
|
||
[attempt-num-expr natural-number/c]
|
||
[retries-expr natural-number/c])]{
|
||
|
||
In its first form, @scheme[generate-term] produces a random term matching
|
||
the given pattern (according to the given language). In its second,
|
||
@scheme[generate-term] produces a procedure for constructing the same.
|
||
This procedure expects @scheme[size-expr] (below) as its sole positional
|
||
argument and allows the same optional keyword arguments as the first form.
|
||
The second form may be more efficient when generating many terms.
|
||
|
||
The argument @scheme[size-expr] bounds the height of the generated term
|
||
(measured as the height of its parse tree).
|
||
|
||
The optional keyword argument @scheme[attempt-num-expr]
|
||
(default @scheme[1]) provides coarse grained control over the random
|
||
decisions made during generation; increasing @scheme[attempt-num-expr]
|
||
tends to increase the complexity of the result. For example, the expected
|
||
length of @pattech[pattern-sequence]s increases with @scheme[attempt-num-expr].
|
||
|
||
The random generation process does not actively consider the constraints
|
||
imposed by @pattech[side-condition] or @tt{_!_} @|pattern|s; instead,
|
||
it uses a ``guess and check'' strategy in which it freely generates
|
||
candidate terms then tests whether they happen to satisfy the constraints,
|
||
repeating as necessary. The optional keyword argument @scheme[retries-expr]
|
||
(default @scheme[100]) bounds the number of times that
|
||
@scheme[generate-term] retries the generation of any pattern. If
|
||
@scheme[generate-term] is unable to produce a satisfying term after
|
||
@scheme[retries-expr] attempts, it raises an exception recognized by
|
||
@scheme[exn:fail:redex:generation-failure?].}
|
||
|
||
@defform/subs[(redex-check language @#,ttpattern property-expr kw-arg ...)
|
||
([kw-arg (code:line #:attempts attempts-expr)
|
||
(code:line #:source metafunction)
|
||
(code:line #:source relation-expr)
|
||
(code:line #:retries retries-expr)])
|
||
#:contracts ([property-expr any/c]
|
||
[attempts-expr natural-number/c]
|
||
[relation-expr reduction-relation?]
|
||
[retries-expr natural-number/c])]{
|
||
Searches for a counterexample to @scheme[property-expr], interpreted
|
||
as a predicate universally quantified over the pattern variables
|
||
bound by @scheme[pattern]. @scheme[redex-check] constructs and tests
|
||
a candidate counterexample by choosing a random term @math{t} that
|
||
matches @scheme[pattern] then evaluating @scheme[property-expr]
|
||
using the @scheme[match-bindings] produced by @scheme[match]ing
|
||
@math{t} against @scheme[pattern].
|
||
|
||
@scheme[redex-check] generates at most @scheme[attempts-expr] (default @scheme[1000])
|
||
random terms in its search. The size and complexity of terms it generates
|
||
gradually increases with each failed attempt.
|
||
|
||
When passed a metafunction or reduction relation via the optional @scheme[#:source]
|
||
argument, @scheme[redex-check] distributes its attempts across the left-hand sides
|
||
of that metafunction/relation by using those patterns, rather than @scheme[pattern],
|
||
as the basis of its generation. It is an error if any left-hand side generates a
|
||
term that does not match @scheme[pattern].}
|
||
|
||
@examples[
|
||
#:eval redex-eval
|
||
(define-language empty-lang)
|
||
|
||
(random-seed 0)
|
||
|
||
(redex-check
|
||
empty-lang
|
||
((number_1 ...)
|
||
(number_2 ...))
|
||
(equal? (reverse (append (term (number_1 ...))
|
||
(term (number_2 ...))))
|
||
(append (reverse (term (number_1 ...)))
|
||
(reverse (term (number_2 ...))))))
|
||
|
||
(redex-check
|
||
empty-lang
|
||
((number_1 ...)
|
||
(number_2 ...))
|
||
(equal? (reverse (append (term (number_1 ...))
|
||
(term (number_2 ...))))
|
||
(append (reverse (term (number_2 ...)))
|
||
(reverse (term (number_1 ...)))))
|
||
#:attempts 200)
|
||
|
||
(let ([R (reduction-relation
|
||
empty-lang
|
||
(--> (Σ) 0)
|
||
(--> (Σ number) number)
|
||
(--> (Σ number_1 number_2 number_3 ...)
|
||
(Σ ,(+ (term number_1) (term number_2))
|
||
number_3 ...)))])
|
||
(redex-check
|
||
empty-lang
|
||
(Σ number ...)
|
||
(printf "~s\n" (term (number ...)))
|
||
#:attempts 3
|
||
#:source R))]
|
||
|
||
@defform/subs[(check-reduction-relation relation property kw-args ...)
|
||
([kw-arg (code:line #:attempts attempts-expr)
|
||
(code:line #:retries retries-expr)])
|
||
#:contracts ([property (-> any/c any/c)]
|
||
[attempts-expr natural-number/c]
|
||
[retries-expr natural-number/c])]{
|
||
Tests @scheme[relation] as follows: for each case of @scheme[relation],
|
||
@scheme[check-reduction-relation] generates @scheme[attempts] random
|
||
terms that match that case's left-hand side and applies @scheme[property]
|
||
to each random term.
|
||
|
||
This form provides a more convenient notation for
|
||
@schemeblock[(redex-check L any (property (term any))
|
||
#:attempts (* n attempts)
|
||
#:source relation)]
|
||
when @scheme[relation] is a relation on @scheme[L] with @scheme[n] rules.}
|
||
|
||
@defform/subs[(check-metafunction metafunction property kw-args ...)
|
||
([kw-arg (code:line #:attempts attempts-expr)
|
||
(code:line #:retries retries-expr)])
|
||
#:contracts ([property (-> any/c any/c)]
|
||
[attempts-expr natural-number/c]
|
||
[retries-expr natural-number/c])]{
|
||
Like @scheme[check-reduction-relation] but for metafunctions.}
|
||
|
||
@defproc[(exn:fail:redex:generation-failure? [v any/c]) boolean?]{
|
||
Recognizes the exceptions raised by @scheme[generate-term],
|
||
@scheme[redex-check], etc. when those forms are unable to produce
|
||
a term matching some pattern.
|
||
}
|
||
|
||
@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 @scheme[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
|
||
@scheme[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
|
||
(cons/c string
|
||
(and/c (listof (or/c string? (is-a?/c color%)))
|
||
(lambda (x) (member (length x) '(2 3 4 6))))))]
|
||
|
||
[#:scheme-colors? scheme-colors? boolean? #t]
|
||
[#:filter term-filter (-> any/c (or/c #f string?) any/c) (lambda (x y) #t)]
|
||
[#:x-spacing number? 15]
|
||
[#:y-spacing number? 15]
|
||
[#:layout layout (-> (listof term-node?) void) void]
|
||
[#:edge-labels? edge-label-font boolean? #t]
|
||
[#:edge-label-font edge-label-font (or/c #f (is-a?/c font%)) #f]
|
||
[#:graph-pasteboard-mixin graph-pasteboard-mixin (make-mixin-contract graph-pasteboard<%>) values])
|
||
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 @scheme[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-list pairs. The traces gui will color arrows
|
||
drawn because of the given reduction name with the given color instead
|
||
of using the default color.
|
||
|
||
The @scheme[cdr] of each of the elements of @scheme[colors] is a list
|
||
of colors, organized in pairs. The first two colors cover the colors
|
||
of the line and the border around the arrow head, the first when the
|
||
mouse is over a graph node that is connected to that arrow, and the
|
||
second for when the mouse is not over that arrow. Similarly, the next
|
||
colors are for the text drawn on the arrow and the last two are for
|
||
the color that fills the arrow head. If fewer than six colors are
|
||
specified, the colors specified colors are used and then defaults are
|
||
filled in for the remaining colors.
|
||
|
||
The @scheme[scheme-colors?] argument, if @scheme[#t] causes
|
||
@scheme[traces] to color the contents of each of the windows according
|
||
to DrScheme's Scheme mode color Scheme. If it is @scheme[#f],
|
||
@scheme[traces] just uses black for the color scheme.
|
||
In addition, Scheme-mode parenthesis highlighting is
|
||
enabled when @scheme[scheme-colors?]
|
||
is @scheme[#t] and not when it is @scheme[#f].
|
||
|
||
The @scheme[term-filter] function is called each time a new node is
|
||
about to be inserted into the graph. If the filter returns false, the
|
||
node is not inserted into the graph.
|
||
|
||
The @scheme[x-spacing] and @scheme[y-spacing] control the amount of
|
||
space put between the snips in the default layout.
|
||
|
||
The @scheme[layout] argument is called (with all of the terms) when
|
||
new terms is inserted into the window. In general, it is called when
|
||
after new terms are inserted in response to the user clicking on the
|
||
reduce button, and after the initial set of terms is inserted.
|
||
See also @scheme[term-node-set-position!].
|
||
|
||
If @scheme[edge-labels?] is @scheme[#t] (the default), then edge labels
|
||
are drawn; otherwise not.
|
||
|
||
The @scheme[edge-label-font] argument is used as the font on the edge
|
||
labels. If @scheme[#f] is suppled, the @scheme[dc<%>] object's default
|
||
font is used.
|
||
|
||
The traces library an instance of the @schememodname[mrlib/graph]
|
||
library's @scheme[graph-pasteboard<%>] interface to layout
|
||
the graphs. Sometimes, overriding one of its methods can
|
||
help give finer-grained control over the layout, so the
|
||
@scheme[graph-pasteboard-mixin] is applied to the class
|
||
before it is instantiated. Also note that all of the snips
|
||
inserted into the editor by this library have a
|
||
@tt{get-term-node} method which returns the snip's
|
||
@scheme[term-node].
|
||
|
||
}
|
||
|
||
@defproc[(traces/ps [reductions reduction-relation?]
|
||
[expr (or/c any/c (listof any/c))]
|
||
[file (or/c path-string? path?)]
|
||
[#: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)) '()]
|
||
[#:filter term-filter (-> any/c (or/c #f string?) any/c) (lambda (x y) #t)]
|
||
[#:layout layout (-> (listof term-node?) void) void]
|
||
[#:x-spacing number? 15]
|
||
[#:y-spacing number? 15]
|
||
[#:edge-labels? edge-label-font boolean? #t]
|
||
[#:edge-label-font edge-label-font (or/c #f (is-a?/c font%)) #f]
|
||
[#:graph-pasteboard-mixin graph-pasteboard-mixin (make-mixin-contract graph-pasteboard<%>) values]
|
||
[#:post-process post-process (-> (is-a?/c graph-pasteboard<%>) any/c)])
|
||
void?]{
|
||
|
||
This function behaves just like the function @scheme[traces], but
|
||
instead of opening a window to show the reduction graph, it just saves
|
||
the reduction graph to the specified @scheme[file].
|
||
|
||
All of the arguments behave like the arguments to @scheme[traces],
|
||
with the exception of the @scheme[post-process] argument. It is called
|
||
just before the PostScript is created with the graph pasteboard.
|
||
}
|
||
|
||
@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-color [tn term-node?]) (or/c string? (is-a?/c color%) false/c)]{
|
||
|
||
Returns the current highlighting of the node. See also @scheme[term-node-set-color!].
|
||
}
|
||
|
||
|
||
@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-set-position! [tn term-node?] [x (and/c real? positive?)] [y (and/c real? positive?)]) void?]{
|
||
|
||
Sets the position of @scheme[tn] in the graph to (@scheme[x],@scheme[y]).
|
||
}
|
||
|
||
@defproc[(term-node-x [tn term-node?]) real]{
|
||
Returns the @tt{x} coordinate of @scheme[tn] in the window.
|
||
}
|
||
@defproc[(term-node-y [tn term-node?]) real]{
|
||
Returns the @tt{y} coordinate of @scheme[tn] in the window.
|
||
}
|
||
@defproc[(term-node-width [tn term-node?]) real]{
|
||
Returns the width of @scheme[tn] in the window.
|
||
}
|
||
@defproc[(term-node-height [tn term-node?]) real?]{
|
||
Returns the height of @scheme[tn] in the window.
|
||
}
|
||
|
||
@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 (or/c number? (-> any/c number?))]{
|
||
|
||
A parameter that determines the initial width of the boxes
|
||
where terms are displayed (measured in characters) for both
|
||
the stepper and traces.
|
||
|
||
If its value is a number, then the number is used as the width for
|
||
every term. If its value is a function, then the function is called
|
||
with each term and the resulting number is used as the width.
|
||
}
|
||
|
||
@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<%>))]{}
|
||
@defparam[dark-text-color color (or/c string? (is-a?/c color<%>))]{}
|
||
@defparam[light-text-color color (or/c string? (is-a?/c color<%>))]{}]]{
|
||
|
||
These six parameters control the color of the edges in the graph.
|
||
|
||
The dark colors are used when the mouse is over one of the nodes that
|
||
is connected to this edge. The light colors are used when it isn't.
|
||
|
||
The pen colors control the color of the line. The brush colors control
|
||
the color used to fill the arrowhead and the text colors control the
|
||
color used to draw the label on the edge.
|
||
}
|
||
|
||
@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-term],
|
||
@scheme[render-language],
|
||
@scheme[render-reduction-relation],
|
||
@scheme[render-metafunctions], and
|
||
@scheme[render-lw],
|
||
and one
|
||
for use in combination with other libraries that operate on picts
|
||
@scheme[term->pict],
|
||
@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-term [lang compiled-lang?] [term any/c] [file (or/c #f path-string?)])
|
||
(if file void? pict?)]{
|
||
Renders the term @scheme[term]. 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] for details on the construction of the pict.
|
||
}
|
||
|
||
|
||
@defproc[(term->pict [lang compiled-lang?] [term any/c]) pict?]{
|
||
Produces a pict like @scheme[render-term], but without
|
||
adjusting @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-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
|
||
adjusting @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].
|
||
|
||
The following forms of arrows can be typeset:
|
||
|
||
@arrows[--> -+> ==> -> => ..> >-> ~~> ~> :-> :--> c->
|
||
-->> >-- --< >>-- --<<]
|
||
|
||
}
|
||
|
||
@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)]{}
|
||
@defform[(render-metafunctions metafunction-name ...)]{}
|
||
@defform/none[#:literals (render-metafunctions)
|
||
(render-metafunctions metafunction-name ... #:file filename)]{}]]{
|
||
|
||
If provided with one argument, @scheme[render-metafunction]
|
||
produces a pict that renders properly in the definitions
|
||
window in DrScheme. If given two arguments, it writes
|
||
postscript into the file named by @scheme[filename] (which
|
||
may be either a string or bytes).
|
||
|
||
Similarly, @scheme[render-metafunctions] accepts multiple
|
||
metafunctions and renders them together, lining up all of the
|
||
clauses together.
|
||
|
||
This function sets @scheme[dc-for-text-size]. See also
|
||
@scheme[metafunction->pict] and
|
||
@scheme[metafunctions->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.
|
||
}
|
||
|
||
@defform[(metafunctions->pict metafunction-name ...)]{
|
||
Like @scheme[metafunction->pict],
|
||
this produces a pict, but without setting @scheme[dc-for-text-size]
|
||
and is suitable for use in Slideshow or other libraries that combine
|
||
picts. Like
|
||
@scheme[render-metafunctions], it accepts multiple metafunctions
|
||
and renders them together.
|
||
}
|
||
|
||
@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 @scheme[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. 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. The
|
||
@scheme['horizontal-left-align] style is like the @scheme['horizontal]
|
||
style, but the left-hand sides of the rules are aligned on the left,
|
||
instead of on the right.
|
||
|
||
}
|
||
|
||
@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
|
||
(or/c 'left-right
|
||
'up-down
|
||
'left-right/vertical-side-conditions
|
||
'up-down/vertical-side-conditions
|
||
'left-right/compact-side-conditions
|
||
'up-down/compact-side-conditions
|
||
'left-right/beside-side-conditions)]{
|
||
|
||
This parameter controls the style used for typesetting
|
||
metafunctions. The @scheme['left-right] style means that the
|
||
results of calling the metafunction are displayed to the
|
||
right of the arguments and the @scheme['up-down] style means that
|
||
the results are displayed below the arguments.
|
||
|
||
The @scheme['left-right/vertical-side-conditions] and
|
||
@scheme['up-down/vertical-side-conditions] variants format side
|
||
conditions each on a separate line, instead of all on the same line.
|
||
|
||
The @scheme['left-right/compact-side-conditions] and
|
||
@scheme['up-down/compact-side-conditions] variants move side
|
||
conditions to separate lines to avoid making the rendered form wider
|
||
than it would be otherwise.
|
||
|
||
The @scheme['left-right/beside-side-conditions] variant is like
|
||
@scheme['left-right], except it puts the side-conditions on the
|
||
same line, instead of on a new line below the case.}
|
||
|
||
|
||
@defparam[metafunction-cases
|
||
cases
|
||
(or/c #f (and/c (listof (and/c integer?
|
||
(or/c zero? positive?)))
|
||
pair?))]{
|
||
|
||
This parameter controls which cases in a metafunction are rendered. If it is @scheme[#f] (the default), then all of the
|
||
cases appear. If it is a list of numbers, then only the selected cases appear (counting from @scheme[0]).
|
||
}
|
||
|
||
@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[non-terminal-superscript-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 @scheme[text] in the texpict collection for
|
||
documentation explaining @scheme[text-style/c]. One of the more
|
||
useful things it can be is one of the symbols @scheme['roman],
|
||
@scheme['swiss], or @scheme['modern], which are a serif, sans-serif, and
|
||
monospaced font, respectively. (It can also encode style
|
||
information, too.)
|
||
|
||
The @scheme[label-style] is used for the reduction rule label
|
||
names. The @scheme[literal-style] is used for names that aren't
|
||
non-terminals that appear in patterns. The
|
||
@scheme[metafunction-style] is used for the names of
|
||
metafunctions.
|
||
|
||
The @scheme[non-terminal-style] is used for the names of non-terminals.
|
||
Two parameters style the text in the (optional) "underscore" component
|
||
of a non-terminal reference. The first, @scheme[non-terminal-subscript-style],
|
||
applies to the segment between the underscore and the first caret (@scheme[^])
|
||
to follow it; the second, @scheme[non-terminal-superscript-style], applies
|
||
to the segment following that caret. For example, in the non-terminal
|
||
reference @scheme[x_y_z], @scheme[x] has style @scheme[non-terminal-style],
|
||
@scheme[y] has style @scheme[non-terminal-subscript-style], and @scheme[z]
|
||
has style @scheme[non-terminal-superscript-style].
|
||
|
||
The
|
||
@scheme[non-terminal-subscript-style] is used for the portion
|
||
after the underscore in non-terminal references.
|
||
|
||
The @scheme[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 @scheme[lw] struct as an argument and should return
|
||
another @scheme[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 @scheme[(listof lw)], and is expected to
|
||
return a new @scheme[(listof (or/c lw? string? pict?))],
|
||
rewritten appropriately.
|
||
|
||
The list passed to the rewriter corresponds to the
|
||
@scheme[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-@scheme[lw]s. That list is then transformed by adding
|
||
@scheme[lw] structs for each of the non-@scheme[lw]s in the
|
||
list (see the description of @scheme[lw] below for an
|
||
explanation of logical-space):
|
||
|
||
@itemize[
|
||
@item{
|
||
If there are two adjacent @scheme[lw]s, then the logical
|
||
space between them is filled with whitespace.}
|
||
|
||
@item{
|
||
If there is a pair of @scheme[lw]s with just a single
|
||
non-@scheme[lw] between them, a @scheme[lw] will be
|
||
created (containing the non-@scheme[lw]) that uses all
|
||
of the available logical space between the @scheme[lw]s.
|
||
}
|
||
|
||
@item{
|
||
If there are two adjacent non-@scheme[lw]s between two
|
||
@scheme[lw]s, the first non-@scheme[lw] is rendered
|
||
right after the first @scheme[lw] with a logical space
|
||
of zero, and the second is rendered right before the
|
||
last @scheme[lw] also with a logical space of zero, and
|
||
the logical space between the two @scheme[lw]s is
|
||
absorbed by a new @scheme[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[]
|
||
|