Subversion Repositories tendra.SVN

Rev

Rev 2 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 7u83 1
/*
2
    		 Crown Copyright (c) 1997
3
 
4
    This TenDRA(r) Computer Program is subject to Copyright
5
    owned by the United Kingdom Secretary of State for Defence
6
    acting through the Defence Evaluation and Research Agency
7
    (DERA).  It is made available to Recipients with a
8
    royalty-free licence for its use, reproduction, transfer
9
    to other parties and amendment for any purpose not excluding
10
    product development provided that any such use et cetera
11
    shall be deemed to be acceptance of the following conditions:-
12
 
13
        (1) Its Recipients shall ensure that this Notice is
14
        reproduced upon any copies or amended versions of it;
15
 
16
        (2) Any amended version of it shall be clearly marked to
17
        show both the nature of and the organisation responsible
18
        for the relevant amendment or amendments;
19
 
20
        (3) Its onward transfer from a recipient to another
21
        party shall be deemed to be that party's acceptance of
22
        these conditions;
23
 
24
        (4) DERA gives no warranty or assurance as to its
25
        quality or suitability for any purpose and DERA accepts
26
        no liability whatsoever in relation to any use to which
27
        it may be put.
28
*/
29
 
30
 
31
#include "config.h"
32
#include "tdf.h"
33
#include "cmd_ops.h"
34
#include "spec_ops.h"
35
#include "error.h"
36
#include "input.h"
37
#include "lex.h"
38
#include "syntax.h"
39
#include "xalloc.h"
40
 
41
 
42
/*
43
    INPUT FILE
44
 
45
    This is the file from which the lexical routine read their input.
46
*/
47
 
48
static FILE *lex_input ;
49
 
50
 
51
/*
52
    PENDING BUFFER
53
 
54
    Pending characters are dealt with by means of this buffer.  pending
55
    is set to the start of the buffer to indicate that there are no
56
    characters pending, otherwise the pending characters are stored in
57
    the buffer.  The buffer may need increasing in size if the look-ahead
58
    required by the lexical analyser increases.
59
*/
60
 
61
static int pending_buff [12] = { '?' } ;
62
static int *pending = pending_buff ;
63
 
64
 
65
/*
66
    MAPPINGS AND DECLARATIONS FOR AUTOMATICALLY GENERATED SECTION
67
 
68
    These macros give the mappings between the actions used in the
69
    automatically generated lexical analyser and the routines defined
70
    in this file.
71
*/
72
 
73
static int read_char PROTO_S ( ( void ) ) ;
74
static int read_comment PROTO_S ( ( void ) ) ;
75
static int read_identifier PROTO_S ( ( int ) ) ;
76
static int read_number PROTO_S ( ( int ) ) ;
77
 
78
#define get_comment( A )	read_comment ()
79
#define get_identifier( A )	read_identifier ( ( A ) )
80
#define get_number( A )		read_number ( ( A ) )
81
#define unknown_token( A )	lex_unknown
82
#define unread_char( A )	*( ++pending ) = ( A )
83
 
84
 
85
/*
86
    AUTOMATICALLY GENERATED SECTION
87
 
88
    The main body of the lexical analyser is automatically generated.
89
*/
90
 
91
#include "lexer.h"
92
 
93
 
94
/*
95
    GET THE NEXT CHARACTER
96
 
97
    This routine reads the next character, either from the pending buffer
98
    or from the input file.
99
*/
100
 
101
static int read_char
102
    PROTO_Z ()
103
{
104
    int c ;
105
    if ( pending != pending_buff ) {
106
	c = *( pending-- ) ;
107
    } else {
108
	c = fgetc ( lex_input ) ;
109
	if ( c == '\n' ) crt_line_no++ ;
110
	if ( c == EOF ) return ( LEX_EOF ) ;
111
	c &= 0xff ;
112
    }
113
    return ( c ) ;
114
}
115
 
116
 
117
/*
118
    TOKEN BUFFER
119
 
120
    This buffer is used by read_token to hold the values of identifiers.
121
    Similarly token_value is used to hold the values of numbers.
122
*/
123
 
