Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – tendra.SVN – Blame – /branches/algol60/src/tools/tspec/lex.c – Rev 7

Subversion Repositories tendra.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 7u83 1
/*
7 7u83 2
 * Copyright (c) 2002-2005 The TenDRA Project <http://www.tendra.org/>.
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions are met:
7
 *
8
 * 1. Redistributions of source code must retain the above copyright notice,
9
 *    this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright notice,
11
 *    this list of conditions and the following disclaimer in the documentation
12
 *    and/or other materials provided with the distribution.
13
 * 3. Neither the name of The TenDRA Project nor the names of its contributors
14
 *    may be used to endorse or promote products derived from this software
15
 *    without specific, prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
18
 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
21
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
 * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 *
29
 * $Id$
30
 */
31
/*
2 7u83 32
    		 Crown Copyright (c) 1997
7 7u83 33
 
2 7u83 34
    This TenDRA(r) Computer Program is subject to Copyright
35
    owned by the United Kingdom Secretary of State for Defence
36
    acting through the Defence Evaluation and Research Agency
37
    (DERA).  It is made available to Recipients with a
38
    royalty-free licence for its use, reproduction, transfer
39
    to other parties and amendment for any purpose not excluding
40
    product development provided that any such use et cetera
41
    shall be deemed to be acceptance of the following conditions:-
7 7u83 42
 
2 7u83 43
        (1) Its Recipients shall ensure that this Notice is
44
        reproduced upon any copies or amended versions of it;
7 7u83 45
 
2 7u83 46
        (2) Any amended version of it shall be clearly marked to
47
        show both the nature of and the organisation responsible
48
        for the relevant amendment or amendments;
7 7u83 49
 
2 7u83 50
        (3) Its onward transfer from a recipient to another
51
        party shall be deemed to be that party's acceptance of
52
        these conditions;
7 7u83 53
 
2 7u83 54
        (4) DERA gives no warranty or assurance as to its
55
        quality or suitability for any purpose and DERA accepts
56
        no liability whatsoever in relation to any use to which
57
        it may be put.
58
*/
59
 
60
 
61
#include "config.h"
62
#include "object.h"
63
#include "hash.h"
64
#include "lex.h"
65
#include "name.h"
66
#include "syntax.h"
67
#include "type.h"
68
#include "utility.h"
69
 
70
 
71
/*
72
    CREATE A KEYWORD
73
 
74
    This routine creates a keyword nm with lexical token value t.
75
*/
76
 
7 7u83 77
static void
78
make_keyword(char *nm, int t)
2 7u83 79
{
7 7u83 80
    object *p = make_object(nm, OBJ_KEYWORD);
81
    p->u.u_num = t;
82
    IGNORE add_hash(keywords, p, no_version);
83
    return;
2 7u83 84
}
85
 
86
 
87
/*
88
    INITIALISE KEYWORDS
89
 
90
    This routine initialises the hash table of keywords.
91
*/
92
 
7 7u83 93
void
94
init_keywords(void)
2 7u83 95
{
7 7u83 96
#define MAKE_KEYWORD(NAME, LEX)\
97
    make_keyword(NAME, LEX)
2 7u83 98
#include "keyword.h"
7 7u83 99
    return;
2 7u83 100
}
101
 
102
 
103
/*
104
    CURRENT LEXICAL TOKEN
105
 
106
    These variables are used to store the value of the current lexical
107
    token.
108
*/
109
 
7 7u83 110
int crt_lex_token = lex_unknown;
111
int saved_lex_token = lex_unknown;
112
char *token_value = null;
2 7u83 113
 
114
 
115
/*
116
    INPUT FILE
117
 
118
    These variable input_file gives the file from which the input is read.
119
    The input_pending variable is used to unread one character.
120
*/
121
 
7 7u83 122
FILE *input_file;
123
int input_pending = LEX_EOF;
2 7u83 124
 
125
 
126
/*
127
    READ A CHARACTER FROM THE INPUT FILE
128
 
129
    This routine reads the next character from the input file.
130
*/
131
 
