cfad47cfa3/t3compiler/tads3/tclibprs.cpp

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
/* 
2
 *   Copyright (c) 2002, 2002 Michael J Roberts.  All Rights Reserved.
3
 *   
4
 *   Please see the accompanying license file, LICENSE.TXT, for information
5
 *   on using and copying this software.  
6
 */
7
/*
8
Name
9
  tclibprs.cpp - tads compiler: library parser
10
Function
11
  Parses .tl files, which are text files that reference source files
12
  for inclusion in a compilation.  A .tl file can be used in a compilation
13
  as though it were a source file, and stands for the set of source files
14
  it references.  A .tl file can reference other .tl files.
15
Notes
16
  
17
Modified
18
  01/08/02 MJRoberts  - Creation
19
*/
20
21
#include <ctype.h>
22
#include <string.h>
23
#include <stdlib.h>
24
#include <stdio.h>
25
26
#include "os.h"
27
#include "t3std.h"
28
#include "tclibprs.h"
29
#include "tcsrc.h"
30
31
32
/*
33
 *   Instantiate.  We'll note the name of the library, but we don't attempt
34
 *   to open it at this point - we won't open the file until parse_lib() is
35
 *   called.  
36
 */
37
CTcLibParser::CTcLibParser(const char *lib_name)
38
{
39
    char lib_path[OSFNMAX];
40
41
    /* 
42
     *   Extract the library's directory path.  Since files within a library
43
     *   are always relative to the directory containing the library itself,
44
     *   we use the full path to the library as the starting point for each
45
     *   file found within the library.  
46
     */
47
    os_get_path_name(lib_path, sizeof(lib_path), lib_name);
48
49
    /* remember the library path */
50
    lib_path_ = lib_copy_str(lib_path);
51
52
    /* remember the name of the library */
53
    lib_name_ = lib_copy_str(lib_name);
54
55
    /* no errors yet */
56
    err_cnt_ = 0;
57
58
    /* no lines read yet */
59
    linenum_ = 0;
60
}
61
62
CTcLibParser::~CTcLibParser()
63
{
64
    /* delete our library filename and path */
65
    lib_free_str(lib_name_);
66
    lib_free_str(lib_path_);
67
}
68
69
/*
70
 *   Parse a library file. 
71
 */