124
char token_buff [2000] ;
125
static char *token_end = token_buff + sizeof ( token_buff ) ;
126
char *first_comment = NULL ;
127
unsigned token_value = 0 ;
128
 
129
 
130
/*
131
    READ AN IDENTIFIER
132
 
133
    This routine reads an identifier beginning with a, returning the
134
    corresponding lexical token.  Keywords are dealt with locally.
135
*/
136
 
137
static int read_identifier
138
    PROTO_N ( ( a ) )
139
    PROTO_T ( int a )
140
{
141
    int c = a, cl ;
142
    char *t = token_buff ;
143
    do {
144
	*( t++ ) = ( char ) c ;
145
	if ( t == token_end ) error ( ERROR_FATAL, "Buffer overflow" ) ;
146
	c = read_char () ;
147
	cl = lookup_char ( c ) ;
148
    } while ( is_alphanum ( cl ) ) ;
149
    *t = 0 ;
150
    unread_char ( c ) ;
151
 
152
    /* Deal with keywords */
153
    t = token_buff ;
154
#define MAKE_KEYWORD( A, B )\
155
    if ( streq ( t, ( A ) ) ) return ( B ) ;
156
#include "keyword.h"
157
    return ( lex_name ) ;
158
}
159
 
160
 
161
/*
162
    READ A NUMBER
163
 
164
    This routine reads a number.  It is entered after the initial digit,
165
    a, has been read.  The number's value is stored in token_value.
166
*/
167
 
168
static int read_number
169
    PROTO_N ( ( a ) )
170
    PROTO_T ( int a )
171
{
172
    int c = a, cl ;
173
    unsigned n = 0 ;
174
    do {
175
	unsigned m = 10 * n + ( unsigned ) ( c - '0' ) ;
176
	if ( m < n ) error ( ERROR_SERIOUS, "Number overflow" ) ;
177
	n = m ;
178
	c = read_char () ;
179
	cl = lookup_char ( c ) ;
180
    } while ( is_digit ( cl ) ) ;
181
    unread_char ( c ) ;
182
    token_value = n ;
183
    return ( lex_number ) ;
184
}
185
 
186
 
187
/*
188
    READ A COMMENT
189
 
190
    This routine reads a shell style comment.  It is entered after the
191
    initial hash character has been read.
192
*/
193
 
194
static int read_comment
195
    PROTO_Z ()
196
{
197
    int c ;
198
    char *t = token_buff ;
199
    do {
200
	*( t++ ) = ' ' ;
201
	if ( t == token_end ) t = token_buff ;
202
	*( t++ ) = '*' ;
203
	if ( t == token_end ) t = token_buff ;
204
	do {
205
	    c = read_char () ;
206
	    if ( c == LEX_EOF ) {
207
		error ( ERROR_SERIOUS, "End of file in comment" ) ;
208
		return ( lex_eof ) ;
209
	    }
210
	    *( t++ ) = ( char ) c ;
211
	    if ( t == token_end ) t = token_buff ;
212
	} while ( c != '\n' ) ;
213
	c = read_char () ;
214
    } while ( c == '#' ) ;
215
    unread_char ( c ) ;
216
    *t = 0 ;
217
    if ( first_comment == 0 ) first_comment = xstrcpy ( token_buff ) ;
218
    return ( read_token () ) ;
219
}
220
 
221
 
222
/*
223
    GET A COMMAND FROM A STRING
224
 
225
    This routine returns the address of the first non-white space character
226
    from the string ps.  It returns the null pointer if the end of the line
227
    is reached.
228
*/
229
 
230
static char *get_command
231
    PROTO_N ( ( ps ) )
232
    PROTO_T ( char **ps )
233
{
234
    char *t = *ps ;
235
    char *s = t ;
236
    if ( s ) {
237
	char c ;
238
	while ( c = *s, ( c == ' ' || c == '\t' || c == '\r' ) ) {
239
	    *s = 0 ;
240
	    s++ ;
241
	}
242
	if ( c == '#' || c == '\n' || c == 0 ) {
243
	    *s = 0 ;
244
	    *ps = NULL ;
245
	    return ( NULL ) ;
246
	}
247
	t = s ;
248
	while ( c = *s, !( c == ' ' || c == '\t' || c == '\r' ||
249
			   c == '\n' || c == 0 ) ) {
250
	    s++ ;
251
	}
252
	*ps = s ;
253
    }
254
    return ( t ) ;
255
}
256
 
