Lab: BugsWorld Virtual Machine ByteCode Generator
Objective
In this lab you will complete the code generation phase of the BL
compiler by writing and testing the Program generatedCode secondary
method.
Setup
To get started, import the project for this lab, ProgramCodeGenerator,
from the ProgramCodeGenerator.zip file available at this
link. If you don't remember how to do this,
see the Setup
instructions
in an earlier lab.
Method
- First carefully look through the file
Program1GeneratedCode1.java, which is an extension forProgram1that overrides theProgramSecondaryimplementation of thegeneratedCodemethod, and make sure that you familiarize yourself with the methods provided. Note that it includes amainmethod that can be used to test your implementation and to display the code generated bygeneratedCode. - In
Program1GeneratedCode1.java, complete the body of thegenerateCodeForStatementstatic method by pasting the code you wrote for the homework and coding the case that takes care of generating code for aCALLstatement. Write the body of thegeneratedCodeinstance method specified in thePrograminterface. Here are a few things to keep in mind:- The contracts for
generatedCodeandgenerateCodeForStatementare informal and vague. The details of the code to generate for each kind of statement are provided in the Code Generation slides (39-47, in particular). The code generated by your implementation must match the code-generation patterns described in the slides. To generate code for a givenProgram(this),generatedCodeneeds to generate code for the body of the program and append aHALTinstruction at the end of that. generatedCodemust restore the value ofthis.generateCodeForStatementhas to be recursive, whilegeneratedCodewill not use recursion.- The code generator is responsible for catching the two possible
remaining syntax errors, which are not checked by the parser,
namely, undefined instructions and direct and/or indirect use of
recursion. To check for these errors use the
Reporter.assertElseFatalError(boolean, String)utility method you are using in the parser project. - When making a (recursive) call to
generateCodeForStatement, be careful to follow best practice with aliases. In particular, if the reference for aStatementblockis an alias provided by aMapcontext, perhaps by use of a call tocontext'svaluemethod, then it would be wrong to pass bothblockandcontextto the same method. You can avoid doing so by obtainingblockusingcontext'sremovemethod instead. Of course,context's value will need to be restored by later adding this name-statement pair back to it. You should then ponder the happy accident that being thus careful about aliases has made it easier for you to catch the syntax errors discussed in the immediately preceding item.
- The contracts for
- You can test your code generator implementation in a couple
different ways. First use JUnit. Open the file
Program1GeneratedCode1Test.javain thetestfolder and review the test cases provided to see how they are set up and how they attempt to test your implementation of the code generator. Run the JUnit test fixture and carefully review any test cases that fail. If that is not enough information to help you find the bug(s), you can also run the main program inProgram1GeneratedCode1and display the code generated by your implementation (but only for inputs that do not have any errors, including those that the code generator is supposed to catch). You can use theTestProgram1.blfile in thedatafolder in your project as test input. The main program generates two output files (both in thedatafolder):expected-output.txtthat contains the correct output for the given input andactual-output.txtthat is the output generated by your implementation. Use Eclipse's file compare feature you learned about in the previous labs to compare the two outputs. If your code does not work, you should create smaller test inputs that try to expose the bug(s) and make it easier to identify them. Fix all the bugs and make sure your code generator passes all the test cases to complete this lab.
Additional Activities
- Although it is a requirement of this lab (and a similar requirement will be in effect on the final exam) to generate code according to the patterns discussed in class, there are some optimizations that could, in principle, be applied to make the generated code shorter and more efficient. Discuss any such optimizations with your partner. Would you implement these optimizations as part of the code generator or as a separate phase following the code generation? What are the trade-offs between the two approaches?