Sample Exam Answers

Question 2.

Part (a).

I would add a new, Boolean field to the symbol-table entry for a function name called isForward. The value would be true if the only declaration seen so far was a forward declaration, and false if a normal declaration for the function had been seen.

Part (b).

The name-analysis method for a forward declaration would work as follows:
  1. Do a local lookup of the name. (Or a global lookup; they are the same in this case, since function declarations only occur at the outermost level.)
  2. If the lookup returns null, create a new Sym object for a function containing the return type and the list of parameter types, and with the new isForward field set to true. Add the function name and the new Sym to the symbol table.
  3. If the lookup does not return null (i.e., returns a Sym), then if that Sym is for a variable (not a function) then give error #1. If that Sym is for a function, then give error #2.
The name-analysis method for a normal function declaration would work as follows:
  1. Do a local (or global) lookup of the name.
  2. If the lookup returns null, create a new Sym object for a function containing the return type and the list of parameter types, and with the new isForward field set to false. Add the function name and the new Sym to the symbol table.
  3. If the lookup does not return null (i.e., returns a Sym), then if that Sym is for a variable (not a function) then give error #1. Otherwise (that Sym is for a function) if that Sym's isForward field is false, give error #3.
  4. If the lookup returns a Sym with isForward == true, then if the return type and/or parameter list of this function don't match those in the Sym, give error #5; otherwise, set the isForward field to false.
Error message #4 would be handled as follows. When name analysis has finished with the whole program, it would look in its (single) remaining hashtable to see if there is any symbol-table entry for a function with the isForward field == true. For each such function it would give error message #4. If we wanted to include the line and character numbers of the bad forward function declaration(s) in those error message, we would include two more new fields in a function's symbol-table entry to hold those line and character numbers. They would be set when name-analysis processed a forward function declaration. We could also keep a separate list of the Syms for forward declarations, adding them to the list (in addition to adding them to the symbol table) when a non-erroneous forward declaration is processed, and removing them fron the list when the corresponding normal declaration is processed. In this case, once name analysis is finished with the whole program, we would give error message #4 for each forward declaration that remains in this separate list.

Question 3.

Part (a).

Task 1 could be done by the called function: NO

Task 2 could be done by the called function: NO

Task 3 could be done by the calling function: YES

Task 4 could be done by the calling function: YES

Task 5 could be done by the calling function: YES

Task 6 could be done by the calling function: YES

Task 7 could be done by the calling function: YES

Part (b).

There is no difference in execution time.

In terms of the size of the generated code: If a function is never called, then it is better to have as many tasks as possible done by the called function (or, even better, to avoid generating code for that function at all). If a function is called exactly once, then it doesn't matter whether the tasks are done by the calling or called function.

However, for a function that is called more than once, the size of the generated code is smaller if as many tasks as possible are done by the called function. This is because there will be just one copy of the code for those tasks. If the tasks are done by the calling function, then there will be one copy of the code for each call.

Question 4.

The important idea for this question is that the codeGen method for the break statement needs to have access to the "false" label of its closest enclosing while loop. This can be done by passing that label as a parameter (to all statement's codeGen methods), or by keeping that label in a new, static field. In the latter case, the label has to be saved and restored by the whole loop's codeGen method so that correct code is generated for a break statement that comes after a nested while loop.

We can also keep a stack of labels, but that is not necessary and is a bit less efficient than keeping a single static field.

Here is the code that uses a new, static String field, with the new code in bold italic font:

Question 5.

x a b output
value value value a: 10 k: 10 x: 7 k: 6 k: 6
reference value reference a: 8 k: 8 x: 4 k: 4 k: 4
value-result value value a: 10 k: 10 x: 7 k: 6 k: 7
value name value a: 10 k: 11 x: 7 k: 7 k: 7