cfad47cfa3/t3compiler/tads3/tcmain.cpp

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#ifdef RCSID
2
static char RCSid[] =
3
"$Header: d:/cvsroot/tads/tads3/TCMAIN.CPP,v 1.4 1999/07/11 00:46:53 MJRoberts Exp $";
4
#endif
5
6
/* 
7
 *   Copyright (c) 1999, 2002 Michael J. Roberts.  All Rights Reserved.
8
 *   
9
 *   Please see the accompanying license file, LICENSE.TXT, for information
10
 *   on using and copying this software.  
11
 */
12
/*
13
Name
14
  tcmain.cpp - TADS 3 Compiler - main compiler driver
15
Function
16
  
17
Notes
18
  
19
Modified
20
  04/22/99 MJRoberts  - Creation
21
*/
22
23
#include "t3std.h"
24
#include "vmerr.h"
25
#include "tcglob.h"
26
#include "tcmain.h"
27
#include "tcerr.h"
28
#include "tctok.h"
29
#include "utf8.h"
30
#include "charmap.h"
31
#include "tchost.h"
32
#include "tcprs.h"
33
#include "tcgen.h"
34
#include "tctarg.h"
35
#include "charmap.h"
36
#include "resload.h"
37
#include "tcunas.h"
38
39
40
/* ------------------------------------------------------------------------ */
41
/*
42
 *   statics 
43
 */
44
45
/* references to the error subsystem */
46
int CTcMain::err_refs_ = 0;
47
48
/* flag: we have failed to load external messages */
49
int CTcMain::err_no_extern_messages_ = FALSE;
50
51
/* console output character mapper */
52
CCharmapToLocal *CTcMain::console_mapper_ = 0;
53
54
55
/* ------------------------------------------------------------------------ */
56
/*
57
 *   Initialize the error subsystem for the compiler 
58
 */
59
void CTcMain::tc_err_init(size_t param_stack_size,
60
                          CResLoader *res_loader)
61
{
62
    /* initialize the error stack */
63
    err_init(1024);
64
65
    /* if this is the first initializer, set things up */
66
    if (err_refs_ == 0)
67
    {
68
        /* if we haven't loaded external compiler messages, load them */
69
        if (!err_no_extern_messages_
70
            && tc_messages == &tc_messages_english[0])
71
        {
72
            osfildef *fp;
73
            
74
            /* try finding a message file */
75
            fp = res_loader->open_res_file("t3make.msg", 0, "XMSG");
76
            if (fp != 0)
77
            {
78
                /* try loading it */
79
                err_load_message_file(fp, &tc_messages, &tc_message_count,
80
                                      &tc_messages_english[0],
81
                                      tc_message_count_english);
82
                
83
                /* done with the file */
84
                osfcls(fp);
85
            }
86
            else
87
            {
88
                /* note the failure, so we don't try again */
89
                err_no_extern_messages_ = FALSE;
90
            }
91
        }
92
    }
93
94
    /* count the reference depth */
95
    ++err_refs_;
96
}
97
98
/*
99
 *   terminate the error subsystem for the compiler 
100
 */
101
void CTcMain::tc_err_term()
102
{
103
    /* reduce the reference count */
104
    --err_refs_;
105
106
    /* delete the error stack */
107
    err_terminate();
108
109
    /* if this is the last reference, clean things up */
110
    if (err_refs_ == 0)
111
    {
112
        /* if we loaded an external message file, unload it */
113
        err_delete_message_array(&tc_messages, &tc_message_count,
114
                                 &tc_messages_english[0],
115
                                 tc_message_count_english);
116
    }
117
}
118
119
/* ------------------------------------------------------------------------ */
120
/*
121
 *   Initialize the compiler 
122
 */
123
void CTcMain::init(CTcHostIfc *hostifc, CResLoader *res_loader,
124
                   const char *default_charset)