7 7u83 132
static int
133
read_char(void)
2 7u83 134
{
7 7u83 135
    int c = input_pending;
136
    if (c == LEX_EOF) {
137
	c = fgetc(input_file);
138
	if (c == '\n')line_no++;
139
	if (c == EOF) return(LEX_EOF);
140
	c &= 0xff;
2 7u83 141
    } else {
7 7u83 142
	input_pending = LEX_EOF;
2 7u83 143
    }
7 7u83 144
    return(c);
2 7u83 145
}
146
 
147
 
148
/*
149
    MAPPINGS OF LEXICAL ANALYSER ROUTINES
150
 
151
    These macros give the mappings from the lexical analyser to the
152
    routines defined in this module.
153
*/
154
 
7 7u83 155
static int read_identifier(int, int, int);
156
static int read_number(int, int);
157
static int read_string(int);
158
static int read_insert(int);
159
static int read_c_comment(int);
160
static int read_comment(int);
2 7u83 161
 
7 7u83 162
#define unread_char(A)	input_pending = (A)
163
#define get_global(A)		read_identifier(0,(A), 0)
164
#define get_local(A, B)	read_identifier((A), (B), 0)
165
#define get_command(A, B)	read_identifier((A), (B), 0)
166
#define get_variable(A, B)	read_identifier((A), (B), 0)
167
#define get_number(A)		read_number((A), 0)
168
#define get_string(A)		read_string(0)
169
#define get_comment(A)	read_comment(0)
170
#define get_c_comment(A, B)	read_c_comment(0)
171
#define get_text(A, B)	read_insert(0)
172
#define unknown_token(A)	lex_unknown
2 7u83 173
 
174
 
175
/*
176
    INCLUDE THE LEXICAL ANALYSER
177
 
178
    The automatically generated lexical analyser is included at this
179
    point.  It defines the routine read_token which reads the next
180
    lexical token from the input file.
181
*/
182
 
183
#include "lexer.h"
184
 
185
 
186
/*
187
    READ AN IDENTIFIER NAME
188
 
189
    This routine reads an identifier name from the input file.  It is
190
    entered after the first character, b, has been read.  a gives the
191
    identifier prefix, '+' for commands, '$' for variables, '~' for
192
    local identifiers, and 0 for normal identifiers.
193
*/
194
 
7 7u83 195
static int
196
read_identifier(int a, int b, int pp)
2 7u83 197
{
7 7u83 198
    int c;
199
    object *p;
200
    int i = 0;
201
    char *s = buffer;
202
    if (a)s [ i++ ] = (char)a;
203
    s [ i++ ] = (char)b;
204
    for (; ;) {
205
	c = read_char();
206
	if (!is_alphanum(lookup_char(c)))break;
207
	s [i] = (char)c;
208
	if (++i >= buffsize) {
209
	    error(ERR_SERIOUS, "Identifier too long");
210
	    i = 1;
2 7u83 211
	}
212
    }
7 7u83 213
    unread_char(c);
214
    s [i] = 0;
215
    p = search_hash(keywords, s, no_version);
216
    if (p) return(p->u.u_num);
217
    token_value = s;
218
    if (a == 0) {
219
	if (!pp)token_value = string_copy(s);
220
	return(lex_name);
2 7u83 221
    }
7 7u83 222
    if (a == '$') {
223
	if (!pp)token_value = string_copy(s);
224
	return(lex_variable);
2 7u83 225
    }
7 7u83 226
    if (a == '+') {
2 7u83 227
	/* Commands */
7 7u83 228
	if (!pp)token_value = string_copy(s);
229
	error(ERR_SERIOUS, "Unknown command, '%s'", s);
230
	return(lex_name);
2 7u83 231
    }
7 7u83 232
    token_value = string_concat(HIDDEN_NAME, s + 1);
233
    return(lex_name);
2 7u83 234
}
235
 
236
 
237
/*
238
    READ A NUMBER
239
 
240
    This routine reads a number from the input file.  It is entered after
241
    the initial character, a, has been read.
242
*/
243
 
