White space and comments only. The devo tree prior to this delta is
[deliverable/binutils-gdb.git] / gas / app.c
1 /* Copyright (C) 1987, 1990, 1991 Free Software Foundation, Inc.
2
3 Modified by Allen Wirfs-Brock, Instantiations Inc 2/90
4 */
5 /* This is the Assembler Pre-Processor
6 Copyright (C) 1987 Free Software Foundation, Inc.
7
8 This file is part of GAS, the GNU Assembler.
9
10 GAS is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2, or (at your option)
13 any later version.
14
15 GAS is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with GAS; see the file COPYING. If not, write to
22 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
23
24 /* App, the assembler pre-processor. This pre-processor strips out excess
25 spaces, turns single-quoted characters into a decimal constant, and turns
26 # <number> <filename> <garbage> into a .line <number>\n.app-file <filename> pair.
27 This needs better error-handling.
28 */
29
30 #include <stdio.h>
31 #include "as.h" /* For BAD_CASE() only */
32
33 #if !defined(__STDC__) && !defined(const)
34 #define const /* Nothing */
35 #endif
36
37 static char lex [256];
38 static char symbol_chars[] =
39 "$._ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
40
41 /* These will go in BSS if not defined elsewhere, producing empty strings. */
42 extern const char comment_chars[];
43 extern const char line_comment_chars[];
44 extern const char line_separator_chars[];
45
46 #define LEX_IS_SYMBOL_COMPONENT 1
47 #define LEX_IS_WHITESPACE 2
48 #define LEX_IS_LINE_SEPARATOR 3
49 #define LEX_IS_COMMENT_START 4
50 #define LEX_IS_LINE_COMMENT_START 5
51 #define LEX_IS_TWOCHAR_COMMENT_1ST 6
52 #define LEX_IS_TWOCHAR_COMMENT_2ND 7
53 #define LEX_IS_STRINGQUOTE 8
54 #define LEX_IS_COLON 9
55 #define LEX_IS_NEWLINE 10
56 #define LEX_IS_ONECHAR_QUOTE 11
57 #define IS_SYMBOL_COMPONENT(c) (lex[c] == LEX_IS_SYMBOL_COMPONENT)
58 #define IS_WHITESPACE(c) (lex[c] == LEX_IS_WHITESPACE)
59 #define IS_LINE_SEPARATOR(c) (lex[c] == LEX_IS_LINE_SEPARATOR)
60 #define IS_COMMENT(c) (lex[c] == LEX_IS_COMMENT_START)
61 #define IS_LINE_COMMENT(c) (lex[c] == LEX_IS_LINE_COMMENT_START)
62 #define IS_NEWLINE(c) (lex[c] == LEX_IS_NEWLINE)
63
64 /* FIXME-soon: The entire lexer/parser thingy should be
65 built statically at compile time rather than dynamically
66 each and every time the assembler is run. xoxorich. */
67
68 void do_scrub_begin() {
69 const char *p;
70
71 lex[' '] = LEX_IS_WHITESPACE;
72 lex['\t'] = LEX_IS_WHITESPACE;
73 lex['\n'] = LEX_IS_NEWLINE;
74 lex[';'] = LEX_IS_LINE_SEPARATOR;
75 lex['"'] = LEX_IS_STRINGQUOTE;
76 lex['\''] = LEX_IS_ONECHAR_QUOTE;
77 lex[':'] = LEX_IS_COLON;
78
79 /* Note that these override the previous defaults, e.g. if ';'
80 is a comment char, then it isn't a line separator. */
81 for (p = symbol_chars; *p; ++p) {
82 lex[*p] = LEX_IS_SYMBOL_COMPONENT;
83 } /* declare symbol characters */
84
85 for (p = line_comment_chars; *p; p++) {
86 lex[*p] = LEX_IS_LINE_COMMENT_START;
87 } /* declare line comment chars */
88
89 for (p = comment_chars; *p; p++) {
90 lex[*p] = LEX_IS_COMMENT_START;
91 } /* declare comment chars */
92
93 for (p = line_separator_chars; *p; p++) {
94 lex[*p] = LEX_IS_LINE_SEPARATOR;
95 } /* declare line separators */
96
97 /* Only allow slash-star comments if slash is not in use */
98 if (lex['/'] == 0) {
99 lex['/'] = LEX_IS_TWOCHAR_COMMENT_1ST;
100 }
101 /* FIXME-soon. This is a bad hack but otherwise, we
102 can't do c-style comments when '/' is a line
103 comment char. xoxorich. */
104 if (lex['*'] == 0) {
105 lex['*'] = LEX_IS_TWOCHAR_COMMENT_2ND;
106 }
107 } /* do_scrub_begin() */
108
109 FILE *scrub_file;
110
111 int scrub_from_file() {
112 return getc(scrub_file);
113 }
114
115 void scrub_to_file(ch)
116 int ch;
117 {
118 ungetc(ch,scrub_file);
119 } /* scrub_to_file() */
120
121 char *scrub_string;
122 char *scrub_last_string;
123
124 int scrub_from_string() {
125 return scrub_string == scrub_last_string ? EOF : *scrub_string++;
126 } /* scrub_from_string() */
127
128 void scrub_to_string(ch)
129 int ch;
130 {
131 *--scrub_string=ch;
132 } /* scrub_to_string() */
133
134 /* Saved state of the scrubber */
135 static int state;
136 static int old_state;
137 static char *out_string;
138 static char out_buf[20];
139 static int add_newlines = 0;
140
141 /* Data structure for saving the state of app across #include's. Note that
142 app is called asynchronously to the parsing of the .include's, so our
143 state at the time .include is interpreted is completely unrelated.
144 That's why we have to save it all. */
145
146 struct app_save {
147 int state;
148 int old_state;
149 char *out_string;
150 char out_buf[sizeof (out_buf)];
151 int add_newlines;
152 char *scrub_string;
153 char *scrub_last_string;
154 FILE *scrub_file;
155 };
156
157 char *app_push() {
158 register struct app_save *saved;
159
160 saved = (struct app_save *) xmalloc(sizeof (*saved));
161 saved->state = state;
162 saved->old_state = old_state;
163 saved->out_string = out_string;
164 bcopy(saved->out_buf, out_buf, sizeof(out_buf));
165 saved->add_newlines = add_newlines;
166 saved->scrub_string = scrub_string;
167 saved->scrub_last_string = scrub_last_string;
168 saved->scrub_file = scrub_file;
169
170 /* do_scrub_begin() is not useful, just wastes time. */
171 return (char *)saved;
172 }
173
174 void app_pop(arg)
175 char *arg;
176 {
177 register struct app_save *saved = (struct app_save *)arg;
178
179 /* There is no do_scrub_end (). */
180 state = saved->state;
181 old_state = saved->old_state;
182 out_string = saved->out_string;
183 bcopy (out_buf, saved->out_buf, sizeof (out_buf));
184 add_newlines = saved->add_newlines;
185 scrub_string = saved->scrub_string;
186 scrub_last_string = saved->scrub_last_string;
187 scrub_file = saved->scrub_file;
188
189 free (arg);
190 } /* app_pop() */
191
192 int do_scrub_next_char(get,unget)
193 int (*get)();
194 void (*unget)();
195 {
196 /*State 0: beginning of normal line
197 1: After first whitespace on line (flush more white)
198 2: After first non-white (opcode) on line (keep 1white)
199 3: after second white on line (into operands) (flush white)
200 4: after putting out a .line, put out digits
201 5: parsing a string, then go to old-state
202 6: putting out \ escape in a "d string.
203 7: After putting out a .app-file, put out string.
204 8: After putting out a .app-file string, flush until newline.
205 -1: output string in out_string and go to the state in old_state
206 -2: flush text until a '*' '/' is seen, then go to state old_state
207 */
208
209 register int ch, ch2 = 0;
210
211 switch (state) {
212 case -1:
213 ch= *out_string++;
214 if(*out_string==0) {
215 state=old_state;
216 old_state=3;
217 }
218 return ch;
219
220 case -2:
221 for(;;) {
222 do {
223 ch=(*get)();
224 } while(ch!=EOF && ch!='\n' && ch!='*');
225 if(ch=='\n' || ch==EOF)
226 return ch;
227
228 /* At this point, ch must be a '*' */
229 while ( (ch=(*get)()) == '*' ){
230 ;
231 }
232 if(ch==EOF || ch=='/')
233 break;
234 (*unget)(ch);
235 }
236 state=old_state;
237 return ' ';
238
239 case 4:
240 ch=(*get)();
241 if(ch==EOF || (ch>='0' && ch<='9'))
242 return ch;
243 else {
244 while(ch!=EOF && IS_WHITESPACE(ch))
245 ch=(*get)();
246 if(ch=='"') {
247 (*unget)(ch);
248 out_string="\n.app-file ";
249 old_state=7;
250 state= -1;
251 return *out_string++;
252 } else {
253 while(ch!=EOF && ch!='\n')
254 ch=(*get)();
255 return ch;
256 }
257 }
258
259 case 5:
260 ch=(*get)();
261 if(ch=='"') {
262 state=old_state;
263 return '"';
264 } else if(ch=='\\') {
265 state=6;
266 return ch;
267 } else if(ch==EOF) {
268 as_warn("End of file in string: inserted '\"'");
269 state=old_state;
270 (*unget)('\n');
271 return '"';
272 } else {
273 return ch;
274 }
275
276 case 6:
277 state=5;
278 ch=(*get)();
279 switch(ch) {
280 /* This is neet. Turn "string
281 more string" into "string\n more string"
282 */
283 case '\n':
284 (*unget)('n');
285 add_newlines++;
286 return '\\';
287
288 case '"':
289 case '\\':
290 case 'b':
291 case 'f':
292 case 'n':
293 case 'r':
294 case 't':
295 #ifdef BACKSLASH_V
296 case 'v':
297 #endif /* BACKSLASH_V */
298 case '0':
299 case '1':
300 case '2':
301 case '3':
302 case '4':
303 case '5':
304 case '6':
305 case '7':
306 break;
307
308 #ifdef ONLY_STANDARD_ESCAPES
309 default:
310 as_warn("Unknown escape '\\%c' in string: Ignored",ch);
311 break;
312 #else /* ONLY_STANDARD_ESCAPES */
313 default:
314 /* Accept \x as x for any x */
315 break;
316 #endif /* ONLY_STANDARD_ESCAPES */
317
318 case EOF:
319 as_warn("End of file in string: '\"' inserted");
320 return '"';
321 }
322 return ch;
323
324 case 7:
325 ch=(*get)();
326 state=5;
327 old_state=8;
328 return ch;
329
330 case 8:
331 do ch= (*get)();
332 while(ch!='\n');
333 state=0;
334 return ch;
335 }
336
337 /* OK, we are somewhere in states 0 through 4 */
338
339 /* flushchar: */
340 ch=(*get)();
341 recycle:
342 if (ch == EOF) {
343 if (state != 0)
344 as_warn("End of file not at end of a line: Newline inserted.");
345 return ch;
346 }
347
348 switch (lex[ch]) {
349 case LEX_IS_WHITESPACE:
350 do ch=(*get)();
351 while(ch!=EOF && IS_WHITESPACE(ch));
352 if(ch==EOF)
353 return ch;
354 if(IS_COMMENT(ch) || (state==0 && IS_LINE_COMMENT(ch)) || ch=='/' || IS_LINE_SEPARATOR(ch)) {
355 goto recycle;
356 }
357 switch (state) {
358 case 0: state++; goto recycle; /* Punted leading sp */
359 case 1: BAD_CASE(state); /* We can't get here */
360 case 2: state++; (*unget)(ch); return ' '; /* Sp after opco */
361 case 3: goto recycle; /* Sp in operands */
362 default: BAD_CASE(state);
363 }
364 break;
365
366 case LEX_IS_TWOCHAR_COMMENT_1ST:
367 ch2=(*get)();
368 if (ch2 != EOF && lex[ch2] == LEX_IS_TWOCHAR_COMMENT_2ND) {
369 for(;;) {
370 do {
371 ch2=(*get)();
372 if(ch2 != EOF && IS_NEWLINE(ch2))
373 add_newlines++;
374 } while(ch2!=EOF &&
375 (lex[ch2] != LEX_IS_TWOCHAR_COMMENT_2ND));
376
377 while (ch2!=EOF &&
378 (lex[ch2] == LEX_IS_TWOCHAR_COMMENT_2ND)){
379 ch2=(*get)();
380 }
381
382 if(ch2==EOF
383 || lex[ch2] == LEX_IS_TWOCHAR_COMMENT_1ST)
384 break;
385 (*unget)(ch);
386 }
387 if(ch2==EOF)
388 as_warn("End of file in multiline comment");
389
390 ch = ' ';
391 goto recycle;
392 } else {
393 if(ch2!=EOF)
394 (*unget)(ch2);
395 return ch;
396 }
397 break;
398
399 case LEX_IS_STRINGQUOTE:
400 old_state=state;
401 state=5;
402 return ch;
403
404 #ifndef IEEE_STYLE
405 case LEX_IS_ONECHAR_QUOTE:
406 ch=(*get)();
407 if(ch==EOF) {
408 as_warn("End-of-file after a one-character quote; \000 inserted");
409 ch=0;
410 }
411 sprintf(out_buf,"%d", (int)(unsigned char)ch);
412
413 /* None of these 'x constants for us. We want 'x'.
414 */
415 if ( (ch=(*get)()) != '\'' ) {
416 #ifdef REQUIRE_CHAR_CLOSE_QUOTE
417 as_warn("Missing close quote: (assumed)");
418 #else
419 (*unget)(ch);
420 #endif
421 }
422
423 old_state=state;
424 state= -1;
425 out_string=out_buf;
426 return *out_string++;
427 #endif
428 case LEX_IS_COLON:
429 if(state!=3)
430 state=0;
431 return ch;
432
433 case LEX_IS_NEWLINE:
434 /* Roll out a bunch of newlines from inside comments, etc. */
435 if(add_newlines) {
436 --add_newlines;
437 (*unget)(ch);
438 }
439 /* fall thru into... */
440
441 case LEX_IS_LINE_SEPARATOR:
442 state=0;
443 return ch;
444
445 case LEX_IS_LINE_COMMENT_START:
446 if (state != 0) /* Not at start of line, act normal */
447 goto de_fault;
448
449 /* FIXME-someday: The two character comment stuff was badly
450 thought out. On i386, we want '/' as line comment start
451 AND we want C style comments. hence this hack. The
452 whole lexical process should be reworked. xoxorich. */
453
454 if (ch == '/' && (ch2 = (*get)()) == '*') {
455 state = -2;
456 return(do_scrub_next_char(get, unget));
457 } else {
458 (*unget)(ch2);
459 } /* bad hack */
460
461 do ch=(*get)();
462 while(ch!=EOF && IS_WHITESPACE(ch));
463 if(ch==EOF) {
464 as_warn("EOF in comment: Newline inserted");
465 return '\n';
466 }
467 if(ch<'0' || ch>'9') {
468 /* Non-numerics: Eat whole comment line */
469 while(ch!=EOF && !IS_NEWLINE(ch))
470 ch=(*get)();
471 if(ch==EOF)
472 as_warn("EOF in Comment: Newline inserted");
473 state=0;
474 return '\n';
475 }
476 /* Numerics begin comment. Perhaps CPP `# 123 "filename"' */
477 (*unget)(ch);
478 old_state=4;
479 state= -1;
480 out_string=".line ";
481 return *out_string++;
482
483 case LEX_IS_COMMENT_START:
484 do ch=(*get)();
485 while(ch!=EOF && !IS_NEWLINE(ch));
486 if(ch==EOF)
487 as_warn("EOF in comment: Newline inserted");
488 state=0;
489 return '\n';
490
491 default:
492 de_fault:
493 /* Some relatively `normal' character. */
494 if(state==0) {
495 state=2; /* Now seeing opcode */
496 return ch;
497 } else if(state==1) {
498 state=2; /* Ditto */
499 return ch;
500 } else {
501 return ch; /* Opcode or operands already */
502 }
503 }
504 return -1;
505 }
506
507 #ifdef TEST
508
509 char comment_chars[] = "|";
510 char line_comment_chars[] = "#";
511
512 main()
513 {
514 int ch;
515
516 app_begin();
517 while((ch=do_scrub_next_char(stdin))!=EOF)
518 putc(ch,stdout);
519 }
520
521 as_warn(str)
522 char *str;
523 {
524 fputs(str,stderr);
525 putc('\n',stderr);
526 }
527 #endif
528
529 /*
530 * Local Variables:
531 * comment-column: 0
532 * fill-column: 131
533 * End:
534 */
535
536 /* end of app.c */
This page took 0.039997 seconds and 4 git commands to generate.