Symbol Table is a stack of scopes. Every time you enter a procedure you push another scope onto the stack. When you leave the procedure, you pop the scope from the stack. Within a scope, names are defined and stored in a symbol table. When a name is encountered in the program, it is looked up in the symbol table in order to obtain important information about this name (e.g. kind: procedure, or variable, or constant; type: int, pointer, etc.). For this assignment, just an integer value needs to be associated with a name, but in your program you will need to store more information about names.
A name defined in a local scope hides the same name defined in an outer scope. It is an error to define the same name twice in the same scope. Symbol lookup goes through all scopes on the stack and returns the first found definition of the name. If no definition is found, an invalid symbol is returned. Returned symbol is tested with Symbol::valid().
The Table::print() function, for each scope, starting with the innermost, prints 'Level' <space> <level number> and each name in the scope, one per line. Table::level() returns the level of current scope. Here's a sample session:
> driver i a i b i a insert failed l a a: 1 + i a l a a: 3 + i b l b b: 4 p Level 3 b Level 2 a Level 1 a b - s level = 2 x >