Skip to content

Latest commit

 

History

History
200 lines (135 loc) · 11.3 KB

day10.md

File metadata and controls

200 lines (135 loc) · 11.3 KB

Day 10: Simple Code Generation

In this lab, you develop a simple code generator, that generates Java bytecode from simple MiniJava programs, which only print integer constants in their main methods.

Overview

Objectives

  1. Write a Jasmin program which prints 42.
  2. Implement a code generator that transforms MiniJava programs into Java bytecode. The code generator should include
    1. A transformation from MiniJava ASTs to Jasmin ASTs which handles
      • MiniJava programs
      • with only a main class
      • with a single print statement with
      • an integer constant expression.
    2. A menu action which invokes the transformation and pretty-prints the Jasmin AST to concrete syntax.
    3. A menu action which generates a Java class file instead of a Jasmin file.
    4. A menu action which runs the program (challenge).

Submission

You need to submit your MiniJava project with a pull request against branch assignment10 on GitHub. Your GitHub repository contains a step-by-step procedure how to file such a request. This project should contain a README.md with a short paragraph explaining the organisation of your Stratego files.

The deadline for submissions is December 10th, 17:59.

Grading

You can earn up to 10 points for your Jasmin program and up to 75 points for your code generator:

  • transformation (50 points)
    • program (2 points)
    • main class (18 points)
    • main method (13 points)
    • print statement (17 points)
  • menu actions (10 points)
    • Jasmin (5 points)
    • Java class file (5 points)
  • challenges (15 points)
    • target directories (4 points)
    • automatic code generation (5 points)
    • runner (6 points)

You can earn up to 5 points for the organisation of your Stratego files and up to 10 points for the quality of your code. We focus on readibility in general, meaningful variable names and the consistent use of Stratego paragdims. We will consider the fact that Stratego is new to you.

Detailed Instructions

Initial Editor Project

We provide you with an initial MiniJava project in the branch assignment10. Make sure you have this branch in your fork as well, before you start working on this assignment.

  1. Import the project into your workspace:
    1. right-click into the Package Explorer
    2. select Import... from the context menu
    3. choose General/Existing Projects into Workspace from the list
    4. select the MiniJava project
    5. press the Finish button
  2. Build the project:
    1. select the project folder
    2. select Build Project from the Project menu
    3. the console will report success or failure

This project contains the following implementations:

  • MiniJava signatures in common/src-gen/signatures/MiniJava-sig.
  • MiniJava pretty-printer in common/src-gen/pp/MiniJava-pp
  • MiniJava syntactic completions in common/src-gen/completions/MiniJava-esv.
  • MiniJava desugarings in common/desugar.
  • MiniJava name analysis in common/names.
  • MiniJava type analysis in common/types.
  • JasminXT signatures in common/src-gen/signatures/JasminXT*-sig
  • JasminXT pretty-printer in common/src-gen/pp/JasminXT*-pp

These implementations are already imported into the initial project.

Update: There is an inconsistency in the pretty-printing rules. You can fix this, by deleting common/src-gen/pp/JasminXT-Extra-PP-Rules.pp.str.

Write Jasmin Code

Consider the following simple MiniJava program:

class Main {
    public static void main(String[] args) {
        System.out.println(42);
    }
}

Write a Jasmin program simple.j, which you expect to be the result of a MiniJava-to-Jasmin compiler. Generate a Java class file from it and run it. Improve your program until it runs without errors.

Integrate Code Generation into the Editor

Code generation should be a service of your MiniJava editor. To achieve this, define a new strategy generate-jbc, which will be associated with the Generate Java bytecode entry in the Generate menu. Implement this strategy in Stratego by following the interface for action strategies:

generate-jbc: (selected, position, ast, path, project-path) -> (filename, result)

The implementation should rely on a strategy program-to-jbc, which transforms MiniJava programs into Java bytecode.

Implement a Code Generation Strategy

Next, you need to implement program-to-jbc. You will do this stepwise over the remaining labs. During this lab, you should implement it for programs that contain only a main class, which prints a single integer constant. To understand Jasmin's abstract syntax, you can either study example ASTs generated by a Jasmin editor, study the grammar in the Jasmin project, or have a closer look into common/src-gen/signatures/JasminXT-sig.str.

  1. Provide a rule for exp-to-jbc, which translates an integer constant from MiniJava into a sequence of Java bytecode instructions, that loads this constant to the operand stack. Note that it is important to generate a sequence here, even if you only need a single instruction, because in general a MiniJava expression translates into a sequence of bytecode instructions.

  2. Provide a rule for stmt-to-jbc, which translates a print statement from MiniJava into a sequence of Java bytecode instructions. This rule should call exp-to-jbc to translate the expression inside the print statement to a Java bytecode sequence.

  3. Provide a rule for class-to-jbc, which translates a main class from MiniJava into a Jasmin class file. This rule should call stmt-to-jbc to translate the statement inside the main method to a Java bytecode sequence.

  4. Provide a rule for program-to-jbc, which translates a MiniJava program into a list of Jasmin class files. This rule should call class-to-jbc to translate the main class of the program into a Jasmin class file.

