Old Exam 2 Questions
Problem 1: TeenyJ expr1 Parsing [5 pts]
Section titled “Problem 1: TeenyJ expr1 Parsing [5 pts]”The language TeenyJ is defined like TinyJ except that the syntax of <expr1> is given by:
‹expr1› ::= UNSIGNEDINT | new int '[' ‹expr3› ']' { '[' ']' }
Suppose the Parser class you completed for TinyJ Assignment 1 is to be modified so that it will parse TeenyJ programs (instead of TinyJ programs). Show how you would complete the following parsing method for <expr1>. (No code generation is expected.)
private static void expr1() throws SourceFileErrorException { TJ.output.printSymbol(NTexpr1); TJ.output.incTreeDepth();
// ... [fill in the gap]
TJ.output.decTreeDepth();}First Solution
private static void expr1() throws SourceFileErrorException { TJ.output.printSymbol(NTexpr1); TJ.output.incTreeDepth(); switch (getCurrentToken()) { case UNSIGNEDINT: nextToken(); break; case NEW: nextToken(); accept(INT); accept(LBRACKET); expr3(); accept(RBRACKET); while (getCurrentToken() == LBRACKET) { nextToken(); accept(RBRACKET); } break; default: throw new SourceFileErrorException("Expected UNSIGNEDINT or new"); } TJ.output.decTreeDepth();}Second Solution
private static void expr1() throws SourceFileErrorException { TJ.output.printSymbol(NTexpr1); TJ.output.incTreeDepth(); if (getCurrentToken() == UNSIGNEDINT) { nextToken(); } else if (getCurrentToken() == NEW) { nextToken(); accept(INT); accept(LBRACKET); expr3(); accept(RBRACKET); while (getCurrentToken() == LBRACKET) { nextToken(); accept(RBRACKET); } } else throw new SourceFileErrorException("Expected UNSIGNEDINT or new"); TJ.output.decTreeDepth();}Third Solution
private static void expr1() throws SourceFileErrorException { TJ.output.printSymbol(NTexpr1); TJ.output.incTreeDepth(); if (getCurrentToken() == UNSIGNEDINT) { nextToken(); } else { accept(NEW); accept(INT); accept(LBRACKET); expr3(); accept(RBRACKET); while (getCurrentToken() == LBRACKET) { nextToken(); accept(RBRACKET); } } TJ.output.decTreeDepth();}Comment on Solutions
The third solution is slightly more concise than the first two solutions, but it gives a slightly less informative error message if expr1 is called when currentToken is neither UNSIGNEDINT nor NEW.
Problem 2: Translating a TinyJ Program [10 pts]
Section titled “Problem 2: Translating a TinyJ Program [10 pts]”Complete the table below the following program to show the TinyJ virtual machine instructions that should be generated by TJasn.TJ (after completion of TinyJ Assignment 2) for this TinyJ program.
Source Code
Section titled “Source Code”class ExamQ { static int b[] = new int[315]; public static void main (String args[]) { int i = g(19); b[271] = i; System.out.print("g(19) is "); System.out.println(b[271]); } static int g(int m) { if (m < 3) return 0; else return m-g(m-1); }}Instruction Template
Section titled “Instruction Template”0: PUSHSTATADDR 01: PUSHNUM 3152: ___________________3: SAVETOADDR4: ___________________5: ___________________6: ___________________7: ___________________8: ___________________9: ___________________10: ___________________11: ___________________12: ___________________13: ___________________14: ___________________15: ___________________16: ___________________17: ___________________18: ___________________19: ___________________20: ___________________21: ___________________22: ___________________23: ___________________24: ___________________25: ___________________26: ___________________27: ___________________28: ___________________29: ___________________30: ___________________31: ___________________32: ___________________33: ___________________34: JUMP 4535: ___________________36: ___________________37: ___________________38: ___________________39: ___________________40: ___________________41: ___________________42: ___________________43: ___________________44: RETURN 145: (method g starts here)Hint
Among the 40 instructions you are asked to write, there are:
- 7 LOADFROMADDR instructions
- 6 PUSHNUM instructions
- 5 PUSHLOCADDR instructions
- 2 each of: ADDTOPTR, CALLSTATMETHOD, INITSTKFRM, PASSPARAM, PUSHSTATADDR, SAVETOADDR, SUB
- 1 each of: HEAPALLOC, JUMPONFALSE, LT, RETURN, STOP, WRITEINT, WRITELNOP, WRITESTRING
Solution
0: PUSHSTATADDR 01: PUSHNUM 3152: HEAPALLOC3: SAVETOADDR4: INITSTKFRM 15: PUSHLOCADDR 16: PUSHNUM 197: PASSPARAM8: CALLSTATMETHOD 269: SAVETOADDR10: PUSHSTATADDR 011: LOADFROMADDR12: PUSHNUM 27113: ADDTOPTR14: PUSHLOCADDR 115: LOADFROMADDR16: SAVETOADDR17: WRITESTRING 1 918: PUSHSTATADDR 019: LOADFROMADDR20: PUSHNUM 27121: ADDTOPTR22: LOADFROMADDR23: WRITEINT24: WRITELNOP25: STOP26: INITSTKFRM 027: PUSHLOCADDR -228: LOADFROMADDR29: PUSHNUM 330: LT31: JUMPONFALSE 3532: PUSHNUM 033: RETURN 134: JUMP 4535: PUSHLOCADDR -236: LOADFROMADDR37: PUSHLOCADDR -238: LOADFROMADDR39: PUSHNUM 140: SUB41: PASSPARAM42: CALLSTATMETHOD 2643: SUB44: RETURN 1Comments on Problem 2
Regarding the Translation of the Statements b[271] = i; and System.out.println(b[271]);
Note: The EXPRSTACK column on the right shows the items on the expression evaluation stack immediately after each VM instruction has been executed. The stack grows downwards––when more than one item is on the stack the first line below the word EXPRSTACK refers to the bottom item on the stack.
Translation of b[271] = i;
The following seven VM instructions are shown on the left below. These instructions are put into code memory at addresses 10 – 16.
| Instruction | Effect | EXPRSTACK |
|---|---|---|
| PUSHSTATADDR 0 | Pushes pointer to b. | ptr to b |
| LOADFROMADDR | Pops pointer to b. Pushes the pointer to b[0] that is stored in b’s location. | ptr to b[0] |
| PUSHNUM 271 | Pushes the integer 271. | ptr to b[0] 271 |
| ADDTOPTR | Pops 271 and pointer to b[0]. Pushes (pointer to b[0]) + 271 (i.e., pointer to b[271]). | ptr to b[271] |
| PUSHLOCADDR 1 | Pushes pointer to i. | ptr to b[271] ptr to i |
| LOADFROMADDR | Pops pointer to i. Pushes the value stored in i’s location (i.e., the value of i). | ptr to b[271] value of i |
| SAVETOADDR | Pops value of i and pointer to b[271]. Saves value of i into the location of b[271]. | (empty) |
Translation of System.out.println(b[271]);
The following seven VM instructions are shown on the left below. These instructions are put into code memory at addresses 18 – 24.
| Instruction | Effect | EXPRSTACK |
|---|---|---|
| PUSHSTATADDR 0 | Pushes pointer to b. | ptr to b |
| LOADFROMADDR | Pops pointer to b. Pushes the pointer to b[0] that is stored in b’s location. | ptr to b[0] |
| PUSHNUM 271 | Pushes the integer 271 | ptr to b[0] 271 |
| ADDTOPTR | Pops 271 and pointer to b[0]. Pushes (pointer to b[0]) + 271 (i.e., pointer to b[271]). | ptr to b[271] |
| LOADFROMADDR | Pops pointer to b[271]. Pushes the value stored in b[271]‘s location (i.e., the value of b[271]). | value of b[271] |
| WRITEINT | Pops value of b[271]. Writes the value on the screen. | (empty) |
| WRITELNOP | Writes a newline to the screen. | (empty) |
Further Problems to Test Understanding
Section titled “Further Problems to Test Understanding”Problem 3
Section titled “Problem 3”Suppose we delete the line static int b[] = new int[315]; from the TinyJ program of problem 2 but insert a line int b[] = new int[536]; at the beginning of the body of main. (Thus b would become the first local variable of main, and i would become the second local variable of main rather than the first local variable.) How would the 14 instructions shown on the previous page change?
Answer
- Change
PUSHLOCADDR 1toPUSHLOCADDR 2 - Change each occurrence of
PUSHSTATADDR 0toPUSHLOCADDR 1
Problem 4
Section titled “Problem 4”Suppose that the first variable declaration in a certain TinyJ program is static int b[][][]; Suppose also that this variable b is used in the following statement later in the program:
System.out.print(b[7][29][5]);What TinyJ VM instructions would the TinyJ compiler translate the latter statement into?
Note: Although Exam 2 may have questions relating to arrays, Exam 2 will not have any question such as this one that involves an indexed variable with more than one actual index. However, there may be a question on the Final Exam that involves an indexed variable with more than one index.
Answer and Explanation
| Instruction | Effect | EXPRSTACK |
|---|---|---|
| PUSHSTATADDR 0 | Pushes pointer to b. | ptr to b |
| LOADFROMADDR | Pops pointer to b. Pushes the pointer to b[0] that is stored in b’s location. | ptr to b[0] |
| PUSHNUM 7 | Pushes the integer 7. | ptr to b[0] 7 |
| ADDTOPTR | Pops 7 and pointer to b[0]. Pushes (pointer to b[0]) + 7 (i.e., pointer to b[7]). | ptr to b[7] |
| LOADFROMADDR | Pops pointer to b[7]. Pushes the pointer to b[7][0] that is stored in b[7]‘s location. | ptr to b[7][0] |
| PUSHNUM 29 | Pushes the integer 29. | ptr to b[7][0] 29 |
| ADDTOPTR | Pops 29 and pointer to b[7][0]. Pushes (pointer to b[7][0]) + 29 (i.e., pointer to b[7][29]). | ptr to b[7][29] |
| LOADFROMADDR | Pops pointer to b[7][29]. Pushes the pointer to b[7][29][0] that is stored in b[7][29]‘s location. | ptr to b[7][29][0] |
| PUSHNUM 5 | Pushes the integer 5. | ptr to b[7][29][0] 5 |
| ADDTOPTR | Pops 5 and pointer to b[7][29][0]. Pushes (pointer to b[7][29][0]) + 5 (i.e., pointer to b[7][29][5]). | ptr to b[7][29][5] |
| LOADFROMADDR | Pops pointer to b[7][29][5]. Pushes value stored in b[7][29][5]‘s location (i.e., value of b[7][29][5]). | value of b[7][29][5] |
| WRITEINT | Pops value of b[7][29][5]. Writes this value to the screen. | (empty) |
Some Properties of the Code Generated by the TinyJ Compiler
Section titled “Some Properties of the Code Generated by the TinyJ Compiler”When answering certain exam questions, it may be useful to remember that the code generated by the TinyJ compiler when it translates a TinyJ program has the following properties:
-
The code generated for any assignment statement consists of code whose execution will push a pointer to the target variable’s location, followed by code whose execution will push what we want to store, followed by a SAVETOADDR instruction. (Note that the code that pushes a pointer to the variable’s location may consist of very many instructions if the variable in question is an indexed variable.)
-
If the target variable of an assignment statement is not an indexed variable, then the assignment statement is translated into code that begins with a PUSHSTATADDR or PUSHLOCADDR instruction that is not immediately followed by a LOADFROMADDR instruction.
-
With the exceptions of the PUSHSTATADDR and PUSHLOCADDR instructions referred to in item 2, every PUSHSTATADDR or PUSHLOCADDR instruction is immediately followed by a LOADFROMADDR instruction.
-
The code generated to push the value of a variable onto EXPRSTACK consists of code whose execution will push a pointer to the variable’s location and an additional LOADFROMADDR instruction at the end. (As mentioned in item 1 above, the code that pushes a pointer to the variable’s location may consist of very many instructions if the variable in question is an indexed variable.)
-
The code generated for any method call includes a PASSPARAM instruction for each argument of the call, and includes a CALLSTATMETHOD instruction; if the call has arguments, then the CALLSTATMETHOD instruction is the next instruction after the last PASSPARAM.
Additional Resources
Section titled “Additional Resources”Much further information about the generated code is provided in the document Memory-allocation-VM-instruction-set-and-hints-for-asn-2 that has been shown in class and is available on Brightspace. Be sure to read the section Code Generation Rules Used by the TinyJ Compiler on the 9th page of that document and the sections relating to whileStmt() and ifStmt() on the 15th and 16th pages.
Note: You can also make up your own hand‐translation examples: If X.java is any valid TinyJ program, then the correct solution to the problem of translating X.java can be obtained by running the provided solution to TinyJ Assignment 2 with X.java as the input file.
Detailed Code Generation Examples
Section titled “Detailed Code Generation Examples”Example (a): Simple Method Translation
Section titled “Example (a): Simple Method Translation”Suppose Instruction.getNextCodeAddress() == 35 when a correct solution to TinyJ Assignment 2 begins to translate the following two methods. What code is generated?
static void m(){ int x = 12, y = 9; System.out.print(p(17, y, x+5));}static int p (int a, int b, int c){ int u = a - b; return c - u;}Solution
35: INITSTKFRM 236: PUSHLOCADDR 137: PUSHNUM 1238: SAVETOADDR39: PUSHLOCADDR 240: PUSHNUM 941: SAVETOADDR42: PUSHNUM 1743: PASSPARAM44: PUSHLOCADDR 245: LOADFROMADDR46: PASSPARAM47: PUSHLOCADDR 148: LOADFROMADDR49: PUSHNUM 550: ADD51: PASSPARAM52: CALLSTATMETHOD 5553: WRITEINT54: RETURN 055: INITSTKFRM 156: PUSHLOCADDR 157: PUSHLOCADDR -458: LOADFROMADDR59: PUSHLOCADDR -360: LOADFROMADDR61: SUB62: SAVETOADDR63: PUSHLOCADDR -264: LOADFROMADDR65: PUSHLOCADDR 166: LOADFROMADDR67: SUB68: RETURN 3Example (b): Arrays
Section titled “Example (b): Arrays”class ArrayTest { static int b[] = new int[10]; public static void main (String args[]) { int a = 1; b[3] = a; System.out.println(b[3]+a); b = new int[5]; int c[][] = new int [7][]; c[4] = b; }}Solution
0: PUSHSTATADDR 01: PUSHNUM 102: HEAPALLOC3: SAVETOADDR4: INITSTKFRM 25: PUSHLOCADDR 16: PUSHNUM 17: SAVETOADDR8: PUSHSTATADDR 09: LOADFROMADDR10: PUSHNUM 311: ADDTOPTR12: PUSHLOCADDR 113: LOADFROMADDR14: SAVETOADDR15: PUSHSTATADDR 016: LOADFROMADDR17: PUSHNUM 318: ADDTOPTR19: LOADFROMADDR20: PUSHLOCADDR 121: LOADFROMADDR22: ADD23: WRITEINT24: WRITELNOP25: PUSHSTATADDR 026: PUSHNUM 527: HEAPALLOC28: SAVETOADDR29: PUSHLOCADDR 230: PUSHNUM 731: HEAPALLOC32: SAVETOADDR33: PUSHLOCADDR 234: LOADFROMADDR35: PUSHNUM 436: ADDTOPTR37: PUSHSTATADDR 038: LOADFROMADDR39: SAVETOADDR40: STOPExample (c): While Loop
Section titled “Example (c): While Loop”class Fall02a { static int a[] = new int[10]; public static void main (String args[]) { int x = 100; while (x > 10) x = f(x, 2); } static int f(int m, int n) { a[3] = m / n; return a[3]; }}Solution
0: PUSHSTATADDR 01: PUSHNUM 102: HEAPALLOC3: SAVETOADDR4: INITSTKFRM 15: PUSHLOCADDR 16: PUSHNUM 1007: SAVETOADDR8: PUSHLOCADDR 19: LOADFROMADDR10: PUSHNUM 1011: GT12: JUMPONFALSE 2213: PUSHLOCADDR 114: PUSHLOCADDR 115: LOADFROMADDR16: PASSPARAM17: PUSHNUM 218: PASSPARAM19: CALLSTATMETHOD 2320: SAVETOADDR21: JUMP 822: STOP23: INITSTKFRM 024: PUSHSTATADDR 025: LOADFROMADDR26: PUSHNUM 327: ADDTOPTR28: PUSHLOCADDR -329: LOADFROMADDR30: PUSHLOCADDR -231: LOADFROMADDR32: DIV33: SAVETOADDR34: PUSHSTATADDR 035: LOADFROMADDR36: PUSHNUM 337: ADDTOPTR38: LOADFROMADDR39: RETURN 2Example (d): Another While Loop
Section titled “Example (d): Another While Loop”class Fall02b { static int a[] = new int [25]; public static void main (String args[]) { a[5] = 900; System.out.print(g(7)); } static int g(int m) { int i = a[5]; while (i > 30) i = i / m; return i; }}Solution
0: PUSHSTATADDR 01: PUSHNUM 252: HEAPALLOC3: SAVETOADDR4: INITSTKFRM 05: PUSHSTATADDR 06: LOADFROMADDR7: PUSHNUM 58: ADDTOPTR9: PUSHNUM 90010: SAVETOADDR11: PUSHNUM 712: PASSPARAM13: CALLSTATMETHOD 1614: WRITEINT15: STOP16: INITSTKFRM 117: PUSHLOCADDR 118: PUSHSTATADDR 019: LOADFROMADDR20: PUSHNUM 521: ADDTOPTR22: LOADFROMADDR23: SAVETOADDR24: PUSHLOCADDR 125: LOADFROMADDR26: PUSHNUM 3027: GT28: JUMPONFALSE 3729: PUSHLOCADDR 130: PUSHLOCADDR 131: LOADFROMADDR32: PUSHLOCADDR -233: LOADFROMADDR34: DIV35: SAVETOADDR36: JUMP 2437: PUSHLOCADDR 138: LOADFROMADDR39: RETURN 1Example (e): If and While
Section titled “Example (e): If and While”import java.util.Scanner;class Spring99 { static Scanner input = new Scanner(System.in); static int x; public static void main (String args[]) { int a; x = input.nextInt(); if (x > 1 & x < 20000) { while (x <= 20000) { int b = 2; x = x * 3; } int c = x; System.out.print(c); } }}Solution
Note that the local variables b and c both have a stackframe offset of 2. At the point where c is declared, b no longer exists—b’s scope is confined to the body of the while loop. Thus stackframe offset 2 can be reallocated to c.
0: INITSTKFRM 21: PUSHSTATADDR 02: READINT3: SAVETOADDR4: PUSHSTATADDR 05: LOADFROMADDR6: PUSHNUM 17: GT8: PUSHSTATADDR 09: LOADFROMADDR10: PUSHNUM 2000011: LT12: AND13: JUMPONFALSE 3614: PUSHSTATADDR 015: LOADFROMADDR16: PUSHNUM 2000017: LE18: JUMPONFALSE 2919: PUSHLOCADDR 220: PUSHNUM 221: SAVETOADDR22: PUSHSTATADDR 023: PUSHSTATADDR 024: LOADFROMADDR25: PUSHNUM 326: MUL27: SAVETOADDR28: JUMP 1429: PUSHLOCADDR 230: PUSHSTATADDR 031: LOADFROMADDR32: SAVETOADDR33: PUSHLOCADDR 234: LOADFROMADDR35: WRITEINT36: STOPExample (f): Complex Method with If and While
Section titled “Example (f): Complex Method with If and While”Suppose Instruction.getNextCodeAddress() == 45 when a correct solution to TinyJ Assignment 2 begins to translate the following method, and suppose z is a static variable with address 2. What TinyJ VM instructions would this method be translated into?
static int p(int x){ int y = 3, w; x = z + y; if (x < 10) z = x; else z = y; while (z <= 100) { System.out.println(z); z = z + y; } return z - x;}Solution
45: INITSTKFRM 246: PUSHLOCADDR 147: PUSHNUM 348: SAVETOADDR49: PUSHLOCADDR -250: PUSHSTATADDR 251: LOADFROMADDR52: PUSHLOCADDR 153: LOADFROMADDR54: ADD55: SAVETOADDR56: PUSHLOCADDR -257: LOADFROMADDR58: PUSHNUM 1059: LT60: JUMPONFALSE 6661: PUSHSTATADDR 262: PUSHLOCADDR -263: LOADFROMADDR64: SAVETOADDR65: JUMP 7066: PUSHSTATADDR 267: PUSHLOCADDR 168: LOADFROMADDR69: SAVETOADDR70: PUSHSTATADDR 271: LOADFROMADDR72: PUSHNUM 10073: LE74: JUMPONFALSE 8775: PUSHSTATADDR 276: LOADFROMADDR77: WRITEINT78: WRITELNOP79: PUSHSTATADDR 280: PUSHSTATADDR 281: LOADFROMADDR82: PUSHLOCADDR 183: LOADFROMADDR84: ADD85: SAVETOADDR86: JUMP 7087: PUSHSTATADDR 288: LOADFROMADDR89: PUSHLOCADDR -290: LOADFROMADDR91: SUB92: RETURN 1A Mistake to Avoid When Writing Recursive Descent Parsing Code
Section titled “A Mistake to Avoid When Writing Recursive Descent Parsing Code”A common mistake in writing recursive descent parsing code is to write:
getCurrentToken() == Xor accept(X) [which performs a getCurrentToken() == X test]
using a Symbols constant X that represents a nonterminal. This is wrong, as getCurrentToken() returns a Symbols constant that represents a token. Here are two examples of this kind of mistake.
Example 1: argumentList() Method
Section titled “Example 1: argumentList() Method”In TinyJ Assignment 1, the method argumentList() should be based on the following EBNF rule:
<argumentList> ::= '(' [ <expr3> { , <expr3> } ] ')'When writing this method it would be wrong to write:
accept(LPAREN);if (getCurrentToken() == NTexpr3) /* INCORRECT! */ { expr3(); // ... a while loop that deals with {, <expr3>}}accept(RPAREN);Here it would be correct to write code of the following form:
accept(LPAREN);if (getCurrentToken() != RPAREN) /* CORRECT */ { expr3(); // ... a while loop that deals with {, <expr3>}}accept(RPAREN);Example 2: expr1() Method
Section titled “Example 2: expr1() Method”When writing the method expr1() for TinyJ Assignment 1, one case that needs to be dealt with relates to the following part of the TinyJ EBNF rule that defines <expr1>:
IDENTIFIER ( . nextInt '(' ')' | [<argumentList>] { '[' <expr3> ']' } )Here it would be wrong to write something like:
case IDENT: nextToken(); if (getCurrentToken() != DOT) { if (getCurrentToken() == NTargumentList /* INCORRECT! */) { argumentList(); } // ... a while loop that deals with { '[' <expr3> ']' } } else { // ... code to deal with . nextInt '(' ')' } break;Instead, you can write something like:
case IDENT: nextToken(); if (getCurrentToken() != DOT) { if (getCurrentToken() == LPAREN /* CORRECT */) { argumentList(); } // ... a while loop that deals with { '[' <expr3> ']' } } else { // ... code to deal with . nextInt '(' ')' } break;The use of LPAREN in the above code is correct because the first token of any instance of <argumentList> must be a left parenthesis, as we see from the EBNF rule:
<argumentList> ::= '(' [ <expr3> { , <expr3> } ] ')'