11#lang scribble/manual
2- @title[#:tag "Assignment 6 " #:style 'unnumbered ]{Assignment 6: Arities! }
2+ @title[#:tag "Assignment 6 " #:style 'unnumbered ]{Assignment 6: Syntax Checking }
33
44@(require (for-label (except-in racket ... )))
5- @(require redex/pict)
5+ @(require "../notes/ev.rkt "
6+ "../notes/utils.rkt " )
67
7- @( require " ../notes/ev.rkt " )
8+ @bold{Due: Tuesday, November 23nd, 11:59PM EDT}
89
9- @bold{Due: Thursday, April 29th, 11:59PM EST}
10+ The goal of this assignment is to add syntax checking to our compiler.
1011
11- @(define repo "https://github.com/cmsc430/assign06 " )
12+ You are given a repository with a starter compiler similar to the
13+ @seclink["Mountebank " ]{Mountebank} language we studied in class. You
14+ are tasked with:
1215
13- The goal of this assignment is (1 ) to implement arity checking in a
14- language with functions, and (2 ) to implement the @racket[procedure-arity]
15- operation for accessing the arity of a function.
16+ @itemlist[
1617
17- Assignment repository:
18- @centered{@link[repo repo]}
18+ @item{implementing compile-time syntax checking.}
19+
20+ ]
21+
22+ @section[#:tag-prefix "a6- " #:style 'unnumbered #:tag "checking " ]{Syntax Checking}
23+
24+
25+ Up until now, we've written our compiler assuming that programs are
26+ well-formed, but there has never been any code that actually checks
27+ this assumption. The assumptions go beyond the properties checked
28+ during parsing. For example, our parser will happily accept a program
29+ like @racket[(lambda (x x) x)], and the compiler will emit code for it
30+ even though this is not a well-formed program.
31+
32+ The idea of this assignment is to implement a function, defined in
33+ @tt{check-syntax.rkt}:
34+
35+ @#reader scribble/comment-reader
36+ (racketblock
37+ ;; Prog -> Prog
38+ (define (check-syntax p) ... )
39+ )
40+
41+ If the program is well-formed, it should behave like the identity
42+ function, returning the same program it was given as input. On
43+ ill-formed programs, it should signal an error using @racket[error].
44+
45+ For the purposes of this assignment, the quality of the error messages
46+ doesn't matter, although in real programming language implementations,
47+ good error messages are crucial.
48+
49+ Here are the properties that should be checked of each program:
50+
51+ @itemlist[
52+
53+ @item{Every @racket[define ]d function should have a distinct name.}
54+
55+ @item{Every function parameter should be distinct from the other
56+ parameters of that function.}
57+
58+ @item{Every function's name should be distinct from all of its
59+ parameters' names.}
60+
61+ @item{Every function name and variable should not clash with any of
62+ the keywords of our language, e.g. @racket[lambda ], @racket[if ], etc.
63+ (Note this is not a restriction Racket puts on programs; the following
64+ is a perfectly reasonable expression: @racket[(λ (λ ) λ )], but we'll
65+ consider this a syntax error.)}
66+
67+ @item{Every pattern variable in a pattern should be distinct. (Racket
68+ also allows this , but it has a complicated run-time semantics which we
69+ don't implement so instead we just rule out these programs.)}
70+
71+ ]
72+
73+ The starter code calls @racket[check-syntax] in both
74+ @tt{compile-file.rkt} and @tt{interp-file.rkt}. The definition of
75+ @racket[check-syntax] is stubbed out in @tt{check-syntax.rkt}.
76+
77+ There are a few tests included in @tt{test/check-syntax.rkt}. For
78+ this assignment, very few tests are included so you should write your
79+ own.
80+
81+ @section[#:tag-prefix "a6- " #:style 'unnumbered #:tag "update " ]{Update a86}
82+
83+ There have been some changes to a86 that you'll need. You can update
84+ the @tt{langs} package with the following:
85+
86+ @verbatim|{raco pkg update langs}|
1987
20- You are given a repository with a starter compiler similar to the
21- @seclink["Loot " ]{Loot} language we studied in class. The only change
22- has been the addition of parsing code for the unary
23- @racket[procedure-arity] primitive.
24-
25- @section[#:tag-prefix "a6- " #:style 'unnumbered ]{Arity-check yourself, before you wreck yourself}
26-
27- When we started looking at functions and function applications, we
28- wrote an interpreter that did arity checking, i.e. just before making
29- a function call, it confirmed that the function definition had as many
30- parameters as the call had arguments.
31-
32- The compiler, however, does no such checking. This means that
33- arguments will silently get dropped when too many are supplied and
34- (much worse!) parameters will be bound to junk values when too few are
35- supplied; the latter has the very unfortunate effect of possibly
36- leaking local variable's values to expressions out of the scope of
37- those variables. (This has important security ramifications.)
38-
39- The challenge here is that the arity needs to be checked at run-time,
40- since we have first class functions. But at run-time, we don't have
41- access to the syntax of the function definition or the call. So in
42- order to check the arity of a call, we must emit code to do the
43- checking and to compute the relevant information for carrying out the
44- check.
45-
46- The main high-level idea is that: when compiling a function
47- definition, the arity of the function is clear from the number of
48- parameters of the definition; when compiling a call, the number of
49- arguments is also obvious. Therefore, what's needed is a way for the
50- the function and the call to communicate and check their corresponding
51- arity information.
52-
53- We recommend storing the arity of the function as an additional piece
54- of information in the closure during its compilation. Then, during a
55- call you can access that arity and check it before making the call.
56- Bonus: it makes implementing @racket[procedure-arity] really
57- straightforward: you just have to access that number.
58-
59- Just like we've been saying all semester, there are multiple other
60- ways of going about this , feel free to design and implement a solution
61- that works correctly - and consider the trade-offs! For example,
62- another approach would be to treat the arity of the function as if it
63- were the first argument of the function. A function of @math{n}
64- arguments would then be compiled as a function of @math{n+1}
65- arguments. A call with @math{m} arguments would be compiled as a call
66- with @math{m+1} arguments, where the value of the first argument is
67- @math{m}. The emitted code for a function should then check that the
68- value of the first argument is equal to @math{n} and signal an error
69- when it is not. But how would you implement @racket[procedure-arity]
70- in this case? (This is not a rhetorical question, if you have a
71- realistic solution to this , send us an e-mail!)
72-
73- Your job is to modify @racket[compile.rkt] and to implement this arity
74- checking protocol and the @racket[procedure-arity] primitive. It might
75- help to implement the primitive before compiling the calls themselves,
76- to partially test your implementation. Unlike previous assignments,
77- there are no explicitly marked TODOs (with the exception of
78- @racket[procedure-arity]). You have to make sure you modify all places
79- where closures are created/accessed to ensure that your changes work
80- correctly!
81-
82- As always, remember to test your code using both the testcases
83- provided and by adding your own!
8488
8589@section[#:tag-prefix "a6- " #:style 'unnumbered ]{Submitting}
8690
87- Submit just the @tt{compile.rkt} file on Gradescope.
91+ You should submit on Gradescope. You should submit a zip file that has
92+ exactly the same structure that the stub contains. We will only use
93+ the @tt{check-syntax.rkt} files for grading, so make sure all your
94+ work is contained there!
0 commit comments