1 static char *sccsid = "@(#)pi.c 1.2 (Berkeley) 01/22/82"; 2 #include <stdio.h> 3 #include <ctype.h> 4 #include "error.h" 5 6 extern char *currentfilename; 7 static char *c_linenumber; 8 static char *unk_hdr[] = {"In", "program", "???"}; 9 static char **c_header = &unk_hdr[0]; 10 11 /* 12 * Attempt to handle error messages produced by pi (and by pc) 13 * 14 * problem #1: There is no file name available when a file does not 15 * use a #include; this will have to be given to error 16 * in the command line. 17 * problem #2: pi doesn't always tell you what line number 18 * a error refers to; for example during the tree 19 * walk phase of code generation and error detection, 20 * an error can refer to "variable foo in procedure bletch" 21 * without giving a line number 22 * problem #3: line numbers, when available, are attached to 23 * the source line, along with the source line itself 24 * These line numbers must be extracted, and 25 * the source line thrown away. 26 * problem #4: Some error messages produce more than one line number 27 * on the same message. 28 * There are only two (I think): 29 * %s undefined on line%s 30 * %s improperly used on line%s 31 * here, the %s makes line plural or singular. 32 * 33 * Here are the error strings used in pi version 1.2 that can refer 34 * to a file name or line number: 35 * 36 * Multiply defined label in case, lines %d and %d 37 * Goto %s from line %d is into a structured statement 38 * End matched %s on line %d 39 * Inserted keyword end matching %s on line %d 40 * 41 * Here are the general pi patterns recognized: 42 * define piptr == -.*^-.* 43 * define msg = .* 44 * define digit = [0-9] 45 * definename = .* 46 * define date_format letter*3 letter*3 (digit | (digit digit)) 47 * (digit | (digit digit)):digit*2 digit*4 48 * 49 * {e,E} (piptr) (msg) Encounter an error during textual scan 50 * E {digit}* - (msg) Have an error message that refers to a new line 51 * E - msg Have an error message that refers to current 52 * function, program or procedure 53 * (date_format) (name): When switch compilation files 54 * ... (msg) When refer to the previous line 55 * 'In' ('procedure'|'function'|'program') (name): 56 * pi is now complaining about 2nd pass errors. 57 * 58 * Here is the output from a compilation 59 * 60 * 61 * 2 var i:integer; 62 * e --------------^--- Inserted ';' 63 * E 2 - All variables must be declared in one var part 64 * E 5 - Include filename must end in .i 65 * Mon Apr 21 15:56 1980 test.h: 66 * 2 begin 67 * e ------^--- Inserted ';' 68 * Mon Apr 21 16:06 1980 test.p: 69 * E 2 - Function type must be specified 70 * 6 procedure foo(var x:real); 71 * e ------^--- Inserted ';' 72 * In function bletch: 73 * E - No assignment to the function variable 74 * w - variable x is never used 75 * E 6 - foo is already defined in this block 76 * In procedure foo: 77 * w - variable x is neither used nor set 78 * 9 z : = 23; 79 * E --------------^--- Undefined variable 80 * 10 y = [1]; 81 * e ----------------^--- Inserted ':' 82 * 13 z := 345.; 83 * e -----------------------^--- Digits required after decimal point 84 * E 10 - Constant set involved in non set context 85 * E 11 - Type clash: real is incompatible with integer 86 * ... Type of expression clashed with type of variable in assignment 87 * E 12 - Parameter type not identical to type of var parameter x of foo 88 * In program mung: 89 * w - variable y is never used 90 * w - type foo is never used 91 * w - function bletch is never used 92 * E - z undefined on lines 9 13 93 */ 94 char *Months[] = { 95 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 96 "Jul", "Aug", "Sep", "Oct","Nov", "Dec", 97 0 98 }; 99 char *Days[] = { 100 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", 0 101 }; 102 char *Piroutines[] = { 103 "program", "function", "procedure", 0 104 }; 105 106 107 static boolean structured, multiple; 108 109 char *pi_Endmatched[] = {"End", "matched"}; 110 char *pi_Inserted[] = {"Inserted", "keyword", "end", "matching"}; 111 112 char *pi_multiple[] = {"Mutiply", "defined", "label", "in", "case,", "line"}; 113 char *pi_structured[] = {"is", "into", "a", "structured", "statement"}; 114 115 char *pi_und1[] = {"undefined", "on", "line"}; 116 char *pi_und2[] = {"undefined", "on", "lines"}; 117 char *pi_imp1[] = {"improperly", "used", "on", "line"}; 118 char *pi_imp2[] = {"improperly", "used", "on", "lines"}; 119 120 boolean alldigits(string) 121 reg char *string; 122 { 123 for (; *string && isdigit(*string); string++) 124 continue; 125 return(*string == '\0'); 126 } 127 boolean instringset(member, set) 128 char *member; 129 reg char **set; 130 { 131 for(; *set; set++){ 132 if (strcmp(*set, member) == 0) 133 return(TRUE); 134 } 135 return(FALSE); 136 } 137 138 boolean isdateformat(wordc, wordv) 139 int wordc; 140 char **wordv; 141 { 142 return( 143 (wordc == 5) 144 && (instringset(wordv[0], Days)) 145 && (instringset(wordv[1], Months)) 146 && (alldigits(wordv[2])) 147 && (alldigits(wordv[4])) ); 148 } 149 150 boolean piptr(string) 151 reg char *string; 152 { 153 if (*string != '-') 154 return(FALSE); 155 while (*string && *string == '-') 156 string++; 157 if (*string != '^') 158 return(FALSE); 159 string++; 160 while (*string && *string == '-') 161 string++; 162 return(*string == '\0'); 163 } 164 165 extern int wordc; 166 extern char **wordv; 167 168 Errorclass pi() 169 { 170 char **nwordv; 171 172 if ( ( strlen(wordv[1]) == 1) 173 && ( (wordv[1][0] == 'e') || (wordv[1][0] == 'E') ) 174 && ( piptr(wordv[2]) ) 175 ) { 176 boolean longpiptr = 0; 177 /* 178 * We have recognized a first pass error of the form: 179 * letter ------^---- message 180 * 181 * turn into an error message of the form: 182 * 183 * file line 'pascal errortype' letter \n |---- message 184 * or of the form: 185 * file line letter |---- message 186 * when there are strlen("(*[pi]") or more 187 * preceding '-' on the error pointer. 188 * 189 * Where the | is intended to be a down arrow, so that 190 * the pi error messages can be inserted above the 191 * line in error, instead of below. (All of the other 192 * langauges put thier messages before the source line, 193 * instead of after it as does pi.) 194 * 195 * where the pointer to the error has been truncated 196 * by 6 characters to account for the fact that 197 * the pointer points into a tab preceded input line. 198 */ 199 language = INPI; 200 (void)substitute(wordv[2], '^', '|'); 201 longpiptr = position(wordv[2],'|') > (6+8); 202 nwordv = wordvsplice(longpiptr ? 2 : 4, wordc, wordv+1); 203 nwordv[0] = strsave(currentfilename); 204 nwordv[1] = strsave(c_linenumber); 205 if (!longpiptr){ 206 nwordv[2] = "pascal errortype"; 207 nwordv[3] = wordv[1]; 208 nwordv[4] = strsave("%%%\n"); 209 if (strlen(nwordv[5]) > (8-2)) /* this is the pointer */ 210 nwordv[5] += (8-2); /* bump over 6 characters */ 211 } 212 wordv = nwordv - 1; /* convert to 1 based */ 213 wordc += longpiptr ? 2 : 4; 214 return(C_TRUE); 215 } 216 if ( (wordc >= 4) 217 && (strlen(wordv[1]) == 1) 218 && ( (*wordv[1] == 'E') || (*wordv[1] == 'w') || (*wordv[1] == 'e') ) 219 && (alldigits(wordv[2])) 220 && (strlen(wordv[3]) == 1) 221 && (wordv[3][0] == '-') 222 ){ 223 /* 224 * Message of the form: letter linenumber - message 225 * Turn into form: filename linenumber letter - message 226 */ 227 language = INPI; 228 nwordv = wordvsplice(1, wordc, wordv + 1); 229 nwordv[0] = strsave(currentfilename); 230 nwordv[1] = wordv[2]; 231 nwordv[2] = wordv[1]; 232 c_linenumber = wordv[2]; 233 wordc += 1; 234 wordv = nwordv - 1; 235 return(C_TRUE); 236 } 237 if ( (wordc >= 3) 238 && (strlen(wordv[1]) == 1) 239 && ( (*(wordv[1]) == 'E') || (*(wordv[1]) == 'w') || (*(wordv[1]) == 'e') ) 240 && (strlen(wordv[2]) == 1) 241 && (wordv[2][0] == '-') 242 ) { 243 /* 244 * Message of the form: letter - message 245 * This happens only when we are traversing the tree 246 * during the second pass of pi, and discover semantic 247 * errors. 248 * 249 * We have already (presumably) saved the header message 250 * and can now construct a nulled error message for the 251 * current file. 252 * 253 * Turns into a message of the form: 254 * filename (header) letter - message 255 * 256 * First, see if it is a message referring to more than 257 * one line number. Only of the form: 258 * %s undefined on line%s 259 * %s improperly used on line%s 260 */ 261 boolean undefined = 0; 262 int wordindex; 263 264 language = INPI; 265 if ( (undefined = (wordvcmp(wordv+2, 3, pi_und1) == 0) ) 266 || (undefined = (wordvcmp(wordv+2, 3, pi_und2) == 0) ) 267 || (wordvcmp(wordv+2, 4, pi_imp1) == 0) 268 || (wordvcmp(wordv+2, 4, pi_imp2) == 0) 269 ){ 270 for (wordindex = undefined ? 5 : 6; wordindex <= wordc; 271 wordindex++){ 272 nwordv = wordvsplice(2, undefined ? 2 : 3, wordv+1); 273 nwordv[0] = strsave(currentfilename); 274 nwordv[1] = wordv[wordindex]; 275 if (wordindex != wordc) 276 erroradd(undefined ? 4 : 5, nwordv, 277 C_TRUE, C_UNKNOWN); 278 } 279 wordc = undefined ? 4 : 5; 280 wordv = nwordv - 1; 281 return(C_TRUE); 282 } 283 284 nwordv = wordvsplice(1+3, wordc, wordv+1); 285 nwordv[0] = strsave(currentfilename); 286 nwordv[1] = strsave(c_header[0]); 287 nwordv[2] = strsave(c_header[1]); 288 nwordv[3] = strsave(c_header[2]); 289 wordv = nwordv - 1; 290 wordc += 1 + 3; 291 return(C_THISFILE); 292 } 293 if (strcmp(wordv[1], "...") == 0){ 294 /* 295 * have a continuation error message 296 * of the form: ... message 297 * Turn into form : filename linenumber message 298 */ 299 language = INPI; 300 nwordv = wordvsplice(1, wordc, wordv+1); 301 nwordv[0] = strsave(currentfilename); 302 nwordv[1] = strsave(c_linenumber); 303 wordv = nwordv - 1; 304 wordc += 1; 305 return(C_TRUE); 306 } 307 if( (wordc == 6) 308 && (lastchar(wordv[6]) == ':') 309 && (isdateformat(5, wordv + 1)) 310 ){ 311 /* 312 * Have message that tells us we have changed files 313 */ 314 language = INPI; 315 currentfilename = strsave(wordv[6]); 316 clob_last(currentfilename, '\0'); 317 return(C_SYNC); 318 } 319 if( (wordc == 3) 320 && (strcmp(wordv[1], "In") == 0) 321 && (lastchar(wordv[3]) == ':') 322 && (instringset(wordv[2], Piroutines)) 323 ) { 324 language = INPI; 325 c_header = wordvsplice(0, wordc, wordv+1); 326 return(C_SYNC); 327 } 328 /* 329 * now, check for just the line number followed by the text 330 */ 331 if (alldigits(wordv[1])){ 332 language = INPI; 333 c_linenumber = wordv[1]; 334 return(C_IGNORE); 335 } 336 /* 337 * Attempt to match messages refering to a line number 338 * 339 * Multiply defined label in case, lines %d and %d 340 * Goto %s from line %d is into a structured statement 341 * End matched %s on line %d 342 * Inserted keyword end matching %s on line %d 343 */ 344 multiple = structured = 0; 345 if ( 346 ( (wordc == 6) && (wordvcmp(wordv+1, 2, pi_Endmatched) == 0)) 347 || ( (wordc == 8) && (wordvcmp(wordv+1, 4, pi_Inserted) == 0)) 348 || ( multiple = ((wordc == 9) && (wordvcmp(wordv+1,6, pi_multiple) == 0) ) ) 349 || ( structured = ((wordc == 10) && (wordvcmp(wordv+6,5, pi_structured) == 0 ) )) 350 ){ 351 language = INPI; 352 nwordv = wordvsplice(2, wordc, wordv+1); 353 nwordv[0] = strsave(currentfilename); 354 nwordv[1] = structured ? wordv [5] : wordv[wordc]; 355 wordc += 2; 356 wordv = nwordv - 1; 357 if (!multiple) 358 return(C_TRUE); 359 erroradd(wordc, nwordv, C_TRUE, C_UNKNOWN); 360 nwordv = wordvsplice(0, wordc, nwordv); 361 nwordv[1] = wordv[wordc - 2]; 362 return(C_TRUE); 363 } 364 return(C_UNKNOWN); 365 } 366