| | 1 | #ifdef RCSID |
| | 2 | static char RCSid[] = |
| | 3 | "$Header: d:/cvsroot/tads/tads3/TCPRS.CPP,v 1.5 1999/07/11 00:46:53 MJRoberts Exp $"; |
| | 4 | #endif |
| | 5 | |
| | 6 | /* |
| | 7 | * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. |
| | 8 | * |
| | 9 | * Please see the accompanying license file, LICENSE.TXT, for information |
| | 10 | * on using and copying this software. |
| | 11 | */ |
| | 12 | /* |
| | 13 | Name |
| | 14 | tcprs.cpp - TADS 3 Compiler Parser |
| | 15 | Function |
| | 16 | This parser module contains code required for any parser usage, both |
| | 17 | for a full compiler and for a debugger. |
| | 18 | Notes |
| | 19 | |
| | 20 | Modified |
| | 21 | 04/30/99 MJRoberts - Creation |
| | 22 | */ |
| | 23 | |
| | 24 | #include <stdlib.h> |
| | 25 | #include <string.h> |
| | 26 | #include <stdio.h> |
| | 27 | #include <assert.h> |
| | 28 | |
| | 29 | #include "os.h" |
| | 30 | #include "t3std.h" |
| | 31 | #include "tcprs.h" |
| | 32 | #include "tctarg.h" |
| | 33 | #include "tcgen.h" |
| | 34 | #include "vmhash.h" |
| | 35 | #include "tcmain.h" |
| | 36 | #include "vmfile.h" |
| | 37 | #include "tctok.h" |
| | 38 | |
| | 39 | |
| | 40 | /* ------------------------------------------------------------------------ */ |
| | 41 | /* |
| | 42 | * Expression Operator parser objects. These objects are linked |
| | 43 | * together in a network that defines the order of precedence for |
| | 44 | * expression operators. |
| | 45 | * |
| | 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. |
| | 50 | */ |
| | 51 | |
| | 52 | /* unary operator parser */ |
| | 53 | static CTcPrsOpUnary S_op_unary; |
| | 54 | |
| | 55 | /* factor group */ |
| | 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, |
| | 62 | S_grp_factor); |
| | 63 | |
| | 64 | /* term group */ |
| | 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, |
| | 69 | S_grp_term); |
| | 70 | |
| | 71 | /* shift group */ |
| | 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, |
| | 76 | S_grp_shift); |
| | 77 | |
| | 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, |
| | 86 | S_grp_relcomp); |
| | 87 | |
| | 88 | /* |
| | 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 |
| | 95 | * simple.) |
| | 96 | * |
| | 97 | * Note also that this is a special binary group - this one recognizes the |
| | 98 | * non-keyword operators 'is in' and 'not in'. |
| | 99 | */ |
| | 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); |
| | 106 | |
| | 107 | /* bitwise AND operator */ |
| | 108 | static const CTcPrsOpBAnd S_op_band(&S_op_eqcomp, &S_op_eqcomp); |
| | 109 | |
| | 110 | /* bitwise XOR operator */ |
| | 111 | static const CTcPrsOpBXor S_op_bxor(&S_op_band, &S_op_band); |
| | 112 | |
| | 113 | /* bitwise OR operator */ |
| | 114 | static const CTcPrsOpBOr S_op_bor(&S_op_bxor, &S_op_bxor); |
| | 115 | |
| | 116 | /* logical AND operator */ |
| | 117 | static const CTcPrsOpAnd S_op_and(&S_op_bor, &S_op_bor); |
| | 118 | |
| | 119 | /* logical OR operator */ |
| | 120 | static const CTcPrsOpOr S_op_or(&S_op_and, &S_op_and); |
| | 121 | |
| | 122 | /* conditional operator */ |
| | 123 | static const CTcPrsOpIf S_op_if; |
| | 124 | |
| | 125 | /* |
| | 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 |
| | 128 | * traditional mode |
| | 129 | */ |
| | 130 | static CTcPrsOpAsi S_op_asi; |
| | 131 | |
| | 132 | /* comma operator */ |
| | 133 | static const CTcPrsOpComma S_op_comma(&S_op_asi, &S_op_asi); |
| | 134 | |
| | 135 | |
| | 136 | /* ------------------------------------------------------------------------ */ |
| | 137 | /* |
| | 138 | * Main Parser |
| | 139 | */ |
| | 140 | |
| | 141 | /* |
| | 142 | * initialize the parser |
| | 143 | */ |
| | 144 | CTcParser::CTcParser() |
| | 145 | { |
| | 146 | size_t i; |
| | 147 | |
| | 148 | /* we don't have any module information yet */ |
| | 149 | module_name_ = 0; |
| | 150 | module_seqno_ = 0; |
| | 151 | |
| | 152 | /* start out in normal mode */ |
| | 153 | pp_expr_mode_ = FALSE; |
| | 154 | src_group_mode_ = FALSE; |
| | 155 | |
| | 156 | /* create the global symbol table */ |
| | 157 | global_symtab_ = new CTcPrsSymtab(0); |
| | 158 | |
| | 159 | /* no enclosing statement yet */ |
| | 160 | enclosing_stm_ = 0; |
| | 161 | |
| | 162 | /* no source location yet */ |
| | 163 | cur_desc_ = 0; |
| | 164 | cur_linenum_ = 0; |
| | 165 | |
| | 166 | /* no dictionaries yet */ |
| | 167 | dict_cur_ = 0; |
| | 168 | dict_head_ = dict_tail_ = 0; |
| | 169 | |
| | 170 | /* no object file dictionary list yet */ |
| | 171 | obj_dict_list_ = 0; |
| | 172 | obj_file_dict_idx_ = 0; |
| | 173 | |
| | 174 | /* no dictionary properties yet */ |
| | 175 | dict_prop_head_ = 0; |
| | 176 | |
| | 177 | /* no grammar productions yet */ |
| | 178 | gramprod_head_ = gramprod_tail_ = 0; |
| | 179 | |
| | 180 | /* no object file symbol list yet */ |
| | 181 | obj_sym_list_ = 0; |
| | 182 | obj_file_sym_idx_ = 0; |
| | 183 | |
| | 184 | /* no object templates yet */ |
| | 185 | template_head_ = template_tail_ = 0; |
| | 186 | |
| | 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_); |
| | 191 | |
| | 192 | /* no locals yet */ |
| | 193 | local_cnt_ = max_local_cnt_ = 0; |
| | 194 | |
| | 195 | /* no local or goto symbol table yet */ |
| | 196 | local_symtab_ = 0; |
| | 197 | enclosing_local_symtab_ = 0; |
| | 198 | goto_symtab_ = 0; |
| | 199 | |
| | 200 | /* no debugger local symbol table yet */ |
| | 201 | debug_symtab_ = 0; |
| | 202 | |
| | 203 | /* not in a preprocessor constant expression */ |
| | 204 | pp_expr_mode_ = FALSE; |
| | 205 | |
| | 206 | /* assume we're doing full compilation */ |
| | 207 | syntax_only_ = FALSE; |
| | 208 | |
| | 209 | /* no nested top-level statements yet */ |
| | 210 | nested_stm_head_ = 0; |
| | 211 | nested_stm_tail_ = 0; |
| | 212 | |
| | 213 | /* no anonymous objects yet */ |
| | 214 | anon_obj_head_ = 0; |
| | 215 | anon_obj_tail_ = 0; |
| | 216 | |
| | 217 | /* no non-symbol objects yet */ |
| | 218 | nonsym_obj_head_ = 0; |
| | 219 | nonsym_obj_tail_ = 0; |
| | 220 | |
| | 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)); |
| | 225 | |
| | 226 | /* no context variable properties assigned yet */ |
| | 227 | ctx_var_props_cnt_ = 0; |
| | 228 | ctx_var_props_used_ = 0; |
| | 229 | |
| | 230 | /* |
| | 231 | * no context variable indices assigned yet - start at one higher |
| | 232 | * than the index at which we always store 'self' |
| | 233 | */ |
| | 234 | next_ctx_arr_idx_ = TCPRS_LOCAL_CTX_METHODCTX + 1; |
| | 235 | |
| | 236 | /* 'self' isn't valid yet */ |
| | 237 | self_valid_ = FALSE; |
| | 238 | |
| | 239 | /* start at enum ID 1 (let 0 serve as an invalid value) */ |
| | 240 | next_enum_id_ = 1; |
| | 241 | |
| | 242 | /* the '+' property is not yet defined */ |
| | 243 | plus_prop_ = 0; |
| | 244 | |
| | 245 | /* no exported symbols yet */ |
| | 246 | exp_head_ = exp_tail_ = 0; |
| | 247 | |
| | 248 | /* allocate an initial '+' stack */ |
| | 249 | plus_stack_alloc_ = 32; |
| | 250 | plus_stack_ = (CTPNStmObject **) |
| | 251 | t3malloc(plus_stack_alloc_ * sizeof(*plus_stack_)); |
| | 252 | |
| | 253 | /* clear out the stack */ |
| | 254 | for (i = 0 ; i < plus_stack_alloc_ ; ++i) |
| | 255 | plus_stack_[i] = 0; |
| | 256 | |
| | 257 | /* there's no current code body (function/method body) yet */ |
| | 258 | cur_code_body_ = 0; |
| | 259 | |
| | 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; |
| | 265 | } |
| | 266 | |
| | 267 | /* |
| | 268 | * Initialize. This must be called after the code generator is set up. |
| | 269 | */ |
| | 270 | void CTcParser::init() |
| | 271 | { |
| | 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; |
| | 288 | CTcSymProp *sym; |
| | 289 | |
| | 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(); |
| | 301 | |
| | 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; |
| | 308 | |
| | 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); |
| | 314 | |
| | 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); |
| | 320 | |
| | 321 | /* add a "grammarInfo" property for grammar production match objects */ |
| | 322 | graminfo_prop_ = new CTcSymProp(graminfo_name, sizeof(graminfo_name) - 1, |
| | 323 | FALSE, |
| | 324 | (tctarg_prop_id_t)graminfo_prop_id); |
| | 325 | graminfo_prop_->mark_referenced(); |
| | 326 | global_symtab_->add_entry(graminfo_prop_); |
| | 327 | |
| | 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); |
| | 333 | |
| | 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, |
| | 337 | FALSE, |
| | 338 | (tctarg_prop_id_t)lexpar_prop_id); |
| | 339 | lexical_parent_sym_->mark_referenced(); |
| | 340 | global_symtab_->add_entry(lexical_parent_sym_); |
| | 341 | |
| | 342 | /* add a "sourceTextOrder" property */ |
| | 343 | src_order_sym_ = new CTcSymProp(src_order_name, |
| | 344 | sizeof(src_order_name) - 1, |
| | 345 | FALSE, |
| | 346 | (tctarg_prop_id_t)src_order_prop_id); |
| | 347 | src_order_sym_->mark_referenced(); |
| | 348 | global_symtab_->add_entry(src_order_sym_); |
| | 349 | |
| | 350 | /* start the sourceTextOrder index at 1 */ |
| | 351 | src_order_idx_ = 1; |
| | 352 | |
| | 353 | /* add a "sourceTextGroup" property */ |
| | 354 | src_group_sym_ = new CTcSymProp(src_group_name, |
| | 355 | sizeof(src_group_name) - 1, |
| | 356 | FALSE, |
| | 357 | (tctarg_prop_id_t)src_group_prop_id); |
| | 358 | src_group_sym_->mark_referenced(); |
| | 359 | global_symtab_->add_entry(src_group_sym_); |
| | 360 | |
| | 361 | /* we haven't created the sourceTextGroup referral object yet */ |
| | 362 | src_group_id_ = TCTARG_INVALID_OBJ; |
| | 363 | |
| | 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_); |
| | 370 | |
| | 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_); |
| | 377 | } |
| | 378 | |
| | 379 | |
| | 380 | /* |
| | 381 | * destroy the parser |
| | 382 | */ |
| | 383 | CTcParser::~CTcParser() |
| | 384 | { |
| | 385 | /* |
| | 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 |
| | 390 | * symbol table. |
| | 391 | */ |
| | 392 | |
| | 393 | /* delete the module name, if it's known */ |
| | 394 | lib_free_str(module_name_); |
| | 395 | |
| | 396 | /* delete the object file symbol fixup list, if present */ |
| | 397 | if (obj_sym_list_ != 0) |
| | 398 | t3free(obj_sym_list_); |
| | 399 | |
| | 400 | /* delete the object file dictionary fixup list, if present */ |
| | 401 | if (obj_dict_list_ != 0) |
| | 402 | t3free(obj_dict_list_); |
| | 403 | |
| | 404 | /* delete the context variable property list */ |
| | 405 | if (ctx_var_props_ != 0) |
| | 406 | t3free(ctx_var_props_); |
| | 407 | |
| | 408 | /* delete the export list */ |
| | 409 | while (exp_head_ != 0) |
| | 410 | { |
| | 411 | CTcPrsExport *nxt; |
| | 412 | |
| | 413 | /* remember the next entry, since we're deleting our pointer to it */ |
| | 414 | nxt = exp_head_->get_next(); |
| | 415 | |
| | 416 | /* delete this entry */ |
| | 417 | delete exp_head_; |
| | 418 | |
| | 419 | /* move on to the next */ |
| | 420 | exp_head_ = nxt; |
| | 421 | } |
| | 422 | |
| | 423 | /* delete the '+' stack */ |
| | 424 | t3free(plus_stack_); |
| | 425 | } |
| | 426 | |
| | 427 | /* ------------------------------------------------------------------------ */ |
| | 428 | /* |
| | 429 | * Set the module information |
| | 430 | */ |
| | 431 | void CTcParser::set_module_info(const char *name, int seqno) |
| | 432 | { |
| | 433 | /* if we have a name stored already, delete the old one */ |
| | 434 | lib_free_str(module_name_); |
| | 435 | |
| | 436 | /* store the new name and sequence number */ |
| | 437 | module_name_ = lib_copy_str(name); |
| | 438 | module_seqno_ = seqno; |
| | 439 | } |
| | 440 | |
| | 441 | /* |
| | 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. |
| | 445 | * |
| | 446 | * #pragma C+: assignment is '=', equality is '==' |
| | 447 | *. #pragma C-: assignment is ':=', equality is '='. |
| | 448 | */ |
| | 449 | void CTcParser::set_pragma_c(int mode) |
| | 450 | { |
| | 451 | /* set the assignment operator */ |
| | 452 | S_op_asi.set_asi_op(mode ? TOKT_EQ : TOKT_ASI); |
| | 453 | |
| | 454 | /* set the equality comparison operator */ |
| | 455 | S_op_eq.set_eq_op(mode ? TOKT_EQEQ : TOKT_EQ); |
| | 456 | } |
| | 457 | |
| | 458 | /* |
| | 459 | * Parse an expression. This parses a top-level comma expression. |
| | 460 | */ |
| | 461 | CTcPrsNode *CTcParser::parse_expr() |
| | 462 | { |
| | 463 | /* parse a comma expression */ |
| | 464 | return S_op_comma.parse(); |
| | 465 | } |
| | 466 | |
| | 467 | /* |
| | 468 | * Parse a condition expression. Warns if the outermost operator is a |
| | 469 | * simple assignment. |
| | 470 | */ |
| | 471 | CTcPrsNode *CTcParser::parse_cond_expr() |
| | 472 | { |
| | 473 | CTcPrsNode *cond; |
| | 474 | |
| | 475 | /* parse the expression */ |
| | 476 | cond = parse_expr(); |
| | 477 | |
| | 478 | /* |
| | 479 | * if the outermost operator is a simple assignment, display an |
| | 480 | * error |
| | 481 | */ |
| | 482 | if (cond != 0 && cond->is_simple_asi() && !G_prs->get_syntax_only()) |
| | 483 | G_tok->log_warning(TCERR_ASI_IN_COND); |
| | 484 | |
| | 485 | /* return the result */ |
| | 486 | return cond; |
| | 487 | } |
| | 488 | |
| | 489 | /* |
| | 490 | * Parse an assignment expression. |
| | 491 | */ |
| | 492 | CTcPrsNode *CTcParser::parse_asi_expr() |
| | 493 | { |
| | 494 | /* parse an assignment expression */ |
| | 495 | return S_op_asi.parse(); |
| | 496 | } |
| | 497 | |
| | 498 | /* |
| | 499 | * Parse an expression or a double-quoted string expression |
| | 500 | */ |
| | 501 | CTcPrsNode *CTcParser::parse_expr_or_dstr(int allow_comma_expr) |
| | 502 | { |
| | 503 | /* |
| | 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) |
| | 507 | */ |
| | 508 | return (allow_comma_expr ? S_op_comma.parse() : S_op_asi.parse()); |
| | 509 | } |
| | 510 | |
| | 511 | /* |
| | 512 | * Parse a required semicolon |
| | 513 | */ |
| | 514 | int CTcParser::parse_req_sem() |
| | 515 | { |
| | 516 | const char eof_str[] = "<end of file>"; |
| | 517 | |
| | 518 | /* check to see if we found the semicolon */ |
| | 519 | if (G_tok->cur() == TOKT_SEM) |
| | 520 | { |
| | 521 | /* success - skip the semicolon and tell the caller to proceed */ |
| | 522 | G_tok->next(); |
| | 523 | return 0; |
| | 524 | } |
| | 525 | |
| | 526 | /* |
| | 527 | * check what we have; the type of error we want to log depends on |
| | 528 | * what we find next |
| | 529 | */ |
| | 530 | switch(G_tok->cur()) |
| | 531 | { |
| | 532 | case TOKT_RPAR: |
| | 533 | /* log the extra ')' error */ |
| | 534 | G_tok->log_error(TCERR_EXTRA_RPAR); |
| | 535 | |
| | 536 | /* |
| | 537 | * we're probably in an expression that ended before the user |
| | 538 | * thought it should; skip the extraneous material up to the |
| | 539 | * next semicolon |
| | 540 | */ |
| | 541 | return skip_to_sem(); |
| | 542 | |
| | 543 | case TOKT_RBRACK: |
| | 544 | /* log the error */ |
| | 545 | G_tok->log_error(TCERR_EXTRA_RBRACK); |
| | 546 | |
| | 547 | /* skip up to the next semicolon */ |
| | 548 | return skip_to_sem(); |
| | 549 | |
| | 550 | case TOKT_EOF: |
| | 551 | /* |
| | 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 |
| | 555 | */ |
| | 556 | G_tok->log_error(TCERR_EXPECTED_SEMI, |
| | 557 | (int)sizeof(eof_str)-1, eof_str); |
| | 558 | return 1; |
| | 559 | |
| | 560 | default: |
| | 561 | /* |
| | 562 | * the source is probably just missing a semicolon; log the |
| | 563 | * error, and tell the caller to proceed from the current |
| | 564 | * position |
| | 565 | */ |
| | 566 | G_tok->log_error_curtok(TCERR_EXPECTED_SEMI); |
| | 567 | return 0; |
| | 568 | } |
| | 569 | } |
| | 570 | |
| | 571 | /* |
| | 572 | * Skip to the next semicolon |
| | 573 | */ |
| | 574 | int CTcParser::skip_to_sem() |
| | 575 | { |
| | 576 | /* keep going until we find a semicolon or some other reason to stop */ |
| | 577 | for (;;) |
| | 578 | { |
| | 579 | /* see what we have next */ |
| | 580 | switch(G_tok->cur()) |
| | 581 | { |
| | 582 | case TOKT_EOF: |
| | 583 | /* end of file - tell the caller not to proceed */ |
| | 584 | return 1; |
| | 585 | |
| | 586 | case TOKT_SEM: |
| | 587 | /* |
| | 588 | * it's the semicolon at last - skip it and tell the caller |
| | 589 | * to proceed |
| | 590 | */ |
| | 591 | G_tok->next(); |
| | 592 | return 0; |
| | 593 | |
| | 594 | case TOKT_LBRACE: |
| | 595 | case TOKT_RBRACE: |
| | 596 | /* |
| | 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. |
| | 601 | */ |
| | 602 | return 0; |
| | 603 | |
| | 604 | default: |
| | 605 | /* skip anything else */ |
| | 606 | G_tok->next(); |
| | 607 | break; |
| | 608 | } |
| | 609 | } |
| | 610 | } |
| | 611 | |
| | 612 | /* |
| | 613 | * Create a symbol node |
| | 614 | */ |
| | 615 | CTcPrsNode *CTcParser::create_sym_node(const textchar_t *sym, size_t sym_len) |
| | 616 | { |
| | 617 | CTcSymbol *entry; |
| | 618 | CTcPrsSymtab *symtab; |
| | 619 | |
| | 620 | /* |
| | 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 |
| | 624 | * use. |
| | 625 | */ |
| | 626 | entry = local_symtab_->find(sym, sym_len, &symtab); |
| | 627 | |
| | 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); |
| | 631 | |
| | 632 | /* if there's a debugger local scope, look it up there */ |
| | 633 | if (debug_symtab_ != 0) |
| | 634 | { |
| | 635 | tcprsdbg_sym_info info; |
| | 636 | |
| | 637 | /* look it up in the debug symbol table */ |
| | 638 | if (debug_symtab_->find_symbol(sym, sym_len, &info)) |
| | 639 | { |
| | 640 | /* found it - return a debugger local variable */ |
| | 641 | return new CTPNSymDebugLocal(&info); |
| | 642 | } |
| | 643 | } |
| | 644 | |
| | 645 | /* |
| | 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 |
| | 649 | * 'self'. |
| | 650 | */ |
| | 651 | set_self_referenced(TRUE); |
| | 652 | return new CTPNSym(sym, sym_len); |
| | 653 | } |
| | 654 | |
| | 655 | /* |
| | 656 | * Find or add a dictionary symbol |
| | 657 | */ |
| | 658 | CTcDictEntry *CTcParser::declare_dict(const char *txt, size_t len) |
| | 659 | { |
| | 660 | CTcSymObj *sym; |
| | 661 | CTcDictEntry *entry = 0; |
| | 662 | |
| | 663 | /* look up the symbol */ |
| | 664 | sym = (CTcSymObj *)global_symtab_->find(txt, len); |
| | 665 | |
| | 666 | /* if it's not defined, add it; otherwise, make sure it's correct */ |
| | 667 | if (sym == 0) |
| | 668 | { |
| | 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, |
| | 673 | TC_META_DICT, 0); |
| | 674 | |
| | 675 | /* add it to the global symbol table */ |
| | 676 | global_symtab_->add_entry(sym); |
| | 677 | |
| | 678 | /* create a new dictionary entry */ |
| | 679 | entry = create_dict_entry(sym); |
| | 680 | } |
| | 681 | else |
| | 682 | { |
| | 683 | /* make sure it's an object of metaclass 'dictionary' */ |
| | 684 | if (sym->get_type() != TC_SYM_OBJ) |
| | 685 | { |
| | 686 | /* log an error */ |
| | 687 | G_tok->log_error_curtok(TCERR_REDEF_AS_OBJ); |
| | 688 | |
| | 689 | /* forget the symbol - it's not even an object */ |
| | 690 | sym = 0; |
| | 691 | } |
| | 692 | else if (sym->get_metaclass() != TC_META_DICT) |
| | 693 | { |
| | 694 | /* it's an object, but not a dictionary - log an error */ |
| | 695 | G_tok->log_error_curtok(TCERR_REDEF_AS_DICT); |
| | 696 | |
| | 697 | /* forget the symbol */ |
| | 698 | sym = 0; |
| | 699 | } |
| | 700 | |
| | 701 | /* find it in our dictionary list */ |
| | 702 | entry = get_dict_entry(sym); |
| | 703 | |
| | 704 | /* |
| | 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 |
| | 707 | */ |
| | 708 | if (entry == 0) |
| | 709 | entry = create_dict_entry(sym); |
| | 710 | } |
| | 711 | |
| | 712 | /* return the new dictionary object */ |
| | 713 | return entry; |
| | 714 | } |
| | 715 | |
| | 716 | /* |
| | 717 | * Find or add a grammar production symbol. Returns the master |
| | 718 | * CTcGramProdEntry object for the grammar production. |
| | 719 | */ |
| | 720 | CTcGramProdEntry *CTcParser::declare_gramprod(const char *txt, size_t len) |
| | 721 | { |
| | 722 | CTcSymObj *sym; |
| | 723 | CTcGramProdEntry *entry; |
| | 724 | |
| | 725 | /* find or define the grammar production object symbol */ |
| | 726 | sym = find_or_def_gramprod(txt, len, &entry); |
| | 727 | |
| | 728 | /* return the entry */ |
| | 729 | return entry; |
| | 730 | } |
| | 731 | |
| | 732 | /* |
| | 733 | * Find or add a grammar production symbol. |
| | 734 | */ |
| | 735 | CTcSymObj *CTcParser::find_or_def_gramprod(const char *txt, size_t len, |
| | 736 | CTcGramProdEntry **entryp) |
| | 737 | { |
| | 738 | CTcSymObj *sym; |
| | 739 | CTcGramProdEntry *entry; |
| | 740 | |
| | 741 | /* look up the symbol */ |
| | 742 | sym = (CTcSymObj *)global_symtab_->find(txt, len); |
| | 743 | |
| | 744 | /* if it's not defined, add it; otherwise, make sure it's correct */ |
| | 745 | if (sym == 0) |
| | 746 | { |
| | 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); |
| | 752 | |
| | 753 | /* add it to the global symbol table */ |
| | 754 | global_symtab_->add_entry(sym); |
| | 755 | |
| | 756 | /* create a new production list entry */ |
| | 757 | entry = create_gramprod_entry(sym); |
| | 758 | } |
| | 759 | else |
| | 760 | { |
| | 761 | /* make sure it's an object of metaclass 'grammar production' */ |
| | 762 | if (sym->get_type() != TC_SYM_OBJ) |
| | 763 | { |
| | 764 | /* log an error */ |
| | 765 | G_tok->log_error_curtok(TCERR_REDEF_AS_OBJ); |
| | 766 | |
| | 767 | /* forget the symbol - it's not even an object */ |
| | 768 | sym = 0; |
| | 769 | } |
| | 770 | else if (sym->get_metaclass() != TC_META_GRAMPROD) |
| | 771 | { |
| | 772 | /* it's an object, but not a production - log an error */ |
| | 773 | G_tok->log_error_curtok(TCERR_REDEF_AS_GRAMPROD); |
| | 774 | |
| | 775 | /* forget the symbol */ |
| | 776 | sym = 0; |
| | 777 | } |
| | 778 | |
| | 779 | /* |
| | 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. |
| | 784 | */ |
| | 785 | if (sym != 0) |
| | 786 | sym->set_extern(FALSE); |
| | 787 | |
| | 788 | /* get the existing production object, if any */ |
| | 789 | entry = get_gramprod_entry(sym); |
| | 790 | |
| | 791 | /* |
| | 792 | * if we didn't find the entry, we must have loaded the symbol |
| | 793 | * from a symbol file - add the entry now |
| | 794 | */ |
| | 795 | if (entry == 0) |
| | 796 | entry = create_gramprod_entry(sym); |
| | 797 | } |
| | 798 | |
| | 799 | /* |
| | 800 | * if the caller is interested in knowing the associated grammar rule |
| | 801 | * list entry, return it |
| | 802 | */ |
| | 803 | if (entryp != 0) |
| | 804 | *entryp = entry; |
| | 805 | |
| | 806 | /* return the new symbol */ |
| | 807 | return sym; |
| | 808 | } |
| | 809 | |
| | 810 | /* |
| | 811 | * Add a symbol loaded from an object file |
| | 812 | */ |
| | 813 | void CTcParser::add_sym_from_obj_file(uint idx, class CTcSymbol *sym) |
| | 814 | { |
| | 815 | /* |
| | 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 |
| | 818 | */ |
| | 819 | obj_sym_list_[idx - 1] = sym; |
| | 820 | } |
| | 821 | |
| | 822 | /* |
| | 823 | * Get an object file symbol, ensuring that it's an object symbol |
| | 824 | */ |
| | 825 | CTcSymObj *CTcParser::get_objfile_objsym(uint idx) |
| | 826 | { |
| | 827 | CTcSymObj *sym; |
| | 828 | |
| | 829 | /* get the object based on the index */ |
| | 830 | sym = (CTcSymObj *)get_objfile_sym(idx); |
| | 831 | |
| | 832 | /* make sure it's an object - if it isn't, return null */ |
| | 833 | if (sym == 0 || sym->get_type() != TC_SYM_OBJ) |
| | 834 | return 0; |
| | 835 | |
| | 836 | /* it checks out - return it */ |
| | 837 | return sym; |
| | 838 | } |
| | 839 | |
| | 840 | |
| | 841 | /* |
| | 842 | * Add a dictionary symbol loaded from an object file |
| | 843 | */ |
| | 844 | void CTcParser::add_dict_from_obj_file(CTcSymObj *sym) |
| | 845 | { |
| | 846 | CTcDictEntry *entry; |
| | 847 | |
| | 848 | /* get the current entry, if any */ |
| | 849 | entry = get_dict_entry(sym); |
| | 850 | |
| | 851 | /* if there's no current entry, create a new one */ |
| | 852 | if (entry == 0) |
| | 853 | { |
| | 854 | /* create the entry */ |
| | 855 | entry = create_dict_entry(sym); |
| | 856 | } |
| | 857 | |
| | 858 | /* add the entry to the object file index list */ |
| | 859 | obj_dict_list_[obj_file_dict_idx_++] = entry; |
| | 860 | } |
| | 861 | |
| | 862 | /* |
| | 863 | * create a new dictionary list entry |
| | 864 | */ |
| | 865 | CTcDictEntry *CTcParser::create_dict_entry(CTcSymObj *sym) |
| | 866 | { |
| | 867 | CTcDictEntry *entry; |
| | 868 | |
| | 869 | /* allocate the new entry */ |
| | 870 | entry = new (G_prsmem) CTcDictEntry(sym); |
| | 871 | |
| | 872 | /* link the new entry into our list */ |
| | 873 | if (dict_tail_ != 0) |
| | 874 | dict_tail_->set_next(entry); |
| | 875 | else |
| | 876 | dict_head_ = entry; |
| | 877 | dict_tail_ = entry; |
| | 878 | |
| | 879 | /* |
| | 880 | * set the metaclass-specific extra data in the object symbol to |
| | 881 | * point to the dictionary list entry |
| | 882 | */ |
| | 883 | sym->set_meta_extra(entry); |
| | 884 | |
| | 885 | /* return the new entry */ |
| | 886 | return entry; |
| | 887 | } |
| | 888 | |
| | 889 | /* |
| | 890 | * find a dictionary list entry for a given object symbol |
| | 891 | */ |
| | 892 | CTcDictEntry *CTcParser::get_dict_entry(CTcSymObj *obj) |
| | 893 | { |
| | 894 | /* the dictionary list entry is the metaclass-specific data pointer */ |
| | 895 | return (obj == 0 ? 0 : (CTcDictEntry *)obj->get_meta_extra()); |
| | 896 | } |
| | 897 | |
| | 898 | /* |
| | 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. |
| | 903 | */ |
| | 904 | CTcGramProdEntry *CTcParser::create_gramprod_entry(CTcSymObj *sym) |
| | 905 | { |
| | 906 | CTcGramProdEntry *entry; |
| | 907 | |
| | 908 | /* allocate the new entry */ |
| | 909 | entry = new (G_prsmem) CTcGramProdEntry(sym); |
| | 910 | |
| | 911 | /* link the new entry into our list of anonymous match entries */ |
| | 912 | if (gramprod_tail_ != 0) |
| | 913 | gramprod_tail_->set_next(entry); |
| | 914 | else |
| | 915 | gramprod_head_ = entry; |
| | 916 | gramprod_tail_ = entry; |
| | 917 | |
| | 918 | /* |
| | 919 | * set the metaclass-specific data in the object symbol to point to |
| | 920 | * the grammar production list entry |
| | 921 | */ |
| | 922 | if (sym != 0) |
| | 923 | sym->set_meta_extra(entry); |
| | 924 | |
| | 925 | /* return the new entry */ |
| | 926 | return entry; |
| | 927 | } |
| | 928 | |
| | 929 | /* |
| | 930 | * find a grammar entry for a given (GrammarProd) object symbol |
| | 931 | */ |
| | 932 | CTcGramProdEntry *CTcParser::get_gramprod_entry(CTcSymObj *obj) |
| | 933 | { |
| | 934 | /* the grammar entry is the metaclass-specific data pointer */ |
| | 935 | return (obj == 0 ? 0 : (CTcGramProdEntry *)obj->get_meta_extra()); |
| | 936 | } |
| | 937 | |
| | 938 | /* |
| | 939 | * Add a nested top-level statement to our list |
| | 940 | */ |
| | 941 | void CTcParser::add_nested_stm(CTPNStmTop *stm) |
| | 942 | { |
| | 943 | /* link it into our list */ |
| | 944 | if (nested_stm_tail_ != 0) |
| | 945 | nested_stm_tail_->set_next_stm_top(stm); |
| | 946 | else |
| | 947 | nested_stm_head_ = stm; |
| | 948 | nested_stm_tail_ = stm; |
| | 949 | } |
| | 950 | |
| | 951 | /* |
| | 952 | * Add an anonymous object to our list |
| | 953 | */ |
| | 954 | void CTcParser::add_anon_obj(CTcSymObj *sym) |
| | 955 | { |
| | 956 | /* link it into our list */ |
| | 957 | if (anon_obj_tail_ != 0) |
| | 958 | anon_obj_tail_->nxt_ = sym; |
| | 959 | else |
| | 960 | anon_obj_head_ = sym; |
| | 961 | anon_obj_tail_ = sym; |
| | 962 | |
| | 963 | /* it's the last one */ |
| | 964 | sym->nxt_ = 0; |
| | 965 | |
| | 966 | /* mark the symbol as anonymous */ |
| | 967 | sym->set_anon(TRUE); |
| | 968 | } |
| | 969 | |
| | 970 | /* |
| | 971 | * Add a non-symbolic object to our list |
| | 972 | */ |
| | 973 | void CTcParser::add_nonsym_obj(tctarg_obj_id_t id) |
| | 974 | { |
| | 975 | tcprs_nonsym_obj *obj; |
| | 976 | |
| | 977 | /* allocate a link structure */ |
| | 978 | obj = new (G_prsmem) tcprs_nonsym_obj(id); |
| | 979 | |
| | 980 | /* link it into our list */ |
| | 981 | if (nonsym_obj_tail_ != 0) |
| | 982 | nonsym_obj_tail_->nxt_ = obj; |
| | 983 | else |
| | 984 | nonsym_obj_head_ = obj; |
| | 985 | nonsym_obj_tail_ = obj; |
| | 986 | } |
| | 987 | |
| | 988 | /* |
| | 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. |
| | 996 | * |
| | 997 | */ |
| | 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) |
| | 1001 | { |
| | 1002 | size_t read_len; |
| | 1003 | size_t alloc_len; |
| | 1004 | |
| | 1005 | /* read the length to read from the file */ |
| | 1006 | read_len = (size_t)fp->read_uint2(); |
| | 1007 | |
| | 1008 | /* if we need null termination, add a byte to the allocation length */ |
| | 1009 | alloc_len = read_len + (ret_len == 0 ? 1 : 0); |
| | 1010 | |
| | 1011 | /* if it won't fit in the temporary buffer, it's an error */ |
| | 1012 | if (alloc_len > tmp_buf_len) |
| | 1013 | { |
| | 1014 | /* log the error and return failure */ |
| | 1015 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, err_if_too_long); |
| | 1016 | return 0; |
| | 1017 | } |
| | 1018 | |
| | 1019 | /* read the bytes into the temporary buffer */ |
| | 1020 | fp->read_bytes(tmp_buf, read_len); |
| | 1021 | |
| | 1022 | /* add null termination if required, or set the return length if not */ |
| | 1023 | if (ret_len == 0) |
| | 1024 | tmp_buf[read_len] = '\0'; |
| | 1025 | else |
| | 1026 | *ret_len = read_len; |
| | 1027 | |
| | 1028 | /* store the result in the tokenizer's text list and return the result */ |
| | 1029 | return G_tok->store_source(tmp_buf, alloc_len); |
| | 1030 | } |
| | 1031 | |
| | 1032 | /* |
| | 1033 | * Read a length prefixed string into a given buffer. Returns zero on |
| | 1034 | * success, non-zero on failure. |
| | 1035 | */ |
| | 1036 | int CTcParser::read_len_prefix_str(CVmFile *fp, char *buf, size_t buf_len, |
| | 1037 | int err_if_too_long) |
| | 1038 | { |
| | 1039 | size_t read_len; |
| | 1040 | size_t alloc_len; |
| | 1041 | |
| | 1042 | /* read the length to read from the file */ |
| | 1043 | read_len = (size_t)fp->read_uint2(); |
| | 1044 | |
| | 1045 | /* add a byte for null termination */ |
| | 1046 | alloc_len = read_len + 1; |
| | 1047 | |
| | 1048 | /* if it won't fit in the temporary buffer, it's an error */ |
| | 1049 | if (alloc_len > buf_len) |
| | 1050 | { |
| | 1051 | /* log the error and return failure */ |
| | 1052 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, err_if_too_long); |
| | 1053 | return 1; |
| | 1054 | } |
| | 1055 | |
| | 1056 | /* read the bytes into the caller's buffer */ |
| | 1057 | fp->read_bytes(buf, read_len); |
| | 1058 | |
| | 1059 | /* add null termination */ |
| | 1060 | buf[read_len] = '\0'; |
| | 1061 | |
| | 1062 | /* success */ |
| | 1063 | return 0; |
| | 1064 | } |
| | 1065 | |
| | 1066 | /* ------------------------------------------------------------------------ */ |
| | 1067 | /* |
| | 1068 | * Constant Value |
| | 1069 | */ |
| | 1070 | |
| | 1071 | /* |
| | 1072 | * set a string value |
| | 1073 | */ |
| | 1074 | void CTcConstVal::set_sstr(const char *val, size_t len) |
| | 1075 | { |
| | 1076 | /* store the type */ |
| | 1077 | typ_ = TC_CVT_SSTR; |
| | 1078 | |
| | 1079 | /* store a pointer to the string */ |
| | 1080 | val_.strval_.strval_ = val; |
| | 1081 | val_.strval_.strval_len_ = len; |
| | 1082 | |
| | 1083 | /* for image file layout purposes, record the length of this string */ |
| | 1084 | G_cg->note_str(len); |
| | 1085 | } |
| | 1086 | |
| | 1087 | /* |
| | 1088 | * set a list value |
| | 1089 | */ |
| | 1090 | void CTcConstVal::set_list(CTPNList *lst) |
| | 1091 | { |
| | 1092 | /* set the type */ |
| | 1093 | typ_ = TC_CVT_LIST; |
| | 1094 | |
| | 1095 | /* remember the list */ |
| | 1096 | val_.listval_ = lst; |
| | 1097 | |
| | 1098 | /* for image file layout purposes, record the length of this list */ |
| | 1099 | G_cg->note_list(lst->get_count()); |
| | 1100 | } |
| | 1101 | |
| | 1102 | /* |
| | 1103 | * Convert a value to a string |
| | 1104 | */ |
| | 1105 | const char *CTcConstVal::cvt_to_str(char *buf, size_t bufl, |
| | 1106 | size_t *result_len) |
| | 1107 | { |
| | 1108 | /* check my type */ |
| | 1109 | switch(typ_) |
| | 1110 | { |
| | 1111 | case TC_CVT_NIL: |
| | 1112 | /* the result is "nil" */ |
| | 1113 | if (bufl < 4) |
| | 1114 | return 0; |
| | 1115 | |
| | 1116 | strcpy(buf, "nil"); |
| | 1117 | *result_len = 3; |
| | 1118 | return buf; |
| | 1119 | |
| | 1120 | case TC_CVT_TRUE: |
| | 1121 | /* the result is "true" */ |
| | 1122 | if (bufl < 5) |
| | 1123 | return 0; |
| | 1124 | |
| | 1125 | strcpy(buf, "true"); |
| | 1126 | *result_len = 4; |
| | 1127 | return buf; |
| | 1128 | |
| | 1129 | case TC_CVT_SSTR: |
| | 1130 | /* it's already a string */ |
| | 1131 | *result_len = get_val_str_len(); |
| | 1132 | return get_val_str(); |
| | 1133 | |
| | 1134 | case TC_CVT_INT: |
| | 1135 | /* convert our signed integer value */ |
| | 1136 | if (bufl < 12) |
| | 1137 | return 0; |
| | 1138 | |
| | 1139 | sprintf(buf, "%ld", get_val_int()); |
| | 1140 | *result_len = strlen(buf); |
| | 1141 | return buf; |
| | 1142 | |
| | 1143 | case TC_CVT_FLOAT: |
| | 1144 | /* we store these as strings */ |
| | 1145 | *result_len = get_val_float_len(); |
| | 1146 | return get_val_float(); |
| | 1147 | |
| | 1148 | default: |
| | 1149 | /* can't convert other types */ |
| | 1150 | return 0; |
| | 1151 | } |
| | 1152 | } |
| | 1153 | |
| | 1154 | /* |
| | 1155 | * Compare for equality to another constant value |
| | 1156 | */ |
| | 1157 | int CTcConstVal::is_equal_to(const CTcConstVal *val) const |
| | 1158 | { |
| | 1159 | CTPNListEle *ele1; |
| | 1160 | CTPNListEle *ele2; |
| | 1161 | |
| | 1162 | /* |
| | 1163 | * if the types are not equal, the values are not equal; otherwise, |
| | 1164 | * check the various types |
| | 1165 | */ |
| | 1166 | if (typ_ != val->get_type()) |
| | 1167 | { |
| | 1168 | /* the types aren't equal, so the values are not equal */ |
| | 1169 | return FALSE; |
| | 1170 | } |
| | 1171 | |
| | 1172 | /* the types are the same; do the comparison based on the type */ |
| | 1173 | switch(typ_) |
| | 1174 | { |
| | 1175 | case TC_CVT_UNK: |
| | 1176 | /* unknown type; unknown values can never be equal */ |
| | 1177 | return FALSE; |
| | 1178 | |
| | 1179 | case TC_CVT_TRUE: |
| | 1180 | case TC_CVT_NIL: |
| | 1181 | /* |
| | 1182 | * nil==nil and true==true; since we know the types are the |
| | 1183 | * same, the values are the same |
| | 1184 | */ |
| | 1185 | return TRUE; |
| | 1186 | |
| | 1187 | case TC_CVT_INT: |
| | 1188 | /* compare the integers */ |
| | 1189 | return (get_val_int() == val->get_val_int()); |
| | 1190 | |
| | 1191 | case TC_CVT_SSTR: |
| | 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); |
| | 1196 | |
| | 1197 | case TC_CVT_LIST: |
| | 1198 | /* |
| | 1199 | * if the lists don't have the same number of elements, they're |
| | 1200 | * not equal |
| | 1201 | */ |
| | 1202 | if (get_val_list()->get_count() != val->get_val_list()->get_count()) |
| | 1203 | return FALSE; |
| | 1204 | |
| | 1205 | /* |
| | 1206 | * compare each element of each list; if they're all the same, |
| | 1207 | * the values are the same |
| | 1208 | */ |
| | 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()) |
| | 1213 | { |
| | 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())) |
| | 1217 | return FALSE; |
| | 1218 | } |
| | 1219 | |
| | 1220 | /* we didn't find any differences, so the lists are equal */ |
| | 1221 | return TRUE; |
| | 1222 | |
| | 1223 | case TC_CVT_OBJ: |
| | 1224 | /* if the object values are the same, the values match */ |
| | 1225 | return (get_val_obj() == val->get_val_obj()); |
| | 1226 | |
| | 1227 | case TC_CVT_PROP: |
| | 1228 | /* if the property values are the same, the values match */ |
| | 1229 | return (get_val_prop() == val->get_val_prop()); |
| | 1230 | |
| | 1231 | case TC_CVT_FUNCPTR: |
| | 1232 | /* |
| | 1233 | * if both symbols are the same, the values match; otherwise, |
| | 1234 | * they refer to different functions |
| | 1235 | */ |
| | 1236 | return (get_val_funcptr_sym() == val->get_val_funcptr_sym()); |
| | 1237 | |
| | 1238 | default: |
| | 1239 | /* unknown type; return unequal */ |
| | 1240 | return FALSE; |
| | 1241 | } |
| | 1242 | } |
| | 1243 | |
| | 1244 | |
| | 1245 | /* ------------------------------------------------------------------------ */ |
| | 1246 | /* |
| | 1247 | * Operator Parsers |
| | 1248 | */ |
| | 1249 | |
| | 1250 | /* ------------------------------------------------------------------------ */ |
| | 1251 | /* |
| | 1252 | * Parse a left-associative binary operator |
| | 1253 | */ |
| | 1254 | CTcPrsNode *CTcPrsOpBin::parse() const |
| | 1255 | { |
| | 1256 | CTcPrsNode *lhs; |
| | 1257 | CTcPrsNode *rhs; |
| | 1258 | |
| | 1259 | /* parse our left side - if that fails, return failure */ |
| | 1260 | if ((lhs = left_->parse()) == 0) |
| | 1261 | return 0; |
| | 1262 | |
| | 1263 | /* keep going as long as we find our operator */ |
| | 1264 | for (;;) |
| | 1265 | { |
| | 1266 | /* check my operator */ |
| | 1267 | if (G_tok->cur() == get_op_tok()) |
| | 1268 | { |
| | 1269 | CTcPrsNode *const_tree; |
| | 1270 | |
| | 1271 | /* skip the matching token */ |
| | 1272 | G_tok->next(); |
| | 1273 | |
| | 1274 | /* parse the right-hand side */ |
| | 1275 | if ((rhs = right_->parse()) == 0) |
| | 1276 | return 0; |
| | 1277 | |
| | 1278 | /* try folding our subnodes into a constant value, if possible */ |
| | 1279 | const_tree = eval_constant(lhs, rhs); |
| | 1280 | |
| | 1281 | /* |
| | 1282 | * if we couldn't calculate a constant value, build the tree |
| | 1283 | * normally |
| | 1284 | */ |
| | 1285 | if (const_tree == 0) |
| | 1286 | { |
| | 1287 | /* |
| | 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. |
| | 1292 | */ |
| | 1293 | lhs = build_tree(lhs, rhs); |
| | 1294 | } |
| | 1295 | else |
| | 1296 | { |
| | 1297 | /* we got a constant value - use it as the result directly */ |
| | 1298 | lhs = const_tree; |
| | 1299 | } |
| | 1300 | } |
| | 1301 | else |
| | 1302 | { |
| | 1303 | /* |
| | 1304 | * it's not my operator - return what we thought might have |
| | 1305 | * been our left-hand side |
| | 1306 | */ |
| | 1307 | return lhs; |
| | 1308 | } |
| | 1309 | } |
| | 1310 | } |
| | 1311 | |
| | 1312 | /* ------------------------------------------------------------------------ */ |
| | 1313 | /* |
| | 1314 | * Parse a group of left-associative binary operators at the same |
| | 1315 | * precedence level |
| | 1316 | */ |
| | 1317 | CTcPrsNode *CTcPrsOpBinGroup::parse() const |
| | 1318 | { |
| | 1319 | CTcPrsNode *lhs; |
| | 1320 | |
| | 1321 | /* parse our left side - if that fails, return failure */ |
| | 1322 | if ((lhs = left_->parse()) == 0) |
| | 1323 | return 0; |
| | 1324 | |
| | 1325 | /* keep going as long as we find one of our operators */ |
| | 1326 | while (find_and_apply_op(&lhs)) ; |
| | 1327 | |
| | 1328 | /* return the expression tree */ |
| | 1329 | return lhs; |
| | 1330 | } |
| | 1331 | |
| | 1332 | /* |
| | 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. |
| | 1335 | */ |
| | 1336 | int CTcPrsOpBinGroup::find_and_apply_op(CTcPrsNode **lhs) const |
| | 1337 | { |
| | 1338 | const CTcPrsOpBin *const *op; |
| | 1339 | CTcPrsNode *rhs; |
| | 1340 | |
| | 1341 | /* check each operator at this precedence level */ |
| | 1342 | for (op = ops_ ; *op != 0 ; ++op) |
| | 1343 | { |
| | 1344 | /* check this operator's token */ |
| | 1345 | if (G_tok->cur() == (*op)->get_op_tok()) |
| | 1346 | { |
| | 1347 | CTcPrsNode *const_tree; |
| | 1348 | |
| | 1349 | /* skip the operator token */ |
| | 1350 | G_tok->next(); |
| | 1351 | |
| | 1352 | /* parse the right-hand side */ |
| | 1353 | if ((rhs = right_->parse()) == 0) |
| | 1354 | { |
| | 1355 | /* error - cancel the entire expression */ |
| | 1356 | *lhs = 0; |
| | 1357 | return FALSE; |
| | 1358 | } |
| | 1359 | |
| | 1360 | /* try folding our subnodes into a constant value */ |
| | 1361 | const_tree = (*op)->eval_constant(*lhs, rhs); |
| | 1362 | |
| | 1363 | /* |
| | 1364 | * if we couldn't calculate a constant value, build the tree |
| | 1365 | * normally |
| | 1366 | */ |
| | 1367 | if (const_tree == 0) |
| | 1368 | { |
| | 1369 | /* |
| | 1370 | * build my tree, replacing the original left-hand side |
| | 1371 | * with the new expression |
| | 1372 | */ |
| | 1373 | *lhs = (*op)->build_tree(*lhs, rhs); |
| | 1374 | } |
| | 1375 | else |
| | 1376 | { |
| | 1377 | /* we got a constant value - use it as the result */ |
| | 1378 | *lhs = const_tree; |
| | 1379 | } |
| | 1380 | |
| | 1381 | /* |
| | 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 |
| | 1385 | * new operator. |
| | 1386 | */ |
| | 1387 | return TRUE; |
| | 1388 | } |
| | 1389 | } |
| | 1390 | |
| | 1391 | /* |
| | 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 |
| | 1394 | */ |
| | 1395 | return FALSE; |
| | 1396 | } |
| | 1397 | |
| | 1398 | |
| | 1399 | /* ------------------------------------------------------------------------ */ |
| | 1400 | /* |
| | 1401 | * Comparison operator group |
| | 1402 | */ |
| | 1403 | CTcPrsNode *CTcPrsOpBinGroupCompare::parse() const |
| | 1404 | { |
| | 1405 | CTcPrsNode *lhs; |
| | 1406 | |
| | 1407 | /* parse our left side - if that fails, return failure */ |
| | 1408 | if ((lhs = left_->parse()) == 0) |
| | 1409 | return 0; |
| | 1410 | |
| | 1411 | /* keep going as long as we find one of our operators */ |
| | 1412 | for (;;) |
| | 1413 | { |
| | 1414 | CTPNArglist *rhs; |
| | 1415 | |
| | 1416 | /* |
| | 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 |
| | 1420 | */ |
| | 1421 | if (find_and_apply_op(&lhs)) |
| | 1422 | continue; |
| | 1423 | |
| | 1424 | /* |
| | 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 |
| | 1428 | */ |
| | 1429 | if (G_tok->cur() == TOKT_SYM |
| | 1430 | && G_tok->getcur()->text_matches("is", 2)) |
| | 1431 | { |
| | 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)) |
| | 1435 | { |
| | 1436 | /* scan the expression list */ |
| | 1437 | rhs = parse_inlist(); |
| | 1438 | if (rhs == 0) |
| | 1439 | return 0; |
| | 1440 | |
| | 1441 | /* build the node */ |
| | 1442 | lhs = new CTPNIsIn(lhs, rhs); |
| | 1443 | |
| | 1444 | /* |
| | 1445 | * we've applied the 'is in' operator - go back for |
| | 1446 | * another operator from the comparison group |
| | 1447 | */ |
| | 1448 | continue; |
| | 1449 | } |
| | 1450 | else |
| | 1451 | { |
| | 1452 | /* it's not 'is in' - throw back the token and keep looking */ |
| | 1453 | G_tok->unget(); |
| | 1454 | } |
| | 1455 | } |
| | 1456 | |
| | 1457 | /* |
| | 1458 | * Check for the 'not in' operator |
| | 1459 | */ |
| | 1460 | if (G_tok->cur() == TOKT_SYM |
| | 1461 | && G_tok->getcur()->text_matches("not", 3)) |
| | 1462 | { |
| | 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)) |
| | 1466 | { |
| | 1467 | /* scan the expression list */ |
| | 1468 | rhs = parse_inlist(); |
| | 1469 | if (rhs == 0) |
| | 1470 | return 0; |
| | 1471 | |
| | 1472 | /* build the node */ |
| | 1473 | lhs = new CTPNNotIn(lhs, rhs); |
| | 1474 | |
| | 1475 | /* |
| | 1476 | * we've applied the 'is in' operator - go back for |
| | 1477 | * another operator from the comparison group |
| | 1478 | */ |
| | 1479 | continue; |
| | 1480 | } |
| | 1481 | else |
| | 1482 | { |
| | 1483 | /* it's not 'is in' - throw back the token and keep looking */ |
| | 1484 | G_tok->unget(); |
| | 1485 | } |
| | 1486 | } |
| | 1487 | |
| | 1488 | /* we didn't find any of our operators - we're done */ |
| | 1489 | break; |
| | 1490 | } |
| | 1491 | |
| | 1492 | /* return the expression */ |
| | 1493 | return lhs; |
| | 1494 | } |
| | 1495 | |
| | 1496 | /* |
| | 1497 | * parse the list for the right-hand side of an 'is in' or 'not in' |
| | 1498 | * expression |
| | 1499 | */ |
| | 1500 | CTPNArglist *CTcPrsOpBinGroupCompare::parse_inlist() const |
| | 1501 | { |
| | 1502 | int argc; |
| | 1503 | CTPNArg *arg_head; |
| | 1504 | CTPNArg *arg_tail; |
| | 1505 | |
| | 1506 | /* skip the second keyword token, and check for an open paren */ |
| | 1507 | if (G_tok->next() == TOKT_LPAR) |
| | 1508 | { |
| | 1509 | /* skip the paren */ |
| | 1510 | G_tok->next(); |
| | 1511 | } |
| | 1512 | else |
| | 1513 | { |
| | 1514 | /* |
| | 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 |
| | 1517 | */ |
| | 1518 | G_tok->log_error_curtok(TCERR_IN_REQ_LPAR); |
| | 1519 | } |
| | 1520 | |
| | 1521 | /* keep going until we find the close paren */ |
| | 1522 | for (argc = 0, arg_head = arg_tail = 0 ;; ) |
| | 1523 | { |
| | 1524 | CTcPrsNode *expr; |
| | 1525 | CTPNArg *arg_cur; |
| | 1526 | |
| | 1527 | /* if this is the close paren, we're done */ |
| | 1528 | if (G_tok->cur() == TOKT_RPAR) |
| | 1529 | break; |
| | 1530 | |
| | 1531 | /* parse this expression */ |
| | 1532 | expr = S_op_asi.parse(); |
| | 1533 | if (expr == 0) |
| | 1534 | return 0; |
| | 1535 | |
| | 1536 | /* count the argument */ |
| | 1537 | ++argc; |
| | 1538 | |
| | 1539 | /* create a new argument node */ |
| | 1540 | arg_cur = new CTPNArg(expr); |
| | 1541 | |
| | 1542 | /* |
| | 1543 | * link the new node at the end of our list (this preserves the |
| | 1544 | * order of the original list) |
| | 1545 | */ |
| | 1546 | if (arg_tail != 0) |
| | 1547 | arg_tail->set_next_arg(arg_cur); |
| | 1548 | else |
| | 1549 | arg_head = arg_cur; |
| | 1550 | arg_tail = arg_cur; |
| | 1551 | |
| | 1552 | /* we need to be looking at a comma or right paren */ |
| | 1553 | if (G_tok->cur() == TOKT_RPAR) |
| | 1554 | { |
| | 1555 | /* that's the end of the list */ |
| | 1556 | break; |
| | 1557 | } |
| | 1558 | else if (G_tok->cur() == TOKT_COMMA) |
| | 1559 | { |
| | 1560 | /* skip the comma and parse the next argument */ |
| | 1561 | G_tok->next(); |
| | 1562 | } |
| | 1563 | else |
| | 1564 | { |
| | 1565 | /* |
| | 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 |
| | 1572 | * argument. |
| | 1573 | */ |
| | 1574 | switch(G_tok->cur()) |
| | 1575 | { |
| | 1576 | default: |
| | 1577 | /* log an error */ |
| | 1578 | G_tok->log_error_curtok(TCERR_EXPECTED_IN_COMMA); |
| | 1579 | |
| | 1580 | /* |
| | 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 |
| | 1584 | */ |
| | 1585 | if (G_tok->cur() == TOKT_EOF) |
| | 1586 | return new CTPNArglist(argc, arg_head); |
| | 1587 | break; |
| | 1588 | |
| | 1589 | case TOKT_SEM: |
| | 1590 | case TOKT_LBRACE: |
| | 1591 | case TOKT_RBRACE: |
| | 1592 | case TOKT_DSTR_MID: |
| | 1593 | case TOKT_DSTR_END: |
| | 1594 | /* |
| | 1595 | * we're apparently at the end of the statement; flag |
| | 1596 | * the error as a missing right paren, and return what |
| | 1597 | * we have so far |
| | 1598 | */ |
| | 1599 | G_tok->log_error_curtok(TCERR_EXPECTED_IN_RPAR); |
| | 1600 | return new CTPNArglist(argc, arg_head); |
| | 1601 | } |
| | 1602 | } |
| | 1603 | } |
| | 1604 | |
| | 1605 | /* skip the closing paren */ |
| | 1606 | G_tok->next(); |
| | 1607 | |
| | 1608 | /* create and return the argument list descriptor */ |
| | 1609 | return new CTPNArglist(argc, arg_head); |
| | 1610 | } |
| | 1611 | |
| | 1612 | /* ------------------------------------------------------------------------ */ |
| | 1613 | /* |
| | 1614 | * Comma Operator |
| | 1615 | */ |
| | 1616 | |
| | 1617 | /* |
| | 1618 | * try to evaluate a constant expression |
| | 1619 | */ |
| | 1620 | CTcPrsNode *CTcPrsOpComma::eval_constant(CTcPrsNode *left, |
| | 1621 | CTcPrsNode *right) const |
| | 1622 | { |
| | 1623 | /* |
| | 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 |
| | 1628 | */ |
| | 1629 | if (left->is_const() && right->is_const()) |
| | 1630 | { |
| | 1631 | /* both are constants - simply return the right constant value */ |
| | 1632 | return right; |
| | 1633 | } |
| | 1634 | else |
| | 1635 | { |
| | 1636 | /* |
| | 1637 | * one or the other is non-constant, so we can't fold the |
| | 1638 | * expression - return null to so indicate |
| | 1639 | */ |
| | 1640 | return 0; |
| | 1641 | } |
| | 1642 | } |
| | 1643 | |
| | 1644 | /* |
| | 1645 | * build a subtree for the comma operator |
| | 1646 | */ |
| | 1647 | CTcPrsNode *CTcPrsOpComma::build_tree(CTcPrsNode *left, |
| | 1648 | CTcPrsNode *right) const |
| | 1649 | { |
| | 1650 | return new CTPNComma(left, right); |
| | 1651 | } |
| | 1652 | |
| | 1653 | /* ------------------------------------------------------------------------ */ |
| | 1654 | /* |
| | 1655 | * logical OR operator |
| | 1656 | */ |
| | 1657 | |
| | 1658 | /* |
| | 1659 | * try to evaluate a constant expression |
| | 1660 | */ |
| | 1661 | CTcPrsNode *CTcPrsOpOr::eval_constant(CTcPrsNode *left, |
| | 1662 | CTcPrsNode *right) const |
| | 1663 | { |
| | 1664 | /* check for constants */ |
| | 1665 | if (left->is_const()) |
| | 1666 | { |
| | 1667 | CTcPrsNode *ret; |
| | 1668 | |
| | 1669 | /* |
| | 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 |
| | 1674 | * is 'nil'). |
| | 1675 | * |
| | 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. |
| | 1680 | */ |
| | 1681 | if (left->get_const_val()->get_val_bool()) |
| | 1682 | { |
| | 1683 | /* |
| | 1684 | * the left is true, so the result is always true, and the |
| | 1685 | * right never gets executed |
| | 1686 | */ |
| | 1687 | ret = left; |
| | 1688 | } |
| | 1689 | else |
| | 1690 | { |
| | 1691 | /* the left is nil, so the result is the right value */ |
| | 1692 | ret = right; |
| | 1693 | } |
| | 1694 | |
| | 1695 | /* ensure the result is a boolean value */ |
| | 1696 | if (ret->is_const()) |
| | 1697 | { |
| | 1698 | /* make it a true/nil constant value */ |
| | 1699 | ret->get_const_val() |
| | 1700 | ->set_bool(ret->get_const_val()->get_val_bool()); |
| | 1701 | } |
| | 1702 | else |
| | 1703 | { |
| | 1704 | /* boolean-ize the value at run-time as needed */ |
| | 1705 | ret = new CTPNBoolize(ret); |
| | 1706 | } |
| | 1707 | |
| | 1708 | /* return the result */ |
| | 1709 | return ret; |
| | 1710 | } |
| | 1711 | else |
| | 1712 | { |
| | 1713 | /* |
| | 1714 | * one or the other is non-constant, so we can't fold the |
| | 1715 | * expression - return null to so indicate |
| | 1716 | */ |
| | 1717 | return 0; |
| | 1718 | } |
| | 1719 | } |
| | 1720 | |
| | 1721 | /* |
| | 1722 | * build the subtree |
| | 1723 | */ |
| | 1724 | CTcPrsNode *CTcPrsOpOr::build_tree(CTcPrsNode *left, |
| | 1725 | CTcPrsNode *right) const |
| | 1726 | { |
| | 1727 | return new CTPNOr(left, right); |
| | 1728 | } |
| | 1729 | |
| | 1730 | /* ------------------------------------------------------------------------ */ |
| | 1731 | /* |
| | 1732 | * logical AND operator |
| | 1733 | */ |
| | 1734 | |
| | 1735 | /* |
| | 1736 | * try to evaluate a constant expression |
| | 1737 | */ |
| | 1738 | CTcPrsNode *CTcPrsOpAnd::eval_constant(CTcPrsNode *left, |
| | 1739 | CTcPrsNode *right) const |
| | 1740 | { |
| | 1741 | /* |
| | 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'). |
| | 1746 | * |
| | 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. |
| | 1751 | */ |
| | 1752 | if (left->is_const()) |
| | 1753 | { |
| | 1754 | CTcPrsNode *ret; |
| | 1755 | |
| | 1756 | /* |
| | 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 |
| | 1759 | * the right half. |
| | 1760 | */ |
| | 1761 | if (left->get_const_val()->get_val_bool()) |
| | 1762 | { |
| | 1763 | /* the left side is true - the result is the right side */ |
| | 1764 | ret = right; |
| | 1765 | } |
| | 1766 | else |
| | 1767 | { |
| | 1768 | /* |
| | 1769 | * The left side is nil - the result is nil, and the right |
| | 1770 | * side never gets executed. |
| | 1771 | */ |
| | 1772 | ret = left; |
| | 1773 | } |
| | 1774 | |
| | 1775 | /* ensure the result is a boolean value */ |
| | 1776 | if (ret->is_const()) |
| | 1777 | { |
| | 1778 | /* make it a true/nil constant value */ |
| | 1779 | ret->get_const_val() |
| | 1780 | ->set_bool(ret->get_const_val()->get_val_bool()); |
| | 1781 | } |
| | 1782 | else |
| | 1783 | { |
| | 1784 | /* boolean-ize the value at run-time as needed */ |
| | 1785 | ret = new CTPNBoolize(ret); |
| | 1786 | } |
| | 1787 | |
| | 1788 | /* return the result */ |
| | 1789 | return ret; |
| | 1790 | } |
| | 1791 | else |
| | 1792 | { |
| | 1793 | /* |
| | 1794 | * one or the other is non-constant, so we can't fold the |
| | 1795 | * expression - return null to so indicate |
| | 1796 | */ |
| | 1797 | return 0; |
| | 1798 | } |
| | 1799 | } |
| | 1800 | |
| | 1801 | /* |
| | 1802 | * build the subtree |
| | 1803 | */ |
| | 1804 | CTcPrsNode *CTcPrsOpAnd::build_tree(CTcPrsNode *left, |
| | 1805 | CTcPrsNode *right) const |
| | 1806 | { |
| | 1807 | return new CTPNAnd(left, right); |
| | 1808 | } |
| | 1809 | |
| | 1810 | /* ------------------------------------------------------------------------ */ |
| | 1811 | /* |
| | 1812 | * Generic Comparison Operator parser base class |
| | 1813 | */ |
| | 1814 | |
| | 1815 | /* |
| | 1816 | * evaluate a constant expression |
| | 1817 | */ |
| | 1818 | CTcPrsNode *CTcPrsOpRel::eval_constant(CTcPrsNode *left, |
| | 1819 | CTcPrsNode *right) const |
| | 1820 | { |
| | 1821 | /* check for constants */ |
| | 1822 | if (left->is_const() && right->is_const()) |
| | 1823 | { |
| | 1824 | tc_constval_type_t typ1, typ2; |
| | 1825 | int sense; |
| | 1826 | |
| | 1827 | /* get the types */ |
| | 1828 | typ1 = left->get_const_val()->get_type(); |
| | 1829 | typ2 = right->get_const_val()->get_type(); |
| | 1830 | |
| | 1831 | /* determine what we're comparing */ |
| | 1832 | if (typ1 == TC_CVT_INT && typ2 == TC_CVT_INT) |
| | 1833 | { |
| | 1834 | long val1, val2; |
| | 1835 | |
| | 1836 | /* get the values */ |
| | 1837 | val1 = left->get_const_val()->get_val_int(); |
| | 1838 | val2 = right->get_const_val()->get_val_int(); |
| | 1839 | |
| | 1840 | /* calculate the sense of the integer comparison */ |
| | 1841 | sense = (val1 < val2 ? -1 : val1 == val2 ? 0 : 1); |
| | 1842 | } |
| | 1843 | else if (typ1 == TC_CVT_SSTR && typ2 == TC_CVT_SSTR) |
| | 1844 | { |
| | 1845 | /* compare the string values */ |
| | 1846 | sense = strcmp(left->get_const_val()->get_val_str(), |
| | 1847 | right->get_const_val()->get_val_str()); |
| | 1848 | } |
| | 1849 | else if (typ1 == TC_CVT_FLOAT || typ2 == TC_CVT_FLOAT) |
| | 1850 | { |
| | 1851 | /* we can't compare floats at compile time, but it's legal */ |
| | 1852 | return 0; |
| | 1853 | } |
| | 1854 | else |
| | 1855 | { |
| | 1856 | /* these types are incomparable */ |
| | 1857 | G_tok->log_error(TCERR_CONST_BAD_COMPARE, |
| | 1858 | G_tok->get_op_text(get_op_tok())); |
| | 1859 | return 0; |
| | 1860 | } |
| | 1861 | |
| | 1862 | /* set the result in the left value */ |
| | 1863 | left->get_const_val()->set_bool(get_bool_val(sense)); |
| | 1864 | |
| | 1865 | /* return the updated left value */ |
| | 1866 | return left; |
| | 1867 | } |
| | 1868 | else |
| | 1869 | { |
| | 1870 | /* |
| | 1871 | * one or the other is non-constant, so we can't fold the |
| | 1872 | * expression - return null to so indicate |
| | 1873 | */ |
| | 1874 | return 0; |
| | 1875 | } |
| | 1876 | } |
| | 1877 | |
| | 1878 | |
| | 1879 | /* ------------------------------------------------------------------------ */ |
| | 1880 | /* |
| | 1881 | * greater-than operator |
| | 1882 | */ |
| | 1883 | |
| | 1884 | /* |
| | 1885 | * build the subtree |
| | 1886 | */ |
| | 1887 | CTcPrsNode *CTcPrsOpGt::build_tree(CTcPrsNode *left, |
| | 1888 | CTcPrsNode *right) const |
| | 1889 | { |
| | 1890 | return new CTPNGt(left, right); |
| | 1891 | } |
| | 1892 | |
| | 1893 | /* ------------------------------------------------------------------------ */ |
| | 1894 | /* |
| | 1895 | * less-than operator |
| | 1896 | */ |
| | 1897 | |
| | 1898 | /* |
| | 1899 | * build the subtree |
| | 1900 | */ |
| | 1901 | CTcPrsNode *CTcPrsOpLt::build_tree(CTcPrsNode *left, |
| | 1902 | CTcPrsNode *right) const |
| | 1903 | { |
| | 1904 | return new CTPNLt(left, right); |
| | 1905 | } |
| | 1906 | |
| | 1907 | /* ------------------------------------------------------------------------ */ |
| | 1908 | /* |
| | 1909 | * greater-or-equal operator |
| | 1910 | */ |
| | 1911 | |
| | 1912 | /* |
| | 1913 | * build the subtree |
| | 1914 | */ |
| | 1915 | CTcPrsNode *CTcPrsOpGe::build_tree(CTcPrsNode *left, |
| | 1916 | CTcPrsNode *right) const |
| | 1917 | { |
| | 1918 | return new CTPNGe(left, right); |
| | 1919 | } |
| | 1920 | |
| | 1921 | /* ------------------------------------------------------------------------ */ |
| | 1922 | /* |
| | 1923 | * less-or-equal operator |
| | 1924 | */ |
| | 1925 | |
| | 1926 | /* |
| | 1927 | * build the subtree |
| | 1928 | */ |
| | 1929 | CTcPrsNode *CTcPrsOpLe::build_tree(CTcPrsNode *left, |
| | 1930 | CTcPrsNode *right) const |
| | 1931 | { |
| | 1932 | return new CTPNLe(left, right); |
| | 1933 | } |
| | 1934 | |
| | 1935 | /* ------------------------------------------------------------------------ */ |
| | 1936 | /* |
| | 1937 | * General equality/inequality operators base class |
| | 1938 | */ |
| | 1939 | |
| | 1940 | /* |
| | 1941 | * evaluate a constant expression |
| | 1942 | */ |
| | 1943 | CTcPrsNode *CTcPrsOpEqComp::eval_constant(CTcPrsNode *left, |
| | 1944 | CTcPrsNode *right) const |
| | 1945 | { |
| | 1946 | int ops_equal; |
| | 1947 | |
| | 1948 | /* check for constants */ |
| | 1949 | if (left->is_const() && right->is_const()) |
| | 1950 | { |
| | 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()); |
| | 1954 | |
| | 1955 | /* set the result in the left value */ |
| | 1956 | left->get_const_val()->set_bool(get_bool_val(ops_equal)); |
| | 1957 | |
| | 1958 | /* return the updated left value */ |
| | 1959 | return left; |
| | 1960 | } |
| | 1961 | else if (left->is_addr() && right->is_addr()) |
| | 1962 | { |
| | 1963 | CTcConstVal cval; |
| | 1964 | int comparable; |
| | 1965 | |
| | 1966 | /* |
| | 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 |
| | 1970 | */ |
| | 1971 | ops_equal = ((CTPNAddr *)left) |
| | 1972 | ->is_addr_eq((CTPNAddr *)right, &comparable); |
| | 1973 | |
| | 1974 | /* if they're not comparable, we can't fold this as a constant */ |
| | 1975 | if (!comparable) |
| | 1976 | return 0; |
| | 1977 | |
| | 1978 | /* generate the appropriate boolean result for the comparison */ |
| | 1979 | cval.set_bool(get_bool_val(ops_equal)); |
| | 1980 | |
| | 1981 | /* return a new constant node with the result */ |
| | 1982 | return new CTPNConst(&cval); |
| | 1983 | } |
| | 1984 | else |
| | 1985 | { |
| | 1986 | /* |
| | 1987 | * one or the other is non-constant, so we can't fold the |
| | 1988 | * expression - return null to so indicate |
| | 1989 | */ |
| | 1990 | return 0; |
| | 1991 | } |
| | 1992 | } |
| | 1993 | |
| | 1994 | |
| | 1995 | /* ------------------------------------------------------------------------ */ |
| | 1996 | /* |
| | 1997 | * equality operator |
| | 1998 | */ |
| | 1999 | |
| | 2000 | /* |
| | 2001 | * build the subtree |
| | 2002 | */ |
| | 2003 | CTcPrsNode *CTcPrsOpEq::build_tree(CTcPrsNode *left, |
| | 2004 | CTcPrsNode *right) const |
| | 2005 | { |
| | 2006 | return new CTPNEq(left, right); |
| | 2007 | } |
| | 2008 | |
| | 2009 | /* ------------------------------------------------------------------------ */ |
| | 2010 | /* |
| | 2011 | * inequality operator |
| | 2012 | */ |
| | 2013 | |
| | 2014 | /* |
| | 2015 | * build the subtree |
| | 2016 | */ |
| | 2017 | CTcPrsNode *CTcPrsOpNe::build_tree(CTcPrsNode *left, |
| | 2018 | CTcPrsNode *right) const |
| | 2019 | { |
| | 2020 | return new CTPNNe(left, right); |
| | 2021 | } |
| | 2022 | |
| | 2023 | /* ------------------------------------------------------------------------ */ |
| | 2024 | /* |
| | 2025 | * 'is in' operator |
| | 2026 | */ |
| | 2027 | |
| | 2028 | /* |
| | 2029 | * construct |
| | 2030 | */ |
| | 2031 | CTPNIsInBase::CTPNIsInBase(CTcPrsNode *lhs, class CTPNArglist *rhs) |
| | 2032 | : CTPNBin(lhs, rhs) |
| | 2033 | { |
| | 2034 | /* presume we don't have a constant value */ |
| | 2035 | const_true_ = FALSE; |
| | 2036 | } |
| | 2037 | |
| | 2038 | /* |
| | 2039 | * fold constants |
| | 2040 | */ |
| | 2041 | CTcPrsNode *CTPNIsInBase::fold_binop() |
| | 2042 | { |
| | 2043 | CTPNArglist *lst; |
| | 2044 | CTPNArg *arg; |
| | 2045 | CTPNArg *prv; |
| | 2046 | CTPNArg *nxt; |
| | 2047 | |
| | 2048 | /* if the left-hand side isn't constant, there's nothing to do */ |
| | 2049 | if (!left_->is_const()) |
| | 2050 | return this; |
| | 2051 | |
| | 2052 | /* the right side is always an argument list */ |
| | 2053 | lst = (CTPNArglist *)right_; |
| | 2054 | |
| | 2055 | /* look for the value in the arguments */ |
| | 2056 | for (prv = 0, arg = lst->get_arg_list_head() ; arg != 0 ; arg = nxt) |
| | 2057 | { |
| | 2058 | /* remember the next argument, in case we eliminate this one */ |
| | 2059 | nxt = arg->get_next_arg(); |
| | 2060 | |
| | 2061 | /* check to see if this argument is a constant */ |
| | 2062 | if (arg->is_const()) |
| | 2063 | { |
| | 2064 | /* |
| | 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. |
| | 2068 | */ |
| | 2069 | if (left_->get_const_val()->is_equal_to(arg->get_const_val())) |
| | 2070 | { |
| | 2071 | /* |
| | 2072 | * The values are equal, so the result of the expression |
| | 2073 | * is definitely 'true'. |
| | 2074 | */ |
| | 2075 | const_true_ = TRUE; |
| | 2076 | |
| | 2077 | /* |
| | 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 |
| | 2082 | * argument list. |
| | 2083 | */ |
| | 2084 | nxt = 0; |
| | 2085 | } |
| | 2086 | |
| | 2087 | /* |
| | 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. |
| | 2093 | */ |
| | 2094 | lst->set_argc(lst->get_argc() - 1); |
| | 2095 | if (prv == 0) |
| | 2096 | lst->set_arg_list_head(nxt); |
| | 2097 | else |
| | 2098 | prv->set_next_arg(nxt); |
| | 2099 | } |
| | 2100 | } |
| | 2101 | |
| | 2102 | /* |
| | 2103 | * If the argument list is now completely empty, the result of the |
| | 2104 | * expression is a constant. |
| | 2105 | */ |
| | 2106 | if (lst->get_arg_list_head() == 0) |
| | 2107 | { |
| | 2108 | /* set the left operand's value to our result */ |
| | 2109 | left_->get_const_val()->set_bool(const_true_); |
| | 2110 | |
| | 2111 | /* return the constant value in place of the entire expression */ |
| | 2112 | return left_; |
| | 2113 | } |
| | 2114 | |
| | 2115 | /* we're not a constant, to return myself unchanged */ |
| | 2116 | return this; |
| | 2117 | } |
| | 2118 | |
| | 2119 | /* ------------------------------------------------------------------------ */ |
| | 2120 | /* |
| | 2121 | * 'not in' operator |
| | 2122 | */ |
| | 2123 | |
| | 2124 | /* |
| | 2125 | * construct |
| | 2126 | */ |
| | 2127 | CTPNNotInBase::CTPNNotInBase(CTcPrsNode *lhs, class CTPNArglist *rhs) |
| | 2128 | : CTPNBin(lhs, rhs) |
| | 2129 | { |
| | 2130 | /* presume we don't have a constant value */ |
| | 2131 | const_false_ = FALSE; |
| | 2132 | } |
| | 2133 | |
| | 2134 | /* |
| | 2135 | * fold constants for binary operator |
| | 2136 | */ |
| | 2137 | CTcPrsNode *CTPNNotInBase::fold_binop() |
| | 2138 | { |
| | 2139 | CTPNArglist *lst; |
| | 2140 | CTPNArg *arg; |
| | 2141 | CTPNArg *prv; |
| | 2142 | CTPNArg *nxt; |
| | 2143 | |
| | 2144 | /* if the left-hand side isn't constant, there's nothing to do */ |
| | 2145 | if (!left_->is_const()) |
| | 2146 | return this; |
| | 2147 | |
| | 2148 | /* the right side is always an argument list */ |
| | 2149 | lst = (CTPNArglist *)right_; |
| | 2150 | |
| | 2151 | /* look for the value in the arguments */ |
| | 2152 | for (prv = 0, arg = lst->get_arg_list_head() ; arg != 0 ; arg = nxt) |
| | 2153 | { |
| | 2154 | /* remember the next argument, in case we eliminate this one */ |
| | 2155 | nxt = arg->get_next_arg(); |
| | 2156 | |
| | 2157 | /* check to see if this argument is a constant */ |
| | 2158 | if (arg->is_const()) |
| | 2159 | { |
| | 2160 | /* |
| | 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. |
| | 2164 | */ |
| | 2165 | if (left_->get_const_val()->is_equal_to(arg->get_const_val())) |
| | 2166 | { |
| | 2167 | /* |
| | 2168 | * The values are equal, so the result of the expression |
| | 2169 | * is definitely 'nil'. |
| | 2170 | */ |
| | 2171 | const_false_ = TRUE; |
| | 2172 | |
| | 2173 | /* |
| | 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 |
| | 2178 | * argument list. |
| | 2179 | */ |
| | 2180 | nxt = 0; |
| | 2181 | } |
| | 2182 | |
| | 2183 | /* |
| | 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. |
| | 2189 | */ |
| | 2190 | lst->set_argc(lst->get_argc() - 1); |
| | 2191 | if (prv == 0) |
| | 2192 | lst->set_arg_list_head(nxt); |
| | 2193 | else |
| | 2194 | prv->set_next_arg(nxt); |
| | 2195 | } |
| | 2196 | } |
| | 2197 | |
| | 2198 | /* |
| | 2199 | * If the argument list is now completely empty, the result of the |
| | 2200 | * expression is a constant. |
| | 2201 | */ |
| | 2202 | if (lst->get_arg_list_head() == 0) |
| | 2203 | { |
| | 2204 | /* set the left operand's value to our result */ |
| | 2205 | left_->get_const_val()->set_bool(!const_false_); |
| | 2206 | |
| | 2207 | /* return the constant value in place of the entire expression */ |
| | 2208 | return left_; |
| | 2209 | } |
| | 2210 | |
| | 2211 | /* we're not a constant, to return myself unchanged */ |
| | 2212 | return this; |
| | 2213 | } |
| | 2214 | |
| | 2215 | /* ------------------------------------------------------------------------ */ |
| | 2216 | /* |
| | 2217 | * General arithmetic operator base class |
| | 2218 | */ |
| | 2219 | |
| | 2220 | /* |
| | 2221 | * evaluate constant value |
| | 2222 | */ |
| | 2223 | CTcPrsNode *CTcPrsOpArith::eval_constant(CTcPrsNode *left, |
| | 2224 | CTcPrsNode *right) const |
| | 2225 | { |
| | 2226 | /* check for constants */ |
| | 2227 | if (left->is_const() && right->is_const()) |
| | 2228 | { |
| | 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) |
| | 2232 | { |
| | 2233 | /* can't do it at compile time, but it's legal */ |
| | 2234 | return 0; |
| | 2235 | } |
| | 2236 | else if (left->get_const_val()->get_type() != TC_CVT_INT |
| | 2237 | || right->get_const_val()->get_type() != TC_CVT_INT) |
| | 2238 | { |
| | 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())); |
| | 2242 | return 0; |
| | 2243 | } |
| | 2244 | else |
| | 2245 | { |
| | 2246 | long result; |
| | 2247 | |
| | 2248 | /* calculate the result */ |
| | 2249 | result = calc_result(left->get_const_val()->get_val_int(), |
| | 2250 | right->get_const_val()->get_val_int()); |
| | 2251 | |
| | 2252 | /* assign the result back to the left operand */ |
| | 2253 | left->get_const_val()->set_int(result); |
| | 2254 | } |
| | 2255 | |
| | 2256 | /* return the updated left value */ |
| | 2257 | return left; |
| | 2258 | } |
| | 2259 | else |
| | 2260 | { |
| | 2261 | /* |
| | 2262 | * one or the other is non-constant, so we can't fold the |
| | 2263 | * expression - return null to so indicate |
| | 2264 | */ |
| | 2265 | return 0; |
| | 2266 | } |
| | 2267 | } |
| | 2268 | |
| | 2269 | /* ------------------------------------------------------------------------ */ |
| | 2270 | /* |
| | 2271 | * bitwise OR operator |
| | 2272 | */ |
| | 2273 | |
| | 2274 | /* |
| | 2275 | * build the subtree |
| | 2276 | */ |
| | 2277 | CTcPrsNode *CTcPrsOpBOr::build_tree(CTcPrsNode *left, |
| | 2278 | CTcPrsNode *right) const |
| | 2279 | { |
| | 2280 | return new CTPNBOr(left, right); |
| | 2281 | } |
| | 2282 | |
| | 2283 | /* ------------------------------------------------------------------------ */ |
| | 2284 | /* |
| | 2285 | * bitwise AND operator |
| | 2286 | */ |
| | 2287 | |
| | 2288 | /* |
| | 2289 | * build the subtree |
| | 2290 | */ |
| | 2291 | CTcPrsNode *CTcPrsOpBAnd::build_tree(CTcPrsNode *left, |
| | 2292 | CTcPrsNode *right) const |
| | 2293 | { |
| | 2294 | return new CTPNBAnd(left, right); |
| | 2295 | } |
| | 2296 | |
| | 2297 | /* ------------------------------------------------------------------------ */ |
| | 2298 | /* |
| | 2299 | * bitwise XOR operator |
| | 2300 | */ |
| | 2301 | |
| | 2302 | /* |
| | 2303 | * build the subtree |
| | 2304 | */ |
| | 2305 | CTcPrsNode *CTcPrsOpBXor::build_tree(CTcPrsNode *left, |
| | 2306 | CTcPrsNode *right) const |
| | 2307 | { |
| | 2308 | return new CTPNBXor(left, right); |
| | 2309 | } |
| | 2310 | |
| | 2311 | |
| | 2312 | /* ------------------------------------------------------------------------ */ |
| | 2313 | /* |
| | 2314 | * shift left operator |
| | 2315 | */ |
| | 2316 | |
| | 2317 | /* |
| | 2318 | * build the subtree |
| | 2319 | */ |
| | 2320 | CTcPrsNode *CTcPrsOpShl::build_tree(CTcPrsNode *left, |
| | 2321 | CTcPrsNode *right) const |
| | 2322 | { |
| | 2323 | return new CTPNShl(left, right); |
| | 2324 | } |
| | 2325 | |
| | 2326 | /* ------------------------------------------------------------------------ */ |
| | 2327 | /* |
| | 2328 | * shift right operator |
| | 2329 | */ |
| | 2330 | |
| | 2331 | /* |
| | 2332 | * build the subtree |
| | 2333 | */ |
| | 2334 | CTcPrsNode *CTcPrsOpShr::build_tree(CTcPrsNode *left, |
| | 2335 | CTcPrsNode *right) const |
| | 2336 | { |
| | 2337 | return new CTPNShr(left, right); |
| | 2338 | } |
| | 2339 | |
| | 2340 | /* ------------------------------------------------------------------------ */ |
| | 2341 | /* |
| | 2342 | * multiplication operator |
| | 2343 | */ |
| | 2344 | |
| | 2345 | /* |
| | 2346 | * build the subtree |
| | 2347 | */ |
| | 2348 | CTcPrsNode *CTcPrsOpMul::build_tree(CTcPrsNode *left, |
| | 2349 | CTcPrsNode *right) const |
| | 2350 | { |
| | 2351 | return new CTPNMul(left, right); |
| | 2352 | } |
| | 2353 | |
| | 2354 | /* ------------------------------------------------------------------------ */ |
| | 2355 | /* |
| | 2356 | * division operator |
| | 2357 | */ |
| | 2358 | |
| | 2359 | /* |
| | 2360 | * build the subtree |
| | 2361 | */ |
| | 2362 | CTcPrsNode *CTcPrsOpDiv::build_tree(CTcPrsNode *left, |
| | 2363 | CTcPrsNode *right) const |
| | 2364 | { |
| | 2365 | return new CTPNDiv(left, right); |
| | 2366 | } |
| | 2367 | |
| | 2368 | /* |
| | 2369 | * evaluate constant result |
| | 2370 | */ |
| | 2371 | long CTcPrsOpDiv::calc_result(long a, long b) const |
| | 2372 | { |
| | 2373 | /* check for divide-by-zero */ |
| | 2374 | if (b == 0) |
| | 2375 | { |
| | 2376 | /* log a divide-by-zero error */ |
| | 2377 | G_tok->log_error(TCERR_CONST_DIV_ZERO); |
| | 2378 | |
| | 2379 | /* the result isn't really meaningful, but return something anyway */ |
| | 2380 | return 1; |
| | 2381 | } |
| | 2382 | else |
| | 2383 | { |
| | 2384 | /* return the result */ |
| | 2385 | return a / b; |
| | 2386 | } |
| | 2387 | } |
| | 2388 | /* ------------------------------------------------------------------------ */ |
| | 2389 | /* |
| | 2390 | * modulo operator |
| | 2391 | */ |
| | 2392 | |
| | 2393 | /* |
| | 2394 | * build the subtree |
| | 2395 | */ |
| | 2396 | CTcPrsNode *CTcPrsOpMod::build_tree(CTcPrsNode *left, |
| | 2397 | CTcPrsNode *right) const |
| | 2398 | { |
| | 2399 | return new CTPNMod(left, right); |
| | 2400 | } |
| | 2401 | |
| | 2402 | /* |
| | 2403 | * evaluate constant result |
| | 2404 | */ |
| | 2405 | long CTcPrsOpMod::calc_result(long a, long b) const |
| | 2406 | { |
| | 2407 | /* check for divide-by-zero */ |
| | 2408 | if (b == 0) |
| | 2409 | { |
| | 2410 | /* log a divide-by-zero error */ |
| | 2411 | G_tok->log_error(TCERR_CONST_DIV_ZERO); |
| | 2412 | |
| | 2413 | /* the result isn't really meaningful, but return something anyway */ |
| | 2414 | return 1; |
| | 2415 | } |
| | 2416 | else |
| | 2417 | { |
| | 2418 | /* return the result */ |
| | 2419 | return a % b; |
| | 2420 | } |
| | 2421 | } |
| | 2422 | |
| | 2423 | /* ------------------------------------------------------------------------ */ |
| | 2424 | /* |
| | 2425 | * subtraction operator |
| | 2426 | */ |
| | 2427 | |
| | 2428 | /* |
| | 2429 | * build the subtree |
| | 2430 | */ |
| | 2431 | CTcPrsNode *CTcPrsOpSub::build_tree(CTcPrsNode *left, |
| | 2432 | CTcPrsNode *right) const |
| | 2433 | { |
| | 2434 | return new CTPNSub(left, right); |
| | 2435 | } |
| | 2436 | |
| | 2437 | /* |
| | 2438 | * evaluate a constant value |
| | 2439 | */ |
| | 2440 | CTcPrsNode *CTcPrsOpSub::eval_constant(CTcPrsNode *left, |
| | 2441 | CTcPrsNode *right) const |
| | 2442 | { |
| | 2443 | if (left->is_const() && right->is_const()) |
| | 2444 | { |
| | 2445 | tc_constval_type_t typ1, typ2; |
| | 2446 | |
| | 2447 | /* get the types */ |
| | 2448 | typ1 = left->get_const_val()->get_type(); |
| | 2449 | typ2 = right->get_const_val()->get_type(); |
| | 2450 | |
| | 2451 | /* check our types */ |
| | 2452 | if (typ1 == TC_CVT_INT && typ2 == TC_CVT_INT) |
| | 2453 | { |
| | 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()); |
| | 2458 | } |
| | 2459 | else if (typ1 == TC_CVT_FLOAT || typ2 == TC_CVT_FLOAT) |
| | 2460 | { |
| | 2461 | /* can't fold float constants at compile time */ |
| | 2462 | return 0; |
| | 2463 | } |
| | 2464 | else if (typ1 == TC_CVT_LIST) |
| | 2465 | { |
| | 2466 | CTPNList *lst; |
| | 2467 | |
| | 2468 | /* get the original list */ |
| | 2469 | lst = left->get_const_val()->get_val_list(); |
| | 2470 | |
| | 2471 | /* |
| | 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 |
| | 2475 | */ |
| | 2476 | if (typ2 == TC_CVT_LIST) |
| | 2477 | { |
| | 2478 | /* remove each element of the rhs list from the lhs list */ |
| | 2479 | CTPNListEle *ele; |
| | 2480 | |
| | 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()) |
| | 2485 | { |
| | 2486 | /* add this element's underlying expression value */ |
| | 2487 | lst->remove_element(ele->get_expr()->get_const_val()); |
| | 2488 | } |
| | 2489 | } |
| | 2490 | else |
| | 2491 | { |
| | 2492 | /* remove the rhs value from the lhs list */ |
| | 2493 | lst->remove_element(right->get_const_val()); |
| | 2494 | } |
| | 2495 | } |
| | 2496 | else |
| | 2497 | { |
| | 2498 | /* these types are incompatible - log an error */ |
| | 2499 | G_tok->log_error(TCERR_CONST_BINMINUS_INCOMPAT); |
| | 2500 | return 0; |
| | 2501 | } |
| | 2502 | |
| | 2503 | /* return the updated left side */ |
| | 2504 | return left; |
| | 2505 | } |
| | 2506 | else |
| | 2507 | { |
| | 2508 | /* they're not constant - we can't generate a constant result */ |
| | 2509 | return 0; |
| | 2510 | } |
| | 2511 | } |
| | 2512 | |
| | 2513 | /* ------------------------------------------------------------------------ */ |
| | 2514 | /* |
| | 2515 | * addition operator |
| | 2516 | */ |
| | 2517 | |
| | 2518 | /* |
| | 2519 | * evaluate constant value |
| | 2520 | */ |
| | 2521 | CTcPrsNode *CTcPrsOpAdd::eval_constant(CTcPrsNode *left, |
| | 2522 | CTcPrsNode *right) const |
| | 2523 | { |
| | 2524 | /* check for constants */ |
| | 2525 | if (left->is_const() && right->is_const()) |
| | 2526 | { |
| | 2527 | tc_constval_type_t typ1, typ2; |
| | 2528 | |
| | 2529 | /* get the types */ |
| | 2530 | typ1 = left->get_const_val()->get_type(); |
| | 2531 | typ2 = right->get_const_val()->get_type(); |
| | 2532 | |
| | 2533 | /* check our types */ |
| | 2534 | if (typ1 == TC_CVT_INT && typ2 == TC_CVT_INT) |
| | 2535 | { |
| | 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()); |
| | 2540 | } |
| | 2541 | else if (typ1 == TC_CVT_FLOAT || typ2 == TC_CVT_FLOAT) |
| | 2542 | { |
| | 2543 | /* can't fold float constants at compile time */ |
| | 2544 | return 0; |
| | 2545 | } |
| | 2546 | else if (typ1 == TC_CVT_LIST) |
| | 2547 | { |
| | 2548 | CTPNList *lst; |
| | 2549 | |
| | 2550 | /* get the original list */ |
| | 2551 | lst = left->get_const_val()->get_val_list(); |
| | 2552 | |
| | 2553 | /* |
| | 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 |
| | 2557 | */ |
| | 2558 | if (typ2 == TC_CVT_LIST) |
| | 2559 | { |
| | 2560 | CTPNListEle *ele; |
| | 2561 | |
| | 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()) |
| | 2566 | { |
| | 2567 | /* add this element's underlying expression value */ |
| | 2568 | lst->add_element(ele->get_expr()); |
| | 2569 | } |
| | 2570 | } |
| | 2571 | else |
| | 2572 | { |
| | 2573 | /* add a new list element for the right side */ |
| | 2574 | lst->add_element(right); |
| | 2575 | } |
| | 2576 | |
| | 2577 | /* |
| | 2578 | * this list is longer than the original(s); tell the parser |
| | 2579 | * about it in case it's the longest list yet |
| | 2580 | */ |
| | 2581 | G_cg->note_list(lst->get_count()); |
| | 2582 | } |
| | 2583 | else if (typ1 == TC_CVT_SSTR || typ2 == TC_CVT_SSTR) |
| | 2584 | { |
| | 2585 | char buf1[128]; |
| | 2586 | char buf2[128]; |
| | 2587 | const char *str1, *str2; |
| | 2588 | size_t len1, len2; |
| | 2589 | char *new_str; |
| | 2590 | |
| | 2591 | /* if the second value is a list, we can't make a constant */ |
| | 2592 | if (typ2 == TC_CVT_LIST) |
| | 2593 | return 0; |
| | 2594 | |
| | 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); |
| | 2600 | |
| | 2601 | /* |
| | 2602 | * if we couldn't convert one or the other, leave the result |
| | 2603 | * non-constant |
| | 2604 | */ |
| | 2605 | if (str1 == 0 || str2 == 0) |
| | 2606 | return 0; |
| | 2607 | |
| | 2608 | /* |
| | 2609 | * allocate space in the node pool for the concatenation of |
| | 2610 | * the two strings - if that fails, don't bother with the |
| | 2611 | * concatenation |
| | 2612 | */ |
| | 2613 | new_str = (char *)G_prsmem->alloc(len1 + len2 + 1); |
| | 2614 | if (new_str == 0) |
| | 2615 | return 0; |
| | 2616 | |
| | 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'; |
| | 2621 | |
| | 2622 | /* set the new value in the left node */ |
| | 2623 | left->get_const_val()->set_sstr(new_str, len1 + len2); |
| | 2624 | } |
| | 2625 | else |
| | 2626 | { |
| | 2627 | /* these types are incompatible - log an error */ |
| | 2628 | G_tok->log_error(TCERR_CONST_BINPLUS_INCOMPAT); |
| | 2629 | return 0; |
| | 2630 | } |
| | 2631 | |
| | 2632 | /* return the updated left value */ |
| | 2633 | return left; |
| | 2634 | } |
| | 2635 | else |
| | 2636 | { |
| | 2637 | /* the values aren't constant, so the result isn't constant */ |
| | 2638 | return 0; |
| | 2639 | } |
| | 2640 | } |
| | 2641 | |
| | 2642 | |
| | 2643 | /* |
| | 2644 | * build the subtree |
| | 2645 | */ |
| | 2646 | CTcPrsNode *CTcPrsOpAdd::build_tree(CTcPrsNode *left, |
| | 2647 | CTcPrsNode *right) const |
| | 2648 | { |
| | 2649 | return new CTPNAdd(left, right); |
| | 2650 | } |
| | 2651 | |
| | 2652 | /* ------------------------------------------------------------------------ */ |
| | 2653 | /* |
| | 2654 | * Assignment Operator Group |
| | 2655 | */ |
| | 2656 | |
| | 2657 | /* |
| | 2658 | * parse an assignment expression |
| | 2659 | */ |
| | 2660 | CTcPrsNode *CTcPrsOpAsi::parse() const |
| | 2661 | { |
| | 2662 | CTcPrsNode *lhs; |
| | 2663 | CTcPrsNode *rhs; |
| | 2664 | tc_toktyp_t curtyp; |
| | 2665 | |
| | 2666 | /* start by parsing a conditional subexpression */ |
| | 2667 | lhs = S_op_if.parse(); |
| | 2668 | if (lhs == 0) |
| | 2669 | return 0; |
| | 2670 | |
| | 2671 | /* get the next operator */ |
| | 2672 | curtyp = G_tok->cur(); |
| | 2673 | |
| | 2674 | /* check to see if it's an assignment operator of some kind */ |
| | 2675 | switch(curtyp) |
| | 2676 | { |
| | 2677 | case TOKT_PLUSEQ: |
| | 2678 | case TOKT_MINEQ: |
| | 2679 | case TOKT_TIMESEQ: |
| | 2680 | case TOKT_DIVEQ: |
| | 2681 | case TOKT_MODEQ: |
| | 2682 | case TOKT_ANDEQ: |
| | 2683 | case TOKT_OREQ: |
| | 2684 | case TOKT_XOREQ: |
| | 2685 | case TOKT_SHLEQ: |
| | 2686 | case TOKT_SHREQ: |
| | 2687 | /* it's an assignment operator - process it */ |
| | 2688 | break; |
| | 2689 | |
| | 2690 | default: |
| | 2691 | /* check against the current simple-assignment operator */ |
| | 2692 | if (curtyp == asi_op_) |
| | 2693 | { |
| | 2694 | /* it's an assignment operator - process it */ |
| | 2695 | break; |
| | 2696 | } |
| | 2697 | else |
| | 2698 | { |
| | 2699 | /* |
| | 2700 | * it's not an assignment - return the original |
| | 2701 | * subexpression with no further elaboration |
| | 2702 | */ |
| | 2703 | return lhs; |
| | 2704 | } |
| | 2705 | } |
| | 2706 | |
| | 2707 | /* check for a valid lvalue */ |
| | 2708 | if (!lhs->check_lvalue()) |
| | 2709 | { |
| | 2710 | /* log an error but continue parsing */ |
| | 2711 | G_tok->log_error(TCERR_INVALID_LVALUE, |
| | 2712 | G_tok->get_op_text(G_tok->cur())); |
| | 2713 | } |
| | 2714 | |
| | 2715 | /* skip the assignment operator */ |
| | 2716 | G_tok->next(); |
| | 2717 | |
| | 2718 | /* |
| | 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. |
| | 2724 | */ |
| | 2725 | rhs = parse(); |
| | 2726 | if (rhs == 0) |
| | 2727 | return 0; |
| | 2728 | |
| | 2729 | /* build and return the result based on the operator type */ |
| | 2730 | switch(curtyp) |
| | 2731 | { |
| | 2732 | case TOKT_PLUSEQ: |
| | 2733 | lhs = new CTPNAddAsi(lhs, rhs); |
| | 2734 | break; |
| | 2735 | |
| | 2736 | case TOKT_MINEQ: |
| | 2737 | lhs = new CTPNSubAsi(lhs, rhs); |
| | 2738 | break; |
| | 2739 | |
| | 2740 | case TOKT_TIMESEQ: |
| | 2741 | lhs = new CTPNMulAsi(lhs, rhs); |
| | 2742 | break; |
| | 2743 | |
| | 2744 | case TOKT_DIVEQ: |
| | 2745 | lhs = new CTPNDivAsi(lhs, rhs); |
| | 2746 | break; |
| | 2747 | |
| | 2748 | case TOKT_MODEQ: |
| | 2749 | lhs = new CTPNModAsi(lhs, rhs); |
| | 2750 | break; |
| | 2751 | |
| | 2752 | case TOKT_ANDEQ: |
| | 2753 | lhs = new CTPNBAndAsi(lhs, rhs); |
| | 2754 | break; |
| | 2755 | |
| | 2756 | case TOKT_OREQ: |
| | 2757 | lhs = new CTPNBOrAsi(lhs, rhs); |
| | 2758 | break; |
| | 2759 | |
| | 2760 | case TOKT_XOREQ: |
| | 2761 | lhs = new CTPNBXorAsi(lhs, rhs); |
| | 2762 | break; |
| | 2763 | |
| | 2764 | case TOKT_SHLEQ: |
| | 2765 | lhs = new CTPNShlAsi(lhs, rhs); |
| | 2766 | break; |
| | 2767 | |
| | 2768 | case TOKT_SHREQ: |
| | 2769 | lhs = new CTPNShrAsi(lhs, rhs); |
| | 2770 | break; |
| | 2771 | |
| | 2772 | default: |
| | 2773 | /* plain assignment operator */ |
| | 2774 | lhs = new CTPNAsi(lhs, rhs); |
| | 2775 | break; |
| | 2776 | } |
| | 2777 | |
| | 2778 | /* return the result */ |
| | 2779 | return lhs; |
| | 2780 | } |
| | 2781 | |
| | 2782 | /* ------------------------------------------------------------------------ */ |
| | 2783 | /* |
| | 2784 | * Tertiary Conditional Operator |
| | 2785 | */ |
| | 2786 | |
| | 2787 | CTcPrsNode *CTcPrsOpIf::parse() const |
| | 2788 | { |
| | 2789 | CTcPrsNode *first; |
| | 2790 | CTcPrsNode *second; |
| | 2791 | CTcPrsNode *third; |
| | 2792 | |
| | 2793 | /* parse the conditional part */ |
| | 2794 | first = S_op_or.parse(); |
| | 2795 | if (first == 0) |
| | 2796 | return 0; |
| | 2797 | |
| | 2798 | /* if we're not looking at the '?' operator, we're done */ |
| | 2799 | if (G_tok->cur() != TOKT_QUESTION) |
| | 2800 | return first; |
| | 2801 | |
| | 2802 | /* skip the '?' operator */ |
| | 2803 | G_tok->next(); |
| | 2804 | |
| | 2805 | /* |
| | 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) |
| | 2811 | */ |
| | 2812 | second = G_prs->parse_expr_or_dstr(TRUE); |
| | 2813 | if (second == 0) |
| | 2814 | return 0; |
| | 2815 | |
| | 2816 | /* make sure we have the ':' after the second part */ |
| | 2817 | if (G_tok->cur() != TOKT_COLON) |
| | 2818 | { |
| | 2819 | /* |
| | 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 |
| | 2823 | */ |
| | 2824 | G_tok->log_error(TCERR_QUEST_WITHOUT_COLON); |
| | 2825 | |
| | 2826 | /* if we're at the end of file, there's no point in continuing */ |
| | 2827 | if (G_tok->cur() == TOKT_EOF) |
| | 2828 | return 0; |
| | 2829 | } |
| | 2830 | |
| | 2831 | /* skip the ':' */ |
| | 2832 | G_tok->next(); |
| | 2833 | |
| | 2834 | /* |
| | 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 ',' |
| | 2838 | */ |
| | 2839 | third = G_prs->parse_expr_or_dstr(FALSE); |
| | 2840 | if (third == 0) |
| | 2841 | return 0; |
| | 2842 | |
| | 2843 | /* |
| | 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 |
| | 2848 | * alternatives. |
| | 2849 | */ |
| | 2850 | if (first->is_const()) |
| | 2851 | { |
| | 2852 | /* |
| | 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 |
| | 2856 | */ |
| | 2857 | return (first->get_const_val()->get_val_bool() |
| | 2858 | ? second : third); |
| | 2859 | } |
| | 2860 | else |
| | 2861 | { |
| | 2862 | /* it's not a constant value - return a new conditional node */ |
| | 2863 | return new CTPNIf(first, second, third); |
| | 2864 | } |
| | 2865 | } |
| | 2866 | |
| | 2867 | /* ------------------------------------------------------------------------ */ |
| | 2868 | /* |
| | 2869 | * Unary Operator Parser |
| | 2870 | */ |
| | 2871 | |
| | 2872 | CTcPrsNode *CTcPrsOpUnary::parse() const |
| | 2873 | { |
| | 2874 | CTcPrsNode *sub; |
| | 2875 | tc_toktyp_t op; |
| | 2876 | |
| | 2877 | /* get the current token, which may be a prefix operator */ |
| | 2878 | op = G_tok->cur(); |
| | 2879 | |
| | 2880 | /* check for prefix operators */ |
| | 2881 | switch(op) |
| | 2882 | { |
| | 2883 | case TOKT_AND: |
| | 2884 | /* skip the '&' */ |
| | 2885 | G_tok->next(); |
| | 2886 | |
| | 2887 | /* parse the address expression */ |
| | 2888 | return parse_addr(); |
| | 2889 | |
| | 2890 | case TOKT_NOT: |
| | 2891 | case TOKT_BNOT: |
| | 2892 | case TOKT_PLUS: |
| | 2893 | case TOKT_MINUS: |
| | 2894 | case TOKT_INC: |
| | 2895 | case TOKT_DEC: |
| | 2896 | case TOKT_DELETE: |
| | 2897 | /* skip the operator */ |
| | 2898 | G_tok->next(); |
| | 2899 | |
| | 2900 | /* |
| | 2901 | * recursively parse the unary expression to which to apply the |
| | 2902 | * operator |
| | 2903 | */ |
| | 2904 | sub = parse(); |
| | 2905 | if (sub == 0) |
| | 2906 | return 0; |
| | 2907 | |
| | 2908 | /* apply the operator */ |
| | 2909 | switch(op) |
| | 2910 | { |
| | 2911 | case TOKT_NOT: |
| | 2912 | /* apply the NOT operator */ |
| | 2913 | return parse_not(sub); |
| | 2914 | |
| | 2915 | case TOKT_BNOT: |
| | 2916 | /* apply the bitwise NOT operator */ |
| | 2917 | return parse_bnot(sub); |
| | 2918 | |
| | 2919 | case TOKT_PLUS: |
| | 2920 | /* apply the unary positive operator */ |
| | 2921 | return parse_pos(sub); |
| | 2922 | |
| | 2923 | case TOKT_MINUS: |
| | 2924 | /* apply the unary negation operator */ |
| | 2925 | return parse_neg(sub); |
| | 2926 | |
| | 2927 | case TOKT_INC: |
| | 2928 | /* apply the pre-increment operator */ |
| | 2929 | return parse_inc(TRUE, sub); |
| | 2930 | |
| | 2931 | case TOKT_DEC: |
| | 2932 | /* apply the pre-decrement operator */ |
| | 2933 | return parse_dec(TRUE, sub); |
| | 2934 | |
| | 2935 | case TOKT_DELETE: |
| | 2936 | /* apply the deletion operator */ |
| | 2937 | return parse_delete(sub); |
| | 2938 | |
| | 2939 | default: |
| | 2940 | break; |
| | 2941 | } |
| | 2942 | |
| | 2943 | default: |
| | 2944 | /* it's not a unary prefix operator - parse a postfix expression */ |
| | 2945 | return parse_postfix(TRUE, TRUE); |
| | 2946 | } |
| | 2947 | } |
| | 2948 | |
| | 2949 | /* |
| | 2950 | * parse a unary NOT expression |
| | 2951 | */ |
| | 2952 | CTcPrsNode *CTcPrsOpUnary::parse_not(CTcPrsNode *subexpr) |
| | 2953 | { |
| | 2954 | CTcPrsNode *ret; |
| | 2955 | |
| | 2956 | /* try folding a constant value */ |
| | 2957 | ret = eval_const_not(subexpr); |
| | 2958 | |
| | 2959 | /* |
| | 2960 | * if we got a constant result, return it; otherwise, create a NOT |
| | 2961 | * node for code generation |
| | 2962 | */ |
| | 2963 | if (ret != 0) |
| | 2964 | return ret; |
| | 2965 | else |
| | 2966 | return new CTPNNot(subexpr); |
| | 2967 | } |
| | 2968 | |
| | 2969 | /* |
| | 2970 | * evaluate a constant NOT expression |
| | 2971 | */ |
| | 2972 | CTcPrsNode *CTcPrsOpUnary::eval_const_not(CTcPrsNode *subexpr) |
| | 2973 | { |
| | 2974 | /* |
| | 2975 | * if the underlying expression is a constant value, apply the |
| | 2976 | * operator |
| | 2977 | */ |
| | 2978 | if (subexpr->is_const()) |
| | 2979 | { |
| | 2980 | /* set the new value */ |
| | 2981 | subexpr->get_const_val() |
| | 2982 | ->set_bool(!subexpr->get_const_val()->get_val_bool()); |
| | 2983 | |
| | 2984 | /* return the modified constant value */ |
| | 2985 | return subexpr; |
| | 2986 | } |
| | 2987 | |
| | 2988 | /* the result is not constant */ |
| | 2989 | return 0; |
| | 2990 | } |
| | 2991 | |
| | 2992 | /* |
| | 2993 | * parse a unary bitwise NOT expression |
| | 2994 | */ |
| | 2995 | CTcPrsNode *CTcPrsOpUnary::parse_bnot(CTcPrsNode *subexpr) |
| | 2996 | { |
| | 2997 | /* |
| | 2998 | * if the underlying expression is a constant value, apply the |
| | 2999 | * operator |
| | 3000 | */ |
| | 3001 | if (subexpr->is_const()) |
| | 3002 | { |
| | 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)); |
| | 3007 | else |
| | 3008 | subexpr->get_const_val() |
| | 3009 | ->set_int(~subexpr->get_const_val()->get_val_int()); |
| | 3010 | |
| | 3011 | /* return the updated value */ |
| | 3012 | return subexpr; |
| | 3013 | } |
| | 3014 | |
| | 3015 | /* create the bitwise NOT node */ |
| | 3016 | return new CTPNBNot(subexpr); |
| | 3017 | } |
| | 3018 | |
| | 3019 | /* |
| | 3020 | * parse a unary address expression |
| | 3021 | */ |
| | 3022 | CTcPrsNode *CTcPrsOpUnary::parse_addr() const |
| | 3023 | { |
| | 3024 | CTcPrsNode *subexpr; |
| | 3025 | |
| | 3026 | /* |
| | 3027 | * if it's a simple symbol, create an unresolved symbol node for it; |
| | 3028 | * otherwise parse the entire expression |
| | 3029 | */ |
| | 3030 | if (G_tok->cur() == TOKT_SYM) |
| | 3031 | { |
| | 3032 | const CTcToken *tok; |
| | 3033 | |
| | 3034 | /* |
| | 3035 | * create an unresolved symbol node - we'll resolve this during |
| | 3036 | * code generation |
| | 3037 | */ |
| | 3038 | tok = G_tok->getcur(); |
| | 3039 | subexpr = new CTPNSym(tok->get_text(), tok->get_text_len()); |
| | 3040 | |
| | 3041 | /* |
| | 3042 | * The address operator implies that the symbol is a property, so |
| | 3043 | * define the property symbol and mark it as referenced if we |
| | 3044 | * haven't already. |
| | 3045 | */ |
| | 3046 | G_prs->get_global_symtab()->find_or_def_prop_explicit( |
| | 3047 | tok->get_text(), tok->get_text_len(), FALSE); |
| | 3048 | |
| | 3049 | /* skip the symbol */ |
| | 3050 | G_tok->next(); |
| | 3051 | } |
| | 3052 | else |
| | 3053 | { |
| | 3054 | /* parse an expression */ |
| | 3055 | subexpr = parse(); |
| | 3056 | if (subexpr == 0) |
| | 3057 | return 0; |
| | 3058 | } |
| | 3059 | |
| | 3060 | /* |
| | 3061 | * The underlying expression must be something that has an address; |
| | 3062 | * if it's not, it's an error. |
| | 3063 | */ |
| | 3064 | if (!subexpr->has_addr()) |
| | 3065 | { |
| | 3066 | /* |
| | 3067 | * can't take the address of the subexpression - log an error, |
| | 3068 | * but continue parsing the expression anyway |
| | 3069 | */ |
| | 3070 | G_tok->log_error(TCERR_NO_ADDRESS); |
| | 3071 | } |
| | 3072 | |
| | 3073 | /* create the address node */ |
| | 3074 | return new CTPNAddr(subexpr); |
| | 3075 | } |
| | 3076 | |
| | 3077 | /* |
| | 3078 | * parse a unary arithmetic positive expression |
| | 3079 | */ |
| | 3080 | CTcPrsNode *CTcPrsOpUnary::parse_pos(CTcPrsNode *subexpr) |
| | 3081 | { |
| | 3082 | /* |
| | 3083 | * if the underlying expression is a constant value, apply the |
| | 3084 | * operator |
| | 3085 | */ |
| | 3086 | if (subexpr->is_const()) |
| | 3087 | { |
| | 3088 | /* if it's a float, a unary '+' has no effect at all */ |
| | 3089 | if (subexpr->get_const_val()->get_type() == TC_CVT_FLOAT) |
| | 3090 | return subexpr; |
| | 3091 | |
| | 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)); |
| | 3096 | |
| | 3097 | /* |
| | 3098 | * positive-ing a value doesn't change the value, so return the |
| | 3099 | * original constant |
| | 3100 | */ |
| | 3101 | return subexpr; |
| | 3102 | } |
| | 3103 | |
| | 3104 | /* create the unary positive node */ |
| | 3105 | return new CTPNPos(subexpr); |
| | 3106 | } |
| | 3107 | |
| | 3108 | /* |
| | 3109 | * parse a unary arithmetic negation expression |
| | 3110 | */ |
| | 3111 | CTcPrsNode *CTcPrsOpUnary::parse_neg(CTcPrsNode *subexpr) |
| | 3112 | { |
| | 3113 | /* |
| | 3114 | * if the underlying expression is a constant value, apply the |
| | 3115 | * operator |
| | 3116 | */ |
| | 3117 | if (subexpr->is_const()) |
| | 3118 | { |
| | 3119 | /* we need an integer or float */ |
| | 3120 | if (subexpr->get_const_val()->get_type() == TC_CVT_INT) |
| | 3121 | { |
| | 3122 | /* set the value negative in the subexpression */ |
| | 3123 | subexpr->get_const_val() |
| | 3124 | ->set_int(-(subexpr->get_const_val()->get_val_int())); |
| | 3125 | } |
| | 3126 | else if (subexpr->get_const_val()->get_type() == TC_CVT_FLOAT) |
| | 3127 | { |
| | 3128 | CTcConstVal *cval = subexpr->get_const_val(); |
| | 3129 | char *new_txt; |
| | 3130 | |
| | 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); |
| | 3133 | |
| | 3134 | /* insert the minus sign */ |
| | 3135 | new_txt[0] = '-'; |
| | 3136 | |
| | 3137 | /* add the original string */ |
| | 3138 | memcpy(new_txt + 1, cval->get_val_float(), |
| | 3139 | cval->get_val_float_len()); |
| | 3140 | |
| | 3141 | /* update the subexpression's constant value to the new text */ |
| | 3142 | cval->set_float(new_txt, cval->get_val_float_len() + 1); |
| | 3143 | } |
| | 3144 | else |
| | 3145 | { |
| | 3146 | /* log the error */ |
| | 3147 | G_tok->log_error(TCERR_CONST_UNARY_REQ_NUM, |
| | 3148 | G_tok->get_op_text(TOKT_MINUS)); |
| | 3149 | } |
| | 3150 | |
| | 3151 | /* return the modified constant value */ |
| | 3152 | return subexpr; |
| | 3153 | } |
| | 3154 | |
| | 3155 | /* create the unary negation node */ |
| | 3156 | return new CTPNNeg(subexpr); |
| | 3157 | } |
| | 3158 | |
| | 3159 | |
| | 3160 | /* |
| | 3161 | * parse a pre-increment expression |
| | 3162 | */ |
| | 3163 | CTcPrsNode *CTcPrsOpUnary::parse_inc(int pre, CTcPrsNode *subexpr) |
| | 3164 | { |
| | 3165 | /* require an lvalue */ |
| | 3166 | if (!subexpr->check_lvalue()) |
| | 3167 | { |
| | 3168 | /* log an error, but continue parsing */ |
| | 3169 | G_tok->log_error(TCERR_INVALID_UNARY_LVALUE, |
| | 3170 | G_tok->get_op_text(TOKT_INC)); |
| | 3171 | } |
| | 3172 | |
| | 3173 | /* apply the increment operator */ |
| | 3174 | if (pre) |
| | 3175 | return new CTPNPreInc(subexpr); |
| | 3176 | else |
| | 3177 | return new CTPNPostInc(subexpr); |
| | 3178 | } |
| | 3179 | |
| | 3180 | /* |
| | 3181 | * parse a pre-decrement expression |
| | 3182 | */ |
| | 3183 | CTcPrsNode *CTcPrsOpUnary::parse_dec(int pre, CTcPrsNode *subexpr) |
| | 3184 | { |
| | 3185 | /* require an lvalue */ |
| | 3186 | if (!subexpr->check_lvalue()) |
| | 3187 | { |
| | 3188 | /* log an error, but continue parsing */ |
| | 3189 | G_tok->log_error(TCERR_INVALID_UNARY_LVALUE, |
| | 3190 | G_tok->get_op_text(TOKT_INC)); |
| | 3191 | } |
| | 3192 | |
| | 3193 | /* apply the pre-increment operator */ |
| | 3194 | if (pre) |
| | 3195 | return new CTPNPreDec(subexpr); |
| | 3196 | else |
| | 3197 | return new CTPNPostDec(subexpr); |
| | 3198 | } |
| | 3199 | |
| | 3200 | /* |
| | 3201 | * parse a unary allocation expression |
| | 3202 | */ |
| | 3203 | CTcPrsNode *CTcPrsOpUnary::parse_new(CTcPrsNode *subexpr, int is_transient) |
| | 3204 | { |
| | 3205 | /* create the allocation node */ |
| | 3206 | return new CTPNNew(subexpr, is_transient); |
| | 3207 | } |
| | 3208 | |
| | 3209 | /* |
| | 3210 | * parse a unary deletion expression |
| | 3211 | */ |
| | 3212 | CTcPrsNode *CTcPrsOpUnary::parse_delete(CTcPrsNode *subexpr) |
| | 3213 | { |
| | 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); |
| | 3217 | |
| | 3218 | /* create the deletion node */ |
| | 3219 | return new CTPNDelete(subexpr); |
| | 3220 | } |
| | 3221 | |
| | 3222 | /* |
| | 3223 | * parse a postfix expression |
| | 3224 | */ |
| | 3225 | CTcPrsNode *CTcPrsOpUnary::parse_postfix(int allow_member_expr, |
| | 3226 | int allow_call_expr) |
| | 3227 | { |
| | 3228 | CTcPrsNode *sub; |
| | 3229 | |
| | 3230 | /* parse a primary expression */ |
| | 3231 | sub = parse_primary(); |
| | 3232 | if (sub == 0) |
| | 3233 | return 0; |
| | 3234 | |
| | 3235 | /* keep going as long as we find postfix operators */ |
| | 3236 | for (;;) |
| | 3237 | { |
| | 3238 | tc_toktyp_t op; |
| | 3239 | |
| | 3240 | /* check for a postfix operator */ |
| | 3241 | op = G_tok->cur(); |
| | 3242 | switch(op) |
| | 3243 | { |
| | 3244 | case TOKT_LPAR: |
| | 3245 | /* left paren - function or method call */ |
| | 3246 | if (allow_call_expr) |
| | 3247 | { |
| | 3248 | /* parse the call expression */ |
| | 3249 | sub = parse_call(sub); |
| | 3250 | } |
| | 3251 | else |
| | 3252 | { |
| | 3253 | /* call expressions aren't allowed - stop here */ |
| | 3254 | return sub; |
| | 3255 | } |
| | 3256 | break; |
| | 3257 | |
| | 3258 | case TOKT_LBRACK: |
| | 3259 | /* left square bracket - subscript */ |
| | 3260 | sub = parse_subscript(sub); |
| | 3261 | break; |
| | 3262 | |
| | 3263 | case TOKT_DOT: |
| | 3264 | /* |
| | 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. |
| | 3268 | */ |
| | 3269 | if (allow_member_expr) |
| | 3270 | { |
| | 3271 | /* |
| | 3272 | * it's allowed - parse it and continue to look for other |
| | 3273 | * postfix expressions following the member expression |
| | 3274 | */ |
| | 3275 | sub = parse_member(sub); |
| | 3276 | } |
| | 3277 | else |
| | 3278 | { |
| | 3279 | /* |
| | 3280 | * member expressions aren't allowed - stop here, |
| | 3281 | * returning the expression up to this point |
| | 3282 | */ |
| | 3283 | return sub; |
| | 3284 | } |
| | 3285 | break; |
| | 3286 | |
| | 3287 | case TOKT_INC: |
| | 3288 | /* post-increment */ |
| | 3289 | G_tok->next(); |
| | 3290 | sub = parse_inc(FALSE, sub); |
| | 3291 | break; |
| | 3292 | |
| | 3293 | case TOKT_DEC: |
| | 3294 | /* post-decrement */ |
| | 3295 | G_tok->next(); |
| | 3296 | sub = parse_dec(FALSE, sub); |
| | 3297 | break; |
| | 3298 | |
| | 3299 | default: |
| | 3300 | /* it's not a postfix operator - return the result */ |
| | 3301 | return sub; |
| | 3302 | } |
| | 3303 | |
| | 3304 | /* if the last parse failed, return failure */ |
| | 3305 | if (sub == 0) |
| | 3306 | return 0; |
| | 3307 | } |
| | 3308 | } |
| | 3309 | |
| | 3310 | /* |
| | 3311 | * Parse an argument list |
| | 3312 | */ |
| | 3313 | CTPNArglist *CTcPrsOpUnary::parse_arg_list() |
| | 3314 | { |
| | 3315 | int argc; |
| | 3316 | CTPNArg *arg_head; |
| | 3317 | |
| | 3318 | /* skip the open paren */ |
| | 3319 | G_tok->next(); |
| | 3320 | |
| | 3321 | /* keep going until we find the close paren */ |
| | 3322 | for (argc = 0, arg_head = 0 ;; ) |
| | 3323 | { |
| | 3324 | CTcPrsNode *expr; |
| | 3325 | CTPNArg *arg_cur; |
| | 3326 | |
| | 3327 | /* if this is the close paren, we're done */ |
| | 3328 | if (G_tok->cur() == TOKT_RPAR) |
| | 3329 | break; |
| | 3330 | |
| | 3331 | /* parse this actual parameter expression */ |
| | 3332 | expr = S_op_asi.parse(); |
| | 3333 | if (expr == 0) |
| | 3334 | return 0; |
| | 3335 | |
| | 3336 | /* count the argument */ |
| | 3337 | ++argc; |
| | 3338 | |
| | 3339 | /* create a new argument node */ |
| | 3340 | arg_cur = new CTPNArg(expr); |
| | 3341 | |
| | 3342 | /* check to see if the argument is followed by an ellipsis */ |
| | 3343 | if (G_tok->cur() == TOKT_ELLIPSIS) |
| | 3344 | { |
| | 3345 | /* skip the ellipsis */ |
| | 3346 | G_tok->next(); |
| | 3347 | |
| | 3348 | /* mark the argument as a list-to-varargs parameter */ |
| | 3349 | arg_cur->set_varargs(TRUE); |
| | 3350 | } |
| | 3351 | |
| | 3352 | /* |
| | 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. |
| | 3356 | */ |
| | 3357 | arg_cur->set_next_arg(arg_head); |
| | 3358 | arg_head = arg_cur; |
| | 3359 | |
| | 3360 | /* we need to be looking at a comma, right paren, or ellipsis */ |
| | 3361 | if (G_tok->cur() == TOKT_RPAR) |
| | 3362 | { |
| | 3363 | /* that's the end of the list */ |
| | 3364 | break; |
| | 3365 | } |
| | 3366 | else if (G_tok->cur() == TOKT_COMMA) |
| | 3367 | { |
| | 3368 | /* skip the comma and parse the next argument */ |
| | 3369 | G_tok->next(); |
| | 3370 | } |
| | 3371 | else |
| | 3372 | { |
| | 3373 | /* |
| | 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 |
| | 3380 | * argument. |
| | 3381 | */ |
| | 3382 | switch(G_tok->cur()) |
| | 3383 | { |
| | 3384 | default: |
| | 3385 | /* log an error */ |
| | 3386 | G_tok->log_error_curtok(TCERR_EXPECTED_ARG_COMMA); |
| | 3387 | |
| | 3388 | /* |
| | 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 |
| | 3392 | */ |
| | 3393 | if (G_tok->cur() == TOKT_EOF) |
| | 3394 | return new CTPNArglist(argc, arg_head); |
| | 3395 | break; |
| | 3396 | |
| | 3397 | case TOKT_SEM: |
| | 3398 | case TOKT_LBRACE: |
| | 3399 | case TOKT_RBRACE: |
| | 3400 | case TOKT_DSTR_MID: |
| | 3401 | case TOKT_DSTR_END: |
| | 3402 | /* |
| | 3403 | * we're apparently at the end of the statement; flag |
| | 3404 | * the error as a missing right paren, and return what |
| | 3405 | * we have so far |
| | 3406 | */ |
| | 3407 | G_tok->log_error_curtok(TCERR_EXPECTED_ARG_RPAR); |
| | 3408 | return new CTPNArglist(argc, arg_head); |
| | 3409 | } |
| | 3410 | } |
| | 3411 | } |
| | 3412 | |
| | 3413 | /* skip the closing paren */ |
| | 3414 | G_tok->next(); |
| | 3415 | |
| | 3416 | /* create and return the argument list descriptor */ |
| | 3417 | return new CTPNArglist(argc, arg_head); |
| | 3418 | } |
| | 3419 | |
| | 3420 | /* |
| | 3421 | * Parse a function call expression |
| | 3422 | */ |
| | 3423 | CTcPrsNode *CTcPrsOpUnary::parse_call(CTcPrsNode *lhs) |
| | 3424 | { |
| | 3425 | CTPNArglist *arglist; |
| | 3426 | |
| | 3427 | /* parse the argument list */ |
| | 3428 | arglist = parse_arg_list(); |
| | 3429 | if (arglist == 0) |
| | 3430 | return 0; |
| | 3431 | |
| | 3432 | /* build and return the function call node */ |
| | 3433 | return new CTPNCall(lhs, arglist); |
| | 3434 | } |
| | 3435 | |
| | 3436 | /* |
| | 3437 | * Parse a subscript expression |
| | 3438 | */ |
| | 3439 | CTcPrsNode *CTcPrsOpUnary::parse_subscript(CTcPrsNode *lhs) |
| | 3440 | { |
| | 3441 | CTcPrsNode *subscript; |
| | 3442 | CTcPrsNode *cval; |
| | 3443 | |
| | 3444 | /* skip the '[' */ |
| | 3445 | G_tok->next(); |
| | 3446 | |
| | 3447 | /* parse the expression within the brackets */ |
| | 3448 | subscript = S_op_comma.parse(); |
| | 3449 | if (subscript == 0) |
| | 3450 | return 0; |
| | 3451 | |
| | 3452 | /* check for the matching ']' */ |
| | 3453 | if (G_tok->cur() == TOKT_RBRACK) |
| | 3454 | { |
| | 3455 | /* got it - skip it */ |
| | 3456 | G_tok->next(); |
| | 3457 | } |
| | 3458 | else |
| | 3459 | { |
| | 3460 | /* log an error, and forgive the missing ']' */ |
| | 3461 | G_tok->log_error_curtok(TCERR_EXPECTED_SUB_RBRACK); |
| | 3462 | } |
| | 3463 | |
| | 3464 | /* try folding constants */ |
| | 3465 | cval = eval_const_subscript(lhs, subscript); |
| | 3466 | |
| | 3467 | /* |
| | 3468 | * if that worked, use the result; otherwise, build an expression |
| | 3469 | * node to generate code for the subscript operator |
| | 3470 | */ |
| | 3471 | if (cval != 0) |
| | 3472 | return cval; |
| | 3473 | else |
| | 3474 | return new CTPNSubscript(lhs, subscript); |
| | 3475 | } |
| | 3476 | |
| | 3477 | /* |
| | 3478 | * Evaluate a constant subscript value |
| | 3479 | */ |
| | 3480 | CTcPrsNode *CTcPrsOpUnary::eval_const_subscript(CTcPrsNode *lhs, |
| | 3481 | CTcPrsNode *subscript) |
| | 3482 | { |
| | 3483 | /* |
| | 3484 | * if we're subscripting a constant list by a constant index value, |
| | 3485 | * we can evaluate a constant result |
| | 3486 | */ |
| | 3487 | if (lhs->is_const() && subscript->is_const()) |
| | 3488 | { |
| | 3489 | long idx; |
| | 3490 | CTcPrsNode *ele; |
| | 3491 | |
| | 3492 | /* |
| | 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 |
| | 3496 | */ |
| | 3497 | if (subscript->get_const_val()->get_type() != TC_CVT_INT) |
| | 3498 | { |
| | 3499 | /* we can't use a non-integer expression as a list index */ |
| | 3500 | G_tok->log_error(TCERR_CONST_IDX_NOT_INT); |
| | 3501 | } |
| | 3502 | else if (lhs->get_const_val()->get_type() != TC_CVT_LIST) |
| | 3503 | { |
| | 3504 | /* we can't index any constant type other than list */ |
| | 3505 | G_tok->log_error(TCERR_CONST_IDX_INV_TYPE); |
| | 3506 | } |
| | 3507 | else |
| | 3508 | { |
| | 3509 | /* get the index value */ |
| | 3510 | idx = subscript->get_const_val()->get_val_int(); |
| | 3511 | |
| | 3512 | /* ask the list to look up the item by index */ |
| | 3513 | ele = lhs->get_const_val()->get_val_list()->get_const_ele(idx); |
| | 3514 | |
| | 3515 | /* if we got a valid result, return it */ |
| | 3516 | if (ele != 0) |
| | 3517 | return ele; |
| | 3518 | } |
| | 3519 | } |
| | 3520 | |
| | 3521 | /* we couldn't fold it to a constant expression */ |
| | 3522 | return 0; |
| | 3523 | } |
| | 3524 | |
| | 3525 | /* |
| | 3526 | * Parse a member selection ('.') expression. If no '.' is actually |
| | 3527 | * present, then '.targetprop' is implied. |
| | 3528 | */ |
| | 3529 | CTcPrsNode *CTcPrsOpUnary::parse_member(CTcPrsNode *lhs) |
| | 3530 | { |
| | 3531 | CTcPrsNode *rhs; |
| | 3532 | int rhs_is_expr; |
| | 3533 | |
| | 3534 | /* |
| | 3535 | * If a '.' is present, skip it; otherwise, '.targetprop' is implied. |
| | 3536 | */ |
| | 3537 | if (G_tok->cur() == TOKT_DOT) |
| | 3538 | { |
| | 3539 | /* we have an explicit property expression - skip the '.' */ |
| | 3540 | G_tok->next(); |
| | 3541 | |
| | 3542 | /* assume the property will be a simple symbol, not an expression */ |
| | 3543 | rhs_is_expr = FALSE; |
| | 3544 | |
| | 3545 | /* we could have a symbol or a parenthesized expression */ |
| | 3546 | switch(G_tok->cur()) |
| | 3547 | { |
| | 3548 | case TOKT_SYM: |
| | 3549 | /* |
| | 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 |
| | 3555 | * code generation. |
| | 3556 | */ |
| | 3557 | rhs = new CTPNSym(G_tok->getcur()->get_text(), |
| | 3558 | G_tok->getcur()->get_text_len()); |
| | 3559 | |
| | 3560 | /* skip the symbol token */ |
| | 3561 | G_tok->next(); |
| | 3562 | |
| | 3563 | /* proceed to check for an argument list */ |
| | 3564 | break; |
| | 3565 | |
| | 3566 | case TOKT_LPAR: |
| | 3567 | /* |
| | 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 |
| | 3575 | * parameter list). |
| | 3576 | */ |
| | 3577 | G_tok->next(); |
| | 3578 | |
| | 3579 | /* remember that it's an expression */ |
| | 3580 | rhs_is_expr = TRUE; |
| | 3581 | |
| | 3582 | /* parse the sub-expression */ |
| | 3583 | rhs = S_op_comma.parse(); |
| | 3584 | if (rhs == 0) |
| | 3585 | return 0; |
| | 3586 | |
| | 3587 | /* require the close paren */ |
| | 3588 | if (G_tok->cur() == TOKT_RPAR) |
| | 3589 | { |
| | 3590 | /* skip the closing paren */ |
| | 3591 | G_tok->next(); |
| | 3592 | } |
| | 3593 | else |
| | 3594 | { |
| | 3595 | /* log the error */ |
| | 3596 | G_tok->log_error_curtok(TCERR_EXPR_MISSING_RPAR); |
| | 3597 | |
| | 3598 | /* |
| | 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 |
| | 3604 | */ |
| | 3605 | if (G_tok->cur() == TOKT_SEM || G_tok->cur() == TOKT_EOF) |
| | 3606 | return lhs; |
| | 3607 | } |
| | 3608 | break; |
| | 3609 | |
| | 3610 | case TOKT_TARGETPROP: |
| | 3611 | /* |
| | 3612 | * it's an unparenthesized "targetprop" expression - skip the |
| | 3613 | * keyword |
| | 3614 | */ |
| | 3615 | G_tok->next(); |
| | 3616 | |
| | 3617 | /* |
| | 3618 | * the property value is the result of evaluating |
| | 3619 | * "targetprop", which is an expression |
| | 3620 | */ |
| | 3621 | rhs = new CTPNTargetprop(); |
| | 3622 | rhs_is_expr = TRUE; |
| | 3623 | |
| | 3624 | /* note the reference to the extended method context */ |
| | 3625 | G_prs->set_full_method_ctx_referenced(TRUE); |
| | 3626 | |
| | 3627 | /* go parse the rest */ |
| | 3628 | break; |
| | 3629 | |
| | 3630 | default: |
| | 3631 | /* anything else is invalid - log an error */ |
| | 3632 | G_tok->log_error_curtok(TCERR_INVALID_PROP_EXPR); |
| | 3633 | |
| | 3634 | /* skip the errant token so we don't loop on it */ |
| | 3635 | G_tok->next(); |
| | 3636 | |
| | 3637 | /* return what we have so far */ |
| | 3638 | return lhs; |
| | 3639 | } |
| | 3640 | } |
| | 3641 | else |
| | 3642 | { |
| | 3643 | /* there's no property specified, so '.targetprop' is implied */ |
| | 3644 | rhs = new CTPNTargetprop(); |
| | 3645 | rhs_is_expr = TRUE; |
| | 3646 | |
| | 3647 | /* |
| | 3648 | * note the reference to the full method context (since |
| | 3649 | * 'targetprop' is part of the extended method context beyond |
| | 3650 | * 'self') |
| | 3651 | */ |
| | 3652 | G_prs->set_full_method_ctx_referenced(TRUE); |
| | 3653 | } |
| | 3654 | |
| | 3655 | /* check for an argument list */ |
| | 3656 | if (G_tok->cur() == TOKT_LPAR) |
| | 3657 | { |
| | 3658 | CTPNArglist *arglist; |
| | 3659 | |
| | 3660 | /* parse the argument list */ |
| | 3661 | arglist = parse_arg_list(); |
| | 3662 | if (arglist == 0) |
| | 3663 | return 0; |
| | 3664 | |
| | 3665 | /* create and return a member-with-arguments node */ |
| | 3666 | return new CTPNMemArg(lhs, rhs, rhs_is_expr, arglist); |
| | 3667 | } |
| | 3668 | else |
| | 3669 | { |
| | 3670 | /* |
| | 3671 | * there's no argument list - create and return a simple member |
| | 3672 | * node |
| | 3673 | */ |
| | 3674 | return new CTPNMember(lhs, rhs, rhs_is_expr); |
| | 3675 | } |
| | 3676 | } |
| | 3677 | |
| | 3678 | /* |
| | 3679 | * Parse a double-quoted string with an embedded expression. We treat |
| | 3680 | * this type of expression as though it were a comma expression. |
| | 3681 | */ |
| | 3682 | CTcPrsNode *CTcPrsOpUnary::parse_dstr_embed() |
| | 3683 | { |
| | 3684 | CTcPrsNode *cur; |
| | 3685 | |
| | 3686 | /* |
| | 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. |
| | 3691 | */ |
| | 3692 | if (G_tok->getcur()->get_text_len() != 0) |
| | 3693 | { |
| | 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()); |
| | 3697 | } |
| | 3698 | else |
| | 3699 | { |
| | 3700 | /* |
| | 3701 | * the initial part of the string is empty, so we don't need a node |
| | 3702 | * for this portion |
| | 3703 | */ |
| | 3704 | cur = 0; |
| | 3705 | } |
| | 3706 | |
| | 3707 | /* skip the dstring */ |
| | 3708 | G_tok->next(); |
| | 3709 | |
| | 3710 | /* keep going until we find the end of the string */ |
| | 3711 | for (;;) |
| | 3712 | { |
| | 3713 | CTcPrsNode *sub; |
| | 3714 | int done; |
| | 3715 | |
| | 3716 | /* |
| | 3717 | * parse the embedded expression, which can be any ordinary |
| | 3718 | * expression type, including a double-quoted string expression |
| | 3719 | */ |
| | 3720 | sub = G_prs->parse_expr_or_dstr(TRUE); |
| | 3721 | if (sub == 0) |
| | 3722 | return 0; |
| | 3723 | |
| | 3724 | /* build an embedding node for the expression */ |
| | 3725 | sub = new CTPNDstrEmbed(sub); |
| | 3726 | |
| | 3727 | |
| | 3728 | /* |
| | 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 |
| | 3732 | */ |
| | 3733 | do_next_segment: |
| | 3734 | switch(G_tok->cur()) |
| | 3735 | { |
| | 3736 | case TOKT_DSTR_MID: |
| | 3737 | /* |
| | 3738 | * It's a string with yet another embedded expression. |
| | 3739 | * Simply continue to the next segment. |
| | 3740 | */ |
| | 3741 | done = FALSE; |
| | 3742 | break; |
| | 3743 | |
| | 3744 | case TOKT_DSTR_END: |
| | 3745 | /* |
| | 3746 | * It's the last segment of the string. We can stop after |
| | 3747 | * processing this segment. |
| | 3748 | */ |
| | 3749 | done = TRUE; |
| | 3750 | break; |
| | 3751 | |
| | 3752 | default: |
| | 3753 | /* |
| | 3754 | * anything else is invalid - we must find the end of the |
| | 3755 | * string. Log an error. |
| | 3756 | */ |
| | 3757 | G_tok->log_error_curtok(TCERR_EXPECTED_DSTR_CONT); |
| | 3758 | |
| | 3759 | /* |
| | 3760 | * if this is the end of the file, there's no point in |
| | 3761 | * continuing; return what we have so far |
| | 3762 | */ |
| | 3763 | if (G_tok->cur() == TOKT_EOF) |
| | 3764 | return (cur != 0 ? cur : sub); |
| | 3765 | |
| | 3766 | /* tell the tokenizer to assume the missing '>>' */ |
| | 3767 | G_tok->assume_missing_dstr_cont(); |
| | 3768 | |
| | 3769 | /* go back and try it again */ |
| | 3770 | goto do_next_segment; |
| | 3771 | } |
| | 3772 | |
| | 3773 | /* |
| | 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. |
| | 3779 | */ |
| | 3780 | if (cur != 0) |
| | 3781 | cur = new CTPNComma(cur, sub); |
| | 3782 | else |
| | 3783 | cur = sub; |
| | 3784 | |
| | 3785 | /* |
| | 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. |
| | 3789 | */ |
| | 3790 | if (G_tok->getcur()->get_text_len() != 0) |
| | 3791 | { |
| | 3792 | CTcPrsNode *newstr; |
| | 3793 | |
| | 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()); |
| | 3797 | |
| | 3798 | /* combine it into the part so far with a comma operator */ |
| | 3799 | cur = new CTPNComma(cur, newstr); |
| | 3800 | } |
| | 3801 | |
| | 3802 | /* skip this string part */ |
| | 3803 | G_tok->next(); |
| | 3804 | |
| | 3805 | /* if that was the last segment, this is the final result */ |
| | 3806 | if (done) |
| | 3807 | return cur; |
| | 3808 | } |
| | 3809 | } |
| | 3810 | |
| | 3811 | /* |
| | 3812 | * Parse a primary expression |
| | 3813 | */ |
| | 3814 | CTcPrsNode *CTcPrsOpUnary::parse_primary() |
| | 3815 | { |
| | 3816 | CTcPrsNode *sub; |
| | 3817 | CTcConstVal cval; |
| | 3818 | |
| | 3819 | /* keep going until we find something interesting */ |
| | 3820 | for (;;) |
| | 3821 | { |
| | 3822 | /* determine what we have */ |
| | 3823 | switch(G_tok->cur()) |
| | 3824 | { |
| | 3825 | case TOKT_LBRACE: |
| | 3826 | /* short form of anonymous function */ |
| | 3827 | return parse_anon_func(TRUE); |
| | 3828 | |
| | 3829 | case TOKT_FUNCTION: |
| | 3830 | /* anonymous function requires 'new' */ |
| | 3831 | G_tok->log_error(TCERR_ANON_FUNC_REQ_NEW); |
| | 3832 | |
| | 3833 | /* |
| | 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 |
| | 3837 | * find out |
| | 3838 | */ |
| | 3839 | return parse_anon_func(FALSE); |
| | 3840 | |
| | 3841 | case TOKT_NEW: |
| | 3842 | /* skip the operator and check for 'function' */ |
| | 3843 | if (G_tok->next() == TOKT_FUNCTION) |
| | 3844 | { |
| | 3845 | /* it's an anonymous function definition - go parse it */ |
| | 3846 | sub = parse_anon_func(FALSE); |
| | 3847 | } |
| | 3848 | else |
| | 3849 | { |
| | 3850 | int trans; |
| | 3851 | |
| | 3852 | /* check for the 'transient' keyword */ |
| | 3853 | trans = (G_tok->cur() == TOKT_TRANSIENT); |
| | 3854 | if (trans) |
| | 3855 | G_tok->next(); |
| | 3856 | |
| | 3857 | /* |
| | 3858 | * it's an ordinary 'new' expression - parse the primary |
| | 3859 | * making up the name |
| | 3860 | */ |
| | 3861 | sub = parse_primary(); |
| | 3862 | |
| | 3863 | /* if there's an argument list, parse the argument list */ |
| | 3864 | if (G_tok->cur() == TOKT_LPAR) |
| | 3865 | sub = parse_call(sub); |
| | 3866 | |
| | 3867 | /* create the 'new' node */ |
| | 3868 | sub = parse_new(sub, trans); |
| | 3869 | } |
| | 3870 | return sub; |
| | 3871 | |
| | 3872 | case TOKT_LPAR: |
| | 3873 | /* left parenthesis - skip it */ |
| | 3874 | G_tok->next(); |
| | 3875 | |
| | 3876 | /* parse the expression */ |
| | 3877 | sub = S_op_comma.parse(); |
| | 3878 | if (sub == 0) |
| | 3879 | return 0; |
| | 3880 | |
| | 3881 | /* require the matching right parenthesis */ |
| | 3882 | if (G_tok->cur() == TOKT_RPAR) |
| | 3883 | { |
| | 3884 | /* skip the right paren */ |
| | 3885 | G_tok->next(); |
| | 3886 | } |
| | 3887 | else |
| | 3888 | { |
| | 3889 | /* |
| | 3890 | * log an error; assume that the paren is simply |
| | 3891 | * missing, so continue with our work |
| | 3892 | */ |
| | 3893 | G_tok->log_error_curtok(TCERR_EXPR_MISSING_RPAR); |
| | 3894 | } |
| | 3895 | |
| | 3896 | /* return the parenthesized expression */ |
| | 3897 | return sub; |
| | 3898 | |
| | 3899 | case TOKT_NIL: |
| | 3900 | /* nil - the result is the constant value nil */ |
| | 3901 | cval.set_nil(); |
| | 3902 | |
| | 3903 | return_constant: |
| | 3904 | /* skip the token */ |
| | 3905 | G_tok->next(); |
| | 3906 | |
| | 3907 | /* return a constant node */ |
| | 3908 | return new CTPNConst(&cval); |
| | 3909 | |
| | 3910 | case TOKT_TRUE: |
| | 3911 | /* true - the result is the constant value true */ |
| | 3912 | cval.set_true(); |
| | 3913 | goto return_constant; |
| | 3914 | |
| | 3915 | case TOKT_INT: |
| | 3916 | /* integer - the result is a constant integer value */ |
| | 3917 | cval.set_int(G_tok->getcur()->get_int_val()); |
| | 3918 | goto return_constant; |
| | 3919 | |
| | 3920 | case TOKT_FLOAT: |
| | 3921 | /* floating point number */ |
| | 3922 | cval.set_float(G_tok->getcur()->get_text(), |
| | 3923 | G_tok->getcur()->get_text_len()); |
| | 3924 | goto return_constant; |
| | 3925 | |
| | 3926 | case TOKT_SSTR: |
| | 3927 | handle_sstring: |
| | 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; |
| | 3932 | |
| | 3933 | case TOKT_DSTR: |
| | 3934 | /* |
| | 3935 | * if we're in preprocessor expression mode, treat this the |
| | 3936 | * same as a single-quoted string |
| | 3937 | */ |
| | 3938 | if (G_prs->get_pp_expr_mode()) |
| | 3939 | goto handle_sstring; |
| | 3940 | |
| | 3941 | /* |
| | 3942 | * a string implicitly references 'self', because we could run |
| | 3943 | * through the default output method on the current object |
| | 3944 | */ |
| | 3945 | G_prs->set_self_referenced(TRUE); |
| | 3946 | |
| | 3947 | /* build a double-quoted string node */ |
| | 3948 | sub = new CTPNDstr(G_tok->getcur()->get_text(), |
| | 3949 | G_tok->getcur()->get_text_len()); |
| | 3950 | |
| | 3951 | /* skip the string */ |
| | 3952 | G_tok->next(); |
| | 3953 | |
| | 3954 | /* return the new node */ |
| | 3955 | return sub; |
| | 3956 | |
| | 3957 | case TOKT_DSTR_START: |
| | 3958 | /* a string implicitly references 'self' */ |
| | 3959 | G_prs->set_self_referenced(TRUE); |
| | 3960 | |
| | 3961 | /* parse the embedding expression */ |
| | 3962 | return parse_dstr_embed(); |
| | 3963 | |
| | 3964 | case TOKT_LBRACK: |
| | 3965 | /* parse the list */ |
| | 3966 | return parse_list(); |
| | 3967 | |
| | 3968 | case TOKT_SYM: |
| | 3969 | /* |
| | 3970 | * symbol - the meaning of the symbol is not yet known, so |
| | 3971 | * create an unresolved symbol node |
| | 3972 | */ |
| | 3973 | sub = G_prs->create_sym_node(G_tok->getcur()->get_text(), |
| | 3974 | G_tok->getcur()->get_text_len()); |
| | 3975 | |
| | 3976 | /* skip the symbol token */ |
| | 3977 | G_tok->next(); |
| | 3978 | |
| | 3979 | /* return the new node */ |
| | 3980 | return sub; |
| | 3981 | |
| | 3982 | case TOKT_SELF: |
| | 3983 | /* note the explicit self-reference */ |
| | 3984 | G_prs->set_self_referenced(TRUE); |
| | 3985 | |
| | 3986 | /* generate the "self" node */ |
| | 3987 | G_tok->next(); |
| | 3988 | return new CTPNSelf(); |
| | 3989 | |
| | 3990 | case TOKT_REPLACED: |
| | 3991 | /* generate the "replaced" node */ |
| | 3992 | G_tok->next(); |
| | 3993 | return new CTPNReplaced(); |
| | 3994 | |
| | 3995 | case TOKT_TARGETPROP: |
| | 3996 | /* note the explicit extended method context reference */ |
| | 3997 | G_prs->set_full_method_ctx_referenced(TRUE); |
| | 3998 | |
| | 3999 | /* generate the "targetprop" node */ |
| | 4000 | G_tok->next(); |
| | 4001 | return new CTPNTargetprop(); |
| | 4002 | |
| | 4003 | case TOKT_TARGETOBJ: |
| | 4004 | /* note the explicit extended method context reference */ |
| | 4005 | G_prs->set_full_method_ctx_referenced(TRUE); |
| | 4006 | |
| | 4007 | /* generate the "targetobj" node */ |
| | 4008 | G_tok->next(); |
| | 4009 | return new CTPNTargetobj(); |
| | 4010 | |
| | 4011 | case TOKT_DEFININGOBJ: |
| | 4012 | /* note the explicit extended method context reference */ |
| | 4013 | G_prs->set_full_method_ctx_referenced(TRUE); |
| | 4014 | |
| | 4015 | /* generate the "definingobj" node */ |
| | 4016 | G_tok->next(); |
| | 4017 | return new CTPNDefiningobj(); |
| | 4018 | |
| | 4019 | case TOKT_ARGCOUNT: |
| | 4020 | /* generate the "argcount" node */ |
| | 4021 | G_tok->next(); |
| | 4022 | return new CTPNArgc(); |
| | 4023 | |
| | 4024 | case TOKT_INHERITED: |
| | 4025 | /* parse the "inherited" operation */ |
| | 4026 | return parse_inherited(); |
| | 4027 | |
| | 4028 | case TOKT_DELEGATED: |
| | 4029 | /* parse the "delegated" operation */ |
| | 4030 | return parse_delegated(); |
| | 4031 | |
| | 4032 | case TOKT_RPAR: |
| | 4033 | /* extra right paren - log an error */ |
| | 4034 | G_tok->log_error(TCERR_EXTRA_RPAR); |
| | 4035 | |
| | 4036 | /* skip it and go back for more */ |
| | 4037 | G_tok->next(); |
| | 4038 | break; |
| | 4039 | |
| | 4040 | case TOKT_RBRACK: |
| | 4041 | /* extra right square bracket - log an error */ |
| | 4042 | G_tok->log_error(TCERR_EXTRA_RBRACK); |
| | 4043 | |
| | 4044 | /* skip it and go back for more */ |
| | 4045 | G_tok->next(); |
| | 4046 | break; |
| | 4047 | |
| | 4048 | case TOKT_DSTR_MID: |
| | 4049 | case TOKT_DSTR_END: |
| | 4050 | case TOKT_SEM: |
| | 4051 | case TOKT_RBRACE: |
| | 4052 | /* |
| | 4053 | * this looks like the end of the statement, but we expected |
| | 4054 | * an operand - note the error and end the statement |
| | 4055 | */ |
| | 4056 | G_tok->log_error_curtok(TCERR_EXPECTED_OPERAND); |
| | 4057 | |
| | 4058 | /* |
| | 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. |
| | 4063 | */ |
| | 4064 | cval.set_int(G_tok->getcur()->get_int_val()); |
| | 4065 | return new CTPNConst(&cval); |
| | 4066 | |
| | 4067 | default: |
| | 4068 | /* invalid primary expression - log the error */ |
| | 4069 | G_tok->log_error_curtok(TCERR_BAD_PRIMARY_EXPR); |
| | 4070 | |
| | 4071 | /* |
| | 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. |
| | 4076 | */ |
| | 4077 | G_tok->next(); |
| | 4078 | |
| | 4079 | /* synthesize a constant zero as the operand value */ |
| | 4080 | cval.set_int(G_tok->getcur()->get_int_val()); |
| | 4081 | goto return_constant; |
| | 4082 | } |
| | 4083 | } |
| | 4084 | } |
| | 4085 | |
| | 4086 | /* |
| | 4087 | * Parse an "inherited" expression |
| | 4088 | */ |
| | 4089 | CTcPrsNode *CTcPrsOpUnary::parse_inherited() |
| | 4090 | { |
| | 4091 | CTcPrsNode *lhs; |
| | 4092 | |
| | 4093 | /* skip the "inherited" keyword and check what follows */ |
| | 4094 | switch(G_tok->next()) |
| | 4095 | { |
| | 4096 | case TOKT_SYM: |
| | 4097 | /* |
| | 4098 | * it's an "inherited superclass..." expression - set up the |
| | 4099 | * "inherited superclass" node |
| | 4100 | */ |
| | 4101 | lhs = new CTPNInhClass(G_tok->getcur()->get_text(), |
| | 4102 | G_tok->getcur()->get_text_len()); |
| | 4103 | |
| | 4104 | /* skip the superclass token */ |
| | 4105 | G_tok->next(); |
| | 4106 | |
| | 4107 | /* parse the member expression portion normally */ |
| | 4108 | break; |
| | 4109 | |
| | 4110 | case TOKT_LT: |
| | 4111 | /* |
| | 4112 | * '<' - this is the start of a multi-method type list. Parse the |
| | 4113 | * list: type1 ',' type2 ',' ... '>', then the argument list to the |
| | 4114 | * 'inherited' call. |
| | 4115 | */ |
| | 4116 | { |
| | 4117 | /* create the formal type list */ |
| | 4118 | CTcFormalTypeList *tl = new (G_prsmem)CTcFormalTypeList(); |
| | 4119 | |
| | 4120 | /* skip the '<' */ |
| | 4121 | G_tok->next(); |
| | 4122 | |
| | 4123 | /* parse each list element */ |
| | 4124 | for (int done = FALSE ; !done ; ) |
| | 4125 | { |
| | 4126 | switch (G_tok->cur()) |
| | 4127 | { |
| | 4128 | case TOKT_GT: |
| | 4129 | /* end of the list - skip the '>', and we're done */ |
| | 4130 | G_tok->next(); |
| | 4131 | done = TRUE; |
| | 4132 | break; |
| | 4133 | |
| | 4134 | case TOKT_ELLIPSIS: |
| | 4135 | /* '...' */ |
| | 4136 | tl->add_ellipsis(); |
| | 4137 | |
| | 4138 | /* this has to be the end of the list */ |
| | 4139 | if (G_tok->next() == TOKT_GT) |
| | 4140 | G_tok->next(); |
| | 4141 | else |
| | 4142 | G_tok->log_error_curtok(TCERR_MMINH_MISSING_GT); |
| | 4143 | |
| | 4144 | /* assume the list ends here in any case */ |
| | 4145 | done = TRUE; |
| | 4146 | break; |
| | 4147 | |
| | 4148 | case TOKT_SYM: |
| | 4149 | /* a type token - add it to the list */ |
| | 4150 | tl->add_typed_param(G_tok->getcur()); |
| | 4151 | |
| | 4152 | finish_type: |
| | 4153 | /* skip the type token */ |
| | 4154 | switch (G_tok->next()) |
| | 4155 | { |
| | 4156 | case TOKT_COMMA: |
| | 4157 | /* another type follows */ |
| | 4158 | G_tok->next(); |
| | 4159 | break; |
| | 4160 | |
| | 4161 | case TOKT_GT: |
| | 4162 | G_tok->next(); |
| | 4163 | done = TRUE; |
| | 4164 | break; |
| | 4165 | |
| | 4166 | case TOKT_SYM: |
| | 4167 | case TOKT_ELLIPSIS: |
| | 4168 | case TOKT_TIMES: |
| | 4169 | /* probably just a missing comma */ |
| | 4170 | G_tok->log_error_curtok(TCERR_MMINH_MISSING_COMMA); |
| | 4171 | break; |
| | 4172 | |
| | 4173 | default: |
| | 4174 | /* anything else is an error */ |
| | 4175 | G_tok->log_error_curtok(TCERR_MMINH_MISSING_COMMA); |
| | 4176 | G_tok->next(); |
| | 4177 | break; |
| | 4178 | } |
| | 4179 | break; |
| | 4180 | |
| | 4181 | case TOKT_TIMES: |
| | 4182 | /* '*' indicates an untyped parameter */ |
| | 4183 | tl->add_untyped_param(); |
| | 4184 | goto finish_type; |
| | 4185 | |
| | 4186 | case TOKT_LPAR: |
| | 4187 | /* probably a missing '>' */ |
| | 4188 | G_tok->log_error_curtok(TCERR_MMINH_MISSING_GT); |
| | 4189 | done = TRUE; |
| | 4190 | break; |
| | 4191 | |
| | 4192 | case TOKT_COMMA: |
| | 4193 | /* probably a missing type */ |
| | 4194 | G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_TYPE); |
| | 4195 | G_tok->next(); |
| | 4196 | break; |
| | 4197 | |
| | 4198 | case TOKT_SEM: |
| | 4199 | case TOKT_RPAR: |
| | 4200 | case TOKT_EOF: |
| | 4201 | /* all of these indicate a premature end of the list */ |
| | 4202 | G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_TYPE); |
| | 4203 | return 0; |
| | 4204 | |
| | 4205 | default: |
| | 4206 | /* anything else is an error */ |
| | 4207 | G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_TYPE); |
| | 4208 | G_prs->skip_to_sem(); |
| | 4209 | return 0; |
| | 4210 | } |
| | 4211 | } |
| | 4212 | |
| | 4213 | /* the left-hand side is an "inherited" node, with the arg list */ |
| | 4214 | lhs = new CTPNInh(); |
| | 4215 | ((CTPNInh *)lhs)->set_typelist(tl); |
| | 4216 | |
| | 4217 | /* an inherited<> expression must have an argument list */ |
| | 4218 | if (G_tok->cur() != TOKT_LPAR) |
| | 4219 | { |
| | 4220 | G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_LIST); |
| | 4221 | G_prs->skip_to_sem(); |
| | 4222 | return 0; |
| | 4223 | } |
| | 4224 | } |
| | 4225 | break; |
| | 4226 | |
| | 4227 | default: |
| | 4228 | /* |
| | 4229 | * There's no explicit superclass name listed, so the left-hand |
| | 4230 | * side of the '.' expression is the simple "inherited" node. |
| | 4231 | */ |
| | 4232 | lhs = new CTPNInh(); |
| | 4233 | |
| | 4234 | /* |
| | 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. |
| | 4238 | */ |
| | 4239 | G_prs->set_full_method_ctx_referenced(TRUE); |
| | 4240 | |
| | 4241 | /* parse the member expression portion normally */ |
| | 4242 | break; |
| | 4243 | } |
| | 4244 | |
| | 4245 | /* parse and return the member expression */ |
| | 4246 | return parse_member(lhs); |
| | 4247 | } |
| | 4248 | |
| | 4249 | /* |
| | 4250 | * Parse a "delegated" expression |
| | 4251 | */ |
| | 4252 | CTcPrsNode *CTcPrsOpUnary::parse_delegated() |
| | 4253 | { |
| | 4254 | CTcPrsNode *lhs; |
| | 4255 | CTcPrsNode *target; |
| | 4256 | |
| | 4257 | /* 'delegated' always references 'self' */ |
| | 4258 | G_prs->set_self_referenced(TRUE); |
| | 4259 | |
| | 4260 | /* skip the "delegated" keyword */ |
| | 4261 | G_tok->next(); |
| | 4262 | |
| | 4263 | /* |
| | 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 |
| | 4270 | * call. |
| | 4271 | */ |
| | 4272 | target = parse_postfix(FALSE, FALSE); |
| | 4273 | |
| | 4274 | /* set up the "delegated" node */ |
| | 4275 | lhs = new CTPNDelegated(target); |
| | 4276 | |
| | 4277 | /* return the rest as a normal member expression */ |
| | 4278 | return parse_member(lhs); |
| | 4279 | } |
| | 4280 | |
| | 4281 | /* |
| | 4282 | * Parse a list |
| | 4283 | */ |
| | 4284 | CTcPrsNode *CTcPrsOpUnary::parse_list() |
| | 4285 | { |
| | 4286 | CTPNList *lst; |
| | 4287 | CTcPrsNode *ele; |
| | 4288 | |
| | 4289 | /* skip the opening '[' */ |
| | 4290 | G_tok->next(); |
| | 4291 | |
| | 4292 | /* |
| | 4293 | * create the list expression -- we'll add elements to the list as |
| | 4294 | * we parse the elements |
| | 4295 | */ |
| | 4296 | lst = new CTPNList(); |
| | 4297 | |
| | 4298 | /* scan all list elements */ |
| | 4299 | for (;;) |
| | 4300 | { |
| | 4301 | /* check what we have */ |
| | 4302 | switch(G_tok->cur()) |
| | 4303 | { |
| | 4304 | case TOKT_RBRACK: |
| | 4305 | /* |
| | 4306 | * that's the end of the list - skip the closing bracket and |
| | 4307 | * return the finished list |
| | 4308 | */ |
| | 4309 | G_tok->next(); |
| | 4310 | goto done; |
| | 4311 | |
| | 4312 | case TOKT_EOF: |
| | 4313 | case TOKT_RBRACE: |
| | 4314 | case TOKT_SEM: |
| | 4315 | case TOKT_DSTR_MID: |
| | 4316 | case TOKT_DSTR_END: |
| | 4317 | /* |
| | 4318 | * these would all seem to imply that the closing ']' was |
| | 4319 | * missing from the list; flag the error and end the list |
| | 4320 | * now |
| | 4321 | */ |
| | 4322 | G_tok->log_error_curtok(TCERR_LIST_MISSING_RBRACK); |
| | 4323 | goto done; |
| | 4324 | |
| | 4325 | case TOKT_RPAR: |
| | 4326 | /* |
| | 4327 | * extra right paren - log an error, but then skip the paren |
| | 4328 | * and try to keep parsing |
| | 4329 | */ |
| | 4330 | G_tok->log_error(TCERR_LIST_EXTRA_RPAR); |
| | 4331 | G_tok->next(); |
| | 4332 | break; |
| | 4333 | |
| | 4334 | default: |
| | 4335 | /* it must be the next element expression */ |
| | 4336 | break; |
| | 4337 | } |
| | 4338 | |
| | 4339 | /* |
| | 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. |
| | 4343 | */ |
| | 4344 | ele = S_op_asi.parse(); |
| | 4345 | if (ele == 0) |
| | 4346 | return 0; |
| | 4347 | |
| | 4348 | /* add the element to the list */ |
| | 4349 | lst->add_element(ele); |
| | 4350 | |
| | 4351 | /* check what follows the element */ |
| | 4352 | switch(G_tok->cur()) |
| | 4353 | { |
| | 4354 | case TOKT_COMMA: |
| | 4355 | /* skip the comma introducing the next element */ |
| | 4356 | G_tok->next(); |
| | 4357 | |
| | 4358 | /* if a close bracket follows, we seem to have an extra comma */ |
| | 4359 | if (G_tok->cur() == TOKT_RBRACK) |
| | 4360 | { |
| | 4361 | /* |
| | 4362 | * log an error about the missing element, then end the |
| | 4363 | * list here |
| | 4364 | */ |
| | 4365 | G_tok->log_error_curtok(TCERR_LIST_EXPECT_ELEMENT); |
| | 4366 | goto done; |
| | 4367 | } |
| | 4368 | break; |
| | 4369 | |
| | 4370 | case TOKT_RBRACK: |
| | 4371 | /* |
| | 4372 | * we're done with the list - skip the bracket and return |
| | 4373 | * the finished list |
| | 4374 | */ |
| | 4375 | G_tok->next(); |
| | 4376 | goto done; |
| | 4377 | |
| | 4378 | case TOKT_EOF: |
| | 4379 | case TOKT_LBRACE: |
| | 4380 | case TOKT_RBRACE: |
| | 4381 | case TOKT_SEM: |
| | 4382 | case TOKT_DSTR_MID: |
| | 4383 | case TOKT_DSTR_END: |
| | 4384 | /* |
| | 4385 | * these would all seem to imply that the closing ']' was |
| | 4386 | * missing from the list; flag the error and end the list |
| | 4387 | * now |
| | 4388 | */ |
| | 4389 | G_tok->log_error_curtok(TCERR_LIST_MISSING_RBRACK); |
| | 4390 | goto done; |
| | 4391 | |
| | 4392 | default: |
| | 4393 | /* |
| | 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. |
| | 4400 | */ |
| | 4401 | G_tok->log_error_curtok(TCERR_LIST_EXPECT_COMMA); |
| | 4402 | |
| | 4403 | /* give up on end of file, otherwise keep going */ |
| | 4404 | if (G_tok->cur() == TOKT_EOF) |
| | 4405 | goto done; |
| | 4406 | break; |
| | 4407 | } |
| | 4408 | } |
| | 4409 | |
| | 4410 | done: |
| | 4411 | /* tell the parser to note this list, in case it's the longest yet */ |
| | 4412 | G_cg->note_list(lst->get_count()); |
| | 4413 | |
| | 4414 | /* return the list */ |
| | 4415 | return lst; |
| | 4416 | } |
| | 4417 | |
| | 4418 | |
| | 4419 | /* ------------------------------------------------------------------------ */ |
| | 4420 | /* |
| | 4421 | * Parse Allocation Object |
| | 4422 | */ |
| | 4423 | |
| | 4424 | /* |
| | 4425 | * memory allocator for parse nodes |
| | 4426 | */ |
| | 4427 | void *CTcPrsAllocObj::operator new(size_t siz) |
| | 4428 | { |
| | 4429 | /* allocate the space out of the node pool */ |
| | 4430 | return G_prsmem->alloc(siz); |
| | 4431 | } |
| | 4432 | |
| | 4433 | |
| | 4434 | /* ------------------------------------------------------------------------ */ |
| | 4435 | /* |
| | 4436 | * Parse Tree space manager |
| | 4437 | */ |
| | 4438 | |
| | 4439 | /* |
| | 4440 | * create |
| | 4441 | */ |
| | 4442 | CTcPrsMem::CTcPrsMem() |
| | 4443 | { |
| | 4444 | /* we have no blocks yet */ |
| | 4445 | head_ = tail_ = 0; |
| | 4446 | |
| | 4447 | /* allocate our first block */ |
| | 4448 | alloc_block(); |
| | 4449 | } |
| | 4450 | |
| | 4451 | CTcPrsMem::~CTcPrsMem() |
| | 4452 | { |
| | 4453 | /* delete all objects in our pool */ |
| | 4454 | delete_all(); |
| | 4455 | } |
| | 4456 | |
| | 4457 | /* |
| | 4458 | * Save state, for later resetting |
| | 4459 | */ |
| | 4460 | void CTcPrsMem::save_state(tcprsmem_state_t *state) |
| | 4461 | { |
| | 4462 | /* save the pool information in the state structure */ |
| | 4463 | state->tail = tail_; |
| | 4464 | state->free_ptr = free_ptr_; |
| | 4465 | state->rem = rem_; |
| | 4466 | } |
| | 4467 | |
| | 4468 | /* |
| | 4469 | * Reset to initial state |
| | 4470 | */ |
| | 4471 | void CTcPrsMem::reset() |
| | 4472 | { |
| | 4473 | /* delete all blocks */ |
| | 4474 | delete_all(); |
| | 4475 | |
| | 4476 | /* re-allocate the initial block */ |
| | 4477 | alloc_block(); |
| | 4478 | } |
| | 4479 | |
| | 4480 | /* |
| | 4481 | * Reset. This deletes all objects allocated out of the parser pool |
| | 4482 | * since the state was saved. |
| | 4483 | */ |
| | 4484 | void CTcPrsMem::reset(const tcprsmem_state_t *state) |
| | 4485 | { |
| | 4486 | tcprsmem_blk_t *cur; |
| | 4487 | |
| | 4488 | /* |
| | 4489 | * delete all of the blocks that were allocated after the last block |
| | 4490 | * that existed when the state was saved |
| | 4491 | */ |
| | 4492 | for (cur = state->tail->next_ ; cur != 0 ; ) |
| | 4493 | { |
| | 4494 | tcprsmem_blk_t *nxt; |
| | 4495 | |
| | 4496 | /* remember the next block */ |
| | 4497 | nxt = cur->next_; |
| | 4498 | |
| | 4499 | /* delete this block */ |
| | 4500 | t3free(cur); |
| | 4501 | |
| | 4502 | /* move on to the next one */ |
| | 4503 | cur = nxt; |
| | 4504 | } |
| | 4505 | |
| | 4506 | /* re-establish the saved last block */ |
| | 4507 | tail_ = state->tail; |
| | 4508 | |
| | 4509 | /* make sure the list is terminated at the last block */ |
| | 4510 | tail_->next_ = 0; |
| | 4511 | |
| | 4512 | /* re-establish the saved allocation point in the last block */ |
| | 4513 | free_ptr_ = state->free_ptr; |
| | 4514 | rem_ = state->rem; |
| | 4515 | } |
| | 4516 | |
| | 4517 | /* |
| | 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 |
| | 4520 | * are unreferenced. |
| | 4521 | */ |
| | 4522 | void CTcPrsMem::delete_all() |
| | 4523 | { |
| | 4524 | /* free all blocks */ |
| | 4525 | while (head_ != 0) |
| | 4526 | { |
| | 4527 | tcprsmem_blk_t *nxt; |
| | 4528 | |
| | 4529 | /* remember the next block after this one */ |
| | 4530 | nxt = head_->next_; |
| | 4531 | |
| | 4532 | /* free this block */ |
| | 4533 | t3free(head_); |
| | 4534 | |
| | 4535 | /* move on to the next one */ |
| | 4536 | head_ = nxt; |
| | 4537 | } |
| | 4538 | |
| | 4539 | /* there's no tail now */ |
| | 4540 | tail_ = 0; |
| | 4541 | } |
| | 4542 | |
| | 4543 | /* |
| | 4544 | * allocate a block |
| | 4545 | */ |
| | 4546 | void CTcPrsMem::alloc_block() |
| | 4547 | { |
| | 4548 | tcprsmem_blk_t *blk; |
| | 4549 | |
| | 4550 | /* |
| | 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) |
| | 4554 | */ |
| | 4555 | const size_t BLOCK_SIZE = 65000; |
| | 4556 | |
| | 4557 | /* allocate space for the block */ |
| | 4558 | blk = (tcprsmem_blk_t *)t3malloc(sizeof(tcprsmem_blk_t) + BLOCK_SIZE - 1); |
| | 4559 | |
| | 4560 | /* if that failed, throw an error */ |
| | 4561 | if (blk == 0) |
| | 4562 | err_throw(TCERR_NO_MEM_PRS_TREE); |
| | 4563 | |
| | 4564 | /* link in the block at the end of our list */ |
| | 4565 | blk->next_ = 0; |
| | 4566 | if (tail_ != 0) |
| | 4567 | tail_->next_ = blk; |
| | 4568 | else |
| | 4569 | head_ = blk; |
| | 4570 | |
| | 4571 | /* the block is now the last block in the list */ |
| | 4572 | tail_ = blk; |
| | 4573 | |
| | 4574 | /* |
| | 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 |
| | 4580 | * compilers. |
| | 4581 | */ |
| | 4582 | free_ptr_ = (char *)osrndpt((unsigned char *)blk->buf_); |
| | 4583 | |
| | 4584 | /* |
| | 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) |
| | 4589 | */ |
| | 4590 | rem_ = BLOCK_SIZE - (free_ptr_ - blk->buf_); |
| | 4591 | } |
| | 4592 | |
| | 4593 | /* |
| | 4594 | * Allocate space |
| | 4595 | */ |
| | 4596 | void *CTcPrsMem::alloc(size_t siz) |
| | 4597 | { |
| | 4598 | char *ret; |
| | 4599 | size_t space_used; |
| | 4600 | |
| | 4601 | /* if there's not enough space available, allocate a new block */ |
| | 4602 | if (siz > rem_) |
| | 4603 | { |
| | 4604 | /* allocate a new block */ |
| | 4605 | alloc_block(); |
| | 4606 | |
| | 4607 | /* |
| | 4608 | * if there's still not enough room, the request must exceed the |
| | 4609 | * largest block we can allocate |
| | 4610 | */ |
| | 4611 | if (siz > rem_) |
| | 4612 | G_tok->throw_internal_error(TCERR_PRS_BLK_TOO_BIG, (ulong)siz); |
| | 4613 | } |
| | 4614 | |
| | 4615 | /* return the free pointer */ |
| | 4616 | ret = free_ptr_; |
| | 4617 | |
| | 4618 | /* advance the free pointer past the space, rounding for alignment */ |
| | 4619 | free_ptr_ = (char *)osrndpt((unsigned char *)free_ptr_ + siz); |
| | 4620 | |
| | 4621 | /* deduct the amount of space we consumed from the available space */ |
| | 4622 | space_used = free_ptr_ - ret; |
| | 4623 | if (space_used > rem_) |
| | 4624 | rem_ = 0; |
| | 4625 | else |
| | 4626 | rem_ -= space_used; |
| | 4627 | |
| | 4628 | /* return the allocated space */ |
| | 4629 | return ret; |
| | 4630 | } |
| | 4631 | |
| | 4632 | /* ------------------------------------------------------------------------ */ |
| | 4633 | /* |
| | 4634 | * parse node base class |
| | 4635 | */ |
| | 4636 | |
| | 4637 | /* |
| | 4638 | * By default, an expression cannot be used as a debugger expression |
| | 4639 | */ |
| | 4640 | CTcPrsNode *CTcPrsNodeBase::adjust_for_debug(const tcpn_debug_info *info) |
| | 4641 | { |
| | 4642 | err_throw(VMERR_INVAL_DBG_EXPR); |
| | 4643 | AFTER_ERR_THROW(return 0;) |
| | 4644 | } |
| | 4645 | |
| | 4646 | |
| | 4647 | /* ------------------------------------------------------------------------ */ |
| | 4648 | /* |
| | 4649 | * constant node |
| | 4650 | */ |
| | 4651 | |
| | 4652 | /* |
| | 4653 | * adjust for debugger use |
| | 4654 | */ |
| | 4655 | CTcPrsNode *CTPNConstBase::adjust_for_debug(const tcpn_debug_info *info) |
| | 4656 | { |
| | 4657 | /* convert to a debugger-constant */ |
| | 4658 | return new CTPNDebugConst(&val_); |
| | 4659 | } |
| | 4660 | |
| | 4661 | |
| | 4662 | /* ------------------------------------------------------------------------ */ |
| | 4663 | /* |
| | 4664 | * List parse node |
| | 4665 | */ |
| | 4666 | |
| | 4667 | /* |
| | 4668 | * add an element to a list |
| | 4669 | */ |
| | 4670 | void CTPNListBase::add_element(CTcPrsNode *expr) |
| | 4671 | { |
| | 4672 | CTPNListEle *ele; |
| | 4673 | |
| | 4674 | /* create a list element object for the new element */ |
| | 4675 | ele = new CTPNListEle(expr); |
| | 4676 | |
| | 4677 | /* count the new entry */ |
| | 4678 | ++cnt_; |
| | 4679 | |
| | 4680 | /* add the element to our linked list */ |
| | 4681 | ele->set_prev(tail_); |
| | 4682 | if (tail_ != 0) |
| | 4683 | tail_->set_next(ele); |
| | 4684 | else |
| | 4685 | head_ = ele; |
| | 4686 | tail_ = ele; |
| | 4687 | |
| | 4688 | /* |
| | 4689 | * if the new element does not have a constant value, the list no |
| | 4690 | * longer has a constant value (if it did before) |
| | 4691 | */ |
| | 4692 | if (!expr->is_const()) |
| | 4693 | is_const_ = FALSE; |
| | 4694 | } |
| | 4695 | |
| | 4696 | /* |
| | 4697 | * remove each occurrence of a given constant value from the list |
| | 4698 | */ |
| | 4699 | void CTPNListBase::remove_element(const CTcConstVal *val) |
| | 4700 | { |
| | 4701 | CTPNListEle *cur; |
| | 4702 | |
| | 4703 | /* scan the list */ |
| | 4704 | for (cur = head_ ; cur != 0 ; cur = cur->get_next()) |
| | 4705 | { |
| | 4706 | /* |
| | 4707 | * if this element is constant, compare it to the value to be |
| | 4708 | * removed; if it matches, remove it |
| | 4709 | */ |
| | 4710 | if (cur->get_expr()->is_const() |
| | 4711 | && cur->get_expr()->get_const_val()->is_equal_to(val)) |
| | 4712 | { |
| | 4713 | /* set the previous element's forward pointer */ |
| | 4714 | if (cur->get_prev() == 0) |
| | 4715 | head_ = cur->get_next(); |
| | 4716 | else |
| | 4717 | cur->get_prev()->set_next(cur->get_next()); |
| | 4718 | |
| | 4719 | /* set the next element's back pointer */ |
| | 4720 | if (cur->get_next() == 0) |
| | 4721 | tail_ = cur->get_prev(); |
| | 4722 | else |
| | 4723 | cur->get_next()->set_prev(cur->get_prev()); |
| | 4724 | |
| | 4725 | /* decrement our element counter */ |
| | 4726 | --cnt_; |
| | 4727 | } |
| | 4728 | } |
| | 4729 | } |
| | 4730 | |
| | 4731 | /* |
| | 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. |
| | 4734 | */ |
| | 4735 | CTcPrsNode *CTPNListBase::get_const_ele(int index) |
| | 4736 | { |
| | 4737 | CTPNListEle *ele; |
| | 4738 | |
| | 4739 | /* if the index is negative, it's out of range */ |
| | 4740 | if (index < 1) |
| | 4741 | { |
| | 4742 | /* log the error and return failure */ |
| | 4743 | G_tok->log_error(TCERR_CONST_IDX_RANGE); |
| | 4744 | return 0; |
| | 4745 | } |
| | 4746 | |
| | 4747 | /* scan the list for the given element */ |
| | 4748 | for (ele = head_ ; ele != 0 && index > 1 ; |
| | 4749 | ele = ele->get_next(), --index) ; |
| | 4750 | |
| | 4751 | /* if we ran out of elements, the index is out of range */ |
| | 4752 | if (ele == 0 || index != 1) |
| | 4753 | { |
| | 4754 | G_tok->log_error(TCERR_CONST_IDX_RANGE); |
| | 4755 | return 0; |
| | 4756 | } |
| | 4757 | |
| | 4758 | /* return the element's constant value */ |
| | 4759 | return ele->get_expr(); |
| | 4760 | } |
| | 4761 | |
| | 4762 | /* |
| | 4763 | * Fold constants |
| | 4764 | */ |
| | 4765 | CTcPrsNode *CTPNListBase::fold_constants(CTcPrsSymtab *symtab) |
| | 4766 | { |
| | 4767 | CTPNListEle *cur; |
| | 4768 | int all_const; |
| | 4769 | |
| | 4770 | /* |
| | 4771 | * if the list is already constant, there's nothing extra we need to |
| | 4772 | * do |
| | 4773 | */ |
| | 4774 | if (is_const_) |
| | 4775 | return this; |
| | 4776 | |
| | 4777 | /* presume the result will be all constants */ |
| | 4778 | all_const = TRUE; |
| | 4779 | |
| | 4780 | /* run through my list and fold each element */ |
| | 4781 | for (cur = head_ ; cur != 0 ; cur = cur->get_next()) |
| | 4782 | { |
| | 4783 | /* fold this element */ |
| | 4784 | cur->fold_constants(symtab); |
| | 4785 | |
| | 4786 | /* |
| | 4787 | * if this element is not a constant, the whole list cannot be |
| | 4788 | * constant |
| | 4789 | */ |
| | 4790 | if (!cur->get_expr()->is_const()) |
| | 4791 | all_const = FALSE; |
| | 4792 | } |
| | 4793 | |
| | 4794 | /* if every element was a constant, the overall list is constant */ |
| | 4795 | if (all_const) |
| | 4796 | is_const_ = TRUE; |
| | 4797 | |
| | 4798 | /* return myself */ |
| | 4799 | return this; |
| | 4800 | } |
| | 4801 | |
| | 4802 | /* |
| | 4803 | * Adjust for debugging |
| | 4804 | */ |
| | 4805 | CTcPrsNode *CTPNListBase::adjust_for_debug(const tcpn_debug_info *info) |
| | 4806 | { |
| | 4807 | CTPNListEle *cur; |
| | 4808 | |
| | 4809 | /* run through my list and adjust each element */ |
| | 4810 | for (cur = head_ ; cur != 0 ; cur = cur->get_next()) |
| | 4811 | { |
| | 4812 | /* adjust this element */ |
| | 4813 | cur->adjust_for_debug(info); |
| | 4814 | } |
| | 4815 | |
| | 4816 | /* |
| | 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 |
| | 4821 | */ |
| | 4822 | is_const_ = FALSE; |
| | 4823 | |
| | 4824 | /* return myself */ |
| | 4825 | return this; |
| | 4826 | } |
| | 4827 | |
| | 4828 | /* ------------------------------------------------------------------------ */ |
| | 4829 | /* |
| | 4830 | * conditional operator node base class |
| | 4831 | */ |
| | 4832 | |
| | 4833 | /* |
| | 4834 | * fold constants |
| | 4835 | */ |
| | 4836 | CTcPrsNode *CTPNIfBase::fold_constants(CTcPrsSymtab *symtab) |
| | 4837 | { |
| | 4838 | /* fold constants in the subnodes */ |
| | 4839 | first_ = first_->fold_constants(symtab); |
| | 4840 | second_ = second_->fold_constants(symtab); |
| | 4841 | third_ = third_->fold_constants(symtab); |
| | 4842 | |
| | 4843 | /* |
| | 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 |
| | 4847 | */ |
| | 4848 | if (first_->is_const()) |
| | 4849 | { |
| | 4850 | /* |
| | 4851 | * the condition is a constant - the result is the 'then' or |
| | 4852 | * 'else' part, based on the condition's value |
| | 4853 | */ |
| | 4854 | return (first_->get_const_val()->get_val_bool() |
| | 4855 | ? second_ : third_); |
| | 4856 | } |
| | 4857 | else |
| | 4858 | { |
| | 4859 | /* we can't fold this node any further - return it unchanged */ |
| | 4860 | return this; |
| | 4861 | } |
| | 4862 | } |
| | 4863 | |
| | 4864 | |
| | 4865 | /* ------------------------------------------------------------------------ */ |
| | 4866 | /* |
| | 4867 | * Double-quoted string node - base class |
| | 4868 | */ |
| | 4869 | |
| | 4870 | /* |
| | 4871 | * create a double-quoted string node |
| | 4872 | */ |
| | 4873 | CTPNDstrBase::CTPNDstrBase(const char *str, size_t len) |
| | 4874 | { |
| | 4875 | /* remember the string */ |
| | 4876 | str_ = str; |
| | 4877 | len_ = len; |
| | 4878 | |
| | 4879 | /* |
| | 4880 | * note the length in the parser, in case it's the longest string |
| | 4881 | * we've seen so far |
| | 4882 | */ |
| | 4883 | G_cg->note_str(len); |
| | 4884 | } |
| | 4885 | |
| | 4886 | /* |
| | 4887 | * adjust for debugger use |
| | 4888 | */ |
| | 4889 | CTcPrsNode *CTPNDstrBase::adjust_for_debug(const tcpn_debug_info *info) |
| | 4890 | { |
| | 4891 | /* |
| | 4892 | * don't allow dstring evaluation in speculative mode, since we |
| | 4893 | * can't execute anything with side effects in this mode |
| | 4894 | */ |
| | 4895 | if (info->speculative) |
| | 4896 | err_throw(VMERR_BAD_SPEC_EVAL); |
| | 4897 | |
| | 4898 | /* return a debugger dstring node */ |
| | 4899 | return new CTPNDebugDstr(str_, len_); |
| | 4900 | } |
| | 4901 | |
| | 4902 | /* ------------------------------------------------------------------------ */ |
| | 4903 | /* |
| | 4904 | * Address-of parse node |
| | 4905 | */ |
| | 4906 | |
| | 4907 | /* |
| | 4908 | * fold constants |
| | 4909 | */ |
| | 4910 | CTcPrsNode *CTPNAddrBase::fold_constants(CTcPrsSymtab *symtab) |
| | 4911 | { |
| | 4912 | CTcPrsNode *ret; |
| | 4913 | |
| | 4914 | /* ask the symbol to generate a constant expression for its address */ |
| | 4915 | ret = get_sub_expr()->fold_addr_const(symtab); |
| | 4916 | |
| | 4917 | /* |
| | 4918 | * if we got a constant value, return it; otherwise, return myself |
| | 4919 | * unchanged |
| | 4920 | */ |
| | 4921 | return (ret != 0 ? ret : this); |
| | 4922 | } |
| | 4923 | |
| | 4924 | /* |
| | 4925 | * determine if my address equals that of another node |
| | 4926 | */ |
| | 4927 | int CTPNAddrBase::is_addr_eq(const CTPNAddr *node, int *comparable) const |
| | 4928 | { |
| | 4929 | /* |
| | 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.) |
| | 4936 | */ |
| | 4937 | if (get_sub_expr()->get_sym_text() != 0 |
| | 4938 | && node->get_sub_expr()->get_sym_text() != 0) |
| | 4939 | { |
| | 4940 | CTcPrsNode *sym1; |
| | 4941 | CTcPrsNode *sym2; |
| | 4942 | |
| | 4943 | /* they're both symbols, so they're comparable */ |
| | 4944 | *comparable = TRUE; |
| | 4945 | |
| | 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); |
| | 4952 | } |
| | 4953 | |
| | 4954 | /* they're not comparable */ |
| | 4955 | *comparable = FALSE; |
| | 4956 | return FALSE; |
| | 4957 | } |
| | 4958 | |
| | 4959 | /* ------------------------------------------------------------------------ */ |
| | 4960 | /* |
| | 4961 | * Symbol parse node base class |
| | 4962 | */ |
| | 4963 | |
| | 4964 | /* |
| | 4965 | * fold constants |
| | 4966 | */ |
| | 4967 | CTcPrsNode *CTPNSymBase::fold_constants(CTcPrsSymtab *symtab) |
| | 4968 | { |
| | 4969 | CTcSymbol *sym; |
| | 4970 | CTcPrsNode *ret; |
| | 4971 | |
| | 4972 | /* |
| | 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 |
| | 4977 | * known. |
| | 4978 | */ |
| | 4979 | sym = symtab->find(get_sym_text(), get_sym_text_len()); |
| | 4980 | if (sym != 0) |
| | 4981 | { |
| | 4982 | /* ask the symbol to do the folding */ |
| | 4983 | ret = sym->fold_constant(); |
| | 4984 | |
| | 4985 | /* if that succeeded, return it; otherwise, return unchanged */ |
| | 4986 | return (ret != 0 ? ret : this); |
| | 4987 | } |
| | 4988 | else |
| | 4989 | { |
| | 4990 | /* not defined - return myself unchanged */ |
| | 4991 | return this; |
| | 4992 | } |
| | 4993 | } |
| | 4994 | |
| | 4995 | /* |
| | 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. |
| | 5000 | */ |
| | 5001 | CTcPrsNode *CTPNSymBase::fold_addr_const(CTcPrsSymtab *symtab) |
| | 5002 | { |
| | 5003 | CTcSymbol *sym; |
| | 5004 | |
| | 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()); |
| | 5007 | if (sym != 0) |
| | 5008 | { |
| | 5009 | /* we got a symbol - ask it to do the folding */ |
| | 5010 | return sym->fold_addr_const(); |
| | 5011 | } |
| | 5012 | else |
| | 5013 | { |
| | 5014 | /* undefined symbol - there's no constant address value */ |
| | 5015 | return 0; |
| | 5016 | } |
| | 5017 | } |
| | 5018 | |
| | 5019 | /* |
| | 5020 | * Determine if I have a return value when called |
| | 5021 | */ |
| | 5022 | int CTPNSymBase::has_return_value_on_call() const |
| | 5023 | { |
| | 5024 | CTcSymbol *sym; |
| | 5025 | |
| | 5026 | /* try resolving my symbol */ |
| | 5027 | sym = G_prs->get_global_symtab()->find(sym_, len_); |
| | 5028 | |
| | 5029 | /* |
| | 5030 | * if we found a symbol, let it resolve the call; otherwise, assume |
| | 5031 | * that we do have a return value |
| | 5032 | */ |
| | 5033 | if (sym != 0) |
| | 5034 | return sym->has_return_value_on_call(); |
| | 5035 | else |
| | 5036 | return TRUE; |
| | 5037 | } |
| | 5038 | |
| | 5039 | /* |
| | 5040 | * Determine if I am a valid lvalue |
| | 5041 | */ |
| | 5042 | int CTPNSymBase::check_lvalue_resolved(class CTcPrsSymtab *symtab) const |
| | 5043 | { |
| | 5044 | CTcSymbol *sym; |
| | 5045 | |
| | 5046 | /* look up the symbol in the given scope */ |
| | 5047 | sym = symtab->find(get_sym_text(), get_sym_text_len()); |
| | 5048 | if (sym != 0) |
| | 5049 | { |
| | 5050 | /* ask the symbol what it thinks */ |
| | 5051 | return sym->check_lvalue(); |
| | 5052 | } |
| | 5053 | else |
| | 5054 | { |
| | 5055 | /* it's undefined - can't be an lvalue */ |
| | 5056 | return FALSE; |
| | 5057 | } |
| | 5058 | } |
| | 5059 | |
| | 5060 | /* |
| | 5061 | * Adjust for debugger use |
| | 5062 | */ |
| | 5063 | CTcPrsNode *CTPNSymBase::adjust_for_debug(const tcpn_debug_info *) |
| | 5064 | { |
| | 5065 | /* |
| | 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. |
| | 5072 | */ |
| | 5073 | if (G_prs->get_global_symtab()->find(sym_, len_) == 0) |
| | 5074 | { |
| | 5075 | /* log the error, to generate an appropriate message */ |
| | 5076 | G_tok->log_error(TCERR_UNDEF_SYM, (int)len_, sym_); |
| | 5077 | |
| | 5078 | /* throw the error as well */ |
| | 5079 | err_throw_a(TCERR_UNDEF_SYM, 2, |
| | 5080 | ERR_TYPE_INT, (int)len_, ERR_TYPE_TEXTCHAR, sym_); |
| | 5081 | } |
| | 5082 | |
| | 5083 | /* return myself unchanged */ |
| | 5084 | return this; |
| | 5085 | } |
| | 5086 | |
| | 5087 | |
| | 5088 | /* ------------------------------------------------------------------------ */ |
| | 5089 | /* |
| | 5090 | * Resolved Symbol parse node base class |
| | 5091 | */ |
| | 5092 | |
| | 5093 | /* |
| | 5094 | * fold constants |
| | 5095 | */ |
| | 5096 | CTcPrsNode *CTPNSymResolvedBase::fold_constants(CTcPrsSymtab *symtab) |
| | 5097 | { |
| | 5098 | CTcPrsNode *ret; |
| | 5099 | |
| | 5100 | /* ask the symbol to generate the folded constant value */ |
| | 5101 | ret = sym_->fold_constant(); |
| | 5102 | |
| | 5103 | /* if that succeeded, return it; otherwise, return myself unchanged */ |
| | 5104 | return (ret != 0 ? ret : this); |
| | 5105 | } |
| | 5106 | |
| | 5107 | /* |
| | 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. |
| | 5112 | */ |
| | 5113 | CTcPrsNode *CTPNSymResolvedBase::fold_addr_const(CTcPrsSymtab *symtab) |
| | 5114 | { |
| | 5115 | /* ask my symbol to generate the folded constant value */ |
| | 5116 | return sym_->fold_addr_const(); |
| | 5117 | } |
| | 5118 | |
| | 5119 | |
| | 5120 | /* ------------------------------------------------------------------------ */ |
| | 5121 | /* |
| | 5122 | * Debugger local variable resolved symbol |
| | 5123 | */ |
| | 5124 | |
| | 5125 | /* |
| | 5126 | * construction |
| | 5127 | */ |
| | 5128 | CTPNSymDebugLocalBase::CTPNSymDebugLocalBase(const tcprsdbg_sym_info *info) |
| | 5129 | { |
| | 5130 | /* save the type information */ |
| | 5131 | switch(info->sym_type) |
| | 5132 | { |
| | 5133 | case TC_SYM_LOCAL: |
| | 5134 | var_id_ = info->var_id; |
| | 5135 | ctx_arr_idx_ = info->ctx_arr_idx; |
| | 5136 | frame_idx_ = info->frame_idx; |
| | 5137 | is_param_ = FALSE; |
| | 5138 | break; |
| | 5139 | |
| | 5140 | case TC_SYM_PARAM: |
| | 5141 | var_id_ = info->var_id; |
| | 5142 | ctx_arr_idx_ = 0; |
| | 5143 | frame_idx_ = info->frame_idx; |
| | 5144 | is_param_ = TRUE; |
| | 5145 | break; |
| | 5146 | |
| | 5147 | default: |
| | 5148 | /* other types are invalid */ |
| | 5149 | assert(FALSE); |
| | 5150 | break; |
| | 5151 | } |
| | 5152 | } |
| | 5153 | |
| | 5154 | /* ------------------------------------------------------------------------ */ |
| | 5155 | /* |
| | 5156 | * Argument List parse node base class |
| | 5157 | */ |
| | 5158 | |
| | 5159 | /* |
| | 5160 | * fold constants |
| | 5161 | */ |
| | 5162 | CTcPrsNode *CTPNArglistBase::fold_constants(CTcPrsSymtab *symtab) |
| | 5163 | { |
| | 5164 | CTPNArg *cur; |
| | 5165 | |
| | 5166 | /* fold each list element */ |
| | 5167 | for (cur = get_arg_list_head() ; cur != 0 ; cur = cur->get_next_arg()) |
| | 5168 | cur->fold_constants(symtab); |
| | 5169 | |
| | 5170 | /* return myself with no further folding */ |
| | 5171 | return this; |
| | 5172 | } |
| | 5173 | |
| | 5174 | /* |
| | 5175 | * adjust for debugger use |
| | 5176 | */ |
| | 5177 | CTcPrsNode *CTPNArglistBase::adjust_for_debug(const tcpn_debug_info *info) |
| | 5178 | { |
| | 5179 | CTPNArg *arg; |
| | 5180 | |
| | 5181 | /* adjust each argument list member */ |
| | 5182 | for (arg = list_ ; arg != 0 ; arg = arg->get_next_arg()) |
| | 5183 | { |
| | 5184 | /* |
| | 5185 | * adjust this argument; assume the argument node itself isn't |
| | 5186 | * affected |
| | 5187 | */ |
| | 5188 | arg->adjust_for_debug(info); |
| | 5189 | } |
| | 5190 | |
| | 5191 | /* return myself otherwise unchanged */ |
| | 5192 | return this; |
| | 5193 | } |
| | 5194 | |
| | 5195 | /* ------------------------------------------------------------------------ */ |
| | 5196 | /* |
| | 5197 | * Argument List Entry parse node base class |
| | 5198 | */ |
| | 5199 | |
| | 5200 | /* |
| | 5201 | * fold constants |
| | 5202 | */ |
| | 5203 | CTcPrsNode *CTPNArgBase::fold_constants(CTcPrsSymtab *symtab) |
| | 5204 | { |
| | 5205 | /* fold my argument expression */ |
| | 5206 | arg_expr_ = arg_expr_->fold_constants(symtab); |
| | 5207 | |
| | 5208 | /* return myself unchanged */ |
| | 5209 | return this; |
| | 5210 | } |
| | 5211 | |
| | 5212 | /* ------------------------------------------------------------------------ */ |
| | 5213 | /* |
| | 5214 | * Member with no arguments |
| | 5215 | */ |
| | 5216 | |
| | 5217 | /* |
| | 5218 | * adjust for debugger use |
| | 5219 | */ |
| | 5220 | CTcPrsNode *CTPNMemberBase::adjust_for_debug(const tcpn_debug_info *info) |
| | 5221 | { |
| | 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); |
| | 5225 | |
| | 5226 | /* return myself otherwise unchanged */ |
| | 5227 | return this; |
| | 5228 | } |
| | 5229 | |
| | 5230 | /* ------------------------------------------------------------------------ */ |
| | 5231 | /* |
| | 5232 | * Member with Arguments parse node base class |
| | 5233 | */ |
| | 5234 | |
| | 5235 | /* |
| | 5236 | * fold constants |
| | 5237 | */ |
| | 5238 | CTcPrsNode *CTPNMemArgBase::fold_constants(CTcPrsSymtab *symtab) |
| | 5239 | { |
| | 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); |
| | 5243 | |
| | 5244 | /* |
| | 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 |
| | 5248 | */ |
| | 5249 | arglist_->fold_constants(symtab); |
| | 5250 | |
| | 5251 | /* return myself with no further evaluation */ |
| | 5252 | return this; |
| | 5253 | } |
| | 5254 | |
| | 5255 | /* |
| | 5256 | * adjust for debugger use |
| | 5257 | */ |
| | 5258 | CTcPrsNode *CTPNMemArgBase::adjust_for_debug(const tcpn_debug_info *info) |
| | 5259 | { |
| | 5260 | /* don't allow in speculative mode due to possible side effects */ |
| | 5261 | if (info->speculative) |
| | 5262 | err_throw(VMERR_BAD_SPEC_EVAL); |
| | 5263 | |
| | 5264 | /* |
| | 5265 | * adjust my object expression, property expression, and argument |
| | 5266 | * list |
| | 5267 | */ |
| | 5268 | obj_expr_ = obj_expr_->adjust_for_debug(info); |
| | 5269 | prop_expr_ = prop_expr_->adjust_for_debug(info); |
| | 5270 | arglist_->adjust_for_debug(info); |
| | 5271 | |
| | 5272 | /* return myself otherwise unchanged */ |
| | 5273 | return this; |
| | 5274 | } |
| | 5275 | |
| | 5276 | |
| | 5277 | /* ------------------------------------------------------------------------ */ |
| | 5278 | /* |
| | 5279 | * Function/Method Call parse node base class |
| | 5280 | */ |
| | 5281 | |
| | 5282 | /* |
| | 5283 | * fold constants |
| | 5284 | */ |
| | 5285 | CTcPrsNode *CTPNCallBase::fold_constants(CTcPrsSymtab *symtab) |
| | 5286 | { |
| | 5287 | /* fold my function expression */ |
| | 5288 | func_ = func_->fold_constants(symtab); |
| | 5289 | |
| | 5290 | /* fold my argument list */ |
| | 5291 | arglist_->fold_constants(symtab); |
| | 5292 | |
| | 5293 | /* return myself unchanged */ |
| | 5294 | return this; |
| | 5295 | } |
| | 5296 | |
| | 5297 | /* |
| | 5298 | * adjust for debugger use |
| | 5299 | */ |
| | 5300 | CTcPrsNode *CTPNCallBase::adjust_for_debug(const tcpn_debug_info *info) |
| | 5301 | { |
| | 5302 | /* don't allow in speculative mode because of side effects */ |
| | 5303 | if (info->speculative) |
| | 5304 | err_throw(VMERR_BAD_SPEC_EVAL); |
| | 5305 | |
| | 5306 | /* adjust the function expression */ |
| | 5307 | func_ = func_->adjust_for_debug(info); |
| | 5308 | |
| | 5309 | /* adjust the argument list (assume it doesn't change) */ |
| | 5310 | arglist_->adjust_for_debug(info); |
| | 5311 | |
| | 5312 | /* return myself otherwise unchanged */ |
| | 5313 | return this; |
| | 5314 | } |
| | 5315 | |
| | 5316 | /* ------------------------------------------------------------------------ */ |
| | 5317 | /* |
| | 5318 | * Parser Symbol Table implementation |
| | 5319 | */ |
| | 5320 | |
| | 5321 | /* static hash function */ |
| | 5322 | CVmHashFunc *CTcPrsSymtab::hash_func_ = 0; |
| | 5323 | |
| | 5324 | /* |
| | 5325 | * allocate parser symbol tables out of the parser memory pool |
| | 5326 | */ |
| | 5327 | void *CTcPrsSymtab::operator new(size_t siz) |
| | 5328 | { |
| | 5329 | return G_prsmem->alloc(siz); |
| | 5330 | } |
| | 5331 | |
| | 5332 | /* |
| | 5333 | * static initialization |
| | 5334 | */ |
| | 5335 | void CTcPrsSymtab::s_init() |
| | 5336 | { |
| | 5337 | /* create our static hash function */ |
| | 5338 | if (hash_func_ == 0) |
| | 5339 | hash_func_ = new CVmHashFuncCS(); |
| | 5340 | } |
| | 5341 | |
| | 5342 | /* |
| | 5343 | * static termination |
| | 5344 | */ |
| | 5345 | void CTcPrsSymtab::s_terminate() |
| | 5346 | { |
| | 5347 | /* delete our static hash function */ |
| | 5348 | if (hash_func_ != 0) |
| | 5349 | { |
| | 5350 | delete hash_func_; |
| | 5351 | hash_func_ = 0; |
| | 5352 | } |
| | 5353 | } |
| | 5354 | |
| | 5355 | /* |
| | 5356 | * initialize |
| | 5357 | */ |
| | 5358 | CTcPrsSymtab::CTcPrsSymtab(CTcPrsSymtab *parent_scope) |
| | 5359 | { |
| | 5360 | size_t hash_table_size; |
| | 5361 | |
| | 5362 | /* |
| | 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. |
| | 5367 | * |
| | 5368 | * Note that we always use the static hash function object, hence |
| | 5369 | * the table doesn't own the object. |
| | 5370 | */ |
| | 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]); |
| | 5375 | |
| | 5376 | /* remember the parent scope */ |
| | 5377 | parent_ = parent_scope; |
| | 5378 | |
| | 5379 | /* we're not in a debugger frame list yet */ |
| | 5380 | list_index_ = 0; |
| | 5381 | list_next_ = 0; |
| | 5382 | } |
| | 5383 | |
| | 5384 | /* |
| | 5385 | * delete |
| | 5386 | */ |
| | 5387 | CTcPrsSymtab::~CTcPrsSymtab() |
| | 5388 | { |
| | 5389 | /* delete our underlying hash table */ |
| | 5390 | delete hashtab_; |
| | 5391 | } |
| | 5392 | |
| | 5393 | /* |
| | 5394 | * Find a symbol, marking it as referenced if we find it. |
| | 5395 | */ |
| | 5396 | CTcSymbol *CTcPrsSymtab::find(const textchar_t *sym, size_t len, |
| | 5397 | CTcPrsSymtab **symtab) |
| | 5398 | { |
| | 5399 | CTcSymbol *entry; |
| | 5400 | |
| | 5401 | /* find the symbol */ |
| | 5402 | entry = find_noref(sym, len, symtab); |
| | 5403 | |
| | 5404 | /* if we found an entry, mark it as referenced */ |
| | 5405 | if (entry != 0) |
| | 5406 | entry->mark_referenced(); |
| | 5407 | |
| | 5408 | /* return the result */ |
| | 5409 | return entry; |
| | 5410 | } |
| | 5411 | |
| | 5412 | /* |
| | 5413 | * Find a symbol. This base version does not affect the 'referenced' |
| | 5414 | * status of the symbol we look up. |
| | 5415 | */ |
| | 5416 | CTcSymbol *CTcPrsSymtab::find_noref(const textchar_t *sym, size_t len, |
| | 5417 | CTcPrsSymtab **symtab) |
| | 5418 | { |
| | 5419 | CTcPrsSymtab *curtab; |
| | 5420 | |
| | 5421 | /* |
| | 5422 | * Look for the symbol. Start in the current symbol table, and work |
| | 5423 | * outwards to the outermost enclosing table. |
| | 5424 | */ |
| | 5425 | for (curtab = this ; curtab != 0 ; curtab = curtab->get_parent()) |
| | 5426 | { |
| | 5427 | CTcSymbol *entry; |
| | 5428 | |
| | 5429 | /* look for the symbol in this table */ |
| | 5430 | if ((entry = curtab->find_direct(sym, len)) != 0) |
| | 5431 | { |
| | 5432 | /* |
| | 5433 | * found it - if the caller wants to know about the symbol |
| | 5434 | * table in which we actually found the symbol, return that |
| | 5435 | * information |
| | 5436 | */ |
| | 5437 | if (symtab != 0) |
| | 5438 | *symtab = curtab; |
| | 5439 | |
| | 5440 | /* return the symbol table entry we found */ |
| | 5441 | return entry; |
| | 5442 | } |
| | 5443 | } |
| | 5444 | |
| | 5445 | /* we didn't find the symbol - return failure */ |
| | 5446 | return 0; |
| | 5447 | } |
| | 5448 | |
| | 5449 | /* |
| | 5450 | * Find a symbol directly in this table |
| | 5451 | */ |
| | 5452 | CTcSymbol *CTcPrsSymtab::find_direct(const textchar_t *sym, size_t len) |
| | 5453 | { |
| | 5454 | /* return the entry from our hash table */ |
| | 5455 | return (CTcSymbol *)get_hashtab()->find(sym, len); |
| | 5456 | } |
| | 5457 | |
| | 5458 | /* |
| | 5459 | * Add a formal parameter symbol |
| | 5460 | */ |
| | 5461 | void CTcPrsSymtab::add_formal(const textchar_t *sym, size_t len, |
| | 5462 | int formal_num, int copy_str) |
| | 5463 | { |
| | 5464 | CTcSymLocal *lcl; |
| | 5465 | |
| | 5466 | /* |
| | 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.) |
| | 5471 | */ |
| | 5472 | if (get_hashtab()->find(sym, len) != 0) |
| | 5473 | { |
| | 5474 | /* log an error */ |
| | 5475 | G_tok->log_error(TCERR_FORMAL_REDEF, (int)len, sym); |
| | 5476 | |
| | 5477 | /* don't add the symbol again */ |
| | 5478 | return; |
| | 5479 | } |
| | 5480 | |
| | 5481 | /* create the symbol entry */ |
| | 5482 | lcl = new CTcSymLocal(sym, len, copy_str, TRUE, formal_num); |
| | 5483 | |
| | 5484 | /* add it to the table */ |
| | 5485 | get_hashtab()->add(lcl); |
| | 5486 | } |
| | 5487 | |
| | 5488 | /* |
| | 5489 | * Add a symbol to the table |
| | 5490 | */ |
| | 5491 | void CTcPrsSymtab::add_entry(CTcSymbol *sym) |
| | 5492 | { |
| | 5493 | /* add it to the table */ |
| | 5494 | get_hashtab()->add(sym); |
| | 5495 | } |
| | 5496 | |
| | 5497 | /* |
| | 5498 | * remove a symbol from the table |
| | 5499 | */ |
| | 5500 | void CTcPrsSymtab::remove_entry(CTcSymbol *sym) |
| | 5501 | { |
| | 5502 | /* remove it from the underyling hash table */ |
| | 5503 | get_hashtab()->remove(sym); |
| | 5504 | } |
| | 5505 | |
| | 5506 | /* |
| | 5507 | * Add a local variable symbol |
| | 5508 | */ |
| | 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) |
| | 5512 | { |
| | 5513 | CTcSymLocal *lcl; |
| | 5514 | |
| | 5515 | /* |
| | 5516 | * make sure the symbol isn't already defined in this scope; if it |
| | 5517 | * is, log an error |
| | 5518 | */ |
| | 5519 | if (get_hashtab()->find(sym, len) != 0) |
| | 5520 | { |
| | 5521 | /* log the error */ |
| | 5522 | G_tok->log_error(TCERR_LOCAL_REDEF, (int)len, sym); |
| | 5523 | |
| | 5524 | /* don't create the symbol again - return the original definition */ |
| | 5525 | return 0; |
| | 5526 | } |
| | 5527 | |
| | 5528 | /* create the symbol entry */ |
| | 5529 | lcl = new CTcSymLocal(sym, len, copy_str, FALSE, local_num); |
| | 5530 | |
| | 5531 | /* |
| | 5532 | * if the symbol is initially to be marked as referenced or |
| | 5533 | * assigned, mark it now |
| | 5534 | */ |
| | 5535 | if (init_assigned) |
| | 5536 | lcl->set_val_assigned(TRUE); |
| | 5537 | if (init_referenced) |
| | 5538 | lcl->set_val_used(TRUE); |
| | 5539 | |
| | 5540 | /* add it to the table */ |
| | 5541 | get_hashtab()->add(lcl); |
| | 5542 | |
| | 5543 | /* return the new local */ |
| | 5544 | return lcl; |
| | 5545 | } |
| | 5546 | |
| | 5547 | /* |
| | 5548 | * Add a code label ('goto') symbol |
| | 5549 | */ |
| | 5550 | CTcSymLabel *CTcPrsSymtab::add_code_label(const textchar_t *sym, size_t len, |
| | 5551 | int copy_str) |
| | 5552 | { |
| | 5553 | CTcSymLabel *lbl; |
| | 5554 | |
| | 5555 | /* |
| | 5556 | * make sure the symbol isn't already defined in this scope; if it |
| | 5557 | * is, log an error |
| | 5558 | */ |
| | 5559 | if (get_hashtab()->find(sym, len) != 0) |
| | 5560 | { |
| | 5561 | /* log the error */ |
| | 5562 | G_tok->log_error(TCERR_CODE_LABEL_REDEF, (int)len, sym); |
| | 5563 | |
| | 5564 | /* don't create the symbol again - return the original definition */ |
| | 5565 | return 0; |
| | 5566 | } |
| | 5567 | |
| | 5568 | /* create the symbol entry */ |
| | 5569 | lbl = new CTcSymLabel(sym, len, copy_str); |
| | 5570 | |
| | 5571 | /* add it to the table */ |
| | 5572 | get_hashtab()->add(lbl); |
| | 5573 | |
| | 5574 | /* return the new label */ |
| | 5575 | return lbl; |
| | 5576 | } |
| | 5577 | |
| | 5578 | |
| | 5579 | /* |
| | 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. |
| | 5583 | */ |
| | 5584 | CTcSymbol *CTcPrsSymtab::find_or_def(const textchar_t *sym, size_t len, |
| | 5585 | int copy_str, tcprs_undef_action action) |
| | 5586 | { |
| | 5587 | CTcSymbol *entry; |
| | 5588 | CTcPrsSymtab *curtab; |
| | 5589 | |
| | 5590 | /* |
| | 5591 | * Look for the symbol. Start in the current symbol table, and work |
| | 5592 | * outwards to the outermost enclosing table. |
| | 5593 | */ |
| | 5594 | for (curtab = this ; ; curtab = curtab->get_parent()) |
| | 5595 | { |
| | 5596 | /* look for the symbol in this table */ |
| | 5597 | entry = (CTcSymbol *)curtab->get_hashtab()->find(sym, len); |
| | 5598 | if (entry != 0) |
| | 5599 | { |
| | 5600 | /* mark the entry as referenced */ |
| | 5601 | entry->mark_referenced(); |
| | 5602 | |
| | 5603 | /* found it - return the symbol */ |
| | 5604 | return entry; |
| | 5605 | } |
| | 5606 | |
| | 5607 | /* |
| | 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'. |
| | 5612 | */ |
| | 5613 | if (curtab->get_parent() == 0) |
| | 5614 | { |
| | 5615 | /* check which action we're being asked to perform */ |
| | 5616 | switch(action) |
| | 5617 | { |
| | 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); |
| | 5621 | |
| | 5622 | /* create a new symbol of type undefined */ |
| | 5623 | entry = new CTcSymUndef(sym, len, copy_str); |
| | 5624 | |
| | 5625 | /* finish up */ |
| | 5626 | goto add_entry; |
| | 5627 | |
| | 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); |
| | 5631 | |
| | 5632 | /* create a new symbol of type property */ |
| | 5633 | entry = new CTcSymProp(sym, len, copy_str, |
| | 5634 | G_cg->new_prop_id()); |
| | 5635 | |
| | 5636 | /* finish up */ |
| | 5637 | goto add_entry; |
| | 5638 | |
| | 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()); |
| | 5643 | |
| | 5644 | /* finish up */ |
| | 5645 | goto add_entry; |
| | 5646 | |
| | 5647 | add_entry: |
| | 5648 | /* add the new entry to the global table */ |
| | 5649 | add_to_global_symtab(curtab, entry); |
| | 5650 | |
| | 5651 | /* return the new entry */ |
| | 5652 | return entry; |
| | 5653 | } |
| | 5654 | } |
| | 5655 | } |
| | 5656 | } |
| | 5657 | |
| | 5658 | /* |
| | 5659 | * Enumerate the entries in a symbol table |
| | 5660 | */ |
| | 5661 | void CTcPrsSymtab::enum_entries(void (*func)(void *, CTcSymbol *), |
| | 5662 | void *ctx) |
| | 5663 | { |
| | 5664 | /* |
| | 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 |
| | 5668 | * danger. |
| | 5669 | */ |
| | 5670 | get_hashtab()->enum_entries((void (*)(void *, CVmHashEntry *))func, ctx); |
| | 5671 | } |
| | 5672 | |
| | 5673 | /* |
| | 5674 | * Scan the symbol table for unreferenced local variables |
| | 5675 | */ |
| | 5676 | void CTcPrsSymtab::check_unreferenced_locals() |
| | 5677 | { |
| | 5678 | /* skip the check if we're only parsing for syntax */ |
| | 5679 | if (!G_prs->get_syntax_only()) |
| | 5680 | { |
| | 5681 | /* run the symbols through our unreferenced local check callback */ |
| | 5682 | enum_entries(&unref_local_cb, this); |
| | 5683 | } |
| | 5684 | } |
| | 5685 | |
| | 5686 | /* |
| | 5687 | * Enumeration callback to check for unreferenced locals |
| | 5688 | */ |
| | 5689 | void CTcPrsSymtab::unref_local_cb(void *, CTcSymbol *sym) |
| | 5690 | { |
| | 5691 | /* check the symbol */ |
| | 5692 | sym->check_local_references(); |
| | 5693 | } |
| | 5694 | |
| | 5695 | /* ------------------------------------------------------------------------ */ |
| | 5696 | /* |
| | 5697 | * Comma node |
| | 5698 | */ |
| | 5699 | |
| | 5700 | /* |
| | 5701 | * fold constants |
| | 5702 | */ |
| | 5703 | CTcPrsNode *CTPNCommaBase::fold_binop() |
| | 5704 | { |
| | 5705 | /* use the normal addition folder */ |
| | 5706 | return S_op_comma.eval_constant(left_, right_); |
| | 5707 | } |
| | 5708 | |
| | 5709 | |
| | 5710 | /* ------------------------------------------------------------------------ */ |
| | 5711 | /* |
| | 5712 | * Addition parse node |
| | 5713 | */ |
| | 5714 | |
| | 5715 | /* |
| | 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. |
| | 5721 | */ |
| | 5722 | CTcPrsNode *CTPNAddBase::fold_binop() |
| | 5723 | { |
| | 5724 | /* use the normal addition folder */ |
| | 5725 | return S_op_add.eval_constant(left_, right_); |
| | 5726 | } |
| | 5727 | |
| | 5728 | /* ------------------------------------------------------------------------ */ |
| | 5729 | /* |
| | 5730 | * Subtraction parse node |
| | 5731 | */ |
| | 5732 | |
| | 5733 | /* |
| | 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. |
| | 5739 | */ |
| | 5740 | CTcPrsNode *CTPNSubBase::fold_binop() |
| | 5741 | { |
| | 5742 | /* use the normal addition folder */ |
| | 5743 | return S_op_sub.eval_constant(left_, right_); |
| | 5744 | } |
| | 5745 | |
| | 5746 | /* ------------------------------------------------------------------------ */ |
| | 5747 | /* |
| | 5748 | * Equality Comparison parse node |
| | 5749 | */ |
| | 5750 | |
| | 5751 | /* |
| | 5752 | * fold constants |
| | 5753 | */ |
| | 5754 | CTcPrsNode *CTPNEqBase::fold_binop() |
| | 5755 | { |
| | 5756 | /* use the normal addition folder */ |
| | 5757 | return S_op_eq.eval_constant(left_, right_); |
| | 5758 | } |
| | 5759 | |
| | 5760 | /* ------------------------------------------------------------------------ */ |
| | 5761 | /* |
| | 5762 | * Inequality Comparison parse node |
| | 5763 | */ |
| | 5764 | |
| | 5765 | /* |
| | 5766 | * fold constants |
| | 5767 | */ |
| | 5768 | CTcPrsNode *CTPNNeBase::fold_binop() |
| | 5769 | { |
| | 5770 | /* use the normal addition folder */ |
| | 5771 | return S_op_ne.eval_constant(left_, right_); |
| | 5772 | } |
| | 5773 | |
| | 5774 | /* ------------------------------------------------------------------------ */ |
| | 5775 | /* |
| | 5776 | * Logical AND parse node |
| | 5777 | */ |
| | 5778 | |
| | 5779 | /* |
| | 5780 | * fold constants |
| | 5781 | */ |
| | 5782 | CTcPrsNode *CTPNAndBase::fold_binop() |
| | 5783 | { |
| | 5784 | /* use the normal addition folder */ |
| | 5785 | return S_op_and.eval_constant(left_, right_); |
| | 5786 | } |
| | 5787 | |
| | 5788 | /* ------------------------------------------------------------------------ */ |
| | 5789 | /* |
| | 5790 | * Logical OR parse node |
| | 5791 | */ |
| | 5792 | |
| | 5793 | /* |
| | 5794 | * fold constants |
| | 5795 | */ |
| | 5796 | CTcPrsNode *CTPNOrBase::fold_binop() |
| | 5797 | { |
| | 5798 | /* use the normal addition folder */ |
| | 5799 | return S_op_or.eval_constant(left_, right_); |
| | 5800 | } |
| | 5801 | |
| | 5802 | /* ------------------------------------------------------------------------ */ |
| | 5803 | /* |
| | 5804 | * NOT parse node |
| | 5805 | */ |
| | 5806 | |
| | 5807 | /* |
| | 5808 | * fold constants |
| | 5809 | */ |
| | 5810 | CTcPrsNode *CTPNNotBase::fold_unop() |
| | 5811 | { |
| | 5812 | /* use the normal addition folder */ |
| | 5813 | return S_op_unary.eval_const_not(sub_); |
| | 5814 | } |
| | 5815 | |
| | 5816 | |
| | 5817 | /* ------------------------------------------------------------------------ */ |
| | 5818 | /* |
| | 5819 | * Subscript parse node |
| | 5820 | */ |
| | 5821 | |
| | 5822 | /* |
| | 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. |
| | 5828 | */ |
| | 5829 | /* ------------------------------------------------------------------------ */ |
| | 5830 | /* |
| | 5831 | * Equality Comparison parse node |
| | 5832 | */ |
| | 5833 | |
| | 5834 | /* |
| | 5835 | * fold constants |
| | 5836 | */ |
| | 5837 | CTcPrsNode *CTPNSubscriptBase::fold_binop() |
| | 5838 | { |
| | 5839 | /* use the normal addition folder */ |
| | 5840 | return S_op_unary.eval_const_subscript(left_, right_); |
| | 5841 | } |
| | 5842 | |
| | 5843 | |
| | 5844 | /* ------------------------------------------------------------------------ */ |
| | 5845 | /* |
| | 5846 | * Parser Symbol Table Entry base class |
| | 5847 | */ |
| | 5848 | |
| | 5849 | /* |
| | 5850 | * Allocate symbol objects from the parse pool, since these objects have |
| | 5851 | * all of the lifespan characteristics of pool objects. |
| | 5852 | */ |
| | 5853 | void *CTcSymbolBase::operator new(size_t siz) |
| | 5854 | { |
| | 5855 | return G_prsmem->alloc(siz); |
| | 5856 | } |
| | 5857 | |
| | 5858 | /* |
| | 5859 | * Write to a symbol file. |
| | 5860 | */ |
| | 5861 | int CTcSymbolBase::write_to_sym_file(CVmFile *fp) |
| | 5862 | { |
| | 5863 | /* do the basic writing */ |
| | 5864 | return write_to_file_gen(fp); |
| | 5865 | } |
| | 5866 | |
| | 5867 | /* |
| | 5868 | * Write to a file. This is a generic base routine that can be used for |
| | 5869 | * writing to a symbol or object file. |
| | 5870 | */ |
| | 5871 | int CTcSymbolBase::write_to_file_gen(CVmFile *fp) |
| | 5872 | { |
| | 5873 | /* write my type */ |
| | 5874 | fp->write_int2((int)get_type()); |
| | 5875 | |
| | 5876 | /* write my name */ |
| | 5877 | return write_name_to_file(fp); |
| | 5878 | } |
| | 5879 | |
| | 5880 | /* |
| | 5881 | * Write the symbol name to a file |
| | 5882 | */ |
| | 5883 | int CTcSymbolBase::write_name_to_file(CVmFile *fp) |
| | 5884 | { |
| | 5885 | /* write the length of my symbol name, followed by the symbol name */ |
| | 5886 | fp->write_int2((int)get_sym_len()); |
| | 5887 | |
| | 5888 | /* write the symbol string */ |
| | 5889 | fp->write_bytes(get_sym(), get_sym_len()); |
| | 5890 | |
| | 5891 | /* we wrote the symbol */ |
| | 5892 | return TRUE; |
| | 5893 | } |
| | 5894 | |
| | 5895 | /* |
| | 5896 | * Read a symbol from a symbol file |
| | 5897 | */ |
| | 5898 | CTcSymbol *CTcSymbolBase::read_from_sym_file(CVmFile *fp) |
| | 5899 | { |
| | 5900 | tc_symtype_t typ; |
| | 5901 | |
| | 5902 | /* |
| | 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) |
| | 5905 | */ |
| | 5906 | typ = (tc_symtype_t)fp->read_uint2(); |
| | 5907 | |
| | 5908 | /* create the object based on the type */ |
| | 5909 | switch(typ) |
| | 5910 | { |
| | 5911 | case TC_SYM_FUNC: |
| | 5912 | return CTcSymFunc::read_from_sym_file(fp); |
| | 5913 | |
| | 5914 | case TC_SYM_OBJ: |
| | 5915 | return CTcSymObj::read_from_sym_file(fp); |
| | 5916 | |
| | 5917 | case TC_SYM_PROP: |
| | 5918 | return CTcSymProp::read_from_sym_file(fp); |
| | 5919 | |
| | 5920 | case TC_SYM_ENUM: |
| | 5921 | return CTcSymEnum::read_from_sym_file(fp); |
| | 5922 | |
| | 5923 | default: |
| | 5924 | /* other types should not be in a symbol file */ |
| | 5925 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_SYMEXP_INV_TYPE); |
| | 5926 | return 0; |
| | 5927 | } |
| | 5928 | } |
| | 5929 | |
| | 5930 | /* |
| | 5931 | * Read the basic information from the symbol file |
| | 5932 | */ |
| | 5933 | const char *CTcSymbolBase::base_read_from_sym_file(CVmFile *fp) |
| | 5934 | { |
| | 5935 | char buf[TOK_SYM_MAX_LEN + 1]; |
| | 5936 | |
| | 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); |
| | 5940 | } |
| | 5941 | |
| | 5942 | /* |
| | 5943 | * Write to an object file. |
| | 5944 | */ |
| | 5945 | int CTcSymbolBase::write_to_obj_file(CVmFile *fp) |
| | 5946 | { |
| | 5947 | /* do the basic writing */ |
| | 5948 | return write_to_file_gen(fp); |
| | 5949 | } |
| | 5950 | |
| | 5951 | |
| | 5952 | /* ------------------------------------------------------------------------ */ |
| | 5953 | /* |
| | 5954 | * function symbol entry base |
| | 5955 | */ |
| | 5956 | |
| | 5957 | /* |
| | 5958 | * fold function name into a function address |
| | 5959 | */ |
| | 5960 | CTcPrsNode *CTcSymFuncBase::fold_constant() |
| | 5961 | { |
| | 5962 | CTcConstVal cval; |
| | 5963 | |
| | 5964 | /* set up the function pointer constant */ |
| | 5965 | cval.set_funcptr((CTcSymFunc *)this); |
| | 5966 | |
| | 5967 | /* return a constant node with the function pointer */ |
| | 5968 | return new CTPNConst(&cval); |
| | 5969 | } |
| | 5970 | |
| | 5971 | /* |
| | 5972 | * Write to a symbol file |
| | 5973 | */ |
| | 5974 | int CTcSymFuncBase::write_to_sym_file(CVmFile *fp) |
| | 5975 | { |
| | 5976 | char buf[5]; |
| | 5977 | CTcSymFunc *cur; |
| | 5978 | int ext_modify; |
| | 5979 | |
| | 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()) ; |
| | 5983 | |
| | 5984 | /* we modify an external if the bottom of our modify stack is external */ |
| | 5985 | ext_modify = (cur != 0 && cur->is_extern()); |
| | 5986 | |
| | 5987 | /* |
| | 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. |
| | 5993 | * |
| | 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 |
| | 6002 | * the base symbol. |
| | 6003 | */ |
| | 6004 | if ((is_extern_ || ext_replace_ || ext_modify) && !mm_def_) |
| | 6005 | return FALSE; |
| | 6006 | |
| | 6007 | /* inherit default */ |
| | 6008 | CTcSymbol::write_to_sym_file(fp); |
| | 6009 | |
| | 6010 | /* write our argument count, varargs flag, and return value flag */ |
| | 6011 | oswp2(buf, argc_); |
| | 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); |
| | 6017 | |
| | 6018 | /* we wrote the symbol */ |
| | 6019 | return TRUE; |
| | 6020 | } |
| | 6021 | |
| | 6022 | /* |
| | 6023 | * add an absolute fixup to my list |
| | 6024 | */ |
| | 6025 | void CTcSymFuncBase::add_abs_fixup(CTcDataStream *ds, ulong ofs) |
| | 6026 | { |
| | 6027 | /* ask the code body to add the fixup */ |
| | 6028 | CTcAbsFixup::add_abs_fixup(&fixups_, ds, ofs); |
| | 6029 | } |
| | 6030 | |
| | 6031 | /* |
| | 6032 | * add an absolute fixup at the current stream offset |
| | 6033 | */ |
| | 6034 | void CTcSymFuncBase::add_abs_fixup(CTcDataStream *ds) |
| | 6035 | { |
| | 6036 | /* ask the code body to add the fixup */ |
| | 6037 | CTcAbsFixup::add_abs_fixup(&fixups_, ds, ds->get_ofs()); |
| | 6038 | } |
| | 6039 | |
| | 6040 | /* |
| | 6041 | * Read from a symbol file |
| | 6042 | */ |
| | 6043 | CTcSymbol *CTcSymFuncBase::read_from_sym_file(CVmFile *fp) |
| | 6044 | { |
| | 6045 | char symbuf[4096]; |
| | 6046 | const char *sym; |
| | 6047 | char info[5]; |
| | 6048 | int argc; |
| | 6049 | int varargs; |
| | 6050 | int has_retval; |
| | 6051 | int is_multimethod, is_multimethod_base; |
| | 6052 | |
| | 6053 | /* |
| | 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. |
| | 6057 | */ |
| | 6058 | if ((sym = CTcParser::read_len_prefix_str( |
| | 6059 | fp, symbuf, sizeof(symbuf), 0, TCERR_SYMEXP_SYM_TOO_LONG)) == 0) |
| | 6060 | return 0; |
| | 6061 | |
| | 6062 | /* read the argument count, varargs flag, and return value flag */ |
| | 6063 | fp->read_bytes(info, 5); |
| | 6064 | argc = osrp2(info); |
| | 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); |
| | 6069 | |
| | 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); |
| | 6074 | } |
| | 6075 | |
| | 6076 | /* |
| | 6077 | * Write to an object file |
| | 6078 | */ |
| | 6079 | int CTcSymFuncBase::write_to_obj_file(CVmFile *fp) |
| | 6080 | { |
| | 6081 | char buf[10]; |
| | 6082 | CTcSymFunc *cur; |
| | 6083 | CTcSymFunc *last_mod; |
| | 6084 | int mod_body_cnt; |
| | 6085 | int ext_modify; |
| | 6086 | |
| | 6087 | /* |
| | 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 |
| | 6091 | * the object file. |
| | 6092 | */ |
| | 6093 | if (is_extern_ && fixups_ == 0) |
| | 6094 | return FALSE; |
| | 6095 | |
| | 6096 | /* |
| | 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 |
| | 6101 | * link time. |
| | 6102 | */ |
| | 6103 | for (mod_body_cnt = 0, last_mod = 0, cur = get_mod_base() ; cur != 0 ; |
| | 6104 | last_mod = cur, cur = cur->get_mod_base()) |
| | 6105 | { |
| | 6106 | /* if this one has an associated code body, count it */ |
| | 6107 | if (cur->get_code_body() != 0 && !cur->get_code_body()->is_replaced()) |
| | 6108 | ++mod_body_cnt; |
| | 6109 | } |
| | 6110 | |
| | 6111 | /* we modify an external if the last in the modify chain is external */ |
| | 6112 | ext_modify = (last_mod != 0 && last_mod->is_extern()); |
| | 6113 | |
| | 6114 | /* inherit default */ |
| | 6115 | CTcSymbol::write_to_obj_file(fp); |
| | 6116 | |
| | 6117 | /* |
| | 6118 | * write our argument count, varargs flag, return value, extern flags, |
| | 6119 | * and the number of our modified base functions with code bodies |
| | 6120 | */ |
| | 6121 | oswp2(buf, argc_); |
| | 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); |
| | 6131 | |
| | 6132 | /* if we modify an external, save its fixup list */ |
| | 6133 | if (ext_modify) |
| | 6134 | CTcAbsFixup::write_fixup_list_to_object_file(fp, last_mod->fixups_); |
| | 6135 | |
| | 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()) |
| | 6138 | { |
| | 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()); |
| | 6142 | } |
| | 6143 | |
| | 6144 | /* |
| | 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 |
| | 6148 | * with the anchor. |
| | 6149 | */ |
| | 6150 | if (is_extern_) |
| | 6151 | CTcAbsFixup::write_fixup_list_to_object_file(fp, fixups_); |
| | 6152 | |
| | 6153 | /* we wrote the symbol */ |
| | 6154 | return TRUE; |
| | 6155 | } |
| | 6156 | |
| | 6157 | /* ------------------------------------------------------------------------ */ |
| | 6158 | /* |
| | 6159 | * local variable symbol entry base |
| | 6160 | */ |
| | 6161 | |
| | 6162 | /* |
| | 6163 | * initialize |
| | 6164 | */ |
| | 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)) |
| | 6168 | { |
| | 6169 | /* remember the information */ |
| | 6170 | var_num_ = var_num; |
| | 6171 | is_param_ = is_param; |
| | 6172 | |
| | 6173 | /* presume it's a regular stack variable (not a context local) */ |
| | 6174 | is_ctx_local_ = FALSE; |
| | 6175 | ctx_orig_ = 0; |
| | 6176 | ctx_var_num_ = 0; |
| | 6177 | ctx_level_ = 0; |
| | 6178 | ctx_var_num_set_ = FALSE; |
| | 6179 | |
| | 6180 | /* so far, the value isn't used anywhere */ |
| | 6181 | val_used_ = FALSE; |
| | 6182 | val_assigned_ = FALSE; |
| | 6183 | |
| | 6184 | /* the symbol has not been referenced so far */ |
| | 6185 | referenced_ = FALSE; |
| | 6186 | |
| | 6187 | /* remember where the symbol is defined in the source file */ |
| | 6188 | G_tok->get_last_pos(&src_desc_, &src_linenum_); |
| | 6189 | } |
| | 6190 | |
| | 6191 | /* |
| | 6192 | * Mark the value of the variable as used |
| | 6193 | */ |
| | 6194 | void CTcSymLocalBase::set_val_used(int f) |
| | 6195 | { |
| | 6196 | /* note the new status */ |
| | 6197 | val_used_ = f; |
| | 6198 | |
| | 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); |
| | 6202 | } |
| | 6203 | |
| | 6204 | /* |
| | 6205 | * Mark the value of the variable as assigned |
| | 6206 | */ |
| | 6207 | void CTcSymLocalBase::set_val_assigned(int f) |
| | 6208 | { |
| | 6209 | /* note the new status */ |
| | 6210 | val_assigned_ = f; |
| | 6211 | |
| | 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); |
| | 6215 | } |
| | 6216 | |
| | 6217 | /* |
| | 6218 | * Check for references to this local |
| | 6219 | */ |
| | 6220 | void CTcSymLocalBase::check_local_references() |
| | 6221 | { |
| | 6222 | int err; |
| | 6223 | tc_severity_t sev = TC_SEV_WARNING; |
| | 6224 | |
| | 6225 | /* |
| | 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 |
| | 6230 | */ |
| | 6231 | if (ctx_orig_ != 0) |
| | 6232 | return; |
| | 6233 | |
| | 6234 | /* |
| | 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 |
| | 6238 | */ |
| | 6239 | if (!get_val_used() && (!get_val_assigned() && !is_param())) |
| | 6240 | { |
| | 6241 | /* the variable is never used at all */ |
| | 6242 | err = TCERR_UNREFERENCED_LOCAL; |
| | 6243 | } |
| | 6244 | else if (!get_val_used()) |
| | 6245 | { |
| | 6246 | if (is_param() || is_list_param()) |
| | 6247 | { |
| | 6248 | /* |
| | 6249 | * it's a parameter, or a local that actually contains a |
| | 6250 | * varargs parameter list - generate only a pedantic error |
| | 6251 | */ |
| | 6252 | sev = TC_SEV_PEDANTIC; |
| | 6253 | err = TCERR_UNREFERENCED_PARAM; |
| | 6254 | } |
| | 6255 | else |
| | 6256 | { |
| | 6257 | /* this local is assigned a value that's never used */ |
| | 6258 | err = TCERR_UNUSED_LOCAL_ASSIGNMENT; |
| | 6259 | } |
| | 6260 | } |
| | 6261 | else if (!get_val_assigned() && !is_param()) |
| | 6262 | { |
| | 6263 | /* it's used but never assigned */ |
| | 6264 | err = TCERR_UNASSIGNED_LOCAL; |
| | 6265 | } |
| | 6266 | else |
| | 6267 | { |
| | 6268 | /* no error */ |
| | 6269 | return; |
| | 6270 | } |
| | 6271 | |
| | 6272 | /* |
| | 6273 | * display the warning message, showing the error location as the |
| | 6274 | * source line where the local was defined |
| | 6275 | */ |
| | 6276 | G_tcmain->log_error(get_src_desc(), get_src_linenum(), |
| | 6277 | sev, err, (int)get_sym_len(), get_sym()); |
| | 6278 | } |
| | 6279 | |
| | 6280 | /* |
| | 6281 | * create a new context variable copy of this symbol |
| | 6282 | */ |
| | 6283 | CTcSymbol *CTcSymLocalBase::new_ctx_var() const |
| | 6284 | { |
| | 6285 | CTcSymLocal *lcl; |
| | 6286 | |
| | 6287 | /* create a new local with the same name */ |
| | 6288 | lcl = new CTcSymLocal(get_sym(), get_sym_len(), FALSE, FALSE, 0); |
| | 6289 | |
| | 6290 | /* refer the copy back to the original (i.e., me) */ |
| | 6291 | lcl->set_ctx_orig((CTcSymLocal *)this); |
| | 6292 | |
| | 6293 | /* set up the context variable information */ |
| | 6294 | if (!is_ctx_local_) |
| | 6295 | { |
| | 6296 | /* |
| | 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 |
| | 6300 | * referenced. |
| | 6301 | */ |
| | 6302 | lcl->set_ctx_level(1); |
| | 6303 | } |
| | 6304 | else |
| | 6305 | { |
| | 6306 | /* |
| | 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. |
| | 6310 | */ |
| | 6311 | lcl->set_ctx_level(ctx_level_ + 1); |
| | 6312 | } |
| | 6313 | |
| | 6314 | /* return the new symbol */ |
| | 6315 | return lcl; |
| | 6316 | } |
| | 6317 | |
| | 6318 | /* |
| | 6319 | * Apply context variable conversion |
| | 6320 | */ |
| | 6321 | int CTcSymLocalBase::apply_ctx_var_conv(CTcPrsSymtab *symtab, |
| | 6322 | CTPNCodeBody *code_body) |
| | 6323 | { |
| | 6324 | /* |
| | 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 |
| | 6329 | */ |
| | 6330 | if (!referenced_) |
| | 6331 | { |
| | 6332 | /* remove the symbol from the table */ |
| | 6333 | symtab->remove_entry(this); |
| | 6334 | |
| | 6335 | /* this variable doesn't need to be converted */ |
| | 6336 | return FALSE; |
| | 6337 | } |
| | 6338 | |
| | 6339 | /* |
| | 6340 | * convert the symbol in the enclosing scope to a context local, if |
| | 6341 | * it's not already |
| | 6342 | */ |
| | 6343 | if (ctx_orig_ != 0) |
| | 6344 | { |
| | 6345 | /* convert the original to a context symbol */ |
| | 6346 | ctx_orig_->convert_to_ctx_var(get_val_used(), get_val_assigned()); |
| | 6347 | |
| | 6348 | /* |
| | 6349 | * ask the code body for the context object's local variable for |
| | 6350 | * our recursion level |
| | 6351 | */ |
| | 6352 | ctx_var_num_ = code_body->get_or_add_ctx_var_for_level(ctx_level_); |
| | 6353 | |
| | 6354 | /* note that we've set our context variable ID */ |
| | 6355 | ctx_var_num_set_ = TRUE; |
| | 6356 | |
| | 6357 | /* this variable was converted */ |
| | 6358 | return TRUE; |
| | 6359 | } |
| | 6360 | |
| | 6361 | /* this variable wasn't converted */ |
| | 6362 | return FALSE; |
| | 6363 | } |
| | 6364 | |
| | 6365 | /* |
| | 6366 | * convert this variable to a context variable |
| | 6367 | */ |
| | 6368 | void CTcSymLocalBase::convert_to_ctx_var(int val_used, int val_assigned) |
| | 6369 | { |
| | 6370 | /* if I'm not already a context local, mark me as a context local */ |
| | 6371 | if (!is_ctx_local_) |
| | 6372 | { |
| | 6373 | /* mark myself as a context local */ |
| | 6374 | is_ctx_local_ = TRUE; |
| | 6375 | |
| | 6376 | /* |
| | 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 |
| | 6380 | * this up |
| | 6381 | */ |
| | 6382 | ctx_var_num_ = 0; |
| | 6383 | ctx_arr_idx_ = 0; |
| | 6384 | } |
| | 6385 | |
| | 6386 | /* note that I've been referenced */ |
| | 6387 | mark_referenced(); |
| | 6388 | |
| | 6389 | /* propagate the value-used and value-assigned flags */ |
| | 6390 | if (val_used) |
| | 6391 | set_val_used(TRUE); |
| | 6392 | if (val_assigned) |
| | 6393 | set_val_assigned(TRUE); |
| | 6394 | |
| | 6395 | /* propagate the conversion to the original symbol */ |
| | 6396 | if (ctx_orig_ != 0) |
| | 6397 | ctx_orig_->convert_to_ctx_var(val_used, val_assigned); |
| | 6398 | } |
| | 6399 | |
| | 6400 | /* |
| | 6401 | * finish the context variable conversion |
| | 6402 | */ |
| | 6403 | void CTcSymLocalBase::finish_ctx_var_conv() |
| | 6404 | { |
| | 6405 | /* |
| | 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. |
| | 6409 | * |
| | 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). |
| | 6414 | */ |
| | 6415 | if (!is_ctx_local_ || ctx_var_num_set_) |
| | 6416 | return; |
| | 6417 | |
| | 6418 | /* |
| | 6419 | * tell the parser to create a local context for this scope, if it |
| | 6420 | * hasn't already |
| | 6421 | */ |
| | 6422 | G_prs->init_local_ctx(); |
| | 6423 | |
| | 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; |
| | 6427 | |
| | 6428 | /* assign our array index */ |
| | 6429 | if (ctx_arr_idx_ == 0) |
| | 6430 | ctx_arr_idx_ = G_prs->alloc_ctx_arr_idx(); |
| | 6431 | } |
| | 6432 | |
| | 6433 | /* |
| | 6434 | * Get my context variable array index |
| | 6435 | */ |
| | 6436 | int CTcSymLocalBase::get_ctx_arr_idx() const |
| | 6437 | { |
| | 6438 | /* |
| | 6439 | * if I'm based on an original symbol from another scope, use the |
| | 6440 | * same property ID as the original symbol |
| | 6441 | */ |
| | 6442 | if (ctx_orig_ != 0) |
| | 6443 | return ctx_orig_->get_ctx_arr_idx(); |
| | 6444 | |
| | 6445 | /* return my context property */ |
| | 6446 | return ctx_arr_idx_; |
| | 6447 | } |
| | 6448 | |
| | 6449 | /* ------------------------------------------------------------------------ */ |
| | 6450 | /* |
| | 6451 | * object symbol entry base |
| | 6452 | */ |
| | 6453 | |
| | 6454 | /* |
| | 6455 | * fold the symbol as a constant |
| | 6456 | */ |
| | 6457 | CTcPrsNode *CTcSymObjBase::fold_constant() |
| | 6458 | { |
| | 6459 | CTcConstVal cval; |
| | 6460 | |
| | 6461 | /* set up the object constant */ |
| | 6462 | cval.set_obj(get_obj_id(), get_metaclass()); |
| | 6463 | |
| | 6464 | /* return a constant node */ |
| | 6465 | return new CTPNConst(&cval); |
| | 6466 | } |
| | 6467 | |
| | 6468 | /* |
| | 6469 | * Write to a symbol file |
| | 6470 | */ |
| | 6471 | int CTcSymObjBase::write_to_sym_file(CVmFile *fp) |
| | 6472 | { |
| | 6473 | int result; |
| | 6474 | |
| | 6475 | /* |
| | 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. |
| | 6484 | */ |
| | 6485 | if (is_extern_ || modified_ || ext_modify_ || ext_replace_) |
| | 6486 | return FALSE; |
| | 6487 | |
| | 6488 | /* inherit default */ |
| | 6489 | result = CTcSymbol::write_to_sym_file(fp); |
| | 6490 | |
| | 6491 | /* if that was successful, write additional object-type-specific data */ |
| | 6492 | if (result) |
| | 6493 | { |
| | 6494 | /* write the metaclass ID */ |
| | 6495 | fp->write_int2((int)metaclass_); |
| | 6496 | |
| | 6497 | /* if it's of metaclass tads-object, write superclass information */ |
| | 6498 | if (metaclass_ == TC_META_TADSOBJ) |
| | 6499 | { |
| | 6500 | char c; |
| | 6501 | size_t cnt; |
| | 6502 | CTPNSuperclass *sc; |
| | 6503 | |
| | 6504 | /* |
| | 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' |
| | 6507 | * object |
| | 6508 | */ |
| | 6509 | c = ((sc_is_root() ? 1 : 0) |
| | 6510 | | (is_class() ? 2 : 0) |
| | 6511 | | (is_transient() ? 4 : 0)); |
| | 6512 | fp->write_bytes(&c, 1); |
| | 6513 | |
| | 6514 | /* count the declared superclasses */ |
| | 6515 | for (cnt = 0, sc = sc_name_head_ ; sc != 0 ; |
| | 6516 | sc = sc->nxt_, ++cnt) ; |
| | 6517 | |
| | 6518 | /* |
| | 6519 | * write the number of declared superclasses followed by the |
| | 6520 | * names of the superclasses |
| | 6521 | */ |
| | 6522 | fp->write_int2(cnt); |
| | 6523 | for (sc = sc_name_head_ ; sc != 0 ; sc = sc->nxt_) |
| | 6524 | { |
| | 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()); |
| | 6528 | } |
| | 6529 | } |
| | 6530 | } |
| | 6531 | |
| | 6532 | /* return the result */ |
| | 6533 | return result; |
| | 6534 | } |
| | 6535 | |
| | 6536 | /* |
| | 6537 | * Read from a symbol file |
| | 6538 | */ |
| | 6539 | CTcSymbol *CTcSymObjBase::read_from_sym_file(CVmFile *fp) |
| | 6540 | { |
| | 6541 | const char *txt; |
| | 6542 | tc_metaclass_t meta; |
| | 6543 | CTcSymObj *sym; |
| | 6544 | char c; |
| | 6545 | size_t cnt; |
| | 6546 | size_t i; |
| | 6547 | |
| | 6548 | /* read the symbol name */ |
| | 6549 | if ((txt = base_read_from_sym_file(fp)) == 0) |
| | 6550 | return 0; |
| | 6551 | |
| | 6552 | /* read the metaclass ID */ |
| | 6553 | meta = (tc_metaclass_t)fp->read_uint2(); |
| | 6554 | |
| | 6555 | /* |
| | 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. |
| | 6559 | * |
| | 6560 | * The same applies to grammar productions, since a grammar production |
| | 6561 | * can be implicitly created in multiple files. |
| | 6562 | */ |
| | 6563 | if (meta == TC_META_DICT || meta == TC_META_GRAMPROD) |
| | 6564 | { |
| | 6565 | CTcSymbol *old_sym; |
| | 6566 | |
| | 6567 | /* look for a previous instance of the symbol */ |
| | 6568 | old_sym = G_prs->get_global_symtab()->find(txt, strlen(txt)); |
| | 6569 | if (old_sym != 0 |
| | 6570 | && old_sym->get_type() == TC_SYM_OBJ |
| | 6571 | && ((CTcSymObj *)old_sym)->get_metaclass() == meta) |
| | 6572 | { |
| | 6573 | /* |
| | 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 |
| | 6577 | */ |
| | 6578 | return old_sym; |
| | 6579 | } |
| | 6580 | } |
| | 6581 | |
| | 6582 | /* create the new symbol */ |
| | 6583 | sym = new CTcSymObj(txt, strlen(txt), FALSE, G_cg->new_obj_id(), |
| | 6584 | TRUE, meta, 0); |
| | 6585 | |
| | 6586 | /* if the metaclass is tads-object, read additional information */ |
| | 6587 | if (meta == TC_META_TADSOBJ) |
| | 6588 | { |
| | 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); |
| | 6593 | if ((c & 4) != 0) |
| | 6594 | sym->set_transient(); |
| | 6595 | |
| | 6596 | /* read the number of superclasses, and read the superclass names */ |
| | 6597 | cnt = fp->read_uint2(); |
| | 6598 | for (i = 0 ; i < cnt ; ++i) |
| | 6599 | { |
| | 6600 | char buf[TOK_SYM_MAX_LEN + 1]; |
| | 6601 | const char *sc_txt; |
| | 6602 | size_t sc_len; |
| | 6603 | |
| | 6604 | /* read the symbol */ |
| | 6605 | sc_txt = CTcParser::read_len_prefix_str( |
| | 6606 | fp, buf, sizeof(buf), &sc_len, TCERR_SYMEXP_SYM_TOO_LONG); |
| | 6607 | |
| | 6608 | /* add the superclass list entry to the symbol */ |
| | 6609 | sym->add_sc_name_entry(sc_txt, sc_len); |
| | 6610 | } |
| | 6611 | } |
| | 6612 | |
| | 6613 | /* return the symbol */ |
| | 6614 | return sym; |
| | 6615 | } |
| | 6616 | |
| | 6617 | /* |
| | 6618 | * Add a superclass name entry. |
| | 6619 | */ |
| | 6620 | void CTcSymObjBase::add_sc_name_entry(const char *txt, size_t len) |
| | 6621 | { |
| | 6622 | CTPNSuperclass *entry; |
| | 6623 | |
| | 6624 | /* create the entry object */ |
| | 6625 | entry = new CTPNSuperclass(txt, len); |
| | 6626 | |
| | 6627 | /* link it into our list */ |
| | 6628 | if (sc_name_tail_ != 0) |
| | 6629 | sc_name_tail_->nxt_ = entry; |
| | 6630 | else |
| | 6631 | sc_name_head_ = entry; |
| | 6632 | sc_name_tail_ = entry; |
| | 6633 | } |
| | 6634 | |
| | 6635 | /* |
| | 6636 | * Check to see if I have a given superclass. |
| | 6637 | */ |
| | 6638 | int CTcSymObjBase::has_superclass(class CTcSymObj *sc_sym) const |
| | 6639 | { |
| | 6640 | CTPNSuperclass *entry; |
| | 6641 | |
| | 6642 | /* |
| | 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. |
| | 6646 | */ |
| | 6647 | for (entry = sc_name_head_ ; entry != 0 ; entry = entry->nxt_) |
| | 6648 | { |
| | 6649 | CTcSymObj *entry_sym; |
| | 6650 | |
| | 6651 | /* look up this symbol */ |
| | 6652 | entry_sym = (CTcSymObj *)G_prs->get_global_symtab()->find( |
| | 6653 | entry->get_sym_txt(), entry->get_sym_len()); |
| | 6654 | |
| | 6655 | /* |
| | 6656 | * if the entry's symbol doesn't exist or isn't an object symbol, |
| | 6657 | * skip it |
| | 6658 | */ |
| | 6659 | if (entry_sym == 0 || entry_sym->get_type() != TC_SYM_OBJ) |
| | 6660 | continue; |
| | 6661 | |
| | 6662 | /* |
| | 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 |
| | 6666 | */ |
| | 6667 | if (entry_sym == sc_sym) |
| | 6668 | return TRUE; |
| | 6669 | |
| | 6670 | /* |
| | 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 |
| | 6674 | */ |
| | 6675 | if (entry_sym->has_superclass(sc_sym)) |
| | 6676 | return TRUE; |
| | 6677 | } |
| | 6678 | |
| | 6679 | /* |
| | 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 |
| | 6682 | */ |
| | 6683 | return FALSE; |
| | 6684 | } |
| | 6685 | |
| | 6686 | /* |
| | 6687 | * Synthesize a placeholder symbol for a modified object. |
| | 6688 | * |
| | 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. |
| | 6693 | */ |
| | 6694 | CTcSymObj *CTcSymObjBase::synthesize_modified_obj_sym(int anon) |
| | 6695 | { |
| | 6696 | char nm[TOK_SYM_MAX_LEN + 1]; |
| | 6697 | const char *stored_nm; |
| | 6698 | tc_obj_id mod_id; |
| | 6699 | CTcSymObj *mod_sym; |
| | 6700 | size_t len; |
| | 6701 | |
| | 6702 | /* create a new ID for the modified object */ |
| | 6703 | mod_id = G_cg->new_obj_id(); |
| | 6704 | |
| | 6705 | /* build the name */ |
| | 6706 | if (anon) |
| | 6707 | { |
| | 6708 | /* it's anonymous - we don't need a real name */ |
| | 6709 | stored_nm = ".anon"; |
| | 6710 | len = strlen(nm); |
| | 6711 | } |
| | 6712 | else |
| | 6713 | { |
| | 6714 | /* synthesize a name */ |
| | 6715 | synthesize_modified_obj_name(nm, mod_id); |
| | 6716 | len = strlen(nm); |
| | 6717 | |
| | 6718 | /* store it in tokenizer memory */ |
| | 6719 | stored_nm = G_tok->store_source(nm, len); |
| | 6720 | } |
| | 6721 | |
| | 6722 | /* create the object */ |
| | 6723 | mod_sym = new CTcSymObj(stored_nm, len, FALSE, mod_id, FALSE, |
| | 6724 | TC_META_TADSOBJ, 0); |
| | 6725 | |
| | 6726 | /* mark it as modified */ |
| | 6727 | mod_sym->set_modified(TRUE); |
| | 6728 | |
| | 6729 | /* add it to the symbol table, if it has a name */ |
| | 6730 | if (!anon) |
| | 6731 | G_prs->get_global_symtab()->add_entry(mod_sym); |
| | 6732 | else |
| | 6733 | G_prs->add_anon_obj(mod_sym); |
| | 6734 | |
| | 6735 | /* return the new symbol */ |
| | 6736 | return mod_sym; |
| | 6737 | } |
| | 6738 | |
| | 6739 | /* |
| | 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. |
| | 6742 | */ |
| | 6743 | void CTcSymObjBase:: |
| | 6744 | synthesize_modified_obj_name(char *buf, tctarg_obj_id_t obj_id) |
| | 6745 | { |
| | 6746 | /* |
| | 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. |
| | 6750 | */ |
| | 6751 | sprintf(buf, " %lx", (ulong)obj_id); |
| | 6752 | } |
| | 6753 | |
| | 6754 | /* |
| | 6755 | * Add a deleted property entry |
| | 6756 | */ |
| | 6757 | void CTcSymObjBase::add_del_prop_to_list(CTcObjPropDel **list_head, |
| | 6758 | CTcSymProp *prop_sym) |
| | 6759 | { |
| | 6760 | CTcObjPropDel *entry; |
| | 6761 | |
| | 6762 | /* create the new entry */ |
| | 6763 | entry = new CTcObjPropDel(prop_sym); |
| | 6764 | |
| | 6765 | /* link it into my list */ |
| | 6766 | entry->nxt_ = *list_head; |
| | 6767 | *list_head = entry; |
| | 6768 | } |
| | 6769 | |
| | 6770 | /* |
| | 6771 | * Add a self-reference fixup |
| | 6772 | */ |
| | 6773 | void CTcSymObjBase::add_self_ref_fixup(CTcDataStream *stream, ulong ofs) |
| | 6774 | { |
| | 6775 | /* add a fixup to our list */ |
| | 6776 | CTcIdFixup::add_fixup(&fixups_, stream, ofs, obj_id_); |
| | 6777 | } |
| | 6778 | |
| | 6779 | /* |
| | 6780 | * Write to a object file |
| | 6781 | */ |
| | 6782 | int CTcSymObjBase::write_to_obj_file(CVmFile *fp) |
| | 6783 | { |
| | 6784 | /* |
| | 6785 | * If the object is external and has never been referenced, don't |
| | 6786 | * bother writing it. |
| | 6787 | * |
| | 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 |
| | 6791 | * modifying object. |
| | 6792 | */ |
| | 6793 | if ((is_extern_ && !ref_) || modified_) |
| | 6794 | return FALSE; |
| | 6795 | |
| | 6796 | /* if the object has already been written, don't write it again */ |
| | 6797 | if (written_to_obj_) |
| | 6798 | { |
| | 6799 | /* |
| | 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 |
| | 6804 | * anything now |
| | 6805 | */ |
| | 6806 | if (!counted_in_obj_) |
| | 6807 | { |
| | 6808 | /* we've now been counted in the object file */ |
| | 6809 | counted_in_obj_ = TRUE; |
| | 6810 | |
| | 6811 | /* indicate that we have been written */ |
| | 6812 | return TRUE; |
| | 6813 | } |
| | 6814 | else |
| | 6815 | { |
| | 6816 | /* we've already been written and counted - don't write again */ |
| | 6817 | return FALSE; |
| | 6818 | } |
| | 6819 | } |
| | 6820 | |
| | 6821 | /* do the main part of the writing */ |
| | 6822 | return write_to_obj_file_main(fp); |
| | 6823 | } |
| | 6824 | |
| | 6825 | /* |
| | 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. |
| | 6829 | */ |
| | 6830 | int CTcSymObjBase::write_to_obj_file_main(CVmFile *fp) |
| | 6831 | { |
| | 6832 | char buf[32]; |
| | 6833 | uint cnt; |
| | 6834 | CTcObjPropDel *delprop; |
| | 6835 | |
| | 6836 | /* take the next object file index */ |
| | 6837 | set_obj_file_idx(G_prs->get_next_obj_file_sym_idx()); |
| | 6838 | |
| | 6839 | /* |
| | 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 |
| | 6843 | */ |
| | 6844 | if (dict_ != 0) |
| | 6845 | dict_->write_sym_to_obj_file(fp); |
| | 6846 | |
| | 6847 | /* |
| | 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) |
| | 6851 | */ |
| | 6852 | if (!anon_) |
| | 6853 | write_to_file_gen(fp); |
| | 6854 | |
| | 6855 | /* |
| | 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 |
| | 6858 | * in the image file |
| | 6859 | */ |
| | 6860 | oswp4(buf, obj_id_); |
| | 6861 | |
| | 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); |
| | 6870 | |
| | 6871 | /* add the metaclass type */ |
| | 6872 | oswp2(buf + 11, (int)metaclass_); |
| | 6873 | |
| | 6874 | /* add the dictionary's object file index, if we have one */ |
| | 6875 | if (dict_ != 0) |
| | 6876 | oswp2(buf + 13, dict_->get_obj_idx()); |
| | 6877 | else |
| | 6878 | oswp2(buf + 13, 0); |
| | 6879 | |
| | 6880 | /* |
| | 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) |
| | 6885 | */ |
| | 6886 | oswp2(buf + 15, get_obj_file_idx()); |
| | 6887 | |
| | 6888 | /* write the data to the file */ |
| | 6889 | fp->write_bytes(buf, 17); |
| | 6890 | |
| | 6891 | /* if we're not external, write our stream address */ |
| | 6892 | if (!is_extern_) |
| | 6893 | fp->write_int4(stream_ofs_); |
| | 6894 | |
| | 6895 | /* if we're modifying another object, store some extra information */ |
| | 6896 | if (mod_base_sym_ != 0) |
| | 6897 | { |
| | 6898 | /* |
| | 6899 | * Write our list of properties to be deleted from base objects |
| | 6900 | * at link time. First, count the properties in the list. |
| | 6901 | */ |
| | 6902 | for (cnt = 0, delprop = first_del_prop_ ; delprop != 0 ; |
| | 6903 | ++cnt, delprop = delprop->nxt_) ; |
| | 6904 | |
| | 6905 | /* write the count */ |
| | 6906 | fp->write_int2(cnt); |
| | 6907 | |
| | 6908 | /* write the deleted property list */ |
| | 6909 | for (delprop = first_del_prop_ ; delprop != 0 ; |
| | 6910 | delprop = delprop->nxt_) |
| | 6911 | { |
| | 6912 | /* |
| | 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) |
| | 6918 | */ |
| | 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()); |
| | 6922 | } |
| | 6923 | } |
| | 6924 | |
| | 6925 | /* write our self-reference fixup list */ |
| | 6926 | CTcIdFixup::write_to_object_file(fp, fixups_); |
| | 6927 | |
| | 6928 | /* |
| | 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. |
| | 6935 | */ |
| | 6936 | if (mod_base_sym_ != 0) |
| | 6937 | { |
| | 6938 | /* write the main part of the definition */ |
| | 6939 | mod_base_sym_->write_to_obj_file_main(fp); |
| | 6940 | } |
| | 6941 | |
| | 6942 | /* mark the object as written to the file */ |
| | 6943 | written_to_obj_ = TRUE; |
| | 6944 | |
| | 6945 | /* written */ |
| | 6946 | return TRUE; |
| | 6947 | } |
| | 6948 | |
| | 6949 | /* |
| | 6950 | * Write cross-references to the object file |
| | 6951 | */ |
| | 6952 | int CTcSymObjBase::write_refs_to_obj_file(CVmFile *fp) |
| | 6953 | { |
| | 6954 | CTPNSuperclass *sc; |
| | 6955 | uint cnt; |
| | 6956 | long cnt_pos; |
| | 6957 | long end_pos; |
| | 6958 | CTcVocabEntry *voc; |
| | 6959 | |
| | 6960 | /* |
| | 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 |
| | 6963 | */ |
| | 6964 | if (!written_to_obj_) |
| | 6965 | return FALSE; |
| | 6966 | |
| | 6967 | /* write my symbol index */ |
| | 6968 | fp->write_int4(get_obj_file_idx()); |
| | 6969 | |
| | 6970 | /* write a placeholder superclass count */ |
| | 6971 | cnt_pos = fp->get_pos(); |
| | 6972 | fp->write_int2(0); |
| | 6973 | |
| | 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_) |
| | 6977 | { |
| | 6978 | CTcSymObj *sym; |
| | 6979 | |
| | 6980 | /* look up this superclass symbol */ |
| | 6981 | sym = (CTcSymObj *)sc->get_sym(); |
| | 6982 | if (sym != 0 && sym->get_type() == TC_SYM_OBJ) |
| | 6983 | { |
| | 6984 | /* write the superclass symbol index */ |
| | 6985 | fp->write_int4(sym->get_obj_file_idx()); |
| | 6986 | |
| | 6987 | /* count it */ |
| | 6988 | ++cnt; |
| | 6989 | } |
| | 6990 | } |
| | 6991 | |
| | 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); |
| | 6997 | |
| | 6998 | /* count my vocabulary words */ |
| | 6999 | for (cnt = 0, voc = vocab_ ; voc != 0 ; ++cnt, voc = voc->nxt_) ; |
| | 7000 | |
| | 7001 | /* write my vocabulary words */ |
| | 7002 | fp->write_int2(cnt); |
| | 7003 | for (voc = vocab_ ; voc != 0 ; voc = voc->nxt_) |
| | 7004 | { |
| | 7005 | /* write the text of the word */ |
| | 7006 | fp->write_int2(voc->len_); |
| | 7007 | fp->write_bytes(voc->txt_, voc->len_); |
| | 7008 | |
| | 7009 | /* write the property ID */ |
| | 7010 | fp->write_int2(voc->prop_); |
| | 7011 | } |
| | 7012 | |
| | 7013 | /* indicate that we wrote the symbol */ |
| | 7014 | return TRUE; |
| | 7015 | } |
| | 7016 | |
| | 7017 | /* |
| | 7018 | * Load references from the object file |
| | 7019 | */ |
| | 7020 | void CTcSymObjBase::load_refs_from_obj_file(CVmFile *fp, const char *, |
| | 7021 | tctarg_obj_id_t *, |
| | 7022 | tctarg_prop_id_t *prop_xlat) |
| | 7023 | { |
| | 7024 | uint i; |
| | 7025 | uint cnt; |
| | 7026 | CTcObjScEntry *sc_tail; |
| | 7027 | |
| | 7028 | /* read the superclass count */ |
| | 7029 | cnt = fp->read_uint2(); |
| | 7030 | |
| | 7031 | /* read the superclass list */ |
| | 7032 | for (sc_tail = 0, i = 0 ; i < cnt ; ++i) |
| | 7033 | { |
| | 7034 | ulong idx; |
| | 7035 | CTcSymObj *sym; |
| | 7036 | CTcObjScEntry *sc; |
| | 7037 | |
| | 7038 | /* read the next index */ |
| | 7039 | idx = fp->read_uint4(); |
| | 7040 | |
| | 7041 | /* get the symbol */ |
| | 7042 | sym = (CTcSymObj *)G_prs->get_objfile_sym(idx); |
| | 7043 | if (sym->get_type() != TC_SYM_OBJ) |
| | 7044 | sym = 0; |
| | 7045 | |
| | 7046 | /* create a new list entry */ |
| | 7047 | sc = new (G_prsmem) CTcObjScEntry(sym); |
| | 7048 | |
| | 7049 | /* link it in at the end of the my superclass list */ |
| | 7050 | if (sc_tail != 0) |
| | 7051 | sc_tail->nxt_ = sc; |
| | 7052 | else |
| | 7053 | sc_ = sc; |
| | 7054 | |
| | 7055 | /* this is now the last entry in my superclass list */ |
| | 7056 | sc_tail = sc; |
| | 7057 | } |
| | 7058 | |
| | 7059 | /* load the vocabulary words */ |
| | 7060 | cnt = fp->read_uint2(); |
| | 7061 | for (i = 0 ; i < cnt ; ++i) |
| | 7062 | { |
| | 7063 | size_t len; |
| | 7064 | char *txt; |
| | 7065 | tctarg_prop_id_t prop; |
| | 7066 | |
| | 7067 | /* read the length of this word's text */ |
| | 7068 | len = fp->read_uint2(); |
| | 7069 | |
| | 7070 | /* allocate parser memory for the word's text */ |
| | 7071 | txt = (char *)G_prsmem->alloc(len); |
| | 7072 | |
| | 7073 | /* read the word into the allocated text buffer */ |
| | 7074 | fp->read_bytes(txt, len); |
| | 7075 | |
| | 7076 | /* read the property */ |
| | 7077 | prop = (tctarg_prop_id_t)fp->read_uint2(); |
| | 7078 | |
| | 7079 | /* translate the property to the new numbering system */ |
| | 7080 | prop = prop_xlat[prop]; |
| | 7081 | |
| | 7082 | /* add the word to our vocabulary */ |
| | 7083 | add_vocab_word(txt, len, prop); |
| | 7084 | } |
| | 7085 | } |
| | 7086 | |
| | 7087 | /* |
| | 7088 | * Add a word to my vocabulary |
| | 7089 | */ |
| | 7090 | void CTcSymObjBase::add_vocab_word(const char *txt, size_t len, |
| | 7091 | tctarg_prop_id_t prop) |
| | 7092 | { |
| | 7093 | CTcVocabEntry *entry; |
| | 7094 | |
| | 7095 | /* create a new vocabulary entry */ |
| | 7096 | entry = new (G_prsmem) CTcVocabEntry(txt, len, prop); |
| | 7097 | |
| | 7098 | /* link it into my list */ |
| | 7099 | entry->nxt_ = vocab_; |
| | 7100 | vocab_ = entry; |
| | 7101 | } |
| | 7102 | |
| | 7103 | /* |
| | 7104 | * Delete a vocabulary property from my list (for 'replace') |
| | 7105 | */ |
| | 7106 | void CTcSymObjBase::delete_vocab_prop(tctarg_prop_id_t prop) |
| | 7107 | { |
| | 7108 | CTcVocabEntry *entry; |
| | 7109 | CTcVocabEntry *prv; |
| | 7110 | CTcVocabEntry *nxt; |
| | 7111 | |
| | 7112 | /* scan my list and delete each word defined for the given property */ |
| | 7113 | for (prv = 0, entry = vocab_ ; entry != 0 ; entry = nxt) |
| | 7114 | { |
| | 7115 | /* remember the next entry */ |
| | 7116 | nxt = entry->nxt_; |
| | 7117 | |
| | 7118 | /* if this entry is for the given property, unlink it */ |
| | 7119 | if (entry->prop_ == prop) |
| | 7120 | { |
| | 7121 | /* |
| | 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 |
| | 7125 | * deleted) |
| | 7126 | */ |
| | 7127 | if (prv != 0) |
| | 7128 | prv->nxt_ = nxt; |
| | 7129 | else |
| | 7130 | vocab_ = nxt; |
| | 7131 | |
| | 7132 | /* |
| | 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) |
| | 7138 | */ |
| | 7139 | entry->nxt_ = 0; |
| | 7140 | } |
| | 7141 | else |
| | 7142 | { |
| | 7143 | /* |
| | 7144 | * this entry is still in the list, so it's now the previous |
| | 7145 | * entry for our scan |
| | 7146 | */ |
| | 7147 | prv = entry; |
| | 7148 | } |
| | 7149 | } |
| | 7150 | } |
| | 7151 | |
| | 7152 | /* |
| | 7153 | * Build my dictionary |
| | 7154 | */ |
| | 7155 | void CTcSymObjBase::build_dictionary() |
| | 7156 | { |
| | 7157 | CTcVocabEntry *entry; |
| | 7158 | |
| | 7159 | /* if I don't have a dictionary, there's nothing to do */ |
| | 7160 | if (dict_ == 0) |
| | 7161 | return; |
| | 7162 | |
| | 7163 | /* |
| | 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 |
| | 7167 | */ |
| | 7168 | if (is_class_) |
| | 7169 | return; |
| | 7170 | |
| | 7171 | /* add inherited words from my superclasses to my list */ |
| | 7172 | inherit_vocab(); |
| | 7173 | |
| | 7174 | /* add each of my words to the dictionary */ |
| | 7175 | for (entry = vocab_ ; entry != 0 ; entry = entry->nxt_) |
| | 7176 | { |
| | 7177 | /* add this word to my dictionary */ |
| | 7178 | dict_->add_word(entry->txt_, entry->len_, FALSE, |
| | 7179 | obj_id_, entry->prop_); |
| | 7180 | } |
| | 7181 | } |
| | 7182 | |
| | 7183 | /* |
| | 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. |
| | 7187 | */ |
| | 7188 | void CTcSymObjBase::inherit_vocab() |
| | 7189 | { |
| | 7190 | CTcObjScEntry *sc; |
| | 7191 | |
| | 7192 | /* |
| | 7193 | * if I've already inherited my superclass vocabulary, there's |
| | 7194 | * nothing more we need to do |
| | 7195 | */ |
| | 7196 | if (vocab_inherited_) |
| | 7197 | return; |
| | 7198 | |
| | 7199 | /* make a note that I've inherited my superclass vocabulary */ |
| | 7200 | vocab_inherited_ = TRUE; |
| | 7201 | |
| | 7202 | /* inherit words from each superclass */ |
| | 7203 | for (sc = sc_ ; sc != 0 ; sc = sc->nxt_) |
| | 7204 | { |
| | 7205 | /* make sure this superclass has built its inherited list */ |
| | 7206 | sc->sym_->inherit_vocab(); |
| | 7207 | |
| | 7208 | /* add this superclass's words to my list */ |
| | 7209 | sc->sym_->add_vocab_to_subclass((CTcSymObj *)this); |
| | 7210 | } |
| | 7211 | } |
| | 7212 | |
| | 7213 | /* |
| | 7214 | * Add my vocabulary words to the given subclass's vocabulary list |
| | 7215 | */ |
| | 7216 | void CTcSymObjBase::add_vocab_to_subclass(CTcSymObj *sub) |
| | 7217 | { |
| | 7218 | CTcVocabEntry *entry; |
| | 7219 | |
| | 7220 | /* add each of my words to the subclass */ |
| | 7221 | for (entry = vocab_ ; entry != 0 ; entry = entry->nxt_) |
| | 7222 | { |
| | 7223 | /* add this word to my dictionary */ |
| | 7224 | sub->add_vocab_word(entry->txt_, entry->len_, entry->prop_); |
| | 7225 | } |
| | 7226 | } |
| | 7227 | |
| | 7228 | /* |
| | 7229 | * Set my base 'modify' object. This tells us the object that we're |
| | 7230 | * modifying. |
| | 7231 | */ |
| | 7232 | void CTcSymObjBase::set_mod_base_sym(CTcSymObj *sym) |
| | 7233 | { |
| | 7234 | /* remember the object I'm modifying */ |
| | 7235 | mod_base_sym_ = sym; |
| | 7236 | |
| | 7237 | /* |
| | 7238 | * set the other object's link back to me, so it knows that I'm the |
| | 7239 | * object that's modifying it |
| | 7240 | */ |
| | 7241 | if (sym != 0) |
| | 7242 | sym->set_modifying_sym((CTcSymObj *)this); |
| | 7243 | } |
| | 7244 | |
| | 7245 | /* |
| | 7246 | * Get the appropriate stream for a given metaclass |
| | 7247 | */ |
| | 7248 | CTcDataStream *CTcSymObjBase::get_stream_from_meta(tc_metaclass_t meta) |
| | 7249 | { |
| | 7250 | switch(meta) |
| | 7251 | { |
| | 7252 | case TC_META_TADSOBJ: |
| | 7253 | /* it's the regular object stream */ |
| | 7254 | return G_os; |
| | 7255 | |
| | 7256 | case TC_META_ICMOD: |
| | 7257 | /* intrinsic class modifier stream */ |
| | 7258 | return G_icmod_stream; |
| | 7259 | |
| | 7260 | default: |
| | 7261 | /* other metaclasses have no stream */ |
| | 7262 | return 0; |
| | 7263 | } |
| | 7264 | } |
| | 7265 | |
| | 7266 | /* |
| | 7267 | * Add a class-specific template |
| | 7268 | */ |
| | 7269 | void CTcSymObjBase::add_template(CTcObjTemplate *tpl) |
| | 7270 | { |
| | 7271 | /* link it in at the tail of our list */ |
| | 7272 | if (template_tail_ != 0) |
| | 7273 | template_tail_->nxt_ = tpl; |
| | 7274 | else |
| | 7275 | template_head_ = tpl; |
| | 7276 | template_tail_ = tpl; |
| | 7277 | } |
| | 7278 | |
| | 7279 | /* |
| | 7280 | * Create a grammar rule list object |
| | 7281 | */ |
| | 7282 | CTcGramProdEntry *CTcSymObjBase::create_grammar_entry( |
| | 7283 | const char *prod_sym, size_t prod_sym_len) |
| | 7284 | { |
| | 7285 | CTcSymObj *sym; |
| | 7286 | |
| | 7287 | /* look up the grammar production symbol */ |
| | 7288 | sym = G_prs->find_or_def_gramprod(prod_sym, prod_sym_len, 0); |
| | 7289 | |
| | 7290 | /* create a new grammar list associated with the production */ |
| | 7291 | grammar_entry_ = new (G_prsmem) CTcGramProdEntry(sym); |
| | 7292 | |
| | 7293 | /* return the new grammar list */ |
| | 7294 | return grammar_entry_; |
| | 7295 | } |
| | 7296 | |
| | 7297 | |
| | 7298 | /* ------------------------------------------------------------------------ */ |
| | 7299 | /* |
| | 7300 | * metaclass symbol |
| | 7301 | */ |
| | 7302 | |
| | 7303 | /* |
| | 7304 | * add a property |
| | 7305 | */ |
| | 7306 | void CTcSymMetaclassBase::add_prop(const char *txt, size_t len, |
| | 7307 | const char *obj_fname, int is_static) |
| | 7308 | { |
| | 7309 | CTcSymProp *prop_sym; |
| | 7310 | |
| | 7311 | /* see if this property is already defined */ |
| | 7312 | prop_sym = (CTcSymProp *)G_prs->get_global_symtab()->find(txt, len); |
| | 7313 | if (prop_sym != 0) |
| | 7314 | { |
| | 7315 | /* it's already defined - make sure it's a property */ |
| | 7316 | if (prop_sym->get_type() != TC_SYM_PROP) |
| | 7317 | { |
| | 7318 | /* |
| | 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 |
| | 7322 | */ |
| | 7323 | if (obj_fname == 0) |
| | 7324 | { |
| | 7325 | /* creating from source - note the code location */ |
| | 7326 | G_tok->log_error_curtok(TCERR_REDEF_AS_PROP); |
| | 7327 | } |
| | 7328 | else |
| | 7329 | { |
| | 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); |
| | 7334 | } |
| | 7335 | |
| | 7336 | /* forget the symbol - it's not a property */ |
| | 7337 | prop_sym = 0; |
| | 7338 | } |
| | 7339 | } |
| | 7340 | else |
| | 7341 | { |
| | 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); |
| | 7345 | } |
| | 7346 | |
| | 7347 | /* |
| | 7348 | * if we found a valid property symbol, add it to the metaclass |
| | 7349 | * property list |
| | 7350 | */ |
| | 7351 | if (prop_sym != 0) |
| | 7352 | { |
| | 7353 | /* |
| | 7354 | * mark the symbol as referenced - even if we don't directly |
| | 7355 | * make use of it, the metaclass table references this symbol |
| | 7356 | */ |
| | 7357 | prop_sym->mark_referenced(); |
| | 7358 | |
| | 7359 | /* add the property to the metaclass list */ |
| | 7360 | add_prop(prop_sym, is_static); |
| | 7361 | } |
| | 7362 | } |
| | 7363 | |
| | 7364 | /* |
| | 7365 | * add a property |
| | 7366 | */ |
| | 7367 | void CTcSymMetaclassBase::add_prop(class CTcSymProp *prop, int is_static) |
| | 7368 | { |
| | 7369 | CTcSymMetaProp *entry; |
| | 7370 | |
| | 7371 | /* create a new list entry for the property */ |
| | 7372 | entry = new (G_prsmem) CTcSymMetaProp(prop, is_static); |
| | 7373 | |
| | 7374 | /* link it at the end of our list */ |
| | 7375 | if (prop_tail_ != 0) |
| | 7376 | prop_tail_->nxt_ = entry; |
| | 7377 | else |
| | 7378 | prop_head_ = entry; |
| | 7379 | prop_tail_ = entry; |
| | 7380 | |
| | 7381 | /* count the addition */ |
| | 7382 | ++prop_cnt_; |
| | 7383 | } |
| | 7384 | |
| | 7385 | /* |
| | 7386 | * write some additional data to the object file |
| | 7387 | */ |
| | 7388 | int CTcSymMetaclassBase::write_to_obj_file(class CVmFile *fp) |
| | 7389 | { |
| | 7390 | CTcSymMetaProp *cur; |
| | 7391 | char buf[16]; |
| | 7392 | |
| | 7393 | /* inherit default */ |
| | 7394 | CTcSymbol::write_to_obj_file(fp); |
| | 7395 | |
| | 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_); |
| | 7400 | |
| | 7401 | /* write my property symbol list */ |
| | 7402 | for (cur = prop_head_ ; cur != 0 ; cur = cur->nxt_) |
| | 7403 | { |
| | 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()); |
| | 7407 | |
| | 7408 | /* set up the flags */ |
| | 7409 | buf[0] = 0; |
| | 7410 | if (cur->is_static_) |
| | 7411 | buf[0] |= 1; |
| | 7412 | |
| | 7413 | /* write the flags */ |
| | 7414 | fp->write_bytes(buf, 1); |
| | 7415 | } |
| | 7416 | |
| | 7417 | /* write our modifying object flag */ |
| | 7418 | buf[0] = (mod_obj_ != 0); |
| | 7419 | fp->write_bytes(buf, 1); |
| | 7420 | |
| | 7421 | /* if we have a modifier object chain, write it out */ |
| | 7422 | if (mod_obj_ != 0) |
| | 7423 | mod_obj_->write_to_obj_file_as_modified(fp); |
| | 7424 | |
| | 7425 | /* written */ |
| | 7426 | return TRUE; |
| | 7427 | } |
| | 7428 | |
| | 7429 | /* |
| | 7430 | * get the nth property in our table |
| | 7431 | */ |
| | 7432 | CTcSymMetaProp *CTcSymMetaclassBase::get_nth_prop(int n) const |
| | 7433 | { |
| | 7434 | CTcSymMetaProp *prop; |
| | 7435 | |
| | 7436 | /* traverse the list to the desired index */ |
| | 7437 | for (prop = prop_head_ ; prop != 0 && n != 0 ; prop = prop->nxt_, --n) ; |
| | 7438 | |
| | 7439 | /* return the property */ |
| | 7440 | return prop; |
| | 7441 | } |
| | 7442 | |
| | 7443 | |
| | 7444 | /* ------------------------------------------------------------------------ */ |
| | 7445 | /* |
| | 7446 | * property symbol entry base |
| | 7447 | */ |
| | 7448 | |
| | 7449 | /* |
| | 7450 | * fold an address constant |
| | 7451 | */ |
| | 7452 | CTcPrsNode *CTcSymPropBase::fold_addr_const() |
| | 7453 | { |
| | 7454 | CTcConstVal cval; |
| | 7455 | |
| | 7456 | /* set up the property pointer constant */ |
| | 7457 | cval.set_prop(get_prop()); |
| | 7458 | |
| | 7459 | /* return a constant node */ |
| | 7460 | return new CTPNConst(&cval); |
| | 7461 | } |
| | 7462 | |
| | 7463 | /* |
| | 7464 | * Read from a symbol file |
| | 7465 | */ |
| | 7466 | CTcSymbol *CTcSymPropBase::read_from_sym_file(CVmFile *fp) |
| | 7467 | { |
| | 7468 | const char *sym; |
| | 7469 | CTcSymbol *old_entry; |
| | 7470 | |
| | 7471 | /* read the symbol name */ |
| | 7472 | if ((sym = base_read_from_sym_file(fp)) == 0) |
| | 7473 | return 0; |
| | 7474 | |
| | 7475 | /* |
| | 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. |
| | 7480 | */ |
| | 7481 | old_entry = G_prs->get_global_symtab()->find(sym, strlen(sym)); |
| | 7482 | if (old_entry != 0 && old_entry->get_type() == TC_SYM_PROP) |
| | 7483 | return old_entry; |
| | 7484 | |
| | 7485 | /* create and return the new symbol */ |
| | 7486 | return new CTcSymProp(sym, strlen(sym), FALSE, G_cg->new_prop_id()); |
| | 7487 | } |
| | 7488 | |
| | 7489 | /* |
| | 7490 | * Write to an object file |
| | 7491 | */ |
| | 7492 | int CTcSymPropBase::write_to_obj_file(CVmFile *fp) |
| | 7493 | { |
| | 7494 | /* |
| | 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 |
| | 7498 | * property. |
| | 7499 | */ |
| | 7500 | if (!ref_) |
| | 7501 | return FALSE; |
| | 7502 | |
| | 7503 | /* inherit default */ |
| | 7504 | CTcSymbol::write_to_obj_file(fp); |
| | 7505 | |
| | 7506 | /* |
| | 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 |
| | 7510 | * file |
| | 7511 | */ |
| | 7512 | fp->write_int4((ulong)prop_); |
| | 7513 | |
| | 7514 | /* written */ |
| | 7515 | return TRUE; |
| | 7516 | } |
| | 7517 | |
| | 7518 | |
| | 7519 | /* ------------------------------------------------------------------------ */ |
| | 7520 | /* |
| | 7521 | * Enumerator symbol base |
| | 7522 | */ |
| | 7523 | |
| | 7524 | /* |
| | 7525 | * fold the symbol as a constant |
| | 7526 | */ |
| | 7527 | CTcPrsNode *CTcSymEnumBase::fold_constant() |
| | 7528 | { |
| | 7529 | CTcConstVal cval; |
| | 7530 | |
| | 7531 | /* set up the enumerator constant */ |
| | 7532 | cval.set_enum(get_enum_id()); |
| | 7533 | |
| | 7534 | /* return a constant node */ |
| | 7535 | return new CTPNConst(&cval); |
| | 7536 | } |
| | 7537 | |
| | 7538 | |
| | 7539 | /* |
| | 7540 | * Write to a symbol file |
| | 7541 | */ |
| | 7542 | int CTcSymEnumBase::write_to_sym_file(CVmFile *fp) |
| | 7543 | { |
| | 7544 | int result; |
| | 7545 | char buf[32]; |
| | 7546 | |
| | 7547 | /* inherit default */ |
| | 7548 | result = CTcSymbol::write_to_sym_file(fp); |
| | 7549 | |
| | 7550 | /* write the 'token' flag */ |
| | 7551 | if (result) |
| | 7552 | { |
| | 7553 | /* clear the flags */ |
| | 7554 | buf[0] = 0; |
| | 7555 | |
| | 7556 | /* set the 'token' flag if appropriate */ |
| | 7557 | if (is_token_) |
| | 7558 | buf[0] |= 1; |
| | 7559 | |
| | 7560 | /* write the flags */ |
| | 7561 | fp->write_bytes(buf, 1); |
| | 7562 | } |
| | 7563 | |
| | 7564 | /* return the result */ |
| | 7565 | return result; |
| | 7566 | } |
| | 7567 | |
| | 7568 | /* |
| | 7569 | * Read from a symbol file |
| | 7570 | */ |
| | 7571 | CTcSymbol *CTcSymEnumBase::read_from_sym_file(CVmFile *fp) |
| | 7572 | { |
| | 7573 | const char *sym; |
| | 7574 | CTcSymEnum *old_entry; |
| | 7575 | char buf[32]; |
| | 7576 | int is_token; |
| | 7577 | |
| | 7578 | /* read the symbol name */ |
| | 7579 | if ((sym = base_read_from_sym_file(fp)) == 0) |
| | 7580 | return 0; |
| | 7581 | |
| | 7582 | /* read the 'token' flag */ |
| | 7583 | fp->read_bytes(buf, 1); |
| | 7584 | is_token = ((buf[0] & 1) != 0); |
| | 7585 | |
| | 7586 | /* |
| | 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. |
| | 7591 | */ |
| | 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) |
| | 7595 | { |
| | 7596 | /* if this is a 'token' enum, mark the old entry as such */ |
| | 7597 | if (is_token) |
| | 7598 | old_entry->set_is_token(TRUE); |
| | 7599 | |
| | 7600 | /* return the original entry */ |
| | 7601 | return old_entry; |
| | 7602 | } |
| | 7603 | |
| | 7604 | /* create and return the new symbol */ |
| | 7605 | return new CTcSymEnum(sym, strlen(sym), FALSE, |
| | 7606 | G_prs->new_enum_id(), is_token); |
| | 7607 | } |
| | 7608 | |
| | 7609 | /* |
| | 7610 | * Write to an object file |
| | 7611 | */ |
| | 7612 | int CTcSymEnumBase::write_to_obj_file(CVmFile *fp) |
| | 7613 | { |
| | 7614 | char buf[32]; |
| | 7615 | |
| | 7616 | /* |
| | 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 |
| | 7620 | * enumerator. |
| | 7621 | */ |
| | 7622 | if (!ref_) |
| | 7623 | return FALSE; |
| | 7624 | |
| | 7625 | /* inherit default */ |
| | 7626 | CTcSymbol::write_to_obj_file(fp); |
| | 7627 | |
| | 7628 | /* |
| | 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 |
| | 7632 | * file |
| | 7633 | */ |
| | 7634 | fp->write_int4((ulong)enum_id_); |
| | 7635 | |
| | 7636 | /* clear the flags */ |
| | 7637 | buf[0] = 0; |
| | 7638 | |
| | 7639 | /* set the 'token' flag if appropriate */ |
| | 7640 | if (is_token_) |
| | 7641 | buf[0] |= 1; |
| | 7642 | |
| | 7643 | /* write the flags */ |
| | 7644 | fp->write_bytes(buf, 1); |
| | 7645 | |
| | 7646 | /* written */ |
| | 7647 | return TRUE; |
| | 7648 | } |
| | 7649 | |
| | 7650 | |
| | 7651 | /* ------------------------------------------------------------------------ */ |
| | 7652 | /* |
| | 7653 | * Built-in function symbol base |
| | 7654 | */ |
| | 7655 | |
| | 7656 | /* |
| | 7657 | * Write to a object file |
| | 7658 | */ |
| | 7659 | int CTcSymBifBase::write_to_obj_file(CVmFile *fp) |
| | 7660 | { |
| | 7661 | char buf[10]; |
| | 7662 | |
| | 7663 | /* inherit default */ |
| | 7664 | CTcSymbol::write_to_obj_file(fp); |
| | 7665 | |
| | 7666 | /* write the varargs and return value flags */ |
| | 7667 | buf[0] = (varargs_ != 0); |
| | 7668 | buf[1] = (has_retval_ != 0); |
| | 7669 | |
| | 7670 | /* write the argument count information */ |
| | 7671 | oswp2(buf+2, min_argc_); |
| | 7672 | oswp2(buf+4, max_argc_); |
| | 7673 | |
| | 7674 | /* |
| | 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 |
| | 7677 | * file |
| | 7678 | */ |
| | 7679 | oswp2(buf+6, func_set_id_); |
| | 7680 | oswp2(buf+8, func_idx_); |
| | 7681 | fp->write_bytes(buf, 10); |
| | 7682 | |
| | 7683 | /* written */ |
| | 7684 | return TRUE; |
| | 7685 | } |
| | 7686 | |
| | 7687 | /* ------------------------------------------------------------------------ */ |
| | 7688 | /* |
| | 7689 | * Parser dictionary hash table entry |
| | 7690 | */ |
| | 7691 | |
| | 7692 | /* |
| | 7693 | * add an item to my list of object associations |
| | 7694 | */ |
| | 7695 | void CVmHashEntryPrsDict::add_item(tc_obj_id obj, tc_prop_id prop) |
| | 7696 | { |
| | 7697 | CTcPrsDictItem *item; |
| | 7698 | |
| | 7699 | /* search my list for an existing association to the same obj/prop */ |
| | 7700 | for (item = list_ ; item != 0 ; item = item->nxt_) |
| | 7701 | { |
| | 7702 | /* if it matches, we don't need to add this one again */ |
| | 7703 | if (item->obj_ == obj && item->prop_ == prop) |
| | 7704 | return; |
| | 7705 | } |
| | 7706 | |
| | 7707 | /* not found - create a new item */ |
| | 7708 | item = new (G_prsmem) CTcPrsDictItem(obj, prop); |
| | 7709 | |
| | 7710 | /* link it into my list */ |
| | 7711 | item->nxt_ = list_; |
| | 7712 | list_ = item; |
| | 7713 | } |
| | 7714 | |
| | 7715 | /* ------------------------------------------------------------------------ */ |
| | 7716 | /* |
| | 7717 | * Dictionary entry - each dictionary object gets one of these objects |
| | 7718 | * to track it |
| | 7719 | */ |
| | 7720 | |
| | 7721 | /* |
| | 7722 | * construction |
| | 7723 | */ |
| | 7724 | CTcDictEntry::CTcDictEntry(CTcSymObj *sym) |
| | 7725 | { |
| | 7726 | const size_t hash_table_size = 128; |
| | 7727 | |
| | 7728 | /* remember my object symbol and word truncation length */ |
| | 7729 | sym_ = sym; |
| | 7730 | |
| | 7731 | /* no object file index yet */ |
| | 7732 | obj_idx_ = 0; |
| | 7733 | |
| | 7734 | /* not in a list yet */ |
| | 7735 | nxt_ = 0; |
| | 7736 | |
| | 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]); |
| | 7742 | } |
| | 7743 | |
| | 7744 | /* |
| | 7745 | * Add a word to the table |
| | 7746 | */ |
| | 7747 | void CTcDictEntry::add_word(const char *txt, size_t len, int copy, |
| | 7748 | tc_obj_id obj, tc_prop_id prop) |
| | 7749 | { |
| | 7750 | CVmHashEntryPrsDict *entry; |
| | 7751 | |
| | 7752 | /* search for an existing entry */ |
| | 7753 | entry = (CVmHashEntryPrsDict *)hashtab_->find(txt, len); |
| | 7754 | |
| | 7755 | /* if there's no entry, create a new one */ |
| | 7756 | if (entry == 0) |
| | 7757 | { |
| | 7758 | /* create a new item */ |
| | 7759 | entry = new (G_prsmem) CVmHashEntryPrsDict(txt, len, copy); |
| | 7760 | |
| | 7761 | /* add it to the table */ |
| | 7762 | hashtab_->add(entry); |
| | 7763 | } |
| | 7764 | |
| | 7765 | /* add this object/property association to the word's hash table entry */ |
| | 7766 | entry->add_item(obj, prop); |
| | 7767 | } |
| | 7768 | |
| | 7769 | /* |
| | 7770 | * Write my symbol to an object file |
| | 7771 | */ |
| | 7772 | void CTcDictEntry::write_sym_to_obj_file(CVmFile *fp) |
| | 7773 | { |
| | 7774 | /* if I already have a non-zero index value, I've already been written */ |
| | 7775 | if (obj_idx_ != 0) |
| | 7776 | return; |
| | 7777 | |
| | 7778 | /* assign myself an object file dictionary index */ |
| | 7779 | obj_idx_ = G_prs->get_next_obj_file_dict_idx(); |
| | 7780 | |
| | 7781 | /* write my symbol to the object file */ |
| | 7782 | sym_->write_to_obj_file(fp); |
| | 7783 | } |
| | 7784 | |
| | 7785 | /* ------------------------------------------------------------------------ */ |
| | 7786 | /* |
| | 7787 | * Grammar production list entry |
| | 7788 | */ |
| | 7789 | CTcGramProdEntry::CTcGramProdEntry(CTcSymObj *prod_sym) |
| | 7790 | { |
| | 7791 | /* remember my object symbol */ |
| | 7792 | prod_sym_ = prod_sym; |
| | 7793 | |
| | 7794 | /* not in a list yet */ |
| | 7795 | nxt_ = 0; |
| | 7796 | |
| | 7797 | /* no alternatives yet */ |
| | 7798 | alt_head_ = alt_tail_ = 0; |
| | 7799 | |
| | 7800 | /* not explicitly declared yet */ |
| | 7801 | is_declared_ = FALSE; |
| | 7802 | } |
| | 7803 | |
| | 7804 | /* |
| | 7805 | * Add an alternative |
| | 7806 | */ |
| | 7807 | void CTcGramProdEntry::add_alt(CTcGramProdAlt *alt) |
| | 7808 | { |
| | 7809 | /* link it at the end of my list */ |
| | 7810 | if (alt_tail_ != 0) |
| | 7811 | alt_tail_->set_next(alt); |
| | 7812 | else |
| | 7813 | alt_head_ = alt; |
| | 7814 | alt_tail_ = alt; |
| | 7815 | |
| | 7816 | /* this is now the last element in our list */ |
| | 7817 | alt->set_next(0); |
| | 7818 | } |
| | 7819 | |
| | 7820 | /* |
| | 7821 | * Move my alternatives to a new owner |
| | 7822 | */ |
| | 7823 | void CTcGramProdEntry::move_alts_to(CTcGramProdEntry *new_entry) |
| | 7824 | { |
| | 7825 | CTcGramProdAlt *alt; |
| | 7826 | CTcGramProdAlt *nxt; |
| | 7827 | |
| | 7828 | /* move each of my alternatives */ |
| | 7829 | for (alt = alt_head_ ; alt != 0 ; alt = nxt) |
| | 7830 | { |
| | 7831 | /* remember the next alternative, since we're unlinking this one */ |
| | 7832 | nxt = alt->get_next(); |
| | 7833 | |
| | 7834 | /* unlink this one from the list */ |
| | 7835 | alt->set_next(0); |
| | 7836 | |
| | 7837 | /* link this one into the new owner's list */ |
| | 7838 | new_entry->add_alt(alt); |
| | 7839 | } |
| | 7840 | |
| | 7841 | /* there's nothing left in our list */ |
| | 7842 | alt_head_ = alt_tail_ = 0; |
| | 7843 | } |
| | 7844 | |
| | 7845 | /* |
| | 7846 | * Write to an object file |
| | 7847 | */ |
| | 7848 | void CTcGramProdEntry::write_to_obj_file(CVmFile *fp) |
| | 7849 | { |
| | 7850 | ulong cnt; |
| | 7851 | CTcGramProdAlt *alt; |
| | 7852 | ulong flags; |
| | 7853 | |
| | 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()); |
| | 7856 | |
| | 7857 | /* set up the flags */ |
| | 7858 | flags = 0; |
| | 7859 | if (is_declared_) |
| | 7860 | flags |= 1; |
| | 7861 | |
| | 7862 | /* write the flags */ |
| | 7863 | fp->write_int4(flags); |
| | 7864 | |
| | 7865 | /* count my alternatives */ |
| | 7866 | for (cnt = 0, alt = alt_head_ ; alt != 0 ; |
| | 7867 | ++cnt, alt = alt->get_next()) ; |
| | 7868 | |
| | 7869 | /* write the count */ |
| | 7870 | fp->write_int4(cnt); |
| | 7871 | |
| | 7872 | /* write each alternative */ |
| | 7873 | for (alt = alt_head_ ; alt != 0 ; alt = alt->get_next()) |
| | 7874 | alt->write_to_obj_file(fp); |
| | 7875 | } |
| | 7876 | |
| | 7877 | /* ------------------------------------------------------------------------ */ |
| | 7878 | /* |
| | 7879 | * Grammar production alternative |
| | 7880 | */ |
| | 7881 | CTcGramProdAlt::CTcGramProdAlt(CTcSymObj *obj_sym, CTcDictEntry *dict) |
| | 7882 | { |
| | 7883 | /* remember the associated processor object */ |
| | 7884 | obj_sym_ = obj_sym; |
| | 7885 | |
| | 7886 | /* remember the default dictionary currently in effect */ |
| | 7887 | dict_ = dict; |
| | 7888 | |
| | 7889 | /* nothing in our token list yet */ |
| | 7890 | tok_head_ = tok_tail_ = 0; |
| | 7891 | |
| | 7892 | /* we don't have a score or badness yet */ |
| | 7893 | score_ = 0; |
| | 7894 | badness_ = 0; |
| | 7895 | |
| | 7896 | /* we're not in a list yet */ |
| | 7897 | nxt_ = 0; |
| | 7898 | } |
| | 7899 | |
| | 7900 | void CTcGramProdAlt::add_tok(CTcGramProdTok *tok) |
| | 7901 | { |
| | 7902 | /* link the token at the end of my list */ |
| | 7903 | if (tok_tail_ != 0) |
| | 7904 | tok_tail_->set_next(tok); |
| | 7905 | else |
| | 7906 | tok_head_ = tok; |
| | 7907 | tok_tail_ = tok; |
| | 7908 | |
| | 7909 | /* there's nothing after this token */ |
| | 7910 | tok->set_next(0); |
| | 7911 | } |
| | 7912 | |
| | 7913 | /* |
| | 7914 | * Write to an object file |
| | 7915 | */ |
| | 7916 | void CTcGramProdAlt::write_to_obj_file(CVmFile *fp) |
| | 7917 | { |
| | 7918 | ulong cnt; |
| | 7919 | CTcGramProdTok *tok; |
| | 7920 | |
| | 7921 | /* write my score and badness */ |
| | 7922 | fp->write_int2(score_); |
| | 7923 | fp->write_int2(badness_); |
| | 7924 | |
| | 7925 | /* write the index of my processor object symbol */ |
| | 7926 | fp->write_int4(obj_sym_ == 0 ? 0 : obj_sym_->get_obj_file_idx()); |
| | 7927 | |
| | 7928 | /* write the dictionary index */ |
| | 7929 | fp->write_int4(dict_ == 0 ? 0 : dict_->get_obj_idx()); |
| | 7930 | |
| | 7931 | /* count my tokens */ |
| | 7932 | for (cnt = 0, tok = tok_head_ ; tok != 0 ; |
| | 7933 | ++cnt, tok = tok->get_next()) ; |
| | 7934 | |
| | 7935 | /* write my token count */ |
| | 7936 | fp->write_int4(cnt); |
| | 7937 | |
| | 7938 | /* write the tokens */ |
| | 7939 | for (tok = tok_head_ ; tok != 0 ; tok = tok->get_next()) |
| | 7940 | tok->write_to_obj_file(fp); |
| | 7941 | } |
| | 7942 | |
| | 7943 | /* ------------------------------------------------------------------------ */ |
| | 7944 | /* |
| | 7945 | * Grammar production token object |
| | 7946 | */ |
| | 7947 | |
| | 7948 | /* |
| | 7949 | * write to an object file |
| | 7950 | */ |
| | 7951 | void CTcGramProdTok::write_to_obj_file(CVmFile *fp) |
| | 7952 | { |
| | 7953 | size_t i; |
| | 7954 | |
| | 7955 | /* write my type */ |
| | 7956 | fp->write_int2((int)typ_); |
| | 7957 | |
| | 7958 | /* write my data */ |
| | 7959 | switch(typ_) |
| | 7960 | { |
| | 7961 | case TCGRAM_PROD: |
| | 7962 | /* write my object's object file index */ |
| | 7963 | fp->write_int4(val_.obj_ != 0 |
| | 7964 | ? val_.obj_->get_obj_file_idx() : 0); |
| | 7965 | break; |
| | 7966 | |
| | 7967 | case TCGRAM_TOKEN_TYPE: |
| | 7968 | /* write my enum token ID */ |
| | 7969 | fp->write_int4(val_.enum_id_); |
| | 7970 | break; |
| | 7971 | |
| | 7972 | case TCGRAM_PART_OF_SPEECH: |
| | 7973 | /* write my property ID */ |
| | 7974 | fp->write_int2(val_.prop_); |
| | 7975 | break; |
| | 7976 | |
| | 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_); |
| | 7981 | break; |
| | 7982 | |
| | 7983 | case TCGRAM_STAR: |
| | 7984 | /* no additional value data */ |
| | 7985 | break; |
| | 7986 | |
| | 7987 | case TCGRAM_PART_OF_SPEECH_LIST: |
| | 7988 | /* write the length */ |
| | 7989 | fp->write_int2(val_.prop_list_.len_); |
| | 7990 | |
| | 7991 | /* write each element */ |
| | 7992 | for (i = 0 ; i < val_.prop_list_.len_ ; ++i) |
| | 7993 | fp->write_int2(val_.prop_list_.arr_[i]); |
| | 7994 | |
| | 7995 | /* done */ |
| | 7996 | break; |
| | 7997 | |
| | 7998 | case TCGRAM_UNKNOWN: |
| | 7999 | /* no value - there's nothing extra to write */ |
| | 8000 | break; |
| | 8001 | } |
| | 8002 | |
| | 8003 | /* write my property association */ |
| | 8004 | fp->write_int2(prop_assoc_); |
| | 8005 | } |
| | 8006 | |
| | 8007 | /* |
| | 8008 | * Initialize with a part-of-speech list |
| | 8009 | */ |
| | 8010 | void CTcGramProdTok::set_match_part_list() |
| | 8011 | { |
| | 8012 | const size_t init_alo = 10; |
| | 8013 | |
| | 8014 | /* remember the type */ |
| | 8015 | typ_ = TCGRAM_PART_OF_SPEECH_LIST; |
| | 8016 | |
| | 8017 | /* we have nothing in the list yet */ |
| | 8018 | val_.prop_list_.len_ = 0; |
| | 8019 | |
| | 8020 | /* set the initial allocation size */ |
| | 8021 | val_.prop_list_.alo_ = init_alo; |
| | 8022 | |
| | 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])); |
| | 8026 | } |
| | 8027 | |
| | 8028 | /* |
| | 8029 | * Add a property to our part-of-speech match list |
| | 8030 | */ |
| | 8031 | void CTcGramProdTok::add_match_part_ele(tctarg_prop_id_t prop) |
| | 8032 | { |
| | 8033 | /* if necessary, re-allocate the array at a larger size */ |
| | 8034 | if (val_.prop_list_.len_ == val_.prop_list_.alo_) |
| | 8035 | { |
| | 8036 | tctarg_prop_id_t *oldp; |
| | 8037 | |
| | 8038 | /* bump up the size a bit */ |
| | 8039 | val_.prop_list_.alo_ += 10; |
| | 8040 | |
| | 8041 | /* remember the current list long enough to copy it */ |
| | 8042 | oldp = val_.prop_list_.arr_; |
| | 8043 | |
| | 8044 | /* reallocate it */ |
| | 8045 | val_.prop_list_.arr_ = (tctarg_prop_id_t *)G_prsmem->alloc( |
| | 8046 | val_.prop_list_.alo_ * sizeof(val_.prop_list_.arr_[0])); |
| | 8047 | |
| | 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])); |
| | 8051 | } |
| | 8052 | |
| | 8053 | /* |
| | 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 |
| | 8056 | */ |
| | 8057 | val_.prop_list_.arr_[val_.prop_list_.len_++] = prop; |
| | 8058 | } |
| | 8059 | |
| | 8060 | /* ------------------------------------------------------------------------ */ |
| | 8061 | /* |
| | 8062 | * Code Body Parse Node |
| | 8063 | */ |
| | 8064 | |
| | 8065 | /* |
| | 8066 | * instantiate |
| | 8067 | */ |
| | 8068 | CTPNCodeBodyBase::CTPNCodeBodyBase(CTcPrsSymtab *lcltab, |
| | 8069 | CTcPrsSymtab *gototab, CTPNStm *stm, |
| | 8070 | int argc, int varargs, |
| | 8071 | int varargs_list, |
| | 8072 | CTcSymLocal *varargs_list_local, |
| | 8073 | int local_cnt, int self_valid, |
| | 8074 | CTcCodeBodyRef *enclosing_code_body) |
| | 8075 | { |
| | 8076 | /* remember the data in the code body */ |
| | 8077 | lcltab_ = lcltab; |
| | 8078 | gototab_ = gototab; |
| | 8079 | stm_ = stm; |
| | 8080 | argc_ = argc; |
| | 8081 | varargs_ = varargs; |
| | 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; |
| | 8088 | |
| | 8089 | /* remember the enclosing code body */ |
| | 8090 | enclosing_code_body_ = enclosing_code_body; |
| | 8091 | |
| | 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; |
| | 8098 | |
| | 8099 | /* presume we have an internal fixup list */ |
| | 8100 | fixup_owner_sym_ = 0; |
| | 8101 | fixup_list_anchor_ = &internal_fixups_; |
| | 8102 | |
| | 8103 | /* no internal fixups yet */ |
| | 8104 | internal_fixups_ = 0; |
| | 8105 | |
| | 8106 | /* we haven't been replaced yet */ |
| | 8107 | replaced_ = FALSE; |
| | 8108 | |
| | 8109 | /* |
| | 8110 | * remember the source location of the closing brace, which should |
| | 8111 | * be the current location when we're instantiated |
| | 8112 | */ |
| | 8113 | end_desc_ = G_tok->get_last_desc(); |
| | 8114 | end_linenum_ = G_tok->get_last_linenum(); |
| | 8115 | } |
| | 8116 | |
| | 8117 | |
| | 8118 | /* |
| | 8119 | * fold constants |
| | 8120 | */ |
| | 8121 | CTcPrsNode *CTPNCodeBodyBase::fold_constants(class CTcPrsSymtab *) |
| | 8122 | { |
| | 8123 | /* |
| | 8124 | * fold constants in our compound statement, in the scope of our |
| | 8125 | * local symbol table |
| | 8126 | */ |
| | 8127 | if (stm_ != 0) |
| | 8128 | stm_->fold_constants(lcltab_); |
| | 8129 | |
| | 8130 | /* we are not directly changed by this operation */ |
| | 8131 | return this; |
| | 8132 | } |
| | 8133 | |
| | 8134 | /* |
| | 8135 | * Check for unreferenced labels |
| | 8136 | */ |
| | 8137 | void CTPNCodeBodyBase::check_unreferenced_labels() |
| | 8138 | { |
| | 8139 | /* |
| | 8140 | * enumerate our labels - skip this check if we're only parsing the |
| | 8141 | * program for syntax |
| | 8142 | */ |
| | 8143 | if (gototab_ != 0 && !G_prs->get_syntax_only()) |
| | 8144 | gototab_->enum_entries(&unref_label_cb, this); |
| | 8145 | } |
| | 8146 | |
| | 8147 | /* |
| | 8148 | * Callback for enumerating labels for checking for unreferenced labels |
| | 8149 | */ |
| | 8150 | void CTPNCodeBodyBase::unref_label_cb(void *, CTcSymbol *sym) |
| | 8151 | { |
| | 8152 | /* if it's a label, check it out */ |
| | 8153 | if (sym->get_type() == TC_SYM_LABEL) |
| | 8154 | { |
| | 8155 | CTcSymLabel *lbl = (CTcSymLabel *)sym; |
| | 8156 | |
| | 8157 | /* |
| | 8158 | * get its underlying statement, and make sure it has a |
| | 8159 | * control-flow flag for goto, continue, or break |
| | 8160 | */ |
| | 8161 | if (lbl->get_stm() != 0) |
| | 8162 | { |
| | 8163 | ulong flags; |
| | 8164 | |
| | 8165 | /* |
| | 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 |
| | 8169 | */ |
| | 8170 | flags = lbl->get_stm()->get_explicit_control_flow_flags(); |
| | 8171 | |
| | 8172 | /* |
| | 8173 | * if the flags aren't set for at least one of the explicit |
| | 8174 | * label uses, the label is unreferenced |
| | 8175 | */ |
| | 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(), |
| | 8180 | lbl->get_sym()); |
| | 8181 | } |
| | 8182 | } |
| | 8183 | } |
| | 8184 | |
| | 8185 | /* |
| | 8186 | * add an absolute fixup to my list |
| | 8187 | */ |
| | 8188 | void CTPNCodeBodyBase::add_abs_fixup(CTcDataStream *ds, ulong ofs) |
| | 8189 | { |
| | 8190 | /* ask the code body to add the fixup */ |
| | 8191 | CTcAbsFixup::add_abs_fixup(fixup_list_anchor_, ds, ofs); |
| | 8192 | } |
| | 8193 | |
| | 8194 | /* |
| | 8195 | * add an absolute fixup at the current stream offset |
| | 8196 | */ |
| | 8197 | void CTPNCodeBodyBase::add_abs_fixup(CTcDataStream *ds) |
| | 8198 | { |
| | 8199 | /* ask the code body to add the fixup */ |
| | 8200 | CTcAbsFixup::add_abs_fixup(fixup_list_anchor_, ds, ds->get_ofs()); |
| | 8201 | } |
| | 8202 | |
| | 8203 | /* |
| | 8204 | * Get the context variable for a given level |
| | 8205 | */ |
| | 8206 | int CTPNCodeBodyBase::get_or_add_ctx_var_for_level(int level) |
| | 8207 | { |
| | 8208 | CTcCodeBodyCtx *ctx; |
| | 8209 | |
| | 8210 | /* scan our list to see if the level is already assigned */ |
| | 8211 | for (ctx = ctx_head_ ; ctx != 0 ; ctx = ctx->nxt_) |
| | 8212 | { |
| | 8213 | /* if we've already set up this level, return its variable */ |
| | 8214 | if (ctx->level_ == level) |
| | 8215 | return ctx->var_num_; |
| | 8216 | } |
| | 8217 | |
| | 8218 | /* we didn't find it - allocate a new level structure */ |
| | 8219 | ctx = new (G_prsmem) CTcCodeBodyCtx(); |
| | 8220 | |
| | 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(); |
| | 8224 | |
| | 8225 | /* |
| | 8226 | * allocating a new variable probably increased the maximum local |
| | 8227 | * variable count - update our information from the parser |
| | 8228 | */ |
| | 8229 | local_cnt_ = G_prs->get_max_local_cnt(); |
| | 8230 | |
| | 8231 | /* link it into our list */ |
| | 8232 | ctx->prv_ = ctx_tail_; |
| | 8233 | ctx->nxt_ = 0; |
| | 8234 | if (ctx_tail_ != 0) |
| | 8235 | ctx_tail_->nxt_ = ctx; |
| | 8236 | else |
| | 8237 | ctx_head_ = ctx; |
| | 8238 | ctx_tail_ = ctx; |
| | 8239 | |
| | 8240 | /* return the variable for the new level */ |
| | 8241 | return ctx->var_num_; |
| | 8242 | } |
| | 8243 | |
| | 8244 | /* |
| | 8245 | * Find a local context for a given level |
| | 8246 | */ |
| | 8247 | int CTPNCodeBodyBase::get_ctx_var_for_level(int level, int *varnum) |
| | 8248 | { |
| | 8249 | CTcCodeBodyCtx *ctx; |
| | 8250 | |
| | 8251 | /* if they want level zero, it's our local context */ |
| | 8252 | if (level == 0) |
| | 8253 | { |
| | 8254 | /* set the variable ID to our local context variable */ |
| | 8255 | *varnum = local_ctx_var_; |
| | 8256 | |
| | 8257 | /* return true only if we actually have a local context */ |
| | 8258 | return has_local_ctx_; |
| | 8259 | } |
| | 8260 | |
| | 8261 | /* scan our list to see if the level is already assigned */ |
| | 8262 | for (ctx = ctx_head_ ; ctx != 0 ; ctx = ctx->nxt_) |
| | 8263 | { |
| | 8264 | /* if we've already set up this level, return its variable */ |
| | 8265 | if (ctx->level_ == level) |
| | 8266 | { |
| | 8267 | /* set the caller's variable number */ |
| | 8268 | *varnum = ctx->var_num_; |
| | 8269 | |
| | 8270 | /* indicate that we found it */ |
| | 8271 | return TRUE; |
| | 8272 | } |
| | 8273 | } |
| | 8274 | |
| | 8275 | /* didn't find it */ |
| | 8276 | return FALSE; |
| | 8277 | } |
| | 8278 | |
| | 8279 | /* |
| | 8280 | * Get the immediately enclosing code body |
| | 8281 | */ |
| | 8282 | CTPNCodeBody *CTPNCodeBodyBase::get_enclosing() const |
| | 8283 | { |
| | 8284 | /* |
| | 8285 | * if we have no enclosing code body reference, we have no enclosing |
| | 8286 | * code body |
| | 8287 | */ |
| | 8288 | if (enclosing_code_body_ == 0) |
| | 8289 | return 0; |
| | 8290 | |
| | 8291 | /* get the code body from my enclosing code body reference object */ |
| | 8292 | return enclosing_code_body_->ptr; |
| | 8293 | } |
| | 8294 | |
| | 8295 | /* |
| | 8296 | * Get the outermost enclosing code body |
| | 8297 | */ |
| | 8298 | CTPNCodeBody *CTPNCodeBodyBase::get_outermost_enclosing() const |
| | 8299 | { |
| | 8300 | CTPNCodeBody *cur; |
| | 8301 | CTPNCodeBody *nxt; |
| | 8302 | |
| | 8303 | /* |
| | 8304 | * scan each enclosing code body until we find one without an enclosing |
| | 8305 | * code body |
| | 8306 | */ |
| | 8307 | for (cur = 0, nxt = get_enclosing() ; nxt != 0 ; |
| | 8308 | cur = nxt, nxt = nxt->get_enclosing()) ; |
| | 8309 | |
| | 8310 | /* return what we found */ |
| | 8311 | return cur; |
| | 8312 | } |
| | 8313 | |
| | 8314 | /* |
| | 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. |
| | 8318 | */ |
| | 8319 | class CTcSymFunc *CTPNCodeBodyBase::get_replaced_func() const |
| | 8320 | { |
| | 8321 | CTcSymFunc *b; |
| | 8322 | CTPNCodeBody *enc; |
| | 8323 | |
| | 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(); |
| | 8327 | |
| | 8328 | /* |
| | 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 |
| | 8331 | * here |
| | 8332 | */ |
| | 8333 | if ((enc = get_enclosing()) != 0) |
| | 8334 | return enc->get_replaced_func(); |
| | 8335 | |
| | 8336 | /* if we haven't found anything yet, we don't have a base function */ |
| | 8337 | return 0; |
| | 8338 | } |
| | 8339 | |
| | 8340 | /* ------------------------------------------------------------------------ */ |
| | 8341 | /* |
| | 8342 | * Generic statement node |
| | 8343 | */ |
| | 8344 | |
| | 8345 | /* |
| | 8346 | * initialize at the tokenizer's current source file position |
| | 8347 | */ |
| | 8348 | CTPNStmBase::CTPNStmBase() |
| | 8349 | { |
| | 8350 | /* get the current source location from the parser */ |
| | 8351 | init(G_prs->get_cur_desc(), G_prs->get_cur_linenum()); |
| | 8352 | } |
| | 8353 | |
| | 8354 | /* |
| | 8355 | * log an error at this statement's source file position |
| | 8356 | */ |
| | 8357 | void CTPNStmBase::log_error(int errnum, ...) const |
| | 8358 | { |
| | 8359 | va_list marker; |
| | 8360 | |
| | 8361 | /* display the message */ |
| | 8362 | va_start(marker, errnum); |
| | 8363 | G_tcmain->v_log_error(file_, linenum_, TC_SEV_ERROR, errnum, marker); |
| | 8364 | va_end(marker); |
| | 8365 | } |
| | 8366 | |
| | 8367 | /* |
| | 8368 | * log a warning at this statement's source file position |
| | 8369 | */ |
| | 8370 | void CTPNStmBase::log_warning(int errnum, ...) const |
| | 8371 | { |
| | 8372 | va_list marker; |
| | 8373 | |
| | 8374 | /* display the message */ |
| | 8375 | va_start(marker, errnum); |
| | 8376 | G_tcmain->v_log_error(file_, linenum_, TC_SEV_WARNING, errnum, marker); |
| | 8377 | va_end(marker); |
| | 8378 | } |
| | 8379 | |
| | 8380 | /* |
| | 8381 | * Generate code for a sub-statement |
| | 8382 | */ |
| | 8383 | void CTPNStmBase::gen_code_substm(CTPNStm *substm) |
| | 8384 | { |
| | 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()); |
| | 8388 | |
| | 8389 | /* generate code for the sub-statement */ |
| | 8390 | substm->gen_code(TRUE, TRUE); |
| | 8391 | |
| | 8392 | /* restore the error reporting location to the main statement */ |
| | 8393 | G_tok->set_line_info(get_source_desc(), get_source_linenum()); |
| | 8394 | } |
| | 8395 | |
| | 8396 | /* ------------------------------------------------------------------------ */ |
| | 8397 | /* |
| | 8398 | * Object Definition Statement |
| | 8399 | */ |
| | 8400 | |
| | 8401 | /* |
| | 8402 | * fold constants |
| | 8403 | */ |
| | 8404 | CTcPrsNode *CTPNStmObjectBase::fold_constants(CTcPrsSymtab *symtab) |
| | 8405 | { |
| | 8406 | CTPNObjProp *prop; |
| | 8407 | |
| | 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); |
| | 8411 | |
| | 8412 | /* we're not changed directly by this */ |
| | 8413 | return this; |
| | 8414 | } |
| | 8415 | |
| | 8416 | /* ------------------------------------------------------------------------ */ |
| | 8417 | /* |
| | 8418 | * superclass record |
| | 8419 | */ |
| | 8420 | |
| | 8421 | /* |
| | 8422 | * get my symbol |
| | 8423 | */ |
| | 8424 | CTcSymbol *CTPNSuperclass::get_sym() const |
| | 8425 | { |
| | 8426 | /* if we know the symbol already, return it directly */ |
| | 8427 | if (sym_ != 0) |
| | 8428 | return sym_; |
| | 8429 | |
| | 8430 | /* look up my symbol by name in the global symbol table */ |
| | 8431 | return G_prs->get_global_symtab()->find(sym_txt_, sym_len_); |
| | 8432 | } |
| | 8433 | |
| | 8434 | /* |
| | 8435 | * am I a subclass of the given class? |
| | 8436 | */ |
| | 8437 | int CTPNSuperclass::is_subclass_of(const CTPNSuperclass *other) const |
| | 8438 | { |
| | 8439 | CTcSymObj *sym; |
| | 8440 | CTPNSuperclass *sc; |
| | 8441 | |
| | 8442 | /* |
| | 8443 | * if my name matches, we're a subclass (we are a subclass of |
| | 8444 | * ourselves) |
| | 8445 | */ |
| | 8446 | if (other->sym_len_ == sym_len_ |
| | 8447 | && memcmp(other->sym_txt_, sym_txt_, sym_len_) == 0) |
| | 8448 | return TRUE; |
| | 8449 | |
| | 8450 | /* |
| | 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 |
| | 8454 | * anything. |
| | 8455 | */ |
| | 8456 | sym = (CTcSymObj *)get_sym(); |
| | 8457 | if (sym == 0 |
| | 8458 | || sym->get_type() != TC_SYM_OBJ |
| | 8459 | || sym->get_metaclass() != TC_META_TADSOBJ) |
| | 8460 | return FALSE; |
| | 8461 | |
| | 8462 | /* scan our symbol's superclass list for a match */ |
| | 8463 | for (sc = sym->get_sc_name_head() ; sc != 0 ; sc = sc->nxt_) |
| | 8464 | { |
| | 8465 | /* |
| | 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 |
| | 8468 | */ |
| | 8469 | if (sc->is_subclass_of(other)) |
| | 8470 | return TRUE; |
| | 8471 | } |
| | 8472 | |
| | 8473 | /* |
| | 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 |
| | 8476 | */ |
| | 8477 | return FALSE; |
| | 8478 | } |
| | 8479 | |
| | 8480 | |
| | 8481 | /* ------------------------------------------------------------------------ */ |
| | 8482 | /* |
| | 8483 | * 'return' statement |
| | 8484 | */ |
| | 8485 | |
| | 8486 | /* |
| | 8487 | * fold constants |
| | 8488 | */ |
| | 8489 | CTcPrsNode *CTPNStmReturnBase::fold_constants(CTcPrsSymtab *symtab) |
| | 8490 | { |
| | 8491 | /* set our location for any errors that occur */ |
| | 8492 | G_tok->set_line_info(get_source_desc(), get_source_linenum()); |
| | 8493 | |
| | 8494 | /* fold constants in the expression, if we have one */ |
| | 8495 | if (expr_ != 0) |
| | 8496 | expr_ = expr_->fold_constants(symtab); |
| | 8497 | |
| | 8498 | /* we are not directly changed by this operation */ |
| | 8499 | return this; |
| | 8500 | } |
| | 8501 | |
| | 8502 | /* ------------------------------------------------------------------------ */ |
| | 8503 | /* |
| | 8504 | * Formal type list |
| | 8505 | */ |
| | 8506 | |
| | 8507 | /* |
| | 8508 | * add a typed parameter to the list - 'tok' is the symbol giving the type |
| | 8509 | * name |
| | 8510 | */ |
| | 8511 | void CTcFormalTypeList::add_typed_param(const CTcToken *tok) |
| | 8512 | { |
| | 8513 | add(new (G_prsmem) CTcFormalTypeEle(tok->get_text(), tok->get_text_len())); |
| | 8514 | } |
| | 8515 | |
| | 8516 | /* add an untyped parameter to the list */ |
| | 8517 | void CTcFormalTypeList::add_untyped_param() |
| | 8518 | { |
| | 8519 | add(new (G_prsmem) CTcFormalTypeEle()); |
| | 8520 | } |
| | 8521 | |
| | 8522 | /* add a list element */ |
| | 8523 | void CTcFormalTypeList::add(CTcFormalTypeEle *ele) |
| | 8524 | { |
| | 8525 | /* link it into our list */ |
| | 8526 | if (tail_ != 0) |
| | 8527 | tail_->nxt_ = ele; |
| | 8528 | else |
| | 8529 | head_ = ele; |
| | 8530 | tail_ = ele; |
| | 8531 | ele->nxt_ = 0; |
| | 8532 | } |
| | 8533 | |
| | 8534 | /* |
| | 8535 | * create a decorated name token for the multi-method defined by the given |
| | 8536 | * function name and our type list |
| | 8537 | */ |
| | 8538 | void CTcFormalTypeList::decorate_name(CTcToken *decorated_name, |
| | 8539 | const CTcToken *func_base_name) |
| | 8540 | { |
| | 8541 | CTcFormalTypeEle *ele; |
| | 8542 | size_t len; |
| | 8543 | const char *p; |
| | 8544 | |
| | 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_) |
| | 8548 | { |
| | 8549 | /* add this type name's length, if there's a name */ |
| | 8550 | if (ele->name_ != 0) |
| | 8551 | len += ele->name_len_; |
| | 8552 | |
| | 8553 | /* add a semicolon after the type */ |
| | 8554 | len += 1; |
| | 8555 | } |
| | 8556 | |
| | 8557 | /* add "...;" if it's varargs */ |
| | 8558 | if (varargs_) |
| | 8559 | len += 4; |
| | 8560 | |
| | 8561 | /* allocate space for the name */ |
| | 8562 | G_tok->reserve_source(len); |
| | 8563 | |
| | 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()); |
| | 8567 | |
| | 8568 | /* add a "*" separator for the multi-method indicator */ |
| | 8569 | G_tok->store_source_partial("*", 1); |
| | 8570 | |
| | 8571 | /* add each type name */ |
| | 8572 | for (ele = head_ ; ele != 0 ; ele = ele->nxt_) |
| | 8573 | { |
| | 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_); |
| | 8577 | |
| | 8578 | /* add a semicolon to terminate the parameter name */ |
| | 8579 | G_tok->store_source_partial(";", 1); |
| | 8580 | } |
| | 8581 | |
| | 8582 | /* add the varargs indicator ("...;"), if applicable */ |
| | 8583 | if (varargs_) |
| | 8584 | G_tok->store_source_partial("...;", 4); |
| | 8585 | |
| | 8586 | /* null-terminate it */ |
| | 8587 | G_tok->store_source_partial("\0", 1); |
| | 8588 | |
| | 8589 | /* set the decorated token name */ |
| | 8590 | decorated_name->settyp(TOKT_SYM); |
| | 8591 | decorated_name->set_text(p, len); |
| | 8592 | } |
| | 8593 | |
| | 8594 | /* formal list element - construction */ |
| | 8595 | CTcFormalTypeEle::CTcFormalTypeEle(const char *name, size_t len) |
| | 8596 | { |
| | 8597 | name_ = new (G_prsmem) char[len + 1]; |
| | 8598 | memcpy(name_, name, len); |
| | 8599 | name_len_ = len; |
| | 8600 | } |