Question 1:
class animal { // OK declaration of animal as a class // methods void attack(int animal) { OK declaration of attack as a method and animal as a param/local for (int animal=0; animal<10; animal++) { // ERROR: animal already declared as a param/local int attack; // OK declaration of attack as a param/local } } int attack(int x) { // ERROR: attack already declared as a method with same param OK declaration of x as a param/local for (int attack=0; attack<10; attack++) { // OK declaration of attack as a param/local int animal; // OK declaration of animal as a param/local } } void animal() { } // OK declaration of animal as a method // fields double attack; // OK declaration of attack as a field int attack; // ERROR: attack already declared as a field int animal; // OK declaration of animal as a field }
Question 2: In the following, each declaration is numbered, and each use is annotated with the number of the corresponding declaration (or is noted as an error).
int (1)k=10, (2)x=20; void (3)foo(int (4)k) { int (5)a = x(2); int (6)x = k(4); int (7)b = x(6); while (...) { int (8)x; if (x(8) == k(4)) { int (9)k, (10)y; k(9) = y(10) = x(8); } if (x(8) == k(4)) { int (11)x = y(ERROR); } } }
Question 1:
When we exit a nested scope (e.g., the body of an if or a loop), the declarations made in that scope are no longer valid (any future use of one of the names declared there is a use of an undeclared variable). Keep a separate symbol table for each scope means that we can still just add a new table on scope entry and remove the first symbol table in the list on scope exit. If we kept all declarations in a single symbol table, then on scope exit we'd have to look through the whole table to find and remove the symbols that were declared in the scope being exited.
Question 2:
Under the scoping rules we've been assuming:
+--------------+ +-------------------------------+ | x: int, 2 |--->| g: (int, int) -> void, 1 | | y: int, 2 | | f: (int, int, int) -> void, 1 | | z: int, 2 | +-------------------------------+ | a: int, 2 | | b: int, 2 | +--------------+
Under actual C++ scoping rules (where parameters are in a different scope than local variables):
+-----------+ +--------------+ +-------------------------------+ | a: int, 3 |--->| x: int, 2 |--->| g: (int, int) -> void, 1 | | b: int, 3 | | y: int, 2 | | f: (int, int, int) -> void, 1 | | x: int, 3 | | z: int, 2 | +-------------------------------+ +-----------+ +--------------+
Question 3:
After processing the header for function g:
+-----------------------------------------+ | +--------------------+ | | g: | int,int -> void, 1 | | | +--------------------+ | +-----------------------------------------+
After processing the top-level local declaration in g:
+-----------------------------------------+ | +--------------------+ | | g: | int,int -> void, 1 | | | +--------------------+ | | | | +-----------+ | | d: | double, 2 | | | +-----------+ | +-----------------------------------------+
After processing the declarations in g's first while loop:
+-----------------------------------------+ | +--------------------+ | | g: | int,int -> void, 1 | | | +--------------------+ | | | | +--------+ +-----------+ | | d: | int, 3 |--->| double, 2 | | | +--------+ +-----------+ | | | | +--------+ | | w: | int, 3 | | | +--------+ | | | | +-----------+ | | x: | double, 3 | | | +-----------+ | | | | +-----------+ | | b: | double, 3 | | | +-----------+ | | | +-----------------------------------------+
After processing the declarations in g's if statement:
+-----------------------------------------+ | +--------------------+ | | g: | int,int -> void, 1 | | | +--------------------+ | | | | +--------+ +-----------+ | | d: | int, 3 |--->| double, 2 | | | +--------+ +-----------+ | | | | +--------+ | | w: | int, 3 | | | +--------+ | | | | +-----------+ | | x: | double, 3 | | | +-----------+ | | | | +--------+ +-----------+ | | b: | int, 4 |--->| double, 3 | | | +--------+ +-----------+ | | | | +--------+ | | a: | int, 4 | | | +--------+ | | | | +--------+ | | c: | int, 4 | | | +--------+ | +-----------------------------------------+
After exiting the scope of g's if statement, we're back to the same table as the one shown above for "After processing the declarations in g's first while loop", and after exiting the scope of g's first while loop we're back to the same table as the one shown above for "After processing the top-level local declaration in g".
After processing the declarations in g's second while loop:
+-----------------------------------------+ | +--------------------+ | | g: | int,int -> void, 1 | | | +--------------------+ | | | | +-----------+ | | d: | double, 2 | | | +-----------+ | | | | +--------+ | | x: | int, 3 | | | +--------+ | | | | +--------+ | | y: | int, 3 | | | +--------+ | | | | +--------+ | | z: | int, 3 | | | +--------+ | +-----------------------------------------+