/* Simply speaking, the symbol table is the compile-type analog of the runtime stack. Runtime (dynamic) scope is not the same as compile-type (static) scope. For example, every function call creates a new dynamic scope. In particular, recursive calls create new scopes (new stack frames). However, for each function there is only one static scope. In some languages, it's possible to define functions inside functions, and functions can contain sub-scopes. For example, in full Java, a for loop for(int i...) defines a new sub-scope with a new variable i. In Minijava+ there's no sub-scope, and there aren't even any for loops, and thus its symbol table is relatively simple, and has a fixed number of levels: global / | \ / | \ class class class / \ vars methods ... / \ args locals ... Thus the static symbol table is a kind of tree. There are several types of table entries, for classes, methods and vars. Furthermore a var entry can indicate an instance var, a formal arg, or a local var. Each entry also has a parent. The global symbol table contains a hashtable of class entries. A method entry contains a table defining each variable local to the method, either an argument or a locally declared variable. A variable entry contains all relevant information for that variable, including its type (in Minijava types can just be strings) and position in a runtime stack frame. I use negative values for locally declared variables and positive numbers for formal parameters. This choice has to do with the standard x86 calling scheme adopted by gcc. The first few instructions of a compiled function are always: pushl %ebp // save previous value of ebp register movl %esp, %ebp // copy current top-of stack register to ebp subl $size, %esp // allocate new space above esp. These instructions mean that arguments passed to a function are beneath the value in the stack base register (ebp), and locally declared variables are above (less than) ebp. A variable entry also points to the method or class that it belongs to, and a method entry points to the class that it belongs to (parent pointer). Each symbol table allows for a symbol (as string) to be looked up. Each entry will invoke the lookup of its parent entry if a symbol is not found locally. A varentry always just invokes the lookup function of its parent. In the following I'm giving you my classes that represent the symbol table entries. For the next stage of the assignment you need to: 1. Write a visitor that constructs a symbol table for a program. 2. Use the symbol table to statically type-check the program (with another visitor, or two). If your parser allowed certain invalid syntax such as "2=3;" as a statement, then these visitors must also identify and report such errors. */ import java.util.*; interface stentry // overall type of all symbol table entries { stentry parent(); stentry lookup(String v); } class varentry implements stentry { String type; String name; int position; // in runtime stack frame + for args, - for locals boolean instance=false; // is it an instance var of a class boolean formal=false; // is it a formal paramerter stentry parent; // parent (static) scope. public varentry(String n, String t, stentry p) {name=n; type=t; parent=p;} public stentry parent() { return parent; } public stentry lookup(String v) {return parent.lookup(v); } } class mthentry implements stentry // method entry { String returntype; // locals contain both formal params and other local vars Hashtable locals = new Hashtable(); // formals points to same varentry's as in locals, in ordered form ArrayList formals = new ArrayList(); boolean ismain = false; clsentry parent; public mthentry(String r, clsentry p) {returntype=r; parent=p;} public clsentry parent() { return parent; } // using covariance public stentry lookup(String v) { stentry ax = locals.get(v); if (ax==null) ax = parent.lookup(v); return ax; }//lookup } class clsentry implements stentry { Hashtable methods = new Hashtable(); Hashtable instvars = new Hashtable(); boolean ismainclass = false; globalentry parent; public clsentry(boolean b,globalentry p) {ismainclass=b; parent=p;} public globalentry parent() {return parent; } public stentry lookup(String v) { stentry ax = instvars.get(v); if (ax==null) ax = methods.get(v); if (ax==null) ax = parent.lookup(v); return ax; } } class globalentry implements stentry { Hashtable classes = new Hashtable(); String mcn; // main class name public stentry lookup(String v) { return classes.get(v); } public stentry parent() { return null; } }