125
{
126
    /* initialize the error subsystem */
127
    tc_err_init(1024, res_loader);
128
    
129
    /* remember the host interface */
130
    G_hostifc = hostifc;
131
132
    /* perform static initializations on the parser symbol table class */
133
    CTcPrsSymtab::s_init();
134
135
    /* create the compiler main object */
136
    G_tcmain = new CTcMain(res_loader, default_charset);
137
}
138
139
/*
140
 *   Terminate the compiler 
141
 */
142
void CTcMain::terminate()
143
{
144
    /* delete the tokenizer */
145
    delete G_tok;
146
    G_tok = 0;
147
148
    /* delete the compiler main object */
149
    delete G_tcmain;
150
    G_tcmain = 0;
151
152
    /* forget any object and property fixups */
153
    G_objfixup = 0;
154
    G_propfixup = 0;
155
    G_enumfixup = 0;
156
157
    /* 
158
     *   make sure we explicitly turn the fixup flags on again if we want
159
     *   them in a future run 
160
     */
161
    G_keep_objfixups = FALSE;
162
    G_keep_propfixups = FALSE;
163
    G_keep_enumfixups = FALSE;
164
165
    /* perform static termination on the parser symbol table class */
166
    CTcPrsSymtab::s_terminate();
167
168
    /* forget the host interface */
169
    G_hostifc = 0;
170
171
    /* terminate the error subsystem */
172
    tc_err_term();
173
}
174
175
/* ------------------------------------------------------------------------ */
176
/*
177
 *   set up the compiler 
178
 */
179
CTcMain::CTcMain(CResLoader *res_loader, const char *default_charset)
180
{
181
    char csbuf[OSFNMAX];
182
    
183
    /* 
184
     *   if the caller didn't provide a default character set, ask the OS
185
     *   what we should use
186
     */
187
    if (default_charset == 0)
188
    {
189
        /* 
190
         *   ask the OS what to use for file contents, since we use this
191
         *   character set to translate the text we read from source files 
192
         */
193
        os_get_charmap(csbuf, OS_CHARMAP_FILECONTENTS);
194
        
195
        /* use our OS-provided character set */
196
        default_charset = csbuf;
197
    }
198
199
    /* if there's no static console output character map, create one */
200
    if (console_mapper_ == 0)
201
    {
202
        char mapname[32];
203
204
        /* get the console character set name */
205
        os_get_charmap(mapname, OS_CHARMAP_DISPLAY);
206
207
        /* create a resource loader for the console character map */
208
        console_mapper_ = CCharmapToLocal::load(res_loader, mapname);
209
210
        /* if that failed, create an ASCII mapper */
211
        if (console_mapper_ == 0)
212
            console_mapper_ = CCharmapToLocal::load(res_loader, "us-ascii");
213
    }
214
    
215
    /* remember our resource loader */
216
    res_loader_ = res_loader;
217
    
218
    /* 
219
     *   set default options - minimum verbosity, no numeric error codes,
220
     *   show standard warnings but not pedantic warnings, not test mode 
221
     */
222
    err_options_ = TCMAIN_ERR_WARNINGS;
223
224
    /* we have no warning suppression list yet */
225
    suppress_list_ = 0;
226
    suppress_cnt_ = 0;
227
228
    /* remember our default character set */
229
    default_charset_ = lib_copy_str(default_charset);
230
231
    /* create the tokenizer */
232
    G_tok = new CTcTokenizer(res_loader_, default_charset_);
233
    
234
    /* 
235
     *   Create the parser and node memory pool.  Create the memory pool
236
     *   first, because the parser allocates objects out of the pool. 
237
     */
238
    G_prsmem = new CTcPrsMem();
239
    G_prs = new CTcParser();
240
241
    /* create the generator data stream (for constant data) */
242
    G_ds = new CTcDataStream(TCGEN_DATA_STREAM);
243
244
    /* create the primary generator code stream */
245
    G_cs_main = new CTcCodeStream(TCGEN_CODE_STREAM);
246
247
    /* create the static initializer code stream */
248
    G_cs_static = new CTcCodeStream(TCGEN_STATIC_CODE_STREAM);
249
250
    /* make the primary code stream active */
251
    G_cs = G_cs_main;
252
253
    /* create the generator object data stream */
254
    G_os = new CTcDataStream(TCGEN_OBJECT_STREAM);
255
256
    /* create the intrinsic class modifier object data stream */
257
    G_icmod_stream = new CTcDataStream(TCGEN_ICMOD_STREAM);
258
259
    /* create the dictionary object data stream */
260
    G_dict_stream = new CTcDataStream(TCGEN_DICT_STREAM);
261
262
    /* create the grammar-production object data stream */
263
    G_gramprod_stream = new CTcDataStream(TCGEN_GRAMPROD_STREAM);
264
265
    /* create the BigNumber object data stream */
266
    G_bignum_stream = new CTcDataStream(TCGEN_BIGNUM_STREAM);
267
268
    /* create the IntrinsicClass object data stream */
269
    G_int_class_stream = new CTcDataStream(TCGEN_INTCLASS_STREAM);
270
271
    /* create the static initializer identifier stream */
272
    G_static_init_id_stream = new CTcDataStream(TCGEN_STATIC_INIT_ID_STREAM);
273
274
    /* create the target-specific code generator */
275
    G_cg = new CTcGenTarg();
276
277
    /* initialize the parser */
278
    G_prs->init();
279
280
    /* no errors or warnings yet */
281
    error_count_ = 0;
282
    warning_count_ = 0;
283
    first_error_ = 0;
284
    first_warning_ = 0;
285
286
    /* set a fairly liberal maximum error limit */
287
    max_error_count_ = 100;
288
289
    /* there's no disassembly output stream yet */
290
    G_disasm_out = 0;
291
}
292
293
294
/* ------------------------------------------------------------------------ */
295
/*
296
 *   delete the compiler driver 
297
 */