7 7u83 244
static int
245
read_number(int a, int pp)
2 7u83 246
{
7 7u83 247
    int c;
248
    int i = 0;
249
    char *s = buffer;
250
    s [ i++ ] = (char)a;
251
    for (; ;) {
252
	c = read_char();
253
	if (!is_digit(lookup_char(c)))break;
254
	s [i] = (char)c;
255
	if (++i >= buffsize) {
256
	    error(ERR_SERIOUS, "Number too long");
257
	    i = 0;
2 7u83 258
	}
259
    }
7 7u83 260
    unread_char(c);
261
    s [i] = 0;
262
    if (pp) {
263
	token_value = s;
2 7u83 264
    } else {
7 7u83 265
	token_value = string_copy(s);
2 7u83 266
    }
7 7u83 267
    return(lex_number);
2 7u83 268
}
269
 
270
 
271
/*
272
    READ A STRING
273
 
274
    This routine reads a string from the input file.  It is entered after
275
    the initial quote has been read.
276
*/
277
 
7 7u83 278
static int
279
read_string(int pp)
2 7u83 280
{
7 7u83 281
    int c;
282
    int i = 0;
283
    char *s = buffer;
284
    for (; ;) {
285
	c = read_char();
286
	if (c == '"') {
2 7u83 287
	    /* End of string */
7 7u83 288
	    break;
289
	} else if (c == '\\') {
2 7u83 290
	    /* Deal with escaped characters */
7 7u83 291
	    c = read_char();
292
	    if (c == '\n' || c == LEX_EOF)goto new_line;
293
	    if (pp) {
2 7u83 294
		/* Preserve escapes when preprocessing */
7 7u83 295
		s [i] = '\\';
296
		i++;
2 7u83 297
	    } else {
298
		/* Examine escape sequence */
7 7u83 299
		switch (c) {
300
		    case 'n': c = '\n'; break;
301
		    case 'r': c = '\r'; break;
302
		    case 't': c = '\t'; break;
2 7u83 303
		}
304
	    }
7 7u83 305
	} else if (c == '\n' || c == LEX_EOF) {
2 7u83 306
	    /* Deal with new lines */
307
	    new_line : {
7 7u83 308
		error(ERR_SERIOUS, "New line in string");
309
		s [i] = 0;
310
		return(lex_string);
2 7u83 311
	    }
312
	}
7 7u83 313
	s [i] = (char)c;
314
	if (++i >= buffsize) {
315
	    error(ERR_SERIOUS, "String too long");
316
	    i = 0;
2 7u83 317
	}
318
    }
7 7u83 319
    s [i] = 0;
320
    if (pp) {
321
	token_value = s;
2 7u83 322
    } else {
7 7u83 323
	token_value = string_copy(s);
2 7u83 324
    }
7 7u83 325
    return(lex_string);
2 7u83 326
}
327
 
328
 
329
/*
330
    READ A SECTION OF QUOTED TEXT
331
 
332
    This routine reads a section of quoted text (indicated by enclosure
333
    in a number of percent signs) into the buffer.  On entry two percents
334
    have already been read.  Firstly any further percents are read, then
335
    the text is read until an equal number of percents are encountered.
336
    Any leading or trailing whitespace is ignored if pp is false.
337
*/
338
 
