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