72
void CTcLibParser::parse_lib()
73
{
74
    CTcSrcFile *fp;
75
76
    /* open our file - we only accept plain ASCII here */
77
    fp = CTcSrcFile::open_ascii(lib_name_);
78
79
    /* if that failed, abort */
80
    if (fp == 0)
81
    {
82
        err_open_file();
83
        ++err_cnt_;
84
        return;
85
    }
86
87
    /* keep going until we run out of file */
88
    for (;;)
89
    {
90
        char buf[1024];
91
        char expbuf[1024];
92
        size_t len;
93
        char *p;
94
        char *dst;
95
        char *sep;
96
        char *var_name;
97
        char *var_val;
98
99
        /* read the next line of text */
100
        if ((len = fp->read_line(buf, sizeof(buf))) == 0)
101
            break;
102
103
        /* count the line */
104
        ++linenum_;
105
106
        /* un-count the terminating null byte in the length */
107
        --len;
108
109
        /* check for a terminating newline sequence */
110
        while (len != 0 && (buf[len-1] == '\n' || buf[len-1] == '\r'))
111
            --len;
112
113
        /* if we didn't find a newline, the line is too long */
114
        if (buf[len] == '\0')
115
        {
116
            char buf2[128];
117
            size_t len2;
118
            
119
            /* 
120
             *   If we can read anything more from the file, this indicates
121
             *   that the line was simply too long to read into our buffer;
122
             *   skip the rest of the line and log an error in this case.
123
             *   If we can't read any more text, it means we've reached the
124
             *   end of the file. 
125
             */
126
            if ((len2 = fp->read_line(buf2, sizeof(buf2))) != 0)
127
            {
128
                /* there's more, so the line is too long - flag an error */
129
                err_line_too_long();
130
                ++err_cnt_;
131
132
                /* skip text until we find a newline */
133
                for (;;)
134
                {
135
                    /* un-count the null terminator */
136
                    --len2;
137
138
                    /* if we've found our newline, we're done skipping */
139
                    if (len2 != 0
140
                        && (buf2[len2-1] == '\n' || buf2[len2-1] == '\r'))
141
                        break;
142
                    
143
                    /* read another chunk */
144
                    if ((len2 = fp->read_line(buf2, sizeof(buf2))) == 0)
145
                        break;
146
                }
147
148
                /* we've skipped the line, so go back for the next */
149
                continue;
150
            }
151
        }
152
153
        /* remove the newline from the buffer */
154
        buf[len] = '\0';
155
156
        /* scan for and expand preprocessor symbols */
157
        for (p = buf, dst = expbuf ; *p != '\0' ; ++p)
158
        {
159
            /* check for the start of a preprocessor symbol */
160
            if (*p == '$')
161
            {
162
                /* 
163
                 *   We have a dollar sign, so this *could* be a preprocessor
164
                 *   symbol.  If the next character is '(', then find the
165
                 *   matching ')', and take the part between the parentheses
166
                 *   as a symbol to expand.  If the next character is '$',
167
                 *   replace the pair with a single dollar sign.  If the next
168
                 *   character is anything else, the '$' is not significant,
169
                 *   so leave it as it is.  
170
                 */
171
                if (*(p+1) == '(')
172
                {
173
                    char *closep;
174
175
                    /* find the close paren */
176
                    for (closep = p + 2 ; *closep != '\0' && *closep != ')' ;
177
                         ++closep) ;
178
179
                    /* 
180
                     *   if we found the close paren, process it; if not,
181
                     *   just ignore the whole thing and treat the '$(' as a
182
                     *   pair of ordinary characters 
183
                     */
184
                    if (*closep == ')')
185
                    {
186
                        int vallen;
187
                        size_t symlen;
188
                        size_t rem;
189
190
                        /* skip to the start of the symbol name */
191
                        p += 2;
192
193
                        /* get the length of the symbol */
194
                        symlen = closep - p;
195
196
                        /* figure out how much space we have left */
197
                        rem = sizeof(expbuf) - (dst - expbuf);
198
199
                        /* look up the symbol's value */
200
                        vallen = get_pp_symbol(dst, rem, p, symlen);
201
202
                        /* note overflow or undefined symbol errors */
203
                        if (vallen < 0)
204
                        {
205
                            /* it's undefined - note it */
206
                            err_undef_sym(p, symlen);
207
                        }
208
                        else if ((size_t)vallen > rem)
209
                        {
210
                            /* it's too long - note it and give up now */
211
                            err_expanded_line_too_long();
212
                            break;
213
                        }
214
                        else
215
                        {
216
                            /* success - advance past the value */
217
                            dst += vallen;
218
                        }
219
220
                        /* 
221
                         *   Skip to the close paren and continue with the
222
                         *   main loop.  We don't need to copy any input for
223
                         *   the symbol, because we've already copied the
224
                         *   expansion instead; so just go back to the start
225
                         *   of the input loop.  
226
                         */
227
                        p = closep;
228
                        continue;
229
                    }
230
                    else
231
                    {
232
                        /* ill-formed macro substitution expression */
233
                        err_invalid_dollar();
234
                    }
235
                }
236
                else if (*(p+1) == '$')
237
                {
238
                    /* 
239
                     *   we have a double dollar sign, so turn it into a
240
                     *   single dollar sign - simply skip the first of the
241
                     *   pair so we only keep one 
242
                     */
243
                    ++p;
244
                }
245
                else
246
                {
247
                    /* invalid '$' sequence - warn about it */
248
                    err_invalid_dollar();
249
                }
250
            }
251
252
            /* make sure we have room for this character plus a null byte */
253
            if ((dst - expbuf) + 2 > sizeof(expbuf))
254
            {
255
                err_expanded_line_too_long();
256
                break;
257
            }
258
259
            /* copy this character to the destination string */
260
            *dst++ = *p;
261
        }
262
263
        /* null-terminate the expanded buffer */
264
        *dst = '\0';
265
266
        /* skip leading spaces */
267
        for (p = expbuf ; isspace(*p) ; ++p) ;
268
269
        /* if the line is empty or starts with '#', skip it */
270
        if (*p == '\0' || *p == '#')
271
            continue;
272
273
        /* the variable name starts here */
274
        var_name = p;
275
276
        /* the variable name ends at the next space or colon */
277
        for ( ; *p != '\0' && *p != ':' && !isspace(*p) ; ++p) ;
278
279
        /* note where the variable name ends */
280
        sep = p;
281
282
        /* skip spaces after the end of the variable name */
283
        for ( ; isspace(*p) ; ++p) ;
284
285
        /* 
286
         *   if we didn't find a colon after the variable name, it's an
287
         *   error; flag the error and skip the line 
288
         */
289
        if (*p != ':')
290
        {
291
            /* check for stand-alone flags */
292
            if (stricmp(var_name, "nodef") == 0)
293
            {
294
                /* scan the "nodef" flag */
295
                scan_nodef();
296
            }
297
            else
298
            {
299
                /* 
300
                 *   it's not a valid stand-alone flag; the definition must
301
                 *   be missing the value portion 
302
                 */
303
                err_missing_colon();
304
                ++err_cnt_;
305
            }
306
307
            /* we're done with this line now */
308
            continue;
309
        }
310
311
        /* put a null terminator at the end of the variable name */
312
        *sep = '\0';
313
314
        /* skip any spaces after the colon */
315
        for (++p ; isspace(*p) ; ++p) ;
316
317
        /* the value starts here */
318
        var_val = p;
319
320
        /* scan the value */
321
        scan_var(var_name, var_val);
322
    }
323
324
    /* we're done - close the file */
325
    delete fp;
326
}
327
328
/*
329
 *   Scan a variable 
330
 */
