 
 
 
 
 
   
We consider the language generated by the following grammar.
| P  D; E | 
| D  D; D | 
| D    :  T | 
| T    | 
| T    | 
| T    [  ]  T | 
| T    T | 
| T  T  T | 
| E    | 
| E    | 
| E    | 
| E  E  E | 
| E  E[E] | 
| E    E | 
| E  E(E) | 
 is the token for identifiers.
 is the token for identifiers.
 [
[ ]
] T is an array type construct
      whereas E[E] refers to an element of an array.
T is an array type construct
      whereas E[E] refers to an element of an array.
 T is a pointer type construct 
      whereas
 T is a pointer type construct 
      whereas 
 E is a pointer dereference.
 E is a pointer dereference.
 T is a function type construct 
      whereas E(E) is a function call.
 T is a function type construct 
      whereas E(E) is a function call.
 E represents a remainder computation.
 E represents a remainder computation.
| Grammar symbol | Synthesized attribute | Inherited attribute | 
| E | E.type (type expression) | |
| T | T.type (type expression) | |
|  |  .entry    | |
|  |  .val    | |
|  |  .val character | 
| D    :  T | { addtype(id.entry, T.type) } | 
| T    | { T.type :=  } | 
| T    | { T.type :=  } | 
| T    [  ]  T1 | { T.type := array
(0 ...  .val - 1, T1.type) } | 
| T    T1 | { T.type := pointer(T1) } | 
| T  T1  T2 | { T.type := 
                                    
(T1.type  T2.type) | 
| D    :  T {addtype(  .entry, T.type)} | (1) | 
| E    | { E.type := |  } | 
| E    | { E.type := |  } | 
| E    | { E.type := | lookup(id.entry) } | 
| E  E1  E2 | { E.type := | if 
E1.type =  | 
| and 
E2.type =  | ||
| then  | ||
| else  } | ||
| E  E1[E2] | { E.type := | if 
E1.type =  | 
| and E2.type = array(n, T) | ||
| then T | ||
| else  } | ||
| E    E1 | { E.type := | if E1.type = pointer(T) | 
| then T | ||
| else  } | ||
| E  E1(E2) | { E.type := | if E2.type = S | 
| and 
E1.type = S  T | ||
| then T | ||
| else  } | 
 E2 which evaluates to boolean
      provided that E1 and E2 have the same basic type, otherwise evaluates to type_error.
  E2 which evaluates to boolean
      provided that E1 and E2 have the same basic type, otherwise evaluates to type_error.
| P  D; S | 
| D  D; D | 
| D    :  T | 
| T    | 
| T    | 
| T    | 
| T    [  ]  T | 
| T    T | 
| E    | 
| E    | 
| E    | 
| E  E  E | 
| E  E  E | 
| E  E[E] | 
| E    E | 
| S  S1; S2 | 
| S    := E | 
| S    E  S | 
| S    E  S | 
| S  S1; S2 | { S.type := | if S1.type = void | 
| and S2.type = void | ||
| then void | ||
| else  } | ||
| S    := E | { S.type := | if E.type =  .type | 
| then void | ||
| else  } | ||
| S    E  S1 | { S.type := | if E.type = boolean | 
| then S1.type | ||
| else  } | ||
| S    E  S1 | { S.type := | if E.type = boolean | 
| then S1.type | ||
| else  } | 
 
 
 
 
