Question 2: 19 points Topic: Writing interpreters and compilers Adapting to language evolution ================================================ Consider the following class dictionary: Main = E EOF. E : S | C. S = int. C = "(" Op E E ")". Op : A | M. A = "+". M = "*" . noparse visitor CompileVisitor = . And the following compiler (in one .beh file called program.beh): Main { {{ static void p(String s){ System.out.println(s); } public static void main(String args[]) throws Exception { Main m = Main.parse(System.in); Main.p(" print expression "); E e = m.get_e(); e.print(); Main.p(""); Main.p(" display expression "); e.display(); Main.p(""); Main.p(" display compiled expression "); e.compile(); } }} } * { // produces print() for all classes in cd void display() to * (DisplayVisitor); void print() to * (PrintVisitor); } CompileVisitor { init {{ Main.p (" start compilation "); }} before S {{ Main.p(" LOC " + host.get_v()); }} after C {{ host.get_op().comp(); }} finish {{ Main.p (" end compilation "); }} } Op { {{ abstract void comp(); }} } A { {{ void comp(){ Main.p(" ADI "); } }} } M { {{ void comp(){ Main.p(" MLI "); } }} } E { void compile() to * (CompileVisitor); } ================================================= Now update the class dictionary as follows: Main = E EOF. E : S | C. S = int. C = "(" Op E Option_E ")". Op : A | M. A = "+". M = "*" . Option_E : NonEmpty_E | Empty_E. NonEmpty_E = E. Empty_E = . noparse visitor CompileVisitor = Op. In other words, we make the second argument of a C-object optional. PART 1: 10 points ================================= Make minimal changes to program.beh to deal with the optional argument correctly. Write a method defaultV() for computing a default value for the second argument when it is missing (Empty_E). You may assume the existence of a method currentEnclosingC() which returns for any expression object the corresponding enclosing C-object. I have implemented this method for you using Java generics (using class Stack in java.util) as follows: CompileVisitor { // want to use Java generics to define the stack {{ Stack enclosingC = new Stack(); public C currentEnclosingC() { return enclosingC.peek(); } }} // leaving Java mode before C {{ enclosingC.push(host); }} after C {{ enclosingC.pop(); }} } This means that somewhere in the extension you have the call: currentEnclosingC().defaultV(); Implement the defaultV method as usually without writing a conditional statement. PART 2: 3 points ============================= The transition from E to Option_E is related to the null pointer exception problem in OO languages. When you use a web page that has not been well tested, you may run into a null pointer exception, meaning that you call a method on a non-existing object (null). How do you guarantee that there is no problem when the arg2 part is absent? Do you have to write a conditional statement to prevent the problem? PART 3: 3 points ============================= How do you have to change your extension from part 1, if you use design 2: Option_E : E | Empty_E. instead of design 1: Option_E : NonEmpty_E | Empty_E. NonEmpty_E = E. Explain your answer. PART 4: 3 points ============================= What are the advantages of design 1 over design 2 from the point of view of having a generic pattern to introduce optional parts for type E. design 1: Option_E : NonEmpty_E | Empty_E. NonEmpty_E = E. Empty_E = . design 2: Option_E : E | Empty_E. Empty_E = . What are the advantages/disadvantages of Option_E over [ E]?