diff --git a/collects/scribble/html-render.ss b/collects/scribble/html-render.ss
index 9ae55d68c9..35f12ed3f3 100644
--- a/collects/scribble/html-render.ss
+++ b/collects/scribble/html-render.ss
@@ -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)))))
diff --git a/collects/scribble/scheme.ss b/collects/scribble/scheme.ss
index a47970e38d..a334090051 100644
--- a/collects/scribble/scheme.ss
+++ b/collects/scribble/scheme.ss
@@ -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!))
diff --git a/collects/scribble/scribble.css b/collects/scribble/scribble.css
index ead5b102ec..80c495193b 100644
--- a/collects/scribble/scribble.css
+++ b/collects/scribble/scribble.css
@@ -144,11 +144,19 @@
}
*/
+ .ghost {
+ color: white;
+ }
+
.scheme em {
color: black;
font-family: serif;
}
+ .highlighted {
+ background-color: #ddddff;
+ }
+
.schemeinput {
color: brown;
background-color: #eeeeee;
diff --git a/collects/scribblings/reference/model.scrbl b/collects/scribblings/reference/model.scrbl
new file mode 100644
index 0000000000..2a0427e250
--- /dev/null
+++ b/collects/scribblings/reference/model.scrbl
@@ -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 #(10 20))}
+ {}
+ (begin (code:hilite (define x ))
+ (define y x)
+ (vector-set! x 0 11)
+ (vector-ref y 0))]
+[{(define #(10 20))}
+ {(define x )}
+ (code:hilite (begin #,(void-const)
+ (define y x)
+ (vector-set! x 0 11)
+ (vector-ref y 0)))]
+[{(define #(10 20))}
+ {(define x )}
+ (begin (define y (code:hilite x))
+ (vector-set! x 0 11)
+ (vector-ref y 0))]
+[{(define #(10 20))}
+ {(define x )}
+ (begin (code:hilite (define y ))
+ (vector-set! x 0 11)
+ (vector-ref y 0))]
+[{(define #(10 20))}
+ {(define x )
+ (define y )}
+ (code:hilite (begin #,(void-const)
+ (vector-set! x 0 11)
+ (vector-ref y 0)))]
+[{(define #(10 20))}
+ {(define x )
+ (define y )}
+ (begin (vector-set! (code:hilite x) 0 11)
+ (vector-ref y 0))]
+[{(define #(10 20))}
+ {(define x )
+ (define y )}
+ (begin (code:hilite (vector-set! 0 11))
+ (vector-ref y 0))]
+[{(define #(11 20))}
+ {(define x )
+ (define y )}
+ (code:hilite (begin #,(void-const)
+ (vector-ref y 0)))]
+[{(define #(11 20))}
+ {(define x )
+ (define y )}
+ (vector-ref (code:hilite y) 0)]
+[{(define #(11 20))}
+ {(define x )
+ (define y )}
+ (code:hilite (vector-ref 0))]
+[{(define #(11 20))}
+ {(define x )
+ (define y )}
+ 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[] 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 #(10 20))
+ (define #(0))}
+ {(define x )}
+ (+ 1 x)]
+]
+
+evaluation cannot depend on @scheme[], 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[] 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 (lambda (x) (+ x 10)))}
+ {(define f )}
+ ((code:hilite f) 7)]
+[{(define (lambda (x) (+ x 10)))}
+ {(define f )}
+ (code:hilite ( 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 (lambda (x) (+ x 10)))}
+ {(define f )}
+ (code:hilite ( 7))]
+[{(define (lambda (x) (+ x 10)))}
+ {(define f )
+ (define xloc 7)}
+ (+ (code:hilite xloc) 10)]
+[{(define (lambda (x) (+ x 10)))}
+ {(define f )
+ (define xloc 7)}
+ (code:hilite (+ 7 10))]
+[{(define (lambda (x) (+ x 10)))}
+ {(define f )
+ (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 (lambda (x) (begin (set! x 3) x)))}
+ {(define f )}
+ ((code:hilite f) 7)]
+[{(define (lambda (x) (begin (set! x 3) x)))}
+ {(define f )}
+ (code:hilite ( 7))]
+[{(define (lambda (x) (begin (set! x 3) x)))}
+ {(define f )
+ (define xloc 7)}
+ (begin (code:hilite (set! xloc 3)) xloc)]
+[{(define (lambda (x) (begin (set! x 3) x)))}
+ {(define f )
+ (define xloc 3)}
+ (code:hilite (begin #,(void-const) xloc))]
+[{(define (lambda (x) (begin (set! x 3) x)))}
+ {(define f )
+ (define xloc 3)}
+ (code:hilite xloc)]
+[{(define (lambda (x) (begin (set! x 3) x)))}
+ {(define f )
+ (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}
+
diff --git a/collects/scribblings/reference/prog-steps.ss b/collects/scribblings/reference/prog-steps.ss
new file mode 100644
index 0000000000..9a8bd9c2a1
--- /dev/null
+++ b/collects/scribblings/reference/prog-steps.ss
@@ -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)))))))
+
+
\ No newline at end of file
diff --git a/collects/scribblings/reference/read.scrbl b/collects/scribblings/reference/read.scrbl
index b057409285..90240479fe 100644
--- a/collects/scribblings/reference/read.scrbl
+++ b/collects/scribblings/reference/read.scrbl
@@ -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
diff --git a/collects/scribblings/reference/reference.scrbl b/collects/scribblings/reference/reference.scrbl
index c1fb5e799e..468d6c4afb 100644
--- a/collects/scribblings/reference/reference.scrbl
+++ b/collects/scribblings/reference/reference.scrbl
@@ -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"]
diff --git a/collects/scribblings/reference/syntax.scrbl b/collects/scribblings/reference/syntax.scrbl
index 8618b6e115..3681e626b6 100644
--- a/collects/scribblings/reference/syntax.scrbl
+++ b/collects/scribblings/reference/syntax.scrbl
@@ -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.}
+ }}
}
@;------------------------------------------------------------------------
diff --git a/collects/tests/mzscheme/for.ss b/collects/tests/mzscheme/for.ss
new file mode 100644
index 0000000000..e9c0fa6c08
--- /dev/null
+++ b/collects/tests/mzscheme/for.ss
@@ -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)