diff --git a/www/assignments.scrbl b/www/assignments.scrbl index 1a3d71e8..8f6f5246 100644 --- a/www/assignments.scrbl +++ b/www/assignments.scrbl @@ -12,7 +12,7 @@ @include-section{assignments/7.scrbl} @include-section{assignments/8.scrbl} @include-section{assignments/9.scrbl} -@include-section{assignments/10.scrbl} +@;include-section{assignments/10.scrbl} @;{assignment 8: quote in general, and quasiquote} diff --git a/www/assignments/5.scrbl b/www/assignments/5.scrbl index 12192892..095265fe 100644 --- a/www/assignments/5.scrbl +++ b/www/assignments/5.scrbl @@ -1,47 +1,113 @@ #lang scribble/manual @(require "../defns.rkt") -@title[#:tag "Assignment 5" #:style 'unnumbered]{Assignment 5: When and unless} +@title[#:tag "Assignment 5" #:style 'unnumbered]{Assignment 5: Let There Be (Many) Variables} @(require (for-label a86/ast (except-in racket ...))) @bold{Due: @assign-deadline[5]} -Details of this assignment will be released later in the semester. +The goal of this assignment is to extend a compiler with binding forms +that can take any number of arguments. -@;{ -The goal of this assignment is to extend the language developed in -@secref{Extort} with new forms of control flow expressions: -@racket[when]- and @racket[unless]-expressions. +@section[#:tag-prefix "a5-" #:style 'unnumbered]{Overview} -@section[#:tag-prefix "a5-" #:style 'unnumbered]{Extort+} +For this assignment, you are given a @tt{fraud-plus.zip} file on ELMS +with a starter compiler similar to the @seclink["Fraud"]{Fraud} +language we studied in class. -The Extort+ language extends Extort in the follow ways: +@section[#:tag-prefix "a5-" #:style 'unnumbered]{Fraud+} + +The Fraud+ language extends the Fraud language we studied in class with some +new features: @itemlist[ -@item{adding @racket[when],} -@item{adding @racket[unless], and} -@item{bringing forward all the features of Dupe+.} + +@item{The features added in @seclink["Assignment 4"]{Assignment 4}, namely: + + @itemlist[ + + @item{@racket[abs], @racket[-], and @racket[not]} + @item{@racket[cond]} + @item{@racket[case]} + + ]} + +@item{New primitives @racket[integer?] and @racket[boolean?].} + +@item{An extended @racket[let] form that can bind multiple variables at once.} + +@item{Back-referencing @racket[let*] form that can bind multiple variables at once.} + ] +@subsection[#:tag-prefix "a5-" #:style 'unnumbered]{From Dupe++ to Fraud+} -@section[#:tag-prefix "a5-" #:style 'unnumbered]{Testing} +Implement the @racket[abs], unary @racket[-], and @racket[not] operations and +the @racket[cond] and @racket[case] forms from +@seclink["Assignment 4"]{Assignment 4} by modifying @tt{interp.rkt}, +@tt{interp-prim.rkt}, @tt{compile.rkt}, and @tt{compile-ops.rkt}. You can +start from your previous code, but you will need to update it to work for the +code you are given. What's essentially left for you to do is to make sure to +correctly signal an error (@racket['err]) when these constructs are +applied to the wrong type of argument. -You can test your code in several ways: +While you're at it, implement the predicates @racket[integer?] and +@racket[boolean?] for checking the type of an argument, modeled by the +@racket[char?] predicate that was covered in the lectures. -@itemlist[ +@subsection[#:tag-prefix "a5-" #:style 'unnumbered]{Generalizing Let} + +The Fraud language has a @tt{let} form that binds a single variable in the +scope of some expression. This is a restriction of the more general form of +@racket[let] that binds any number of expressions. So, for example, + +@racketblock[ +(let ((x 1) (y 2) (z 3)) + _e) +] + +simultaneously binds @racket[x], @racket[y], and @racket[z] in the scope of +@racket[_e]. + +The syntax of a @racket[let] expression allows any number of binders to occur, +so @racket[(let () _e)] is valid syntax and is equivalent to @racket[_e]. + +The binding of each variable is only in-scope within the body, @bold{not} in +the right-hand sides of any of the @racket[let]. So, for example, +@racketblock[(let ((x 1) (y x)) 0)] is a syntax error because the occurrence of +@racket[x] is not bound. + +The provided interpreter and compiler work when the @racket[let] +expression happens to bind a single variable, but you must revise the +code to work for any number of bindings. - @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 } to - test only @tt{}.} +@subsection[#:tag-prefix "a5-" #:style 'unnumbered]{Back-Referencing Let} + +Similar to @racket[let], there is also @racket[let*] that can also bind any +number of expressions. The difference is that previous bindings are available +in the right-hand sides of subsequent bindings. For example, + +@racketblock[ +(let* ((x 1) (y 2) (z (add1 y))) + _e) ] -Note that only a small number of tests are given to you, so you should -write additional test cases. +binds @racket[x] to 1, @racket[y] to 2, and @racket[z] to 3 in +the scope of @racket[_e]. + +The syntax of a @racket[let*] expression allows any number of binders to occur, +so @racket[(let* () _e)] is valid syntax and is equivalent to @racket[_e]. + +Unlike @racket[let], @racketblock[(let* ((x 1) (y x)) 0)] is @emph{not} a +syntax error. However, bindings are only available forward, so +@racketblock[(let* ((x y) (y 1)) 0)] @emph{is} a syntax error. + +The provided interpreter and compiler work when the @racket[let*] +expression happens to bind a single variable, but you must revise the +code to work for any number of bindings. @section[#:tag-prefix "a5-" #:style 'unnumbered]{Submitting} -To submit, use @tt{make} from within the code directory to create a -zip file containing your work and submit it to Gradescope. -} \ No newline at end of file +To submit, use @tt{make} from within the @tt{fraud-plus} directory to +create a zip file containing your work and submit it to Gradescope. diff --git a/www/assignments/6.scrbl b/www/assignments/6.scrbl index 6fa300c9..aace6dc9 100644 --- a/www/assignments/6.scrbl +++ b/www/assignments/6.scrbl @@ -1,6 +1,6 @@ #lang scribble/manual @(require "../defns.rkt") -@title[#:tag "Assignment 6" #:style 'unnumbered]{Assignment 6: Binding many variables} +@title[#:tag "Assignment 6" #:style 'unnumbered]{Assignment 6: List primitives and n-ary primitives} @(require (for-label a86 (except-in racket ...))) diff --git a/www/assignments/8.scrbl b/www/assignments/8.scrbl index 7cb3f28e..5755b31f 100644 --- a/www/assignments/8.scrbl +++ b/www/assignments/8.scrbl @@ -1,6 +1,6 @@ #lang scribble/manual @(require "../defns.rkt") -@title[#:tag "Assignment 8" #:style 'unnumbered]{Assignment 8: List primitives} +@title[#:tag "Assignment 8" #:style 'unnumbered]{Assignment 8: Functions with default arguments} @(require (for-label a86 (except-in racket ...))) diff --git a/www/assignments/9.scrbl b/www/assignments/9.scrbl index d9b501b0..6f5d77f2 100644 --- a/www/assignments/9.scrbl +++ b/www/assignments/9.scrbl @@ -1,6 +1,6 @@ #lang scribble/manual @(require "../defns.rkt") -@title[#:tag "Assignment 9" #:style 'unnumbered]{Assignment 9: Functions with default arguments} +@title[#:tag "Assignment 9" #:style 'unnumbered]{Assignment 9: Patterns} @(require (for-label a86 (except-in racket ...))) diff --git a/www/defns.rkt b/www/defns.rkt index 6c813a86..2d675351 100644 --- a/www/defns.rkt +++ b/www/defns.rkt @@ -58,7 +58,6 @@ "Thursday, September 18, 11:59PM" "Thursday, September 25, 11:59PM" "Thursday, October 2, 11:59PM" - "Thursday, October 9, 11:59PM" "Thursday, October 23, 11:59PM" "Thursday, October 30, 11:59PM" "Thursday, November 6, 11:59PM" diff --git a/www/notes/abscond.scrbl b/www/notes/abscond.scrbl index ce9a20b2..b8ab85b6 100644 --- a/www/notes/abscond.scrbl +++ b/www/notes/abscond.scrbl @@ -13,9 +13,7 @@ @(define codeblock-include (make-codeblock-include #'here)) -@(ev '(require rackunit a86)) -@(for-each (λ (f) (ev `(require (file ,(path->string (build-path langs "abscond" f)))))) - '("main.rkt" "correct.rkt")) +@(ev '(require rackunit a86 abscond abscond/correct)) @(define (shellbox . s) (parameterize ([current-directory (build-path langs "abscond")]) diff --git a/www/notes/evildoer.scrbl b/www/notes/evildoer.scrbl index b02ff0bd..794bc823 100644 --- a/www/notes/evildoer.scrbl +++ b/www/notes/evildoer.scrbl @@ -18,12 +18,9 @@ @(define codeblock-include (make-codeblock-include #'h)) -@(ev '(require rackunit a86)) -@(for-each (λ (f) (ev `(require (file ,(path->string (build-path langs "evildoer" f)))))) - '("main.rkt" "compile-ops.rkt" "correct.rkt")) - +@(ev '(require rackunit a86 evildoer evildoer/correct evildoer/compile-ops)) +@;{This is needed for the example that uses current-objs} @(ev `(current-directory ,(path->string (build-path langs "evildoer")))) -@(void (ev '(with-output-to-string (thunk (system "make runtime.o"))))) @(require (for-syntax racket/base)) @(begin-for-syntax diff --git a/www/notes/extort.scrbl b/www/notes/extort.scrbl index d95279eb..5957ba96 100644 --- a/www/notes/extort.scrbl +++ b/www/notes/extort.scrbl @@ -15,12 +15,8 @@ @(define codeblock-include (make-codeblock-include #'h)) -@(ev '(require rackunit a86)) -@(for-each (λ (f) (ev `(require (file ,(path->string (build-path langs "extort" f)))))) - '("main.rkt" "correct.rkt" "compile-ops.rkt")) +@(ev '(require rackunit a86 extort extort/compile-ops extort/correct)) -@(ev `(current-directory ,(path->string (build-path langs "extort")))) -@(void (ev '(with-output-to-string (thunk (system "make runtime.o"))))) @;{Hack to get un-provided functions from compile-ops} @(ev '(require (only-in rackunit require/expose))) @(ev '(require/expose extort/compile-ops [assert-integer assert-char assert-byte assert-codepoint])) @@ -270,20 +266,19 @@ check: (ex ;; Produces (add1 v) if v is an integer value, #f otherwise (define (plus1 v) ;; Value -> Integer | Boolean - (bits->value - (asm-interp - (prog (Global 'entry) - (Label 'entry) - (Mov 'rax (value->bits v)) - (Mov 'r9 'rax) - (And 'r9 mask-int) - (Cmp 'r9 type-int) - (Jne 'err) - (Add 'rax (value->bits 1)) - (Ret) - (Label 'err) - (Mov 'rax (value->bits #f)) - (Ret))))) + (run + (prog (Global 'entry) + (Label 'entry) + (Mov 'rax (value->bits v)) + (Mov 'r9 'rax) + (And 'r9 mask-int) + (Cmp 'r9 type-int) + (Jne 'err) + (Add 'rax (value->bits 1)) + (Ret) + (Label 'err) + (Mov 'rax (value->bits #f)) + (Ret)))) (plus1 0) (plus1 1) @@ -301,8 +296,7 @@ error. The @racket[asm-interp] intercepts these calls are returns the @racket['err] symbol to match what the interpreter does: @ex[ -(current-objs '("runtime.o")) -(asm-interp +(run (prog (Global 'entry) (Label 'entry) (Extern 'raise_error) @@ -315,22 +309,19 @@ error: (ex ;; Produces (add1 v) if v is an integer, 'err otherwise (define (plus1 v) ;; Value -> Integer | 'err - (match - (asm-interp - (prog (Global 'entry) - (Label 'entry) - (Mov 'rax (value->bits v)) - (Mov 'r9 'rax) - (And 'r9 mask-int) - (Cmp 'r9 type-int) - (Jne 'err) - (Add 'rax (value->bits 1)) - (Ret) - (Label 'err) - (Extern 'raise_error) - (Call 'raise_error))) - ['err 'err] - [b (bits->value b)])) + (run + (prog (Global 'entry) + (Label 'entry) + (Mov 'rax (value->bits v)) + (Mov 'r9 'rax) + (And 'r9 mask-int) + (Cmp 'r9 type-int) + (Jne 'err) + (Add 'rax (value->bits 1)) + (Ret) + (Label 'err) + (Extern 'raise_error) + (Call 'raise_error)))) (plus1 0) (plus1 1) @@ -427,6 +418,6 @@ totality of the semantics: And again, we can randomly test the compiler by generating programs and inputs: @ex[ -(require "random.rkt") +(require extort/random) (for ((i 100)) (check-compiler (random-expr) (random-input)))] diff --git a/www/notes/fraud.scrbl b/www/notes/fraud.scrbl index 71afcb38..1120f3fb 100644 --- a/www/notes/fraud.scrbl +++ b/www/notes/fraud.scrbl @@ -2,22 +2,26 @@ @(require (for-label (except-in racket ... compile) a86/ast)) @(require redex/pict + redex/reduction-semantics racket/runtime-path scribble/examples - (except-in fraud/semantics ext lookup) - (prefix-in sem: (only-in fraud/semantics ext lookup)) "utils.rkt" "ev.rkt" "../utils.rkt") -@(define codeblock-include (make-codeblock-include #'h)) -@(ev '(require rackunit a86)) -@(ev `(current-directory ,(path->string (build-path langs "fraud")))) -@(void (ev '(with-output-to-string (thunk (system "make runtime.o"))))) -@(for-each (λ (f) (ev `(require (file ,f)))) - '("main.rkt" "translate.rkt")) +@(define-language L (e ::= ignored)) +@(define-extended-language F-let L + (e ::= .... x (let ((x e)) e)) + (x ::= variable)) + +@(define-extended-language F-prim2 L + (e ::= .... (p2 e_1 e_2)) + (p2 ::= + - < =)) + +@(define codeblock-include (make-codeblock-include #'h)) +@(ev '(require rackunit a86 fraud fraud/translate)) @(define this-lang "Fraud") @(define prefix (string-append this-lang "-")) @@ -66,9 +70,10 @@ what it should produce. Adding a notion of variable binding also means we need to add variables to the syntax of expressions. -Together this leads to the following grammar for @|this-lang|: +Together this leads to the following grammar for concrete +@|this-lang|: -@centered{@render-language[F-pre]} +@centered{@render-language[F-let]} Which can be modeled with the following data type definition: @@ -117,9 +122,10 @@ What's new are the following @emph{binary} operations: (= _e0 _e1) ] -This leads to the following revised grammar for @|this-lang|: +This leads to the following additions to the grammar for concrete +@|this-lang|: -@centered[(render-language G)] +@centered[(render-language F-prim2)] We can model it as a datatype as usual: