/
syntax.scm
168 lines (145 loc) · 5.38 KB
/
syntax.scm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
; Control structures
; (cond) goes through a list of tests, evaluating each one
; in order of appearance. Once a matching precondition is
; found, its consequent is tail-called and no further
; preconditions are evaluated.
(define-syntax cond (syntax-rules (else =>)
[(cond) #f]
[(cond (else expr1 expr2 ...))
(begin expr1 expr2 ...)]
[(cond (test => function) clause ...)
(let ([temp test])
(if temp
(function temp)
(cond clause ...)))]
[(cond (test expression ...) clause ...)
(if test
(begin expression ...)
(cond clause ...))]))
; (case) acts like Ruby's case statement. The value of the
; given expression is compared against a series of lists;
; once a list is found to include the value, the expressions
; following the list are evaluated and no further lists
; are tested.
(define-syntax case (syntax-rules (else)
[(case key) #f]
[(case key (else expr1 expr2 ...))
(begin expr1 expr2 ...)]
[(case key
((cell ...) expr1 expr2 ...)
clause ...)
(let ([temp key])
(if (member temp '(cell ...))
(begin expr1 expr2 ...)
(case temp
clause ...)))]))
;----------------------------------------------------------------
; Binding constructs
; (let), (let*) and (letrec) each create a new scope and bind
; values to some symbols before executing a series of lists.
; They differ according to how they evaluate the bound values.
; (let) evaluates values in the enclosing scope, so lambdas will
; not be able to refer to other values assigned using the (let).
(define-syntax let (syntax-rules ()
[(let ([variable init] ...) body ...)
((lambda (variable ...)
body ...)
init ...)]
[(let name ([variable init] ...) body ...)
(letrec ([name (lambda (variable ...)
body ...)])
(name init ...))]))
; (let*) creates a new scope for each variable and evaluates
; each expression in its enclosing scope. Basically a shorthand
; for several nested (let)s. Variables may refer to those that
; preceed them but not vice versa.
(define-syntax let* (syntax-rules ()
[(let* ([n1 e1] [n2 e2] [n3 e3] ...) body ...) ; 2 or more bindings
(let ([n1 e1])
(let* ([n2 e2] [n3 e3] ...) body ...))]
[(let* ([name expression] ...) body ...) ; 0 or 1 binding
(let ([name expression] ...) body ...)]))
; (letrec) evaluates values in the inner scope, so lambdas are
; able to refer to other values assigned using the (letrec).
(define-syntax letrec (syntax-rules ()
[(letrec ([variable init] ...) body ...)
((lambda ()
(define variable init) ...
body ...))]))
(define let-syntax let)
(define letrec-syntax letrec)
;----------------------------------------------------------------
; Iteration
; (do) is similar to the 'while' construct in procedural
; languages. It assigns initial values to a set of variables,
; then performs the list of given commands in a loop. If
; before any iteration the test is found to be false, the
; loop is halted and the value of the expression following
; the test is returned.
(define-syntax do (syntax-rules ()
[(do ([variable init step ...] ...) ; Allow 0 or 1 step
(test expression ...)
command ...)
(let loop ([variable init] ...)
(if test
(begin expression ...)
(begin
command ...
(loop (do "step" variable step ...) ...))))]
[(do "step" variable)
variable]
[(do "step" variable step)
step]))
;----------------------------------------------------------------
; Boolean combinators
; (and) returns the first falsey value returned by the list
; of expressions, or returns the value of the last expression
; if all values are truthy.
(define-syntax and (syntax-rules ()
[(and test) test]
[(and test1 test2 ...)
(let ([temp test1])
(if (not temp)
temp
(and test2 ...)))]))
; (or) returns the first truthy value returned by the list
; of expressions, or returns the value of the last expression
; if all values are falsey.
(define-syntax or (syntax-rules ()
[(or test) test]
[(or test1 test2 ...)
(let ([temp test1])
(if temp
temp
(or test2 ...)))]))
;----------------------------------------------------------------
; Delayed evaluation
; (delay) allows the evaluation of an expression to be delayed
; by wrapping it in a promise. Use (force) to evaluate the promise
; at a later time. The expression inside a promise is only
; ever evaluated once, so a promise can be implemented as a
; memoized closure.
(define-syntax delay (syntax-rules ()
[(delay expression)
(let ([forced #f]
[memo #f])
(lambda ()
(if forced
memo
(begin
(set! memo expression)
(set! forced #t)
memo))))]))
;----------------------------------------------------------------
; Quasiquotation
; (quasiquote) is similar to (quote), except that when it
; encounters an (unquote) or (unquote-splicing) expression
; it will evaluate it and insert the result into the
; surrounding quoted list.
(define-syntax quasiquote (syntax-rules (unquote unquote-splicing)
[`,expr expr]
[`(,@first . rest) (append first `rest)]
[`(first . rest) (cons `first `rest)]
[`#(,@first rest ...) (list->vector `(,@first rest ...))]
[`#(expr ...) (list->vector `(expr ...))]
[`expr 'expr]))