257
 
258
/*
259
    READ A TEMPLATE FILE
260
 
261
    This routine reads a template file from the current input file.
262
*/
263
 
264
COMMAND read_template
265
    PROTO_N ( ( p ) )
266
    PROTO_T ( COMMAND p )
267
{
268
    int go = 1 ;
269
    char buff [1000] ;
270
    FILE *f = lex_input ;
271
    int ln1 = crt_line_no ;
272
    LIST ( COMMAND ) q = NULL_list ( COMMAND ) ;
273
    do {
274
	COMMAND r = NULL_cmd ;
275
	int ln2 = crt_line_no ;
276
	char *s = fgets ( buff, 1000, f ) ;
277
	if ( s == NULL ) {
278
	    /* End of file */
279
	    if ( IS_cmd_cond ( p ) ) {
280
		error ( ERROR_SERIOUS, "End of '@if' expected" ) ;
281
	    } else if ( IS_cmd_loop ( p ) ) {
282
		error ( ERROR_SERIOUS, "End of '@loop' expected" ) ;
283
	    }
284
	    break ;
285
	}
286
	s = xstrcpy ( s ) ;
287
	if ( s [0] == '@' ) {
288
	    /* Complex command */
289
	    int complex = 1 ;
290
	    char *s1, *s2, *s3 ;
291
	    s++ ;
292
	    s1 = get_command ( &s ) ;
293
	    if ( s1 == NULL ) s1 = "<empty>" ;
294
	    s2 = get_command ( &s ) ;
295
	    s3 = get_command ( &s ) ;
296
	    if ( streq ( s1, "if" ) ) {
297
		if ( s2 == NULL ) {
298
		    error ( ERROR_SERIOUS, "Incomplete '@%s' command", s1 ) ;
299
		    s2 = "true" ;
300
		}
301
		MAKE_cmd_cond ( ln2, s2, NULL_cmd, NULL_cmd, r ) ;
302
	    } else if ( streq ( s1, "else" ) ) {
303
		if ( IS_cmd_cond ( p ) ) {
304
		    COMMAND v = DEREF_cmd ( cmd_cond_true_code ( p ) ) ;
305
		    if ( !IS_NULL_cmd ( v ) ) {
306
			error ( ERROR_SERIOUS, "Duplicate '@%s' command", s1 ) ;
307
		    }
308
		    q = REVERSE_list ( q ) ;
309
		    MAKE_cmd_compound ( ln1, q, v ) ;
310
		    COPY_cmd ( cmd_cond_true_code ( p ), v ) ;
311
		    q = NULL_list ( COMMAND ) ;
312
		    ln1 = ln2 ;
313
		} else {
314
		    error ( ERROR_SERIOUS, "Misplaced '@%s' command", s1 ) ;
315
		}
316
		s3 = s2 ;
317
	    } else if ( streq ( s1, "endif" ) ) {
318
		if ( IS_cmd_cond ( p ) ) {
319
		    go = 0 ;
320
		} else {
321
		    error ( ERROR_SERIOUS, "Misplaced '@%s' command", s1 ) ;
322
		}
323
		s3 = s2 ;
324
	    } else if ( streq ( s1, "loop" ) ) {
325
		if ( s2 == NULL ) {
326
		    error ( ERROR_SERIOUS, "Incomplete '@%s' command", s1 ) ;
327
		    s2 = "false" ;
328
		}
329
		MAKE_cmd_loop ( ln2, s2, NULL_cmd, r ) ;
330
	    } else if ( streq ( s1, "end" ) ) {
331
		if ( IS_cmd_loop ( p ) ) {
332
		    go = 0 ;
333
		} else {
334
		    error ( ERROR_SERIOUS, "Misplaced '@%s' command", s1 ) ;
335
		}
336
		s3 = s2 ;
337
	    } else if ( streq ( s1, "use" ) ) {
338
		if ( s2 == NULL ) {
339
		    error ( ERROR_SERIOUS, "Incomplete '@%s' command", s1 ) ;
340
		    s2 = "all" ;
341
		}
342
		MAKE_cmd_use ( ln2, s2, s3, r ) ;
343
		if ( s3 ) s3 = get_command ( &s ) ;
344
		complex = 0 ;
345
	    } else if ( streq ( s1, "special" ) ) {
346
		if ( s2 == NULL ) {
347
		    error ( ERROR_SERIOUS, "Incomplete '@%s' command", s1 ) ;
348
		    s2 = "<none>" ;
349
		}
350
		MAKE_cmd_special ( ln2, s2, s3, r ) ;
351
		if ( s3 ) s3 = get_command ( &s ) ;
352
		complex = 0 ;
353
	    } else if ( streq ( s1, "comment" ) ) {
354
		s3 = NULL ;
355
	    } else {
356
		error ( ERROR_SERIOUS, "Unknown command, '@%s'", s1 ) ;
357
		s3 = NULL ;
358
	    }
359
	    if ( s3 ) {
360
		error ( ERROR_SERIOUS, "End of '@%s' expected", s1 ) ;
361
	    }
362
	    crt_line_no = ln2 + 1 ;
363
	    if ( !IS_NULL_cmd ( r ) ) {
364
		/* Read body of command */
365
		if ( complex ) {
366
		    COMMAND u = read_template ( r ) ;
367
		    if ( IS_cmd_cond ( r ) ) {
368
			COMMAND v = DEREF_cmd ( cmd_cond_true_code ( r ) ) ;
369
			if ( IS_NULL_cmd ( v ) ) {
370
			    COPY_cmd ( cmd_cond_true_code ( r ), u ) ;
371
			} else {
372
			    COPY_cmd ( cmd_cond_false_code ( r ), u ) ;
373
			}
374
		    } else if ( IS_cmd_loop ( r ) ) {
375
			COPY_cmd ( cmd_loop_body ( r ), u ) ;
376
		    }
377
		}
378
		CONS_cmd ( r, q, q ) ;
379
	    }
380
	} else {
381
	    /* Simple command */
382
	    MAKE_cmd_simple ( ln2, s, r ) ;
383
	    CONS_cmd ( r, q, q ) ;
384
	    crt_line_no = ln2 + 1 ;
385
	}
386
    } while ( go ) ;
387
    q = REVERSE_list ( q ) ;
388
    MAKE_cmd_compound ( ln1, q, p ) ;
389
    return ( p ) ;
390
}
391
 
