Skip to content
Merged

Next #44

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions www/assignments.scrbl
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@
@include-section{assignments/4.scrbl}
@include-section{assignments/5.scrbl}
@include-section{assignments/6.scrbl}
@include-section{assignments/7.scrbl}

@;{assignment 8: quote in general, and quasiquote}
@;{assignment 9: standard library, IO}
235 changes: 235 additions & 0 deletions www/assignments/7.scrbl
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
#lang scribble/manual
@title[#:tag "Assignment 7" #:style 'unnumbered]{Assignment 7: Symbols, interning, and gensym}

@(require (for-label (except-in racket ...)))
@;(require "../notes/fraud-plus/semantics.rkt")
@;(require redex/pict)

@(require "../notes/ev.rkt")

@bold{Due: Tues, Nov 12, 11:59PM}

@(define repo "https://classroom.github.com/a/5UM2CXXa")

The goal of this assignment is to (1) implement symbols and the
@racket[eq?] primitive operation, (2) to implement symbol interning by
program transformation.

Assignment repository:
@centered{@link[repo repo]}

You are given a repository with a starter compiler similar to the
@seclink["Loot"]{Loot} language we studied in class.

The given code also implements all the ``plus'' features we've
developed in past assignments.

@section[#:tag-prefix "a7-" #:style 'unnumbered]{Symbols}

Your first task is to implement symbols for the Loot+ language.
You've used symbols extensively throughout the semester, so their use
should be familiar to you. A symbol evaluates to itself:

@ex[
'foo
]

Your first task is to implement a symbol data type. The given code
includes syntax checking for programs that may contain symbols and
run-time support for printing symbols. The compiler has been stubbed
for compiling symbols. You will need to implement
@racket[compile-symbol] in @tt{compile.rkt}.

A symbol can be represented much like a string: as a continuous
sequence of characters in memory, along with a length field. The type
tag is different, since strings and symbols should be disjoint data
types.

Once you implement @racket[compile-symbol], you should be able to
write programs that contain symbols.

@section[#:tag-prefix "a7-" #:style 'unnumbered]{Pointer equality}

Your next task is to implement the @racket[eq?] primitive operation,
which compares two values for pointer equality. Immediate values
(characters, integers, booleans, empty list, etc.) should be
pointer-equal to values that are ``the same.'' So for example:

@ex[
(eq? '() '())
(eq? 5 5)
(eq? #\a #\a)
(eq? #\t #\t)
]

On the other hand, values that are allocated in memory such as boxes,
pairs, procedures, etc., are only @racket[eq?] to each other if they
are allocated to the same location in memory. So for example, the
following could all produce @racket[#f]:

@ex[
(eq? (λ (x) x) (λ (x) x))
(eq? (cons 1 2) (cons 1 2))
(eq? (box 1) (box 1))
]

However these must be produce @racket[#t]:

@ex[
(let ((x (λ (x) x)))
(eq? x x))
(let ((x (cons 1 2)))
(eq? x x))
(let ((x (box 1)))
(eq? x x))
]

Applying @racket[eq?] to any two values from disjoint data types
should produce @racket[#f]:

@ex[
(eq? 0 #f)
(eq? #\a "a")
(eq? '() #t)
(eq? 'fred "fred")
]

The given compiler is stubbed for the @racket[eq?] primitive. You
must implement @racket[compile-eq?].

@section[#:tag-prefix "a7-" #:style 'unnumbered]{Interning symbols}

One thing you may notice at this point is that because symbols are
allocated in memory, the behavior @racket[eq?] with your compiler
differs from Racket's behavior.

In Racket, two symbols which are written the same way in a given
program are @racket[eq?] to each other.

@ex[
(eq? 'x 'x)
]

But your compiler will (probably) produce @racket[#f].

The problem is that Racket ``interns'' symbols, meaning that all
occurrences of a symbol are allocated to the same memory location.
(Languages like Java also do this with string literals.)

Extend your compiler so that @racket[eq?] behaves correctly on
symbols. Note, you should @emph{not change the way @racket[eq?]
works}, rather you should change how symbols are handled by the
compiler.

The most effective way to implement symbol interning is to apply a
program transformation to the given program to compile. This
transformation should replace multiple occurrences of the same symbol
with a variable that is bound to that symbol, and that symbol should
be allocated exactly once.

So for example,

@racketblock[
(eq? 'fred 'fred)
]

could be transformed to:

@racket[
(let ((x 'fred))
(eq? x x))
]

The latter should result in @racket[#t] since the @racket['fred]
symbol is allocated exactly once.

The compiler uses a @racket[intern-symbols] function, which does
nothing in the given code, but should be re-defined to perform the
symbol interning program transformation. Note: you probably want to
define a few helper functions to make @racket[intern-symbols] work.

@section[#:tag-prefix "a7-" #:style 'unnumbered]{Generating symbols}

Finally, implement the @racket[gensym] primitive, which generates a
symbol distinct from all other symbols.

To keep things simple, you should implement the nullary version of
@racket[gensym], i.e. it should take zero arguments and produce a new
symbol.

The following program should always produce @racket[#f]:

@ex[
(eq? (gensym) (gensym))
]

But the following should always produce @racket[#t]:


@ex[
(let ((x (gensym)))
(eq? x x))
]

Note: Racket's @racket[gensym] will generate a new name for a symbol,
usually something like @racket['g123456], where each successive call
to @racket[gensym] will produce @racket['g123457], @racket['g123458],
@racket['g123459], etc. Yours does not have to do this (although it's
fine if it does). All that matters is that @racket[gensym] produces a
symbol that is not @racket[eq?] to any other symbol but itself.

@section[#:tag-prefix "a7-" #:style 'unnumbered]{Bonus}

Should you find yourself having completed the assignment with time to
spare, you could try implementing @racket[compile-tail-apply], which
compiles uses of @racket[apply] that appear in tail position. It is
currently defined to use the non-tail-call code generator, which means
@racket[apply] does not make a proper tail call.

Keep in mind that this language, the subexpression of @racket[apply]
are arbitrary expressions: @racket[(apply _e0 _e1)] and that
@racket[_e0] may evaluate to a closure, i.e. a function with a saved
environment. Moreover, the function may have been defined to have
variable arity. All of these issues will conspire to make tail calls
with @racket[apply] tricky to get right.

This isn't worth any credit, but you might learn something.

@section[#:tag-prefix "a7-" #:style 'unnumbered]{Testing}

You can test your code in several ways:

@itemlist[

@item{Using the command line @tt{raco test .} from
the directory containing the repository to test everything.}

@item{Using the command line @tt{raco test <file>} to
test only @tt{<file>}.}

@item{Pushing to github. You can
see test reports at:
@centered{@link["https://travis-ci.com/cmsc430/"]{
https://travis-ci.com/cmsc430/}}

(You will need to be signed in in order see results for your private repo.)}]

Note that only a small number of tests are given to you, so you should
write additional test cases.

@bold{There is separate a repository for tests!} When you push your
code, Travis will automatically run your code against the tests. If
you would like to run the tests locally, clone the following
repository into the directory that contains your compiler and run
@tt{raco test .} to test everything:

@centered{@tt{https://github.com/cmsc430/assign07-test.git}}

This repository will evolve as the week goes on, but any time there's
a significant update it will be announced on Piazza.

@section[#:tag-prefix "a7-" #:style 'unnumbered]{Submitting}

Pushing your local repository to github ``submits'' your work. We
will grade the latest submission that occurs before the deadline.

27 changes: 21 additions & 6 deletions www/notes/loot.scrbl
Original file line number Diff line number Diff line change
Expand Up @@ -707,19 +707,19 @@ Here's the function for emitting closure construction code:

@#reader scribble/comment-reader
(racketblock
;; (Listof Variable) Label Expr CEnv -> Asm
(define (compile-λ xs f e0 c)
;; (Listof Variable) Label (Listof Varialbe) CEnv -> Asm
(define (compile-λ xs f ys c)
`(;; Save label address
(lea rax (offset ,f 0))
(mov (offset rdi 0) rax)

;; Save the environment
(mov r8 ,(length ys))
(mov (offset rdi 1) r8)
(mov r9 rdi)
(add r9 16)
,@(copy-env-to-heap ys c 0)

;; Return a pointer to the closure
(mov rax rdi)
(or rax ,type-proc)
Expand Down Expand Up @@ -903,8 +903,8 @@ handle the tasks listed above:
;; (Listof Variable) (Listof Lambda) Expr CEnv -> Asm
(define (compile-letrec fs ls e c)
(let ((c0 (compile-letrec-λs ls c))
(c1 (compile-letrec-init fs ls (append fs c)))
(c2 (compile-e e (append fs c))))
(c1 (compile-letrec-init fs ls (append (reverse fs) c)))
(c2 (compile-e e (append (reverse fs) c))))
`(,@c0
,@c1
,@c2)))
Expand Down Expand Up @@ -969,9 +969,24 @@ We can give a spin:
#f
(even? (sub1 x))))))
(even? 10))))

(asm-interp
(compile
'(letrec ((map (λ (f ls)
(letrec ((mapper (λ (ls)
(if (empty? ls)
'()
(cons (f (car ls)) (mapper (cdr ls)))))))
(mapper ls)))))
(map (λ (f) (f 0))
(cons (λ (x) (add1 x))
(cons (λ (x) (sub1 x))
'()))))))
]




@section[#:tag-prefix "loot"]{Syntactic sugar for function definitions}

The @racket[letrec] form is a generlization of the
Expand Down
19 changes: 19 additions & 0 deletions www/notes/loot/compile-file.rkt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#lang racket
(provide (all-defined-out))
(require "compile.rkt" #;"syntax.rkt" "asm/printer.rkt")

;; String -> Void
;; Compile contents of given file name,
;; emit asm code on stdout
(define (main fn)
(with-input-from-file fn
(λ ()
(let ((p (read-program)))
; assumed OK for now
;(unless (and (prog? p) (closed? p))
; (error "syntax error"))
(asm-display (compile p))))))

(define (read-program)
(regexp-match "^#lang racket" (current-input-port))
(read))
Loading