1 /* source.c -- Source code management for Libmaa
2  * Created: Mon Dec 26 19:42:03 1994 by faith@dict.org
3  * Copyright 1994-1998, 2002 Rickard E. Faith (faith@dict.org)
4  * Copyright 2002-2008 Aleksey Cheusov (vle@gmx.net)
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  *
25  * \section{Source Code Management}
26  *
27  * \intro Most of the source code management routines are designed to be
28  * called from within a lexical analyzer.  The routines support the storage
29  * and retrieval of source code lines and source code token information.  A
30  * single, global, source code management object is supported.
31  *
32  * \textbf{This documentation is incomplete.}
33  *
34  * \textbf{These routines have not been fully implemented.}
35  *
36  */
37 
38 #include "maaP.h"
39 
40 typedef struct source {
41 	const char *file;
42 	int        line;
43 	int        offset;
44 	int        length;
45 	int        index;
46 } sourceType;
47 
48 #define INCREMENT 1000
49 
50 static const char **Lines;
51 static int        Count;
52 static int        Used;
53 static sourceType Info;
54 static mem_String StringHeap;
55 static mem_Object InfoHeap;
56 
57 /* \doc The |src_create| functions initializes the global source code
58    management object.  This routine should only be called once, if at all.
59    If the other routines are used as expected, |src_create| will be called
60    automatically when it is needed. */
61 
src_create(void)62 void src_create(void)
63 {
64 	if (Lines)
65 		err_fatal(__func__, "Source manager already created");
66 
67 	Lines      = xmalloc((Count = INCREMENT) * sizeof(char *));
68 	StringHeap = mem_create_strings();
69 	InfoHeap   = mem_create_objects(sizeof(struct source));
70 }
71 
72 /* \doc |src_destroy| frees all memory associated with the global source
73    code management object. */
74 
src_destroy(void)75 void src_destroy(void)
76 {
77 	if (!Lines) return;
78 	mem_destroy_objects(InfoHeap);
79 	mem_destroy_strings(StringHeap);
80 	xfree(Lines);		/* terminal */
81 	Lines = StringHeap = InfoHeap = NULL;
82 }
83 
84 /* \doc |src_line| stores a |line| of length |len|. */
85 
src_line(const char * line,int len)86 const char *src_line(const char *line, int len)
87 {
88 	char *pt;
89 
90 	if (!Lines) src_create();
91 
92 	++Info.line;
93 	Info.index  = Used;
94 	Info.offset = 0;
95 
96 	Lines[Used] = mem_strncpy(StringHeap, line, len);
97 	for (pt = (char*)__UNCONST(Lines[Used]); *pt; pt++) if (*pt == '\t') *pt = ' ';
98 
99 	PRINTF(MAA_SRC,("Line %d: %s",Used,Lines[Used]));
100 
101 	if (++Used >= Count)
102 		Lines = xrealloc(Lines, (Count += INCREMENT) * sizeof(char *));
103 
104 	return Lines[ Used - 1 ];
105 }
106 
107 /* \doc |src_new_file| specifies that |filename| is the name of the current
108    file being read by the lexer. */
109 
src_new_file(const char * filename)110 void src_new_file(const char *filename)
111 {
112    Info.file = str_find(filename);
113 }
114 
115 /* \doc |src_new_line| specifies that |line| is the number of the current
116    line being read by the lexer.  Line numbers start at 1. */
117 
src_new_line(int line)118 void src_new_line(int line)
119 {
120 	Info.line = line;
121 }
122 
123 /* \doc |src_advance| is used to advance the token offset pointer |length|
124    bytes. */
125 
src_advance(int length)126 void src_advance(int length)
127 {
128 	Info.offset += length;
129 }
130 
src_cpp_line(const char * line,int length)131 void src_cpp_line(const char *line, int length)
132 {
133 	arg_List args;
134 	char     *tmp = alloca(length + 1);
135 	int      lineno;
136 
137 	strncpy(tmp, line, length);
138 	tmp [ length ] = '\0';
139 
140 	args = arg_argify(tmp, 0);
141 
142 	if ((lineno = atoi(arg_get(args, 1))) > 0) --lineno;
143 	else                                           lineno = 1;
144 	src_new_line(lineno);
145 
146 	/* FIXME! This is debugging cruft to be
147 	   removed. */
148 	if (arg_count(args) == 2) {
149 		PRINTF(MAA_SRC,("lineno %s\n", arg_get(args, 1)));
150 	} else {
151 		PRINTF(MAA_SRC,("lineno %s in %s\n",
152 						arg_get(args, 1), arg_get(args, 2)));
153 		src_new_file(arg_get(args, 2));
154 	}
155 
156 	arg_destroy(args);
157 }
158 
159 /* \doc |src_get| is used to get a |src_Type| object for the current token.
160    This object contains file, line, offset, and length information which
161    can be used to print parse error messages and provide detailed token
162    tracking. */
163 
src_get(int length)164 src_Type src_get(int length)
165 {
166 	sourceType *new;
167 
168 	if (!Lines)
169 		err_fatal(__func__, "Source manager does not exist");
170 
171 	Info.length = length;
172 	new = mem_get_object(InfoHeap);
173 
174 	new->file   = Info.file;
175 	new->line   = Info.line;
176 	new->offset = Info.offset;
177 	new->length = Info.length;
178 	new->index  = Info.index;	/* Index into Lines array. */
179 
180 	if (dbg_test(MAA_SRC)) {
181 		printf("%s:%d @ %d, %d; %d\n",
182 			   new->file,
183 			   new->line,
184 			   new->offset,
185 			   new->length,
186 			   new->index);
187 	}
188 
189 	src_advance(length);
190 
191 	return new;
192 }
193 
194 /* \doc |src_filename| returns the |file| associated with the specified
195    |source| information. */
196 
src_filename(src_Type source)197 const char *src_filename(src_Type source)
198 {
199 	sourceType *s = (sourceType *)source;
200 
201 	if (!Lines)
202 		err_fatal(__func__, "Source manager never created");
203 
204 	return s ? s->file : "";
205 }
206 
207 /* \doc |src_linenumber| returns the |line| associated with the specified
208    |source| information. */
209 
src_linenumber(src_Type source)210 int src_linenumber(src_Type source)
211 {
212 	sourceType *s = (sourceType *)source;
213 
214 	if (!Lines)
215 		err_fatal(__func__, "Source manager never created");
216 
217 	return s ? s->line : 0;
218 }
219 
220 /* \doc |src_offset| returns the token |offset| associated with the
221    specified |source| information. */
222 
src_offset(src_Type source)223 int src_offset(src_Type source)
224 {
225 	sourceType *s = (sourceType *)source;
226 
227 	if (!Lines)
228 		err_fatal(__func__, "Source manager never created");
229 
230 	return s ? s->offset : 0;
231 }
232 
233 /* \doc |src_length| returns the token |length| associated with the
234    specified |source| information. */
235 
src_length(src_Type source)236 int src_length(src_Type source)
237 {
238 	sourceType *s = (sourceType *)source;
239 
240 	if (!Lines)
241 		err_fatal(__func__, "Source manager never created");
242 
243 	return s ? s->length : 0;
244 }
245 
246 /* \doc |src_source_line| returns the full source line associated with the
247    specified |source| information. */
248 
src_source_line(src_Type source)249 const char *src_source_line(src_Type source)
250 {
251 	sourceType *s = (sourceType *)source;
252 
253 	if (!Lines)
254 		err_fatal(__func__, "Source manager never created");
255 
256 	return s ? Lines[ s->index ] : "";
257 }
258 
259 /* \doc |src_get_stats| returns the statistics associated with the source
260    code manager.  The |src_Stats| structure is shown in
261    \grind{src_Stats}. */
262 
src_get_stats(void)263 src_Stats src_get_stats(void)
264 {
265 	src_Stats s = xmalloc(sizeof(struct src_Stats));
266 
267 	if (Lines) {
268 		mem_StringStats ms = mem_get_string_stats(StringHeap);
269 		mem_ObjectStats mo = mem_get_object_stats(InfoHeap);
270 
271 		s->lines_used      = Used;
272 		s->lines_allocated = Count;
273 		s->lines_bytes     = ms->bytes;
274 		s->tokens_total    = mo->total;
275 		s->tokens_reused   = mo->reused;
276 		s->tokens_size     = mo->size;
277 
278 		xfree(ms);		/* rare */
279 		xfree(mo);		/* rare */
280 	} else {
281 		s->lines_used      = 0;
282 		s->lines_allocated = 0;
283 		s->lines_bytes     = 0;
284 		s->tokens_total    = 0;
285 		s->tokens_reused   = 0;
286 		s->tokens_size     = 0;
287 	}
288 
289 	return s;
290 }
291 
292 /* \doc |src_print_stats| prints the statistics about the source code
293    manager to the specified |stream|.  If |stream| is "NULL", then "stdout"
294    will be used. */
295 
src_print_stats(FILE * stream)296 void src_print_stats(FILE *stream)
297 {
298 	FILE      *str = stream ? stream : stdout;
299 	src_Stats s    = src_get_stats();
300 
301 	fprintf(str, "Statistics for source manager:\n");
302 	fprintf(str, "   %d lines using %d bytes (%d allocated)\n",
303 			s->lines_used, s->lines_bytes, s->lines_allocated);
304 	fprintf(str, "   %d tokens using %d bytes (%d reused)\n",
305 			s->tokens_total, s->tokens_total * s->tokens_size,
306 			s->tokens_reused);
307 	xfree(s);			/* rare */
308 }
309 
_src_print_yyerror(FILE * str,const char * message)310 static void _src_print_yyerror(FILE *str, const char *message)
311 {
312 	const char *pt;
313 	char       buf[1024];
314 	char       *b;
315 	const char *concrete;
316 
317 	assert(str);
318 
319 	if (!message) {
320 		fprintf(str, "(null)");
321 		return;
322 	}
323 
324 	for (pt = message; *pt; pt++) {
325 		if (*pt == '`') {		/* clean up character constants */
326 			if (pt[1] == '`' && pt[2] && pt[3] == '\'' && pt[4] == '\'') {
327 				fprintf(str, "`%c'", pt[2]);
328 				pt += 4;
329 			} else if (pt[1] == 'T' && pt[2] == '_') { /* replace symbols */
330 				for (b = buf, ++pt; *pt && *pt != '\''; b++, pt++) *b = *pt;
331 				*b = '\0';
332 				if ((concrete = prs_concrete(buf)))
333 					fprintf(str, "`%s'", concrete);
334 				else
335 					fprintf(str, "`%s'", buf);
336 			} else
337 				putc(*pt, str);
338 		} else
339 			putc(*pt, str);
340 	}
341 }
342 
343 /* \doc |src_print_line| prints a line of source code, as described by
344    |source| to the specified |stream|.  If |stream| is "NULL", then
345    "stdout" will be used. */
346 
src_print_line(FILE * stream,src_Type source)347 void src_print_line(FILE *stream, src_Type source)
348 {
349 	sourceType *s   = (sourceType *)source;
350 	FILE       *str = stream ? stream : stdout;
351 
352 	if (s)
353 		fprintf(str, "%s:%d: %s", s->file, s->line, Lines[ s->index ]);
354 	else
355 		fprintf(str, "?:?: <source line not available>\n");
356 }
357 
_src_print_error(FILE * str,sourceType * s,int fudge)358 static void _src_print_error(FILE *str, sourceType *s, int fudge)
359 {
360 	int        i;
361 
362 	assert(str);
363 
364 	src_print_line(str, s);
365 	if (s) {
366 		PRINTF(MAA_SRC,("s->offset = %d, fudge = %d, s->length = %d\n",
367 						s->offset, fudge, s->length));
368 		fprintf(str, "%s:%d: ", s->file, s->line);
369 		for (i = 0; i < s->offset - fudge; i++) putc(' ', str);
370 		for (i = 0; i < s->length; i++) putc('^', str);
371 	} else {
372 		fprintf(str, "?:?: ");
373 	}
374 	putc('\n', str);
375 }
376 
377 /* \doc |src_parse_error| will print the error |message| to the specified
378    |stream|, followed by the offending line of source code, as specified by
379    |source|.  If |stream| is "NULL", then "stdout" will be used.
380 
381    It is assumed that |message| has the format of |yyerror| so that
382    massaging of the string can be performed to make it more readable (token
383    names are assumed to start with ``T\_'' and will be changed to more
384    readable names).  This massaging should be done by a user-defined
385    function, since it is relatively specific to this author's coding
386    conventions. */
387 
src_parse_error(FILE * stream,src_Type source,const char * message)388 void src_parse_error(FILE *stream, src_Type source, const char *message)
389 {
390 	sourceType *s   = (sourceType *)source;
391 	FILE       *str = stream ? stream : stdout;
392 
393 	if (s) fprintf(str, "%s:%d: ", s->file, s->line);
394 	else   fprintf(str, "?:?: ");
395 	_src_print_yyerror(str, message);
396 	putc('\n', str);
397 
398 	_src_print_error(str, s, 0);
399 }
400 
401 /* \doc |src_print_error| will print an arbitrary error, specified as for
402    |printf| by the |format| variable, to the specified |stream|, followed
403    by the line of source code specified by |source|.  If |stream| is
404    "NULL", then "stdout" will be used. */
405 
src_print_error(FILE * str,src_Type source,const char * format,...)406 void src_print_error(FILE *str, src_Type source, const char *format, ...)
407 {
408 	va_list    ap;
409 	sourceType *s = (sourceType *)source;
410 
411 	fflush(str);
412 	if (format) {
413 		if (s)
414 			fprintf(str, "%s:%d: ", s->file, s->line);
415 		else
416 			fprintf(str, "?:?: ");
417 		va_start(ap, format);
418 		vfprintf(str, format, ap);
419 		va_end(ap);
420 		putc('\n', str);
421 	}
422 
423 	_src_print_error(str, s, 0);
424 }
425 
426 /* \doc |src_print_message| will just print a message labeled with a line. */
427 
src_print_message(FILE * str,src_Type source,const char * format,...)428 void src_print_message(FILE *str, src_Type source, const char *format, ...)
429 {
430 	va_list    ap;
431 	sourceType *s = (sourceType *)source;
432 
433 	fflush(str);
434 	if (format) {
435 		if (s)
436 			fprintf(str, "%s:%d: ", s->file, s->line);
437 		else
438 			fprintf(str, "?:?: ");
439 		va_start(ap, format);
440 		vfprintf(str, format, ap);
441 		va_end(ap);
442 		putc('\n', str);
443 	}
444 }
445