SDTs used to implement SDDs A non-cyclic SDD (having definitions of attributes) can always be...

10
SDTs used to implement SDDs A non-cyclic SDD (having definitions of attributes) can always be implemented by a SDT (having actions that assign values to attributes) by arranging that 1. the parser first builds a tree, ignoring the actions of the SDT 2. the tree is then walked, executing the actions of the SDT Two interesting cases where SDDs may be implemented by SDTs whose actions can be executed during parsing, in 1 pass, without a parse tree being constructed S-attributed SDD and parsing is LR (deterministic bottom-up) – this is the simplest case – all actions can be grouped at the end of a production – result is a “postfix SDT” L-attributed SDD and parsing can be LL (deterministic top-down) – if a grammar can be parsed LL, it can also be parsed LR http://csiweb.ucd.ie/staff/acater/comp30330.html Compiler Construction 1

description

Implemention issues with postfix SDTs Attributes can be stored in records/structs/dictionaries on the symbol stack, alongside LR parser states on the state stack. Furthermore the grammar symbol itself need not be kept on the symbol stack If some attribute values have unbounded size – e.g. for strings or code lists – it is best to store fixed-size pointers in the stack structures, and store the actual values elsewhere When a parser reduce occur – say for T ::= T 1 * F – the stack records containing needed values (T1’s for T1.val, F’s for F.val) will be at known offsets (-1, 0) relative to the top of stack. The result can be left on top of the stack after reduce step. With single productions, e.g. T ::= F, no change to the topmost stack entry is necessary since the item at the top must stay at the top. (but top state will usually change) Compiler Construction3

Transcript of SDTs used to implement SDDs A non-cyclic SDD (having definitions of attributes) can always be...

Page 1: SDTs used to implement SDDs A non-cyclic SDD (having definitions of attributes) can always be implemented…

SDTs used to implement SDDs

• A non-cyclic SDD (having definitions of attributes) can always be implemented by a SDT (having actions that assign values to attributes) by arranging that

1. the parser first builds a tree, ignoring the actions of the SDT2. the tree is then walked, executing the actions of the SDT

• Two interesting cases where SDDs may be implemented by SDTs whose actions can be executed during parsing, in 1 pass, without a parse tree being constructed

S-attributed SDD and parsing is LR (deterministic bottom-up)– this is the simplest case– all actions can be grouped at the end of a production– result is a “postfix SDT”

L-attributed SDD and parsing can be LL (deterministic top-down)– if a grammar can be parsed LL, it can also be parsed LR

http://csiweb.ucd.ie/staff/acater/comp30330.html Compiler Construction 1

Page 2: SDTs used to implement SDDs A non-cyclic SDD (having definitions of attributes) can always be implemented…

Postfix SDTs

• If grammar can be parsed deterministically bottom-up, & all attributes “synthetic”• Then all actions of a production can be executed along with the parser’s reduce step• They can all be grouped at the end of the production, forming a postfix to it

http://csiweb.ucd.ie/staff/acater/comp30330.html Compiler Construction 2

L ::= E \nE ::= E1 + TE ::= TT ::= T1 * FT ::= FF ::= ( E )F ::= digit

{ print(E.val) }{ E.val=E1.val+T.val }{ E.val=T.val }{ T.val=T1.val*F.val }{ T.val = F.val }{ F.val=E.val }{ F.val=digit.lexval }

e.g. Postfix SDT for very-simple desk calculator

all attributes are synthetic,

all actions occur at end of productionand from the parser’s point of view they are part of the production,

the grammar is LR(1).

Page 3: SDTs used to implement SDDs A non-cyclic SDD (having definitions of attributes) can always be implemented…

Implemention issues with postfix SDTs

• Attributes can be stored in records/structs/dictionaries on the symbol stack, alongside LR parser states on the state stack.

• Furthermore the grammar symbol itself need not be kept on the symbol stack

• If some attribute values have unbounded size – e.g. for strings or code lists – it is best to store fixed-size pointers in the stack structures, and store the actual values elsewhere

• When a parser reduce occur – say for T ::= T1 * F – the stack records containing needed values (T1’s for T1.val, F’s for F.val) will be at known offsets (-1, 0) relative to the top of stack. The result can be left on top of the stack after reduce step.

• With single productions, e.g. T ::= F, no change to the topmost stack entry is necessary since the item at the top must stay at the top. (but top state will usually change)

http://csiweb.ucd.ie/staff/acater/comp30330.html Compiler Construction 3

Page 4: SDTs used to implement SDDs A non-cyclic SDD (having definitions of attributes) can always be implemented…

Parser stack manipulations made explicit

http://csiweb.ucd.ie/staff/acater/comp30330.html Compiler Construction 4

L ::= E \n

E ::= E1 + T

E ::= T

T ::= T1 * F

T ::= F

F ::= ( E )

F ::= digit

