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)