schemeblock improvements and reference-manual work

svn: r6468
This commit is contained in:
Matthew Flatt 2007-06-04 06:34:16 +00:00
parent 1489f1b870
commit d2f0b1756c
9 changed files with 872 additions and 82 deletions

View File

@ -175,7 +175,10 @@
[(sf) `((b (font ([size "-1"][face "Helvetica"]) ,@(super render-element e part ht))))]
[(subscript) `((sub ,@(super render-element e part ht)))]
[(superscript) `((sup ,@(super render-element e part ht)))]
[(hspace) `((tt ,@(map (lambda (c) 'nbsp) (string->list (content->string (element-content e))))))]
[(hspace) `((tt ,@(let ([str (content->string (element-content e))])
(if (= 1 (string-length str))
'(" ")
(map (lambda (c) 'nbsp) (string->list str))))))]
[else (error 'html-render "unrecognized style symbol: ~e" style)])]
[(string? style)
`((span ([class ,style]) ,@(super render-element e part ht)))]
@ -195,15 +198,22 @@
[(at-left) '((align "left"))]
[else null]))
,@(map (lambda (flows)
`(tr ,@(map (lambda (d a)
`(td ,@(case a
[(#f) null]
[(right) '(((align "right")))]
[(left) '(((align "left")))])
`(tr ,@(map (lambda (d a va)
`(td (,@(case a
[(#f) null]
[(right) '((align "right"))]
[(left) '((align "left"))])
,@(case va
[(#f) null]
[(top) '((valign "top"))]
[(bottom) '((valign "bottom"))]))
,@(render-flow d part ht)))
flows
(cdr (or (and (list? (table-style t))
(assoc 'alignment (or (table-style t) null)))
(cons #f (map (lambda (x) #f) flows))))
(cdr (or (and (list? (table-style t))
(assoc 'valignment (or (table-style t) null)))
(cons #f (map (lambda (x) #f) flows)))))))
(table-flowss t)))))

View File

@ -34,7 +34,8 @@
quote quasiquote unquote unquote-splicing
syntax quasisyntax unsyntax unsyntax-splicing
for/fold for/list for*/list for for/and for/or for* for*/or for*/and for*/fold
for-values for*/list-values for/first for/last)))
for-values for*/list-values for/first for/last
set!)))
(define current-variable-list
(make-parameter null))
@ -50,45 +51,57 @@
[init-col (or (syntax-column first) 0)]
[src-col init-col]
[dest-col 0]
[highlight? #f]
[col-map (make-hash-table 'equal)]
[next-col-map (make-hash-table 'equal)]
[line (or (syntax-line first) 0)])
(define (finish-line!)
(when multi-line?
(set! docs (cons (make-flow (list (make-paragraph (reverse content))))
docs))
(set! content null)))
(define (out v cls)
(unless (equal? v "")
(if (equal? v "\n")
(if multi-line?
(begin
(finish-line!)
(out prefix cls))
(out " " cls))
(begin
(set! content (cons (if color?
(make-element cls (list v))
(make-element 'tt (list v)))
content))
(set! dest-col (+ dest-col (if (string? v) (string-length v) 1)))))))
(define out
(case-lambda
[(v cls)
(out v cls (if (string? v) (string-length v) 1))]
[(v cls len)
(unless (equal? v "")
(if (equal? v "\n")
(if multi-line?
(begin
(finish-line!)
(out prefix cls))
(out " " cls))
(begin
(set! content (cons ((if highlight?
(lambda (c)
(make-element "highlighted" (list c)))
values)
(if color?
(make-element cls (list v))
(make-element 'tt (list v))))
content))
(set! dest-col (+ dest-col len)))))]))
(define advance
(case-lambda
[(c init-line! delta)
(let ([c (+ delta (syntax-column c))]
[l (syntax-line c)]
[span (syntax-span c)])
(when (and l (l . > . line))
(out "\n" no-color)
(set! line l)
(init-line!))
(when c
(let ([d-col (hash-table-get col-map src-col src-col)])
(let ([amt (+ (- c src-col) (- d-col dest-col))])
[l (syntax-line c)])
(let ([new-line? (and l (l . > . line))])
(when new-line?
(out "\n" no-color)
(set! line l)
(set! col-map next-col-map)
(set! next-col-map (make-hash-table 'equal))
(init-line!))
(let ([d-col (hash-table-get col-map c (+ dest-col (- c src-col)))])
(let ([amt (- d-col dest-col)])
(when (positive? amt)
(let ([old-dest-col dest-col])
(out (make-element 'hspace (list (make-string amt #\space))) #f)
(set! dest-col (+ old-dest-col amt))))))
(set! src-col (+ c (or span 1)))))]
(set! src-col c)
(hash-table-put! next-col-map src-col dest-col)))]
[(c init-line!) (advance c init-line! 0)]))
(define (convert-infix c quote-depth)
(let ([l (syntax->list c)])
@ -167,21 +180,36 @@
l))]
[(and (pair? (syntax-e c))
(eq? (syntax-e (car (syntax-e c))) 'code:line))
(for-each (loop init-line! quote-depth)
(cdr (syntax->list c)))]
(let ([l (cdr (syntax->list c))])
(for-each (loop init-line! quote-depth)
l))]
[(and (pair? (syntax-e c))
(eq? (syntax-e (car (syntax-e c))) 'code:hilite))
(let ([l (syntax->list c)]
[h? highlight?])
(unless (and l (= 2 (length l)))
(error "bad code:redex: ~e" (syntax-object->datum c)))
(advance c init-line!)
(set! src-col (syntax-column (cadr l)))
(hash-table-put! next-col-map src-col dest-col)
(set! highlight? #t)
((loop init-line! quote-depth) (cadr l))
(set! highlight? h?)
(set! src-col (add1 src-col)))]
[(and (pair? (syntax-e c))
(eq? (syntax-e (car (syntax-e c))) 'code:quote))
(advance c init-line!)
(out "(" (if (positive? quote-depth) value-color paren-color))
(set! src-col (+ src-col 1))
(hash-table-put! col-map src-col dest-col)
(hash-table-put! next-col-map src-col dest-col)
((loop init-line! quote-depth)
(datum->syntax-object #'here 'quote (car (syntax-e c))))
(for-each (loop init-line! (add1 quote-depth))
(cdr (syntax->list c)))
(out ")" (if (positive? quote-depth) value-color paren-color))
(set! src-col (+ src-col 1))
(hash-table-put! col-map src-col dest-col)]
#;
(hash-table-put! next-col-map src-col dest-col)]
[(and (pair? (syntax-e c))
(memq (syntax-e (car (syntax-e c)))
'(quote quasiquote unquote unquote-splicing
@ -200,13 +228,14 @@
meta-color))
(let ([i (cadr (syntax->list c))])
(set! src-col (or (syntax-column i) src-col))
(hash-table-put! col-map src-col dest-col)
(hash-table-put! next-col-map src-col dest-col)
((loop init-line! (+ quote-depth quote-delta)) i)))]
[(and (pair? (syntax-e c))
(convert-infix c quote-depth))
=> (lambda (converted)
((loop init-line! quote-depth) converted))]
[(or (pair? (syntax-e c))
(null? (syntax-e c))
(vector? (syntax-e c)))
(let* ([sh (or (syntax-property c 'paren-shape)
#\()]
@ -220,14 +249,20 @@
paren-color))])
(advance c init-line!)
(when (vector? (syntax-e c))
(out (format "#~a" (vector-length (syntax-e c))) p-color))
(let ([vec (syntax-e c)])
(out (format "#~a" (vector-length vec)) p-color)
(if (zero? (vector-length vec))
(set! src-col (+ src-col (- (syntax-span c) 2)))
(set! src-col (+ src-col (- (syntax-column (vector-ref vec 0))
(syntax-column c)
1))))))
(out (case sh
[(#\[ #\?) "["]
[(#\{) "{"]
[else "("])
p-color)
(set! src-col (+ src-col 1))
(hash-table-put! col-map src-col dest-col)
(hash-table-put! next-col-map src-col dest-col)
(let lloop ([l (if (vector? (syntax-e c))
(vector->short-list (syntax-e c) syntax-e)
c)])
@ -246,7 +281,7 @@
(advance l init-line! -2)
(out ". " (if (positive? quote-depth) value-color paren-color))
(set! src-col (+ src-col 3))
(hash-table-put! col-map src-col dest-col)
(hash-table-put! next-col-map src-col dest-col)
((loop init-line! quote-depth) l)]))
(out (case sh
[(#\[ #\?) "]"]
@ -254,12 +289,13 @@
[else ")"])
p-color)
(set! src-col (+ src-col 1))
(hash-table-put! col-map src-col dest-col))]
#;
(hash-table-put! next-col-map src-col dest-col))]
[(box? (syntax-e c))
(advance c init-line!)
(out "#&" value-color)
(set! src-col (+ src-col 2))
(hash-table-put! col-map src-col dest-col)
(hash-table-put! next-col-map src-col dest-col)
((loop init-line! +inf.0) (unbox (syntax-e c)))]
[(hash-table? (syntax-e c))
(advance c init-line!)
@ -269,7 +305,7 @@
"#hasheq")
value-color)
(set! src-col (+ src-col 5 (if equal-table? 2 0)))
(hash-table-put! col-map src-col dest-col)
(hash-table-put! next-col-map src-col dest-col)
((loop init-line! +inf.0)
(syntax-ize (hash-table-map (syntax-e c) cons)
(syntax-column c))))]
@ -323,10 +359,14 @@
variable-color]
[it? variable-color]
[else symbol-color])]
[else paren-color])))
(hash-table-put! col-map src-col dest-col))])))
(hash-table-put! col-map src-col dest-col)
[else paren-color])
(string-length s)))
(set! src-col (+ src-col (or (syntax-span c) 1)))
#;
(hash-table-put! next-col-map src-col dest-col))])))
(out prefix1 #f)
(set! dest-col 0)
(hash-table-put! next-col-map init-col dest-col)
((loop (lambda () (set! src-col init-col) (set! dest-col 0)) 0) c)
(unless (null? content)
(finish-line!))

View File

@ -144,11 +144,19 @@
}
*/
.ghost {
color: white;
}
.scheme em {
color: black;
font-family: serif;
}
.highlighted {
background-color: #ddddff;
}
.schemeinput {
color: brown;
background-color: #eeeeee;

View File

@ -0,0 +1,521 @@
#reader(lib "docreader.ss" "scribble")
@require[(lib "struct.ss" "scribble")]
@require-for-syntax[mzscheme]
@require["mz.ss"]
@require["prog-steps.ss"]
@define[reduces (make-element #f (list 'rarr))]
@define[rspace (make-element "ghost" (list 'rarr))]
@define[*redex (lambda (c)
(make-element "highlighted" (list c)))]
@define-syntax[redex (syntax-rules ()
[(_ a) (*redex (scheme a))])]
@define[hole (make-element #f (list "[]"))]
@define[(*sub c e) (make-element #f (list c "[" e "]"))]
@define[langle (make-element 'tt (list "<"))]
@define[rangle (make-element 'tt (list ">"))]
@define[comma (make-element 'tt (list ", "))]
@define-syntax[sub (syntax-rules ()
[(_ a b) (*sub (scheme a) (scheme b))])]
@define[(*state c e) (make-element #f (list langle c comma e rangle))]
@define-syntax[state (syntax-rules ()
[(_ a b) (*state (scheme a) (scheme b))])]
@;------------------------------------------------------------------------
@title{Language Model}
Scheme evaluation can be viewed as the simplification of expressions
to obtain values. For example, just as an elementary-school student
simplifies
@verbatim{ 1 + 1 = 2}
Scheme evaluation simplifies
@schemeblock[
(+ 1 1) #, @reduces 2
]
The arrow @reduces above replaces the more traditional @tt{=} to
emphasize that evaluation proceeds in a particular direction towards
simplier expressions. In particular, a @defterm{value} is an
expression that evaluation simplifies no further, such as the number
@scheme[2].
@;------------------------------------------------------------------------
@section{Sub-expression Evaluation}
Some simplifications require more than one step. For example:
@schemeblock[
(- 4 #,(redex (+ 1 1))) #,reduces #,(redex (- 4 2)) #,reduces 2
]
An expression that is not a value can always be partitioned into two
parts: a @defterm{redex}, which is the part that changed in a
single-step simplification (show in blue), and the
@defterm{continuation}, which is the surrounding expression
context. In @scheme[(- 4 (+ 1 1))], the redex is @scheme[(+ 1 1)], and
the continuation is @scheme[(- 4 #, @hole)], where @hole takes the
place of the redex. That is, the continuation says how to ``continue''
after the redex is reduced to a value.
Before some things can be evaluated, some sub-expressions must be
evaluated; for example, in the application @scheme[(- 4 (+ 1 1))], the
application of @scheme[-] cannot be reduced until the sub-expression
@scheme[(+ 1 1)] is reduced.
Thus, the specification of each syntactic form specifies how (some of)
it's sub-expressions are evaluated, and then how the results are
combined to reduce the form away.
The @defterm{dynamic extent} of an expression is the sequence of
evaluation steps during which an expression contains the redex.
@;------------------------------------------------------------------------
@section{Tail Position}
An expression @scheme[_expr1] is in @defterm{tail position} with
respect to an enclosing expression @scheme[_expr2] if, whenever
@scheme[_expr1] becomes a redex, its continuation is the same as was
the enclosing @scheme[_expr2]'s continuation.
For example, the @scheme[(+ 1 1)] expression is @italic{not} in tail
position with respect to @scheme[(- 4 (+ 1 1))]. To illustrate, we use
the notation @sub[_C _expr] to mean the expression that is produced by
substituing @scheme[_expr] in place of @hole in the continuation
@scheme[_C]:
@schemeblock[
#, @sub[_C (- 4 (+ 1 1))] #, @reduces #, @sub[_C (- 4 2)]
]
In this case, the continuation for reducing @scheme[(+ 1 1)] is @sub[_C (+
4 #, @hole)], not just @scheme[_C].
In contrast, @scheme[(+ 1 1)] is in tail position with respect to
@scheme[(if (zero? 0) (+ 1 1) 3)], because, for any continuation @scheme[_C],
@schemeblock[
#, @sub[_C (if (zero? 0) (+ 1 1) 3)] #, @reduces #, @sub[_C (if #t (+ 1 1) 3)] #, @reduces #, @sub[_C (+ 1 1)]
]
The steps in this reduction sequence are driven by the definition of
@scheme[if], and they do not depend on the continuation
@scheme[_C]. The ``then'' branch of an @scheme[if] form is always in
tail position with respect to the @scheme[if] form. Due to a similar
reduction rule for @scheme[if] and @scheme[#f], the ``else'' branch of
an @scheme[if] form is also in tail position.
Tail-position specifications provide a guarantee about the asymtotic
space consumption of a computation. In general, the specification of
tail positions goes with each syntactic form, like @scheme[if].
@;------------------------------------------------------------------------
@section{Multiple Return Values}
A Scheme expression can evaluate to @defterm{multiple values}, in the
same way that a procedure can accept multiple arguments.
Most continuations expect a particular number of result values.
Indeed, most continuations, such as @scheme[(+ #, @hole 1)] expect a
single value. The continuation @scheme[(let-values ([(x y) #, @hole])
_expr)] expects two result values; the first result replaces
@scheme[x] in the body @scheme[_expr], and the second replaces
@scheme[y] in @scheme[_expr]. The continuation @scheme[(begin #, @hole
(+ 1 2))] accepts any number of result values, because it ignores the
result(s).
In general, the specification of a syntactic form inidicates the
number of values that it produces and the number that it expects from
each of its sub-expression. In addtion, some procedures (notably
@scheme[values]) produce multiple values, and some procedures (notably
@scheme[call-with-values]) create continuations internally that accept
a certain number of values.
@;------------------------------------------------------------------------
@section{Top-level and Module Bindings}
Given
@verbatim{ x = 10}
then an algebra student simplifies @tt{x + 1} as follows:
@verbatim{ x + 1 = 10 + 1 = 11}
Scheme works much the same way, in that a set of top-level bindings
are available for substitutions on demand during evaluation. For
example, given
@schemeblock[
(define x 10)
]
then
@schemeblock[
#,(redex (+ x 1)) #,reduces #,(redex (+ 10 1)) #,reduces 11
]
In Scheme, the way definitions appear is just as important as the way
that they are used. Scheme evaluation thus keeps track of both
definitions and the current expression, and it extends the set of
definitions in response to evaluating forms such as @scheme[define].
Each evaluation step, then, takes the current set of definitions and
program to a new set of definitions and program. Before a
@scheme[define] can be moved into the set of definitions, its
right-hand expression must be reduced to a value.
@prog-steps/no-obj[
[{}
(begin (define x (code:hilite (+ 9 1))) (+ x 1))]
[{}
(begin (code:hilite (define x 10)) (+ x 1))]
[{(define x 10)}
(code:hilite (begin #,(void-const) (+ x 1)))]
[{(define x 10)}
(+ (code:hilite x) 1)]
[{(define x 10)}
(code:hilite (+ 10 1))]
[{(define x 10)}
11]
]
Most definitions in PLT Scheme are in modules. In terms of evaluation,
a module is simply a prefix on a defined name, so that different
modules can define the name.
Using @scheme[set!], a program can change the value associated with an
existing top-level binding:
@prog-steps/no-obj[
[{(define x 10)}
(begin (code:hilite (set! x 8)) x)]
[{(define x 8)}
(code:hilite (begin #,(void-const) x))]
[{(define x 8)}
(code:hilite x)]
[{(define x 8)}
8]
]
@;------------------------------------------------------------------------
@section{Objects and Imperative Update}
In addition to @scheme[set!] for imperative update of top-level
bindings, various procedures enable the modification of elements
within a compound data structure. For example, @scheme[vector-set!]
modifies the content of a vector.
To allow such modifications to data, we must distingiush between
values, which are the results of expressions, and @defterm{objects},
which hold the data referenced by a value.
A few kinds of objects can serve directly as values, including
booleans, @void-const[], and small exact integers. More generally,
however, a value is a reference to an object. For example, a value can
be a reference to a particular vector that currently holds the value
@scheme[10] in its first slot. If an object is modified, then the
modification is visible through all copies of the value that reference
the same object.
In the evaluation model, a set of objects must be carried along with
each step in evaluation, just like the definition set. Operations that
create objects, such as @scheme[vector], add to the set of objects:
@prog-steps[
[{}
{}
(begin (define x (code:hilite (vector 10 20)))
(define y x)
(vector-set! x 0 11)
(vector-ref y 0))]
[{(define <o1> #(10 20))}
{}
(begin (code:hilite (define x <o1>))
(define y x)
(vector-set! x 0 11)
(vector-ref y 0))]
[{(define <o1> #(10 20))}
{(define x <o1>)}
(code:hilite (begin #,(void-const)
(define y x)
(vector-set! x 0 11)
(vector-ref y 0)))]
[{(define <o1> #(10 20))}
{(define x <o1>)}
(begin (define y (code:hilite x))
(vector-set! x 0 11)
(vector-ref y 0))]
[{(define <o1> #(10 20))}
{(define x <o1>)}
(begin (code:hilite (define y <o1>))
(vector-set! x 0 11)
(vector-ref y 0))]
[{(define <o1> #(10 20))}
{(define x <o1>)
(define y <o1>)}
(code:hilite (begin #,(void-const)
(vector-set! x 0 11)
(vector-ref y 0)))]
[{(define <o1> #(10 20))}
{(define x <o1>)
(define y <o1>)}
(begin (vector-set! (code:hilite x) 0 11)
(vector-ref y 0))]
[{(define <o1> #(10 20))}
{(define x <o1>)
(define y <o1>)}
(begin (code:hilite (vector-set! <o1> 0 11))
(vector-ref y 0))]
[{(define <o1> #(11 20))}
{(define x <o1>)
(define y <o1>)}
(code:hilite (begin #,(void-const)
(vector-ref y 0)))]
[{(define <o1> #(11 20))}
{(define x <o1>)
(define y <o1>)}
(vector-ref (code:hilite y) 0)]
[{(define <o1> #(11 20))}
{(define x <o1>)
(define y <o1>)}
(code:hilite (vector-ref <o1> 0))]
[{(define <o1> #(11 20))}
{(define x <o1>)
(define y <o1>)}
11]
]
The distinction between a top-level binding is an object reference is
crucial. A top-level binding is not a value; each time a binding
expression is evaluated, the value is extracted from the current set
of definitions. An object reference, in contrast is a value, and
therefore needs no further evaluation. The model evaluation steps
above use angle-bracketed @scheme[<o1>] for an object reference to
distinguish it from a variable name.
A direct object reference can never appear in a text-based source
program. A program representation created with
@scheme[datum->syntax-object], however, can embed direct references to
existing objects.
@;------------------------------------------------------------------------
@section{Object Identity and Comparisons}
The @scheme[eq?] operator compares two values, returning @scheme[#t]
when the values refer to the same object. This form of equality is
suitabel for comparing objects that support imperative update (e.g.,
to determine that the effect of modifying an object through one
reference is visible through another reference). Also, an @scheme[eq?]
test evaluates quickly, and @scheme[eq?]-based hashing is more
lightweight than @scheme[equal?]-based hashing in hash tables.
In some cases, however, @scheme[eq?] is unsuitable as a comparison
operator, because the generation of objects is not clearly defined. In
particular, two applications of @scheme[+] to the same two exact
integers may or may not produce results that are @scheme[eq?],
although the results are always @scheme[equal?]. Similarly, evaluation
of a @scheme[lambda] form typically generates a new procedure object,
but it may re-use a procedure object previously generated by the same
source @scheme[lambda] form.
The behavior of a datatype with respect to @scheme[eq?] is generally
specified with the datatype and its associated procedures.
@;------------------------------------------------------------------------
@section{Garbage Collection}
In the program state
@prog-steps[
[{(define <o1> #(10 20))
(define <o2> #(0))}
{(define x <o1>)}
(+ 1 x)]
]
evaluation cannot depend on @scheme[<o2>], because it is not part of
the program to evaluate, and it is not referenced by any definition
that is accessible in the program. The object @scheme[<o2>] may
therefore be removed from the evaluation by @defterm{garbage
collection}.
A few special compound datatypes hold @defterm{weak references} to
objects. Such weak references are treated specialy by the garbage
collector in determining which objects are reachable for the remainder
of the computation. If an object is reachable only via a weak
reference, then the object can be reclaimed, and the weak reference is
replaced by a different value (typically @scheme[#f]).
@;------------------------------------------------------------------------
@section{Procedure Applications and Local Bindings}
Given
@verbatim{ f(x) = x + 10}
then an algebra student simplifies @tt{f(1)} as follows:
@verbatim{ f(7) = 7 + 10 = 17}
The key step in this simplification is take the body of the defined
function @tt{f}, and then replace each @tt{x} with the actual value
@tt{1}.
Scheme procedure application works much the same way. A procedure is
an object, so evaluating @scheme[(f 7)] starts with a variable lookup:
@prog-steps[
[{(define <p1> (lambda (x) (+ x 10)))}
{(define f <p1>)}
((code:hilite f) 7)]
[{(define <p1> (lambda (x) (+ x 10)))}
{(define f <p1>)}
(code:hilite (<p1> 7))]
]
Unlike in algebra, however, the value associated with an argument can
be changed in the body of a procedure by using @scheme[set!], as in
the example @scheme[(lambda (x) (begin (set! x 3) x))]. Since the value
associated with @scheme[x] can be changed, an actual value for cannot
be substituted for @scheme[x] when the procedure is applied.
Instead, a new @defterm{location} is created for each variable on each
application. The argument value is placed in the location, and each
insteace of the variable in the procedure body is replaced with the
new location:
@prog-steps[
[{(define <p1> (lambda (x) (+ x 10)))}
{(define f <p1>)}
(code:hilite (<p1> 7))]
[{(define <p1> (lambda (x) (+ x 10)))}
{(define f <p1>)
(define xloc 7)}
(+ (code:hilite xloc) 10)]
[{(define <p1> (lambda (x) (+ x 10)))}
{(define f <p1>)
(define xloc 7)}
(code:hilite (+ 7 10))]
[{(define <p1> (lambda (x) (+ x 10)))}
{(define f <p1>)
(define xloc 7)}
17]
]
A location is the same as a top-level binding, but when a location is
generated, it (conceptually) uses a name that has not been used before
and that cannot not be generated again or accessed directly.
Generating a location in this way means that @scheme[set!] evaluates
for local variables in the same way as for top-level bindings, because
the variable is always replaced with a location by the time the
@scheme[set!] form is evaluated:
@prog-steps[
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
{(define f <p1>)}
((code:hilite f) 7)]
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
{(define f <p1>)}
(code:hilite (<p1> 7))]
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
{(define f <p1>)
(define xloc 7)}
(begin (code:hilite (set! xloc 3)) xloc)]
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
{(define f <p1>)
(define xloc 3)}
(code:hilite (begin #,(void-const) xloc))]
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
{(define f <p1>)
(define xloc 3)}
(code:hilite xloc)]
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
{(define f <p1>)
(define xloc 3)}
3]
]
The substition and location-generation step of procedure application
requires that the argument is a value. Therefore, in @scheme[((lambda
(x) (+ x 10)) (+ 1 2))], the @scheme[(+ 1 2)] sub-expression must be
simplified to the value @scheme[3], and then @scheme[3] can be placed
into a location for @scheme[x]. In other words, Scheme is a
@defterm{call-by-value} language.
Evaluation of a local binding, such as @scheme[(let ([x (+ 1 2)])
_expr)], is the same as for a procedure call. After @scheme[(+ 1 2)]
produces a value, it is stored in a fresh location that replaces every
instance of @scheme[x] in @scheme[_expr].
@;------------------------------------------------------------------------
@section{Identifiers, Variables, and Locations}
A @defterm{variable} is a placeholder for a value, and an expressions
in an initial program refer to variables. A top-level binding is both
a variable and a location. Any other variable is always replaced by a
location at run-time, so that evaluation of expressions involves only
locations. A single non-top-level variable, such as a procedure
argument, can correspond to multiple locations at different times.
The replacement of a variable with a location during evaluation
implements Scheme's @defterm{lexical scoping}. For example, when the
procedure-argument variable @scheme[x] is replaced by the location
@scheme[xloc], then it is replaced throughout the body of the
procedure, including with any nested @scheme[lambda] forms. As a
result, future references of the variable always access the same
location.
An @defterm{identifier} is source-program entity. Parsing a Scheme
program reveals that some identifiers correspond to variables, some
refer to syntactic forms, and some are quoted to produce a symbol or a
syntax object.
Throughout the documentation, identifiers are typeset to suggest the
way that they are parsed. A black, boldface identifier like
@scheme[lambda] indicates as a reference to a syntactic form. A plain
blue identifer like @schemeidfont{x} is a variable or a reference to
an unspecified top-level definition. A hyperlinked identifier
@scheme[cons] is a reference to a specific top-level definition.
@;------------------------------------------------------------------------
@section{Parsing and Compilation}
The syntax of a Scheme program is defined by
@itemize{
@item{a @defterm{read} phase that processes a character stream into a
Scheme value, especially one composed of pairs and symbols,
and}
@item{an @defterm{expand} phase that processes the value to finish
parsing the code.}
}
For details on the read phase, see @secref["mz:reader"]. Source code is
normally read in @scheme[read-syntax] mode, otherwise it must be
converted to syntax using @scheme[datum->syntax-object]; the expand
phase is defined in terms of syntax objects.
Expansion recursively processes a syntax-wrapped datum; for details,
see @secref["mz:expansion"]. Ultimately, expansion leads to the
synactic forms described in @secref["mz:syntax"].
...
@;------------------------------------------------------------------------
@section{Namespaces}
@;------------------------------------------------------------------------
@section{Threads}

View File

@ -0,0 +1,85 @@
(module prog-steps mzscheme
(require (lib "struct.ss" "scribble")
(lib "decode.ss" "scribble")
(lib "manual.ss" "scribble")
(lib "scheme.ss" "scribble")
(lib "kw.ss")
(lib "class.ss")
(lib "for.ss"))
(provide prog-steps
prog-steps/cont
prog-steps/no-obj)
(define-syntax prog-steps/no-obj
(syntax-rules ()
[(_ [{def ...} prog] ...)
(*prog-steps
#f
#f
(list (schemeblock0 def ...) ...)
(list (schemeblock0 prog) ...))]))
(define-syntax prog-steps
(syntax-rules ()
[(_ [{obj ...} {def ...} prog] ...)
(*prog-steps
#f
(list (schemeblock0 obj ...) ...)
(list (schemeblock0 def ...) ...)
(list (schemeblock0 prog) ...))]))
(define-syntax prog-steps/cont
(syntax-rules ()
[(_ [{obj ...} {def ...} prog] ...)
(*prog-steps
#t
(list (schemeblock0 obj ...) ...)
(list (schemeblock0 def ...) ...)
(list (schemeblock0 prog) ...))]))
(define (to-flow e) (make-flow (list (make-paragraph (list e)))))
(define (*prog-steps cont? objs defs progs)
(make-table
'((valignment top top top top top top))
(apply
append
(for/list ([obj (or objs (in-naturals))]
[def defs]
[prog progs]
[i (in-naturals)])
(let ([l
(list
(list (to-flow " ")
(to-flow (if (and (or (positive? i)
cont?)
(not objs))
'rarr
" "))
(to-flow " ")
(to-flow "defined:")
(to-flow " ")
(make-flow (list def)))
(list (to-flow " ")
(to-flow " ")
(to-flow " ")
(to-flow "evaluate:")
(to-flow " ")
(make-flow (list prog))))])
(if objs
(cons (list
(to-flow " ")
(to-flow (if (or (positive? i)
cont?)
'rarr
" "))
(to-flow " ")
(to-flow "objects:")
(to-flow " ")
(make-flow (list obj)))
l)
l)))))))

View File

@ -15,7 +15,7 @@
@define[(graph-defn) @elem{@litchar{#}@graph-tag[]@litchar{=}}]
@define[(graph-ref) @elem{@litchar{#}@graph-tag[]@litchar{#}}]
@title[#:tag "mz:reader"]{Reading Data}
@title[#:tag "mz:reader"]{Reading}
Scheme's reader is a recursive-descent parser that can be configured
through a @seclink["readtable"]{readtable} and various other

View File

@ -3,10 +3,18 @@
@title{PLT Scheme Reference Manual}
This manual defines the core PLT Scheme language and describes its
most prominent libraries. The companion manual
@italic{@link["../guide/index.html"]{A Guide to PLT Scheme}} provides
a friendlier (though less precise and complete) overview of the
language.
@table-of-contents[]
@include-section["data.scrbl"]
@include-section["syntax.scrbl"]
@include-section["model.scrbl"]
@include-section["read.scrbl"]
@include-section["macros.scrbl"]
@include-section["syntax.scrbl"]
@include-section["data.scrbl"]
@index-section["mzscheme-index"]

View File

@ -1,48 +1,37 @@
#reader(lib "docreader.ss" "scribble")
@require["mz.ss"]
@title{Syntax}
The syntax of a Scheme program is defined by
@itemize{
@item{a @defterm{read} phase that processes a character stream into a
Scheme value, especially one composed of pairs and symbols,
and}
@item{an @defterm{expand} phase that processes the value to finish
parsing the code.}
}
For details on the read phase, see @secref["mz:reader"]. Source code is
normally read in @scheme[read-syntax] mode, otherwise it must be
converted to syntax using @scheme[datum->syntax-object]; the expand
phase is defined in terms of syntax objects.
Expansion recursively processes a syntax-wrapped datum; for details,
see @secref["mz:expansion"]. Ultimately, expansion leads to the
synactic forms described in this section.
@title[#:tag "mz:syntax"]{Core Syntactic Forms}
A syntactic form is described by a BNF-like notation that describes a
combination of (syntax-wrapped) pairs, symbols, and other data (not a
sequence of characters). In this notation, @scheme[...] indicates zero
or more repetitions of the preceding datum, @scheme[...+] indicates
one or more repetitions, and @scheme[?] means zero or one
instance. Italic sequences of characters play the role of
non-terminals. In particular:
sequence of characters):
@itemize{
@item{A sequence that ends in @scheme[_id] refers to a syntax-wrapped
symbol.}
@item{@scheme[...] indicates zero or more
repetitions of the preceding datum.}
@item{A sequence that ends in @scheme[_keyword] refers to a
syntax-wrapped keyword.}
@item{@scheme[...+] indicates one or
more repetitions of the preceding datum.}
@item{A sequence that end with @scheme[_expr] refers to a sub-form
that is expanded as an expression.}
@item{@scheme[?] means zero instances or one instance
of the preceding datum.}
@item{Italic sequences of characters play the role of non-terminals. In
particular:
@itemize{
@item{A sequence that ends in @scheme[_id] refers to a
syntax-wrapped symbol.}
@item{A sequence that ends in @scheme[_keyword] refers to a
syntax-wrapped keyword.}
@item{A sequence that end with @scheme[_expr] refers to a sub-form
that is expanded as an expression.}
}}
}
@;------------------------------------------------------------------------

View File

@ -0,0 +1,129 @@
(load-relative "loadtest.ss")
(require (lib "for.ss"))
(Section 'generator)
(define-syntax (test-multi-generator stx)
(syntax-case stx ()
[(_ [(v ...) ...] gen)
(with-syntax ([(id ...) (generate-temporaries #'((v ...) ...))]
[(id2 ...) (generate-temporaries #'((v ...) ...))]
[((v2 ...) ...)
(apply map list (map syntax->list (syntax->list #'((v ...) ...))))])
#'(begin
(test '((v2 ...) ...) 'gen (for/list-values ([(id ...) gen])
(list id ...)))
(test-values '((v ...) ...) (lambda ()
(for/lists-values (id2 ...) ([(id ...) gen])
(values id ...))))
(test #t 'gen (for/and-values ([(id ...) gen])
(and (member (list id ...) '((v2 ...) ...)) #t)))
(test (list (for/last-values ([(id ...) gen])
(list id ...)))
'gen (for/and-values ([(id ...) gen])
(member (list id ...) '((v2 ...) ...))))
(test (for/first-values ([(id ...) gen])
(list id ...))
'gen (for/or-values ([(id ...) gen])
(car (member (list id ...) '((v2 ...) ...)))))
(void)))]))
(define-syntax test-generator
(syntax-rules ()
[(_ [seq] gen) ; we assume that seq has at least 2 elements, and all are unique
(begin
;; Some tests specific to single-values:
(test 'seq 'gen (for/list ([i gen]) i))
(test 'seq 'gen (for/list ([i gen][b gen]) i))
(test 'seq 'gen (for/list ([i gen][b gen]) b))
(test 'seq 'gen (for*/list ([i gen][b '(#t)]) i))
(test (map (lambda (x) #t) 'seq) 'gen (for*/list ([i gen][b '(#t)]) b))
(test (append 'seq 'seq) 'gen (for*/list ([b '(#f #t)][i gen]) i))
(test (append 'seq 'seq) 'gen (for/list ([b '(#f #t)] #:when #t [i gen]) i))
(test 'seq 'gen (let ([g gen]) (for/list ([i g]) i)))
(test 'seq 'gen (let ([r null])
(for ([i gen]) (set! r (cons i r)))
(reverse r)))
(test 'seq 'gen (reverse (for/fold ([a null]) ([i gen])
(cons i a))))
(test 'seq 'gen (let-values ([(more? next) (sequence-generator gen)])
(let loop ()
(if (more?)
(cons (next) (loop))
null))))
(test-values '(seq seq) (lambda ()
(for/lists (r1 r2) ([id gen])
(values id id))))
(test (list (for/last ([i gen]) i)) 'gen (for/and ([i gen]) (member i 'seq)))
(test 'seq 'gen (for/or ([i gen]) (member i 'seq)))
(test (for/first ([i gen]) i) 'gen (for/or ([i gen]) (and (member i 'seq) i)))
(test #t 'gen (for/and-values ([(i k) (in-parallel gen 'seq)])
(equal? i k)))
(test #f 'gen (for/and ([i gen])
(member i (cdr (reverse 'seq)))))
(test #f 'gen (for/or ([i gen]) (equal? i 'something-else)))
(let ([count 0])
(test #t 'or (for/or ([i gen]) (set! count (add1 count)) #t))
(test 1 'count count)
(test #f 'or (for/or ([i gen]) (set! count (add1 count)) #f))
(test (+ 1 (length 'seq)) 'count count)
(set! count 0)
(let ([second (for/last-values ([(i pos) (in-parallel gen (in-naturals))] #:when (< pos 2))
(set! count (add1 count))
i)])
(test second list-ref 'seq 1)
(test 2 values count)
(for ([i gen] #:when (equal? i second)) (set! count (add1 count)))
(for* ([i gen] #:when (equal? i second)) (set! count (add1 count)))
(test 4 values count)
(for ([i (stop-before gen (lambda (x) (equal? x second)))]) (set! count (add1 count)))
(test 5 values count)
(let ([g (stop-before gen (lambda (x) (equal? x second)))])
(for ([i g]) (set! count (add1 count))))
(test 6 values count)
(for ([i (stop-after gen (lambda (x) (equal? x second)))]) (set! count (add1 count)))
(test 8 values count)
(let ([g (stop-after gen (lambda (x) (equal? x second)))])
(for ([i g]) (set! count (add1 count))))
(test 10 values count))
(set! count 0)
(test #t 'and (for/and-values ([(e idx) (in-indexed gen)]) (set! count (add1 count)) (equal? idx (sub1 count))))
(test #t 'and (let ([g (in-indexed gen)])
(set! count 0)
(for/and-values ([(e idx) g]) (set! count (add1 count)) (equal? idx (sub1 count)))))
(void))
;; Run multi-value tests:
(test-multi-generator [seq] gen))]
[(_ seqs gen)
(test-multi-generator seqs gen)]))
(test-generator [(0 1 2)] (in-range 3))
(test-generator [(3 4 5)] (in-range 3 6))
(test-generator [(7 6 5)] (in-range 7 4 -1))
(test-generator [(a b c)] '(a b c))
(test-generator [(a b c)] (in-list '(a b c)))
(test-generator [(a b c)] #(a b c))
(test-generator [(a b c)] (in-vector #(a b c)))
(test-generator [(#\a #\b #\c)] "abc")
(test-generator [(#\a #\b #\c)] (in-string "abc"))
(test-generator [(65 66 67)] #"ABC")
(test-generator [(65 66 67)] (in-bytes #"ABC"))
(test-generator [(#\a #\b #\c)] (in-input-port-chars (open-input-string "abc")))
(test-generator [(65 66 67)] (open-input-bytes #"ABC"))
(test-generator [(65 66 67)] (in-input-port-bytes (open-input-bytes #"ABC")))
(test-generator [(0 1 2) (a b c)] (in-parallel (in-range 3) (in-list '(a b c))))
(test-generator [(0 1 2) (a b c)] (in-parallel (in-range 10) (in-list '(a b c))))
(test-generator [(0 1 2) (a b c)] (in-parallel (in-range 3) (in-list '(a b c d))))
(test-generator [(0 1 2) (a b c)] (in-parallel (in-range 3) '(a b c)))
(test-generator [(a b c)] (stop-after (in-list '(a b c d e)) (lambda (x) (equal? x 'c))))
(test-generator [(a b c)] (stop-before (in-list '(a b c d e)) (lambda (x) (equal? x 'd))))
(test-generator [(3 4 5)] (stop-before (in-naturals 3) (lambda (x) (= x 6))))
(test-generator [(a b c) (0 1 2)] (in-indexed '(a b c)))
(report-errs)