Subversion Repositories DevTools

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
263 dpurdie 1
/*
2
	CPP V5 -- Token parsing routines.
3
 
4
	source:  tok.c
5
	started: October 7, 1985
6
	version: May 26, 1988; July 21, 1988
7
 
8
	Written by Edward K. Ream.
9
	This software is in the public domain.
10
 
11
	See the read.me file for disclaimer and other information.
12
*/
13
 
14
#include "cpp.h"
15
 
16
/* Declare internal variables and routines. */
17
static int	 is_base_digit	(int);
18
static void	 scan_number	(int);
19
static en_tokens t_int		(void);
20
 
21
/*
22
	Return >= 0 if ch is a digit in the indicated base.
23
	Otherwise, return -1.
24
*/
25
static int
26
is_base_digit(base)
27
int base;
28
{
29
	TRACECH("is_base_digit");
30
 
31
	if (ch >= '0' && ch <= '9') {
32
		RETURN_INT("is_base_digit", ch - '0');
33
	}
34
	else if (base != 16) {
35
		RETURN_INT("is_base_digit", -1);
36
	}
37
	else if (ch >= 'a' && ch <= 'f') {
38
		RETURN_INT("is_base_digit", ch - 'a' + 10);
39
	}
40
	else if (ch >= 'A' && ch <= 'F') {
41
 		RETURN_INT("is_base_digit", ch - 'A' + 10);
42
	}
43
	else {
44
		RETURN_INT("is_base_digit", -1);
45
	}
46
}
47
 
48
/*
49
	Get value of a string of digits into t_value.
50
	Continue until a non base digit is found.
51
*/
52
static void
53
scan_number(base)
54
int base;
55
{
56
	int result;
57
 
58
	TRACECH("scan_number");
59
 
60
	t_value = 0;
61
	while (	(result = is_base_digit(base)) != -1) {
62
		t_value = ((long)base * t_value) + (long) result;
63
		sysnext();
64
	}
65
 
66
	LEAVE("scan_number");
67
}
68
 
69
/*
70
	Output a comment. Allow nested comments if nest_flag is TRUE.
71
	Surprisingly, this routine needs to be as fast as possible.
72
*/
73
void
74
copy_comment()
75
{
76
	register int clevel;
77
	int start_line;
78
	char line [LONG_DIGITS];
79
 
80
	TRACECH("copy_comment");
81
 
82
	/* Save starting line number for run-on comments. */
83
	start_line = t_line;
84
	clevel = 1;
85
 
86
	for (;;) {
87
		switch (ch) {
88
 
89
		case END_FILE:
90
			conv2s(start_line, line);
91
			err3("File ends in a comment starting at line ",
92
				line, ".");
93
			RETURN_VOID("copy_comment");
94
 
95
		case '\n':
96
			/* Keep track of line numbering. */
97
			bump_line();
98
			sysnlput();
99
			sysnext();
100
			continue;
101
 
102
		case '/':
103
 
104
			syscput(ch);
105
			sysnext();
106
			if (ch == '*') {
107
				syscput(ch);
108
				sysnext();
109
				if (nest_flag) {
110
					clevel++;
111
				}
112
			}
113
			continue;
114
 
115
		case '*':
116
			syscput(ch);
117
			sysnext();
118
			if (ch == '/') {
119
				syscput(ch);
120
				sysnext();
121
				if (--clevel == 0) {
122
					RETURN_VOID("copy_comment");
123
				}
124
			}
125
			continue;
126
 
127
 
128
		default:
129
			syscput(ch);
130
			sysnext();
131
		}
132
	}
133
}
134
 
135
void
136
skip_comment()
137
{
138
	register int clevel;
139
	int start_line;
140
	char line [LONG_DIGITS];
141
 
142
	TRACECH("skip_comment");
143
 
144
	/* Save starting line number for run-on comments. */
145
	start_line = t_line;
146
	clevel = 1;
147
 
148
	for (;;) {
149
		switch (ch) {
150
 
151
		case END_FILE:
152
			conv2s(start_line, line);
153
			err3("File ends in a comment starting at line ",
154
				line, ".");
155
			RETURN_VOID("skip_comment");
156
 
157
		case EORT:
158
			conv2s(start_line, line);
159
			err3("Macro arg ends in a comment starting at line ",
160
				line, ".");
161
			RETURN_VOID("skip_comment");
162
 
163
 
164
		case '\n':
165
			/* Keep track of line numbering. */
166
			bump_line();
167
			sysnext();
168
			continue;
169
 
170
		case '/':
171
			sysnext();
172
			if (ch == '*') {
173
				sysnext();
174
				if (nest_flag) {
175
					clevel++;
176
				}
177
			}
178
			continue;
179
 
180
		case '*':
181
			sysnext();
182
			if (ch == '/') {
183
				sysnext();
184
				if (--clevel == 0) {
185
					RETURN_VOID("skip_comment");
186
				}
187
			}
188
			continue;
189
 
190
		default:
191
			sysnext();
192
		}
193
	}
194
}
195
 
