Lecture 17 - University of Florida

Download Report

Transcript Lecture 17 - University of Florida

Attribute Grammars
Programming Language Principles
Lecture 17
Prepared by
Manuel E. Bermúdez, Ph.D.
Associate Professor
University of Florida
Functional Graphs
• Definition:
• A functional graph is a directed
graph in which:
• Nodes represent functions.
• Incoming edges are parameters.
• Outgoing edges represent
functional values.
• Edges represent data
transmission among functions.
Example
2
3
+
5
• Here we have three functions:
• Constant function 2.
• Constant function 3.
• Binary function + (addition).
• After some delay, the output value is 5.
Cycles in Functional Graphs
• Can make sense only if the graph can
achieve a steady state.
• Example:
1
+
?
• No steady state is achieved, because
the output value keeps incrementing.
Example
true
and
?
• A steady state is achieved.
• If the "AND" were changed to a
"NAND," no steady state.
• Undecidable (halting problem) whether
a steady state will ever occur.
• We will assume that all functional
graphs are acyclic.
Evaluation of Functional Graphs
• First insert registers.
• Then propagate values along the
edges.
• Register insertion:
• Given edges E1 ... En from node A to
nodes B1 ... Bn,
• insert a register R:
• one edge from A to R, and n
• edges from R to B1 ... Bn.
Example
B1
B1
gets
A
B2
…
Bn
converted
to
A
B2
…
Bn
• All registers initialized to some
"undefined" value.
• Top-most registers have no outgoing edges.
Functional Value Propagation
• Two algorithms:
• Data Flow Analysis: repeated passes on
the graph, evaluating functions whose
inputs (registers) are defined.
• Lazy Evaluation", performs a depth-first
search, backwards on the edges.
Data Flow Algorithm
While any top-most register is undefined {
for each node N in the graph {
if all inputs of N come from defined
register then
evaluate N;
update N's output register
}
}
Lazy Evaluation
for each top-most register R, { push (stack, R)
while stack not empty {
current := top (stack);
if current = undefined {
computable := true;
for each R in dependency (current), while
computable {
if R = undefined {
computable := false;
push (stack,R); }
}
if computable {
compute_value(current); pop(stack); }
}
else pop (stack)
}
}
Data Flow and Lazy Evaluation
• Data Flow Analysis:
• Starts at constants and propagates
values forward.
• No stack. Algorithm computes ALL
values, needed or not.
Data Flow and Lazy Evaluation
(cont’d)
• Lazy evaluation:
• Starts at the target nodes.
• Chases dependencies backwards.
• Evaluates functions ONLY if they are
needed.
• More storage expensive (stack), but
faster.
Attribute Grammars
• Associate constructs in an AST
with segments of a functional graph.
• It's a context-free grammar:
• Each rule augmented with
a set of axioms.
• Axioms specify graph segments.
• As AST is built (bottom-up);
segments of the functional
graph are "pasted" together.
Attribute Grammars (cont’d)
• After completing AST (and graph),
evaluate graph (data flow or lazy
evaluation).
• After graph evaluation, top-most
graph register(s) (presumably) contain
the output of the translation.
Definition
An attribute grammar consists of:
1. A context-free grammar (structure of the
parse tree)
2. A set of attributes ATT.
• Each attribute "decorates" some node in
the AST, later becomes a register in the
functional graph.
3. A set of axioms defining relationships
among attributes (nodes in the graph).
Example: Binary Numbers
• String-to-tree transduction grammar:
S
N
D
D
→ N
=> .
→ N . N => .
→ N D
=> cat
→ D
→ 0
=> 0
→1
=> 1
• This grammar specifies "concrete" syntax.
• Notice left recursion.
Abstract Syntax Tree Grammar
• Use < ... > tree notation.
S
N
D
→
→
→
→
→
→
<. N N>
<. N>
<cat N D>
D
0
1
• This grammar (our choice for AG's)
specifies "abstract" syntax.
Attributes
• Two types:
• Synthesized: pass information UP the tree.
• Inherited: pass information DOWN the tree.
• If tree is traversed recursively with
(s1, … sn) ProcessNode(tree T, (i1,…,im)),
• m inherited attributes are parameters to
ProcessNode.
• n synthesized attributes are return values of
ProcessNode.
Attributes (cont’d)
For binary numbers,
ATT = {value, length, exp}, where
• value: decimal value of the binary
number.
• length: number of binary digits to the
left of the right-most digit in the subtree. Used to generate negative
exponents (fractional part).
• exp: exponent (of 2), to be multiplied
by the right-most binary digit in the
subtree.
Attributes (cont’d)
• Synthesized and inherited attributes
specified by two subsets of ATT:
• SATT = {value, length}
• IATT = {exp}
• Note: SATT and IATT are disjoint in
this case. Not always.
Attributes Associated with Tree
Nodes
S:  → PowerSet (SATT)
I:  → PowerSet (IATT)
 = { 0, 1, cat, . } (tree grammar vocabulary).
S(0)
S(1)
S(cat)
S(.)
I(0)
I(1)
I(cat)
I(.)
=
=
=
=
=
=
=
=
{
{
{
{
{
{
{
{
value, length }
value, length }
value, length }
value }
exp }
exp }
exp }
}
Example
• Input: 10.1.
• Convention:
• Inherited attributes depicted on the
LEFT.
• Synthesized attributes depicted on
the RIGHT.
• Flow of information: top-down on
left, bottom-up on right.
Tree Addressing Scheme
Given a tree node T, with kids T1, ... Tn,
a() denotes attribute "a" at node T, and
a(i) denotes attribute "a" at the i’th child
of node T (node Ti).
• So,
• v() is the "value" attribute at T
• v(1) is the "value" attribute at T1.
• v(2) is the "value" attribute at T2.
Rules for axiom specification.
•
•
Consider a production rule A → <r K1 ... Kn>.
Let r1, ..., rn be the roots of subtrees K1, ... , Kn.
1. Need one axiom per synthesized attribute at r
(specify what goes up at root).
2. Need one axiom per inherited attribute at each kid ri
(specify what goes to the kids).
3. Axioms are of the form a=f(w1, ..., wm),
where each wi is either
3.1.
3.2.
•
inherited at r,
synthesized from ri, for some i.
Note: this doesn’t prevent cycles.
Attribute Grammar, Binary Numbers
• Production rule S → <. N N>.
• Need three axioms:
• one for v at ".",
• one for e at T1,
• one for e at T2.
• Axioms:
value()
exp(1)
exp(2)
= value(1) + value(2)
= 0
= - length(2)
Notes
• length attribute from kid 1 ignored.
• length attribute from kid 2 is negated,
sent down to e(2). Only use length
to calculate negative exponents
(fractional part, second kid).
• length will start at 1, at the bottom of
the tree. Will be incremented on the
way up.
The Complete Attribute Grammar
for Binary Numbers
S -> <. N N>
value() = value(1) + value(2)
exp(1) = 0
exp(2) = - length(2)
S -> <. N>
value() = value(1)
up.
exp(1) = 0
# no fraction;
# copy value
The Complete Attribute Grammar
for Binary Numbers (cont’d)
N →
<cat N D>
value() = value(1) + value (2) #
#
length() = length(1) + 1
#
#
exp(1) = exp () + 1
#
#
exp(2) = exp ()
#
#
N →
D
# No axioms !
add two
values.
increment
length up left.
increment
exp down left.
copy exp
down right.
The Complete Attribute Grammar
for Binary Numbers (cont’d)
D →
D →
0
value() = 0
length() = 1
# zero * 2exp.
# initial length.
value() = 2 ** exp()
length() = 1
# compute value.
# initial length.
1
Example
• Example, for input 10.1
Generation of Functional Graphs
• Optional, see notes
Attribute Grammars
Programming Language Principles
Lecture 17
Prepared by
Manuel E. Bermúdez, Ph.D.
Associate Professor
University of Florida