 Welcome to part 4 of the lecture on semantic analysis with attribute grammars. We have already looked at a few examples of attribute grammars and attributed translation grammars. Today, we will continue with the semantic analysis part. So, to do a brief recap this was the grammar which I showed you last time and this is the grammar on which we are going to do some semantic analysis. So, this is the, this is a you know a shortened grammar for declarations similar to the you know the declarations in the language C. So, we have a D list which produces several declarations and then each declaration of these of the form type followed by a list of identifiers. We allow only two types, intent float and then in the list of identifiers, we allow either arrays or normal single identifiers. So, arrays would you know single identifiers would be just id whereas, arrays would have two formats id followed by time list in brackets or id followed by b r time list which produces again the you know several dimensions of the array each enclosed in a different set of brackets whereas, here only the dimensions will be you know listed here separated by a comma. The here are a couple of important observations. The first one is the grammar I showed you is not l l 1. So, modifications are needed to make it l l 1 and therefore, we assume that the parse tree is available and the attribute evaluation is performed over the parse tree. So, we already know how to do this. The productions you know 1 to 4 we have already dealt with this in detail in the previous lectures. So, we are not going to repeat that part here. We are going to concentrate on these productions only. So, constant declarations are similar to variable declarations and we need to enter the variable you know the identifier associated with the constant into the symbol table along with a you know tag that it is a constant, but otherwise searching the symbol table etcetera is all very similar. So, I showed you the identifier type information record last time. So, we have for each identifier we need to store its name its type the element type and you know a pointer to the list of dimensions. So, the type can be either simple or array whereas, the element type is either integer or real of course, error type is to take care of semantic analysis. So, the time list pointer points to a list of ranges of the dimensions of an array for example, if we have a float my array 5 12 15 then there is a list of items 5 comma 12 comma 15 and that is pointed to by time list pointer. This will be required later for you know to get the number of elements in the array etcetera. So, let us get started with production number 1 this is a LATG and that is L attributed translation grammar. So, therefore, the actions have to appear you know inbuilt into the production itself. So, the actions before array the non-terminal array actually copy the inherited attribute L 1 dot type to the inherited attribute id array dot type. So, this is the initialization of the inherited attribute and then we have the id array non-terminal for which the appropriate procedure would be called. And then we have initialization of the inherited attribute L 2 dot type with the value L 1 dot type and then the procedure L 2 for L 2 is called. So, this is quite straight forward what we really do here is take the type information and pass it on to all the elements in the list of identifiers produced by L. So, if we have L 2 id array alone then you know we just pass id array dot type equal to L dot type. Now, so we have you know the type information actually is synthesized in this you know t to int or t to float and the type information has to be passed from this t into L. So, this is how it works. So, let us keep remember that and then go further. So, I now at this point L 1 dot type already has the type information this was given to the production you know the non-terminal L by the production d going to t L. This is what we have seen before. So, id array now produces id. So, this is the production that we have used. So, what do we do now? The semantic analysis is actually quite simple in this case we such the symbol table. So, such sim tab is the function which does it. It takes the name of the identifier and a variable you know which is returned as found. So, found is either true or false depending on whether the name is found in the symbol table or not. If the name is already found that means the identifier has already been declared. So, there is an error message which is printed out by the compiler identifier already declared. If the name is not found in the symbol table then it is time to insert it into the symbol table. So, what is the type of this particular name? It is actually id array you know dot type. So, we have to introduce the type information into the id information record and then insert it into the symbol table. So, for that purpose we declare t as a temporary name type r x star t. So, t pointer type is set as simple because we have a single identifier here and then t pointer elli type is made as id array dot type whatever is obtained from the above and insert some sim tab inserts this name with this particular type information into the symbol table. So, what if id array produces an array name? So, the name of the array is id and time list is the you know list of dimensions of the array. So, the first part is very similar you search the symbol table and if it is already found the name is already found then an error message is issued. If the name is not found in the symbol table we introduce it into the symbol table. So, let us see how to fill up the information into the identifier record. So, we have the same type r x star t a temporary t pointer type is now set as array. In the previous case it was set as simple now it is set as array the element type is same as id array dot type which is obtained from the top. So, if you have int my array 15 20 25 then int would be the type of the array element which is obtained from the top. Then t pointer time list pointer is set as time list dot p t r. So, we will see how to synthesize time list dot p t r in the productions below. So, after this the symbol table call is made to insert the name with this type information into the symbol table. So, if you observe even though this is an array t g in most of the cases the actions appear at the end this is perfectly because actions can appear in the middle or at the end in the case of fill a t g depending on the requirement for you know computing the inherited attributes. In the productions here we had actions in between also, but for the productions here we have an action only at the end of this particular production. Ideally we could have actually broken this production into two. So, id array goes to some you know non-terminal say a followed by another non-terminal b. The non-terminal a goes to id and then you introduce the search and if found in that case. So, this is possible, but you know and it would probably issue an error message which is close to id, but otherwise since the dimension list is usually quite small it is not a list of expressions, but it is only a list of constants producing an error message after the entire array declaration is over is still not too bad. So, this is then the production becomes straight forward it need not be broken into two and that is the approach we have taken in this production. Let us see how the dimension list produces various ranges of the arrays. So, one possibility is time list goes to num that means the production terminates the list of dimensions. The second one is time list going to num comma time list that means we are producing a list of dimensions 1, 2, 3 etcetera for the last one we use the production time list going to num. So, in this case time list dot pointer is a list made out of num dot value. So, make list makes a list out of the argument. So, num dot value is the range of the first that particular dimension not the first, but that particular dimension. So, time list goes to num comma time list. So, we have produced you know a dimension and rest of the dimension will be produced from time list 2 here. So, there is already a time list pointer available for time list we have a new dimension which is produced here. So, time list 1 dot p t r will be set as a combination of num dot value that is this particular value and time list 2 dot pointer. So, this value is appended to this particular dimension. So, this way we produce you know, but the order is important we must retain this first and then this. So, the concatenation should not be such that num dot value appears at the end of the dimension list 1 dot pointer it must actually appear right in the beginning. So, the order is very important because these are the various ranges of the array. So, that is about this was about producing the you know rather analyzing the variables and their declarations the error messages given were quite simple the most of the error messages would actually be in the usage part, but before doing that we have to look at a very important computation called as the storage offset computation for variables which is actually related to the declarations and the list of declarations to be very specific. So, let us see what these are the compiler is supposed to compute the offset that which the variables and constants will be stored in the activation record on the stack. So, as you perhaps know when the program starts running activation records for various procedures are created on the stack and each activation record holds the data of that particular function or procedure. So, there is a stack pointer and there is a pointer pointing to the most recent activation record. So, we need to compute the offsets at which variables and constants will be stored in these activation records for that particular procedure. The offsets will always you know start with 0, but then depending on the specific format of the activation record the code generator has to add a particular extra value in order to produce correct addresses. These offsets will be with respect to the pointer pointing to the beginning of the activation record. So, that is what I said they will all be with respect to the for starting from 0 variables are usually stored in the activation record in the declaration order. So, if we have a declaration float c into d 10 float e 5 comma 15 and then int a comma b the order in which the variables will be allocated space on the activation record would be first c and then d then e then a then b. So, this is how they would be assigned locations in the activation record offsets can be easily computed while performing semantic analysis of declarations. So, the basic idea is you know described here. So, assuming that this is our declaration the offsets for the first variable is always 0. So, the offsets for c is 0 assuming that int takes 4 bytes and float takes 8 bytes. So, we add 8 to the offset of c. So, that we get 8 and that would be the offset for d. So, d gets an offset of 8 and the 10 elements of d which occupy 10 into 4 40 bytes would stretch from 8 onwards the offset 8 onwards. So, now because d takes 40 bytes and we have an initial you know offset of 8 for d the offset for e will obviously be 40 plus 8 that is 48 and now e has you know 75 elements 15 into 5 each of which takes 8 bytes. So, that would be 600 and we add 600 you know to this 48. So, we get 648 as the this you know offset for e sorry. So, because e takes e has an offset of 48 and e itself has a size of 600 bytes. So, a gets 648 as its offset add 4 to it and you get the offset for b that is 652. So, this is precisely the computation that we need to do whenever we have a list of declarations we keep accumulating the offset and then add it to the next declaration in order to get the new offset. Let us see how it has done for our declaration you know grammar. We have a an inherited attribute called d list dot in offset which is initialized to 0 at the top because as I said all offsets actually are with start with the value 0 and then we increment them. So, in this production d list going to d we transfer the offset d list dot in offset to d dot in offset because this is just one declaration and then call the procedure for d. In the production d list going to d semicolon d list the offset from here is transferred to d. So, d dot in offset is equal to d list 1 dot in offset and then the declaration d has certain variables with various sizes. So, the total size is added to the incoming offset and that would be the outgoing offset of d and that is transferred to d list 2. So, after d we have d list 2 dot in offset equal to d dot out offset. So, d produces the new offset which can be assigned to d list 2 and then d list 2 is called. So, this is a standard strategy we always produce you know we take an in offset add the size of variables that our declaration produces and then transfer that as the out offset. So, d going to t l t produces its size. So, t dot size and l gets its in offset through d. So, l dot in offset equal to d dot in offset then the type size of l is t dot size. So, the list of identifiers produced by l you know will have this l will have an inherited attribute called type sort type l dot type size which is nothing but t dot size. Now l produces identifiers. So, it adds its you know variable list size to the in offset and that would become d dot out offset. So, t dot size is set to either 4 or 8 depending on the variable type being integer or float. So, let us look at a example in part. So, we start with declaration which goes to d list and dollar this goes to d semicolon d list. So, we have a 0 initialization here which is passed on to this d and then the t here gets its size from float and the 0 is passed on to l. So, you can see that l has in offset type size and out offset in that order. So, 0 is you know given to l as in offset and that is passed on to the next levels that we are going to see very soon. So, let us consider this part of the example later. So, again here the incoming offset is 8 which is given by d list and how did it get its incoming offset as 8 it actually got it from d you know. So, d produced 8 as its out offset and that was pushed into d list as its in offset that you can see here. So, d list dot in offset is d dot out offset and that is sent to this d which again in turn goes about sending it to this l and l sends it to id r a. This d list produces a d which again goes to t and l. So, this l gets its 48 and then it takes it further. So, this is how the inherited attributes you know start from the top and flow to the non terminals below. So, let us continue our example. So, l going to id r a and l going to id r a comma l 2 these are the two which produce the list of declarations with a certain type t. So, the l dot type size you know will already be available. So, id r a dot type size is initialized to l dot type size id r a dot in offset is initialized to l dot in offset and then id r a is called finally, l dot offset out offset is the synthesized attribute of l that is made as id r a dot out offset. So, whatever id r a produces is transferred to l what about this case again the inherited attributes of l 1 some of them are transferred to id r a and l 2 for example, type size id r a dot type size is also l 1 dot type size and l 2 dot type size is also l 1 dot type size. Whereas, the inherited attribute in offset is copied from l 1. So, id r a dot in offset is l 1 dot in offset and then id r a is called. So, the out offset of id r 2 goes in as in offset of l 2 that we saw already here. So, l 2 dot in offset equal to id r a dot out offset and then l 2 is called and l 1 dot out offset is made as l 2 dot out offset. Id r a itself produces either an id or you know an array. So, when it produces a simple table id what we really do is insert the offset into the symbol table for the corresponding name id dot name and the offset being id r a dot in offset. So, you we must remember here that the semantic analysis part which deals with error messages etcetera appear before this. So, this part is executed only if there are no other error messages given out. Id r a dot out offset you know the this name has a certain size and what is that size id r a dot in offset plus id r a dot sorry id r a dot type size is the size of this particular name. So, what is the out offset it is the sum of in offset plus type size that is assigned to out offset. So, let us see how the example proceeds here as well. So, from l the 0 is transferred to id r a and then the name is transferred from id to id r a it is introduced into the symbol table and since this id is of type the float the size 8 of the float actually gets inherited into l it comes down to id r a and then it combines with the in offset and produces an out offset of it which is passed on to l which is in turn passed on to d and that in turn goes into d list. So, d list passes it on to this d which is actually 8 here right. So, this is the size of the int which is 4 that is inherited into l. So, 8 and 4 are both inherited into l which are passed on to id r a. So, this we are going to see in the next slide. So, here d list produce you know passes the in offset 48 into d which is again passed to l this 4 is also passed into l. So, this l produces id r a comma l. So, id r a gets in half set of 48 and type size of 4 with the combination of these two it associates inserts the name i a into symbol table and it is out offset is 52 48 plus 4 and that would be an you know the inherited attribute of this l. So, 52 comes here and then this id r a gets 52 it also gets 4 which is nothing but the type size here and at this point this name gets the in offset 52 its size is 4. So, the out offset is 52 which is synthesized and produce sent to l which in turn traverses all the way up to this point. So, let us see how the arrays are processed. So, id r a produces id followed by a dimension list the id is inserted into the symbol table in rather the offset is inserted into the symbol table with id r a dot in offset as the incoming offset. Then the dimension list is passed and now we produce id r a dot out offset as id r a dot in offset or I know rather. So, whatever is coming in into id r a is the in offset. Then we consider the id r a dot type size and finally, multiply it by dimension list dot num. So, what is dimension list dot num that is actually the number of elements in the array. So, that when multiplied by the elements size of the array which is invites gives you the total size of the array and that is added to the incoming offset and that would produce the outgoing offset id r a dot offset out offset. Dimension list either is a single number or a sequence of numbers separated by a comma. So, if when it is a symbol number we just have dimension list dot num equal to num dot value. When it is a sequence of numbers dimension list 1 dot num that is a synthesized attribute is dimension list 2 dot num multiplied by num dot value because this is the earlier dimension. So, if we have a 10 comma 15. So, each element of the first dimension is of the size 15. So, 10 into 15 is the size of the array and that is what we are doing here. This is the rest of the array this is the foremost dimension of the array as far as this list is concerned. So, we take the dimension list 2 dot num multiplied by the num value and that gives us the total size of the array. So, far as as far as this particular list is concerned this processing the same array declaration with a different format is very similar to these two nothing very special. So, we are going to skip that part. So, we looked at the example up to this point. So, the incoming offset is 8 the type of the array is 4 bytes in size. So, here we have a single dimensional array. So, dimension list 10 produces sorry num 10 produces dimension list 10. So, number of elements is 10 here and that multiplied by 4 gives us 40 added to 8 the incoming offset gives us 48 that is the outgoing offset of ID array which traverses all the way up to this point and goes down as I already discussed. So, now we come to the semantic analysis of statements and expressions. So, let us see how this is done. So, we have seen declarations so far. Now, let us look at the body of the code. A couple of assumptions here we are assuming that there are no scopes for the names. In other words there are no blocks within the statements. So, declarations you know processing declarations within blocks etcetera is a little more complicated and we will do it a little later. For the present we are assuming that there is only one block and that block has declarations earlier which we have already seen and then there are statements of this kind. For example, there is an if then statement and also an if then else statement. There is a while do statement, there is an assignment statement and the left hand side of the assignment can be just a simple ID or an array element. The dimensions of the array are produced using elist going to e or elist comma e. The expressions are of various types. So, e plus e e minus e e star e e slash e minus e parenthesis expression then l and num. So, these are the various possibilities for expressions. So, remember there is no e going to id, but l produces id that is a very important observation necessary otherwise we will assume that there is no id that is wrong actually. And e can also be of you know Boolean expression type. So, these are all arithmetic expressions and these are all Boolean expressions e or e e and e and not e and we also have expressions which compare other expressions. So, e less than e e greater than e or e equal to e. There are many other possibilities for relational operators, but processing them will be very similar. So, let us consider the very basic relational operators and then it does not matter what others are the processing will be similar. A very important observation here is that we cannot separate the arithmetic expressions from either the Boolean expressions or the relational expressions. So, this is necessary for writing a proper grammar and the contextually grammars cannot differentiate between arithmetic expressions, Boolean expressions and relational expressions. So, it really permits all types of identifiers in each of these places for example, it will permit r a minus r a which is not permitted in c and here it may actually put characters or arrays again. And here also it can put you know any type of operands including arrays which are not permitted. So, the semantic analysis routine must look at the two operands look at the operator and then decide whether the you know the arithmetic operation should be allowed at all or an error message should be given. The same has to be done for the Boolean operators and the relational operators. Again this grammar is ambiguous. So, we will assume that modifications to make it l a l r 1 are possible and that would be left for exercises. The parse tree is assumed to be available and we will assume that the tribute evaluation is performed over the parse tree. We will also skip rules for similar productions. For example, we will handle e going to e plus e and skip these that is because they are you know very similar. Similarly, we will handle e going to e or e and skip the other two. We will handle e less than e and skip the other two. So, this is an SATG you know. So, we already saw an example of l a TG for declarations. So, let us see how synthesized attributes are used in type checking for statements. So, all the attributes are synthesized and therefore, we will drop the arrow symbol because it is just extra every attribute will have this. So, we are going to skip it. What are the various attributes? Expression and l and num have an attribute called type. The type can be either integer or real or Boolean or error type. So, error type is new. So, this is indicative of error. Boolean type is possible only during you know the parsing of expressions. So, it cannot be specified by the programmer. Integer and real are the only ones which are specified by the programmer. Num will also obviously have value as an attribute which is of no use to us right now, but it will be useful later. E list will have a diamond num as an attribute which is of type the integer. So, number of you know the dimensions and things like that. So, let us consider the expressions one by one sorry the productions one by one s going to if x then s. This particular production actually would appear as if e then s otherwise if e then s else s and so on right. We have chosen to break the production into two. The first part is if x then s. The second part is if x going to if e. Why did we really do it? I hinted at something like this for you know array you know generation of variables which are arrays declarations which are arrays. The reason is suppose we had a single production s going to if e then s and we attach a semantic action right here saying if e dot type is not Boolean then error Boolean expression expected. It is possible that the expression e itself is very large and it is also possible that s is a fairly large body of statements. So, if we attach the you know the semantic check and the error is actually given out it would be actually given probably half a page later because the entire this entire if e then s would be parsed and only then the semantic action would have been executed. So, whereas here the as soon as if and e are parsed this semantic action gets executed. So, as soon as it finds that the expression parsed is not a Boolean expression the error message is printed out at the right place instead of providing the error message after s is also completely parsed. So, this is all you know that is necessary while doing semantic analysis you must make sure that the error message is actually given where it is relevant. The situation is similar here also we could have said s going to while e do s, but then if s is a very large body of statements this error would have been issued after half a page or one page of code. So, we break the production into two parts s going to while x do s and the other one being while x going to while e and the semantic check is the same if e dot type is not Boolean then Boolean expression expected error message is issued. If there is no error message then the semantic analysis continues. So, it is possible to do this type of attribute computation during L R parsing provided the grammar is not you know ambiguous. Let us take a simple assignment L equal to e. So, now L would be a large expression e would be similarly a fairly large expression there could have been a semantic error in the midst of any of these expressions. So, it is possible that L dot type has become error type and it is also possible that e dot type has become error type one or both have become error type in the process of analyzing both L and e. So, if they are error types then you know the there is nothing to do really because L dot type is error type and e dot type is error type. So, there is nothing to do no more error messages need to be given out, but suppose both are perfectly you know honorable types and they are not of type error type. Then we check whether it is possible to convert L dot type to e dot type or vice versa why should we do this the point is L is the left hand side of an assignment e is the right hand side of the assignment. So, if e is of type say integer and L is of type float then we must convert int into float and then generate code for the assignment. So, the coercible function checks whether such coercions in type are permitted or possible etcetera. So, if it cannot be converted then there is an error message given out type mismatch of operands in assignment statement. So, what does the function coercible do it takes two operands it takes two operands type a and type b checks is type a integer or is it real. So, if it is one of these then it is a valid type is type b integer or is it real if it is one of these then that is also a valid type otherwise they would be error type. So, if it is one these two are satisfied together that is why the and return success by returning a one otherwise return a zero. So, not coercible implies zero. So, coercible implies one. So, in the in case the coercible function returns a zero we print out an error message again I am showing you the type information record just to show you that the number of elements in the array you know rather the number of dimensions of the array will have to be considered when we do semantic analysis that is what we are going to do next. E going to num. So, E dot type is num dot type there is nothing much to do here whatever is the type of this number either integer or by mistake a float or a character etcetera the type of that particular number is transferred as E dot type what about L going to I D. So, if there is an identifier it is always necessary to search that symbol table find out whether the name exists in the symbol table if it exists then we need to extract the type information of that particular identifier. So, for that purpose we have type x star t this is the same temporary type information record search the symbol table with the name of the identifier the flag missing and the temporary t. So, if the flag missing is true then the name is not present in the symbol table and if the flag missing is false then the name is indeed present in the symbol table. So, if missing is true we issue an error message identifier not declared and assign the type as L dot type equal to error type. So, this is how L would get the type information during its parsing and semantic analysis phase. Else if t pointer type is array. So, we have really got the symbol table you know rather the name and its information from the symbol table. So, we know the type of that particular identifier here this name does not have any array subscripts following it. So, it must be a simple name. So, if t dot type is array give an error message cannot assign whole arrays. So, we do not permit that in this particular small grammar. So, it is possible to permit assignment of one array to another array or parts of arrays to another array and so on. But our language does not permit it at this point if we want to permit such extra behavior the semantic analysis has to be modified appropriately. So, if the identifier is of type array we assign error type because there is an error it has to be of simple type. Otherwise L dot type is t pointer L E type. So, L E type would be either integer or real and that is what is really obtained here. So, t dot type is already you know L dot type is a t dot type is already simple. So, L dot type would be t pointer L E type. Now, we come to the semantic analysis of arrays. So, we have L going to identifier followed by an expression list. We assume that you know we do not assign arrays to arrays, but we can only assign to an element of the array or assign an element of the array to some other variable. So, this E list covers all the dimensions of the name I D type rec star t as usual. We declare a type rec you know a record of type type rec and a pointer to it such the symbol table for I D dot name missing n t as before. If missing issue an error message and make L dot type error type and there ends the matter. Otherwise in this case this I D is supposed to be of type array. So, is t pointer type not equal to array if it is not then there is an error. So, identifier not of array type in the previous case we showed an error message if it was an array because this was supposed to be an ordinary identifier. So, if it is indeed an array we find the dimensions of the array. The parameters of this find dime function are t pointer dym list pointer. So, we are pointing to the dym list pointer of the name and the number of dimensions is provided in dym num. So, if dym num that is obtained from the symbol table is not equal to the number of expressions present in E list dot dym num that is here whatever subscripts are available here we count them and make them available as E list dot dym num. If these two do not match it is possible that we have written less number of subscripts here or it is also possible that we have written more number of subscripts here than necessary. So, in both cases there is a mismatch in array declaration induce check index list that is the message which is printed out L dot type in that case becomes error type. So, if everything is then we assign L dot type as t pointer L E type again L E type would be integer or real. So, how do we count the dimensions? E list going to E. So, R E list going to E list comma E. So, in the case of E list going to E if E dot type is not integer subscripts how to evaluate to integer type they cannot become floats. Then the error message illegal subscript type is provided and E list dot dym num becomes 1. The reason is whenever when we use this this would actually be the first dimension or first subscript of the array subscription list because E list 2 let us say there are there is a three dimensional array we would use E list going to E list comma E once. Then this E list would be again expanded as E list comma E and the third E list would be actually expanded to E. So, whenever we use this particular production we would be looking at the first dimension of the array. So, that is why it is ok to say dym num equal to 1 at this point. Here this would have actually seen a certain number of dimensions and this is adding another dimension here. So, E list 1 dot dym num equal to E list 2 dot dym num plus 1 is the correct estimate of the number of dimensions seen. So, far and obviously E dot type equal to integer is essential otherwise an error message is given out. What we should observe further here is even if one of the dimensions creates an error the semantic analyzer does not stop it actually goes to the next dimension and continues the semantic analysis. This is a very desirable feature because the parser or the compiler is supposed to give out as many errors as possible error messages as possible in one parser of the entire program. So, if we catch just one error and then ask the programmer to repair it and run it you know run it through the compiler all over again to get the next error possibly nobody would use our compiler. So, now we go to a very important part of this semantic analysis you know semantic analysis of expressions this plus is indicative as I said it can be replaced by minus star slash etcetera. So, in the case of expressions of this type these are arithmetic expressions and in the following slides we will look at Boolean and relational expressions. So, if E 2 dot type is not equal to error type and E 3 dot type not equal to error type only then we do this part of it otherwise we simply set even dot type as error type and get out because what is the point in doing semantic analysis further if either one or both of these is errors erroneous type. So, assuming that both are valid types and not error types next we check whether E 2 dot type and E 3 dot type you know are compatible with each other they can be converted from one to another. So, let us see what that means. So, the coerc sorry coercable we already saw compatible earth up is what we need to see later coercable simply says if E 2 dot types here yes coercable simply says if the first one is either integer or real and the second one is either integer or real then return 1 L 0. So, in this case it is possible to keep the operands as both real one of them real and other one as you know this can be int and this can be real or this can be int and this can be real both can be int both can be real these are the four possibilities. So, among these int plus int is easy to handle real plus real is also easy to handle if one is int and the other is real we can still handle it nothing wrong with it and similarly if this is int and this is real we can still handle this expression and there is nothing wrong with it. So, that is what this coercable checks if the coercable part of it is false we also of course need to check whether the arithmetic operator and the two operands are compatible with each other let us see what this compatible earth top is compatible earth top says take two types if type A is integer or type A is real similarly type B is integer or type B is real return 1 else 0. So, we actually permit other possibilities here you know, but our explanation at this point of time does not contain them. So, we may say look in see for example, this could become a string you know. So, this could become a string rather a single character this could also become an integer. So, both these can still be added because characters can be converted to integer. So, all these possibilities exist in the real language see, but in our language we assume that the only four possibilities permitted are int int, int real, real int and real real. So, these are all compatible with the plus operation. So, compatible earth top checks for these in fact these two are very similar the only reason we have said separately is the bigger compiler can actually expand the you know coercible and compatible earth top functions to add many possibilities. So, in general keeping them separate shows the structure of the compiler a little more clearly. So, if it is neither coercible nor compatible earth top then a type mismatch in expression has happened and an error message is issued. So, even dot type in that case becomes error type. So, otherwise if everything is even dot type becomes compare types e 2 dot type comma e 3 dot type. So, what does that really do? So, this takes the two types you know if they are depending on whether they are integer the other one being type b being integer then it returns integer if one of them both of them are real then it returns real if one is integer the other one is real it returns real and similarly first one is real and second one is integer then also it returns real. The reason is simple if you are adding a floating point number and an integer the integer needs to be converted to floating point because the range of floating point numbers is much larger than that of integers. So, that is what is done here next is the Boolean operation and finally the relational operator. This is fairly strength powered if neither are error type then we go ahead otherwise we set even dot type as error type e 2 dot type can be Boolean e 3 dot type can be integer or the other possibility is e 3 dot type is Boolean e 3 dot type is integer. So, if both these hold then even dot type would be Boolean. So, in other words we do not mind having int or Boolean Boolean or int int or int Boolean or Boolean all these four possibilities are permitted by this combination. So, if none of these are true then type mismatch in expression is given out and even dot type is set as error type. If these conditions hold then even dot type is set as Boolean and instead of or we could have used and also in this case. The last one is even going to e 2 less than e 3. So, in this case if they are not error types we check whether they are. So, in this case this less than is very similar to an arithmetic operator because we can only you know compare either Booleans or integers usually sorry or arithmetic numbers. So, these have to be numbers. So, we cannot have any other type here. So, that is why we check coercible and then compatible or if neither of them is true then we generate error message otherwise even dot type is termed as Boolean. This is the only difference between e 2 plus e 3 and e 2 less than e 3 finally even dot type really becomes Boolean. So, let us stop here and continue analysis of see in the next lecture. Thank you.