196
/*
197
	Copy an identifier into symbol[] and its length in the global t_length.
198
	Surprisingly, this routine should be as fast as possible.
199
*/
200
void
201
t_id(symbol, max_length)
202
char *	symbol;
203
int	max_length;
204
{
205
	int length = 0;
206
 
207
	TRACEPB("t_id", printf("(%p, %d)\n", symbol, max_length));
208
 
209
	max_length--;
210
	while (isid2(ch) && length < max_length) {
211
		*symbol++ = ch;
212
		length++;
213
		sysnext();
214
	}
215
	*symbol = '\0';
216
	t_length = length;
217
 
218
	if (length >= max_length) {
219
		error("Identifier too long.");
220
	}
221
 
222
	LEAVE("t_id");
223
}
224
 
225
/*
226
	Parse an integer constant (octal, decimal or hexadecimal) or float.
227
	Put the value in t_value if it is an integer.
228
 
229
	dot_flag is TRUE if a decimal point has been seen.
230
 
231
	Return the token type (INT_TOK, LONG_TOK, FLOAT_TOK).
232
 
233
	Legal integer forms:	ddd,	0ddd,	0xddd
234
	Legal float forms:	xxx.yyyE+zzz
235
 
236
	+-zzz is optional
237
	one of xxx and yyy may be omitted
238
	one of . and E may be omitted
239
*/
240
en_tokens
241
t_number(dot_flag)
242
bool dot_flag;
243
{
244
	en_tokens result;
245
	bool need_exp = FALSE;
246
 
247
	TRACECH("t_number");
248
 
249
	/* Defaults. */
250
	t_value = 0;
251
 
252
	if (dot_flag) {
253
		goto frac_part;
254
	}
255
 
256
	/* Integer part. */
257
 
258
	result = t_int();
259
	if (ch == '.') {
260
		sysnext();
261
		goto frac_part;
262
	}
263
	else if (ch == 'e' || ch == 'E') {
264
		goto exp_part;
265
	}
266
	else {
267
		RETURN_INT("t_number", result);
268
	}
269
 
270
	/* Fraction part. */
271
frac_part:	
272
	t_int();
273
 
274
exp_part:
275
	if (ch == 'e' || ch == 'E') {
276
		need_exp = TRUE;
277
		sysnext();
278
	}
279
	if (ch == '+' || ch == '-') {
280
		need_exp = TRUE;
281
		sysnext();
282
	}
283
	if (ch >= '0' && ch <= '9') {
284
		t_int();
285
	}
286
	else if (need_exp) {
287
		error("Ill formed floating constant.");
288
	}
289
 
290
	if (ch == 'l' || ch == 'L' || ch == 'f' || ch == 'F') {
291
		sysnext();
292
	}
293
 
294
	RETURN_INT("t_number", FLOAT_TOK);
295
}
296
 
297
static en_tokens
298
t_int()
299
{
300
	TRACECH("t_int");
301
 
302
	/* Leading 0 or 0x changes base. */	
303
	if (ch == '0') {
304
		sysnext();
305
		if (ch == 'x' || ch == 'X') {
306
			sysnext();
307
			scan_number(16);
308
		}
309
		else if (isdigit(ch)) {
310
			scan_number(8);
311
		}
312
		else {
313
			/* Lone '0'. */
314
			t_value = 0;
315
		}
316
	}
317
	else {
318
		scan_number(10);
319
	}
320
 
321
	if (ch == 'l' || ch == 'L') {
322
		sysnext();
323
		if(ch == 'u' || ch == 'U') {
324
			sysnext();
325
		}
326
		RETURN_INT("t_int", LONG_TOK);
327
	}
328
	else if (ch == 'u' || ch == 'U') {
329
		sysnext();
330
		if (ch == 'l' || ch == 'L') {
331
			RETURN_INT("t_int", LONG_TOK);
332
		}
333
		else {
334
			RETURN_INT("t_int", INT_TOK);
335
		}
336
	}
337
	else {
338
		RETURN_INT("t_int", INT_TOK);
339
	}
340
}
341
 
