3
"$Header: d:/cvsroot/tads/tads3/TCPRS.CPP,v 1.5 1999/07/11 00:46:53 MJRoberts Exp $";
7
* Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved.
9
* Please see the accompanying license file, LICENSE.TXT, for information
10
* on using and copying this software.
14
tcprs.cpp - TADS 3 Compiler Parser
16
This parser module contains code required for any parser usage, both
17
for a full compiler and for a debugger.
21
04/30/99 MJRoberts - Creation
40
/* ------------------------------------------------------------------------ */
42
* Expression Operator parser objects. These objects are linked
43
* together in a network that defines the order of precedence for
44
* expression operators.
46
* These objects use static storage. This is acceptable, even though
47
* these objects aren't all "const" qualified, because the compiler uses
48
* a single global parser object; since there's only the one parser,
49
* there only needs to be a single network of these objects.
52
/* unary operator parser */
53
static CTcPrsOpUnary S_op_unary;
56
static const CTcPrsOpMul S_op_mul;
57
static const CTcPrsOpDiv S_op_div;
58
static const CTcPrsOpMod S_op_mod;
59
static const CTcPrsOpBin *const
60
S_grp_factor[] = { &S_op_mul, &S_op_div, &S_op_mod, 0 };
61
static const CTcPrsOpBinGroup S_op_factor(&S_op_unary, &S_op_unary,
65
static const CTcPrsOpAdd S_op_add;
66
static const CTcPrsOpSub S_op_sub;
67
static const CTcPrsOpBin *const S_grp_term[] = { &S_op_add, &S_op_sub, 0 };
68
static const CTcPrsOpBinGroup S_op_term(&S_op_factor, &S_op_factor,
72
static const CTcPrsOpShl S_op_shl;
73
static const CTcPrsOpShr S_op_shr;
74
static const CTcPrsOpBin *const S_grp_shift[] = { &S_op_shl, &S_op_shr, 0 };
75
static const CTcPrsOpBinGroup S_op_shift(&S_op_term, &S_op_term,
78
/* magnitude comparisons group */
79
static const CTcPrsOpGt S_op_gt;
80
static const CTcPrsOpGe S_op_ge;
81
static const CTcPrsOpLt S_op_lt;
82
static const CTcPrsOpLe S_op_le;
83
static const CTcPrsOpBin *const
84
S_grp_relcomp[] = { &S_op_gt, &S_op_ge, &S_op_lt, &S_op_le, 0 };
85
static const CTcPrsOpBinGroup S_op_relcomp(&S_op_shift, &S_op_shift,
89
* Equality/inequality comparison group. Note that the equality operator
90
* is non-const because we want the option to change the operator on the
91
* fly based on syntax mode - '==' in C-mode, '=' in TADS traditional mode.
92
* (This was a feature of tads 2, but we have since deprecated it in tads
93
* 3, so this ability is actually just vestigial at this point. No harm in
94
* keeping around the code internally for it, though, since it's pretty
97
* Note also that this is a special binary group - this one recognizes the
98
* non-keyword operators 'is in' and 'not in'.
100
static CTcPrsOpEq S_op_eq;
101
static const CTcPrsOpNe S_op_ne;
102
static const CTcPrsOpBin *const
103
S_grp_eqcomp[] = { &S_op_eq, &S_op_ne, 0 };
104
static const CTcPrsOpBinGroupCompare
105
S_op_eqcomp(&S_op_relcomp, &S_op_relcomp, S_grp_eqcomp);
107
/* bitwise AND operator */
108
static const CTcPrsOpBAnd S_op_band(&S_op_eqcomp, &S_op_eqcomp);
110
/* bitwise XOR operator */
111
static const CTcPrsOpBXor S_op_bxor(&S_op_band, &S_op_band);
113
/* bitwise OR operator */
114
static const CTcPrsOpBOr S_op_bor(&S_op_bxor, &S_op_bxor);
116
/* logical AND operator */
117
static const CTcPrsOpAnd S_op_and(&S_op_bor, &S_op_bor);
119
/* logical OR operator */
120
static const CTcPrsOpOr S_op_or(&S_op_and, &S_op_and);
122
/* conditional operator */
123
static const CTcPrsOpIf S_op_if;
126
* assignment operator - note that this is non-const, because we must be
127
* able to change the operator - '=' in C-mode, and ':=' in TADS
130
static CTcPrsOpAsi S_op_asi;
133
static const CTcPrsOpComma S_op_comma(&S_op_asi, &S_op_asi);
136
/* ------------------------------------------------------------------------ */
142
* initialize the parser
144
CTcParser::CTcParser()
148
/* we don't have any module information yet */
152
/* start out in normal mode */
153
pp_expr_mode_ = FALSE;
154
src_group_mode_ = FALSE;
156
/* create the global symbol table */
157
global_symtab_ = new CTcPrsSymtab(0);
159
/* no enclosing statement yet */
162
/* no source location yet */
166
/* no dictionaries yet */
168
dict_head_ = dict_tail_ = 0;
170
/* no object file dictionary list yet */
172
obj_file_dict_idx_ = 0;
174
/* no dictionary properties yet */
177
/* no grammar productions yet */
178
gramprod_head_ = gramprod_tail_ = 0;
180
/* no object file symbol list yet */
182
obj_file_sym_idx_ = 0;
184
/* no object templates yet */
185
template_head_ = template_tail_ = 0;
187
/* allocate some initial template parsing space */
188
template_expr_max_ = 16;
189
template_expr_ = (CTcObjTemplateInst *)G_prsmem->
190
alloc(sizeof(template_expr_[0]) * template_expr_max_);
193
local_cnt_ = max_local_cnt_ = 0;
195
/* no local or goto symbol table yet */
197
enclosing_local_symtab_ = 0;
200
/* no debugger local symbol table yet */
203
/* not in a preprocessor constant expression */
204
pp_expr_mode_ = FALSE;
206
/* assume we're doing full compilation */
207
syntax_only_ = FALSE;
209
/* no nested top-level statements yet */
210
nested_stm_head_ = 0;
211
nested_stm_tail_ = 0;
213
/* no anonymous objects yet */
217
/* no non-symbol objects yet */
218
nonsym_obj_head_ = 0;
219
nonsym_obj_tail_ = 0;
221
/* allocate an initial context variable property array */
222
ctx_var_props_size_ = 50;
223
ctx_var_props_ = (tctarg_prop_id_t *)
224
t3malloc(ctx_var_props_size_ * sizeof(tctarg_prop_id_t));
226
/* no context variable properties assigned yet */
227
ctx_var_props_cnt_ = 0;
228
ctx_var_props_used_ = 0;
231
* no context variable indices assigned yet - start at one higher
232
* than the index at which we always store 'self'
234
next_ctx_arr_idx_ = TCPRS_LOCAL_CTX_METHODCTX + 1;
236
/* 'self' isn't valid yet */
239
/* start at enum ID 1 (let 0 serve as an invalid value) */
242
/* the '+' property is not yet defined */
245
/* no exported symbols yet */
246
exp_head_ = exp_tail_ = 0;
248
/* allocate an initial '+' stack */
249
plus_stack_alloc_ = 32;
250
plus_stack_ = (CTPNStmObject **)
251
t3malloc(plus_stack_alloc_ * sizeof(*plus_stack_));
253
/* clear out the stack */
254
for (i = 0 ; i < plus_stack_alloc_ ; ++i)
257
/* there's no current code body (function/method body) yet */
260
/* nothing in the local context has been referenced yet */
261
self_referenced_ = FALSE;
262
local_ctx_needs_self_ = FALSE;
263
full_method_ctx_referenced_ = FALSE;
264
local_ctx_needs_full_method_ctx_ = FALSE;
268
* Initialize. This must be called after the code generator is set up.
270
void CTcParser::init()
272
static const char construct_name[] = "construct";
273
static const char finalize_name[] = "finalize";
274
static const char objcall_name[] = ".objcall";
275
static const char graminfo_name[] = "grammarInfo";
276
static const char miscvocab_name[] = "miscVocab";
277
static const char lexical_parent_name[] = "lexicalParent";
278
static const char src_order_name[] = "sourceTextOrder";
279
static const char src_group_name[] = "sourceTextGroup";
280
static const char src_group_mod_name[] = "sourceTextGroupName";
281
static const char src_group_seq_name[] = "sourceTextGroupOrder";
282
tctarg_prop_id_t graminfo_prop_id;
283
tctarg_prop_id_t lexpar_prop_id;
284
tctarg_prop_id_t src_order_prop_id;
285
tctarg_prop_id_t src_group_prop_id;
286
tctarg_prop_id_t src_group_mod_prop_id;
287
tctarg_prop_id_t src_group_seq_prop_id;
290
/* allocate and note our special property ID's */
291
constructor_prop_ = G_cg->new_prop_id();
292
finalize_prop_ = G_cg->new_prop_id();
293
objcall_prop_ = G_cg->new_prop_id();
294
graminfo_prop_id = G_cg->new_prop_id();
295
miscvocab_prop_ = G_cg->new_prop_id();
296
lexpar_prop_id = G_cg->new_prop_id();
297
src_order_prop_id = G_cg->new_prop_id();
298
src_group_prop_id = G_cg->new_prop_id();
299
src_group_mod_prop_id = G_cg->new_prop_id();
300
src_group_seq_prop_id = G_cg->new_prop_id();
302
/* add a "construct" property for constructors */
303
sym = new CTcSymProp(construct_name, sizeof(construct_name) - 1,
304
FALSE, (tctarg_prop_id_t)constructor_prop_);
305
sym->mark_referenced();
306
global_symtab_->add_entry(sym);
307
constructor_sym_ = sym;
309
/* add a "finalize" property for finalizers */
310
sym = new CTcSymProp(finalize_name, sizeof(finalize_name) - 1,
311
FALSE, (tctarg_prop_id_t)finalize_prop_);
312
sym->mark_referenced();
313
global_symtab_->add_entry(sym);
315
/* add an "object call" property for anonymous functions */
316
sym = new CTcSymProp(objcall_name, sizeof(objcall_name) - 1,
317
FALSE, (tctarg_prop_id_t)objcall_prop_);
318
sym->mark_referenced();
319
global_symtab_->add_entry(sym);
321
/* add a "grammarInfo" property for grammar production match objects */
322
graminfo_prop_ = new CTcSymProp(graminfo_name, sizeof(graminfo_name) - 1,
324
(tctarg_prop_id_t)graminfo_prop_id);
325
graminfo_prop_->mark_referenced();
326
global_symtab_->add_entry(graminfo_prop_);
328
/* add a "miscVocab" property for miscellaneous vocabulary words */
329
sym = new CTcSymProp(miscvocab_name, sizeof(miscvocab_name) - 1,
330
FALSE, miscvocab_prop_);
331
sym->mark_referenced();
332
global_symtab_->add_entry(sym);
334
/* add a "lexicalParent" property for a nested object's parent */
335
lexical_parent_sym_ = new CTcSymProp(lexical_parent_name,
336
sizeof(lexical_parent_name) - 1,
338
(tctarg_prop_id_t)lexpar_prop_id);
339
lexical_parent_sym_->mark_referenced();
340
global_symtab_->add_entry(lexical_parent_sym_);
342
/* add a "sourceTextOrder" property */
343
src_order_sym_ = new CTcSymProp(src_order_name,
344
sizeof(src_order_name) - 1,
346
(tctarg_prop_id_t)src_order_prop_id);
347
src_order_sym_->mark_referenced();
348
global_symtab_->add_entry(src_order_sym_);
350
/* start the sourceTextOrder index at 1 */
353
/* add a "sourceTextGroup" property */
354
src_group_sym_ = new CTcSymProp(src_group_name,
355
sizeof(src_group_name) - 1,
357
(tctarg_prop_id_t)src_group_prop_id);
358
src_group_sym_->mark_referenced();
359
global_symtab_->add_entry(src_group_sym_);
361
/* we haven't created the sourceTextGroup referral object yet */
362
src_group_id_ = TCTARG_INVALID_OBJ;
364
/* add a "sourceTextGroupName" property */
365
src_group_mod_sym_ = new CTcSymProp(
366
src_group_mod_name, sizeof(src_group_mod_name) - 1, FALSE,
367
(tctarg_prop_id_t)src_group_mod_prop_id);
368
src_group_mod_sym_->mark_referenced();
369
global_symtab_->add_entry(src_group_mod_sym_);
371
/* add a "sourceTextGroupOrder" property */
372
src_group_seq_sym_ = new CTcSymProp(
373
src_group_seq_name, sizeof(src_group_seq_name) - 1, FALSE,
374
(tctarg_prop_id_t)src_group_seq_prop_id);
375
src_group_seq_sym_->mark_referenced();
376
global_symtab_->add_entry(src_group_seq_sym_);
383
CTcParser::~CTcParser()
386
* Note that we don't have to delete certain objects, because we
387
* allocated them out of the parser memory pool and will be
388
* automatically deleted when the pool is deleted. For example, we
389
* don't have to delete any symbol tables, including the global
393
/* delete the module name, if it's known */
394
lib_free_str(module_name_);
396
/* delete the object file symbol fixup list, if present */
397
if (obj_sym_list_ != 0)
398
t3free(obj_sym_list_);
400
/* delete the object file dictionary fixup list, if present */
401
if (obj_dict_list_ != 0)
402
t3free(obj_dict_list_);
404
/* delete the context variable property list */
405
if (ctx_var_props_ != 0)
406
t3free(ctx_var_props_);
408
/* delete the export list */
409
while (exp_head_ != 0)
413
/* remember the next entry, since we're deleting our pointer to it */
414
nxt = exp_head_->get_next();
416
/* delete this entry */
419
/* move on to the next */
423
/* delete the '+' stack */
427
/* ------------------------------------------------------------------------ */
429
* Set the module information
431
void CTcParser::set_module_info(const char *name, int seqno)
433
/* if we have a name stored already, delete the old one */
434
lib_free_str(module_name_);
436
/* store the new name and sequence number */
437
module_name_ = lib_copy_str(name);
438
module_seqno_ = seqno;
442
* Change the #pragma C mode. On changing this mode, we'll change the
443
* assignment operator and equality operator tokens. If 'mode' is true,
444
* we're in C mode; otherwise, we're in traditional TADS mode.
446
* #pragma C+: assignment is '=', equality is '=='
447
*. #pragma C-: assignment is ':=', equality is '='.
449
void CTcParser::set_pragma_c(int mode)
451
/* set the assignment operator */
452
S_op_asi.set_asi_op(mode ? TOKT_EQ : TOKT_ASI);
454
/* set the equality comparison operator */
455
S_op_eq.set_eq_op(mode ? TOKT_EQEQ : TOKT_EQ);
459
* Parse an expression. This parses a top-level comma expression.
461
CTcPrsNode *CTcParser::parse_expr()
463
/* parse a comma expression */
464
return S_op_comma.parse();
468
* Parse a condition expression. Warns if the outermost operator is a
471
CTcPrsNode *CTcParser::parse_cond_expr()
475
/* parse the expression */
479
* if the outermost operator is a simple assignment, display an
482
if (cond != 0 && cond->is_simple_asi() && !G_prs->get_syntax_only())
483
G_tok->log_warning(TCERR_ASI_IN_COND);
485
/* return the result */
490
* Parse an assignment expression.
492
CTcPrsNode *CTcParser::parse_asi_expr()
494
/* parse an assignment expression */
495
return S_op_asi.parse();
499
* Parse an expression or a double-quoted string expression
501
CTcPrsNode *CTcParser::parse_expr_or_dstr(int allow_comma_expr)
504
* parse the appropriate kind of expression - if a comma expression is
505
* allowed, parse that, otherwise parse an assignment expression (as
506
* that's the next thing down the hierarchy from the comma operator)
508
return (allow_comma_expr ? S_op_comma.parse() : S_op_asi.parse());
512
* Parse a required semicolon
514
int CTcParser::parse_req_sem()
516
const char eof_str[] = "<end of file>";
518
/* check to see if we found the semicolon */
519
if (G_tok->cur() == TOKT_SEM)
521
/* success - skip the semicolon and tell the caller to proceed */
527
* check what we have; the type of error we want to log depends on
533
/* log the extra ')' error */
534
G_tok->log_error(TCERR_EXTRA_RPAR);
537
* we're probably in an expression that ended before the user
538
* thought it should; skip the extraneous material up to the
541
return skip_to_sem();
545
G_tok->log_error(TCERR_EXTRA_RBRACK);
547
/* skip up to the next semicolon */
548
return skip_to_sem();
552
* missing semicolon at end of file - log the missing-semicolon
553
* error and tell the caller not to proceed, since there's
554
* nothing left to parse
556
G_tok->log_error(TCERR_EXPECTED_SEMI,
557
(int)sizeof(eof_str)-1, eof_str);
562
* the source is probably just missing a semicolon; log the
563
* error, and tell the caller to proceed from the current
566
G_tok->log_error_curtok(TCERR_EXPECTED_SEMI);
572
* Skip to the next semicolon
574
int CTcParser::skip_to_sem()
576
/* keep going until we find a semicolon or some other reason to stop */
579
/* see what we have next */
583
/* end of file - tell the caller not to proceed */
588
* it's the semicolon at last - skip it and tell the caller
597
* Don't skip past braces - the caller probably simply left
598
* out a semicolon at the end of a statement, and we've now
599
* reached the next block start or end. Stop here and tell
600
* the caller to proceed.
605
/* skip anything else */
613
* Create a symbol node
615
CTcPrsNode *CTcParser::create_sym_node(const textchar_t *sym, size_t sym_len)
618
CTcPrsSymtab *symtab;
621
* First, look up the symbol in local scope. Local scope symbols
622
* can always be resolved during parsing, because the language
623
* requires that local scope items be declared before their first
626
entry = local_symtab_->find(sym, sym_len, &symtab);
628
/* if we found it in local scope, return a resolved symbol node */
629
if (entry != 0 && symtab != global_symtab_)
630
return new CTPNSymResolved(entry);
632
/* if there's a debugger local scope, look it up there */
633
if (debug_symtab_ != 0)
635
tcprsdbg_sym_info info;
637
/* look it up in the debug symbol table */
638
if (debug_symtab_->find_symbol(sym, sym_len, &info))
640
/* found it - return a debugger local variable */
641
return new CTPNSymDebugLocal(&info);
646
* We didn't find it in local scope, so the symbol cannot be resolved
647
* until code generation - return an unresolved symbol node. Note a
648
* possible implicit self-reference, since this could be a property of
651
set_self_referenced(TRUE);
652
return new CTPNSym(sym, sym_len);
656
* Find or add a dictionary symbol
658
CTcDictEntry *CTcParser::declare_dict(const char *txt, size_t len)
661
CTcDictEntry *entry = 0;
663
/* look up the symbol */
664
sym = (CTcSymObj *)global_symtab_->find(txt, len);
666
/* if it's not defined, add it; otherwise, make sure it's correct */
669
/* it's not yet defined - define it as a dictionary */
670
sym = new CTcSymObj(G_tok->getcur()->get_text(),
671
G_tok->getcur()->get_text_len(),
672
FALSE, G_cg->new_obj_id(), FALSE,
675
/* add it to the global symbol table */
676
global_symtab_->add_entry(sym);
678
/* create a new dictionary entry */
679
entry = create_dict_entry(sym);
683
/* make sure it's an object of metaclass 'dictionary' */
684
if (sym->get_type() != TC_SYM_OBJ)
687
G_tok->log_error_curtok(TCERR_REDEF_AS_OBJ);
689
/* forget the symbol - it's not even an object */
692
else if (sym->get_metaclass() != TC_META_DICT)
694
/* it's an object, but not a dictionary - log an error */
695
G_tok->log_error_curtok(TCERR_REDEF_AS_DICT);
697
/* forget the symbol */
701
/* find it in our dictionary list */
702
entry = get_dict_entry(sym);
705
* if we didn't find the entry, we must have loaded the symbol
706
* from a symbol file - add the dictionary list entry now
709
entry = create_dict_entry(sym);
712
/* return the new dictionary object */
717
* Find or add a grammar production symbol. Returns the master
718
* CTcGramProdEntry object for the grammar production.
720
CTcGramProdEntry *CTcParser::declare_gramprod(const char *txt, size_t len)
723
CTcGramProdEntry *entry;
725
/* find or define the grammar production object symbol */
726
sym = find_or_def_gramprod(txt, len, &entry);
728
/* return the entry */
733
* Find or add a grammar production symbol.
735
CTcSymObj *CTcParser::find_or_def_gramprod(const char *txt, size_t len,
736
CTcGramProdEntry **entryp)
739
CTcGramProdEntry *entry;
741
/* look up the symbol */
742
sym = (CTcSymObj *)global_symtab_->find(txt, len);
744
/* if it's not defined, add it; otherwise, make sure it's correct */
747
/* it's not yet defined - define it as a grammar production */
748
sym = new CTcSymObj(G_tok->getcur()->get_text(),
749
G_tok->getcur()->get_text_len(),
750
FALSE, G_cg->new_obj_id(), FALSE,
751
TC_META_GRAMPROD, 0);
753
/* add it to the global symbol table */
754
global_symtab_->add_entry(sym);
756
/* create a new production list entry */
757
entry = create_gramprod_entry(sym);
761
/* make sure it's an object of metaclass 'grammar production' */
762
if (sym->get_type() != TC_SYM_OBJ)
765
G_tok->log_error_curtok(TCERR_REDEF_AS_OBJ);
767
/* forget the symbol - it's not even an object */
770
else if (sym->get_metaclass() != TC_META_GRAMPROD)
772
/* it's an object, but not a production - log an error */
773
G_tok->log_error_curtok(TCERR_REDEF_AS_GRAMPROD);
775
/* forget the symbol */
780
* If we found the symbol, make sure it's not marked as external,
781
* since we're actually defining a rule for this production. If
782
* the production is exported from any other modules, we'll share
783
* the production object with the other modules.
786
sym->set_extern(FALSE);
788
/* get the existing production object, if any */
789
entry = get_gramprod_entry(sym);
792
* if we didn't find the entry, we must have loaded the symbol
793
* from a symbol file - add the entry now
796
entry = create_gramprod_entry(sym);
800
* if the caller is interested in knowing the associated grammar rule
801
* list entry, return it
806
/* return the new symbol */
811
* Add a symbol loaded from an object file
813
void CTcParser::add_sym_from_obj_file(uint idx, class CTcSymbol *sym)
816
* add the entry to the object file index list - adjust from the
817
* 1-based index used in the file to an array index
819
obj_sym_list_[idx - 1] = sym;
823
* Get an object file symbol, ensuring that it's an object symbol
825
CTcSymObj *CTcParser::get_objfile_objsym(uint idx)
829
/* get the object based on the index */
830
sym = (CTcSymObj *)get_objfile_sym(idx);
832
/* make sure it's an object - if it isn't, return null */
833
if (sym == 0 || sym->get_type() != TC_SYM_OBJ)
836
/* it checks out - return it */
842
* Add a dictionary symbol loaded from an object file
844
void CTcParser::add_dict_from_obj_file(CTcSymObj *sym)
848
/* get the current entry, if any */
849
entry = get_dict_entry(sym);
851
/* if there's no current entry, create a new one */
854
/* create the entry */
855
entry = create_dict_entry(sym);
858
/* add the entry to the object file index list */
859
obj_dict_list_[obj_file_dict_idx_++] = entry;
863
* create a new dictionary list entry
865
CTcDictEntry *CTcParser::create_dict_entry(CTcSymObj *sym)
869
/* allocate the new entry */
870
entry = new (G_prsmem) CTcDictEntry(sym);
872
/* link the new entry into our list */
874
dict_tail_->set_next(entry);
880
* set the metaclass-specific extra data in the object symbol to
881
* point to the dictionary list entry
883
sym->set_meta_extra(entry);
885
/* return the new entry */
890
* find a dictionary list entry for a given object symbol
892
CTcDictEntry *CTcParser::get_dict_entry(CTcSymObj *obj)
894
/* the dictionary list entry is the metaclass-specific data pointer */
895
return (obj == 0 ? 0 : (CTcDictEntry *)obj->get_meta_extra());
899
* Create a grammar production list entry. This creates a
900
* CTcGramProdEntry object associated with the given grammar production
901
* symbol, and links the new entry into the master list of grammar
902
* production entries.
904
CTcGramProdEntry *CTcParser::create_gramprod_entry(CTcSymObj *sym)
906
CTcGramProdEntry *entry;
908
/* allocate the new entry */
909
entry = new (G_prsmem) CTcGramProdEntry(sym);
911
/* link the new entry into our list of anonymous match entries */
912
if (gramprod_tail_ != 0)
913
gramprod_tail_->set_next(entry);
915
gramprod_head_ = entry;
916
gramprod_tail_ = entry;
919
* set the metaclass-specific data in the object symbol to point to
920
* the grammar production list entry
923
sym->set_meta_extra(entry);
925
/* return the new entry */
930
* find a grammar entry for a given (GrammarProd) object symbol
932
CTcGramProdEntry *CTcParser::get_gramprod_entry(CTcSymObj *obj)
934
/* the grammar entry is the metaclass-specific data pointer */
935
return (obj == 0 ? 0 : (CTcGramProdEntry *)obj->get_meta_extra());
939
* Add a nested top-level statement to our list
941
void CTcParser::add_nested_stm(CTPNStmTop *stm)
943
/* link it into our list */
944
if (nested_stm_tail_ != 0)
945
nested_stm_tail_->set_next_stm_top(stm);
947
nested_stm_head_ = stm;
948
nested_stm_tail_ = stm;
952
* Add an anonymous object to our list
954
void CTcParser::add_anon_obj(CTcSymObj *sym)
956
/* link it into our list */
957
if (anon_obj_tail_ != 0)
958
anon_obj_tail_->nxt_ = sym;
960
anon_obj_head_ = sym;
961
anon_obj_tail_ = sym;
963
/* it's the last one */
966
/* mark the symbol as anonymous */
971
* Add a non-symbolic object to our list
973
void CTcParser::add_nonsym_obj(tctarg_obj_id_t id)
975
tcprs_nonsym_obj *obj;
977
/* allocate a link structure */
978
obj = new (G_prsmem) tcprs_nonsym_obj(id);
980
/* link it into our list */
981
if (nonsym_obj_tail_ != 0)
982
nonsym_obj_tail_->nxt_ = obj;
984
nonsym_obj_head_ = obj;
985
nonsym_obj_tail_ = obj;
989
* Basic routine to read a length-prefixed symbol. Uses the given
990
* temporary buffer, then stores the text in tokenizer memory (which
991
* remains valid and available throughout compilation). If the length
992
* exceeds the temporary buffer length, we'll flag the given error and
993
* return null. The length return pointer can be null if the caller wants
994
* the results null-terminated rather than returned with a counted length.
995
* If the length pointer is given, the result will not be null-terminated.
998
const char *CTcParser::read_len_prefix_str
999
(CVmFile *fp, char *tmp_buf, size_t tmp_buf_len, size_t *ret_len,
1000
int err_if_too_long)
1005
/* read the length to read from the file */
1006
read_len = (size_t)fp->read_uint2();
1008
/* if we need null termination, add a byte to the allocation length */
1009
alloc_len = read_len + (ret_len == 0 ? 1 : 0);
1011
/* if it won't fit in the temporary buffer, it's an error */
1012
if (alloc_len > tmp_buf_len)
1014
/* log the error and return failure */
1015
G_tcmain->log_error(0, 0, TC_SEV_ERROR, err_if_too_long);
1019
/* read the bytes into the temporary buffer */
1020
fp->read_bytes(tmp_buf, read_len);
1022
/* add null termination if required, or set the return length if not */
1024
tmp_buf[read_len] = '\0';
1026
*ret_len = read_len;
1028
/* store the result in the tokenizer's text list and return the result */
1029
return G_tok->store_source(tmp_buf, alloc_len);
1033
* Read a length prefixed string into a given buffer. Returns zero on
1034
* success, non-zero on failure.
1036
int CTcParser::read_len_prefix_str(CVmFile *fp, char *buf, size_t buf_len,
1037
int err_if_too_long)
1042
/* read the length to read from the file */
1043
read_len = (size_t)fp->read_uint2();
1045
/* add a byte for null termination */
1046
alloc_len = read_len + 1;
1048
/* if it won't fit in the temporary buffer, it's an error */
1049
if (alloc_len > buf_len)
1051
/* log the error and return failure */
1052
G_tcmain->log_error(0, 0, TC_SEV_ERROR, err_if_too_long);
1056
/* read the bytes into the caller's buffer */
1057
fp->read_bytes(buf, read_len);
1059
/* add null termination */
1060
buf[read_len] = '\0';
1066
/* ------------------------------------------------------------------------ */
1072
* set a string value
1074
void CTcConstVal::set_sstr(const char *val, size_t len)
1076
/* store the type */
1079
/* store a pointer to the string */
1080
val_.strval_.strval_ = val;
1081
val_.strval_.strval_len_ = len;
1083
/* for image file layout purposes, record the length of this string */
1084
G_cg->note_str(len);
1090
void CTcConstVal::set_list(CTPNList *lst)
1095
/* remember the list */
1096
val_.listval_ = lst;
1098
/* for image file layout purposes, record the length of this list */
1099
G_cg->note_list(lst->get_count());
1103
* Convert a value to a string
1105
const char *CTcConstVal::cvt_to_str(char *buf, size_t bufl,
1112
/* the result is "nil" */
1121
/* the result is "true" */
1125
strcpy(buf, "true");
1130
/* it's already a string */
1131
*result_len = get_val_str_len();
1132
return get_val_str();
1135
/* convert our signed integer value */
1139
sprintf(buf, "%ld", get_val_int());
1140
*result_len = strlen(buf);
1144
/* we store these as strings */
1145
*result_len = get_val_float_len();
1146
return get_val_float();
1149
/* can't convert other types */
1155
* Compare for equality to another constant value
1157
int CTcConstVal::is_equal_to(const CTcConstVal *val) const
1163
* if the types are not equal, the values are not equal; otherwise,
1164
* check the various types
1166
if (typ_ != val->get_type())
1168
/* the types aren't equal, so the values are not equal */
1172
/* the types are the same; do the comparison based on the type */
1176
/* unknown type; unknown values can never be equal */
1182
* nil==nil and true==true; since we know the types are the
1183
* same, the values are the same
1188
/* compare the integers */
1189
return (get_val_int() == val->get_val_int());
1192
/* compare the strings */
1193
return (get_val_str_len() == val->get_val_str_len()
1194
&& memcmp(get_val_str(), val->get_val_str(),
1195
get_val_str_len()) == 0);
1199
* if the lists don't have the same number of elements, they're
1202
if (get_val_list()->get_count() != val->get_val_list()->get_count())
1206
* compare each element of each list; if they're all the same,
1207
* the values are the same
1209
ele1 = get_val_list()->get_head();
1210
ele2 = val->get_val_list()->get_head();
1211
for ( ; ele1 != 0 && ele2 != 0 ;
1212
ele1 = ele1->get_next(), ele2 = ele2->get_next())
1214
/* if these elements aren't equal, the lists aren't equal */
1215
if (!ele1->get_expr()->get_const_val()
1216
->is_equal_to(ele2->get_expr()->get_const_val()))
1220
/* we didn't find any differences, so the lists are equal */
1224
/* if the object values are the same, the values match */
1225
return (get_val_obj() == val->get_val_obj());
1228
/* if the property values are the same, the values match */
1229
return (get_val_prop() == val->get_val_prop());
1231
case TC_CVT_FUNCPTR:
1233
* if both symbols are the same, the values match; otherwise,
1234
* they refer to different functions
1236
return (get_val_funcptr_sym() == val->get_val_funcptr_sym());
1239
/* unknown type; return unequal */
1245
/* ------------------------------------------------------------------------ */
1250
/* ------------------------------------------------------------------------ */
1252
* Parse a left-associative binary operator
1254
CTcPrsNode *CTcPrsOpBin::parse() const
1259
/* parse our left side - if that fails, return failure */
1260
if ((lhs = left_->parse()) == 0)
1263
/* keep going as long as we find our operator */
1266
/* check my operator */
1267
if (G_tok->cur() == get_op_tok())
1269
CTcPrsNode *const_tree;
1271
/* skip the matching token */
1274
/* parse the right-hand side */
1275
if ((rhs = right_->parse()) == 0)
1278
/* try folding our subnodes into a constant value, if possible */
1279
const_tree = eval_constant(lhs, rhs);
1282
* if we couldn't calculate a constant value, build the tree
1285
if (const_tree == 0)
1288
* Build my tree, then proceed to parse any additional
1289
* occurrences of our operator, with the result of
1290
* applying this occurrence of the operator as the
1291
* left-hand side of the new operator.
1293
lhs = build_tree(lhs, rhs);
1297
/* we got a constant value - use it as the result directly */
1304
* it's not my operator - return what we thought might have
1305
* been our left-hand side
1312
/* ------------------------------------------------------------------------ */
1314
* Parse a group of left-associative binary operators at the same
1317
CTcPrsNode *CTcPrsOpBinGroup::parse() const
1321
/* parse our left side - if that fails, return failure */
1322
if ((lhs = left_->parse()) == 0)
1325
/* keep going as long as we find one of our operators */
1326
while (find_and_apply_op(&lhs)) ;
1328
/* return the expression tree */
1333
* Find an apply one of our operators to the already-parsed left-hand
1334
* side. Returns true if we found an operator, false if not.
1336
int CTcPrsOpBinGroup::find_and_apply_op(CTcPrsNode **lhs) const
1338
const CTcPrsOpBin *const *op;
1341
/* check each operator at this precedence level */
1342
for (op = ops_ ; *op != 0 ; ++op)
1344
/* check this operator's token */
1345
if (G_tok->cur() == (*op)->get_op_tok())
1347
CTcPrsNode *const_tree;
1349
/* skip the operator token */
1352
/* parse the right-hand side */
1353
if ((rhs = right_->parse()) == 0)
1355
/* error - cancel the entire expression */
1360
/* try folding our subnodes into a constant value */
1361
const_tree = (*op)->eval_constant(*lhs, rhs);
1364
* if we couldn't calculate a constant value, build the tree
1367
if (const_tree == 0)
1370
* build my tree, replacing the original left-hand side
1371
* with the new expression
1373
*lhs = (*op)->build_tree(*lhs, rhs);
1377
/* we got a constant value - use it as the result */
1382
* Tell the caller to proceed to parse any additional
1383
* occurrences of our operator - this will apply the next
1384
* occurrence of the operator as the left-hand side of the
1392
* if we got here, we didn't find an operator - tell the caller that
1393
* we've reached the end of this operator's possible span
1399
/* ------------------------------------------------------------------------ */
1401
* Comparison operator group
1403
CTcPrsNode *CTcPrsOpBinGroupCompare::parse() const
1407
/* parse our left side - if that fails, return failure */
1408
if ((lhs = left_->parse()) == 0)
1411
/* keep going as long as we find one of our operators */
1417
* try one of our regular operators - if we find it, go back for
1418
* another round to see if there's another operator following
1419
* the next expression
1421
if (find_and_apply_op(&lhs))
1425
* check for the 'is in' operator - 'is' and 'in' aren't
1426
* keywords, so we must check for symbol tokens with the text of
1427
* these context-sensitive keywords
1429
if (G_tok->cur() == TOKT_SYM
1430
&& G_tok->getcur()->text_matches("is", 2))
1432
/* we have 'is' - get the next token and check if it's 'in' */
1433
if (G_tok->next() == TOKT_SYM
1434
&& G_tok->getcur()->text_matches("in", 2))
1436
/* scan the expression list */
1437
rhs = parse_inlist();
1441
/* build the node */
1442
lhs = new CTPNIsIn(lhs, rhs);
1445
* we've applied the 'is in' operator - go back for
1446
* another operator from the comparison group
1452
/* it's not 'is in' - throw back the token and keep looking */
1458
* Check for the 'not in' operator
1460
if (G_tok->cur() == TOKT_SYM
1461
&& G_tok->getcur()->text_matches("not", 3))
1463
/* we have 'is' - get the next token and check if it's 'in' */
1464
if (G_tok->next() == TOKT_SYM
1465
&& G_tok->getcur()->text_matches("in", 2))
1467
/* scan the expression list */
1468
rhs = parse_inlist();
1472
/* build the node */
1473
lhs = new CTPNNotIn(lhs, rhs);
1476
* we've applied the 'is in' operator - go back for
1477
* another operator from the comparison group
1483
/* it's not 'is in' - throw back the token and keep looking */
1488
/* we didn't find any of our operators - we're done */
1492
/* return the expression */
1497
* parse the list for the right-hand side of an 'is in' or 'not in'
1500
CTPNArglist *CTcPrsOpBinGroupCompare::parse_inlist() const
1506
/* skip the second keyword token, and check for an open paren */
1507
if (G_tok->next() == TOKT_LPAR)
1509
/* skip the paren */
1515
* log an error, and keep going on the assumption that it was
1516
* merely omitted and the rest of the list is well-formed
1518
G_tok->log_error_curtok(TCERR_IN_REQ_LPAR);
1521
/* keep going until we find the close paren */
1522
for (argc = 0, arg_head = arg_tail = 0 ;; )
1527
/* if this is the close paren, we're done */
1528
if (G_tok->cur() == TOKT_RPAR)
1531
/* parse this expression */
1532
expr = S_op_asi.parse();
1536
/* count the argument */
1539
/* create a new argument node */
1540
arg_cur = new CTPNArg(expr);
1543
* link the new node at the end of our list (this preserves the
1544
* order of the original list)
1547
arg_tail->set_next_arg(arg_cur);
1552
/* we need to be looking at a comma or right paren */
1553
if (G_tok->cur() == TOKT_RPAR)
1555
/* that's the end of the list */
1558
else if (G_tok->cur() == TOKT_COMMA)
1560
/* skip the comma and parse the next argument */
1566
* If we're at the end of the file, there's no point
1567
* proceding, so return failure. If we've reached something
1568
* that looks like a statement separator (semicolon, curly
1569
* brace), also return failure, since the problem is clearly
1570
* a missing right paren. Otherwise, assume that a comma
1571
* was missing and continue as though we have another
1574
switch(G_tok->cur())
1578
G_tok->log_error_curtok(TCERR_EXPECTED_IN_COMMA);
1581
* if we're at the end of file, return what we have so
1582
* far; otherwise continue, assuming that they merely
1583
* left out a comma between two argument expressions
1585
if (G_tok->cur() == TOKT_EOF)
1586
return new CTPNArglist(argc, arg_head);
1595
* we're apparently at the end of the statement; flag
1596
* the error as a missing right paren, and return what
1599
G_tok->log_error_curtok(TCERR_EXPECTED_IN_RPAR);
1600
return new CTPNArglist(argc, arg_head);
1605
/* skip the closing paren */
1608
/* create and return the argument list descriptor */
1609
return new CTPNArglist(argc, arg_head);
1612
/* ------------------------------------------------------------------------ */
1618
* try to evaluate a constant expression
1620
CTcPrsNode *CTcPrsOpComma::eval_constant(CTcPrsNode *left,
1621
CTcPrsNode *right) const
1624
* if both sides are constants, the result is the constant on the
1625
* right side; we can't simply fold down to a right-side constant if
1626
* the left side is not constant, though, because we must still
1627
* evaluate the left side at run-time for any possible side effects
1629
if (left->is_const() && right->is_const())
1631
/* both are constants - simply return the right constant value */
1637
* one or the other is non-constant, so we can't fold the
1638
* expression - return null to so indicate
1645
* build a subtree for the comma operator
1647
CTcPrsNode *CTcPrsOpComma::build_tree(CTcPrsNode *left,
1648
CTcPrsNode *right) const
1650
return new CTPNComma(left, right);
1653
/* ------------------------------------------------------------------------ */
1655
* logical OR operator
1659
* try to evaluate a constant expression
1661
CTcPrsNode *CTcPrsOpOr::eval_constant(CTcPrsNode *left,
1662
CTcPrsNode *right) const
1664
/* check for constants */
1665
if (left->is_const())
1670
* Check for constants. If the first expression is constant,
1671
* the result will always be either 'true' (if the first
1672
* expression's constant value is true), or the value of the
1673
* second expression (if the first expression's constant value
1676
* Note that it doesn't matter whether or not the right side is
1677
* a constant. If the left is true, the right will never be
1678
* executed because of the short-circuit logic; if the left is
1679
* nil, the result will always be the result of the right value.
1681
if (left->get_const_val()->get_val_bool())
1684
* the left is true, so the result is always true, and the
1685
* right never gets executed
1691
/* the left is nil, so the result is the right value */
1695
/* ensure the result is a boolean value */
1696
if (ret->is_const())
1698
/* make it a true/nil constant value */
1699
ret->get_const_val()
1700
->set_bool(ret->get_const_val()->get_val_bool());
1704
/* boolean-ize the value at run-time as needed */
1705
ret = new CTPNBoolize(ret);
1708
/* return the result */
1714
* one or the other is non-constant, so we can't fold the
1715
* expression - return null to so indicate
1724
CTcPrsNode *CTcPrsOpOr::build_tree(CTcPrsNode *left,
1725
CTcPrsNode *right) const
1727
return new CTPNOr(left, right);
1730
/* ------------------------------------------------------------------------ */
1732
* logical AND operator
1736
* try to evaluate a constant expression
1738
CTcPrsNode *CTcPrsOpAnd::eval_constant(CTcPrsNode *left,
1739
CTcPrsNode *right) const
1742
* Check for constants. If the first expression is constant, the
1743
* result will always be either 'nil' (if the first expression's
1744
* constant value is nil), or the value of the second expression (if
1745
* the first expression's constant value is 'true').
1747
* Note that it doesn't matter whether or not the right side is a
1748
* constant. If the left is nil, the right will never be executed
1749
* because of the short-circuit logic; if the left is true, the
1750
* result will always be the result of the right value.
1752
if (left->is_const())
1757
* The left value is a constant, so the result is always know.
1758
* If the left value is nil, the result is nil; otherwise, it's
1761
if (left->get_const_val()->get_val_bool())
1763
/* the left side is true - the result is the right side */
1769
* The left side is nil - the result is nil, and the right
1770
* side never gets executed.
1775
/* ensure the result is a boolean value */
1776
if (ret->is_const())
1778
/* make it a true/nil constant value */
1779
ret->get_const_val()
1780
->set_bool(ret->get_const_val()->get_val_bool());
1784
/* boolean-ize the value at run-time as needed */
1785
ret = new CTPNBoolize(ret);
1788
/* return the result */
1794
* one or the other is non-constant, so we can't fold the
1795
* expression - return null to so indicate
1804
CTcPrsNode *CTcPrsOpAnd::build_tree(CTcPrsNode *left,
1805
CTcPrsNode *right) const
1807
return new CTPNAnd(left, right);
1810
/* ------------------------------------------------------------------------ */
1812
* Generic Comparison Operator parser base class
1816
* evaluate a constant expression
1818
CTcPrsNode *CTcPrsOpRel::eval_constant(CTcPrsNode *left,
1819
CTcPrsNode *right) const
1821
/* check for constants */
1822
if (left->is_const() && right->is_const())
1824
tc_constval_type_t typ1, typ2;
1828
typ1 = left->get_const_val()->get_type();
1829
typ2 = right->get_const_val()->get_type();
1831
/* determine what we're comparing */
1832
if (typ1 == TC_CVT_INT && typ2 == TC_CVT_INT)
1836
/* get the values */
1837
val1 = left->get_const_val()->get_val_int();
1838
val2 = right->get_const_val()->get_val_int();
1840
/* calculate the sense of the integer comparison */
1841
sense = (val1 < val2 ? -1 : val1 == val2 ? 0 : 1);
1843
else if (typ1 == TC_CVT_SSTR && typ2 == TC_CVT_SSTR)
1845
/* compare the string values */
1846
sense = strcmp(left->get_const_val()->get_val_str(),
1847
right->get_const_val()->get_val_str());
1849
else if (typ1 == TC_CVT_FLOAT || typ2 == TC_CVT_FLOAT)
1851
/* we can't compare floats at compile time, but it's legal */
1856
/* these types are incomparable */
1857
G_tok->log_error(TCERR_CONST_BAD_COMPARE,
1858
G_tok->get_op_text(get_op_tok()));
1862
/* set the result in the left value */
1863
left->get_const_val()->set_bool(get_bool_val(sense));
1865
/* return the updated left value */
1871
* one or the other is non-constant, so we can't fold the
1872
* expression - return null to so indicate
1879
/* ------------------------------------------------------------------------ */
1881
* greater-than operator
1887
CTcPrsNode *CTcPrsOpGt::build_tree(CTcPrsNode *left,
1888
CTcPrsNode *right) const
1890
return new CTPNGt(left, right);
1893
/* ------------------------------------------------------------------------ */
1895
* less-than operator
1901
CTcPrsNode *CTcPrsOpLt::build_tree(CTcPrsNode *left,
1902
CTcPrsNode *right) const
1904
return new CTPNLt(left, right);
1907
/* ------------------------------------------------------------------------ */
1909
* greater-or-equal operator
1915
CTcPrsNode *CTcPrsOpGe::build_tree(CTcPrsNode *left,
1916
CTcPrsNode *right) const
1918
return new CTPNGe(left, right);
1921
/* ------------------------------------------------------------------------ */
1923
* less-or-equal operator
1929
CTcPrsNode *CTcPrsOpLe::build_tree(CTcPrsNode *left,
1930
CTcPrsNode *right) const
1932
return new CTPNLe(left, right);
1935
/* ------------------------------------------------------------------------ */
1937
* General equality/inequality operators base class
1941
* evaluate a constant expression
1943
CTcPrsNode *CTcPrsOpEqComp::eval_constant(CTcPrsNode *left,
1944
CTcPrsNode *right) const
1948
/* check for constants */
1949
if (left->is_const() && right->is_const())
1951
/* both sides are constants - determine if they're equal */
1952
ops_equal = left->get_const_val()
1953
->is_equal_to(right->get_const_val());
1955
/* set the result in the left value */
1956
left->get_const_val()->set_bool(get_bool_val(ops_equal));
1958
/* return the updated left value */
1961
else if (left->is_addr() && right->is_addr())
1967
* both sides are addresses - if they're both addresses of the
1968
* same subexpression, then the values are comparable as
1969
* compile-time constants
1971
ops_equal = ((CTPNAddr *)left)
1972
->is_addr_eq((CTPNAddr *)right, &comparable);
1974
/* if they're not comparable, we can't fold this as a constant */
1978
/* generate the appropriate boolean result for the comparison */
1979
cval.set_bool(get_bool_val(ops_equal));
1981
/* return a new constant node with the result */
1982
return new CTPNConst(&cval);
1987
* one or the other is non-constant, so we can't fold the
1988
* expression - return null to so indicate
1995
/* ------------------------------------------------------------------------ */
2003
CTcPrsNode *CTcPrsOpEq::build_tree(CTcPrsNode *left,
2004
CTcPrsNode *right) const
2006
return new CTPNEq(left, right);
2009
/* ------------------------------------------------------------------------ */
2011
* inequality operator
2017
CTcPrsNode *CTcPrsOpNe::build_tree(CTcPrsNode *left,
2018
CTcPrsNode *right) const
2020
return new CTPNNe(left, right);
2023
/* ------------------------------------------------------------------------ */
2031
CTPNIsInBase::CTPNIsInBase(CTcPrsNode *lhs, class CTPNArglist *rhs)
2034
/* presume we don't have a constant value */
2035
const_true_ = FALSE;
2041
CTcPrsNode *CTPNIsInBase::fold_binop()
2048
/* if the left-hand side isn't constant, there's nothing to do */
2049
if (!left_->is_const())
2052
/* the right side is always an argument list */
2053
lst = (CTPNArglist *)right_;
2055
/* look for the value in the arguments */
2056
for (prv = 0, arg = lst->get_arg_list_head() ; arg != 0 ; arg = nxt)
2058
/* remember the next argument, in case we eliminate this one */
2059
nxt = arg->get_next_arg();
2061
/* check to see if this argument is a constant */
2062
if (arg->is_const())
2065
* This one's a constant, so check to see if we found the
2066
* left side value. If the left side equals this value,
2067
* note that we found the value.
2069
if (left_->get_const_val()->is_equal_to(arg->get_const_val()))
2072
* The values are equal, so the result of the expression
2073
* is definitely 'true'.
2078
* Because the 'is in' operator only evaluates operands
2079
* from the 'in' list until it finds one that matches,
2080
* any remaining operands will simply never be
2081
* evaluated. We can thus discard the rest of the
2088
* We now know whether the left side equals this constant
2089
* list element. This is never going to change because both
2090
* values are constant, so there's no point in making this
2091
* same comparison over and over again at run-time. We can
2092
* thus eliminate this argument from the list.
2094
lst->set_argc(lst->get_argc() - 1);
2096
lst->set_arg_list_head(nxt);
2098
prv->set_next_arg(nxt);
2103
* If the argument list is now completely empty, the result of the
2104
* expression is a constant.
2106
if (lst->get_arg_list_head() == 0)
2108
/* set the left operand's value to our result */
2109
left_->get_const_val()->set_bool(const_true_);
2111
/* return the constant value in place of the entire expression */
2115
/* we're not a constant, to return myself unchanged */
2119
/* ------------------------------------------------------------------------ */
2127
CTPNNotInBase::CTPNNotInBase(CTcPrsNode *lhs, class CTPNArglist *rhs)
2130
/* presume we don't have a constant value */
2131
const_false_ = FALSE;
2135
* fold constants for binary operator
2137
CTcPrsNode *CTPNNotInBase::fold_binop()
2144
/* if the left-hand side isn't constant, there's nothing to do */
2145
if (!left_->is_const())
2148
/* the right side is always an argument list */
2149
lst = (CTPNArglist *)right_;
2151
/* look for the value in the arguments */
2152
for (prv = 0, arg = lst->get_arg_list_head() ; arg != 0 ; arg = nxt)
2154
/* remember the next argument, in case we eliminate this one */
2155
nxt = arg->get_next_arg();
2157
/* check to see if this argument is a constant */
2158
if (arg->is_const())
2161
* This one's a constant, so check to see if we found the
2162
* left side value. If the left side equals this value,
2163
* note that we found the value.
2165
if (left_->get_const_val()->is_equal_to(arg->get_const_val()))
2168
* The values are equal, so the result of the expression
2169
* is definitely 'nil'.
2171
const_false_ = TRUE;
2174
* Because the 'not in' operator only evaluates operands
2175
* from the 'in' list until it finds one that matches,
2176
* any remaining operands will simply never be
2177
* evaluated. We can thus discard the rest of the
2184
* We now know whether the left side equals this constant
2185
* list element. This is never going to change because both
2186
* values are constant, so there's no point in making this
2187
* same comparison over and over again at run-time. We can
2188
* thus eliminate this argument from the list.
2190
lst->set_argc(lst->get_argc() - 1);
2192
lst->set_arg_list_head(nxt);
2194
prv->set_next_arg(nxt);
2199
* If the argument list is now completely empty, the result of the
2200
* expression is a constant.
2202
if (lst->get_arg_list_head() == 0)
2204
/* set the left operand's value to our result */
2205
left_->get_const_val()->set_bool(!const_false_);
2207
/* return the constant value in place of the entire expression */
2211
/* we're not a constant, to return myself unchanged */
2215
/* ------------------------------------------------------------------------ */
2217
* General arithmetic operator base class
2221
* evaluate constant value
2223
CTcPrsNode *CTcPrsOpArith::eval_constant(CTcPrsNode *left,
2224
CTcPrsNode *right) const
2226
/* check for constants */
2227
if (left->is_const() && right->is_const())
2229
/* require that both values are integers or floats */
2230
if (left->get_const_val()->get_type() == TC_CVT_FLOAT
2231
|| right->get_const_val()->get_type() == TC_CVT_FLOAT)
2233
/* can't do it at compile time, but it's legal */
2236
else if (left->get_const_val()->get_type() != TC_CVT_INT
2237
|| right->get_const_val()->get_type() != TC_CVT_INT)
2239
/* incompatible types - log an error */
2240
G_tok->log_error(TCERR_CONST_BINARY_REQ_NUM,
2241
G_tok->get_op_text(get_op_tok()));
2248
/* calculate the result */
2249
result = calc_result(left->get_const_val()->get_val_int(),
2250
right->get_const_val()->get_val_int());
2252
/* assign the result back to the left operand */
2253
left->get_const_val()->set_int(result);
2256
/* return the updated left value */
2262
* one or the other is non-constant, so we can't fold the
2263
* expression - return null to so indicate
2269
/* ------------------------------------------------------------------------ */
2271
* bitwise OR operator
2277
CTcPrsNode *CTcPrsOpBOr::build_tree(CTcPrsNode *left,
2278
CTcPrsNode *right) const
2280
return new CTPNBOr(left, right);
2283
/* ------------------------------------------------------------------------ */
2285
* bitwise AND operator
2291
CTcPrsNode *CTcPrsOpBAnd::build_tree(CTcPrsNode *left,
2292
CTcPrsNode *right) const
2294
return new CTPNBAnd(left, right);
2297
/* ------------------------------------------------------------------------ */
2299
* bitwise XOR operator
2305
CTcPrsNode *CTcPrsOpBXor::build_tree(CTcPrsNode *left,
2306
CTcPrsNode *right) const
2308
return new CTPNBXor(left, right);
2312
/* ------------------------------------------------------------------------ */
2314
* shift left operator
2320
CTcPrsNode *CTcPrsOpShl::build_tree(CTcPrsNode *left,
2321
CTcPrsNode *right) const
2323
return new CTPNShl(left, right);
2326
/* ------------------------------------------------------------------------ */
2328
* shift right operator
2334
CTcPrsNode *CTcPrsOpShr::build_tree(CTcPrsNode *left,
2335
CTcPrsNode *right) const
2337
return new CTPNShr(left, right);
2340
/* ------------------------------------------------------------------------ */
2342
* multiplication operator
2348
CTcPrsNode *CTcPrsOpMul::build_tree(CTcPrsNode *left,
2349
CTcPrsNode *right) const
2351
return new CTPNMul(left, right);
2354
/* ------------------------------------------------------------------------ */
2362
CTcPrsNode *CTcPrsOpDiv::build_tree(CTcPrsNode *left,
2363
CTcPrsNode *right) const
2365
return new CTPNDiv(left, right);
2369
* evaluate constant result
2371
long CTcPrsOpDiv::calc_result(long a, long b) const
2373
/* check for divide-by-zero */
2376
/* log a divide-by-zero error */
2377
G_tok->log_error(TCERR_CONST_DIV_ZERO);
2379
/* the result isn't really meaningful, but return something anyway */
2384
/* return the result */
2388
/* ------------------------------------------------------------------------ */
2396
CTcPrsNode *CTcPrsOpMod::build_tree(CTcPrsNode *left,
2397
CTcPrsNode *right) const
2399
return new CTPNMod(left, right);
2403
* evaluate constant result
2405
long CTcPrsOpMod::calc_result(long a, long b) const
2407
/* check for divide-by-zero */
2410
/* log a divide-by-zero error */
2411
G_tok->log_error(TCERR_CONST_DIV_ZERO);
2413
/* the result isn't really meaningful, but return something anyway */
2418
/* return the result */
2423
/* ------------------------------------------------------------------------ */
2425
* subtraction operator
2431
CTcPrsNode *CTcPrsOpSub::build_tree(CTcPrsNode *left,
2432
CTcPrsNode *right) const
2434
return new CTPNSub(left, right);
2438
* evaluate a constant value
2440
CTcPrsNode *CTcPrsOpSub::eval_constant(CTcPrsNode *left,
2441
CTcPrsNode *right) const
2443
if (left->is_const() && right->is_const())
2445
tc_constval_type_t typ1, typ2;
2448
typ1 = left->get_const_val()->get_type();
2449
typ2 = right->get_const_val()->get_type();
2451
/* check our types */
2452
if (typ1 == TC_CVT_INT && typ2 == TC_CVT_INT)
2454
/* calculate the integer sum */
2455
left->get_const_val()
2456
->set_int(left->get_const_val()->get_val_int()
2457
- right->get_const_val()->get_val_int());
2459
else if (typ1 == TC_CVT_FLOAT || typ2 == TC_CVT_FLOAT)
2461
/* can't fold float constants at compile time */
2464
else if (typ1 == TC_CVT_LIST)
2468
/* get the original list */
2469
lst = left->get_const_val()->get_val_list();
2472
* if the right side is a list, remove each element of that
2473
* list from the list on the left; otherwise, remove the
2474
* value on the right from the list on the left
2476
if (typ2 == TC_CVT_LIST)
2478
/* remove each element of the rhs list from the lhs list */
2481
/* scan the list, adding each element */
2482
for (ele = right->get_const_val()
2483
->get_val_list()->get_head() ;
2484
ele != 0 ; ele = ele->get_next())
2486
/* add this element's underlying expression value */
2487
lst->remove_element(ele->get_expr()->get_const_val());
2492
/* remove the rhs value from the lhs list */
2493
lst->remove_element(right->get_const_val());
2498
/* these types are incompatible - log an error */
2499
G_tok->log_error(TCERR_CONST_BINMINUS_INCOMPAT);
2503
/* return the updated left side */
2508
/* they're not constant - we can't generate a constant result */
2513
/* ------------------------------------------------------------------------ */
2519
* evaluate constant value
2521
CTcPrsNode *CTcPrsOpAdd::eval_constant(CTcPrsNode *left,
2522
CTcPrsNode *right) const
2524
/* check for constants */
2525
if (left->is_const() && right->is_const())
2527
tc_constval_type_t typ1, typ2;
2530
typ1 = left->get_const_val()->get_type();
2531
typ2 = right->get_const_val()->get_type();
2533
/* check our types */
2534
if (typ1 == TC_CVT_INT && typ2 == TC_CVT_INT)
2536
/* calculate the integer sum */
2537
left->get_const_val()
2538
->set_int(left->get_const_val()->get_val_int()
2539
+ right->get_const_val()->get_val_int());
2541
else if (typ1 == TC_CVT_FLOAT || typ2 == TC_CVT_FLOAT)
2543
/* can't fold float constants at compile time */
2546
else if (typ1 == TC_CVT_LIST)
2550
/* get the original list */
2551
lst = left->get_const_val()->get_val_list();
2554
* if the right side is also a list, concatenate it onto the
2555
* left list; otherwise, just add the right side as a new
2556
* element to the existing list
2558
if (typ2 == TC_CVT_LIST)
2562
/* scan the list, adding each element */
2563
for (ele = right->get_const_val()
2564
->get_val_list()->get_head() ;
2565
ele != 0 ; ele = ele->get_next())
2567
/* add this element's underlying expression value */
2568
lst->add_element(ele->get_expr());
2573
/* add a new list element for the right side */
2574
lst->add_element(right);
2578
* this list is longer than the original(s); tell the parser
2579
* about it in case it's the longest list yet
2581
G_cg->note_list(lst->get_count());
2583
else if (typ1 == TC_CVT_SSTR || typ2 == TC_CVT_SSTR)
2587
const char *str1, *str2;
2591
/* if the second value is a list, we can't make a constant */
2592
if (typ2 == TC_CVT_LIST)
2595
/* convert both values to strings if they're not already */
2596
str1 = left->get_const_val()
2597
->cvt_to_str(buf1, sizeof(buf1), &len1);
2598
str2 = right->get_const_val()
2599
->cvt_to_str(buf2, sizeof(buf2), &len2);
2602
* if we couldn't convert one or the other, leave the result
2605
if (str1 == 0 || str2 == 0)
2609
* allocate space in the node pool for the concatenation of
2610
* the two strings - if that fails, don't bother with the
2613
new_str = (char *)G_prsmem->alloc(len1 + len2 + 1);
2617
/* copy the two string values into the new space */
2618
memcpy(new_str, str1, len1);
2619
memcpy(new_str + len1, str2, len2);
2620
new_str[len1 + len2] = '\0';
2622
/* set the new value in the left node */
2623
left->get_const_val()->set_sstr(new_str, len1 + len2);
2627
/* these types are incompatible - log an error */
2628
G_tok->log_error(TCERR_CONST_BINPLUS_INCOMPAT);
2632
/* return the updated left value */
2637
/* the values aren't constant, so the result isn't constant */
2646
CTcPrsNode *CTcPrsOpAdd::build_tree(CTcPrsNode *left,
2647
CTcPrsNode *right) const
2649
return new CTPNAdd(left, right);
2652
/* ------------------------------------------------------------------------ */
2654
* Assignment Operator Group
2658
* parse an assignment expression
2660
CTcPrsNode *CTcPrsOpAsi::parse() const
2666
/* start by parsing a conditional subexpression */
2667
lhs = S_op_if.parse();
2671
/* get the next operator */
2672
curtyp = G_tok->cur();
2674
/* check to see if it's an assignment operator of some kind */
2687
/* it's an assignment operator - process it */
2691
/* check against the current simple-assignment operator */
2692
if (curtyp == asi_op_)
2694
/* it's an assignment operator - process it */
2700
* it's not an assignment - return the original
2701
* subexpression with no further elaboration
2707
/* check for a valid lvalue */
2708
if (!lhs->check_lvalue())
2710
/* log an error but continue parsing */
2711
G_tok->log_error(TCERR_INVALID_LVALUE,
2712
G_tok->get_op_text(G_tok->cur()));
2715
/* skip the assignment operator */
2719
* Recursively parse an assignment subexpression. Do this
2720
* recursively rather than iteratively, because assignment operators
2721
* group right-to-left. By recursively parsing an assignment, our
2722
* right-hand side will contain all remaining assignment expressions
2723
* incorporated into it.
2729
/* build and return the result based on the operator type */
2733
lhs = new CTPNAddAsi(lhs, rhs);
2737
lhs = new CTPNSubAsi(lhs, rhs);
2741
lhs = new CTPNMulAsi(lhs, rhs);
2745
lhs = new CTPNDivAsi(lhs, rhs);
2749
lhs = new CTPNModAsi(lhs, rhs);
2753
lhs = new CTPNBAndAsi(lhs, rhs);
2757
lhs = new CTPNBOrAsi(lhs, rhs);
2761
lhs = new CTPNBXorAsi(lhs, rhs);
2765
lhs = new CTPNShlAsi(lhs, rhs);
2769
lhs = new CTPNShrAsi(lhs, rhs);
2773
/* plain assignment operator */
2774
lhs = new CTPNAsi(lhs, rhs);
2778
/* return the result */
2782
/* ------------------------------------------------------------------------ */
2784
* Tertiary Conditional Operator
2787
CTcPrsNode *CTcPrsOpIf::parse() const
2793
/* parse the conditional part */
2794
first = S_op_or.parse();
2798
/* if we're not looking at the '?' operator, we're done */
2799
if (G_tok->cur() != TOKT_QUESTION)
2802
/* skip the '?' operator */
2806
* parse the second part, which can be any expression, including a
2807
* double-quoted string expression or a comma expression (even though
2808
* the '?:' operator overall has higher precedence than ',', we can't
2809
* steal away operands from a ',' before our ':' because that would
2810
* leave the ':' with nothing to go with)
2812
second = G_prs->parse_expr_or_dstr(TRUE);
2816
/* make sure we have the ':' after the second part */
2817
if (G_tok->cur() != TOKT_COLON)
2820
* log the error, but continue parsing as though we found the
2821
* ':' - if the ':' is simply missing, this will allow us to
2822
* recover and continue parsing the rest of the expression
2824
G_tok->log_error(TCERR_QUEST_WITHOUT_COLON);
2826
/* if we're at the end of file, there's no point in continuing */
2827
if (G_tok->cur() == TOKT_EOF)
2835
* parse the third part, which can be any other expression, including a
2836
* double-quoted string expression - but not a comma expression, since
2837
* we have higher precedence than ','
2839
third = G_prs->parse_expr_or_dstr(FALSE);
2844
* If the condition is constant, we can choose the second or third
2845
* expression directly. It doesn't matter whether or not the second
2846
* and/or third parts are themselves constant, because a constant
2847
* condition means that we'll always execute only one of the
2850
if (first->is_const())
2853
* evaluate the conditional value as a true/false value, and
2854
* return the second part's constant if the condition is true,
2855
* or the third part's constant if the condition is false
2857
return (first->get_const_val()->get_val_bool()
2862
/* it's not a constant value - return a new conditional node */
2863
return new CTPNIf(first, second, third);
2867
/* ------------------------------------------------------------------------ */
2869
* Unary Operator Parser
2872
CTcPrsNode *CTcPrsOpUnary::parse() const
2877
/* get the current token, which may be a prefix operator */
2880
/* check for prefix operators */
2887
/* parse the address expression */
2888
return parse_addr();
2897
/* skip the operator */
2901
* recursively parse the unary expression to which to apply the
2908
/* apply the operator */
2912
/* apply the NOT operator */
2913
return parse_not(sub);
2916
/* apply the bitwise NOT operator */
2917
return parse_bnot(sub);
2920
/* apply the unary positive operator */
2921
return parse_pos(sub);
2924
/* apply the unary negation operator */
2925
return parse_neg(sub);
2928
/* apply the pre-increment operator */
2929
return parse_inc(TRUE, sub);
2932
/* apply the pre-decrement operator */
2933
return parse_dec(TRUE, sub);
2936
/* apply the deletion operator */
2937
return parse_delete(sub);
2944
/* it's not a unary prefix operator - parse a postfix expression */
2945
return parse_postfix(TRUE, TRUE);
2950
* parse a unary NOT expression
2952
CTcPrsNode *CTcPrsOpUnary::parse_not(CTcPrsNode *subexpr)
2956
/* try folding a constant value */
2957
ret = eval_const_not(subexpr);
2960
* if we got a constant result, return it; otherwise, create a NOT
2961
* node for code generation
2966
return new CTPNNot(subexpr);
2970
* evaluate a constant NOT expression
2972
CTcPrsNode *CTcPrsOpUnary::eval_const_not(CTcPrsNode *subexpr)
2975
* if the underlying expression is a constant value, apply the
2978
if (subexpr->is_const())
2980
/* set the new value */
2981
subexpr->get_const_val()
2982
->set_bool(!subexpr->get_const_val()->get_val_bool());
2984
/* return the modified constant value */
2988
/* the result is not constant */
2993
* parse a unary bitwise NOT expression
2995
CTcPrsNode *CTcPrsOpUnary::parse_bnot(CTcPrsNode *subexpr)
2998
* if the underlying expression is a constant value, apply the
3001
if (subexpr->is_const())
3003
/* we need an integer - log an error if it's not */
3004
if (subexpr->get_const_val()->get_type() != TC_CVT_INT)
3005
G_tok->log_error(TCERR_CONST_UNARY_REQ_NUM,
3006
G_tok->get_op_text(TOKT_BNOT));
3008
subexpr->get_const_val()
3009
->set_int(~subexpr->get_const_val()->get_val_int());
3011
/* return the updated value */
3015
/* create the bitwise NOT node */
3016
return new CTPNBNot(subexpr);
3020
* parse a unary address expression
3022
CTcPrsNode *CTcPrsOpUnary::parse_addr() const
3024
CTcPrsNode *subexpr;
3027
* if it's a simple symbol, create an unresolved symbol node for it;
3028
* otherwise parse the entire expression
3030
if (G_tok->cur() == TOKT_SYM)
3032
const CTcToken *tok;
3035
* create an unresolved symbol node - we'll resolve this during
3038
tok = G_tok->getcur();
3039
subexpr = new CTPNSym(tok->get_text(), tok->get_text_len());
3042
* The address operator implies that the symbol is a property, so
3043
* define the property symbol and mark it as referenced if we
3046
G_prs->get_global_symtab()->find_or_def_prop_explicit(
3047
tok->get_text(), tok->get_text_len(), FALSE);
3049
/* skip the symbol */
3054
/* parse an expression */
3061
* The underlying expression must be something that has an address;
3062
* if it's not, it's an error.
3064
if (!subexpr->has_addr())
3067
* can't take the address of the subexpression - log an error,
3068
* but continue parsing the expression anyway
3070
G_tok->log_error(TCERR_NO_ADDRESS);
3073
/* create the address node */
3074
return new CTPNAddr(subexpr);
3078
* parse a unary arithmetic positive expression
3080
CTcPrsNode *CTcPrsOpUnary::parse_pos(CTcPrsNode *subexpr)
3083
* if the underlying expression is a constant value, apply the
3086
if (subexpr->is_const())
3088
/* if it's a float, a unary '+' has no effect at all */
3089
if (subexpr->get_const_val()->get_type() == TC_CVT_FLOAT)
3092
/* we need an integer - log an error if it's not */
3093
if (subexpr->get_const_val()->get_type() != TC_CVT_INT)
3094
G_tok->log_error(TCERR_CONST_UNARY_REQ_NUM,
3095
G_tok->get_op_text(TOKT_PLUS));
3098
* positive-ing a value doesn't change the value, so return the
3104
/* create the unary positive node */
3105
return new CTPNPos(subexpr);
3109
* parse a unary arithmetic negation expression
3111
CTcPrsNode *CTcPrsOpUnary::parse_neg(CTcPrsNode *subexpr)
3114
* if the underlying expression is a constant value, apply the
3117
if (subexpr->is_const())
3119
/* we need an integer or float */
3120
if (subexpr->get_const_val()->get_type() == TC_CVT_INT)
3122
/* set the value negative in the subexpression */
3123
subexpr->get_const_val()
3124
->set_int(-(subexpr->get_const_val()->get_val_int()));
3126
else if (subexpr->get_const_val()->get_type() == TC_CVT_FLOAT)
3128
CTcConstVal *cval = subexpr->get_const_val();
3131
/* allocate a buffer for a copy of the float text plus a '-' */
3132
new_txt = (char *)G_prsmem->alloc(cval->get_val_float_len() + 1);
3134
/* insert the minus sign */
3137
/* add the original string */
3138
memcpy(new_txt + 1, cval->get_val_float(),
3139
cval->get_val_float_len());
3141
/* update the subexpression's constant value to the new text */
3142
cval->set_float(new_txt, cval->get_val_float_len() + 1);
3147
G_tok->log_error(TCERR_CONST_UNARY_REQ_NUM,
3148
G_tok->get_op_text(TOKT_MINUS));
3151
/* return the modified constant value */
3155
/* create the unary negation node */
3156
return new CTPNNeg(subexpr);
3161
* parse a pre-increment expression
3163
CTcPrsNode *CTcPrsOpUnary::parse_inc(int pre, CTcPrsNode *subexpr)
3165
/* require an lvalue */
3166
if (!subexpr->check_lvalue())
3168
/* log an error, but continue parsing */
3169
G_tok->log_error(TCERR_INVALID_UNARY_LVALUE,
3170
G_tok->get_op_text(TOKT_INC));
3173
/* apply the increment operator */
3175
return new CTPNPreInc(subexpr);
3177
return new CTPNPostInc(subexpr);
3181
* parse a pre-decrement expression
3183
CTcPrsNode *CTcPrsOpUnary::parse_dec(int pre, CTcPrsNode *subexpr)
3185
/* require an lvalue */
3186
if (!subexpr->check_lvalue())
3188
/* log an error, but continue parsing */
3189
G_tok->log_error(TCERR_INVALID_UNARY_LVALUE,
3190
G_tok->get_op_text(TOKT_INC));
3193
/* apply the pre-increment operator */
3195
return new CTPNPreDec(subexpr);
3197
return new CTPNPostDec(subexpr);
3201
* parse a unary allocation expression
3203
CTcPrsNode *CTcPrsOpUnary::parse_new(CTcPrsNode *subexpr, int is_transient)
3205
/* create the allocation node */
3206
return new CTPNNew(subexpr, is_transient);
3210
* parse a unary deletion expression
3212
CTcPrsNode *CTcPrsOpUnary::parse_delete(CTcPrsNode *subexpr)
3214
/* the delete operator is obsolete in TADS 3 - warn about it */
3215
if (!G_prs->get_syntax_only())
3216
G_tok->log_warning(TCERR_DELETE_OBSOLETE);
3218
/* create the deletion node */
3219
return new CTPNDelete(subexpr);
3223
* parse a postfix expression
3225
CTcPrsNode *CTcPrsOpUnary::parse_postfix(int allow_member_expr,
3226
int allow_call_expr)
3230
/* parse a primary expression */
3231
sub = parse_primary();
3235
/* keep going as long as we find postfix operators */
3240
/* check for a postfix operator */
3245
/* left paren - function or method call */
3246
if (allow_call_expr)
3248
/* parse the call expression */
3249
sub = parse_call(sub);
3253
/* call expressions aren't allowed - stop here */
3259
/* left square bracket - subscript */
3260
sub = parse_subscript(sub);
3265
* Dot - member selection. If a member expression is allowed
3266
* by the caller, parse it; otherwise, just return the
3267
* expression up to this point.
3269
if (allow_member_expr)
3272
* it's allowed - parse it and continue to look for other
3273
* postfix expressions following the member expression
3275
sub = parse_member(sub);
3280
* member expressions aren't allowed - stop here,
3281
* returning the expression up to this point
3288
/* post-increment */
3290
sub = parse_inc(FALSE, sub);
3294
/* post-decrement */
3296
sub = parse_dec(FALSE, sub);
3300
/* it's not a postfix operator - return the result */
3304
/* if the last parse failed, return failure */
3311
* Parse an argument list
3313
CTPNArglist *CTcPrsOpUnary::parse_arg_list()
3318
/* skip the open paren */
3321
/* keep going until we find the close paren */
3322
for (argc = 0, arg_head = 0 ;; )
3327
/* if this is the close paren, we're done */
3328
if (G_tok->cur() == TOKT_RPAR)
3331
/* parse this actual parameter expression */
3332
expr = S_op_asi.parse();
3336
/* count the argument */
3339
/* create a new argument node */
3340
arg_cur = new CTPNArg(expr);
3342
/* check to see if the argument is followed by an ellipsis */
3343
if (G_tok->cur() == TOKT_ELLIPSIS)
3345
/* skip the ellipsis */
3348
/* mark the argument as a list-to-varargs parameter */
3349
arg_cur->set_varargs(TRUE);
3353
* Link the new node in at the beginning of our list - this will
3354
* ensure that the list is built in reverse order, which is the
3355
* order in which we push the arguments onto the stack.
3357
arg_cur->set_next_arg(arg_head);
3360
/* we need to be looking at a comma, right paren, or ellipsis */
3361
if (G_tok->cur() == TOKT_RPAR)
3363
/* that's the end of the list */
3366
else if (G_tok->cur() == TOKT_COMMA)
3368
/* skip the comma and parse the next argument */
3374
* If we're at the end of the file, there's no point
3375
* proceding, so return failure. If we've reached something
3376
* that looks like a statement separator (semicolon, curly
3377
* brace), also return failure, since the problem is clearly
3378
* a missing right paren. Otherwise, assume that a comma
3379
* was missing and continue as though we have another
3382
switch(G_tok->cur())
3386
G_tok->log_error_curtok(TCERR_EXPECTED_ARG_COMMA);
3389
* if we're at the end of file, return what we have so
3390
* far; otherwise continue, assuming that they merely
3391
* left out a comma between two argument expressions
3393
if (G_tok->cur() == TOKT_EOF)
3394
return new CTPNArglist(argc, arg_head);
3403
* we're apparently at the end of the statement; flag
3404
* the error as a missing right paren, and return what
3407
G_tok->log_error_curtok(TCERR_EXPECTED_ARG_RPAR);
3408
return new CTPNArglist(argc, arg_head);
3413
/* skip the closing paren */
3416
/* create and return the argument list descriptor */
3417
return new CTPNArglist(argc, arg_head);
3421
* Parse a function call expression
3423
CTcPrsNode *CTcPrsOpUnary::parse_call(CTcPrsNode *lhs)
3425
CTPNArglist *arglist;
3427
/* parse the argument list */
3428
arglist = parse_arg_list();
3432
/* build and return the function call node */
3433
return new CTPNCall(lhs, arglist);
3437
* Parse a subscript expression
3439
CTcPrsNode *CTcPrsOpUnary::parse_subscript(CTcPrsNode *lhs)
3441
CTcPrsNode *subscript;
3447
/* parse the expression within the brackets */
3448
subscript = S_op_comma.parse();
3452
/* check for the matching ']' */
3453
if (G_tok->cur() == TOKT_RBRACK)
3455
/* got it - skip it */
3460
/* log an error, and forgive the missing ']' */
3461
G_tok->log_error_curtok(TCERR_EXPECTED_SUB_RBRACK);
3464
/* try folding constants */
3465
cval = eval_const_subscript(lhs, subscript);
3468
* if that worked, use the result; otherwise, build an expression
3469
* node to generate code for the subscript operator
3474
return new CTPNSubscript(lhs, subscript);
3478
* Evaluate a constant subscript value
3480
CTcPrsNode *CTcPrsOpUnary::eval_const_subscript(CTcPrsNode *lhs,
3481
CTcPrsNode *subscript)
3484
* if we're subscripting a constant list by a constant index value,
3485
* we can evaluate a constant result
3487
if (lhs->is_const() && subscript->is_const())
3493
* make sure the index value is an integer and the value being
3494
* indexed is a list; if either type is wrong, the indexing
3495
* expression is invalid
3497
if (subscript->get_const_val()->get_type() != TC_CVT_INT)
3499
/* we can't use a non-integer expression as a list index */
3500
G_tok->log_error(TCERR_CONST_IDX_NOT_INT);
3502
else if (lhs->get_const_val()->get_type() != TC_CVT_LIST)
3504
/* we can't index any constant type other than list */
3505
G_tok->log_error(TCERR_CONST_IDX_INV_TYPE);
3509
/* get the index value */
3510
idx = subscript->get_const_val()->get_val_int();
3512
/* ask the list to look up the item by index */
3513
ele = lhs->get_const_val()->get_val_list()->get_const_ele(idx);
3515
/* if we got a valid result, return it */
3521
/* we couldn't fold it to a constant expression */
3526
* Parse a member selection ('.') expression. If no '.' is actually
3527
* present, then '.targetprop' is implied.
3529
CTcPrsNode *CTcPrsOpUnary::parse_member(CTcPrsNode *lhs)
3535
* If a '.' is present, skip it; otherwise, '.targetprop' is implied.
3537
if (G_tok->cur() == TOKT_DOT)
3539
/* we have an explicit property expression - skip the '.' */
3542
/* assume the property will be a simple symbol, not an expression */
3543
rhs_is_expr = FALSE;
3545
/* we could have a symbol or a parenthesized expression */
3546
switch(G_tok->cur())
3550
* It's a simple property name - create a symbol node. Note
3551
* that we must explicitly create an unresolved symbol node,
3552
* since we want to ignore any local variable with the same
3553
* name and look only in the global symbol table for a
3554
* property; we must hence defer resolving the symbol until
3557
rhs = new CTPNSym(G_tok->getcur()->get_text(),
3558
G_tok->getcur()->get_text_len());
3560
/* skip the symbol token */
3563
/* proceed to check for an argument list */
3568
* It's a parenthesized expression - parse it. First, skip
3569
* the open paren - we don't want the sub-expression to go
3570
* beyond the close paren (if we didn't skip the open paren,
3571
* the open paren would be part of the sub-expression, hence
3572
* any postfix expression after the close paren would be
3573
* considered part of the sub-expression; this would be
3574
* invalid, since we might want to find a postfix actual
3579
/* remember that it's an expression */
3582
/* parse the sub-expression */
3583
rhs = S_op_comma.parse();
3587
/* require the close paren */
3588
if (G_tok->cur() == TOKT_RPAR)
3590
/* skip the closing paren */
3596
G_tok->log_error_curtok(TCERR_EXPR_MISSING_RPAR);
3599
* if we're at a semicolon or end of file, we must be on
3600
* to the next statement - stop trying to parse this one
3601
* if so; otherwise, continue on the assumption that they
3602
* merely left out the close paren and what follows is
3603
* more expression for us to process
3605
if (G_tok->cur() == TOKT_SEM || G_tok->cur() == TOKT_EOF)
3610
case TOKT_TARGETPROP:
3612
* it's an unparenthesized "targetprop" expression - skip the
3618
* the property value is the result of evaluating
3619
* "targetprop", which is an expression
3621
rhs = new CTPNTargetprop();
3624
/* note the reference to the extended method context */
3625
G_prs->set_full_method_ctx_referenced(TRUE);
3627
/* go parse the rest */
3631
/* anything else is invalid - log an error */
3632
G_tok->log_error_curtok(TCERR_INVALID_PROP_EXPR);
3634
/* skip the errant token so we don't loop on it */
3637
/* return what we have so far */
3643
/* there's no property specified, so '.targetprop' is implied */
3644
rhs = new CTPNTargetprop();
3648
* note the reference to the full method context (since
3649
* 'targetprop' is part of the extended method context beyond
3652
G_prs->set_full_method_ctx_referenced(TRUE);
3655
/* check for an argument list */
3656
if (G_tok->cur() == TOKT_LPAR)
3658
CTPNArglist *arglist;
3660
/* parse the argument list */
3661
arglist = parse_arg_list();
3665
/* create and return a member-with-arguments node */
3666
return new CTPNMemArg(lhs, rhs, rhs_is_expr, arglist);
3671
* there's no argument list - create and return a simple member
3674
return new CTPNMember(lhs, rhs, rhs_is_expr);
3679
* Parse a double-quoted string with an embedded expression. We treat
3680
* this type of expression as though it were a comma expression.
3682
CTcPrsNode *CTcPrsOpUnary::parse_dstr_embed()
3687
* First, create a node for the initial part of the string. This is
3688
* just an ordinary double-quoted string node. If the initial part of
3689
* the string is zero-length, don't create an initial node at all,
3690
* since this would just generate do-nothing code.
3692
if (G_tok->getcur()->get_text_len() != 0)
3694
/* create the node for the initial part of the string */
3695
cur = new CTPNDstr(G_tok->getcur()->get_text(),
3696
G_tok->getcur()->get_text_len());
3701
* the initial part of the string is empty, so we don't need a node
3707
/* skip the dstring */
3710
/* keep going until we find the end of the string */
3717
* parse the embedded expression, which can be any ordinary
3718
* expression type, including a double-quoted string expression
3720
sub = G_prs->parse_expr_or_dstr(TRUE);
3724
/* build an embedding node for the expression */
3725
sub = new CTPNDstrEmbed(sub);
3729
* after the expression, we must find either another string
3730
* segment with another embedded expression following, or the
3731
* final string segment; anything else is an error
3734
switch(G_tok->cur())
3738
* It's a string with yet another embedded expression.
3739
* Simply continue to the next segment.
3746
* It's the last segment of the string. We can stop after
3747
* processing this segment.
3754
* anything else is invalid - we must find the end of the
3755
* string. Log an error.
3757
G_tok->log_error_curtok(TCERR_EXPECTED_DSTR_CONT);
3760
* if this is the end of the file, there's no point in
3761
* continuing; return what we have so far
3763
if (G_tok->cur() == TOKT_EOF)
3764
return (cur != 0 ? cur : sub);
3766
/* tell the tokenizer to assume the missing '>>' */
3767
G_tok->assume_missing_dstr_cont();
3769
/* go back and try it again */
3770
goto do_next_segment;
3774
* Build a node representing everything so far: do this by
3775
* combining the sub-expression with everything preceding, using a
3776
* comma operator. This isn't necessary if there's nothing
3777
* preceding the sub-expression, since this means the
3778
* sub-expression itself is everything so far.
3781
cur = new CTPNComma(cur, sub);
3786
* Combine the part so far with the next string segment, using a
3787
* comma operator. If the next string segment is empty, there's no
3788
* need to add anything for it.
3790
if (G_tok->getcur()->get_text_len() != 0)
3794
/* create a node for the new string segment */
3795
newstr = new CTPNDstr(G_tok->getcur()->get_text(),
3796
G_tok->getcur()->get_text_len());
3798
/* combine it into the part so far with a comma operator */
3799
cur = new CTPNComma(cur, newstr);
3802
/* skip this string part */
3805
/* if that was the last segment, this is the final result */
3812
* Parse a primary expression
3814
CTcPrsNode *CTcPrsOpUnary::parse_primary()
3819
/* keep going until we find something interesting */
3822
/* determine what we have */
3823
switch(G_tok->cur())
3826
/* short form of anonymous function */
3827
return parse_anon_func(TRUE);
3830
/* anonymous function requires 'new' */
3831
G_tok->log_error(TCERR_ANON_FUNC_REQ_NEW);
3834
* parse it as an anonymous function anyway, even though the
3835
* syntax isn't quite correct - the rest of it might still
3836
* be okay, so we can at least continue parsing from here to
3839
return parse_anon_func(FALSE);
3842
/* skip the operator and check for 'function' */
3843
if (G_tok->next() == TOKT_FUNCTION)
3845
/* it's an anonymous function definition - go parse it */
3846
sub = parse_anon_func(FALSE);
3852
/* check for the 'transient' keyword */
3853
trans = (G_tok->cur() == TOKT_TRANSIENT);
3858
* it's an ordinary 'new' expression - parse the primary
3859
* making up the name
3861
sub = parse_primary();
3863
/* if there's an argument list, parse the argument list */
3864
if (G_tok->cur() == TOKT_LPAR)
3865
sub = parse_call(sub);
3867
/* create the 'new' node */
3868
sub = parse_new(sub, trans);
3873
/* left parenthesis - skip it */
3876
/* parse the expression */
3877
sub = S_op_comma.parse();
3881
/* require the matching right parenthesis */
3882
if (G_tok->cur() == TOKT_RPAR)
3884
/* skip the right paren */
3890
* log an error; assume that the paren is simply
3891
* missing, so continue with our work
3893
G_tok->log_error_curtok(TCERR_EXPR_MISSING_RPAR);
3896
/* return the parenthesized expression */
3900
/* nil - the result is the constant value nil */
3904
/* skip the token */
3907
/* return a constant node */
3908
return new CTPNConst(&cval);
3911
/* true - the result is the constant value true */
3913
goto return_constant;
3916
/* integer - the result is a constant integer value */
3917
cval.set_int(G_tok->getcur()->get_int_val());
3918
goto return_constant;
3921
/* floating point number */
3922
cval.set_float(G_tok->getcur()->get_text(),
3923
G_tok->getcur()->get_text_len());
3924
goto return_constant;
3928
/* single-quoted string - the result is a constant string value */
3929
cval.set_sstr(G_tok->getcur()->get_text(),
3930
G_tok->getcur()->get_text_len());
3931
goto return_constant;
3935
* if we're in preprocessor expression mode, treat this the
3936
* same as a single-quoted string
3938
if (G_prs->get_pp_expr_mode())
3939
goto handle_sstring;
3942
* a string implicitly references 'self', because we could run
3943
* through the default output method on the current object
3945
G_prs->set_self_referenced(TRUE);
3947
/* build a double-quoted string node */
3948
sub = new CTPNDstr(G_tok->getcur()->get_text(),
3949
G_tok->getcur()->get_text_len());
3951
/* skip the string */
3954
/* return the new node */
3957
case TOKT_DSTR_START:
3958
/* a string implicitly references 'self' */
3959
G_prs->set_self_referenced(TRUE);
3961
/* parse the embedding expression */
3962
return parse_dstr_embed();
3965
/* parse the list */
3966
return parse_list();
3970
* symbol - the meaning of the symbol is not yet known, so
3971
* create an unresolved symbol node
3973
sub = G_prs->create_sym_node(G_tok->getcur()->get_text(),
3974
G_tok->getcur()->get_text_len());
3976
/* skip the symbol token */
3979
/* return the new node */
3983
/* note the explicit self-reference */
3984
G_prs->set_self_referenced(TRUE);
3986
/* generate the "self" node */
3988
return new CTPNSelf();
3991
/* generate the "replaced" node */
3993
return new CTPNReplaced();
3995
case TOKT_TARGETPROP:
3996
/* note the explicit extended method context reference */
3997
G_prs->set_full_method_ctx_referenced(TRUE);
3999
/* generate the "targetprop" node */
4001
return new CTPNTargetprop();
4003
case TOKT_TARGETOBJ:
4004
/* note the explicit extended method context reference */
4005
G_prs->set_full_method_ctx_referenced(TRUE);
4007
/* generate the "targetobj" node */
4009
return new CTPNTargetobj();
4011
case TOKT_DEFININGOBJ:
4012
/* note the explicit extended method context reference */
4013
G_prs->set_full_method_ctx_referenced(TRUE);
4015
/* generate the "definingobj" node */
4017
return new CTPNDefiningobj();
4020
/* generate the "argcount" node */
4022
return new CTPNArgc();
4024
case TOKT_INHERITED:
4025
/* parse the "inherited" operation */
4026
return parse_inherited();
4028
case TOKT_DELEGATED:
4029
/* parse the "delegated" operation */
4030
return parse_delegated();
4033
/* extra right paren - log an error */
4034
G_tok->log_error(TCERR_EXTRA_RPAR);
4036
/* skip it and go back for more */
4041
/* extra right square bracket - log an error */
4042
G_tok->log_error(TCERR_EXTRA_RBRACK);
4044
/* skip it and go back for more */
4053
* this looks like the end of the statement, but we expected
4054
* an operand - note the error and end the statement
4056
G_tok->log_error_curtok(TCERR_EXPECTED_OPERAND);
4059
* Synthesize a constant zero as the operand value. Do not
4060
* skip the current token, because it's almost certainly not
4061
* meant to be part of the expression; we want to stay put
4062
* so that the caller can resynchronize on this token.
4064
cval.set_int(G_tok->getcur()->get_int_val());
4065
return new CTPNConst(&cval);
4068
/* invalid primary expression - log the error */
4069
G_tok->log_error_curtok(TCERR_BAD_PRIMARY_EXPR);
4072
* Skip the token that's causing the problem; this will
4073
* ensure that we don't loop indefinitely trying to figure
4074
* out what this token is about, and return a constant zero
4075
* value as the primary.
4079
/* synthesize a constant zero as the operand value */
4080
cval.set_int(G_tok->getcur()->get_int_val());
4081
goto return_constant;
4087
* Parse an "inherited" expression
4089
CTcPrsNode *CTcPrsOpUnary::parse_inherited()
4093
/* skip the "inherited" keyword and check what follows */
4094
switch(G_tok->next())
4098
* it's an "inherited superclass..." expression - set up the
4099
* "inherited superclass" node
4101
lhs = new CTPNInhClass(G_tok->getcur()->get_text(),
4102
G_tok->getcur()->get_text_len());
4104
/* skip the superclass token */
4107
/* parse the member expression portion normally */
4112
* '<' - this is the start of a multi-method type list. Parse the
4113
* list: type1 ',' type2 ',' ... '>', then the argument list to the
4117
/* create the formal type list */
4118
CTcFormalTypeList *tl = new (G_prsmem)CTcFormalTypeList();
4123
/* parse each list element */
4124
for (int done = FALSE ; !done ; )
4126
switch (G_tok->cur())
4129
/* end of the list - skip the '>', and we're done */
4138
/* this has to be the end of the list */
4139
if (G_tok->next() == TOKT_GT)
4142
G_tok->log_error_curtok(TCERR_MMINH_MISSING_GT);
4144
/* assume the list ends here in any case */
4149
/* a type token - add it to the list */
4150
tl->add_typed_param(G_tok->getcur());
4153
/* skip the type token */
4154
switch (G_tok->next())
4157
/* another type follows */
4169
/* probably just a missing comma */
4170
G_tok->log_error_curtok(TCERR_MMINH_MISSING_COMMA);
4174
/* anything else is an error */
4175
G_tok->log_error_curtok(TCERR_MMINH_MISSING_COMMA);
4182
/* '*' indicates an untyped parameter */
4183
tl->add_untyped_param();
4187
/* probably a missing '>' */
4188
G_tok->log_error_curtok(TCERR_MMINH_MISSING_GT);
4193
/* probably a missing type */
4194
G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_TYPE);
4201
/* all of these indicate a premature end of the list */
4202
G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_TYPE);
4206
/* anything else is an error */
4207
G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_TYPE);
4208
G_prs->skip_to_sem();
4213
/* the left-hand side is an "inherited" node, with the arg list */
4214
lhs = new CTPNInh();
4215
((CTPNInh *)lhs)->set_typelist(tl);
4217
/* an inherited<> expression must have an argument list */
4218
if (G_tok->cur() != TOKT_LPAR)
4220
G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_LIST);
4221
G_prs->skip_to_sem();
4229
* There's no explicit superclass name listed, so the left-hand
4230
* side of the '.' expression is the simple "inherited" node.
4232
lhs = new CTPNInh();
4235
* Since we don't have an explicit superclass, we'll need the
4236
* method context at run-time to establish the next class in
4237
* inheritance order. Flag the need for the full method context.
4239
G_prs->set_full_method_ctx_referenced(TRUE);
4241
/* parse the member expression portion normally */
4245
/* parse and return the member expression */
4246
return parse_member(lhs);
4250
* Parse a "delegated" expression
4252
CTcPrsNode *CTcPrsOpUnary::parse_delegated()
4257
/* 'delegated' always references 'self' */
4258
G_prs->set_self_referenced(TRUE);
4260
/* skip the "delegated" keyword */
4264
* Parse a postfix expression giving the delegatee. Don't allow
4265
* nested member subexpressions (unless they're enclosed in
4266
* parentheses, of course) - our implicit '.' postfix takes
4267
* precedence. Also, don't allow call subexpressions (unless enclosed
4268
* in parens), since a postfix argument list binds to the 'delegated'
4269
* expression, not to a subexpression involving a function/method
4272
target = parse_postfix(FALSE, FALSE);
4274
/* set up the "delegated" node */
4275
lhs = new CTPNDelegated(target);
4277
/* return the rest as a normal member expression */
4278
return parse_member(lhs);
4284
CTcPrsNode *CTcPrsOpUnary::parse_list()
4289
/* skip the opening '[' */
4293
* create the list expression -- we'll add elements to the list as
4294
* we parse the elements
4296
lst = new CTPNList();
4298
/* scan all list elements */
4301
/* check what we have */
4302
switch(G_tok->cur())
4306
* that's the end of the list - skip the closing bracket and
4307
* return the finished list
4318
* these would all seem to imply that the closing ']' was
4319
* missing from the list; flag the error and end the list
4322
G_tok->log_error_curtok(TCERR_LIST_MISSING_RBRACK);
4327
* extra right paren - log an error, but then skip the paren
4328
* and try to keep parsing
4330
G_tok->log_error(TCERR_LIST_EXTRA_RPAR);
4335
/* it must be the next element expression */
4340
* Attempt to parse another list element expression. Parse just
4341
* below a comma expression, because commas can be used to
4342
* separate list elements.
4344
ele = S_op_asi.parse();
4348
/* add the element to the list */
4349
lst->add_element(ele);
4351
/* check what follows the element */
4352
switch(G_tok->cur())
4355
/* skip the comma introducing the next element */
4358
/* if a close bracket follows, we seem to have an extra comma */
4359
if (G_tok->cur() == TOKT_RBRACK)
4362
* log an error about the missing element, then end the
4365
G_tok->log_error_curtok(TCERR_LIST_EXPECT_ELEMENT);
4372
* we're done with the list - skip the bracket and return
4385
* these would all seem to imply that the closing ']' was
4386
* missing from the list; flag the error and end the list
4389
G_tok->log_error_curtok(TCERR_LIST_MISSING_RBRACK);
4394
* Anything else is an error - note that we expected a
4395
* comma, then proceed with parsing from the current token
4396
* as though we had found the comma (in all likelihood, the
4397
* comma was accidentally omitted). If we've reached the
4398
* end of the file, return what we have so far, since it's
4399
* pointless to keep looping.
4401
G_tok->log_error_curtok(TCERR_LIST_EXPECT_COMMA);
4403
/* give up on end of file, otherwise keep going */
4404
if (G_tok->cur() == TOKT_EOF)
4411
/* tell the parser to note this list, in case it's the longest yet */
4412
G_cg->note_list(lst->get_count());
4414
/* return the list */
4419
/* ------------------------------------------------------------------------ */
4421
* Parse Allocation Object
4425
* memory allocator for parse nodes
4427
void *CTcPrsAllocObj::operator new(size_t siz)
4429
/* allocate the space out of the node pool */
4430
return G_prsmem->alloc(siz);
4434
/* ------------------------------------------------------------------------ */
4436
* Parse Tree space manager
4442
CTcPrsMem::CTcPrsMem()
4444
/* we have no blocks yet */
4447
/* allocate our first block */
4451
CTcPrsMem::~CTcPrsMem()
4453
/* delete all objects in our pool */
4458
* Save state, for later resetting
4460
void CTcPrsMem::save_state(tcprsmem_state_t *state)
4462
/* save the pool information in the state structure */
4463
state->tail = tail_;
4464
state->free_ptr = free_ptr_;
4469
* Reset to initial state
4471
void CTcPrsMem::reset()
4473
/* delete all blocks */
4476
/* re-allocate the initial block */
4481
* Reset. This deletes all objects allocated out of the parser pool
4482
* since the state was saved.
4484
void CTcPrsMem::reset(const tcprsmem_state_t *state)
4486
tcprsmem_blk_t *cur;
4489
* delete all of the blocks that were allocated after the last block
4490
* that existed when the state was saved
4492
for (cur = state->tail->next_ ; cur != 0 ; )
4494
tcprsmem_blk_t *nxt;
4496
/* remember the next block */
4499
/* delete this block */
4502
/* move on to the next one */
4506
/* re-establish the saved last block */
4507
tail_ = state->tail;
4509
/* make sure the list is terminated at the last block */
4512
/* re-establish the saved allocation point in the last block */
4513
free_ptr_ = state->free_ptr;
4518
* Delete all parser memory. This deletes all objects allocated out of
4519
* parser memory, so the caller must be sure that all of these objects
4522
void CTcPrsMem::delete_all()
4524
/* free all blocks */
4527
tcprsmem_blk_t *nxt;
4529
/* remember the next block after this one */
4532
/* free this block */
4535
/* move on to the next one */
4539
/* there's no tail now */
4546
void CTcPrsMem::alloc_block()
4548
tcprsmem_blk_t *blk;
4551
* block size - pick a size that's large enough that we won't be
4552
* unduly inefficient (in terms of having tons of blocks), but still
4553
* friendly to 16-bit platforms (i.e., under 64k)
4555
const size_t BLOCK_SIZE = 65000;
4557
/* allocate space for the block */
4558
blk = (tcprsmem_blk_t *)t3malloc(sizeof(tcprsmem_blk_t) + BLOCK_SIZE - 1);
4560
/* if that failed, throw an error */
4562
err_throw(TCERR_NO_MEM_PRS_TREE);
4564
/* link in the block at the end of our list */
4571
/* the block is now the last block in the list */
4575
* Set up to allocate out of our block. Make sure the free pointer
4576
* starts out on a worst-case alignment boundary; normally, the C++
4577
* compiler will properly align our "buf_" structure member on a
4578
* worst-case boundary, so this calculation won't actually change
4579
* anything, but this will help ensure portability even to weird
4582
free_ptr_ = (char *)osrndpt((unsigned char *)blk->buf_);
4585
* get the amount of space remaining in the block (in the unlikely
4586
* event that worst-case alignment actually moved the free pointer
4587
* above the start of the buffer, we'll have lost a little space in
4588
* the buffer for the alignment offset)
4590
rem_ = BLOCK_SIZE - (free_ptr_ - blk->buf_);
4596
void *CTcPrsMem::alloc(size_t siz)
4601
/* if there's not enough space available, allocate a new block */
4604
/* allocate a new block */
4608
* if there's still not enough room, the request must exceed the
4609
* largest block we can allocate
4612
G_tok->throw_internal_error(TCERR_PRS_BLK_TOO_BIG, (ulong)siz);
4615
/* return the free pointer */
4618
/* advance the free pointer past the space, rounding for alignment */
4619
free_ptr_ = (char *)osrndpt((unsigned char *)free_ptr_ + siz);
4621
/* deduct the amount of space we consumed from the available space */
4622
space_used = free_ptr_ - ret;
4623
if (space_used > rem_)
4628
/* return the allocated space */
4632
/* ------------------------------------------------------------------------ */
4634
* parse node base class
4638
* By default, an expression cannot be used as a debugger expression
4640
CTcPrsNode *CTcPrsNodeBase::adjust_for_debug(const tcpn_debug_info *info)
4642
err_throw(VMERR_INVAL_DBG_EXPR);
4643
AFTER_ERR_THROW(return 0;)
4647
/* ------------------------------------------------------------------------ */
4653
* adjust for debugger use
4655
CTcPrsNode *CTPNConstBase::adjust_for_debug(const tcpn_debug_info *info)
4657
/* convert to a debugger-constant */
4658
return new CTPNDebugConst(&val_);
4662
/* ------------------------------------------------------------------------ */
4668
* add an element to a list
4670
void CTPNListBase::add_element(CTcPrsNode *expr)
4674
/* create a list element object for the new element */
4675
ele = new CTPNListEle(expr);
4677
/* count the new entry */
4680
/* add the element to our linked list */
4681
ele->set_prev(tail_);
4683
tail_->set_next(ele);
4689
* if the new element does not have a constant value, the list no
4690
* longer has a constant value (if it did before)
4692
if (!expr->is_const())
4697
* remove each occurrence of a given constant value from the list
4699
void CTPNListBase::remove_element(const CTcConstVal *val)
4704
for (cur = head_ ; cur != 0 ; cur = cur->get_next())
4707
* if this element is constant, compare it to the value to be
4708
* removed; if it matches, remove it
4710
if (cur->get_expr()->is_const()
4711
&& cur->get_expr()->get_const_val()->is_equal_to(val))
4713
/* set the previous element's forward pointer */
4714
if (cur->get_prev() == 0)
4715
head_ = cur->get_next();
4717
cur->get_prev()->set_next(cur->get_next());
4719
/* set the next element's back pointer */
4720
if (cur->get_next() == 0)
4721
tail_ = cur->get_prev();
4723
cur->get_next()->set_prev(cur->get_prev());
4725
/* decrement our element counter */
4732
* Get the constant value of the element at the given index. Logs an
4733
* error and returns null if there's no such element.
4735
CTcPrsNode *CTPNListBase::get_const_ele(int index)
4739
/* if the index is negative, it's out of range */
4742
/* log the error and return failure */
4743
G_tok->log_error(TCERR_CONST_IDX_RANGE);
4747
/* scan the list for the given element */
4748
for (ele = head_ ; ele != 0 && index > 1 ;
4749
ele = ele->get_next(), --index) ;
4751
/* if we ran out of elements, the index is out of range */
4752
if (ele == 0 || index != 1)
4754
G_tok->log_error(TCERR_CONST_IDX_RANGE);
4758
/* return the element's constant value */
4759
return ele->get_expr();
4765
CTcPrsNode *CTPNListBase::fold_constants(CTcPrsSymtab *symtab)
4771
* if the list is already constant, there's nothing extra we need to
4777
/* presume the result will be all constants */
4780
/* run through my list and fold each element */
4781
for (cur = head_ ; cur != 0 ; cur = cur->get_next())
4783
/* fold this element */
4784
cur->fold_constants(symtab);
4787
* if this element is not a constant, the whole list cannot be
4790
if (!cur->get_expr()->is_const())
4794
/* if every element was a constant, the overall list is constant */
4803
* Adjust for debugging
4805
CTcPrsNode *CTPNListBase::adjust_for_debug(const tcpn_debug_info *info)
4809
/* run through my list and adjust each element */
4810
for (cur = head_ ; cur != 0 ; cur = cur->get_next())
4812
/* adjust this element */
4813
cur->adjust_for_debug(info);
4817
* force the list to be non-constant - in debugger mode, we have to
4818
* build the value we push as a dynamic object, never as an actual
4819
* constant, to ensure that the generated code can be deleted
4820
* immediately after being executed
4828
/* ------------------------------------------------------------------------ */
4830
* conditional operator node base class
4836
CTcPrsNode *CTPNIfBase::fold_constants(CTcPrsSymtab *symtab)
4838
/* fold constants in the subnodes */
4839
first_ = first_->fold_constants(symtab);
4840
second_ = second_->fold_constants(symtab);
4841
third_ = third_->fold_constants(symtab);
4844
* if the first is now a constant, we can fold this entire
4845
* expression node by choosing the second or third based on its
4846
* value; otherwise, return myself unchanged
4848
if (first_->is_const())
4851
* the condition is a constant - the result is the 'then' or
4852
* 'else' part, based on the condition's value
4854
return (first_->get_const_val()->get_val_bool()
4855
? second_ : third_);
4859
/* we can't fold this node any further - return it unchanged */
4865
/* ------------------------------------------------------------------------ */
4867
* Double-quoted string node - base class
4871
* create a double-quoted string node
4873
CTPNDstrBase::CTPNDstrBase(const char *str, size_t len)
4875
/* remember the string */
4880
* note the length in the parser, in case it's the longest string
4883
G_cg->note_str(len);
4887
* adjust for debugger use
4889
CTcPrsNode *CTPNDstrBase::adjust_for_debug(const tcpn_debug_info *info)
4892
* don't allow dstring evaluation in speculative mode, since we
4893
* can't execute anything with side effects in this mode
4895
if (info->speculative)
4896
err_throw(VMERR_BAD_SPEC_EVAL);
4898
/* return a debugger dstring node */
4899
return new CTPNDebugDstr(str_, len_);
4902
/* ------------------------------------------------------------------------ */
4904
* Address-of parse node
4910
CTcPrsNode *CTPNAddrBase::fold_constants(CTcPrsSymtab *symtab)
4914
/* ask the symbol to generate a constant expression for its address */
4915
ret = get_sub_expr()->fold_addr_const(symtab);
4918
* if we got a constant value, return it; otherwise, return myself
4921
return (ret != 0 ? ret : this);
4925
* determine if my address equals that of another node
4927
int CTPNAddrBase::is_addr_eq(const CTPNAddr *node, int *comparable) const
4930
* If both sides are symbols, the addresses are equal if and only if
4931
* the symbols are identical. One symbol has exactly one meaning in
4932
* a given context, and no two symbols can have the same meaning.
4933
* (It's important that we be able to state this for all symbols,
4934
* because we can't necessarily know during parsing the meaning of a
4935
* given symbol, since the symbol could be a forward reference.)
4937
if (get_sub_expr()->get_sym_text() != 0
4938
&& node->get_sub_expr()->get_sym_text() != 0)
4943
/* they're both symbols, so they're comparable */
4946
/* they're the same if both symbols have the same text */
4947
sym1 = get_sub_expr();
4948
sym2 = node->get_sub_expr();
4949
return (sym1->get_sym_text_len() == sym2->get_sym_text_len()
4950
&& memcmp(sym1->get_sym_text(), sym2->get_sym_text(),
4951
sym1->get_sym_text_len()) == 0);
4954
/* they're not comparable */
4955
*comparable = FALSE;
4959
/* ------------------------------------------------------------------------ */
4961
* Symbol parse node base class
4967
CTcPrsNode *CTPNSymBase::fold_constants(CTcPrsSymtab *symtab)
4973
* Look up my symbol. At this stage, don't assume a definition;
4974
* merely look to see if it's already known. We don't have enough
4975
* information to determine how we should define the symbol, so
4976
* leave it undefined until code generation if it's not already
4979
sym = symtab->find(get_sym_text(), get_sym_text_len());
4982
/* ask the symbol to do the folding */
4983
ret = sym->fold_constant();
4985
/* if that succeeded, return it; otherwise, return unchanged */
4986
return (ret != 0 ? ret : this);
4990
/* not defined - return myself unchanged */
4996
* Fold my address to a constant node. If I have no address value, I'll
4997
* simply return myself unchanged. Note that it's an error if I have no
4998
* constant value, but we'll count on the code generator to report the
4999
* error, and simply ignore it for now.
5001
CTcPrsNode *CTPNSymBase::fold_addr_const(CTcPrsSymtab *symtab)
5005
/* look up my symbol; if we don't find it, don't define it */
5006
sym = symtab->find(get_sym_text(), get_sym_text_len());
5009
/* we got a symbol - ask it to do the folding */
5010
return sym->fold_addr_const();
5014
/* undefined symbol - there's no constant address value */
5020
* Determine if I have a return value when called
5022
int CTPNSymBase::has_return_value_on_call() const
5026
/* try resolving my symbol */
5027
sym = G_prs->get_global_symtab()->find(sym_, len_);
5030
* if we found a symbol, let it resolve the call; otherwise, assume
5031
* that we do have a return value
5034
return sym->has_return_value_on_call();
5040
* Determine if I am a valid lvalue
5042
int CTPNSymBase::check_lvalue_resolved(class CTcPrsSymtab *symtab) const
5046
/* look up the symbol in the given scope */
5047
sym = symtab->find(get_sym_text(), get_sym_text_len());
5050
/* ask the symbol what it thinks */
5051
return sym->check_lvalue();
5055
/* it's undefined - can't be an lvalue */
5061
* Adjust for debugger use
5063
CTcPrsNode *CTPNSymBase::adjust_for_debug(const tcpn_debug_info *)
5066
* If this symbol isn't defined in the global symbol table, we can't
5067
* evaluate this expression in the debugger - new symbols can never
5068
* be defined in the debugger, so there's no point in trying to hold
5069
* a forward reference as we normally would for an undefined symbol.
5070
* We need look only in the global symbol table because local
5071
* symbols will already have been resolved.
5073
if (G_prs->get_global_symtab()->find(sym_, len_) == 0)
5075
/* log the error, to generate an appropriate message */
5076
G_tok->log_error(TCERR_UNDEF_SYM, (int)len_, sym_);
5078
/* throw the error as well */
5079
err_throw_a(TCERR_UNDEF_SYM, 2,
5080
ERR_TYPE_INT, (int)len_, ERR_TYPE_TEXTCHAR, sym_);
5083
/* return myself unchanged */
5088
/* ------------------------------------------------------------------------ */
5090
* Resolved Symbol parse node base class
5096
CTcPrsNode *CTPNSymResolvedBase::fold_constants(CTcPrsSymtab *symtab)
5100
/* ask the symbol to generate the folded constant value */
5101
ret = sym_->fold_constant();
5103
/* if that succeeded, return it; otherwise, return myself unchanged */
5104
return (ret != 0 ? ret : this);
5108
* Fold my address to a constant node. If I have no address value, I'll
5109
* simply return myself unchanged. Note that it's an error if I have no
5110
* constant value, but we'll count on the code generator to report the
5111
* error, and simply ignore it for now.
5113
CTcPrsNode *CTPNSymResolvedBase::fold_addr_const(CTcPrsSymtab *symtab)
5115
/* ask my symbol to generate the folded constant value */
5116
return sym_->fold_addr_const();
5120
/* ------------------------------------------------------------------------ */
5122
* Debugger local variable resolved symbol
5128
CTPNSymDebugLocalBase::CTPNSymDebugLocalBase(const tcprsdbg_sym_info *info)
5130
/* save the type information */
5131
switch(info->sym_type)
5134
var_id_ = info->var_id;
5135
ctx_arr_idx_ = info->ctx_arr_idx;
5136
frame_idx_ = info->frame_idx;
5141
var_id_ = info->var_id;
5143
frame_idx_ = info->frame_idx;
5148
/* other types are invalid */
5154
/* ------------------------------------------------------------------------ */
5156
* Argument List parse node base class
5162
CTcPrsNode *CTPNArglistBase::fold_constants(CTcPrsSymtab *symtab)
5166
/* fold each list element */
5167
for (cur = get_arg_list_head() ; cur != 0 ; cur = cur->get_next_arg())
5168
cur->fold_constants(symtab);
5170
/* return myself with no further folding */
5175
* adjust for debugger use
5177
CTcPrsNode *CTPNArglistBase::adjust_for_debug(const tcpn_debug_info *info)
5181
/* adjust each argument list member */
5182
for (arg = list_ ; arg != 0 ; arg = arg->get_next_arg())
5185
* adjust this argument; assume the argument node itself isn't
5188
arg->adjust_for_debug(info);
5191
/* return myself otherwise unchanged */
5195
/* ------------------------------------------------------------------------ */
5197
* Argument List Entry parse node base class
5203
CTcPrsNode *CTPNArgBase::fold_constants(CTcPrsSymtab *symtab)
5205
/* fold my argument expression */
5206
arg_expr_ = arg_expr_->fold_constants(symtab);
5208
/* return myself unchanged */
5212
/* ------------------------------------------------------------------------ */
5214
* Member with no arguments
5218
* adjust for debugger use
5220
CTcPrsNode *CTPNMemberBase::adjust_for_debug(const tcpn_debug_info *info)
5222
/* adjust the object and property expressions */
5223
obj_expr_ = obj_expr_->adjust_for_debug(info);
5224
prop_expr_ = prop_expr_->adjust_for_debug(info);
5226
/* return myself otherwise unchanged */
5230
/* ------------------------------------------------------------------------ */
5232
* Member with Arguments parse node base class
5238
CTcPrsNode *CTPNMemArgBase::fold_constants(CTcPrsSymtab *symtab)
5240
/* fold constants in the object and property expressions */
5241
obj_expr_ = obj_expr_->fold_constants(symtab);
5242
prop_expr_ = prop_expr_->fold_constants(symtab);
5245
* fold constants in the argument list; an argument list node never
5246
* changes to a new node type during constant folding, so we don't
5247
* need to update the member
5249
arglist_->fold_constants(symtab);
5251
/* return myself with no further evaluation */
5256
* adjust for debugger use
5258
CTcPrsNode *CTPNMemArgBase::adjust_for_debug(const tcpn_debug_info *info)
5260
/* don't allow in speculative mode due to possible side effects */
5261
if (info->speculative)
5262
err_throw(VMERR_BAD_SPEC_EVAL);
5265
* adjust my object expression, property expression, and argument
5268
obj_expr_ = obj_expr_->adjust_for_debug(info);
5269
prop_expr_ = prop_expr_->adjust_for_debug(info);
5270
arglist_->adjust_for_debug(info);
5272
/* return myself otherwise unchanged */
5277
/* ------------------------------------------------------------------------ */
5279
* Function/Method Call parse node base class
5285
CTcPrsNode *CTPNCallBase::fold_constants(CTcPrsSymtab *symtab)
5287
/* fold my function expression */
5288
func_ = func_->fold_constants(symtab);
5290
/* fold my argument list */
5291
arglist_->fold_constants(symtab);
5293
/* return myself unchanged */
5298
* adjust for debugger use
5300
CTcPrsNode *CTPNCallBase::adjust_for_debug(const tcpn_debug_info *info)
5302
/* don't allow in speculative mode because of side effects */
5303
if (info->speculative)
5304
err_throw(VMERR_BAD_SPEC_EVAL);
5306
/* adjust the function expression */
5307
func_ = func_->adjust_for_debug(info);
5309
/* adjust the argument list (assume it doesn't change) */
5310
arglist_->adjust_for_debug(info);
5312
/* return myself otherwise unchanged */
5316
/* ------------------------------------------------------------------------ */
5318
* Parser Symbol Table implementation
5321
/* static hash function */
5322
CVmHashFunc *CTcPrsSymtab::hash_func_ = 0;
5325
* allocate parser symbol tables out of the parser memory pool
5327
void *CTcPrsSymtab::operator new(size_t siz)
5329
return G_prsmem->alloc(siz);
5333
* static initialization
5335
void CTcPrsSymtab::s_init()
5337
/* create our static hash function */
5338
if (hash_func_ == 0)
5339
hash_func_ = new CVmHashFuncCS();
5343
* static termination
5345
void CTcPrsSymtab::s_terminate()
5347
/* delete our static hash function */
5348
if (hash_func_ != 0)
5358
CTcPrsSymtab::CTcPrsSymtab(CTcPrsSymtab *parent_scope)
5360
size_t hash_table_size;
5363
* Create the hash table. If we're at global scope (parent_scope ==
5364
* 0), create a large hash table, since we'll probably add lots of
5365
* symbols; otherwise, it's just a local table, which probably won't
5366
* have many entries, so create a small table.
5368
* Note that we always use the static hash function object, hence
5369
* the table doesn't own the object.
5371
hash_table_size = (parent_scope == 0 ? 512 : 32);
5372
hashtab_ = new (G_prsmem)
5373
CVmHashTable(hash_table_size, hash_func_, FALSE,
5374
new (G_prsmem) CVmHashEntry *[hash_table_size]);
5376
/* remember the parent scope */
5377
parent_ = parent_scope;
5379
/* we're not in a debugger frame list yet */
5387
CTcPrsSymtab::~CTcPrsSymtab()
5389
/* delete our underlying hash table */
5394
* Find a symbol, marking it as referenced if we find it.
5396
CTcSymbol *CTcPrsSymtab::find(const textchar_t *sym, size_t len,
5397
CTcPrsSymtab **symtab)
5401
/* find the symbol */
5402
entry = find_noref(sym, len, symtab);
5404
/* if we found an entry, mark it as referenced */
5406
entry->mark_referenced();
5408
/* return the result */
5413
* Find a symbol. This base version does not affect the 'referenced'
5414
* status of the symbol we look up.
5416
CTcSymbol *CTcPrsSymtab::find_noref(const textchar_t *sym, size_t len,
5417
CTcPrsSymtab **symtab)
5419
CTcPrsSymtab *curtab;
5422
* Look for the symbol. Start in the current symbol table, and work
5423
* outwards to the outermost enclosing table.
5425
for (curtab = this ; curtab != 0 ; curtab = curtab->get_parent())
5429
/* look for the symbol in this table */
5430
if ((entry = curtab->find_direct(sym, len)) != 0)
5433
* found it - if the caller wants to know about the symbol
5434
* table in which we actually found the symbol, return that
5440
/* return the symbol table entry we found */
5445
/* we didn't find the symbol - return failure */
5450
* Find a symbol directly in this table
5452
CTcSymbol *CTcPrsSymtab::find_direct(const textchar_t *sym, size_t len)
5454
/* return the entry from our hash table */
5455
return (CTcSymbol *)get_hashtab()->find(sym, len);
5459
* Add a formal parameter symbol
5461
void CTcPrsSymtab::add_formal(const textchar_t *sym, size_t len,
5462
int formal_num, int copy_str)
5467
* Make sure it's not already defined in our own symbol table - if
5468
* it is, log an error and ignore the redundant definition. (We
5469
* only care about our own scope, not enclosing scopes, since it's
5470
* perfectly fine to hide variables in enclosing scopes.)
5472
if (get_hashtab()->find(sym, len) != 0)
5475
G_tok->log_error(TCERR_FORMAL_REDEF, (int)len, sym);
5477
/* don't add the symbol again */
5481
/* create the symbol entry */
5482
lcl = new CTcSymLocal(sym, len, copy_str, TRUE, formal_num);
5484
/* add it to the table */
5485
get_hashtab()->add(lcl);
5489
* Add a symbol to the table
5491
void CTcPrsSymtab::add_entry(CTcSymbol *sym)
5493
/* add it to the table */
5494
get_hashtab()->add(sym);
5498
* remove a symbol from the table
5500
void CTcPrsSymtab::remove_entry(CTcSymbol *sym)
5502
/* remove it from the underyling hash table */
5503
get_hashtab()->remove(sym);
5507
* Add a local variable symbol
5509
CTcSymLocal *CTcPrsSymtab::add_local(const textchar_t *sym, size_t len,
5510
int local_num, int copy_str,
5511
int init_assigned, int init_referenced)
5516
* make sure the symbol isn't already defined in this scope; if it
5519
if (get_hashtab()->find(sym, len) != 0)
5522
G_tok->log_error(TCERR_LOCAL_REDEF, (int)len, sym);
5524
/* don't create the symbol again - return the original definition */
5528
/* create the symbol entry */
5529
lcl = new CTcSymLocal(sym, len, copy_str, FALSE, local_num);
5532
* if the symbol is initially to be marked as referenced or
5533
* assigned, mark it now
5536
lcl->set_val_assigned(TRUE);
5537
if (init_referenced)
5538
lcl->set_val_used(TRUE);
5540
/* add it to the table */
5541
get_hashtab()->add(lcl);
5543
/* return the new local */
5548
* Add a code label ('goto') symbol
5550
CTcSymLabel *CTcPrsSymtab::add_code_label(const textchar_t *sym, size_t len,
5556
* make sure the symbol isn't already defined in this scope; if it
5559
if (get_hashtab()->find(sym, len) != 0)
5562
G_tok->log_error(TCERR_CODE_LABEL_REDEF, (int)len, sym);
5564
/* don't create the symbol again - return the original definition */
5568
/* create the symbol entry */
5569
lbl = new CTcSymLabel(sym, len, copy_str);
5571
/* add it to the table */
5572
get_hashtab()->add(lbl);
5574
/* return the new label */
5580
* Find a symbol; if the symbol isn't defined, add a new entry according
5581
* to the action flag. Because we add a symbol entry if the symbol
5582
* isn't defined, this *always* returns a valid symbol object.
5584
CTcSymbol *CTcPrsSymtab::find_or_def(const textchar_t *sym, size_t len,
5585
int copy_str, tcprs_undef_action action)
5588
CTcPrsSymtab *curtab;
5591
* Look for the symbol. Start in the current symbol table, and work
5592
* outwards to the outermost enclosing table.
5594
for (curtab = this ; ; curtab = curtab->get_parent())
5596
/* look for the symbol in this table */
5597
entry = (CTcSymbol *)curtab->get_hashtab()->find(sym, len);
5600
/* mark the entry as referenced */
5601
entry->mark_referenced();
5603
/* found it - return the symbol */
5608
* If there's no parent symbol table, the symbol is undefined.
5609
* Add a new symbol according to the action parameter. Note
5610
* that we always add the new symbol at global scope, hence we
5611
* add it to 'curtab', not 'this'.
5613
if (curtab->get_parent() == 0)
5615
/* check which action we're being asked to perform */
5618
case TCPRS_UNDEF_ADD_UNDEF:
5619
/* add an "undefined" entry - log an error */
5620
G_tok->log_error(TCERR_UNDEF_SYM, (int)len, sym);
5622
/* create a new symbol of type undefined */
5623
entry = new CTcSymUndef(sym, len, copy_str);
5628
case TCPRS_UNDEF_ADD_PROP:
5629
/* add a new "property" entry - log a warning */
5630
G_tok->log_warning(TCERR_ASSUME_SYM_PROP, (int)len, sym);
5632
/* create a new symbol of type property */
5633
entry = new CTcSymProp(sym, len, copy_str,
5634
G_cg->new_prop_id());
5639
case TCPRS_UNDEF_ADD_PROP_NO_WARNING:
5640
/* add a new property entry with no warning */
5641
entry = new CTcSymProp(sym, len, copy_str,
5642
G_cg->new_prop_id());
5648
/* add the new entry to the global table */
5649
add_to_global_symtab(curtab, entry);
5651
/* return the new entry */
5659
* Enumerate the entries in a symbol table
5661
void CTcPrsSymtab::enum_entries(void (*func)(void *, CTcSymbol *),
5665
* Ask the hash table to perform the enumeration. We know that all
5666
* of our entries in the symbol table are CTcSymbol objects, so we
5667
* can coerce the callback function to the appropriate type without
5670
get_hashtab()->enum_entries((void (*)(void *, CVmHashEntry *))func, ctx);
5674
* Scan the symbol table for unreferenced local variables
5676
void CTcPrsSymtab::check_unreferenced_locals()
5678
/* skip the check if we're only parsing for syntax */
5679
if (!G_prs->get_syntax_only())
5681
/* run the symbols through our unreferenced local check callback */
5682
enum_entries(&unref_local_cb, this);
5687
* Enumeration callback to check for unreferenced locals
5689
void CTcPrsSymtab::unref_local_cb(void *, CTcSymbol *sym)
5691
/* check the symbol */
5692
sym->check_local_references();
5695
/* ------------------------------------------------------------------------ */
5703
CTcPrsNode *CTPNCommaBase::fold_binop()
5705
/* use the normal addition folder */
5706
return S_op_comma.eval_constant(left_, right_);
5710
/* ------------------------------------------------------------------------ */
5712
* Addition parse node
5716
* Fold constants. We override the default fold_constants() for
5717
* addition nodes because addition constancy can be affected by symbol
5718
* resolution. In particular, if we resolve symbols in a list, the list
5719
* could turn constant, which could in turn make the result of an
5720
* addition operator with the list as an operand turn constant.
5722
CTcPrsNode *CTPNAddBase::fold_binop()
5724
/* use the normal addition folder */
5725
return S_op_add.eval_constant(left_, right_);
5728
/* ------------------------------------------------------------------------ */
5730
* Subtraction parse node
5734
* Fold constants. We override the default fold_constants() for the
5735
* subtraction node because subtraction constancy can be affected by
5736
* symbol resolution. In particular, if we resolve symbols in a list,
5737
* the list could turn constant, which could in turn make the result of
5738
* a subtraction operator with the list as an operand turn constant.
5740
CTcPrsNode *CTPNSubBase::fold_binop()
5742
/* use the normal addition folder */
5743
return S_op_sub.eval_constant(left_, right_);
5746
/* ------------------------------------------------------------------------ */
5748
* Equality Comparison parse node
5754
CTcPrsNode *CTPNEqBase::fold_binop()
5756
/* use the normal addition folder */
5757
return S_op_eq.eval_constant(left_, right_);
5760
/* ------------------------------------------------------------------------ */
5762
* Inequality Comparison parse node
5768
CTcPrsNode *CTPNNeBase::fold_binop()
5770
/* use the normal addition folder */
5771
return S_op_ne.eval_constant(left_, right_);
5774
/* ------------------------------------------------------------------------ */
5776
* Logical AND parse node
5782
CTcPrsNode *CTPNAndBase::fold_binop()
5784
/* use the normal addition folder */
5785
return S_op_and.eval_constant(left_, right_);
5788
/* ------------------------------------------------------------------------ */
5790
* Logical OR parse node
5796
CTcPrsNode *CTPNOrBase::fold_binop()
5798
/* use the normal addition folder */
5799
return S_op_or.eval_constant(left_, right_);
5802
/* ------------------------------------------------------------------------ */
5810
CTcPrsNode *CTPNNotBase::fold_unop()
5812
/* use the normal addition folder */
5813
return S_op_unary.eval_const_not(sub_);
5817
/* ------------------------------------------------------------------------ */
5819
* Subscript parse node
5823
* Fold constants. We override the default fold_constants() for
5824
* subscript nodes because subscript constancy can be affected by symbol
5825
* resolution. In particular, if we resolve symbols in a list, the list
5826
* could turn constant, which could in turn make the result of a
5827
* subscript operator with the list as an operand turn constant.
5829
/* ------------------------------------------------------------------------ */
5831
* Equality Comparison parse node
5837
CTcPrsNode *CTPNSubscriptBase::fold_binop()
5839
/* use the normal addition folder */
5840
return S_op_unary.eval_const_subscript(left_, right_);
5844
/* ------------------------------------------------------------------------ */
5846
* Parser Symbol Table Entry base class
5850
* Allocate symbol objects from the parse pool, since these objects have
5851
* all of the lifespan characteristics of pool objects.
5853
void *CTcSymbolBase::operator new(size_t siz)
5855
return G_prsmem->alloc(siz);
5859
* Write to a symbol file.
5861
int CTcSymbolBase::write_to_sym_file(CVmFile *fp)
5863
/* do the basic writing */
5864
return write_to_file_gen(fp);
5868
* Write to a file. This is a generic base routine that can be used for
5869
* writing to a symbol or object file.
5871
int CTcSymbolBase::write_to_file_gen(CVmFile *fp)
5874
fp->write_int2((int)get_type());
5877
return write_name_to_file(fp);
5881
* Write the symbol name to a file
5883
int CTcSymbolBase::write_name_to_file(CVmFile *fp)
5885
/* write the length of my symbol name, followed by the symbol name */
5886
fp->write_int2((int)get_sym_len());
5888
/* write the symbol string */
5889
fp->write_bytes(get_sym(), get_sym_len());
5891
/* we wrote the symbol */
5896
* Read a symbol from a symbol file
5898
CTcSymbol *CTcSymbolBase::read_from_sym_file(CVmFile *fp)
5903
* read the type - this is the one thing we know is always present
5904
* for every symbol (the rest of the data might vary per subclass)
5906
typ = (tc_symtype_t)fp->read_uint2();
5908
/* create the object based on the type */
5912
return CTcSymFunc::read_from_sym_file(fp);
5915
return CTcSymObj::read_from_sym_file(fp);
5918
return CTcSymProp::read_from_sym_file(fp);
5921
return CTcSymEnum::read_from_sym_file(fp);
5924
/* other types should not be in a symbol file */
5925
G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_SYMEXP_INV_TYPE);
5931
* Read the basic information from the symbol file
5933
const char *CTcSymbolBase::base_read_from_sym_file(CVmFile *fp)
5935
char buf[TOK_SYM_MAX_LEN + 1];
5937
/* read, null-terminate, and return the string */
5938
return CTcParser::read_len_prefix_str(fp, buf, sizeof(buf), 0,
5939
TCERR_SYMEXP_SYM_TOO_LONG);
5943
* Write to an object file.
5945
int CTcSymbolBase::write_to_obj_file(CVmFile *fp)
5947
/* do the basic writing */
5948
return write_to_file_gen(fp);
5952
/* ------------------------------------------------------------------------ */
5954
* function symbol entry base
5958
* fold function name into a function address
5960
CTcPrsNode *CTcSymFuncBase::fold_constant()
5964
/* set up the function pointer constant */
5965
cval.set_funcptr((CTcSymFunc *)this);
5967
/* return a constant node with the function pointer */
5968
return new CTPNConst(&cval);
5972
* Write to a symbol file
5974
int CTcSymFuncBase::write_to_sym_file(CVmFile *fp)
5980
/* scan for the bottom of our modify stack */
5981
for (cur = get_mod_base() ; cur != 0 && cur->get_mod_base() != 0 ;
5982
cur = cur->get_mod_base()) ;
5984
/* we modify an external if the bottom of our modify stack is external */
5985
ext_modify = (cur != 0 && cur->is_extern());
5988
* If we're external, don't bother writing to the file - if we're
5989
* importing a function, we don't want to export it as well. Note that
5990
* a function that is replacing or modifying an external function is
5991
* fundamentally external itself, because the function must be defined
5992
* in another file to be replaceable/modifiable.
5994
* As an exception, if this is a multi-method base symbol, and a
5995
* multi-method with this name is defined in this file, export it even
5996
* though it's technically an extern symbol. We don't export most
5997
* extern symbols because we count on the definer to export them, but
5998
* in the case of multi-method base symbols, there is no definer - the
5999
* base symbol is basically a placeholder to be filled in by the
6000
* linker. So *someone* has to export these. The logical place to
6001
* export them is from any file that defines a multi-method based on
6004
if ((is_extern_ || ext_replace_ || ext_modify) && !mm_def_)
6007
/* inherit default */
6008
CTcSymbol::write_to_sym_file(fp);
6010
/* write our argument count, varargs flag, and return value flag */
6012
buf[2] = (varargs_ != 0);
6013
buf[3] = (has_retval_ != 0);
6014
buf[4] = (is_multimethod_ ? 1 : 0)
6015
| (is_multimethod_base_ ? 2 : 0);
6016
fp->write_bytes(buf, 5);
6018
/* we wrote the symbol */
6023
* add an absolute fixup to my list
6025
void CTcSymFuncBase::add_abs_fixup(CTcDataStream *ds, ulong ofs)
6027
/* ask the code body to add the fixup */
6028
CTcAbsFixup::add_abs_fixup(&fixups_, ds, ofs);
6032
* add an absolute fixup at the current stream offset
6034
void CTcSymFuncBase::add_abs_fixup(CTcDataStream *ds)
6036
/* ask the code body to add the fixup */
6037
CTcAbsFixup::add_abs_fixup(&fixups_, ds, ds->get_ofs());
6041
* Read from a symbol file
6043
CTcSymbol *CTcSymFuncBase::read_from_sym_file(CVmFile *fp)
6051
int is_multimethod, is_multimethod_base;
6054
* Read the symbol name. Use a custom reader instead of the base
6055
* reader, because function symbols can be quite long, due to
6056
* multimethod name decoration.
6058
if ((sym = CTcParser::read_len_prefix_str(
6059
fp, symbuf, sizeof(symbuf), 0, TCERR_SYMEXP_SYM_TOO_LONG)) == 0)
6062
/* read the argument count, varargs flag, and return value flag */
6063
fp->read_bytes(info, 5);
6065
varargs = (info[2] != 0);
6066
has_retval = (info[3] != 0);
6067
is_multimethod = ((info[4] & 1) != 0);
6068
is_multimethod_base = ((info[4] & 2) != 0);
6070
/* create and return the new symbol */
6071
return new CTcSymFunc(sym, strlen(sym), FALSE, argc,
6072
varargs, has_retval,
6073
is_multimethod, is_multimethod_base, TRUE);
6077
* Write to an object file
6079
int CTcSymFuncBase::write_to_obj_file(CVmFile *fp)
6083
CTcSymFunc *last_mod;
6088
* If it's external, and we have no fixups, don't bother writing it to
6089
* the object file. If there are no fixups, we don't have any
6090
* references to the function, hence there's no need to include it in
6093
if (is_extern_ && fixups_ == 0)
6097
* If we have a modified base function, scan down the chain of modified
6098
* bases until we reach the last one. If it's external, we need to
6099
* note this, and we need to store the fixup list for the external
6100
* symbol so that we can explicitly link it to the imported symbol at
6103
for (mod_body_cnt = 0, last_mod = 0, cur = get_mod_base() ; cur != 0 ;
6104
last_mod = cur, cur = cur->get_mod_base())
6106
/* if this one has an associated code body, count it */
6107
if (cur->get_code_body() != 0 && !cur->get_code_body()->is_replaced())
6111
/* we modify an external if the last in the modify chain is external */
6112
ext_modify = (last_mod != 0 && last_mod->is_extern());
6114
/* inherit default */
6115
CTcSymbol::write_to_obj_file(fp);
6118
* write our argument count, varargs flag, return value, extern flags,
6119
* and the number of our modified base functions with code bodies
6122
buf[2] = (varargs_ != 0);
6123
buf[3] = (has_retval_ != 0);
6124
buf[4] = (is_extern_ != 0);
6125
buf[5] = (ext_replace_ != 0);
6126
buf[6] = (ext_modify != 0);
6127
buf[7] = (is_multimethod_ ? 1 : 0)
6128
| (is_multimethod_base_ ? 2 : 0);
6129
oswp2(buf + 8, mod_body_cnt);
6130
fp->write_bytes(buf, 10);
6132
/* if we modify an external, save its fixup list */
6134
CTcAbsFixup::write_fixup_list_to_object_file(fp, last_mod->fixups_);
6136
/* write the code stream offsets of the modified base function bodies */
6137
for (cur = get_mod_base() ; cur != 0 ; cur = cur->get_mod_base())
6139
/* if this one has a code body, write its code stream offset */
6140
if (cur->get_code_body() != 0)
6141
fp->write_int4(cur->get_anchor()->get_ofs());
6145
* If we're defined as external, write our fixup list. Since this
6146
* is an external symbol, it will have no anchor in the code stream,
6147
* hence we need to write our fixup list with the symbol and not
6151
CTcAbsFixup::write_fixup_list_to_object_file(fp, fixups_);
6153
/* we wrote the symbol */
6157
/* ------------------------------------------------------------------------ */
6159
* local variable symbol entry base
6165
CTcSymLocalBase::CTcSymLocalBase(const char *str, size_t len, int copy,
6166
int is_param, int var_num)
6167
: CTcSymbol(str, len, copy, (is_param ? TC_SYM_PARAM : TC_SYM_LOCAL))
6169
/* remember the information */
6171
is_param_ = is_param;
6173
/* presume it's a regular stack variable (not a context local) */
6174
is_ctx_local_ = FALSE;
6178
ctx_var_num_set_ = FALSE;
6180
/* so far, the value isn't used anywhere */
6182
val_assigned_ = FALSE;
6184
/* the symbol has not been referenced so far */
6185
referenced_ = FALSE;
6187
/* remember where the symbol is defined in the source file */
6188
G_tok->get_last_pos(&src_desc_, &src_linenum_);
6192
* Mark the value of the variable as used
6194
void CTcSymLocalBase::set_val_used(int f)
6196
/* note the new status */
6199
/* if we have now assigned the value, propagate to the original */
6200
if (f && ctx_orig_ != 0)
6201
ctx_orig_->set_val_used(TRUE);
6205
* Mark the value of the variable as assigned
6207
void CTcSymLocalBase::set_val_assigned(int f)
6209
/* note the new status */
6212
/* if we have now assigned the value, propagate to the original */
6213
if (f && ctx_orig_ != 0)
6214
ctx_orig_->set_val_assigned(TRUE);
6218
* Check for references to this local
6220
void CTcSymLocalBase::check_local_references()
6223
tc_severity_t sev = TC_SEV_WARNING;
6226
* if this isn't an original, but is simply a copy of a variable
6227
* inherited from an enclosing scope (such as into an anonymous
6228
* function), don't bother even checking for errors - we'll let the
6229
* original symbol take care of reporting its own errors
6235
* if it's unreferenced or unassigned (or both), log an error; note
6236
* that a formal parameter is always assigned, since the value is
6237
* assigned by the caller
6239
if (!get_val_used() && (!get_val_assigned() && !is_param()))
6241
/* the variable is never used at all */
6242
err = TCERR_UNREFERENCED_LOCAL;
6244
else if (!get_val_used())
6246
if (is_param() || is_list_param())
6249
* it's a parameter, or a local that actually contains a
6250
* varargs parameter list - generate only a pedantic error
6252
sev = TC_SEV_PEDANTIC;
6253
err = TCERR_UNREFERENCED_PARAM;
6257
/* this local is assigned a value that's never used */
6258
err = TCERR_UNUSED_LOCAL_ASSIGNMENT;
6261
else if (!get_val_assigned() && !is_param())
6263
/* it's used but never assigned */
6264
err = TCERR_UNASSIGNED_LOCAL;
6273
* display the warning message, showing the error location as the
6274
* source line where the local was defined
6276
G_tcmain->log_error(get_src_desc(), get_src_linenum(),
6277
sev, err, (int)get_sym_len(), get_sym());
6281
* create a new context variable copy of this symbol
6283
CTcSymbol *CTcSymLocalBase::new_ctx_var() const
6287
/* create a new local with the same name */
6288
lcl = new CTcSymLocal(get_sym(), get_sym_len(), FALSE, FALSE, 0);
6290
/* refer the copy back to the original (i.e., me) */
6291
lcl->set_ctx_orig((CTcSymLocal *)this);
6293
/* set up the context variable information */
6297
* The original is a true local - we're at the first context
6298
* level, and we don't yet have a property assigned, since we
6299
* don't know if this variable is actually going to be
6302
lcl->set_ctx_level(1);
6307
* The original was already a context variable - we're at one
6308
* higher context level in this function, and we use the same
6309
* context property as the original did.
6311
lcl->set_ctx_level(ctx_level_ + 1);
6314
/* return the new symbol */
6319
* Apply context variable conversion
6321
int CTcSymLocalBase::apply_ctx_var_conv(CTcPrsSymtab *symtab,
6322
CTPNCodeBody *code_body)
6325
* if this symbol isn't referenced, simply delete it from the table,
6326
* so that it doesn't get entered in the debug records; and there's
6327
* no need to propagate it back to the enclosing scope as a context
6328
* variable, since it's not referenced from this enclosed scope
6332
/* remove the symbol from the table */
6333
symtab->remove_entry(this);
6335
/* this variable doesn't need to be converted */
6340
* convert the symbol in the enclosing scope to a context local, if
6345
/* convert the original to a context symbol */
6346
ctx_orig_->convert_to_ctx_var(get_val_used(), get_val_assigned());
6349
* ask the code body for the context object's local variable for
6350
* our recursion level
6352
ctx_var_num_ = code_body->get_or_add_ctx_var_for_level(ctx_level_);
6354
/* note that we've set our context variable ID */
6355
ctx_var_num_set_ = TRUE;
6357
/* this variable was converted */
6361
/* this variable wasn't converted */
6366
* convert this variable to a context variable
6368
void CTcSymLocalBase::convert_to_ctx_var(int val_used, int val_assigned)
6370
/* if I'm not already a context local, mark me as a context local */
6373
/* mark myself as a context local */
6374
is_ctx_local_ = TRUE;
6377
* we haven't yet assigned our local context variable, since
6378
* we're still processing the inner scope at this point; just
6379
* store placeholders for now so we know to come back and fix
6386
/* note that I've been referenced */
6389
/* propagate the value-used and value-assigned flags */
6393
set_val_assigned(TRUE);
6395
/* propagate the conversion to the original symbol */
6397
ctx_orig_->convert_to_ctx_var(val_used, val_assigned);
6401
* finish the context variable conversion
6403
void CTcSymLocalBase::finish_ctx_var_conv()
6406
* If this isn't already marked as a context variable, there's
6407
* nothing to do - this variable must not have been referenced from
6408
* an anonymous function yet, and hence can be kept in the stack.
6410
* Similarly, if my context local variable number has been assigned
6411
* already, there's nothing to do - we must have been set to refer
6412
* back to a context variable in an enclosing scope (this can happen
6413
* in a nested anonymous function).
6415
if (!is_ctx_local_ || ctx_var_num_set_)
6419
* tell the parser to create a local context for this scope, if it
6422
G_prs->init_local_ctx();
6424
/* use the local context variable specified by the parser */
6425
ctx_var_num_ = G_prs->get_local_ctx_var();
6426
ctx_var_num_set_ = TRUE;
6428
/* assign our array index */
6429
if (ctx_arr_idx_ == 0)
6430
ctx_arr_idx_ = G_prs->alloc_ctx_arr_idx();
6434
* Get my context variable array index
6436
int CTcSymLocalBase::get_ctx_arr_idx() const
6439
* if I'm based on an original symbol from another scope, use the
6440
* same property ID as the original symbol
6443
return ctx_orig_->get_ctx_arr_idx();
6445
/* return my context property */
6446
return ctx_arr_idx_;
6449
/* ------------------------------------------------------------------------ */
6451
* object symbol entry base
6455
* fold the symbol as a constant
6457
CTcPrsNode *CTcSymObjBase::fold_constant()
6461
/* set up the object constant */
6462
cval.set_obj(get_obj_id(), get_metaclass());
6464
/* return a constant node */
6465
return new CTPNConst(&cval);
6469
* Write to a symbol file
6471
int CTcSymObjBase::write_to_sym_file(CVmFile *fp)
6476
* If we're external, don't bother writing to the file - if we're
6477
* importing an object, we don't want to export it as well. If it's
6478
* modified, don't write it either, because modified symbols cannot
6479
* be referenced directly by name (the symbol for a modified object
6480
* is a fake symbol anyway). In addition, don't write the symbol if
6481
* it's a 'modify' or 'replace' definition that applies to an
6482
* external base object - instead, we'll pick up the symbol from the
6483
* other symbol file with the original definition.
6485
if (is_extern_ || modified_ || ext_modify_ || ext_replace_)
6488
/* inherit default */
6489
result = CTcSymbol::write_to_sym_file(fp);
6491
/* if that was successful, write additional object-type-specific data */
6494
/* write the metaclass ID */
6495
fp->write_int2((int)metaclass_);
6497
/* if it's of metaclass tads-object, write superclass information */
6498
if (metaclass_ == TC_META_TADSOBJ)
6505
* set up our flags: indicate whether or not we're explicitly
6506
* based on the root object class, and if we're a 'class'
6509
c = ((sc_is_root() ? 1 : 0)
6510
| (is_class() ? 2 : 0)
6511
| (is_transient() ? 4 : 0));
6512
fp->write_bytes(&c, 1);
6514
/* count the declared superclasses */
6515
for (cnt = 0, sc = sc_name_head_ ; sc != 0 ;
6516
sc = sc->nxt_, ++cnt) ;
6519
* write the number of declared superclasses followed by the
6520
* names of the superclasses
6522
fp->write_int2(cnt);
6523
for (sc = sc_name_head_ ; sc != 0 ; sc = sc->nxt_)
6525
/* write the counted-length identifier */
6526
fp->write_int2(sc->get_sym_len());
6527
fp->write_bytes(sc->get_sym_txt(), sc->get_sym_len());
6532
/* return the result */
6537
* Read from a symbol file
6539
CTcSymbol *CTcSymObjBase::read_from_sym_file(CVmFile *fp)
6542
tc_metaclass_t meta;
6548
/* read the symbol name */
6549
if ((txt = base_read_from_sym_file(fp)) == 0)
6552
/* read the metaclass ID */
6553
meta = (tc_metaclass_t)fp->read_uint2();
6556
* If it's a dictionary object, check to see if it's already defined -
6557
* a dictionary object can be exported from multiple modules without
6558
* error, since dictionaries are shared across modules.
6560
* The same applies to grammar productions, since a grammar production
6561
* can be implicitly created in multiple files.
6563
if (meta == TC_META_DICT || meta == TC_META_GRAMPROD)
6567
/* look for a previous instance of the symbol */
6568
old_sym = G_prs->get_global_symtab()->find(txt, strlen(txt));
6570
&& old_sym->get_type() == TC_SYM_OBJ
6571
&& ((CTcSymObj *)old_sym)->get_metaclass() == meta)
6574
* the dictionary is already in the symbol table - return the
6575
* existing one, since there's no conflict with importing the
6576
* dictionary from multiple places
6582
/* create the new symbol */
6583
sym = new CTcSymObj(txt, strlen(txt), FALSE, G_cg->new_obj_id(),
6586
/* if the metaclass is tads-object, read additional information */
6587
if (meta == TC_META_TADSOBJ)
6589
/* read the root-object-superclass flag and the class-object flag */
6590
fp->read_bytes(&c, 1);
6591
sym->set_sc_is_root((c & 1) != 0);
6592
sym->set_is_class((c & 2) != 0);
6594
sym->set_transient();
6596
/* read the number of superclasses, and read the superclass names */
6597
cnt = fp->read_uint2();
6598
for (i = 0 ; i < cnt ; ++i)
6600
char buf[TOK_SYM_MAX_LEN + 1];
6604
/* read the symbol */
6605
sc_txt = CTcParser::read_len_prefix_str(
6606
fp, buf, sizeof(buf), &sc_len, TCERR_SYMEXP_SYM_TOO_LONG);
6608
/* add the superclass list entry to the symbol */
6609
sym->add_sc_name_entry(sc_txt, sc_len);
6613
/* return the symbol */
6618
* Add a superclass name entry.
6620
void CTcSymObjBase::add_sc_name_entry(const char *txt, size_t len)
6622
CTPNSuperclass *entry;
6624
/* create the entry object */
6625
entry = new CTPNSuperclass(txt, len);
6627
/* link it into our list */
6628
if (sc_name_tail_ != 0)
6629
sc_name_tail_->nxt_ = entry;
6631
sc_name_head_ = entry;
6632
sc_name_tail_ = entry;
6636
* Check to see if I have a given superclass.
6638
int CTcSymObjBase::has_superclass(class CTcSymObj *sc_sym) const
6640
CTPNSuperclass *entry;
6643
* Scan my direct superclasses. For each one, check to see if my
6644
* superclass matches the given superclass, or if my superclass
6645
* inherits from the given superclass.
6647
for (entry = sc_name_head_ ; entry != 0 ; entry = entry->nxt_)
6649
CTcSymObj *entry_sym;
6651
/* look up this symbol */
6652
entry_sym = (CTcSymObj *)G_prs->get_global_symtab()->find(
6653
entry->get_sym_txt(), entry->get_sym_len());
6656
* if the entry's symbol doesn't exist or isn't an object symbol,
6659
if (entry_sym == 0 || entry_sym->get_type() != TC_SYM_OBJ)
6663
* if it matches the given superclass, we've found the given
6664
* superclass among our direct superclasses, so we definitely have
6665
* the given superclass
6667
if (entry_sym == sc_sym)
6671
* ask the symbol if the given class is among its direct or
6672
* indirect superclasses - if it's a superclass of my superclass,
6673
* it's also my superclass
6675
if (entry_sym->has_superclass(sc_sym))
6680
* we didn't find the given class anywhere among our superclasses or
6681
* their superclasses, so it must not be a superclass of ours
6687
* Synthesize a placeholder symbol for a modified object.
6689
* The new symbol is not for use by the source code; we add it merely as
6690
* a placeholder. Build its name starting with a space so that it can
6691
* never be reached from source code, and use the object number to
6692
* ensure it's unique within the file.
6694
CTcSymObj *CTcSymObjBase::synthesize_modified_obj_sym(int anon)
6696
char nm[TOK_SYM_MAX_LEN + 1];
6697
const char *stored_nm;
6702
/* create a new ID for the modified object */
6703
mod_id = G_cg->new_obj_id();
6705
/* build the name */
6708
/* it's anonymous - we don't need a real name */
6709
stored_nm = ".anon";
6714
/* synthesize a name */
6715
synthesize_modified_obj_name(nm, mod_id);
6718
/* store it in tokenizer memory */
6719
stored_nm = G_tok->store_source(nm, len);
6722
/* create the object */
6723
mod_sym = new CTcSymObj(stored_nm, len, FALSE, mod_id, FALSE,
6724
TC_META_TADSOBJ, 0);
6726
/* mark it as modified */
6727
mod_sym->set_modified(TRUE);
6729
/* add it to the symbol table, if it has a name */
6731
G_prs->get_global_symtab()->add_entry(mod_sym);
6733
G_prs->add_anon_obj(mod_sym);
6735
/* return the new symbol */
6740
* Build the name of a synthesized placeholder symbol for a given object
6741
* number. The buffer should be TOK_SYM_MAX_LEN + 1 bytes long.
6743
void CTcSymObjBase::
6744
synthesize_modified_obj_name(char *buf, tctarg_obj_id_t obj_id)
6747
* Build the fake name, based on the object ID to ensure uniqueness
6748
* and so that we can look it up based on the object ID. Start it
6749
* with a space so that no source token can ever refer to it.
6751
sprintf(buf, " %lx", (ulong)obj_id);
6755
* Add a deleted property entry
6757
void CTcSymObjBase::add_del_prop_to_list(CTcObjPropDel **list_head,
6758
CTcSymProp *prop_sym)
6760
CTcObjPropDel *entry;
6762
/* create the new entry */
6763
entry = new CTcObjPropDel(prop_sym);
6765
/* link it into my list */
6766
entry->nxt_ = *list_head;
6771
* Add a self-reference fixup
6773
void CTcSymObjBase::add_self_ref_fixup(CTcDataStream *stream, ulong ofs)
6775
/* add a fixup to our list */
6776
CTcIdFixup::add_fixup(&fixups_, stream, ofs, obj_id_);
6780
* Write to a object file
6782
int CTcSymObjBase::write_to_obj_file(CVmFile *fp)
6785
* If the object is external and has never been referenced, don't
6786
* bother writing it.
6788
* In addition, if the object is marked as modified, don't write it.
6789
* We write modified base objects specially, because we must control
6790
* the order in which a modified base object is written relative its
6793
if ((is_extern_ && !ref_) || modified_)
6796
/* if the object has already been written, don't write it again */
6797
if (written_to_obj_)
6800
* if we've never been counted in the object file before, we
6801
* must have been written indirectly in the course of writing
6802
* another symbol - in this case, return true to indicate that
6803
* we are in the file, even though we're not actually writing
6806
if (!counted_in_obj_)
6808
/* we've now been counted in the object file */
6809
counted_in_obj_ = TRUE;
6811
/* indicate that we have been written */
6816
/* we've already been written and counted - don't write again */
6821
/* do the main part of the writing */
6822
return write_to_obj_file_main(fp);
6826
* Write the object symbol to an object file. This main routine does
6827
* most of the actual work, once we've decided that we're actually going
6828
* to write the symbol.
6830
int CTcSymObjBase::write_to_obj_file_main(CVmFile *fp)
6834
CTcObjPropDel *delprop;
6836
/* take the next object file index */
6837
set_obj_file_idx(G_prs->get_next_obj_file_sym_idx());
6840
* if I have a dictionary object, make sure it's in the object file
6841
* before I am - we need to be able to reference the object during
6842
* load, so it has to be written before me
6845
dict_->write_sym_to_obj_file(fp);
6848
* if I'm not anonymous, write the basic header information for the
6849
* symbol (don't do this for anonymous objects, since they don't
6850
* have a name to write)
6853
write_to_file_gen(fp);
6856
* write my object ID, so that we can translate from the local
6857
* numbering system in the object file to the new numbering system
6860
oswp4(buf, obj_id_);
6862
/* write the flags */
6863
buf[4] = (is_extern_ != 0);
6864
buf[5] = (ext_replace_ != 0);
6865
buf[6] = (modified_ != 0);
6866
buf[7] = (mod_base_sym_ != 0);
6867
buf[8] = (ext_modify_ != 0);
6868
buf[9] = (obj_stm_ != 0 && obj_stm_->is_class());
6869
buf[10] = (transient_ != 0);
6871
/* add the metaclass type */
6872
oswp2(buf + 11, (int)metaclass_);
6874
/* add the dictionary's object file index, if we have one */
6876
oswp2(buf + 13, dict_->get_obj_idx());
6881
* add my object file index (we store this to eliminate any
6882
* dependency on the load order - this allows us to write other
6883
* symbols recursively without worrying about exactly where the
6884
* recursion occurs relative to assigning the file index)
6886
oswp2(buf + 15, get_obj_file_idx());
6888
/* write the data to the file */
6889
fp->write_bytes(buf, 17);
6891
/* if we're not external, write our stream address */
6893
fp->write_int4(stream_ofs_);
6895
/* if we're modifying another object, store some extra information */
6896
if (mod_base_sym_ != 0)
6899
* Write our list of properties to be deleted from base objects
6900
* at link time. First, count the properties in the list.
6902
for (cnt = 0, delprop = first_del_prop_ ; delprop != 0 ;
6903
++cnt, delprop = delprop->nxt_) ;
6905
/* write the count */
6906
fp->write_int2(cnt);
6908
/* write the deleted property list */
6909
for (delprop = first_del_prop_ ; delprop != 0 ;
6910
delprop = delprop->nxt_)
6913
* write out this property symbol (we write the symbol
6914
* rather than the ID, because when we load the object file,
6915
* we'll need to adjust the ID to new global numbering
6916
* system in the image file; the easiest way to do this is
6917
* to write the symbol and look it up at load time)
6919
fp->write_int2(delprop->prop_sym_->get_sym_len());
6920
fp->write_bytes(delprop->prop_sym_->get_sym(),
6921
delprop->prop_sym_->get_sym_len());
6925
/* write our self-reference fixup list */
6926
CTcIdFixup::write_to_object_file(fp, fixups_);
6929
* If this is a modifying object, we must write the entire chain of
6930
* modified base objects immediately after this object. When we're
6931
* reading the symbol table, this ensures that we can read each
6932
* modified base object recursively as we read its modifiers, which
6933
* is necessary so that we can build up the same modification chain
6934
* on loading the object file.
6936
if (mod_base_sym_ != 0)
6938
/* write the main part of the definition */
6939
mod_base_sym_->write_to_obj_file_main(fp);
6942
/* mark the object as written to the file */
6943
written_to_obj_ = TRUE;
6950
* Write cross-references to the object file
6952
int CTcSymObjBase::write_refs_to_obj_file(CVmFile *fp)
6961
* if this symbol wasn't written to the object file in the first
6962
* place, we obviously don't want to include any extra data for it
6964
if (!written_to_obj_)
6967
/* write my symbol index */
6968
fp->write_int4(get_obj_file_idx());
6970
/* write a placeholder superclass count */
6971
cnt_pos = fp->get_pos();
6974
/* write my superclass list */
6975
for (sc = (obj_stm_ != 0 ? obj_stm_->get_first_sc() : 0), cnt = 0 ;
6976
sc != 0 ; sc = sc->nxt_)
6980
/* look up this superclass symbol */
6981
sym = (CTcSymObj *)sc->get_sym();
6982
if (sym != 0 && sym->get_type() == TC_SYM_OBJ)
6984
/* write the superclass symbol index */
6985
fp->write_int4(sym->get_obj_file_idx());
6992
/* go back and write the superclass count */
6993
end_pos = fp->get_pos();
6994
fp->set_pos(cnt_pos);
6995
fp->write_int2(cnt);
6996
fp->set_pos(end_pos);
6998
/* count my vocabulary words */
6999
for (cnt = 0, voc = vocab_ ; voc != 0 ; ++cnt, voc = voc->nxt_) ;
7001
/* write my vocabulary words */
7002
fp->write_int2(cnt);
7003
for (voc = vocab_ ; voc != 0 ; voc = voc->nxt_)
7005
/* write the text of the word */
7006
fp->write_int2(voc->len_);
7007
fp->write_bytes(voc->txt_, voc->len_);
7009
/* write the property ID */
7010
fp->write_int2(voc->prop_);
7013
/* indicate that we wrote the symbol */
7018
* Load references from the object file
7020
void CTcSymObjBase::load_refs_from_obj_file(CVmFile *fp, const char *,
7022
tctarg_prop_id_t *prop_xlat)
7026
CTcObjScEntry *sc_tail;
7028
/* read the superclass count */
7029
cnt = fp->read_uint2();
7031
/* read the superclass list */
7032
for (sc_tail = 0, i = 0 ; i < cnt ; ++i)
7038
/* read the next index */
7039
idx = fp->read_uint4();
7041
/* get the symbol */
7042
sym = (CTcSymObj *)G_prs->get_objfile_sym(idx);
7043
if (sym->get_type() != TC_SYM_OBJ)
7046
/* create a new list entry */
7047
sc = new (G_prsmem) CTcObjScEntry(sym);
7049
/* link it in at the end of the my superclass list */
7055
/* this is now the last entry in my superclass list */
7059
/* load the vocabulary words */
7060
cnt = fp->read_uint2();
7061
for (i = 0 ; i < cnt ; ++i)
7065
tctarg_prop_id_t prop;
7067
/* read the length of this word's text */
7068
len = fp->read_uint2();
7070
/* allocate parser memory for the word's text */
7071
txt = (char *)G_prsmem->alloc(len);
7073
/* read the word into the allocated text buffer */
7074
fp->read_bytes(txt, len);
7076
/* read the property */
7077
prop = (tctarg_prop_id_t)fp->read_uint2();
7079
/* translate the property to the new numbering system */
7080
prop = prop_xlat[prop];
7082
/* add the word to our vocabulary */
7083
add_vocab_word(txt, len, prop);
7088
* Add a word to my vocabulary
7090
void CTcSymObjBase::add_vocab_word(const char *txt, size_t len,
7091
tctarg_prop_id_t prop)
7093
CTcVocabEntry *entry;
7095
/* create a new vocabulary entry */
7096
entry = new (G_prsmem) CTcVocabEntry(txt, len, prop);
7098
/* link it into my list */
7099
entry->nxt_ = vocab_;
7104
* Delete a vocabulary property from my list (for 'replace')
7106
void CTcSymObjBase::delete_vocab_prop(tctarg_prop_id_t prop)
7108
CTcVocabEntry *entry;
7112
/* scan my list and delete each word defined for the given property */
7113
for (prv = 0, entry = vocab_ ; entry != 0 ; entry = nxt)
7115
/* remember the next entry */
7118
/* if this entry is for the given property, unlink it */
7119
if (entry->prop_ == prop)
7122
* it matches - unlink it from the list (note that we don't
7123
* have to delete the entry, because it's allocated in
7124
* parser memory and thus will be deleted when the parser is
7133
* this entry is no longer in any list (we don't really have
7134
* to clear the 'next' pointer here, since nothing points to
7135
* 'entry' any more, but doing so will make it obvious that
7136
* the entry was removed from the list, which could be handy
7137
* during debugging from time to time)
7144
* this entry is still in the list, so it's now the previous
7145
* entry for our scan
7153
* Build my dictionary
7155
void CTcSymObjBase::build_dictionary()
7157
CTcVocabEntry *entry;
7159
/* if I don't have a dictionary, there's nothing to do */
7164
* if I'm a class, there's nothing to do, since vocabulary defined
7165
* in a class is only entered in the dictionary for the instances of
7166
* the class, not for the class itself
7171
/* add inherited words from my superclasses to my list */
7174
/* add each of my words to the dictionary */
7175
for (entry = vocab_ ; entry != 0 ; entry = entry->nxt_)
7177
/* add this word to my dictionary */
7178
dict_->add_word(entry->txt_, entry->len_, FALSE,
7179
obj_id_, entry->prop_);
7184
* Add my words to the dictionary, associating the words with the given
7185
* object. This can be used to add my own words to the dictionary or to
7186
* add my words to a subclass's dictionary.
7188
void CTcSymObjBase::inherit_vocab()
7193
* if I've already inherited my superclass vocabulary, there's
7194
* nothing more we need to do
7196
if (vocab_inherited_)
7199
/* make a note that I've inherited my superclass vocabulary */
7200
vocab_inherited_ = TRUE;
7202
/* inherit words from each superclass */
7203
for (sc = sc_ ; sc != 0 ; sc = sc->nxt_)
7205
/* make sure this superclass has built its inherited list */
7206
sc->sym_->inherit_vocab();
7208
/* add this superclass's words to my list */
7209
sc->sym_->add_vocab_to_subclass((CTcSymObj *)this);
7214
* Add my vocabulary words to the given subclass's vocabulary list
7216
void CTcSymObjBase::add_vocab_to_subclass(CTcSymObj *sub)
7218
CTcVocabEntry *entry;
7220
/* add each of my words to the subclass */
7221
for (entry = vocab_ ; entry != 0 ; entry = entry->nxt_)
7223
/* add this word to my dictionary */
7224
sub->add_vocab_word(entry->txt_, entry->len_, entry->prop_);
7229
* Set my base 'modify' object. This tells us the object that we're
7232
void CTcSymObjBase::set_mod_base_sym(CTcSymObj *sym)
7234
/* remember the object I'm modifying */
7235
mod_base_sym_ = sym;
7238
* set the other object's link back to me, so it knows that I'm the
7239
* object that's modifying it
7242
sym->set_modifying_sym((CTcSymObj *)this);
7246
* Get the appropriate stream for a given metaclass
7248
CTcDataStream *CTcSymObjBase::get_stream_from_meta(tc_metaclass_t meta)
7252
case TC_META_TADSOBJ:
7253
/* it's the regular object stream */
7257
/* intrinsic class modifier stream */
7258
return G_icmod_stream;
7261
/* other metaclasses have no stream */
7267
* Add a class-specific template
7269
void CTcSymObjBase::add_template(CTcObjTemplate *tpl)
7271
/* link it in at the tail of our list */
7272
if (template_tail_ != 0)
7273
template_tail_->nxt_ = tpl;
7275
template_head_ = tpl;
7276
template_tail_ = tpl;
7280
* Create a grammar rule list object
7282
CTcGramProdEntry *CTcSymObjBase::create_grammar_entry(
7283
const char *prod_sym, size_t prod_sym_len)
7287
/* look up the grammar production symbol */
7288
sym = G_prs->find_or_def_gramprod(prod_sym, prod_sym_len, 0);
7290
/* create a new grammar list associated with the production */
7291
grammar_entry_ = new (G_prsmem) CTcGramProdEntry(sym);
7293
/* return the new grammar list */
7294
return grammar_entry_;
7298
/* ------------------------------------------------------------------------ */
7306
void CTcSymMetaclassBase::add_prop(const char *txt, size_t len,
7307
const char *obj_fname, int is_static)
7309
CTcSymProp *prop_sym;
7311
/* see if this property is already defined */
7312
prop_sym = (CTcSymProp *)G_prs->get_global_symtab()->find(txt, len);
7315
/* it's already defined - make sure it's a property */
7316
if (prop_sym->get_type() != TC_SYM_PROP)
7319
* it's something other than a property - log the
7320
* appropriate type of error, depending on whether we're
7321
* loading this from an object file or from source code
7325
/* creating from source - note the code location */
7326
G_tok->log_error_curtok(TCERR_REDEF_AS_PROP);
7330
/* loading from an object file */
7331
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
7332
TCERR_OBJFILE_REDEF_SYM_TYPE,
7333
(int)len, txt, "property", obj_fname);
7336
/* forget the symbol - it's not a property */
7342
/* add the property definition */
7343
prop_sym = new CTcSymProp(txt, len, FALSE, G_cg->new_prop_id());
7344
G_prs->get_global_symtab()->add_entry(prop_sym);
7348
* if we found a valid property symbol, add it to the metaclass
7354
* mark the symbol as referenced - even if we don't directly
7355
* make use of it, the metaclass table references this symbol
7357
prop_sym->mark_referenced();
7359
/* add the property to the metaclass list */
7360
add_prop(prop_sym, is_static);
7367
void CTcSymMetaclassBase::add_prop(class CTcSymProp *prop, int is_static)
7369
CTcSymMetaProp *entry;
7371
/* create a new list entry for the property */
7372
entry = new (G_prsmem) CTcSymMetaProp(prop, is_static);
7374
/* link it at the end of our list */
7375
if (prop_tail_ != 0)
7376
prop_tail_->nxt_ = entry;
7381
/* count the addition */
7386
* write some additional data to the object file
7388
int CTcSymMetaclassBase::write_to_obj_file(class CVmFile *fp)
7390
CTcSymMetaProp *cur;
7393
/* inherit default */
7394
CTcSymbol::write_to_obj_file(fp);
7396
/* write my metaclass index, class object ID, and property count */
7397
fp->write_int2(meta_idx_);
7398
fp->write_int4(class_obj_);
7399
fp->write_int2(prop_cnt_);
7401
/* write my property symbol list */
7402
for (cur = prop_head_ ; cur != 0 ; cur = cur->nxt_)
7404
/* write this symbol name */
7405
fp->write_int2(cur->prop_->get_sym_len());
7406
fp->write_bytes(cur->prop_->get_sym(), cur->prop_->get_sym_len());
7408
/* set up the flags */
7410
if (cur->is_static_)
7413
/* write the flags */
7414
fp->write_bytes(buf, 1);
7417
/* write our modifying object flag */
7418
buf[0] = (mod_obj_ != 0);
7419
fp->write_bytes(buf, 1);
7421
/* if we have a modifier object chain, write it out */
7423
mod_obj_->write_to_obj_file_as_modified(fp);
7430
* get the nth property in our table
7432
CTcSymMetaProp *CTcSymMetaclassBase::get_nth_prop(int n) const
7434
CTcSymMetaProp *prop;
7436
/* traverse the list to the desired index */
7437
for (prop = prop_head_ ; prop != 0 && n != 0 ; prop = prop->nxt_, --n) ;
7439
/* return the property */
7444
/* ------------------------------------------------------------------------ */
7446
* property symbol entry base
7450
* fold an address constant
7452
CTcPrsNode *CTcSymPropBase::fold_addr_const()
7456
/* set up the property pointer constant */
7457
cval.set_prop(get_prop());
7459
/* return a constant node */
7460
return new CTPNConst(&cval);
7464
* Read from a symbol file
7466
CTcSymbol *CTcSymPropBase::read_from_sym_file(CVmFile *fp)
7469
CTcSymbol *old_entry;
7471
/* read the symbol name */
7472
if ((sym = base_read_from_sym_file(fp)) == 0)
7476
* If this property is already defined, this is a harmless
7477
* redefinition - every symbol file can define the same property
7478
* without any problem. Indicate the harmless redefinition by
7479
* returning the original symbol.
7481
old_entry = G_prs->get_global_symtab()->find(sym, strlen(sym));
7482
if (old_entry != 0 && old_entry->get_type() == TC_SYM_PROP)
7485
/* create and return the new symbol */
7486
return new CTcSymProp(sym, strlen(sym), FALSE, G_cg->new_prop_id());
7490
* Write to an object file
7492
int CTcSymPropBase::write_to_obj_file(CVmFile *fp)
7495
* If the property has never been referenced, don't bother writing
7496
* it. We must have picked up the definition from an external
7497
* symbol set we loaded but have no references of our own to the
7503
/* inherit default */
7504
CTcSymbol::write_to_obj_file(fp);
7507
* write my local property ID value - when we load the object file,
7508
* we'll need to figure out the translation from our original
7509
* numbering system to the new numbering system used in the image
7512
fp->write_int4((ulong)prop_);
7519
/* ------------------------------------------------------------------------ */
7521
* Enumerator symbol base
7525
* fold the symbol as a constant
7527
CTcPrsNode *CTcSymEnumBase::fold_constant()
7531
/* set up the enumerator constant */
7532
cval.set_enum(get_enum_id());
7534
/* return a constant node */
7535
return new CTPNConst(&cval);
7540
* Write to a symbol file
7542
int CTcSymEnumBase::write_to_sym_file(CVmFile *fp)
7547
/* inherit default */
7548
result = CTcSymbol::write_to_sym_file(fp);
7550
/* write the 'token' flag */
7553
/* clear the flags */
7556
/* set the 'token' flag if appropriate */
7560
/* write the flags */
7561
fp->write_bytes(buf, 1);
7564
/* return the result */
7569
* Read from a symbol file
7571
CTcSymbol *CTcSymEnumBase::read_from_sym_file(CVmFile *fp)
7574
CTcSymEnum *old_entry;
7578
/* read the symbol name */
7579
if ((sym = base_read_from_sym_file(fp)) == 0)
7582
/* read the 'token' flag */
7583
fp->read_bytes(buf, 1);
7584
is_token = ((buf[0] & 1) != 0);
7587
* If this enumerator is already defined, this is a harmless
7588
* redefinition - every symbol file can define the same enumerator
7589
* without any problem. Indicate the harmless redefinition by
7590
* returning the original symbol.
7592
old_entry = (CTcSymEnum *)
7593
G_prs->get_global_symtab()->find(sym, strlen(sym));
7594
if (old_entry != 0 && old_entry->get_type() == TC_SYM_ENUM)
7596
/* if this is a 'token' enum, mark the old entry as such */
7598
old_entry->set_is_token(TRUE);
7600
/* return the original entry */
7604
/* create and return the new symbol */
7605
return new CTcSymEnum(sym, strlen(sym), FALSE,
7606
G_prs->new_enum_id(), is_token);
7610
* Write to an object file
7612
int CTcSymEnumBase::write_to_obj_file(CVmFile *fp)
7617
* If the enumerator has never been referenced, don't bother writing
7618
* it. We must have picked up the definition from an external
7619
* symbol set we loaded but have no references of our own to the
7625
/* inherit default */
7626
CTcSymbol::write_to_obj_file(fp);
7629
* write my local enumerator ID value - when we load the object file,
7630
* we'll need to figure out the translation from our original
7631
* numbering system to the new numbering system used in the image
7634
fp->write_int4((ulong)enum_id_);
7636
/* clear the flags */
7639
/* set the 'token' flag if appropriate */
7643
/* write the flags */
7644
fp->write_bytes(buf, 1);
7651
/* ------------------------------------------------------------------------ */
7653
* Built-in function symbol base
7657
* Write to a object file
7659
int CTcSymBifBase::write_to_obj_file(CVmFile *fp)
7663
/* inherit default */
7664
CTcSymbol::write_to_obj_file(fp);
7666
/* write the varargs and return value flags */
7667
buf[0] = (varargs_ != 0);
7668
buf[1] = (has_retval_ != 0);
7670
/* write the argument count information */
7671
oswp2(buf+2, min_argc_);
7672
oswp2(buf+4, max_argc_);
7675
* write the function set ID and index - these are required to match
7676
* those used in all other object files that make up a single image
7679
oswp2(buf+6, func_set_id_);
7680
oswp2(buf+8, func_idx_);
7681
fp->write_bytes(buf, 10);
7687
/* ------------------------------------------------------------------------ */
7689
* Parser dictionary hash table entry
7693
* add an item to my list of object associations
7695
void CVmHashEntryPrsDict::add_item(tc_obj_id obj, tc_prop_id prop)
7697
CTcPrsDictItem *item;
7699
/* search my list for an existing association to the same obj/prop */
7700
for (item = list_ ; item != 0 ; item = item->nxt_)
7702
/* if it matches, we don't need to add this one again */
7703
if (item->obj_ == obj && item->prop_ == prop)
7707
/* not found - create a new item */
7708
item = new (G_prsmem) CTcPrsDictItem(obj, prop);
7710
/* link it into my list */
7715
/* ------------------------------------------------------------------------ */
7717
* Dictionary entry - each dictionary object gets one of these objects
7724
CTcDictEntry::CTcDictEntry(CTcSymObj *sym)
7726
const size_t hash_table_size = 128;
7728
/* remember my object symbol and word truncation length */
7731
/* no object file index yet */
7734
/* not in a list yet */
7737
/* create my hash table */
7738
hashtab_ = new (G_prsmem)
7739
CVmHashTable(hash_table_size,
7740
new (G_prsmem) CVmHashFuncCI(), TRUE,
7741
new (G_prsmem) CVmHashEntry *[hash_table_size]);
7745
* Add a word to the table
7747
void CTcDictEntry::add_word(const char *txt, size_t len, int copy,
7748
tc_obj_id obj, tc_prop_id prop)
7750
CVmHashEntryPrsDict *entry;
7752
/* search for an existing entry */
7753
entry = (CVmHashEntryPrsDict *)hashtab_->find(txt, len);
7755
/* if there's no entry, create a new one */
7758
/* create a new item */
7759
entry = new (G_prsmem) CVmHashEntryPrsDict(txt, len, copy);
7761
/* add it to the table */
7762
hashtab_->add(entry);
7765
/* add this object/property association to the word's hash table entry */
7766
entry->add_item(obj, prop);
7770
* Write my symbol to an object file
7772
void CTcDictEntry::write_sym_to_obj_file(CVmFile *fp)
7774
/* if I already have a non-zero index value, I've already been written */
7778
/* assign myself an object file dictionary index */
7779
obj_idx_ = G_prs->get_next_obj_file_dict_idx();
7781
/* write my symbol to the object file */
7782
sym_->write_to_obj_file(fp);
7785
/* ------------------------------------------------------------------------ */
7787
* Grammar production list entry
7789
CTcGramProdEntry::CTcGramProdEntry(CTcSymObj *prod_sym)
7791
/* remember my object symbol */
7792
prod_sym_ = prod_sym;
7794
/* not in a list yet */
7797
/* no alternatives yet */
7798
alt_head_ = alt_tail_ = 0;
7800
/* not explicitly declared yet */
7801
is_declared_ = FALSE;
7805
* Add an alternative
7807
void CTcGramProdEntry::add_alt(CTcGramProdAlt *alt)
7809
/* link it at the end of my list */
7811
alt_tail_->set_next(alt);
7816
/* this is now the last element in our list */
7821
* Move my alternatives to a new owner
7823
void CTcGramProdEntry::move_alts_to(CTcGramProdEntry *new_entry)
7825
CTcGramProdAlt *alt;
7826
CTcGramProdAlt *nxt;
7828
/* move each of my alternatives */
7829
for (alt = alt_head_ ; alt != 0 ; alt = nxt)
7831
/* remember the next alternative, since we're unlinking this one */
7832
nxt = alt->get_next();
7834
/* unlink this one from the list */
7837
/* link this one into the new owner's list */
7838
new_entry->add_alt(alt);
7841
/* there's nothing left in our list */
7842
alt_head_ = alt_tail_ = 0;
7846
* Write to an object file
7848
void CTcGramProdEntry::write_to_obj_file(CVmFile *fp)
7851
CTcGramProdAlt *alt;
7854
/* write the object file index of my production object symbol */
7855
fp->write_int4(prod_sym_ == 0 ? 0 : prod_sym_->get_obj_file_idx());
7857
/* set up the flags */
7862
/* write the flags */
7863
fp->write_int4(flags);
7865
/* count my alternatives */
7866
for (cnt = 0, alt = alt_head_ ; alt != 0 ;
7867
++cnt, alt = alt->get_next()) ;
7869
/* write the count */
7870
fp->write_int4(cnt);
7872
/* write each alternative */
7873
for (alt = alt_head_ ; alt != 0 ; alt = alt->get_next())
7874
alt->write_to_obj_file(fp);
7877
/* ------------------------------------------------------------------------ */
7879
* Grammar production alternative
7881
CTcGramProdAlt::CTcGramProdAlt(CTcSymObj *obj_sym, CTcDictEntry *dict)
7883
/* remember the associated processor object */
7886
/* remember the default dictionary currently in effect */
7889
/* nothing in our token list yet */
7890
tok_head_ = tok_tail_ = 0;
7892
/* we don't have a score or badness yet */
7896
/* we're not in a list yet */
7900
void CTcGramProdAlt::add_tok(CTcGramProdTok *tok)
7902
/* link the token at the end of my list */
7904
tok_tail_->set_next(tok);
7909
/* there's nothing after this token */
7914
* Write to an object file
7916
void CTcGramProdAlt::write_to_obj_file(CVmFile *fp)
7919
CTcGramProdTok *tok;
7921
/* write my score and badness */
7922
fp->write_int2(score_);
7923
fp->write_int2(badness_);
7925
/* write the index of my processor object symbol */
7926
fp->write_int4(obj_sym_ == 0 ? 0 : obj_sym_->get_obj_file_idx());
7928
/* write the dictionary index */
7929
fp->write_int4(dict_ == 0 ? 0 : dict_->get_obj_idx());
7931
/* count my tokens */
7932
for (cnt = 0, tok = tok_head_ ; tok != 0 ;
7933
++cnt, tok = tok->get_next()) ;
7935
/* write my token count */
7936
fp->write_int4(cnt);
7938
/* write the tokens */
7939
for (tok = tok_head_ ; tok != 0 ; tok = tok->get_next())
7940
tok->write_to_obj_file(fp);
7943
/* ------------------------------------------------------------------------ */
7945
* Grammar production token object
7949
* write to an object file
7951
void CTcGramProdTok::write_to_obj_file(CVmFile *fp)
7956
fp->write_int2((int)typ_);
7962
/* write my object's object file index */
7963
fp->write_int4(val_.obj_ != 0
7964
? val_.obj_->get_obj_file_idx() : 0);
7967
case TCGRAM_TOKEN_TYPE:
7968
/* write my enum token ID */
7969
fp->write_int4(val_.enum_id_);
7972
case TCGRAM_PART_OF_SPEECH:
7973
/* write my property ID */
7974
fp->write_int2(val_.prop_);
7977
case TCGRAM_LITERAL:
7978
/* write my string */
7979
fp->write_int2(val_.str_.len_);
7980
fp->write_bytes(val_.str_.txt_, val_.str_.len_);
7984
/* no additional value data */
7987
case TCGRAM_PART_OF_SPEECH_LIST:
7988
/* write the length */
7989
fp->write_int2(val_.prop_list_.len_);
7991
/* write each element */
7992
for (i = 0 ; i < val_.prop_list_.len_ ; ++i)
7993
fp->write_int2(val_.prop_list_.arr_[i]);
7998
case TCGRAM_UNKNOWN:
7999
/* no value - there's nothing extra to write */
8003
/* write my property association */
8004
fp->write_int2(prop_assoc_);
8008
* Initialize with a part-of-speech list
8010
void CTcGramProdTok::set_match_part_list()
8012
const size_t init_alo = 10;
8014
/* remember the type */
8015
typ_ = TCGRAM_PART_OF_SPEECH_LIST;
8017
/* we have nothing in the list yet */
8018
val_.prop_list_.len_ = 0;
8020
/* set the initial allocation size */
8021
val_.prop_list_.alo_ = init_alo;
8023
/* allocate the initial list */
8024
val_.prop_list_.arr_ = (tctarg_prop_id_t *)G_prsmem->alloc(
8025
init_alo * sizeof(val_.prop_list_.arr_[0]));
8029
* Add a property to our part-of-speech match list
8031
void CTcGramProdTok::add_match_part_ele(tctarg_prop_id_t prop)
8033
/* if necessary, re-allocate the array at a larger size */
8034
if (val_.prop_list_.len_ == val_.prop_list_.alo_)
8036
tctarg_prop_id_t *oldp;
8038
/* bump up the size a bit */
8039
val_.prop_list_.alo_ += 10;
8041
/* remember the current list long enough to copy it */
8042
oldp = val_.prop_list_.arr_;
8045
val_.prop_list_.arr_ = (tctarg_prop_id_t *)G_prsmem->alloc(
8046
val_.prop_list_.alo_ * sizeof(val_.prop_list_.arr_[0]));
8048
/* copy the old list into the new one */
8049
memcpy(val_.prop_list_.arr_, oldp,
8050
val_.prop_list_.len_ * sizeof(val_.prop_list_.arr_[0]));
8054
* we now know we have space for the new element, so add it, bumping up
8055
* the length counter to account for the addition
8057
val_.prop_list_.arr_[val_.prop_list_.len_++] = prop;
8060
/* ------------------------------------------------------------------------ */
8062
* Code Body Parse Node
8068
CTPNCodeBodyBase::CTPNCodeBodyBase(CTcPrsSymtab *lcltab,
8069
CTcPrsSymtab *gototab, CTPNStm *stm,
8070
int argc, int varargs,
8072
CTcSymLocal *varargs_list_local,
8073
int local_cnt, int self_valid,
8074
CTcCodeBodyRef *enclosing_code_body)
8076
/* remember the data in the code body */
8082
varargs_list_ = varargs_list;
8083
varargs_list_local_ = varargs_list_local;
8084
local_cnt_ = local_cnt;
8085
self_valid_ = self_valid;
8086
self_referenced_ = FALSE;
8087
full_method_ctx_referenced_ = FALSE;
8089
/* remember the enclosing code body */
8090
enclosing_code_body_ = enclosing_code_body;
8092
/* presume we won't need a local context object */
8093
has_local_ctx_ = FALSE;
8094
local_ctx_arr_size_ = 0;
8095
ctx_head_ = ctx_tail_ = 0;
8096
local_ctx_needs_self_ = FALSE;
8097
local_ctx_needs_full_method_ctx_ = FALSE;
8099
/* presume we have an internal fixup list */
8100
fixup_owner_sym_ = 0;
8101
fixup_list_anchor_ = &internal_fixups_;
8103
/* no internal fixups yet */
8104
internal_fixups_ = 0;
8106
/* we haven't been replaced yet */
8110
* remember the source location of the closing brace, which should
8111
* be the current location when we're instantiated
8113
end_desc_ = G_tok->get_last_desc();
8114
end_linenum_ = G_tok->get_last_linenum();
8121
CTcPrsNode *CTPNCodeBodyBase::fold_constants(class CTcPrsSymtab *)
8124
* fold constants in our compound statement, in the scope of our
8125
* local symbol table
8128
stm_->fold_constants(lcltab_);
8130
/* we are not directly changed by this operation */
8135
* Check for unreferenced labels
8137
void CTPNCodeBodyBase::check_unreferenced_labels()
8140
* enumerate our labels - skip this check if we're only parsing the
8141
* program for syntax
8143
if (gototab_ != 0 && !G_prs->get_syntax_only())
8144
gototab_->enum_entries(&unref_label_cb, this);
8148
* Callback for enumerating labels for checking for unreferenced labels
8150
void CTPNCodeBodyBase::unref_label_cb(void *, CTcSymbol *sym)
8152
/* if it's a label, check it out */
8153
if (sym->get_type() == TC_SYM_LABEL)
8155
CTcSymLabel *lbl = (CTcSymLabel *)sym;
8158
* get its underlying statement, and make sure it has a
8159
* control-flow flag for goto, continue, or break
8161
if (lbl->get_stm() != 0)
8166
* get the explicit control flow flags for this statement --
8167
* these flags indicate the use of the label in a goto,
8168
* break, or continue statement
8170
flags = lbl->get_stm()->get_explicit_control_flow_flags();
8173
* if the flags aren't set for at least one of the explicit
8174
* label uses, the label is unreferenced
8176
if ((flags & (TCPRS_FLOW_GOTO | TCPRS_FLOW_BREAK
8177
| TCPRS_FLOW_CONT)) == 0)
8178
lbl->get_stm()->log_warning(TCERR_UNREFERENCED_LABEL,
8179
(int)lbl->get_sym_len(),
8186
* add an absolute fixup to my list
8188
void CTPNCodeBodyBase::add_abs_fixup(CTcDataStream *ds, ulong ofs)
8190
/* ask the code body to add the fixup */
8191
CTcAbsFixup::add_abs_fixup(fixup_list_anchor_, ds, ofs);
8195
* add an absolute fixup at the current stream offset
8197
void CTPNCodeBodyBase::add_abs_fixup(CTcDataStream *ds)
8199
/* ask the code body to add the fixup */
8200
CTcAbsFixup::add_abs_fixup(fixup_list_anchor_, ds, ds->get_ofs());
8204
* Get the context variable for a given level
8206
int CTPNCodeBodyBase::get_or_add_ctx_var_for_level(int level)
8208
CTcCodeBodyCtx *ctx;
8210
/* scan our list to see if the level is already assigned */
8211
for (ctx = ctx_head_ ; ctx != 0 ; ctx = ctx->nxt_)
8213
/* if we've already set up this level, return its variable */
8214
if (ctx->level_ == level)
8215
return ctx->var_num_;
8218
/* we didn't find it - allocate a new level structure */
8219
ctx = new (G_prsmem) CTcCodeBodyCtx();
8221
/* set up its level and allocate a new variable and property for it */
8222
ctx->level_ = level;
8223
ctx->var_num_ = G_prs->alloc_ctx_holder_var();
8226
* allocating a new variable probably increased the maximum local
8227
* variable count - update our information from the parser
8229
local_cnt_ = G_prs->get_max_local_cnt();
8231
/* link it into our list */
8232
ctx->prv_ = ctx_tail_;
8235
ctx_tail_->nxt_ = ctx;
8240
/* return the variable for the new level */
8241
return ctx->var_num_;
8245
* Find a local context for a given level
8247
int CTPNCodeBodyBase::get_ctx_var_for_level(int level, int *varnum)
8249
CTcCodeBodyCtx *ctx;
8251
/* if they want level zero, it's our local context */
8254
/* set the variable ID to our local context variable */
8255
*varnum = local_ctx_var_;
8257
/* return true only if we actually have a local context */
8258
return has_local_ctx_;
8261
/* scan our list to see if the level is already assigned */
8262
for (ctx = ctx_head_ ; ctx != 0 ; ctx = ctx->nxt_)
8264
/* if we've already set up this level, return its variable */
8265
if (ctx->level_ == level)
8267
/* set the caller's variable number */
8268
*varnum = ctx->var_num_;
8270
/* indicate that we found it */
8275
/* didn't find it */
8280
* Get the immediately enclosing code body
8282
CTPNCodeBody *CTPNCodeBodyBase::get_enclosing() const
8285
* if we have no enclosing code body reference, we have no enclosing
8288
if (enclosing_code_body_ == 0)
8291
/* get the code body from my enclosing code body reference object */
8292
return enclosing_code_body_->ptr;
8296
* Get the outermost enclosing code body
8298
CTPNCodeBody *CTPNCodeBodyBase::get_outermost_enclosing() const
8304
* scan each enclosing code body until we find one without an enclosing
8307
for (cur = 0, nxt = get_enclosing() ; nxt != 0 ;
8308
cur = nxt, nxt = nxt->get_enclosing()) ;
8310
/* return what we found */
8315
* Get the base function symbol for a code body defining a modified
8316
* function (i.e., 'modify <funcname>...'). This is the function to which
8317
* 'replaced' refers within this code body and within nested code bodies.
8319
class CTcSymFunc *CTPNCodeBodyBase::get_replaced_func() const
8324
/* if we have an associated function symbol, it's the base function */
8325
if ((b = get_func_sym()) != 0)
8326
return b->get_mod_base();
8329
* if we have an enclosing code body, then 'replaced' here means the
8330
* same thing it does there, since we don't explicitly replace anything
8333
if ((enc = get_enclosing()) != 0)
8334
return enc->get_replaced_func();
8336
/* if we haven't found anything yet, we don't have a base function */
8340
/* ------------------------------------------------------------------------ */
8342
* Generic statement node
8346
* initialize at the tokenizer's current source file position
8348
CTPNStmBase::CTPNStmBase()
8350
/* get the current source location from the parser */
8351
init(G_prs->get_cur_desc(), G_prs->get_cur_linenum());
8355
* log an error at this statement's source file position
8357
void CTPNStmBase::log_error(int errnum, ...) const
8361
/* display the message */
8362
va_start(marker, errnum);
8363
G_tcmain->v_log_error(file_, linenum_, TC_SEV_ERROR, errnum, marker);
8368
* log a warning at this statement's source file position
8370
void CTPNStmBase::log_warning(int errnum, ...) const
8374
/* display the message */
8375
va_start(marker, errnum);
8376
G_tcmain->v_log_error(file_, linenum_, TC_SEV_WARNING, errnum, marker);
8381
* Generate code for a sub-statement
8383
void CTPNStmBase::gen_code_substm(CTPNStm *substm)
8385
/* set the error reporting location to refer to the sub-statement */
8386
G_tok->set_line_info(substm->get_source_desc(),
8387
substm->get_source_linenum());
8389
/* generate code for the sub-statement */
8390
substm->gen_code(TRUE, TRUE);
8392
/* restore the error reporting location to the main statement */
8393
G_tok->set_line_info(get_source_desc(), get_source_linenum());
8396
/* ------------------------------------------------------------------------ */
8398
* Object Definition Statement
8404
CTcPrsNode *CTPNStmObjectBase::fold_constants(CTcPrsSymtab *symtab)
8408
/* fold constants in each of our property list entries */
8409
for (prop = first_prop_ ; prop != 0 ; prop = prop->nxt_)
8410
prop->fold_constants(symtab);
8412
/* we're not changed directly by this */
8416
/* ------------------------------------------------------------------------ */
8424
CTcSymbol *CTPNSuperclass::get_sym() const
8426
/* if we know the symbol already, return it directly */
8430
/* look up my symbol by name in the global symbol table */
8431
return G_prs->get_global_symtab()->find(sym_txt_, sym_len_);
8435
* am I a subclass of the given class?
8437
int CTPNSuperclass::is_subclass_of(const CTPNSuperclass *other) const
8443
* if my name matches, we're a subclass (we are a subclass of
8446
if (other->sym_len_ == sym_len_
8447
&& memcmp(other->sym_txt_, sym_txt_, sym_len_) == 0)
8451
* We're a subclass if any of our superclasses are subclasses of the
8452
* given object. Get my object symbol, and make sure it's really a
8453
* tads-object - if it's not, we're definitely not a subclass of
8456
sym = (CTcSymObj *)get_sym();
8458
|| sym->get_type() != TC_SYM_OBJ
8459
|| sym->get_metaclass() != TC_META_TADSOBJ)
8462
/* scan our symbol's superclass list for a match */
8463
for (sc = sym->get_sc_name_head() ; sc != 0 ; sc = sc->nxt_)
8466
* if this one's a subclass of the given class, we're a subclass
8467
* as well, since we're a subclass of this superclass
8469
if (sc->is_subclass_of(other))
8474
* we didn't find any superclass that's a subclass of the given
8475
* class, so we're not a subclass of the given class
8481
/* ------------------------------------------------------------------------ */
8483
* 'return' statement
8489
CTcPrsNode *CTPNStmReturnBase::fold_constants(CTcPrsSymtab *symtab)
8491
/* set our location for any errors that occur */
8492
G_tok->set_line_info(get_source_desc(), get_source_linenum());
8494
/* fold constants in the expression, if we have one */
8496
expr_ = expr_->fold_constants(symtab);
8498
/* we are not directly changed by this operation */
8502
/* ------------------------------------------------------------------------ */
8508
* add a typed parameter to the list - 'tok' is the symbol giving the type
8511
void CTcFormalTypeList::add_typed_param(const CTcToken *tok)
8513
add(new (G_prsmem) CTcFormalTypeEle(tok->get_text(), tok->get_text_len()));
8516
/* add an untyped parameter to the list */
8517
void CTcFormalTypeList::add_untyped_param()
8519
add(new (G_prsmem) CTcFormalTypeEle());
8522
/* add a list element */
8523
void CTcFormalTypeList::add(CTcFormalTypeEle *ele)
8525
/* link it into our list */
8535
* create a decorated name token for the multi-method defined by the given
8536
* function name and our type list
8538
void CTcFormalTypeList::decorate_name(CTcToken *decorated_name,
8539
const CTcToken *func_base_name)
8541
CTcFormalTypeEle *ele;
8545
/* figure out how much space we need for the decorated name */
8546
for (len = func_base_name->get_text_len() + 1, ele = head_ ;
8547
ele != 0 ; ele = ele->nxt_)
8549
/* add this type name's length, if there's a name */
8550
if (ele->name_ != 0)
8551
len += ele->name_len_;
8553
/* add a semicolon after the type */
8557
/* add "...;" if it's varargs */
8561
/* allocate space for the name */
8562
G_tok->reserve_source(len);
8564
/* start with the function name */
8565
p = G_tok->store_source_partial(func_base_name->get_text(),
8566
func_base_name->get_text_len());
8568
/* add a "*" separator for the multi-method indicator */
8569
G_tok->store_source_partial("*", 1);
8571
/* add each type name */
8572
for (ele = head_ ; ele != 0 ; ele = ele->nxt_)
8574
/* add the type, if it has one (if not, leave the type empty) */
8575
if (ele->name_ != 0)
8576
G_tok->store_source_partial(ele->name_, ele->name_len_);
8578
/* add a semicolon to terminate the parameter name */
8579
G_tok->store_source_partial(";", 1);
8582
/* add the varargs indicator ("...;"), if applicable */
8584
G_tok->store_source_partial("...;", 4);
8586
/* null-terminate it */
8587
G_tok->store_source_partial("\0", 1);
8589
/* set the decorated token name */
8590
decorated_name->settyp(TOKT_SYM);
8591
decorated_name->set_text(p, len);
8594
/* formal list element - construction */
8595
CTcFormalTypeEle::CTcFormalTypeEle(const char *name, size_t len)
8597
name_ = new (G_prsmem) char[len + 1];
8598
memcpy(name_, name, len);