7 7u83 339
static int
340
read_insert(int pp)
2 7u83 341
{
7 7u83 342
    int c;
343
    int i = 0;
344
    int p = 0;
345
    int percents = 2;
346
    char *s = buffer;
347
    while (c = read_char(), c == '%')percents++;
348
    unread_char(c);
349
    if (pp) {
2 7u83 350
	/* Preserve percents when preprocessing */
7 7u83 351
	if (percents < buffsize) {
352
	    for (i = 0; i < percents; i++)s [i] = '%';
2 7u83 353
	} else {
7 7u83 354
	    error(ERR_SERIOUS, "Insert too long");
2 7u83 355
	}
356
    }
357
    do {
7 7u83 358
	c = read_char();
359
	if (c == '%') {
360
	    p++;
2 7u83 361
	} else {
7 7u83 362
	    if (c == LEX_EOF) {
363
		error(ERR_SERIOUS, "End of file in quoted text");
364
		return(lex_eof);
2 7u83 365
	    }
7 7u83 366
	    p = 0;
2 7u83 367
	}
7 7u83 368
	s [i] = (char)c;
369
	if (++i >= buffsize) {
370
	    error(ERR_SERIOUS, "Insert too long");
371
	    i = 0;
2 7u83 372
	}
7 7u83 373
    } while (p != percents);
374
    if (pp) {
2 7u83 375
	/* Preserve percents when preprocessing */
7 7u83 376
	s [i] = 0;
377
	token_value = s;
2 7u83 378
    } else {
379
	/* Strip out initial and final white space */
7 7u83 380
	if (i >= p)i -= p;
381
	s [i] = 0;
382
	while (--i >= 0) {
383
	    int a = (int)s [i];
384
	    int t = lookup_char(a & 0xff);
385
	    if (!is_white(t))break;
386
	    s [i] = 0;
2 7u83 387
	}
7 7u83 388
	i = 0;
389
	for (; ;) {
390
	    int a = (int)s [i];
391
	    int t = lookup_char(a & 0xff);
392
	    if (!is_white(t))break;
393
	    i++;
2 7u83 394
	}
7 7u83 395
	token_value = string_copy(s + i);
2 7u83 396
    }
7 7u83 397
    return(percents % 2 ? lex_build_Hinsert : lex_insert);
2 7u83 398
}
399
 
400
 
401
/*
402
    READ A C COMMENT
403
 
404
    This routine reads a C-style comment into the buffer.  The routine is
405
    entered just after the initial / * has been read, and continues until
406
    the corresponding * /.
407
*/
408
 
7 7u83 409
static int
410
read_c_comment(int pp)
2 7u83 411
{
7 7u83 412
    int c;
413
    int i = 2;
414
    int p = 0;
415
    char *s = buffer;
416
    s [0] = '/';
417
    s [1] = '*';
2 7u83 418
    do {
7 7u83 419
	c = read_char();
420
	if (c == '*' && p == 0) {
421
	    p = 1;
422
	} else if (c == '/' && p == 1) {
423
	    p = 2;
2 7u83 424
	} else {
7 7u83 425
	    p = 0;
2 7u83 426
	}
7 7u83 427
	if (c == LEX_EOF) {
428
	    error(ERR_SERIOUS, "End of file in comment");
429
	    return(lex_eof);
2 7u83 430
	}
7 7u83 431
	s [i] = (char)c;
432
	if (++i >= buffsize) {
433
	    error(ERR_SERIOUS, "Comment too long");
434
	    i = 2;
2 7u83 435
	}
7 7u83 436
    } while (p != 2);
437
    s [i] = 0;
438
    if (pp) {
439
	token_value = s;
2 7u83 440
    } else {
7 7u83 441
	token_value = string_copy(s);
2 7u83 442
    }
7 7u83 443
    return(lex_comment);
2 7u83 444
}
445
 
446
 
447
/*
448
    READ A TSPEC COMMENT
449
 
450
    This routine steps over a tspec comment.  It is entered after the
451
    initial '#' has been read and skips to the end of the line.  If pp
452
    is false then the next token is returned.
453
*/
454
 
7 7u83 455
static int
456
read_comment(int pp)
2 7u83 457
{
7 7u83 458
    int c;
459
    while (c = read_char(), c != '\n') {
460
	if (c == LEX_EOF) {
461
	    error(ERR_SERIOUS, "End of file in comment");
462
	    return(lex_eof);
2 7u83 463
	}
464
    }
7 7u83 465
    if (pp) return(lex_unknown);
466
    return(read_token());
2 7u83 467
}
468
 
469
 
470
/*
471
    READ A PREPROCESSING TOKEN
472
 
473
    This routine is a stripped down version of read_token which is used
474
    in preprocessing.  Initial white space is skipped if w is true.
475
    The token read is always stored in the buffer.
476
*/
477
 
