Skip to content

Commit 31e0d11

Browse files
authored
Merge pull request #44 from dvanhorn/next
Next
2 parents f0902fc + 1569180 commit 31e0d11

9 files changed

Lines changed: 465 additions & 77 deletions

File tree

www/assignments.scrbl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@
99
@include-section{assignments/4.scrbl}
1010
@include-section{assignments/5.scrbl}
1111
@include-section{assignments/6.scrbl}
12+
@include-section{assignments/7.scrbl}
13+
14+
@;{assignment 8: quote in general, and quasiquote}
15+
@;{assignment 9: standard library, IO}

www/assignments/7.scrbl

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
#lang scribble/manual
2+
@title[#:tag "Assignment 7" #:style 'unnumbered]{Assignment 7: Symbols, interning, and gensym}
3+
4+
@(require (for-label (except-in racket ...)))
5+
@;(require "../notes/fraud-plus/semantics.rkt")
6+
@;(require redex/pict)
7+
8+
@(require "../notes/ev.rkt")
9+
10+
@bold{Due: Tues, Nov 12, 11:59PM}
11+
12+
@(define repo "https://classroom.github.com/a/5UM2CXXa")
13+
14+
The goal of this assignment is to (1) implement symbols and the
15+
@racket[eq?] primitive operation, (2) to implement symbol interning by
16+
program transformation.
17+
18+
Assignment repository:
19+
@centered{@link[repo repo]}
20+
21+
You are given a repository with a starter compiler similar to the
22+
@seclink["Loot"]{Loot} language we studied in class.
23+
24+
The given code also implements all the ``plus'' features we've
25+
developed in past assignments.
26+
27+
@section[#:tag-prefix "a7-" #:style 'unnumbered]{Symbols}
28+
29+
Your first task is to implement symbols for the Loot+ language.
30+
You've used symbols extensively throughout the semester, so their use
31+
should be familiar to you. A symbol evaluates to itself:
32+
33+
@ex[
34+
'foo
35+
]
36+
37+
Your first task is to implement a symbol data type. The given code
38+
includes syntax checking for programs that may contain symbols and
39+
run-time support for printing symbols. The compiler has been stubbed
40+
for compiling symbols. You will need to implement
41+
@racket[compile-symbol] in @tt{compile.rkt}.
42+
43+
A symbol can be represented much like a string: as a continuous
44+
sequence of characters in memory, along with a length field. The type
45+
tag is different, since strings and symbols should be disjoint data
46+
types.
47+
48+
Once you implement @racket[compile-symbol], you should be able to
49+
write programs that contain symbols.
50+
51+
@section[#:tag-prefix "a7-" #:style 'unnumbered]{Pointer equality}
52+
53+
Your next task is to implement the @racket[eq?] primitive operation,
54+
which compares two values for pointer equality. Immediate values
55+
(characters, integers, booleans, empty list, etc.) should be
56+
pointer-equal to values that are ``the same.'' So for example:
57+
58+
@ex[
59+
(eq? '() '())
60+
(eq? 5 5)
61+
(eq? #\a #\a)
62+
(eq? #\t #\t)
63+
]
64+
65+
On the other hand, values that are allocated in memory such as boxes,
66+
pairs, procedures, etc., are only @racket[eq?] to each other if they
67+
are allocated to the same location in memory. So for example, the
68+
following could all produce @racket[#f]:
69+
70+
@ex[
71+
(eq? (λ (x) x) (λ (x) x))
72+
(eq? (cons 1 2) (cons 1 2))
73+
(eq? (box 1) (box 1))
74+
]
75+
76+
However these must be produce @racket[#t]:
77+
78+
@ex[
79+
(let ((x (λ (x) x)))
80+
(eq? x x))
81+
(let ((x (cons 1 2)))
82+
(eq? x x))
83+
(let ((x (box 1)))
84+
(eq? x x))
85+
]
86+
87+
Applying @racket[eq?] to any two values from disjoint data types
88+
should produce @racket[#f]:
89+
90+
@ex[
91+
(eq? 0 #f)
92+
(eq? #\a "a")
93+
(eq? '() #t)
94+
(eq? 'fred "fred")
95+
]
96+
97+
The given compiler is stubbed for the @racket[eq?] primitive. You
98+
must implement @racket[compile-eq?].
99+
100+
@section[#:tag-prefix "a7-" #:style 'unnumbered]{Interning symbols}
101+
102+
One thing you may notice at this point is that because symbols are
103+
allocated in memory, the behavior @racket[eq?] with your compiler
104+
differs from Racket's behavior.
105+
106+
In Racket, two symbols which are written the same way in a given
107+
program are @racket[eq?] to each other.
108+
109+
@ex[
110+
(eq? 'x 'x)
111+
]
112+
113+
But your compiler will (probably) produce @racket[#f].
114+
115+
The problem is that Racket ``interns'' symbols, meaning that all
116+
occurrences of a symbol are allocated to the same memory location.
117+
(Languages like Java also do this with string literals.)
118+
119+
Extend your compiler so that @racket[eq?] behaves correctly on
120+
symbols. Note, you should @emph{not change the way @racket[eq?]
121+
works}, rather you should change how symbols are handled by the
122+
compiler.
123+
124+
The most effective way to implement symbol interning is to apply a
125+
program transformation to the given program to compile. This
126+
transformation should replace multiple occurrences of the same symbol
127+
with a variable that is bound to that symbol, and that symbol should
128+
be allocated exactly once.
129+
130+
So for example,
131+
132+
@racketblock[
133+
(eq? 'fred 'fred)
134+
]
135+
136+
could be transformed to:
137+
138+
@racket[
139+
(let ((x 'fred))
140+
(eq? x x))
141+
]
142+
143+
The latter should result in @racket[#t] since the @racket['fred]
144+
symbol is allocated exactly once.
145+
146+
The compiler uses a @racket[intern-symbols] function, which does
147+
nothing in the given code, but should be re-defined to perform the
148+
symbol interning program transformation. Note: you probably want to
149+
define a few helper functions to make @racket[intern-symbols] work.
150+
151+
@section[#:tag-prefix "a7-" #:style 'unnumbered]{Generating symbols}
152+
153+
Finally, implement the @racket[gensym] primitive, which generates a
154+
symbol distinct from all other symbols.
155+
156+
To keep things simple, you should implement the nullary version of
157+
@racket[gensym], i.e. it should take zero arguments and produce a new
158+
symbol.
159+
160+
The following program should always produce @racket[#f]:
161+
162+
@ex[
163+
(eq? (gensym) (gensym))
164+
]
165+
166+
But the following should always produce @racket[#t]:
167+
168+
169+
@ex[
170+
(let ((x (gensym)))
171+
(eq? x x))
172+
]
173+
174+
Note: Racket's @racket[gensym] will generate a new name for a symbol,
175+
usually something like @racket['g123456], where each successive call
176+
to @racket[gensym] will produce @racket['g123457], @racket['g123458],
177+
@racket['g123459], etc. Yours does not have to do this (although it's
178+
fine if it does). All that matters is that @racket[gensym] produces a
179+
symbol that is not @racket[eq?] to any other symbol but itself.
180+
181+
@section[#:tag-prefix "a7-" #:style 'unnumbered]{Bonus}
182+
183+
Should you find yourself having completed the assignment with time to
184+
spare, you could try implementing @racket[compile-tail-apply], which
185+
compiles uses of @racket[apply] that appear in tail position. It is
186+
currently defined to use the non-tail-call code generator, which means
187+
@racket[apply] does not make a proper tail call.
188+
189+
Keep in mind that this language, the subexpression of @racket[apply]
190+
are arbitrary expressions: @racket[(apply _e0 _e1)] and that
191+
@racket[_e0] may evaluate to a closure, i.e. a function with a saved
192+
environment. Moreover, the function may have been defined to have
193+
variable arity. All of these issues will conspire to make tail calls
194+
with @racket[apply] tricky to get right.
195+
196+
This isn't worth any credit, but you might learn something.
197+
198+
@section[#:tag-prefix "a7-" #:style 'unnumbered]{Testing}
199+
200+
You can test your code in several ways:
201+
202+
@itemlist[
203+
204+
@item{Using the command line @tt{raco test .} from
205+
the directory containing the repository to test everything.}
206+
207+
@item{Using the command line @tt{raco test <file>} to
208+
test only @tt{<file>}.}
209+
210+
@item{Pushing to github. You can
211+
see test reports at:
212+
@centered{@link["https://travis-ci.com/cmsc430/"]{
213+
https://travis-ci.com/cmsc430/}}
214+
215+
(You will need to be signed in in order see results for your private repo.)}]
216+
217+
Note that only a small number of tests are given to you, so you should
218+
write additional test cases.
219+
220+
@bold{There is separate a repository for tests!} When you push your
221+
code, Travis will automatically run your code against the tests. If
222+
you would like to run the tests locally, clone the following
223+
repository into the directory that contains your compiler and run
224+
@tt{raco test .} to test everything:
225+
226+
@centered{@tt{https://github.com/cmsc430/assign07-test.git}}
227+
228+
This repository will evolve as the week goes on, but any time there's
229+
a significant update it will be announced on Piazza.
230+
231+
@section[#:tag-prefix "a7-" #:style 'unnumbered]{Submitting}
232+
233+
Pushing your local repository to github ``submits'' your work. We
234+
will grade the latest submission that occurs before the deadline.
235+

www/notes/loot.scrbl

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -707,19 +707,19 @@ Here's the function for emitting closure construction code:
707707

708708
@#reader scribble/comment-reader
709709
(racketblock
710-
;; (Listof Variable) Label Expr CEnv -> Asm
711-
(define (compile-λ xs f e0 c)
710+
;; (Listof Variable) Label (Listof Varialbe) CEnv -> Asm
711+
(define (compile-λ xs f ys c)
712712
`(;; Save label address
713713
(lea rax (offset ,f 0))
714714
(mov (offset rdi 0) rax)
715-
715+
716716
;; Save the environment
717717
(mov r8 ,(length ys))
718718
(mov (offset rdi 1) r8)
719719
(mov r9 rdi)
720720
(add r9 16)
721721
,@(copy-env-to-heap ys c 0)
722-
722+
723723
;; Return a pointer to the closure
724724
(mov rax rdi)
725725
(or rax ,type-proc)
@@ -903,8 +903,8 @@ handle the tasks listed above:
903903
;; (Listof Variable) (Listof Lambda) Expr CEnv -> Asm
904904
(define (compile-letrec fs ls e c)
905905
(let ((c0 (compile-letrec-λs ls c))
906-
(c1 (compile-letrec-init fs ls (append fs c)))
907-
(c2 (compile-e e (append fs c))))
906+
(c1 (compile-letrec-init fs ls (append (reverse fs) c)))
907+
(c2 (compile-e e (append (reverse fs) c))))
908908
`(,@c0
909909
,@c1
910910
,@c2)))
@@ -969,9 +969,24 @@ We can give a spin:
969969
#f
970970
(even? (sub1 x))))))
971971
(even? 10))))
972+
973+
(asm-interp
974+
(compile
975+
'(letrec ((map (λ (f ls)
976+
(letrec ((mapper (λ (ls)
977+
(if (empty? ls)
978+
'()
979+
(cons (f (car ls)) (mapper (cdr ls)))))))
980+
(mapper ls)))))
981+
(map (λ (f) (f 0))
982+
(cons (λ (x) (add1 x))
983+
(cons (λ (x) (sub1 x))
984+
'()))))))
972985
]
973986

974987

988+
989+
975990
@section[#:tag-prefix "loot"]{Syntactic sugar for function definitions}
976991

977992
The @racket[letrec] form is a generlization of the

www/notes/loot/compile-file.rkt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#lang racket
2+
(provide (all-defined-out))
3+
(require "compile.rkt" #;"syntax.rkt" "asm/printer.rkt")
4+
5+
;; String -> Void
6+
;; Compile contents of given file name,
7+
;; emit asm code on stdout
8+
(define (main fn)
9+
(with-input-from-file fn
10+
(λ ()
11+
(let ((p (read-program)))
12+
; assumed OK for now
13+
;(unless (and (prog? p) (closed? p))
14+
; (error "syntax error"))
15+
(asm-display (compile p))))))
16+
17+
(define (read-program)
18+
(regexp-match "^#lang racket" (current-input-port))
19+
(read))

0 commit comments

Comments
 (0)