Com S 342 --- Principles of Programming Languages HOMEWORK 11: OBJECTS AND CLASSES (File $Date: 2005/04/19 23:43:21 $) Due: problems 4-5 at beginning of class, Thursday, April 21, 2005; problem 6 at beginning of class, Tuesday, April 26, 2005. In this homework, you will learn more about object-oriented languages. For this homework, be sure to clearly mark what parts of the code you changed (e.g., with a comment in the Scheme code) to solve what problems. For coding problems, aside from your printout of the code, you must also hand in a transcript of testing. See the directory $PUB/homework/hw11 for test scripts for each coding problem. Use the procedure test-hw11 to run our tests. If you want to see more output, execute (show-test-output!); to see less output, execute (hide-test-output!). The section headings below give the readings related to the problems. Please read them. Refer to homework 7 for some ideas to help aid your understanding when reading chapter 3 of the text. ESSENTIALS OF PROGRAMMING LANGUAGES: Chapter 5, pages 115-119 1. (suggested practice) [why list-find-last-position works] Do exercise 5.2 on page 189 in the text. (Our implementation of envrionments for the section 5.2 interpreter is found in $PUB/lib342/environment-5-4-2.scm.) 2. (suggested practice) [queue class] Using one of the object-oriented interpreters (for example, the one in $PUB/lib342/5-4-4.scm), do exercise 5.8 on page 198 of the text. For the extensions called for in parts 2 and 3, don't modify the queue class you write in section 1, but instead use a subclass. 3. (50 points; extra credit) [lexical addressing for OO programs] Do exercise 5.9 in the text, by modifying a copy of $PUB/lib342/5-4-4.scm. Hand in a printout of your code, with the changes clearly marked, and its testing. 4. (15 points) [instanceof] Do exercise 5.11 on page 199 of the text. To start, copy $PUB/lib342/5-4-4.scm to your directory, as in cp $PUB/lib342/5-4-4.scm my-5-4-4.scm The problem is to implement a new expression, instanceof, with syntax: (expression ("instanceof" expression identifier ) instanceof-exp) (Note that the concrete syntax was corrected in the second printing of the text, so that it looks more like Java; so now it looks like instanceof o c1 instead of instanceof(o,c1).) The following gives several examples of this operator. --> class c1 extends object method initialize () 1 class c2 extends c1 method initialize () 1 class c3 extends c2 method initialize () 1 let o1 = new c1() o2 = new c2() o3 = new c3() in list(instanceof o1 c1, instanceof o2 c1, instanceof o3 c1, instanceof o1 c2, instanceof o2 c2, instanceof o3 c2, instanceof o1 c3, instanceof o2 c3, instanceof o3 c3) (1 1 1 0 1 1 0 0 1) A hint: make the test of whether one class is a subclass of another as a separate Scheme procedure. You can test this problem using (test-hw11 "instanceof") (If you test this problem after doing problem 6 below, which changes the syntax of the defined language in an incompatible way, use (test-hw11 "privacy-instanceof") instead of the above.) As always hand in a printout of a transcript of your testing and your code. However, you can wait to print out your code, until you are done with the problems in this section. If you do that, clearly mark with comments the part of the code used to solve this problem. 5. (20 points) [fieldref and fieldset] Do exercise 5.14 on page 200 of the text. For this problem you will be adding the following syntax to the language: (expression ("fieldref" expression identifier) fieldref-exp) (expression ("fieldset" expression identifier "=" expression) fieldset-exp) (Note that the concrete syntax of fieldset was corrected in the second printing of the text, so that it looks like fieldset o x = e instead of fieldset o x e.) We leave the value returned by a fieldset expression undefined; that is, you can make it whatever you wish. You can test this problem using (test-hw11 "fieldref-fieldset") (If you test this problem after doing problem 6 below, which changes the syntax of the defined language in an incompatible way, use (test-hw11 "privacy-fieldref-fieldset") instead of the above.) As always hand in a printout of a transcript of your testing and your code. However, you can wait to print out your code, until you are done with the problems in this section. If you do that, clearly mark with comments the part of the code used to solve this problem. 6. (50 points) [visibility for methods] Do exercise 5.17 on page 200 of the text. Note that the rules given in the text are a bit ambiguous and don't match typical OO langauges, like Java. To clarify the rule for private methods, a private method declared in a class C may only be called from within code that appears in class C. The rule for protected methods given in the text is incomplete for languages like Java. As stated in the text, a protected method declared in class C may only be called from within code that appears in class C or a subclass of C. However, there is an additional condition: a protected method call with receiver o can only be called from a class D if the runtime class of o is D or a subclass of D. (See the examples below, and also consider the Java code in $PUB/homework/hw11/package1/C1.java and $PUB/homework/hw11/package2/C2.java, which gives the same error.) As part of solving this problem, replace the concrete syntax for method-decl with the following: (method-decl (modifier "method" identifier "(" (separated-list identifier ",") ")" ; method formals expression ) a-method-decl) And add the following syntax for modifiers: (modifier ("public") public-modifier) (modifier ("protected") protected-modifier) (modifier ("private") private-modifier) You will then need to add appropriate abstract syntax, and finally implement this in the interpreter. The following are some examples of errors that should be detected. Your program should generate these same error messages. (We use eopl:error to generate them.) --> class c1 extends object public method initialize () 1 protected method mprot () 2 let o1 = new c1() in send o1 mprot() check-privacy: Method mprot is a protected method in class c1, so it can't be called from the main expression --> class c1 extends object public method initialize () 1 private method mpriv () 2 let o1 = new c1() in send o1 mpriv() check-privacy: Method mpriv is a private method in class c1, so it can't be called from the main expression --> class c1 extends object public method initialize () 1 protected method mprot () 2 class c2 extends object public method initialize () 1 public method mpub (o1) send o1 mprot() let o1 = new c1() o2 = new c2() in send o2 mpub(o1) check-privacy: Method mprot is a protected method in class c1, so it can't be called from class c2 --> class c1 extends object public method initialize () 1 private method mpriv () 2 class c2 extends object public method initialize () 1 public method mpub (o1) send o1 mpriv() let o1 = new c1() o2 = new c2() in send o2 mpub(o1) check-privacy: Method mpriv is a private method in class c1, so it can't be called from class c2 --> class c1 extends object public method initialize () 1 protected method mprot () 2 class c2 extends c1 public method initialize () 1 public method mpub (o1) send o1 mprot() let o1 = new c1() o2 = new c2() in send o2 mpub(o1) check-privacy: Method mprot is a protected method in class c1, so it can't be called on an object of class c1 from class c2 In languages like Java and C#, making a method private also has the effect of making calls to that method not be dynamically dispatched, however, doing this is *not* part of this problem. That is, you should still call even private methods using dynamic dispatch. This will cause an example like the following to have an error, like that shown: --> class c1 extends object public method initialize () 1 protected method mprot () send self mpriv() private method mpriv () 2 class c2 extends c1 public method initialize () 1 protected method mprot () super mprot () private method mpriv () 3 public method mpub () send self mprot() let o2 = new c2() in send o2 mpub() check-privacy: Method mpriv is a private method in class c2, so it can't be called from class c1 The error above happens because mpub calls mprot (in c2) which calls mprot in c1, which calls mpriv, but because we use dynamic dispatch, this call is resolved to the method mpriv in c2, not to mpriv in c1. Although we will call this behavior correct for purposes of this problem, it isn't want is really desirable in a real language. However, it would add too many complications to the problem for you to deal with it properly. So in working the problem, you only need to report errors when methods are called that shouldn't be, and don't need to change the way dispatch works for private methods. For testing, since the main point of this is to prevent use of certain methods from certain classes, you will have to test this yourself. Show that all kinds of method calls that should work do work, and also show that all kinds of method calls that should not work give appropriate error messages. You can use (test-hw11 "privacy") to test that method calls that should work do work, but you'll have to systematically test other conditions that generate errors yourself, as they are not tested in our tests. Hints: there are two ways to go find the errors required by this problem: statically or dynamically. Dynamic checking means checking method calls while the program is running. To do this, you will need to be able to determine, at the time when a method is being called: a. the name of the current class in which the call is happening (if any), and b. the class in which the method was declared. This information is not currently tracked by the interpreters, but you can add it to the environment (why?), in order to make the checks at runtime. Static checking means checking method calls before the program is run; if you do this, you will have to make a pass over the AST of the classes, for example in elaborate-class-decls! or in evaluate-program. If you do this, then you will also need to enforce the rule that an overriding method in a subclass cannot be more private than the method it overrides (why?). For example, if c2 is a subclass of c1, then c2's method m() cannot be private if c1's m() method is public. You may need to add additional helping methods to determine information such as an upper bound on the class of an expression. Since this is similar to type checking (see chapter 4), we suggest that you try the dynamic approach described above, as it probably involves less code and code that you are more familiar with. As always hand in a printout of a transcript of your testing and your code. However, you can wait to print out your code, until you are done with the problems in this section. If you do that, clearly mark with comments the part of the code used to solve this problem. 8. (suggested practice) Read over the other exercises on pages 200-203 to get some idea of the issues for object-oriented languages. 7. (30 points; extra credit) [name mangling for overloading] Do exercise 5.25 on page 202.