298
CTcMain::~CTcMain()
299
{
300
    /* if there's a disassembly stream, delete it */
301
    if (G_disasm_out != 0)
302
        delete G_disasm_out;
303
304
    /* delete the various data streams */
305
    delete G_cs_main;
306
    delete G_cs_static;
307
    delete G_ds;
308
    delete G_os;
309
    delete G_icmod_stream;
310
    delete G_dict_stream;
311
    delete G_gramprod_stream;
312
    delete G_bignum_stream;
313
    delete G_int_class_stream;
314
    delete G_static_init_id_stream;
315
316
    /* delete the console output character map, if there is one */
317
    if (console_mapper_ != 0)
318
    {
319
        /* release our reference on it */
320
        console_mapper_->release_ref();
321
322
        /* forget it (since it's static) */
323
        console_mapper_ = 0;
324
    }
325
326
    /* delete the target-specific code generator */
327
    delete G_cg;
328
329
    /* delete the parser and node memory pool */
330
    delete G_prs;
331
    delete G_prsmem;
332
333
    /* delete the parser */
334
    delete G_tok;
335
336
    /* delete our default character set name string */
337
    lib_free_str(default_charset_);
338
}
339
340
341
/* ------------------------------------------------------------------------ */
342
/*
343
 *   Log an error, part 1: show the message prefix.  This should be called
344
 *   before formatting the text of the message, and part 2 should be called
345
 *   after formatting the message text.  Returns a pointer to the message
346
 *   template text.  
347
 */
348
static const char *log_msg_internal_1(
349
    CTcTokFileDesc *linedesc, long linenum,
350
    int *err_counter, int *warn_counter, int *first_error, int *first_warning,
351
    unsigned long options, const int *suppress_list, size_t suppress_cnt,
352
    tc_severity_t severity, int err)
