Code Portfolio
Ari Wilson
Compilers Code

This project was created as a class project for a course on compiler design and was created in a group of two. It implements most of a compiler for COOL (the Classroom Object-Oriented Language), the homepage of which is linked to from my website. We used the open-source bison and flex tools to generate most of a lexer and parser for the language and implemented language semantics and type checking in C++ in part 4 of the project. The flex definition of COOL is contained in cool.flex and the corresponding bison definition is in cool.y, while most other code is contained within semant.* and cool-tree.*. The file good.cl illustrates a COOL program that is accepted and processed by the compiler produced up to this point, and bad.cl shows a number of ways in which the compiler reacts appropriately to error conditions. 

The original readmes created for this project follow:

Write-up for PA2
----------------

For my lexer, I chose to encapsulate two different environments to handle potentially complicated lexing issues. One environment, comment, was designed specifically to handle errors and issues related to multi-line comments (as in encountering an EOF unexpectedly). The other, string, was designed to catch errors related to string constants defined in the program text (new line, null, or EOF in character stream along with string too long) as well as converting some escape characters to their single-character equivalent. The rest of the lexer is fairly standard and follows the specifications of the COOL language (as laid out in the CoolAid), with the error-checking mechanism simply matching any number of illegal printable characters (found on a standard keyboard). It lexes my PA1 correctly and with the rest of mycoolc, allows for it to run in spim.

Write-up for PA3
by Ari Wilson and Lizzie Linn
-----------------------------
 
For our parser, we chose to follow fairly strictly the COOL context-free grammar outlined on page 15 of the COOLaid with the precedence rules listed on page 14. The output of our parser is, if the program parses correctly, an abstract syntax tree of the program that can be piped into later stages of the coolc compiler to produce a MIPS assembly file. If the program does not parse correctly, some error productions are in place to recover and parse further and print further error messages. Some additional constructs were created to deal with ambiguity, especially for the plural constructs such as expression list as well as for let statements to aid with precedence. Precedence rules were created for standard arithmetic operators along with some other operators in the COOL language. Additional error rules were inserted to deal with the standard cases listed in the program assignment along with recovering from error in formal argument lists and case statements. We have included two files that test the functionality of our parser - good.cl and bad.cl. good.cl contains a list of correctly-defined constructs that test the various rules of the COOL grammar and demonstrate the correct formation of the abstract syntax tree by our parser. bad.cl contains many errors that can be handled by our parser and skipped over until at the end of the file parsing must be halted. The errors our parser attempts to catch are listed in that file.

Write-up for PA4
by Ari Wilson and Lizzie Linn
-----------------------------

For the semantic analysis portion of our compiler, we incorporated a class inheritance graph generator and checker as well as an overall type checker, implemented using the rules on pages 16-20 of the COOLaid. Specifically, we implemented checks for imaginary and cyclic inheritance as well as implementing all type checking rules mentioned by p4_hints.txt along with let blocks (with one variable declaration only), dynamic dispatch, loops, conditionals, attributes and printing associated errors. Associated with these type checking routines are our implementations of conforms_to() and least_upper_bound() in class util which are both implemented using the class inheritance graph. The main loop of our semantic checker makes 2 major passes over the body of our program and one minor pass. One major pass consist of adding each member function and attribute to the appropriate MethodTable and AttrTable, as well as adding all inherited functions to same. This information is then used in the second pass, when, over every class and every method within each class, type checking is performed recursively, with the base cases being integer constants, string constants, bool constants and identifiers. The type checker requires 6 arguments: the current filename, the error stream, a global error reporting variable, a mapping between currently scoped identifiers and types, and a dispatch lookup table (to look up methods and types in other classes). The minor pass happens before the other two passes and constructs the class inheritance graph using parentptr and childrenptrs.

Our test cases (written in good.cl and bad.cl) test all major type checking features of the program as listed above. good.cl contains a COOL program which type checks correctly showing a type-annotated AST, while bad.cl presents a program that type checks incorrectly, demonstrating our error catching features. To check that proper error checking occurs on the inheritance graph, consider the following test file: 
class A inherits C {};
class B inherits A {};
class C inherits B {}; -- circular inheritance
class E inherits D {}; -- no such class D