392
 
393
/*
394
    CURRENT TOKEN
395
 
396
    These variables are used by the parser to hold the current and former
397
    lexical tokens.
398
*/
399
 
400
int crt_lex_token ;
401
int saved_lex_token ;
402
 
403
 
404
/*
405
    OPEN AN INPUT FILE
406
 
407
    This routine opens the input file nm.  It returns true if the file is
408
    opened successfully.
409
*/
410
 
411
int open_file
412
    PROTO_N ( ( nm ) )
413
    PROTO_T ( char *nm )
414
{
415
    crt_line_no = 1 ;
416
    if ( nm == NULL || streq ( nm, "-" ) ) {
417
	crt_file_name = "stdin" ;
418
	lex_input = stdin ;
419
    } else {
420
	crt_file_name = nm ;
421
	lex_input = fopen ( nm, "r" ) ;
422
	if ( lex_input == NULL ) {
423
	    error ( ERROR_SERIOUS, "Can't open input file, '%s'", nm ) ;
424
	    return ( 0 ) ;
425
	}
426
    }
427
    return ( 1 ) ;
428
}
429
 
430
 
431
/*
432
    CLOSE THE INPUT FILE
433
 
434
    This routine closes the current input file.
435
*/
436
 
437
void close_file
438
    PROTO_Z ()
439
{
440
    FILE *f = lex_input ;
441
    if ( f != stdin ) fclose_v ( f ) ;
442
    return ;
443
}