353
{
354
    const char *msg;
355
    const char *prefix;
356
357
    /*
358
     *   If this is a warning or a pedantic warning, and it's in the list of
359
     *   suppressed messages, ignore it entirely.  
360
     */
361
    if (severity == TC_SEV_PEDANTIC || severity == TC_SEV_WARNING)
362
    {
363
        size_t rem;
364
        const int *p;
365
366
        /* scan the suppress list */
367
        for (p = suppress_list, rem = suppress_cnt ; rem != 0 ; ++p, --rem)
368
        {
369
            /* check for a match */
370
            if (*p == err)
371
            {
372
                /* it's in the suppress list - ignore the error */
373
                return 0;
374
            }
375
        }
376
    }
377
378
    /* increment the appropriate counter */
379
    switch(severity)
380
    {
381
    case TC_SEV_INFO:
382
        /* 
383
         *   we don't need to count informational messages, and no prefix is
384
         *   required 
385
         */
386
        prefix = "";
387
        break;
388
389
    case TC_SEV_PEDANTIC:
390
        /* 
391
         *   if we're not in "pedantic" mode, or we're not even showing
392
         *   regular errors, ignore it 
393
         */
394
        if (!(options & TCMAIN_ERR_PEDANTIC)
395
            || !(options & TCMAIN_ERR_WARNINGS))
396
            return 0;
397
398
        /* if this is the first warning, remember the code */
399
        if (*warn_counter == 0 && first_warning != 0)
400
            *first_warning = err;
401
402
        /* count it */
403
        ++(*warn_counter);
404
405
        /* set the prefix */
406
        prefix = "warning";
407
        break;
408
409
    case TC_SEV_WARNING:
410
        /* if we're suppressing warnings, ignore it */
411
        if (!(options & TCMAIN_ERR_WARNINGS))
412
            return 0;
413
414
        /* if this is the first warning, remember the code */
415
        if (*warn_counter == 0 && first_warning != 0)
416
            *first_warning = err;
417
418
        /* count the warning */
419
        ++(*warn_counter);
420
421
        /* use an appropriate prefix */
422
        prefix = "warning";
423
        break;
424
425
    case TC_SEV_ERROR:
426
        /* if this is the first error, remember the code */
427
        if (*err_counter == 0 && first_error != 0)
428
            *first_error = err;
429
430
        /* count the error */
431
        ++(*err_counter);
432
433
        /* use an appropriate prefix */
434
        prefix = "error";
435
        break;
436
437
    case TC_SEV_FATAL:
438
        /* if this is the first error, remember the code */
439
        if (*err_counter == 0 && first_error != 0)
440
            *first_error = err;
441
442
        /* count this as an error */
443
        ++(*err_counter);
444
445
        /* use an appropriate prefix */
446
        prefix = "fatal error";
447
        break;
448
449
    case TC_SEV_INTERNAL:
450
        /* if this is the first error, remember the code */
451
        if (*err_counter == 0 && first_error != 0)
452
            *first_error = err;
453
454
        /* count this as an error */
455
        ++(*err_counter);
456
457
        /* use an appropriate prefix */
458
        prefix = "internal error";
459
        break;
460
    }
461
462
    /* display the current parsing position, if available */
463
    if (linedesc != 0)
464
    {
465
        const char *fname;
466
        char qu_buf[OSFNMAX*2 + 2];
467
468
        /* get the filename from the source descriptor */
469
        fname = linedesc->get_fname();
470
471
        /* 
472
         *   if we're in test reporting mode, show only the root part of the
473
         *   filename 
474
         */
475
        if ((options & TCMAIN_ERR_TESTMODE) != 0)
476
            fname = os_get_root_name((char *)fname);
477
478
        /* if they want quoted filenames, quote the filename */
479
        if ((options & TCMAIN_ERR_FNAME_QU) != 0)
480
        {
481
            const char *src;
482
            char *dst;
483
484
            /* quote each character of the filename */
485
            for (src = fname, qu_buf[0] = '"', dst = qu_buf + 1 ;
486
                 *src != '\0' ; )
487
            {
488
                /* if this is a quote character, stutter it */
489
                if (*src == '"')
490
                    *dst++ = '"';
491
492
                /* add this character */
493
                *dst++ = *src++;
494
            }
495
496
            /* add a closing quote and trailing null */
497
            *dst++ = '"';
498
            *dst = '\0';
499
500
            /* use the quoted version of the filename */
501
            fname = qu_buf;
502
        }
503
504
        /* show the filename and line number prefix */
505
        G_hostifc->print_err("%s(%ld): ", fname, linenum);
506
    }
507
508
    /* display the error type prefix */
509
    G_hostifc->print_err("%s", prefix);
510
511
    /* add the error number, if we're showing error numbers */
512
    if ((options & TCMAIN_ERR_NUMBERS) != 0 && severity != TC_SEV_INFO)
513
        G_hostifc->print_err(" %u", (unsigned int)err);
514
515
    /* add a colon and a space for separation */
516
    G_hostifc->print_err(": ");
517
518
    /* get the error message */
519
    msg = tcerr_get_msg(err, (options & TCMAIN_ERR_VERBOSE) != 0);
520
    if (msg == 0)
521
        msg = "[Unable to find message text for this error code.  "
522
              "This might indicate an internal problem with the compiler, "
523
              "or it might be caused by an installation problem that is "
524
              "preventing the compiler from finding an external message "
525
              "file that it requires.]";
526
527
    /* return the message text, so the caller can format it with arguments */
528
    return msg;
529
}
530
531
/*
532
 *   Log an error, part 2: show the message suffix.
533
 */
