Compiler construction
Abstract
A compiler is just a translator from a language understandable by humans (in our case Minijava+) and a language understandable only by computers (java bytecode). The compiler we realized analyzes and parses a .java file we give in argument. It looks if it is a valid file (according to the language grammar) and when it read the entire file and decided it is valid, starts to generate the bytecode. Our compiler also emits warnings when the user did something odd (like initializing variables without using them later). Finally we also perform a data-flow analysis to prove certain assertions. Here is a little example of how a program is compiled:
1. Introduction
Writing a compiler is helpful as it shows us how the computer works and thinks. After writing this we understand way better how to optimize some code and how it will be understood by the computer. We will explain step by step how we realized this compiler, from the lexer until the code generation and then control flow graphs. This compiler is useful because it is dedicated to a special language: Minijava+ with which we can still write nice programs.
This program would first be transformed into a set of tokens:
2. Example
CLASS Rationnal LACCO PUBLIC
STATIC VOID MAIN … denom RPAREN SEMICOLON RACCO RETURN 0 SEMICOLON RACCO RACCO EOF
It is done in the following way: we read the file character by character, with reserved keywords like class, static … After each character we allow and recognize, we create the corresponding Token and add it to the list. When we recognize some patterns like comments we don’t add them to the Tokens.
With this, the compiler knows the meaning of each word from the original file (.java). Then we parse those tokens and create the appropriate class/methods. It is in this part that we find the typo errors. After this we have a complete AST of our program. Now we analyze this AST, and find errors like call