{ print (stack[top-1].val; top=top-1; }{ stack[top-2].val=stack[top-2].val+stack[top].val; top=top-2; }{ }

stack[top-2].val=stack[top-2].val*stack[top].val; top=top-2; }{ }

{ stack[top-2].val=stack[top-1].val; top=top-2; }{ } // if we may assume that val and lexval attributes // occupy the same locations in a struct

T: T.val=3*E: E.val=6

……

……………

top

effect is to place 9 into the existing E element’s .val and make top point to it

Page 5: SDTs used to implement SDDs A non-cyclic SDD (having definitions of attributes) can always be implemented…

SDTs with actions inside productions - 1

• Where SDD is not S-attributed (it may be L-attributed or worse) then no postfix SDT can be constructed: actions inside productions will be necessary

productions have the form X ::= {a} where is not

• If such a SDT cannot be handled during parsing – because it is worse than L-attributed while still being acyclic – then

parse, ignoring actions, and producing an explicit parse tree then, knowing what productions have been used to parse, add extra child nodes

for the particular actions within those productions perform preorder traversal of tree, executing the actions

http://csiweb.ucd.ie/staff/acater/comp30330.html Compiler Construction 5

X

X

{a}

Page 6: SDTs used to implement SDDs A non-cyclic SDD (having definitions of attributes) can always be implemented…

SDTs with actions inside productions - 2

• If such a SDT can be handled during parsing, then

if parsing bottom-up, treat the action like a non-terminal:– invent a unique marker non-terminal for each action– such a non-terminal has one empty production– perform the action when that non-terminal is on the top of the stack

if parsing top-down– perform the action just before processing the next grammar symbol

• checking the next terminal, if begins with a terminal• expanding the next non-terminal, if begins with a non-terminal

http://csiweb.ucd.ie/staff/acater/comp30330.html Compiler Construction 6

X ::= M39 M39 ::=

Page 7: SDTs used to implement SDDs A non-cyclic SDD (having definitions of attributes) can always be implemented…

Eliminating left recursion from SDTs

• Grammars, and hence SDTs, cannot be used for top-down parsing if left recursive

• Treat actions like terminals, since order of terminals is preserved by left recursion elimination (although if any production begins with an action, the grammar with SDT will not be

possible to parse deterministically either top-down or even bottom-up, and it will then be necessary to walk a tree)

• General idea of left recursion elimination (without actions) is to replace left recursion by right recursion involving a new non-terminal capable of generating

http://csiweb.ucd.ie/staff/acater/comp30330.html Compiler Construction 7

A ::= A | A ::= b RR ::= R |

• With actions {a} and {b} in SDD, similar transformation requires care

A ::= A {a} | {b} A ::= b {?1} R {?2}R ::= {?3} R {?4} | {?5}

Page 8: SDTs used to implement SDDs A non-cyclic SDD (having definitions of attributes) can always be implemented…

… the right-recursive version should achieve same result

http://csiweb.ucd.ie/staff/acater/comp30330.html Compiler Construction 8

Example left-recursive original SDT with only one S-attribute ‘q’

A ::= A1 b { A.q=f(A1.q, b.q);} | c { A.q=g(c);}

For sample input ‘cbb’,flow of information is always upwards

A.q=f(g(c),b.q)

A.q=f(f(g(c),b.q),b.q)A

A

A b

c

b

A.q=g(c)

After eliminating left recursion,the shape of the parse tree will beradically different, but the end resultcalculated for topmost A.q shouldbe the same

Page 9: SDTs used to implement SDDs A non-cyclic SDD (having definitions of attributes) can always be implemented…

… needing inherited attributes

http://csiweb.ucd.ie/staff/acater/comp30330.html Compiler Construction 9

A.q=f(g(c),b.q)

A.q=f(f(g(c),b.q),b.q)A

A

A b

c

b

A.q=g(c)

A

R

R

R

b

b

c

Only at this point can it be known that the correct result isf(f(g(c),b.q),b.q)

A.q=R.sR.i=g(c)

R.s=R1.sR1.i=f(g(c),b.q)

R.s=R1.sR1.i=f(f(g(c),b.q),b.q)

R.s=R.i

with left recursive grammar

Page 10: SDTs used to implement SDDs A non-cyclic SDD (having definitions of attributes) can always be implemented…

… and appropriate evaluation order

• Getting the actions done in the right order and with the right information available requires use of inherited attributes

values of inherited attributes must be calculated immediately before the right-recursive use of the non-terminal R

• Getting the result(s) back to the top node involves a new synthesized attribute(s) values of synthesized attributes may be calculated right at the ends of

productions, just as with postfix SDTs

http://csiweb.ucd.ie/staff/acater/comp30330.html Compiler Construction 10

A ::= c {R.i=g(c)} R {A.q=R.s}R ::= b {R1.i=f(R.i, b.q)} R {R.s=R1.s}R ::= {R.s=R.i}