534
static void log_msg_internal_2(unsigned long options, tc_severity_t severity)
535
{
536
    /* 
537
     *   if we're in verbose mode, and this is an internal error, add the
538
     *   internal error explanation text 
539
     */
540
    if (severity == TC_SEV_INTERNAL && (options & TCMAIN_ERR_VERBOSE) != 0)
541
    {
542
        const char *msg;
543
        
544
        /* get the internal error explanation text and display it */
545
        msg = tcerr_get_msg(TCERR_INTERNAL_EXPLAN, TRUE);
546
        if (msg != 0)
547
            G_hostifc->print_err("\n%s", msg);
548
    }
549
550
    /* end the line */
551
    G_hostifc->print_err((options & TCMAIN_ERR_VERBOSE) != 0 ? "\n\n" : "\n");
552
}
553
554
/* ------------------------------------------------------------------------ */
555
/*
556
 *   Print an error message.  Word-wrap the message if we're in verbose mode.
557
 */
558
static void format_message(const char *msg, unsigned long options)
559
{
560
    /*
561
     *   if we're in verbose mode, word-wrap to 80 columns; otherwise just
562
     *   print it as-is 
563
     */
564
    if ((options & TCMAIN_ERR_VERBOSE) != 0)
565
    {
566
        const char *p;
567
        const int line_wid = 79;
568
569
        /* start on a new line, to skip any prefix text */
570
        G_hostifc->print_err("\n");
571
572
        /* word-wrap to 80 columns */
573
        for (p = msg ; *p != '\0' ; )
574
        {
575
            const char *start;
576
            const char *sp;
577
578
            /* find the next word break */
579
            for (sp = 0, start = p ; ; )
580
            {
581
                /* if this is a space, note it */
582
                if (*p == ' ')
583
                    sp = p;
584
585
                /* 
586
                 *   if we're at the end of the line, or we're over the line
587
                 *   width and we found a space, break here 
588
                 */
589
                if (*p == '\0' || p - start >= line_wid && sp != 0)
590
                {
591
                    /* if we've reached the end, print the rest */
592
                    if (*p == '\0')
593
                        sp = p;
594
595
                    /* trim off trailing spaces */
596
                    for ( ; sp > start && *(sp-1) == ' ' ; --sp) ;
597
598
                    /* show this part */
599
                    G_hostifc->print_err("%.*s\n", sp - start, start);
600
601
                    /* skip leading spaces */
602
                    for ( ; *sp == ' ' ; ++sp) ;
603
604
                    /* if this is the end of the line, we're done */
605
                    if (*p == '\0')
606
                        break;
607
608
                    /* start over here */
609
                    start = p = sp;
610
                }
611
                else
612
                {
613
                    /* this one fits - skip it */
614
                    ++p;
615
                }
616
            }
617
        }
618
    }
619
    else
620
    {
621
        /* display it */
622
        G_hostifc->print_err("%s", msg);
623
    }
624
}
625
626
/* ------------------------------------------------------------------------ */
627
/*
628
 *   log an error from an exception object 
629
 */
