This chapter provides formal descriptions of what has already been
described informally in previous chapters of this report.
This section provides a formal syntax for Scheme written in an extended
BNF.
All spaces in the grammar are for legibility. Case is not significant
except in the definitions of <letter>, <character name> and <mnemonic escape>; for example, #x1A
and #X1a are equivalent, but foo and Foo
and #\space and #\Space are distinct.
<empty> stands for the empty string.
The following extensions to BNF are used to make the description more
concise: <thing>* means zero or more occurrences of
<thing>; and <thing>+ means at least one
<thing>.
This section describes how individual tokens(identifiers,
numbers, etc.) are formed from sequences of characters. The following
sections describe how expressions and programs are formed from sequences
of tokens.
<Intertoken space> can occur on either side of any token, but not
within a token.
Identifiers that do not begin with a vertical line are
terminated by a <delimiter> or by the end of the input.
So are dot, numbers, characters, and booleans.
Identifiers that begin with a vertical line are terminated by another vertical line.
The following four characters from the ASCII repertoire
are reserved for future extensions to the
language: [
]
{
}
In addition to the identifier characters of the ASCII repertoire specified
below, Scheme implementations may permit any additional repertoire of
non-ASCII Unicode characters to be employed in identifiers,
provided that each such character has a Unicode general category of Lu,
Ll, Lt, Lm, Lo, Mn, Mc, Me, Nd, Nl, No, Pd, Pc, Po, Sc, Sm, Sk, So,
or Co, or is U+200C or U+200D (the zero-width non-joiner and joiner,
respectively, which are needed for correct spelling in Persian, Hindi,
and other languages).
However, it is an error for the first character to have a general category
of Nd, Mc, or Me. It is also an error to use a non-Unicode character
in symbols or identifiers.
All Scheme implementations must permit the escape sequence
\x<hexdigits>;
to appear in Scheme identifiers that are enclosed in vertical lines. If the character
with the given Unicode scalar value is supported by the implementation,
identifiers containing such a sequence are equivalent to identifiers
containing the corresponding character.
<token> ⟶<identifier> ∣<boolean> ∣<number> ∣<character> ∣<string>
∣( ∣) ∣#( ∣#u8( ∣' ∣` ∣, ∣,@ ∣.
<delimiter> ⟶<whitespace> ∣<vertical line>
∣( ∣) ∣" ∣;
<intraline whitespace> ⟶<space or tab>
<whitespace> ⟶<intraline whitespace> ∣<line ending>
<vertical line> ⟶|
<line ending> ⟶<newline> ∣<return> <newline>
∣<return>
<comment> ⟶; ⟨all subsequent characters up to a
line ending⟩ ∣<nested comment>
∣#; <intertoken space> <datum>
<nested comment> ⟶#| <comment text>
<comment cont>* |#
<comment text> ⟶⟨character sequence not containing
#| or |#⟩
<comment cont> ⟶<nested comment> <comment text>
<directive> ⟶#!fold-case ∣#!no-fold-case
Note that it is ungrammatical to follow a <directive> with anything
but a <delimiter> or the end of file.
<atmosphere> ⟶<whitespace> ∣<comment> ∣<directive>
<intertoken space> ⟶<atmosphere>*
Note that +i, -i and <infnan> below are exceptions to the
<peculiar identifier> rule; they are parsed as numbers, not
identifiers.
<identifier> ⟶<initial> <subsequent>*
∣<vertical line> <symbol element>* <vertical line>
∣<peculiar identifier>
<initial> ⟶<letter> ∣<special initial>
<letter> ⟶a ∣b ∣c ∣... ∣z
∣A ∣B ∣C ∣... ∣Z
<special initial> ⟶! ∣$ ∣% ∣&
∣* ∣/ ∣: ∣< ∣=
∣> ∣? ∣@ ∣^
∣_
∣~
<subsequent> ⟶<initial> ∣<digit>
∣<special subsequent>
<digit> ⟶0 ∣1 ∣2 ∣3 ∣4 ∣5 ∣6 ∣7 ∣8 ∣9
<hex digit> ⟶<digit> ∣a ∣b ∣c ∣d ∣e ∣f
<explicit sign> ⟶+ ∣-
<special subsequent> ⟶<explicit sign> ∣. ∣@
<inline hex escape> ⟶\x<hex scalar value>;
<hex scalar value> ⟶<hex digit>+
<mnemonic escape> ⟶\a ∣\b ∣\t ∣\n ∣\r
<peculiar identifier> ⟶<explicit sign>
∣<explicit sign> <sign subsequent> <subsequent>*
∣<explicit sign> . <dot subsequent> <subsequent>*
∣. <dot subsequent> <subsequent>*
<dot subsequent> ⟶<sign subsequent> ∣.
<sign subsequent> ⟶<initial> ∣<explicit sign> ∣@
<symbol element> ⟶ <any character other than <vertical line> or \>
∣<inline hex escape> ∣<mnemonic escape> ∣\|
<boolean> ⟶#t ∣#f ∣#true ∣#false
<character> ⟶#\ <any character>
∣#\ <character name>
∣#\x<hex scalar value>
<character name> ⟶alarm ∣backspace ∣delete
∣escape ∣newline ∣null ∣return ∣space ∣tab
<string> ⟶" <string element>* "
<string element> ⟶<any character other than " or \>
∣<mnemonic escape> ∣\" ∣\\ ∣\|
∣\<intraline whitespace>*<line ending>
<intraline whitespace>*
∣<inline hex escape>
<bytevector> ⟶#u8(<byte>*)
<byte> ⟶<any exact integer between 0 and 255>
<number> ⟶<num 2> ∣<num 8>
∣<num 10> ∣<num 16>
The following rules for <num
R>, <complex
R>, <real
R>, <ureal
R>, <uinteger
R>, and <prefix
R>
are implicitly replicated for
R = 2, 8, 10,
and 16. There are no rules for <decimal 2>, <decimal
8>, and <decimal 16>, which means that numbers containing
decimal points or exponents are always in decimal radix.
Although not shown below, all alphabetic characters used in the grammar
of numbers can appear in either upper or lower case.
<num R> ⟶<prefix R> <complex R>
<complex R> ⟶<real R> ∣<real R> @ <real R>
∣<real R> + <ureal R> i ∣<real R> - <ureal R> i
∣<real R> + i ∣<real R> - i ∣<real R> <infnan> i
∣+ <ureal R> i ∣- <ureal R> i
∣<infnan> i ∣+ i ∣- i
<real R> ⟶<sign> <ureal R>
∣<infnan>
<ureal R> ⟶<uinteger R>
∣<uinteger R> / <uinteger R>
∣<decimal R>
<decimal 10> ⟶<uinteger 10> <suffix>
∣. <digit 10>+ <suffix>
∣<digit 10>+ . <digit 10>* <suffix>
<uinteger R> ⟶<digit R>+
<prefix R> ⟶<radix R> <exactness>
∣<exactness> <radix R>
<infnan> ⟶+inf.0 ∣-inf.0 ∣+nan.0 ∣-nan.0
<suffix> ⟶<empty>
∣<exponent marker> <sign> <digit 10>+
<exponent marker> ⟶e
<sign> ⟶<empty> ∣+ ∣-
<exactness> ⟶<empty> ∣#i∣#e<radix 2> ⟶#b<radix 8> ⟶#o<radix 10> ⟶<empty> ∣#d
<radix 16> ⟶#x<digit 2> ⟶0 ∣1
<digit 8> ⟶0 ∣1 ∣2 ∣3 ∣4 ∣5 ∣6 ∣7
<digit 10> ⟶<digit>
<digit 16> ⟶<digit 10> ∣a ∣b ∣c ∣d ∣e ∣f
<Datum> is what the read procedure (section 6.13.2)
successfully parses. Note that any string that parses as an
<expression> will also parse as a <datum>.
<datum> ⟶<simple datum> ∣<compound datum>
∣<label> = <datum> ∣<label> #
<simple datum> ⟶<boolean> ∣<number>
∣<character> ∣<string> ∣<symbol> ∣<bytevector>
<symbol> ⟶<identifier>
<compound datum> ⟶<list> ∣<vector> ∣<abbreviation>
<list> ⟶(<datum>*) ∣(<datum>+ . <datum>)
<abbreviation> ⟶<abbrev prefix> <datum>
<abbrev prefix> ⟶' ∣` ∣, ∣,@
<vector> ⟶#(<datum>*)
<label> ⟶# <uinteger 10>
The definitions in this and the following subsections assume that all
the syntax keywords defined in this report have been properly imported
from their libraries, and that none of them have been redefined or shadowed.
<expression> ⟶<identifier>
∣<literal>
∣<procedure call>
∣<lambda expression>
∣<conditional>
∣<assignment>
∣<derived expression>
∣<macro use>
∣<macro block>
∣<includer>
<literal> ⟶<quotation> ∣<self-evaluating>
<self-evaluating> ⟶<boolean> ∣<number> ∣<vector>
∣<character> ∣<string> ∣<bytevector>
<quotation> ⟶'<datum> ∣(quote <datum>)
<procedure call> ⟶(<operator> <operand>*)
<operator> ⟶<expression>
<operand> ⟶<expression>
<lambda expression> ⟶(lambda <formals> <body>)
<formals> ⟶(<identifier>*) ∣<identifier>
∣(<identifier>+ . <identifier>)
<body> ⟶<definition>* <sequence>
<sequence> ⟶<command>* <expression>
<command> ⟶<expression>
<conditional> ⟶(if <test> <consequent> <alternate>)
<test> ⟶<expression>
<consequent> ⟶<expression>
<alternate> ⟶<expression> ∣<empty>
<assignment> ⟶(set! <identifier> <expression>)
<derived expression> ⟶ (cond <cond clause>+)
∣(cond <cond clause>* (else <sequence>))
∣(case <expression>
<case clause>+)
∣(case <expression>
<case clause>*
(else <sequence>))
∣(case <expression>
<case clause>*
(else => <recipient>))
∣(and <test>*)
∣(or <test>*)
∣(when <test> <sequence>)
∣(unless <test> <sequence>)
∣(let (<binding spec>*) <body>)
∣(let <identifier> (<binding spec>*) <body>)
∣(let* (<binding spec>*) <body>)
∣(letrec (<binding spec>*) <body>)
∣(letrec* (<binding spec>*) <body>)
∣(let-values (<mv binding spec>*) <body>)
∣(let*-values (<mv binding spec>*) <body>)
∣(begin <sequence>)
∣(do (<iteration spec>*)
(<test> <do result>)
<command>*)
∣(delay <expression>)
∣(delay-force <expression>)
∣(parameterize ((<expression> <expression>)*)
<body>)
∣(guard (<identifier> <cond clause>*) <body>)
∣<quasiquotation>
∣(case-lambda <case-lambda clause>*)
<cond clause> ⟶(<test> <sequence>)
∣(<test>)
∣(<test> => <recipient>)
<recipient> ⟶<expression>
<case clause> ⟶((<datum>*) <sequence>)
∣((<datum>*) => <recipient>)
<binding spec> ⟶(<identifier> <expression>)
<mv binding spec> ⟶(<formals> <expression>)
<iteration spec> ⟶(<identifier> <init> <step>)
∣(<identifier> <init>)
<case-lambda clause> ⟶(<formals> <body>)
<init> ⟶<expression>
<step> ⟶<expression>
<do result> ⟶<sequence> ∣<empty>
<macro use> ⟶(<keyword> <datum>*)
<keyword> ⟶<identifier>
<macro block> ⟶ (let-syntax (<syntax spec>*) <body>)
∣(letrec-syntax (<syntax spec>*) <body>)
<syntax spec> ⟶(<keyword> <transformer spec>)
<includer> ⟶ ∣(include <string>+)
∣(include-ci <string>+)
The following grammar for quasiquote expressions is not context-free.
It is presented as a recipe for generating an infinite number of
production rules. Imagine a copy of the following rules for D = 1, 2,
3, …, where D is the nesting depth.
<quasiquotation> ⟶<quasiquotation 1>
<qq template 0> ⟶<expression>
<quasiquotation D> ⟶`<qq template D>
∣(quasiquote <qq template D>)
<qq template D> ⟶<simple datum>
∣<list qq template D>
∣<vector qq template D>
∣<unquotation D>
<list qq template D> ⟶(<qq template or splice D>*)
∣(<qq template or splice D>+ . <qq template D>)
∣'<qq template D>
∣<quasiquotation D + 1>
<vector qq template D> ⟶#(<qq template or splice D>*)
<unquotation D> ⟶,<qq template D−1>
∣(unquote <qq template D−1>)
<qq template or splice D> ⟶<qq template D>
∣<splicing unquotation D>
<splicing unquotation D> ⟶,@<qq template D−1>
∣(unquote-splicing <qq template D−1>)
In <quasiquotation>s, a <list qq template
D> can sometimes
be confused with either an <unquotation
D> or a <splicing
unquotation
D>. The interpretation as an
<unquotation> or <splicing
unquotation
D> takes precedence.
Note: Though this grammar does not say so, a top-level syntax-rules
pattern must be a list pattern, not a vector pattern or an identifier pattern.
<transformer spec> ⟶ (syntax-rules (<identifier>*) <syntax rule>*)
∣(syntax-rules <identifier> (<identifier>*)
<syntax rule>*)
<syntax rule> ⟶(<pattern> <template>)
<pattern> ⟶<pattern identifier>
∣<underscore>
∣(<pattern>*)
∣(<pattern>+ . <pattern>)
∣(<pattern>* <pattern> <ellipsis> <pattern>*)
∣(<pattern>* <pattern> <ellipsis> <pattern>*
. <pattern>)
∣#(<pattern>*)
∣#(<pattern>* <pattern> <ellipsis> <pattern>*)
∣<pattern datum>
<pattern datum> ⟶<string>
∣<character>
∣<boolean>
∣<number>
∣<bytevector>
<template> ⟶<pattern identifier>
∣(<template element>*)
∣(<template element>+ . <template>)
∣#(<template element>*)
∣<template datum>
<template element> ⟶<template>
∣<template> <ellipsis>
<template datum> ⟶<pattern datum>
<pattern identifier> ⟶<any identifier except ...>
<ellipsis> ⟶<an identifier defaulting to ...>
<underscore> ⟶<the identifier _>
<program> ⟶ <import declaration>+
<command or definition>+
<command or definition> ⟶<command>
∣<definition>
∣(begin <command or definition>+)
<definition> ⟶(define <identifier> <expression>)
∣(define (<identifier> <def formals>) <body>)
∣<syntax definition>
∣(define-values <formals> <body>)
∣(define-record-type <identifier>
<constructor> <identifier> <field spec>*)
∣(begin <definition>*)
<def formals> ⟶<identifier>*
∣<identifier>* . <identifier>
<constructor> ⟶(<identifier> <field name>*)
<field spec> ⟶(<field name> <accessor>)
∣(<field name> <accessor> <mutator>)
<field name> ⟶<identifier>
<accessor> ⟶<identifier>
<mutator> ⟶<identifier>
<syntax definition> ⟶ (define-syntax <keyword> <transformer spec>)
<library> ⟶ (define-library <library name>
<library declaration>*)
<library name> ⟶(<library name part>+)
<library name part> ⟶<identifier> ∣<uinteger 10>
<library declaration> ⟶(export <export spec>*)
∣<import declaration>
∣(begin <command or definition>*)
∣<includer>
∣(include-library-declarations <string>+)
∣(cond-expand <cond-expand clause>+)
∣(cond-expand <cond-expand clause>+
0 (else <library declaration>*))
<import declaration> ⟶(import <import set>+)
<export spec> ⟶<identifier>
∣(rename <identifier> <identifier>)
<import set> ⟶<library name>
∣(only <import set> <identifier>+)
∣(except <import set> <identifier>+)
∣(prefix <import set> <identifier>)
∣(rename <import set> (<identifier> <identifier>)+)
<cond-expand clause> ⟶ (<feature requirement> <library declaration>*)
<feature requirement> ⟶<identifier>
∣(library <library name>)
∣(and <feature requirement>*)
∣(or <feature requirement>*)
∣(not <feature requirement>)
This section provides a formal denotational semantics for the primitive
expressions of Scheme and selected built-in procedures. The concepts
and notation used here are described in [36]; the definition of
dynamic-wind is taken from [39].
The notation is summarized below:
The reason that expression continuations take sequences of values instead
of single values is to simplify the formal treatment of procedure calls
and multiple return values.
The boolean flag associated with pairs, vectors, and strings will be true
for mutable objects and false for immutable objects.
The order of evaluation within a call is unspecified. We mimic that
here by applying arbitrary permutations permute and
unpermute, which must be inverses, to the arguments in a call before
and after they are evaluated. This is not quite right since it suggests,
incorrectly, that the order of evaluation is constant throughout a program (for
any given number of arguments), but it is a closer approximation to the intended
semantics than a left-to-right evaluation would be.
The storage allocator new is implementation-dependent, but it must
obey the following axiom: if new σ ∈ L, then
σ (new σ ∣ L)↓2 = false.
The definition of calK is omitted because an accurate definition of
calK would complicate the semantics without being very interesting.
If P is a program in which all variables are defined before being
referenced or assigned, then the meaning of P is
| | | [ [ | ((lambda (I*) P’)
<undefined> …) |
| ] ] |
| |
where I* is the sequence of variables defined in P, P′
is the sequence of expressions obtained by replacing every definition
in P by an assignment, <undefined> is an expression that evaluates
to undefined, and
calE is the semantic function that assigns meaning to expressions.
Definition of calK deliberately omitted.
Here and elsewhere, any expressed value other than undefined may
be used in place of unspecified.
This section gives syntax definitions for the derived expression types in
terms of the primitive expression types (literal, variable, call, lambda,
if, and set!), except for quasiquote.
Conditional derived syntax types:
(define-syntax cond
(syntax-rules (else =>)
((cond (else result1 result2 ...))
(begin result1 result2 ...))
((cond (test => result))
(let ((temp test))
(if temp (result temp))))
((cond (test => result) clause1 clause2 ...)
(let ((temp test))
(if temp
(result temp)
(cond clause1 clause2 ...))))
((cond (test)) test)
((cond (test) clause1 clause2 ...)
(let ((temp test))
(if temp
temp
(cond clause1 clause2 ...))))
((cond (test result1 result2 ...))
(if test (begin result1 result2 ...)))
((cond (test result1 result2 ...)
clause1 clause2 ...)
(if test
(begin result1 result2 ...)
(cond clause1 clause2 ...)))))
(define-syntax case
(syntax-rules (else =>)
((case (key ...)
clauses ...)
(let ((atom-key (key ...)))
(case atom-key clauses ...)))
((case key
(else => result))
(result key))
((case key
(else result1 result2 ...))
(begin result1 result2 ...))
((case key
((atoms ...) => result))
(if (memv key '(atoms ...))
(result key)))
((case key
((atoms ...) result1 result2 ...))
(if (memv key '(atoms ...))
(begin result1 result2 ...)))
((case key
((atoms ...) => result)
clause clauses ...)
(if (memv key '(atoms ...))
(result key)
(case key clause clauses ...)))
((case key
((atoms ...) result1 result2 ...)
clause clauses ...)
(if (memv key '(atoms ...))
(begin result1 result2 ...)
(case key clause clauses ...)))))
(define-syntax and
(syntax-rules ()
((and) #t)
((and test) test)
((and test1 test2 ...)
(if test1 (and test2 ...) #f))))
(define-syntax or
(syntax-rules ()
((or) #f)
((or test) test)
((or test1 test2 ...)
(let ((x test1))
(if x x (or test2 ...))))))
(define-syntax when
(syntax-rules ()
((when test result1 result2 ...)
(if test
(begin result1 result2 ...)))))
(define-syntax unless
(syntax-rules ()
((unless test result1 result2 ...)
(if (not test)
(begin result1 result2 ...)))))
Binding constructs:
(define-syntax let
(syntax-rules ()
((let ((name val) ...) body1 body2 ...)
((lambda (name ...) body1 body2 ...)
val ...))
((let tag ((name val) ...) body1 body2 ...)
((letrec ((tag (lambda (name ...)
body1 body2 ...)))
tag)
val ...))))
(define-syntax let*
(syntax-rules ()
((let* () body1 body2 ...)
(let () body1 body2 ...))
((let* ((name1 val1) (name2 val2) ...)
body1 body2 ...)
(let ((name1 val1))
(let* ((name2 val2) ...)
body1 body2 ...)))))
The following
letrec macro uses the symbol
<undefined>
in place of an expression which returns something that when stored in
a location makes it an error to try to obtain the value stored in the
location. (No such expression is defined in Scheme.)
A trick is used to generate the temporary names needed to avoid
specifying the order in which the values are evaluated.
This could also be accomplished by using an auxiliary macro.
(define-syntax letrec
(syntax-rules ()
((letrec ((var1 init1) ...) body ...)
(letrec "generate_temp_names"
(var1 ...)
()
((var1 init1) ...)
body ...))
((letrec "generate_temp_names"
()
(temp1 ...)
((var1 init1) ...)
body ...)
(let ((var1 <undefined>) ...)
(let ((temp1 init1) ...)
(set! var1 temp1)
...
body ...)))
((letrec "generate_temp_names"
(x y ...)
(temp ...)
((var1 init1) ...)
body ...)
(letrec "generate_temp_names"
(y ...)
(newtemp temp ...)
((var1 init1) ...)
body ...))))
(define-syntax letrec*
(syntax-rules ()
((letrec* ((var1 init1) ...) body1 body2 ...)
(let ((var1 <undefined>) ...)
(set! var1 init1)
...
(let () body1 body2 ...)))))
(define-syntax let-values
(syntax-rules ()
((let-values (binding ...) body0 body1 ...)
(let-values "bind"
(binding ...) () (begin body0 body1 ...)))
((let-values "bind" () tmps body)
(let tmps body))
((let-values "bind" ((b0 e0)
binding ...) tmps body)
(let-values "mktmp" b0 e0 ()
(binding ...) tmps body))
((let-values "mktmp" () e0 args
bindings tmps body)
(call-with-values
(lambda () e0)
(lambda args
(let-values "bind"
bindings tmps body))))
((let-values "mktmp" (a . b) e0 (arg ...)
bindings (tmp ...) body)
(let-values "mktmp" b e0 (arg ... x)
bindings (tmp ... (a x)) body))
((let-values "mktmp" a e0 (arg ...)
bindings (tmp ...) body)
(call-with-values
(lambda () e0)
(lambda (arg ... . x)
(let-values "bind"
bindings (tmp ... (a x)) body))))))
(define-syntax let*-values
(syntax-rules ()
((let*-values () body0 body1 ...)
(let () body0 body1 ...))
((let*-values (binding0 binding1 ...)
body0 body1 ...)
(let-values (binding0)
(let*-values (binding1 ...)
body0 body1 ...)))))
(define-syntax define-values
(syntax-rules ()
((define-values () expr)
(define dummy
(call-with-values (lambda () expr)
(lambda args #f))))
((define-values (var) expr)
(define var expr))
((define-values (var0 var1 ... varn) expr)
(begin
(define var0
(call-with-values (lambda () expr)
list))
(define var1
(let ((v (cadr var0)))
(set-cdr! var0 (cddr var0))
v)) ...
(define varn
(let ((v (cadr var0)))
(set! var0 (car var0))
v))))
((define-values (var0 var1 ... . varn) expr)
(begin
(define var0
(call-with-values (lambda () expr)
list))
(define var1
(let ((v (cadr var0)))
(set-cdr! var0 (cddr var0))
v)) ...
(define varn
(let ((v (cdr var0)))
(set! var0 (car var0))
v))))
((define-values var expr)
(define var
(call-with-values (lambda () expr)
list)))))
(define-syntax begin
(syntax-rules ()
((begin exp ...)
((lambda () exp ...)))))
The following alternative expansion for
begin does not make use of
the ability to write more than one expression in the body of a lambda
expression. In any case, note that these rules apply only if the body
of the
begin contains no definitions.
(define-syntax begin
(syntax-rules ()
((begin exp)
exp)
((begin exp1 exp2 ...)
(call-with-values
(lambda () exp1)
(lambda args
(begin exp2 ...))))))
The following syntax definition
of
do uses a trick to expand the variable clauses.
As with
letrec above, an auxiliary macro would also work.
The expression
(if #f #f) is used to obtain an unspecific
value.
(define-syntax do
(syntax-rules ()
((do ((var init step ...) ...)
(test expr ...)
command ...)
(letrec
((loop
(lambda (var ...)
(if test
(begin
(if #f #f)
expr ...)
(begin
command
...
(loop (do "step" var step ...)
...))))))
(loop init ...)))
((do "step" x)
x)
((do "step" x y)
y)))
Here is a possible implementation of
delay,
force and
delay-force. We define the expression
(delay-force <expression>)
to have the same meaning as the procedure call
(make-promise #f (lambda () <expression>))
as follows
(define-syntax delay-force
(syntax-rules ()
((delay-force expression)
(make-promise #f (lambda () expression)))))
and we define the expression
(delay <expression>)
to have the same meaning as:
(delay-force (make-promise #t <expression>))
as follows
(define-syntax delay
(syntax-rules ()
((delay expression)
(delay-force (make-promise #t expression)))))
where
make-promise is defined as follows:
(define make-promise
(lambda (done? proc)
(list (cons done? proc))))
Finally, we define
force to call the procedure expressions in
promises iteratively using a trampoline technique following
[
38] until a non-lazy result (i.e. a value created by
delay instead of
delay-force) is returned, as follows:
(define (force promise)
(if (promise-done? promise)
(promise-value promise)
(let ((promise* ((promise-value promise))))
(unless (promise-done? promise)
(promise-update! promise* promise))
(force promise))))
with the following promise accessors:
(define promise-done?
(lambda (x) (car (car x))))
(define promise-value
(lambda (x) (cdr (car x))))
(define promise-update!
(lambda (new old)
(set-car! (car old) (promise-done? new))
(set-cdr! (car old) (promise-value new))
(set-car! new (car old))))
The following implementation of
make-parameter and
parameterize is suitable for an implementation with no threads.
Parameter objects are implemented here as procedures, using two
arbitrary unique objects
<param-set!> and
<param-convert>:
(define (make-parameter init . o)
(let* ((converter
(if (pair? o) (car o) (lambda (x) x)))
(value (converter init)))
(lambda args
(cond
((null? args)
value)
((eq? (car args) <param-set!>)
(set! value (cadr args)))
((eq? (car args) <param-convert>)
converter)
(else
(error "bad parameter syntax"))))))
Then
parameterize uses
dynamic-wind to dynamically rebind
the associated value:
(define-syntax parameterize
(syntax-rules ()
((parameterize ("step")
((param value p old new) ...)
()
body)
(let ((p param) ...)
(let ((old (p)) ...
(new ((p <param-convert>) value)) ...)
(dynamic-wind
(lambda () (p <param-set!> new) ...)
(lambda () . body)
(lambda () (p <param-set!> old) ...)))))
((parameterize ("step")
args
((param value) . rest)
body)
(parameterize ("step")
((param value p old new) . args)
rest
body))
((parameterize ((param value) ...) . body)
(parameterize ("step")
()
((param value) ...)
body))))
The following implementation of
guard depends on an auxiliary
macro, here called
guard-aux.
(define-syntax guard
(syntax-rules ()
((guard (var clause ...) e1 e2 ...)
((call/cc
(lambda (guard-k)
(with-exception-handler
(lambda (condition)
((call/cc
(lambda (handler-k)
(guard-k
(lambda ()
(let ((var condition))
(guard-aux
(handler-k
(lambda ()
(raise-continuable condition)))
clause ...))))))))
(lambda ()
(call-with-values
(lambda () e1 e2 ...)
(lambda args
(guard-k
(lambda ()
(apply values args)))))))))))))
(define-syntax guard-aux
(syntax-rules (else =>)
((guard-aux reraise (else result1 result2 ...))
(begin result1 result2 ...))
((guard-aux reraise (test => result))
(let ((temp test))
(if temp
(result temp)
reraise)))
((guard-aux reraise (test => result)
clause1 clause2 ...)
(let ((temp test))
(if temp
(result temp)
(guard-aux reraise clause1 clause2 ...))))
((guard-aux reraise (test))
(or test reraise))
((guard-aux reraise (test) clause1 clause2 ...)
(let ((temp test))
(if temp
temp
(guard-aux reraise clause1 clause2 ...))))
((guard-aux reraise (test result1 result2 ...))
(if test
(begin result1 result2 ...)
reraise))
((guard-aux reraise
(test result1 result2 ...)
clause1 clause2 ...)
(if test
(begin result1 result2 ...)
(guard-aux reraise clause1 clause2 ...)))))
(define-syntax case-lambda
(syntax-rules ()
((case-lambda (params body0 ...) ...)
(lambda args
(let ((len (length args)))
(letrec-syntax
((cl (syntax-rules ::: ()
((cl)
(error "no matching clause"))
((cl ((p :::) . body) . rest)
(if (= len (length '(p :::)))
(apply (lambda (p :::)
. body)
args)
(cl . rest)))
((cl ((p ::: . tail) . body)
. rest)
(if (>= len (length '(p :::)))
(apply
(lambda (p ::: . tail)
. body)
args)
(cl . rest))))))
(cl (params body0 ...) ...)))))))
This definition of
cond-expand does not interact with the
features procedure. It requires that each feature identifier provided
by the implementation be explicitly mentioned.
(define-syntax cond-expand
;; Extend this to mention all feature ids and libraries
(syntax-rules (and or not else r7rs library scheme base)
((cond-expand)
(syntax-error "Unfulfilled cond-expand"))
((cond-expand (else body ...))
(begin body ...))
((cond-expand ((and) body ...) more-clauses ...)
(begin body ...))
((cond-expand ((and req1 req2 ...) body ...)
more-clauses ...)
(cond-expand
(req1
(cond-expand
((and req2 ...) body ...)
more-clauses ...))
more-clauses ...))
((cond-expand ((or) body ...) more-clauses ...)
(cond-expand more-clauses ...))
((cond-expand ((or req1 req2 ...) body ...)
more-clauses ...)
(cond-expand
(req1
(begin body ...))
(else
(cond-expand
((or req2 ...) body ...)
more-clauses ...))))
((cond-expand ((not req) body ...)
more-clauses ...)
(cond-expand
(req
(cond-expand more-clauses ...))
(else body ...)))
((cond-expand (r7rs body ...)
more-clauses ...)
(begin body ...))
;; Add clauses here for each
;; supported feature identifier.
;; Samples:
;; ((cond-expand (exact-closed body ...)
;; more-clauses ...)
;; (begin body ...))
;; ((cond-expand (ieee-float body ...)
;; more-clauses ...)
;; (begin body ...))
((cond-expand ((library (scheme base))
body ...)
more-clauses ...)
(begin body ...))
;; Add clauses here for each library
((cond-expand (feature-id body ...)
more-clauses ...)
(cond-expand more-clauses ...))
((cond-expand ((library (name ...))
body ...)
more-clauses ...)
(cond-expand more-clauses ...))))