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