630
void CTcMain::S_log_error(CTcTokFileDesc *linedesc, long linenum,
631
                          int *err_counter, int *warn_counter,
632
                          unsigned long options,
633
                          const int *suppress_list, size_t suppress_cnt,
634
                          tc_severity_t severity, CVmException *exc)
635
{
636
    const char *msg;
637
    char msgbuf[1024];
638
    
639
    /* show the prefix */
640
    msg = log_msg_internal_1(linedesc, linenum, err_counter, warn_counter,
641
                             0, 0, options, suppress_list, suppress_cnt,
642
                             severity, exc->get_error_code());
643
644
    /* if the message is suppressed, we're done */
645
    if (msg == 0)
646
        return;
647
648
    /* format the message using arguments stored in the exception */
649
    err_format_msg(msgbuf, sizeof(msgbuf), msg, exc);
650
651
    /* print the error */
652
    format_message(msgbuf, options);
653
654
    /* show the suffix */
655
    log_msg_internal_2(options, severity);
656
}
657
658
/* ------------------------------------------------------------------------ */
659
/*
660
 *   Log an error using va_list arguments
661
 */
662
void CTcMain::S_v_log_error(CTcTokFileDesc *linedesc, long linenum,
663
                            int *err_counter, int *warn_counter,
664
                            int *first_error, int *first_warning,
665
                            unsigned long options,
666
                            const int *suppress_list, size_t suppress_cnt,
667
                            tc_severity_t severity, int err, va_list args)
668
{
669
    const char *msg;
670
    char msgbuf[2048];
671
672
    /* show the prefix */
673
    msg = log_msg_internal_1(linedesc, linenum, err_counter, warn_counter,
674
                             first_error, first_warning, options,
675
                             suppress_list, suppress_cnt, severity, err);
676
677
    /* if the message is suppressed, we're done */
678
    if (msg == 0)
679
        return;
680
681
    /* format the message using the va_list argument */
682
    t3vsprintf(msgbuf, sizeof(msgbuf), msg, args);
683
684
    /* display it */
685
    format_message(msgbuf, options);
686
687
    /* show the suffix */
688
    log_msg_internal_2(options, severity);
689
}
690
691
/* ------------------------------------------------------------------------ */
692
/*
693
 *   Check the current error count against the maximum error limit, and throw
694
 *   a fatal error if we've reached the limit.  
695
 */
696
void CTcMain::check_error_limit()
697
{
698
    /* check the error count against the limit */
699
    if (error_count_ > max_error_count_)
700
    {
701
        /* 
702
         *   raise the maximum error count a bit so that we don't encounter
703
         *   another maximum error situation and loop on flagging the
704
         *   too-many-errors error while trying to display a too-many-errors
705
         *   error 
706
         */
707
        max_error_count_ = error_count_ + 100;
708
709
        /* display a message explaining the problem */
710
        log_error(G_tok->get_last_desc(), G_tok->get_last_linenum(),
711
                  TC_SEV_FATAL, TCERR_TOO_MANY_ERRORS);
712
713
        /* throw the generic fatal error, since we've logged this */
714
        err_throw(TCERR_FATAL_ERROR);
715
    }
716
}
717