530 lines
18 KiB
Racket
530 lines
18 KiB
Racket
#reader scribble/reader
|
|
#lang racket/base
|
|
(require "common.ss"
|
|
scribble/decode
|
|
scribble/struct
|
|
scribble/racket
|
|
racket/list
|
|
racket/pretty
|
|
syntax/docprovide
|
|
(for-syntax racket/base)
|
|
)
|
|
|
|
(provide prim-variables
|
|
prim-forms
|
|
define-forms/normal
|
|
define-form/explicit-lambda
|
|
intermediate-forms
|
|
prim-ops
|
|
prim-op-defns)
|
|
|
|
(define (maybe-make-table l t)
|
|
(if (paragraph? t)
|
|
(make-paragraph
|
|
(append l (cons " "
|
|
(paragraph-content t))))
|
|
(make-table
|
|
"prototype"
|
|
(list (list (make-flow (list (make-paragraph l)))
|
|
(make-flow (list t)))))))
|
|
|
|
|
|
(define (typeset-type type)
|
|
(let-values ([(in out) (make-pipe)])
|
|
(parameterize ([pretty-print-columns 50])
|
|
(pretty-print type out))
|
|
(port-count-lines! in)
|
|
(read-syntax #f in)))
|
|
|
|
(define (sort-category category)
|
|
(sort
|
|
(cadr category)
|
|
(lambda (x y)
|
|
(string<=? (symbol->string (car x))
|
|
(symbol->string (car y))))))
|
|
|
|
|
|
(define (make-proto func ctx-stx)
|
|
(maybe-make-table
|
|
(list
|
|
(hspace 2)
|
|
(to-element (datum->syntax ctx-stx (car func)))
|
|
(hspace 1)
|
|
":"
|
|
(hspace 1))
|
|
(to-paragraph
|
|
(typeset-type (cadr func)))))
|
|
|
|
(define-syntax-rule (prim-variables (section-prefix) empty true false)
|
|
(make-splice
|
|
(list
|
|
|
|
@section[#:tag (string-append section-prefix " Pre-Defined Variables")]{Pre-Defined Variables}
|
|
@defthing[empty empty?]{
|
|
|
|
The empty list.}
|
|
|
|
@defthing[true boolean?]{
|
|
|
|
The true value.}
|
|
|
|
@defthing[false boolean?]{
|
|
|
|
The false value.}
|
|
|
|
)))
|
|
|
|
(define-syntax-rule (define-forms/normal define)
|
|
|
|
(make-splice
|
|
(list
|
|
@defform*[[(define (... (name variable variable ...)) expression)]]{
|
|
|
|
Defines a function named @racket[name]. The @racket[expression] is the body
|
|
of the function. When the function is called,
|
|
the values of the arguments are inserted into the body in place of the
|
|
@racket[variable]s. The function returns the value of that new expression.
|
|
|
|
The function name's cannot be the same as that of another function or
|
|
variable.}
|
|
|
|
@defform/none[#:literals (define) (define name expression)]{
|
|
|
|
Defines a variable called @racket[name] with the the value of
|
|
@racket[expression]. The variable name's cannot be the same as that of
|
|
another function or variable, and @racket[name] itself must not appear in
|
|
@racket[expression].}
|
|
|
|
)))
|
|
|
|
(define-syntax-rule (define-form/explicit-lambda define lambda)
|
|
|
|
(make-splice
|
|
(list
|
|
|
|
@defform/none[#:literals (define lambda)
|
|
(... (define name (lambda (variable variable ...) expression)))]{
|
|
|
|
An alternate way on defining functions. The @racket[name] is the name of
|
|
the function, which cannot be the same as that of another function or
|
|
variable.
|
|
|
|
@defidform/inline[lambda] cannot be used outside of this alternate syntax.
|
|
}
|
|
)))
|
|
|
|
|
|
(define-syntax-rule (prim-forms
|
|
(section-prefix)
|
|
define
|
|
lambda
|
|
define-struct
|
|
define-wish
|
|
cond
|
|
else
|
|
if
|
|
and
|
|
or
|
|
check-expect
|
|
check-within
|
|
check-error
|
|
check-member-of
|
|
check-range
|
|
require)
|
|
(make-splice
|
|
(list
|
|
|
|
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
|
|
@defform*[[(... (define-struct structure-name (field-name ...)))]]{
|
|
|
|
Defines a new structure called @racket[field-name]. The structure's fields are
|
|
named by the @racket[field-name]s. After the @racket[define-struct], the following new
|
|
functions are available:
|
|
|
|
@itemize[
|
|
|
|
@item{@racketidfont{make-}@racket[structure-name] : takes in a number of
|
|
arguments equal to the number of fields in the structure,
|
|
and creates a new instance of that structure.}
|
|
|
|
@item{@racket[structure-name]@racketidfont{-}@racket[field-name] : takes in an
|
|
instance of the structure and returns the value in the field named by
|
|
@racket[field-name].}
|
|
|
|
@item{@racket[structure-name]@racketidfont{?} : takes in any value, and returns
|
|
@racket[true] if the value is an instance of the structure.}
|
|
|
|
]
|
|
|
|
The name of the new functions introduced by @racket[define-struct] must not be the same as that of other functions or
|
|
variables, otherwise @racket[define-struct] reports an error.}
|
|
|
|
#|
|
|
|
|
@defform*[[(define-wish name)]]{
|
|
|
|
Defines a function called @racket[name] that we wish exists but have not
|
|
implemented yet. The wished-for function can be called with one argument, and
|
|
are reported in the test report for the current program.
|
|
|
|
The name of the function cannot be the same as another function or variable.}
|
|
|
|
|
|
@defform/none[#:literals (define-wish)
|
|
(define-wish name expression)]{
|
|
Similar to the above form, defines a wished-for function named @racket[name]. If the
|
|
wished-for function is called with one value, it returns the values of @racket[expression]. }
|
|
|#
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
|
|
@defform*[[(... (name expression expression ...))]]{
|
|
|
|
Calls the function named @racket[name]. The value of the call is the value of
|
|
@racket[name]'s body when every one of the function's variables are
|
|
replaced by the values of the corresponding @racket[expression]s.
|
|
|
|
The function named @racket[name] must defined before it can be called. The
|
|
number of argument @racket[expression]s must be the same as the number of arguments
|
|
expected by the function.}
|
|
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
|
|
@defform*[#:literals (cond else)
|
|
[(... (cond [question-expression answer-expression] ...))
|
|
(... (cond [question-expression answer-expression] ... [else answer-expression]))]]{
|
|
|
|
Chooses a clause base on a condition. @racket[cond] finds the first
|
|
@racket[question-expression] which evaluates to @racket[true], then it evaluates
|
|
the corresponding @racket[answer-expression].
|
|
|
|
If none of the @racket[question-expression]s evaluates to @racket[true],
|
|
@racket[cond]'s value is the @racket[answer-expression] of the
|
|
@racket[else] clause. If there is no @racket[else], @racket[cond] reports
|
|
an error. If the result of a @racket[question-expression] is neither
|
|
@racket[true] nor @racket[false], @racket[cond] also reports an error.
|
|
|
|
|
|
@defidform/inline[else] cannot be used outside of @racket[cond].
|
|
}
|
|
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
|
|
@defform*[[(if test-expression then-expression else-expression)]]{
|
|
|
|
When the value of the @racket[test-expression] is @racket[true],
|
|
@racket[if] evaluates the @racket[then-expression]. When the test is
|
|
@racket[false], @racket[if] evaluates the @racket[else-expression].
|
|
|
|
If the @racket[test-expression] is neither @racket[true] nor
|
|
@racket[false], @racket[if] reports an error.}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
|
|
@defform*[[(... (and expression expression expression ...))]]{
|
|
|
|
@racket[and] evaluates to @racket[true] if all the @racket[expression]s are
|
|
@racket[true]. If any @racket[expression] is false, the @racket[and]
|
|
expression immediately evaluates to @racket[false] (the expressions to the
|
|
right of that expression are not evaluated.)
|
|
|
|
If any of the expressions evaluate to a value other than @racket[true] or
|
|
@racket[false], it is an error.}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
|
|
@defform*[[(... (or expression expression expression ...))]]{
|
|
|
|
@racket[or] evaluates to @racket[true] as soon as one of the
|
|
@racket[expression]s is @racket[true] (the expressions to the right of that
|
|
expression are not evaluated.) If all the @racket[expression] are false,
|
|
@racket[or] is @racket[false].
|
|
|
|
If any of the expressions evaluate to a value other than @racket[true] or
|
|
@racket[false], @racket[or] reports an error.}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
|
|
@defform*[[(check-expect expression expected-expression)]]{
|
|
|
|
Checks that the first @racket[expression] evaluates to the same value as the
|
|
@racket[expected-expression].}
|
|
|
|
@defform*[[(check-within expression expected-expression delta-expression)]]{
|
|
|
|
Checks that the first @racket[expression] evaluates to a value within
|
|
@racket[delta-expression] of the @racket[expected-expression]. If
|
|
@racket[delta-expression] is not a number, @racket[check-within] report an
|
|
error.}
|
|
|
|
@defform*[[(check-error expression expression)
|
|
(check-error expression)]]{
|
|
|
|
Checks that the first @racket[expression] reports an error,
|
|
where the error messages matches the string produced by the second
|
|
@racket[expression], if it is present.}
|
|
|
|
@defform*[[(... (check-member-of expression expression expression ...))]]{
|
|
|
|
Checks that the first @racket[expression] produces the same value as one of
|
|
the following @racket[expression]s.}
|
|
|
|
@defform*[[(check-range expression expression expression)]]{
|
|
|
|
Checks that the first @racket[expression] produces a number in between the numbers
|
|
produced by the second and third @racket[expression]s, inclusive.}
|
|
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
|
|
@defform*[[(require string)]]{
|
|
|
|
Makes the definitions of the module specified by @racket[string]
|
|
available in the current module (i.e., the current file), where @racket[string]
|
|
refers to a file relative to the current file.
|
|
|
|
The @racket[string] is constrained in several ways to avoid problems
|
|
with different path conventions on different platforms: a @litchar{/}
|
|
is a directory separator, @litchar{.} always means the current
|
|
directory, @litchar{..} always means the parent directory, path
|
|
elements can use only @litchar{a} through @litchar{z} (uppercase or
|
|
lowercase), @litchar{0} through @litchar{9}, @litchar{-}, @litchar{_},
|
|
and @litchar{.}, and the string cannot be empty or contain a leading
|
|
or trailing @litchar{/}.}
|
|
|
|
@defform/none[#:literals (require)
|
|
(require module-name)]{
|
|
|
|
Accesses a file in an installed library. The library name is an identifier
|
|
with the same constraints as for a relative-path string (though without the
|
|
quotes), with the additional constraint that it must not contain a
|
|
@litchar{.}.}
|
|
|
|
@defform/none[#:literals (require lib)
|
|
(... (require (lib string string ...)))]{
|
|
|
|
Accesses a file in an installed library, making its definitions
|
|
available in the current module (i.e., the current file). The first
|
|
@racket[string] names the library file, and the remaining
|
|
@racket[string]s name the collection (and sub-collection, and so on)
|
|
where the file is installed. Each string is constrained in the same
|
|
way as for the @racket[(require string)] form.}
|
|
|
|
|
|
@defform/none[#:literals (require planet)
|
|
(require (planet string (string string number number)))]{
|
|
|
|
|
|
Accesses a library that is distributed on the internet via the PLaneT
|
|
server, making it definitions available in the current module (i.e.,
|
|
current file).}
|
|
|
|
)))
|
|
|
|
|
|
(define-syntax-rule
|
|
(intermediate-forms lambda
|
|
quote
|
|
quasiquote
|
|
unquote
|
|
unquote-splicing
|
|
local
|
|
letrec
|
|
let*
|
|
let
|
|
time)
|
|
|
|
(make-splice
|
|
(list
|
|
|
|
@deftogether[(
|
|
@defform/none[(unsyntax @elem{@racketvalfont{'}@racket[name]})]
|
|
@defform/none[(unsyntax @elem{@racketvalfont{'}@racket[part]})]
|
|
@defform[(quote name)]
|
|
@defform/none[(quote part)]
|
|
)]{
|
|
|
|
A quoted name is a symbol. A quote part is an abbreviation for a nested lists.
|
|
|
|
Normally, this quotation is written with a @litchar{'}, like
|
|
@racket['(apple banana)], but it can also be written with @racket[quote], like
|
|
@racket[(@#,racket[quote] (apple banana))].}
|
|
|
|
|
|
@deftogether[(
|
|
@defform/none[(unsyntax @elem{@racketvalfont{`}@racket[name]})]
|
|
@defform/none[(unsyntax @elem{@racketvalfont{`}@racket[part]})]
|
|
@defform[(quasiquote name)]
|
|
@defform/none[(quasiquote part)]
|
|
)]{
|
|
|
|
Like @racket[quote], but also allows escaping to expression ``unquotes.''
|
|
|
|
Normally, quasi-quotations are written with a backquote, @litchar{`}, like
|
|
@racket[`(apple ,(+ 1 2))], but they can also be written with
|
|
@racket[quasiquote], like
|
|
@racket[(@#,racket[quasiquote] (apple ,(+ 1 2)))].}
|
|
|
|
|
|
@deftogether[(
|
|
@defform/none[(unsyntax @elem{@racketvalfont{,}@racket[expression]})]
|
|
@defform[(unquote expression)]
|
|
)]{
|
|
|
|
Under a single quasiquote, @racketfont{,}@racket[expression] escapes from
|
|
the quote to include an evaluated expression whose result is inserted
|
|
into the abbreviated list.
|
|
|
|
Under multiple quasiquotes, @racketfont{,}@racket[expression] is really
|
|
the literal @racketfont{,}@racket[expression], decrementing the quasiquote count
|
|
by one for @racket[expression].
|
|
|
|
Normally, an unquote is written with @litchar{,}, but it can also be
|
|
written with @racket[unquote].}
|
|
|
|
|
|
@deftogether[(
|
|
@defform/none[(unsyntax @elem{@racketvalfont[",@"]@racket[expression]})]
|
|
@defform[(unquote-splicing expression)]
|
|
)]{
|
|
|
|
Under a single quasiquote, @racketfont[",@"]@racket[expression] escapes from
|
|
the quote to include an evaluated expression whose result is a list to
|
|
splice into the abbreviated list.
|
|
|
|
Under multiple quasiquotes, a splicing unquote is like an unquote;
|
|
that is, it decrements the quasiquote count by one.
|
|
|
|
Normally, a splicing unquote is written with @litchar{,}, but it can
|
|
also be written with @racket[unquote-splicing].}
|
|
|
|
@defform[(... (local [definition ...] expression))]{
|
|
|
|
Groups related definitions for use in @racket[expression]. Each
|
|
@racket[definition] can be either a variable definition, a function
|
|
definition, or a structure definition, using the usual syntax.
|
|
|
|
When evaluating @racket[local], each @racket[definition] is evaluated in
|
|
order, and finally the body @racket[expression] is evaluated. Only the
|
|
expressions within the @racket[local] (including the right-hand-sides of
|
|
the @racket[definition]s and the @racket[expression]) may refer to the
|
|
names defined by the @racket[definition]s. If a name defined in the
|
|
@racket[local] is the same as a top-level binding, the inner one
|
|
``shadows'' the outer one. That is, inside the @racket[local], any
|
|
references to that name refer to the inner one.}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
|
|
@defform[(... (letrec ([name expr-for-let] ...) expression))]{
|
|
|
|
Like @racket[local], but with a simpler syntax. Each @racket[name] defines
|
|
a variables (or a functions) with the value of the corresponding
|
|
@racket[expr-for-let]. If @racket[expr-for-let] is a @racket[lambda],
|
|
@racket[letrec] defines a function, otherwise it defines a variable.}
|
|
|
|
@defform[(... (let* ([name expr-for-let] ...) expression))]{
|
|
|
|
Like @racket[letrec], but each @racket[name] can only be used in
|
|
@racket[expression], and in @racket[expr-for-let]s occuring after that
|
|
@racket[name].}
|
|
|
|
@defform[(... (let ([name expr-for-let] ...) expression))]{
|
|
|
|
Like @racket[letrec], but the defined @racket[name]s can be used only in
|
|
the last @racket[expression], not the @racket[expr-for-let]s next to the
|
|
@racket[name]s.}
|
|
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
|
|
@defform[(time expression)]{
|
|
|
|
Measures the time taken to evaluate @racket[expression]. After evaluating
|
|
@racket[expression], @racket[time] prints out the time taken by the
|
|
evaluation (including real time, time taken by the cpu, and the time spent
|
|
collecting free memory). The value of @racket[time] is the same as that of @racket[expression].}
|
|
)))
|
|
|
|
(define (prim-ops lib ctx-stx)
|
|
(let ([ops (map (lambda (cat)
|
|
(cons (car cat)
|
|
(list (cdr cat))))
|
|
(lookup-documentation lib 'procedures))])
|
|
(make-table
|
|
#f
|
|
(cons
|
|
(list
|
|
(make-flow
|
|
(list
|
|
(make-paragraph
|
|
(list "In function calls, the function appearing immediatly after the open parenthesis can be any functions
|
|
defined with " (racket define) " or " (racket define-struct) ", or any one of:")))))
|
|
(apply
|
|
append
|
|
(map (lambda (category)
|
|
(cons
|
|
(list (make-flow
|
|
(list
|
|
(make-paragraph (list (hspace 1)
|
|
(bold (car category)))))))
|
|
(map (lambda (func)
|
|
(list
|
|
(make-flow
|
|
(list
|
|
(make-proto func ctx-stx)))))
|
|
(sort-category category))))
|
|
ops))))))
|
|
|
|
|
|
(define (prim-op-defns lib ctx-stx not-in)
|
|
(make-splice
|
|
(let ([ops (map (lambda (cat)
|
|
(cons (car cat)
|
|
(list (cdr cat))))
|
|
(lookup-documentation lib 'procedures))]
|
|
[not-in-ns (map (lambda (not-in-mod)
|
|
(let ([ns (make-base-namespace)])
|
|
(parameterize ([current-namespace ns])
|
|
(namespace-require `(for-label ,not-in-mod)))
|
|
ns))
|
|
not-in)])
|
|
(apply
|
|
append
|
|
(map (lambda (category)
|
|
(cons
|
|
(subsection #:tag-prefix (format "~a" lib) (car category))
|
|
(filter values
|
|
(map
|
|
(lambda (func)
|
|
(let ([id (datum->syntax ctx-stx (car func))])
|
|
(and (not (ormap
|
|
(lambda (ns)
|
|
(free-label-identifier=?
|
|
id
|
|
(parameterize ([current-namespace ns])
|
|
(namespace-syntax-introduce (datum->syntax #f (car func))))))
|
|
not-in-ns))
|
|
(let ([desc-strs (cddr func)])
|
|
(defthing/proc
|
|
id
|
|
(to-paragraph (typeset-type (cadr func)))
|
|
desc-strs)))))
|
|
(sort-category category)))))
|
|
ops)))))
|
|
|