COP5556 Assignment 6
Complete the implementation of your CodeGenVisitor class so that it implements our entire language. Also, modify your TypeCheckVisitor from Assignment 4 to allow expressions like a <- b where b is an image. See the yellow highlighted text in Assignment 4 for more details. Abstract Syntax Mapping to JVM Program ::= (Dec | Statements)* The program itself does not have a name in the source. The generated class needs a name: this is passed as a parameter to the CodeGenVisitor constructor. The class is name, the class file is name.class. Dec ::= DecVar | DecImage DecVar ::= Type IDENT (Expression | ϵ ) Variables in our language map to static variables in the generated class of type String or int. Expression, if given, is the initial value of the variable. If not, the initial value of an Int is 0, of an Image or String is null. Type ::= Int | String | Image | Boolean | Void (nothing to do here) DecImage ∷= Type ( Expression0 Expression1 | ϵ) IDENT ( OP Expression2 | ϵ ) See below Statement ∷= StatementAssign | StatementOutFile | StatementOutScreen |StatementImageIn | StatementLoop The classes main(String[] args) method will contain instructions implementing our program. StatementOutFile ::= IDENT Expression Write the image in the variable to a file with the name given by the expression. Use LoggedIO.imageToFile. The imageToFile routine will prepend the value of cop5556fa20.resources.ImageResources.prefix + java.io.File.separator to allow one to conveniently specify the directory where images will be written. It also will append the suffix “.png”. StatementOutScreen ::= IDENT (Expression0 Expression1 | ϵ ) If the IDENT is int or string, generate code to output the value of the variable to the console. Use routines in the provided LoggedIO class. Display the image on the screen. Use the imageToScreen method in the updated LoggedIO class. StatementImageIn ::= IDENT Expression The IDENT represents a variable declared to be of type image. If expression is a string, the string should be the url or fully qualified filename of an image. The image will be loaded (use BufferedImageUtils.fetchImage). If the variable was declared with a size, the loaded image should be resized. If the Expression is an image, then the image should be copied, resizing if necessary. Use BufferedImageUtils.copyBufferedImage and, if needed, BufferedImageUtils.resizeBufferedImage. StatementAssign ::= IDENT Expression Generate code to evaluate the expression and store its value in the given variable. When the type is the image, set of image field of the LHS image to the reference of the image field in the RHS image. If the LHS image has a declared type, this is only allowed if the RHS image is that size. This requires a runtime test and should throw a PLPImageException with appropriate error message if it fails. A PLPImageException should also be thrown if the RHS image field is null. See below for more about exceptions. StatementLoop ::= IDENT (Expression0 | ϵ ) Expression1 See below Expression For each Expression type, generate code to evaluate the expression and leave its value on top of the stack. ExprConditional ::= Expression0 Expression1 Expression2 Generate code to Evaluate Expression0 If true, evaluate Expression1 and leave its value on top of the stack. Otherwise evaluate Expression2 and leave its value on top of the stack. BinaryExpr ::= Expression0 OP Expression1 Generate code to evaluate Expression0 and Expression1 and leave their values on top of the stack. Then generate code to apply the operator to those value and leave the result on top of the stack. ExprUnary ::= OP Expression Generate code to evaluate the Expression, then apply the operator and leave the result on top of the stack. ExprHash ::= Expression Attribute red, green, and blue get the corresponding color components of a pixel (int). Invoke routines in the provided PixelOps class. width and height get the width and height of the image. If the image has not been initialized, throw a PLPImageException with an appropriate error message. ExprIntLit Generate code to load the value of the constant onto the stack. (visitLcdInsn) ExprVar Get the value of the variable and leave it on the stack. (visitFieldInsn(GETSTATIC,...)). Handle X and Y, which may now appear in the context of loop statements. They should be implemented as local variables in the main method you are constructing. If the var is of type image, load a reference to the PLPImage onto the stack. ExprStringLit Generate code to load a reference to the String onto the stack. (visitLcdInsn) ExprConst Load the value of the constant onto the stack. ExprPixelSelector ::= Expression ExpressionX ExpressionY Generate code to leave the value of the three expressions on top of the stack and invoke PLPImage.selectPixel ExprPixelConstructor ::= Expressionr Expressiong Expressionb Generate code to leave the three expressions on top of the stack and invoke PixelOps.makePixel ExprArg ::= Expression Evaluate the expression and load the value of the String[] args array onto the stack. If the type is int, convert the String to an int by invoking Integer.parseInt. • Provided code in package cop5556fq20.runtime. This code is invoked by the programs in our language when they are executed, thus they are in a package called runtime. They contain some constants that may be used by your compiler, so they are also needed for code generation. The updated version of LoggedIO contains methods imageToFile and imageToScreen that are called in programs to write an image to a file or the screen respectively. They also include a PLPImageFile class that is used in Junit tests for images written to a file. BufferedImageUtils contains some useful routines needed to handle BufferedImages. PixelOps contains routines for manipulating pixels. PLPImage is the type used to implement the image type in our language. • Handling DecImage Images will be represented by instances of the provided class PLPImage. When a variable of image type is declared, a static variable of the class with that name should be added to the class (as was done with int and string variables in assignment 5) In addition, an instance of this class should be created and its reference stored in the variable. If the variable does not have a declared size, then the declaredSize field is null. If the declaration does not have a right hand side, then the “image” field is null. Images with a declared size retain that size throughout the program, resizing other images if necessary. Images without a declared size may take different sizes in the course of the program. The table below summarizes the values for image and declaredSize for various cases for image declarations. The class BufferedImageUtils provides several useful routines. Declaration form image declaredSize image a; null null image[w,h] a; null Dimension(w,h) image a <- b; (b is a string) Fetch BufferedImage from source b. b is a string holding a url or filename. Use BufferedImageUtils.fetchBufferedImage. null image[w,h] a <- b; (b is a string) Fetch BufferedImage from source b and resize to width = w, height = h. Use BufferedImageUtils.fetchBufferedImage and BufferedImageUtils.resizeBufferedImage. new Dimension(w,h) image a <- b; (b is an image) Copy b.image to this image. Used BufferedImage.Utils.copyBufferedImage. null image[w,h] a <- b; (b is an image) Copy b.image to this image and resize. Use BufferedImageUtils.copyBufferedImage and BufferedImageUtils.resizeBufferedImage. Dimension(w,h) image a = b; this.image = b.image; Set the bufferedImage image reference in PLPImage a to the same image as in PLPImage b. null image[w,h] a = b; Set the bufferedImage image reference in PLPImage a to the same image as in PLPImage b. This is only allowed if the current size of b is (w,h), which requires a runtime test to verify. If it fails a PLPImageException should be thrown. If the size is correct, then this.image = b.image. Dimension(w,h) Handling StatementLoop This is easiest to explain with an example from the concrete syntax: image[400,500] a; a = *[X,Y]: X <= Y : RED; The LHS of the loop statement should have been declared to be an image, and it must have a size, either given at its declaration as in the example, or by initializing it with another image. If a does not have a declared size and has not been initialized another way, then throw a PLPImageException with appropriate error message. The statement loops over all of the pixels in the image, using X and Y as variables selecting the pixel. For each pixel, the condition, if one has been given, is evaluated (in the example, the condition is X<= Y) and if true, the corresponding pixel of a is set to the value of the expression, here RED. This example, including the declaration, is equivalent to the following Java code fragment. PLPImage a = new PLPImage(BufferedImageUtils.createBufferedImage(400, 500), new Dimension(400,500)); a.ensureImageAllocated(0, 0); //These parameters are not correct. //They should be the line and posInLine from the source. See the discussion about exceptions. int w = a.getWidth(); int h = a.getHeight(); for (int X = 0; X < w; X++) { for (int Y = 0; Y < h; Y++) { if (X <= Y) { a.updatePixel(X, Y, RED); } } } • Handling exceptions If an exception is thrown during the runtime of our generated program, it should give the location in the source code. At any ASTNode, the line and posInLIne can be obtained from the first Token stored in the ASTNode. In my implementation, all exceptions have been thrown from java methods, so these values need to be loaded on the stack and passed as parameters to these methods. This is required for those methods that may throw a PLPImageException. • Using Java. Several classes containing methods written in Java have been provided in the package cop5556fa20.runtime. The name of the package indicates that these classes must be available when the classfile generated by our compiler is run. You will probably want to implement some Java methods of your own, when it would be easier to call a method than to use asm to generate the methods functionality. This is fine (and recommended). Put any additional classes needed at runtime in the cop5556fa20.runtime package. • Test cases. • You will need to change to ImageResources.java. Set binDir to the directory where you want the generated classfiles to go. It is easiest if this is the bin directory where the classfiles for your compiler is going. (Those will be in subdirectories corresponding to their packages. Yours should go in the bin dir itself with the default package.) If you do this, you should be able to run your generated file from the command line without having to deal with specifying classpaths for the cop5556fa20.runtime classes. You will need to specify the --enable-preview option. Also, for convenience, it is handy to define something like fileImage0 and fileImage1 to the fully qualified paths of images on your local machine. • A JFrame created in a Junit test will close when the tests are finished. To allow you to have more time to look at your images, call keepFrame() at the end of your test case and set doKeepFrames to true. This will cause the test case to wait for a character to be input from the console. • A getInputFromFile routine has been added if you want to read your program from a file. You have all the pieces now and it should be obvious how to put the code we have together to create a stand-alone compiler. • Running your generated classfiles • Example: java --enable-preview loopExampleFromDesc • This command should execute the code generated by provided test case loopExampleFromDesc, and show an image on the screen that looks like this: • Turn in • Jar file containing the source code CodeGenVisitorComplete along with Scanner.java, Parser.java, TypeCheckVisitor. Also include any provided code (such as the AST classes and the Runtime and utility classes necessary for 1. your compiler to run, and 2 generated code to run. As usual, also include CodeGenTest.java. (You do not need to include the asm jar files) • zip file with your git repository containing the history of your development • Naming format • firstname_lastname_ufid_hw6.jar • firstname_lastname_ufid_hw6git.zip You must create the zip file from a local clone of your repository. The file created with the github zipfile option does not include the history. The default jar file exported from eclipse does not include sources by default. You will need to check a box to ensure they are included. Your CodeGenTest and github repo will not be graded, but may be looked at in case of academic honesty issues. Failure to turn in conforming files may result in a zero on the assignment. We will subject your code to our set of junit tests and your grade will be determined solely by how many tests are passed. Additional requirements: • This code must remain in the provided packages. • Names and signatures of public or protected elements in the starter code must not be changed. • Your code should not import any classes other than those from the standard Java distribution and asm • You can submit multiple times and we will only grade your latest submission. The system will append a suffix to the file name for resubmissions—this is OK. And only the latest one will be graded. • Important!!! It is YOUR RESPONSIBILITY to submit the assignment on time, where time is determined by Canvas. If you wait until the last minute, it is possible that your submission will fail due to server overload. Start early and submit early.