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