Branch data Line data Source code
1 : : /* MD reader for GCC.
2 : : Copyright (C) 1987-2025 Free Software Foundation, Inc.
3 : :
4 : : This file is part of GCC.
5 : :
6 : : GCC is free software; you can redistribute it and/or modify it under
7 : : the terms of the GNU General Public License as published by the Free
8 : : Software Foundation; either version 3, or (at your option) any later
9 : : version.
10 : :
11 : : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12 : : WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 : : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 : : for more details.
15 : :
16 : : You should have received a copy of the GNU General Public License
17 : : along with GCC; see the file COPYING3. If not see
18 : : <http://www.gnu.org/licenses/>. */
19 : :
20 : : /* This file is compiled twice: once for the generator programs
21 : : once for the compiler. */
22 : : #ifdef GENERATOR_FILE
23 : : #include "bconfig.h"
24 : : #else
25 : : #include "config.h"
26 : : #endif
27 : : #include "system.h"
28 : : #include "coretypes.h"
29 : : #ifdef GENERATOR_FILE
30 : : #include "errors.h"
31 : : #endif /* #ifdef GENERATOR_FILE */
32 : : #include "statistics.h"
33 : : #include "vec.h"
34 : : #include "read-md.h"
35 : :
36 : : #ifndef GENERATOR_FILE
37 : :
38 : : /* Minimal reimplementation of errors.cc for use by RTL frontend
39 : : within cc1. */
40 : :
41 : : int have_error = 0;
42 : :
43 : : #endif /* #ifndef GENERATOR_FILE */
44 : :
45 : :
46 : : /* This callback will be invoked whenever an md include directive is
47 : : processed. To be used for creation of the dependency file. */
48 : : void (*include_callback) (const char *);
49 : :
50 : : /* Global singleton. */
51 : :
52 : : md_reader *md_reader_ptr;
53 : :
54 : : /* Given an object that starts with a char * name field, return a hash
55 : : code for its name. */
56 : :
57 : : hashval_t
58 : 8297 : leading_string_hash (const void *def)
59 : : {
60 : 8297 : return htab_hash_string (*(const char *const *) def);
61 : : }
62 : :
63 : : /* Given two objects that start with char * name fields, return true if
64 : : they have the same name. */
65 : :
66 : : int
67 : 0 : leading_string_eq_p (const void *def1, const void *def2)
68 : : {
69 : 0 : return strcmp (*(const char *const *) def1,
70 : 0 : *(const char *const *) def2) == 0;
71 : : }
72 : :
73 : : /* Return a hash value for the pointer pointed to by DEF. */
74 : :
75 : : static hashval_t
76 : 112 : leading_ptr_hash (const void *def)
77 : : {
78 : 112 : return htab_hash_pointer (*(const void *const *) def);
79 : : }
80 : :
81 : : /* Return true if DEF1 and DEF2 are pointers to the same pointer. */
82 : :
83 : : static int
84 : 0 : leading_ptr_eq_p (const void *def1, const void *def2)
85 : : {
86 : 0 : return *(const void *const *) def1 == *(const void *const *) def2;
87 : : }
88 : :
89 : : /* Associate PTR with the file position given by FILE_LOC. */
90 : :
91 : : void
92 : 112 : md_reader::set_md_ptr_loc (const void *ptr, file_location file_loc)
93 : : {
94 : 112 : struct ptr_loc *loc;
95 : :
96 : 112 : loc = (struct ptr_loc *) obstack_alloc (&m_ptr_loc_obstack,
97 : : sizeof (struct ptr_loc));
98 : 112 : loc->ptr = ptr;
99 : 112 : loc->loc = file_loc;
100 : 112 : *htab_find_slot (m_ptr_locs, loc, INSERT) = loc;
101 : 112 : }
102 : :
103 : : /* Return the position associated with pointer PTR. Return null if no
104 : : position was set. */
105 : :
106 : : const md_reader::ptr_loc *
107 : 0 : md_reader::get_md_ptr_loc (const void *ptr)
108 : : {
109 : 0 : return (const struct ptr_loc *) htab_find (m_ptr_locs, &ptr);
110 : : }
111 : :
112 : : /* Associate NEW_PTR with the same file position as OLD_PTR. */
113 : :
114 : : void
115 : 0 : md_reader::copy_md_ptr_loc (const void *new_ptr, const void *old_ptr)
116 : : {
117 : 0 : const struct ptr_loc *loc = get_md_ptr_loc (old_ptr);
118 : 0 : if (loc != 0)
119 : 0 : set_md_ptr_loc (new_ptr, loc->loc);
120 : 0 : }
121 : :
122 : : /* If PTR is associated with a known file position, print a #line
123 : : directive for it to OUTF. */
124 : :
125 : : void
126 : 0 : md_reader::fprint_md_ptr_loc (FILE *outf, const void *ptr)
127 : : {
128 : 0 : const struct ptr_loc *loc = get_md_ptr_loc (ptr);
129 : 0 : if (loc != 0)
130 : 0 : fprintf (outf, "#line %d \"%s\"\n", loc->loc.lineno, loc->loc.filename);
131 : 0 : }
132 : :
133 : : /* Special fprint_md_ptr_loc for writing to STDOUT. */
134 : : void
135 : 0 : md_reader::print_md_ptr_loc (const void *ptr, FILE *file)
136 : : {
137 : 0 : fprint_md_ptr_loc (file, ptr);
138 : 0 : }
139 : :
140 : : /* Return a condition that satisfies both COND1 and COND2. Either string
141 : : may be null or empty. */
142 : :
143 : : const char *
144 : 0 : md_reader::join_c_conditions (const char *cond1, const char *cond2)
145 : : {
146 : 0 : char *result;
147 : 0 : const void **entry;
148 : :
149 : 0 : if (cond1 == 0 || cond1[0] == 0)
150 : : return cond2;
151 : :
152 : 0 : if (cond2 == 0 || cond2[0] == 0)
153 : : return cond1;
154 : :
155 : 0 : if (strcmp (cond1, cond2) == 0)
156 : : return cond1;
157 : :
158 : 0 : result = concat ("(", cond1, ") && (", cond2, ")", NULL);
159 : 0 : obstack_ptr_grow (&m_joined_conditions_obstack, result);
160 : 0 : obstack_ptr_grow (&m_joined_conditions_obstack, cond1);
161 : 0 : obstack_ptr_grow (&m_joined_conditions_obstack, cond2);
162 : 0 : entry = XOBFINISH (&m_joined_conditions_obstack, const void **);
163 : 0 : *htab_find_slot (m_joined_conditions, entry, INSERT) = entry;
164 : 0 : return result;
165 : : }
166 : :
167 : : /* Print condition COND to OUTF, wrapped in brackets. If COND was created
168 : : by join_c_conditions, recursively invoke this function for the original
169 : : conditions and join the result with "&&". Otherwise print a #line
170 : : directive for COND if its original file position is known. */
171 : :
172 : : void
173 : 0 : md_reader::fprint_c_condition (FILE *outf, const char *cond)
174 : : {
175 : 0 : const char **halves = (const char **) htab_find (m_joined_conditions, &cond);
176 : 0 : if (halves != 0)
177 : : {
178 : 0 : fprintf (outf, "(");
179 : 0 : fprint_c_condition (outf, halves[1]);
180 : 0 : fprintf (outf, " && ");
181 : 0 : fprint_c_condition (outf, halves[2]);
182 : 0 : fprintf (outf, ")");
183 : : }
184 : : else
185 : : {
186 : 0 : fputc ('\n', outf);
187 : 0 : fprint_md_ptr_loc (outf, cond);
188 : 0 : fprintf (outf, "(%s)", cond);
189 : : }
190 : 0 : }
191 : :
192 : : /* Special fprint_c_condition for writing to STDOUT. */
193 : :
194 : : void
195 : 0 : md_reader::print_c_condition (FILE *outf, const char *cond)
196 : : {
197 : 0 : fprint_c_condition (outf, cond);
198 : 0 : }
199 : :
200 : : /* A vfprintf-like function for reporting an error against line LINENO
201 : : of the current MD file. */
202 : :
203 : : static void ATTRIBUTE_PRINTF(2,0)
204 : 0 : message_at_1 (file_location loc, const char *msg, va_list ap)
205 : : {
206 : 0 : fprintf (stderr, "%s:%d:%d: ", loc.filename, loc.lineno, loc.colno);
207 : 0 : vfprintf (stderr, msg, ap);
208 : 0 : fputc ('\n', stderr);
209 : 0 : }
210 : :
211 : : /* A printf-like function for reporting a message against location LOC. */
212 : :
213 : : void
214 : 0 : message_at (file_location loc, const char *msg, ...)
215 : : {
216 : 0 : va_list ap;
217 : :
218 : 0 : va_start (ap, msg);
219 : 0 : message_at_1 (loc, msg, ap);
220 : 0 : va_end (ap);
221 : 0 : }
222 : :
223 : : /* Like message_at, but treat the condition as an error. */
224 : :
225 : : void
226 : 0 : error_at (file_location loc, const char *msg, ...)
227 : : {
228 : 0 : va_list ap;
229 : :
230 : 0 : va_start (ap, msg);
231 : 0 : message_at_1 (loc, msg, ap);
232 : 0 : va_end (ap);
233 : 0 : have_error = 1;
234 : 0 : }
235 : :
236 : : /* Like message_at, but treat the condition as a fatal error. */
237 : :
238 : : void
239 : 0 : fatal_at (file_location loc, const char *msg, ...)
240 : : {
241 : 0 : va_list ap;
242 : :
243 : 0 : va_start (ap, msg);
244 : 0 : message_at_1 (loc, msg, ap);
245 : 0 : va_end (ap);
246 : 0 : exit (1);
247 : : }
248 : :
249 : : /* A printf-like function for reporting an error against the current
250 : : position in the MD file. */
251 : :
252 : : void
253 : 1 : fatal_with_file_and_line (const char *msg, ...)
254 : : {
255 : 1 : char context[64];
256 : 1 : size_t i;
257 : 1 : int c;
258 : 1 : va_list ap;
259 : :
260 : 1 : va_start (ap, msg);
261 : :
262 : 1 : fprintf (stderr, "%s:%d:%d: error: ", md_reader_ptr->get_filename (),
263 : : md_reader_ptr->get_lineno (),
264 : : md_reader_ptr->get_colno ());
265 : 1 : vfprintf (stderr, msg, ap);
266 : 1 : putc ('\n', stderr);
267 : :
268 : : /* Gather some following context. */
269 : 43 : for (i = 0; i < sizeof (context)-1; ++i)
270 : : {
271 : 42 : c = read_char ();
272 : 42 : if (c == EOF)
273 : : break;
274 : 42 : if (c == '\r' || c == '\n')
275 : : {
276 : 1 : unread_char (c);
277 : 1 : break;
278 : : }
279 : 41 : context[i] = c;
280 : : }
281 : 1 : context[i] = '\0';
282 : :
283 : 1 : fprintf (stderr, "%s:%d:%d: note: following context is `%s'\n",
284 : : md_reader_ptr->get_filename (),
285 : : md_reader_ptr->get_lineno (),
286 : : md_reader_ptr->get_colno (), context);
287 : :
288 : 1 : va_end (ap);
289 : 1 : exit (1);
290 : : }
291 : :
292 : : /* Report that we found character ACTUAL when we expected to find
293 : : character EXPECTED. */
294 : :
295 : : void
296 : 0 : fatal_expected_char (int expected, int actual)
297 : : {
298 : 0 : if (actual == EOF)
299 : 0 : fatal_with_file_and_line ("expected character `%c', found EOF",
300 : : expected);
301 : : else
302 : 0 : fatal_with_file_and_line ("expected character `%c', found `%c'",
303 : : expected, actual);
304 : : }
305 : :
306 : : /* Read chars from the MD file until a non-whitespace char and return that.
307 : : Comments, both Lisp style and C style, are treated as whitespace. */
308 : :
309 : : int
310 : 19929 : read_skip_spaces (void)
311 : : {
312 : 50195 : int c;
313 : :
314 : 50195 : while (1)
315 : : {
316 : 50195 : c = read_char ();
317 : 50195 : switch (c)
318 : : {
319 : : case ' ': case '\t': case '\f': case '\r': case '\n':
320 : : break;
321 : :
322 : 5390 : case ';':
323 : 5390 : do
324 : 5390 : c = read_char ();
325 : 5390 : while (c != '\n' && c != EOF);
326 : : break;
327 : :
328 : 4 : case '/':
329 : 4 : {
330 : 4 : int prevc;
331 : 4 : c = read_char ();
332 : 4 : if (c != '*')
333 : : {
334 : 0 : unread_char (c);
335 : 0 : fatal_with_file_and_line ("stray '/' in file");
336 : : }
337 : :
338 : : prevc = 0;
339 : 580 : while ((c = read_char ()) && c != EOF)
340 : : {
341 : 580 : if (prevc == '*' && c == '/')
342 : : break;
343 : : prevc = c;
344 : : }
345 : : }
346 : : break;
347 : :
348 : 19929 : default:
349 : 19929 : return c;
350 : : }
351 : : }
352 : : }
353 : :
354 : : /* Consume the next character, issuing a fatal error if it is not
355 : : EXPECTED. */
356 : :
357 : : void
358 : 564 : md_reader::require_char (char expected)
359 : : {
360 : 564 : int ch = read_char ();
361 : 564 : if (ch != expected)
362 : 0 : fatal_expected_char (expected, ch);
363 : 564 : }
364 : :
365 : : /* Consume any whitespace, then consume the next non-whitespace
366 : : character, issuing a fatal error if it is not EXPECTED. */
367 : :
368 : : void
369 : 5138 : md_reader::require_char_ws (char expected)
370 : : {
371 : 5138 : int ch = read_skip_spaces ();
372 : 5138 : if (ch != expected)
373 : 0 : fatal_expected_char (expected, ch);
374 : 5138 : }
375 : :
376 : : /* Consume any whitespace, then consume the next word (as per read_name),
377 : : issuing a fatal error if it is not EXPECTED. */
378 : :
379 : : void
380 : 282 : md_reader::require_word_ws (const char *expected)
381 : : {
382 : 282 : struct md_name name;
383 : 282 : read_name (&name);
384 : 282 : if (strcmp (name.string, expected))
385 : 0 : fatal_with_file_and_line ("missing '%s'", expected);
386 : 282 : }
387 : :
388 : : /* Read the next character from the file. */
389 : :
390 : : int
391 : 102439 : md_reader::read_char (void)
392 : : {
393 : 102439 : int ch;
394 : :
395 : 102439 : ch = getc (m_read_md_file);
396 : 102439 : if (ch == '\n')
397 : : {
398 : 2995 : m_read_md_lineno++;
399 : 2995 : m_last_line_colno = m_read_md_colno;
400 : 2995 : m_read_md_colno = 0;
401 : : }
402 : : else
403 : 99444 : m_read_md_colno++;
404 : :
405 : : /* If we're filtering lines, treat everything before the range of
406 : : interest as a space, and as EOF for everything after. */
407 : 102439 : if (m_first_line && m_last_line)
408 : : {
409 : 61643 : if (m_read_md_lineno < m_first_line)
410 : : return ' ';
411 : 54192 : if (m_read_md_lineno > m_last_line)
412 : 32 : return EOF;
413 : : }
414 : :
415 : : return ch;
416 : : }
417 : :
418 : : /* Put back CH, which was the last character read from the file. */
419 : :
420 : : void
421 : 11414 : md_reader::unread_char (int ch)
422 : : {
423 : 11414 : if (ch == '\n')
424 : : {
425 : 441 : m_read_md_lineno--;
426 : 441 : m_read_md_colno = m_last_line_colno;
427 : : }
428 : : else
429 : 10973 : m_read_md_colno--;
430 : 11414 : ungetc (ch, m_read_md_file);
431 : 11414 : }
432 : :
433 : : /* Peek at the next character from the file without consuming it. */
434 : :
435 : : int
436 : 2154 : md_reader::peek_char (void)
437 : : {
438 : 2154 : int ch = read_char ();
439 : 2154 : unread_char (ch);
440 : 2154 : return ch;
441 : : }
442 : :
443 : : /* Read an rtx code name into NAME. It is terminated by any of the
444 : : punctuation chars of rtx printed syntax. */
445 : :
446 : : bool
447 : 6819 : md_reader::read_name_1 (struct md_name *name, file_location *out_loc)
448 : : {
449 : 6819 : int c;
450 : 6819 : size_t i;
451 : 6819 : int angle_bracket_depth;
452 : :
453 : 6819 : c = read_skip_spaces ();
454 : :
455 : 6819 : *out_loc = get_current_location ();
456 : :
457 : 6819 : i = 0;
458 : 6819 : angle_bracket_depth = 0;
459 : 61191 : while (1)
460 : : {
461 : 34005 : if (c == '<')
462 : 249 : angle_bracket_depth++;
463 : :
464 : 34005 : if ((c == '>') && (angle_bracket_depth > 0))
465 : 249 : angle_bracket_depth--;
466 : :
467 : 34005 : if (c == ' ' || c == '\n' || c == '\t' || c == '\f' || c == '\r'
468 : : || c == EOF)
469 : : break;
470 : 29560 : if (angle_bracket_depth == 0)
471 : : {
472 : 29036 : if (c == ':' || c == ')' || c == ']'
473 : : || c == '"' || c == '/' || c == '(' || c == '[')
474 : : {
475 : 2374 : unread_char (c);
476 : 2374 : break;
477 : : }
478 : : }
479 : :
480 : 27186 : if (i == sizeof (name->buffer) - 1)
481 : 0 : fatal_with_file_and_line ("name too long");
482 : 27186 : name->buffer[i++] = c;
483 : :
484 : 27186 : c = read_char ();
485 : : }
486 : :
487 : 6819 : if (i == 0)
488 : : return false;
489 : :
490 : 6819 : name->buffer[i] = 0;
491 : 6819 : name->string = name->buffer;
492 : :
493 : 6819 : if (m_md_constants)
494 : : {
495 : : /* Do constant expansion. */
496 : 6819 : struct md_constant *def;
497 : :
498 : 6819 : do
499 : : {
500 : 6819 : struct md_constant tmp_def;
501 : :
502 : 6819 : tmp_def.name = name->string;
503 : 6819 : def = (struct md_constant *) htab_find (m_md_constants, &tmp_def);
504 : 6819 : if (def)
505 : 0 : name->string = def->value;
506 : : }
507 : 6819 : while (def);
508 : : }
509 : :
510 : : return true;
511 : : }
512 : :
513 : : /* Read an rtx code name into NAME. It is terminated by any of the
514 : : punctuation chars of rtx printed syntax. */
515 : :
516 : : file_location
517 : 6819 : md_reader::read_name (struct md_name *name)
518 : : {
519 : 6819 : file_location loc;
520 : 6819 : if (!read_name_1 (name, &loc))
521 : 0 : fatal_with_file_and_line ("missing name or number");
522 : 6819 : return loc;
523 : : }
524 : :
525 : : file_location
526 : 0 : md_reader::read_name_or_nil (struct md_name *name)
527 : : {
528 : 0 : file_location loc;
529 : 0 : if (!read_name_1 (name, &loc))
530 : : {
531 : 0 : file_location loc = get_current_location ();
532 : 0 : read_skip_construct (0, loc);
533 : : /* Skip the ')'. */
534 : 0 : read_char ();
535 : 0 : name->buffer[0] = 0;
536 : 0 : name->string = name->buffer;
537 : : }
538 : 0 : return loc;
539 : : }
540 : :
541 : : /* Subroutine of the string readers. Handles backslash escapes.
542 : : Caller has read the backslash, but not placed it into the obstack. */
543 : :
544 : : void
545 : 0 : md_reader::read_escape ()
546 : : {
547 : 0 : int c = read_char ();
548 : :
549 : 0 : switch (c)
550 : : {
551 : : /* Backslash-newline is replaced by nothing, as in C. */
552 : : case '\n':
553 : : return;
554 : :
555 : : /* \" \' \\ are replaced by the second character. */
556 : : case '\\':
557 : : case '"':
558 : : case '\'':
559 : : break;
560 : :
561 : : /* Standard C string escapes:
562 : : \a \b \f \n \r \t \v
563 : : \[0-7] \x
564 : : all are passed through to the output string unmolested.
565 : : In normal use these wind up in a string constant processed
566 : : by the C compiler, which will translate them appropriately.
567 : : We do not bother checking that \[0-7] are followed by up to
568 : : two octal digits, or that \x is followed by N hex digits.
569 : : \? \u \U are left out because they are not in traditional C. */
570 : 0 : case 'a': case 'b': case 'f': case 'n': case 'r': case 't': case 'v':
571 : 0 : case '0': case '1': case '2': case '3': case '4': case '5': case '6':
572 : 0 : case '7': case 'x':
573 : 0 : obstack_1grow (&m_string_obstack, '\\');
574 : 0 : break;
575 : :
576 : : /* \; makes stuff for a C string constant containing
577 : : newline and tab. */
578 : 0 : case ';':
579 : 0 : obstack_grow (&m_string_obstack, "\\n\\t", 4);
580 : 0 : return;
581 : :
582 : : /* pass anything else through, but issue a warning. */
583 : 0 : default:
584 : 0 : fprintf (stderr, "%s:%d: warning: unrecognized escape \\%c\n",
585 : : get_filename (), get_lineno (),
586 : : c);
587 : 0 : obstack_1grow (&m_string_obstack, '\\');
588 : 0 : break;
589 : : }
590 : :
591 : 0 : obstack_1grow (&m_string_obstack, c);
592 : : }
593 : :
594 : : /* Read a double-quoted string onto the obstack. Caller has scanned
595 : : the leading quote. */
596 : :
597 : : char *
598 : 587 : md_reader::read_quoted_string ()
599 : : {
600 : 11516 : int c;
601 : :
602 : 11516 : while (1)
603 : : {
604 : 11516 : c = read_char (); /* Read the string */
605 : 11516 : if (c == '\\')
606 : : {
607 : 0 : read_escape ();
608 : 0 : continue;
609 : : }
610 : 11516 : else if (c == '"' || c == EOF)
611 : : break;
612 : :
613 : 10929 : obstack_1grow (&m_string_obstack, c);
614 : : }
615 : :
616 : 587 : obstack_1grow (&m_string_obstack, 0);
617 : 587 : return XOBFINISH (&m_string_obstack, char *);
618 : : }
619 : :
620 : : /* Read a braced string (a la Tcl) onto the string obstack. Caller
621 : : has scanned the leading brace. Note that unlike quoted strings,
622 : : the outermost braces _are_ included in the string constant. */
623 : :
624 : : char *
625 : 0 : md_reader::read_braced_string ()
626 : : {
627 : 0 : int c;
628 : 0 : int brace_depth = 1; /* caller-processed */
629 : 0 : unsigned long starting_read_md_lineno = get_lineno ();
630 : :
631 : 0 : obstack_1grow (&m_string_obstack, '{');
632 : 0 : while (brace_depth)
633 : : {
634 : 0 : c = read_char (); /* Read the string */
635 : :
636 : 0 : if (c == '{')
637 : 0 : brace_depth++;
638 : 0 : else if (c == '}')
639 : 0 : brace_depth--;
640 : 0 : else if (c == '\\')
641 : : {
642 : 0 : read_escape ();
643 : 0 : continue;
644 : : }
645 : 0 : else if (c == EOF)
646 : 0 : fatal_with_file_and_line
647 : 0 : ("missing closing } for opening brace on line %lu",
648 : : starting_read_md_lineno);
649 : :
650 : 0 : obstack_1grow (&m_string_obstack, c);
651 : : }
652 : :
653 : 0 : obstack_1grow (&m_string_obstack, 0);
654 : 0 : return XOBFINISH (&m_string_obstack, char *);
655 : : }
656 : :
657 : : /* Read some kind of string constant. This is the high-level routine
658 : : used by read_rtx. It handles surrounding parentheses, leading star,
659 : : and dispatch to the appropriate string constant reader. */
660 : :
661 : : char *
662 : 120 : md_reader::read_string (int star_if_braced)
663 : : {
664 : 120 : char *stringbuf;
665 : 120 : int saw_paren = 0;
666 : 120 : int c;
667 : :
668 : 120 : c = read_skip_spaces ();
669 : 120 : if (c == '(')
670 : : {
671 : 26 : saw_paren = 1;
672 : 26 : c = read_skip_spaces ();
673 : : }
674 : :
675 : 120 : file_location loc = get_current_location ();
676 : 120 : if (c == '"')
677 : 112 : stringbuf = read_quoted_string ();
678 : 8 : else if (c == '{')
679 : : {
680 : 0 : if (star_if_braced)
681 : 0 : obstack_1grow (&m_string_obstack, '*');
682 : 0 : stringbuf = read_braced_string ();
683 : : }
684 : 8 : else if (saw_paren && c == 'n')
685 : : {
686 : : /* Handle (nil) by returning NULL. */
687 : 8 : require_char ('i');
688 : 8 : require_char ('l');
689 : 8 : require_char_ws (')');
690 : 8 : return NULL;
691 : : }
692 : : else
693 : 0 : fatal_with_file_and_line ("expected `\"' or `{', found `%c'", c);
694 : :
695 : 112 : if (saw_paren)
696 : 18 : require_char_ws (')');
697 : :
698 : 112 : set_md_ptr_loc (stringbuf, loc);
699 : 112 : return stringbuf;
700 : : }
701 : :
702 : : /* Skip the rest of a construct that started at line LINENO and that
703 : : is currently nested by DEPTH levels of parentheses. */
704 : :
705 : : void
706 : 0 : md_reader::read_skip_construct (int depth, file_location loc)
707 : : {
708 : 0 : struct md_name name;
709 : 0 : int c;
710 : :
711 : 0 : do
712 : : {
713 : 0 : c = read_skip_spaces ();
714 : 0 : if (c == EOF)
715 : : {
716 : 0 : error_at (loc, "unterminated construct");
717 : 0 : exit (1);
718 : : }
719 : 0 : switch (c)
720 : : {
721 : 0 : case '(':
722 : 0 : depth++;
723 : 0 : break;
724 : :
725 : 0 : case ')':
726 : 0 : depth--;
727 : 0 : break;
728 : :
729 : : case ':':
730 : : case '[':
731 : : case ']':
732 : : case '/':
733 : : break;
734 : :
735 : 0 : case '\"':
736 : 0 : case '{':
737 : 0 : unread_char (c);
738 : 0 : read_string (false);
739 : 0 : break;
740 : :
741 : 0 : default:
742 : 0 : unread_char (c);
743 : 0 : read_name (&name);
744 : 0 : break;
745 : : }
746 : : }
747 : 0 : while (depth > 0);
748 : 0 : unread_char (c);
749 : 0 : }
750 : :
751 : : /* Given a string, return the number of comma-separated elements in it.
752 : : Return 0 for the null string. */
753 : :
754 : : int
755 : 0 : n_comma_elts (const char *s)
756 : : {
757 : 0 : int n;
758 : :
759 : 0 : if (*s == '\0')
760 : : return 0;
761 : :
762 : 0 : for (n = 1; *s; s++)
763 : 0 : if (*s == ',')
764 : 0 : n++;
765 : :
766 : : return n;
767 : : }
768 : :
769 : : /* Given a pointer to a (char *), return a pointer to the beginning of the
770 : : next comma-separated element in the string. Advance the pointer given
771 : : to the end of that element. Return NULL if at end of string. Caller
772 : : is responsible for copying the string if necessary. White space between
773 : : a comma and an element is ignored. */
774 : :
775 : : const char *
776 : 0 : scan_comma_elt (const char **pstr)
777 : : {
778 : 0 : const char *start;
779 : 0 : const char *p = *pstr;
780 : :
781 : 0 : if (*p == ',')
782 : 0 : p++;
783 : 0 : while (ISSPACE (*p))
784 : 0 : p++;
785 : :
786 : 0 : if (*p == '\0')
787 : : return NULL;
788 : :
789 : : start = p;
790 : :
791 : 0 : while (*p != ',' && *p != '\0')
792 : 0 : p++;
793 : :
794 : 0 : *pstr = p;
795 : 0 : return start;
796 : : }
797 : :
798 : : /* Convert STRING to uppercase. */
799 : :
800 : : void
801 : 0 : upcase_string (char *string)
802 : : {
803 : 0 : int i;
804 : :
805 : 0 : for (i = 0; string[i]; i++)
806 : 0 : string[i] = TOUPPER (string[i]);
807 : 0 : }
808 : :
809 : : /* Add a NAME = VALUE definition to md_constants-style hash table DEFS,
810 : : where both NAME and VALUE are malloc()ed strings. PARENT_ENUM is the
811 : : enum to which NAME belongs, or null if NAME is a stand-alone constant. */
812 : :
813 : : static struct md_constant *
814 : 0 : add_constant (htab_t defs, char *name, char *value,
815 : : struct enum_type *parent_enum)
816 : : {
817 : 0 : struct md_constant *def, tmp_def;
818 : 0 : void **entry_ptr;
819 : :
820 : 0 : tmp_def.name = name;
821 : 0 : entry_ptr = htab_find_slot (defs, &tmp_def, INSERT);
822 : 0 : if (*entry_ptr)
823 : : {
824 : 0 : def = (struct md_constant *) *entry_ptr;
825 : 0 : if (strcmp (def->value, value) != 0)
826 : 0 : fatal_with_file_and_line ("redefinition of `%s', was `%s', now `%s'",
827 : : def->name, def->value, value);
828 : 0 : else if (parent_enum || def->parent_enum)
829 : 0 : fatal_with_file_and_line ("redefinition of `%s'", def->name);
830 : 0 : free (name);
831 : 0 : free (value);
832 : : }
833 : : else
834 : : {
835 : 0 : def = XNEW (struct md_constant);
836 : 0 : def->name = name;
837 : 0 : def->value = value;
838 : 0 : def->parent_enum = parent_enum;
839 : 0 : *entry_ptr = def;
840 : : }
841 : 0 : return def;
842 : : }
843 : :
844 : : /* Process a define_constants directive, starting with the optional space
845 : : after the "define_constants". */
846 : :
847 : : void
848 : 0 : md_reader::handle_constants ()
849 : : {
850 : 0 : int c;
851 : 0 : htab_t defs;
852 : :
853 : 0 : require_char_ws ('[');
854 : :
855 : : /* Disable constant expansion during definition processing. */
856 : 0 : defs = m_md_constants;
857 : 0 : m_md_constants = 0;
858 : 0 : while ( (c = read_skip_spaces ()) != ']')
859 : : {
860 : 0 : struct md_name name, value;
861 : :
862 : 0 : if (c != '(')
863 : 0 : fatal_expected_char ('(', c);
864 : :
865 : 0 : read_name (&name);
866 : 0 : read_name (&value);
867 : 0 : add_constant (defs, xstrdup (name.string), xstrdup (value.string), 0);
868 : :
869 : 0 : require_char_ws (')');
870 : : }
871 : 0 : m_md_constants = defs;
872 : 0 : }
873 : :
874 : : /* For every constant definition, call CALLBACK with two arguments:
875 : : a pointer a pointer to the constant definition and INFO.
876 : : Stop when CALLBACK returns zero. */
877 : :
878 : : void
879 : 0 : md_reader::traverse_md_constants (htab_trav callback, void *info)
880 : : {
881 : 0 : htab_traverse (get_md_constants (), callback, info);
882 : 0 : }
883 : :
884 : : /* Return a malloc()ed decimal string that represents number NUMBER. */
885 : :
886 : : static char *
887 : 0 : md_decimal_string (int number)
888 : : {
889 : : /* A safe overestimate. +1 for sign, +1 for null terminator. */
890 : 0 : char buffer[sizeof (int) * CHAR_BIT + 1 + 1];
891 : :
892 : 0 : sprintf (buffer, "%d", number);
893 : 0 : return xstrdup (buffer);
894 : : }
895 : :
896 : : /* Process a define_enum or define_c_enum directive, starting with
897 : : the optional space after the "define_enum". LINENO is the line
898 : : number on which the directive started and MD_P is true if the
899 : : directive is a define_enum rather than a define_c_enum. */
900 : :
901 : : void
902 : 0 : md_reader::handle_enum (file_location loc, bool md_p)
903 : : {
904 : 0 : char *enum_name, *value_name;
905 : 0 : unsigned int cur_value;
906 : 0 : struct md_name name, value;
907 : 0 : struct enum_type *def;
908 : 0 : struct enum_value *ev;
909 : 0 : void **slot;
910 : 0 : int c;
911 : :
912 : 0 : enum_name = read_string (false);
913 : 0 : slot = htab_find_slot (m_enum_types, &enum_name, INSERT);
914 : 0 : if (*slot)
915 : : {
916 : 0 : def = (struct enum_type *) *slot;
917 : 0 : if (def->md_p != md_p)
918 : 0 : error_at (loc, "redefining `%s' as a different type of enum",
919 : : enum_name);
920 : : }
921 : : else
922 : : {
923 : 0 : def = XNEW (struct enum_type);
924 : 0 : def->name = enum_name;
925 : 0 : def->md_p = md_p;
926 : 0 : def->values = 0;
927 : 0 : def->tail_ptr = &def->values;
928 : 0 : def->num_values = 0;
929 : 0 : *slot = def;
930 : : }
931 : :
932 : 0 : cur_value = def->num_values;
933 : 0 : require_char_ws ('[');
934 : :
935 : 0 : while ((c = read_skip_spaces ()) != ']')
936 : : {
937 : 0 : if (c == EOF)
938 : : {
939 : 0 : error_at (loc, "unterminated construct");
940 : 0 : exit (1);
941 : : }
942 : 0 : if (c == '(')
943 : : {
944 : 0 : read_name (&name);
945 : 0 : read_name (&value);
946 : 0 : require_char_ws (')');
947 : 0 : cur_value = atoi (value.string);
948 : : }
949 : : else
950 : : {
951 : 0 : unread_char (c);
952 : 0 : read_name (&name);
953 : : }
954 : :
955 : 0 : ev = XNEW (struct enum_value);
956 : 0 : ev->next = 0;
957 : 0 : if (md_p)
958 : : {
959 : 0 : value_name = concat (def->name, "_", name.string, NULL);
960 : 0 : upcase_string (value_name);
961 : 0 : ev->name = xstrdup (name.string);
962 : : }
963 : : else
964 : : {
965 : 0 : value_name = xstrdup (name.string);
966 : 0 : ev->name = value_name;
967 : : }
968 : 0 : ev->def = add_constant (get_md_constants (), value_name,
969 : : md_decimal_string (cur_value), def);
970 : :
971 : 0 : *def->tail_ptr = ev;
972 : 0 : def->tail_ptr = &ev->next;
973 : 0 : def->num_values++;
974 : 0 : cur_value++;
975 : : }
976 : 0 : }
977 : :
978 : : /* Try to find the definition of the given enum. Return null on failure. */
979 : :
980 : : struct enum_type *
981 : 0 : md_reader::lookup_enum_type (const char *name)
982 : : {
983 : 0 : return (struct enum_type *) htab_find (m_enum_types, &name);
984 : : }
985 : :
986 : : /* For every enum definition, call CALLBACK with two arguments:
987 : : a pointer to the constant definition and INFO. Stop when CALLBACK
988 : : returns zero. */
989 : :
990 : : void
991 : 0 : md_reader::traverse_enum_types (htab_trav callback, void *info)
992 : : {
993 : 0 : htab_traverse (m_enum_types, callback, info);
994 : 0 : }
995 : :
996 : :
997 : : /* Constructor for md_reader. */
998 : :
999 : 94 : md_reader::md_reader (bool compact)
1000 : 94 : : m_compact (compact),
1001 : 94 : m_toplevel_fname (NULL),
1002 : 94 : m_base_dir (NULL),
1003 : 94 : m_read_md_file (NULL),
1004 : 94 : m_read_md_filename (NULL),
1005 : 94 : m_read_md_lineno (0),
1006 : 94 : m_read_md_colno (0),
1007 : 94 : m_first_dir_md_include (NULL),
1008 : 94 : m_last_dir_md_include_ptr (&m_first_dir_md_include),
1009 : 94 : m_first_line (0),
1010 : 94 : m_last_line (0),
1011 : 94 : m_first_overload (NULL),
1012 : 94 : m_next_overload_ptr (&m_first_overload),
1013 : 94 : m_overloads_htab (NULL)
1014 : : {
1015 : : /* Set the global singleton pointer. */
1016 : 94 : md_reader_ptr = this;
1017 : :
1018 : 94 : obstack_init (&m_string_obstack);
1019 : :
1020 : 94 : m_ptr_locs = htab_create (161, leading_ptr_hash, leading_ptr_eq_p, 0);
1021 : 94 : obstack_init (&m_ptr_loc_obstack);
1022 : :
1023 : 94 : m_joined_conditions = htab_create (161, leading_ptr_hash, leading_ptr_eq_p, 0);
1024 : 94 : obstack_init (&m_joined_conditions_obstack);
1025 : :
1026 : 94 : m_md_constants = htab_create (31, leading_string_hash,
1027 : : leading_string_eq_p, (htab_del) 0);
1028 : :
1029 : 94 : m_enum_types = htab_create (31, leading_string_hash,
1030 : : leading_string_eq_p, (htab_del) 0);
1031 : :
1032 : : /* Unlock the stdio streams. */
1033 : 94 : unlock_std_streams ();
1034 : 94 : }
1035 : :
1036 : : /* md_reader's destructor. */
1037 : :
1038 : 93 : md_reader::~md_reader ()
1039 : : {
1040 : 93 : free (m_base_dir);
1041 : :
1042 : 93 : htab_delete (m_enum_types);
1043 : :
1044 : 93 : htab_delete (m_md_constants);
1045 : :
1046 : 93 : obstack_free (&m_joined_conditions_obstack, NULL);
1047 : 93 : htab_delete (m_joined_conditions);
1048 : :
1049 : 93 : obstack_free (&m_ptr_loc_obstack, NULL);
1050 : 93 : htab_delete (m_ptr_locs);
1051 : :
1052 : 93 : obstack_free (&m_string_obstack, NULL);
1053 : :
1054 : : /* Clear the global singleton pointer. */
1055 : 93 : md_reader_ptr = NULL;
1056 : 93 : }
1057 : :
1058 : : /* Process an "include" directive, starting with the optional space
1059 : : after the "include". Read in the file and use HANDLE_DIRECTIVE
1060 : : to process each unknown directive. LINENO is the line number on
1061 : : which the "include" occurred. */
1062 : :
1063 : : void
1064 : 0 : md_reader::handle_include (file_location loc)
1065 : : {
1066 : 0 : const char *filename;
1067 : 0 : const char *old_filename;
1068 : 0 : int old_lineno, old_colno;
1069 : 0 : char *pathname;
1070 : 0 : FILE *input_file, *old_file;
1071 : :
1072 : 0 : filename = read_string (false);
1073 : 0 : input_file = NULL;
1074 : :
1075 : : /* If the specified file name is absolute, skip the include stack. */
1076 : 0 : if (!IS_ABSOLUTE_PATH (filename))
1077 : : {
1078 : 0 : struct file_name_list *stackp;
1079 : :
1080 : : /* Search the directory path, trying to open the file. */
1081 : 0 : for (stackp = m_first_dir_md_include; stackp; stackp = stackp->next)
1082 : : {
1083 : 0 : static const char sep[2] = { DIR_SEPARATOR, '\0' };
1084 : :
1085 : 0 : pathname = concat (stackp->fname, sep, filename, NULL);
1086 : 0 : input_file = fopen (pathname, "r");
1087 : 0 : if (input_file != NULL)
1088 : : break;
1089 : 0 : free (pathname);
1090 : : }
1091 : : }
1092 : :
1093 : : /* If we haven't managed to open the file yet, try combining the
1094 : : filename with BASE_DIR. */
1095 : 0 : if (input_file == NULL)
1096 : : {
1097 : 0 : if (m_base_dir)
1098 : 0 : pathname = concat (m_base_dir, filename, NULL);
1099 : : else
1100 : 0 : pathname = xstrdup (filename);
1101 : 0 : input_file = fopen (pathname, "r");
1102 : : }
1103 : :
1104 : 0 : if (input_file == NULL)
1105 : : {
1106 : 0 : free (pathname);
1107 : 0 : error_at (loc, "include file `%s' not found", filename);
1108 : 0 : return;
1109 : : }
1110 : :
1111 : : /* Save the old cursor. Note that the LINENO argument to this
1112 : : function is the beginning of the include statement, while
1113 : : read_md_lineno has already been advanced. */
1114 : 0 : old_file = m_read_md_file;
1115 : 0 : old_filename = m_read_md_filename;
1116 : 0 : old_lineno = m_read_md_lineno;
1117 : 0 : old_colno = m_read_md_colno;
1118 : :
1119 : 0 : if (include_callback)
1120 : 0 : include_callback (pathname);
1121 : :
1122 : 0 : m_read_md_file = input_file;
1123 : 0 : m_read_md_filename = pathname;
1124 : :
1125 : 0 : handle_file ();
1126 : :
1127 : : /* Restore the old cursor. */
1128 : 0 : m_read_md_file = old_file;
1129 : 0 : m_read_md_filename = old_filename;
1130 : 0 : m_read_md_lineno = old_lineno;
1131 : 0 : m_read_md_colno = old_colno;
1132 : :
1133 : : /* Do not free the pathname. It is attached to the various rtx
1134 : : queue elements. */
1135 : : }
1136 : :
1137 : : /* Process the current file, assuming that read_md_file and
1138 : : read_md_filename are valid. Use HANDLE_DIRECTIVE to handle
1139 : : unknown directives. */
1140 : :
1141 : : void
1142 : 94 : md_reader::handle_file ()
1143 : : {
1144 : 94 : struct md_name directive;
1145 : 94 : int c;
1146 : :
1147 : 94 : m_read_md_lineno = 1;
1148 : 94 : m_read_md_colno = 0;
1149 : 187 : while ((c = read_skip_spaces ()) != EOF)
1150 : : {
1151 : 94 : file_location loc = get_current_location ();
1152 : 94 : if (c != '(')
1153 : 0 : fatal_expected_char ('(', c);
1154 : :
1155 : 94 : read_name (&directive);
1156 : 94 : if (strcmp (directive.string, "define_constants") == 0)
1157 : 0 : handle_constants ();
1158 : 94 : else if (strcmp (directive.string, "define_enum") == 0)
1159 : 0 : handle_enum (loc, true);
1160 : 94 : else if (strcmp (directive.string, "define_c_enum") == 0)
1161 : 0 : handle_enum (loc, false);
1162 : 94 : else if (strcmp (directive.string, "include") == 0)
1163 : 0 : handle_include (loc);
1164 : : else
1165 : 94 : handle_unknown_directive (loc, directive.string);
1166 : :
1167 : 93 : require_char_ws (')');
1168 : : }
1169 : 93 : fclose (m_read_md_file);
1170 : 93 : }
1171 : :
1172 : : /* Like handle_file, but for top-level files. Set up m_toplevel_fname
1173 : : and m_base_dir accordingly. */
1174 : :
1175 : : void
1176 : 94 : md_reader::handle_toplevel_file ()
1177 : : {
1178 : 94 : const char *base;
1179 : :
1180 : 94 : m_toplevel_fname = m_read_md_filename;
1181 : 94 : base = lbasename (m_toplevel_fname);
1182 : 94 : if (base == m_toplevel_fname)
1183 : 0 : m_base_dir = NULL;
1184 : : else
1185 : 94 : m_base_dir = xstrndup (m_toplevel_fname, base - m_toplevel_fname);
1186 : :
1187 : 94 : handle_file ();
1188 : 93 : }
1189 : :
1190 : : file_location
1191 : 9056 : md_reader::get_current_location () const
1192 : : {
1193 : 9056 : return file_location (m_read_md_filename, m_read_md_lineno, m_read_md_colno);
1194 : : }
1195 : :
1196 : : /* Parse a -I option with argument ARG. */
1197 : :
1198 : : void
1199 : 0 : md_reader::add_include_path (const char *arg)
1200 : : {
1201 : 0 : struct file_name_list *dirtmp;
1202 : :
1203 : 0 : dirtmp = XNEW (struct file_name_list);
1204 : 0 : dirtmp->next = 0;
1205 : 0 : dirtmp->fname = arg;
1206 : 0 : *m_last_dir_md_include_ptr = dirtmp;
1207 : 0 : m_last_dir_md_include_ptr = &dirtmp->next;
1208 : 0 : }
1209 : :
1210 : : #ifdef GENERATOR_FILE
1211 : :
1212 : : /* The main routine for reading .md files. Try to process all the .md
1213 : : files specified on the command line and return true if no error occurred.
1214 : :
1215 : : ARGC and ARGV are the arguments to main.
1216 : :
1217 : : PARSE_OPT, if nonnull, is passed all unknown command-line arguments.
1218 : : It should return true if it recognizes the argument or false if a
1219 : : generic error should be reported. */
1220 : :
1221 : : bool
1222 : : md_reader::read_md_files (int argc, const char **argv,
1223 : : bool (*parse_opt) (const char *))
1224 : : {
1225 : : int i;
1226 : : bool no_more_options;
1227 : : bool already_read_stdin;
1228 : : int num_files;
1229 : :
1230 : : /* First we loop over all the options. */
1231 : : for (i = 1; i < argc; i++)
1232 : : if (argv[i][0] == '-')
1233 : : {
1234 : : /* An argument consisting of exactly one dash is a request to
1235 : : read stdin. This will be handled in the second loop. */
1236 : : if (argv[i][1] == '\0')
1237 : : continue;
1238 : :
1239 : : /* An argument consisting of just two dashes causes option
1240 : : parsing to cease. */
1241 : : if (argv[i][1] == '-' && argv[i][2] == '\0')
1242 : : break;
1243 : :
1244 : : if (argv[i][1] == 'I')
1245 : : {
1246 : : if (argv[i][2] != '\0')
1247 : : add_include_path (argv[i] + 2);
1248 : : else if (++i < argc)
1249 : : add_include_path (argv[i]);
1250 : : else
1251 : : fatal ("directory name missing after -I option");
1252 : : continue;
1253 : : }
1254 : :
1255 : : /* The program may have provided a callback so it can
1256 : : accept its own options. */
1257 : : if (parse_opt && parse_opt (argv[i]))
1258 : : continue;
1259 : :
1260 : : fatal ("invalid option `%s'", argv[i]);
1261 : : }
1262 : :
1263 : : /* Now loop over all input files. */
1264 : : num_files = 0;
1265 : : no_more_options = false;
1266 : : already_read_stdin = false;
1267 : : for (i = 1; i < argc; i++)
1268 : : {
1269 : : if (argv[i][0] == '-')
1270 : : {
1271 : : if (argv[i][1] == '\0')
1272 : : {
1273 : : /* Read stdin. */
1274 : : if (already_read_stdin)
1275 : : fatal ("cannot read standard input twice");
1276 : :
1277 : : m_read_md_file = stdin;
1278 : : m_read_md_filename = "<stdin>";
1279 : : handle_toplevel_file ();
1280 : : already_read_stdin = true;
1281 : : continue;
1282 : : }
1283 : : else if (argv[i][1] == '-' && argv[i][2] == '\0')
1284 : : {
1285 : : /* No further arguments are to be treated as options. */
1286 : : no_more_options = true;
1287 : : continue;
1288 : : }
1289 : : else if (!no_more_options)
1290 : : continue;
1291 : : }
1292 : :
1293 : : /* If we get here we are looking at a non-option argument, i.e.
1294 : : a file to be processed. */
1295 : : m_read_md_filename = argv[i];
1296 : : m_read_md_file = fopen (m_read_md_filename, "r");
1297 : : if (m_read_md_file == 0)
1298 : : {
1299 : : perror (m_read_md_filename);
1300 : : return false;
1301 : : }
1302 : : handle_toplevel_file ();
1303 : : num_files++;
1304 : : }
1305 : :
1306 : : /* If we get to this point without having seen any files to process,
1307 : : read the standard input now. */
1308 : : if (num_files == 0 && !already_read_stdin)
1309 : : {
1310 : : m_read_md_file = stdin;
1311 : : m_read_md_filename = "<stdin>";
1312 : : handle_toplevel_file ();
1313 : : }
1314 : :
1315 : : return !have_error;
1316 : : }
1317 : :
1318 : : #endif /* #ifdef GENERATOR_FILE */
1319 : :
1320 : : /* Read FILENAME. */
1321 : :
1322 : : bool
1323 : 76 : md_reader::read_file (const char *filename)
1324 : : {
1325 : 76 : m_read_md_filename = filename;
1326 : 76 : m_read_md_file = fopen (m_read_md_filename, "r");
1327 : 76 : if (m_read_md_file == 0)
1328 : : {
1329 : 0 : perror (m_read_md_filename);
1330 : 0 : return false;
1331 : : }
1332 : 76 : handle_toplevel_file ();
1333 : 76 : return !have_error;
1334 : : }
1335 : :
1336 : : /* Read FILENAME, filtering to just the given lines. */
1337 : :
1338 : : bool
1339 : 18 : md_reader::read_file_fragment (const char *filename,
1340 : : int first_line,
1341 : : int last_line)
1342 : : {
1343 : 18 : m_read_md_filename = filename;
1344 : 18 : m_read_md_file = fopen (m_read_md_filename, "r");
1345 : 18 : if (m_read_md_file == 0)
1346 : : {
1347 : 0 : perror (m_read_md_filename);
1348 : 0 : return false;
1349 : : }
1350 : 18 : m_first_line = first_line;
1351 : 18 : m_last_line = last_line;
1352 : 18 : handle_toplevel_file ();
1353 : 17 : return !have_error;
1354 : : }
1355 : :
1356 : : /* class noop_reader : public md_reader */
1357 : :
1358 : : /* A dummy implementation which skips unknown directives. */
1359 : : void
1360 : 0 : noop_reader::handle_unknown_directive (file_location loc, const char *)
1361 : : {
1362 : 0 : read_skip_construct (1, loc);
1363 : 0 : }
|