-
Notifications
You must be signed in to change notification settings - Fork 28
/
09-desugaring.rkt
182 lines (131 loc) · 5.32 KB
/
09-desugaring.rkt
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#lang racket
#|-----------------------------------------------------------------------------
;; Desugaring (Syntax Transformation)
Our language now includes arithmetic expressions, variables, and closures.
We can (and will!) grow the language, but how should we go about building
support for new language constructs?
We could modify the interpreter to recognize and implement each new construct,
but this might not scale well. Our interpreter could grow complex and hard to
maintain, and our language might become bloated and messy.
Alternatively, we could specify a small "core" language which will provide a
minimal set of features needed to implement all other language constructs.
The parser would accept programs written in the full language (aka the "input
language") and create a syntax tree, as before. Before sending the tree to the
interpreter, however, we would apply one or more "desugaring" passes to the
syntax tree to transform all its nodes into those of the core language.
The final desugared form of the syntax tree -- now serving as a flexible
internal representation (IR) of our program -- can be directly evaluated.
Here is the process described above:
Program (input language) => Parser => Syntax Tree / IR (input language)
IR (input language) => Desugaring Pass(es) ... => IR (core language)
IR (core language) => Interpreter / Eval
---
As an example of desugaring, we will add support for lambdas and function
applications that accept > 1 params/args. This will *not* require any
modifications to our interpreter, as we can rewrite such expressions using
the existing core language.
e.g., support for lambda and function application with > 1 params/args
(lambda (x y z ...) can be written as (lambda (x)
body) (lambda (y)
(lambda (z)
...
body)))
if we ensure that all function applications of the form (f x y z ...)
are rewritten as ((((f x) y) z) ...)
-----------------------------------------------------------------------------|#
;; Some test cases
(define p1 '(lambda (x y z) (* x (+ y z))))
(define p2 '(f x y z))
(define p3 '((lambda (x y z) (* x (+ y z))) 2 3 4))
;; integer value
(struct int-exp (val) #:transparent)
;; arithmetic expression
(struct arith-exp (op lhs rhs) #:transparent)
;; variable
(struct var-exp (id) #:transparent)
;; let expression
(struct let-exp (ids vals body) #:transparent)
;; lambda expression
(struct lambda-exp (id body) #:transparent)
;; function application
(struct app-exp (fn arg) #:transparent)
;; Parser
(define (parse sexp)
(match sexp
;; integer literal
[(? integer?)
(int-exp sexp)]
;; arithmetic expression
[(list '+ lhs rhs)
(arith-exp "PLUS" (parse lhs) (parse rhs))]
[(list '* lhs rhs)
(arith-exp "TIMES" (parse lhs) (parse rhs))]
;; identifier (variable)
[(? symbol?)
(var-exp sexp)]
;; let expressions
[(list 'let (list (list id val) ...) body)
(let-exp (map parse id) (map parse val) (parse body))]
;; lambda expression -- modified for > 1 params
[(list 'lambda (list ids ...) body)
(lambda-exp ids (parse body))]
;; function application -- modified for > 1 args
[(list f args ...)
(app-exp (parse f) (map parse args))]
;; basic error handling
[_ (error (format "Can't parse: ~a" sexp))]))
;; Desugar-er -- i.e., syntax transformer
(define (desugar exp)
(match exp
((arith-exp op lhs rhs)
(arith-exp op (desugar lhs) (desugar rhs)))
((let-exp ids vals body)
(let-exp ids (map desugar vals) (desugar body)))
((lambda-exp ids body)
(foldr (lambda (id body) (lambda-exp id body))
(desugar body)
ids))
((app-exp fn args)
(foldl (lambda (id fn) (app-exp fn id))
(desugar fn)
(map desugar args)))
(_ exp)))
;; function value + closure
(struct fun-val (id body env) #:transparent)
;; Interpreter
(define (eval expr)
(let eval-env ([expr expr]
[env '()])
(match expr
;; int literal
[(int-exp val) val]
;; arithmetic expression
[(arith-exp "PLUS" lhs rhs)
(+ (eval-env lhs env) (eval-env rhs env))]
[(arith-exp "TIMES" lhs rhs)
(* (eval-env lhs env) (eval-env rhs env))]
;; variable binding
[(var-exp id)
(let ([pair (assoc id env)])
(if pair (cdr pair) (error (format "~a not bound!" id))))]
;; let expression
[(let-exp (list (var-exp id) ...) (list val ...) body)
(let ([vars (map cons id
(map (lambda (v) (eval-env v env)) val))])
(eval-env body (append vars env)))]
;; lambda expression
[(lambda-exp id body)
(fun-val id body env)]
;; function application
[(app-exp f arg)
(match-let ([(fun-val id body clenv) (eval-env f env)]
[arg-val (eval-env arg env)])
(eval-env body (cons (cons id arg-val) clenv)))]
;; basic error handling
[_ (error (format "Can't evaluate: ~a" expr))])))
;; REPL
(define (repl)
(let ([stx (desugar (parse (read)))]) ; added desugaring step
(when stx
(println (eval stx))
(repl))))