7 7u83 478
static int
479
read_pptoken(int w)
2 7u83 480
{
7 7u83 481
    int c;
482
    int t = lex_unknown;
2 7u83 483
    do {
7 7u83 484
	c = read_char();
485
    } while (w && is_white(lookup_char(c)));
486
    switch (c) {
487
	case '"': {
488
	    return(read_string(1));
2 7u83 489
	}
7 7u83 490
	case '#': {
491
	    IGNORE read_comment(1);
492
	    if (w) return(read_pptoken(w));
493
	    c = '\n';
494
	    break;
2 7u83 495
	}
7 7u83 496
	case '%': {
497
	    int a = read_char();
498
	    if (a == '%') return(read_insert(1));
499
	    unread_char(a);
500
	    break;
2 7u83 501
	}
7 7u83 502
	case '+': {
503
	    int a = read_char();
504
	    if (is_alpha(lookup_char(a))) {
505
		return(read_identifier(c, a, 1));
2 7u83 506
	    }
7 7u83 507
	    unread_char(a);
508
	    break;
2 7u83 509
	}
7 7u83 510
	case '/': {
511
	    int a = read_char();
512
	    if (a == '*') return(read_c_comment(1));
513
	    unread_char(a);
514
	    break;
2 7u83 515
	}
7 7u83 516
	case ':': {
517
	    int a = read_char();
518
	    if (a == '=') {
519
		buffer [0] = (char)c;
520
		buffer [1] = (char)a;
521
		buffer [2] = 0;
522
		return(lex_assign);
2 7u83 523
	    }
7 7u83 524
	    unread_char(a);
525
	    break;
2 7u83 526
	}
7 7u83 527
	case '(': t = lex_open_Hround; break;
528
	case ')': t = lex_close_Hround; break;
529
	case '{': t = lex_open_Hbrace; break;
530
	case '}': t = lex_close_Hbrace; break;
531
	case ';': t = lex_semicolon; break;
532
	case ',': t = lex_comma; break;
533
	case LEX_EOF: t = lex_eof; break;
2 7u83 534
    }
7 7u83 535
    buffer [0] = (char)c;
536
    buffer [1] = 0;
537
    return(t);
2 7u83 538
}
539
 
540
 
541
/*
542
    READ A STRING
543
 
544
    This routine reads a string plus one other character from the input
545
    file, storing the string in str and returning the other character.
546
    b is set to true if the string is enclosed in brackets.
547
*/
548
 
7 7u83 549
static int
550
read_pp_string(char **str, int *b)
2 7u83 551
{
7 7u83 552
    int c = read_pptoken(1);
553
    if (c == lex_open_Hround) {
554
	*b = 1;
555
	c = read_pptoken(1);
2 7u83 556
    }
7 7u83 557
    if (c != lex_string) {
558
	error(ERR_SERIOUS, "Syntax error - string expected");
559
	*str = "???";
560
	return(c);
2 7u83 561
    }
7 7u83 562
    *str = string_copy(buffer);
563
    c = read_pptoken(1);
564
    if (*b) {
565
	if (c != lex_close_Hround) {
566
	    error(ERR_SERIOUS, "Syntax error - ')' expected");
2 7u83 567
	}
7 7u83 568
	c = read_pptoken(1);
2 7u83 569
    }
7 7u83 570
    return(c);
2 7u83 571
}
572
 
573
 
574
/*
575
    PRINT A SUBSET NAME
576
 
577
    This routine prints the command cmd "api", "file", "subset" to the
578
    file output.
579
*/
580
 
7 7u83 581
static void
582
print_subset_name(FILE *output, char *cmd, char *api, char *file,
583
		  char *subset, int b)
2 7u83 584
{
7 7u83 585
    if (b) {
586
	IGNORE fprintf(output, "%s ( \"%s\" )", cmd, api);
2 7u83 587
    } else {
7 7u83 588
	IGNORE fprintf(output, "%s \"%s\"", cmd, api);
2 7u83 589
    }
7 7u83 590
    if (file)IGNORE fprintf(output, ", \"%s\"", file);
591
    if (subset) {
592
	if (file == null)IGNORE fputs(", \"\"", output);
593
	IGNORE fprintf(output, ", \"%s\"", subset);
2 7u83 594
    }
7 7u83 595
    return;
2 7u83 596
}
597
 
598
 
599
/*
600
    PRINT THE CURRENT FILE POSITION
601
 
602
    This routine prints file name and line number directives to the file
603
    output.
604
*/
605
 
