Download Attribute Grammar: A Compiler Design Example and more Study notes Programming Languages in PDF only on Docsity! 22C:185 page 1 Attribute Grammar Example This example illustrates the most common use of attribute grammars — prototyping compilers. It is adapted from B. Courcelle “Attribute grammars: theory and applications”, Formalization of Programming Concepts, Lect. Notes in Comp. Sci., V.107. This example concerns itself with only a small part of a compiler, namely translating assignment statements. In particular, it focuses on the process of allocating temporary storage for the evaluation of expressions. For instance, in the expression a+b*c, the result of b*c must first be computed and stored in some memory location d, and then the addition a+d performed. In complicated expressions, numerous intermediate results must be generated and the locations where they are stored retained for later use as appropriate. This methodology is described here by means of an attribute grammar. Some pool of available temporary memory locations must be assumed. In order to focus on the desired issues, this is taken to be the symbolic locations L0, L1, L2, … Courcelle expresses the code for an assignment statement in terms of “three address instructions”. That is, the usual precedence is used to resolve the order of operations, and suitable temporary locations are determined, but the code is symbolically indicated to avoid computer architecture details. Also, temporary locations are reused when their prior use is completed. For example, the assignment X := 3.14 * (X+Y) will be translated into L0 := 3.14; L1 := X; L2 := Y; L1 := L1+L2; L0 := L0*L1; X:= L0. This attribute grammar uses the attribute ‘free’ to denote the first unused temporary memory location available, the attribute ‘res’ to denote the memory location used to hold the result of an expression, term, etc., and ‘code’ to denote the sequence of instructions for an assignment, expression, etc. The semantic rules also use the function ‘next’ applied to temporary memory locations with the obvious result (e.g., next(L0) = L1, etc.). 22C:185 page 2 BNF Semantic Rules 1. <asn> Æ <id> := <exp> asn.code = [exp.code ; id:= exp.res] asn.free = L0 exp.free = asn.free 2. <exp> Æ <trm> exp.code = trm.code exp.res = trm.res trm.free = exp.free 3. <exp>0 Æ <exp>1 + <trm> exp0.code = [exp1.code ; trm.code ; exp1.res:= exp1.res+trm.res] exp0.res = exp1.res exp1.free = exp0.free trm.free = next(exp1.res) 4. <trm> Æ <fct> trm.code = fct.code trm.res = fct.res fct.free = trm.free 5. <trm>0 Æ <trm>1 * <fct> trm0.code = [trm1.code ; fct.code ; trm0.res:= trm1.res*fct.res] trm0.res = trm1.res trm1.free = trm0.free fct.free = next(trm1.res) 6. <fct> Æ ( <exp> ) fct.code = exp.code fct.res = exp.res exp.free = fct.free 7. <fct> Æ <id> fct.code = fct.free:= id fct.res = fct.free 8. <fct> Æ <const> fct.code = fct.free:= const fct.res = fct.free The attribute grammar above omits subtraction and division operations for brevity, but they would be treated entirely similarly to table entries 3 and 5, respectively. The attribute dependencies in this example are rather intricate. These are perhaps best depicted in terms of the associated derivation tree nodes. So for production #1 there are the following dependencies, where the arrow shows the direction of information flow. <asn> code free <id> := <exp> code res free