Testing

For testing purposes, you can define another menu entry which calls a strategy to-jbc, which dispatches to your different implementation rules:

to-jbc = program-to-jbc + class-to-jbc + stmt-to-jbc + exp-to-jbc

This allows you to test your implementation by selecting a code fragment in the MiniJava editor and running your code generation builder.

Some Notes on Sequences

You can represent sequences as lists. Sequences of bytecode instructions are never nested. Thus, your generated sequences should also be flat lists. This requires you to compose sequences from recursive calls with surrounding instructions. In general, there are four different approaches to this in Stratego:

  1. Generate nested lists and flatten these lists afterwards by applying flatten-list (not recommended).

  2. Compose lists with a head and tails notation. For example, seq might be a sequence generated by a recursive call. You can precede this sequence with instructions instr1, ..., instrn by writing [ instr1, ..., instrn | seq ].

  3. Compose lists explicitly.

    • <conc> (l1, l2) concatenates two lists l1and l2.
    • <concat> [l1, ..., ln] concatenates lists l1 ... ln.
  4. Use special list variables (recommended). Stratego provides special variable names for sequences. These names end in *, for example instr*. When using such variables in a list, Stratego will inline the list elements at that position instead of creating a nested list. For example,

     instr1* := [LDC(Int("1")), LDC(Int("2"))]; instr2* := [LDC(Int("0")), instr1*, LDC(Int("3"))]
    

    is equivalent to

     instr1* := [LDC(Int("1")), LDC(Int("2"))]; instr2* := [LDC(Int("0")), LDC(Int("1")), LDC(Int("2")), LDC(Int("3"))]
    

Generate Concrete Syntax

As you may have noticed, your implementation follows the code generation by transformation paradigm. The result of to-jbc is a Jasmin AST. Extend your builder strategy generate-jbc to generate concrete syntax. Therefor, you need to pretty-print the Jasmin ASTs, which are generated by program-to-jbc. You can find a pretty-printing strategy for Jasmin in trans/jasmin.str.

Generate Java Class Files

At this point, you can generate Jasmin class files from simple MiniJava programs. To run your programs in the Java Virtual Machine, you need to generate Java class files. Define a new builder Generate Java class files with a corresponding builder strategy generate-jc, that generates the Jasmin file and turns it into a Java class file. To generate the class file, you first need to generate the Jasmin AST.

To translate a Jasmin AST into a Java class file, you can use the strategy jasmin-generate defined in trans/jasmin.str. This strategy will only work, if you specify the path of the source file in a Jasmin directive. The source directive is used by the JVM for debugging purposes and error messages. The Jasmin builder uses it to figure out where to store the class file. Thus, make sure your Jasmin AST has the form JBCFile(JBCHeader(_, JBCSource(<double-quote> path), _, _, _, _, _, _, _, _), _, _). You can pass path as an strategy argument from generate-jbc to program-to-jbc.

Notes on names in Jasmin

Even though in MiniJava some keywords are reserved, Jasmin has its own keywords. Some Jasmin implementations consider those as reserved, some do not. Typical examples are keywords such as field or class. In the Spoofax implementation, we do not reserve these, but other Jasmin implementations might do, particularly if they may not have the latest updates (Debian for example). See StackOverflow for more information about this.

Challenges

Challenges are meant to distinguish excellent solutions from good solutions. Typically, they are less guided and require more investigation and programming skills.

Target Directories

Real-world compilers like the JDT place generated code in designated directories, such as bin/ or src-gen/. Implement a similar behaviour for your MiniJava compiler. Make sure you can handle classes with the same name from different programs and files with the same name from different directories.

The following strategies deal with directories:

  • <file-exists ; filemode ; isdir> path succeeds if path is a path to an existing directory in the file system.
  • <mkdir> path creates a new directory.

Automatic Code Generation

Real-world compilers like the JDT generate code silently in the background. Implement a similar behaviour, using Spoofax's on-save builder. In your main Stratego file, change editor-save to run your compiler: editor-save = where(analysis-save-default(|<language>)); generate-java-class-files. editor-save has the same input interface as a builder, but the output should be None(). This means that you cannot return a (filename, result) tuple like a normal builder, but need to write to a file yourself.

Implement a strategy write-file that rewrites a pair of file name and file content to itself. As a side effect, this strategy should create (if necessary) and write a file. These strategies might be useful:

  • <fopen> (filename, "w") opens a file for writing and gives you a file descriptor of the open file.
  • <fputs> (content, filedescriptor) writes to a file.
  • <fclose> filedescriptor closes a file.
  • <refresh-workspace-file> filename refreshes a file in the Eclipse workspace.

Run a MiniJava Program

Add a builder that runs a MiniJava program by running the main method from the generated class file.