CS301 W'99 Lecture Notes Lecture 12 PSU CS301 W'99 Lecture 12 Oc Andrew Tolmach 1992-99 1 Inherited Attributes Sometimes convenient to make node's attributes dependent on siblings or ancestors in tree. Useful for expressing dependence on context, e.g., relating identifier uses to declarations. (This is especially impor- tant because CFG cannot capture such dependencies.) Example: Parsing Declarations D ! T L L.type := T.type T ! int T.type := integer T ! real T.type := real L ! L1 , id { L1 .type := L.type; addsymb(id.name, L.type) } L ! id addsymb(id.name, L.type) where addsymb adds id and its type to symbol table. Here L.type is inherited attribute. D @ @ @ @ @@ T L Parse tree for real a,b,c | | | @ | | @ | | @ | | | @@ real L , c || @ | @ | @@ , L b | | a PSU CS301 W'99 Lecture 12 Oc Andrew Tolmach 1992-99 2 Dependency Graphs Parse tree for real a,b,c Arrows show dependency relation among attributes. Taken together, arrow describe dependency graph. Must evaluate attributes in topological order of depen- dency graph. If attributes are defined on parse tree, may want to evaluate attributes while (or instead of ) building the tree. This is sometimes possible: o Saw how to evaluate synthesized attributes during bottom- up parser; this method doesn't work for inherited attributes. o Top-down parser can easily evaluate L-attributed gram- mars, in which attributes don't depend on their right ances- tors. (Bottom-up parsers can sometimes handle these too, though with difficulty.) PSU CS301 W'99 Lecture 12 Oc Andrew Tolmach 1992-99 3 o For some attribute grammars, must build entire tree be- fore evaluating attributes. PSU CS301 W'99 Lecture 12 Oc Andrew Tolmach 1992-99 4 Attribute Evaluation during Recursive Descent Each non-terminal function is modified to take inherited attribute values as arguments and return (record of ) syn- thesized attribute value(s) as result. All inherited values are known when function is called. All synthesized values are known when function returns. Example revisited (with left-recursion removed): typedef enum -intTy,realTy" typ; void D() - typ ty = T(); L(ty); " typ T() - if (tok == INT) - lex(); return intTy; " else if (tok == REAL) - lex(); return realTy; " else error(); " void L(typ ty) - if (tok == ID) - addsymb(lexeme,ty); lex(); " else error(); if (tok == ',') - lex(); L(ty); " " PSU CS301 W'99 Lecture 12 Oc Andrew Tolmach 1992-99 5 Avoiding Inherited Attributes When using bottom-up parser (e.g., with yacc), it is desir- able to avoid inherited attributes. There are several approaches: o Move the activity requiring the attribute to a higher node in the tree, by substituting a synthesized attribute for the inherited one, e.g.: D ! T L for each id in L.list addsymb(id.name, T.type) T ! int T.type := integer T ! real T.type := real L ! L1 , id L.list := mk __Ids(id,L1 .list) L ! id L.list := mk __Ids(id,nul l) o Can sometimes rewrite grammar, e.g.: D ! T id {D.type := T.type; addsymb(id.name,T.type) } D ! D1 , id {D.type := D1 .type; addsymb(id.name,D.type) } T ! int T.type := integer T ! real T.type := real PSU CS301 W'99 Lecture 12 Oc Andrew Tolmach 1992-99 6 Attributes on AST's Attribute grammar method extends to abstract grammars (not intended for parsing), e.g., AST grammars. o Same concept, but evaluation always occurs after whole tree is built. o Can use recursive descent to evaluate (rather than parse). o Typical applications: typechecking, code generation, inter- pretation. Why attribute grammars? o Compact, convenient formalism. o Local rules describe entire computation. o Separate traversal from computation. o (Purely functional rules can be evaluated in any order.) PSU CS301 W'99 Lecture 12 Oc Andrew Tolmach 1992-99 7 Checking of E Language (Homework 1) Can view checking process as evaluation of following at- tribute grammar, where o exp.ok and exps.ok are synthesized boolean attributes in- dicating whether expression has checked successfully; and o exp.env and exps.env are inherited environment attributes (with operators empty, extend, and lookup) containing entries for all in-scope variables. program ! exp exp.env := empty exp ! ID exp.ok := lookup(exp.env,ID.name) ! NUM exp.ok := true ! exp1 '+' exp2 { exp1 .env := exp2 .env = exp.env; exp.ok := exp1 .ok AND exp2 .ok } ! exp1 '-' exp2 { exp1 .env := exp2 .env = exp.env; exp.ok := exp1 .ok AND exp2 .ok } ! ID '=' exp1 { exp1 .env := exp.env; exp.ok := lookup(exp.env,ID.name) AND exp1 .ok } ! if0 exp1 exp2 exp3 { exp1 .env := exp2 .env := exp3 .env := exp.env; exp.ok := exp1 .ok AND exp2 .ok AND exp3 .ok } ! '{' vars ';' exps '}' { exps.env := extend(exp.env,vars); exp.ok := exps.ok } exps ! exp { exp.env := exps.env; exps.ok := exp.ok } ! exp ';' exps1 { exp.env := exps1 .env := exps.env; exps.ok := exp.ok AND exps1 .ok }