331
void CTcLibParser::scan_var(const char *name, const char *val)
332
{
333
    /* call the appropriate routine based on the variable name */
334
    if (stricmp(name, "name") == 0)
335
        scan_name(val);
336
    else if (stricmp(name, "source") == 0)
337
        scan_source(val);
338
    else if (stricmp(name, "library") == 0)
339
        scan_library(val);
340
    else if (stricmp(name, "resource") == 0)
341
        scan_resource(val);
342
    else if (stricmp(name, "needmacro") == 0)
343
        scan_needmacro(val);
344
    else
345
        err_unknown_var(name, val);
346
}
347
348
/*
349
 *   Scan a source filename.  We build the full filename by combining the
350
 *   library path and the given filename, then we call scan_full_source().  
351
 */
352
void CTcLibParser::scan_source(const char *val)
353
{
354
    char rel_path[OSFNMAX];
355
    char full_name[OSFNMAX];
356
357
    /* convert the value from a URL-style path to a local path */
358
    os_cvt_url_dir(rel_path, sizeof(rel_path), val, FALSE);
359
360
    /* build the full name */
361
    os_build_full_path(full_name, sizeof(full_name), lib_path_, rel_path);
362
363
    /* call the full filename scanner */
364
    scan_full_source(val, full_name);
365
}
366
367
/*
368
 *   Scan a library filename.  We build the full filename by combining the
369
 *   enclosing library path and the given filename, then we call
370
 *   scan_full_library().  
371
 */
372
void CTcLibParser::scan_library(const char *val)
373
{
374
    char rel_path[OSFNMAX];
375
    char full_name[OSFNMAX];
376
377
    /* convert the value from a URL-style path to a local path */
378
    os_cvt_url_dir(rel_path, sizeof(rel_path), val, FALSE);
379
380
    /* build the full name */
381
    os_build_full_path(full_name, sizeof(full_name), lib_path_, rel_path);
382
383
    /* call the full library filename scanner */
384
    scan_full_library(val, full_name);
385
}
386
387
/*
388
 *   Scan a resource filename.  We build the full filename by combining the
389
 *   enclosing library path and the given filename, then we call
390
 *   scan_full_resource().  
391
 */
392
void CTcLibParser::scan_resource(const char *val)
393
{
394
    char rel_path[OSFNMAX];
395
    char full_name[OSFNMAX];
396
397
    /* convert the value from a URL-style path to a local path */
398
    os_cvt_url_dir(rel_path, sizeof(rel_path), val, FALSE);
399
400
    /* build the full name */
401
    os_build_full_path(full_name, sizeof(full_name), lib_path_, rel_path);
402
403
    /* call the full resource filename scanner */
404
    scan_full_resource(val, full_name);
405
}
406
407
/*
408
 *   scan a "needmacro" definition 
409
 */
410
void CTcLibParser::scan_needmacro(const char *val)
411
{
412
    const char *p;
413
    const char *macro_name;
414
    size_t macro_len;
415
416
    /* skip leading whitespace */
417
    for (p = val ; isspace(*p) ; ++p) ;
418
419
    /* the macro starts here */
420
    macro_name = p;
421
422
    /* find the next whitspace, which separates the token */
423
    for ( ; *p != '\0' && !isspace(*p) ; ++p) ;
424
425
    /* note the length of the macro name */
426
    macro_len = p - macro_name;
427
428
    /* skip the whitspace */
429
    for ( ; isspace(*p) ; ++p) ;
430
431
    /* process the parsed needmacro definition */
432
    scan_parsed_needmacro(macro_name, macro_len, p);
433
}
434
435
/*
436
 *   Scan a parsed "needmacro" definition.  This default implementation shows
437
 *   an error and the library-defined warning text.  
438
 */
439
void CTcLibParser::scan_parsed_needmacro(const char *macro_name,
440
                                         size_t macro_len,
441
                                         const char *warning_text)
442
{
443
    char buf[100+128+128];
444
445
    /* look up the symbol; if it's not defined, show a warning */
446
    if (get_pp_symbol(buf, sizeof(buf), macro_name, macro_len) < 0)
447
    {
448
        /* limit the symbol length to avoid overflowing the error buffer */
449
        if (macro_len > 128)
450
            macro_len = 128;
451
452
        /* show a suitable warning */
453
        src_err_msg("library requires preprocessor symbol \"%.*s\" to be "
454
                    "defined; use -D %.*s=value to define it (%s)",
455
                    (int)macro_len, macro_name, (int)macro_len, macro_name,
456
                    warning_text);
457
    }
458
}
459
460
/* 
461
 *   log an error in a source line 
462
 */
463
void CTcLibParser::src_err_msg(const char *msg, ...)
464
{
465
    char buf[1024];
466
    va_list args;
467
468
    /* format the caller's message and its arguments */
469
    va_start(args, msg);
470
    t3vsprintf(buf, sizeof(buf), msg, args);
471
    va_end(args);
472
473
    /* display the message with the source name and line number */
474
    err_msg("%s (%lu): %s", lib_name_, linenum_, buf);
475
}