Compiler Construction - Final Exam Answers - Spring, 2009 PROBLEM 1 --------- SKIP: { " " | "\t" | "\n" | "\r" } TOKEN: { "{" | "}" | "[" | "]" | "," | ":" | )?> | <#EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+> | | (||"_")*> | <#LETTER: ["A"-"Z", "a"-"z"] > | <#DIGIT: ["0"-"9"] > } void parseObject(): {} { "{" parseBinding() ("," parseBinding())* "}" } void parseBinding(): {} { ":" parseValue() } void parseValue(): {} { | | | parseArray() | parseObject() } void parseArray(): {} { "[" parseValue() ("," parseValue())* "]" } (If this were part of a production system, there would be a rule with an in it somewhere, but the problem wasn't really clear as to where this would be.) PROBLEM 2 --------- Fun | +--------+--------------+------------+-----------+ | | | | | void f Param Variadic Block / \ | string z | | +-------------------------------+----------+----+ | | | Struct Var Call | / \ / \ +-----+---------------------+ e[] p print [] | | | / \ e Field Method MethodCall | / \ / | \ / | / \ real x string f Block [] f 6 >> | / \ / \ return . 1 + x | / \ / \ * p c ~ 2 / \ | * # x / \ | " " y z PROBLEM 3 --------- global timesSeven section .text timesSeven: fld qword [esp+4] ; x fld st0 ; x x fadd st0 ; 2x x fld st0 ; 2x 2x x fadd st1 ; 4x 2x x faddp st1 ; 6x x faddp st1 ; 7x ret PROBLEM 4 --------- a) Just fine (ints are auto-converted to reals) b) Syntax (volatile is a reserved word) c) Just fine (by the way NO BREAK SPACE is not a control char) d) Syntax (no way to have two "<" operators at same level e) Just fine f) Static semantic g) Static semantic h) Just fine i) Just fine j) Static semantic PROBLEM 5 --------- a) The generated tuple sequence for x || y || z is copy x, r0 jnz r0, L1 copy y, r0 L1: copy r0, r1 jnz r1, L0 copy z, r1 L0: ; use r1 here because when short-circuit operators are treated as strictly binary operators we're looking at the above source code as if it were (x || y) || z. The x || y part is targeted into r0, and the whole thing is targeted into r1. Worse, if we find x to be true, we don't jump to the end of the *whole* expression; we jump only to a part where it is copied into r1 and tested *again*. b) The following is way better copy x, r0 jnz r0, L0 copy y, r0 jnz r0, L0 copy z, r0 L0: ; use r0 here c) This is done by noticing that the assignment (copy r0, r1) is the last time r0 is used and the first time r1 is used, so we would basically rename r1 to r0 and eliminate this assignment. How "easy" this is depends on aliasing and whether this kind of code exists in a loop where r0 is supposed to retain as its initial value the value it ended up with in its last iteration. Alias and dataflow analysis can determine this stuff, but with some work. You can make a case, in your answer, for this being easy or hard. PROBLEM 6 --------- a) This is the language of identifiers mixed into a sequence of properly matched, nested and balanced curly braces, parens, and square brackets. For example {{x}x}{[{x}]}{{((x))x}x} b) It's ambiguous alright: ID ID ID can have multiple parse trees: EXP EXP / \ / \ EXP EXP EXP EXP / \ | | / \ EXP EXP | | EXP EXP | | | | | | ID ID ID ID ID ID c) It's not LL(k) for any k because it is ambiguous. d) E -> "{" E1 "}" E.level = E1.level + 1 E -> "(" E1 ")" E.level = E1.level + 1 E -> "[" E1 "]" E.level = E1.level + 1 E -> E1 E2 E1.level := E.level E2.level := E.level E -> I I.level = E.level This grammar really needs a different start symbol, with the following rule: S -> E E.level := 0 Otherwise this AG is pretty useless. PROBLEM 7 --------- a) The first assert checks that the array is not null and is necessary. The second is part of a range check, which is not necessary, since the array is only indexed from 0 to length-1. Technically you could say it is required, since the array may be shared between threads. b) The induction variable is i1 here; we are using i1*4 in the loop and incrementing it by 1 each time. So we want to use a temporary for it that is incremented by 4 each time. We have to change all uses of i1 (starting and ending conditions especially). Then i1 can go away. We get (in the book's notation): r0 := *(a+4) r1 := a -- this is the "new" loop var r2 := r0 * 4 -- adjustment L0: if r1 >= r2 goto L1 r3 := to_string r1 param r3 call __print, 4 r4 := r1 - 4 -- (#a-1)*4 if r1 == r4 goto L2 param s0 call __print, 4 L2: r1 := r1 + 4 goto L0 L1: If you really want to optimize, first realize the source code is poorly written; it should have been: if (#a > 0) { for (int i = 0; i < #a-1; i++) { print($a[i]); print(", "); } print($a[#a - 1]); } We can also count down instead of up, and precompute some values outside the loop. r0 := *(a-4) ; length of a if r0 <= 0 goto L2 ; skip EVERYTHING if empty array r1 := r0 - 1 ; upper bound of for loop for counting down r2 := a ; address of a[i] goto L1 L0: r3 := to_string *r2 param r3 call __print, 4 ; print string rep of current element param s0 call __print, 4 ; print the comma and space r2 := r2 + 4 ; point to next array element dec t1 ; decrement counter (better than compare w/ ub) L1: if r1 > 0 goto L0 ; continue if counter not yet 0 r4 := to_string *r2 ; Yay t2 already pointing to last element param r4 call __print, 4 L2: PROBLEM 8 --------- a) 0 | 00 | [01]*000 b) [A-Fa-f0-9]{3}[08] c) \d+\.\d+([Ee][+-]?\d{1,3})? d) ([^r]|r(?!(eturn|etry)))*