7 7u83 606
static void
607
print_posn(FILE *output)
2 7u83 608
{
7 7u83 609
    static char *last_filename = "";
610
    if (!streq(filename, last_filename)) {
611
	IGNORE fprintf(output, "$FILE = \"%s\" ;\n", filename);
612
	last_filename = filename;
2 7u83 613
    }
7 7u83 614
    IGNORE fprintf(output, "$LINE = %d ;\n", line_no - 1);
615
    return;
2 7u83 616
}
617
 
618
 
619
/*
620
    PREPROCESS A SUBFILE
621
 
622
    This routine reads a +IMPLEMENT or +USE directive (indicated by n)
623
    from the input file to output.
624
*/
625
 
7 7u83 626
static void
627
preproc_subfile(FILE *output, char *cmd)
2 7u83 628
{
7 7u83 629
    int c;
630
    int txt;
631
    int b = 0;
632
    char *api = null;
633
    char *file = null;
634
    char *subset = null;
635
    c = read_pp_string(&api, &b);
636
    if (c == lex_comma) {
637
	int d = 0;
638
	c = read_pp_string(&file, &d);
639
	if (d) {
640
	    error(ERR_SERIOUS, "Illegally bracketed string");
641
	    d = 0;
2 7u83 642
	}
7 7u83 643
	if (c == lex_comma) {
644
	    c = read_pp_string(&subset, &d);
645
	    if (d)error(ERR_SERIOUS, "Illegally bracketed string");
2 7u83 646
	}
7 7u83 647
	if (*file == 0)file = null;
2 7u83 648
    }
7 7u83 649
    if (c == lex_semicolon) {
650
	txt = ';';
651
    } else if (c == lex_open_Hround) {
652
	txt = '(';
2 7u83 653
    } else {
7 7u83 654
	error(ERR_SERIOUS, "Syntax error - ';' or '(' expected");
655
	txt = ';';
2 7u83 656
    }
7 7u83 657
    preproc(output, api, file, subset);
658
    print_posn(output);
659
    print_subset_name(output, cmd, api, file, subset, b);
660
    IGNORE fputc(' ', output);
661
    IGNORE fputc(txt, output);
662
    return;
2 7u83 663
}
664
 
665
 
666
/*
667
    PREPROCESS A FILE
668
 
669
    This routine preprocesses the subset api:file:subset into output.
670
*/
671
 