342
/*
343
	Copy a string into out[] and puts its length in the global t_length.
344
	Copy the opening or closing delimiters if the copy_flag is TRUE.
345
 
346
	This is used to parse both strings and character constants.
347
*/
348
void
349
t_string(out, max_out, copy_flag)
350
register char * out;	/* The output buffer.				*/
351
int	max_out;	/* The size of out[].				*/
352
bool	copy_flag;	/* Copy the delimiters if copy_flag is TRUE.	*/
353
{
354
	register int length;
355
	int	start_line;
356
	char *	start_string;
357
	char	line [10];
358
	int	delim;
359
 
360
	TRACECH("t_string");
361
 
362
	/* Save starting line number for error messages. */
363
	start_line = t_line;
364
	start_string = out;
365
 
366
	/* Handle the opening single or double quote */
367
	delim = ch;
368
	sysnext();
369
	length = 0;
370
	if (copy_flag) {
371
		*out++ = delim;
372
		length++;
373
	}
374
 
375
	max_out--;
376
	while (length < max_out) {
377
 
378
		switch(ch) {
379
 
380
		case END_FILE:
381
		case '\n':
382
			goto runon1;
383
 
384
		case '"':
385
		case '\'':
386
			if (ch == delim) {
387
				sysnext();
388
				if(copy_flag) {
389
					*out++ = delim;
390
					length++;
391
				}
392
				*out++ = '\0';
393
				t_length  = length;
394
				TRACEP("t_string", printf("<%s>\n",
395
					start_string));
396
				RETURN_VOID("t_string");
397
			}
398
			else{
399
				*out++ = ch;
400
				length++;
401
				sysnext();
402
			}
403
			continue;
404
 
405
		case '\\':
406
 
407
			sysnext();
408
			if (ch == END_FILE) {
409
				goto runon1;
410
			}
411
			else if (ch == '\n') {
412
				/* Ignore back slash and newline. */
413
				t_line++;
414
				sysnext();
415
			}
416
			else {
417
				*out++ = '\\';
418
				*out++ = ch;
419
				length += 2;
420
				sysnext();
421
			}
422
			continue;
423
 
424
		default:
425
			*out++ = ch;
426
			length++;
427
			sysnext();
428
		}
429
	}
430
 
431
	conv2s(start_line, line);
432
	err3("String starting at line ", line, " is too long.");
433
 
434
	*out = '\0';
435
	t_length = length;
436
	RETURN_VOID("t_string");
437
 
438
runon1:
439
	*out   = '\0';
440
	err2("String crosses a line: ", start_string);
441
	t_length  = length;
442
	LEAVE("t_string");
443
}
444
 
445
/*
446
	Parse a string (including delimiters) from in[] to out.
447
	Return the length of the ORIGINAL string.
448
*/
449
int
450
in_string(in, out, max_out)
451
char *	in;		/* The output buffer	*/
452
char *	out;		/* The output buffer.	*/
453
int	max_out;	/* The size of out[].	*/
454
{
455
	register int length;
456
	int	start_line;
457
	char *	start_string;
458
	char	line [10];
459
	int	delim;
460
 
461
	TRACEPB("in_string", printf("(<%s>, %p, %d)\n",
462
		in, out, max_out));
463
 
464
	/* Save starting line number for error messages. */
465
	start_line = t_line;
466
	start_string = out;
467
 
468
	/* Copy the opening single or double quote */
469
	*out++ = delim = *in++;
470
	length = 1;
471
	max_out--;
472
	while (length < max_out) {
473
 
474
		switch(*in) {
475
 
476
		case END_FILE:
477
		case '\n':
478
			goto runon1;
479
 
480
		case '"':
481
		case '\'':
482
			if (*in == delim) {
483
				*out++ = *in++;
484
				*out++ = '\0';
485
				length++;
486
				RETURN_INT("in_string", length);
487
			}
488
			else{
489
				*out++ = *in++;
490
				length++;
491
			}
492
			continue;
493
 
494
		case '\\':
495
 
496
			in++;
497
			if (*in == END_FILE) {
498
				goto runon1;
499
			}
500
			else if (*in == '\n') {
501
				/* Ignore back slash and newline. */
502
				t_line++;
503
				in++;
504
 
505
				/* Keep track of ORIGINAL length. */
506
				length += 2;
507
			}
508
			else {
509
				*out++ = '\\';
510
				*out++ = *in++;
511
				length += 2;
512
			}
513
			continue;
514
 
515
		default:
516
			*out++ = *in++;
517
			length++;
518
		}
519
	}
520
 
521
	conv2s(start_line, line);
522
	err3("Strinized argument starting at line ", line, " is too long.");
523
	*out = '\0';
524
	RETURN_INT("in_string", length);
525
 
526
runon1:
527
	*out   = '\0';
528
	err2("Stringized argument crosses a line: ", start_string);
529
	RETURN_INT("in_string", length);
530
}