Comp 271 Lab 6 - The Compiler


All of these can be done entirely in the SymbolTable class, by editing An optional goal is to implement a multi-level symbol table.

An IntelliJ project for the whole compiler is available at The compiler notes are at compiler.html; this includes discussion of individual files.

Symbol Table

To implement a two-level symbol table, use the built-in Dictionary class but create separate dictionaries for the global scope and for each separate local scope. When you reach the end of a local scope (endscope(), likely corresponding to the end of a function), the current local-scope Dictionary will be discarded. There may be additional global declarations before the next local scope is entered; at this point you will create a new local scope.

For the two-level scope model, we are at any point either in the global scope (scopecount == 0) or the local scope (scopecount > 0).

Suppose the two Dictionaries are named GlobalDict and LocalDict. If we are currently in a local scope, then allocation will always involve creating entries in LocalDict. GlobalDict will only receive new entries when we are in the global scope..

However, variable lookup while in the local scope will involve first looking up the identifier in LocalDict, and then, if it is not found there, looking in GlobalDict.

The changes here should functionally be equivalent to how the module symboltable1.cs works, but should be more efficient since the approach here avoids cloning the Dictionary. After you finish implementation of this step, the following should still compile:
Optional: implement a multi-level symbol table that supports arbitrary nesting. See compiler.html for further discussion.

Duplicate Declarations

The current compiler will give an error message indicating an identifier is undefined if the call to SymbolTable.lookup() returns null, so undeclared identifiers are detected. A declaration is a duplicate if there is a previous declaration of that identifier at the same level. This would occur if we had

int n;
int n;

You can detect this by noticing if, when allocVar()/allocConst()/allocProc() are called, there is already a declaration of the identifier in question at the same level. In the following code, the first n is at global scope and the second is at local scope; this is legal.

int n;
foo() {
    int n;

Functions are always declared at the global scope, but you still have to check if a function is redeclared (or if a global variable or constant is declared with the same name as the function).

If you detect a duplicate declaration, call t.error(string) with a suitable error-message string that includes the identifier in question. This will print the string and halt the compiler.

The file dupdecl.mjava should not compile after your changes.


To implement constants, all you have to do is make sure allocConst() works. The compiler syntax for defining constants is

final int two = 2;
final int three = 3;
final int costPerYear = 10000;

The file consts.mjava should then be able to run.

The only file you should have to modify is symboltable.cs (or, preferably, symboltable1.cs).