7 7u83 672
void
673
preproc(FILE *output, char *api, char *file, char *subset)
2 7u83 674
{
7 7u83 675
    int c;
676
    char *s;
677
    object *p;
678
    char *sn, *nm;
679
    FILE *old_file;
680
    int old_pending;
681
    int old_line_no;
682
    char *old_filename;
683
    boolean found = 0;
684
    int brackets = 0;
685
    int end_brackets = 0;
686
    int if_depth = 0;
687
    int else_depth = 0;
688
    FILE *input = null;
689
    boolean printing = (boolean)(subset ? 0 : 1);
2 7u83 690
 
691
    /* Check for previous inclusion */
7 7u83 692
    sn = subset_name(api, file, subset);
693
    p = search_hash(subsets, sn, no_version);
694
    if (p != null) {
695
	if (p->u.u_info == null) {
696
	    error(ERR_SERIOUS, "Recursive inclusion of '%s'", sn);
697
	} else if (p->u.u_info->implemented) {
698
	    error(ERR_SERIOUS, "Set '%s' not found", sn);
2 7u83 699
	}
7 7u83 700
	return;
2 7u83 701
    }
702
 
703
    /* Open the input file */
7 7u83 704
    nm = (file ? file : MASTER_FILE);
705
    if (!streq(api, LOCAL_API)) {
706
	nm = string_printf("%s/%s", api, nm);
2 7u83 707
    }
7 7u83 708
    s = input_dir;
709
    while (s) {
710
	char *t = strchr(s, ':');
711
	if (t == null) {
712
	   IGNORE sprintf(buffer, "%s/%s", s, nm);
713
	   s = null;
2 7u83 714
	} else {
7 7u83 715
	   IGNORE strcpy(buffer, s);
716
	   IGNORE sprintf(buffer + (t - s), "/%s", nm);
717
	   s = t + 1;
2 7u83 718
	}
7 7u83 719
	input = fopen(buffer, "r");
720
	if (input) {
721
	    nm = string_copy(buffer);
722
	    break;
2 7u83 723
	}
724
    }
7 7u83 725
    if (input == null) {
726
	input = fopen(nm, "r");
727
	if (input == null) {
728
	    char *err = "Set '%s' not found (can't find file %s)";
729
	    error(ERR_SERIOUS, err, sn, nm);
730
	    p = make_object(sn, OBJ_SUBSET);
731
	    IGNORE add_hash(subsets, p, no_version);
732
	    p->u.u_info = make_info(api, file, subset);
733
	    p->u.u_info->implemented = 1;
734
	    return;
2 7u83 735
	}
736
    }
7 7u83 737
    if (verbose > 1) {
738
	if (subset) {
739
	    IGNORE printf("Preprocessing %s [%s] ...\n", nm, subset);
2 7u83 740
	} else {
7 7u83 741
	    IGNORE printf("Preprocessing %s ...\n", nm);
2 7u83 742
	}
743
    }
7 7u83 744
    old_filename = filename;
745
    old_line_no = line_no;
746
    old_file = input_file;
747
    old_pending = input_pending;
748
    filename = nm;
749
    line_no = 1;
750
    input_file = input;
751
    input_pending = LEX_EOF;
752
    p = make_object(sn, OBJ_SUBSET);
753
    IGNORE add_hash(subsets, p, no_version);
2 7u83 754
 
755
    /* Print position identifier */
7 7u83 756
    print_subset_name(output, "+SET", api, file, subset, 0);
757
    IGNORE fputs(" := {\n", output);
758
    if (printing)print_posn(output);
2 7u83 759
 
760
    /* Process the input */
7 7u83 761
    while (c = read_pptoken(0), c != lex_eof) {
762
	switch (c) {
2 7u83 763
 
7 7u83 764
	    case lex_subset: {
2 7u83 765
		/* Deal with subsets */
7 7u83 766
		int d = 0;
767
		c = read_pp_string(&s, &d);
768
		if (d)error(ERR_SERIOUS, "Illegally bracketed string");
769
		if (c != lex_assign) {
770
		    error(ERR_SERIOUS, "Syntax error - ':=' expected");
2 7u83 771
		}
7 7u83 772
		c = read_pptoken(1);
773
		if (c != lex_open_Hbrace) {
774
		    error(ERR_SERIOUS, "Syntax error - '{' expected");
2 7u83 775
		}
7 7u83 776
		brackets++;
777
		if (printing) {
778
		    int b = brackets;
779
		    char *cmd = "+IMPLEMENT";
780
		    preproc(output, api, file, s);
781
		    print_subset_name(output, cmd, api, file, s, 0);
782
		    IGNORE fputs(" ;\n", output);
2 7u83 783
		    do {
7 7u83 784
			c = read_pptoken(0);
785
			if (c == lex_open_Hbrace) {
786
			    brackets++;
787
			} else if (c == lex_close_Hbrace) {
788
			    brackets--;
789
			} else if (c == lex_eof) {
790
			    char *err = "Can't find end of subset '%s'";
791
			    error(ERR_SERIOUS, err, s);
792
			    goto end_of_file;
2 7u83 793
			}
7 7u83 794
		    } while (brackets >= b);
795
		    c = read_pptoken(1);
796
		    if (c != lex_semicolon) {
797
			error(ERR_SERIOUS, "Syntax error - ';' expected");
2 7u83 798
		    }
7 7u83 799
		    print_posn(output);
2 7u83 800
		} else {
7 7u83 801
		    if (streq(s, subset)) {
802
			if (found) {
803
			    char *err = "Set '%s' already defined (line %d)";
804
			    error(ERR_SERIOUS, err, sn, p->line_no);
2 7u83 805
			} else {
7 7u83 806
			    found = 1;
807
			    printing = 1;
808
			    print_posn(output);
809
			    p->line_no = line_no;
810
			    end_brackets = brackets;
2 7u83 811
			}
812
		    }
813
		}
7 7u83 814
		break;
2 7u83 815
	    }
816
 
7 7u83 817
	    case lex_implement: {
2 7u83 818
		/* Deal with subset uses */
7 7u83 819
		if (printing)preproc_subfile(output, "+IMPLEMENT");
820
		break;
2 7u83 821
	    }
822
 
7 7u83 823
	    case lex_use: {
2 7u83 824
		/* Deal with subset uses */
7 7u83 825
		if (printing)preproc_subfile(output, "+USE");
826
		break;
2 7u83 827
	    }
828
 
7 7u83 829
	    case lex_set: {
2 7u83 830
		/* Deal with sets */
7 7u83 831
		error(ERR_SERIOUS, "+SET directive in preprocessor");
832
		goto default_lab;
2 7u83 833
	    }
834
 
7 7u83 835
	    case lex_if:
836
	    case lex_ifdef:
837
	    case lex_ifndef: {
838
		if_depth++;
839
		else_depth = 0;
840
		goto default_lab;
2 7u83 841
	    }
842
 
7 7u83 843
	    case lex_else: {
844
		if (if_depth == 0) {
845
		    error(ERR_SERIOUS, "+ELSE without +IF");
2 7u83 846
		} else {
7 7u83 847
		    if (else_depth) {
848
			error(ERR_SERIOUS, "Duplicate +ELSE");
2 7u83 849
		    }
7 7u83 850
		    else_depth = 1;
2 7u83 851
		}
7 7u83 852
		goto default_lab;
2 7u83 853
	    }
854
 
7 7u83 855
	    case lex_endif: {
856
		if (if_depth == 0) {
857
		    error(ERR_SERIOUS, "+ENDIF without +IF");
2 7u83 858
		} else {
7 7u83 859
		    if_depth--;
2 7u83 860
		}
7 7u83 861
		else_depth = 0;
862
		goto default_lab;
2 7u83 863
	    }
864
 
7 7u83 865
	    case lex_string: {
2 7u83 866
		/* Deal with strings */
7 7u83 867
		if (printing) {
868
		    IGNORE fprintf(output, "\"%s\"", buffer);
2 7u83 869
		}
7 7u83 870
		break;
2 7u83 871
	    }
872
 
7 7u83 873
	    case lex_open_Hbrace: {
2 7u83 874
		/* Start of subset */
7 7u83 875
		brackets++;
876
		goto default_lab;
2 7u83 877
	    }
878
 
7 7u83 879
	    case lex_close_Hbrace: {
2 7u83 880
		/* End of subset */
7 7u83 881
		brackets--;
882
		if (brackets < 0) {
883
		    error(ERR_SERIOUS, "Unmatched '}'");
884
		    brackets = 0;
2 7u83 885
		}
7 7u83 886
		if (subset && brackets < end_brackets) {
887
		    printing = 0;
2 7u83 888
		}
7 7u83 889
		goto default_lab;
2 7u83 890
	    }
891
 
892
	    default :
893
	    default_lab : {
894
		/* Deal with simple tokens */
7 7u83 895
		if (printing)IGNORE fputs(buffer, output);
896
		break;
2 7u83 897
	    }
898
	}
899
    }
900
 
901
    /* End of file */
902
    end_of_file : {
7 7u83 903
	if (brackets) {
904
	    error(ERR_SERIOUS, "Bracket imbalance of %d", brackets);
2 7u83 905
	}
7 7u83 906
	while (if_depth) {
907
	    error(ERR_SERIOUS, "+IF without +ENDIF");
908
	    if_depth--;
2 7u83 909
	}
7 7u83 910
	IGNORE fputs("} ;\n", output);
911
	IGNORE fclose(input);
912
	p->u.u_info = make_info(api, file, subset);
913
	filename = old_filename;
914
	line_no = old_line_no;
915
	input_file = old_file;
916
	input_pending = old_pending;
917
	if (subset && !found) {
918
	    char *err = "Set '%s' not found (can't find subset '%s')";
919
	    error(ERR_SERIOUS, err, sn, subset);
920
	    p->u.u_info->implemented = 1;
2 7u83 921
	}
7 7u83 922
	return;
2 7u83 923
    }
924
}