cfad47cfa3/t3compiler/tads3/tcprsstm.cpp

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#ifdef RCSID
2
static char RCSid[] =
3
"$Header: d:/cvsroot/tads/tads3/TCPRSSTM.CPP,v 1.1 1999/07/11 00:46:54 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
  tcprs.cpp - TADS 3 Compiler Parser - statement node classes
15
Function
16
  This parser module includes statement node classes that are needed
17
  only for a full compiler.  This module should not be needed for
18
  tools that need only to compile expressions, such as debuggers.
19
Notes
20
  
21
Modified
22
  04/30/99 MJRoberts  - Creation
23
*/
24
25
#include <stdlib.h>
26
#include <string.h>
27
#include <stdio.h>
28
29
#include "os.h"
30
#include "t3std.h"
31
#include "utf8.h"
32
#include "tcprs.h"
33
#include "tctarg.h"
34
#include "tcgen.h"
35
#include "vmhash.h"
36
#include "tcmain.h"
37
#include "vmfile.h"
38
#include "tcmake.h"
39
40
41
/* ------------------------------------------------------------------------ */
42
/*
43
 *   Add an entry to a global symbol table, and mark the symbol as
44
 *   referenced.  
45
 */
46
void CTcPrsSymtab::add_to_global_symtab(CTcPrsSymtab *tab, CTcSymbol *entry)
47
{
48
    /* add the entry to the symbol table */
49
    tab->get_hashtab()->add(entry);
50
51
    /* mark the entry as referenced */
52
    entry->mark_referenced();
53
}
54
55
56
/* ------------------------------------------------------------------------ */
57
/*
58
 *   Callback context for enumerating the global symbol table entries for
59
 *   writing a symbol export file 
60
 */
61
struct prs_wsf_ctx
62
{
63
    /* number of symbols so far */
64
    ulong cnt;
65
    
66
    /* the file we're writing */
67
    CVmFile *fp;
68
69
    /* the parser object */
70
    CTcParser *prs;
71
};
72
73
/*
74
 *   Signature string for symbol export file.  The numerical suffix at the
75
 *   end of the initial string is a format version number - we simply
76
 *   increment this each time we make a change to the format.  On loading a
77
 *   symbol file, we check to make sure the format matches; if it doesn't,
78
 *   we'll abort with an error, ensuring that we don't try to read a file
79
 *   that's not formatted with the current structure.  Since symbol files are
80
 *   derived directly from source files via compilation, the worst case is
81
 *   that the user has to do a full recompile when upgrading to a new
82
 *   compiler version, so we don't bother trying to maintain upward
83
 *   compatibility among the symbol file formats.  
84
 */
85
static const char symbol_export_sig[] = "TADS3.SymbolExport.0006\n\r\032";
86
87
/*
88
 *   Write a symbol export file.  
89
 */
90
void CTcParser::write_symbol_file(CVmFile *fp, CTcMake *make_obj)
91
{
92
    prs_wsf_ctx ctx;
93
    long size_ofs;
94
    long cnt_ofs;
95
    long end_ofs;
96
    int i;
97
    int cnt;
98
99
    /* write the file signature */
100
    fp->write_bytes(symbol_export_sig, sizeof(symbol_export_sig) - 1);
101
102
    /* write a placeholder for the make-config block size */
103
    size_ofs = fp->get_pos();
104
    fp->write_int4(0);
105
106
    /* if there's a make object, tell it to write out its config data */
107
    if (make_obj != 0)
108
    {
109
        long end_ofs;
110
111
        /* write out the data */
112
        make_obj->write_build_config_to_sym_file(fp);
113
114
        /* go back and fix up the size */
115
        end_ofs = fp->get_pos();
116
        fp->set_pos(size_ofs);
117
        fp->write_int4(end_ofs - size_ofs - 4);
118
119
        /* seek back to the end for continued writing */
120
        fp->set_pos(end_ofs);
121
    }
122
123
    /* 
124
     *   write the number of intrinsic function sets, followed by the
125
     *   function set names 
126
     */
127
    fp->write_int4(cnt = G_cg->get_fnset_cnt());
128
    for (i = 0 ; i < cnt ; ++i)
129
    {
130
        const char *nm;
131
        size_t len;
132
133
        /* get the name */
134
        nm = G_cg->get_fnset_name(i);
135
136
        /* write the length followed by the name */
137
        fp->write_int2(len = strlen(nm));
138
        fp->write_bytes(nm, len);
139
    }
140
141
    /* 
142
     *   write the number of intrinsic classes, followed by the intrinsic
143
     *   class names 
144
     */
145
    fp->write_int4(cnt = G_cg->get_meta_cnt());
146
    for (i = 0 ; i < cnt ; ++i)
147
    {
148
        const char *nm;
149
        size_t len;
150
151
        /* get the name */
152
        nm = G_cg->get_meta_name(i);
153
154
        /* write the length followed by the name */
155
        fp->write_int2(len = strlen(nm));
156
        fp->write_bytes(nm, len);
157
    }
158
159
    /* 
160
     *   write a placeholder for the symbol count, first remembering where
161
     *   we put it so that we can come back and fix it up after we know
162
     *   how many symbols there actually are 
163
     */
164
    cnt_ofs = fp->get_pos();
165
    fp->write_int4(0);
166
167
    /* write each entry from the global symbol table */
168
    ctx.cnt = 0;
169
    ctx.fp = fp;
170
    ctx.prs = this;
171
    global_symtab_->enum_entries(&write_sym_cb, &ctx);
172
173
    /* go back and fix up the counter */
174
    end_ofs = fp->get_pos();
175
    fp->set_pos(cnt_ofs);
176
    fp->write_int4(ctx.cnt);
177
178
    /* 
179
     *   seek back to the ending offset, in case the caller is embedding
180
     *   the symbol data stream in a larger file structure of some kind
181
     *   and will be writing additional data after the end of our portion 
182
     */
183
    fp->set_pos(end_ofs);
184
}
185
186
/*
187
 *   Callback for symbol table enumeration for writing a symbol export
188
 *   file.  Writes one symbol to the file.  
189
 */
190
void CTcParser::write_sym_cb(void *ctx0, CTcSymbol *sym)
191
{
192
    prs_wsf_ctx *ctx = (prs_wsf_ctx *)ctx0;
193
194
    /* ask the symbol to write itself to the file */
195
    if (sym->write_to_sym_file(ctx->fp))
196
    {
197
        /* we wrote this symbol - count it */
198
        ++(ctx->cnt);
199
    }
200
}
201
202
/* ------------------------------------------------------------------------ */
203
/*
204
 *   Seek to the start of the build configuration information in an object
205
 *   file 
206
 */
207
ulong CTcParser::seek_sym_file_build_config_info(class CVmFile *fp)
208
{
209
    char buf[128];
210
    ulong siz;
211
    int err = FALSE;
212
213
    /* read the signature, and make sure it matches */
214
    err_try
215
    {
216
        fp->read_bytes(buf, sizeof(symbol_export_sig) - 1);
217
        if (memcmp(buf, symbol_export_sig,
218
                   sizeof(symbol_export_sig) - 1) != 0)
219
        {
220
            /* invalid signature - no config data available */
221
            return 0;
222
        }
223
224
        /* read the size of the block */
225
        siz = fp->read_uint4();
226
    }
227
    err_catch(exc)
228
    {
229
        /* note the error */
230
        err = TRUE;
231
    }
232
    err_end;
233
234
    /* 
235
     *   if an error occurred, indicate that no configuration block is
236
     *   available 
237
     */
238
    if (err)
239
        return 0;
240
241
    /* 
242
     *   tell the caller the size of the block; the block immediately
243
     *   follows the size prefix, so the file pointer is set to the right
244
     *   position now 
245
     */
246
    return siz;
247
}
248
249
250
/* ------------------------------------------------------------------------ */
251
/*
252
 *   Read a symbol file 
253
 */
254
int CTcParser::read_symbol_file(CVmFile *fp)
255
{
256
    char buf[128];
257
    ulong cnt;
258
    ulong i;
259
    ulong skip_len;
260
    
261
    /* read the signature and ensure it's valid */
262
    fp->read_bytes(buf, sizeof(symbol_export_sig) - 1);
263
    if (memcmp(buf, symbol_export_sig, sizeof(symbol_export_sig) - 1) != 0)
264
    {
265
        /* it's invalid - log an error */
266
        G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_SYMEXP_INV_SIG);
267
268
        /* return failure */
269
        return 1;
270
    }
271
272
    /* 
273
     *   read the make-config data size and skip the data block - it's here
274
     *   for use by the make utility, and we don't have any use for it here 
275
     */
276
    skip_len = fp->read_uint4();
277
    fp->set_pos(fp->get_pos() + skip_len);
278
279
    /* read the intrinsic function set count, then read the function sets */
280
    cnt = fp->read_uint4();
281
    for (i = 0 ; i < cnt ; ++i)
282
    {
283
        char buf[256];
284
285
        /* read the function set name */
286
        if (CTcParser::read_len_prefix_str(fp, buf, sizeof(buf),
287
                                           TCERR_SYMEXP_SYM_TOO_LONG))
288
            return 101;
289
290
        /* add it to the function set list */
291
        G_cg->add_fnset(buf);
292
    }
293
294
    /* read the metaclass count, then read the metaclasses */
295
    cnt = fp->read_uint4();
296
    for (i = 0 ; i < cnt ; ++i)
297
    {
298
        char buf[256];
299
        
300
        /* read the metaclass external name */
301
        if (CTcParser::read_len_prefix_str(fp, buf, sizeof(buf),
302
                                           TCERR_SYMEXP_SYM_TOO_LONG))
303
            return 102;
304
305
        /* add it to the metaclass list */
306
        G_cg->find_or_add_meta(buf, strlen(buf), 0);
307
    }
308
309
    /* read the symbol count */
310
    cnt = fp->read_uint4();
311
312
    /* read the symbols */
313
    for ( ; cnt != 0 ; --cnt)
314
    {
315
        CTcSymbol *sym;
316
        CTcSymbol *old_sym;
317
        
318
        /* read a symbol */
319
        sym = CTcSymbol::read_from_sym_file(fp);
320
321
        /* if that failed, abort */
322
        if (sym == 0)
323
            return 2;
324
325
        /* check for an existing definition */
326
        old_sym = global_symtab_->find(sym->get_sym(), sym->get_sym_len());
327
328
        /* 
329
         *   If the symbol is already in the symbol table, log a warning
330
         *   and ignore the symbol.  If the new symbol we loaded is the
331
         *   same object as the original symbol table entry, it means that
332
         *   this is a valid redefinition - the symbol subclass loader
333
         *   indicated this by returning the same entry.  
334
         */
335
        if (old_sym == sym)
336
        {
337
            /* 
338
             *   it's a harmless redefinition - there's no need to warn
339
             *   and no need to add the symbol to the table again 
340
             */
341
        }
342
        else if (old_sym != 0)
343
        {
344
            /* 
345
             *   it's already defined - log it as a pedantic warning (it's
346
             *   not a standard-level warning because, if it is actually a
347
             *   problem, it'll cause an actual error at link time, so
348
             *   there's little value in complaining about it now)
349
             */
350
            G_tcmain->log_error(0, 0, TC_SEV_PEDANTIC, TCERR_SYMEXP_REDEF,
351
                                (int)sym->get_sym_len(), sym->get_sym());
352
        }
353
        else
354
        {
355
            /* add it to the global symbol table */
356
            global_symtab_->add_entry(sym);
357
        }
358
    }
359
360
    /* success */
361
    return 0;
362
}
363
364
/* ------------------------------------------------------------------------ */
365
/*
366
 *   Write to an object file 
367
 */
368
369
/*
370
 *   Write to an object file
371
 */
372
void CTcParser::write_to_object_file(CVmFile *fp)
373
{
374
    prs_wsf_ctx ctx;
375
    long cnt_ofs;
376
    long end_ofs;
377
    ulong anon_cnt;
378
    ulong nonsym_cnt;
379
    tcprs_nonsym_obj *nonsym;
380
    CTcSymObj *anon_obj;
381
    CTcGramProdEntry *prod;
382
    ulong prod_cnt;
383
    CTcPrsExport *exp;
384
    ulong exp_cnt;
385
386
    /* 
387
     *   reset the object file symbol and dictionary index values - start
388
     *   with 1, since 0 is a "null pointer" index value 
389
     */
390
    obj_file_sym_idx_ = 1;
391
    obj_file_dict_idx_ = 1;
392
393
    /* 
394
     *   write a placeholder for the symbol index table count, the
395
     *   dictionary table count, and the actual symbol count 
396
     */
397
    cnt_ofs = fp->get_pos();
398
    fp->write_int4(0);
399
    fp->write_int4(0);
400
    fp->write_int4(0);
401
402
    /* write each entry from the global symbol table */
403
    ctx.cnt = 0;
404
    ctx.fp = fp;
405
    ctx.prs = this;
406
    global_symtab_->enum_entries(&write_obj_cb, &ctx);
407
408
    /* count the anonymous objects */
409
    for (anon_cnt = 0, anon_obj = anon_obj_head_ ; anon_obj != 0 ;
410
         ++anon_cnt, anon_obj = (CTcSymObj *)anon_obj->nxt_) ;
411
412
    /* write the number of anonymous objects */
413
    fp->write_int4(anon_cnt);
414
415
    /* write the anonymous objects */
416
    for (anon_obj = anon_obj_head_ ; anon_obj != 0 ;
417
         anon_obj = (CTcSymObj *)anon_obj->nxt_)
418
    {
419
        /* write this anonymous object */
420
        anon_obj->write_to_obj_file(fp);
421
    }
422
423
    /* count the non-symbol objects */
424
    for (nonsym_cnt = 0, nonsym = nonsym_obj_head_ ; nonsym != 0 ;
425
         ++nonsym_cnt, nonsym = nonsym->nxt_) ;
426
427
    /* write the non-symbol object ID's */
428
    fp->write_int4(nonsym_cnt);
429
    for (nonsym = nonsym_obj_head_ ; nonsym != 0 ; nonsym = nonsym->nxt_)
430
        fp->write_int4((ulong)nonsym->id_);
431
432
    /* remember where we are, and seek back to the counters for fixup */
433
    end_ofs = fp->get_pos();
434
    fp->set_pos(cnt_ofs);
435
436
    /* write the actual symbol index count */
437
    fp->write_int4(obj_file_sym_idx_);
438
439
    /* write the actual dictionary index count */
440
    fp->write_int4(obj_file_dict_idx_);
441
442
    /* write the total named symbol count */
443
    fp->write_int4(ctx.cnt);
444
445
    /* 
446
     *   seek back to the ending offset so we can continue on to write the
447
     *   remainder of the file 
448
     */
449
    fp->set_pos(end_ofs);
450
451
    /* write a placeholder for the symbol cross-reference entries */
452
    cnt_ofs = fp->get_pos();
453
    fp->write_int4(0);
454
455
    /* 
456
     *   enumerate entries again, this time writing cross-object
457
     *   references (this must be done after we've written out the main
458
     *   part of each object, so that we have a full table of all of the
459
     *   objects loaded before we write any references) 
460
     */
461
    ctx.cnt = 0;
462
    global_symtab_->enum_entries(&write_obj_ref_cb, &ctx);
463
464
    /* go back and fix up the cross-reference count */
465
    end_ofs = fp->get_pos();
466
    fp->set_pos(cnt_ofs);
467
    fp->write_int4(ctx.cnt);
468
    fp->set_pos(end_ofs);
469
470
    /* write the anonymous object cross-references */
471
    fp->write_int4(anon_cnt);
472
    for (anon_obj = anon_obj_head_ ; anon_obj != 0 ;
473
         anon_obj = (CTcSymObj *)anon_obj->nxt_)
474
    {
475
        /* write this anonymous object's cross-references */
476
        anon_obj->write_refs_to_obj_file(fp);
477
    }
478
479
    /* count the grammar productions */
480
    for (prod_cnt = 0, prod = gramprod_head_ ; prod != 0 ;
481
         ++prod_cnt, prod = prod->get_next()) ;
482
483
    /* write the number of productions */
484
    fp->write_int4(prod_cnt);
485
486
    /* 
487
     *   write the main list of grammar rules - this is the list of
488
     *   productions that are associated with anonymous match objects and
489
     *   hence cannot be replaced or modified 
490
     */
491
    for (prod = gramprod_head_ ; prod != 0 ; prod = prod->get_next())
492
    {
493
        /* write this entry */
494
        prod->write_to_obj_file(fp);
495
    }
496
497
    /* write a placeholder for the named grammar rule count */
498
    cnt_ofs = fp->get_pos();
499
    fp->write_int4(0);
500
501
    /* 
502
     *   write the private grammar rules - this is the set of rules
503
     *   associated with named match objects, so we write these by
504
     *   enumerating the symbol table again through the grammar rule writer
505
     *   callback 
506
     */
507
    ctx.cnt = 0;
508
    global_symtab_->enum_entries(&write_obj_gram_cb, &ctx);
509
510
    /* go back and fix up the named grammar rule count */
511
    end_ofs = fp->get_pos();
512
    fp->set_pos(cnt_ofs);
513
    fp->write_int4(ctx.cnt);
514
    fp->set_pos(end_ofs);
515
516
    /* count the exports */
517
    for (exp_cnt = 0, exp = exp_head_ ; exp != 0 ;
518
         ++exp_cnt, exp = exp->get_next());
519
520
    /* write the number of exports */
521
    fp->write_int4(exp_cnt);
522
523
    /* write the exports */
524
    for (exp = exp_head_ ; exp != 0 ; exp = exp->get_next())
525
    {
526
        /* write this entry */
527
        exp->write_to_obj_file(fp);
528
    }
529
}
530
531
/*
532
 *   Callback for symbol table enumeration for writing an object file.
533
 *   Writes one symbol to the file.  
534
 */
535
void CTcParser::write_obj_cb(void *ctx0, CTcSymbol *sym)
536
{
537
    prs_wsf_ctx *ctx = (prs_wsf_ctx *)ctx0;
538
539
    /* ask the symbol to write itself to the file */
540
    if (sym->write_to_obj_file(ctx->fp))
541
    {
542
        /* we wrote this symbol - count it */
543
        ++(ctx->cnt);
544
    }
545
}
546
547
/*
548
 *   Callback for symbol table numeration - write object references to the
549
 *   object file. 
550
 */
551
void CTcParser::write_obj_ref_cb(void *ctx0, CTcSymbol *sym)
552
{
553
    prs_wsf_ctx *ctx = (prs_wsf_ctx *)ctx0;
554
555
    /* ask the symbol to write its references to the file */
556
    if (sym->write_refs_to_obj_file(ctx->fp))
557
        ++(ctx->cnt);
558
}
559
560
/* 
561
 *   Callback for symbol table enumeration - write named grammar rules 
562
 */
563
void CTcParser::write_obj_gram_cb(void *ctx0, CTcSymbol *sym)
564
{
565
    prs_wsf_ctx *ctx = (prs_wsf_ctx *)ctx0;
566
    CTcSymObj *obj_sym;
567
568
    /* if it's not an object symbol, we can ignore it */
569
    if (sym->get_type() != TC_SYM_OBJ)
570
        return;
571
572
    /* cast it to the proper type */
573
    obj_sym = (CTcSymObj *)sym;
574
575
    /* if it doesn't have a private grammar rule list, ignore it */
576
    if (obj_sym->get_grammar_entry() == 0)
577
        return;
578
579
    /* count it */
580
    ++(ctx->cnt);
581
582
    /* write the defining symbol's object file index */
583
    ctx->fp->write_int4(obj_sym->get_obj_file_idx());
584
585
    /* write the grammar rule */
586
    obj_sym->get_grammar_entry()->write_to_obj_file(ctx->fp);
587
}
588
589
/* ------------------------------------------------------------------------ */
590
/*
591
 *   Turn sourceTextGroup mode on or off 
592
 */
593
void CTcParser::set_source_text_group_mode(int f)
594
{
595
    /* set the new mode */
596
    src_group_mode_ = (f != 0);
597
598
    /* 
599
     *   If we're turning this mode on for the first time, add the object
600
     *   definition for the sourceTextGroup object - this is an anonymous
601
     *   object that we automatically create, once per source module; all of
602
     *   the sourceTextGroup properties in the module point to this same
603
     *   object, which is how we can tell at run-time that they were defined
604
     *   in the same module.  
605
     */
606
    if (f && src_group_id_ == TCTARG_INVALID_OBJ)
607
    {
608
        CTcSymObj *obj;
609
        CTPNStmObject *obj_stm;
610
        CTcConstVal cval;
611
612
        /* create the anonymous object */
613
        src_group_id_ = G_cg->new_obj_id();
614
615
        /* create an anonymous symbol table entry for the object */
616
        obj = new CTcSymObj(".sourceTextGroup", 16, FALSE,
617
                            src_group_id_, FALSE, TC_META_TADSOBJ, 0);
618
        G_prs->add_anon_obj(obj);
619
620
        /* create an object statement for it */
621
        obj_stm = new CTPNStmObject(obj, FALSE);
622
        obj->set_obj_stm(obj_stm);
623
624
        /* 
625
         *   if we know the module name and sequence number, set properties
626
         *   for them in the referent object 
627
         */
628
        if (module_name_ != 0)
629
        {
630
            cval.set_sstr(module_name_, strlen(module_name_));
631
            obj_stm->add_prop(src_group_mod_sym_, new CTPNConst(&cval),
632
                              FALSE, FALSE);
633
        }
634
        if (module_seqno_ != 0)
635
        {
636
            cval.set_int(module_seqno_);
637
            obj_stm->add_prop(src_group_seq_sym_, new CTPNConst(&cval),
638
                              FALSE, FALSE);
639
        }
640
641
        /* add it to the nested statement list */
642
        add_nested_stm(obj_stm);
643
    }
644
}
645
646
/* ------------------------------------------------------------------------ */
647
/*
648
 *   Parse top-level definitions.  Returns the head of a list of statement
649
 *   nodes; the statements are the top-level statements in the program.  
650
 */
651
class CTPNStmProg *CTcParser::parse_top()
652
{
653
    int err;
654
    int done;
655
    CTPNStmTop *first_stm;
656
    CTPNStmTop *last_stm;
657
    int suppress_error;
658
659
    /* nothing in our list yet */
660
    first_stm = last_stm = 0;
661
662
    /* if there are any pending nested statements, add them to the list now */
663
    if (nested_stm_head_ != 0)
664
    {
665
        /* move the nested statement list into the top-level list */
666
        first_stm = nested_stm_head_;
667
        last_stm = nested_stm_tail_;
668
669
        /* clear out the old list */
670
        nested_stm_head_ = nested_stm_tail_ = 0;
671
    }
672
673
    /* do not suppress errors */
674
    suppress_error = FALSE;
675
    
676
    /* no end-of-file error yet */
677
    err = FALSE;
678
679
    /* keep going until we reach the end of the file */
680
    for (done = FALSE ; !done && !err ; )
681
    {
682
        CTPNStmTop *cur_stm;
683
        int suppress_next_error;
684
685
        /* we don't have a statement yet */
686
        cur_stm = 0;
687
688
        /* 
689
         *   presume we'll find a valid statement and hence won't need to
690
         *   suppress subsequent errors 
691
         */
692
        suppress_next_error = FALSE;
693
        
694
        /* see what we have */
695
        switch(G_tok->cur())
696
        {
697
        case TOKT_EOF:
698
            /* we're done */
699
            done = TRUE;
700
            break;
701
            
702
        case TOKT_FUNCTION:
703
            /* parse the function definition */
704
            cur_stm = parse_function(&err, FALSE, FALSE, FALSE, TRUE);
705
            break;
706
707
        case TOKT_EXTERN:
708
            /* parse the extern definition */
709
            parse_extern(&err);
710
            break;
711
712
        case TOKT_INTRINSIC:
713
            /* parse an intrinsic function set definition */
714
            cur_stm = parse_intrinsic(&err);
715
            break;
716
717
        case TOKT_TRANSIENT:
718
        case TOKT_SYM:
719
            /* it's an object or function definition */
720
            cur_stm = parse_object_or_func(&err, FALSE, suppress_error,
721
                                           &suppress_next_error);
722
            break;
723
724
        case TOKT_OBJECT:
725
            /* 
726
             *   it's an anonymous object with 'object' superclass, or an
727
             *   'object template' definition 
728
             */
729
            cur_stm = parse_object_stm(&err, FALSE);
730
            break;
731
732
        case TOKT_PLUS:
733
        case TOKT_INC:
734
            cur_stm = parse_plus_object(&err);
735
            break;
736
737
        case TOKT_CLASS:
738
            /* it's a class definition */
739
            cur_stm = parse_class(&err);
740
            break;
741
742
        case TOKT_REPLACE:
743
            /* it's a 'replace' statement */
744
            cur_stm = parse_replace(&err);
745
            break;
746
747
        case TOKT_MODIFY:
748
            /* it's a 'modify' statement */
749
            cur_stm = parse_modify(&err);
750
            break;
751
752
        case TOKT_PROPERTY:
753
            /* it's a 'property' statement */
754
            parse_property(&err);
755
            break;
756
757
        case TOKT_EXPORT:
758
            /* it's an 'export' statement */
759
            parse_export(&err);
760
            break;
761
762
        case TOKT_DICTIONARY:
763
            /* it's a 'dictionary' statement */
764
            cur_stm = parse_dict(&err);
765
            break;
766
767
        case TOKT_GRAMMAR:
768
            /* it's a 'grammar' statement */
769
            cur_stm = parse_grammar(&err, FALSE, FALSE);
770
            break;
771
772
        case TOKT_ENUM:
773
            /* it's an 'enum' statement */
774
            parse_enum(&err);
775
            break;
776
777
        case TOKT_SEM:
778
            /* empty statement - skip it */
779
            G_tok->next();
780
            break;
781
782
        default:
783
            /* 
784
             *   note the error, unless we just generated the same error
785
             *   for the previous token - if we did, there's no point in
786
             *   showing it again, since we're probably skipping lots of
787
             *   tokens trying to get resynchronized 
788
             */
789
            if (!suppress_error)
790
                G_tok->log_error_curtok(TCERR_REQ_FUNC_OR_OBJ);
791
792
            /* suppress the next error of the same kind */
793
            suppress_next_error = TRUE;
794
            
795
            /* skip the errant token */
796
            G_tok->next();
797
            break;
798
        }
799
800
        /* 
801
         *   set the error suppression status according to whether we just
802
         *   found an error - if we did, don't report another of the same
803
         *   kind twice in a row, since we're probably just scanning
804
         *   through tons of stuff trying to get re-synchronized 
805
         */
806
        suppress_error = suppress_next_error;
807
808
        /* if we parsed a statement, add it to our list */
809
        if (cur_stm != 0)
810
        {
811
            /* link the statement at the end of our list */
812
            if (last_stm != 0)
813
                last_stm->set_next_stm_top(cur_stm);
814
            else
815
                first_stm = cur_stm;
816
            last_stm = cur_stm;
817
        }
818
    }
819
820
    /* add the list of nested top-level statements to the statement list */
821
    if (nested_stm_head_ != 0)
822
    {
823
        if (last_stm != 0)
824
            last_stm->set_next_stm_top(nested_stm_head_);
825
        else
826
            first_stm = nested_stm_head_;
827
    }
828
829
    /* 
830
     *   we've now moved the nested top-level statement list into the normal
831
     *   program statement list, so we can forget this as a separate list 
832
     */
833
    nested_stm_head_ = nested_stm_tail_ = 0;
834
835
    /* construct a compound statement to hold the top-level statement list */
836
    return new CTPNStmProg(first_stm);
837
}
838
839
/*
840
 *   Parse a 'property' top-level statement 
841
 */
842
void CTcParser::parse_property(int *err)
843
{
844
    int done;
845
    
846
    /* skip the 'property' token */
847
    G_tok->next();
848
849
    /* parse property names until we run out */
850
    for (done = FALSE ; !done ; )
851
    {
852
        /* we should be looking at a property name */
853
        if (G_tok->cur() == TOKT_SYM)
854
        {
855
            /* 
856
             *   look up the property; this will add it to the symbol table
857
             *   if it isn't already in there, and will generate an error if
858
             *   it's already defined as something other than a property 
859
             */
860
            look_up_prop(G_tok->getcur(), TRUE);
861
            
862
            /* skip the symbol and check what follows */
863
            if (G_tok->next() == TOKT_COMMA)
864
            {
865
                /* skip the comma and continue */
866
                G_tok->next();
867
            }
868
            else if (G_tok->cur() == TOKT_SEM)
869
            {
870
                /* 
871
                 *   end of the statement - skip the semicolon and we're
872
                 *   done 
873
                 */
874
                G_tok->next();
875
                break;
876
            }
877
            else
878
            {
879
                /* 
880
                 *   if it's a brace, they probably left out the semicolon;
881
                 *   if it's a symbol, they probably left out the comma;
882
                 *   otherwise, it's probably a stray token 
883
                 */
884
                switch(G_tok->cur())
885
                {
886
                case TOKT_SYM:
887
                    /* 
888
                     *   probably left out a comma - flag an error, but
889
                     *   proceed with parsing this new symbol 
890
                     */
891
                    G_tok->log_error_curtok(TCERR_PROPDECL_REQ_COMMA);
892
                    break;
893
                    
894
                case TOKT_LBRACE:
895
                case TOKT_RBRACE:
896
                case TOKT_EOF:
897
                    /* they probably left out the semicolon */
898
                    G_tok->log_error_curtok(TCERR_EXPECTED_SEMI);
899
                    return;
900
                    
901
                default:
902
                    /* log an error */
903
                    G_tok->log_error_curtok(TCERR_PROPDECL_REQ_COMMA);
904
                    
905
                    /* skip the errant symbol */
906
                    G_tok->next();
907
                    
908
                    /* 
909
                     *   if a comma follows, something was probably just
910
                     *   inserted - skip the comma; if a semicolon follows,
911
                     *   assume we're at the end of the statement 
912
                     */
913
                    if (G_tok->cur() == TOKT_COMMA)
914
                    {
915
                        /* 
916
                         *   we're probably okay again - skip the comma and
917
                         *   continue 
918
                         */
919
                        G_tok->next();
920
                    }
921
                    else if (G_tok->cur() == TOKT_SEM)
922
                    {
923
                        /* skip the semicolon, and we're done */
924
                        G_tok->next();
925
                        done = TRUE;
926
                    }
927
                    
928
                    /* proceed with what follows */
929
                    break;
930
                }
931
            }
932
        }
933
        else
934
        {
935
            /* log an error */
936
            G_tok->log_error_curtok(TCERR_PROPDECL_REQ_SYM);
937
938
            switch(G_tok->cur())
939
            {
940
            case TOKT_SEM:
941
            case TOKT_LBRACE:
942
            case TOKT_RBRACE:
943
            case TOKT_EOF:
944
                /* they're probably done with the statement */
945
                return;
946
947
            case TOKT_COMMA:
948
                /* 
949
                 *   they probably just doubled a comma - skip it and
950
                 *   continue 
951
                 */
952
                G_tok->next();
953
                break;
954
                
955
            default:
956
                /* skip this token */
957
                G_tok->next();
958
                
959
                /* 
960
                 *   if a comma follows, they probably put in a keyword or
961
                 *   something like that - skip the comma and continue 
962
                 */
963
                if (G_tok->cur() == TOKT_COMMA)
964
                    G_tok->next();
965
                break;
966
            }
967
            
968
            /* if a semicolon follows, we're done */
969
            if (G_tok->cur() == TOKT_SEM)
970
            {
971
                /* skip the semicolon and we're done */
972
                G_tok->next();
973
                break;
974
            }
975
        }
976
    }
977
}
978
979
/*
980
 *   Parse an 'export' top-level statement 
981
 */
982
void CTcParser::parse_export(int *err)
983
{
984
    CTcPrsExport *exp;
985
    
986
    /* skip the 'export' token and see what follows */
987
    switch(G_tok->next())
988
    {
989
    case TOKT_SYM:
990
        /* create a new export record for this symbol token */
991
        exp = add_export(G_tok->getcur()->get_text(),
992
                         G_tok->getcur()->get_text_len());
993
994
        /* 
995
         *   Check to see what follows.  We can either have a single-quoted
996
         *   string giving the external name by which this symbol should be
997
         *   known, or the end of the statement.  If it's the latter, the
998
         *   external name is the same as the symbol name. 
999
         */
1000
        switch(G_tok->next())
1001
        {
1002
        case TOKT_SEM:
1003
            /* end of the statement - skip the semicolon, and we're done */
1004
            G_tok->next();
1005
            break;
1006
1007
        case TOKT_SSTR:
1008
            /* 
1009
             *   it's an explicit external name specification - save the
1010
             *   name with the export record 
1011
             */
1012
            exp->set_extern_name(G_tok->getcur()->get_text(),
1013
                                 G_tok->getcur()->get_text_len());
1014
1015
            /* if it's too long, flag an error */
1016
            if (G_tok->getcur()->get_text_len() > TOK_SYM_MAX_LEN)
1017
                G_tok->log_error_curtok(TCERR_EXPORT_EXT_TOO_LONG);
1018
1019
            /* skip the string and check for a semicolon */
1020
            if (G_tok->next() != TOKT_SEM)
1021
            {
1022
                /* assume they just left out the semicolon, but log it */
1023
                G_tok->log_error_curtok(TCERR_EXPECTED_SEMI);
1024
            }
1025
            break;
1026
1027
        default:
1028
            /* 
1029
             *   anything else is an error; assume they simply omitted the
1030
             *   semicolon 
1031
             */
1032
            G_tok->log_error_curtok(TCERR_EXPECTED_SEMI);
1033
        }
1034
1035
        /* done */
1036
        break;
1037
1038
    case TOKT_SEM:
1039
    case TOKT_LBRACE:
1040
    case TOKT_RBRACE:
1041
    case TOKT_EOF:
1042
        /* 
1043
         *   they probably just left something out; consider this the end of
1044
         *   the statement, but log an error about it 
1045
         */
1046
        G_tok->log_error_curtok(TCERR_EXPORT_REQ_SYM);
1047
        break;
1048
        
1049
    default:
1050
        /* 
1051
         *   something weird happened; skip the errant token, and consider
1052
         *   it the end of the statement 
1053
         */
1054
        G_tok->log_error_curtok(TCERR_EXPORT_REQ_SYM);
1055
        G_tok->next();
1056
        break;
1057
    }
1058
}
1059
1060
/*
1061
 *   Add an export 
1062
 */
1063
CTcPrsExport *CTcParser::add_export(const char *sym, size_t len)
1064
{
1065
    CTcPrsExport *exp;
1066
    
1067
    /* create a new record */
1068
    exp = new CTcPrsExport(sym, len);
1069
1070
    /* add it to our list */
1071
    add_export_to_list(exp);
1072
1073
    /* return it */
1074
    return exp;
1075
}
1076
1077
/*
1078
 *   Add an export to our list 
1079
 */
1080
void CTcParser::add_export_to_list(CTcPrsExport *exp)
1081
{
1082
    /* link it at the end of our list */
1083
    if (exp_tail_ == 0)
1084
        exp_head_ = exp;
1085
    else
1086
        exp_tail_->set_next(exp);
1087
    exp_tail_ = exp;
1088
}
1089
1090
/*
1091
 *   Parse a 'dictionary' top-level statement 
1092
 */
1093
CTPNStmTop *CTcParser::parse_dict(int *err)
1094
{
1095
    int done;
1096
        
1097
    /* skip the 'dictionary' token and check what follows */
1098
    switch(G_tok->next())
1099
    {
1100
    case TOKT_PROPERTY:
1101
        /* 
1102
         *   'dictionary property' definition - what follows is a list of
1103
         *   properties that are going to be considered dictionary
1104
         *   properties. 
1105
         */
1106
1107
        /* skip the 'property' token */
1108
        G_tok->next();
1109
1110
        /* parse the list of property names */
1111
        for (done = FALSE ; !done ; )
1112
        {
1113
            /* check what we have */
1114
            if (G_tok->cur() == TOKT_SYM)
1115
            {
1116
                CTcSymProp *sym;
1117
1118
                /* look up the property */
1119
                sym = look_up_prop(G_tok->getcur(), TRUE);
1120
1121
                /* 
1122
                 *   if it's already marked as a dictionary property,
1123
                 *   there's nothing we need to do now; otherwise, mark it 
1124
                 */
1125
                if (sym != 0 && !sym->is_vocab())
1126
                {
1127
                    CTcDictPropEntry *entry;
1128
1129
                    /* mark it as a vocabulary property */
1130
                    sym->set_vocab(TRUE);
1131
1132
                    /* 
1133
                     *   include the symbol in our master dictionary
1134
                     *   property list 
1135
                     */
1136
                    
1137
                    /* create a new list entry */
1138
                    entry = new (G_prsmem) CTcDictPropEntry(sym);
1139
                    
1140
                    /* link it into our list */
1141
                    entry->nxt_ = dict_prop_head_;
1142
                    dict_prop_head_ = entry;
1143
                }
1144
1145
                /* skip the symbol and check what follows */
1146
                if (G_tok->next() == TOKT_COMMA)
1147
                {
1148
                    /* skip the comma and continue */
1149
                    G_tok->next();
1150
                }
1151
                else if (G_tok->cur() == TOKT_SEM)
1152
                {
1153
                    /* 
1154
                     *   end of the statement - skip the semicolon and
1155
                     *   we're done 
1156
                     */
1157
                    G_tok->next();
1158
                    break;
1159
                }
1160
                else
1161
                {
1162
                    /* 
1163
                     *   if it's a brace, they probably left out the
1164
                     *   semicolon; if it's a symbol, they probably left
1165
                     *   out the comma; otherwise, it's probably a stray
1166
                     *   token 
1167
                     */
1168
                    switch(G_tok->cur())
1169
                    {
1170
                    case TOKT_SYM:
1171
                        /* 
1172
                         *   probably left out a comma - flag an error,
1173
                         *   but proceed with parsing this new symbol 
1174
                         */
1175
                        G_tok->log_error_curtok(TCERR_DICT_PROP_REQ_COMMA);
1176
                        break;
1177
1178
                    case TOKT_LBRACE:
1179
                    case TOKT_RBRACE:
1180
                    case TOKT_EOF:
1181
                        /* they probably left out the semicolon */
1182
                        G_tok->log_error_curtok(TCERR_EXPECTED_SEMI);
1183
                        return 0;
1184
1185
                    default:
1186
                        /* log an error */
1187
                        G_tok->log_error_curtok(TCERR_DICT_PROP_REQ_COMMA);
1188
1189
                        /* skip the errant symbol */
1190
                        G_tok->next();
1191
1192
                        /* 
1193
                         *   if a comma follows, something was probably
1194
                         *   just inserted - skip the comma; if a
1195
                         *   semicolon follows, assume we're at the end of
1196
                         *   the statement 
1197
                         */
1198
                        if (G_tok->cur() == TOKT_COMMA)
1199
                        {
1200
                            /* 
1201
                             *   we're probably okay again - skip the
1202
                             *   comma and continue 
1203
                             */
1204
                            G_tok->next();
1205
                        }
1206
                        else if (G_tok->cur() == TOKT_SEM)
1207
                        {
1208
                            /* skip the semicolon, and we're done */
1209
                            G_tok->next();
1210
                            done = TRUE;
1211
                        }
1212
1213
                        /* proceed with what follows */
1214
                        break;
1215
                    }
1216
                }
1217
            }
1218
            else
1219
            {
1220
                /* log an error */
1221
                G_tok->log_error_curtok(TCERR_DICT_PROP_REQ_SYM);
1222
1223
                switch(G_tok->cur())
1224
                {
1225
                case TOKT_SEM:
1226
                case TOKT_LBRACE:
1227
                case TOKT_RBRACE:
1228
                case TOKT_EOF:
1229
                    /* they're probably done with the statement */
1230
                    return 0;
1231
1232
                case TOKT_COMMA:
1233
                    /* 
1234
                     *   they probably just doubled a comma - skip it and
1235
                     *   continue 
1236
                     */
1237
                    G_tok->next();
1238
                    break;
1239
1240
                default:
1241
                    /* skip this token */
1242
                    G_tok->next();
1243
1244
                    /* 
1245
                     *   if a comma follows, they probably put in a
1246
                     *   keyword or something like that - skip the comma
1247
                     *   and continue 
1248
                     */
1249
                    if (G_tok->cur() == TOKT_COMMA)
1250
                        G_tok->next();
1251
                    break;
1252
                }
1253
1254
                /* if a semicolon follows, we're done */
1255
                if (G_tok->cur() == TOKT_SEM)
1256
                {
1257
                    /* skip the semicolon and we're done */
1258
                    G_tok->next();
1259
                    break;
1260
                }
1261
            }
1262
        }
1263
1264
        /* 
1265
         *   this statement doesn't result in adding anything to the parse
1266
         *   tree 
1267
         */
1268
        return 0;
1269
1270
    case TOKT_SYM:
1271
        /* 
1272
         *   Dictionary object definition - what follows is the name of an
1273
         *   object to be created of metaclass 'dictionary'.  This object
1274
         *   is to become the active dictionary for subsequent parsing. 
1275
         */
1276
        {
1277
            CTcDictEntry *dict;
1278
            CTPNStmDict *dict_stm = 0;
1279
1280
            /* find the dictionary entry */
1281
            dict = declare_dict(G_tok->getcur()->get_text(),
1282
                                G_tok->getcur()->get_text_len());
1283
            
1284
            /* if we have a symbol, create the statement node */
1285
            if (dict != 0)
1286
            {
1287
                /* create the 'dictionary' statement node */
1288
                dict_stm = new CTPNStmDict(dict);
1289
1290
                /* 
1291
                 *   remember the active dictionary - this is the
1292
                 *   dictionary into which subsequent vocabulary will be
1293
                 *   inserted 
1294
                 */
1295
                dict_cur_ = dict;
1296
1297
                /* mark the dictionary as non-external */
1298
                dict->get_sym()->set_extern(FALSE);
1299
            }
1300
1301
            /* skip the symbol and parse the required closing semicolon */
1302
            G_tok->next();
1303
            if (parse_req_sem())
1304
            {
1305
                *err = TRUE;
1306
                return 0;
1307
            }
1308
1309
            /* return the 'dictionary' statement */
1310
            return dict_stm;
1311
        }
1312
1313
    default:
1314
        /* anything else is invalid */
1315
        G_tok->log_error_curtok(TCERR_DICT_SYNTAX);
1316
        return 0;
1317
    }
1318
}
1319
1320
/* ------------------------------------------------------------------------ */
1321
/*
1322
 *   Grammar tree node - base class 
1323
 */
1324
class CTcPrsGramNode: public CTcTokenSource
1325
{
1326
public:
1327
    CTcPrsGramNode()
1328
    {
1329
        /* we have no siblings yet */
1330
        next_ = 0;
1331
    }
1332
    
1333
    /* get the next token - does nothing by default */
1334
    virtual const CTcToken *get_next_token() { return 0; }
1335
1336
    /* consolidate OR nodes at the top of the subtree */
1337
    virtual CTcPrsGramNode *consolidate_or() = 0;
1338
1339
    /* flatten CAT nodes together in the tree */
1340
    virtual void flatten_cat() { /* by default, do nothing */ }
1341
1342
    /* am I an "or" node? */
1343
    virtual int is_or() { return FALSE; }
1344
1345
    /* am I a "cat" node? */
1346
    virtual int is_cat() { return FALSE; }
1347
1348
    /* get my token - if I'm not a token node, returns null */
1349
    virtual const CTcToken *get_tok() const { return 0; }
1350
1351
    /* initialize expansion - by default, we do nothing */
1352
    virtual void init_expansion() { }
1353
1354
    /* 
1355
     *   Advance to the next expansion state.  Returns true if we 'carry'
1356
     *   out of the current item, which means that we were already at our
1357
     *   last state and hence are wrapping back to our first state.  Returns
1358
     *   false if we advanced to a new state without wrapping back.
1359
     *   
1360
     *   By default, since normal items have only one alternative, we don't
1361
     *   do anything but return a 'carry, since each advance takes us back
1362
     *   to our single and initial state. 
1363
     */
1364
    virtual int advance_expansion() { return TRUE; }
1365
1366
    /* clone the current expansion subtree */
1367
    virtual CTcPrsGramNode *clone_expansion() const = 0;
1368
1369
    /* next sibling node */
1370
    CTcPrsGramNode *next_;
1371
};
1372
1373
1374
class CTcPrsGramNodeWithChildren: public CTcPrsGramNode
1375
{
1376
public:
1377
    CTcPrsGramNodeWithChildren()
1378
    {
1379
        /* no subitems yet */
1380
        sub_head_ = sub_tail_ = 0;
1381
    }
1382
1383
    /* add a sub-item */
1384
    void add_sub_item(CTcPrsGramNode *sub)
1385
    {
1386
        /* add it to the end of the list */
1387
        if (sub_tail_ != 0)
1388
            sub_tail_->next_ = sub;
1389
        else
1390
            sub_head_ = sub;
1391
1392
        /* it's now the last item */
1393
        sub_tail_ = sub;
1394
        sub->next_ = 0;
1395
    }
1396
1397
    virtual void flatten_cat()
1398
    {
1399
        CTcPrsGramNode *cur;
1400
        
1401
        /* flatten CAT nodes in our subnodes */
1402
        for (cur = sub_head_ ; cur != 0 ; cur = cur->next_)
1403
            cur->flatten_cat();
1404
    }
1405
    
1406
    /* initialize expansion */
1407
    virtual void init_expansion()
1408
    {
1409
        CTcPrsGramNode *cur;
1410
1411
        /* initialize subnodes */
1412
        for (cur = sub_head_ ; cur != 0 ; cur = cur->next_)
1413
            cur->init_expansion();
1414
    }
1415
1416
    /* head and tail of list of subnodes */
1417
    CTcPrsGramNode *sub_head_;
1418
    CTcPrsGramNode *sub_tail_;
1419
};
1420
1421
/*
1422
 *   Grammar tree node - CAT node.  This represents a string of
1423
 *   concatenated tokens. 
1424
 */
1425
class CTcPrsGramNodeCat: public CTcPrsGramNodeWithChildren
1426
{
1427
public:
1428
    /* I'm a CAT node */
1429
    virtual int is_cat() { return TRUE; }
1430
1431
    /* 
1432
     *   Consolidate all of the OR's at the top of the tree.  If we have any
1433
     *   OR subnodes, we'll rewrite ourselves as an OR of the concatenations
1434
     *   of all of the sub-OR's in all combinations.  
1435
     */
1436
    virtual CTcPrsGramNode *consolidate_or();
1437
1438
    /* 
1439
     *   Flatten CAT nodes together.  If we have any CAT nodes below us,
1440
     *   move their contents into our own list directly. 
1441
     */
1442
    virtual void flatten_cat()
1443
    {
1444
        CTcPrsGramNode *prv;
1445
        CTcPrsGramNode *cur;
1446
        CTcPrsGramNode *nxt;
1447
        
1448
        /*
1449
         *   Check each child.  For each child that's a CAT node, move its
1450
         *   contents directly into our list, removing the redundant
1451
         *   intervening CAT node. 
1452
         */
1453
        for (prv = 0, cur = sub_head_ ; cur != 0 ; cur = nxt)
1454
        {
1455
            /* note the next item, in case we fiddle with the list */
1456
            nxt = cur->next_;
1457
1458
            /* flatten the CAT items below this one */
1459
            cur->flatten_cat();
1460
1461
            /* if this item is a CAT, move its contents directly into us */
1462
            if (cur->is_cat())
1463
            {
1464
                CTcPrsGramNodeCat *cur_cat;
1465
                
1466
                /* cast it to a CAT node */
1467
                cur_cat = (CTcPrsGramNodeCat *)cur;
1468
1469
                /* if it's empty, just remove it; otherwise, link it in */
1470
                if (cur_cat->sub_head_ != 0)
1471
                {
1472
                    /* link from the previous item to the sublist head */
1473
                    if (prv == 0)
1474
                    {
1475
                        /* 
1476
                         *   this is the first item - put it at the start of
1477
                         *   our list 
1478
                         */
1479
                        sub_head_ = cur_cat->sub_head_;
1480
                    }
1481
                    else
1482
                    {
1483
                        /* link it after the previous item */
1484
                        prv->next_ = cur_cat->sub_head_;
1485
                    }
1486
                    
1487
                    /* link from the tail of the sublist to the next item */
1488
                    cur_cat->sub_tail_->next_ = nxt;
1489
                    
1490
                    /* if it's the last item, set our tail pointer */
1491
                    if (sub_tail_ == cur)
1492
                        sub_tail_ = cur_cat->sub_tail_;
1493
                    
1494
                    /* 
1495
                     *   the tail of the sublist is now the previous item in
1496
                     *   the list for the purposes of the next iteration 
1497
                     */
1498
                    prv = cur_cat->sub_tail_;
1499
                }
1500
                else
1501
                {
1502
                    /* unlink it from our list */
1503
                    if (prv != 0)
1504
                        prv->next_ = nxt;
1505
                    else
1506
                        sub_head_ = nxt;
1507
1508
                    /* update the tail pointer if removing the last item */
1509
                    if (sub_tail_ == cur_cat)
1510
                        sub_tail_ = prv;
1511
1512
                    /* note that the previous item is unchanged */
1513
                }
1514
            }
1515
            else
1516
            {
1517
                /* 
1518
                 *   it's not a CAT, so we're not changing it; this is the
1519
                 *   previous item for the next iteration 
1520
                 */
1521
                prv = cur;
1522
            }
1523
        }
1524
    }
1525
1526
    /* clone the current expansion subtree */
1527
    virtual CTcPrsGramNode *clone_expansion() const
1528
    {
1529
        CTcPrsGramNodeCat *new_cat;
1530
        CTcPrsGramNode *cur;
1531
1532
        /* create a new 'cat' node */
1533
        new_cat = new (G_prsmem) CTcPrsGramNodeCat();
1534
1535
        /* add a clone of each of my children and add it to my replica */
1536
        for (cur = sub_head_ ; cur != 0 ; cur = cur->next_)
1537
            new_cat->add_sub_item(cur->clone_expansion());
1538
1539
        /* return my replica */
1540
        return new_cat;
1541
    }
1542
1543
    /* clone this node (without any children) */
1544
    virtual CTcPrsGramNodeWithChildren *clone_this_node()
1545
    {
1546
        return new CTcPrsGramNodeCat();
1547
    }
1548
1549
    /* initialize expansion */
1550
    virtual void init_expansion()
1551
    {
1552
        /* set to expand from our first child */
1553
        cur_sub_ = sub_head_;
1554
1555
        /* inherit default to initialize subnodes */
1556
        CTcPrsGramNodeWithChildren::init_expansion();
1557
    }
1558
1559
    /* get the next token in a token expansion */
1560
    const CTcToken *get_next_token()
1561
    {
1562
        /* advance to the next subitem */
1563
        cur_sub_ = cur_sub_->next_;
1564
        
1565
        /* 
1566
         *   If there's anything left, return it; otherwise, return null.
1567
         *   During final token expansion, a 'cat' node never has anything
1568
         *   but tokens beneath it, since we move all the 'or' nodes to a
1569
         *   single 'or' at the top of the tree and flatten out the 'cat'
1570
         *   nodes to a single level under that; so, we can simply return
1571
         *   the token directly from the next underlying node.  
1572
         */
1573
        return (cur_sub_ != 0 ? cur_sub_->get_tok() : 0);
1574
    }
1575
1576
    /* 
1577
     *   get my current token - we'll just return the token from the current
1578
     *   subitem 
1579
     */
1580
    const CTcToken *get_tok() const
1581
    {
1582
        return (cur_sub_ != 0 ? cur_sub_->get_tok() : 0);
1583
    }
1584
1585
    /* current subitem in expansion */
1586
    CTcPrsGramNode *cur_sub_;
1587
};
1588
1589
/*
1590
 *   Grammar tree node - OR node.  This represents a set of alternatives. 
1591
 */
1592
class CTcPrsGramNodeOr: public CTcPrsGramNodeWithChildren
1593
{
1594
public:
1595
    /* I'm an "or" node */
1596
    virtual int is_or() { return TRUE; }
1597
1598
    /* 
1599
     *   Consolidate all of the OR's at the top of the tree.
1600
     */
1601
    virtual CTcPrsGramNode *consolidate_or()
1602
    {
1603
        CTcPrsGramNode *cur;
1604
        CTcPrsGramNode *nxt;
1605
        CTcPrsGramNode *old_head;
1606
1607
        /*
1608
         *   Consolidate OR's in all subtrees.  If any of our immediate
1609
         *   children turn into OR's, simply pull their children into our OR
1610
         *   list directly - OR(a, OR(b, c)) is equivalent to OR(a, b, c).
1611
         *   
1612
         *   Before we build the new list, stash away our entire subtree,
1613
         *   since we'll rebuild the tree from the updated versions.  
1614
         */
1615
        old_head = sub_head_;
1616
        sub_head_ = sub_tail_ = 0;
1617
1618
        /* run through our list and build the new, consolidated list */
1619
        for (cur = old_head ; cur != 0 ; cur = nxt)
1620
        {
1621
            CTcPrsGramNode *new_sub;
1622
1623
            /* remember the next item in the old list */
1624
            nxt = cur->next_;
1625
            
1626
            /* consolidate OR's in the subtree */
1627
            new_sub = cur->consolidate_or();
1628
1629
            /* 
1630
             *   if this is an OR node, add its children directly to us;
1631
             *   otherwise, add the node itself
1632
             */
1633
            if (new_sub->is_or())
1634
            {
1635
                CTcPrsGramNodeOr *new_or;
1636
                CTcPrsGramNode *chi;
1637
                CTcPrsGramNode *next_chi;
1638
1639
                /* cast it */
1640
                new_or = (CTcPrsGramNodeOr *)new_sub;
1641
1642
                /* add each of the sub-OR's children as our direct children */
1643
                for (chi = new_or->sub_head_ ; chi != 0 ; chi = next_chi)
1644
                {
1645
                    /* remember the next child in the old sub-list */
1646
                    next_chi = chi->next_;
1647
1648
                    /* move it directly to our list */
1649
                    add_sub_item(chi);
1650
                }
1651
1652
                /* the old OR item is now empty of children */
1653
                new_or->sub_head_ = new_or->sub_tail_ = 0;
1654
            }
1655
            else
1656
            {
1657
                /* it's not an OR - add it directly to our list */
1658
                add_sub_item(new_sub);
1659
            }
1660
        }
1661
1662
        /* return myself with no further changes */
1663
        return this;
1664
    }
1665
1666
    /* initialize expansion - set up at the first alternative */
1667
    virtual void init_expansion()
1668
    {
1669
        /* start at the first alternative */
1670
        cur_alt_ = sub_head_;
1671
1672
        /* initialize the first alternative for expansion */
1673
        cur_alt_->init_expansion();
1674
1675
        /* we didn't just do an 'or' */
1676
        just_did_or_ = FALSE;
1677
1678
        /* we haven't yet returned the first token */
1679
        before_first_ = TRUE;
1680
    }
1681
1682
    /* advance to the next alternative for expansion */
1683
    virtual int advance_expansion()
1684
    {
1685
        /* advance to the next state */
1686
        cur_alt_ = cur_alt_->next_;
1687
1688
        /* 
1689
         *   if that was the last state, wrap back to the first state and
1690
         *   indicate a 'carry'; otherwise, indicate no carry 
1691
         */
1692
        if (cur_alt_ == 0)
1693
        {
1694
            /* we ran out of states - wrap back to the first one */
1695
            cur_alt_ = sub_head_;
1696
1697
            /* initialize expansion in the new subitem */
1698
            cur_alt_->init_expansion();
1699
1700
            /* indicate that we've wrapped and should carry forward */
1701
            return TRUE;
1702
        }
1703
        else
1704
        {
1705
            /* initialize expansion in the new subitem */
1706
            cur_alt_->init_expansion();
1707
1708
            /* this is another valid state - no carry */
1709
            return FALSE;
1710
        }
1711
    }
1712
    
1713
    /* clone the current expansion subtree */
1714
    virtual CTcPrsGramNode *clone_expansion() const
1715
    {
1716
        /* return a replica of the current alternative being expanded */
1717
        return cur_alt_->clone_expansion();
1718
    }
1719
1720
    /* get the next token in a token expansion */
1721
    const CTcToken *get_next_token()
1722
    {
1723
        const CTcToken *tok;
1724
1725
        /* 
1726
         *   If we returned the synthesized '|' token between alternatives
1727
         *   last time, advance to the next alternative and return its first
1728
         *   token.  
1729
         */
1730
        if (just_did_or_)
1731
        {
1732
            /* we no longer just did an '|' */
1733
            just_did_or_ = FALSE;
1734
            
1735
            /* advance to the next alternative */
1736
            cur_alt_ = cur_alt_->next_;
1737
1738
            /* if there is no next alternative, there's nothing to return */
1739
            if (cur_alt_ == 0)
1740
                return 0;
1741
1742
            /* initialize the new alternative */
1743
            cur_alt_->init_expansion();
1744
1745
            /* if this alternative has no subitems, return another '|' */
1746
            if (cur_alt_->get_tok() == 0)
1747
            {
1748
                /* flag the '|' */
1749
                just_did_or_ = TRUE;
1750
1751
                /* if this was the last alternative, we're done */
1752
                if (cur_alt_->next_ == 0)
1753
                    return 0;
1754
1755
                /* 
1756
                 *   return the '|' again (we know it's already set up,
1757
                 *   because we only get here if we did an '|' previously) 
1758
                 */
1759
                return &or_tok_;
1760
            }
1761
1762
            /* return its current token */
1763
            return cur_alt_->get_tok();
1764
        }
1765
1766
        /*
1767
         *   Get the first or next token in the current alternative,
1768
         *   depending on where we are. 
1769
         */
1770
        if (before_first_)
1771
        {
1772
            /* we're before the first token - get the current token */
1773
            tok = cur_alt_->get_tok();
1774
1775
            /* we're now past the first token */
1776
            before_first_ = FALSE;
1777
        }
1778
        else
1779
        {
1780
            /* we're past the first token, so get the next one */
1781
            tok = cur_alt_->get_next_token();
1782
        }
1783
1784
        /* if we got a token from the current alternative, return it */
1785
        if (tok != 0)
1786
            return tok;
1787
1788
        /* 
1789
         *   We've run out of tokens in this alternative - synthesize an OR
1790
         *   token ('|') and return it, so the caller will know a new
1791
         *   top-level alternative follows.
1792
         *   
1793
         *   Do NOT synthesize an OR token after our last alternative.  
1794
         */
1795
        if (cur_alt_->next_ == 0)
1796
        {
1797
            /* this was our last alternative - we're done */
1798
            return 0;
1799
        }
1800
1801
        /* we have another alternative - synthesize an OR */
1802
        or_tok_.settyp(TOKT_OR);
1803
        or_tok_.set_text("|", 1);
1804
1805
        /* flag that we just did an '|' */
1806
        just_did_or_ = TRUE;
1807
1808
        /* return the synthesized '|' token */
1809
        return &or_tok_;
1810
    }
1811
1812
    /* current alternative being expanded */
1813
    CTcPrsGramNode *cur_alt_;
1814
1815
    /* 
1816
     *   my synthesized token for returning the '|' token between
1817
     *   alternatives during expansion 
1818
     */
1819
    CTcToken or_tok_;
1820
1821
    /* flag: we just returned the 'or' between two alternatives */
1822
    unsigned int just_did_or_ : 1;
1823
1824
    /* flag: we haven't yet returned the first token */
1825
    unsigned int before_first_ : 1;
1826
};
1827
1828
/* 
1829
 *   Consolidate all of the OR's at the top of the tree.  If we have any OR
1830
 *   subnodes, we'll rewrite ourselves as an OR of the concatenations of all
1831
 *   of the sub-OR's in all combinations.  
1832
 */
1833
CTcPrsGramNode *CTcPrsGramNodeCat::consolidate_or()
1834
{
1835
    CTcPrsGramNode *old_head;
1836
    CTcPrsGramNodeOr *new_or;
1837
    CTcPrsGramNode *cur;
1838
    CTcPrsGramNode *nxt;
1839
    int or_cnt;
1840
1841
    /* 
1842
     *   Consolidate OR's in all subtrees.  Before we do, stash away our
1843
     *   entire subtree, since we'll rebuild the tree from the updated
1844
     *   versions.  
1845
     */
1846
    old_head = sub_head_;
1847
    sub_head_ = sub_tail_ = 0;
1848
1849
    /* run through our old list and build the new, consolidated list */
1850
    for (cur = old_head ; cur != 0 ; cur = nxt)
1851
    {
1852
        /* remember the next item in the old list */
1853
        nxt = cur->next_;
1854
1855
        /* consolidate the subtree and add it to our new list */
1856
        add_sub_item(cur->consolidate_or());
1857
    }
1858
1859
    /* 
1860
     *   Count of OR nodes - if we don't have any, we don't have to make any
1861
     *   changes.  Since we've already consolidated all OR's out of
1862
     *   sub-nodes into a single node at the top of each subtree, we don't
1863
     *   have to worry about OR's below our immediate children.  
1864
     */
1865
    for (or_cnt = 0, cur = sub_head_ ; cur != 0 ; cur = cur->next_)
1866
    {
1867
        /* if it's an OR node, count it */
1868
        if (cur->is_or())
1869
            ++or_cnt;
1870
    }
1871
1872
    /* if we have no OR nodes, we need no changes */
1873
    if (or_cnt == 0)
1874
        return this;
1875
1876
    /* create a new OR node to contain the list of expansions */
1877
    new_or = new (G_prsmem) CTcPrsGramNodeOr();
1878
1879
    /* set up each OR node with the next alternative */
1880
    for (cur = sub_head_ ; cur != 0 ; cur = cur->next_)
1881
        cur->init_expansion();
1882
1883
    /*
1884
     *   Iterate through all of the possibilities.  For each iteration,
1885
     *   we'll build the currently selected alternative, then we'll advance
1886
     *   by one alternative.  We'll keep doing this until we've advanced
1887
     *   through all of the alternatives.  
1888
     */
1889
    for (;;)
1890
    {
1891
        CTcPrsGramNodeCat *new_cat;
1892
1893
        /* create a new CAT node for the current selected alternative */
1894
        new_cat = new (G_prsmem) CTcPrsGramNodeCat();
1895
1896
        /* clone and add each current alternative to the new CAT node */
1897
        for (cur = sub_head_ ; cur != 0 ; cur = cur->next_)
1898
            new_cat->add_sub_item(cur->clone_expansion());
1899
1900
        /* 
1901
         *   we've finished this entire expansion, so add it to the new main
1902
         *   OR we're building 
1903
         */
1904
        new_or->add_sub_item(new_cat);
1905
1906
        /*
1907
         *   Go through the list of subitems and advance to the next OR
1908
         *   state.  Stop when we reach the first item that advances without
1909
         *   a 'carry'.  If we're still carrying when we reach the last
1910
         *   item, we know we've wrapped around back to the first
1911
         *   alternative and hence are done.  
1912
         */
1913
        for (cur = sub_head_ ; cur != 0 ; cur = cur->next_)
1914
        {
1915
            /* advance this one; if it doesn't carry, we're done */
1916
            if (!cur->advance_expansion())
1917
                break;
1918
        }
1919
1920
        /* 
1921
         *   if we carried past the last item, we've wrapped around back to
1922
         *   the first alternative, so we're done 
1923
         */
1924
        if (cur == 0)
1925
            break;
1926
    }
1927
    
1928
    /* return the new main OR we built */
1929
    return new_or;
1930
}
1931
1932
/*
1933
 *   Grammar tree node - token node.  This is a terminal node representing a
1934
 *   single token.  
1935
 */
1936
class CTcPrsGramNodeTok: public CTcPrsGramNode
1937
{
1938
public:
1939
    CTcPrsGramNodeTok(const CTcToken *tok)
1940
    {
1941
        /* remember the token */
1942
        G_tok->copytok(&tok_, tok);
1943
    }
1944
1945
    /* consolidate OR's */
1946
    virtual CTcPrsGramNode *consolidate_or()
1947
    {
1948
        /* since I'm a terminal node, there's nothing to do here */
1949
        return this;
1950
    }
1951
1952
    /* clone the current expansion subtree */
1953
    virtual CTcPrsGramNode *clone_expansion() const
1954
    {
1955
        /* return a new token node with my same token value */
1956
        return new (G_prsmem) CTcPrsGramNodeTok(&tok_);
1957
    }
1958
1959
    /* get my token */
1960
    virtual const CTcToken *get_tok() const { return &tok_; }
1961
1962
    /* my token */
1963
    CTcToken tok_;
1964
};
1965
1966
1967
/* ------------------------------------------------------------------------ */
1968
/*
1969
 *   Parse a 'grammar' top-level statement
1970
 */
1971
CTPNStmTop *CTcParser::parse_grammar(int *err, int replace, int modify)
1972
{
1973
    CTcSymObj *gram_obj;
1974
    CTcToken prod_name;
1975
    CTcGramProdEntry *prod;
1976
    int prod_name_valid;
1977
    int done;
1978
    CTPNStmObject *stm;
1979
    CTcGramProdAlt *alt;
1980
    CTcGramProdTok *tok;
1981
    const char *name_tag;
1982
    size_t name_tag_len;
1983
    const size_t prop_arrow_max = 100;
1984
    CTcSymProp *prop_arrows[prop_arrow_max];
1985
    size_t prop_arrow_cnt;
1986
    CTcPrsGramNode *tree;
1987
    CTcSymObj *mod_orig_sym;
1988
    int is_anon;
1989
    int need_private_prod;
1990
1991
    /* presume we're not modifying anything */
1992
    mod_orig_sym = 0;
1993
1994
    /* presume we won't need a private production object */
1995
    need_private_prod = FALSE;
1996
1997
    /* no property arrow assignments yet */
1998
    prop_arrow_cnt = 0;
1999
2000
    /* presume we won't find a valid production name */
2001
    prod_name_valid = FALSE;
2002
    prod = 0;
2003
2004
    /* presume it will be anonymous (i.e., no name tag) */
2005
    is_anon = TRUE;
2006
2007
    /* skip the 'grammar' token and check for the production name */
2008
    if (G_tok->next() != TOKT_SYM)
2009
    {
2010
        /* log an error, then proceed without a symbol name */
2011
        G_tok->log_error_curtok(TCERR_GRAMMAR_REQ_SYM);
2012
    }
2013
    else
2014
    {
2015
        /* remember the production name */
2016
        prod_name = *G_tok->copycur();
2017
        prod_name_valid = TRUE;
2018
2019
        /* find or create the 'grammar production' entry */
2020
        prod = declare_gramprod(prod_name.get_text(),
2021
                                prod_name.get_text_len());
2022
    }
2023
2024
    /* use the production name as the name tag */
2025
    name_tag = prod_name.get_text();
2026
    name_tag_len = prod_name.get_text_len();
2027
2028
    /* check for the optional name-tag token */
2029
    if (G_tok->next() == TOKT_LPAR)
2030
    {
2031
        char tag_buf[TOK_SYM_MAX_LEN*2 + 32];
2032
        size_t copy_len;
2033
        int is_class;
2034
        int trans;
2035
        
2036
        /* skip the paren - the name token follows */
2037
        G_tok->next();
2038
2039
        /* 
2040
         *   limit the added copying so we don't overflow our buffer - note
2041
         *   that we need two bytes for parens 
2042
         */
2043
        copy_len = G_tok->getcur()->get_text_len();
2044
        if (copy_len > sizeof(tag_buf) - name_tag_len - 2)
2045
            copy_len = sizeof(tag_buf) - name_tag_len - 2;
2046
2047
        /* build the name */
2048
        memcpy(tag_buf, name_tag, name_tag_len);
2049
        tag_buf[name_tag_len] = '(';
2050
        memcpy(tag_buf + name_tag_len + 1,
2051
               G_tok->getcur()->get_text(), copy_len);
2052
        tag_buf[name_tag_len + 1 + copy_len] = ')';
2053
2054
        /* store the tag in tokenizer memory */
2055
        name_tag_len += 2 + copy_len;
2056
        name_tag = G_tok->store_source(tag_buf, name_tag_len);
2057
2058
        /* require the closing paren */
2059
        if (G_tok->next() == TOKT_RPAR)
2060
        {
2061
            /* found it - skip it */
2062
            G_tok->next();
2063
        }
2064
        else
2065
        {
2066
            /* flag the error, but proceed as though it was there */
2067
            G_tok->log_error_curtok(TCERR_GRAMMAR_REQ_NAME_RPAR);
2068
        }
2069
2070
        /*
2071
         *   There's a tag, so the grammar match object has a symbol name
2072
         *   given by the full "prod(tag)" string.  Note that grammar match
2073
         *   definitions are inherently classes.  
2074
         */
2075
        is_class = TRUE;
2076
        trans = FALSE;
2077
        gram_obj = find_or_def_obj(name_tag, name_tag_len,
2078
                                   replace, modify, &is_class,
2079
                                   &mod_orig_sym, 0, &trans);
2080
2081
        /* note that the object is named */
2082
        is_anon = FALSE;
2083
2084
        /*
2085
         *   Since this is a named grammar match object, we must keep the
2086
         *   list of grammar rules defined here in a private list.  At link
2087
         *   time, we'll merge this private grammar rule list with the
2088
         *   master list for the production, but we must keep a private list
2089
         *   until link time in case this match object is modified or
2090
         *   replaced.  
2091
         */
2092
        need_private_prod = TRUE;
2093
    }
2094
    else if (!replace && !modify && G_tok->cur() == TOKT_SEM)
2095
    {
2096
        /* 
2097
         *   It's an empty 'grammar x;' statement, with no rule definition
2098
         *   and no properties.  This is a simple declaration of the
2099
         *   production name and defines no rules for it; the only thing we
2100
         *   have to do is mark the production object as explicitly declared.
2101
         */
2102
        if (prod != 0)
2103
            prod->set_declared(TRUE);
2104
2105
        /* skip the semicolon */
2106
        G_tok->next();
2107
2108
        /* 
2109
         *   we're done - a simple grammar production declaration generates
2110
         *   no parse tree object 
2111
         */
2112
        return 0;
2113
    }
2114
    else
2115
    {
2116
        /* 
2117
         *   There's no tag, so the grammar match object is anonymous.
2118
         *   Create a new anonymous object symbol for it.  
2119
         */
2120
        gram_obj = new CTcSymObj(".anon", 5, FALSE, G_cg->new_obj_id(),
2121
                                 FALSE, TC_META_TADSOBJ, 0);
2122
2123
        /* 
2124
         *   'replace' and 'modify' can only be used with tagged grammar
2125
         *   rules, since that's the only way we can refer to a specific rule
2126
         *   previously defined 
2127
         */
2128
        if (replace || modify)
2129
            G_tok->log_error(TCERR_GRAMMAR_MOD_REQ_TAG);
2130
    }
2131
2132
    /* check for and skip the required colon */
2133
    if (G_tok->cur() == TOKT_COLON)
2134
    {
2135
        /* it's there - skip it and proceed */
2136
        G_tok->next();
2137
    }
2138
    else
2139
    {
2140
        /* 
2141
         *   log an error, then proceed on the assumption that the colon
2142
         *   was simply omitted 
2143
         */
2144
        G_tok->log_error_curtok(TCERR_GRAMMAR_REQ_COLON);
2145
    }
2146
2147
    /* 
2148
     *   mark it as a class - this object exists as a factory for match tree
2149
     *   instances 
2150
     */
2151
    if (gram_obj != 0)
2152
        gram_obj->set_is_class(TRUE);
2153
2154
    /*
2155
     *   If we're modifying the rule, check for an empty rule list.  With
2156
     *   'modify', an empty rule list has the special meaning that we retain
2157
     *   the original rule list of the base object being modified.  
2158
     */
2159
    if (modify && G_tok->cur() == TOKT_COLON)
2160
    {
2161
        /* 
2162
         *   This is a 'modify', and the rule list is empty.  This means that
2163
         *   we're to leave the original rule list of the base object intact,
2164
         *   so we don't need to flatten the input syntax (since there's no
2165
         *   input syntax to flatten), we don't need to create a new private
2166
         *   list (since we want to leave the existing private list intact),
2167
         *   and we don't need to set up the first alternative for the rule
2168
         *   (since there will be no alternatives at all).  
2169
         */
2170
    }
2171
    else
2172
    {
2173
        /* 
2174
         *   We don't have an empty 'modify' rule list, so we are defining a
2175
         *   rule list for this production.  If we decided that we need to
2176
         *   store the list in a private list, create the private list.  
2177
         */
2178
        if (need_private_prod && gram_obj != 0)
2179
            prod = gram_obj->create_grammar_entry(
2180
                prod_name.get_text(), prod_name.get_text_len());
2181
2182
        /* set up the first alternative and add it to the production */
2183
        alt = new (G_prsmem) CTcGramProdAlt(gram_obj, dict_cur_);
2184
        if (prod != 0)
2185
            prod->add_alt(alt);
2186
2187
        /* flatten the grammar rules */
2188
        tree = flatten_gram_rule(err);
2189
        if (tree == 0 || *err != 0)
2190
            return 0;
2191
2192
        /* 
2193
         *   install the tree as the nested token source, so that we read
2194
         *   tokens from our expanded set of grammar rules rather than from
2195
         *   the original input stream 
2196
         */
2197
        G_tok->set_external_source(tree);
2198
    }
2199
2200
    /* parse the token specification list */
2201
    for (done = FALSE ; !done ; )
2202
    {
2203
        /* see what we have */
2204
        switch(G_tok->cur())
2205
        {
2206
        case TOKT_LBRACK:
2207
            {
2208
                int skip_to_close;
2209
2210
                /* presume we won't need to skip anything */
2211
                skip_to_close = FALSE;
2212
                
2213
                /* make sure we're at the start of the alternative */
2214
                if (alt->get_tok_head() != 0)
2215
                    G_tok->log_warning(TCERR_GRAMMAR_QUAL_NOT_FIRST);
2216
2217
                /* skip the open bracket and make sure a symbol follows */
2218
                if (G_tok->next() == TOKT_SYM)
2219
                {
2220
                    /* check what qualifier we have */
2221
                    if (G_tok->cur_tok_matches("badness", 7))
2222
                    {
2223
                        int val;
2224
2225
                        /* parse the integer-valued qualifier */
2226
                        val = parse_gram_qual_int(err, "badness", &done);
2227
                        if (*err != 0)
2228
                            return 0;
2229
2230
                        /* set the badness for the alternative */
2231
                        alt->set_badness(val);
2232
                    }
2233
                    else
2234
                    {
2235
                        /* not a recognized qualifier */
2236
                        G_tok->log_error_curtok(TCERR_BAD_GRAMMAR_QUAL);
2237
2238
                        /* skip remaining tokens to the ']' */
2239
                        skip_to_close = TRUE;
2240
                    }
2241
2242
                    /* check for proper close if we're is okay so far */
2243
                    if (!skip_to_close)
2244
                    {
2245
                        /* make sure we're at the close bracket */
2246
                        if (G_tok->cur() != TOKT_RBRACK)
2247
                        {
2248
                            /* log an error */
2249
                            G_tok->log_error_curtok(
2250
                                TCERR_GRAMMAR_QUAL_REQ_RBRACK);
2251
                            
2252
                            /* skip to the bracket */
2253
                            skip_to_close = TRUE;
2254
                        }
2255
                        else
2256
                        {
2257
                            /* skip the bracket */
2258
                            G_tok->next();
2259
                        }
2260
                    }
2261
                }
2262
                else
2263
                {
2264
                    /* log the error */
2265
                    G_tok->log_error_curtok(TCERR_GRAMMAR_QUAL_REQ_SYM);
2266
                    
2267
                    /* skip to the end */
2268
                    skip_to_close = TRUE;
2269
                }
2270
2271
                /* 
2272
                 *   If we encountered an error, skip ahead until we find
2273
                 *   a ']' or something that looks like the end of the
2274
                 *   statement.
2275
                 */
2276
                if (skip_to_close)
2277
                {
2278
                    /* skip to the end of the qualifier */
2279
                    parse_gram_qual_skip(err, &done);
2280
                    if (*err)
2281
                        return 0;
2282
                }
2283
            }
2284
2285
            /* done */
2286
            break;
2287
            
2288
        case TOKT_COLON:
2289
            /* this is the end of the list - we're done */
2290
            G_tok->next();
2291
            done = TRUE;
2292
            break;
2293
2294
        case TOKT_OR:
2295
            /* 
2296
             *   alternator - create a new alternative and add it to the
2297
             *   production's list 
2298
             */
2299
            alt = new (G_prsmem) CTcGramProdAlt(gram_obj, dict_cur_);
2300
            if (prod != 0)
2301
                prod->add_alt(alt);
2302
2303
            /* skip the '|' token and proceed to the next token list */
2304
            G_tok->next();
2305
            break;
2306
2307
        case TOKT_SSTR:
2308
            /* quoted string - this gives a literal string to match */
2309
            tok = new (G_prsmem) CTcGramProdTok();
2310
            tok->set_match_literal(G_tok->getcur()->get_text(),
2311
                                   G_tok->getcur()->get_text_len());
2312
2313
            /* add it to the current alternative's list */
2314
            alt->add_tok(tok);
2315
2316
            /* 
2317
             *   go check for an arrow (to assign the matching token to a
2318
             *   property) 
2319
             */
2320
            goto check_for_arrow;
2321
2322
        case TOKT_LT:
2323
            /* 
2324
             *   part-of-speech list - this gives a list, enclosed in angle
2325
             *   brackets, of part-of-speech properties that can be matched
2326
             *   by the token 
2327
             */
2328
2329
            /* skip the open angle bracket */
2330
            G_tok->next();
2331
2332
            /* set up a new token with a part-of-speech list */
2333
            tok = new (G_prsmem) CTcGramProdTok();
2334
            tok->set_match_part_list();
2335
2336
            /* add it to the current alternative's list */
2337
            alt->add_tok(tok);
2338
2339
            /* keep going until we reach the closing bracket */
2340
            for (;;)
2341
            {
2342
                CTcSymbol *sym;
2343
2344
                /* see what we have */
2345
                switch(G_tok->cur())
2346
                {
2347
                case TOKT_GT:
2348
                    /* 
2349
                     *   closing angle bracket - we're done, so go check for
2350
                     *   an arrow to assign the match to a property 
2351
                     */
2352
                    goto check_for_arrow;
2353
2354
                case TOKT_EOF:
2355
                    /* log the error */
2356
                    G_tok->log_error_curtok(TCERR_GRAMMAR_INVAL_TOK);
2357
2358
                    /* give up */
2359
                    *err = TRUE;
2360
                    return 0;
2361
2362
                case TOKT_SYM:
2363
                    /* 
2364
                     *   symbol - look it up, defining it as a property if
2365
                     *   it's not already defined as a property 
2366
                     */
2367
                    sym = global_symtab_->find_or_def_prop_explicit(
2368
                        G_tok->getcur()->get_text(),
2369
                        G_tok->getcur()->get_text_len(), FALSE);
2370
2371
                    /* make sure it's a property */
2372
                    if (sym != 0 && sym->get_type() == TC_SYM_PROP)
2373
                    {
2374
                        CTcSymProp *propsym = (CTcSymProp *)sym;
2375
2376
                        /* add the property to the token's list */
2377
                        tok->add_match_part_ele(propsym->get_prop());
2378
                    }
2379
                    else
2380
                    {
2381
                        /* flag an error */
2382
                        G_tok->log_error_curtok(TCERR_GRAMMAR_LIST_REQ_PROP);
2383
                    }
2384
2385
                    /* we're done with the symbol, so skip it */
2386
                    G_tok->next();
2387
2388
                    /* back for more */
2389
                    break;
2390
2391
                default:
2392
                    /* 
2393
                     *   anything else is an error; assume the '>' was
2394
                     *   missing, so just flag the error and then act as
2395
                     *   though we reached the end of the list 
2396
                     */
2397
                    G_tok->log_error_curtok(TCERR_GRAMMAR_LIST_UNCLOSED);
2398
                    goto check_for_arrow;
2399
                }
2400
            }
2401
            /* NOT REACHED */
2402
2403
        case TOKT_SYM:
2404
            /*
2405
             *   symbol token - this gives a part-of-speech or
2406
             *   sub-production match, depending on whether the symbol
2407
             *   refers to a property (in which case we have a
2408
             *   part-of-speech match) or an object (in which case we have a
2409
             *   sub-production match).  
2410
             */
2411
            {
2412
                CTcSymbol *sym;
2413
2414
                /* look it up in the global symbol table */
2415
                sym = global_symtab_->find(G_tok->getcur()->get_text(),
2416
                                           G_tok->getcur()->get_text_len());
2417
2418
                /* 
2419
                 *   if it's not defined, and we're not in syntax-only mode,
2420
                 *   provide a default definition of the symbol as a
2421
                 *   production object 
2422
                 */
2423
                if (sym == 0 && !syntax_only_)
2424
                {
2425
                    CTcGramProdEntry *sub_prod;
2426
                    
2427
                    /* it's undefined - presume it's a production */
2428
                    sub_prod = declare_gramprod(
2429
                        G_tok->getcur()->get_text(),
2430
                        G_tok->getcur()->get_text_len());
2431
2432
                    /* get the production's symbol */
2433
                    sym = sub_prod->get_prod_sym();
2434
                }
2435
2436
                /* check what kind of symbol we have */
2437
                if (sym == 0)
2438
                {
2439
                    /* 
2440
                     *   we're in parse-only mode and we have no symbol -
2441
                     *   we can just ignore this until we're really
2442
                     *   compiling 
2443
                     */
2444
                    tok = 0;
2445
                }
2446
                else if (sym->get_type() == TC_SYM_OBJ)
2447
                {
2448
                    CTcSymObj *objsym = (CTcSymObj *)sym;
2449
                    
2450
                    /* make sure it's a production */
2451
                    if (objsym->get_metaclass() != TC_META_GRAMPROD)
2452
                        G_tok->log_error_curtok(TCERR_GRAMMAR_REQ_PROD);
2453
2454
                    /* create the production token */
2455
                    tok = new (G_prsmem) CTcGramProdTok();
2456
                    tok->set_match_prod(objsym);
2457
                }
2458
                else if (sym->get_type() == TC_SYM_PROP)
2459
                {
2460
                    CTcSymProp *propsym = (CTcSymProp *)sym;
2461
                    
2462
                    /* create the part-of-speech token */
2463
                    tok = new (G_prsmem) CTcGramProdTok();
2464
                    tok->set_match_part_of_speech(propsym->get_prop());
2465
                }
2466
                else if (sym->get_type() == TC_SYM_ENUM)
2467
                {
2468
                    CTcSymEnum *enumsym = (CTcSymEnum *)sym;
2469
2470
                    /* make sure it's a token enum */
2471
                    if (!enumsym->is_token())
2472
                        G_tok->log_error_curtok(TCERR_GRAMMAR_BAD_ENUM);
2473
2474
                    /* create the token-type token */
2475
                    tok = new (G_prsmem) CTcGramProdTok();
2476
                    tok->set_match_token_type(enumsym->get_enum_id());
2477
                }
2478
                else
2479
                {
2480
                    /* it's an error */
2481
                    G_tok->log_error_curtok(TCERR_GRAMMAR_REQ_OBJ_OR_PROP);
2482
2483
                    /* no token */
2484
                    tok = 0;
2485
                }
2486
            }
2487
2488
            /* if we have a token, add it to the alternative's list */
2489
            if (tok != 0)
2490
                alt->add_tok(tok);
2491
2492
        check_for_arrow:
2493
            /* skip the symbol and check for a '->' specification */
2494
            if (G_tok->next() == TOKT_ARROW)
2495
            {
2496
                /* skip the arrow and make sure a symbol follows */
2497
                if (G_tok->next() == TOKT_SYM)
2498
                {
2499
                    CTcSymbol *sym;
2500
                    
2501
                    /* look it up */
2502
                    sym = look_up_prop(G_tok->getcur(), FALSE);
2503
                    
2504
                    /* set the association if we got a symbol */
2505
                    if (sym != 0)
2506
                    {
2507
                        CTcSymProp *propsym = (CTcSymProp *)sym;
2508
                        size_t i;
2509
                        
2510
                        /* set the property association for the token */
2511
                        if (tok != 0)
2512
                            tok->set_prop_assoc(propsym->get_prop());
2513
2514
                        /* 
2515
                         *   Add the property to our list of assigned
2516
                         *   properties - we'll use this to build the
2517
                         *   grammar info list for the match object.  Only
2518
                         *   add the property if it's not already in the
2519
                         *   list.
2520
                         */
2521
                        for (i = 0 ; i < prop_arrow_cnt ; ++i)
2522
                        {
2523
                            /* if we found it, stop looking */
2524
                            if (prop_arrows[i] == propsym)
2525
                                break;
2526
                        }
2527
2528
                        /* 
2529
                         *   if we didn't find it, and we have room for
2530
                         *   another, add it 
2531
                         */
2532
                        if (i == prop_arrow_cnt
2533
                            && prop_arrow_cnt < prop_arrow_max)
2534
                        {
2535
                            /* it's not there - add it */
2536
                            prop_arrows[prop_arrow_cnt++] = propsym;
2537
                        }
2538
                    }
2539
                    else
2540
                    {
2541
                        /* log an error */
2542
                        G_tok->log_error_curtok(TCERR_GRAMMAR_ARROW_REQ_PROP);
2543
                    }
2544
                    
2545
                    /* skip the symbol */
2546
                    G_tok->next();
2547
                }
2548
                else
2549
                {
2550
                    /* it's an error */
2551
                    G_tok->log_error_curtok(TCERR_GRAMMAR_ARROW_REQ_PROP);
2552
                }
2553
            }
2554
            break;
2555
2556
        case TOKT_TIMES:
2557
            /* free-floating end token */
2558
            tok = new (G_prsmem) CTcGramProdTok();
2559
            tok->set_match_star();
2560
2561
            /* add it to the current alternative's list */
2562
            alt->add_tok(tok);
2563
2564
            /* skip the star */
2565
            G_tok->next();
2566
2567
            /* 
2568
             *   this must be the last token in the alternative, so we
2569
             *   must have a ':' or '|' following 
2570
             */
2571
            if (G_tok->cur() != TOKT_OR && G_tok->cur() != TOKT_COLON)
2572
                G_tok->log_error_curtok(TCERR_GRAMMAR_STAR_NOT_END);
2573
            break;
2574
2575
        case TOKT_EOF:
2576
            /* log the error */
2577
            G_tok->log_error_curtok(TCERR_GRAMMAR_INVAL_TOK);
2578
            
2579
            /* give up */
2580
            *err = TRUE;
2581
            return 0;
2582
2583
        case TOKT_LBRACE:
2584
        case TOKT_RBRACE:
2585
        case TOKT_SEM:
2586
            /* 
2587
             *   they probably meant to end the statement, even though
2588
             *   this isn't the time or place to do so - log an error and
2589
             *   stop scanning 
2590
             */
2591
            G_tok->log_error_curtok(TCERR_GRAMMAR_INVAL_TOK);
2592
            done = TRUE;
2593
            break;
2594
2595
        default:
2596
            /* log an error */
2597
            G_tok->log_error_curtok(TCERR_GRAMMAR_INVAL_TOK);
2598
2599
            /* 
2600
             *   skip the offending token, and hope that they merely
2601
             *   inserted something invalid 
2602
             */
2603
            G_tok->next();
2604
            break;
2605
        }
2606
    }
2607
2608
    /* parse the object body */
2609
    stm = parse_object_body(err, gram_obj, TRUE, is_anon, TRUE,
2610
                            FALSE, modify, mod_orig_sym, 0, 0, 0, FALSE);
2611
2612
    /*
2613
     *   Add the grammarInfo property.  This property is a method that
2614
     *   returns a list whose first element is the match's name tag, and
2615
     *   whose subsequent elements are the arrow-assigned properties.
2616
     *   
2617
     *   This is only necessary if we managed to create a statement object.  
2618
     */
2619
    if (stm != 0)
2620
    {
2621
        CTPNList *lst;
2622
        CTcConstVal cval;
2623
        size_t i;
2624
2625
        /* create the list expression */
2626
        lst = new CTPNList();
2627
2628
        /* add the name-tag string element */
2629
        cval.set_sstr(name_tag, name_tag_len);
2630
        lst->add_element(new CTPNConst(&cval));
2631
2632
        /* add each property to the list */
2633
        for (i = 0 ; i < prop_arrow_cnt ; ++i)
2634
        {
2635
            /* add a node to evaluate this property symbol */
2636
            lst->add_element(create_sym_node(prop_arrows[i]->get_sym(),
2637
                                             prop_arrows[i]->get_sym_len()));
2638
        }
2639
2640
        /* add a property for this */
2641
        stm->add_prop(graminfo_prop_, lst, FALSE, FALSE);
2642
    }
2643
2644
    /* return the object definition statement */
2645
    return stm;
2646
}
2647
2648
/*
2649
 *   Flatten a set of grammar rules.  Each time we find a parenthesized
2650
 *   alternator, we'll expand the alternatives, until we have no
2651
 *   parenthesized alternators. 
2652
 */
2653
CTcPrsGramNode *CTcParser::flatten_gram_rule(int *err)
2654
{
2655
    CTcPrsGramNode *tree;
2656
    
2657
    /* first, build the top-level 'or' tree */
2658
    tree = parse_gram_or(err, 0);
2659
    if (tree == 0 || *err != 0)
2660
        return 0;
2661
2662
    /* move all of the OR's to a single OR at the top of the tree */
2663
    tree = tree->consolidate_or();
2664
2665
    /* flatten CAT nodes in the resulting tree */
2666
    tree->flatten_cat();
2667
2668
    /* 
2669
     *   if the top-level node isn't an OR, insert an OR at the top - this
2670
     *   makes the tree always follow the same shape, which makes it easier
2671
     *   to step through the tokens in the expansion
2672
     */
2673
    if (!tree->is_or())
2674
    {
2675
        CTcPrsGramNodeOr *new_or;
2676
2677
        /* create an OR node for the root of the tree */
2678
        new_or = new (G_prsmem) CTcPrsGramNodeOr();
2679
2680
        /* insert our old root as the single alternative under the OR */
2681
        new_or->add_sub_item(tree);
2682
2683
        /* the OR is now the root of the tree */
2684
        tree = new_or;
2685
    }
2686
2687
    /* start reading from the first token in the tree */
2688
    tree->init_expansion();
2689
2690
    /* return the tree */
2691
    return tree;
2692
}
2693
2694
/*
2695
 *   Parse an OR expression in a grammar rule.  Returns an OR node
2696
 *   representing the expression.  
2697
 */
2698
CTcPrsGramNode *CTcParser::parse_gram_or(int *err, int level)
2699
{
2700
    CTcPrsGramNodeOr *or_node;
2701
    CTcPrsGramNode *sub;
2702
2703
    /* build our 'or' node */
2704
    or_node = new (G_prsmem) CTcPrsGramNodeOr();
2705
2706
    /* if the rule is completely empty, warn about it */
2707
    if (level == 0 && G_tok->cur() == TOKT_COLON && !syntax_only_)
2708
        G_tok->log_warning(TCERR_GRAMMAR_EMPTY);
2709
2710
    /* parse our initial 'cat' subnode */
2711
    sub = parse_gram_cat(err, level + 1);
2712
2713
    /* abort on error */
2714
    if (sub == 0 || *err != 0)
2715
        return 0;
2716
2717
    /* add the subnode to our 'or' list */
2718
    or_node->add_sub_item(sub);
2719
2720
    /* keep going as long as we have more rules appended with '|' */
2721
    while (G_tok->cur() == TOKT_OR)
2722
    {
2723
        /* skip the '|' */
2724
        G_tok->next();
2725
2726
        /* 
2727
         *   if we're at the top level, and the next token is the closing
2728
         *   ':', warn about the empty last rule 
2729
         */
2730
        if (level == 0 && G_tok->cur() == TOKT_COLON && !syntax_only_)
2731
            G_tok->log_warning(TCERR_GRAMMAR_ENDS_WITH_OR);
2732
2733
        /* parse the 'cat' subnode */
2734
        sub = parse_gram_cat(err, level + 1);
2735
2736
        /* abort on error */
2737
        if (sub == 0 || *err != 0)
2738
            return 0;
2739
2740
        /* add the subnode to our 'or' list */
2741
        or_node->add_sub_item(sub);
2742
    }
2743
2744
    /* return the 'or' list */
2745
    return or_node;
2746
}
2747
2748
/*
2749
 *   Parse a concatenation expression in a grammar rule.  Returns a CAT node
2750
 *   representing the expression.  
2751
 */
2752
CTcPrsGramNode *CTcParser::parse_gram_cat(int *err, int level)
2753
{
2754
    CTcPrsGramNodeCat *cat_node;
2755
    CTcPrsGramNode *sub;
2756
2757
    /* build our concatenation node */
2758
    cat_node = new (G_prsmem) CTcPrsGramNodeCat();
2759
2760
    /* add tokens to the cat list until we find the end of the list */
2761
    for (;;)
2762
    {
2763
        /* see what we have */
2764
        switch(G_tok->cur())
2765
        {
2766
        case TOKT_LPAR:
2767
            /* skip the paren */
2768
            G_tok->next();
2769
2770
            /* parse the OR expression */
2771
            sub = parse_gram_or(err, level + 1);
2772
2773
            /* if that failed, abort */
2774
            if (sub == 0 || *err != 0)
2775
                return 0;
2776
2777
            /* add the 'OR' to our concatenation expression */
2778
            cat_node->add_sub_item(sub);
2779
2780
            /* make sure we're at the close paren */
2781
            if (G_tok->cur() != TOKT_RPAR)
2782
                G_tok->log_error_curtok(TCERR_GRAMMAR_REQ_RPAR_AFTER_GROUP);
2783
            else
2784
                G_tok->next();
2785
2786
            /* 
2787
             *   make sure we don't have an arrow immediately following; an
2788
             *   arrow isn't allowed after a close paren, because a group
2789
             *   isn't a true subproduction 
2790
             */
2791
            if (G_tok->cur() == TOKT_ARROW)
2792
                G_tok->log_error(TCERR_GRAMMAR_GROUP_ARROW_NOT_ALLOWED);
2793
2794
            /* done with the 'or' expression */
2795
            break;
2796
2797
        case TOKT_OR:
2798
            /* 
2799
             *   the 'or' has lower precedence than concatenation, so we've
2800
             *   reached the end of the concatenation expression - simply
2801
             *   return what we have so far and let the caller figure out
2802
             *   where to go next 
2803
             */
2804
            return cat_node;
2805
2806
        case TOKT_COLON:
2807
        case TOKT_SEM:
2808
        case TOKT_LBRACE:
2809
        case TOKT_RBRACE:
2810
        case TOKT_EOF:
2811
        case TOKT_RPAR:
2812
            /* we've reached the end of the rule */
2813
            return cat_node;
2814
2815
        default:
2816
            /* anything else is simply concatenated to the list so far */
2817
            cat_node->add_sub_item(
2818
                new (G_prsmem) CTcPrsGramNodeTok(G_tok->getcur()));
2819
2820
            /* skip the token and keep scanning */
2821
            G_tok->next();
2822
            break;
2823
        }
2824
    }
2825
}
2826
            
2827
/*
2828
 *   Parse a grammar qualifier integer value 
2829
 */
2830
int CTcParser::parse_gram_qual_int(int *err, const char *qual_name,
2831
                                   int *stm_end)
2832
{
2833
    CTcPrsNode *expr;
2834
    CTcConstVal *cval;
2835
2836
    /* skip the qualifier name */
2837
    G_tok->next();
2838
2839
    /* check for a missing expression */
2840
    if (G_tok->cur() == TOKT_RBRACK)
2841
    {
2842
        /* don't bother looking for an expression - it's not there */
2843
        expr = 0;
2844
    }
2845
    else
2846
    {
2847
        /* parse an expression */
2848
        expr = parse_expr();
2849
    }
2850
2851
    /* 
2852
     *   if we didn't get an expression, or it doesn't have a constant
2853
     *   value, or the constant value is something other than an integer,
2854
     *   it's an error 
2855
     */
2856
    if (expr == 0
2857
        || (cval = expr->get_const_val()) == 0
2858
        || cval->get_type() != TC_CVT_INT)
2859
    {
2860
        /* log an error */
2861
        G_tok->log_error(TCERR_GRAMMAR_QUAL_REQ_INT, qual_name);
2862
2863
        /* skip to the closing bracket */
2864
        parse_gram_qual_skip(err, stm_end);
2865
2866
        /* we don't have a value to return; use zero by default */
2867
        return 0;
2868
    }
2869
    else
2870
    {
2871
        /* return the constant expression value */
2872
        return cval->get_val_int();
2873
    }
2874
}
2875
2876
/*
2877
 *   Skip to the end of a grammar qualifier.  This is used when a syntax
2878
 *   error occurs and we abandon parsing the rest of the qualifier. 
2879
 */
2880
void CTcParser::parse_gram_qual_skip(int *err, int *stm_end)
2881
{
2882
    /* scan until we find the end */
2883
    for (;;)
2884
    {
2885
        switch(G_tok->next())
2886
        {
2887
        case TOKT_RBRACK:
2888
            /* 
2889
             *   that's the end of the mal-formed qualifier; skip the
2890
             *   bracket and keep going from here 
2891
             */
2892
            G_tok->next();
2893
            break;
2894
2895
        case TOKT_EOF:
2896
        case TOKT_RBRACE:
2897
        case TOKT_LBRACE:
2898
        case TOKT_SEM:
2899
            /* 
2900
             *   probably the end of the statement - stop scanning, and
2901
             *   set the 'stm_end' flag to tell the caller that we're done
2902
             *   parsing the entire statement 
2903
             */
2904
            *stm_end = TRUE;
2905
            return;
2906
            
2907
        default:
2908
            /* skip everything else and just keep going */
2909
            break;
2910
        }
2911
    }
2912
}
2913
2914
/* ------------------------------------------------------------------------ */
2915
/*
2916
 *   Parse an 'enum' top-level statement 
2917
 */
2918
void CTcParser::parse_enum(int *err)
2919
{
2920
    int done;
2921
    int is_token;
2922
2923
    /* presume it's not a 'token' enum */
2924
    is_token = FALSE;
2925
    
2926
    /* skip the 'enum' token */
2927
    G_tok->next();
2928
2929
    /* 
2930
     *   if this is the 'token' context-sensitive keyword, note it and
2931
     *   skip it 
2932
     */
2933
    if (G_tok->cur() == TOKT_SYM && G_tok->cur_tok_matches("token", 5))
2934
    {
2935
        /* remember that it's a 'token' enum */
2936
        is_token = TRUE;
2937
2938
        /* skip the keyword */
2939
        G_tok->next();
2940
    }
2941
2942
    /* parse enum constants */
2943
    for (done = FALSE ; !done ; )
2944
    {
2945
        CTcSymEnum *sym;
2946
        
2947
        switch (G_tok->cur())
2948
        {
2949
        case TOKT_SYM:
2950
            /* make sure the symbol isn't already defined */
2951
            sym = (CTcSymEnum *)
2952
                  global_symtab_->find(G_tok->getcur()->get_text(),
2953
                                       G_tok->getcur()->get_text_len());
2954
2955
            /* if it's already defined, make sure it's an enum */
2956
            if (sym != 0)
2957
            {
2958
                /* if it's not an enum, it's an error */
2959
                if (sym->get_type() != TC_SYM_ENUM)
2960
                {
2961
                    /* log the error */
2962
                    G_tok->log_error_curtok(TCERR_REDEF_AS_ENUM);
2963
                }
2964
                else if (is_token)
2965
                {
2966
                    /* 
2967
                     *   we're defining a token - mark the old symbol as a
2968
                     *   token if it wasn't already 
2969
                     */
2970
                    sym->set_is_token(TRUE);
2971
                }
2972
            }
2973
            else
2974
            {
2975
                /* create the new symbol */
2976
                sym = new CTcSymEnum(G_tok->getcur()->get_text(),
2977
                                     G_tok->getcur()->get_text_len(),
2978
                                     FALSE, new_enum_id(), is_token);
2979
2980
                /* add it to the symbol table */
2981
                global_symtab_->add_entry(sym);
2982
2983
                /* mark it as referenced, since it's defined here */
2984
                sym->mark_referenced();
2985
            }
2986
2987
            /* skip the symbol name and see what follows */
2988
            switch(G_tok->next())
2989
            {
2990
            case TOKT_COMMA:
2991
                /* skip the comma and keep going */
2992
                G_tok->next();
2993
                break;
2994
                
2995
            case TOKT_SEM:
2996
                /* end of the statement */
2997
                done = TRUE;
2998
                G_tok->next();
2999
                break;
3000
3001
            case TOKT_LBRACE:
3002
            case TOKT_RBRACE:
3003
            case TOKT_EOF:
3004
                /* they probably omitted the closing semicolon */
3005
                G_tok->log_error_curtok(TCERR_EXPECTED_SEMI);
3006
                done = TRUE;
3007
                break;
3008
3009
            default:
3010
                /* a comma was required */
3011
                G_tok->log_error_curtok(TCERR_ENUM_REQ_COMMA);
3012
3013
                /* skip the offending symbol and proceed */
3014
                G_tok->next();
3015
                break;
3016
            }
3017
3018
            /* done with this token */
3019
            break;
3020
3021
        case TOKT_LBRACE:
3022
        case TOKT_RBRACE:
3023
        case TOKT_EOF:
3024
            /* 
3025
             *   they probably omitted the closing semicolon - log an
3026
             *   error, then stop scanning the statement, since we're
3027
             *   probably in the next statement already 
3028
             */
3029
            G_tok->log_error_curtok(TCERR_EXPECTED_SEMI);
3030
            done = TRUE;
3031
            break;
3032
3033
        case TOKT_SEM:
3034
            /* they must have left out a symbol */
3035
            G_tok->log_error_curtok(TCERR_ENUM_REQ_SYM);
3036
3037
            /* accept the semicolon as the end of the statement */
3038
            G_tok->next();
3039
            done = TRUE;
3040
            break;
3041
            
3042
        default:
3043
            /* anything else is an error */
3044
            G_tok->log_error_curtok(TCERR_ENUM_REQ_SYM);
3045
3046
            /* skip the offending token and keep parsing */
3047
            G_tok->next();
3048
            break;
3049
        }
3050
    }
3051
}
3052
3053
/*
3054
 *   Parse an 'extern' top-level statement 
3055
 */
3056
void CTcParser::parse_extern(int *err)
3057
{
3058
    int is_class;
3059
3060
    /* skip the 'extern' token and check what follows */
3061
    switch(G_tok->next())
3062
    {
3063
    case TOKT_FUNCTION:
3064
        /* parse the function definition for the prototype only */
3065
        parse_function(err, TRUE, FALSE, FALSE, TRUE);
3066
        break;
3067
3068
    case TOKT_CLASS:
3069
        /* note that it's a class, and process it otherwise like 'object' */
3070
        is_class = TRUE;
3071
        goto do_object;
3072
3073
    case TOKT_OBJECT:
3074
        /* note that it's not a class */
3075
        is_class = FALSE;
3076
3077
    do_object:
3078
        /* external object/class - skip the keyword and get the symbol */
3079
        if (G_tok->next() == TOKT_SYM)
3080
        {
3081
            CTcSymObj *sym;
3082
3083
            /* check for a prior definition */
3084
            sym = (CTcSymObj *)
3085
                  global_symtab_->find(G_tok->getcur()->get_text(),
3086
                                       G_tok->getcur()->get_text_len());
3087
            if (sym == 0)
3088
            {
3089
                /* not yet defined - create the external object symbol */
3090
                sym = new CTcSymObj(G_tok->getcur()->get_text(),
3091
                                    G_tok->getcur()->get_text_len(),
3092
                                    FALSE, G_cg->new_obj_id(), TRUE,
3093
                                    TC_META_TADSOBJ, 0);
3094
3095
                /* add it to the symbol table */
3096
                global_symtab_->add_entry(sym);
3097
            }
3098
            else if (sym->get_type() == TC_SYM_OBJ
3099
                     && sym->get_metaclass() == TC_META_TADSOBJ
3100
                     && ((sym->is_class() != 0) == (is_class != 0)))
3101
            {
3102
                /* 
3103
                 *   It's already defined as this same type, so this
3104
                 *   definition is merely redundant - ignore it.  
3105
                 */
3106
            }
3107
            else
3108
            {
3109
                /* 
3110
                 *   it's already defined, but not as an object - log an
3111
                 *   error and ignore the redefinition 
3112
                 */
3113
                G_tok->log_error_curtok(TCERR_OBJ_REDEF);
3114
            }
3115
3116
            /* parse the required semicolon */
3117
            G_tok->next();
3118
            if (parse_req_sem())
3119
                *err = TRUE;
3120
        }
3121
        else
3122
        {
3123
            /* invalid syntax - log an error */
3124
            G_tok->log_error_curtok(TCERR_EXTERN_OBJ_REQ_SYM);
3125
3126
            /* skip to the next semicolon */
3127
            if (skip_to_sem())
3128
                *err = TRUE;
3129
        }
3130
        break;
3131
3132
    default:
3133
        /* log an error */
3134
        G_tok->log_error_curtok(TCERR_INVAL_EXTERN);
3135
3136
        /* skip to the next semicolon */
3137
        if (skip_to_sem())
3138
            *err = TRUE;
3139
        break;
3140
    }
3141
}
3142
3143
/*
3144
 *   Parse a 'function' top-level statement 
3145
 */
3146
CTPNStmTop *CTcParser::parse_function(int *err, int is_extern,
3147
                                      int replace, int modify,
3148
                                      int func_kw_present)
3149
{
3150
    CTcToken tok;
3151
    CTcSymFunc *func_sym;
3152
    CTPNCodeBody *code_body;
3153
    int argc;
3154
    int varargs;
3155
    int varargs_list;
3156
    CTcSymLocal *varargs_list_local;
3157
    int has_retval;
3158
    int redef;
3159
    CTcTokFileDesc *sym_file;
3160
    long sym_linenum;
3161
    CTcFormalTypeList *type_list = 0;
3162
3163
    /* skip the 'function' keyword if present */
3164
    if (func_kw_present)
3165
        G_tok->next();
3166
3167
    /* we need a symbol */
3168
    if (G_tok->cur() != TOKT_SYM)
3169
    {
3170
        /* log an error */
3171
        G_tok->log_error_curtok(TCERR_FUNC_REQ_SYM);
3172
        
3173
        /* proceed from this token */
3174
        return 0;
3175
    }
3176
3177
    /* remember the symbol token */
3178
    tok = *G_tok->copycur();
3179
3180
    /* remember the location of the symbol (for later error reporting) */
3181
    G_tok->get_last_pos(&sym_file, &sym_linenum);
3182
3183
    /* 
3184
     *   Skip past the function name.  If the next token is a semicolon,
3185
     *   this is a forward declaration; otherwise, we need either an open
3186
     *   brace or an argument list 
3187
     */
3188
    switch(G_tok->next())
3189
    {
3190
    case TOKT_SEM:
3191
        /* it's just a forward declaration - skip the semicolon */
3192
        G_tok->next();
3193
3194
        /* 
3195
         *   if this is an 'extern' declaration, then declare the function
3196
         *   as extern with zero arguments 
3197
         */
3198
        if (is_extern)
3199
        {
3200
            /* no arguments */
3201
            argc = 0;
3202
            varargs = FALSE;
3203
            varargs_list = FALSE;
3204
3205
            /* assume it has a return value */
3206
            has_retval = TRUE;
3207
3208
            /* no code body */
3209
            code_body = 0;
3210
3211
            /* handle the declaration */
3212
            goto decl_func;
3213
        }
3214
3215
        /* 
3216
         *   A non-extern forward declaration has no effect.  Ignore the
3217
         *   definition and keep going. 
3218
         */
3219
        return 0;
3220
        
3221
    case TOKT_LPAR:
3222
    case TOKT_LBRACE:
3223
        /* 
3224
         *   check whether this is an 'extern' declaration or the actual
3225
         *   definition, and parse accordingly
3226
         */
3227
        if (is_extern)
3228
        {
3229
            /* 
3230
             *   It's external - parse simply the formal parameter list.
3231
             *   A brace is not allowed here. 
3232
             */
3233
            if (G_tok->cur() == TOKT_LPAR)
3234
            {
3235
                /* skip the paren */
3236
                G_tok->next();
3237
3238
                /* parse the formals */
3239
                parse_formal_list(TRUE, FALSE, &argc, 0, &varargs,
3240
                                  &varargs_list, &varargs_list_local,
3241
                                  err, 0, FALSE, &type_list);
3242
3243
                /* 
3244
                 *   if it looks as though there's a code body, log an
3245
                 *   error and skip and ignore the code body 
3246
                 */
3247
                if (G_tok->cur() == TOKT_LBRACE
3248
                    || G_tok->cur() == TOKT_EQ)
3249
                {
3250
                    /* log the error */
3251
                    G_tok->log_error(TCERR_EXTERN_NO_CODE_BODY);
3252
3253
                    /* parse and ignore the code body */
3254
                    parse_code_body(FALSE, FALSE, FALSE,
3255
                                    &argc, &varargs,
3256
                                    &varargs_list, &varargs_list_local,
3257
                                    &has_retval, err, 0, TCPRS_CB_NORMAL,
3258
                                    0, 0, 0, 0);
3259
3260
                    /* return failure */
3261
                    return 0;
3262
                }
3263
3264
                /* parse the required terminating semicolon */
3265
                parse_req_sem();
3266
            }
3267
            else
3268
            {
3269
                /* code body is not allowed - log an error */
3270
                G_tok->log_error(TCERR_EXTERN_NO_CODE_BODY);
3271
3272
                /* parse and ignore the code body */
3273
                parse_code_body(FALSE, FALSE, FALSE, &argc, &varargs,
3274
                                &varargs_list, &varargs_list_local,
3275
                                &has_retval, err, 0, TCPRS_CB_NORMAL,
3276
                                0, 0, 0, 0);
3277
3278
                /* done */
3279
                return 0;
3280
            }
3281
3282
            /* there's no code body for an 'extern' definintion */
3283
            code_body = 0;
3284
3285
            /* assume all externals return a value */
3286
            has_retval = TRUE;
3287
        }
3288
        else
3289
        {
3290
            /*
3291
             *   We're parsing the actual definition.  Parse the formal
3292
             *   parameter list and the code body.  
3293
             */
3294
            code_body = parse_code_body(FALSE, FALSE, FALSE, &argc, &varargs,
3295
                                        &varargs_list, &varargs_list_local,
3296
                                        &has_retval, err, 0, TCPRS_CB_NORMAL,
3297
                                        0, 0, 0, &type_list);
3298
        }
3299
3300
        /* if that failed, do not go on */
3301
        if (*err || (code_body == 0 && !is_extern))
3302
            return 0;
3303
3304
    decl_func:
3305
        /* if we have a type list, generate the decorated name */
3306
        if (type_list != 0)
3307
        {
3308
            /*
3309
             *   The function we're defining here will go by a decorated
3310
             *   name, not the base name, since it's a particular version of
3311
             *   this multi-method.  So, we need to replace the function name
3312
             *   token with the decorated form, which incorporates the typed
3313
             *   parameter list to distinguish this version from other
3314
             *   multi-methods with the same base name and different
3315
             *   parameter type lists.
3316
             *   
3317
             *   Before we generate the decorated name, though, define the
3318
             *   base name as an external multi-method function, so that
3319
             *   we'll know what to do when we see calls to the base
3320
             *   function.  We define the base name as external because the
3321
             *   current source code isn't actually a definition of the base
3322
             *   name; the base name refers to an implied function that's not
3323
             *   actually defined anywhere in the source code, but rather is
3324
             *   generated automatically by the compiler.  As such, the
3325
             *   source we're compiling effectively declares the existence of
3326
             *   a function with the base name, but says that it will be
3327
             *   defined elsewhere - this is, of course, the exact purpose of
3328
             *   an external declaration.
3329
             *   
3330
             *   So, if the base name isn't yet defined, define it as an
3331
             *   external function, and mark it as a multi-method.  Start by
3332
             *   looking up the base function name.  
3333
             */
3334
            func_sym = (CTcSymFunc *)global_symtab_->find(
3335
                tok.get_text(), tok.get_text_len());
3336
3337
            /* 
3338
             *   if it's defined, make sure the existing definition is
3339
             *   compatible; if it's not defined, define it now 
3340
             */
3341
            if (func_sym != 0)
3342
            {
3343
                /* 
3344
                 *   it's previously defined: to be compatible, it has to be
3345
                 *   an external, multi-method function 
3346
                 */
3347
                if (func_sym->get_type() != TC_SYM_FUNC)
3348
                {
3349
                    /* it's already defined as a non-function */
3350
                    G_tcmain->log_error(
3351
                        sym_file, sym_linenum,
3352
                        TC_SEV_ERROR, TCERR_REDEF_AS_FUNC,
3353
                        (int)tok.get_text_len(), tok.get_text());
3354
                }
3355
                else if (!(func_sym->is_extern()
3356
                           && func_sym->is_multimethod()))
3357
                {
3358
                    /* it's already defined incompatibly - flag the error */
3359
                    G_tcmain->log_error(
3360
                        sym_file, sym_linenum,
3361
                        TC_SEV_ERROR, TCERR_FUNC_REDEF_AS_MULTIMETHOD,
3362
                        (int)tok.get_text_len(), tok.get_text());
3363
3364
                    /* ignore the redefinition */
3365
                    return 0;
3366
                }
3367
            }
3368
            else
3369
            {
3370
                /* 
3371
                 *   It's not defined yet, so add it as an external
3372
                 *   multi-method function.  This base form is always a
3373
                 *   varargs function with no fixed arguments, since the
3374
                 *   parameter lists of the different definitions can vary.
3375
                 *   We'll likewise assume that there's a return value, since
3376
                 *   some instances might have returns.  
3377
                 */
3378
                func_sym = new CTcSymFunc(tok.get_text(), tok.get_text_len(),
3379
                                          FALSE, 0, TRUE, TRUE,
3380
                                          TRUE, TRUE, TRUE);
3381
3382
                /* 
3383
                 *   Mark the function symbol as defined in this file.  This
3384
                 *   will ensure that we export it to our symbol file, even
3385
                 *   though it's defined here as an extern symbol.  We don't
3386
                 *   normally export externs, since we'd simply count on the
3387
                 *   defining file to export them; but the base symbol for a
3388
                 *   multi-method has no definer at all until link time, so
3389
                 *   *someone* needs to export it.  So export it from any
3390
                 *   file that defines a multi-method with the base name.  
3391
                 */
3392
                func_sym->set_mm_def(TRUE);
3393
3394
                /* add it to the symbol table */
3395
                global_symtab_->add_entry(func_sym);
3396
            }
3397
3398
            /* 
3399
             *   Now we can generate the decorated name.  This name
3400
             *   distinguishes this type-list version of the function from
3401
             *   other versions with the same base name, and is the name by
3402
             *   which the actual function definition will be known.  We'll
3403
             *   also operate on the decorated name for 'replace' or
3404
             *   'modify', since what we're replacing or modifying is this
3405
             *   specific type-list version of the function.  
3406
             */
3407
            type_list->decorate_name(&tok, &tok);
3408
        }
3409
3410
        /* find any existing function symbol */
3411
        func_sym = (CTcSymFunc *)
3412
                   global_symtab_->find(tok.get_text(), tok.get_text_len());
3413
3414
        /* 
3415
         *   If the symbol was already defined, the previous definition must
3416
         *   be external, or this new definition must be external, or the new
3417
         *   definition must be a 'replace' definition; and the two
3418
         *   definitions must exactly match the new parameters and the
3419
         *   multi-method status.  Ignore differences in the return value,
3420
         *   since we have no way to specify in 'extern' definitions whether
3421
         *   a function has a return value or not.  
3422
         */
3423
        redef = (func_sym != 0
3424
                 && (func_sym->get_type() != TC_SYM_FUNC
3425
                     || (!func_sym->is_extern()
3426
                         && !is_extern && !replace && !modify)
3427
                     || func_sym->get_argc() != argc
3428
                     || func_sym->is_varargs() != varargs
3429
                     || func_sym->is_multimethod() != (type_list != 0)));
3430
3431
        /* 
3432
         *   If the symbol was already defined, log an error, then ignore
3433
         *   the redefinition and return an empty statement.  Note that we
3434
         *   waited until now because we still wanted to parse the syntax
3435
         *   of the function, but we can't really do anything with it once
3436
         *   we're finished with the parsing.  
3437
         */
3438
        if (redef)
3439
        {
3440
            int err;
3441
            
3442
            /* 
3443
             *   Note the problem.  Select the error message to display
3444
             *   depending on whether the symbol was previously defined as
3445
             *   a function (in which case the problem is the conflicting
3446
             *   prototype) or as another type.  
3447
             */
3448
            if (func_sym->get_type() == TC_SYM_FUNC)
3449
            {
3450
                /* 
3451
                 *   if one or the other definition is external, or we're
3452
                 *   replacing the function, the problem is a mismatch in
3453
                 *   the parameter lists; otherwise, the problem is that
3454
                 *   the function is simply defined twice 
3455
                 */
3456
                if (func_sym->is_extern() || is_extern || replace || modify)
3457
                    err = TCERR_INCOMPAT_FUNC_REDEF;
3458
                else
3459
                    err = TCERR_FUNC_REDEF;
3460
            }
3461
            else
3462
            {
3463
                /* it's being redefined as a new type */
3464
                err = TCERR_REDEF_AS_FUNC;
3465
            }
3466
3467
            /* log the error (at the symbol's position) */
3468
            G_tcmain->log_error(sym_file, sym_linenum, TC_SEV_ERROR, err,
3469
                                (int)tok.get_text_len(), tok.get_text());
3470
3471
            /* ignore the re-definition */
3472
            return 0;
3473
        }
3474
3475
        /* 
3476
         *   create a symbol table entry for the function if we didn't
3477
         *   have on already 
3478
         */
3479
        if (func_sym == 0)
3480
        {
3481
            /* we didn't have a symbol previously - create one */
3482
            func_sym = new CTcSymFunc(tok.get_text(), tok.get_text_len(),
3483
                                      FALSE, argc, varargs, has_retval,
3484
                                      type_list != 0, FALSE, is_extern);
3485
3486
            /* add the entry to the global symbol table */
3487
            global_symtab_->add_entry(func_sym);
3488
3489
            /* 
3490
             *   We can't replace/modify a function that wasn't previously
3491
             *   defined - if we're replacing it, note the error.  Don't
3492
             *   bother with this check if we're parsing only for syntax.  
3493
             */
3494
            if ((replace || modify) && !syntax_only_)
3495
                G_tok->log_error(TCERR_REPFUNC_UNDEF);
3496
        }
3497
        else
3498
        {
3499
            /* replace or modify the previous definition if appropriate */
3500
            if (replace)
3501
            {
3502
                /* 
3503
                 *   check to see whether it's external or previously
3504
                 *   defined in the same translation unit 
3505
                 */
3506
                if (func_sym->is_extern())
3507
                {
3508
                    /* 
3509
                     *   The function was previously external, and we're
3510
                     *   replacing it -- mark it as such so that we'll
3511
                     *   know to perform the replacement at link time.  We
3512
                     *   only need to do this when replacing an external
3513
                     *   function, because if the function is defined in
3514
                     *   the same translation unit then the replacement
3515
                     *   will be completed right now, and we don't need to
3516
                     *   do any more work at link time.  
3517
                     */
3518
                    func_sym->set_ext_replace(TRUE);
3519
                }
3520
                else
3521
                {
3522
                    CTcSymFunc *mod_base;
3523
3524
                    /*
3525
                     *   The function was previously defined in the same
3526
                     *   translation unit.  Mark the previous code body as
3527
                     *   replaced so that we don't bother generating any
3528
                     *   code for it. 
3529
                     */
3530
                    if (func_sym->get_code_body() != 0)
3531
                        func_sym->get_code_body()->set_replaced(TRUE);
3532
3533
                    /*
3534
                     *   If the function we're replacing modifies any base
3535
                     *   functions, we're effectively replacing all of the
3536
                     *   base functions as well.  None of these functions
3537
                     *   will be reachable, since a modified base function is
3538
                     *   only reachable through its modifying function; since
3539
                     *   the function we're replacing is becoming
3540
                     *   unreachable, therefore, all of its base functions
3541
                     *   are as well.  
3542
                     */
3543
                    for (mod_base = func_sym->get_mod_base() ; mod_base != 0 ;
3544
                         mod_base = mod_base->get_mod_base())
3545
                    {
3546
                        /* mark this base function as replaced */
3547
                        if (mod_base->is_extern())
3548
                        {
3549
                            /* mark it as a replaced extern */
3550
                            mod_base->set_ext_replace(TRUE);
3551
3552
                            /* note that our function replaces an external */
3553
                            func_sym->set_ext_replace(TRUE);
3554
                        }
3555
                        else if (mod_base->get_code_body() != 0)
3556
                        {
3557
                            /* mark its code body as replaced */
3558
                            mod_base->get_code_body()->set_replaced(TRUE);
3559
                        }
3560
                    }
3561
3562
                    /* 
3563
                     *   since we're replacing the function, it no longer
3564
                     *   has any modified base functions 
3565
                     */
3566
                    func_sym->set_mod_base(0);
3567
                }
3568
            }
3569
            else if (modify)
3570
            {
3571
                CTcSymFunc *mod_sym;
3572
3573
                /* 
3574
                 *   Create a new dummy symbol for the original version.
3575
                 *   This symbol doesn't go in the symbol table; instead, we
3576
                 *   link it into our modified base function list behind the
3577
                 *   symbol table entry.
3578
                 *   
3579
                 *   The name is important, though: we have to give this an
3580
                 *   empty name so that the object file reader will know that
3581
                 *   this isn't an actual global symbol.  This will let the
3582
                 *   object file loader's code stream reader know that it
3583
                 *   can't fix up references to the code stream anchor by
3584
                 *   looking at the global symbol table.  (We have to write
3585
                 *   some extra code in the function symbol loader to deal
3586
                 *   with this, since the code stream reader can't.)
3587
                 *   
3588
                 *   Note that we want the new base function symbol to keep
3589
                 *   the same 'extern' attribute as the original symbol.  If
3590
                 *   we're modifying an external function, this will allow us
3591
                 *   to apply the modification at link time between this
3592
                 *   module and the module where the imported original
3593
                 *   function was defined.  
3594
                 */
3595
                mod_sym = new CTcSymFunc("", 0, FALSE,
3596
                                         argc, varargs, has_retval,
3597
                                         func_sym->is_multimethod(),
3598
                                         func_sym->is_multimethod_base(),
3599
                                         func_sym->is_extern());
3600
3601
                /* 
3602
                 *   If the original has a code body, switch the code body to
3603
                 *   use our new symbol's fixup list.
3604
                 *   
3605
                 *   The only way to reference the original code body is
3606
                 *   through a 'replaced()' call in the modifying function,
3607
                 *   so any fixups currently associated with the function
3608
                 *   symbol should remain with the function symbol - in other
3609
                 *   words, we don't have to move any existing fixups
3610
                 *   anywhere.  
3611
                 */
3612
                if (func_sym->get_code_body() != 0)
3613
                    func_sym->get_code_body()->set_symbol_fixup_list(
3614
                        mod_sym, mod_sym->get_fixup_list_anchor());
3615
3616
                /* transfer the old code body to the modified base symbol */
3617
                mod_sym->set_code_body(func_sym->get_code_body());
3618
3619
                /* 
3620
                 *   put the new modified base function at the head of the
3621
                 *   modified base list 
3622
                 */
3623
                mod_sym->set_mod_base(func_sym->get_mod_base());
3624
                func_sym->set_mod_base(mod_sym);
3625
            }
3626
            
3627
            /* 
3628
             *   The function was already defined.  If this is an actual
3629
             *   definition (not an 'extern' declaration), then clear the
3630
             *   'extern' flag on the existing symbol.  
3631
             */
3632
            if (!is_extern)
3633
                func_sym->set_extern(FALSE);
3634
        }
3635
3636
        /* 
3637
         *   If there's a code body, tell the code body about its symbol
3638
         *   table entry.  (If the function is 'extern' it has no code
3639
         *   body at this point.)  
3640
         */
3641
        if (code_body != 0)
3642
            code_body->set_symbol_fixup_list(
3643
                func_sym, func_sym->get_fixup_list_anchor());
3644
3645
        /* tell the function about its code body */
3646
        func_sym->set_code_body(code_body);
3647
3648
        /* return the code body as the result */
3649
        return code_body;
3650
3651
    default:
3652
        /* log an error and continue from here */
3653
        G_tok->log_error_curtok(TCERR_REQ_CODE_BODY);
3654
        return 0;
3655
    }
3656
}
3657
3658
/*
3659
 *   Parse an 'intrinsic' top-level statement 
3660
 */
3661
CTPNStmTop *CTcParser::parse_intrinsic(int *err)
3662
{
3663
    int list_done;
3664
    int fn_set_id;
3665
    int fn_set_idx;
3666
    tc_toktyp_t tok;
3667
    
3668
    /* skip the 'intrinsic' keyword, and check for the function set name */
3669
    if ((tok = G_tok->next()) == TOKT_SSTR)
3670
    {
3671
        /* tell the code generator to add this function set */
3672
        fn_set_id = G_cg->add_fnset(G_tok->getcur()->get_text(),
3673
                                    G_tok->getcur()->get_text_len());
3674
3675
        /* skip the name */
3676
        G_tok->next();
3677
    }
3678
    else if (tok == TOKT_CLASS)
3679
    {
3680
        /* it's an intrinsic class (metaclass) definition - go parse it */
3681
        return parse_intrinsic_class(err);
3682
    }
3683
    else
3684
    {
3685
        /* 
3686
         *   note the error, then keep going, assuming that the name was
3687
         *   missing but that the rest is correctly formed 
3688
         */
3689
        G_tok->log_error_curtok(TCERR_REQ_INTRINS_NAME);
3690
3691
        /* use an arbitrary function set ID, since we're failing */
3692
        fn_set_id = 0;
3693
    }
3694
3695
    /* we need an open brace after that */
3696
    if (G_tok->cur() == TOKT_LBRACE)
3697
    {
3698
        /* skip the brace */
3699
        G_tok->next();
3700
    }
3701
    else
3702
    {
3703
        /* note the error, but keep going */
3704
        G_tok->log_error_curtok(TCERR_REQ_INTRINS_LBRACE);
3705
    }
3706
3707
    /* keep going until we find the closing brace */
3708
    for (list_done = FALSE, fn_set_idx = 0 ; !list_done ; )
3709
    {
3710
        CTcToken fn_tok;
3711
        int argc;
3712
        int optargc;
3713
        int varargs;
3714
        int varargs_list;
3715
        CTcSymLocal *varargs_list_local;
3716
        CTcSymBif *bif_sym;
3717
3718
        /* check what we have */
3719
        switch(G_tok->cur())
3720
        {
3721
        case TOKT_RBRACE:
3722
            /* closing brace - skip it, and we're done */
3723
            G_tok->next();
3724
            list_done = TRUE;
3725
            break;
3726
3727
        case TOKT_EOF:
3728
            /* end of file - log an error */
3729
            G_tok->log_error(TCERR_EOF_IN_INTRINS);
3730
            
3731
            /* return failure */
3732
            *err = TRUE;
3733
            return 0;
3734
3735
        case TOKT_SEM:
3736
            /* empty list element; skip it */
3737
            G_tok->next();
3738
            break;
3739
3740
        case TOKT_SYM:
3741
            /* remember the function name */
3742
            fn_tok = *G_tok->copycur();
3743
3744
            /* the next token must be an open paren */
3745
            if (G_tok->next() == TOKT_LPAR)
3746
            {
3747
                /* skip the paren */
3748
                G_tok->next();
3749
            }
3750
            else
3751
            {
3752
                /* log an error, and assume the parent was simply missing */
3753
                G_tok->log_error_curtok(TCERR_REQ_INTRINS_LPAR);
3754
            }
3755
3756
            /* 
3757
             *   parse the formals - igore the names (hence 'count_only' =
3758
             *   true), and allow optional arguments 
3759
             */
3760
            parse_formal_list(TRUE, TRUE, &argc, &optargc, &varargs,
3761
                              &varargs_list, &varargs_list_local, err, 0,
3762
                              FALSE, 0);
3763
            if (*err)
3764
                return 0;
3765
3766
            /* require the closing semicolon */
3767
            if (parse_req_sem())
3768
            {
3769
                *err = TRUE;
3770
                return 0;
3771
            }
3772
3773
            /* add a global symbol table entry for the function */
3774
            bif_sym = new CTcSymBif(fn_tok.get_text(),
3775
                                    fn_tok.get_text_len(), FALSE,
3776
                                    fn_set_id, fn_set_idx, TRUE,
3777
                                    argc - optargc, argc, varargs);
3778
            global_symtab_->add_entry(bif_sym);
3779
3780
            /* count the function set entry */
3781
            ++fn_set_idx;
3782
3783
            /* done with this function */
3784
            break;
3785
3786
        default:
3787
            /* anything else is an error */
3788
            G_tok->log_error_curtok(TCERR_REQ_INTRINS_SYM);
3789
3790
            /* skip the errant token and proceed */
3791
            G_tok->next();
3792
            break;
3793
        }
3794
    }
3795
3796
    /* 
3797
     *   there's no node to return - a function set doesn't generate any
3798
     *   code, but simply adds entries to the symbol table, which we've
3799
     *   already done 
3800
     */
3801
    return 0;
3802
}
3803
3804
/*
3805
 *   Parse an 'intrinsic class' definition
3806
 */
3807
CTPNStmTop *CTcParser::parse_intrinsic_class(int *err)
3808
{
3809
    int meta_id;
3810
    int list_done;
3811
    int got_name_tok = FALSE;
3812
    CTcToken meta_tok;
3813
    CTcSymMetaclass *meta_sym = 0;
3814
    CTcSymMetaclass *old_meta_sym = 0;
3815
    
3816
    /* skip the 'class' keyword and check the class name symbol */
3817
    if (G_tok->next() == TOKT_SYM)
3818
    {
3819
        /* get the token */
3820
        meta_tok = *G_tok->copycur();
3821
3822
        /* see if it's defined yet */
3823
        old_meta_sym = (CTcSymMetaclass *)
3824
                       global_symtab_->find(meta_tok.get_text(),
3825
                                            meta_tok.get_text_len());
3826
        if (old_meta_sym != 0)
3827
        {
3828
            /* log an error */
3829
            G_tok->log_error_curtok(TCERR_REDEF_INTRINS_NAME);
3830
        }
3831
        else
3832
        {
3833
            /* note that we got the name token successfully */
3834
            got_name_tok = TRUE;
3835
        }
3836
3837
        /* move on to the next token */
3838
        G_tok->next();
3839
    }
3840
    else
3841
    {
3842
        /* 
3843
         *   note the error, then keep going, assuming the name was
3844
         *   missing but that the rest is well-formed 
3845
         */
3846
        G_tok->log_error_curtok(TCERR_REQ_INTRINS_CLASS_NAME_SYM);
3847
    }
3848
3849
    /* skip the name symbol, and check for the global name string */
3850
    if (G_tok->cur() == TOKT_SSTR)
3851
    {
3852
        /* set up the definitions if we got a valid name token */
3853
        if (got_name_tok)
3854
        {
3855
            CTcSymMetaclass *table_sym;
3856
            
3857
            /* define the new symbol */
3858
            meta_sym = new CTcSymMetaclass(meta_tok.get_text(),
3859
                                           meta_tok.get_text_len(),
3860
                                           FALSE, 0, G_cg->new_obj_id());
3861
            global_symtab_->add_entry(meta_sym);
3862
3863
            /* tell the code generator to add this metaclass */
3864
            meta_id = G_cg->find_or_add_meta(G_tok->getcur()->get_text(),
3865
                                             G_tok->getcur()->get_text_len(),
3866
                                             meta_sym);
3867
3868
            /* 
3869
             *   if the metaclass was already defined for another symbol,
3870
             *   it's an error
3871
             */
3872
            table_sym = G_cg->get_meta_sym(meta_id);
3873
            if (table_sym != meta_sym)
3874
                G_tok->log_error(TCERR_META_ALREADY_DEF,
3875
                                 (int)table_sym->get_sym_len(),
3876
                                 table_sym->get_sym());
3877
3878
            /* set the metaclass ID in the symbol */
3879
            meta_sym->set_meta_idx(meta_id);
3880
        }
3881
3882
        /* skip the name */
3883
        G_tok->next();
3884
    }
3885
    else
3886
    {
3887
        /* 
3888
         *   note the error, then keep going, assuming that the name was
3889
         *   missing but that the rest is correctly formed 
3890
         */
3891
        G_tok->log_error_curtok(TCERR_REQ_INTRINS_CLASS_NAME);
3892
    }
3893
3894
    /* check for the optional intrinsic superclass */
3895
    if (G_tok->cur() == TOKT_COLON)
3896
    {
3897
        /* skip the colon and get the symbol */
3898
        if (G_tok->next() != TOKT_SYM)
3899
        {
3900
            /* note the error, and just continue from here */
3901
            G_tok->log_error_curtok(TCERR_REQ_INTRINS_SUPERCLASS_NAME);
3902
        }
3903
        else
3904
        {
3905
            CTcSymMetaclass *sc_meta_sym;
3906
            
3907
            /* look up the superclass in the global symbols */
3908
            sc_meta_sym = (CTcSymMetaclass *)global_symtab_->find(
3909
                G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len());
3910
3911
            /* make sure we found the symbol, and that it's a metaclass */
3912
            if (sc_meta_sym == 0)
3913
            {
3914
                /* the intrinsic superclass is not defined */
3915
                G_tok->log_error_curtok(TCERR_INTRINS_SUPERCLASS_UNDEF);
3916
            }
3917
            else if (sc_meta_sym->get_type() != TC_SYM_METACLASS)
3918
            {
3919
                /* this is not an intrinsic class */
3920
                G_tok->log_error_curtok(TCERR_INTRINS_SUPERCLASS_NOT_INTRINS);
3921
            }
3922
            else
3923
            {
3924
                /* remember the supermetaclass */
3925
                if (meta_sym != 0)
3926
                    meta_sym->set_super_meta(sc_meta_sym);
3927
            }
3928
3929
            /* we're done with the name token */
3930
            G_tok->next();
3931
        }
3932
    }
3933
3934
    /* we need an open brace after that */
3935
    if (G_tok->cur() == TOKT_LBRACE)
3936
    {
3937
        /* skip the brace */
3938
        G_tok->next();
3939
    }
3940
    else
3941
    {
3942
        /* note the error, but keep going */
3943
        G_tok->log_error_curtok(TCERR_REQ_INTRINS_CLASS_LBRACE);
3944
    }
3945
3946
    /* keep going until we find the closing brace */
3947
    for (list_done = FALSE ; !list_done ; )
3948
    {
3949
        CTcToken prop_tok;
3950
        int is_static;
3951
3952
        /* presume it won't be a 'static' property */
3953
        is_static = FALSE;
3954
3955
        /* check what we have */
3956
        switch(G_tok->cur())
3957
        {
3958
        case TOKT_RBRACE:
3959
            /* closing brace - skip it, and we're done */
3960
            G_tok->next();
3961
            list_done = TRUE;
3962
            break;
3963
3964
        case TOKT_EOF:
3965
            /* end of file - log an error */
3966
            G_tok->log_error(TCERR_EOF_IN_INTRINS_CLASS);
3967
3968
            /* return failure */
3969
            *err = TRUE;
3970
            return 0;
3971
3972
        case TOKT_SEM:
3973
            /* empty list element; skip it */
3974
            G_tok->next();
3975
            break;
3976
3977
        case TOKT_STATIC:
3978
            /* note that we have a static property */
3979
            is_static = TRUE;
3980
3981
            /* a symbol must follow */
3982
            if (G_tok->next() != TOKT_SYM)
3983
            {
3984
                G_tok->log_error(TCERR_REQ_INTRINS_CLASS_PROP);
3985
                break;
3986
            }
3987
3988
            /* fall through to parse the rest of the property definition */
3989
3990
        case TOKT_SYM:
3991
            /* remember the property name */
3992
            prop_tok = *G_tok->copycur();
3993
3994
            /* add the property */
3995
            if (meta_sym != 0)
3996
                meta_sym->add_prop(prop_tok.get_text(),
3997
                                   prop_tok.get_text_len(), 0, is_static);
3998
3999
            /* skip the property name symbol */
4000
            G_tok->next();
4001
4002
            /* if there's a formal parameter list, parse it */
4003
            if (G_tok->cur() == TOKT_LPAR)
4004
            {
4005
                int argc;
4006
                int opt_argc;
4007
                int varargs;
4008
                int varargs_list;
4009
                CTcSymLocal *varargs_list_local;
4010
4011
                /* skip the open paren */
4012
                G_tok->next();
4013
                
4014
                /* parse the list, ignoring the symbols defined */
4015
                parse_formal_list(TRUE, TRUE, &argc, &opt_argc, &varargs,
4016
                                  &varargs_list, &varargs_list_local,
4017
                                  err, 0, FALSE, 0);
4018
4019
                /* if a fatal error occurred, return failure */
4020
                if (*err != 0)
4021
                    return 0;
4022
            }
4023
4024
            /* require a semicolon after the property definition */
4025
            if (parse_req_sem())
4026
            {
4027
                *err = TRUE;
4028
                return 0;
4029
            }
4030
4031
            /* done with this function */
4032
            break;
4033
4034
        default:
4035
            /* anything else is an error */
4036
            G_tok->log_error_curtok(TCERR_REQ_INTRINS_CLASS_PROP);
4037
4038
            /* skip the errant token and proceed */
4039
            G_tok->next();
4040
            break;
4041
        }
4042
    }
4043
4044
    /* 
4045
     *   there's no node to return - a metaclass definition doesn't
4046
     *   generate any code, but simply adds entries to the symbol table,
4047
     *   which we've already done 
4048
     */
4049
    return 0;
4050
}
4051
4052
/*
4053
 *   Parse an 'object' statement - this can be either an 'object template'
4054
 *   statement or an anonymous object definition for a base object.  
4055
 */
4056
CTPNStmTop *CTcParser::parse_object_stm(int *err, int trans)
4057
{
4058
    /* it's either a template definition or an anonymous object */
4059
    if (G_tok->next() == TOKT_TEMPLATE)
4060
    {
4061
        /* 
4062
         *   it's an 'object template' statement - go parse the template,
4063
         *   with no superclass token 
4064
         */
4065
        return parse_template_def(err, 0);
4066
    }
4067
    else
4068
    {
4069
        /* 
4070
         *   no 'template' token, so this is an anonymous object instance
4071
         *   definition - put back the 'object' token 
4072
         */
4073
        G_tok->unget();
4074
4075
        /* parse the anonymous object statement */
4076
        return parse_anon_object(err, 0, FALSE, 0, trans);
4077
    }
4078
}
4079
4080
/*
4081
 *   Parse an object template definition.  If the class token is null, this
4082
 *   is a root object template; otherwise, the class token gives the class
4083
 *   symbol with which to associate the template.  
4084
 */
4085
CTPNStmTop *CTcParser::parse_template_def(int *err, const CTcToken *class_tok)
4086
{
4087
    int done;
4088
    int all_ok;
4089
    CTcToken prop_tok;
4090
    CTcObjTemplateItem *alt_group_head;
4091
    CTcObjTemplateItem *item_head;
4092
    CTcObjTemplateItem *item_tail;
4093
    size_t item_cnt;
4094
    CTcSymObj *class_sym;
4095
    int found_inh;
4096
4097
    /* no items in our list yet */
4098
    item_head = item_tail = alt_group_head = 0;
4099
    item_cnt = 0;
4100
4101
    /* presume we won't find an 'inherited' token */
4102
    found_inh = FALSE;
4103
4104
    /* 
4105
     *   If there's a class token, it must refer to an object class.  If
4106
     *   there's no such symbol defined yet, define it as an external
4107
     *   object; otherwise, make sure the existing symbol is an object of
4108
     *   metaclass tads-object.  
4109
     */
4110
    if (class_tok != 0)
4111
    {
4112
        /* 
4113
         *   Look up the symbol.  Don't mark the symbol as referenced; merely
4114
         *   defining a template for an object doesn't require an external
4115
         *   reference on the object.  
4116
         */
4117
        class_sym = (CTcSymObj *)get_global_symtab()->find_noref(
4118
            class_tok->get_text(), class_tok->get_text_len(), 0);
4119
4120
        /* 
4121
         *   if we didn't find it, define it; otherwise, ensure it's defined
4122
         *   as an object 
4123
         */
4124
        if (class_sym == 0)
4125
        {
4126
            /* 
4127
             *   it's undefined, so add a forward definition by creating the
4128
             *   symbol as an external object 
4129
             */
4130
            class_sym = new CTcSymObj(class_tok->get_text(),
4131
                                      class_tok->get_text_len(), FALSE,
4132
                                      G_cg->new_obj_id(), TRUE,
4133
                                      TC_META_TADSOBJ, 0);
4134
4135
            /* add it to the master symbol table */
4136
            get_global_symtab()->add_entry(class_sym);
4137
        }
4138
        else if (class_sym->get_type() != TC_SYM_OBJ
4139
                 || class_sym->get_metaclass() != TC_META_TADSOBJ)
4140
        {
4141
            /* it's defined incorrectly - flag the error */
4142
            G_tok->log_error_curtok(TCERR_REDEF_AS_OBJ);
4143
4144
            /* forget the conflicting symbol and proceed with parsing */
4145
            class_sym = 0;
4146
        }
4147
    }
4148
    else
4149
    {
4150
        /* this is a root object template - there's no class symbol */
4151
        class_sym = 0;
4152
    }
4153
4154
    /* move on to the next token */
4155
    G_tok->next();
4156
4157
    /* keep going until we run out of template tokens */
4158
    for (done = FALSE, all_ok = TRUE ; !done ; )
4159
    {
4160
        tc_toktyp_t def_tok;
4161
        int ok;
4162
        int is_inh;
4163
        int is_alt, is_opt;
4164
4165
        /* presume we will find a valid token */
4166
        ok = TRUE;
4167
4168
        /* presume the defining token type is the current token */
4169
        def_tok = G_tok->cur();
4170
4171
        /* presume this won't be an 'inherited' token */
4172
        is_inh = FALSE;
4173
4174
        /* see what we have next */
4175
        switch(G_tok->cur())
4176
        {
4177
        case TOKT_SEM:
4178
            /* 
4179
             *   that's the end of the statement - skip the semicolon, and
4180
             *   we're done 
4181
             */
4182
            G_tok->next();
4183
            done = TRUE;
4184
            break;
4185
4186
        case TOKT_INHERITED:
4187
            /* flag that we are to inherit superclass templates */
4188
            is_inh = TRUE;
4189
            found_inh = TRUE;
4190
            break;
4191
4192
        case TOKT_SSTR:
4193
        case TOKT_DSTR:
4194
            /* string property */
4195
            {
4196
                utf8_ptr p;
4197
                size_t rem;
4198
                int valid;
4199
                tc_toktyp_t kwtok;
4200
4201
                /* this is our property name token */
4202
                prop_tok = *G_tok->copycur();
4203
4204
                /* 
4205
                 *   this is also our defining token - the actual value in
4206
                 *   object instances will use this string type 
4207
                 */
4208
                def_tok = G_tok->cur();
4209
                
4210
                /* 
4211
                 *   make sure that the contents of the string forms a
4212
                 *   valid symbol token 
4213
                 */
4214
4215
                /* set up at the start of the token */
4216
                p.set((char *)G_tok->getcur()->get_text());
4217
                rem = G_tok->getcur()->get_text_len();
4218
4219
                /* make sure the first character is valid */
4220
                valid = (rem != 0 && is_syminit(p.getch()));
4221
                
4222
                /* skip the first character */
4223
                if (rem != 0)
4224
                    p.inc(&rem);
4225
4226
                /* scan the rest of the string */
4227
                for ( ; rem != 0 && valid ; p.inc(&rem))
4228
                {
4229
                    /* 
4230
                     *   if this isn't a valid symbol character, we don't
4231
                     *   have a valid symbol 
4232
                     */
4233
                    if (!is_sym(p.getch()))
4234
                        valid = FALSE;
4235
                }
4236
4237
                /* 
4238
                 *   if the string is lexically valid as a symbol, make
4239
                 *   sure it's not a keyword - if it is, it can't be a
4240
                 *   property name 
4241
                 */
4242
                if (valid && G_tok->look_up_keyword(G_tok->getcur(), &kwtok))
4243
                    valid = FALSE;
4244
4245
                /* if the symbol isn't valid, it's an error */
4246
                if (!valid)
4247
                {
4248
                    /* log the error */
4249
                    G_tok->log_error_curtok(TCERR_OBJ_TPL_STR_REQ_PROP);
4250
4251
                    /* note the problem */
4252
                    ok = FALSE;
4253
                }
4254
            }
4255
            break;
4256
4257
        case TOKT_LBRACK:
4258
            /* list property - defining instances will use lists here */
4259
            def_tok = G_tok->cur();
4260
4261
            /* the next token must be the property name */
4262
            if (G_tok->next() != TOKT_SYM)
4263
            {
4264
                /* log an error */
4265
                G_tok->log_error(TCERR_OBJ_TPL_OP_REQ_PROP,
4266
                                 G_tok->get_op_text(def_tok),
4267
                                 (int)G_tok->getcur()->get_text_len(),
4268
                                 G_tok->getcur()->get_text());
4269
4270
                /* note that we don't have a valid token */
4271
                ok = FALSE;
4272
4273
                /* 
4274
                 *   if this token isn't ']' or something that looks like
4275
                 *   an end-of-statement token, skip it 
4276
                 */
4277
                switch(G_tok->cur())
4278
                {
4279
                case TOKT_SEM:
4280
                case TOKT_EOF:
4281
                case TOKT_LBRACE:
4282
                case TOKT_RBRACE:
4283
                    /* 
4284
                     *   don't skip any of these - they might be statement
4285
                     *   enders 
4286
                     */
4287
                    break;
4288
4289
                case TOKT_RBRACK:
4290
                    /* 
4291
                     *   they must simply have left out the property - don't
4292
                     *   skip this, since we'll skip a token shortly anyway 
4293
                     */
4294
                    break;
4295
4296
                default:
4297
                    /* skip the errant token */
4298
                    G_tok->next();
4299
                    break;
4300
                }
4301
            }
4302
            else
4303
            {
4304
                /* remember the property token */
4305
                prop_tok = *G_tok->copycur();
4306
4307
                /* require the ']' token */
4308
                if (G_tok->next() != TOKT_RBRACK)
4309
                {
4310
                    /* log the error */
4311
                    G_tok->log_error_curtok(TCERR_OBJ_TPL_REQ_RBRACK);
4312
4313
                    /* 
4314
                     *   proceed on the assumption that they simply left
4315
                     *   out the ']' 
4316
                     */
4317
                }
4318
            }
4319
            break;
4320
4321
        case TOKT_AT:
4322
        case TOKT_PLUS:
4323
        case TOKT_MINUS:
4324
        case TOKT_TIMES:
4325
        case TOKT_DIV:
4326
        case TOKT_MOD:
4327
        case TOKT_ARROW:
4328
        case TOKT_AND:
4329
        case TOKT_NOT:
4330
        case TOKT_BNOT:
4331
        case TOKT_COMMA:
4332
            /* the operator is the defining token */
4333
            def_tok = G_tok->cur();
4334
4335
            /* the next token must be the property name */
4336
            if (G_tok->next() != TOKT_SYM)
4337
            {
4338
                /* log an error */
4339
                G_tok->log_error(TCERR_OBJ_TPL_OP_REQ_PROP,
4340
                                 G_tok->get_op_text(def_tok),
4341
                                 (int)G_tok->getcur()->get_text_len(),
4342
                                 G_tok->getcur()->get_text());
4343
                
4344
                /* note that we don't have a valid token */
4345
                ok = FALSE;
4346
            }
4347
4348
            /* remember the property token */
4349
            prop_tok = *G_tok->copycur();
4350
            break;
4351
4352
        case TOKT_LBRACE:
4353
        case TOKT_RBRACE:
4354
        case TOKT_EOF:
4355
        case TOKT_OBJECT:
4356
            /* they must have left off the ';' */
4357
            G_tok->log_error_curtok(TCERR_OBJ_TPL_BAD_TOK);
4358
4359
            /* 
4360
             *   stop parsing here, assuming the statement should have
4361
             *   ended by now 
4362
             */
4363
            done = TRUE;
4364
            ok = FALSE;
4365
            break;
4366
4367
        default:
4368
            /* log an error and skip the invalid token */
4369
            G_tok->log_error_curtok(TCERR_OBJ_TPL_BAD_TOK);
4370
            G_tok->next();
4371
            ok = FALSE;
4372
            break;
4373
        }
4374
4375
        /* presume we won't find an alternative or optionality suffix */
4376
        is_opt = FALSE;
4377
        is_alt = FALSE;
4378
4379
        /* 
4380
         *   if we haven't reached the end of the statement, check to see if
4381
         *   the item is optional or an alternative 
4382
         */
4383
        if (!done)
4384
        {
4385
            /* get the next token */
4386
            G_tok->next();
4387
            
4388
            /* check to see if the item is optional */
4389
            if (G_tok->cur() == TOKT_QUESTION)
4390
            {
4391
                /* it's optional */
4392
                is_opt = TRUE;
4393
4394
                /* skip the '?' */
4395
                G_tok->next();
4396
            }
4397
4398
            /* check for an alternative */
4399
            if (G_tok->cur() == TOKT_OR)
4400
            {
4401
                /* this item is an alternative */
4402
                is_alt = TRUE;
4403
4404
                /* skip the '|' */
4405
                G_tok->next();
4406
            }
4407
        }
4408
4409
        /* 
4410
         *   If the previous item was followed by '|', we're in the same
4411
         *   alternative group with that item.  This means we're optional if
4412
         *   it's marked as optional, and all of the preceding items in our
4413
         *   group are optional if we're marked as optional.  
4414
         */
4415
        if (item_tail != 0 && item_tail->is_alt_)
4416
        {
4417
            /* 
4418
             *   if we're optional, mark all prior items in the group as
4419
             *   optional 
4420
             */
4421
            if (is_opt)
4422
            {
4423
                CTcObjTemplateItem *item;
4424
4425
                /* mark everything in our group as optional */
4426
                for (item = alt_group_head ; item != 0 ; item = item->nxt_)
4427
                    item->is_opt_ = TRUE;
4428
            }
4429
            
4430
            /* if the prior item was optional, we're optional */
4431
            if (item_tail->is_opt_)
4432
                is_opt = TRUE;
4433
        }
4434
4435
        /* 
4436
         *   if we encountered any problems, note that we have an error in
4437
         *   the overall statement 
4438
         */
4439
        if (!ok)
4440
            all_ok = FALSE;
4441
4442
        /* if we found a valid property token, add it to the template */
4443
        if (!done && ok)
4444
        {
4445
            CTcSymProp *prop_sym;
4446
            CTcObjTemplateItem *item;
4447
4448
            /* presume we won't create a new item */
4449
            item = 0;
4450
4451
            /* check to see if it's a property or an 'inherited' token */
4452
            if (is_inh)
4453
            {
4454
                /* create an 'inherited' template item */
4455
                item = new (G_prsmem)
4456
                       CTcObjTemplateItem(0, TOKT_INHERITED, FALSE, FALSE);
4457
            }
4458
            else
4459
            {
4460
                /* make sure we have a valid property name */
4461
                prop_sym = look_up_prop(&prop_tok, FALSE);
4462
                if (prop_sym == 0)
4463
                {
4464
                    /* couldn't find the property - log an error */
4465
                    G_tok->log_error(TCERR_OBJ_TPL_SYM_NOT_PROP,
4466
                                     (int)prop_tok.get_text_len(),
4467
                                     prop_tok.get_text());
4468
                }
4469
                else if (prop_sym->is_vocab())
4470
                {
4471
                    /* dictionary properties are not valid in templates */
4472
                    G_tok->log_error(TCERR_OBJ_TPL_NO_VOCAB,
4473
                                     (int)prop_sym->get_sym_len(),
4474
                                     prop_sym->get_sym());
4475
                }
4476
                else
4477
                {
4478
                    /* 
4479
                     *   Scan the list so far to ensure that this same
4480
                     *   property isn't already part of the list.  However,
4481
                     *   do allow duplicates within the run of alternatives
4482
                     *   of which the new item will be a part, since we'll
4483
                     *   only end up using one of the alternatives and hence
4484
                     *   the property will only be entered once in the
4485
                     *   property even if it appears multiple times in the
4486
                     *   run.  The run of alternatives of which we're a part
4487
                     *   is the final run in the list, since we're being
4488
                     *   added at the end of the list.  
4489
                     */
4490
                    for (item = item_head ; item != 0 ; item = item->nxt_)
4491
                    {
4492
                        /* check for a duplicate of this item's property */
4493
                        if (item->prop_ == prop_sym)
4494
                        {
4495
                            CTcObjTemplateItem *sub;
4496
                            
4497
                            /* 
4498
                             *   if everything from here to the end of the
4499
                             *   list is marked as an alternative, then we're
4500
                             *   just adding a duplicate property to a run of
4501
                             *   alternatives, which is fine 
4502
                             */
4503
                            for (sub = item ; sub != 0 && sub->is_alt_ ;
4504
                                 sub = sub->nxt_) ;
4505
4506
                            /* 
4507
                             *   if we found a non-alternative following this
4508
                             *   item, then this is indeed a duplicate; if we
4509
                             *   didn't find any non-alternatives, we're just
4510
                             *   adding this to a run of alternatives, so
4511
                             *   we're okay 
4512
                             */
4513
                            if (sub != 0)
4514
                            {
4515
                                /* it's a duplicate - log the error */
4516
                                G_tok->log_error(TCERR_OBJ_TPL_PROP_DUP,
4517
                                    (int)prop_sym->get_sym_len(),
4518
                                    prop_sym->get_sym());
4519
                            
4520
                                /* no need to look any further */
4521
                                break;
4522
                            }
4523
                        }
4524
                    }
4525
                    
4526
                    /* create the template item */
4527
                    item = new (G_prsmem)
4528
                           CTcObjTemplateItem(prop_sym, def_tok,
4529
                                              is_alt, is_opt);
4530
                }
4531
            }
4532
            
4533
            /* if we have a valid new item, add it to our list */
4534
            if (item != 0)
4535
            {
4536
                /* 
4537
                 *   if we're an alternative and the prior item was not,
4538
                 *   we're the head of a new alternative group; if we're not
4539
                 *   an alternative, then we're not in an alternative group
4540
                 *   after this item 
4541
                 */
4542
                if (is_alt && (item_tail == 0 || !item_tail->is_alt_))
4543
                {
4544
                    /* we're the head of a new group */
4545
                    alt_group_head = item;
4546
                }
4547
                else if (!is_alt)
4548
                {
4549
                    /* we're no longer in an alternative group */
4550
                    alt_group_head = 0;
4551
                }
4552
4553
                /* link it into our list */
4554
                if (item_tail != 0)
4555
                    item_tail->nxt_ = item;
4556
                else
4557
                    item_head = item;
4558
                item_tail = item;
4559
            
4560
                /* count it */
4561
                ++item_cnt;
4562
            }
4563
        }
4564
    }
4565
4566
    /* if the template is empty, warn about it */
4567
    if (item_cnt == 0)
4568
    {
4569
        /* flag it as an error */
4570
        G_tok->log_error(TCERR_TEMPLATE_EMPTY);
4571
4572
        /* there's no point in saving an empty template */
4573
        all_ok = FALSE;
4574
    }
4575
4576
    /* if we were successful, add the new template to our master list */
4577
    if (all_ok)
4578
    {
4579
        /*
4580
         *   If we found an 'inherited' keyword, we must expand the template
4581
         *   with all inherited templates.  Otherwise, simply add the
4582
         *   template exactly as given. 
4583
         */
4584
        if (found_inh)
4585
        {
4586
            /*
4587
             *   Traverse all superclass templates and add each inherited
4588
             *   form. 
4589
             */
4590
            add_inherited_templates(class_sym, item_head, item_cnt);
4591
        }
4592
        else
4593
        {
4594
            /* add our single template */
4595
            add_template_def(class_sym, item_head, item_cnt);
4596
        }
4597
    }
4598
4599
    /* an 'object template' statement generates no tree data */
4600
    return 0;
4601
}
4602
4603
/*
4604
 *   Linked list structure for inherited template list 
4605
 */
4606
struct inh_tpl_entry
4607
{
4608
    inh_tpl_entry() { }
4609
    
4610
    /* this template */
4611
    CTcObjTemplate *tpl;
4612
    
4613
    /* next in list */
4614
    inh_tpl_entry *nxt;
4615
};
4616
4617
/*
4618
 *   Expand a template definition for inherited superclass templates 
4619
 */
4620
void CTcParser::add_inherited_templates(CTcSymObj *sc_sym,
4621
                                        CTcObjTemplateItem *item_head,
4622
                                        size_t item_cnt)
4623
{
4624
    inh_tpl_entry *head;
4625
    inh_tpl_entry *tail;
4626
    inh_tpl_entry *cur;
4627
    CTcObjTemplate *tpl;
4628
    
4629
    /* start with an empty list */
4630
    head = tail = 0;
4631
4632
    /* 
4633
     *   traverse the superclass tree, building a list of all unique
4634
     *   templates inherited from the tree 
4635
     */
4636
    build_super_template_list(&head, &tail, sc_sym);
4637
4638
    /* expand our template for each item in the superclass template list */
4639
    for (cur = head ; cur != 0 ; cur = cur->nxt)
4640
        expand_and_add_inherited_template(sc_sym, item_head, cur->tpl);
4641
4642
    /* expand our template for each item in the root template list */
4643
    for (tpl = template_head_ ; tpl != 0 ; tpl = tpl->nxt_)
4644
        expand_and_add_inherited_template(sc_sym, item_head, tpl);
4645
4646
    /* 
4647
     *   finally, add the trivial expansion (in other words, with the
4648
     *   'inherited' keyword replaced by nothing) 
4649
     */
4650
    expand_and_add_inherited_template(sc_sym, item_head, 0);
4651
}
4652
4653
/*
4654
 *   Expand a template that contains an 'inherited' keyword, substituting
4655
 *   the given inherited template list for the 'inherited' keyword, and add
4656
 *   the resulting template to the template list for the given class. 
4657
 */
4658
void CTcParser::expand_and_add_inherited_template(
4659
    CTcSymObj *sc_sym, CTcObjTemplateItem *item_head,
4660
    CTcObjTemplate *sc_tpl)
4661
{
4662
    CTcObjTemplateItem *exp_head;
4663
    CTcObjTemplateItem *exp_tail;
4664
    CTcObjTemplateItem *cur;
4665
    CTcObjTemplateItem *end_nxt;
4666
    size_t cnt;
4667
4668
    /* our new list is empty thus far */
4669
    exp_head = exp_tail = 0;
4670
4671
    /*
4672
     *   Build a new list.  Copy items from the original list until we find
4673
     *   the 'inherited' item, then copy items from the inherited template,
4674
     *   and finally copy the remaining items from the original list. 
4675
     */
4676
    for (end_nxt = 0, cur = item_head, cnt = 0 ; cur != 0 ; cur = cur->nxt_)
4677
    {
4678
        CTcObjTemplateItem *new_item;
4679
        
4680
        /* if this is the 'inherited' entry, switch to the superclass list */
4681
        if (cur->tok_type_ == TOKT_INHERITED)
4682
        {
4683
            /* 
4684
             *   if we're doing a trivial expansion, indicated by a null
4685
             *   superclass template, simply skip the 'inherited' keyword
4686
             *   altogether and proceed with copying the main list
4687
             */
4688
            if (sc_tpl == 0)
4689
                continue;
4690
            
4691
            /* remember where to pick up in the containing list */
4692
            end_nxt = cur;
4693
4694
            /* switch to the inherited list */
4695
            cur = sc_tpl->items_;
4696
        }
4697
4698
        /* make a copy of the current item */
4699
        new_item = new (G_prsmem)
4700
                   CTcObjTemplateItem(cur->prop_, cur->tok_type_,
4701
                                      cur->is_alt_, cur->is_opt_);
4702
4703
        /* link it into our list */
4704
        if (exp_tail != 0)
4705
            exp_tail->nxt_ = new_item;
4706
        else
4707
            exp_head = new_item;
4708
        exp_tail = new_item;
4709
4710
        /* count the item */
4711
        ++cnt;
4712
4713
        /* 
4714
         *   if we're at the end of the current list, and we have an outer
4715
         *   list to resume, resume the outer list 
4716
         */
4717
        if (cur->nxt_ == 0 && end_nxt != 0)
4718
        {
4719
            /* resume at the outer list */
4720
            cur = end_nxt;
4721
4722
            /* forget the outer list now that we're resuming it */
4723
            end_nxt = 0;
4724
        }
4725
    }
4726
4727
    /* add the expanded template definition */
4728
    add_template_def(sc_sym, exp_head, cnt);
4729
}
4730
4731
/*
4732
 *   Build a list of inherited templates given the base class.  We add only
4733
 *   templates from superclasses of the given class.
4734
 *   
4735
 *   We add only templates that aren't already in the list, because the
4736
 *   class could conceivably inherit from the same base class more than
4737
 *   once, if it (or a superclass) inherits from multiple base classes that
4738
 *   share a common base class.  
4739
 */
4740
void CTcParser::build_super_template_list(inh_tpl_entry **list_head,
4741
                                          inh_tpl_entry **list_tail,
4742
                                          CTcSymObj *sc_sym)
4743
{
4744
    CTPNSuperclass *sc;
4745
4746
    /* if this object was defined as a subclass of 'object', we're done */
4747
    if (sc_sym->sc_is_root())
4748
        return;
4749
    
4750
    /* scan all superclasses of the given superclass */
4751
    for (sc = sc_sym->get_sc_name_head() ; sc != 0 ; sc = sc->nxt_)
4752
    {
4753
        CTcSymObj *sc_sym_cur;
4754
        CTcObjTemplate *tpl;
4755
        
4756
        /* if this item is not an object, skip it */
4757
        sc_sym_cur = (CTcSymObj *)sc->get_sym();
4758
        if (sc_sym_cur == 0 || sc_sym_cur->get_type() != TC_SYM_OBJ)
4759
            continue;
4760
4761
        /* 
4762
         *   scan this superclass's templates and add each one that's not
4763
         *   already in the list 
4764
         */
4765
        for (tpl = sc_sym_cur->get_first_template() ; tpl != 0 ;
4766
             tpl = tpl->nxt_)
4767
        {
4768
            inh_tpl_entry *entry;
4769
4770
            /* find this template in the inherited list */
4771
            for (entry = *list_head ; entry != 0 ; entry = entry->nxt)
4772
            {
4773
                /* if it's in the list, stop looking for it */
4774
                if (entry->tpl == tpl)
4775
                    break;
4776
            }
4777
4778
            /* if we didn't find this template, add it */
4779
            if (entry == 0)
4780
            {
4781
                /* create a new list item */
4782
                entry = new (G_prsmem) inh_tpl_entry();
4783
                entry->tpl = tpl;
4784
4785
                /* 
4786
                 *   Link it in at the end of the list.  Note that we want to
4787
                 *   link it at the end because this puts the template
4788
                 *   inheritance list in the same order as the superclass
4789
                 *   definitions.  Since we search the template list in
4790
                 *   order, this ensures that we find the first matching
4791
                 *   template in superclass inheritance order in cases where
4792
                 *   there are multiple superclasses.  
4793
                 */
4794
                if (*list_tail != 0)
4795
                    (*list_tail)->nxt = entry;
4796
                else
4797
                    *list_head = entry;
4798
                *list_tail = entry;
4799
                entry->nxt = 0;
4800
            }
4801
        }
4802
4803
        /* inherit from this superclass's superclasses */
4804
        build_super_template_list(list_head, list_tail, sc_sym_cur);
4805
    }
4806
}
4807
4808
/*
4809
 *   Add a template definition 
4810
 */
4811
void CTcParser::add_template_def(CTcSymObj *class_sym,
4812
                                 CTcObjTemplateItem *item_head,
4813
                                 size_t item_cnt)
4814
{
4815
    CTcObjTemplate *tpl;
4816
    
4817
    /* create the new template list entry */
4818
    tpl = new (G_prsmem) CTcObjTemplate(item_head, item_cnt);
4819
    
4820
    /* 
4821
     *   link it into the appropriate list - if it's associated with a
4822
     *   class, link it into the class symbol's list; otherwise link it into
4823
     *   the master list for the root object class 
4824
     */
4825
    if (class_sym != 0)
4826
    {
4827
        /* link it into the class's list */
4828
        class_sym->add_template(tpl);
4829
    }
4830
    else
4831
    {
4832
        /* it's for the root class - link it into the master list */
4833
        if (template_tail_ != 0)
4834
            template_tail_->nxt_ = tpl;
4835
        else
4836
            template_head_ = tpl;
4837
        template_tail_ = tpl;
4838
    }
4839
    
4840
    /* 
4841
     *   if this is the longest template so far, extend the template
4842
     *   instance expression array to make room for parsing this template's
4843
     *   instances 
4844
     */
4845
    if (item_cnt > template_expr_max_)
4846
    {
4847
        /* 
4848
         *   note the new length, rounding up to ensure that we don't have
4849
         *   to repeatedly reallocate if we have several more of roughly
4850
         *   this same length 
4851
         */
4852
        template_expr_max_ = (item_cnt + 15) & ~15;
4853
        
4854
        /* reallocate the list */
4855
        template_expr_ = (CTcObjTemplateInst *)G_prsmem->
4856
                         alloc(sizeof(template_expr_[0])
4857
                               * template_expr_max_);
4858
    }
4859
}
4860
4861
/*
4862
 *   Parse an object or function definition 
4863
 */
4864
CTPNStmTop *CTcParser::parse_object_or_func(int *err, int replace,
4865
                                            int suppress_error,
4866
                                            int *suppress_next_error)
4867
{
4868
    CTcToken init_tok;
4869
    int trans;
4870
4871
    /* if the token is 'transient', it's an object definition */
4872
    if ((trans = G_tok->cur() == TOKT_TRANSIENT) != 0)
4873
    {
4874
        /* skip the 'transient' keyword */
4875
        G_tok->next();
4876
4877
        /* 
4878
         *   if 'object' follows, treat this as an object statement;
4879
         *   otherwise, we'll process using our normal handling, nothing our
4880
         *   'transient' status 
4881
         */
4882
        if (G_tok->cur() == TOKT_OBJECT)
4883
            return parse_object_stm(err, TRUE);
4884
    }
4885
4886
    /* remember the initial token */
4887
    init_tok = *G_tok->copycur();
4888
    
4889
    /* check the next token */
4890
    switch(G_tok->next())
4891
    {
4892
    case TOKT_COLON:
4893
        /* 
4894
         *   it's an object definition - back up to the object name symbol
4895
         *   and go parse the object definition 
4896
         */
4897
        G_tok->unget();
4898
        return parse_object(err, replace, FALSE, FALSE, 0, trans);
4899
4900
    case TOKT_LPAR:
4901
        /* 'transient' isn't allowed here */
4902
        if (trans)
4903
            G_tok->log_error(TCERR_INVAL_TRANSIENT);
4904
4905
        /* 
4906
         *   it's a function definition - back up to the function name
4907
         *   symbol and go parse the function 
4908
         */
4909
        G_tok->unget();
4910
        return parse_function(err, FALSE, replace, FALSE, FALSE);
4911
4912
    case TOKT_TEMPLATE:
4913
        /* 'transient template' is an error - flag it, but proceed anyway */
4914
        if (trans)
4915
            G_tok->log_error(TCERR_INVAL_TRANSIENT);
4916
4917
        /* it's a template for a specific object - parse it */
4918
        return parse_template_def(err, &init_tok);
4919
4920
    default:
4921
        /* 
4922
         *   if the first token was a symbol that has already been defined
4923
         *   as a class or object, this must be an anonymous object
4924
         *   definition 
4925
         */
4926
        if (init_tok.gettyp() == TOKT_SYM)
4927
        {
4928
            CTcSymObj *sym;
4929
            
4930
            /* look up the symbol in the global symbol table */
4931
            sym = (CTcSymObj *)
4932
                  global_symtab_->find(init_tok.get_text(),
4933
                                       init_tok.get_text_len());
4934
4935
            /* 
4936
             *   If it's a TADS Object symbol, this must be an anonymous
4937
             *   instance definition.  If the symbol is not yet defined
4938
             *   and we're doing a syntax-only pass, assume the same
4939
             *   thing.  
4940
             */
4941
            if ((sym !=0
4942
                 && sym->get_type() == TC_SYM_OBJ
4943
                 && sym->get_metaclass() == TC_META_TADSOBJ)
4944
                || (sym == 0 && syntax_only_))
4945
            {
4946
                /*
4947
                 *   If this is a 'replace' statement, we must have a class
4948
                 *   list with the replacement object, so this is invalid. 
4949
                 */
4950
                if (replace)
4951
                {
4952
                    /* log the error, but continue parsing despite it */
4953
                    G_tok->log_error(TCERR_REPLACE_OBJ_REQ_SC);
4954
                }
4955
4956
                /* put the initial symbol back */
4957
                G_tok->unget();
4958
4959
                /* parse the anonymous object definition */
4960
                return parse_anon_object(err, 0, FALSE, 0, trans);
4961
            }
4962
        }
4963
        else if (init_tok.gettyp() == TOKT_OBJECT)
4964
        {
4965
            /* 
4966
             *   this is a base anonymous object - put the 'object' token
4967
             *   back and go parse the object definition
4968
             */
4969
            G_tok->unget();
4970
            return parse_anon_object(err, 0, FALSE, 0, trans);
4971
        }
4972
        
4973
        /* 
4974
         *   anything else is an error - log an error, unless we just
4975
         *   logged this same error on the previous token 
4976
         */
4977
        if (!suppress_error)
4978
            G_tok->log_error_curtok(TCERR_REQ_FUNC_OR_OBJ);
4979
4980
        /* 
4981
         *   suppress this same error if it occurs again on the next
4982
         *   token, since if it does we're probably just scanning past
4983
         *   tons of garbage trying to resynchronize 
4984
         */
4985
        if (suppress_next_error != 0)
4986
            *suppress_next_error = TRUE;
4987
        return 0;
4988
    }
4989
}
4990
4991
/*
4992
 *   Look up a property symbol.  If the symbol is not defined, add it.  If
4993
 *   it's defined as some other kind of symbol, we'll log an error and
4994
 *   return null. 
4995
 */
4996
CTcSymProp *CTcParser::look_up_prop(const CTcToken *tok, int show_err)
4997
{
4998
    CTcSymProp *prop;
4999
    
5000
    /* look up the symbol */
5001
    prop = (CTcSymProp *)global_symtab_->find(tok->get_text(),
5002
                                              tok->get_text_len());
5003
5004
    /* if it's already defined, make sure it's a property */
5005
    if (prop != 0)
5006
    {
5007
        /* make sure it's a property */
5008
        if (prop->get_type() != TC_SYM_PROP)
5009
        {
5010
            /* log an error if appropriate */
5011
            if (show_err)
5012
                G_tok->log_error(TCERR_REDEF_AS_PROP,
5013
                                 (int)tok->get_text_len(), tok->get_text());
5014
            
5015
            /* we can't use the symbol, so forget it */
5016
            prop = 0;
5017
        }
5018
    }
5019
    else
5020
    {
5021
        /* create the new property symbol */
5022
        prop = new CTcSymProp(tok->get_text(), tok->get_text_len(),
5023
                              FALSE, G_cg->new_prop_id());
5024
5025
        /* add it to the global symbol table */
5026
        global_symtab_->add_entry(prop);
5027
5028
        /* mark it as referenced */
5029
        prop->mark_referenced();
5030
    }
5031
5032
    /* return the property symbol */
5033
    return prop;
5034
}
5035
5036
/*
5037
 *   Parse a '+' object definition, or a '+ property' statement 
5038
 */
5039
CTPNStmTop *CTcParser::parse_plus_object(int *err)
5040
{
5041
    int cnt;
5042
    int is_class;
5043
    int anon;
5044
    int trans;
5045
    
5046
    /* count the '+' or '++' tokens */
5047
    for (cnt = 0 ; G_tok->cur() == TOKT_PLUS || G_tok->cur() == TOKT_INC ;
5048
          G_tok->next())
5049
    {
5050
        /* count it */
5051
        ++cnt;
5052
5053
        /* '++' counts twice for obvious reasons */
5054
        if (G_tok->cur() == TOKT_INC)
5055
            ++cnt;
5056
    }
5057
5058
    /* 
5059
     *   if the count is one, and the next token is 'property', it's the
5060
     *   '+' property definition 
5061
     */
5062
    if (cnt == 1 && G_tok->cur() == TOKT_PROPERTY)
5063
    {
5064
        /* skip the 'property' token and make sure we have a symbol */
5065
        if (G_tok->next() != TOKT_SYM)
5066
        {
5067
            /* log the error */
5068
            G_tok->log_error_curtok(TCERR_PLUSPROP_REQ_SYM);
5069
5070
            /* if it's anything other than a ';', skip it */
5071
            if (G_tok->cur() != TOKT_SEM)
5072
                G_tok->next();
5073
        }
5074
        else
5075
        {
5076
            /* set the property */
5077
            plus_prop_ = look_up_prop(G_tok->getcur(), TRUE);
5078
5079
            /* skip the token */
5080
            G_tok->next();
5081
        }
5082
5083
        /* require the closing semicolon */
5084
        if (parse_req_sem())
5085
        {
5086
            *err = TRUE;
5087
            return 0;
5088
        }
5089
5090
        /* done - there's no statement object for this directive */
5091
        return 0;
5092
    }
5093
5094
    /* presume it's an ordinary object */
5095
    is_class = FALSE;
5096
    trans = FALSE;
5097
    anon = FALSE;
5098
5099
    /* check for the 'class' keyword or the 'transient' keyword */
5100
    if (G_tok->cur() == TOKT_CLASS)
5101
    {
5102
        /* skip the 'class' keyword */
5103
        G_tok->next();
5104
5105
        /* note it */
5106
        is_class = TRUE;
5107
    }
5108
    else if (G_tok->cur() == TOKT_TRANSIENT)
5109
    {
5110
        /* skip the 'transient' keyword */
5111
        G_tok->next();
5112
5113
        /* note it */
5114
        trans = TRUE;
5115
    }
5116
5117
    /* 
5118
     *   check to see if the object name is followed by a colon - if not,
5119
     *   it's an anonymous object definition 
5120
     */
5121
    if (G_tok->cur() == TOKT_SYM)
5122
    {
5123
        /* check the next symbol to see if it's a colon */
5124
        anon = (G_tok->next() != TOKT_COLON);
5125
5126
        /* put back the token */
5127
        G_tok->unget();
5128
    }
5129
    else if (G_tok->cur() == TOKT_OBJECT)
5130
    {
5131
        /* it's an anonymous base object */
5132
        anon = TRUE;
5133
    }
5134
5135
    /* parse appropriately for anonymous or named object */
5136
    if (anon)
5137
    {
5138
        /* parse the anonymous object definition */
5139
        return parse_anon_object(err, cnt, FALSE, 0, trans);
5140
    }
5141
    else
5142
    {
5143
        /* parse the object definition */
5144
        return parse_object(err, FALSE, FALSE, is_class, cnt, trans);
5145
    }
5146
}
5147
5148
/*
5149
 *   Find or define an object symbol. 
5150
 */
5151
CTcSymObj *CTcParser::find_or_def_obj(const char *tok_txt, size_t tok_len,
5152
                                      int replace, int modify,
5153
                                      int *is_class,
5154
                                      CTcSymObj **mod_orig_sym,
5155
                                      CTcSymMetaclass **meta_sym,
5156
                                      int *trans)
5157
{
5158
    CTcSymbol *sym;
5159
    CTcSymObj *obj_sym;
5160
5161
    /* we don't have a modify base symbol yet */
5162
    *mod_orig_sym = 0;
5163
5164
    /* presume we won't find a modified metaclass definition */
5165
    if (meta_sym != 0)
5166
        *meta_sym = 0;
5167
5168
    /* look up the symbol to see if it's already defined */
5169
    sym = global_symtab_->find(tok_txt, tok_len);
5170
5171
    /* check for 'modify' used with an intrinsic class */
5172
    if (meta_sym != 0
5173
        && modify
5174
        && sym != 0
5175
        && sym->get_type() == TC_SYM_METACLASS)
5176
    {
5177
        CTcSymObj *old_mod_obj;
5178
5179
        /*
5180
         *   We're modifying an intrinsic class.  In this case, the base
5181
         *   object is the previous modification object for the class, or no
5182
         *   object at all.  In either case, we must create a new object and
5183
         *   assign it to the metaclass.  
5184
         */
5185
5186
        /* get the metaclass symbol */
5187
        *meta_sym = (CTcSymMetaclass *)sym;
5188
5189
        /* get the original modification object */
5190
        old_mod_obj = (*meta_sym)->get_mod_obj();
5191
5192
        /* 
5193
         *   If there is no original modification object, create one - this
5194
         *   is necessary so that there's always a dummy object at the root
5195
         *   of the modification chain in each object file.  During linking,
5196
         *   when one object file modifies one of these chains loaded from
5197
         *   another object file, we'll use the pointer to the dummy root
5198
         *   object in the second object file to point instead to the top of
5199
         *   the chain in the first object file.  
5200
         */
5201
        if (old_mod_obj == 0)
5202
        {
5203
            CTPNStmObject *base_stm;
5204
5205
            /* create an anonymous base object */
5206
            old_mod_obj = CTcSymObj::synthesize_modified_obj_sym(FALSE);
5207
5208
            /* the object is of metaclass IntrinsicClassModifier */
5209
            old_mod_obj->set_metaclass(TC_META_ICMOD);
5210
5211
            /* give the dummy base object an empty statement */
5212
            base_stm = new CTPNStmObject(old_mod_obj, FALSE);
5213
            old_mod_obj->set_obj_stm(base_stm);
5214
5215
            /* 
5216
             *   add the base statement to our nested top-level statement
5217
             *   list, so that we generate this object's code 
5218
             */
5219
            add_nested_stm(base_stm);
5220
        }
5221
5222
        /* 
5223
         *   note the superclass of the new modification object - this is
5224
         *   simply the previous modification object, which we're further
5225
         *   modifying, thus making this previous modification object the
5226
         *   base of the new modification 
5227
         */
5228
        *mod_orig_sym = old_mod_obj;
5229
5230
        /* create a symbol for the new modification object */
5231
        obj_sym = CTcSymObj::synthesize_modified_obj_sym(FALSE);
5232
5233
        /* the object is of metaclass IntrinsicClassModifier */
5234
        obj_sym->set_metaclass(TC_META_ICMOD);
5235
5236
        /* set the new object's base modified object */
5237
        obj_sym->set_mod_base_sym(old_mod_obj);
5238
5239
        /* make this the new metaclass modifier object */
5240
        (*meta_sym)->set_mod_obj(obj_sym);
5241
5242
        /* return the symbol */
5243
        return obj_sym;
5244
    }
5245
5246
    /* assume for now that the symbol is an object (we'll check shortly) */
5247
    obj_sym = (CTcSymObj *)sym;
5248
    if (obj_sym != 0)
5249
    {
5250
        /* 
5251
         *   This symbol is already defined.  The previous definition must
5252
         *   be an object, or this definition is incompatible.  If it's an
5253
         *   object, then it must have been external, *or* we must be
5254
         *   replacing or modifying the previous definition.  
5255
         */
5256
        if (obj_sym->get_type() != TC_SYM_OBJ)
5257
        {
5258
            /* 
5259
             *   it's not an object - log an error; continue parsing the
5260
             *   definition for syntax, but the definition is ultimately
5261
             *   invalid because the object name is invalid 
5262
             */
5263
            G_tok->log_error_curtok(TCERR_REDEF_AS_OBJ);
5264
5265
            /* ignore the definition */
5266
            obj_sym = 0;
5267
        }
5268
        else if ((modify || replace)
5269
                 && obj_sym->get_metaclass() != TC_META_TADSOBJ)
5270
        {
5271
            /* 
5272
             *   we can't modify or replace BigNumber instances, Dictionary
5273
             *   instances, or anything other than ordinary TADS objects 
5274
             */
5275
            G_tok->log_error(TCERR_CANNOT_MOD_OR_REP_TYPE);
5276
5277
            /* ignore the definition */
5278
            obj_sym = 0;
5279
        }
5280
        else if (modify)
5281
        {
5282
            CTPNStmObject *mod_orig_stm;
5283
            CTcSymObj *mod_sym;
5284
5285
            /* 
5286
             *   remember the original pre-modified object parse tree - we
5287
             *   might need to delete properties in the tree (for any
5288
             *   properties defined with the "replace" keyword) 
5289
             */
5290
            mod_orig_stm = obj_sym->get_obj_stm();
5291
5292
            /* 
5293
             *   We're modifying the previous definition.  The object was
5294
             *   previously defined within this translation unit, so the
5295
             *   modification can be conducted entirely within the
5296
             *   translation unit (i.e., there's no link-time work we'll
5297
             *   need to do).
5298
             *   
5299
             *   Create a new fake symbol for the original object, and
5300
             *   assign it a new ID.  We need a symbol table entry because
5301
             *   the object statement parse tree depends upon having a
5302
             *   symbol table entry, and because the symbol table entry is
5303
             *   the mechanism by which the linker can fix up the object ID
5304
             *   from object file local numbering to image file global
5305
             *   numbering.  
5306
             */
5307
            mod_sym = CTcSymObj::synthesize_modified_obj_sym(FALSE);
5308
5309
            if (mod_orig_stm != 0)
5310
            {
5311
                /* 
5312
                 *   transfer the old object definition tree to the new
5313
                 *   symbol, since the new symbol now holds the original
5314
                 *   object - the real symbol is going to have the new
5315
                 *   (modified) object definition instead 
5316
                 */
5317
                mod_sym->set_obj_stm(mod_orig_stm);
5318
                mod_orig_stm->set_obj_sym(mod_sym);
5319
5320
                /* mark the original as modified */
5321
                mod_orig_stm->set_modified(TRUE);
5322
5323
                /* keep the same class attributes as the original */
5324
                *is_class = mod_orig_stm->is_class();
5325
5326
                /* keep the same transient status as the original */
5327
                *trans = mod_orig_stm->is_transient();
5328
            }
5329
            else
5330
            {
5331
                /* 
5332
                 *   it's external - use the class and transient status of
5333
                 *   the imported symbol 
5334
                 */
5335
                *is_class = obj_sym->is_class();
5336
                *trans = obj_sym->is_transient();
5337
            }
5338
5339
            /* 
5340
             *   transfer the property deletion list from the original
5341
             *   symbol to the new symbol 
5342
             */
5343
            mod_sym->set_del_prop_head(obj_sym->get_first_del_prop());
5344
            obj_sym->set_del_prop_head(0);
5345
5346
            /* transfer the vocabulary list from the original */
5347
            mod_sym->set_vocab_head(obj_sym->get_vocab_head());
5348
            obj_sym->set_vocab_head(0);
5349
5350
            /* copy the 'transient' status to the new symbol */
5351
            if (obj_sym->is_transient())
5352
                mod_sym->set_transient();
5353
5354
            /*
5355
             *   Build a token containing the synthesized object name for
5356
             *   the pre-modified object, so that we can refer to it in the
5357
             *   superclass list of the new object.  
5358
             */
5359
            *mod_orig_sym = mod_sym;
5360
5361
            /* 
5362
             *   transfer the base 'modify' symbol to the new symbol for the
5363
             *   original object 
5364
             */
5365
            mod_sym->set_mod_base_sym(obj_sym->get_mod_base_sym());
5366
5367
            /* 
5368
             *   If we're modifying an external object, we must mark the
5369
             *   symbol as such - this will put the 'modify' flag with the
5370
             *   symbol in the object file, so that we'll apply the 'modify'
5371
             *   at link time.  
5372
             */
5373
            if (obj_sym->is_extern())
5374
            {
5375
                /* mark this symbol as modifying an external symbol */
5376
                obj_sym->set_ext_modify(TRUE);
5377
5378
                /* we're no longer external */
5379
                obj_sym->set_extern(FALSE);
5380
5381
                /* mark the synthesized original symbol as external */
5382
                mod_sym->set_extern(TRUE);
5383
            }
5384
5385
            /* remember the base symbol */
5386
            obj_sym->set_mod_base_sym(mod_sym);
5387
5388
            /* 
5389
             *   keep the original version's dictionary with the base
5390
             *   symbol, and change the modified symbol to use the
5391
             *   dictionary now active 
5392
             */
5393
            mod_sym->set_dict(obj_sym->get_dict());
5394
            obj_sym->set_dict(dict_cur_);
5395
        }
5396
        else if (obj_sym->is_extern())
5397
        {
5398
            /* 
5399
             *   it was previously defined as external, so this definition
5400
             *   is acceptable - simply remove the 'extern' flag from the
5401
             *   object symbol now that we have the real definition 
5402
             */
5403
            obj_sym->set_extern(FALSE);
5404
5405
            /* set the object's dictionary to the active dictionary */
5406
            obj_sym->set_dict(dict_cur_);
5407
5408
            /*
5409
             *   If we're replacing an external object, we must mark the
5410
             *   symbol as a replacement - this will put the 'replace' flag
5411
             *   with the symbol in the object file, so that we'll apply the
5412
             *   replacement at link time.  
5413
             */
5414
            if (replace)
5415
                obj_sym->set_ext_replace(TRUE);
5416
        }
5417
        else if (replace)
5418
        {
5419
            /* 
5420
             *   We're replacing the previous definition.  We can simply
5421
             *   discard the previous definition and replace its parse tree
5422
             *   with our own.  Mark the original object parse tree as
5423
             *   replaced, so that we do not generate any code for it.  
5424
             */
5425
            obj_sym->get_obj_stm()->set_replaced(TRUE);
5426
5427
            /*
5428
             *   Note that we do NOT mark the object as a 'replace' object
5429
             *   in the symbol table.  Because the object was previously
5430
             *   defined within this translation unit, the replacement is
5431
             *   now completed - there is no need to apply any replacement
5432
             *   from this object at link time, and in fact it would be
5433
             *   incorrect to do so.  
5434
             */
5435
        }
5436
        else
5437
        {
5438
            /* 
5439
             *   the symbol is already defined in this module, and we're not
5440
             *   replacing or modifying it -- this is an error, because we
5441
             *   can only define an object once 
5442
             */
5443
            G_tok->log_error(TCERR_OBJ_REDEF, (int)tok_len, tok_txt);
5444
5445
            /* ignore the definition */
5446
            obj_sym = 0;
5447
        }
5448
    }
5449
    else
5450
    {
5451
        /* 
5452
         *   the object is not already defined in the global symbol table --
5453
         *   create a new object symbol 
5454
         */
5455
        obj_sym = new CTcSymObj(tok_txt, tok_len, FALSE,
5456
                                G_cg->new_obj_id(), FALSE,
5457
                                TC_META_TADSOBJ, dict_cur_);
5458
5459
        /* add the object to the global symbol table */
5460
        global_symtab_->add_entry(obj_sym);
5461
5462
        /* 
5463
         *   We can't replace or modify an object that hasn't been defined
5464
         *   yet (it has to have been defined at least as an external).
5465
         *   Do not make this check in syntax-only mode, because it's not
5466
         *   important to the syntax.  
5467
         */
5468
        if (replace || modify)
5469
        {
5470
            /* only log an error in full compile mode */
5471
            if (!syntax_only_)
5472
            {
5473
                /* full compilation - log the error */
5474
                G_tok->log_error(TCERR_REPMODOBJ_UNDEF);
5475
            }
5476
            else
5477
            {
5478
                /*
5479
                 *   We're only parsing - mark this as an external replace
5480
                 *   or external modify on the assumption that the symbol
5481
                 *   will be defined externally in the actual compilation.
5482
                 *   In particular, if we're building a symbol file, we
5483
                 *   don't want to write undefined 'modify' or 'replace'
5484
                 *   symbols, because such symbols are not really defined
5485
                 *   by this module.  
5486
                 */
5487
                if (replace)
5488
                    obj_sym->set_ext_replace(TRUE);
5489
                else
5490
                    obj_sym->set_ext_modify(TRUE);
5491
            }
5492
        }
5493
    }
5494
5495
    /* mark the object as a class if appropriate */
5496
    if (*is_class && obj_sym != 0)
5497
        obj_sym->set_is_class(TRUE);
5498
5499
    /* mark the object as transient */
5500
    if (*trans && obj_sym != 0)
5501
        obj_sym->set_transient();
5502
5503
    /* return the object symbol */
5504
    return obj_sym;
5505
}
5506
5507
/*
5508
 *   Parse an object definition 
5509
 */
5510
CTPNStmTop *CTcParser::parse_object(int *err, int replace, int modify,
5511
                                    int is_class, int plus_cnt, int trans)
5512
{
5513
    CTcSymObj *obj_sym;
5514
    CTcSymObj *mod_orig_sym;
5515
    CTcSymMetaclass *meta_sym;
5516
5517
    /* find or define the symbol */
5518
    obj_sym = find_or_def_obj(G_tok->getcur()->get_text(),
5519
                              G_tok->getcur()->get_text_len(),
5520
                              replace, modify, &is_class,
5521
                              &mod_orig_sym, &meta_sym, &trans);
5522
5523
    /* skip the object name */
5524
    G_tok->next();
5525
5526
    /* parse the body */
5527
    return parse_object_body(err, obj_sym, is_class, FALSE, FALSE, FALSE,
5528
                             modify, mod_orig_sym, plus_cnt, meta_sym, 0,
5529
                             trans);
5530
}
5531
5532
/*
5533
 *   Parse an anonymous object 
5534
 */
5535
CTPNStmObject *CTcParser::parse_anon_object(int *err, int plus_cnt,
5536
                                            int is_nested,
5537
                                            tcprs_term_info *term_info,
5538
                                            int trans)
5539
{
5540
    CTcSymObj *obj_sym;
5541
    
5542
    /* create an anonymous object symbol */
5543
    obj_sym = new CTcSymObj(".anon", 5, FALSE, G_cg->new_obj_id(),
5544
                            FALSE, TC_META_TADSOBJ, 0);
5545
5546
    /* set the dictionary for the new object */
5547
    obj_sym->set_dict(dict_cur_);
5548
5549
    /* parse the object body */
5550
    return parse_object_body(err, obj_sym, FALSE, TRUE, FALSE, is_nested,
5551
                             FALSE, 0, plus_cnt, 0, term_info, trans);
5552
}
5553
5554
/* ------------------------------------------------------------------------ */
5555
/*
5556
 *   'propertyset' definition structure.  Each property set defines a
5557
 *   property pattern and an optional argument list for the properties
5558
 *   within the propertyset group.  
5559
 */
5560
struct propset_def
5561
{
5562
    /* the property name pattern */
5563
    const char *prop_pattern;
5564
    size_t prop_pattern_len;
5565
5566
    /* head of list of tokens in the parameter list */
5567
    struct propset_tok *param_tok_head;
5568
};
5569
5570
/*
5571
 *   propertyset token list entry 
5572
 */
5573
struct propset_tok
5574
{
5575
    propset_tok(const CTcToken *t)
5576
    {
5577
        /* copy the token */
5578
        this->tok = *t;
5579
5580
        /* we're not in a list yet */
5581
        nxt = 0;
5582
    }
5583
    
5584
    /* the token */
5585
    CTcToken tok;
5586
    
5587
    /* next token in the list */
5588
    propset_tok *nxt;
5589
};
5590
5591
/*
5592
 *   Token source for parsing formal parameters using property set formal
5593
 *   lists.  This retrieves tokens from a propertyset stack.  
5594
 */
5595
class propset_token_source: public CTcTokenSource
5596
{
5597
public:
5598
    propset_token_source()
5599
    {
5600
        /* nothing in our list yet */
5601
        nxt_tok = last_tok = 0;
5602
    }
5603
    
5604
    /* get the next token */
5605
    virtual const CTcToken *get_next_token()
5606
    {
5607
        /* if we have another entry in our list, retrieve it */
5608
        if (nxt_tok != 0)
5609
        {
5610
            CTcToken *ret;
5611
5612
            /* remember the token to return */
5613
            ret = &nxt_tok->tok;
5614
5615
            /* advance our internal position to the next token */
5616
            nxt_tok = nxt_tok->nxt;
5617
5618
            /* return the token */
5619
            return ret;
5620
        }
5621
        else
5622
        {
5623
            /* we have nothing more to return */
5624
            return 0;
5625
        }
5626
    }
5627
5628
    /* insert a token */
5629
    void insert_token(const CTcToken *tok)
5630
    {
5631
        propset_tok *cur;
5632
        
5633
        /* create a new link entry and initialize it */
5634
        cur = new (G_prsmem) propset_tok(tok);
5635
5636
        /* link it into our list */
5637
        if (last_tok != 0)
5638
            last_tok->nxt = cur;
5639
        else
5640
            nxt_tok = cur;
5641
        last_tok = cur;
5642
    }
5643
5644
    /* insert a token based on type */
5645
    void insert_token(tc_toktyp_t typ, const char *txt, size_t len)
5646
    {
5647
        CTcToken tok;
5648
5649
        /* set up the token object */
5650
        tok.settyp(typ);
5651
        tok.set_text(txt, len);
5652
5653
        /* insert it */
5654
        insert_token(&tok);
5655
    }
5656
5657
    /* the next token we're to retrieve */
5658
    propset_tok *nxt_tok;
5659
5660
    /* tail of our list */
5661
    propset_tok *last_tok;
5662
};
5663
5664
/* maximum propertyset nesting depth */
5665
const size_t MAX_PROPSET_DEPTH = 10;
5666
5667
/* ------------------------------------------------------------------------ */
5668
/*
5669
 *   Parse an object body 
5670
 */
5671
CTPNStmObject *CTcParser::parse_object_body(int *err, CTcSymObj *obj_sym,
5672
                                            int is_class, int is_anon,
5673
                                            int is_grammar,
5674
                                            int is_nested, int modify,
5675
                                            CTcSymObj *mod_orig_sym,
5676
                                            int plus_cnt,
5677
                                            CTcSymMetaclass *meta_sym,
5678
                                            tcprs_term_info *term_info,
5679
                                            int trans)
5680
{
5681
    CTPNStmObject *obj_stm;
5682
    CTcDictPropEntry *dict_prop;
5683
    int done;
5684
    int braces;
5685
    tcprs_term_info my_term_info;
5686
    int propset_depth;
5687
    propset_def propset_stack[MAX_PROPSET_DEPTH];
5688
    CTcSymObj *sc_sym;
5689
    int old_self_valid;
5690
5691
    /* we are not in a propertyset definition yet */
5692
    propset_depth = 0;
5693
5694
    /* 
5695
     *   If we don't have an enclosing term_info, use my own; if a valid
5696
     *   term_info was passed in, continue using the enclosing one.  We
5697
     *   always want to use the outermost term_info, because our heuristic
5698
     *   is to assume that any lack of termination applies to the first
5699
     *   object it possibly could apply to. 
5700
     */
5701
    if (term_info == 0)
5702
        term_info = &my_term_info;
5703
5704
    /* presume the object won't use braces around its property list */
5705
    braces = FALSE;
5706
5707
    /* create the object statement */
5708
    obj_stm = new CTPNStmObject(obj_sym, is_class);
5709
    if (obj_sym != 0)
5710
        obj_sym->set_obj_stm(obj_stm);
5711
5712
    /* set the 'transient' status if appropriate */
5713
    if (trans)
5714
        obj_stm->set_transient();
5715
5716
    /* remember whether 'self' was valid in the enclosing context */
5717
    old_self_valid = self_valid_;
5718
5719
    /* 'self' is valid within object definitions */
5720
    self_valid_ = TRUE;
5721
5722
    /* if it's anonymous, add it to the anonymous object list */
5723
    if (is_anon && obj_sym != 0)
5724
        add_anon_obj(obj_sym);
5725
5726
    /* 
5727
     *   set the object's line location (we must do this explicitly
5728
     *   because we're in a top-level statement and hence do not have a
5729
     *   current source position saved at the moment) 
5730
     */
5731
    obj_stm->set_source_pos(G_tok->get_last_desc(),
5732
                            G_tok->get_last_linenum());
5733
5734
    /* 
5735
     *   If we're not modifying, parse the class list.  If this is a
5736
     *   'modify' statement, there's no class list.  
5737
     */
5738
    if (!modify)
5739
    {
5740
        /* 
5741
         *   Parse and skip the colon, if required.  All objects except
5742
         *   anonymous objects and grammar rule definitions have the colon;
5743
         *   for anonymous objects and grammar definitions, the caller is
5744
         *   required to advance us to the class list before parsing the
5745
         *   object body.  
5746
         */
5747
        if (is_anon || is_grammar)
5748
        {
5749
            /* 
5750
             *   it's anonymous, or it's a grammar definition - the caller
5751
             *   will have already advanced us to the class list, so there's
5752
             *   no need to look for a colon 
5753
             */
5754
        }
5755
        else if (G_tok->cur() == TOKT_COLON)
5756
        {
5757
            /* skip the colon */
5758
            G_tok->next();
5759
        }
5760
        else
5761
        {
5762
            /* it's an error, but assume they simply left off the colon */
5763
            G_tok->log_error_curtok(TCERR_OBJDEF_REQ_COLON);
5764
        }
5765
5766
        /* 
5767
         *   Drop any existing list of superclass names from the symbol.  We
5768
         *   might have a list of names from loading the symbol file; if so,
5769
         *   forget that, and use the list defined here instead.  
5770
         */
5771
        if (obj_sym != 0)
5772
            obj_sym->clear_sc_names();
5773
        
5774
        /* parse the superclass list */
5775
        for (done = FALSE ; !done ; )
5776
        {
5777
            /* we need a symbol */
5778
            switch(G_tok->cur())
5779
            {
5780
            case TOKT_SYM:
5781
                /* 
5782
                 *   It's another superclass.  Look up the superclass
5783
                 *   symbol.  
5784
                 */
5785
                sc_sym = (CTcSymObj *)get_global_symtab()->find(
5786
                    G_tok->getcur()->get_text(),
5787
                    G_tok->getcur()->get_text_len());
5788
5789
                /* 
5790
                 *   If this symbol is defined, and it's an object, check to
5791
                 *   make sure this won't set up a circular class definition
5792
                 *   - so, make sure the base class isn't the same as the
5793
                 *   object being defined, and that it doesn't inherit from
5794
                 *   the object being defined.  
5795
                 */
5796
                if (sc_sym != 0
5797
                    && sc_sym->get_type() == TC_SYM_OBJ
5798
                    && (sc_sym == obj_sym
5799
                        || sc_sym->has_superclass(obj_sym)))
5800
                {
5801
                    /* 
5802
                     *   this is a circular class definition - complain
5803
                     *   about it and don't add it to my superclass list 
5804
                     */
5805
                    G_tok->log_error(TCERR_CIRCULAR_CLASS_DEF,
5806
                                     (int)sc_sym->get_sym_len(),
5807
                                     sc_sym->get_sym(),
5808
                                     (int)obj_sym->get_sym_len(),
5809
                                     obj_sym->get_sym());
5810
                }
5811
                else
5812
                {
5813
                    /* it's good - add the new superclass to our list */
5814
                    obj_stm->add_superclass(G_tok->getcur());
5815
5816
                    /* 
5817
                     *   add it to the symbol's superclass name list as well
5818
                     *   - we use this for keeping track of the hierarchy in
5819
                     *   the symbol file for compile-time access 
5820
                     */
5821
                    if (obj_sym != 0)
5822
                        obj_sym->add_sc_name_entry(
5823
                            G_tok->getcur()->get_text(),
5824
                            G_tok->getcur()->get_text_len());
5825
                }
5826
                                
5827
                /* skip the symbol and see what follows */
5828
                switch (G_tok->next())
5829
                {
5830
                case TOKT_COMMA:
5831
                    /* we have another superclass following */
5832
                    G_tok->next();
5833
                    break;
5834
                    
5835
                default:
5836
                    /* no more superclasses */
5837
                    done = TRUE;
5838
                    break;
5839
                }
5840
                break;
5841
                
5842
            case TOKT_OBJECT:
5843
                /* 
5844
                 *   it's a basic object definition - make sure other
5845
                 *   superclasses weren't specified 
5846
                 */
5847
                if (obj_stm->get_first_sc() != 0)
5848
                    G_tok->log_error(TCERR_OBJDEF_OBJ_NO_SC);
5849
                
5850
                /* 
5851
                 *   mark the object as having an explicit superclass of the
5852
                 *   root object class 
5853
                 */
5854
                if (obj_sym != 0)
5855
                    obj_sym->set_sc_is_root(TRUE);
5856
                
5857
                /* 
5858
                 *   skip the 'object' keyword and we're done - there's no
5859
                 *   superclass list 
5860
                 */
5861
                G_tok->next();
5862
                done = TRUE;
5863
                break;
5864
                
5865
            default:
5866
                /* premature end of the object list */
5867
                G_tok->log_error_curtok(TCERR_OBJDEF_REQ_SC);
5868
                
5869
                /* stop here */
5870
                done = TRUE;
5871
                break;
5872
            }
5873
        }
5874
    }
5875
    else
5876
    {
5877
        /*
5878
         *   This is a 'modify' statement.  The original pre-modified
5879
         *   object is our single superclass. 
5880
         *   
5881
         *   Note that we do not do anything with the superclass name list,
5882
         *   because we're reassigning the symbol representing the global
5883
         *   name of the object (obj_sym) to refer to the new modifier
5884
         *   object.  As far as the name structure of the program is
5885
         *   concerned, this object's superclass list is the same as the
5886
         *   original object's superclass list.  
5887
         */
5888
        if (mod_orig_sym != 0)
5889
            obj_stm->add_superclass(mod_orig_sym);
5890
    }
5891
5892
    /* 
5893
     *   clear the list of dictionary properties, so we can keep track of
5894
     *   which dictionary properties this object explicitly defines 
5895
     */
5896
    for (dict_prop = dict_prop_head_ ; dict_prop != 0 ;
5897
         dict_prop = dict_prop->nxt_)
5898
    {
5899
        /* mark this dictionary property as not defined for this object */
5900
        dict_prop->defined_ = FALSE;
5901
    }
5902
5903
    /*
5904
     *   If we have a '+' list, figure the object's location by finding
5905
     *   the item in the location stack at the desired depth. 
5906
     */
5907
    if (plus_cnt != 0)
5908
    {
5909
        /* 
5910
         *   if no '+' property has been defined, or they're asking for a
5911
         *   nesting level that doesn't exist, note the error and ignore the
5912
         *   '+' setting 
5913
         */
5914
        if (plus_prop_ == 0
5915
            || (size_t)plus_cnt > plus_stack_alloc_
5916
            || plus_stack_[plus_cnt - 1] == 0)
5917
        {
5918
            /* log the error */
5919
            G_tok->log_error(TCERR_PLUSOBJ_TOO_MANY);
5920
        }
5921
        else
5922
        {
5923
            CTPNStmObject *loc;
5924
            CTcConstVal cval;
5925
            CTcPrsNode *expr;
5926
            
5927
            /* 
5928
             *   find the object - the location of an object with N '+'
5929
             *   signs is the object most recently defined with N-1 '+'
5930
             *   signs 
5931
             */
5932
            loc = plus_stack_[plus_cnt - 1];
5933
5934
            /* add the '+' property value, if it's a valid object symbol */
5935
            if (loc->get_obj_sym() != 0)
5936
            {
5937
                CTPNObjProp *prop;
5938
5939
                /* set up a constant expression for the object reference */
5940
                cval.set_obj(loc->get_obj_sym()->get_obj_id(),
5941
                             loc->get_obj_sym()->get_metaclass());
5942
                expr = new CTPNConst(&cval);
5943
                
5944
                /* add the location property */
5945
                prop = obj_stm->add_prop(plus_prop_, expr, FALSE, FALSE);
5946
5947
                /* 
5948
                 *   mark it as overwritable, since it was added via the '+'
5949
                 *   notation 
5950
                 */
5951
                prop->set_overwritable();
5952
            }
5953
        }
5954
    }
5955
5956
    /*
5957
     *   Remember this object as the most recent object defined with its
5958
     *   number of '+' signs.  Only pay attention to '+' signs for top-level
5959
     *   objects - if this is a nested object, it doesn't participate in the
5960
     *   '+' mechanism at all.  Also, ignore classes defined with no '+'
5961
     *   signs - the '+' mechanism is generally only useful for instances,
5962
     *   so ignore it for classes that don't use the notation explicitly.  
5963
     */
5964
    if (!is_nested && (!is_class || plus_cnt != 0))
5965
    {
5966
        /* first, allocate more space in the '+' stack if necessary */
5967
        if ((size_t)plus_cnt >= plus_stack_alloc_)
5968
        {
5969
            size_t new_alloc;
5970
            size_t i;
5971
            
5972
            /* 
5973
             *   allocate more space - go a bit above what we need to avoid
5974
             *   having to reallocate again immediately if they a few levels
5975
             *   deeper 
5976
             */
5977
            new_alloc = plus_cnt + 16;
5978
            plus_stack_ = (CTPNStmObject **)
5979
                          t3realloc(plus_stack_,
5980
                                    new_alloc * sizeof(*plus_stack_));
5981
            
5982
            /* clear out the newly-allocated entries */
5983
            for (i = plus_stack_alloc_ ; i < new_alloc ; ++i)
5984
                plus_stack_[i] = 0;
5985
            
5986
            /* remember the new allocation size */
5987
            plus_stack_alloc_ = new_alloc;
5988
        }
5989
        
5990
        /* remember this object as the last object at its depth */
5991
        plus_stack_[plus_cnt] = obj_stm;
5992
    }
5993
5994
    /*
5995
     *   Check for an open brace.  If the object uses braces around its
5996
     *   property list, template properties can be either before the open
5997
     *   brace or just inside it. 
5998
     */
5999
    if (G_tok->cur() == TOKT_LBRACE)
6000
    {
6001
        /* skip the open brace */
6002
        G_tok->next();
6003
6004
        /* note that we're using braces */
6005
        braces = TRUE;
6006
    }
6007
6008
    /*
6009
     *   check for template syntax 
6010
     */
6011
    switch(G_tok->cur())
6012
    {
6013
    case TOKT_SSTR:
6014
    case TOKT_DSTR:
6015
    case TOKT_DSTR_START:
6016
    case TOKT_LBRACK:
6017
    case TOKT_AT:
6018
    case TOKT_PLUS:
6019
    case TOKT_MINUS:
6020
    case TOKT_TIMES:
6021
    case TOKT_DIV:
6022
    case TOKT_MOD:
6023
    case TOKT_ARROW:
6024
    case TOKT_AND:
6025
    case TOKT_NOT:
6026
    case TOKT_BNOT:
6027
    case TOKT_COMMA:
6028
        /* we have an object template */
6029
        parse_obj_template(err, obj_stm);
6030
6031
        /* if that failed, return failure */
6032
        if (*err)
6033
        {
6034
            /* restore the original 'self' validity, and return failure */
6035
            self_valid_ = old_self_valid;
6036
            return 0;
6037
        }
6038
6039
        /* proceed to parse the rest of the object body */
6040
        break;
6041
6042
    default:
6043
        /* it's not a template - proceed to the rest of the object body */
6044
        break;
6045
    }
6046
6047
    /*
6048
     *   If we didn't already find an open brace, try again now that we've
6049
     *   scanned the template properties, since template properties can
6050
     *   either immediately precede or immediately follow the open brace, if
6051
     *   present. 
6052
     */
6053
    if (!braces && G_tok->cur() == TOKT_LBRACE)
6054
    {
6055
        /* skip the open brace */
6056
        G_tok->next();
6057
6058
        /* note that we're using braces */
6059
        braces = TRUE;
6060
    }
6061
6062
    /*
6063
     *   If this is a nested object, the property list must be enclosed in
6064
     *   braces.  If we haven't found a brace by this point, it's an error.  
6065
     */
6066
    if (is_nested && !braces)
6067
    {
6068
        /* 
6069
         *   No braces - this is illegal, because a nested object requires
6070
         *   braces.  The most likely error is that the previous object was
6071
         *   not properly terminated, and this is simply a new object
6072
         *   definition.  Flag the error as a missing terminator on the
6073
         *   enclosing object.  
6074
         */
6075
        G_tcmain->log_error(term_info->desc_, term_info->linenum_,
6076
                            TC_SEV_ERROR, TCERR_UNTERM_OBJ_DEF);
6077
6078
        /* set the termination error flag for the caller */
6079
        term_info->unterm_ = TRUE;
6080
    }
6081
6082
    /*
6083
     *   If we're not a class, and we're not a 'modify', add the automatic
6084
     *   'sourceTextOrder' property, and the 'sourceTextGroup' if desired.
6085
     *   sourceTextOrder provides a source file sequence number that can be
6086
     *   used to order a set of objects into the same sequence in which they
6087
     *   appeared in the original source file, which is often useful for
6088
     *   assembling pre-initialized structures.  sourceTextGroup identifies
6089
     *   the file itself.  
6090
     */
6091
    if (!is_class && !modify)
6092
    {
6093
        CTPNObjProp *prop;
6094
        CTcConstVal cval;
6095
        CTcPrsNode *expr;
6096
        
6097
        /* set up the constant expression for the source sequence number */
6098
        cval.set_int(src_order_idx_++);
6099
        expr = new CTPNConst(&cval);
6100
6101
        /* create the new sourceTextOrder property */
6102
        prop = obj_stm->add_prop(src_order_sym_, expr, FALSE, FALSE);
6103
6104
        /* mark it as overwritable with an explicit property definition */
6105
        prop->set_overwritable();
6106
6107
        /* if we're generating sourceTextGroup properties, add it */
6108
        if (src_group_mode_)
6109
        {
6110
            /* set up the expression for the source group object */
6111
            cval.set_obj(src_group_id_, TC_META_TADSOBJ);
6112
            expr = new CTPNConst(&cval);
6113
6114
            /* create the new sourceTextGroup property */
6115
            prop = obj_stm->add_prop(src_group_sym_, expr, FALSE, FALSE);
6116
6117
            /* mark it as overwritable */
6118
            prop->set_overwritable();
6119
        }
6120
    }
6121
6122
    /*
6123
     *   Parse the property list.  Keep going until we get to the closing
6124
     *   semicolon.  
6125
     */
6126
    for (done = FALSE ; !done ; )
6127
    {
6128
        /* 
6129
         *   Remember the statement start location.  Some types of
6130
         *   property definitions result in implicit statements being
6131
         *   created, so we must record the statement start position for
6132
         *   those cases.  
6133
         */
6134
        cur_desc_ = G_tok->get_last_desc();
6135
        cur_linenum_ = G_tok->get_last_linenum();
6136
6137
        /* parse the property definition */
6138
        switch(G_tok->cur())
6139
        {
6140
        case TOKT_PROPERTYSET:
6141
            {
6142
                propset_def *cur_def;
6143
                propset_def dummy_def;
6144
                
6145
                /* 
6146
                 *   It's a property set definition.  These definitions
6147
                 *   nest; make sure we have space left in our stack.  
6148
                 */
6149
                if (propset_depth >= MAX_PROPSET_DEPTH)
6150
                {
6151
                    /* 
6152
                     *   nested too deeply - flag the error if this is the
6153
                     *   one that pushes us over the edge 
6154
                     */
6155
                    if (propset_depth == MAX_PROPSET_DEPTH)
6156
                        G_tok->log_error(TCERR_PROPSET_TOO_DEEP);
6157
                    
6158
                    /* 
6159
                     *   keep going with a syntactic parse despite the
6160
                     *   problems - increment the current depth, but use a
6161
                     *   dummy definition object, since we have no more
6162
                     *   stack entries to store the real data 
6163
                     */
6164
                    ++propset_depth;
6165
                    cur_def = &dummy_def;
6166
                }
6167
                else
6168
                {
6169
                    /* set up to use the next available stack entry */
6170
                    cur_def = &propset_stack[propset_depth];
6171
6172
                    /* consume this stack entry */
6173
                    ++propset_depth;
6174
                }
6175
                
6176
                /* skip the keyword and get the property name pattern */
6177
                if (G_tok->next() == TOKT_SSTR)
6178
                {
6179
                    int star_cnt;
6180
                    int inval_cnt;
6181
                    utf8_ptr p;
6182
                    size_t rem;
6183
                    
6184
                    /* note the pattern string */
6185
                    cur_def->prop_pattern = G_tok->getcur()->get_text();
6186
                    cur_def->prop_pattern_len =
6187
                        G_tok->getcur()->get_text_len();
6188
6189
                    /* 
6190
                     *   Validate the pattern.  It must consist of valid
6191
                     *   token characters, except for a single asterisk,
6192
                     *   which it must have.
6193
                     */
6194
                    for (star_cnt = inval_cnt = 0,
6195
                         p.set((char *)cur_def->prop_pattern),
6196
                         rem = cur_def->prop_pattern_len ;
6197
                         rem != 0 ; p.inc(&rem))
6198
                    {
6199
                        /* check this character */
6200
                        if (p.getch() == '*')
6201
                            ++star_cnt;
6202
                        else if (!is_sym(p.getch()))
6203
                            ++inval_cnt;
6204
                    }
6205
6206
                    /* 
6207
                     *   if the first character isn't an asterisk, it must
6208
                     *   be a valid initial symbol character 
6209
                     */
6210
                    if (cur_def->prop_pattern_len != 0)
6211
                    {
6212
                        wchar_t firstch;
6213
6214
                        /* get the first character */
6215
                        firstch = utf8_ptr::s_getch(cur_def->prop_pattern);
6216
6217
                        /* 
6218
                         *   if it's not a '*', it must be a valid initial
6219
                         *   token character 
6220
                         */
6221
                        if (firstch != '*' && !is_syminit(firstch))
6222
                            ++inval_cnt;
6223
                    }
6224
6225
                    /* 
6226
                     *   if it has no '*' or more than one '*', or it has
6227
                     *   invalid characters, flag it as invalid 
6228
                     */
6229
                    if (star_cnt != 1 || inval_cnt != 0)
6230
                        G_tok->log_error_curtok(TCERR_PROPSET_INVAL_PAT);
6231
                }
6232
                else
6233
                {
6234
                    /* not a string; flag an error, but try to keep going */
6235
                    G_tok->log_error_curtok(TCERR_PROPSET_REQ_STR);
6236
                    
6237
                    /* we have no pattern string */
6238
                    cur_def->prop_pattern = "";
6239
                    cur_def->prop_pattern_len = 0;
6240
                }
6241
6242
                /* we have no parameter token list yet */
6243
                cur_def->param_tok_head = 0;
6244
6245
                /* 
6246
                 *   skip the pattern string, and see if we have an argument
6247
                 *   list 
6248
                 */
6249
                if (G_tok->next() == TOKT_LPAR)
6250
                {
6251
                    propset_tok *last;
6252
                    propset_tok *cur;
6253
                    int star_cnt;
6254
6255
                    /*
6256
                     *   Current parsing state: 0=start, 1=after symbol or
6257
                     *   '*', 2=after comma, 3=done 
6258
                     */
6259
                    int state;
6260
6261
                    /* start with an empty token list */
6262
                    last = 0;
6263
                    
6264
                    /*
6265
                     *   We have a formal parameter list that is to be
6266
                     *   applied to each property in the set.  Parse tokens
6267
                     *   up to the closing paren, stashing them in our list.
6268
                     */
6269
                    for (G_tok->next(), state = 0, star_cnt = 0 ;
6270
                         state != 3 ; )
6271
                    {
6272
                        /* check the token */
6273
                        switch (G_tok->cur())
6274
                        {
6275
                        case TOKT_LBRACE:
6276
                        case TOKT_RBRACE:
6277
                        case TOKT_SEM:
6278
                            /* 
6279
                             *   A brace or semicolon - assume that the right
6280
                             *   paren was accidentally omitted.  Flag the
6281
                             *   error and consider the list to be finished. 
6282
                             */
6283
                            G_tok->log_error_curtok(
6284
                                TCERR_MISSING_RPAR_FORMAL);
6285
6286
                            /* switch to 'done' state */
6287
                            state = 3;
6288
                            break;
6289
6290
                        case TOKT_RPAR:
6291
                            /* 
6292
                             *   Right paren.  This ends the list.  If we're
6293
                             *   in state 2 ('after comma'), the paren is out
6294
                             *   of place, so flag it as an error - but still
6295
                             *   count it as ending the list.  
6296
                             */
6297
                            if (state == 2)
6298
                                G_tok->log_error(TCERR_MISSING_LAST_FORMAL);
6299
6300
                            /* skip the paren */
6301
                            G_tok->next();
6302
6303
                            /* switch to state 'done' */
6304
                            state = 3;
6305
                            break;
6306
6307
                        case TOKT_SYM:
6308
                        case TOKT_TIMES:
6309
                            /*
6310
                             *   Formal parameter name or '*'.  We can accept
6311
                             *   these in states 'start' and 'after comma' -
6312
                             *   in those states, just go add the token to
6313
                             *   the list we're gathering.  In state 'after
6314
                             *   formal', this is an error - a comma should
6315
                             *   have come between the two parameters.  
6316
                             */
6317
                            if (state == 1)
6318
                            {
6319
                                /* 
6320
                                 *   'after formal' - a comma must be
6321
                                 *   missing.  Flag the error, but then
6322
                                 *   proceed as though the comma had been
6323
                                 *   there. 
6324
                                 */
6325
                                G_tok->log_error_curtok(
6326
                                    TCERR_REQ_COMMA_FORMAL);
6327
                            }
6328
6329
                            /* switch to state 'after formal' */
6330
                            state = 1;
6331
6332
                        add_to_list:
6333
                            /* add the current token to the token list */
6334
                            cur = new (G_prsmem) propset_tok(G_tok->getcur());
6335
                            if (last != 0)
6336
                                last->nxt = cur;
6337
                            else
6338
                                cur_def->param_tok_head = cur;
6339
                            last = cur;
6340
6341
                            /* if it's a '*', count it */
6342
                            if (G_tok->cur() == TOKT_TIMES)
6343
                                ++star_cnt;
6344
6345
                            /* skip it */
6346
                            G_tok->next();
6347
                            break;
6348
6349
                        case TOKT_COMMA:
6350
                            /*
6351
                             *   In state 'after formal', a comma just goes
6352
                             *   into our token list.  A comma is invalid in
6353
                             *   any other state.  
6354
                             */
6355
                            if (state == 1)
6356
                            {
6357
                                /* switch to state 'after comma' */
6358
                                state = 2;
6359
6360
                                /* go add the token to our list */
6361
                                goto add_to_list;
6362
                            }
6363
                            else
6364
                            {
6365
                                /* otherwise, go handle as a bad token */
6366
                                goto bad_token;
6367
                            }
6368
6369
                        default:
6370
                        bad_token:
6371
                            /* generate an error according to our state */
6372
                            switch (state)
6373
                            {
6374
                            case 0:
6375
                            case 2:
6376
                                /* 
6377
                                 *   'start' or 'after comma' - we expected a
6378
                                 *   formal name 
6379
                                 */
6380
                                G_tok->log_error_curtok(TCERR_REQ_SYM_FORMAL);
6381
                                break;
6382
6383
                            case 1:
6384
                                /* 'after formal' - expected a comma */
6385
                                G_tok->log_error_curtok(
6386
                                    TCERR_REQ_COMMA_FORMAL);
6387
                                break;
6388
                            }
6389
6390
                            /* skip the token and keep going */
6391
                            G_tok->next();
6392
                            break;
6393
                        }
6394
                    }
6395
6396
                    /* make sure we have exactly one star */
6397
                    if (star_cnt != 1)
6398
                        G_tok->log_error(TCERR_PROPSET_INVAL_FORMALS);
6399
                }
6400
6401
                /* require the open brace for the set */
6402
                if (G_tok->cur() == TOKT_LBRACE)
6403
                    G_tok->next();
6404
                else
6405
                    G_tok->log_error_curtok(TCERR_PROPSET_REQ_LBRACE);
6406
            }
6407
                
6408
            /* proceed to parse the properties within */
6409
            break;
6410
            
6411
        case TOKT_SEM:
6412
            /*
6413
             *   If we're using brace notation (i.e., the property list is
6414
             *   surrounded by braces), a semicolon inside the property list
6415
             *   is ignored.  If we're using regular notation, a semicolon
6416
             *   ends the property list.  
6417
             */
6418
            if (braces || propset_depth != 0)
6419
            {
6420
                /* 
6421
                 *   we're using brace notation, or we're inside the braces
6422
                 *   of a propertyset, so semicolons are ignored 
6423
                 */
6424
            }
6425
            else
6426
            {
6427
                /* 
6428
                 *   we're using regular notation, so this semicolon
6429
                 *   terminates the property list and the object body - note
6430
                 *   that we're done parsing the object body 
6431
                 */
6432
                done = TRUE;
6433
            }
6434
6435
            /* in any case, skip the semicolon and carry on */
6436
            G_tok->next();
6437
            break;
6438
6439
        case TOKT_CLASS:
6440
        case TOKT_EXTERN:
6441
        case TOKT_MODIFY:
6442
        case TOKT_DICTIONARY:
6443
        case TOKT_PROPERTY:
6444
        case TOKT_PLUS:
6445
        case TOKT_INC:
6446
        case TOKT_INTRINSIC:
6447
        case TOKT_OBJECT:
6448
        case TOKT_GRAMMAR:
6449
        case TOKT_ENUM:
6450
            /* 
6451
             *   All of these probably indicate the start of a new
6452
             *   statement - they probably just left off the closing
6453
             *   semicolon of the current object.  Flag the error and
6454
             *   return, on the assumption that we'll find a new statement
6455
             *   starting here. 
6456
             */
6457
            G_tok->log_error_curtok(braces || propset_depth != 0
6458
                                    ? TCERR_OBJ_DEF_REQ_RBRACE
6459
                                    : TCERR_OBJ_DEF_REQ_SEM);
6460
            done = TRUE;
6461
            break;
6462
6463
        case TOKT_SYM:
6464
            /* 
6465
             *   Property definition.  For better recovery from certain
6466
             *   common errors, check a couple of scenarios.
6467
             *   
6468
             *   Look ahead one token.  If the next token is a colon, then
6469
             *   we have a nested object definition (the property is
6470
             *   assigned to a reference to an anonymous object about to be
6471
             *   defined in-line).  This could also indicate a missing
6472
             *   semicolon (or close brace) immediately before the symbol
6473
             *   we're taking as a property name - the symbol we just parsed
6474
             *   could actually be intended as the next object's name, and
6475
             *   the colon introduces its superclass list.  If the new
6476
             *   object's property list isn't enclosed in braces, this is
6477
             *   probably what happened.
6478
             *   
6479
             *   If the next token is '(', '{', or '=', it's almost
6480
             *   certainly a property definition, so proceed on that
6481
             *   assumption.
6482
             *   
6483
             *   If the next token is a symbol, we have one of two probable
6484
             *   errors.  Either we have a missing '=' in a property
6485
             *   definition, or this is a new anonymous object definition,
6486
             *   and the current object definition is missing its
6487
             *   terminating semicolon.  If the current symbol is a class
6488
             *   name, it's almost certainly a new object definition;
6489
             *   otherwise, assume it's really a property definition, and
6490
             *   we're just missing the '='.  
6491
             */
6492
6493
            /* look ahead at the next token */
6494
            switch(G_tok->next())
6495
            {
6496
            case TOKT_LPAR:
6497
            case TOKT_LBRACE:
6498
            case TOKT_EQ:
6499
                /* 
6500
                 *   It's almost certainly a property definition, no
6501
                 *   matter what the current symbol looks like.  Put back
6502
                 *   the token and proceed.  
6503
                 */
6504
                G_tok->unget();
6505
                break;
6506
6507
            case TOKT_COLON:
6508
                /* 
6509
                 *   "symbol: " - a nested object definition, or what's
6510
                 *   meant to be a new object definition, with the
6511
                 *   terminating semicolon or brace of the current object
6512
                 *   missing.  Assume for now it's correct, in which case
6513
                 *   it's a nested object definition; put back the token and
6514
                 *   proceed.  
6515
                 */
6516
                G_tok->unget();
6517
                break;
6518
6519
            case TOKT_SYM:
6520
            case TOKT_AT:
6521
            case TOKT_PLUS:
6522
            case TOKT_MINUS:
6523
            case TOKT_TIMES:
6524
            case TOKT_DIV:
6525
            case TOKT_MOD:
6526
            case TOKT_ARROW:
6527
            case TOKT_AND:
6528
            case TOKT_NOT:
6529
            case TOKT_BNOT:
6530
            case TOKT_COMMA:
6531
                {
6532
                    CTcSymbol *sym;
6533
                    
6534
                    /* 
6535
                     *   Two symbols in a row, or a symbol followed by
6536
                     *   what might be a template operator - either an
6537
                     *   equals sign was left out, or this is a new object
6538
                     *   definition.  Put back the token and check what
6539
                     *   kind of symbol we had in the first place.  
6540
                     */
6541
                    G_tok->unget();
6542
                    sym = global_symtab_
6543
                          ->find(G_tok->getcur()->get_text(),
6544
                                 G_tok->getcur()->get_text_len());
6545
6546
                    /* 
6547
                     *   if it's an object symbol, we almost certainly
6548
                     *   have a new object definition 
6549
                     */
6550
                    if (sym != 0 && sym->get_type() == TC_SYM_OBJ)
6551
                    {
6552
                        /* log the error */
6553
                        G_tok->log_error_curtok(braces || propset_depth != 0
6554
                                                ? TCERR_OBJ_DEF_REQ_RBRACE
6555
                                                : TCERR_OBJ_DEF_REQ_SEM);
6556
6557
                        /* assume the object is finished */
6558
                        done = TRUE;
6559
                    }
6560
                }
6561
                break;
6562
6563
            default:
6564
                /* 
6565
                 *   anything else is probably an error, but it's hard to
6566
                 *   guess what the error is; proceed on the assumption
6567
                 *   that it's going to be a property definition, and let
6568
                 *   the property definition parser take care of analyzing
6569
                 *   the problem 
6570
                 */
6571
                G_tok->unget();
6572
                break;
6573
            }
6574
6575
            /* 
6576
             *   if we decided that the object is finished, cancel the
6577
             *   property definition parsing 
6578
             */
6579
            if (done)
6580
                break;
6581
6582
            /* 
6583
             *   initialize my termination information object with the
6584
             *   current location (note that we don't necessarily initialize
6585
             *   the current termination info, since only the outermost one
6586
             *   matters; if ours happens to be active, it's because it's
6587
             *   the outermost) 
6588
             */
6589
            my_term_info.init(G_tok->get_last_desc(),
6590
                              G_tok->get_last_linenum());
6591
6592
            /* go parse the property definition */
6593
            parse_obj_prop(err, obj_stm, FALSE, meta_sym, term_info,
6594
                           propset_stack, propset_depth, is_nested);
6595
6596
            /* if we ran into an unrecoverable error, give up */
6597
            if (*err)
6598
            {
6599
                /* restore the 'self' validity, and return failure */
6600
                self_valid_ = old_self_valid;
6601
                return 0;
6602
            }
6603
6604
            /* 
6605
             *   if we encountered a termination error, assume the current
6606
             *   object was intended to be terminated, so stop here 
6607
             */
6608
            if (term_info->unterm_)
6609
            {
6610
                /* 
6611
                 *   we found what looks like the end of the object within
6612
                 *   the property parser - assume we're done with this
6613
                 *   object 
6614
                 */
6615
                done = TRUE;
6616
            }
6617
6618
            /* done */
6619
            break;
6620
6621
        case TOKT_REPLACE:
6622
            /* 'replace' is only allowed with 'modify' objects */
6623
            if (!modify)
6624
                G_tok->log_error(TCERR_REPLACE_PROP_REQ_MOD_OBJ);
6625
            
6626
            /* skip the 'replace' keyword */
6627
            G_tok->next();
6628
6629
            /* 
6630
             *   initialize my termination information object, in case it's
6631
             *   the active (i.e., outermost) one 
6632
             */
6633
            my_term_info.init(G_tok->get_last_desc(),
6634
                              G_tok->get_last_linenum());
6635
6636
            /* parse the property */
6637
            parse_obj_prop(err, obj_stm, TRUE, meta_sym, term_info,
6638
                           propset_stack, propset_depth, is_nested);
6639
6640
            /* if we ran into an unrecoverable error, give up */
6641
            if (*err)
6642
            {
6643
                /* restore the old 'self' validity, and return failure */
6644
                self_valid_ = old_self_valid;
6645
                return 0;
6646
            }
6647
6648
            /* 
6649
             *   if we encountered a termination error, assume the current
6650
             *   object was intended to be terminated, so stop here 
6651
             */
6652
            if (term_info->unterm_)
6653
            {
6654
                /* 
6655
                 *   we found what looks like the end of the object within
6656
                 *   the property parser - assume we're done with this
6657
                 *   object 
6658
                 */
6659
                done = TRUE;
6660
            }
6661
6662
            /* done */
6663
            break;
6664
6665
        case TOKT_RBRACE:
6666
            /*
6667
             *   If we're in a 'propertyset' definition, this ends the
6668
             *   current propertyset.  If the property list is enclosed in
6669
             *   braces, this brace terminates the property list and the
6670
             *   object body.  Otherwise, it's an error.  
6671
             */
6672
            if (propset_depth != 0)
6673
            {
6674
                /* pop a propertyset level */
6675
                --propset_depth;
6676
6677
                /* 
6678
                 *   skip the brace, and proceed with parsing the rest of
6679
                 *   the object
6680
                 */
6681
                G_tok->next();
6682
            }
6683
            else if (braces)
6684
            {
6685
                /* we're done with the object body */
6686
                done = TRUE;
6687
6688
                /* skip the brace */
6689
                G_tok->next();
6690
            }
6691
            else
6692
            {
6693
                /* 
6694
                 *   this property list isn't enclosed in braces, so this is
6695
                 *   completely unexpected - treat it the same as any other
6696
                 *   invalid token 
6697
                 */
6698
                goto inval_token;
6699
            }
6700
            break;
6701
6702
        case TOKT_EOF:
6703
        default:
6704
        inval_token:
6705
            /* anything else is invalid */
6706
            G_tok->log_error(TCERR_OBJDEF_REQ_PROP,
6707
                             (int)G_tok->getcur()->get_text_len(),
6708
                             G_tok->getcur()->get_text());
6709
6710
            /* skip the errant token and continue */
6711
            G_tok->next();
6712
6713
            /* if we're at EOF, abort */
6714
            if (G_tok->cur() == TOKT_EOF)
6715
            {
6716
                *err = TRUE;
6717
                self_valid_ = old_self_valid;
6718
                return 0;
6719
            }
6720
            break;
6721
        }
6722
    }
6723
6724
    /* 
6725
     *   Finish the object by adding dictionary property placeholders.  Do
6726
     *   not add the placeholders if this is an intrinsic class modifier,
6727
     *   since intrinsic class objects cannot have dictionary associations.  
6728
     */
6729
    if (obj_stm != 0 && meta_sym == 0)
6730
    {
6731
        /* 
6732
         *   Add a placeholder for each dictionary property we didn't
6733
         *   explicitly define for the object.  This will ensure that we
6734
         *   have sufficient property slots at link time if the object
6735
         *   inherits vocabulary from its superclasses.  (The added slots
6736
         *   will be removed at link time if not used at that point, so this
6737
         *   will not affect run-time efficiency or the ultimate size of the
6738
         *   image file.)  
6739
         */
6740
        for (dict_prop = dict_prop_head_ ; dict_prop != 0 ;
6741
             dict_prop = dict_prop->nxt_)
6742
        {
6743
            /* if this one isn't defined, add a placeholder */
6744
            if (!dict_prop->defined_)
6745
            {
6746
                CTcConstVal cval;
6747
                CTcPrsNode *expr;
6748
                
6749
                /* set up a VOCAB_LIST value */
6750
                cval.set_vocab_list();
6751
                expr = new CTPNConst(&cval);
6752
6753
                /* add it */
6754
                obj_stm->add_prop(dict_prop->prop_, expr, FALSE, FALSE);
6755
            }
6756
        }
6757
    }
6758
6759
    /* restore the enclosing 'self' validity */
6760
    self_valid_ = old_self_valid;
6761
6762
    /* return the object statement node */
6763
    return obj_stm;
6764
}
6765
6766
/*
6767
 *   Parse an object template instance at the beginning of an object body 
6768
 */
6769
void CTcParser::parse_obj_template(int *err, CTPNStmObject *obj_stm)
6770
{
6771
    size_t cnt;
6772
    CTcObjTemplateInst *p;
6773
    const CTcObjTemplate *tpl;
6774
    int done;
6775
    int undesc_class;
6776
    CTcPrsPropExprSave save_info;
6777
    const CTPNSuperclass *def_sc;
6778
6779
    /* parse the expressions until we reach the end of the template */
6780
    for (cnt = 0, p = template_expr_, done = FALSE ; !done ; ++cnt)
6781
    {
6782
        /* 
6783
         *   remember the statment start location, in case we have a
6784
         *   template element that generates code (such as a double-quoted
6785
         *   string with an embedded expression) 
6786
         */
6787
        cur_desc_ = G_tok->get_last_desc();
6788
        cur_linenum_ = G_tok->get_last_linenum();
6789
6790
        /* 
6791
         *   note the token, so that we can figure out which template we
6792
         *   are using 
6793
         */
6794
        p->def_tok_ = G_tok->cur();
6795
6796
        /* assume this will also be the first token of the value expression */
6797
        p->expr_tok_ = *G_tok->copycur();
6798
6799
        /* we don't have any expression yet */
6800
        p->expr_ = 0;
6801
        p->code_body_ = 0;
6802
6803
        /* prepare to parse a property value expression */
6804
        begin_prop_expr(&save_info);
6805
6806
        /* check to see if this is another template item */
6807
        switch(G_tok->cur())
6808
        {
6809
        case TOKT_SSTR:
6810
            /* single-quoted string - parse just the string */
6811
            p->expr_ = CTcPrsOpUnary::parse_primary();
6812
            break;
6813
6814
        case TOKT_DSTR:
6815
            /* string - parse it */
6816
            p->expr_ = parse_expr_or_dstr(TRUE);
6817
            break;
6818
6819
        case TOKT_DSTR_START:
6820
            /* it's a single-quoted string - parse it */
6821
            p->expr_ = parse_expr_or_dstr(TRUE);
6822
6823
            /* 
6824
             *   treat it as a regular double-quoted string for the
6825
             *   purposes of matching the template 
6826
             */
6827
            p->def_tok_ = TOKT_DSTR;
6828
            break;
6829
            
6830
        case TOKT_LBRACK:
6831
            /* it's a list */
6832
            p->expr_ = CTcPrsOpUnary::parse_list();
6833
            break;
6834
            
6835
        case TOKT_AT:
6836
        case TOKT_PLUS:
6837
        case TOKT_MINUS:
6838
        case TOKT_TIMES:
6839
        case TOKT_DIV:
6840
        case TOKT_MOD:
6841
        case TOKT_ARROW:
6842
        case TOKT_AND:
6843
        case TOKT_NOT:
6844
        case TOKT_BNOT:
6845
        case TOKT_COMMA:
6846
            /* skip the operator token */
6847
            G_tok->next();
6848
6849
            /* the value expression starts with this token */
6850
            p->expr_tok_ = *G_tok->copycur();
6851
6852
            /* a primary expression must follow */
6853
            p->expr_ = CTcPrsOpUnary::parse_primary();
6854
            break;
6855
6856
        case TOKT_EOF:
6857
            /* end of file - return and let the caller deal with it */
6858
            return;
6859
6860
        default:
6861
            /* anything else ends the template list */
6862
            done = TRUE;
6863
6864
            /* don't count this item after all */
6865
            --cnt;
6866
            break;
6867
        }
6868
6869
        /* 
6870
         *   check for embedded anonymous functions, and wrap the expression
6871
         *   in a code body if necessary 
6872
         */
6873
        p->code_body_ = finish_prop_expr(&save_info, p->expr_, FALSE, 0);
6874
6875
        /* if we wrapped it in a code body, discard the naked expression */
6876
        if (p->code_body_ != 0)
6877
            p->expr_ = 0;
6878
6879
        /* 
6880
         *   move on to the next expression slot if we have room (if we
6881
         *   don't, we won't match anything anyway; just keep writing over
6882
         *   the last slot so that we can at least keep parsing entries) 
6883
         */
6884
        if (cnt + 1 < template_expr_max_)
6885
            ++p;
6886
    }
6887
6888
    /* we have no matching template yet */
6889
    tpl = 0;
6890
    def_sc = 0;
6891
6892
    /* presume we don't have any undescribed classes in our hierarchy */
6893
    undesc_class = FALSE;
6894
6895
    /*
6896
     *   Search for the template, using the normal inheritance rules that we
6897
     *   use at run-time: start with the first superclass and look for a
6898
     *   match; if we find a match, look at subsequent superclasses to look
6899
     *   for one that overrides the match.  
6900
     */
6901
    if (obj_stm != 0)
6902
    {
6903
        /* search our superclasses for a match */
6904
        tpl = find_class_template(obj_stm->get_first_sc(),
6905
                                  template_expr_, cnt, &def_sc,
6906
                                  &undesc_class);
6907
6908
        /* remember the 'undescribed class' status */
6909
        obj_stm->set_undesc_sc(undesc_class);
6910
    }
6911
6912
    /* if we didn't find a match, look for a root object match */
6913
    if (tpl == 0 && !undesc_class)
6914
        tpl = find_template_match(template_head_, template_expr_, cnt);
6915
6916
    /* if we didn't find a match, it's an error */
6917
    if (tpl == 0)
6918
    {
6919
        /*
6920
         *   Note the error, but don't report it yet.  It might be that we
6921
         *   failed to find a template match simply because one of our
6922
         *   superclass names was misspelled.  If that's the case, then the
6923
         *   missing template is the least of our problems, and it's not
6924
         *   worth reporting since it's probably just a side effect of the
6925
         *   missing superclass (that is, once the superclass misspelling is
6926
         *   corrected and the code is re-compiled, we might find that the
6927
         *   template is correct after all, since we'll know which class to
6928
         *   scan for the needed template.)  At code generation time, we'll
6929
         *   be able to resolve the superclasses and find out what's really
6930
         *   going on, so that we can flag the appropriate error.  
6931
         */
6932
        if (obj_stm != 0)
6933
            obj_stm->note_bad_template(TRUE);
6934
        
6935
        /* ignore the template instance */
6936
        return;
6937
    }
6938
6939
    /* if there's no object statement, there's nothing left to do */
6940
    if (obj_stm == 0)
6941
        return;
6942
6943
    /* 
6944
     *   we know we have a matching template, so populate our actual
6945
     *   parameter list with the property identifiers for the matching
6946
     *   template 
6947
     */
6948
    match_template(tpl->items_, template_expr_, cnt);
6949
6950
    /* define the property values according to the template */
6951
    for (p = template_expr_ ; cnt != 0 ; ++p, --cnt)
6952
    {
6953
        /* add this property */
6954
        if (p->expr_ != 0)
6955
            obj_stm->add_prop(p->prop_, p->expr_, FALSE, FALSE);
6956
        else
6957
            obj_stm->add_method(p->prop_, p->code_body_, FALSE);
6958
    }
6959
}
6960
6961
/*
6962
 *   search a class for a template match 
6963
 */
6964
const CTcObjTemplate *CTcParser::
6965
   find_class_template(const CTPNSuperclass *first_sc,
6966
                       CTcObjTemplateInst *src, size_t src_cnt,
6967
                       const CTPNSuperclass **def_sc,
6968
                       int *undesc_class)
6969
{
6970
    const CTPNSuperclass *sc;
6971
    const CTcObjTemplate *tpl;
6972
6973
    /* scan each superclass in the list for a match */
6974
    for (tpl = 0, sc = first_sc ; sc != 0 ; sc = sc->nxt_)
6975
    {
6976
        const CTPNSuperclass *cur_def_sc;
6977
        const CTcObjTemplate *cur_tpl;
6978
        CTcSymObj *sc_sym;
6979
6980
        /* find the symbol for this superclass */
6981
        sc_sym = (CTcSymObj *)get_global_symtab()->find(
6982
            sc->get_sym_txt(), sc->get_sym_len());
6983
6984
        /* if there's no symbol, or it's not a tads-object, give up */
6985
        if (sc_sym == 0
6986
            || sc_sym->get_type() != TC_SYM_OBJ
6987
            || sc_sym->get_metaclass() != TC_META_TADSOBJ)
6988
        {
6989
            /* 
6990
             *   this class has an invalid superclass - just give up without
6991
             *   issuing any errors now, since we'll have plenty to say
6992
             *   about this when building the object file data 
6993
             */
6994
            return 0;
6995
        }
6996
6997
        /* find a match in this superclass hierarchy */
6998
        cur_tpl = find_template_match(sc_sym->get_first_template(),
6999
                                      src, src_cnt);
7000
7001
        /* see what we found */
7002
        if (cur_tpl != 0)
7003
        {
7004
            /* we found it - note the current defining superclass */
7005
            cur_def_sc = sc;
7006
        }
7007
        else
7008
        {
7009
            /* 
7010
             *   If this one has no superclass list, and it's not explicitly
7011
             *   a subclass of the root class, then this is an undescribed
7012
             *   class and cannot be used with templates at all.  A class is
7013
             *   undescribed when it is explicitly declared as 'extern', and
7014
             *   does not have a definition in any imported symbol file in
7015
             *   the current compilation.  If this is the case, flag it so
7016
             *   the caller will know we have an undescribed class.
7017
             *   
7018
             *   Note that we only set this flag if we failed to find a
7019
             *   template.  A template can still be used if a matching
7020
             *   template is explicitly defined on the class in this
7021
             *   compilation unit, since in that case we don't need to look
7022
             *   up the inheritance hierarchy for the class.  That's why we
7023
             *   set the flag here, only after we have failed to find a
7024
             *   template for the object.  
7025
             */
7026
            if (sc_sym->get_sc_name_head() == 0 && !sc_sym->sc_is_root())
7027
            {
7028
                /* tell the caller we have an undescribed class */
7029
                *undesc_class = TRUE;
7030
7031
                /* 
7032
                 *   there's no need to look any further, since any matches
7033
                 *   we might find among our other superclasses would be in
7034
                 *   doubt because of the lack of information about this
7035
                 *   earlier class, which might override later superclasses
7036
                 *   if we knew more about it 
7037
                 */
7038
                return 0;
7039
            }
7040
7041
            /* we didn't find it - search superclasses of this class */
7042
            cur_tpl = find_class_template(sc_sym->get_sc_name_head(),
7043
                                          src, src_cnt, &cur_def_sc,
7044
                                          undesc_class);
7045
7046
            /* 
7047
             *   if we have an undescribed class among our superclasses,
7048
             *   we're implicitly undescribed as well - if that's the case,
7049
             *   there's no need to look any further, so return failure 
7050
             */
7051
            if (*undesc_class)
7052
                return 0;
7053
        }
7054
7055
        /* if we found a match, see if we want to keep it */
7056
        if (cur_tpl != 0)
7057
        {
7058
            /* 
7059
             *   if this is our first match, note it; if it's not, see if it
7060
             *   overrides the previous match 
7061
             */
7062
            if (tpl == 0)
7063
            {
7064
                /* this is the first match - definitely keep it */
7065
                tpl = cur_tpl;
7066
                *def_sc = cur_def_sc;
7067
            }
7068
            else
7069
            {
7070
                /* 
7071
                 *   if the current source object descends from the previous
7072
                 *   source object, this definition overrides the previous
7073
                 *   definition, so keep it rather than the last one 
7074
                 */
7075
                if (cur_def_sc->is_subclass_of(*def_sc))
7076
                {
7077
                    /* it overrides the previous one - keep the new one */
7078
                    tpl = cur_tpl;
7079
                    *def_sc = cur_def_sc;
7080
                }
7081
            }
7082
        }
7083
    }
7084
7085
    /* return the best match we found */
7086
    return tpl;
7087
}
7088
7089
/*
7090
 *   Find a matching template in the given template list 
7091
 */
7092
const CTcObjTemplate *CTcParser::
7093
   find_template_match(const CTcObjTemplate *first_tpl,
7094
                       CTcObjTemplateInst *src, size_t src_cnt)
7095
{
7096
    const CTcObjTemplate *tpl;
7097
7098
    /* find the matching template */
7099
    for (tpl = first_tpl ; tpl != 0 ; tpl = tpl->nxt_)
7100
    {
7101
        /* check for a match */
7102
        if (match_template(tpl->items_, src, src_cnt))
7103
        {
7104
            /* it's a match - return this template */
7105
            return tpl;
7106
        }
7107
    }
7108
7109
    /* we didn't find a match */
7110
    return 0;
7111
}
7112
7113
/*
7114
 *   Match a template to an actual template parameter list.  
7115
 */
7116
int CTcParser::match_template(const CTcObjTemplateItem *tpl_head,
7117
                              CTcObjTemplateInst *src, size_t src_cnt)
7118
{
7119
    CTcObjTemplateInst *p;
7120
    const CTcObjTemplateItem *item;
7121
    size_t rem;
7122
7123
    /* check each element of the list */
7124
    for (p = src, rem = src_cnt, item = tpl_head ; item != 0 && rem != 0 ;
7125
         item = item->nxt_)
7126
    {
7127
        int match;
7128
        int is_opt;
7129
7130
        /* 
7131
         *   Note whether or not this item is optional.  Every element of an
7132
         *   alternative group must have the same optional status, so we need
7133
         *   only note the status of the first item if this is a group. 
7134
         */
7135
        is_opt = item->is_opt_;
7136
7137
        /* 
7138
         *   Scan each alternative in the current group.  Note that if we're
7139
         *   not in an alternative group, the logic is the same: we won't
7140
         *   have any 'alt' flags, so we'll just scan a single item. 
7141
         */
7142
        for (match = FALSE ; ; item = item->nxt_)
7143
        {
7144
            /* if this one matches, note the match */
7145
            if (item->tok_type_ == p->def_tok_)
7146
            {
7147
                /* note the match */
7148
                match = TRUE;
7149
                
7150
                /* this is the property to assign for the actual */
7151
                p->prop_ = item->prop_;
7152
            }
7153
7154
            /* 
7155
             *   If this one is not marked as an alternative, we're done.
7156
             *   The last item of an alternative group is identified by
7157
             *   having its 'alt' flag cleared.  Also, if we somehow have an
7158
             *   ill-formed list, where we don't have a terminating
7159
             *   non-flagged item, we can stop now as well.  
7160
             */
7161
            if (!item->is_alt_ || item->nxt_ == 0)
7162
                break;
7163
        }
7164
7165
        /* check to see if the current item is optional */
7166
        if (is_opt)
7167
        {
7168
            /* 
7169
             *   The item is optional.  If it matches, try it both ways:
7170
             *   first try matching the item, then try skipping it.  If we
7171
             *   can match this item and still match the rest of the string,
7172
             *   take that interpretation; otherwise, if we can skip this
7173
             *   item and match the rest of the string, take *that*
7174
             *   interpretation.  If we can't match it either way, we don't
7175
             *   have a match.  
7176
             *   
7177
             *   First, check to see if we can match the item and still match
7178
             *   the rest of the string.  
7179
             */
7180
            if (match && match_template(item->nxt_, p + 1, rem - 1))
7181
            {
7182
                /* we have a match */
7183
                return TRUE;
7184
            }
7185
7186
            /* 
7187
             *   Matching this optional item doesn't let us match the rest of
7188
             *   the string, so try it with this optional item omitted - in
7189
             *   other words, just match the rest of the string, including
7190
             *   the current source item, to the rest of the template,
7191
             *   *excluding* the current optional item.
7192
             *   
7193
             *   There's no need to recurse to do this; simply continue
7194
             *   iterating, but do NOT skip the current source item.  
7195
             */
7196
        }
7197
        else
7198
        {
7199
            /* 
7200
             *   It's not optional, so if it doesn't match, the whole
7201
             *   template fails to match; if it does match, simply proceed
7202
             *   through the rest of the template.  
7203
             */
7204
            if (!match)
7205
                return FALSE;
7206
7207
            /* we matched, so consume this source item */
7208
            ++p;
7209
            --rem;
7210
        }
7211
    }
7212
7213
    /* skip any trailing optional items in the template list */
7214
    while (item != 0 && item->is_opt_)
7215
        item = item->nxt_;
7216
    
7217
    /* 
7218
     *   it's a match if and only if we reached the end of both lists at the
7219
     *   same time 
7220
     */
7221
    return (item == 0 && rem == 0);
7222
}
7223
7224
7225
/*
7226
 *   Parse a property definition within an object definition.
7227
 *   
7228
 *   'obj_is_nested' indicates that the enclosing object is a nested object.
7229
 *   This tells us that we can't allow the lexicalParent property to be
7230
 *   manually defined, since we will automatically define it explicitly for
7231
 *   the enclosing object by virtue of its being nested.  
7232
 */
7233
void CTcParser::parse_obj_prop(int *err, CTPNStmObject *obj_stm,
7234
                               int replace, CTcSymMetaclass *meta_sym,
7235
                               tcprs_term_info *term_info,
7236
                               propset_def *propset_stack, int propset_depth,
7237
                               int obj_is_nested)
7238
{
7239
    CTcToken prop_tok;
7240
    CTPNCodeBody *code_body;
7241
    CTcPrsNode *expr;
7242
    int argc;
7243
    int varargs;
7244
    int varargs_list;
7245
    CTcSymLocal *varargs_list_local;
7246
    int has_retval;
7247
    CTcSymProp *prop_sym;
7248
    CTPNObjProp *obj_prop;
7249
    int is_static;
7250
    CTcSymbol *sym;
7251
7252
    /* presume it's not a static property definition */
7253
    is_static = FALSE;
7254
    
7255
    /* remember the property token */
7256
    prop_tok = *G_tok->copycur();
7257
7258
    /* 
7259
     *   if we're in a property set, apply the property set pattern to the
7260
     *   property name 
7261
     */
7262
    if (propset_depth != 0)
7263
    {
7264
        char expbuf[TOK_SYM_MAX_LEN];
7265
        size_t explen;
7266
        propset_def *cur;
7267
        int i;
7268
        const char *newstr;
7269
        
7270
        /*
7271
         *   Build the real property name based on all enclosing propertyset
7272
         *   pattern strings.  Start with the current (innermost) pattern,
7273
         *   then apply the next pattern out, and so on.
7274
         *   
7275
         *   Note that if the current nesting depth is greater than the
7276
         *   maximum nesting depth, ignore the illegally deep levels and
7277
         *   just start with the deepest legal level.  
7278
         */
7279
        i = propset_depth;
7280
        if (i > MAX_PROPSET_DEPTH)
7281
            i = MAX_PROPSET_DEPTH;
7282
        
7283
        /* start with the current token */
7284
        explen = G_tok->getcur()->get_text_len();
7285
        memcpy(expbuf, G_tok->getcur()->get_text(), explen);
7286
        
7287
        /* iterate through the propertyset stack */
7288
        for (cur = &propset_stack[i-1] ; i > 0 ; --i, --cur)
7289
        {
7290
            char tmpbuf[TOK_SYM_MAX_LEN];
7291
            utf8_ptr src;
7292
            utf8_ptr dst;
7293
            size_t rem;
7294
7295
            /* if we'd exceed the maximum token length, stop here */
7296
            if (explen + cur->prop_pattern_len > TOK_SYM_MAX_LEN)
7297
            {
7298
                /* complain about it */
7299
                G_tok->log_error(TCERR_PROPSET_TOK_TOO_LONG);
7300
                
7301
                /* stop the expansion */
7302
                break;
7303
            }
7304
            
7305
            /* copy the pattern up to the '*' */
7306
            for (src.set((char *)cur->prop_pattern),
7307
                 dst.set(tmpbuf), rem = cur->prop_pattern_len ;
7308
                 rem != 0 && src.getch() != '*' ; src.inc(&rem))
7309
            {
7310
                /* copy this character */
7311
                dst.setch(src.getch());
7312
            }
7313
            
7314
            /* if we found a '*', skip it */
7315
            if (rem != 0 && src.getch() == '*')
7316
                src.inc(&rem);
7317
            
7318
            /* insert the expansion from the last round here */
7319
            memcpy(dst.getptr(), expbuf, explen);
7320
            
7321
            /* advance our output pointer */
7322
            dst.set(dst.getptr() + explen);
7323
            
7324
            /* copy the remainder of the pattern string */
7325
            if (rem != 0)
7326
            {
7327
                /* copy the remaining bytes */
7328
                memcpy(dst.getptr(), src.getptr(), rem);
7329
7330
                /* advance the output pointer past the copied bytes */
7331
                dst.set(dst.getptr() + rem);
7332
            }
7333
            
7334
            /* copy the result back to the expansion buffer */
7335
            explen = dst.getptr() - tmpbuf;
7336
            memcpy(expbuf, tmpbuf, explen);
7337
        }
7338
7339
        /* store the new token in tokenizer memory */
7340
        newstr = G_tok->store_source(expbuf, explen);
7341
7342
        /* set the new text in the property token */
7343
        prop_tok.set_text(newstr, explen);
7344
    }
7345
7346
    /* presume we won't find a valid symbol for the property token */
7347
    prop_sym = 0;
7348
7349
    /* 
7350
     *   look up the property token as a symbol, to see if it's already
7351
     *   defined - we don't want to presume it's a property quite yet, but
7352
     *   we can at least look to see if it's defined as something else 
7353
     */
7354
    sym = global_symtab_->find(prop_tok.get_text(),
7355
                               prop_tok.get_text_len());
7356
7357
    /* make sure that the object doesn't already define this propery */
7358
    if (sym != 0)
7359
    {
7360
        int is_dup;
7361
7362
        /* presume it's not a duplicate */
7363
        is_dup = FALSE;
7364
        
7365
        /* 
7366
         *   if the enclosing object is nested, and this is the
7367
         *   lexicalParent property, then it's defined automatically by the
7368
         *   compiler and thus can't be redefined explicitly
7369
         */
7370
        if (obj_is_nested && sym == lexical_parent_sym_)
7371
            is_dup = TRUE;
7372
7373
        /* if we didn't already find a duplicate, scan the existing list */
7374
        if (!is_dup)
7375
        {
7376
            /* scan the property list for this object */
7377
            for (obj_prop = obj_stm->get_first_prop() ; obj_prop != 0 ;
7378
                 obj_prop = obj_prop->get_next_prop())
7379
            {
7380
                /* if it matches, log an error */
7381
                if (obj_prop->get_prop_sym() == sym)
7382
                {
7383
                    /* 
7384
                     *   if this property is overwritable, we're allowed to
7385
                     *   replace it with a new value; otherwise, it's an
7386
                     *   illegal duplicate 
7387
                     */
7388
                    if (obj_prop->is_overwritable())
7389
                    {
7390
                        /* 
7391
                         *   we're allowed to overwrite it with an explicit
7392
                         *   redefinition; simply remove the old property
7393
                         *   from the list so we can add the new one 
7394
                         */
7395
                        obj_stm->delete_property(obj_prop->get_prop_sym());
7396
                    }
7397
                    else
7398
                    {
7399
                        /* note that we found a duplicate property */
7400
                        is_dup = TRUE;
7401
                    }
7402
                        
7403
                    /* no need to continue looking */
7404
                    break;
7405
                }
7406
            }
7407
        }
7408
7409
        /* if we found a duplicate, log an error */
7410
        if (is_dup)
7411
            G_tok->log_error(TCERR_PROP_REDEF_IN_OBJ,
7412
                             (int)sym->get_sym_len(), sym->get_sym());
7413
    }
7414
7415
    /* skip it and see what comes next */
7416
    switch (G_tok->next())
7417
    {
7418
    case TOKT_LPAR:
7419
    case TOKT_LBRACE:
7420
    parse_method:
7421
        /* look up the symbol */
7422
        prop_sym = look_up_prop(&prop_tok, TRUE);
7423
7424
        /* vocabulary properties can't be code */
7425
        if (prop_sym != 0 && prop_sym->is_vocab())
7426
            G_tok->log_error_curtok(TCERR_VOCAB_REQ_SSTR);
7427
7428
        /* 
7429
         *   a left paren starts a formal parameter list, a left brace
7430
         *   starts a code body - in either case, we have a method, so
7431
         *   parse the code body 
7432
         */
7433
        code_body = parse_code_body(FALSE, TRUE, TRUE,
7434
                                    &argc, &varargs,
7435
                                    &varargs_list, &varargs_list_local,
7436
                                    &has_retval, err, 0, TCPRS_CB_NORMAL,
7437
                                    propset_stack, propset_depth, 0, 0);
7438
        if (*err)
7439
            return;
7440
7441
        /* add the method definition */
7442
        if (prop_sym != 0)
7443
            obj_stm->add_method(prop_sym, code_body, replace);
7444
        break;
7445
7446
    case TOKT_SEM:
7447
    case TOKT_RBRACE:
7448
    case TOKT_RPAR:
7449
    case TOKT_RBRACK:
7450
    case TOKT_COMMA:
7451
        /* log the error */
7452
        G_tok->log_error(TCERR_OBJDEF_REQ_PROPVAL,
7453
                         (int)prop_tok.get_text_len(), prop_tok.get_text(),
7454
                         (int)G_tok->getcur()->get_text_len(),
7455
                         G_tok->getcur()->get_text());
7456
7457
        /* if it's anything other than a semicolon, skip it */
7458
        if (G_tok->cur() != TOKT_SEM)
7459
            G_tok->next();
7460
        break;
7461
7462
    case TOKT_COLON:
7463
        /*
7464
         *   It's a nested object definition.  The value of the property is
7465
         *   a reference to an anonymous object that we now define in-line.
7466
         *   The in-line object definition is pretty much normal: following
7467
         *   the colon is the superclass list, then the property list.  The
7468
         *   property list must be enclosed in braces, though.
7469
         *   
7470
         *   Note that we hold off defining the property symbol for as long
7471
         *   as possible, since we want to make sure that this isn't
7472
         *   actually meant to be a new object definition (following an
7473
         *   object not properly terminated) rather than a nested object.  
7474
         */
7475
        {
7476
            CTcConstVal cval;
7477
            CTPNStmObject *nested_obj_stm;
7478
            
7479
            /* 
7480
             *   If the symbol we're taking to be a property name is already
7481
             *   known as an object name, we must have mistaken a new object
7482
             *   definition for a nested object definition - if this is the
7483
             *   case, flag the termination error and proceed to parse the
7484
             *   new object definition.  
7485
             */
7486
            if (sym != 0 && sym->get_type() == TC_SYM_OBJ)
7487
            {
7488
                /* 
7489
                 *   flag the unterminated object error - note that the
7490
                 *   location where termination should have occurred is
7491
                 *   where the caller tells us 
7492
                 */
7493
                G_tcmain->log_error(term_info->desc_, term_info->linenum_,
7494
                                    TC_SEV_ERROR, TCERR_UNTERM_OBJ_DEF);
7495
7496
                /* unget the colon so we can parse the new object def */
7497
                G_tok->unget();
7498
7499
                /* flag the termination error for the caller */
7500
                term_info->unterm_ = TRUE;
7501
7502
                /* we're done */
7503
                return;
7504
            }
7505
7506
            /* 
7507
             *   skip the colon, since the object body parser requires us to
7508
             *   advance to the class list before parsing any anonymous
7509
             *   object body 
7510
             */
7511
            G_tok->next();
7512
            
7513
            /* parse the body of the nested object */
7514
            nested_obj_stm = parse_anon_object(err, 0, TRUE,
7515
                                               term_info, FALSE);
7516
7517
            /* 
7518
             *   If we encountered a termination error, assume the error is
7519
             *   actually in the enclosing object, and that this wasn't
7520
             *   meant to be a nested object after all.  Do not define the
7521
             *   symbol we took as a property to be a property, since it
7522
             *   might well have been meant as an object name instead.  If a
7523
             *   termination error did occur, simply return, so that the
7524
             *   caller can handle the assumed termination.  
7525
             */
7526
            if (term_info->unterm_)
7527
                return;
7528
7529
            /* make sure we didn't encounter an error */
7530
            if (*err != 0 )
7531
                return;
7532
            
7533
            /* make sure we created a valid parse tree for the object */
7534
            if (nested_obj_stm == 0)
7535
            {
7536
                *err = TRUE;
7537
                return;
7538
            }
7539
            
7540
            /* the value we wish to assign is a reference to this object */
7541
            cval.set_obj(nested_obj_stm->get_obj_sym()->get_obj_id(),
7542
                         nested_obj_stm->get_obj_sym()->get_metaclass());
7543
            expr = new CTPNConst(&cval);
7544
7545
            /* 
7546
             *   look up the property symbol - we can finally look it up
7547
             *   with reasonable confidence, since it appears we do indeed
7548
             *   have a valid nested object definition, hence we can stop
7549
             *   waiting to see if something goes wrong 
7550
             */
7551
            prop_sym = look_up_prop(&prop_tok, TRUE);
7552
7553
            /* if it's a vocabulary property, this type isn't allowed */
7554
            if (prop_sym != 0 && prop_sym->is_vocab())
7555
                G_tok->log_error(TCERR_VOCAB_REQ_SSTR, 1, ":");
7556
7557
            /* if we have a valid property and expression, add it */
7558
            if (prop_sym != 0)
7559
                obj_stm->add_prop(prop_sym, expr, replace, FALSE);
7560
7561
            /*
7562
             *   Define the lexicalParent property in the nested object with
7563
             *   a reference back to the parent object. 
7564
             */
7565
            cval.set_obj(obj_stm->get_obj_sym() != 0
7566
                         ? obj_stm->get_obj_sym()->get_obj_id()
7567
                         : TCTARG_INVALID_OBJ,
7568
                         obj_stm->get_obj_sym() != 0
7569
                         ? obj_stm->get_obj_sym()->get_metaclass()
7570
                         : TC_META_UNKNOWN);
7571
            expr = new CTPNConst(&cval);
7572
            nested_obj_stm->add_prop(lexical_parent_sym_, expr, FALSE, FALSE);
7573
7574
            /* 
7575
             *   add the nested object's parse tree to the queue of nested
7576
             *   top-level statements - since an object is a top-level
7577
             *   statement, but we're not parsing it at the top level, we
7578
             *   need to add it to the pending queue explicitly 
7579
             */
7580
            add_nested_stm(nested_obj_stm);
7581
        }
7582
        
7583
        /* done */
7584
        break;
7585
7586
    case TOKT_EQ:
7587
        /* skip the '=' */
7588
        G_tok->next();
7589
7590
        /* 
7591
         *   if a brace follows, this must be using the obsolete TADS 2
7592
         *   notation, in which an equals sign followed a property name even
7593
         *   if a method was being defined; if this is the case, generate an
7594
         *   error, but then proceed to treat what follows as a method 
7595
         */
7596
        if (G_tok->cur() == TOKT_LBRACE)
7597
        {
7598
            /* log the error... */
7599
            G_tok->log_error(TCERR_EQ_WITH_METHOD_OBSOLETE);
7600
7601
            /* ...but then go ahead and parse it as a method anyway */
7602
            goto parse_method;
7603
        }
7604
7605
        /* go parse the value */
7606
        goto parse_value;
7607
7608
    default:
7609
        /* 
7610
         *   an '=' is required after a value property name; log the error
7611
         *   and proceed, assuming that the '=' was left out but that the
7612
         *   property value is otherwise well-formed 
7613
         */
7614
        G_tok->log_error_curtok(TCERR_PROP_REQ_EQ);
7615
7616
    parse_value:
7617
        /* look up the property symbol */
7618
        prop_sym = look_up_prop(&prop_tok, TRUE);
7619
7620
        /* 
7621
         *   presume the value will be a simple expression that will not
7622
         *   require a code body wrapper 
7623
         */
7624
        code_body = 0;
7625
7626
        /* 
7627
         *   if this is a vocabulary property, perform special vocabulary
7628
         *   list parsing - a vocabulary property can have one or more
7629
         *   single-quoted strings that we store in a list, but the list
7630
         *   does not have to be enclosed in brackets 
7631
         */
7632
        if (prop_sym != 0 && prop_sym->is_vocab()
7633
            && G_tok->cur() == TOKT_SSTR)
7634
        {
7635
            CTcSymObj *obj_sym;
7636
            CTcConstVal cval;
7637
7638
            /* 
7639
             *   set the property to a vocab list placeholder for now in
7640
             *   the object - we'll replace it during linking with the
7641
             *   actual vocabulary list
7642
             */
7643
            cval.set_vocab_list();
7644
            expr = new CTPNConst(&cval);
7645
7646
            /* if I have no dictionary, it's an error */
7647
            if (dict_cur_ == 0)
7648
                G_tok->log_error(TCERR_VOCAB_NO_DICT);
7649
7650
            /* get the object symbol */
7651
            obj_sym = (obj_stm != 0 ? obj_stm->get_obj_sym() : 0);
7652
            
7653
            /* parse the list entries */
7654
            for (;;)
7655
            {
7656
                /* add the word to our vocabulary list */
7657
                if (obj_sym != 0)
7658
                    obj_sym->add_vocab_word(G_tok->getcur()->get_text(),
7659
                                            G_tok->getcur()->get_text_len(),
7660
                                            prop_sym->get_prop());
7661
                                            
7662
                /* 
7663
                 *   skip the string; if another string doesn't
7664
                 *   immediately follow, we're done with the implied list 
7665
                 */
7666
                if (G_tok->next() != TOKT_SSTR)
7667
                    break;
7668
            }
7669
        }
7670
        else
7671
        {
7672
            CTcPrsPropExprSave save_info;
7673
7674
            /* if it's a vocabulary property, other types aren't allowed */
7675
            if (prop_sym != 0 && prop_sym->is_vocab())
7676
                G_tok->log_error_curtok(TCERR_VOCAB_REQ_SSTR);
7677
7678
            /* check for the 'static' keyword */
7679
            if (G_tok->cur() == TOKT_STATIC)
7680
            {
7681
                /* note that it's static */
7682
                is_static = TRUE;
7683
7684
                /* skip the 'static' keyword */
7685
                G_tok->next();
7686
            }
7687
7688
            /* prepare to parse the property value expression */
7689
            begin_prop_expr(&save_info);
7690
7691
            /* 
7692
             *   parse the expression (which can be a double-quoted string
7693
             *   instead of a value expression) 
7694
             */
7695
            expr = parse_expr_or_dstr(TRUE);
7696
            if (expr == 0)
7697
            {
7698
                *err = TRUE;
7699
                return;
7700
            }
7701
7702
            /* 
7703
             *   check for embedded anonymous functions and wrap the
7704
             *   expression in a code body if necessary; if we do wrap it,
7705
             *   discard the expression, since the code body contains the
7706
             *   value now 
7707
             */
7708
            code_body = finish_prop_expr(&save_info, expr,
7709
                                         is_static, prop_sym);
7710
            if (code_body != 0)
7711
                expr = 0;
7712
        }
7713
7714
        /* if we have a valid property and expression, add it */
7715
        if (prop_sym != 0)
7716
        {
7717
            /* add the expression or code body, as appropriate */
7718
            if (expr != 0)
7719
                obj_stm->add_prop(prop_sym, expr, replace, is_static);
7720
            else if (code_body != 0)
7721
                obj_stm->add_method(prop_sym, code_body, replace);
7722
        }
7723
7724
        /* done with the property */
7725
        break;
7726
    }
7727
7728
    /* 
7729
     *   if we're modifying an intrinsic class, make sure the property isn't
7730
     *   defined in the class's native interface 
7731
     */
7732
    if (meta_sym != 0 && prop_sym != 0)
7733
    {
7734
        CTcSymMetaclass *cur_meta;
7735
        
7736
        /* check this intrinsic class and all of its superclasses */
7737
        for (cur_meta = meta_sym ; cur_meta != 0 ;
7738
             cur_meta = cur_meta->get_super_meta())
7739
        {
7740
            CTcSymMetaProp *mprop;
7741
7742
            /* scan the list of native methods */
7743
            for (mprop = cur_meta->get_prop_head() ; mprop != 0 ;
7744
                 mprop = mprop->nxt_)
7745
            {
7746
                /* if it matches, flag an error */
7747
                if (mprop->prop_ == prop_sym)
7748
                {
7749
                    /* log the error */
7750
                    G_tok->log_error(TCERR_CANNOT_MOD_META_PROP,
7751
                                     prop_sym->get_sym_len(),
7752
                                     prop_sym->get_sym());
7753
                    
7754
                    /* no need to look any further */
7755
                    break;
7756
                }
7757
            }
7758
        }
7759
    }
7760
7761
    /* 
7762
     *   if this is a dictionary property, note that it's been defined for
7763
     *   this object 
7764
     */
7765
    if (prop_sym != 0 && prop_sym->is_vocab())
7766
    {
7767
        CTcDictPropEntry *entry;
7768
7769
        /* scan the list of dictionary properties for this one */
7770
        for (entry = dict_prop_head_ ; entry != 0 ; entry = entry->nxt_)
7771
        {
7772
            /* check this one for a match */
7773
            if (entry->prop_ == prop_sym)
7774
            {
7775
                /* mark this entry as defined for this object */
7776
                entry->defined_ = TRUE;
7777
7778
                /* no need to look any further */
7779
                break;
7780
            }
7781
        }
7782
    }
7783
}
7784
7785
/*
7786
 *   Begin a property expression 
7787
 */
7788
void CTcParser::begin_prop_expr(CTcPrsPropExprSave *save_info)
7789
{
7790
    /* save the current parser state */
7791
    save_info->has_local_ctx_ = has_local_ctx_;
7792
    save_info->local_ctx_var_num_ = local_ctx_var_num_;
7793
    save_info->ctx_var_props_used_ = ctx_var_props_used_;
7794
    save_info->next_ctx_arr_idx_ = next_ctx_arr_idx_;
7795
    save_info->self_referenced_ = self_referenced_;
7796
    save_info->cur_code_body_ = cur_code_body_;
7797
    save_info->full_method_ctx_referenced_ = full_method_ctx_referenced_;
7798
    save_info->local_ctx_needs_self_ = local_ctx_needs_self_;
7799
    save_info->local_ctx_needs_full_method_ctx_ =
7800
        local_ctx_needs_full_method_ctx_;
7801
7802
    /* 
7803
     *   we've saved the local context information, so clear it out for the
7804
     *   next parse job 
7805
     */
7806
    clear_local_ctx();
7807
7808
    /* there's no current code body */
7809
    cur_code_body_ = 0;
7810
7811
    /* this is parsed in a global symbol table context */
7812
    local_symtab_ = global_symtab_;
7813
}
7814
7815
/*
7816
 *   Finish a property expression, checking for anonymous functions and
7817
 *   wrapping the expression in a code body if necesssary.  If the
7818
 *   expression contains an anonymous function which needs to share context
7819
 *   with its enclosing scope, we need to build the code body wrapper
7820
 *   immediately so that we can capture the context information, which is
7821
 *   stored in the parser object itself (i.e,.  'this').  
7822
 */
7823
CTPNCodeBody *CTcParser::finish_prop_expr(CTcPrsPropExprSave *save_info,
7824
                                          CTcPrsNode *expr,
7825
                                          int is_static,
7826
                                          CTcSymProp *prop_sym)
7827
{
7828
    CTPNStm *stm;
7829
    CTPNCodeBody *cb;
7830
7831
    /* presume we won't need to create a code body */
7832
    cb = 0;
7833
7834
    /* 
7835
     *   make sure that we have an expression and a local context - if
7836
     *   there's no expression at all, or we don't have a shared context,
7837
     *   there's nothing extra we need to do - we can return the expression
7838
     *   as it is 
7839
     */
7840
    if (expr != 0 && has_local_ctx_)
7841
    {
7842
        /* create a return statement returning the value of the expression */
7843
        if (is_static)
7844
        {
7845
            /* 
7846
             *   it's a static initializer - wrap it in a static initializer
7847
             *   statement node 
7848
             */
7849
            stm = new CTPNStmStaticPropInit(expr, prop_sym->get_prop());
7850
        }
7851
        else if (expr->has_return_value())
7852
        {
7853
            /* normal property value - wrap it in a 'return' */
7854
            stm = new CTPNStmReturn(expr);
7855
        }
7856
        else
7857
        {
7858
            /* 
7859
             *   it's an expression that yields no value, such as a
7860
             *   double-quoted string expression or a call to a void
7861
             *   function; just use the expression itself 
7862
             */
7863
            stm = new CTPNStmExpr(expr);
7864
        }
7865
        
7866
        /* 
7867
         *   Create a code body to wrap the expression.  Note that we have no
7868
         *   lexically enclosing code body for a property expression.  
7869
         */
7870
        cb = new CTPNCodeBody(G_prs->get_global_symtab(), 0, stm,
7871
                              0, FALSE, FALSE, 0, local_cnt_,
7872
                              self_valid_, 0);
7873
        
7874
        /* set the local context information in the code body */
7875
        cb->set_local_ctx(local_ctx_var_num_, next_ctx_arr_idx_ - 1);
7876
7877
        /* mark the code body for references to the method context */
7878
        cb->set_self_referenced(self_referenced_);
7879
        cb->set_full_method_ctx_referenced(full_method_ctx_referenced_);
7880
7881
        /* mark the code body for inclusion in any local context */
7882
        cb->set_local_ctx_needs_self(local_ctx_needs_self_);
7883
        cb->set_local_ctx_needs_full_method_ctx(
7884
            local_ctx_needs_full_method_ctx_);
7885
    }
7886
        
7887
    /* restore the saved parser state */
7888
    has_local_ctx_ = save_info->has_local_ctx_;
7889
    local_ctx_var_num_ = save_info->local_ctx_var_num_;
7890
    ctx_var_props_used_ = save_info->ctx_var_props_used_;
7891
    next_ctx_arr_idx_ = save_info->next_ctx_arr_idx_;
7892
    cur_code_body_ = save_info->cur_code_body_;
7893
    self_referenced_ = save_info->self_referenced_;
7894
    full_method_ctx_referenced_ = save_info->full_method_ctx_referenced_;
7895
    local_ctx_needs_self_ = save_info->local_ctx_needs_self_;
7896
    local_ctx_needs_full_method_ctx_ =
7897
        save_info->local_ctx_needs_full_method_ctx_;
7898
7899
    /* return the code body, if we created one */
7900
    return cb;
7901
}
7902
7903
/*
7904
 *   Clear the local anonymous function shared context information 
7905
 */
7906
void CTcParser::clear_local_ctx()
7907
{
7908
    /* we don't have a local context */
7909
    has_local_ctx_ = FALSE;
7910
7911
    /* there is no local context local yet */
7912
    local_ctx_var_num_ = 0;
7913
7914
    /* no variable properties are used */
7915
    ctx_var_props_used_ = 0;
7916
7917
    /* 
7918
     *   start the context array index at the next entry after the slot we
7919
     *   always use to store the method context of the enclosing lexical
7920
     *   scope 
7921
     */
7922
    next_ctx_arr_idx_ = TCPRS_LOCAL_CTX_METHODCTX + 1;
7923
7924
    /* 
7925
     *   We haven't yet referenced any method context variables.
7926
     *   
7927
     *   However, even if we don't detect a reference to 'self', mark 'self'
7928
     *   as referenced anyway.  We err on the side of safety here: there are
7929
     *   so many ways that 'self' can be implicitly referenced that it seems
7930
     *   safer to assume it's needed in all cases.  Ideally, we wouldn't be
7931
     *   so cautious, but the cost of assuming that 'self' is referenced in
7932
     *   all code bodies is (for the moment, at least) relatively small, in
7933
     *   that there are relatively few optimizations we can perform based on
7934
     *   this information.  
7935
     */
7936
    self_referenced_ = TRUE;
7937
    full_method_ctx_referenced_ = FALSE;
7938
7939
    /* we don't yet need the method context in the local context */
7940
    local_ctx_needs_self_ = FALSE;
7941
    local_ctx_needs_full_method_ctx_ = FALSE;
7942
}
7943
7944
/*
7945
 *   Parse a class definition 
7946
 */
7947
CTPNStmTop *CTcParser::parse_class(int *err)
7948
{
7949
    /* skip the 'class' keyword */
7950
    G_tok->next();
7951
7952
    /* parse the object definition */
7953
    return parse_object(err, FALSE, FALSE, TRUE, 0, FALSE);
7954
}
7955
7956
/*
7957
 *   Parse a 'modify' statement
7958
 */
7959
CTPNStmTop *CTcParser::parse_modify(int *err)
7960
{
7961
    /* skip the 'modify' keyword */
7962
    G_tok->next();
7963
7964
    /* if 'grammar' follows, parse the grammar definition */
7965
    if (G_tok->cur() == TOKT_GRAMMAR)
7966
        return parse_grammar(err, FALSE, TRUE);
7967
7968
    /* if 'function' follows, parse the function definition */
7969
    if (G_tok->cur() == TOKT_FUNCTION)
7970
        return parse_function(err, FALSE, FALSE, TRUE, TRUE);
7971
7972
    /* if a symbol and an open paren follow, parse the function definition */
7973
    if (G_tok->cur() == TOKT_SYM)
7974
    {
7975
        /* we have a symbol - check the next token, but put it right back */
7976
        int is_lpar = (G_tok->next() == TOKT_LPAR);
7977
        G_tok->unget();
7978
7979
        /* if a paren follows the symbol, it's a function */
7980
        if (is_lpar)
7981
            return parse_function(err, FALSE, FALSE, TRUE, FALSE);
7982
    }        
7983
7984
    /* parse the object definition */
7985
    return parse_object(err, FALSE, TRUE, FALSE, 0, FALSE);
7986
}
7987
7988
/*
7989
 *   Parse a 'replace' statement 
7990
 */
7991
CTPNStmTop *CTcParser::parse_replace(int *err)
7992
{
7993
    /* skip the 'replace' keyword and see what follows */
7994
    switch (G_tok->next())
7995
    {
7996
    case TOKT_FUNCTION:
7997
        /* replace the function */
7998
        return parse_function(err, FALSE, TRUE, FALSE, TRUE);
7999
8000
    case TOKT_CLASS:
8001
        /* skip the 'class' keyword */
8002
        G_tok->next();
8003
8004
        /* parse the object definition */
8005
        return parse_object(err, TRUE, FALSE, TRUE, 0, FALSE);
8006
8007
    case TOKT_GRAMMAR:
8008
        /* parse the 'grammar' definition */
8009
        return parse_grammar(err, TRUE, FALSE);
8010
8011
    case TOKT_SYM:
8012
    case TOKT_TRANSIENT:
8013
        /* replace the object or function definition */
8014
        return parse_object_or_func(err, TRUE, 0, 0);
8015
8016
    default:
8017
        /* it's an error */
8018
        G_tok->log_error_curtok(TCERR_REPLACE_REQ_OBJ_OR_FUNC);
8019
8020
        /* skip the invalid token */
8021
        G_tok->next();
8022
        return 0;
8023
    }
8024
}
8025
8026
8027
/*
8028
 *   Parse a formal parameter list.  If 'count_only' is true, we're only
8029
 *   interested in counting the formals, and we don't bother adding them
8030
 *   to any symbol table.  If 'opt_allowed' is true, a parameter name can
8031
 *   be followed by a question mark token to mark the parameter as
8032
 *   optional.
8033
 *   
8034
 *   '*argc' returns with the number of parameters.  'opt_argc' can be
8035
 *   null; if it's not null, '*opt_argc' returns with the number of
8036
 *   parameters marked as optional.
8037
 *   
8038
 *   'base_formal_num' is the starting formal parameter number to use in
8039
 *   creating symbol table entries.  This is meaningful only if
8040
 *   'count_only' is false.
8041
 *   
8042
 *   The caller must already have checked for an open paren and skipped
8043
 *   it.  
8044
 */
8045
void CTcParser::parse_formal_list(int count_only, int opt_allowed,
8046
                                  int *argc, int *opt_argc, int *varargs,
8047
                                  int *varargs_list,
8048
                                  CTcSymLocal **varargs_list_local,
8049
                                  int *err, int base_formal_num,
8050
                                  int for_short_anon_func,
8051
                                  CTcFormalTypeList **type_list)
8052
{
8053
    int done;
8054
    int missing_end_tok_err;
8055
    tc_toktyp_t end_tok;
8056
8057
    /* 
8058
     *   choose the end token - if this is a normal argument list, the
8059
     *   ending token is a right parenthesis; for a short-form anonymous
8060
     *   function, it's a colon 
8061
     */
8062
    end_tok = (for_short_anon_func ? TOKT_COLON : TOKT_RPAR);
8063
    missing_end_tok_err = (for_short_anon_func
8064
                           ? TCERR_MISSING_COLON_FORMAL
8065
                           : TCERR_MISSING_RPAR_FORMAL);
8066
    
8067
    /* no arguments yet */
8068
    *argc = 0;
8069
    if (opt_argc != 0)
8070
        *opt_argc = 0;
8071
    *varargs = FALSE;
8072
    *varargs_list = FALSE;
8073
8074
    /* no error yet */
8075
    *err = FALSE;
8076
8077
    /* we've only just begun */
8078
    done = FALSE;
8079
8080
    /* check for an empty list */
8081
    if (G_tok->cur() == end_tok)
8082
    {
8083
        /* the list is empty - we're already done */
8084
        done = TRUE;
8085
8086
        /* skip the closing token */
8087
        G_tok->next();
8088
    }
8089
8090
    /* keep going until done */
8091
    while (!done)
8092
    {
8093
        /* see what comes next */
8094
        switch(G_tok->cur())
8095
        {
8096
        case TOKT_ELLIPSIS:
8097
            /* it's an ellipsis - note that we have varargs */
8098
            *varargs = TRUE;
8099
8100
        parse_ellipsis:
8101
            /* add the varargs indicator to the formal type list */
8102
            if (type_list != 0 && *type_list != 0)
8103
                (*type_list)->add_ellipsis();
8104
8105
            /* the next token must be the close paren */
8106
            if (G_tok->next() == end_tok)
8107
            {
8108
                /* we've reached the end of the list */
8109
                goto handle_end_tok;
8110
            }
8111
            else
8112
            {
8113
                /* this is an error - guess about the problem */
8114
                switch(G_tok->cur())
8115
                {
8116
                case TOKT_COMMA:
8117
                    /* 
8118
                     *   we seem to have more in the list - log an error,
8119
                     *   but continue parsing the list 
8120
                     */
8121
                    G_tok->next();
8122
                    G_tok->log_error(TCERR_ELLIPSIS_NOT_LAST);
8123
                    break;
8124
                    
8125
                default:
8126
                    /* 
8127
                     *   anything else is probably a missing right paren -
8128
                     *   provide it and exit the formal list 
8129
                     */
8130
                    done = TRUE;
8131
                    G_tok->log_error_curtok(missing_end_tok_err);
8132
                    break;
8133
                }
8134
            }
8135
            break;
8136
            
8137
        case TOKT_LBRACK:
8138
            /* 
8139
             *   varargs with named list variable for last parameter -
8140
             *   this generates setup code that stores the arguments from
8141
             *   this one forward in a list to be stored in this variable 
8142
             */
8143
8144
            /* note that we have varargs */
8145
            *varargs = TRUE;
8146
8147
            /* note it in the type list as well */
8148
            if (type_list != 0 && *type_list != 0)
8149
                (*type_list)->add_ellipsis();
8150
8151
            /* skip the bracket and check that a symbol follows */
8152
            switch(G_tok->next())
8153
            {
8154
            case TOKT_RBRACK:
8155
                /* 
8156
                 *   empty brackets - treat this as identical to an
8157
                 *   ellipsis; since they didn't name the varargs list
8158
                 *   parameter, it's just a varargs indication 
8159
                 */
8160
                goto parse_ellipsis;
8161
                
8162
            case TOKT_SYM:
8163
                /* if we're creating a symbol table, add the symbol */
8164
                if (!count_only)
8165
                {
8166
                    int local_id;
8167
                    
8168
                    /* create a local scope if we haven't already */
8169
                    create_scope_local_symtab();
8170
8171
                    /* note that we have a varargs list parameter */
8172
                    *varargs_list = TRUE;
8173
8174
                    /* 
8175
                     *   insert the new variable as a local - it's not a
8176
                     *   formal, since we're going to take the actuals,
8177
                     *   make a list, and store them in this local; the
8178
                     *   formal in this position will not be named, since
8179
                     *   this is a varargs function 
8180
                     */
8181
                    local_id = alloc_local();
8182
                    *varargs_list_local = local_symtab_->add_local(
8183
                        G_tok->getcur()->get_text(),
8184
                        G_tok->getcur()->get_text_len(),
8185
                        local_id, FALSE, TRUE, FALSE);
8186
8187
                    /* mark it as a list parameter */
8188
                    (*varargs_list_local)->set_list_param(TRUE);
8189
                }
8190
8191
                /* a close bracket must follow */
8192
                switch (G_tok->next())
8193
                {
8194
                case TOKT_RBRACK:
8195
                    /* skip the close bracket, and check for the end token */
8196
                    if (G_tok->next() == end_tok)
8197
                    {
8198
                        /* that's it - the list is done */
8199
                        goto handle_end_tok;
8200
                    }
8201
8202
                    /* we didn't find the end token - guess about the error */
8203
                    switch (G_tok->cur())
8204
                    {
8205
                    case TOKT_COMMA:
8206
                        /* 
8207
                         *   they seem to want another parameter - this is
8208
                         *   an error, but keep parsing anyway 
8209
                         */
8210
                        G_tok->log_error(TCERR_LISTPAR_NOT_LAST);
8211
                        break;
8212
8213
                    case TOKT_LBRACE:
8214
                    case TOKT_RBRACE:
8215
                    case TOKT_LPAR:
8216
                    case TOKT_EOF:
8217
                        /* the ending token was probably just forgotten */
8218
                        G_tok->log_error_curtok(missing_end_tok_err);
8219
8220
                        /* presume that the argument list is done */
8221
                        done = TRUE;
8222
                        break;
8223
8224
                    default:
8225
                        /* end token is missing - log an error */
8226
                        G_tok->log_error_curtok(missing_end_tok_err);
8227
8228
                        /* 
8229
                         *   skip the errant token; if the ending token
8230
                         *   follows this one, skip it as well, since
8231
                         *   there's probably just a stray extra token
8232
                         *   before the ending token 
8233
                         */
8234
                        if (G_tok->next() == end_tok)
8235
                            G_tok->next();
8236
8237
                        /* presume they simply left out the paren */
8238
                        done = TRUE;
8239
                        break;
8240
                    }
8241
                    break;
8242
8243
                case TOKT_LBRACE:
8244
                case TOKT_RBRACE:
8245
                case TOKT_EOF:
8246
                    /* they must have left out the bracket and paren */
8247
                    G_tok->log_error_curtok(TCERR_LISTPAR_REQ_RBRACK);
8248
                    done = TRUE;
8249
                    break;
8250
                    
8251
                default:
8252
                    /* note the missing bracket */
8253
                    G_tok->log_error_curtok(TCERR_LISTPAR_REQ_RBRACK);
8254
8255
                    /* skip the errant token and assume we're done */
8256
                    G_tok->next();
8257
                    done = TRUE;
8258
                    break;
8259
                }
8260
                break;
8261
8262
            default:
8263
                /* log an error */
8264
                G_tok->log_error_curtok(TCERR_LISTPAR_REQ_SYM);
8265
8266
                /* if this is the ending token, we're done */
8267
                if (G_tok->cur() == end_tok)
8268
                    goto handle_end_tok;
8269
8270
                /* 
8271
                 *   if we're not on something that looks like the end of
8272
                 *   the parameter list, skip the errant token 
8273
                 */
8274
                switch(G_tok->cur())
8275
                {
8276
                case TOKT_RBRACE:
8277
                case TOKT_LBRACE:
8278
                case TOKT_EOF:
8279
                    /* looks like they left out the closing markers */
8280
                    done = TRUE;
8281
                    break;
8282
8283
                default:
8284
                    /* 
8285
                     *   skip the errant token and continue - they
8286
                     *   probably have more argument list following 
8287
                     */
8288
                    G_tok->next();
8289
                    break;
8290
                }
8291
                break;
8292
            }
8293
            break;
8294
8295
        case TOKT_SYM:
8296
            /*
8297
             *   If a formal type list is allowed, check to see if the next
8298
             *   token is a symbol - if it is, the current token is the type,
8299
             *   and the next token is the variable name. 
8300
             */
8301
            if (type_list != 0)
8302
            {
8303
                /* save the initial token */
8304
                CTcToken init_tok = *G_tok->copycur();
8305
8306
                /* check the next one */
8307
                if (G_tok->next() == TOKT_SYM)
8308
                {
8309
                    /* 
8310
                     *   We have another symbol, so the first token is the
8311
                     *   type name, and the current token is the param name.
8312
                     *   Add the type to the formal type list.
8313
                     */
8314
8315
                    /* first, make sure we've created the type list */
8316
                    if (*type_list == 0)
8317
                    {
8318
                        /* 
8319
                         *   We haven't created one yet - create it now.
8320
                         *   Since we're just getting around to creating the
8321
                         *   list, add untyped elements for the parameters
8322
                         *   we've seen so far - they all must have been
8323
                         *   untyped, since we create the list on
8324
                         *   encountering the first typed parameter.  
8325
                         */
8326
                        *type_list = new (G_prsmem) CTcFormalTypeList();
8327
                        (*type_list)->add_untyped_params(*argc);
8328
                    }
8329
8330
                    /* add this typed parameter */
8331
                    (*type_list)->add_typed_param(&init_tok);
8332
                    
8333
                    /* 
8334
                     *   make sure the type symbol is defined as a class or
8335
                     *   object 
8336
                     */
8337
                    CTcSymbol *clsym = global_symtab_->find(
8338
                        init_tok.get_text(), init_tok.get_text_len());
8339
                    if (clsym == 0)
8340
                    {
8341
                        /* it's not defined - add it as an external object */
8342
                        clsym = new CTcSymObj(
8343
                            init_tok.get_text(), init_tok.get_text_len(),
8344
                            FALSE, G_cg->new_obj_id(), TRUE,
8345
                            TC_META_TADSOBJ, 0);
8346
8347
                        /* mark it as referenced */
8348
                        clsym->mark_referenced();
8349
8350
                        /* add it to the table */
8351
                        global_symtab_->add_entry(clsym);
8352
                    }
8353
                    else if (clsym->get_type() != TC_SYM_OBJ
8354
                             && clsym->get_type() != TC_SYM_METACLASS)
8355
                    {
8356
                        /* it's something other than an object */
8357
                        G_tok->log_error(TCERR_MMPARAM_NOT_OBJECT,
8358
                                         (int)init_tok.get_text_len(),
8359
                                         init_tok.get_text());
8360
                    }
8361
                }
8362
                else
8363
                {
8364
                    /* there's no type name - back up to the param name */
8365
                    G_tok->unget();
8366
8367
                    /* add an untyped variable to the type list */
8368
                    if (*type_list)
8369
                        (*type_list)->add_untyped_param();
8370
                }
8371
            }
8372
8373
            /* if we're creating symbol table entries, add this symbol */
8374
            if (!count_only)
8375
            {
8376
                /* 
8377
                 *   create a new local symbol table if we don't already
8378
                 *   have one 
8379
                 */
8380
                create_scope_local_symtab();
8381
8382
                /* insert the new formal */
8383
                local_symtab_->add_formal(G_tok->getcur()->get_text(),
8384
                                          G_tok->getcur()->get_text_len(),
8385
                                          base_formal_num + *argc, FALSE);
8386
            }
8387
            
8388
            /* count the parameter */
8389
            ++(*argc);
8390
8391
            /* skip the symbol */
8392
            G_tok->next();
8393
            
8394
            /* check for an optional question mark */
8395
            if (opt_allowed && G_tok->cur() == TOKT_QUESTION)
8396
            {
8397
                /* count the optional argument */
8398
                if (opt_argc != 0)
8399
                    ++(*opt_argc);
8400
                
8401
                /* skip the question mark */
8402
                G_tok->next();
8403
            }
8404
8405
            /* check for the closing token */
8406
            if (G_tok->cur() == end_tok)
8407
            {
8408
                /* it's the closing token - skip it and stop scanning */
8409
                goto handle_end_tok;
8410
            }
8411
8412
            /* check what follows */
8413
            switch (G_tok->cur())
8414
            {
8415
            case TOKT_COMMA:
8416
                /* skip the comma and continue */
8417
                G_tok->next();
8418
                break;
8419
8420
            case TOKT_SEM:
8421
            case TOKT_RBRACE:
8422
            case TOKT_LBRACE:
8423
            case TOKT_EQ:
8424
            case TOKT_EOF:
8425
                /* 
8426
                 *   We've obviously left the list - the problem is
8427
                 *   probably that we're missing the right paren.  Catch
8428
                 *   it in the main loop.  
8429
                 */
8430
                break;
8431
8432
            case TOKT_SYM:
8433
                /* 
8434
                 *   they seem to have left out a comma - keep parsing
8435
                 *   from the symbol token 
8436
                 */
8437
                G_tok->log_error_curtok(TCERR_REQ_COMMA_FORMAL);
8438
                break;
8439
8440
            default:
8441
                /* anything else is an error */
8442
                G_tok->log_error_curtok(TCERR_REQ_COMMA_FORMAL);
8443
                
8444
                /* skip the errant token and continue */
8445
                G_tok->next();
8446
                break;
8447
            }
8448
8449
            /* done with the formal */
8450
            break;
8451
8452
        case TOKT_SEM:
8453
        case TOKT_RBRACE:
8454
        case TOKT_LBRACE:
8455
        case TOKT_EOF:
8456
            /* 
8457
             *   We've obviously left the list - the problem is probably
8458
             *   that we're missing the right paren.  Log an error and
8459
             *   stop scanning.  
8460
             */
8461
            G_tok->log_error_curtok(missing_end_tok_err);
8462
            done = TRUE;
8463
            break;
8464
8465
        default:
8466
            /* check to see if it's the ending token */
8467
            if (G_tok->cur() == end_tok)
8468
            {
8469
                /* 
8470
                 *   they seem to have put in a comma followed by the
8471
                 *   ending token - it's probably just a stray extra comma 
8472
                 */
8473
                G_tok->log_error(TCERR_MISSING_LAST_FORMAL);
8474
                
8475
                /* skip the paren and stop scanning */
8476
                G_tok->next();
8477
                done = TRUE;
8478
                break;
8479
            }
8480
8481
            /*
8482
             *   If this is a short-form anonymous function's parameter
8483
             *   list, they probably forgot the colon - generate a more
8484
             *   specific error for this case, and assume the list ends
8485
             *   here.  
8486
             */
8487
            if (for_short_anon_func)
8488
            {
8489
                /* tell them they left out the colon */
8490
                G_tok->log_error_curtok(TCERR_MISSING_COLON_FORMAL);
8491
                
8492
                /* presume the argument list was meant to end here */
8493
                done = TRUE;
8494
                break;
8495
            }
8496
            
8497
            /* 
8498
             *   anything else is probably just an extraneous token; skip
8499
             *   it and go on 
8500
             */
8501
            G_tok->log_error_curtok(TCERR_REQ_SYM_FORMAL);
8502
            G_tok->next();
8503
            break;
8504
8505
        handle_end_tok:
8506
            /* we've reached the end token - skip it, and we're done */
8507
            G_tok->next();
8508
            done = TRUE;
8509
            break;
8510
        }
8511
    }
8512
8513
    /*
8514
     *   Check for the "multimethod" modifier, if allowed.  It's allowed if a
8515
     *   formal type list is allowed and this is a normal full parameter list
8516
     *   (i.e., specified in parentheses rather than as an anonymous function
8517
     *   formal list).  
8518
     */
8519
    if (G_tok->cur() == TOKT_SYM
8520
        && G_tok->cur_tok_matches("multimethod", 11))
8521
    {
8522
        /* make sure it's allowed */
8523
        if (type_list != 0 && end_tok == TOKT_RPAR)
8524
        {
8525
            /* it's allowed - create the type list if we haven't already */
8526
            if (*type_list == 0)
8527
            {
8528
                /* create the type list */
8529
                *type_list = new (G_prsmem) CTcFormalTypeList();
8530
8531
                /* add untyped parameters for the ones we've defined */
8532
                (*type_list)->add_untyped_params(*argc);
8533
8534
                /* mark it as varargs if applicable */
8535
                if (*varargs)
8536
                    (*type_list)->add_ellipsis();
8537
            }
8538
        }
8539
        else
8540
        {
8541
            /* it's not allowed - flag it as an error */
8542
            G_tok->log_error(TCERR_MULTIMETHOD_NOT_ALLOWED);
8543
        }
8544
8545
        /* skip the token */
8546
        G_tok->next();
8547
    }
8548
}
8549
8550
/*
8551
 *   Parse a nested code body (such as an anonymous function code body) 
8552
 */
8553
CTPNCodeBody *CTcParser::
8554
   parse_nested_code_body(int eq_before_brace,
8555
                          int self_valid,
8556
                          int *p_argc, int *p_varargs,
8557
                          int *p_varargs_list,
8558
                          CTcSymLocal **p_varargs_list_local,
8559
                          int *p_has_retval,
8560
                          int *err, CTcPrsSymtab *local_symtab,
8561
                          tcprs_codebodytype cb_type)
8562
{
8563
    CTcPrsSymtab *old_local_symtab;
8564
    CTcPrsSymtab *old_enclosing_local_symtab;
8565
    CTPNStmEnclosing *old_enclosing_stm;
8566
    CTcPrsSymtab *old_goto_symtab;
8567
    int old_local_cnt;
8568
    int old_max_local_cnt;
8569
    CTPNCodeBody *code_body;
8570
    int old_has_local_ctx;
8571
    int old_local_ctx_var_num;
8572
    size_t old_ctx_var_props_used;
8573
    int old_next_ctx_arr_idx;
8574
    int old_self_valid;
8575
    int old_self_referenced;
8576
    int old_full_method_ctx_referenced;
8577
    int old_local_ctx_needs_self;
8578
    int old_local_ctx_needs_full_method_ctx;
8579
    CTcCodeBodyRef *old_cur_code_body;
8580
8581
    /* remember the original parser state */
8582
    old_local_symtab = local_symtab_;
8583
    old_enclosing_local_symtab = enclosing_local_symtab_;
8584
    old_enclosing_stm = enclosing_stm_;
8585
    old_goto_symtab = goto_symtab_;
8586
    old_local_cnt = local_cnt_;
8587
    old_max_local_cnt = max_local_cnt_;
8588
    old_has_local_ctx = has_local_ctx_;
8589
    old_local_ctx_var_num = local_ctx_var_num_;
8590
    old_ctx_var_props_used = ctx_var_props_used_;
8591
    old_next_ctx_arr_idx = next_ctx_arr_idx_;
8592
    old_self_valid = self_valid_;
8593
    old_self_referenced = self_referenced_;
8594
    old_full_method_ctx_referenced = full_method_ctx_referenced_;
8595
    old_local_ctx_needs_self = local_ctx_needs_self_;
8596
    old_local_ctx_needs_full_method_ctx = local_ctx_needs_full_method_ctx_;
8597
    old_cur_code_body = cur_code_body_;
8598
8599
    /* parse the code body */
8600
    code_body = parse_code_body(eq_before_brace, FALSE, self_valid,
8601
                                p_argc, p_varargs,
8602
                                p_varargs_list, p_varargs_list_local,
8603
                                p_has_retval, err, local_symtab, cb_type,
8604
                                0, 0, cur_code_body_, 0);
8605
8606
    /* restore the parser state */
8607
    cur_code_body_ = old_cur_code_body;
8608
    local_symtab_ = old_local_symtab;
8609
    enclosing_local_symtab_ = old_enclosing_local_symtab;
8610
    enclosing_stm_ = old_enclosing_stm;
8611
    goto_symtab_ = old_goto_symtab;
8612
    local_cnt_ = old_local_cnt;
8613
    max_local_cnt_ = old_max_local_cnt;
8614
    has_local_ctx_ = old_has_local_ctx;
8615
    local_ctx_var_num_ = old_local_ctx_var_num;
8616
    ctx_var_props_used_ = old_ctx_var_props_used;
8617
    next_ctx_arr_idx_ = old_next_ctx_arr_idx;
8618
    self_valid_ = old_self_valid;
8619
    self_referenced_ = old_self_referenced;
8620
    full_method_ctx_referenced_= old_full_method_ctx_referenced;
8621
    local_ctx_needs_self_ = old_local_ctx_needs_self;
8622
    local_ctx_needs_full_method_ctx_ = old_local_ctx_needs_full_method_ctx;
8623
8624
    /* return the code body we parsed */
8625
    return code_body;
8626
}
8627
8628
/*
8629
 *   Set up the current code body with a local variable context 
8630
 */
8631
void CTcParser::init_local_ctx()
8632
{
8633
    /* if I already have a local variable context, there's nothing to do */
8634
    if (has_local_ctx_)
8635
        return;
8636
8637
    /* note that we now require a local variable context object */
8638
    has_local_ctx_ = TRUE;
8639
8640
    /* create the local context variable */
8641
    local_ctx_var_num_ = alloc_local();
8642
}
8643
8644
/*
8645
 *   Allocate a context variable index 
8646
 */
8647
int CTcParser::alloc_ctx_arr_idx()
8648
{
8649
    return next_ctx_arr_idx_++;
8650
}
8651
8652
/*
8653
 *   Allocate a context variable property ID 
8654
 */
8655
tctarg_prop_id_t CTcParser::alloc_ctx_var_prop()
8656
{
8657
    CTcSymProp *sym;
8658
    char prop_name[50];
8659
    char *prop_txt;
8660
    tctarg_prop_id_t prop;
8661
    
8662
    /* if possible, return an existing property ID */
8663
    if (ctx_var_props_used_ < ctx_var_props_cnt_)
8664
        return ctx_var_props_[ctx_var_props_used_++];
8665
8666
    /* if we need more room in the array, reallocate the array */
8667
    if (ctx_var_props_cnt_ == ctx_var_props_size_)
8668
    {
8669
        /* reallocate a larger array */
8670
        ctx_var_props_size_ += 50;
8671
        ctx_var_props_ = (tctarg_prop_id_t *)
8672
                         t3realloc(ctx_var_props_,
8673
                                   ctx_var_props_size_
8674
                                   * sizeof(tctarg_prop_id_t));
8675
    }
8676
8677
    /* allocate the new property ID */
8678
    prop = G_cg->new_prop_id();
8679
8680
    /* synthesize a name for the private property */
8681
    sprintf(prop_name, ".anon_var[%d]", (int)ctx_var_props_cnt_);
8682
8683
    /* copy the name to parser memory */
8684
    prop_txt = (char *)G_prsmem->alloc(strlen(prop_name));
8685
    strcpy(prop_txt, prop_name);
8686
8687
    /* allocate a new property symbol and add it to the table */
8688
    sym = new CTcSymProp(prop_txt, strlen(prop_txt), FALSE, prop);
8689
    global_symtab_->add_entry(sym);
8690
8691
    /* mark the property as referenced */
8692
    sym->mark_referenced();
8693
8694
    /* add it to our table */
8695
    ctx_var_props_[ctx_var_props_cnt_++] = prop;
8696
8697
    /* return the property */
8698
    return ctx_var_props_[ctx_var_props_used_++];
8699
}
8700
8701
/* 
8702
 *   enumeration callback context 
8703
 */
8704
struct enum_locals_ctx
8705
{
8706
    /* symbol table containing context locals */
8707
    CTcPrsSymtab *symtab;
8708
8709
    /* code body */
8710
    CTPNCodeBody *code_body;
8711
};
8712
8713
/*
8714
 *   Enumeration callback - find local variables inherited from enclosing
8715
 *   scopes (for anonymous functions) 
8716
 */
8717
void CTcParser::enum_for_ctx_locals(void *ctx0, CTcSymbol *sym)
8718
{
8719
    enum_locals_ctx *ctx = (enum_locals_ctx *)ctx0;
8720
8721
    /* tell this symbol to apply its local variable conversion */
8722
    sym->apply_ctx_var_conv(ctx->symtab, ctx->code_body);
8723
}
8724
8725
/*
8726
 *   Parse a function or method body 
8727
 */
8728
CTPNCodeBody *CTcParser::parse_code_body(int eq_before_brace, int is_obj_prop,
8729
                                         int self_valid,
8730
                                         int *p_argc, int *p_varargs,
8731
                                         int *p_varargs_list,
8732
                                         CTcSymLocal **p_varargs_list_local,
8733
                                         int *p_has_retval,
8734
                                         int *err, CTcPrsSymtab *local_symtab,
8735
                                         tcprs_codebodytype cb_type,
8736
                                         struct propset_def *propset_stack,
8737
                                         int propset_depth,
8738
                                         CTcCodeBodyRef *enclosing_code_body,
8739
                                         CTcFormalTypeList **type_list)
8740
{
8741
    int formal_num;
8742
    int varargs;
8743
    int varargs_list;
8744
    CTcSymLocal *varargs_list_local;
8745
    CTPNStmComp *stm;
8746
    unsigned long flow_flags;
8747
    CTPNCodeBody *body_stm;
8748
    int parsing_anon_fn;
8749
8750
    /* 
8751
     *   create a new code body reference - this will let nested code bodies
8752
     *   refer back to the code body object we're about to parse, even though
8753
     *   we won't create the actual code body object until we're done parsing
8754
     *   the entire code body 
8755
     */
8756
    cur_code_body_ = new (G_prsmem) CTcCodeBodyRef();
8757
8758
    /* note if we're parsing some kind of anonymous function */
8759
    parsing_anon_fn = (cb_type == TCPRS_CB_ANON_FN
8760
                       || cb_type == TCPRS_CB_SHORT_ANON_FN);
8761
8762
    /* remember the 'self' validity */
8763
    self_valid_ = self_valid;
8764
8765
    /* presume we will not need a local variable context object */
8766
    clear_local_ctx();
8767
8768
    /* 
8769
     *   Set the outer local symbol table.  If the caller has provided us
8770
     *   with an explicit pre-constructed local symbol table, use that;
8771
     *   otherwise, use the global symbol table, since we have no locals
8772
     *   of our own yet.  
8773
     */
8774
    local_symtab_ = (local_symtab == 0 ? global_symtab_ : local_symtab);
8775
    enclosing_local_symtab_ = (local_symtab_->get_parent() == 0
8776
                               ? global_symtab_
8777
                               : local_symtab_->get_parent());
8778
8779
    /* there's no enclosing statement yet */
8780
    enclosing_stm_ = 0;
8781
8782
    /* 
8783
     *   defer creating a 'goto' symbol table until we encounter a label
8784
     *   or a 'goto' 
8785
     */
8786
    goto_symtab_ = 0;
8787
8788
    /* no locals yet */
8789
    local_cnt_ = 0;
8790
    max_local_cnt_ = 0;
8791
8792
    /* no formals yet */
8793
    formal_num = 0;
8794
    varargs = FALSE;
8795
    varargs_list = FALSE;
8796
    varargs_list_local = 0;
8797
8798
    /* check for a short anonymous function, which uses unusual syntax */
8799
    if (cb_type == TCPRS_CB_SHORT_ANON_FN)
8800
    {
8801
        CTcPrsNode *expr;
8802
        CTPNStm *ret_stm;
8803
        
8804
        /* 
8805
         *   a short-form anonymous function always has an argument list,
8806
         *   but it uses special notation: the argument list is simply the
8807
         *   first thing after the function's open brace, and ends with a
8808
         *   colon 
8809
         */
8810
        parse_formal_list(FALSE, FALSE, &formal_num, 0, &varargs,
8811
                          &varargs_list, &varargs_list_local, err,
8812
                          0, TRUE, 0);
8813
        if (*err)
8814
            return 0;
8815
8816
        /*
8817
         *   The contents of a short-form anonymous function are simply an
8818
         *   expression, whose value is implicitly returned by the
8819
         *   function.  Parse the expression.
8820
         */
8821
        expr = parse_expr_or_dstr(TRUE);
8822
8823
        /*
8824
         *   The next token must be the closing brace ('}') of the
8825
         *   function.  If the next token is a semicolon, it's an error,
8826
         *   but it's probably harmless to parsing synchronization, since
8827
         *   the semicolon is almost certainly superfluous, and we can
8828
         *   simply skip it to find our close brace.  
8829
         */
8830
        if (G_tok->cur() == TOKT_SEM)
8831
        {
8832
            /* log an error explaining the problem */
8833
            G_tok->log_error(TCERR_SEM_IN_SHORT_ANON_FN);
8834
8835
            /* skip the semicolon */
8836
            G_tok->next();
8837
        }
8838
8839
        /* check for the brace */
8840
        switch (G_tok->cur())
8841
        {
8842
        case TOKT_RBRACE:
8843
            /* this is what we want - skip it and continue */
8844
            G_tok->next();
8845
            break;
8846
8847
        case TOKT_EOF:
8848
            /* log an error and give up */
8849
            G_tok->log_error_curtok(TCERR_SHORT_ANON_FN_REQ_RBRACE);
8850
            *err = 1;
8851
            return 0;
8852
8853
        default:
8854
            /* log an error, assuming they simply forgot the '}' */
8855
            G_tok->log_error_curtok(TCERR_SHORT_ANON_FN_REQ_RBRACE);
8856
            break;
8857
        }
8858
8859
        /* 
8860
         *   This anonymous function syntax implicitly returns the value of
8861
         *   the expression, so generate a 'return' statement node that
8862
         *   returns the expression.  If the expression has no return value,
8863
         *   we're simply evaluating it for side-effects, so wrap it in a
8864
         *   simple 'expression' statement.  
8865
         */
8866
        if (expr->has_return_value())
8867
            ret_stm = new CTPNStmReturn(expr);
8868
        else
8869
            ret_stm = new CTPNStmExpr(expr);
8870
8871
        /* put the 'return' statement inside a compound statement */
8872
        stm = new CTPNStmComp(ret_stm, local_symtab_);
8873
    }
8874
    else
8875
    {
8876
        propset_token_source tok_src;
8877
8878
        /*
8879
         *   If we have a propertyset stack, set up an inserted token stream
8880
         *   with the expanded token list for the formals, combining the
8881
         *   formals from the enclosing propertyset definitions with the
8882
         *   formals defined here.  
8883
         */
8884
        if (propset_depth != 0)
8885
        {
8886
            int i;
8887
            int formals_found;
8888
            
8889
            /* 
8890
             *   First, determine if we have any added formals from
8891
             *   propertyset definitions.  
8892
             */
8893
            for (formals_found = FALSE, i = 0 ; i < propset_depth ; ++i)
8894
            {
8895
                /* if this one has formals, so note */
8896
                if (propset_stack[i].param_tok_head != 0)
8897
                {
8898
                    /* note it, and we need not look further */
8899
                    formals_found = TRUE;
8900
                    break;
8901
                }
8902
            }
8903
8904
            /*
8905
             *   If we found formals from property sets, we must expand them
8906
             *   into the token stream. 
8907
             */
8908
            if (formals_found)
8909
            {
8910
                int need_comma;
8911
8912
                /* insert an open paren at the start of the expansion list */
8913
                tok_src.insert_token(TOKT_LPAR, "(", 1);
8914
8915
                /* we don't yet need a leading comma */
8916
                need_comma = FALSE;
8917
8918
                /*
8919
                 *   Add the tokens from each propertyset in the stack, from
8920
                 *   the outside in, until we reach an asterisk in each
8921
                 *   stack. 
8922
                 */
8923
                for (i = 0 ; i < propset_depth ; ++i)
8924
                {
8925
                    propset_tok *cur;
8926
8927
                    /* add the tokens from the stack element */
8928
                    for (cur = propset_stack[i].param_tok_head ; cur != 0 ;
8929
                         cur = cur->nxt)
8930
                    {
8931
                        /*
8932
                         *   If we need a comma before the next real
8933
                         *   element, add it now.  
8934
                         */
8935
                        if (need_comma)
8936
                        {
8937
                            tok_src.insert_token(TOKT_COMMA, ",", 1);
8938
                            need_comma = FALSE;
8939
                        }
8940
                        
8941
                        /*
8942
                         *   If this is a comma and the next item is the
8943
                         *   '*', omit the comma - if we have nothing more
8944
                         *   following, we want to suppress the comma. 
8945
                         */
8946
                        if (cur->tok.gettyp() == TOKT_COMMA
8947
                            && cur->nxt != 0
8948
                            && cur->nxt->tok.gettyp() == TOKT_TIMES)
8949
                        {
8950
                            /* 
8951
                             *   it's the comma before the star - simply
8952
                             *   stop here for this list, but note that we
8953
                             *   need a comma before any additional formals
8954
                             *   that we add in the future 
8955
                             */
8956
                            need_comma = TRUE;
8957
                            break;
8958
                        }
8959
                        
8960
                        /* 
8961
                         *   if it's the '*' for this list, stop here, since
8962
                         *   we want to insert the next level in before we
8963
                         *   add these tokens 
8964
                         */
8965
                        if (cur->tok.gettyp() == TOKT_TIMES)
8966
                            break;
8967
                        
8968
                        /* insert it into our expansion list */
8969
                        tok_src.insert_token(&cur->tok);
8970
                    }
8971
                }
8972
8973
                /*
8974
                 *   If we have explicit formals in the true input stream,
8975
                 *   add them, up to but not including the close paren. 
8976
                 */
8977
                if (G_tok->cur() == TOKT_LPAR)
8978
                {
8979
                    /* skip the open paren */
8980
                    G_tok->next();
8981
8982
                    /* check for a non-empty list */
8983
                    if (G_tok->cur() != TOKT_RPAR)
8984
                    {
8985
                        /* 
8986
                         *   the list is non-empty - if we need a comma, add
8987
                         *   it now 
8988
                         */
8989
                        if (need_comma)
8990
                            tok_src.insert_token(TOKT_COMMA, ",", 1);
8991
8992
                        /* we will need a comma at the end of this list */
8993
                        need_comma = TRUE;
8994
                    }
8995
8996
                    /* 
8997
                     *   copy everything up to but not including the close
8998
                     *   paren to the expansion list
8999
                     */
9000
                    while (G_tok->cur() != TOKT_RPAR
9001
                           && G_tok->cur() != TOKT_EOF)
9002
                    {
9003
                        /* insert this token into our expansion list */
9004
                        tok_src.insert_token(G_tok->getcur());
9005
9006
                        /* skip it */
9007
                        G_tok->next();
9008
                    }
9009
9010
                    /* skip the closing paren */
9011
                    if (G_tok->cur() == TOKT_RPAR)
9012
                        G_tok->next();
9013
                }
9014
9015
                /* 
9016
                 *   Finish the expansion by adding the parts of each
9017
                 *   propertyset list after the '*' to the expansion list.
9018
                 *   Copy from the inside out, since we want to unwind the
9019
                 *   nesting from outside in that we did to start with.  
9020
                 */
9021
                for (i = propset_depth ; i != 0 ; )
9022
                {
9023
                    propset_tok *cur;
9024
9025
                    /* move down to the next level */
9026
                    --i;
9027
9028
                    /* find the '*' in this list */
9029
                    for (cur = propset_stack[i].param_tok_head ;
9030
                         cur != 0 && cur->tok.gettyp() != TOKT_TIMES ;
9031
                         cur = cur->nxt) ;
9032
9033
                    /* if we found the '*', skip it */
9034
                    if (cur != 0)
9035
                        cur = cur->nxt;
9036
9037
                    /* 
9038
                     *   also skip the comma following the '*', if present -
9039
                     *   we'll explicitly insert the needed extra comma if
9040
                     *   we actually find we need one 
9041
                     */
9042
                    if (cur != 0 && cur->tok.gettyp() == TOKT_COMMA)
9043
                        cur = cur->nxt;
9044
9045
                    /* 
9046
                     *   insert the remainder of the list into the expansion
9047
                     *   list 
9048
                     */
9049
                    for ( ; cur != 0 ; cur = cur->nxt)
9050
                    {
9051
                        /* if we need a comma, add it now */
9052
                        if (need_comma)
9053
                        {
9054
                            tok_src.insert_token(TOKT_COMMA, ",", 1);
9055
                            need_comma = FALSE;
9056
                        }
9057
9058
                        /* insert this token */
9059
                        tok_src.insert_token(&cur->tok);
9060
                    }
9061
                }
9062
9063
                /* add the closing paren at the end of the expansion list */
9064
                tok_src.insert_token(TOKT_RPAR, ")", 1);
9065
9066
                /*
9067
                 *   We've fully expanded the formal list.  Now all that
9068
                 *   remains is to insert the expanded token list into the
9069
                 *   token input stream, so that we read from the expanded
9070
                 *   list instead of from the original token stream.
9071
                 */
9072
                G_tok->set_external_source(&tok_src);
9073
            }
9074
        }
9075
9076
        /* 
9077
         *   if we have an explicit left parenthesis, or an implied formal
9078
         *   list from an enclosing propertyset, parse the list 
9079
         */
9080
        if (G_tok->cur() == TOKT_LPAR)
9081
        {
9082
            /* skip the open paren */
9083
            G_tok->next();
9084
            
9085
            /* 
9086
             *   Parse the formal list.  Add the symbols to the local
9087
             *   table (hence 'count_only' = false), and don't allow
9088
             *   optional arguments.  
9089
             */
9090
            parse_formal_list(FALSE, FALSE, &formal_num, 0, &varargs,
9091
                              &varargs_list, &varargs_list_local, err,
9092
                              0, FALSE, type_list);
9093
            if (*err)
9094
                return 0;
9095
        }
9096
9097
        /* parse an equals sign, if present */
9098
        if (G_tok->cur() == TOKT_EQ)
9099
        {
9100
            /*
9101
             *   An equals sign after a formal parameter list can be used if
9102
             *   the 'eq_before_brace' flag is set.  Otherwise, if we're
9103
             *   defining an object property, this is an error, since it's
9104
             *   obsolete TADS 2 syntax that we no longer allow - because
9105
             *   this is a change in syntax, we want to catch it
9106
             *   specifically so we can provide good diagnostic information
9107
             *   for it.  
9108
             */
9109
            if (eq_before_brace && G_tok->cur() == TOKT_EQ)
9110
            {
9111
                /* it's allowed - skip the '=' */
9112
                G_tok->next();
9113
            }
9114
            else if (is_obj_prop)
9115
            {
9116
                /* obsolete tads 2 syntax - flag the error */
9117
                G_tok->log_error(TCERR_EQ_WITH_METHOD_OBSOLETE);
9118
9119
                /* 
9120
                 *   skip the '=' so we can continue parsing the rest of the
9121
                 *   code body without cascading errors 
9122
                 */
9123
                G_tok->next();
9124
            }
9125
            else
9126
            {
9127
                /* 
9128
                 *   it's not a situation where we allow '=' specifically,
9129
                 *   or where we know why it might be present erroneously -
9130
                 *   let it go for now, as we'll flag the error in the
9131
                 *   normal compound statement parsing 
9132
                 */
9133
            }
9134
        }
9135
9136
        /* check for '(' syntax */
9137
        //$$$
9138
9139
        /* require the '{' */
9140
        switch (G_tok->cur())
9141
        {
9142
        case TOKT_LBRACE:
9143
        parse_body:
9144
            /* parse the compound statement */
9145
            stm = parse_compound(err, TRUE, 0, TRUE);
9146
            break;
9147
9148
        case TOKT_SEM:
9149
        case TOKT_RBRACE:
9150
            /* 
9151
             *   we seem to have found the end of the object definition, or
9152
             *   the end of a code body - treat it as an empty code body 
9153
             */
9154
            G_tok->log_error_curtok(TCERR_REQ_LBRACE_CODE);
9155
            stm = new CTPNStmComp(0, 0);
9156
            break;
9157
9158
        default:
9159
            /* 
9160
             *   the '{' was missing - log an error, but proceed from the
9161
             *   current token on the assumption that they merely left out
9162
             *   the open brace 
9163
             */
9164
            G_tok->log_error_curtok(TCERR_REQ_LBRACE_CODE);
9165
            goto parse_body;
9166
        }
9167
    }
9168
9169
    /* if that failed, return the error */
9170
    if (*err || stm == 0)
9171
        return 0;
9172
9173
    /* 
9174
     *   determine how the statement exits, and generate any internal flow
9175
     *   warnings within the body code
9176
     */
9177
    flow_flags = stm->get_control_flow(TRUE);
9178
9179
    /*
9180
     *   Warn if the function has both explicit void and value returns.
9181
     *   If not, check to see if it continues; if so, it implicitly
9182
     *   returns a void value by falling off the end, so warn if it both
9183
     *   falls off the end and returns a value somewhere else.  Suppress
9184
     *   this warning if this is a syntax check only.  
9185
     */
9186
    if (!G_prs->get_syntax_only())
9187
    {
9188
        if ((flow_flags & TCPRS_FLOW_RET_VAL) != 0
9189
            && (flow_flags & TCPRS_FLOW_RET_VOID) != 0)
9190
        {
9191
            /* it has explicit void and value returns */
9192
            stm->log_warning(TCERR_RET_VAL_AND_VOID);
9193
        }
9194
        else if ((flow_flags & TCPRS_FLOW_RET_VAL) != 0
9195
                 && (flow_flags & TCPRS_FLOW_NEXT) != 0)
9196
        {
9197
            /* it has explicit value returns, and implicit void return */
9198
            stm->log_warning(TCERR_RET_VAL_AND_IMP_VOID);
9199
        }
9200
    }
9201
        
9202
    /* if the caller is interested, return the interface details */
9203
    if (p_argc != 0)
9204
        *p_argc = formal_num;
9205
    if (p_varargs != 0)
9206
        *p_varargs = varargs;
9207
    if (p_varargs_list != 0)
9208
        *p_varargs_list = varargs_list;
9209
    if (p_varargs_list_local != 0)
9210
        *p_varargs_list_local = varargs_list_local;
9211
    if (p_has_retval)
9212
        *p_has_retval = ((flow_flags & TCPRS_FLOW_RET_VAL) != 0);
9213
9214
    /* create a code body node for the result */
9215
    body_stm = new CTPNCodeBody(local_symtab_, goto_symtab_, stm,
9216
                                formal_num, varargs,
9217
                                varargs_list, varargs_list_local,
9218
                                max_local_cnt_, self_valid,
9219
                                enclosing_code_body);
9220
9221
    /* store this new statement in the current code body reference object */
9222
    cur_code_body_->ptr = body_stm;
9223
9224
    /* 
9225
     *   set the end location in the new code body to the end location in
9226
     *   the underlying compound statement 
9227
     */
9228
    body_stm->set_end_location(stm->get_end_desc(), stm->get_end_linenum());
9229
9230
    /* if we have a local context, mark the code body accordingly */
9231
    if (has_local_ctx_)
9232
        body_stm->set_local_ctx(local_ctx_var_num_, next_ctx_arr_idx_ - 1);
9233
9234
    /* 
9235
     *   If the caller passed in a local symbol table, check the table for
9236
     *   context variables from enclosing scopes, and assign the local
9237
     *   holder for each such variable. 
9238
     */
9239
    if (local_symtab != 0)
9240
    {
9241
        enum_locals_ctx ctx;
9242
        CTcPrsSymtab *tab, *par;
9243
9244
        /* 
9245
         *   consider only the outermost local table, since that's where
9246
         *   the shared locals reside 
9247
         */
9248
        for (tab = local_symtab ;
9249
             par = tab->get_parent(),
9250
             par != 0 && par != G_prs->get_global_symtab() ;
9251
             tab = par) ;
9252
9253
        /* enumerate the variables */
9254
        ctx.symtab = tab;
9255
        ctx.code_body = body_stm;
9256
        tab->enum_entries(&enum_for_ctx_locals, &ctx);
9257
    }
9258
9259
    /* 
9260
     *   if 'self' is valid, and we're parsing an anonymous function, and we
9261
     *   have any references in this code body to any method context
9262
     *   variables (self, targetprop, targetobj, definingobj), make certain
9263
     *   that the code body has a context at level 1, so that it can pick up
9264
     *   our method context 
9265
     */
9266
    if (self_valid && parsing_anon_fn
9267
        && (self_referenced_ || full_method_ctx_referenced_))
9268
        body_stm->get_or_add_ctx_var_for_level(1);
9269
9270
    /* mark the code body for references to the method context */
9271
    body_stm->set_self_referenced(self_referenced_);
9272
    body_stm->set_full_method_ctx_referenced(full_method_ctx_referenced_);
9273
9274
    /* 
9275
     *   mark the code body for inclusion in any local context of the method
9276
     *   context 
9277
     */
9278
    body_stm->set_local_ctx_needs_self(local_ctx_needs_self_);
9279
    body_stm->set_local_ctx_needs_full_method_ctx(
9280
        local_ctx_needs_full_method_ctx_);
9281
9282
    /* return the new body statement */
9283
    return body_stm;
9284
}
9285
9286
/*
9287
 *   Parse a compound statement 
9288
 */
9289
CTPNStmComp *CTcParser::parse_compound(int *err, int skip_lbrace,
9290
                                       CTPNStmSwitch *enclosing_switch,
9291
                                       int use_enclosing_scope)
9292
{
9293
    CTPNStm *first_stm;
9294
    CTPNStm *last_stm;
9295
    CTPNStm *cur_stm;
9296
    CTPNStmComp *comp_stm;
9297
    int done;
9298
    tcprs_scope_t scope_data;
9299
    CTcTokFileDesc *file;
9300
    long linenum;
9301
    int skip_rbrace;
9302
9303
    /* save the current line information for later */
9304
    G_tok->get_last_pos(&file, &linenum);
9305
9306
    /* skip the '{' if we're on one and the caller wants us to */
9307
    if (skip_lbrace && G_tok->cur() == TOKT_LBRACE)
9308
        G_tok->next();
9309
9310
    /* enter a scope */
9311
    if (!use_enclosing_scope)
9312
        enter_scope(&scope_data);
9313
9314
    /* we don't have any statements in our sublist yet */
9315
    first_stm = last_stm = 0;
9316
9317
    /* presume we won't find the closing brace */
9318
    skip_rbrace = FALSE;
9319
9320
    /* keep going until we reach the closing '}' */
9321
    for (done = FALSE ; !done ; )
9322
    {
9323
        /* check what we've found */
9324
        switch (G_tok->cur())
9325
        {
9326
        case TOKT_RBRACE:
9327
            /* it's our closing brace - we're done */
9328
            done = TRUE;
9329
            cur_stm = 0;
9330
9331
            /* note that we must still skip the closing brace */
9332
            skip_rbrace = TRUE;
9333
9334
            /* stop scanning statements */
9335
            break;
9336
9337
        default:
9338
            /* parse a statement */
9339
            cur_stm = parse_stm(err, enclosing_switch, FALSE);
9340
9341
            /* if an error occurred, stop parsing */
9342
            if (*err)
9343
                done = TRUE;
9344
            break;
9345
        }
9346
9347
        /* if we parsed a statement, add it to our list */
9348
        if (cur_stm != 0)
9349
        {
9350
            /* link the statement at the end of our list */
9351
            if (last_stm != 0)
9352
                last_stm->set_next_stm(cur_stm);
9353
            else
9354
                first_stm = cur_stm;
9355
            last_stm = cur_stm;
9356
        }
9357
    }
9358
9359
    /* if there's no statement, make the body a null statement */
9360
    if (first_stm == 0)
9361
        first_stm = new CTPNStmNull();
9362
9363
    /* build the compound statement node */
9364
    comp_stm = new CTPNStmComp(first_stm, local_symtab_);
9365
9366
    /* set some additional information if we created a statement */
9367
    if (comp_stm != 0)
9368
    {
9369
        /* set the statement's line to the start of the compound */
9370
        comp_stm->set_source_pos(file, linenum);
9371
9372
        /* note whether or not we have our own private scope */
9373
        comp_stm->set_has_own_scope(!use_enclosing_scope
9374
                                    && (local_symtab_
9375
                                        != scope_data.local_symtab));
9376
    }
9377
9378
    /* if necessary, skip the closing brace */
9379
    if (skip_rbrace)
9380
        G_tok->next();
9381
9382
    /* leave the local scope */
9383
    if (!use_enclosing_scope)
9384
        leave_scope(&scope_data);
9385
9386
    /* return the compound statement object */
9387
    return comp_stm;
9388
}
9389
9390
/*
9391
 *   Create a local symbol table for the current scope, if necessary 
9392
 */
9393
void CTcParser::create_scope_local_symtab()
9394
{
9395
    /* 
9396
     *   if our symbol table is the same as the enclosing symbol table, we
9397
     *   must create our own table 
9398
     */
9399
    if (local_symtab_ == enclosing_local_symtab_)        
9400
    {
9401
        /* 
9402
         *   Create our own local symbol table, replacing the current one
9403
         *   - we saved the enclosing one already when we entered the
9404
         *   scope, so we'll restore it on our way out.  The new local
9405
         *   symbol table has the enclosing symbol table as its parent
9406
         *   scope.  
9407
         */
9408
        local_symtab_ = new CTcPrsSymtab(local_symtab_);
9409
    }
9410
}
9411
9412
/*
9413
 *   Parse a local variable definition 
9414
 */
9415
CTPNStm *CTcParser::parse_local(int *err)
9416
{
9417
    int done;
9418
    CTPNStm *first_stm;
9419
    CTPNStm *last_stm;
9420
9421
    /* we have no initializer statements yet */
9422
    first_stm = last_stm = 0;
9423
9424
    /* skip the 'local' keyword */
9425
    G_tok->next();
9426
9427
    /* keep going until we reach the closing semicolon */
9428
    for (done = FALSE ; !done ; )
9429
    {
9430
        /* we need a symbol name */
9431
        if (G_tok->cur() == TOKT_SYM)
9432
        {
9433
            const char *sym;
9434
            size_t symlen;
9435
            CTcSymLocal *lcl;
9436
            CTPNStm *stm;
9437
            CTcPrsNode *expr;
9438
            
9439
            /* get the symbol string from the token */
9440
            sym = G_tok->getcur()->get_text();
9441
            symlen = G_tok->getcur()->get_text_len();
9442
            
9443
            /* add the new local variable to our symbol table */
9444
            lcl = local_symtab_->add_local(sym, symlen, alloc_local(),
9445
                                           FALSE, FALSE, FALSE);
9446
9447
            /* skip the symbol and check for an initial value assignment */
9448
            switch (G_tok->next())
9449
            {
9450
            case TOKT_EQ:
9451
            case TOKT_ASI:
9452
                /* parse the initializer */
9453
                expr = parse_local_initializer(lcl, err);
9454
9455
                /* if we didn't get a statement, we can't proceed */
9456
                if (expr == 0)
9457
                {
9458
                    done = TRUE;
9459
                    break;
9460
                }
9461
9462
                /* create a statement for the assignment */
9463
                stm = new CTPNStmExpr(expr);
9464
9465
                /* 
9466
                 *   set the statement's source location according to the
9467
                 *   current source location - if we have multiple
9468
                 *   initializers over several lines, this will allow the
9469
                 *   debugger to step through the individual
9470
                 *   initializations 
9471
                 */
9472
                stm->set_source_pos(G_tok->get_last_desc(),
9473
                                    G_tok->get_last_linenum());
9474
9475
                /* add the statement to our list */
9476
                if (last_stm != 0)
9477
                    last_stm->set_next_stm(stm);
9478
                else
9479
                    first_stm = stm;
9480
                last_stm = stm;
9481
9482
                /* done */
9483
                break;
9484
9485
            default:
9486
                /* there's nothing more to do with this variable */
9487
                break;
9488
            }
9489
9490
            /* 
9491
             *   check what follows - we can have a comma to introduce
9492
             *   another local variable, or a semicolon to end the
9493
             *   statement 
9494
             */
9495
            switch(G_tok->cur())
9496
            {
9497
            case TOKT_COMMA:
9498
                /* skip the comma and go on to the next variable */
9499
                G_tok->next();
9500
                break;
9501
9502
            case TOKT_SEM:
9503
                /* skip the semicolon, and stop scanning */
9504
                G_tok->next();
9505
                done = TRUE;
9506
                break;
9507
9508
            case TOKT_SYM:
9509
                /* 
9510
                 *   they probably just left out a comma - assume the
9511
                 *   comma is there and keep going 
9512
                 */
9513
                G_tok->log_error_curtok(TCERR_LOCAL_REQ_COMMA);
9514
                break;
9515
9516
            default:
9517
                /* 
9518
                 *   these almost certainly indicate that they left out a
9519
                 *   semicolon - report the error and continue from here 
9520
                 */
9521
                G_tok->log_error_curtok(TCERR_LOCAL_REQ_COMMA);
9522
                done = TRUE;
9523
                break;
9524
            }
9525
        }
9526
        else
9527
        {
9528
            /* symbol required - log the error */
9529
            G_tok->log_error_curtok(TCERR_LOCAL_REQ_SYM);
9530
9531
            /* determine how to proceed based on what we have */
9532
            switch(G_tok->cur())
9533
            {
9534
            case TOKT_COMMA:
9535
                /* 
9536
                 *   they probably just put in an extra comma - skip it
9537
                 *   and keep trying to parse the local list 
9538
                 */
9539
                G_tok->next();
9540
                break;
9541
9542
            case TOKT_SEM:
9543
                /* that's the end of the statement */
9544
                G_tok->next();
9545
                done = TRUE;
9546
                break;
9547
9548
            case TOKT_EOF:
9549
                /* set the error flag and stop scanning */
9550
                *err = TRUE;
9551
                done = TRUE;
9552
                break;
9553
9554
            default:
9555
                /* try skipping this token and trying again */
9556
                G_tok->next();
9557
                break;
9558
            }
9559
        }
9560
    }
9561
9562
    /* 
9563
     *   if we have one statement, return it; if we have more than one,
9564
     *   return a compound statement to contain the list; if we have
9565
     *   nothing, return nothing 
9566
     */
9567
    if (first_stm == 0)
9568
        return 0;
9569
    else if (first_stm == last_stm)
9570
        return first_stm;
9571
    else
9572
        return new CTPNStmComp(first_stm, local_symtab_);
9573
}
9574
9575
/*
9576
 *   Parse a local variable initializer 
9577
 */
9578
CTcPrsNode *CTcParser::parse_local_initializer(CTcSymLocal *lcl, int *err)
9579
{
9580
    CTcPrsNode *expr;
9581
    
9582
    /* 
9583
     *   skip the assignment operator and parse the expression (which
9584
     *   cannot use the comma operator) 
9585
     */
9586
    G_tok->next();
9587
    expr = parse_asi_expr();
9588
9589
    /* if that failed, return failure */
9590
    if (expr == 0)
9591
        return 0;
9592
9593
    /* 
9594
     *   if we have a valid local, return a new expression node for the
9595
     *   assignment; otherwise just return the expression, since we have
9596
     *   nothing to assign to 
9597
     */
9598
    return (lcl != 0 ? new CTPNAsi(new CTPNSymResolved(lcl), expr) : expr);
9599
}
9600
9601
/*
9602
 *   Parse a statement 
9603
 */
9604
CTPNStm *CTcParser::parse_stm(int *err, CTPNStmSwitch *enclosing_switch,
9605
                              int compound_use_enclosing_scope)
9606
{
9607
    CTcToken tok;
9608
9609
    /* 
9610
     *   remember where the statement starts - when we create the
9611
     *   statement object, it will refer to these values to set its
9612
     *   internal memory of the statement's source file location 
9613
     */
9614
    cur_desc_ = G_tok->get_last_desc();
9615
    cur_linenum_ = G_tok->get_last_linenum();
9616
9617
    /* see what we have */
9618
try_again:
9619
    switch(G_tok->cur())
9620
    {
9621
    case TOKT_EOF:
9622
        /* unexpected end of file - log an error */
9623
        G_tok->log_error(TCERR_EOF_IN_CODE);
9624
        
9625
        /* set the caller's error flag */
9626
        *err = TRUE;
9627
9628
        /* there's no statement to return, obviously */
9629
        return 0;
9630
9631
    case TOKT_DSTR_MID:
9632
    case TOKT_DSTR_END:
9633
    case TOKT_RBRACE:
9634
        /* 
9635
         *   we shouldn't be looking at any of these at the start of a
9636
         *   statement 
9637
         */
9638
        G_tok->log_error_curtok(TCERR_EXPECTED_STMT_START);
9639
        G_tok->next();
9640
        return 0;
9641
9642
    case TOKT_SEM:
9643
        /* 
9644
         *   null statement - this doesn't generate any code; simply skip
9645
         *   the semicolon and keep going 
9646
         */
9647
        G_tok->next();
9648
9649
        /* this doesn't generate any code */
9650
        return 0;
9651
9652
    case TOKT_LOCAL:
9653
        /* if we don't have our own local symbol table, create one */
9654
        create_scope_local_symtab();
9655
9656
        /* parse the local variable definition and return the result */
9657
        return parse_local(err);
9658
9659
    case TOKT_LBRACE:
9660
        /* it's a compound statement */
9661
        return parse_compound(err, TRUE, 0, compound_use_enclosing_scope);
9662
        
9663
    case TOKT_IF:
9664
        /* parse an if statement */
9665
        return parse_if(err);
9666
9667
    case TOKT_RETURN:
9668
        /* parse a return statement */
9669
        return parse_return(err);
9670
9671
    case TOKT_FOR:
9672
        /* parse a for statement */
9673
        return parse_for(err);
9674
9675
    case TOKT_FOREACH:
9676
        /* parse a foreach statement */
9677
        return parse_foreach(err);
9678
9679
    case TOKT_WHILE:
9680
        /* parse a while statement */
9681
        return parse_while(err);
9682
9683
    case TOKT_DO:
9684
        /* parse a do-while */
9685
        return parse_do_while(err);
9686
9687
    case TOKT_SWITCH:
9688
        /* parse a switch */
9689
        return parse_switch(err);
9690
9691
    case TOKT_GOTO:
9692
        /* parse a 'goto' */
9693
        return parse_goto(err);
9694
9695
    case TOKT_BREAK:
9696
        return parse_break(err);
9697
9698
    case TOKT_CONTINUE:
9699
        return parse_continue(err);
9700
9701
    case TOKT_TRY:
9702
        return parse_try(err);
9703
9704
    case TOKT_THROW:
9705
        return parse_throw(err);
9706
9707
    case TOKT_CATCH:
9708
        /* misplaced 'catch' clause - log an error */
9709
        G_tok->log_error(TCERR_MISPLACED_CATCH);
9710
9711
        /* 
9712
         *   skip the following open paren, class name, variable name, and
9713
         *   closing paren, as long as we find all of these 
9714
         */
9715
        if (G_tok->next() == TOKT_LPAR
9716
            && G_tok->next() == TOKT_SYM
9717
            && G_tok->next() == TOKT_SYM
9718
            && G_tok->next() == TOKT_RPAR)
9719
            G_tok->next();
9720
9721
        /* there's no valid statement to return */
9722
        return 0;
9723
9724
    case TOKT_FINALLY:
9725
        /* misplaced 'finally' clause - log an error */
9726
        G_tok->log_error(TCERR_MISPLACED_FINALLY);
9727
9728
        /* skip the 'finally' keyword, and return failure */
9729
        G_tok->next();
9730
        return 0;
9731
9732
    case TOKT_ELSE:
9733
        /* 
9734
         *   misplaced 'else' clause - log an error, skip the 'else'
9735
         *   keyword, and proceed with what follows 
9736
         */
9737
        G_tok->log_error(TCERR_MISPLACED_ELSE);
9738
        G_tok->next();
9739
        return 0;
9740
9741
    case TOKT_CASE:
9742
        /* 
9743
         *   if we're in a 'switch', it's a valid 'case' label; otherwise,
9744
         *   it's misplaced 
9745
         */
9746
        if (enclosing_switch != 0)
9747
        {
9748
            /* parse the 'case' label */
9749
            return parse_case(err, enclosing_switch);
9750
        }
9751
        else
9752
        {
9753
            /* 
9754
             *   not directly within a 'switch', so this is a misplaced
9755
             *   'case' keyword - log an error 
9756
             */
9757
            G_tok->log_error(TCERR_MISPLACED_CASE);
9758
            
9759
            /* skip the 'case' keyword */
9760
            G_tok->next();
9761
            
9762
            /* assume there's an expression here, and skip that as well */
9763
            parse_expr();
9764
            
9765
            /* if there's a colon, skip it, too */
9766
            if (G_tok->cur() == TOKT_COLON)
9767
                G_tok->next();
9768
            
9769
            /* proceed from here */
9770
            return 0;
9771
        }
9772
9773
    case TOKT_DEFAULT:
9774
        /* allow this only if we're directly in a 'switch' body */
9775
        if (enclosing_switch != 0)
9776
        {
9777
            /* parse the 'default' label */
9778
            return parse_default(err, enclosing_switch);
9779
        }
9780
        else
9781
        {
9782
            /* misplaced 'default' keyword - log an error */
9783
            G_tok->log_error(TCERR_MISPLACED_DEFAULT);
9784
            
9785
            /* skip the 'default' keyword; if there's a colon, skip it, too */
9786
            if (G_tok->next() == TOKT_COLON)
9787
                G_tok->next();
9788
9789
            /* proceed from here */
9790
            return 0;
9791
        }
9792
9793
    case TOKT_SYM:
9794
        /*
9795
         *   It's a symbol.  First, check for a label.  This requires that
9796
         *   we look ahead one token, because we have to look at the next
9797
         *   token to see if it's a colon; if it's not, we have to back up
9798
         *   and parse the symbol as the start of an expression.  So,
9799
         *   remember the current symbol token, then look at what follows.
9800
         */
9801
        tok = *G_tok->copycur();
9802
        if (G_tok->next() == TOKT_COLON)
9803
        {
9804
            CTPNStmEnclosing *old_enclosing;
9805
            CTPNStmLabel *label_stm;
9806
            CTcSymLabel *lbl;
9807
            CTPNStm *stm;
9808
9809
            /* it's a label - create a symbol table entry for it */
9810
            lbl = add_code_label(&tok);
9811
9812
            /* create the labeled statement node */
9813
            label_stm = new CTPNStmLabel(lbl, enclosing_stm_);
9814
9815
            /* skip the colon */
9816
            G_tok->next();
9817
9818
            /* 
9819
             *   set our new label to be the enclosing label for
9820
             *   everything contained within its statement 
9821
             */
9822
            old_enclosing = set_enclosing_stm(label_stm);
9823
9824
            /* parse the labeled statement */
9825
            stm = parse_stm(err, enclosing_switch, FALSE);
9826
9827
            /* restore our enclosing statement */
9828
            set_enclosing_stm(old_enclosing);
9829
9830
            /* if parsing the labeled statement failed, give up */
9831
            if (*err)
9832
                return 0;
9833
9834
            /* connect to the label to the statement it labels */
9835
            label_stm->set_stm(stm);
9836
9837
            /* point the label symbol to its statement node */
9838
            if (lbl != 0)
9839
                lbl->set_stm(label_stm);
9840
9841
            /* return the labeled statement node */
9842
            return label_stm;
9843
        }
9844
9845
        /* 
9846
         *   it's not a label - push the colon back into the input stream
9847
         *   so that we read it again, then parse this as an ordinary
9848
         *   expression 
9849
         */
9850
        G_tok->unget();
9851
        goto do_parse_expr;
9852
9853
    case TOKT_RPAR:
9854
        /* 
9855
         *   they probably had too many close parens in something like a
9856
         *   'for' or 'if' statement - flag the error 
9857
         */
9858
        G_tok->log_error(TCERR_EXTRA_RPAR);
9859
9860
        /* skip the extra paren and go back for another try */
9861
        G_tok->next();
9862
        goto try_again;
9863
        
9864
    default:
9865
    do_parse_expr:
9866
        /* anything else must be the start of an expression */
9867
        {
9868
            CTcPrsNode *expr;
9869
            
9870
            /* parse the expression */
9871
            expr = parse_expr_or_dstr(TRUE);
9872
9873
            /* the statement must be terminated with a semicolon */
9874
            if (parse_req_sem())
9875
            {
9876
                /* set the error flag */
9877
                *err = TRUE;
9878
9879
                /* there's no statement to return */
9880
                return 0;
9881
            }
9882
            
9883
            /* 
9884
             *   if we successfully parsed an expression, create a
9885
             *   statement node for the expression; if expr is null, the
9886
             *   expression parser will already have issued an error, so
9887
             *   we can simply ignore the failed expression and continue
9888
             *   to the next statement 
9889
             */
9890
            if (expr != 0)
9891
                return new CTPNStmExpr(expr);
9892
            else
9893
                return 0;
9894
        }
9895
    }
9896
}
9897
9898
9899
/*
9900
 *   Add a 'goto' label symbol to the current code body
9901
 */
9902
CTcSymLabel *CTcParser::add_code_label(const CTcToken *tok)
9903
{
9904
    /* if there's no 'goto' symbol table, create one */
9905
    if (goto_symtab_ == 0)
9906
        goto_symtab_ = new CTcPrsSymtab(0);
9907
9908
    /* create the label and return it */
9909
    return goto_symtab_->add_code_label(tok->get_text(),
9910
                                        tok->get_text_len(), FALSE);
9911
}
9912
9913
9914
/* 
9915
 *   Parse an 'if' statement 
9916
 */
9917
CTPNStm *CTcParser::parse_if(int *err)
9918
{
9919
    CTcPrsNode *cond_expr;
9920
    CTPNStm *if_stm;
9921
    CTPNStm *then_stm;
9922
    CTPNStm *else_stm;
9923
    CTcTokFileDesc *file;
9924
    long linenum;
9925
    
9926
    /* save the starting line information for later */
9927
    G_tok->get_last_pos(&file, &linenum);
9928
9929
    /* skip the 'if' keyword, and require the open paren */
9930
    if (G_tok->next() == TOKT_LPAR)
9931
    {
9932
        /* skip the left paren */
9933
        G_tok->next();
9934
    }
9935
    else
9936
    {
9937
        /* 
9938
         *   log an error, but proceed on the assumption that they simply
9939
         *   left out the paren 
9940
         */
9941
        G_tok->log_error_curtok(TCERR_REQ_LPAR_IF);
9942
    }
9943
9944
    /* parse the expression */
9945
    cond_expr = parse_cond_expr();
9946
9947
    /* if that failed, return failure */
9948
    if (cond_expr == 0)
9949
    {
9950
        *err = TRUE;
9951
        return 0;
9952
    }
9953
9954
    /* require the close paren */
9955
    if (G_tok->cur() == TOKT_RPAR)
9956
    {
9957
        /* skip it */
9958
        G_tok->next();
9959
    }
9960
    else
9961
    {
9962
        /* 
9963
         *   log an error, then proceed assuming that the paren was merely
9964
         *   omitted 
9965
         */
9966
        G_tok->log_error_curtok(TCERR_REQ_RPAR_IF);
9967
    }
9968
9969
    /* parse the true-part statement */
9970
    then_stm = parse_stm(err, 0, FALSE);
9971
9972
    /* if an error occurred, return failure */
9973
    if (*err)
9974
        return 0;
9975
9976
    /* check for 'else' */
9977
    if (G_tok->cur() == TOKT_ELSE)
9978
    {
9979
        /* skip the 'else' keyword */
9980
        G_tok->next();
9981
9982
        /* parse the false-part statement */
9983
        else_stm = parse_stm(err, 0, FALSE);
9984
9985
        /* if an error occurred, return failure */
9986
        if (*err)
9987
            return 0;
9988
    }
9989
    else
9990
    {
9991
        /* there's no 'else' part */
9992
        else_stm = 0;
9993
    }
9994
9995
    /* create and return the 'if' statement node */
9996
    if_stm = new CTPNStmIf(cond_expr, then_stm, else_stm);
9997
9998
    /* set the original statement position in the node */
9999
    if_stm->set_source_pos(file, linenum);
10000
10001
    /* return the 'if' statement node */
10002
    return if_stm;
10003
}
10004
10005
/*
10006
 *   Parse a 'return' statement 
10007
 */
10008
CTPNStm *CTcParser::parse_return(int *err)
10009
{
10010
    CTPNStm *stm;
10011
    
10012
    /* skip the 'return' keyword and see what we have */
10013
    switch(G_tok->next())
10014
    {
10015
    case TOKT_SEM:
10016
        /* 
10017
         *   end of the statement - this is a void return; skip the
10018
         *   semicolon, and return a void return statement node 
10019
         */
10020
        G_tok->next();
10021
        stm = new CTPNStmReturn(0);
10022
        break;
10023
10024
    case TOKT_LBRACE:
10025
    case TOKT_RBRACE:
10026
        /* 
10027
         *   they probably just left out a semicolon - flag the error, and
10028
         *   continue parsing from this token 
10029
         */
10030
        G_tok->log_error_curtok(TCERR_RET_REQ_EXPR);
10031
        return 0;
10032
10033
    default:
10034
        /* it's a return with an expression - parse the expression */
10035
        stm = new CTPNStmReturn(parse_expr());
10036
10037
        /* make sure we're on a semicolon */
10038
        if (parse_req_sem())
10039
        {
10040
            *err = TRUE;
10041
            return 0;
10042
        }
10043
10044
        /* done */
10045
        break;
10046
    }
10047
10048
    /* return the statement node we created */
10049
    return stm;
10050
}
10051
10052
/* 
10053
 *   Parse a 'for' statement 
10054
 */
10055
CTPNStm *CTcParser::parse_for(int *err)
10056
{
10057
    tcprs_scope_t scope_data;
10058
    int done;
10059
    CTcPrsNode *init_expr;
10060
    CTcPrsNode *cond_expr;
10061
    CTcPrsNode *reinit_expr;
10062
    CTPNStm *body_stm;
10063
    CTPNStmFor *for_stm;
10064
    CTcTokFileDesc *file;
10065
    long linenum;
10066
    CTPNStmEnclosing *old_enclosing;
10067
    
10068
    /* save the current line information for later */
10069
    G_tok->get_last_pos(&file, &linenum);
10070
10071
    /* 
10072
     *   enter a scope, in case we create a local symbol table for local
10073
     *   variables defined within the 'for' statement 
10074
     */
10075
    enter_scope(&scope_data);
10076
10077
    /* parse the open paren */
10078
    if (G_tok->next() == TOKT_LPAR)
10079
    {
10080
        /* skip it */
10081
        G_tok->next();
10082
    }
10083
    else
10084
    {
10085
        /* log an error, and proceed, assuming it was simply left out */
10086
        G_tok->log_error_curtok(TCERR_REQ_FOR_LPAR);
10087
    }
10088
10089
    /* we don't have any of the expressions yet */
10090
    init_expr = 0;
10091
    cond_expr = 0;
10092
    reinit_expr = 0;
10093
10094
    /* parse the initializer list */
10095
    for (done = FALSE ; !done ; )
10096
    {
10097
        CTcPrsNode *expr;
10098
10099
        /* presume we won't find an expression on this round */
10100
        expr = 0;
10101
        
10102
        /* check what we have */
10103
        switch(G_tok->cur())
10104
        {
10105
        case TOKT_LOCAL:
10106
            /* 
10107
             *   if we haven't created our own symbol table local to the
10108
             *   'for' loop, do so now 
10109
             */
10110
            create_scope_local_symtab();
10111
            
10112
            /* skip the 'local' keyword and get the local name */
10113
            if (G_tok->next() == TOKT_SYM)
10114
            {
10115
                CTcSymLocal *lcl;
10116
                
10117
                /* add the local symbol */
10118
                lcl = local_symtab_
10119
                      ->add_local(G_tok->getcur()->get_text(),
10120
                                  G_tok->getcur()->get_text_len(),
10121
                                  alloc_local(), FALSE, FALSE, FALSE);
10122
10123
                /* check for the required initializer */
10124
                switch(G_tok->next())
10125
                {
10126
                case TOKT_ASI:
10127
                case TOKT_EQ:
10128
                    /* parse the initializer */
10129
                    expr = parse_local_initializer(lcl, err);
10130
                    break;
10131
                    
10132
                default:
10133
                    /* log an error - an initializer is required */
10134
                    G_tok->log_error_curtok(TCERR_REQ_FOR_LOCAL_INIT);
10135
                    break;
10136
                }
10137
            }
10138
            else
10139
            {
10140
                /* 
10141
                 *   the 'local' statement isn't constructed properly -
10142
                 *   this is difficult to recover from intelligently, so
10143
                 *   just log an error and keep going from here 
10144
                 */
10145
                G_tok->log_error_curtok(TCERR_LOCAL_REQ_SYM);
10146
                break;
10147
            }
10148
            break;
10149
10150
        case TOKT_SEM:
10151
            /* it's a semicolon - we're done with the initializer list */
10152
            done = TRUE;
10153
10154
            /* 
10155
             *   if we have an expression already, it means that the
10156
             *   previous token was a comma - this is an error, since we
10157
             *   have a missing expression; log the error but continue
10158
             *   anyway 
10159
             */
10160
            if (init_expr != 0)
10161
                G_tok->log_error(TCERR_MISSING_FOR_INIT_EXPR);
10162
            break;
10163
10164
        case TOKT_RPAR:
10165
        case TOKT_LBRACE:
10166
        case TOKT_RBRACE:
10167
            /* premature end of the list - log an error and stop */
10168
            G_tok->log_error_curtok(TCERR_MISSING_FOR_PART);
10169
            done = TRUE;
10170
            break;
10171
10172
        default:
10173
            /* 
10174
             *   This must be an expression - parse it.  Parse an
10175
             *   assignment expression, not a comma expression, because we
10176
             *   must check for a "local" clause after each comma. 
10177
             */
10178
            expr = parse_asi_expr();
10179
10180
            /* if that failed, stop scanning the "for" */
10181
            if (expr == 0)
10182
                done = TRUE;
10183
            break;
10184
        }
10185
10186
        /* if we're done, we can stop now */
10187
        if (done)
10188
            break;
10189
10190
        /* 
10191
         *   if we got an expression, add it into the initializer
10192
         *   expression under construction by adding it under a "comma"
10193
         *   node 
10194
         */
10195
        if (expr != 0)
10196
        {
10197
            /* 
10198
             *   if there's an expression, build a comma expression for
10199
             *   the expression so far plus the new expression; otherwise,
10200
             *   the new expression becomes the entire expression so far 
10201
             */
10202
            if (init_expr != 0)
10203
                init_expr = new CTPNComma(init_expr, expr);
10204
            else
10205
                init_expr = expr;
10206
        }
10207
10208
        /* 
10209
         *   we must have a semicolon or comma after each initializer
10210
         *   expression 
10211
         */
10212
        switch(G_tok->cur())
10213
        {
10214
        case TOKT_SEM:
10215
            /* that's the end of the statement - stop now */
10216
            done = TRUE;
10217
            break;
10218
10219
        case TOKT_COMMA:
10220
            /* skip the comma and parse the next initializer */
10221
            G_tok->next();
10222
            break;
10223
10224
        case TOKT_RPAR:
10225
        case TOKT_LBRACE:
10226
        case TOKT_RBRACE:
10227
            /* log an error, and stop parsing the expression list */
10228
            G_tok->log_error_curtok(TCERR_MISSING_FOR_PART);
10229
            done = TRUE;
10230
            break;
10231
10232
        default:
10233
            /* log an error */
10234
            G_tok->log_error_curtok(TCERR_REQ_FOR_INIT_COMMA);
10235
10236
            /* skip the errant token and keep going */
10237
            G_tok->next();
10238
            break;
10239
        }
10240
    }
10241
10242
    /* 
10243
     *   if we successfully found the ';' at the end of the initializer
10244
     *   list, parse the condition expression 
10245
     */
10246
    if (G_tok->cur() == TOKT_SEM)
10247
    {
10248
        int cont_to_reinit;
10249
10250
        /* presume we'll want to continue to the reinit expression */
10251
        cont_to_reinit = TRUE;
10252
10253
        /* skip the ';' */
10254
        G_tok->next();
10255
        
10256
        /* if the condition isn't empty, parse it */
10257
        if (G_tok->cur() != TOKT_SEM)
10258
            cond_expr = parse_cond_expr();
10259
10260
        /* require the ';' after the expression */
10261
        switch(G_tok->cur())
10262
        {
10263
        case TOKT_SEM:
10264
            /* it's fine - keep going from here */
10265
            G_tok->next();
10266
            break;
10267
10268
        case TOKT_RPAR:
10269
        case TOKT_LBRACE:
10270
        case TOKT_RBRACE:
10271
            /* missing part */
10272
            G_tok->log_error_curtok(TCERR_MISSING_FOR_PART);
10273
10274
            /* don't bother trying to find a reinitialization expression */
10275
            cont_to_reinit = FALSE;
10276
            break;
10277
10278
        default:
10279
            /* 
10280
             *   we seem to be missing the semicolon; keep going from
10281
             *   here, assuming that they simply left out the semicolon
10282
             *   between the condition and reinitializer expressions 
10283
             */
10284
            G_tok->log_error_curtok(TCERR_REQ_FOR_COND_SEM);
10285
            break;
10286
        }
10287
10288
        /* 
10289
         *   if we're to continue to the reinitializer, parse it; there is
10290
         *   no reinitialization expression if the next token is a right
10291
         *   paren 
10292
         */
10293
        if (cont_to_reinit && G_tok->cur() != TOKT_RPAR)
10294
        {
10295
            /* parse the expression */
10296
            reinit_expr = parse_expr();
10297
        }
10298
10299
        /* make sure we have the right paren */
10300
        if (G_tok->cur() == TOKT_RPAR)
10301
        {
10302
            /* skip the paren */
10303
            G_tok->next();
10304
        }
10305
        else
10306
        {
10307
            /* 
10308
             *   log an error, and try parsing the body from here, on the
10309
             *   assumption that they simply forgot about the right paren
10310
             *   and jumped right into the body 
10311
             */
10312
            G_tok->log_error_curtok(TCERR_REQ_FOR_RPAR);
10313
        }
10314
    }
10315
    else if (G_tok->cur() == TOKT_RPAR)
10316
    {
10317
        /* 
10318
         *   we already found the right paren - early, so we logged an
10319
         *   error - simply skip it now so that we can proceed to the body
10320
         *   of the 'for' 
10321
         */
10322
        G_tok->next();
10323
    }
10324
10325
    /* create the "for" node */
10326
    for_stm = new CTPNStmFor(init_expr, cond_expr, reinit_expr,
10327
                             local_symtab_, enclosing_stm_);
10328
10329
    /* set the 'for' to enclose its body */
10330
    old_enclosing = set_enclosing_stm(for_stm);
10331
10332
    /* parse the body of the "for" loop */
10333
    body_stm = parse_stm(err, 0, FALSE);
10334
10335
    /* restore the old enclosing statement */
10336
    set_enclosing_stm(old_enclosing);
10337
10338
    /* if that failed, return failure */
10339
    if (*err)
10340
        return 0;
10341
10342
    /* set the body of the 'for' */
10343
    for_stm->set_body(body_stm);
10344
10345
    /* set the original statement position in the node */
10346
    for_stm->set_source_pos(file, linenum);
10347
10348
    /* set the own-scope flag */
10349
    for_stm->set_has_own_scope(local_symtab_ != scope_data.local_symtab);
10350
10351
    /* exit any local scope we created */
10352
    leave_scope(&scope_data);
10353
10354
    /* return the "for" node */
10355
    return for_stm;
10356
}
10357
10358
/* 
10359
 *   Parse a 'foreach' statement 
10360
 */
10361
CTPNStm *CTcParser::parse_foreach(int *err)
10362
{
10363
    tcprs_scope_t scope_data;
10364
    CTcPrsNode *iter_expr;
10365
    CTcPrsNode *coll_expr;
10366
    CTPNStm *body_stm;
10367
    CTPNStmForeach *foreach_stm;
10368
    CTcTokFileDesc *file;
10369
    long linenum;
10370
    CTPNStmEnclosing *old_enclosing;
10371
10372
    /* save the current line information for later */
10373
    G_tok->get_last_pos(&file, &linenum);
10374
10375
    /* 
10376
     *   enter a scope, in case we create a local symbol table for local
10377
     *   variables defined within the 'for' statement 
10378
     */
10379
    enter_scope(&scope_data);
10380
10381
    /* parse the open paren */
10382
    if (G_tok->next() == TOKT_LPAR)
10383
    {
10384
        /* skip it */
10385
        G_tok->next();
10386
    }
10387
    else
10388
    {
10389
        /* log an error, and proceed, assuming it was simply left out */
10390
        G_tok->log_error_curtok(TCERR_REQ_FOREACH_LPAR);
10391
    }
10392
10393
    /* we don't have the iterator lvalue or collection expression yet */
10394
    iter_expr = 0;
10395
    coll_expr = 0;
10396
10397
    /* check for 'local' before the iteration variable */
10398
    switch (G_tok->cur())
10399
    {
10400
    case TOKT_LOCAL:
10401
        /* 
10402
         *   if we haven't created our own symbol table local to the 'for'
10403
         *   loop, do so now 
10404
         */
10405
        create_scope_local_symtab();
10406
10407
        /* skip the 'local' keyword and get the local name */
10408
        if (G_tok->next() == TOKT_SYM)
10409
        {
10410
            /* add the local symbol */
10411
            local_symtab_->add_local(G_tok->getcur()->get_text(),
10412
                                     G_tok->getcur()->get_text_len(),
10413
                                     alloc_local(), FALSE, FALSE, FALSE);
10414
        }
10415
        else
10416
        {
10417
            /* log the error */
10418
            G_tok->log_error_curtok(TCERR_LOCAL_REQ_SYM);
10419
        }
10420
10421
        /* go handle the local as the iteration expression */
10422
        goto do_expr;
10423
10424
    case TOKT_LPAR:
10425
    case TOKT_SYM:
10426
    do_expr:
10427
        /* parse the iterator lvalue expression */
10428
        iter_expr = parse_expr();
10429
        if (iter_expr == 0)
10430
        {
10431
            *err = TRUE;
10432
            return 0;
10433
        }
10434
        break;
10435
        
10436
    default:
10437
        /* premature end of the list - log an error and stop */
10438
        G_tok->log_error_curtok(TCERR_MISSING_FOREACH_EXPR);
10439
        return 0;
10440
    }
10441
10442
    /* require the 'in' keyword */
10443
    if (G_tok->cur() != TOKT_SYM || !G_tok->getcur()->text_matches("in", 2))
10444
    {
10445
        /* log an error */
10446
        G_tok->log_error_curtok(TCERR_FOREACH_REQ_IN);
10447
10448
        /* see what we have */
10449
        switch(G_tok->cur())
10450
        {
10451
        case TOKT_LBRACE:
10452
        case TOKT_RBRACE:
10453
        case TOKT_SEM:
10454
        case TOKT_EOF:
10455
            /* probably end of statement */
10456
            return 0;
10457
            
10458
        case TOKT_RPAR:
10459
            /* 
10460
             *   probably an extra paren in the variable expression - skip
10461
             *   the paren and continue 
10462
             */
10463
            G_tok->next();
10464
            break;
10465
10466
        default:
10467
            /* probably just left out 'in' - continue from here */
10468
            break;
10469
        }
10470
    }
10471
    else
10472
    {
10473
        /* skip the 'in' */
10474
        G_tok->next();
10475
    }
10476
10477
    /* parse the collection expression */
10478
    coll_expr = parse_expr();
10479
    if (coll_expr == 0)
10480
    {
10481
        *err = TRUE;
10482
        return 0;
10483
    }
10484
10485
    /* make sure we have the close paren */
10486
    if (G_tok->cur() != TOKT_RPAR)
10487
    {
10488
        /* 
10489
         *   log the error, but continue from here on the assumption that
10490
         *   they simply left out the paren 
10491
         */
10492
        G_tok->log_error_curtok(TCERR_REQ_FOREACH_RPAR);
10493
    }
10494
    else
10495
    {
10496
        /* skip the paren */
10497
        G_tok->next();
10498
    }
10499
10500
    /* 
10501
     *   create the "foreach" node, allocating a private local variable
10502
     *   for holding the iterator object 
10503
     */
10504
    foreach_stm = new CTPNStmForeach(iter_expr, coll_expr, 
10505
                                     local_symtab_, enclosing_stm_,
10506
                                     alloc_local());
10507
10508
    /* set the "foreach" node to enclose its body */
10509
    old_enclosing = set_enclosing_stm(foreach_stm);
10510
10511
    /* parse the body of the loop */
10512
    body_stm = parse_stm(err, 0, FALSE);
10513
10514
    /* restore the old enclosing statement */
10515
    set_enclosing_stm(old_enclosing);
10516
10517
    /* if that failed, return failure */
10518
    if (*err)
10519
        return 0;
10520
10521
    /* set the body of the 'for' */
10522
    foreach_stm->set_body(body_stm);
10523
10524
    /* set the original statement position in the node */
10525
    foreach_stm->set_source_pos(file, linenum);
10526
10527
    /* set the own-scope flag */
10528
    foreach_stm->set_has_own_scope(local_symtab_ != scope_data.local_symtab);
10529
10530
    /* exit any local scope we created */
10531
    leave_scope(&scope_data);
10532
10533
    /* return the new statement node */
10534
    return foreach_stm;
10535
}
10536
10537
/*
10538
 *   Parse a 'break' statement 
10539
 */
10540
CTPNStm *CTcParser::parse_break(int *err)
10541
{
10542
    CTPNStmBreak *brk_stm;
10543
10544
    /* create the 'break' statement */
10545
    brk_stm = new CTPNStmBreak();
10546
10547
    /* skip the 'break' keyword and check what follows */
10548
    switch(G_tok->next())
10549
    {
10550
    case TOKT_SYM:
10551
        /* set the label in the statement */
10552
        brk_stm->set_label(G_tok->getcur());
10553
10554
        /* skip the label token */
10555
        G_tok->next();
10556
        break;
10557
10558
    case TOKT_SEM:
10559
        /* keep going - we'll skip it in a moment */
10560
        break;
10561
10562
    case TOKT_LBRACE:
10563
    case TOKT_RBRACE:
10564
        /* 
10565
         *   they almost certainly simply left off the semicolon - don't
10566
         *   bother with a "label expected" error, since the real error is
10567
         *   most likely just "missing semicolon" 
10568
         */
10569
        break;
10570
10571
    default:
10572
        /* log the error */
10573
        G_tok->log_error_curtok(TCERR_BREAK_REQ_LABEL);
10574
        break;
10575
    }
10576
10577
    /* parse the required terminating semicolon */
10578
    if (parse_req_sem())
10579
    {
10580
        *err = TRUE;
10581
        return 0;
10582
    }
10583
10584
    /* return the 'break' node */
10585
    return brk_stm;
10586
}
10587
10588
/*
10589
 *   Parse a 'continue' statement 
10590
 */
10591
CTPNStm *CTcParser::parse_continue(int *err)
10592
{
10593
    CTPNStmContinue *cont_stm;
10594
10595
    /* create the 'break' statement */
10596
    cont_stm = new CTPNStmContinue();
10597
10598
    /* skip the 'continue' keyword and check what follows */
10599
    switch(G_tok->next())
10600
    {
10601
    case TOKT_SYM:
10602
        /* set the label in the statement */
10603
        cont_stm->set_label(G_tok->getcur());
10604
10605
        /* skip the label token */
10606
        G_tok->next();
10607
        break;
10608
10609
    case TOKT_SEM:
10610
        /* keep going - we'll skip it in a moment */
10611
        break;
10612
10613
    case TOKT_LBRACE:
10614
    case TOKT_RBRACE:
10615
        /* 
10616
         *   they almost certainly simply left off the semicolon - don't
10617
         *   bother with a "label expected" error, since the real error is
10618
         *   most likely just "missing semicolon" 
10619
         */
10620
        break;
10621
10622
    default:
10623
        /* log the error */
10624
        G_tok->log_error_curtok(TCERR_CONT_REQ_LABEL);
10625
        break;
10626
    }
10627
10628
    /* parse the required terminating semicolon */
10629
    if (parse_req_sem())
10630
    {
10631
        *err = TRUE;
10632
        return 0;
10633
    }
10634
10635
    /* return the 'continue' node */
10636
    return cont_stm;
10637
}
10638
10639
/* 
10640
 *   Parse a 'while' statement 
10641
 */
10642
CTPNStm *CTcParser::parse_while(int *err)
10643
{
10644
    CTcPrsNode *expr;
10645
    CTPNStm *body_stm;
10646
    CTPNStmWhile *while_stm;
10647
    CTPNStmEnclosing *old_enclosing;
10648
    
10649
    /* skip the 'while' and check for the open paren */
10650
    if (G_tok->next() == TOKT_LPAR)
10651
    {
10652
        /* skip the paren */
10653
        G_tok->next();
10654
    }
10655
    else
10656
    {
10657
        /* 
10658
         *   log an error, and proceed on the assumption that the paren
10659
         *   was simply left out and the statement is otherwise
10660
         *   well-formed 
10661
         */
10662
        G_tok->log_error_curtok(TCERR_REQ_WHILE_LPAR);
10663
    }
10664
10665
    /* parse the condition expression */
10666
    expr = parse_cond_expr();
10667
    if (expr == 0)
10668
    {
10669
        *err = TRUE;
10670
        return 0;
10671
    }
10672
10673
    /* create the 'while' statement node */
10674
    while_stm = new CTPNStmWhile(expr, enclosing_stm_);
10675
10676
    /* check for the close paren */
10677
    if (G_tok->cur() == TOKT_RPAR)
10678
    {
10679
        /* skip the paren */
10680
        G_tok->next();
10681
    }
10682
    else
10683
    {
10684
        /* log an error, and continue from here */
10685
        G_tok->log_error_curtok(TCERR_REQ_WHILE_RPAR);
10686
    }
10687
10688
    /* set the 'while' to enclose its body */
10689
    old_enclosing = set_enclosing_stm(while_stm);
10690
10691
    /* parse the loop body */
10692
    body_stm = parse_stm(err, 0, FALSE);
10693
10694
    /* restore the old enclosing statement */
10695
    set_enclosing_stm(old_enclosing);
10696
10697
    /* give up on error */
10698
    if (*err)
10699
        return 0;
10700
10701
    /* set the body */
10702
    while_stm->set_body(body_stm);
10703
10704
    /* that's it - build and return the 'while' node */
10705
    return while_stm;
10706
}
10707
10708
/* 
10709
 *   Parse a 'do-while' statement 
10710
 */
10711
CTPNStm *CTcParser::parse_do_while(int *err)
10712
{
10713
    CTPNStm *body_stm;
10714
    CTcPrsNode *expr;
10715
    CTPNStmDoWhile *do_stm;
10716
    CTPNStmEnclosing *old_enclosing;
10717
    
10718
    /* create the statement object */
10719
    do_stm = new CTPNStmDoWhile(enclosing_stm_);
10720
10721
    /* skip the 'do' keyword */
10722
    G_tok->next();
10723
10724
    /* set the 'do' to be the enclosing statement */
10725
    old_enclosing = set_enclosing_stm(do_stm);
10726
10727
    /* parse the loop body */
10728
    body_stm = parse_stm(err, 0, FALSE);
10729
10730
    /* restore the enclosing statement */
10731
    set_enclosing_stm(old_enclosing);
10732
10733
    /* return on failure */
10734
    if (*err)
10735
        return 0;
10736
10737
    /* require the 'while' keyword */
10738
    if (G_tok->cur() == TOKT_WHILE)
10739
    {
10740
        /* skip the 'while' */
10741
        G_tok->next();
10742
    }
10743
    else
10744
    {
10745
        /* 
10746
         *   no 'while' keyword - there's no obvious way to correct this,
10747
         *   so simply ignore the 'do' statement and keep going from here,
10748
         *   on the assumption that they inadvertantly started a new
10749
         *   statement without finishing the 'do' 
10750
         */
10751
        G_tok->log_error_curtok(TCERR_REQ_DO_WHILE);
10752
    }
10753
10754
    /* require the open paren */
10755
    if (G_tok->cur() == TOKT_LPAR)
10756
        G_tok->next();
10757
    else
10758
        G_tok->log_error_curtok(TCERR_REQ_WHILE_LPAR);
10759
10760
    /* parse the expression */
10761
    expr = parse_cond_expr();
10762
    if (expr == 0)
10763
    {
10764
        *err = TRUE;
10765
        return 0;
10766
    }
10767
10768
    /* require the close paren */
10769
    if (G_tok->cur() == TOKT_RPAR)
10770
        G_tok->next();
10771
    else
10772
        G_tok->log_error_curtok(TCERR_REQ_WHILE_RPAR);
10773
10774
    /* set the condition expression and body in the 'do' node */
10775
    do_stm->set_cond(expr);
10776
    do_stm->set_body(body_stm);
10777
10778
    /* 
10779
     *   remember the location of the 'while' part, since this part
10780
     *   generates code 
10781
     */
10782
    do_stm->set_while_pos(G_tok->get_last_desc(), G_tok->get_last_linenum());
10783
10784
    /* parse the required closing semicolon */
10785
    if (parse_req_sem())
10786
    {
10787
        *err = TRUE;
10788
        return 0;
10789
    }
10790
10791
    /* return the new 'do-while' node */
10792
    return do_stm;
10793
}
10794
10795
/* 
10796
 *   Parse a 'switch' statement 
10797
 */
10798
CTPNStm *CTcParser::parse_switch(int *err)
10799
{
10800
    CTcPrsNode *expr;
10801
    CTPNStmSwitch *switch_stm;
10802
    CTPNStm *body_stm;
10803
    int skip;
10804
    int unreachable_error_shown;
10805
    CTPNStmEnclosing *old_enclosing;
10806
    
10807
    /* create the switch statement object */
10808
    switch_stm = new CTPNStmSwitch(enclosing_stm_);
10809
10810
    /* skip the 'switch' and check for the left paren */
10811
    if (G_tok->next() == TOKT_LPAR)
10812
    {
10813
        /* skip the left paren */
10814
        G_tok->next();
10815
    }
10816
    else
10817
    {
10818
        /* log an error, and assume the paren is simply missing */
10819
        G_tok->log_error_curtok(TCERR_REQ_SWITCH_LPAR);
10820
    }
10821
10822
    /* parse the controlling expression */
10823
    expr = parse_expr();
10824
    if (expr == 0)
10825
    {
10826
        *err = TRUE;
10827
        return 0;
10828
    }
10829
10830
    /* set expression in the switch statement node */
10831
    switch_stm->set_expr(expr);
10832
10833
    /* check for and skip the close paren */
10834
    if (G_tok->cur() == TOKT_RPAR)
10835
    {
10836
        /* the right paren is present - skip it */
10837
        G_tok->next();
10838
    }
10839
    else
10840
    {
10841
        /* log an error, and keep going from here */
10842
        G_tok->log_error_curtok(TCERR_REQ_SWITCH_RPAR);
10843
    }
10844
10845
    /* check for and skip the brace */
10846
    if (G_tok->cur() == TOKT_LBRACE)
10847
    {
10848
        /* it's there - skip it */
10849
        G_tok->next();
10850
    }
10851
    else
10852
    {
10853
        /* 
10854
         *   log an error, and keep going on the assumption that the brace
10855
         *   is simply missing but the switch body is otherwise correct 
10856
         */
10857
        G_tok->log_error_curtok(TCERR_REQ_SWITCH_LBRACE);
10858
    }
10859
10860
    /* 
10861
     *   The first thing in the switch body must be a 'case', 'default',
10862
     *   or closing brace.  Other statements preceding the first 'case' or
10863
     *   'default' label within the switch body are not allowed, because
10864
     *   they would be unreachable.  Keep skipping statements until we get
10865
     *   to one of these.  
10866
     */
10867
    for (skip = TRUE, unreachable_error_shown = FALSE ; skip ; )
10868
    {
10869
        /* see what we have */
10870
        switch(G_tok->cur())
10871
        {
10872
        case TOKT_CASE:
10873
        case TOKT_DEFAULT:
10874
        case TOKT_RBRACE:
10875
            /* this is what we're looking for */
10876
            skip = FALSE;
10877
            break;
10878
10879
        case TOKT_EOF:
10880
            /* end of file within the switch - log an error */
10881
            G_tok->log_error(TCERR_EOF_IN_SWITCH);
10882
10883
            /* return failure */
10884
            *err = TRUE;
10885
            return 0;
10886
10887
        default:
10888
            /* 
10889
             *   for anything else, log an error explaining that the code
10890
             *   is unreachable - do this only once, no matter how many
10891
             *   unreachable statements precede the first case label 
10892
             */
10893
            if (!unreachable_error_shown && !G_prs->get_syntax_only())
10894
            {
10895
                /* show the error */
10896
                G_tok->log_error(TCERR_UNREACHABLE_CODE_IN_SWITCH);
10897
                
10898
                /* 
10899
                 *   note that we've shown the error, so we don't show it
10900
                 *   again if more unreachable statements follow
10901
                 */
10902
                unreachable_error_shown = TRUE;
10903
            }
10904
10905
            /* parse (and ignore) this statement */
10906
            parse_stm(err, switch_stm, FALSE);
10907
            if (*err != 0)
10908
                return 0;
10909
10910
            /* keep looking for the first label */
10911
            break;
10912
        }
10913
    }
10914
10915
    /* the 'switch' is the enclosing statement for children */
10916
    old_enclosing = set_enclosing_stm(switch_stm);
10917
10918
    /* parse the switch body */
10919
    body_stm = parse_compound(err, FALSE, switch_stm, FALSE);
10920
10921
    /* restore the enclosing statement */
10922
    set_enclosing_stm(old_enclosing);
10923
10924
    /* if we failed to parse the compound statement, give up */
10925
    if (*err)
10926
        return 0;
10927
10928
    /* connect the switch to its body */
10929
    switch_stm->set_body(body_stm);
10930
10931
    /* return the switch statement node */
10932
    return switch_stm;
10933
}
10934
10935
/*
10936
 *   Parse a 'case' label 
10937
 */
10938
CTPNStm *CTcParser::parse_case(int *err, CTPNStmSwitch *enclosing_switch)
10939
{
10940
    CTcPrsNode *expr;
10941
    CTPNStm *stm;
10942
    CTPNStmCase *case_stm;
10943
    
10944
    /* skip the 'case' keyword */
10945
    G_tok->next();
10946
10947
    /* create the 'case' statement node */
10948
    case_stm = new CTPNStmCase();
10949
10950
    /* parse the expression */
10951
    expr = parse_expr();
10952
    if (expr == 0)
10953
    {
10954
        *err = TRUE;
10955
        return 0;
10956
    }
10957
10958
    /* store the expression in the case statement node */
10959
    case_stm->set_expr(expr);
10960
10961
    /* require the colon */
10962
    if (G_tok->cur() == TOKT_COLON)
10963
    {
10964
        /* skip the colon */
10965
        G_tok->next();
10966
    }
10967
    else
10968
    {
10969
        /* log the error */
10970
        G_tok->log_error_curtok(TCERR_REQ_CASE_COLON);
10971
    }
10972
10973
    /* 
10974
     *   parse the labeled statement - it's directly within this same
10975
     *   enclosing switch, because a case label doesn't create a new
10976
     *   expression nesting level (hence another 'case' label immediately
10977
     *   following without an intervening statement is perfectly valid) 
10978
     */
10979
    stm = parse_stm(err, enclosing_switch, FALSE);
10980
10981
    /* set the statement in the case node */
10982
    case_stm->set_stm(stm);
10983
10984
    /* count the 'case' label in the 'switch' node */
10985
    enclosing_switch->inc_case_cnt();
10986
10987
    /* return the case node */
10988
    return case_stm;
10989
}
10990
10991
/*
10992
 *   Parse a 'default' label 
10993
 */
10994
CTPNStm *CTcParser::parse_default(int *err, CTPNStmSwitch *enclosing_switch)
10995
{
10996
    CTPNStm *stm;
10997
    CTPNStmDefault *default_stm;
10998
10999
    /* create the 'default' statement node */
11000
    default_stm = new CTPNStmDefault();
11001
11002
    /* 
11003
     *   if the enclosing 'switch' already has a 'default' label, it's an
11004
     *   error; continue anyway, since we still want to finish parsing the
11005
     *   syntax 
11006
     */
11007
    if (enclosing_switch->get_has_default())
11008
        G_tok->log_error(TCERR_DEFAULT_REDEF);
11009
11010
    /* mark the switch as having a 'default' case */
11011
    enclosing_switch->set_has_default();
11012
11013
    /* skip the 'default' node, and require the colon */
11014
    if (G_tok->next() == TOKT_COLON)
11015
    {
11016
        /* skip the colon */
11017
        G_tok->next();
11018
    }
11019
    else
11020
    {
11021
        /* 
11022
         *   log an error, and keep going, assuming that the token was
11023
         *   accidentally omitted 
11024
         */
11025
        G_tok->log_error_curtok(TCERR_REQ_DEFAULT_COLON);
11026
    }
11027
11028
    /* 
11029
     *   parse the labeled statement - it's directly within this same
11030
     *   enclosing switch, because a 'default' label doesn't create a new
11031
     *   expression nesting level (hence another 'case' label immediately
11032
     *   following without an intervening statement is perfectly valid) 
11033
     */
11034
    stm = parse_stm(err, enclosing_switch, FALSE);
11035
11036
    /* set the statement in the 'default' node */
11037
    default_stm->set_stm(stm);
11038
11039
    /* return the 'default' node */
11040
    return default_stm;
11041
}
11042
11043
/* 
11044
 *   Parse a 'goto' statement 
11045
 */
11046
CTPNStm *CTcParser::parse_goto(int *err)
11047
{
11048
    CTPNStmGoto *goto_stm;
11049
    
11050
    /* skip the 'goto' keyword, and demand that a symbol follows */
11051
    if (G_tok->next() == TOKT_SYM)
11052
    {
11053
        /* create the parse node for the 'goto' statement */
11054
        goto_stm = new CTPNStmGoto(G_tok->getcur()->get_text(),
11055
                                   G_tok->getcur()->get_text_len());
11056
        
11057
        /* skip the symbol */
11058
        G_tok->next();
11059
    }
11060
    else
11061
    {
11062
        /* log the error */
11063
        G_tok->log_error(TCERR_GOTO_REQ_LABEL);
11064
11065
        /* no statement */
11066
        goto_stm = 0;
11067
    }
11068
11069
    /* parse the required semicolon */
11070
    if (parse_req_sem())
11071
    {
11072
        *err = TRUE;
11073
        return 0;
11074
    }
11075
11076
    /* return the statement node */
11077
    return goto_stm;
11078
}
11079
11080
/* 
11081
 *   Parse a 'try-catch-finally' statement 
11082
 */
11083
CTPNStm *CTcParser::parse_try(int *err)
11084
{
11085
    CTPNStmTry *try_stm;
11086
    CTPNStmEnclosing *old_enclosing;
11087
    CTPNStm *body_stm;
11088
11089
    /* create the 'try' statement node */
11090
    try_stm = new CTPNStmTry(enclosing_stm_);
11091
11092
    /* 
11093
     *   the 'try' is the enclosing statement for the duration of its
11094
     *   protected code block 
11095
     */
11096
    old_enclosing = set_enclosing_stm(try_stm);
11097
    
11098
    /* skip the 'try' */
11099
    G_tok->next();
11100
11101
    /* parse the body of the 'try' block */
11102
    body_stm = parse_stm(err, 0, FALSE);
11103
11104
    /* restore the previous enclosing statement */
11105
    set_enclosing_stm(old_enclosing);
11106
11107
    /* if parsing the body failed, stop now */
11108
    if (*err)
11109
        return 0;
11110
11111
    /* add the body to the 'try' */
11112
    try_stm->set_body_stm(body_stm);
11113
11114
    /* 
11115
     *   check for 'catch' clauses - there could be several, so keep going
11116
     *   until we stop seeing 'catch' keywords 
11117
     */
11118
    while (G_tok->cur() == TOKT_CATCH)
11119
    {
11120
        int catch_has_err;
11121
        CTPNStm *catch_body;
11122
        CTPNStmCatch *catch_stm;
11123
        tcprs_scope_t scope_data;
11124
11125
        /* create a local scope for the 'catch' clause */
11126
        enter_scope(&scope_data);
11127
        create_scope_local_symtab();
11128
        
11129
        /* create the 'catch' statement node */
11130
        catch_stm = new CTPNStmCatch();
11131
11132
        /* 
11133
         *   set the 'catch' clause's source position independently of the
11134
         *   overall 'try' statement, so that the debugger can track entry
11135
         *   into this clause 
11136
         */
11137
        catch_stm->set_source_pos(G_tok->get_last_desc(),
11138
                                  G_tok->get_last_linenum());
11139
11140
        /* presume we'll parse this successfully */
11141
        catch_has_err = FALSE;
11142
            
11143
        /* skip the 'catch' keyword and check for the left paren */
11144
        if (G_tok->next() == TOKT_LPAR)
11145
        {
11146
            /* skip the paren */
11147
            G_tok->next();
11148
        }
11149
        else
11150
        {
11151
            /* log the error */
11152
            G_tok->log_error_curtok(TCERR_REQ_CATCH_LPAR);
11153
        }
11154
11155
        /* get the exception class token */
11156
        if (G_tok->cur() == TOKT_SYM)
11157
        {
11158
            /* set the class name in the 'catch' clause */
11159
            catch_stm->set_exc_class(G_tok->getcur());
11160
11161
            /* move on */
11162
            G_tok->next();
11163
        }
11164
        else
11165
        {
11166
            /* flag the problem */
11167
            G_tok->log_error_curtok(TCERR_REQ_CATCH_CLASS);
11168
11169
            /* unless this is a close paren, skip the errant token */
11170
            if (G_tok->cur() != TOKT_RPAR)
11171
                G_tok->next();
11172
11173
            /* note the error */
11174
            catch_has_err = TRUE;
11175
        }
11176
11177
        /* get the variable name token */
11178
        if (G_tok->cur() == TOKT_SYM)
11179
        {
11180
            CTcSymLocal *var;
11181
            
11182
            /* 
11183
             *   create the local variable - note that this variable is
11184
             *   implicitly assigned when the 'catch' clause is entered, so
11185
             *   mark it as initially assigned; we don't care if the
11186
             *   variable is ever used, so also mark it as used 
11187
             */
11188
            var = local_symtab_->add_local(G_tok->getcur()->get_text(),
11189
                                           G_tok->getcur()->get_text_len(),
11190
                                           alloc_local(),
11191
                                           FALSE, TRUE, TRUE);
11192
            
11193
            /* set the variable in the 'catch' clause */
11194
            if (!catch_has_err)
11195
                catch_stm->set_exc_var(var);
11196
11197
            /* move on */
11198
            G_tok->next();
11199
        }
11200
        else
11201
        {
11202
            /* flag the problem */
11203
            G_tok->log_error_curtok(TCERR_REQ_CATCH_VAR);
11204
11205
            /* unless this is a close paren, skip the errant token */
11206
            if (G_tok->cur() != TOKT_RPAR)
11207
                G_tok->next();
11208
11209
            /* note the error */
11210
            catch_has_err = TRUE;
11211
        }
11212
11213
        /* check for the close paren */
11214
        if (G_tok->cur() == TOKT_RPAR)
11215
        {
11216
            /* skip the paren */
11217
            G_tok->next();
11218
        }
11219
        else
11220
        {
11221
            /* 
11222
             *   log the error and continue, assuming that the paren is
11223
             *   simply missing and things are otherwise okay 
11224
             */
11225
            G_tok->log_error_curtok(TCERR_REQ_CATCH_RPAR);
11226
        }
11227
11228
        /* 
11229
         *   parse the 'catch' statement block - we've already established
11230
         *   a special scope for the 'catch' block, so don't start a new
11231
         *   scope if the block contains a compound statement 
11232
         */
11233
        catch_body = parse_stm(err, 0, TRUE);
11234
11235
        /* leave the special 'catch' scope */
11236
        leave_scope(&scope_data);
11237
11238
        /* if the statement block failed, give up now */
11239
        if (*err)
11240
            return 0;
11241
11242
        /* add the 'catch' clause to the 'try' if we were successful */
11243
        if (!catch_has_err)
11244
        {
11245
            /* set the 'catch' node's body */
11246
            catch_stm->set_body(catch_body);
11247
11248
            /* set the local scope in the 'catch' */
11249
            catch_stm->set_symtab(local_symtab_);
11250
            
11251
            /* add the 'catch' to the 'try' */
11252
            try_stm->add_catch(catch_stm);
11253
        }
11254
    }
11255
11256
    /* check for a 'finally' clause */
11257
    if (G_tok->cur() == TOKT_FINALLY)
11258
    {
11259
        CTPNStmFinally *fin_stm;
11260
        CTPNStm *fin_body;
11261
        tcprs_scope_t scope_data;
11262
        CTPNStmEnclosing *old_enclosing;
11263
11264
        /* 
11265
         *   the locals we allocate for the 'finally' are in the finally's
11266
         *   own scope - the slots can be reused later 
11267
         */
11268
        enter_scope(&scope_data);
11269
11270
        /* create the 'finally' node */
11271
        fin_stm = new CTPNStmFinally(enclosing_stm_,
11272
                                     alloc_local(), alloc_local());
11273
11274
        /* 
11275
         *   set the 'finally' clause's source position - we want this
11276
         *   clause to have its own source position independent of the
11277
         *   'try' statement of which it is a part, so that the debugger
11278
         *   can keep track of entry into this clause's generated code 
11279
         */
11280
        fin_stm->set_source_pos(G_tok->get_last_desc(),
11281
                                G_tok->get_last_linenum());
11282
11283
        /* skip the 'finally' keyword */
11284
        G_tok->next();
11285
11286
        /* set the 'finally' to enclose its body */
11287
        old_enclosing = set_enclosing_stm(fin_stm);
11288
11289
        /* parse the 'finally' statement block */
11290
        fin_body = parse_stm(err, 0, FALSE);
11291
11292
        /* set the 'finally' block's closing position */
11293
        fin_stm->set_end_pos(fin_body->get_end_desc(),
11294
                             fin_body->get_end_linenum());
11295
11296
        /* restore the enclosing statement */
11297
        set_enclosing_stm(old_enclosing);
11298
11299
        /* we're done with the special scope */
11300
        leave_scope(&scope_data);
11301
11302
        /* if that failed, give up now */
11303
        if (*err)
11304
            return 0;
11305
11306
        /* set the 'finally' node's body */
11307
        fin_stm->set_body(fin_body);
11308
11309
        /* add the 'finally' to the 'try' */
11310
        try_stm->set_finally(fin_stm);
11311
    }
11312
11313
    /* make sure we have at least one 'catch' or 'finally' clause */
11314
    if (!try_stm->has_catch_or_finally())
11315
        try_stm->log_error(TCERR_TRY_WITHOUT_CATCH);
11316
11317
    /* return the 'try' statement node */
11318
    return try_stm;
11319
}
11320
11321
/* 
11322
 *   Parse a 'throw' statement 
11323
 */
11324
CTPNStm *CTcParser::parse_throw(int *err)
11325
{
11326
    CTcPrsNode *expr;
11327
    CTPNStmThrow *throw_stm;
11328
    
11329
    /* skip the 'throw' keyword */
11330
    G_tok->next();
11331
11332
    /* parse the expression to be thrown */
11333
    expr = parse_expr();
11334
    if (expr == 0)
11335
    {
11336
        *err = TRUE;
11337
        return 0;
11338
    }
11339
11340
    /* create the statement node */
11341
    throw_stm = new CTPNStmThrow(expr);
11342
11343
    /* require a terminating semicolon */
11344
    if (parse_req_sem())
11345
    {
11346
        *err = TRUE;
11347
        return 0;
11348
    }
11349
11350
    /* return the statement node */
11351
    return throw_stm;
11352
}
11353
11354
/* ------------------------------------------------------------------------ */
11355
/*
11356
 *   Unary operator parsing 
11357
 */
11358
11359
/* callback context for local symbol enumeration */
11360
struct enum_for_anon_ctx
11361
{
11362
    /* new local symbol table for anonymous function scope */
11363
    CTcPrsSymtab *symtab;
11364
11365
    /* number of context locals */
11366
    int cnt;
11367
};
11368
11369
/*
11370
 *   local symbol table enumeration callback for anonymous function setup 
11371
 */
11372
void CTcPrsOpUnary::enum_for_anon(void *ctx0, CTcSymbol *sym)
11373
{
11374
    enum_for_anon_ctx *ctx = (enum_for_anon_ctx *)ctx0;
11375
    CTcSymbol *new_sym;
11376
11377
    /* 
11378
     *   If this symbol is already in our table, another symbol from an
11379
     *   enclosed scope hides it, so ignore this one.  Note that we're only
11380
     *   interested in the symbols defined directly in our table - we hide
11381
     *   symbols defined in the enclosing global scope, so we don't care if
11382
     *   they're already defined there.  
11383
     */
11384
    if (ctx->symtab->find_direct(sym->get_sym(), sym->get_sym_len()) != 0)
11385
        return;
11386
11387
    /* create a context-variable copy */
11388
    new_sym = sym->new_ctx_var();
11389
11390
    /* if we got a new symbol, add it to the new symbol table */
11391
    if (new_sym != 0)
11392
        ctx->symtab->add_entry(new_sym);
11393
}
11394
11395
/*
11396
 *   local symbol table enumeration callback for anonymous function -
11397
 *   follow-up - mark the code body as having a context if needed 
11398
 */
11399
void CTcPrsOpUnary::enum_for_anon2(void *, CTcSymbol *sym)
11400
{
11401
    /* ask the symbol to apply the necessary conversion */
11402
    sym->finish_ctx_var_conv();
11403
}
11404
11405
/*
11406
 *   Parse an anonymous function 
11407
 */
11408
CTcPrsNode *CTcPrsOpUnary::parse_anon_func(int short_form)
11409
{
11410
    CTPNCodeBody *code_body;
11411
    int err;
11412
    int has_retval;
11413
    CTcPrsSymtab *new_lcl_symtab;
11414
    CTcPrsSymtab *inner_lcl_symtab;
11415
    CTcPrsSymtab *tab;
11416
    enum_for_anon_ctx ctx;
11417
    tcprs_codebodytype cb_type;
11418
11419
    /* 
11420
     *   our code body type is either an anonymous function or a short
11421
     *   anonymous function 
11422
     */
11423
    cb_type = (short_form ? TCPRS_CB_SHORT_ANON_FN : TCPRS_CB_ANON_FN);
11424
    
11425
    /* skip the initial token */
11426
    G_tok->next();
11427
11428
    /* 
11429
     *   Create a new local symbol table to represent the enclosing scope of
11430
     *   the new nested scope.  Unlike most nested scopes, we can't simply
11431
     *   plug in the current scope's symbol table - instead, we have to build
11432
     *   a special representation of the enclosing scope to handle the
11433
     *   "closure" behavior.  The enclosing scope's local variable set
11434
     *   effectively becomes an object rather than a simple stack frame.  The
11435
     *   enclosing lexical scope and the anonymous function then both
11436
     *   reference the shared locals object.
11437
     *   
11438
     *   This synthesized enclosing scope will directly represent all of the
11439
     *   nested scopes up but not including to the root global scope.  The
11440
     *   global scope doesn't need the special closure representation, since
11441
     *   it's already shared among all lexical scopes anyway.  Since we're
11442
     *   not linking in to the enclosing scope list the way we normally
11443
     *   would, we need to explicitly link in the global scope.  To do this,
11444
     *   we can simply make the global scope the enclosing scope of our
11445
     *   synthesized outer scope table.  
11446
     */
11447
    new_lcl_symtab = new CTcPrsSymtab(G_prs->get_global_symtab());
11448
11449
    /* set up the enumeration callback context */
11450
    ctx.symtab = new_lcl_symtab;
11451
    ctx.cnt = 0;
11452
11453
    /* 
11454
     *   fill the new local symbol table with the inherited local symbols
11455
     *   from the current local scope and any enclosing scopes - but stop
11456
     *   when we reach the global scope, since this is already shared by all
11457
     *   scopes and doesn't need any special closure representation 
11458
     */
11459
    for (tab = G_prs->get_local_symtab() ;
11460
         tab != 0 && tab != G_prs->get_global_symtab() ;
11461
         tab = tab->get_parent())
11462
    {
11463
        /* enumerate entries in this table */
11464
        tab->enum_entries(&enum_for_anon, &ctx);
11465
    }
11466
11467
    /* 
11468
     *   create another local symbol table, this time nested within the
11469
     *   table containing the locals shared from the enclosing scope -
11470
     *   this one will contain any formals defined for the anonymous
11471
     *   function, which hide inherited locals from the enclosing scope 
11472
     */
11473
    inner_lcl_symtab = new CTcPrsSymtab(new_lcl_symtab);
11474
11475
    /* 
11476
     *   Parse the code body.  Give the code body the same defining object
11477
     *   and property that we have in our own code body, so that it can
11478
     *   reference 'self' if we can, and use 'inherited' if we can.  
11479
     */
11480
    err = 0;
11481
    code_body = G_prs->parse_nested_code_body(FALSE,
11482
                                              G_prs->is_self_valid(),
11483
                                              0, 0, 0, 0,
11484
                                              &has_retval, &err,
11485
                                              inner_lcl_symtab, cb_type);
11486
11487
    /* if that failed, return failure */
11488
    if (code_body == 0 || err != 0)
11489
        return 0;
11490
11491
    /* 
11492
     *   If the nested code body references 'self' or the full method
11493
     *   context, then so does the enclosing code body, and we need these
11494
     *   variables in the local context for the topmost enclosing code body.
11495
     */
11496
    if (code_body->self_referenced())
11497
    {
11498
        G_prs->set_self_referenced(TRUE);
11499
        G_prs->set_local_ctx_needs_self(TRUE);
11500
    }
11501
    if (code_body->full_method_ctx_referenced())
11502
    {
11503
        G_prs->set_full_method_ctx_referenced(TRUE);
11504
        G_prs->set_local_ctx_needs_full_method_ctx(TRUE);
11505
    }
11506
11507
    /*
11508
     *   Enumerate all of the entries in our scope once again - this time,
11509
     *   we want to determine if there are any variables that were not
11510
     *   previously referenced from anonymous functions but have been now;
11511
     *   we need to convert all such variables to context locals.  
11512
     */
11513
    for (tab = G_prs->get_local_symtab() ;
11514
         tab != 0 && tab != G_prs->get_global_symtab() ;
11515
         tab = tab->get_parent())
11516
    {
11517
        /* enumerate entries in this table */
11518
        tab->enum_entries(&enum_for_anon2, &ctx);
11519
    }
11520
11521
    /* 
11522
     *   if there's a 'self' object, and we referenced 'self' or the full
11523
     *   method context in the nested code body, we'll definitely need a
11524
     *   local context, so make sure we have one initialized even if we don't
11525
     *   have any local variables shared 
11526
     */
11527
    if (G_prs->is_self_valid()
11528
        && (code_body->self_referenced()
11529
            || code_body->full_method_ctx_referenced()))
11530
    {
11531
        /* initialize a local context here, in the enclosing level */
11532
        G_prs->init_local_ctx();
11533
    }
11534
11535
    /* 
11536
     *   add the code body to the parser's master list of nested top-level
11537
     *   statements for the current program 
11538
     */
11539
    G_prs->add_nested_stm(code_body);
11540
11541
    /* return a new anonymous function node */
11542
    return new CTPNAnonFunc(code_body, has_retval);
11543
}
11544
11545
11546
/* ------------------------------------------------------------------------ */
11547
/*
11548
 *   Generic statement node 
11549
 */
11550
11551
/*
11552
 *   Add a debugging line record for the current statement.  Call this
11553
 *   just before generating instructions for the statement; we'll create a
11554
 *   line record associating the current byte-code location with our
11555
 *   source file and line number.  
11556
 */
11557
void CTPNStmBase::add_debug_line_rec()
11558
{
11559
    /* add a line record to the code generator's list */
11560
    if (file_ != 0)
11561
        G_cg->add_line_rec(file_, linenum_);
11562
}
11563
11564
/*
11565
 *   Add a debugging line record for a given position 
11566
 */
11567
void CTPNStmBase::add_debug_line_rec(CTcTokFileDesc *desc, long linenum)
11568
{
11569
    /* add a line record to the code generator's list */
11570
    G_cg->add_line_rec(desc, linenum);
11571
}
11572
11573
/* ------------------------------------------------------------------------ */
11574
/*
11575
 *   compound statement base class 
11576
 */
11577
11578
/*
11579
 *   instantiate 
11580
 */
11581
CTPNStmCompBase::CTPNStmCompBase(CTPNStm *first_stm, CTcPrsSymtab *symtab)
11582
{
11583
    /* remember the first statement in our statement list */
11584
    first_stm_ = first_stm;
11585
    
11586
    /* remember our local symbol table */
11587
    symtab_ = symtab;
11588
    
11589
    /* 
11590
     *   presume we don't have our own private symbol table, but will
11591
     *   simply use our parent's 
11592
     */
11593
    has_own_scope_ = FALSE;
11594
11595
    /* 
11596
     *   remember the source location of the closing brace, which should
11597
     *   be the current location when we're instantiated 
11598
     */
11599
    end_desc_ = G_tok->get_last_desc();
11600
    end_linenum_ = G_tok->get_last_linenum();
11601
}
11602
11603
/*
11604
 *   fold constants - we simply fold constants in each of our statements 
11605
 */
11606
CTcPrsNode *CTPNStmCompBase::fold_constants(class CTcPrsSymtab *symtab)
11607
{
11608
    CTPNStm *cur;
11609
    
11610
    /* iterate through our statements and fold constants in each one */
11611
    for (cur = first_stm_ ; cur != 0 ; cur = cur->get_next_stm())
11612
    {
11613
        /* 
11614
         *   set this statement's source line location to be the current
11615
         *   location for error reporting 
11616
         */
11617
        G_tok->set_line_info(cur->get_source_desc(),
11618
                             cur->get_source_linenum());
11619
11620
        /* 
11621
         *   Fold constants, using the enclosing symbol table (not the
11622
         *   local symbol table - during code generation we can resolve
11623
         *   only to the global scope, since local scope symbols must
11624
         *   always be resolved during parsing).  We assume that statement
11625
         *   nodes are never replaced during constant folding, so we
11626
         *   ignore the result of this call and keep our original
11627
         *   statement list entry.  
11628
         */
11629
        cur->fold_constants(symtab);
11630
    }
11631
11632
    /* 
11633
     *   although nodes within our subtree might have been changed, this
11634
     *   compound statement itself is unchanged by constant folding 
11635
     */
11636
    return this;
11637
}
11638
11639
/*
11640
 *   Generate code 
11641
 */
11642
void CTPNStmCompBase::gen_code(int, int)
11643
{
11644
    CTPNStm *cur;
11645
    CTcPrsSymtab *old_frame;
11646
11647
    /* set my local scope symbol frame, if necessary */
11648
    old_frame = G_cs->set_local_frame(symtab_);
11649
11650
    /* set the code location for the start of the group */
11651
    add_debug_line_rec();
11652
11653
    /* 
11654
     *   iterate through our statements and generate code for each in
11655
     *   sequence 
11656
     */
11657
    for (cur = first_stm_ ; cur != 0 ; cur = cur->get_next_stm())
11658
    {
11659
        /* 
11660
         *   set this statement's source line location to be the current
11661
         *   location for error reporting 
11662
         */
11663
        G_tok->set_line_info(cur->get_source_desc(),
11664
                             cur->get_source_linenum());
11665
11666
        /* 
11667
         *   Generate code for the statement.  Note that we generate in
11668
         *   the scope of the enclosing symbol table, because we never
11669
         *   resolve symbols to local scope during code generation - local
11670
         *   scope symbols must be resolved during parsing, because these
11671
         *   symbols must always be declared before first being used 
11672
         *   
11673
         *   We have no use for the results of any expressions, so discard
11674
         *   = true; we don't care about the form of any logical operator
11675
         *   results, so use the looser "for condition" rules 
11676
         */
11677
        cur->gen_code(TRUE, TRUE);
11678
    }
11679
11680
    /* set the source location for the end of the group */
11681
    add_debug_line_rec(end_desc_, end_linenum_);
11682
11683
    /* check for unreferenced local variables */
11684
    if (symtab_ != 0 && has_own_scope_)
11685
    {
11686
        /* 
11687
         *   set our line information to be current again, so that any
11688
         *   unreferenced local errors are reported at the start of the
11689
         *   compound statement 
11690
         */
11691
        G_tok->set_line_info(get_source_desc(),
11692
                             get_source_linenum());
11693
    }
11694
11695
    /* restore the enclosing local frame */
11696
    old_frame = G_cs->set_local_frame(old_frame);
11697
}
11698
11699
/*
11700
 *   Chart the control flow through the statement list
11701
 */
11702
unsigned long CTPNStmCompBase::get_control_flow(int warn) const
11703
{
11704
    CTPNStm *cur;
11705
    unsigned long flags;
11706
11707
    /* 
11708
     *   presume we will not find a 'throw' or 'return', and that we'll be
11709
     *   able to reach the next statement after the list 
11710
     */
11711
    flags = TCPRS_FLOW_NEXT;
11712
11713
    /* 
11714
     *   iterate through the statements in our list, since they'll be
11715
     *   executed in the same sequence at run-time 
11716
     */
11717
    for (cur = first_stm_ ; cur != 0 ; cur = cur->get_next_stm())
11718
    {
11719
        unsigned long flags1;
11720
        
11721
        /* get this statement's control flow characteristics */
11722
        flags1 = cur->get_control_flow(warn);
11723
11724
        /* 
11725
         *   OR each of 'throws', 'returns void', and 'returns value',
11726
         *   since anything a reachable statement does to return or throw,
11727
         *   the overall list can do to leave.  Likewise, the 'break',
11728
         *   'goto', and 'continue' flags push up into the enclosing
11729
         *   statement.  
11730
         */
11731
        flags |= (flags1 & (TCPRS_FLOW_THROW
11732
                            | TCPRS_FLOW_RET_VOID
11733
                            | TCPRS_FLOW_RET_VAL
11734
                            | TCPRS_FLOW_GOTO
11735
                            | TCPRS_FLOW_BREAK
11736
                            | TCPRS_FLOW_CONT));
11737
11738
        /* 
11739
         *   if this statement can't continue to the next statement, the
11740
         *   remainder of this compound statement is unreachable, unless
11741
         *   it has a code label 
11742
         */
11743
        if (!(flags1 & TCPRS_FLOW_NEXT))
11744
        {
11745
            CTPNStm *nxt;
11746
11747
            /*
11748
             *   we can't reach the next statement; unless we find a label
11749
             *   at some point after this, we won't be able to reach any
11750
             *   of the remaining statements, hence we won't be able to
11751
             *   reach the statement after our list 
11752
             */
11753
            flags &= ~TCPRS_FLOW_NEXT;
11754
            
11755
            /* 
11756
             *   If another statement follows, check to see if it's
11757
             *   reachable by label, because it's not reachable by any
11758
             *   other means
11759
             */
11760
            nxt = cur->get_next_stm();
11761
            if (nxt != 0)
11762
            {
11763
                /* 
11764
                 *   check to see if this statement has a label; if not,
11765
                 *   warn that it is unreachable 
11766
                 */
11767
                if (warn
11768
                    && !nxt->has_code_label()
11769
                    && !G_prs->get_syntax_only())
11770
                    nxt->log_warning(TCERR_UNREACHABLE_CODE);
11771
11772
                /* skip ahead until we find something with a label */
11773
                while (nxt != 0 && !nxt->has_code_label())
11774
                {
11775
                    /* remember the previous statement */
11776
                    cur = nxt;
11777
11778
                    /* move on to the next statement */
11779
                    nxt = nxt->get_next_stm();
11780
                }
11781
11782
                /* 
11783
                 *   if we found a reachable statement again, we might be
11784
                 *   able to continue from the end of the list after all 
11785
                 */
11786
                if (nxt != 0)
11787
                    flags |= TCPRS_FLOW_NEXT;
11788
            }
11789
        }
11790
    }
11791
11792
    /* return the result */
11793
    return flags;
11794
}
11795
11796
/* ------------------------------------------------------------------------ */
11797
/*
11798
 *   expression statement base class 
11799
 */
11800
11801
/*
11802
 *   fold constants
11803
 */
11804
CTcPrsNode *CTPNStmExprBase::fold_constants(class CTcPrsSymtab *symtab)
11805
{
11806
    /* set our location for any errors that occur */
11807
    G_tok->set_line_info(get_source_desc(), get_source_linenum());
11808
11809
    /* 
11810
     *   fold constants in our expression, replacing our expression with
11811
     *   the folded version 
11812
     */
11813
    expr_ = expr_->fold_constants(symtab);
11814
11815
    /* 
11816
     *   although our expression subtree might have been changed, this
11817
     *   compound statement itself is unchanged by constant folding 
11818
     */
11819
    return this;
11820
}
11821
11822
/*
11823
 *   Generate code 
11824
 */
11825
void CTPNStmExprBase::gen_code(int, int)
11826
{
11827
    /* add a debug line record for the statement */
11828
    add_debug_line_rec();
11829
11830
    /* 
11831
     *   generate code in our expression - the result will not be used in any
11832
     *   further calculation, so discard it; and since we don't care about
11833
     *   the return value, use the looser "for condition" rules for any
11834
     *   top-level logical operators in the expression so that we don't
11835
     *   bother applying any extra conversions to a value we're just
11836
     *   discarding anyway 
11837
     */
11838
    expr_->gen_code(TRUE, TRUE);
11839
}
11840
11841
/* ------------------------------------------------------------------------ */
11842
/*
11843
 *   'if' statement 
11844
 */
11845
11846
/*
11847
 *   fold constants 
11848
 */
11849
CTcPrsNode *CTPNStmIfBase::fold_constants(CTcPrsSymtab *symtab)
11850
{
11851
    /* set our location for any errors that occur */
11852
    G_tok->set_line_info(get_source_desc(), get_source_linenum());
11853
11854
    /* 
11855
     *   fold constants in the condition, replacing the expression with
11856
     *   the folded expression 
11857
     */
11858
    cond_expr_ = cond_expr_->fold_constants(symtab);
11859
11860
    /* fold constants in the substatements */
11861
    if (then_part_ != 0)
11862
        then_part_ = (CTPNStm *)then_part_->fold_constants(symtab);
11863
    if (else_part_ != 0)
11864
        else_part_ = (CTPNStm *)else_part_->fold_constants(symtab);
11865
11866
    /* we are not directly changed by this operation */
11867
    return this;
11868
}
11869
11870
/*
11871
 *   Chart the control flow through the conditional 
11872
 */
11873
unsigned long CTPNStmIfBase::get_control_flow(int warn) const
11874
{
11875
    unsigned long flags1;
11876
    unsigned long flags2;
11877
    
11878
    /* 
11879
     *   if my condition is constant, our control flow is determined
11880
     *   entirely by the branch we'll take 
11881
     */
11882
    if (cond_expr_ != 0 && cond_expr_->is_const())
11883
    {
11884
        CTPNStm *part;
11885
        
11886
        /* 
11887
         *   We have a constant expression - if it's true, our control
11888
         *   flow is determined by the 'then' part, otherwise it's
11889
         *   determines by the 'else' part. 
11890
         */
11891
        part = (cond_expr_->get_const_val()->get_val_bool()
11892
                ? then_part_ : else_part_);
11893
11894
        /* 
11895
         *   if the part determining the control flow is a null statement,
11896
         *   we'll simply continue to the next statement; otherwise, the
11897
         *   result is the control flow of the determining statement 
11898
         */
11899
        if (part == 0)
11900
        {
11901
            /* it's a null statement, which merely continues */
11902
            return TCPRS_FLOW_NEXT;
11903
        }
11904
        else
11905
        {
11906
            /* our control flow is this statement's control flow */
11907
            return part->get_control_flow(warn);
11908
        }
11909
    }
11910
    
11911
    /*
11912
     *   The control flow through the 'if' is a combination of the control
11913
     *   flow through the two parts.  If one of the parts is a void
11914
     *   statement, it continues to the next statement after the 'if'.  
11915
     */
11916
11917
    /* check the 'then' part if present */
11918
    flags1 = (then_part_ != 0
11919
              ? then_part_->get_control_flow(warn) : TCPRS_FLOW_NEXT);
11920
11921
    /* check the 'else' part if present */
11922
    flags2 = (else_part_ != 0
11923
              ? else_part_->get_control_flow(warn) : TCPRS_FLOW_NEXT);
11924
11925
    /* combine the parts to get the possible control flows */
11926
    return flags1 | flags2;
11927
}
11928
11929
/* ------------------------------------------------------------------------ */
11930
/*
11931
 *   Program Statement node
11932
 */
11933
11934
/*
11935
 *   fold constants - we simply fold constants in each of our statements 
11936
 */
11937
CTcPrsNode *CTPNStmProg::fold_constants(class CTcPrsSymtab *symtab)
11938
{
11939
    CTPNStmTop *cur;
11940
11941
    /* iterate through our statements and fold constants in each one */
11942
    for (cur = first_stm_ ; cur != 0 ; cur = cur->get_next_stm_top())
11943
        cur->fold_constants(symtab);
11944
11945
    /* 
11946
     *   although nodes within our subtree might have been changed, this
11947
     *   compound statement itself is unchanged by constant folding 
11948
     */
11949
    return this;
11950
}
11951
11952
/*
11953
 *   Generate code 
11954
 */
11955
void CTPNStmProg::gen_code(int, int)
11956
{
11957
    CTPNStmTop *cur;
11958
11959
    /* 
11960
     *   iterate through our statements and generate code for each in
11961
     *   sequence 
11962
     */
11963
    for (cur = first_stm_ ; cur != 0 ; cur = cur->get_next_stm_top())
11964
        cur->gen_code(TRUE, TRUE);
11965
11966
    /* 
11967
     *   iterate again, this time checking local symbol tables - we must
11968
     *   wait to do this until after we've generated all of the code,
11969
     *   because local symbol tables can be cross-referenced in some cases
11970
     *   (in particular, when locals are shared through contexts, such as
11971
     *   with anonymous functions) 
11972
     */
11973
    for (cur = first_stm_ ; cur != 0 ; cur = cur->get_next_stm_top())
11974
        cur->check_locals();
11975
}
11976
11977
/* ------------------------------------------------------------------------ */
11978
/*
11979
 *   "for" statement node
11980
 */
11981
11982
/*
11983
 *   fold constants 
11984
 */
11985
CTcPrsNode *CTPNStmForBase::fold_constants(CTcPrsSymtab *symtab)
11986
{
11987
    /* set our location for any errors that occur */
11988
    G_tok->set_line_info(get_source_desc(), get_source_linenum());
11989
11990
    /* 
11991
     *   fold constants in each of our expressions, replacing the
11992
     *   expressions with the folded versions
11993
     */
11994
    if (init_expr_ != 0)
11995
        init_expr_ = init_expr_->fold_constants(symtab);
11996
    if (cond_expr_ != 0)
11997
        cond_expr_ = cond_expr_->fold_constants(symtab);
11998
    if (reinit_expr_ != 0)
11999
        reinit_expr_ = reinit_expr_->fold_constants(symtab);
12000
12001
    /* fold constants in the body statement */
12002
    if (body_stm_ != 0)
12003
        body_stm_ = (CTPNStm *)body_stm_->fold_constants(symtab);
12004
12005
    /* we are not directly changed by this operation */
12006
    return this;
12007
}
12008
12009
/*
12010
 *   Chart the control flow through the loop
12011
 */
12012
unsigned long CTPNStmForBase::get_control_flow(int warn) const
12013
{
12014
    unsigned long flags;
12015
12016
    /*
12017
     *   If we have a condition with a constant false value, and there's
12018
     *   either a reinitialization expression or a loop body, warn about
12019
     *   the unreachable code.  If the body is directly reachable via a
12020
     *   code label, we don't need this warning.  
12021
     */
12022
    if (cond_expr_ != 0
12023
        && cond_expr_->is_const()
12024
        && !cond_expr_->get_const_val()->get_val_bool()
12025
        && (reinit_expr_ != 0 || body_stm_ != 0)
12026
        && (body_stm_ != 0 && !body_stm_->has_code_label())
12027
        && !G_prs->get_syntax_only())
12028
    {
12029
        /* log a warning if desired */
12030
        if (warn)
12031
            log_warning(TCERR_FOR_COND_FALSE);
12032
12033
        /* this will simply continue to the next statement */
12034
        return TCPRS_FLOW_NEXT;
12035
    }
12036
12037
    /* check for a body */
12038
    if (body_stm_ != 0)
12039
    {
12040
        /* determine how our body can exit */
12041
        flags = body_stm_->get_control_flow(warn);
12042
    }
12043
    else
12044
    {
12045
        /* 
12046
         *   We have no body, so it's entirely up to the loop - we'll
12047
         *   evaluate those parameters below, but for now clear all flags 
12048
         */
12049
        flags = 0;
12050
    }
12051
12052
    /* 
12053
     *   ignore any 'next' flag that comes out of the body, because 'next'
12054
     *   from the last statement in the body actually takes us back to the
12055
     *   top of the loop 
12056
     */
12057
    flags &= ~TCPRS_FLOW_NEXT;
12058
12059
    /* 
12060
     *   if there's a 'break' in the body, we can continue to the next
12061
     *   statement, since a break in the body takes us to the statement
12062
     *   after the 'for' 
12063
     */
12064
    if ((flags & TCPRS_FLOW_BREAK) != 0)
12065
        flags |= TCPRS_FLOW_NEXT;
12066
12067
    /* 
12068
     *   if we have a condition, and either the condition is a constant
12069
     *   false value or has a non-constant value, assume that the
12070
     *   condition can possibly become false and hence that the loop can
12071
     *   exit, and hence that the next statement is reachable 
12072
     */
12073
    if (cond_expr_ != 0
12074
        && (!cond_expr_->is_const()
12075
            || (cond_expr_->is_const()
12076
                && !cond_expr_->get_const_val()->get_val_bool())))
12077
        flags |= TCPRS_FLOW_NEXT;
12078
12079
    /* 
12080
     *   clear the 'break' and 'continue' flags, since we capture them in
12081
     *   our own scope - if our child breaks or continues, it won't affect
12082
     *   the "for" loop's container 
12083
     */
12084
    flags &= ~(TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT);
12085
12086
    /* return the flags */
12087
    return flags;
12088
}
12089
12090
12091
/* ------------------------------------------------------------------------ */
12092
/*
12093
 *   "foreach" statement node 
12094
 */
12095
12096
/*
12097
 *   fold constants 
12098
 */
12099
CTcPrsNode *CTPNStmForeachBase::fold_constants(CTcPrsSymtab *symtab)
12100
{
12101
    /* set our location for any errors that occur */
12102
    G_tok->set_line_info(get_source_desc(), get_source_linenum());
12103
12104
    /* 
12105
     *   fold constants in each of our expressions, replacing the
12106
     *   expressions with the folded versions 
12107
     */
12108
    if (iter_expr_ != 0)
12109
        iter_expr_ = iter_expr_->fold_constants(symtab);
12110
    if (coll_expr_ != 0)
12111
        coll_expr_ = coll_expr_->fold_constants(symtab);
12112
12113
    /* fold constants in the body statement */
12114
    if (body_stm_ != 0)
12115
        body_stm_ = (CTPNStm *)body_stm_->fold_constants(symtab);
12116
    
12117
    /* we are not directly changed by this operation */
12118
    return this;
12119
}
12120
12121
/*
12122
 *   Chart the control flow through the loop 
12123
 */
12124
unsigned long CTPNStmForeachBase::get_control_flow(int warn) const
12125
{
12126
    unsigned long flags;
12127
    
12128
    /* check for a body */
12129
    if (body_stm_ != 0)
12130
    {
12131
        /* determine how our body can exit */
12132
        flags = body_stm_->get_control_flow(warn);
12133
12134
        /* 
12135
         *   add in the 'next' flag, because collection iterations always
12136
         *   terminate 
12137
         */
12138
        flags |= TCPRS_FLOW_NEXT;
12139
12140
        /* 
12141
         *   clear the 'break' and 'continue' flags, since we capture them
12142
         *   in our own scope - if our child breaks or continues, it won't
12143
         *   affect the container of the 'foreach' itself
12144
         */
12145
        flags &= ~(TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT);
12146
    }
12147
    else
12148
    {
12149
        /* 
12150
         *   we have no body, so it's entirely up to the loop, which
12151
         *   simply iterates over the list and goes to the next statement 
12152
         */
12153
        flags = TCPRS_FLOW_NEXT;
12154
    }
12155
12156
    /* return the flags */
12157
    return flags;
12158
}
12159
12160
/* ------------------------------------------------------------------------ */
12161
/*
12162
 *   "while" statement node 
12163
 */
12164
12165
/*
12166
 *   fold constants 
12167
 */
12168
CTcPrsNode *CTPNStmWhileBase::fold_constants(CTcPrsSymtab *symtab)
12169
{
12170
    /* set our location for any errors that occur */
12171
    G_tok->set_line_info(get_source_desc(), get_source_linenum());
12172
12173
    /* 
12174
     *   fold constants in our condition expression, replacing the
12175
     *   expression with the folded version
12176
     */
12177
    if (cond_expr_ != 0)
12178
        cond_expr_ = cond_expr_->fold_constants(symtab);
12179
12180
    /* fold constants in the body statement */
12181
    if (body_stm_ != 0)
12182
        body_stm_ = (CTPNStm *)body_stm_->fold_constants(symtab);
12183
12184
    /* we are not directly changed by this operation */
12185
    return this;
12186
}
12187
12188
/*
12189
 *   Chart the control flow through the statement
12190
 */
12191
unsigned long CTPNStmWhileBase::get_control_flow(int warn) const
12192
{
12193
    unsigned long flags;
12194
12195
    /*
12196
     *   If we have a condition with a constant false value, and we have a
12197
     *   non-empty body, and the body isn't reachable via a label, warn
12198
     *   about the unreachable code 
12199
     */
12200
    if (cond_expr_->is_const()
12201
        && !cond_expr_->get_const_val()->get_val_bool()
12202
        && body_stm_ != 0
12203
        && !body_stm_->has_code_label()
12204
        && G_prs->get_syntax_only())
12205
    {
12206
        /* log a warning if desired */
12207
        if (warn)
12208
            log_warning(TCERR_WHILE_COND_FALSE);
12209
12210
        /* this will simply continue to the next statement */
12211
        return TCPRS_FLOW_NEXT;
12212
    }
12213
12214
    /* check for a body */
12215
    if (body_stm_ != 0)
12216
    {
12217
        /* determine how our body can exit */
12218
        flags = body_stm_->get_control_flow(warn);
12219
    }
12220
    else
12221
    {
12222
        /* 
12223
         *   We have no body, so it's entirely up to the loop - we'll
12224
         *   evaluate those parameters below, but for now clear all flags 
12225
         */
12226
        flags = 0;
12227
    }
12228
12229
    /* 
12230
     *   ignore any 'next' flag that comes out of the body, because 'next'
12231
     *   from the last statement in the body actually takes us back to the
12232
     *   top of the loop 
12233
     */
12234
    flags &= ~TCPRS_FLOW_NEXT;
12235
12236
    /* 
12237
     *   if there's a 'break' in the body, we can continue to the next
12238
     *   statement, since a break in the body takes us to the statement
12239
     *   after the 'for' 
12240
     */
12241
    if ((flags & TCPRS_FLOW_BREAK) != 0)
12242
        flags |= TCPRS_FLOW_NEXT;
12243
12244
    /* 
12245
     *   if the condition is a constant false value, or has a non-constant
12246
     *   value, assume that the condition can possibly become false and
12247
     *   hence that the loop can exit, and hence that the next statement
12248
     *   is reachable 
12249
     */
12250
    if (!cond_expr_->is_const()
12251
        || (cond_expr_->is_const()
12252
            && !cond_expr_->get_const_val()->get_val_bool()))
12253
        flags |= TCPRS_FLOW_NEXT;
12254
12255
    /* 
12256
     *   clear the 'break' and 'continue' flags, since we capture them in
12257
     *   our own scope - if our child breaks or continues, it won't affect
12258
     *   the loop's container 
12259
     */
12260
    flags &= ~(TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT);
12261
12262
    /* return the flags */
12263
    return flags;
12264
}
12265
12266
/* ------------------------------------------------------------------------ */
12267
/*
12268
 *   "do-while" statement node 
12269
 */
12270
12271
/*
12272
 *   fold constants 
12273
 */
12274
CTcPrsNode *CTPNStmDoWhileBase::fold_constants(CTcPrsSymtab *symtab)
12275
{
12276
    /* set our location for any errors that occur */
12277
    G_tok->set_line_info(get_source_desc(), get_source_linenum());
12278
12279
    /* 
12280
     *   fold constants in our condition expression, replacing the
12281
     *   expression with the folded version 
12282
     */
12283
    if (cond_expr_ != 0)
12284
        cond_expr_ = cond_expr_->fold_constants(symtab);
12285
12286
    /* fold constants in the body statement */
12287
    if (body_stm_ != 0)
12288
        body_stm_ = (CTPNStm *)body_stm_->fold_constants(symtab);
12289
12290
    /* we are not directly changed by this operation */
12291
    return this;
12292
}
12293
12294
/*
12295
 *   Chart the control flow through the statement 
12296
 */
12297
unsigned long CTPNStmDoWhileBase::get_control_flow(int warn) const
12298
{
12299
    unsigned long flags;
12300
12301
    /* check for a body */
12302
    if (body_stm_ != 0)
12303
    {
12304
        /* determine how our body can exit */
12305
        flags = body_stm_->get_control_flow(warn);
12306
    }
12307
    else
12308
    {
12309
        /* 
12310
         *   We have no body, so it's entirely up to the loop - we'll
12311
         *   evaluate those parameters below, but for now clear all flags 
12312
         */
12313
        flags = 0;
12314
    }
12315
12316
    /*
12317
     *   If we have a 'next' flag, it means that control can flow out from
12318
     *   the end of the last statement in the loop.  That in turn means that
12319
     *   we can reach the code where we evaluate the condition.  So, in order
12320
     *   for flow to proceed out of the loop, we have to be able to reach the
12321
     *   condition, and then the condition has to be capable of being false
12322
     *   (because if it's always true, we'll simply go back to the start of
12323
     *   the loop every time we hit the condition).  
12324
     */
12325
    if ((flags & TCPRS_FLOW_NEXT) != 0)
12326
    {
12327
        /*
12328
         *   The 'next' flag up to this point only means that control can
12329
         *   reach the condition at the bottom of the loop.  Since we want to
12330
         *   tell our caller whether or not control can flow *out* of the
12331
         *   loop, this is now irrelevant, so clear the 'next' flag for now. 
12332
         */
12333
        flags &= ~TCPRS_FLOW_NEXT;
12334
12335
        /*
12336
         *   Okay, we now know we can reach the condition at the bottom of
12337
         *   the loop.  If the condition is a constant false value, we know
12338
         *   that we can flow out of the loop, since we know we'll hit the
12339
         *   condition, find that it's false, and exit the loop.  Likewise,
12340
         *   if the condition hasa non-constant value, assume that it can
12341
         *   possibly become false and hence that the loop can exit, and
12342
         *   hence that the next statement is reachable.
12343
         */
12344
        if (!cond_expr_->is_const()
12345
            || (cond_expr_->is_const()
12346
                && !cond_expr_->get_const_val()->get_val_bool()))
12347
            flags |= TCPRS_FLOW_NEXT;
12348
    }
12349
12350
    /* 
12351
     *   if there's a 'break' in the body, we can continue to the next
12352
     *   statement, since a break in the body takes us to the statement
12353
     *   after the 'while' 
12354
     */
12355
    if ((flags & TCPRS_FLOW_BREAK) != 0)
12356
        flags |= TCPRS_FLOW_NEXT;
12357
12358
    /* 
12359
     *   clear the 'break' and 'continue' flags, since we capture them in
12360
     *   our own scope - if our child breaks or continues, it won't affect
12361
     *   the loop's container 
12362
     */
12363
    flags &= ~(TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT);
12364
12365
    /* return the flags */
12366
    return flags;
12367
}
12368
12369
12370
/* ------------------------------------------------------------------------ */
12371
/*
12372
 *   "switch" statement node 
12373
 */
12374
12375
/*
12376
 *   fold constants 
12377
 */
12378
CTcPrsNode *CTPNStmSwitchBase::fold_constants(CTcPrsSymtab *symtab)
12379
{
12380
    /* set our location for any errors that occur */
12381
    G_tok->set_line_info(get_source_desc(), get_source_linenum());
12382
12383
    /* fold constants in our expression and body */
12384
    expr_ = expr_->fold_constants(symtab);
12385
    if (body_ != 0)
12386
        body_ = (CTPNStm *)body_->fold_constants(symtab);
12387
12388
    /* we're unchanged by constant folding */
12389
    return this;
12390
}
12391
12392
/*
12393
 *   Chart the control flow through the statement 
12394
 */
12395
unsigned long CTPNStmSwitchBase::get_control_flow(int warn) const
12396
{
12397
    unsigned long flags;
12398
    
12399
    /*
12400
     *   Control flow through a switch is controlled by control flow
12401
     *   through the compound statement.  If the compound statement can
12402
     *   break, then we flow to the next statement.  If the compound
12403
     *   statement can reach the next statement, then so can we.  
12404
     */
12405
    flags = (body_ != 0 ? body_->get_control_flow(warn) : TCPRS_FLOW_NEXT);
12406
12407
    /* 
12408
     *   check for a reachable break in the body - a break means the next
12409
     *   statement is reachable 
12410
     */
12411
    if ((flags & TCPRS_FLOW_BREAK) != 0)
12412
    {
12413
        /* 
12414
         *   break takes us to the next statement, and doesn't propagate
12415
         *   up to a break in any enclosing statement 
12416
         */
12417
        flags |= TCPRS_FLOW_NEXT;
12418
        flags &= ~TCPRS_FLOW_BREAK;
12419
    }
12420
12421
    /*
12422
     *   If the switch has no 'default' case, it is almost certain that we
12423
     *   can reach the next statement after the switch, because we can
12424
     *   probably assume that not every case is accounted for and hence we
12425
     *   have the equivalent of a null 'default' case.
12426
     *   
12427
     *   Note that we can't know for sure that every case hasn't been
12428
     *   accounted for, because we do not have datatype information for the
12429
     *   controlling expression; even if we did know the controlling
12430
     *   expression's datatype, it is impossible to enumerate every possible
12431
     *   value for expressions of certain datatypes (such as lists and
12432
     *   strings, which have effectively infinite ranges).  However, it is a
12433
     *   reasonable assumption that not every possible case has been
12434
     *   accounted for, 
12435
     */
12436
    if (!get_has_default())
12437
        flags |= TCPRS_FLOW_NEXT;
12438
12439
    /* return the flags */
12440
    return flags;
12441
}
12442
12443
/* ------------------------------------------------------------------------ */
12444
/*
12445
 *   code label statement node 
12446
 */
12447
12448
/*
12449
 *   fold constants 
12450
 */
12451
CTcPrsNode *CTPNStmLabelBase::fold_constants(CTcPrsSymtab *symtab)
12452
{
12453
    /* set our location for any errors that occur */
12454
    G_tok->set_line_info(get_source_desc(), get_source_linenum());
12455
12456
    /* fold constants in our labeled statement */
12457
    if (stm_ != 0)
12458
        stm_ = (CTPNStm *)stm_->fold_constants(symtab);
12459
12460
    /* we're unchanged by constant folding */
12461
    return this;
12462
}
12463
12464
/*
12465
 *   Chart the control flow through the statement 
12466
 */
12467
unsigned long CTPNStmLabelBase::get_control_flow(int warn) const
12468
{
12469
    ulong flags;
12470
    
12471
    /* 
12472
     *   if we have an underlying statement, control flow is the same as
12473
     *   the statement's control flow; if we don't have a statement (i.e.,
12474
     *   our labeled statement is an empty statement), we flow to the next
12475
     *   statement 
12476
     */
12477
    flags = (stm_ != 0 ? stm_->get_control_flow(warn) : TCPRS_FLOW_NEXT);
12478
12479
    /* 
12480
     *   if we have an explicit 'break' flag in our control flow flags, it
12481
     *   means that we were targeted specifically with a 'break' statement
12482
     *   contained within our body - this transfers control to the next
12483
     *   statement after me, therefore my next statement is reachable in
12484
     *   this case 
12485
     */
12486
    if ((control_flow_flags_ & TCPRS_FLOW_BREAK) != 0)
12487
        flags |= TCPRS_FLOW_NEXT;
12488
12489
    /* return the result */
12490
    return flags;
12491
}
12492
12493
/*
12494
 *   add control flow flags 
12495
 */
12496
void CTPNStmLabelBase::add_control_flow_flags(ulong flags)
12497
{
12498
    /* add the flags to any we already have */
12499
    control_flow_flags_ |= flags;
12500
}
12501
12502
/* ------------------------------------------------------------------------ */
12503
/*
12504
 *   'goto' statement node 
12505
 */
12506
12507
/*
12508
 *   Chart the control flow through the statement 
12509
 */
12510
unsigned long CTPNStmGotoBase::get_control_flow(int warn) const
12511
{
12512
    CTcSymbol *sym;
12513
    
12514
    /* find our target statement */
12515
    if (G_prs->get_goto_symtab() != 0
12516
        && (sym = G_prs->get_goto_symtab()->find(lbl_, lbl_len_)) != 0
12517
        && sym->get_type() == TC_SYM_LABEL)
12518
    {
12519
        /* 
12520
         *   we found our label - mark its statement as having a 'goto'
12521
         *   targeted at it 
12522
         */
12523
        ((CTcSymLabel *)sym)->add_control_flow_flags(TCPRS_FLOW_GOTO);
12524
    }
12525
    
12526
    /* indicate that we jump somewhere */
12527
    return TCPRS_FLOW_GOTO;
12528
}
12529
12530
/* ------------------------------------------------------------------------ */
12531
/*
12532
 *   'case' label statement node 
12533
 */
12534
12535
/*
12536
 *   fold constants 
12537
 */
12538
CTcPrsNode *CTPNStmCaseBase::fold_constants(CTcPrsSymtab *symtab)
12539
{
12540
    /* set our location for any errors that occur */
12541
    G_tok->set_line_info(get_source_desc(), get_source_linenum());
12542
12543
    /* fold constants in our expression */
12544
    expr_ = expr_->fold_constants(symtab);
12545
12546
    /* if the expression isn't a constant, it's an error */
12547
    if (!expr_->is_const())
12548
        log_error(TCERR_CASE_NOT_CONSTANT);
12549
    
12550
    /* fold constants in our labeled statement */
12551
    if (stm_ != 0)
12552
        stm_ = (CTPNStm *)stm_->fold_constants(symtab);
12553
12554
    /* we're unchanged by constant folding */
12555
    return this;
12556
}
12557
12558
/*
12559
 *   Chart the control flow through the statement 
12560
 */
12561
unsigned long CTPNStmCaseBase::get_control_flow(int warn) const
12562
{
12563
    /* 
12564
     *   if we have an underlying statement, control flow is the same as
12565
     *   the statement's control flow; if we don't have a statement (i.e.,
12566
     *   our labeled statement is an empty statement), we flow to the next
12567
     *   statement 
12568
     */
12569
    return (stm_ != 0 ? stm_->get_control_flow(warn) : TCPRS_FLOW_NEXT);
12570
}
12571
12572
/* ------------------------------------------------------------------------ */
12573
/*
12574
 *   'default' label statement node 
12575
 */
12576
12577
/*
12578
 *   fold constants 
12579
 */
12580
CTcPrsNode *CTPNStmDefaultBase::fold_constants(CTcPrsSymtab *symtab)
12581
{
12582
    /* set our location for any errors that occur */
12583
    G_tok->set_line_info(get_source_desc(), get_source_linenum());
12584
12585
    /* fold constants in our labeled statement */
12586
    if (stm_ != 0)
12587
        stm_ = (CTPNStm *)stm_->fold_constants(symtab);
12588
12589
    /* we're unchanged by constant folding */
12590
    return this;
12591
}
12592
12593
/*
12594
 *   Chart the control flow through the statement 
12595
 */
12596
unsigned long CTPNStmDefaultBase::get_control_flow(int warn) const
12597
{
12598
    /* 
12599
     *   if we have an underlying statement, control flow is the same as
12600
     *   the statement's control flow; if we don't have a statement (i.e.,
12601
     *   our labeled statement is an empty statement), we flow to the next
12602
     *   statement 
12603
     */
12604
    return (stm_ != 0 ? stm_->get_control_flow(warn) : TCPRS_FLOW_NEXT);
12605
}
12606
12607
/* ------------------------------------------------------------------------ */
12608
/*
12609
 *   'try' statement 
12610
 */
12611
12612
/*
12613
 *   add a 'catch' clause 
12614
 */
12615
void CTPNStmTryBase::add_catch(CTPNStmCatch *catch_stm)
12616
{
12617
    /* link it in at the end of our list */
12618
    if (last_catch_stm_ != 0)
12619
        last_catch_stm_->set_next_catch(catch_stm);
12620
    else
12621
        first_catch_stm_ = catch_stm;
12622
12623
    /* it's now the last catch statement */
12624
    last_catch_stm_ = catch_stm;
12625
    catch_stm->set_next_catch(0);
12626
12627
    /* it's the last statement in its group */
12628
    catch_stm->set_next_stm(0);
12629
}
12630
12631
/*
12632
 *   fold constants 
12633
 */
12634
CTcPrsNode *CTPNStmTryBase::fold_constants(CTcPrsSymtab *symtab)
12635
{
12636
    CTPNStmCatch *cur_catch;
12637
    
12638
    /* fold constants in our protected code */
12639
    if (body_stm_ != 0)
12640
        body_stm_->fold_constants(symtab);
12641
12642
    /* fold constants in our catch list */
12643
    for (cur_catch = first_catch_stm_ ; cur_catch != 0 ;
12644
         cur_catch = cur_catch->get_next_catch())
12645
    {
12646
        /* fold constants; assume this will not change the statement */
12647
        cur_catch->fold_constants(symtab);
12648
    }
12649
12650
    /* fold constants in our 'finally' clause */
12651
    if (finally_stm_ != 0)
12652
        finally_stm_->fold_constants(symtab);
12653
12654
    /* we're unchanged by constant folding */
12655
    return this;
12656
}
12657
12658
/*
12659
 *   Chart the control flow through the statement 
12660
 */
12661
unsigned long CTPNStmTryBase::get_control_flow(int warn) const
12662
{
12663
    unsigned long flags;
12664
    unsigned long fin_flags;
12665
    CTPNStmCatch *cur_catch;
12666
    
12667
    /*
12668
     *   First, determine the control flow through the protected code.  If
12669
     *   it exits, we either exit or proceed to the 'finally' clause.  
12670
     */
12671
    flags = (body_stm_ != 0
12672
             ? body_stm_->get_control_flow(warn) : TCPRS_FLOW_NEXT);
12673
12674
    /* 
12675
     *   Next, figure out what the various 'catch' clauses can do.  We
12676
     *   assume that each 'catch' clause is enterable, since any exception
12677
     *   could occur within the protected code (whether it explicitly
12678
     *   throws or not - it could call a function or method that throws).
12679
     *   So, we can simply OR together with the main block's flags all of
12680
     *   the ways the different 'catch' clauses can exit.  
12681
     */
12682
    for (cur_catch = first_catch_stm_ ; cur_catch != 0 ;
12683
         cur_catch = cur_catch->get_next_catch())
12684
    {
12685
        /* OR in the flags for this 'catch' block */
12686
        flags |= cur_catch->get_control_flow(warn);
12687
    }
12688
12689
    /*
12690
     *   If we have a 'finally' clause, it is definitely enterable, since
12691
     *   every other way of leaving the protected code goes through it.
12692
     *   Find how it can exit.  
12693
     */
12694
    fin_flags = (finally_stm_ != 0
12695
                 ? finally_stm_->get_control_flow(warn)
12696
                 : TCPRS_FLOW_NEXT);
12697
12698
    /* 
12699
     *   If the 'finally' block can't flow to next, then neither can the
12700
     *   main/catch blocks.  In addition, the main/catch blocks can't
12701
     *   'break', 'continue', 'goto', or 'return' either, because the
12702
     *   'finally' block will not allow the other blocks to proceed with
12703
     *   whatever transfers they attempt.  So, if 'next' isn't set in the
12704
     *   'finally' flags, clear out all of these flags from the main/catch
12705
     *   flags.  
12706
     */
12707
    if (!(fin_flags & TCPRS_FLOW_NEXT))
12708
        flags &= ~(TCPRS_FLOW_NEXT
12709
                   | TCPRS_FLOW_BREAK
12710
                   | TCPRS_FLOW_CONT
12711
                   | TCPRS_FLOW_RET_VAL
12712
                   | TCPRS_FLOW_RET_VOID
12713
                   | TCPRS_FLOW_GOTO);
12714
12715
    /* 
12716
     *   Add in the remaining flags from the 'finally' block, since
12717
     *   whatever it does, the overall statement does.  However, ignore
12718
     *   'next' flags in the 'finally' block, because if the 'finally'
12719
     *   code goes to next, it just means that we keep doing whatever we
12720
     *   were doing in the main block.  
12721
     */
12722
    fin_flags &= ~TCPRS_FLOW_NEXT;
12723
    flags |= fin_flags;
12724
12725
    /* return the result */
12726
    return flags;
12727
}
12728
12729
12730
/* ------------------------------------------------------------------------ */
12731
/*
12732
 *   'catch' clause
12733
 */
12734
12735
/*
12736
 *   set our exception class name 
12737
 */
12738
void CTPNStmCatchBase::set_exc_class(const CTcToken *tok)
12739
{
12740
    /* 
12741
     *   remember the token's contents - the tokenizer keeps token symbol
12742
     *   strings in memory throughout the compilation, so we can simply
12743
     *   store a direct reference to the string 
12744
     */
12745
    exc_class_ = tok->get_text();
12746
    exc_class_len_ = tok->get_text_len();
12747
}
12748
12749
/*
12750
 *   fold constants 
12751
 */
12752
CTcPrsNode *CTPNStmCatchBase::fold_constants(CTcPrsSymtab *symtab)
12753
{
12754
    /* fold constants in our body */
12755
    if (body_ != 0)
12756
        body_ = (CTPNStm *)body_->fold_constants(symtab);
12757
12758
    /* we're unchanged by constant folding */
12759
    return this;
12760
}
12761
12762
/*
12763
 *   Chart the control flow through the statement 
12764
 */
12765
unsigned long CTPNStmCatchBase::get_control_flow(int warn) const
12766
{
12767
    /*
12768
     *   our control flow is that of the body 
12769
     */
12770
    return (body_ != 0 ? body_->get_control_flow(warn) : TCPRS_FLOW_NEXT);
12771
}
12772
12773
12774
12775
/* ------------------------------------------------------------------------ */
12776
/*
12777
 *   'finally' clause 
12778
 */
12779
12780
/*
12781
 *   fold constants 
12782
 */
12783
CTcPrsNode *CTPNStmFinallyBase::fold_constants(CTcPrsSymtab *symtab)
12784
{
12785
    /* fold constants in our body */
12786
    if (body_ != 0)
12787
        body_ = (CTPNStm *)body_->fold_constants(symtab);
12788
12789
    /* we're unchanged by constant folding */
12790
    return this;
12791
}
12792
12793
/*
12794
 *   Chart the control flow through the statement 
12795
 */
12796
unsigned long CTPNStmFinallyBase::get_control_flow(int warn) const
12797
{
12798
    /* our control flow is that of the body */
12799
    return (body_ != 0 ? body_->get_control_flow(warn) : TCPRS_FLOW_NEXT);
12800
}
12801
12802
12803
12804
/* ------------------------------------------------------------------------ */
12805
/*
12806
 *   'throw' statement
12807
 */
12808
12809
/*
12810
 *   fold constants 
12811
 */
12812
CTcPrsNode *CTPNStmThrowBase::fold_constants(CTcPrsSymtab *symtab)
12813
{
12814
    /* set our location for any errors that occur */
12815
    G_tok->set_line_info(get_source_desc(), get_source_linenum());
12816
12817
    /* fold constants in our expression */
12818
    expr_ = expr_->fold_constants(symtab);
12819
    
12820
    /* we're unchanged by constant folding */
12821
    return this;
12822
}
12823
12824
/* ------------------------------------------------------------------------ */
12825
/*
12826
 *   'break' statement
12827
 */
12828
12829
/*
12830
 *   set the label 
12831
 */
12832
void CTPNStmBreakBase::set_label(const CTcToken *tok)
12833
{
12834
    /* 
12835
     *   token text is kept in memory throughout compilation, so we can
12836
     *   simply store a reference to the text without copying it 
12837
     */
12838
    lbl_ = tok->get_text();
12839
    lbl_len_ = tok->get_text_len();
12840
}
12841
12842
/*
12843
 *   chart control flow 
12844
 */
12845
ulong CTPNStmBreakBase::get_control_flow(int warn) const
12846
{
12847
    /*
12848
     *   If we don't have a label, we break out of our enclosing control
12849
     *   structure.  If we have a label, we act like a 'goto' statement. 
12850
     */
12851
    if (lbl_ == 0)
12852
    {
12853
        /* no label - we simply break out of the enclosing loop or switch */
12854
        return TCPRS_FLOW_BREAK;
12855
    }
12856
    else
12857
    {
12858
        CTcSymbol *sym;
12859
        
12860
        /* 
12861
         *   We have a label - we act like a 'goto' statement.  Find our
12862
         *   target statement and mark it as having a targeted 'break'.  
12863
         */
12864
        if (G_prs->get_goto_symtab() != 0
12865
            && (sym = G_prs->get_goto_symtab()->find(lbl_, lbl_len_)) != 0
12866
            && sym->get_type() == TC_SYM_LABEL)
12867
        {
12868
            /* 
12869
             *   we found our label - mark its statement as having a
12870
             *   'break' targeted at it 
12871
             */
12872
            ((CTcSymLabel *)sym)->add_control_flow_flags(TCPRS_FLOW_BREAK);
12873
        }
12874
12875
        /* indicate that we jump somewhere else */
12876
        return TCPRS_FLOW_GOTO;
12877
    }
12878
}
12879
12880
12881
/* ------------------------------------------------------------------------ */
12882
/*
12883
 *   'continue' statement 
12884
 */
12885
12886
/*
12887
 *   set the label 
12888
 */
12889
void CTPNStmContinueBase::set_label(const CTcToken *tok)
12890
{
12891
    /* 
12892
     *   token text is kept in memory throughout compilation, so we can
12893
     *   simply store a reference to the text without copying it 
12894
     */
12895
    lbl_ = tok->get_text();
12896
    lbl_len_ = tok->get_text_len();
12897
}
12898
12899
/*
12900
 *   chart control flow 
12901
 */
12902
ulong CTPNStmContinueBase::get_control_flow(int warn) const
12903
{
12904
    /*
12905
     *   If we don't have a label, we continue in our enclosing control
12906
     *   structure.  If we have a label, we act like a 'goto' statement.  
12907
     */
12908
    if (lbl_ == 0)
12909
    {
12910
        /* no label - we simply continue with the enclosing loop */
12911
        return TCPRS_FLOW_CONT;
12912
    }
12913
    else
12914
    {
12915
        CTcSymbol *sym;
12916
12917
        /* 
12918
         *   We have a label - we act like a 'goto' statement.  Find our
12919
         *   target statement and mark it as having a targeted 'continue'.
12920
         */
12921
        if (G_prs->get_goto_symtab() != 0
12922
            && (sym = G_prs->get_goto_symtab()->find(lbl_, lbl_len_)) != 0
12923
            && sym->get_type() == TC_SYM_LABEL)
12924
        {
12925
            /* 
12926
             *   we found our label - mark its statement as having a
12927
             *   'continue' targeted at it 
12928
             */
12929
            ((CTcSymLabel *)sym)->add_control_flow_flags(TCPRS_FLOW_CONT);
12930
        }
12931
12932
        /* indicate that we jump somewhere else */
12933
        return TCPRS_FLOW_GOTO;
12934
    }
12935
}
12936
12937
/* ------------------------------------------------------------------------ */
12938
/*
12939
 *   Object Definition Statement 
12940
 */
12941
12942
/*
12943
 *   add a superclass given the name
12944
 */
12945
void CTPNStmObjectBase::add_superclass(const CTcToken *tok)
12946
{
12947
    CTPNSuperclass *sc;
12948
    
12949
    /* create a new superclass list entry */
12950
    sc = new CTPNSuperclass(tok->get_text(), tok->get_text_len());
12951
12952
    /* add it to the end of my list */
12953
    if (last_sc_ != 0)
12954
        last_sc_->nxt_ = sc;
12955
    else
12956
        first_sc_ = sc;
12957
    last_sc_ = sc;
12958
}
12959
12960
/*
12961
 *   add a superclass given the object symbol
12962
 */
12963
void CTPNStmObjectBase::add_superclass(CTcSymbol *sym)
12964
{
12965
    CTPNSuperclass *sc;
12966
12967
    /* create a new superclass list entry */
12968
    sc = new CTPNSuperclass(sym);
12969
12970
    /* add it to the end of my list */
12971
    if (last_sc_ != 0)
12972
        last_sc_->nxt_ = sc;
12973
    else
12974
        first_sc_ = sc;
12975
    last_sc_ = sc;
12976
}
12977
12978
/*
12979
 *   Add a property value 
12980
 */
12981
CTPNObjProp *CTPNStmObjectBase::add_prop(CTcSymProp *prop_sym,
12982
                                         CTcPrsNode *expr,
12983
                                         int replace, int is_static)
12984
{
12985
    CTPNObjProp *prop;
12986
        
12987
    /* create the new property item */
12988
    prop = new CTPNObjProp((CTPNStmObject *)this, prop_sym, expr, 0,
12989
                           is_static);
12990
12991
    /* link it into my list */
12992
    add_prop_entry(prop, replace);
12993
12994
    /* return the new property to our caller */
12995
    return prop;
12996
}
12997
12998
/*
12999
 *   Add a method 
13000
 */
13001
void CTPNStmObjectBase::add_method(CTcSymProp *prop_sym,
13002
                                   CTPNCodeBody *code_body, int replace)
13003
{
13004
    /* create a new property and link it into my list */
13005
    add_prop_entry(new CTPNObjProp((CTPNStmObject *)this, prop_sym,
13006
                                   0, code_body, FALSE), replace);
13007
}
13008
13009
/*
13010
 *   Add an entry to my property list
13011
 */
13012
void CTPNStmObjectBase::add_prop_entry(CTPNObjProp *prop, int replace)
13013
{
13014
    /* link it in at the end of my list */
13015
    if (last_prop_ != 0)
13016
        last_prop_->nxt_ = prop;
13017
    else
13018
        first_prop_ = prop;
13019
    last_prop_ = prop;
13020
13021
    /* this is the last element */
13022
    prop->nxt_ = 0;
13023
13024
    /* count the new property */
13025
    ++prop_cnt_;
13026
13027
    /* check for replacement */
13028
    if (replace && !G_prs->get_syntax_only())
13029
    {
13030
        CTPNStmObject *stm;
13031
13032
        /* start at the current object */
13033
        stm = (CTPNStmObject *)this;
13034
13035
        /* iterate through all previous versions of the object */
13036
        for (;;)
13037
        {
13038
            CTcSymObj *sym;
13039
            CTPNSuperclass *sc;
13040
            
13041
            /* 
13042
             *   Get this object's superclass - since this is a 'modify'
13043
             *   object, its only superclass is object it directly
13044
             *   modifies.  If there's no superclass, the modified base
13045
             *   object must have been undefined, so we have errors to
13046
             *   begin with - ignore the replacement in this case.  
13047
             */
13048
            sc = stm->get_first_sc();
13049
            if (sc == 0)
13050
                break;
13051
13052
            /* get the symbol for the superclass */
13053
            sym = (CTcSymObj *)sc->get_sym();
13054
13055
            /* 
13056
             *   if this object is external, we must mark the property for
13057
             *   replacement so that we replace it at link time, after
13058
             *   we've loaded and resolved the external original object 
13059
             */
13060
            if (sym->is_extern())
13061
            {
13062
                /*
13063
                 *   Add an entry to my list of properties to delete in my
13064
                 *   base objects at link time.
13065
                 */
13066
                obj_sym_->add_del_prop_entry(prop->get_prop_sym());
13067
                
13068
                /* 
13069
                 *   no need to look any further for modified originals --
13070
                 *   we won't find any more of them in this translation
13071
                 *   unit, since they must all be external from here on up
13072
                 *   the chain 
13073
                 */
13074
                break;
13075
            }
13076
13077
            /* 
13078
             *   Get this symbol's object parse tree - since the symbol
13079
             *   isn't external, and because we can only modify objects
13080
             *   that we've already seen, the parse tree must be present.
13081
             *   If the parse tree isn't here, stop now; we might be
13082
             *   parsing for syntax only in this case.  
13083
             */
13084
            stm = sym->get_obj_stm();
13085
            if (stm == 0)
13086
                break;
13087
13088
            /* if this object isn't modified, we're done */
13089
            if (!sym->get_modified())
13090
                break;
13091
        
13092
            /* delete the property in the original object */
13093
            stm->delete_property(prop->get_prop_sym());
13094
        }
13095
    }
13096
}
13097
13098
/*
13099
 *   Delete a property - this is used when we 'replace' a property in a
13100
 *   'modify' object 
13101
 */
13102
void CTPNStmObjectBase::delete_property(CTcSymProp *prop_sym)
13103
{
13104
    CTPNObjProp *cur;
13105
    CTPNObjProp *prv;
13106
13107
    /* scan our and look for a property matching the given symbol */
13108
    for (prv = 0, cur = first_prop_ ; cur != 0 ; prv = cur, cur = cur->nxt_)
13109
    {
13110
        /* check for a match */
13111
        if (cur->get_prop_sym() == prop_sym)
13112
        {
13113
            /* this is the one to delete - unlink it from the list */
13114
            if (prv != 0)
13115
                prv->nxt_ = cur->nxt_;
13116
            else
13117
                first_prop_ = cur->nxt_;
13118
13119
            /* if that left the list empty, clear the tail pointer */
13120
            if (first_prop_ == 0)
13121
                last_prop_ = 0;
13122
13123
            /* decrement the property count */
13124
            --prop_cnt_;
13125
13126
            /* delete the property from the vocabulary list as well */
13127
            if (obj_sym_ != 0)
13128
                obj_sym_->delete_vocab_prop(prop_sym->get_prop());
13129
13130
            /* we need look no further */
13131
            break;
13132
        }
13133
    }
13134
}
13135
13136
/*
13137
 *   Add an implicit constructor, if one is required.  
13138
 */
13139
void CTPNStmObjectBase::add_implicit_constructor()
13140
{
13141
    int sc_cnt;
13142
    CTPNSuperclass *sc;
13143
13144
    /* count the superclasses */
13145
    for (sc_cnt = 0, sc = first_sc_ ; sc != 0 ; sc = sc->nxt_, ++sc_cnt) ;
13146
13147
    /*
13148
     *   If the object has multiple superclasses and doesn't have an
13149
     *   explicit constructor, add an implicit constructor that simply
13150
     *   invokes each superclass constructor using the same argument list
13151
     *   sent to our constructor.  
13152
     */
13153
    if (sc_cnt > 1)
13154
    {
13155
        CTPNObjProp *prop;
13156
        int has_constructor;
13157
13158
        /* scan the property list for a constructor property */
13159
        for (prop = first_prop_, has_constructor = FALSE ; prop != 0 ;
13160
             prop = prop->nxt_)
13161
        {
13162
            /* 
13163
             *   if this is a constructor, note that the object has an
13164
             *   explicit constructor 
13165
             */
13166
            if (prop->is_constructor())
13167
            {
13168
                /* we have a consructor */
13169
                has_constructor = TRUE;
13170
13171
                /* that's all we needed to know - no need to keep looking */
13172
                break;
13173
            }
13174
        }
13175
13176
        /* if we don't have a constructor, add one */
13177
        if (!has_constructor)
13178
        {
13179
            CTPNStmImplicitCtor *stm;
13180
            CTPNCodeBody *cb;
13181
            CTcSymLocal *varargs_local;
13182
13183
            /* 
13184
             *   Create a placeholder symbol for the varargs list variable
13185
             *   - we don't really need a symbol for this, but we do need
13186
             *   the symbol object so we can generate code for it
13187
             *   properly.  Note that a varargs list variable is a local,
13188
             *   not a formal.  
13189
             */
13190
            varargs_local = new CTcSymLocal("*", 1, FALSE, FALSE, 0);
13191
13192
            /* create the pseudo-statement for the implicit constructor */
13193
            stm = new CTPNStmImplicitCtor((CTPNStmObject *)this);
13194
13195
            /* 
13196
             *   Create a code body to hold the statement.  The code body
13197
             *   has one local symbol (the vararg list formal parameter),
13198
             *   and is defined by the 'constructor' property.  
13199
             */
13200
            cb = new CTPNCodeBody(G_prs->get_global_symtab(),
13201
                                  G_prs->get_goto_symtab(),
13202
                                  stm, 0, TRUE, TRUE, varargs_local, 1,
13203
                                  TRUE, 0);
13204
13205
            /* 
13206
             *   set the implicit constructor's source file location to the
13207
             *   source file location of the start of the object definition 
13208
             */
13209
            cb->set_source_pos(file_, linenum_);
13210
            
13211
            /* add it to the object's property list */
13212
            add_method(G_prs->get_constructor_sym(), cb, FALSE);
13213
        }
13214
    }
13215
}
13216
13217
13218
/* ------------------------------------------------------------------------ */
13219
/*
13220
 *   Object Property node 
13221
 */
13222
13223
/*
13224
 *   fold constants 
13225
 */
13226
CTcPrsNode *CTPNObjPropBase::fold_constants(CTcPrsSymtab *symtab)
13227
{
13228
    /* fold constants in our expression or code body, as appropriate */
13229
    if (expr_ != 0)
13230
    {
13231
        /* 
13232
         *   set this statement's source line location to be the current
13233
         *   location for error reporting 
13234
         */
13235
        G_tok->set_line_info(get_source_desc(),
13236
                             get_source_linenum());
13237
        
13238
        /* fold constants in our expression */
13239
        expr_ = expr_->fold_constants(symtab);
13240
        
13241
        /*
13242
         *   If the resulting expression isn't a constant value, we must
13243
         *   convert our expression to a method, since we need to evaluate
13244
         *   the value at run-time with code.  Create an expression
13245
         *   statement to hold the expression, then create a code body to
13246
         *   hold the expression statement.  
13247
         */
13248
        if (!expr_->is_const() && !expr_->is_dstring())
13249
        {
13250
            CTPNStm *stm;
13251
            
13252
            /*
13253
             *   Generate an implicit 'return' for a regular expression,
13254
             *   or an implicit static initializer for a static property.  
13255
             */
13256
            if (is_static_)
13257
            {
13258
                /* 
13259
                 *   it's a static initializer - generate a static
13260
                 *   initializer expression 
13261
                 */
13262
                stm = new CTPNStmStaticPropInit(expr_, prop_sym_->get_prop());
13263
            }
13264
            else if (expr_->has_return_value())
13265
            {
13266
                /* 
13267
                 *   create a 'return' statement to return the value of
13268
                 *   the expression 
13269
                 */
13270
                stm = new CTPNStmReturn(expr_);
13271
            }
13272
            else
13273
            {
13274
                /* 
13275
                 *   It has no return value, so it must be something like a
13276
                 *   double-quoted string or a call to a void function.  Wrap
13277
                 *   this in an 'expression statement' so that we evaluate
13278
                 *   the expression for its side effects but discard any
13279
                 *   return value.  
13280
                 */
13281
                stm = new CTPNStmExpr(expr_);
13282
            }
13283
13284
            /* create the code body */
13285
            code_body_ = new CTPNCodeBody(G_prs->get_global_symtab(), 0,
13286
                                          stm, 0, FALSE, FALSE, 0, 0,
13287
                                          TRUE, 0);
13288
13289
            /* 
13290
             *   forget about our expression, since it's now incorporated
13291
             *   into the code body 
13292
             */
13293
            expr_ = 0;
13294
13295
            /* 
13296
             *   set the source file location in both the code body and
13297
             *   the expression statement nodes to use our own source file
13298
             *   location 
13299
             */
13300
            stm->set_source_pos(file_, linenum_);
13301
            code_body_->set_source_pos(file_, linenum_);
13302
        }
13303
    }
13304
    else if (code_body_ != 0)
13305
    {
13306
        /* fold constants in our code body */
13307
        code_body_->fold_constants(symtab);
13308
    }
13309
13310
    /* we're not changed directly by this */
13311
    return this;
13312
}
13313
13314
/*
13315
 *   Am I a constructor?  
13316
 */
13317
int CTPNObjPropBase::is_constructor() const
13318
{
13319
    /*
13320
     *   I'm a constructor if I have a code body, and my property is the
13321
     *   constructor property.  
13322
     */
13323
    return (code_body_ != 0
13324
            && prop_sym_ != 0
13325
            && prop_sym_->get_prop() == G_prs->get_constructor_prop());
13326
}
13327
13328
/* ------------------------------------------------------------------------ */
13329
/*
13330
 *   code ('goto') label base 
13331
 */
13332
13333
/*
13334
 *   Add control flow flags to my statement 
13335
 */
13336
void CTcSymLabelBase::add_control_flow_flags(ulong flags)
13337
{
13338
    if (stm_ != 0)
13339
        stm_->add_control_flow_flags(flags);
13340
}
13341
13342
/* ------------------------------------------------------------------------ */
13343
/*
13344
 *   Export symbol 
13345
 */
13346
13347
/*
13348
 *   write to object file 
13349
 */
13350
void CTcPrsExport::write_to_obj_file(CVmFile *fp)
13351
{
13352
    /* write our internal symbol name, with length prefix */
13353
    fp->write_int2((int)get_sym_len());
13354
    fp->write_bytes(get_sym(), get_sym_len());
13355
13356
    /* write our external name, with length prefix */
13357
    fp->write_int2((int)get_ext_len());
13358
    fp->write_bytes(get_ext_name(), get_ext_len());
13359
}
13360
13361
/*
13362
 *   read from object file 
13363
 */
13364
CTcPrsExport *CTcPrsExport::read_from_obj_file(CVmFile *fp)
13365
{
13366
    char buf[TOK_SYM_MAX_LEN + 1];
13367
    size_t sym_len;
13368
    size_t ext_len;
13369
    const char *sym;
13370
    const char *ext;
13371
    CTcPrsExport *exp;
13372
    
13373
    /* read the symbol */
13374
    sym = CTcParser::read_len_prefix_str(fp, buf, sizeof(buf), &sym_len,
13375
                                         TCERR_EXPORT_SYM_TOO_LONG);
13376
    if (sym == 0)
13377
        return 0;
13378
13379
    /* read the external name */
13380
    ext = CTcParser::read_len_prefix_str(fp, buf, sizeof(buf), &ext_len,
13381
                                         TCERR_EXPORT_SYM_TOO_LONG);
13382
    if (ext == 0)
13383
        return 0;
13384
13385
    /* create a new export record for the symbol */
13386
    exp = new CTcPrsExport(sym, sym_len);
13387
13388
    /* set the external name */
13389
    exp->set_extern_name(ext, ext_len);
13390
13391
    /* return the new record */
13392
    return exp;
13393
}