 Welcome to the lecture part two of semantic analysis. So, in this lecture we will continue with our discussion on attribute grammars and attributed translation grammars. So, to do a bit of recap attribute grammars are extended context free grammars every symbol of the set n union t has associated with it a few attributes. There are two types of attributes inherited and synthesized of course, the same attribute cannot be both inherited and synthesized, but there could be you know several inherited and several synthesized attributes associated with each symbol. Each attribute takes a value from a specified domain such as integer or real or cross product of these etcetera etcetera and we associate attribute computation rules with each production. So, specifically we associate rules for the computation of synthesized attributes of the left hand side non terminal and we associate rules for the computation of inherited attributes of the right hand side non terminals of the production. Of course, the most important aspect of an attribute grammar is that it is strictly the rules are strictly local to each production p there are no side effects in the rules. The attributes of symbols are evaluated over a parse tree by making passes over the parse tree it is possible to have more than one pass and in each pass there would be at least one attribute computed at the nodes. So, the synthesized attributes are computed in a bottom up fashion from the leaves upwards. So, basically they are synthesized from the attribute values of the children of the node in the parse tree. Leaf nodes that is the terminals they have only synthesized attributes and these are initialized by the lexical analyzer and of course, they cannot be modified. As far as the inherited attributes go these are actually the values flow down from the parent or the siblings to the node in question and then of course, they flow downwards again from that node further down. Brief overview of the attribute evaluation strategy we must build the parse tree then we construct the dependence graph of the attributes perform the topological sort on the dependence graph and we obtain an evaluation order. Of course, then attribute evaluation is carried out using this order and the attribute evaluations attached to the respective productions are used in the attribute evaluation process. So, we look at the example continue looking at the example that I showed you last time. So, this is the attribute grammar for the evaluation of a real number from its bit string representation for example, if we have a bit string 110.101 its decimal value is 6.625. Here is a context free grammar n going to l.r, l generates several bits on the left side of the dot and the grammar is l going to b l or b. Similarly, r generates the bits on the right side of the dot and the grammar is r going to b r or b, b is of course, a bit either 0 or 1. Now, the attributes of the various. So, if you look at this string it generates this grammar it generates binary strings with a dot in the middle, but we have no indication of what its decimal value is. The attribute grammar is supposed to give us the decimal value of this any particular bit string generated by this grammar. The non terminals n, r and b have just one synthesized attribute value which is from the domain of reals whereas, the non terminal l has a synthesized attribute length and has another synthesized attribute value. So, length is from the domain of integers and real is from the domain of real numbers. Why do we require two attributes for l whereas, one suffices for n, l, r and b. So, to convince ourselves that this is required let us just take a look at the strings. For the left hand side of the dot you know suppose we consider this one of the string 1, 1, 0 the in a power that is given to rather the exponentiation value given to this particular 1 that is 2 to the power 2. So, the first one is a 0, 2 to the power 0, second one is 2 to the power 1 and the third one is 2 to the power 2. This actually is based on the number of bits to its right side till the dot. So, there are 2 here if we had more that would be the power of the base 2. This cannot be determined and if you look at the grammar for l, this b indicates this 1 that we are considering and l to the right of it indicates the rest of the bits to the right of this particular 1 in this case. So, without knowing the length of the string which is generated by l it is not possible to assign the appropriate weight to this particular bit. In the case of the fraction the weight actually starts with minus 1 right after the dot. So, this is 2 to the power minus 1, this is 2 to the power minus 2 and the third bit is 2 to the power minus 3 and the grammar is also such that the b which is nearest to the dot gets generated first and r is the rest of the dots after the b. So, therefore assigning a weight to b is not at all difficult and it is sufficient to have just value as the attribute of this non-terminal. So, let us look at look at the attribute grammar n going to l dot r. So, the value of n is obviously the value of l plus the value of r assuming that we have assigned weights to the bits in both l and r appropriately. So, l generates a bit by the production l going to b. So, l dot value will be just b dot value either 0 or 1 and l dot length is 1 because we are generating just 1 bit. So, l 1 going to b l 2. So, l 2 has a certain length and b is an extra bit which has now been generated. So, l 1 dot length is obviously l 2 dot length plus 1. What about the value? l 1 dot value is obviously, we take the l 2 dot value just like we take 2 to the power 2 plus the value of 1 0 which is 2 that is total will be 6. So, similarly we take the value of l 2. So, l 2 dot value and we take the value of the bit b dot value either 0 or 1 multiplied by the appropriate weight 2 to the power l 2 dot length. So, here l 2 dot length would have been 2. So, this would have been assigned the value 2 to the power 2. So, that is how the value of l 1 gets computed r to b is very simple r dot value equal to b dot value slash 2 and r 1 going to b r 2. So, the reason we have b dot value slash 2 is that we generate the you know first bit after the dot gets the value 2 to the power minus gets the weight 2 to the power minus 1. So, the bit value divided by 2 is r dot value. Now, r going to b r 2 r 1 dot value will be take b dot value plus r 2 dot value and the whole thing is divided by 2. That is fairly easy to see if you look at the production because r 2 had some value. Now, because of the bit it has been right shifted by one position. So, it is its value divided by 2 is the right weight and value for this particular r 2 and a new bit has been introduced. So, its value is b dot value and as I said we must always divide the bit value by 2 because the weights begin with minus 1 here. B to 0 is b dot value equal to 0 and b 2 1 will be b dot value equal to 1. So, here is a sample string 110.101 it is parse tree the red ones are all the leaves and this is the dependence graph based on this parse tree and the attribute grammar. So, n dot value depends on l dot value and r dot value. Then n dot length l dot length at this node depends on the l dot length below, whereas the l dot value here depends on b dot value l dot length and l dot value at the lower level. So, here in this case we are looking at this node. So, l dot value here will depend on b dot value then the l dot length and l dot value at this point. So, similarly for the other nodes of the tree as well and similar for the r part r dot value depends on b dot value and r dot value. So, this recursively downwards. So, let us see how the attribute evaluation takes place given this particular dependence graph a topological sort of the dependence graph makes these leaves to be evaluated first and then the next level and then the next level and so on and so forth. Here is the same graph written you know in a slightly different way with the nodes numbered according to the sequence of evaluation. So, 1 2 then 3 these are the 3 nodes which are evaluated first because they do not require any other node to be evaluated. So, it is possible to evaluate l dot length also along with these 2 or rather these 3 because it does not require anything else at this point of time, but we cannot evaluate l dot value because l dot value requires b dot value. Therefore, we would rather wait until the b dot values are all completed and then go to this node 4 which has these 2 attributes and evaluate both of them at the same time. So, the as I said node 1 is evaluated first then node 2 and then node 3 just to save space I have showed all these 3 evaluations in the same slide. At nodes 1 and 2 we apply the production b going to 1 and the value obtained is b dot value equal to 1 at node 3 we apply the production b to 0 with the value b dot value equal to 0 and then. So, these 3 were evaluated already now it is the turn of these nodes to be evaluated. So, now l dot length equal to 1 l dot value equal to 0. So, 4 and then 5 and you know these are the nodes which will be evaluated at node 4 the production even though I have shown all the values as I said node 4 is evaluated first and then node 5 because the values of node 4 are required for node 5. So, at the node l of a 4 l to b is applied and the value can be computed using the production rule. So, l dot value equal to b dot value and l dot length equal to 1. So, that appropriately uses 1 and 0 here as we go upwards the production applied is l 1 going to b l 2 so length gets incremented. So, this becomes 2 and l 1 dot value is computed using the rule b dot value into 2 to the power l 2 dot length plus l 2 dot value. So, l 2 dot value is 0 l 2 dot length is 1. So, this becomes 2 to the power 1 into 1 that is just 2. So, now we go further now node 6 node 7 8 and 9 in that order will be evaluated at 6 we have the same l 1 going to b l 2 and therefore, the value obtained here would be 2 plus 2 square so that would be 6 and length becomes 3 because we have generated 3 bits so far at nodes 7 and 9. So, 7 and 9 we apply b going to 1 and at node 8 we apply b going to 0. So, the value here b is 1 and these 2 this is 0 and these 2 are 1 then we evaluate the nodes 10, 11 and 12 and 13 in that order. So, this gets r dot value equal to 0.5 because of the rule r going to be this gets the value r dot value equal to 0.25 because this is a 0 this gets divided by 2 and then this gets you know the value 0.625 because this is a 1 which gives you 0.5 and this is 0.25. Finally, adding up the values from the left side and the right side we get n dot value equal to 6.625. So, this is the order in which the evaluation happens over the parse tree and over the dependence graph. So, let us take another example. So, this particular example is again for computation of a real number from its bit string representation, but the method is very different. So, look at the grammar the previous grammar had l dot r and 2 different grammars for l and r productions for l and r, but here we have just one non terminal x dot you know x and the production is n going to x dot x x going to b x r b b going to 0 r 1. So, when the non terminal x produces bits it is not possible to know whether we are on the right side of the dot or on the left side of the dot. So, because of this problem we cannot use the same attribute grammar as we had studied before the strategy is going to be very different, but in some sense it is a simpler strategy as well we compute the value of the string as it is. So, we really do not worry about the bit at the beginning as are the dot at the beginning. So, 1 1 0 has the value 6 and 1 0 1 0 has the value 10 decimal value 10. Now, there are 4 bits after the dot. So, the length of the string here is 4. So, we simply divide the fraction part by 2 to the power 4 and that gives us 6 plus 10 by 16 which is 6 plus 0.625 thereby we get the old value 6.625. So, this is invariant you know the value is invariant even if we had a 0 extra here or a 0 on the left side of this etcetera etcetera. Let us take the case of this becoming 1 0 1 0 0. So, in that case the value is really double of 10 that is 20, but at the same time the number of bits also becomes 5. So, instead of 10 by 2 to the power 4 we really have 20 by 2 to the power 5 which is same as 10 by 2 to the power 4 and the value remains 6.625. Here again we require you know the value as an attribute of both n and b and as far as the non terminal x is concerned we require the length and also the value. So, that is very easy to see this is the value and this is the length, but in the commutation of the value we are not going to differentiate between the fraction part and the integer part. So, n going to x dot x we write it as x 1 dot x 2 just to differentiate between the two instances of the x. So, n dot value will be x 1 dot value plus x 2 dot value divided by 2 to the power x 2 dot length. So, x 2 dot value divided by 2 to the power x 2 dot length is what we have done here. x 2 b is straight forward we get x dot value equal to b dot value and x dot length equal to 1. x going to b x will give us x 1 dot length equal to x 2 dot length plus 1 which is very easy because we have added a bit here and x 1 dot value is b dot value into to the power x 2 dot length. So, that is also something from the previous grammar plus x 2 dot value these two are straight forward as before. So, this is the attribute grammar a different one for the computation of the real number from a bit string representation. The moral of this these two examples is that the sentences in the language may be the same the context free grammars may become different. And thereby the attribute grammars for the computation of the same value may also have to become different. So, let us move on let us take a different example an attribute grammar for associating type information with the names in variable declarations. So, we have the grammar is given here. So, let us see how it how it works d list is either d or d list semicolon d. So, here d generates a single declaration and d list generates a list of declarations within d we have d going to t l. So, t is the type. So, either int or float and l is a list of names of this particular type. So, example is given here int a b c float x y etcetera. So, int a b c is generated by d going to t l similarly float x y is generated by d going to t l, but these two together will be generated by d list going to d list semicolon d and then the d list generating another d. So, that is how these two declarations get generated. Now, coming to the attributes of this grammar here for the first time we introduce inherited attributes. So, for example, the non-terminal l and the non-terminal i d both of them have the type information as an inherited grammar attribute. Let us assume a user defined scalar type you know integer comma real which is permitted in c c plus plus pascal etcetera. So, type is from the domain of two quantities integer comma real and these are inherited attributes of l and d whereas, the term the term the non-terminal t has a synthesized attribute type which is again of type integer or real. The non-terminal i d also has a synthesized attribute which is the name of the variable and the terminal symbol identifier also has name as its synthesized attribute which is nothing but a string of characters. So, how does this particular attribute grammar work you know as we go on for example, let us take this example int a b c. So, the type of the three variables a b c is int right a is of type int b is of type int and c is also of type int. So, these names a b and c are really generated at this level by the production i d going to identifier. To associate the name with its type we must actually take this particular int and make it available to the list of names that is being generated. So, that is precisely what we do here the first production first two d list going to delist semicolon d have no computation associated with them d going to t l has attribute computation rule. So, it says l dot type that is the type of the names generated by l is nothing but t dot type. So, l dot type is inherited and t dot type is synthesized. So, this thing since synthesized and coming into l we will see an example with a parse tree within a short while t going to int. So, here t dot type is integer. So, that is very simple similarly t going to float gives us t dot type equal to real l going to i d. So, again l has type as an inherited attribute and that has to be passed on to i d as an inherited attribute. So, i d dot type equal to l dot type l 1 going to l 2 comma i d. So, the l the type information which is given to l 1 from the top is passed on to both l 2 and i d here. So, therefore, l 2 dot type equal to l 1 dot type and i d dot type equal to l 1 dot type i d going to identifier. So, i d dot name is identifier dot name. So, at the level of i d we have associated both the type and the name of that particular variable. So, a b and c are tagged with type integer x y z dot tagged with type real in this particular example. So, we must observe here that the notation for inherited attributes is the attribute and then down arrow. And we have attribute rules computation rules for the inherited attributes on the right hand side of the production. So, l has inherited attributes. So, we provide a rule of computation for it, but we do not provide any rule of computation for the synthesized attribute of t because it is on the right hand side of this particular production whereas, t is on the left hand side of this production. So, we provide a rule of computation for it. So, this and similarly l 1 going to l 2 comma i d l 2 dot type is inherited. So, there is a rule of computation i d dot type is inherited. So, there is a rule of computation, but l 1 dot type is also inherited. So, and it is on the left hand side of the production. So, we do not provide any rule of computation for l 1 dot type. So, let us take this example and understand how the inherited attributes flow over the parse tree. So, the red edges are all attribute dependence edges and the black edges are all parse tree edges. So, the sentence is in t a semicolon float x. So, d list gives us d list semicolon d. This d generates float x and this d list generates d and then that d generates in t a. So, to begin with of course, at this level d list going to d list semicolon d there is no contribution rather no attribute computation here. The first attribute computation is associated only with d. So, let us take this first it is simpler. So, we have t and then that goes to float and the here l it goes to i d and i d goes to identifier the identifier is x. So, here float is a since size attribute and it flows to the node t and the rule of computation is t going to float. So, t dot type gets real. So, that is how this attribute real is computed here the string x flows to i d. So, i d e going to identifier and the computation is i d dot name equal to identifier dot name. So, that is how this x gets computed this particular node. Now, the attribute real which is synthesized by t is passed on as an infinite attribute to l and that happens in the rule d going to t l. So, we have d going to t l here. So, l dot type is t dot type. So, that is the dependence and the computation associated with this production. So, similarly l going to i d will pass on the attribute from l to i d. So, l going to i d. So, i d dot type equal to l dot type. So, the dependence is correct the same thing holds here as well. So, int becomes computes into integer at t and then t flows in as an inherited attribute to l and that flows into i d as an inherited attribute the name a flows as a synthesized attribute to the non-terminal d. So, this is the flow of attributes here. So, there are two nodes which are provided here number one attribute evaluation cannot be done along with l r parsing that is because there are inherited attributes here and that will become clear as we go along l l parsing of this grammar is not possible because the grammar is left recursive. So, this is also necessary because we are going to see how to modify this grammar later to enable l l parsing. So, another example of attribute grammars and this is a much more complicated example. Here is a fairly simple language of expressions s going to e e going to e plus t or t or a new type of expression let i d equal to e in e. Then we have as usual t going to t star f going to parenthesis e parenthesis r number r i d. So, the novelty and the specialty of this particular languages we are permitted to define a value e for this particular name i d and that binding can be used within this expression e. So, the this can be nested and as you can see e can again be a let i d expression and this also can be a let i d expression recursively. So, this language permits expressions to be nested inside expressions and have scopes for the names. So, let me show you with an example what we mean by the scope of this particular name i d in this particular production e going to let i d equal to e in e. Here is the example let a equal to 5 in now a. So, we have so far said let i d equal to e. So, i d is a e is 5 and then we have a new expression this entire bracketed term. So, that is the new e sorry the second e. The second e has similar structure let a equal to 6 in again a new expression begins a star 7 and that completes and then we have a minus a. So, this entire thing let a equal to 6 in a star 7 is the part of e here and then we have you know a which is the second part for this. So, the scopes of the variables a in both cases are shown in different colors. So, this a equal to 5 is valid for this particular a only whereas, within this parenthesized expression this a equal to 6 is valid here at this point of the expression. So, if we so the meaning is this a takes the value of 6. So, giving us 42 and then this entire expression let a equal to 6 in a star 7 has the value 42. So, now this particular a equal to 5 is valid for this a. So, this takes the value 5 this entire expression took the value 42. So, 42 minus 5 is 37. So, the scope of this a and this a are clearly different. So, the same name a, but two different values and two different scopes. Parsing and evaluating such expressions is again not trivial it requires a the concept of a scoped symbol table to make it happen. We are going to see that as well. So, what we are going to show is an abstract attribute grammar for the above language which uses both inherited and synthesized attributes. And these attributes can of course, be evaluated in one pass over the parse tree from left to right I will show you an example of how that can be done as well. Inherited attributes cannot be evaluated during L R parsing. So, this is a general rule unless we place a large number of restrictions on the inherited attributes this rule holds. Therefore, whenever we use inherited attributes the assumption is that we use L L parsing and whenever there are only synthesis attributes then the grammar can be used along with L R parsers. So, here is the attribute grammar for that particular context tree grammar the symbol table is an entity which stores the names and their values. The symbol table which is valid at a particular point in the expression is actually handed down to the expression from its parent in the parse tree. So, for example, in this case the symbol table which is valid within the expression E is phi because this is the start symbol of the grammar. So, E dot sim tab equal to phi and that is E dot sim tab is an inherited attribute. And after the evaluation is completed E produces a value and E dot value is now assigned to S dot val and that is the value of the entire expression. Let us take this production E 1 going to E 2 plus t. So, E going to E plus t and the instances are numbered as 1 and 2. There was a symbol table available to E 1 and that is the same symbol table which will be made available to both E 2 and T without any modification. So, E 2 dot sim tab is E 1 dot sim tab E 1 dot and T dot sim tab is also E 1 dot sim tab. So, remember the order in which this attribute computation rules are written is not very important because this is not the final order. E 1 dot val is nothing but the sum of the values produced by this E 2 and this T. So, E 2 dot val plus T dot val. So, this is the value of E 1 dot val. So, E 2 T is trivial there is just a copy of the two attributes from E 2 T and so T dot sim tab is E dot sim tab and E dot val is T dot val. The important things happen in production number 4 here and then it also happens in production number 9. So, let us take production number 4 E going to let I D equal to E 2 in E 3. So, the symbol table which was available to E 1 is made available to the expression E 2. So, that means E 2 dot sim tab is equal to E 1 dot sim tab. It is also made available to E 3. So, the symbol table of E 1 is made available to E 3 also, but it so happens that this particular association of I D and E 2 dot value now actually is introduced into the symbol table produced by E 1. So, that is written as E 3 dot sim tab is equal to E 1 dot sim tab over written by the entity or the association I D dot name going to E 2 dot val. So, the consequence of this is two fold. For example, if the name I D dot name already you know is available in E 1. So, there is another association of that particular name to some other value then that particular association is forgotten and a new association of I D to E 2 dot val is produced. So, the old association is suppressed and the new association rules within the expression E 3. I said carefully suppressed and not deleted, because as soon as the scope of this particular and expression E let I D going to let I D equal to E 2 in E 3 is completed the old value of the association from the name I D to some other value that was present in E 1 should be made available again. So, in this case for example, a equal to 5 is suppressed in a star 7 by the association of a to 6, but as soon as this expression is completed the value of a going a to 5 is available in this particular expression or the variable a. So, this operator is known as the overriding operator and not a not the deletion operator. T 1 going to T 2 star f is very similar to even going to E 2 plus D and does not require too much you know elaboration. The symbol table is just copied T 2 dot sim tab and f dot sim tab or T 1 dot sim tab and T 1 dot val is T 2 dot val star f dot val T 2 f is similar to E 2 t. So, and there is just a copy of the attributes f going to parenthesis E parenthesis also has just a copy of the attributes here f 2 number says f dot val equal to number dot val and now f 2 I D. Again here is the usage of the expression variable. So, for example, in this a star 7 a is a usage of this particular definition and this a is a usage of this particular definition. So, that definition you know that usage would have been produced by f 2 I D. So, f dot val is obtained by looking up the name I D dot name in f dot sim tab which is a similar f dot sim tab is available here as an inherited attribute and I D dot name is available as a synthesized attribute of the terminal. So, we look up this name in the symbol table and produce value which is assigned to f dot val. So, this looking up operation is indicated by the two brackets and the name inside. So, here is the is an example showing you how the flow of attributes takes place. The expression is let a equal to 4 in a plus 3. So, a equal to 4. So, a plus 3 becomes 7. So, that is the value which is produced here that is correct. So, now s going to e. So, it initializes the symbol table to phi. So, that is why the symbol table is shown as phi here. The notation used is slightly different here the inherited attribute has its arrow to the left side of the value and the similarly the synthesized attribute has up arrow left to the left side of the value. Both notations are used when the rules are written writing it the way I showed you before is usual practice and when we write it along with the non-terminal this is the usual practice. So, this phi now is available in the non-terminal e and at this point we have applied the production e going to let i d equal to number or rather expression in parenthesis etcetera etcetera. So, the production used is let i d equal to e in e at this point and as the rules indicate the symbol table phi is passed on to this e which is the second one and the third e as well. So, this is the third e for both of them the phi is made available this phi is copied and sent downwards to t and f as we as indicated here and this name a is synthesized from here and is made available to this particular e. So, i d the a that I have talked about here is nothing but the i d here. So, this i d to e association is made available to this e. So, let us see how that happens the number 4. So, a equal to 4 is the expression. So, this e is nothing but just 4. So, that goes upwards to f and then t and then e as synthesized attribute and that e is made available to this particular e. So, a to 4 is the mapping or the association which is produced and that overrides whatever association was present in phi for the same name a well in this case phi indicates nothing. So, a to 4 is the only association now in the inherited attribute as a symbol table of this particular instance of e. Remember that we have not yet produced this value 7. So, this a to 4 is the new symbol table and that is made available to this e and this t because the production that we have applied is e going to e plus t at this point in the symbol in the syntax tree. So, this flows downwards from e to t and t to f and here is the usage of a. So, in this production in this expression e. So, we must look up the name the name the name a in the symbol table and that is what we have done. So, the symbol table is a to 4 when we look up the name a we get the value 4 here and that value 4 is passed on to the next level as the value of f. So, this goes upwards as the value of t and this value is passed on as the value of e. Similarly, f to number produces 3 which is passed upwards and the rule the associated with e to e plus t combines 4 and 3 in a summation to produce the value 7 which is passed upwards as the synthesized attribute of e and that in turn goes to s. So, this e as I told you is the this particular e. So, that is the value which has produced 7 really which is produced as 7 and that is the value which is sent upwards as the value of the entire expression. So, this is how the attribute evaluation happens the sequence in which it happens we will see in a while. So, before we look at the sequence of attribute evaluations for the examples that I showed you let us look at a classification of attribute grammars called L attributed grammars. And S attributed grammars. So, if you have only synthesized attributes in an attribute grammar then it is called as an S attributed grammar. So, it is a very simple definition there are no inherited attributes at all and in the case of such an SAG any bottom up evaluation order over a parse tree can be used to evaluate the attributes and obviously a single parse over the tree is sufficient. And it so happens that attribute evaluation can be combined with L R parsing as well because L R parsing produces one bottom up I know does a bottom up parse over the parse tree rather the construction of the parse tree itself is done in a bottom up fashion. It is quite you know convenient to do the attribute evaluation also in a combined fashion along with L R parsing. So, Yacht permits only synthesized attributes and the roles that we write for Yacht you know Yacht productions will be executed as we go up in the L R production L R parsing process. I will show you some examples of this very soon what about L attributed grammars L attributed grammars the how their attribute dependencies go from left to right. So, that very precisely so each attribute must be synthesized in which case there is no problem at all or it could be inherited and if it is inherited then there is a limitation. Suppose the production is so now we formalize what is known as a left to right dependence suppose the production is a going to x 1 x 2 etcetera x n and let the attribute x i dot a be an inherited attribute of the symbol x i. So, if x i dot a is the attribute it may use only the inherited attributes of the non terminal a the left hand side or the elements of a i of x k which means the inherited attributes of the symbols to the left of i or the synthesized attributes of s k k going from 1 to i minus 1. So, when we are looking at position i 1 to i minus 1 simply means the symbols to the left side of that particular non terminal x i. So, we can use the inherited attribute or the synthesized attributes of the symbols to the left of x i so x 1 to x i minus 1 only. So, that is what we mean by the statement the dependencies go from left to right. So, we will not be able to use any attribute of a symbol which is to the right of x i. So, x i plus 1 cannot supply any attributes to x i if it does then it does not become l attributed. The advantage of l attributed grammars is that we can make a series of left to right evaluations over the parse tree and evaluate all the attributes. If we further restrict the attribute evaluation such that it can be done in one parse over the parse tree from left to right then it is called as l a g 1 or one parse l a g. So, one parse l a g is the attribute evaluation can be very easily done with l l or you know recursive descent parsers. For s a g it can be done with l r l l r r d parsing any of them, but of course, if there are inherited attributes in general we cannot use l r parsing to parse to go evaluate the attributes. So, how does one do attribute evaluation in the case of l attributed grammars or l a g's. So, let us assume that the parse tree t with un evaluated attribute instances is given to us and we are supposed to produce a parse tree t with consistent attribute values after the evaluation process. So, basically we do a depth first search or depth first visit on the parse tree. So, d f visit with the root n is the beginning point for each child m of n from left to right do evaluate the inherited attributes of m then do a d f visit on m. So, first the on entry to a node we evaluate the inherited attributes then do d f visit which in turn goes downwards in the tree and after we complete the evaluation of all the dependence of this m, we evaluate the synthesized attributes of n and then return to the higher level. So, first inherited then visit the node and then compute the synthesized attributed the node and then return. So, this is the general procedure. So, let us apply that procedure to our examples here. So, this was the declaration example. So, we have you know this grammar is of course, L attributed. So, let us make sure of that first. So, here L dot type equal to t dot type. So, L dot type uses only the synthesized attribute of t here of course, we have only synthesized attribute. So, there is no violation of any rule the same is true for t to float as well L to I d we have I d dot type equal to L dot type. So, I d dot type is an inherited attribute which uses the inherited attribute of L. So, no violation L 1 going to L 2 dot I d. So, we L 2 dot type uses the parents attribute and I d dot type also uses parents attribute. So, no violation and I d dot name is a synthesized attribute identified dot name is also synthesized. So, there is no violation of any rule. So, this is a perfectly valid L attributed grammar here is the evaluation order. So, if we do a depth first search on this tree we begin at the root. So, the green ones are all visits for the first time and the orange ones are all visits of the same node a second time. So, that a total of 25 you know visits are required. So, and the d f visit evaluation order is 1 2 etcetera up to 25. So, that means, we visit 1 then 2 then 3 and then 4 5's then you know 6 here then 7 8 9 10 11 12 13 then we come you know to the semicolon even though we do not have anything to do we just have to visit it. Then 15 16 then 17 this is 18 this is 19 20 21 22 23 24 and 20 20 20 20 20 20 25. So, when we do this let us see how the attribute evaluation takes place. So, this is the first rule d going to T L at which an inherited attribute will have to be evaluated, but you know we really cannot L dot type equal to T dot type cannot be evaluated immediately. So, what we really do is we go down to 4 and then 5 and when we come back to 4 in the that is the visit number 6 we can compute the synthesizer attribute of T then we are ready to compute the inherited attribute of this particular L. So, that is possible and then we go down again pass it down to I D this identifier is then passed on to this. So, that is the end of this particular evaluation in this subtree the evaluation happens in this subtree as well in a similar way. So, basically we must do a d f visit and at each node if the dependencies are satisfied we will be able to apply a production rather a computation rule evaluate that particular attribute and then go further. So, here as we go on. So, this the inherited attribute of the d is nothing whereas, for T there is no inherited attribute. So, if we go down and there is a synthesizer attribute which can be evaluated. So, on the return path then just before visit to L we can you know evaluate the inherited attribute of L and then we go down to I D nothing to do as you know and the inherited attribute of I D can be evaluated and when we return from here the synthesizer attribute of I D can be evaluated as well. So, this is how the attribute evaluations happen. Thank you very much we will continue the lecture in the next part. Thank you.