| | 1 | /* $Header: d:/cvsroot/tads/tads3/tcprs.h,v 1.5 1999/07/11 00:46:58 MJRoberts Exp $ */ |
| | 2 | |
| | 3 | /* |
| | 4 | * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. |
| | 5 | * |
| | 6 | * Please see the accompanying license file, LICENSE.TXT, for information |
| | 7 | * on using and copying this software. |
| | 8 | */ |
| | 9 | /* |
| | 10 | Name |
| | 11 | tcprs.h - TADS 3 Compiler - parser |
| | 12 | Function |
| | 13 | |
| | 14 | Notes |
| | 15 | |
| | 16 | Modified |
| | 17 | 04/29/99 MJRoberts - Creation |
| | 18 | */ |
| | 19 | |
| | 20 | #ifndef TCPRS_H |
| | 21 | #define TCPRS_H |
| | 22 | |
| | 23 | #include <assert.h> |
| | 24 | |
| | 25 | #include "vmtype.h" |
| | 26 | #include "t3std.h" |
| | 27 | #include "tcglob.h" |
| | 28 | #include "tctok.h" |
| | 29 | #include "tctargty.h" |
| | 30 | #include "tcprstyp.h" |
| | 31 | |
| | 32 | |
| | 33 | /* ------------------------------------------------------------------------ */ |
| | 34 | /* |
| | 35 | * Object ID type |
| | 36 | */ |
| | 37 | typedef ulong tc_obj_id; |
| | 38 | |
| | 39 | /* |
| | 40 | * Property ID type |
| | 41 | */ |
| | 42 | typedef uint tc_prop_id; |
| | 43 | |
| | 44 | |
| | 45 | /* ------------------------------------------------------------------------ */ |
| | 46 | /* |
| | 47 | * scope data structure |
| | 48 | */ |
| | 49 | struct tcprs_scope_t |
| | 50 | { |
| | 51 | /* local symbol table */ |
| | 52 | class CTcPrsSymtab *local_symtab; |
| | 53 | |
| | 54 | /* enclosing scope's local symbol table */ |
| | 55 | class CTcPrsSymtab *enclosing_symtab; |
| | 56 | |
| | 57 | /* number of locals allocated in scope */ |
| | 58 | int local_cnt; |
| | 59 | }; |
| | 60 | |
| | 61 | /* ------------------------------------------------------------------------ */ |
| | 62 | /* |
| | 63 | * Code body parsing types. Each type of code body is essentially the |
| | 64 | * same with minor variations, so we use a common code body parser that |
| | 65 | * checks the parsing type to apply the variations. |
| | 66 | */ |
| | 67 | enum tcprs_codebodytype |
| | 68 | { |
| | 69 | /* a standard function or method code body */ |
| | 70 | TCPRS_CB_NORMAL, |
| | 71 | |
| | 72 | /* anonymous function */ |
| | 73 | TCPRS_CB_ANON_FN, |
| | 74 | |
| | 75 | /* short-form anonymous function */ |
| | 76 | TCPRS_CB_SHORT_ANON_FN |
| | 77 | }; |
| | 78 | |
| | 79 | |
| | 80 | /* ------------------------------------------------------------------------ */ |
| | 81 | /* |
| | 82 | * the saved method context is always at index 1 in local variable context |
| | 83 | * arrays, when we're using local variable context arrays |
| | 84 | */ |
| | 85 | #define TCPRS_LOCAL_CTX_METHODCTX 1 |
| | 86 | |
| | 87 | |
| | 88 | /* ------------------------------------------------------------------------ */ |
| | 89 | /* |
| | 90 | * Parser |
| | 91 | */ |
| | 92 | class CTcParser |
| | 93 | { |
| | 94 | public: |
| | 95 | CTcParser(); |
| | 96 | ~CTcParser(); |
| | 97 | |
| | 98 | /* initialize - call this after the code generator is set up */ |
| | 99 | void init(); |
| | 100 | |
| | 101 | /* |
| | 102 | * Set the module information. This tells us the module's name (as |
| | 103 | * it's given in the makefile (.t3m) or library (.tl) file) and its |
| | 104 | * sequence number (an ordinal giving its position in the list of |
| | 105 | * modules making up the overall program build). We use this |
| | 106 | * information in generating the sourceTextGroup object. |
| | 107 | */ |
| | 108 | void set_module_info(const char *name, int seqno); |
| | 109 | |
| | 110 | /* |
| | 111 | * Write an exported symbol file. An exported symbol file |
| | 112 | * facilitates separate compilation by providing a listing of the |
| | 113 | * symbols defined in another module. If module A depends on the |
| | 114 | * symbols from module B, the user can first create an exported |
| | 115 | * symbol file for module B, then can compile module A in the |
| | 116 | * presence of B's symbol file, without actually loading B, and |
| | 117 | * without manually entering a set of external definitions in module |
| | 118 | * A's source code. |
| | 119 | */ |
| | 120 | void write_symbol_file(class CVmFile *fp, class CTcMake *make_obj); |
| | 121 | |
| | 122 | /* |
| | 123 | * Seek to the start of the build configuration information in a symbol |
| | 124 | * file. The return value is the number of bytes stored in the build |
| | 125 | * configuration block; on return, the file object will have its seek |
| | 126 | * offset set to the first byte of the build configuration data. |
| | 127 | * Returns zero if the symbol file is invalid or does not contain any |
| | 128 | * configuration data. |
| | 129 | */ |
| | 130 | static ulong seek_sym_file_build_config_info(class CVmFile *fp); |
| | 131 | |
| | 132 | /* |
| | 133 | * Write the global table to an object file. |
| | 134 | */ |
| | 135 | void write_to_object_file(class CVmFile *fp); |
| | 136 | |
| | 137 | /* |
| | 138 | * Read an object file and load it into the global symbol table. We |
| | 139 | * will fill in the object and property ID translation tables |
| | 140 | * provided with the translated values for the object and property |
| | 141 | * symbols that we find in the object file. |
| | 142 | * |
| | 143 | * Returns zero on success; logs error messages and returns non-zero |
| | 144 | * on error. Note that a non-zero value should be returned only |
| | 145 | * when the file appears to be corrupted or an I/O error occurs; |
| | 146 | * errors involving conflicting symbols, or other problems that do |
| | 147 | * not prevent us from continuing to read the file in an orderly |
| | 148 | * fashion, should not return failure but should simply log the |
| | 149 | * error and continue; this way, we can detect any additional symbol |
| | 150 | * conflicts or other errors. This routine should return failure |
| | 151 | * only when it is not possible to continue reading the file. |
| | 152 | */ |
| | 153 | int load_object_file(class CVmFile *fp, |
| | 154 | const textchar_t *fname, |
| | 155 | tctarg_obj_id_t *obj_xlat, |
| | 156 | tctarg_prop_id_t *prop_xlat, |
| | 157 | ulong *enum_xlat); |
| | 158 | |
| | 159 | /* |
| | 160 | * Apply internal object/property ID fixups. This traverses the |
| | 161 | * symbol table and calls each symbol's apply_internal_fixups() |
| | 162 | * method. This can be called once after loading all object files. |
| | 163 | */ |
| | 164 | void apply_internal_fixups(); |
| | 165 | |
| | 166 | /* |
| | 167 | * Read an exported symbol file. Reads the file and loads the |
| | 168 | * global symbol table with the symbols in the file, with each |
| | 169 | * symbol marked as external. |
| | 170 | * |
| | 171 | * This can be used for separate compilation. If module A depends |
| | 172 | * on symbols in module B, first create a symbol file for module B, |
| | 173 | * then module A can be compiled simply be pre-loading B's symbol |
| | 174 | * file. Any symbol files that a module depends upon must be loaded |
| | 175 | * before the module is compiled - symbol file loading must precede |
| | 176 | * parsing. |
| | 177 | * |
| | 178 | * If any errors occur, we'll log the errors and return non-zero. |
| | 179 | * We'll return zero on success. |
| | 180 | */ |
| | 181 | int read_symbol_file(class CVmFile *fp); |
| | 182 | |
| | 183 | /* get the global symbol table */ |
| | 184 | class CTcPrsSymtab *get_global_symtab() const { return global_symtab_; } |
| | 185 | |
| | 186 | /* get the current local symbol table */ |
| | 187 | class CTcPrsSymtab *get_local_symtab() const { return local_symtab_; } |
| | 188 | |
| | 189 | /* get the 'goto' symbol table */ |
| | 190 | class CTcPrsSymtab *get_goto_symtab() const { return goto_symtab_; } |
| | 191 | |
| | 192 | /* set the current pragma C mode */ |
| | 193 | void set_pragma_c(int mode); |
| | 194 | |
| | 195 | /* turn preprocess expression mode on or off */ |
| | 196 | void set_pp_expr_mode(int f) { pp_expr_mode_ = f; } |
| | 197 | |
| | 198 | /* get the current preprocess expression mode flag */ |
| | 199 | int get_pp_expr_mode() const { return pp_expr_mode_; } |
| | 200 | |
| | 201 | /* set/get the sourceTextGroup mode */ |
| | 202 | void set_source_text_group_mode(int f); |
| | 203 | int get_source_text_group_mode() const { return src_group_mode_; } |
| | 204 | |
| | 205 | /* get/set the syntax-only mode flag */ |
| | 206 | int get_syntax_only() const { return syntax_only_; } |
| | 207 | void set_syntax_only(int f) { syntax_only_ = f; } |
| | 208 | |
| | 209 | /* |
| | 210 | * Get the constructor and finalize property ID's - all constructors |
| | 211 | * and finalizers have these property ID's respectively |
| | 212 | */ |
| | 213 | tc_prop_id get_constructor_prop() const { return constructor_prop_; } |
| | 214 | tc_prop_id get_finalize_prop() const { return finalize_prop_; } |
| | 215 | |
| | 216 | /* get the constructor property symbol */ |
| | 217 | class CTcSymProp *get_constructor_sym() const { return constructor_sym_; } |
| | 218 | |
| | 219 | /* get the object-call property */ |
| | 220 | tc_prop_id get_objcall_prop() const { return objcall_prop_; } |
| | 221 | |
| | 222 | /* |
| | 223 | * Check for unresolved external symbols. Scans the global symbol |
| | 224 | * table and logs an error for each unresolved external. Returns |
| | 225 | * true if any unresolved externals exist, false if not. |
| | 226 | */ |
| | 227 | int check_unresolved_externs(); |
| | 228 | |
| | 229 | /* |
| | 230 | * build the dictionaries - scans the global symbol table, and |
| | 231 | * inserts each object symbol's dictionary words into its |
| | 232 | * corresponding dictionary |
| | 233 | */ |
| | 234 | void build_dictionaries(); |
| | 235 | |
| | 236 | /* build the grammar productions */ |
| | 237 | void build_grammar_productions(); |
| | 238 | |
| | 239 | /* |
| | 240 | * Top-level parser. Parse functions, objects, and other top-level |
| | 241 | * definitions and declarations. |
| | 242 | */ |
| | 243 | class CTPNStmProg *parse_top(); |
| | 244 | |
| | 245 | /* |
| | 246 | * Parse a required semicolon. If the semicolon is present, we'll |
| | 247 | * simply skip it. If it's missing, we'll log an error and try to |
| | 248 | * resynchronize. If we find something that looks like it should go |
| | 249 | * at the end of an expression, we'll try to skip up to the next |
| | 250 | * semicolon; otherwise, we'll simply stay put. |
| | 251 | * |
| | 252 | * Returns zero if the caller should proceed, non-zero if we're at |
| | 253 | * end of file, in which case there's nothing more for the caller to |
| | 254 | * parse. |
| | 255 | */ |
| | 256 | static int parse_req_sem(); |
| | 257 | |
| | 258 | /* |
| | 259 | * Skip to the next semicolon, ignoring any tokens up to that point. |
| | 260 | * This can be used when the caller encounters an error that makes |
| | 261 | * it impossible to process the current statement further, and wants |
| | 262 | * to find the next semicolon in the hope that it will be a good |
| | 263 | * place to start again with the next statement. |
| | 264 | * |
| | 265 | * Returns zero if the caller should proceed, non-zero if we reach |
| | 266 | * the end of the file. |
| | 267 | */ |
| | 268 | static int skip_to_sem(); |
| | 269 | |
| | 270 | /* |
| | 271 | * Parse an expression. This parses a top-level "comma" expression. |
| | 272 | */ |
| | 273 | class CTcPrsNode *parse_expr(); |
| | 274 | |
| | 275 | /* |
| | 276 | * Parse a condition expression. This parses a top-level "comma" |
| | 277 | * expression, but displays a warning if the outermost operator in |
| | 278 | * the expression is an assignment, because such expressions are |
| | 279 | * very frequently meant as comparisons, but the '=' operator was |
| | 280 | * inadvertantly used instead of '=='. |
| | 281 | */ |
| | 282 | class CTcPrsNode *parse_cond_expr(); |
| | 283 | |
| | 284 | /* |
| | 285 | * Parse a value expression or a double-quoted string expression |
| | 286 | * (including a double-quoted string with embedded expressions). If |
| | 287 | * allow_comma_expr is true, we'll parse a comma expression; |
| | 288 | * otherwise, we'll parse an assignment expression. (A comma |
| | 289 | * expression is broader than an assignment expression, since the |
| | 290 | * comma separates assignment expressions.) |
| | 291 | */ |
| | 292 | class CTcPrsNode *parse_expr_or_dstr(int allow_comma_expr); |
| | 293 | |
| | 294 | /* |
| | 295 | * Parse an assignment expression - this is the next precedence |
| | 296 | * level down from comma expressions. In certain contexts, a |
| | 297 | * top-level comma expression is not allowed because a comma has a |
| | 298 | * separate meaning (in the initializer clause of a 'for' statement, |
| | 299 | * for example, or in a list element). |
| | 300 | */ |
| | 301 | class CTcPrsNode *parse_asi_expr(); |
| | 302 | |
| | 303 | /* parse an 'enum' top-level statement */ |
| | 304 | void parse_enum(int *err); |
| | 305 | |
| | 306 | /* parse a 'dictionary' top-level statement */ |
| | 307 | class CTPNStmTop *parse_dict(int *err); |
| | 308 | |
| | 309 | /* parse a 'grammar' top-level statement */ |
| | 310 | class CTPNStmTop *parse_grammar(int *err, int replace, int modify); |
| | 311 | |
| | 312 | /* parse and flatten a set of grammar rules */ |
| | 313 | class CTcPrsGramNode *flatten_gram_rule(int *err); |
| | 314 | |
| | 315 | /* parse a 'grammar' OR node */ |
| | 316 | class CTcPrsGramNode *parse_gram_or(int *err, int level); |
| | 317 | |
| | 318 | /* parse a 'grammar' CAT node */ |
| | 319 | class CTcPrsGramNode *parse_gram_cat(int *err, int level); |
| | 320 | |
| | 321 | /* parse a 'grammar' qualifier int value */ |
| | 322 | int parse_gram_qual_int(int *err, const char *qual_name, int *stm_end); |
| | 323 | |
| | 324 | /* skip to the end of a mal-formed grammar qualifier */ |
| | 325 | void parse_gram_qual_skip(int *err, int *stm_end); |
| | 326 | |
| | 327 | /* |
| | 328 | * Parse a 'function' top-level statement. If 'is_extern' is true, |
| | 329 | * the function is being defined externally, so it should have no |
| | 330 | * code body defined here (just the prototype). If 'replace' is |
| | 331 | * true, we're replacing an existing function. |
| | 332 | * |
| | 333 | * If 'func_kw_present' is true, the 'function' keyword is present |
| | 334 | * and must be skipped; otherwise, the function definition elides |
| | 335 | * the 'function' keyword and starts directly with the function name |
| | 336 | * symbol. |
| | 337 | */ |
| | 338 | class CTPNStmTop *parse_function(int *err, int is_extern, |
| | 339 | int replace, int modify, |
| | 340 | int func_kw_present); |
| | 341 | |
| | 342 | /* parse an 'intrinsic' top-level statement */ |
| | 343 | class CTPNStmTop *parse_intrinsic(int *err); |
| | 344 | |
| | 345 | /* parse an 'intrinsic class' top-level statement */ |
| | 346 | class CTPNStmTop *parse_intrinsic_class(int *err); |
| | 347 | |
| | 348 | /* parse an 'extern' top-level statement */ |
| | 349 | void parse_extern(int *err); |
| | 350 | |
| | 351 | /* |
| | 352 | * parse an object or function defintion (this is called when the |
| | 353 | * first thing in a statement is a symbol; we must check what |
| | 354 | * follows to determine what type of definition it is) |
| | 355 | */ |
| | 356 | class CTPNStmTop *parse_object_or_func(int *err, int replace, |
| | 357 | int suppress_error, |
| | 358 | int *suppress_next_error); |
| | 359 | |
| | 360 | /* parse a template definition statement */ |
| | 361 | class CTPNStmTop *parse_template_def(int *err, |
| | 362 | const class CTcToken *class_tok); |
| | 363 | |
| | 364 | /* add a template definition */ |
| | 365 | void add_template_def(class CTcSymObj *class_sym, |
| | 366 | class CTcObjTemplateItem *item_head, |
| | 367 | size_t item_cnt); |
| | 368 | |
| | 369 | /* add inherited template definitions */ |
| | 370 | void add_inherited_templates(class CTcSymObj *sc_sym, |
| | 371 | class CTcObjTemplateItem *item_head, |
| | 372 | size_t item_cnt); |
| | 373 | |
| | 374 | /* |
| | 375 | * expand the 'inherited' keyword in a template for the given |
| | 376 | * superclass template and add the result to the template list for the |
| | 377 | * class |
| | 378 | */ |
| | 379 | void expand_and_add_inherited_template(class CTcSymObj *sc_sym, |
| | 380 | class CTcObjTemplateItem *items, |
| | 381 | class CTcObjTemplate *sc_tpl); |
| | 382 | |
| | 383 | /* |
| | 384 | * build a list of superclass templates, for expanding an 'inherited' |
| | 385 | * token in a template definition |
| | 386 | */ |
| | 387 | void build_super_template_list(struct inh_tpl_entry **list_head, |
| | 388 | struct inh_tpl_entry **list_tail, |
| | 389 | class CTcSymObj *sc_sym); |
| | 390 | |
| | 391 | /* parse an 'object' statement */ |
| | 392 | class CTPNStmTop *parse_object_stm(int *err, int is_transient); |
| | 393 | |
| | 394 | /* |
| | 395 | * parse an object definition that starts with a '+' string; this |
| | 396 | * also parses '+ property' statements |
| | 397 | */ |
| | 398 | class CTPNStmTop *parse_plus_object(int *err); |
| | 399 | |
| | 400 | /* |
| | 401 | * Parse an object definition. If 'replace' is true, this |
| | 402 | * definition is to replace a previous definition of the same |
| | 403 | * object; if 'modify' is true, this definition is to modify a |
| | 404 | * previous definition. If 'is_class' is true, the definition is |
| | 405 | * for a class, otherwise it's for a static instance. |
| | 406 | * |
| | 407 | * If the definition uses the '+' notation to set the location, |
| | 408 | * plus_cnt gives the number of '+' signs preceding the object |
| | 409 | * definition. |
| | 410 | */ |
| | 411 | class CTPNStmTop *parse_object(int *err, int replace, int modify, |
| | 412 | int is_class, int plus_cnt, |
| | 413 | int is_transient); |
| | 414 | |
| | 415 | /* find or define an object symbol */ |
| | 416 | CTcSymObj *find_or_def_obj(const char *tok_txt, size_t tok_len, |
| | 417 | int replace, int modify, int *is_class, |
| | 418 | class CTcSymObj **mod_orig_sym, |
| | 419 | class CTcSymMetaclass **meta_sym, |
| | 420 | int *is_transient); |
| | 421 | |
| | 422 | /* parse an anonymous object */ |
| | 423 | class CTPNStmObject *parse_anon_object(int *err, int plus_cnt, |
| | 424 | int is_nested, |
| | 425 | struct tcprs_term_info *term_info, |
| | 426 | int is_transient); |
| | 427 | |
| | 428 | /* |
| | 429 | * Parse an object body. We start parsing from the colon that |
| | 430 | * introduces the class list, and parse the class list and the |
| | 431 | * property list for the object. |
| | 432 | * |
| | 433 | * If 'is_anon' is true, this is an anonymous object. 'obj_sym' |
| | 434 | * should be null in this case. |
| | 435 | * |
| | 436 | * If 'is_nested' is true, this is a nested object defined in-line in |
| | 437 | * an object's property list. Note that is_nested implies is_anon, |
| | 438 | * since nested objects are always anonymous. |
| | 439 | * |
| | 440 | * If this is a 'modify' definition, 'mod_orig_tok' should be set up |
| | 441 | * with the synthesized symbol for the modified base object; |
| | 442 | * otherwise, 'mod_orig_tok' should be null. |
| | 443 | * |
| | 444 | * If 'meta_sym' is non-null, we're modifying an intrinsic class. |
| | 445 | * This imposes certain restrictions; in particular, we cannot modify |
| | 446 | * a method defined in the native interface to the class. |
| | 447 | */ |
| | 448 | class CTPNStmObject *parse_object_body(int *err, class CTcSymObj *obj_sym, |
| | 449 | int is_class, int is_anon, |
| | 450 | int is_grammar, |
| | 451 | int is_nested, int modify, |
| | 452 | class CTcSymObj *mod_orig_sym, |
| | 453 | int plus_cnt, |
| | 454 | class CTcSymMetaclass *meta_sym, |
| | 455 | struct tcprs_term_info *term_info, |
| | 456 | int is_transient); |
| | 457 | |
| | 458 | /* parse an object template instance in an object body */ |
| | 459 | void parse_obj_template(int *err, class CTPNStmObject *obj_stm); |
| | 460 | |
| | 461 | /* search a superclass list for a template match */ |
| | 462 | const class CTcObjTemplate |
| | 463 | *find_class_template(const class CTPNSuperclass *first_sc, |
| | 464 | class CTcObjTemplateInst *src, |
| | 465 | size_t src_cnt, const CTPNSuperclass **def_sc, |
| | 466 | int *undescribed_class); |
| | 467 | |
| | 468 | /* find a match for a given template in the given list */ |
| | 469 | const class CTcObjTemplate |
| | 470 | *find_template_match(const class CTcObjTemplate *first_tpl, |
| | 471 | class CTcObjTemplateInst *src, |
| | 472 | size_t src_cnt); |
| | 473 | |
| | 474 | /* |
| | 475 | * Match a template to a given actual template parameter list. Returns |
| | 476 | * true if we match, false if not. We'll fill in the actual list with |
| | 477 | * the property symbols that we matched; these values are only |
| | 478 | * meaningful if we return true to indicate a match. |
| | 479 | */ |
| | 480 | int match_template(const class CTcObjTemplateItem *tpl_head, |
| | 481 | class CTcObjTemplateInst *src, size_t src_cnt); |
| | 482 | |
| | 483 | /* parse property definition within an object */ |
| | 484 | void parse_obj_prop(int *err, class CTPNStmObject *obj_stm, int replace, |
| | 485 | class CTcSymMetaclass *meta_sym, |
| | 486 | struct tcprs_term_info *term_info, |
| | 487 | struct propset_def *propset_stack, int propset_depth, |
| | 488 | int enclosing_obj_is_nested); |
| | 489 | |
| | 490 | /* parse a class definition */ |
| | 491 | class CTPNStmTop *parse_class(int *err); |
| | 492 | |
| | 493 | /* parse a 'modify' definition */ |
| | 494 | class CTPNStmTop *parse_modify(int *err); |
| | 495 | |
| | 496 | /* parse a 'replace' definition */ |
| | 497 | class CTPNStmTop *parse_replace(int *err); |
| | 498 | |
| | 499 | /* parse a 'property' statement */ |
| | 500 | void parse_property(int *err); |
| | 501 | |
| | 502 | /* parse an 'export' statement */ |
| | 503 | void parse_export(int *err); |
| | 504 | |
| | 505 | /* add an export for the given symbol; returns the new export record */ |
| | 506 | class CTcPrsExport *add_export(const char *sym, size_t sym_len); |
| | 507 | |
| | 508 | /* add an export record to our list */ |
| | 509 | void add_export_to_list(class CTcPrsExport *exp); |
| | 510 | |
| | 511 | /* get the head of the export list */ |
| | 512 | class CTcPrsExport *get_exp_head() const { return exp_head_; } |
| | 513 | |
| | 514 | /* |
| | 515 | * Parse a function or method body, starting with the formal parameter |
| | 516 | * list. If 'eq_before_brace' is set, we expect an '=' before the |
| | 517 | * opening brace of the code body, and we allow the expression syntax, |
| | 518 | * where an expression enclosed in parentheses can be used. |
| | 519 | * 'self_valid' indicates whether or not 'self' is valid in the context |
| | 520 | * of the code being compiled; for an object method, 'self' is usually |
| | 521 | * valid, while for a stand-alone function it isn't. |
| | 522 | */ |
| | 523 | class CTPNCodeBody *parse_code_body(int eq_before_brace, int is_obj_prop, |
| | 524 | int self_valid, |
| | 525 | int *p_argc, int *p_varargs, |
| | 526 | int *p_varargs_list, |
| | 527 | class CTcSymLocal ** |
| | 528 | p_varargs_list_local, |
| | 529 | int *has_retval, int *err, |
| | 530 | class CTcPrsSymtab *local_symtab, |
| | 531 | tcprs_codebodytype cb_type, |
| | 532 | struct propset_def *propset_stack, |
| | 533 | int propset_depth, |
| | 534 | struct CTcCodeBodyRef *enclosing, |
| | 535 | class CTcFormalTypeList **type_list); |
| | 536 | |
| | 537 | /* parse a nested code body (such as an anonymous function) */ |
| | 538 | class CTPNCodeBody *parse_nested_code_body( |
| | 539 | int eq_before_brace, |
| | 540 | int self_valid, |
| | 541 | int *p_argc, int *p_varargs, |
| | 542 | int *p_varargs_list, |
| | 543 | class CTcSymLocal **p_varargs_list_local, |
| | 544 | int *has_retval, int *err, |
| | 545 | class CTcPrsSymtab *local_symtab, |
| | 546 | tcprs_codebodytype cb_type); |
| | 547 | |
| | 548 | /* parse a formal parameter list */ |
| | 549 | void parse_formal_list(int count_only, int opt_allowed, |
| | 550 | int *argc, int *opt_argc, int *varargs, |
| | 551 | int *varargs_list, |
| | 552 | class CTcSymLocal **varargs_list_local, |
| | 553 | int *err, int base_formal_num, |
| | 554 | int for_short_anon_func, |
| | 555 | class CTcFormalTypeList **type_list); |
| | 556 | |
| | 557 | /* |
| | 558 | * Parse a compound statement. The caller must skip the opening |
| | 559 | * '{'; on return, we'll have skipped the closing '}'. |
| | 560 | * enclosing_symtab is the enclosing scope's symbol table, and |
| | 561 | * local_symtab is the symbol table for the new scope within the |
| | 562 | * compound statement; if the caller has not already allocated a new |
| | 563 | * symbol table for the inner scope, it should simply pass the same |
| | 564 | * value for both symbol tables. |
| | 565 | * |
| | 566 | * 'enclosing_switch' is the immediately enclosing switch statement, |
| | 567 | * if any. This is only set when we're parsing the immediate body |
| | 568 | * of a switch statement. |
| | 569 | */ |
| | 570 | class CTPNStmComp *parse_compound(int *err, int skip_lbrace, |
| | 571 | class CTPNStmSwitch *enclosing_switch, |
| | 572 | int use_enclosing_scope); |
| | 573 | |
| | 574 | /* parse a local variable definition */ |
| | 575 | class CTPNStm *parse_local(int *err); |
| | 576 | |
| | 577 | /* parse a local initializer */ |
| | 578 | class CTcPrsNode *parse_local_initializer(class CTcSymLocal *lcl, |
| | 579 | int *err); |
| | 580 | |
| | 581 | /* |
| | 582 | * Parse an individual statement. |
| | 583 | * |
| | 584 | * If 'compound_use_enclosing_scope' is true, then if the statement |
| | 585 | * is a compound statement (i.e., the current token is a left |
| | 586 | * brace), the compound statement will use the current scope rather |
| | 587 | * than creating its own scope. Normally, a compound statement |
| | 588 | * establishes its own scope, so that local variables can hide |
| | 589 | * locals and parameters defined outside the braces. In certain |
| | 590 | * cases, however, locals defined within the braces should share the |
| | 591 | * enclosing scope: at the top level of a function or method, for |
| | 592 | * example, the formal parameters and the locals within the function |
| | 593 | * body go in the same scope, so the function body's compound |
| | 594 | * statement doesn't create its own scope. |
| | 595 | */ |
| | 596 | class CTPNStm *parse_stm(int *err, class CTPNStmSwitch *enclosing_switch, |
| | 597 | int compound_use_enclosing_scope); |
| | 598 | |
| | 599 | /* parse a 'case' label */ |
| | 600 | class CTPNStm *parse_case(int *err, |
| | 601 | class CTPNStmSwitch *enclosing_switch); |
| | 602 | |
| | 603 | /* parse a 'default' label */ |
| | 604 | class CTPNStm *parse_default(int *err, |
| | 605 | class CTPNStmSwitch *enclosing_switch); |
| | 606 | |
| | 607 | /* parse an 'if' statement */ |
| | 608 | class CTPNStm *parse_if(int *err); |
| | 609 | |
| | 610 | /* parse a 'return' statement */ |
| | 611 | class CTPNStm *parse_return(int *err); |
| | 612 | |
| | 613 | /* parse a 'for' statement */ |
| | 614 | class CTPNStm *parse_for(int *err); |
| | 615 | |
| | 616 | /* parse a 'foreach' statement */ |
| | 617 | class CTPNStm *parse_foreach(int *err); |
| | 618 | |
| | 619 | /* parse a 'break' statement */ |
| | 620 | class CTPNStm *parse_break(int *err); |
| | 621 | |
| | 622 | /* parse a 'continue' statement */ |
| | 623 | class CTPNStm *parse_continue(int *err); |
| | 624 | |
| | 625 | /* parse a 'while' */ |
| | 626 | class CTPNStm *parse_while(int *err); |
| | 627 | |
| | 628 | /* parse a 'do-while' */ |
| | 629 | class CTPNStm *parse_do_while(int *err); |
| | 630 | |
| | 631 | /* parse a 'switch' */ |
| | 632 | class CTPNStm *parse_switch(int *err); |
| | 633 | |
| | 634 | /* parse a 'goto' */ |
| | 635 | class CTPNStm *parse_goto(int *err); |
| | 636 | |
| | 637 | /* parse a 'try' */ |
| | 638 | class CTPNStm *parse_try(int *err); |
| | 639 | |
| | 640 | /* parse a 'throw' */ |
| | 641 | class CTPNStm *parse_throw(int *err); |
| | 642 | |
| | 643 | /* |
| | 644 | * Create a symbol node. We'll look up the symbol in local scope. |
| | 645 | * If we find the symbol in local scope, we'll return a resolved |
| | 646 | * symbol node for the local scope item. If the symbol isn't |
| | 647 | * defined in local scope, we'll return an unresolved symbol node, |
| | 648 | * so that the symbol's resolution can be deferred until code |
| | 649 | * generation. |
| | 650 | */ |
| | 651 | class CTcPrsNode *create_sym_node(const textchar_t *sym, size_t sym_len); |
| | 652 | |
| | 653 | /* |
| | 654 | * Get the source file descriptor and line number for the current |
| | 655 | * source line. We note this at the start of each statement, so |
| | 656 | * that a statement node constructed when we finish parsing the |
| | 657 | * statement can record the location of the start of the statement. |
| | 658 | */ |
| | 659 | class CTcTokFileDesc *get_cur_desc() const { return cur_desc_; } |
| | 660 | long get_cur_linenum() const { return cur_linenum_; } |
| | 661 | |
| | 662 | /* |
| | 663 | * Get/set the current enclosing statement. An enclosing statement |
| | 664 | * is a 'try' or 'label:' container. At certain times, we need to |
| | 665 | * know the current enclosing statement, or one of its enclosing |
| | 666 | * statements; for example, a 'break' with a label must find the |
| | 667 | * label in the enclosing statement list to know where to jump to |
| | 668 | * after the 'break', and must also know about all of the enclosing |
| | 669 | * 'try' blocks our to that point so that it can invoke their |
| | 670 | * 'finally' blocks. |
| | 671 | */ |
| | 672 | class CTPNStmEnclosing *get_enclosing_stm() const |
| | 673 | { return enclosing_stm_; } |
| | 674 | class CTPNStmEnclosing *set_enclosing_stm(class CTPNStmEnclosing *stm) |
| | 675 | { |
| | 676 | class CTPNStmEnclosing *old_enclosing; |
| | 677 | |
| | 678 | /* remember the current enclosing statement for a moment */ |
| | 679 | old_enclosing = enclosing_stm_; |
| | 680 | |
| | 681 | /* set the new enclosing statement */ |
| | 682 | enclosing_stm_ = stm; |
| | 683 | |
| | 684 | /* |
| | 685 | * return the previous enclosing statement - this allows the |
| | 686 | * caller to restore the previous enclosing statement upon |
| | 687 | * leaving a nested block, if that's why the caller is setting a |
| | 688 | * new enclosing statement |
| | 689 | */ |
| | 690 | return old_enclosing; |
| | 691 | } |
| | 692 | |
| | 693 | /* get the current code body reference object */ |
| | 694 | struct CTcCodeBodyRef *get_cur_code_body() const |
| | 695 | { return cur_code_body_; } |
| | 696 | |
| | 697 | /* determine if 'self' is valid in the current context */ |
| | 698 | int is_self_valid() const { return self_valid_; } |
| | 699 | |
| | 700 | /* |
| | 701 | * get/set the 'self' reference status - this indicates whether or not |
| | 702 | * 'self' has been referenced, explicitly via the 'self' |
| | 703 | * pseudo-variable or implicitly (such as via a property reference or |
| | 704 | * method call), in the code body currently being parsed |
| | 705 | */ |
| | 706 | int self_referenced() const { return self_referenced_; } |
| | 707 | void set_self_referenced(int f) { self_referenced_ = f; } |
| | 708 | |
| | 709 | /* |
| | 710 | * get/set the full method context reference status - this indicates |
| | 711 | * whether or not any of the method context variables (self, |
| | 712 | * targetprop, targetobj, definingobj) have been referenced, explicitly |
| | 713 | * or implicitly, in the code body currently being parsed |
| | 714 | */ |
| | 715 | int full_method_ctx_referenced() const |
| | 716 | { return full_method_ctx_referenced_; } |
| | 717 | void set_full_method_ctx_referenced(int f) |
| | 718 | { full_method_ctx_referenced_ = f; } |
| | 719 | |
| | 720 | /* |
| | 721 | * Get/set the flag indicating whether or not the local context of the |
| | 722 | * outermost code body needs 'self'. The outer code body needs 'self' |
| | 723 | * in the local context if any lexically nested code body requires |
| | 724 | * access to 'self'. |
| | 725 | */ |
| | 726 | int local_ctx_needs_self() const { return local_ctx_needs_self_; } |
| | 727 | void set_local_ctx_needs_self(int f) { local_ctx_needs_self_ = f; } |
| | 728 | |
| | 729 | /* |
| | 730 | * Get/set the flag indicating whether or not the local context of the |
| | 731 | * outermost code body needs the full method context stored in its |
| | 732 | * local context. The outer code body needs the full context stored if |
| | 733 | * any lexically nested code body requires access to any of the method |
| | 734 | * context variables besides 'self' (targetprop, targetobj, |
| | 735 | * definingobj). |
| | 736 | */ |
| | 737 | int local_ctx_needs_full_method_ctx() const |
| | 738 | { return local_ctx_needs_full_method_ctx_; } |
| | 739 | void set_local_ctx_needs_full_method_ctx(int f) |
| | 740 | { local_ctx_needs_full_method_ctx_ = f; } |
| | 741 | |
| | 742 | /* |
| | 743 | * Add a code label. This creates a 'goto' symbol table for the |
| | 744 | * current code body if one doesn't already exist |
| | 745 | */ |
| | 746 | class CTcSymLabel *add_code_label(const class CTcToken *tok); |
| | 747 | |
| | 748 | /* |
| | 749 | * Set the debugger local symbol table. Returns the previous symbol |
| | 750 | * table so that it can be restored if desired. |
| | 751 | */ |
| | 752 | class CTcPrsDbgSymtab *set_debug_symtab(class CTcPrsDbgSymtab *tab) |
| | 753 | { |
| | 754 | class CTcPrsDbgSymtab *old_tab; |
| | 755 | |
| | 756 | /* remember the original for later use */ |
| | 757 | old_tab = debug_symtab_; |
| | 758 | |
| | 759 | /* set the new table */ |
| | 760 | debug_symtab_ = tab; |
| | 761 | |
| | 762 | /* return the original */ |
| | 763 | return old_tab; |
| | 764 | } |
| | 765 | |
| | 766 | /* |
| | 767 | * given a (1-based) object file symbol index, get the symbol |
| | 768 | */ |
| | 769 | class CTcSymbol *get_objfile_sym(uint idx) |
| | 770 | { return (idx == 0 ? 0 : obj_sym_list_[idx - 1]); } |
| | 771 | |
| | 772 | /* |
| | 773 | * given a 1-based object file symbol index, get an object symbol; |
| | 774 | * if the symbol does not refer to an object, we'll return null |
| | 775 | */ |
| | 776 | class CTcSymObj *get_objfile_objsym(uint idx); |
| | 777 | |
| | 778 | /* |
| | 779 | * given an object file (1-based) object file dictionary index, get |
| | 780 | * the dictionary entry |
| | 781 | */ |
| | 782 | class CTcDictEntry *get_obj_dict(uint idx) |
| | 783 | { return (idx == 0 ? 0 : obj_dict_list_[idx - 1]); } |
| | 784 | |
| | 785 | /* add a dictionary object loaded from the object file */ |
| | 786 | void add_dict_from_obj_file(class CTcSymObj *sym); |
| | 787 | |
| | 788 | /* add a symbol object loaded from the object file */ |
| | 789 | void add_sym_from_obj_file(uint idx, class CTcSymbol *sym); |
| | 790 | |
| | 791 | /* |
| | 792 | * Get the next object file symbol index. Object file symbol |
| | 793 | * indices are used to relate symbols stored in the object file to |
| | 794 | * the corresponding symbol object in memory when the object file is |
| | 795 | * reloaded. |
| | 796 | */ |
| | 797 | uint get_next_obj_file_sym_idx() |
| | 798 | { |
| | 799 | /* return the next index, consuming the index value */ |
| | 800 | return obj_file_sym_idx_++; |
| | 801 | } |
| | 802 | |
| | 803 | /* |
| | 804 | * Get the next object file dictionary index. |
| | 805 | */ |
| | 806 | uint get_next_obj_file_dict_idx() |
| | 807 | { |
| | 808 | /* return the next index, consuming the index value */ |
| | 809 | return obj_file_dict_idx_++; |
| | 810 | } |
| | 811 | |
| | 812 | /* |
| | 813 | * add an anonymous function or other anonymous top-level statement |
| | 814 | * to our list of nested top-level statements |
| | 815 | */ |
| | 816 | void add_nested_stm(class CTPNStmTop *stm); |
| | 817 | |
| | 818 | /* add an anonymous object to our list */ |
| | 819 | void add_anon_obj(class CTcSymObj *obj); |
| | 820 | |
| | 821 | /* add a non-symbolic object ID */ |
| | 822 | void add_nonsym_obj(tctarg_obj_id_t id); |
| | 823 | |
| | 824 | /* determine if the current code body has a local context */ |
| | 825 | int has_local_ctx() const { return has_local_ctx_ != 0; } |
| | 826 | |
| | 827 | /* get the local context variable number */ |
| | 828 | int get_local_ctx_var() const { return local_ctx_var_num_; } |
| | 829 | |
| | 830 | /* set up a local context */ |
| | 831 | void init_local_ctx(); |
| | 832 | |
| | 833 | /* allocate a context variable property ID */ |
| | 834 | tctarg_prop_id_t alloc_ctx_var_prop(); |
| | 835 | |
| | 836 | /* |
| | 837 | * allocate a context variable index - this assigns an array index |
| | 838 | * for a context variable within the context object that contains |
| | 839 | * the shared locals for its scope |
| | 840 | */ |
| | 841 | int alloc_ctx_arr_idx(); |
| | 842 | |
| | 843 | /* allocate a local for use as a local context holder */ |
| | 844 | int alloc_ctx_holder_var() { return alloc_local(); } |
| | 845 | |
| | 846 | /* get the maximum number of locals required in the function */ |
| | 847 | int get_max_local_cnt() const { return max_local_cnt_; } |
| | 848 | |
| | 849 | /* |
| | 850 | * find a grammar production symbol, adding a new one if needed, |
| | 851 | * returning the grammar production list entry for the object |
| | 852 | */ |
| | 853 | class CTcGramProdEntry *declare_gramprod(const char *sym, size_t len); |
| | 854 | |
| | 855 | /* find a grammar production list entry for a given object */ |
| | 856 | class CTcGramProdEntry *get_gramprod_entry(class CTcSymObj *sym); |
| | 857 | |
| | 858 | /* find a grammar production symbol, adding a new one if needed */ |
| | 859 | class CTcSymObj *find_or_def_gramprod(const char *txt, size_t len, |
| | 860 | class CTcGramProdEntry **entryp); |
| | 861 | |
| | 862 | /* allocate a new enumerator ID */ |
| | 863 | ulong new_enum_id() { return next_enum_id_++; } |
| | 864 | |
| | 865 | /* get the number of enumerator ID's allocated */ |
| | 866 | ulong get_enum_count() const { return next_enum_id_; } |
| | 867 | |
| | 868 | /* |
| | 869 | * Look up a property symbol, adding it if not yet defined. If the |
| | 870 | * symbol is defined as another type, we'll show an error if |
| | 871 | * show_err is true, and return null. |
| | 872 | */ |
| | 873 | CTcSymProp *look_up_prop(const class CTcToken *tok, int show_err); |
| | 874 | |
| | 875 | /* get the '+' property for tracking the location graph */ |
| | 876 | CTcSymProp *get_plus_prop() const { return plus_prop_; } |
| | 877 | |
| | 878 | /* |
| | 879 | * Read a length-prefixed string from a file. Copies the string into |
| | 880 | * tokenizer space (which is guaranteed valid throughout compilation), |
| | 881 | * and returns a pointer to the tokenizer copy. If ret_len is null, |
| | 882 | * we'll return a null-terminated string; otherwise, we'll return a |
| | 883 | * non-null-terminated string and set *ret_len to the length of the |
| | 884 | * string. |
| | 885 | * |
| | 886 | * The string must fit in the temporary buffer to be read, but the |
| | 887 | * permanent tokenizer copy is returned rather than the temp buffer. |
| | 888 | * If the string doesn't fit in the temp buffer (with null |
| | 889 | * termination, if null termination is requested), we'll log the given |
| | 890 | * error. |
| | 891 | */ |
| | 892 | static const char *read_len_prefix_str |
| | 893 | (CVmFile *fp, char *tmp_buf, size_t tmp_buf_len, size_t *ret_len, |
| | 894 | int err_if_too_long); |
| | 895 | |
| | 896 | /* |
| | 897 | * Read a length-prefixed string into the given buffer, null |
| | 898 | * terminating the result. If the string is too long for the buffer, |
| | 899 | * we'll flag the given error code and return non-zero. If |
| | 900 | * successful, we'll return zero. |
| | 901 | */ |
| | 902 | static int read_len_prefix_str(CVmFile *fp, char *buf, size_t buf_len, |
| | 903 | int err_if_too_long); |
| | 904 | |
| | 905 | |
| | 906 | /* get the miscVocab property symbol */ |
| | 907 | tctarg_prop_id_t get_miscvocab_prop() const { return miscvocab_prop_; } |
| | 908 | |
| | 909 | private: |
| | 910 | /* clear the anonymous function local context information */ |
| | 911 | void clear_local_ctx(); |
| | 912 | |
| | 913 | /* |
| | 914 | * begin a property expression, saving parser state for later |
| | 915 | * restoration with finish_prop_expr |
| | 916 | */ |
| | 917 | void begin_prop_expr(class CTcPrsPropExprSave *save_info); |
| | 918 | |
| | 919 | /* |
| | 920 | * Finish a property expression, wrapping it in a code body if |
| | 921 | * necessary to allow for an embedded anonymous function. Returns |
| | 922 | * null if no wrapping is required, in which case the original |
| | 923 | * expression should continue to be used, or the non-null code body |
| | 924 | * wrapper if needed, in which case the original expression should be |
| | 925 | * discarded in favor of the fully wrapped code body. |
| | 926 | */ |
| | 927 | class CTPNCodeBody *finish_prop_expr(class CTcPrsPropExprSave *save_info, |
| | 928 | class CTcPrsNode *expr, |
| | 929 | int is_static, |
| | 930 | class CTcSymProp *prop_sym); |
| | 931 | |
| | 932 | /* |
| | 933 | * callback for symbol table enumeration for writing a symbol export |
| | 934 | * file |
| | 935 | */ |
| | 936 | static void write_sym_cb(void *ctx, class CTcSymbol *sym); |
| | 937 | |
| | 938 | /* callback for symbol table enumeration for writing an object file */ |
| | 939 | static void write_obj_cb(void *ctx, class CTcSymbol *sym); |
| | 940 | |
| | 941 | /* callback for symbol table enumeration for writing cross references */ |
| | 942 | static void write_obj_ref_cb(void *ctx, class CTcSymbol *sym); |
| | 943 | |
| | 944 | /* callback for symbol table enumeration for named grammar rules */ |
| | 945 | static void write_obj_gram_cb(void *ctx, class CTcSymbol *sym); |
| | 946 | |
| | 947 | /* callback for symbol table enumeration for merging grammar rules */ |
| | 948 | static void build_grammar_cb(void *ctx, class CTcSymbol *sym); |
| | 949 | |
| | 950 | |
| | 951 | /* |
| | 952 | * Enter a scope. Upon entering, we'll remember the current local |
| | 953 | * variable data; on leaving, we'll restore the enclosing scope. |
| | 954 | */ |
| | 955 | void enter_scope(struct tcprs_scope_t *info) |
| | 956 | { |
| | 957 | /* remember the current scope information */ |
| | 958 | info->local_symtab = local_symtab_; |
| | 959 | info->enclosing_symtab = enclosing_local_symtab_; |
| | 960 | info->local_cnt = local_cnt_; |
| | 961 | |
| | 962 | /* |
| | 963 | * We haven't yet allocated a symbol table local to the new |
| | 964 | * scope -- we defer this until we actually need to insert a |
| | 965 | * symbol into the new scope. In order to detect when we need |
| | 966 | * to create our own local symbol table, we keep track of the |
| | 967 | * enclosing symbol table; when the local table is the same as |
| | 968 | * the enclosing table, and we need to insert a symbol, it means |
| | 969 | * that we must create a new table for the current scope. |
| | 970 | */ |
| | 971 | enclosing_local_symtab_ = local_symtab_; |
| | 972 | } |
| | 973 | |
| | 974 | /* leave a scope */ |
| | 975 | void leave_scope(struct tcprs_scope_t *info) |
| | 976 | { |
| | 977 | /* restore enclosing scope information */ |
| | 978 | local_symtab_ = info->local_symtab; |
| | 979 | enclosing_local_symtab_ = info->enclosing_symtab; |
| | 980 | |
| | 981 | /* return to the local count in the enclosing scope */ |
| | 982 | // $$$ we can't actually do this because variables could |
| | 983 | // be allocated after this scope ends, but need lifetimes |
| | 984 | // that overlap with the enclosed scope; what we actually |
| | 985 | // need to do, if we wanted to optimize things, would be |
| | 986 | // to allow this block of variables to be used in *disjoint* |
| | 987 | // scopes, but not again in enclosing scopes. We can easily, |
| | 988 | // though suboptimally, handle this by simply not allowing |
| | 989 | // the variables in the enclosed scope to be re-used at all |
| | 990 | // in the current code block. |
| | 991 | // local_cnt_ = info->local_cnt; |
| | 992 | } |
| | 993 | |
| | 994 | /* |
| | 995 | * Create a local symbol table in the current scope, if necessary. |
| | 996 | * If we've already created a local symbol table for the current |
| | 997 | * scope, this has no effect. |
| | 998 | */ |
| | 999 | void create_scope_local_symtab(); |
| | 1000 | |
| | 1001 | /* allocate a new local variable ID */ |
| | 1002 | int alloc_local() |
| | 1003 | { |
| | 1004 | /* |
| | 1005 | * if this exceeds the maximum depth in the block so far, note |
| | 1006 | * the new maximum depth |
| | 1007 | */ |
| | 1008 | if (local_cnt_ + 1 > max_local_cnt_) |
| | 1009 | max_local_cnt_ = local_cnt_ + 1; |
| | 1010 | |
| | 1011 | /* return the local number, and increment the counter */ |
| | 1012 | return local_cnt_++; |
| | 1013 | } |
| | 1014 | |
| | 1015 | /* find a dictionary symbol, adding a new one if needed */ |
| | 1016 | class CTcDictEntry *declare_dict(const char *sym, size_t len); |
| | 1017 | |
| | 1018 | /* create a new dictionary list entry */ |
| | 1019 | class CTcDictEntry *create_dict_entry(class CTcSymObj *sym); |
| | 1020 | |
| | 1021 | /* find a dictionary list entry for a given object */ |
| | 1022 | class CTcDictEntry *get_dict_entry(class CTcSymObj *sym); |
| | 1023 | |
| | 1024 | /* create a new grammar production list entry */ |
| | 1025 | class CTcGramProdEntry *create_gramprod_entry(class CTcSymObj *sym); |
| | 1026 | |
| | 1027 | /* symbol enumerator - look for unresolved external references */ |
| | 1028 | static void enum_sym_extref(void *ctx, class CTcSymbol *sym); |
| | 1029 | |
| | 1030 | /* symbol enumerator - apply internal fixups */ |
| | 1031 | static void enum_sym_internal_fixup(void *ctx, class CTcSymbol *sym); |
| | 1032 | |
| | 1033 | /* symbol enumerator - build dictionary */ |
| | 1034 | static void enum_sym_dict(void *ctx, class CTcSymbol *sym); |
| | 1035 | |
| | 1036 | /* enumeration callback - context local conversion */ |
| | 1037 | static void enum_for_ctx_locals(void *ctx, class CTcSymbol *sym); |
| | 1038 | |
| | 1039 | /* global symbol table */ |
| | 1040 | class CTcPrsSymtab *global_symtab_; |
| | 1041 | |
| | 1042 | /* the constructor property ID and symbol */ |
| | 1043 | tc_prop_id constructor_prop_; |
| | 1044 | class CTcSymProp *constructor_sym_; |
| | 1045 | |
| | 1046 | /* the finalizer property ID */ |
| | 1047 | tc_prop_id finalize_prop_; |
| | 1048 | |
| | 1049 | /* object-call property ID */ |
| | 1050 | tc_prop_id objcall_prop_; |
| | 1051 | |
| | 1052 | /* grammarInfo property symbol */ |
| | 1053 | class CTcSymProp *graminfo_prop_; |
| | 1054 | |
| | 1055 | /* miscVocab property ID */ |
| | 1056 | tctarg_prop_id_t miscvocab_prop_; |
| | 1057 | |
| | 1058 | /* lexicalParent property symbol */ |
| | 1059 | class CTcSymProp *lexical_parent_sym_; |
| | 1060 | |
| | 1061 | /* sourceTextOrder property symbol */ |
| | 1062 | class CTcSymProp *src_order_sym_; |
| | 1063 | |
| | 1064 | /* sourceTextGroup property symbol */ |
| | 1065 | class CTcSymProp *src_group_sym_; |
| | 1066 | |
| | 1067 | /* sourceTextGroupName, sourceTextGroupOrder */ |
| | 1068 | class CTcSymProp *src_group_mod_sym_; |
| | 1069 | class CTcSymProp *src_group_seq_sym_; |
| | 1070 | |
| | 1071 | |
| | 1072 | /* |
| | 1073 | * Source text order index. Each time we encounter an object |
| | 1074 | * definition in the source code, we assign the current index value to |
| | 1075 | * the object's 'sourceTextOrder' property, then we increment the |
| | 1076 | * index. This provides the game program with information on the order |
| | 1077 | * in which static objects appear in the source code, so that the |
| | 1078 | * program can sort a collection of objects into their source file |
| | 1079 | * order if desired. |
| | 1080 | */ |
| | 1081 | long src_order_idx_; |
| | 1082 | |
| | 1083 | /* |
| | 1084 | * Source group object. If we're assigning source text group values, |
| | 1085 | * we create an object for each source module to identify the module. |
| | 1086 | */ |
| | 1087 | tctarg_obj_id_t src_group_id_; |
| | 1088 | |
| | 1089 | /* |
| | 1090 | * flag: in preprocessor constant expression mode; double-quoted |
| | 1091 | * strings should be treated the same as single-quoted strings for |
| | 1092 | * concatenation and comparisons |
| | 1093 | */ |
| | 1094 | uint pp_expr_mode_ : 1; |
| | 1095 | |
| | 1096 | /* |
| | 1097 | * Is source text mode turned on? If this is true, we'll generate |
| | 1098 | * sourceTextGroup properties for objects, otherwise we won't. |
| | 1099 | */ |
| | 1100 | uint src_group_mode_ : 1; |
| | 1101 | |
| | 1102 | /* |
| | 1103 | * Flag: syntax-only mode. We use this mode to analyze the syntax |
| | 1104 | * of the file without building the image; this is used, for |
| | 1105 | * example, to build the exported symbol file for a source file. In |
| | 1106 | * this mode, we'll suppress certain warnings and avoid doing work |
| | 1107 | * that's not necessary for syntactic analysis; for example, we |
| | 1108 | * won't show "unreachable code" errors. |
| | 1109 | */ |
| | 1110 | uint syntax_only_ : 1; |
| | 1111 | |
| | 1112 | /* |
| | 1113 | * Code block parsing state |
| | 1114 | */ |
| | 1115 | |
| | 1116 | /* |
| | 1117 | * 'goto' symbol table for the current code block - there's only one |
| | 1118 | * 'goto' scope for an entire code block, so this never changes over |
| | 1119 | * the course of a code block |
| | 1120 | */ |
| | 1121 | class CTcPrsSymtab *goto_symtab_; |
| | 1122 | |
| | 1123 | /* |
| | 1124 | * Current local symbol table. Each inner scope that defines its |
| | 1125 | * own local variables has its own local symbol table, nested within |
| | 1126 | * the enclosing scope's. When leaving an inner scope, this should |
| | 1127 | * always be restored to the local symbol table of the enclosing |
| | 1128 | * scope. |
| | 1129 | */ |
| | 1130 | class CTcPrsSymtab *local_symtab_; |
| | 1131 | |
| | 1132 | /* |
| | 1133 | * Enclosing local symbol table. If this is the same as |
| | 1134 | * local_symtab_, it means that the current scope has not yet |
| | 1135 | * created its own local symbol table. We defer this creation until |
| | 1136 | * we find we actually need a local symbol table in a scope, since |
| | 1137 | * most scopes don't define any of their own local variables. |
| | 1138 | */ |
| | 1139 | class CTcPrsSymtab *enclosing_local_symtab_; |
| | 1140 | |
| | 1141 | /* |
| | 1142 | * Current debugger local symbol table. When we're compiling a |
| | 1143 | * debugger expression, this will provide access to the current |
| | 1144 | * local scope in the debug records. |
| | 1145 | */ |
| | 1146 | class CTcPrsDbgSymtab *debug_symtab_; |
| | 1147 | |
| | 1148 | /* |
| | 1149 | * Number of local variables allocated so far in current code block |
| | 1150 | * -- this reflects nesting to the current innermost scope, because |
| | 1151 | * variables in inner scope are allocated in the same stack frame as |
| | 1152 | * the enclosing scopes. When leaving an inner scope, this should |
| | 1153 | * be restored |
| | 1154 | */ |
| | 1155 | int local_cnt_; |
| | 1156 | |
| | 1157 | /* |
| | 1158 | * maximum local variable depth for the current code block -- this |
| | 1159 | * reflects the maximum depth, including all inner scopes so far |
| | 1160 | */ |
| | 1161 | int max_local_cnt_; |
| | 1162 | |
| | 1163 | /* |
| | 1164 | * Enclosing statement - this is the innermost 'try' or 'label:' |
| | 1165 | * enclosing the current code. |
| | 1166 | */ |
| | 1167 | class CTPNStmEnclosing *enclosing_stm_; |
| | 1168 | |
| | 1169 | /* file descriptor and line number at start of current statement */ |
| | 1170 | class CTcTokFileDesc *cur_desc_; |
| | 1171 | long cur_linenum_; |
| | 1172 | |
| | 1173 | /* currently active dictionary */ |
| | 1174 | class CTcDictEntry *dict_cur_; |
| | 1175 | |
| | 1176 | /* head and tail of dictionary list */ |
| | 1177 | class CTcDictEntry *dict_head_; |
| | 1178 | class CTcDictEntry *dict_tail_; |
| | 1179 | |
| | 1180 | /* head and tail of grammar production entry list */ |
| | 1181 | class CTcGramProdEntry *gramprod_head_; |
| | 1182 | class CTcGramProdEntry *gramprod_tail_; |
| | 1183 | |
| | 1184 | /* |
| | 1185 | * array of symbols loaded from the object file - these are indexed |
| | 1186 | * by the object file symbol index stored in symbol references in |
| | 1187 | * the object file, allowing us to fix up references from one symbol |
| | 1188 | * to another during loading |
| | 1189 | */ |
| | 1190 | class CTcSymbol **obj_sym_list_; |
| | 1191 | |
| | 1192 | /* |
| | 1193 | * array of dictionary objects for the object file being loaded - |
| | 1194 | * these are indexed by the dictionary index stored in symbol |
| | 1195 | * references in the object file, allowing us to fix up references |
| | 1196 | * from an object to its dictionary |
| | 1197 | */ |
| | 1198 | class CTcDictEntry **obj_dict_list_; |
| | 1199 | |
| | 1200 | /* next available object file dictionary index */ |
| | 1201 | uint obj_file_dict_idx_; |
| | 1202 | |
| | 1203 | /* next available object file symbol index */ |
| | 1204 | uint obj_file_sym_idx_; |
| | 1205 | |
| | 1206 | /* dictionary property list head */ |
| | 1207 | class CTcDictPropEntry *dict_prop_head_; |
| | 1208 | |
| | 1209 | /* |
| | 1210 | * Head and tail of list of nested top-level statements parsed for the |
| | 1211 | * current top-level statement. This list includes anonymous |
| | 1212 | * functions and nested objects, since these statements must |
| | 1213 | * ultimately be linked into the top-level statement queue, but can't |
| | 1214 | * be linked in while they're being parsed because of their nested |
| | 1215 | * location in the recursive descent. We'll throw each new nested |
| | 1216 | * top-level statement into this list as we parse them, then add this |
| | 1217 | * list to the top-level statement list when we're done with the |
| | 1218 | * entire program. |
| | 1219 | */ |
| | 1220 | class CTPNStmTop *nested_stm_head_; |
| | 1221 | class CTPNStmTop *nested_stm_tail_; |
| | 1222 | |
| | 1223 | /* |
| | 1224 | * Anonymous object list. This is a list of objects which are |
| | 1225 | * defined without symbol names. |
| | 1226 | */ |
| | 1227 | class CTcSymObj *anon_obj_head_; |
| | 1228 | class CTcSymObj *anon_obj_tail_; |
| | 1229 | |
| | 1230 | /* |
| | 1231 | * Non-symbolic object list. This is a list of objects that are |
| | 1232 | * defined without symbols at all. |
| | 1233 | */ |
| | 1234 | struct tcprs_nonsym_obj *nonsym_obj_head_; |
| | 1235 | struct tcprs_nonsym_obj *nonsym_obj_tail_; |
| | 1236 | |
| | 1237 | /* |
| | 1238 | * Object template list - this is the master list of templates for the |
| | 1239 | * root object class. |
| | 1240 | */ |
| | 1241 | class CTcObjTemplate *template_head_; |
| | 1242 | class CTcObjTemplate *template_tail_; |
| | 1243 | |
| | 1244 | /* |
| | 1245 | * Object template instance parsing expression array. Each time we |
| | 1246 | * define a new template, we'll make sure this array is long enough |
| | 1247 | * for the longest defined template. We use this list when we're |
| | 1248 | * parsing a template instance to keep track of the expressions in |
| | 1249 | * the template instance - we can't know until we have the entire |
| | 1250 | * list which template we're using, so we must keep track of the |
| | 1251 | * entire list until we reach the end of the list. |
| | 1252 | */ |
| | 1253 | class CTcObjTemplateInst *template_expr_; |
| | 1254 | size_t template_expr_max_; |
| | 1255 | |
| | 1256 | /* head and tail of exported symbol list */ |
| | 1257 | class CTcPrsExport *exp_head_; |
| | 1258 | class CTcPrsExport *exp_tail_; |
| | 1259 | |
| | 1260 | /* |
| | 1261 | * Flag: current code body has a local variable context object. If |
| | 1262 | * this is set, we must generate code that sets up the context |
| | 1263 | * object on entry to the code body. |
| | 1264 | */ |
| | 1265 | unsigned int has_local_ctx_ : 1; |
| | 1266 | |
| | 1267 | /* local variable number of the code body's local variable context */ |
| | 1268 | int local_ctx_var_num_; |
| | 1269 | |
| | 1270 | /* array of context variable property values */ |
| | 1271 | tctarg_prop_id_t *ctx_var_props_; |
| | 1272 | |
| | 1273 | /* size of array */ |
| | 1274 | size_t ctx_var_props_size_; |
| | 1275 | |
| | 1276 | /* number of context variable property values in the list */ |
| | 1277 | size_t ctx_var_props_cnt_; |
| | 1278 | |
| | 1279 | /* |
| | 1280 | * number of context variable property values assigned to the |
| | 1281 | * current code body |
| | 1282 | */ |
| | 1283 | size_t ctx_var_props_used_; |
| | 1284 | |
| | 1285 | /* next available local variable context index */ |
| | 1286 | int next_ctx_arr_idx_; |
| | 1287 | |
| | 1288 | /* reference to the current code body being parsed */ |
| | 1289 | CTcCodeBodyRef *cur_code_body_; |
| | 1290 | |
| | 1291 | /* flag: 'self' is valid in current code body */ |
| | 1292 | int self_valid_; |
| | 1293 | |
| | 1294 | /* |
| | 1295 | * flag: 'self' is used (explicitly or implicitly, such as via a |
| | 1296 | * property reference or method call) in the current code body |
| | 1297 | */ |
| | 1298 | int self_referenced_; |
| | 1299 | |
| | 1300 | /* |
| | 1301 | * Flag: method context beyond 'self' (targetprop, targetobj, |
| | 1302 | * definingobj) is referenced (explicitly or implicitly, such as via |
| | 1303 | * 'inherited' or 'delegated') in the current code body. |
| | 1304 | */ |
| | 1305 | int full_method_ctx_referenced_; |
| | 1306 | |
| | 1307 | /* |
| | 1308 | * Flags: the local context of the outermost code body requires |
| | 1309 | * 'self'/the full method context to be stored. |
| | 1310 | */ |
| | 1311 | int local_ctx_needs_self_; |
| | 1312 | int local_ctx_needs_full_method_ctx_; |
| | 1313 | |
| | 1314 | /* next available enumerator ID */ |
| | 1315 | ulong next_enum_id_; |
| | 1316 | |
| | 1317 | /* |
| | 1318 | * The '+' property - this is the property that defines the |
| | 1319 | * containment graph for the purposes of the '+' syntax. |
| | 1320 | */ |
| | 1321 | class CTcSymProp *plus_prop_; |
| | 1322 | |
| | 1323 | /* |
| | 1324 | * '+' property location stack. Each time the program defines an |
| | 1325 | * object using the '+' notation to set the location, we'll update our |
| | 1326 | * record here of the last object at that depth. Any time an object |
| | 1327 | * is defined at depth N (i.e., using N '+' signs), its location is |
| | 1328 | * set to the last object at depth N-1. An object with no '+' signs |
| | 1329 | * is at depth zero. |
| | 1330 | */ |
| | 1331 | class CTPNStmObject **plus_stack_; |
| | 1332 | size_t plus_stack_alloc_; |
| | 1333 | |
| | 1334 | /* |
| | 1335 | * The module name and sequence number, if known. The module name is |
| | 1336 | * the name as it appears on the command line, makefile (.t3m), or |
| | 1337 | * library (.tl) file. The sequence number is an ordinal giving its |
| | 1338 | * position in the list of modules making up the overall program build. |
| | 1339 | * We use this information in generating the sourceTextGroup object. |
| | 1340 | */ |
| | 1341 | char *module_name_; |
| | 1342 | int module_seqno_; |
| | 1343 | }; |
| | 1344 | |
| | 1345 | /* ------------------------------------------------------------------------ */ |
| | 1346 | /* |
| | 1347 | * Statement termination information. This is used for certain nested |
| | 1348 | * definition parsers, where a lack of termination in the nested |
| | 1349 | * definition is to be interpreted as being actually caused by a lack of |
| | 1350 | * termination of the enclosing definition. |
| | 1351 | */ |
| | 1352 | struct tcprs_term_info |
| | 1353 | { |
| | 1354 | /* initialize */ |
| | 1355 | void init(class CTcTokFileDesc *desc, long linenum) |
| | 1356 | { |
| | 1357 | /* remember the current location */ |
| | 1358 | desc_ = desc; |
| | 1359 | linenum_ = linenum; |
| | 1360 | |
| | 1361 | /* no termination error yet */ |
| | 1362 | unterm_ = FALSE; |
| | 1363 | } |
| | 1364 | |
| | 1365 | /* |
| | 1366 | * source location where original terminator might have been - this is |
| | 1367 | * where we decided to go into a nested definition, so if it turns out |
| | 1368 | * that the definintion shouldn't have been nested after all, there |
| | 1369 | * was missing termination here |
| | 1370 | */ |
| | 1371 | class CTcTokFileDesc *desc_; |
| | 1372 | long linenum_; |
| | 1373 | |
| | 1374 | /* |
| | 1375 | * flag: termination was in fact missing in the nested definition; the |
| | 1376 | * nested parser sets this to relay the problem to the caller |
| | 1377 | */ |
| | 1378 | int unterm_; |
| | 1379 | }; |
| | 1380 | |
| | 1381 | /* ------------------------------------------------------------------------ */ |
| | 1382 | /* |
| | 1383 | * Object template list entry |
| | 1384 | */ |
| | 1385 | class CTcObjTemplate |
| | 1386 | { |
| | 1387 | public: |
| | 1388 | CTcObjTemplate(class CTcObjTemplateItem *item_head, size_t item_cnt) |
| | 1389 | { |
| | 1390 | /* remember my item list */ |
| | 1391 | items_ = item_head; |
| | 1392 | item_cnt_ = item_cnt; |
| | 1393 | |
| | 1394 | /* not in a list yet */ |
| | 1395 | nxt_ = 0; |
| | 1396 | } |
| | 1397 | |
| | 1398 | /* head of list of template items */ |
| | 1399 | class CTcObjTemplateItem *items_; |
| | 1400 | |
| | 1401 | /* number of items in the list */ |
| | 1402 | size_t item_cnt_; |
| | 1403 | |
| | 1404 | /* next template in master list of templates */ |
| | 1405 | CTcObjTemplate *nxt_; |
| | 1406 | }; |
| | 1407 | |
| | 1408 | /* |
| | 1409 | * Object template list item |
| | 1410 | */ |
| | 1411 | class CTcObjTemplateItem |
| | 1412 | { |
| | 1413 | public: |
| | 1414 | CTcObjTemplateItem(class CTcSymProp *prop, tc_toktyp_t tok_type, |
| | 1415 | int is_alt, int is_opt) |
| | 1416 | { |
| | 1417 | /* remember my defining information */ |
| | 1418 | prop_ = prop; |
| | 1419 | tok_type_ = tok_type; |
| | 1420 | is_alt_ = is_alt; |
| | 1421 | is_opt_ = is_opt; |
| | 1422 | |
| | 1423 | /* not in a list yet */ |
| | 1424 | nxt_ = 0; |
| | 1425 | } |
| | 1426 | |
| | 1427 | /* property that the item in this position defines */ |
| | 1428 | class CTcSymProp *prop_; |
| | 1429 | |
| | 1430 | /* token type of item in this position */ |
| | 1431 | tc_toktyp_t tok_type_; |
| | 1432 | |
| | 1433 | /* next item in this template's item list */ |
| | 1434 | CTcObjTemplateItem *nxt_; |
| | 1435 | |
| | 1436 | /* flag: this item is an alternative to the previous item */ |
| | 1437 | unsigned int is_alt_ : 1; |
| | 1438 | |
| | 1439 | /* flag: this item is optional */ |
| | 1440 | unsigned int is_opt_ : 1; |
| | 1441 | }; |
| | 1442 | |
| | 1443 | /* |
| | 1444 | * Template item instance - we keep track of the actual parameters to a |
| | 1445 | * template with these items. |
| | 1446 | */ |
| | 1447 | class CTcObjTemplateInst |
| | 1448 | { |
| | 1449 | public: |
| | 1450 | /* |
| | 1451 | * expression value for the actual parameter, as either a naked |
| | 1452 | * expression (expr_) or as a code body (code_body_) - only one of |
| | 1453 | * expr_ or code_body_ will be valid |
| | 1454 | */ |
| | 1455 | class CTcPrsNode *expr_; |
| | 1456 | class CTPNCodeBody *code_body_; |
| | 1457 | |
| | 1458 | /* |
| | 1459 | * the introductory token of the parameter - if the parameter is |
| | 1460 | * introduced by an operator token, this will not be part of the |
| | 1461 | * expression |
| | 1462 | */ |
| | 1463 | tc_toktyp_t def_tok_; |
| | 1464 | |
| | 1465 | /* the first token of the value */ |
| | 1466 | CTcToken expr_tok_; |
| | 1467 | |
| | 1468 | /* |
| | 1469 | * The property to which to assign this actual parameter value. This |
| | 1470 | * isn't filled in until we match the full list to an actual template, |
| | 1471 | * since we don't know the meanings of the parameters until we match |
| | 1472 | * the actuals to an existing template in memory. |
| | 1473 | */ |
| | 1474 | class CTcSymProp *prop_; |
| | 1475 | }; |
| | 1476 | |
| | 1477 | |
| | 1478 | /* ------------------------------------------------------------------------ */ |
| | 1479 | /* |
| | 1480 | * Non-symbolic object list entry |
| | 1481 | */ |
| | 1482 | struct tcprs_nonsym_obj |
| | 1483 | { |
| | 1484 | tcprs_nonsym_obj(tctarg_obj_id_t id) |
| | 1485 | { |
| | 1486 | /* remember the ID */ |
| | 1487 | id_ = id; |
| | 1488 | |
| | 1489 | /* not in a list yet */ |
| | 1490 | nxt_ = 0; |
| | 1491 | } |
| | 1492 | |
| | 1493 | /* ID of this object */ |
| | 1494 | tctarg_obj_id_t id_; |
| | 1495 | |
| | 1496 | /* next entry in the list */ |
| | 1497 | tcprs_nonsym_obj *nxt_; |
| | 1498 | }; |
| | 1499 | |
| | 1500 | /* ------------------------------------------------------------------------ */ |
| | 1501 | /* |
| | 1502 | * Dictionary property list entry. Each time the source code defines a |
| | 1503 | * dictionary property, we'll make an entry in this list. |
| | 1504 | */ |
| | 1505 | class CTcDictPropEntry |
| | 1506 | { |
| | 1507 | public: |
| | 1508 | CTcDictPropEntry(class CTcSymProp *prop) |
| | 1509 | { |
| | 1510 | /* remember the property */ |
| | 1511 | prop_ = prop; |
| | 1512 | |
| | 1513 | /* not in a list yet */ |
| | 1514 | nxt_ = 0; |
| | 1515 | |
| | 1516 | /* not defined for current object yet */ |
| | 1517 | defined_ = FALSE; |
| | 1518 | } |
| | 1519 | |
| | 1520 | /* my property */ |
| | 1521 | class CTcSymProp *prop_; |
| | 1522 | |
| | 1523 | /* next entry in list */ |
| | 1524 | CTcDictPropEntry *nxt_; |
| | 1525 | |
| | 1526 | /* flag: the current object definition includes this property */ |
| | 1527 | unsigned int defined_ : 1; |
| | 1528 | }; |
| | 1529 | |
| | 1530 | /* ------------------------------------------------------------------------ */ |
| | 1531 | /* |
| | 1532 | * Dictionary list entry. Each dictionary object gets an entry in this |
| | 1533 | * list. |
| | 1534 | */ |
| | 1535 | class CTcDictEntry |
| | 1536 | { |
| | 1537 | public: |
| | 1538 | CTcDictEntry(class CTcSymObj *sym); |
| | 1539 | |
| | 1540 | /* get/set my object file index */ |
| | 1541 | uint get_obj_idx() const { return obj_idx_; } |
| | 1542 | void set_obj_idx(uint idx) { obj_idx_ = idx; } |
| | 1543 | |
| | 1544 | /* get my object symbol */ |
| | 1545 | class CTcSymObj *get_sym() const { return sym_; } |
| | 1546 | |
| | 1547 | /* get/set the next item in the list */ |
| | 1548 | CTcDictEntry *get_next() const { return nxt_; } |
| | 1549 | void set_next(CTcDictEntry *nxt) { nxt_ = nxt; } |
| | 1550 | |
| | 1551 | /* add a word to the table */ |
| | 1552 | void add_word(const char *txt, size_t len, int copy, |
| | 1553 | tc_obj_id obj, tc_prop_id prop); |
| | 1554 | |
| | 1555 | /* write my symbol to the object file if I haven't already done so */ |
| | 1556 | void write_sym_to_obj_file(CVmFile *fp); |
| | 1557 | |
| | 1558 | /* get the hash table */ |
| | 1559 | class CVmHashTable *get_hash_table() const { return hashtab_; } |
| | 1560 | |
| | 1561 | protected: |
| | 1562 | /* enumeration callback - write to object file */ |
| | 1563 | static void enum_cb_writeobj(void *ctx, class CVmHashEntry *entry); |
| | 1564 | |
| | 1565 | /* associated object symbol */ |
| | 1566 | class CTcSymObj *sym_; |
| | 1567 | |
| | 1568 | /* |
| | 1569 | * object file index (we use this to match up the dictionary objects |
| | 1570 | * when we re-load the object file) |
| | 1571 | */ |
| | 1572 | uint obj_idx_; |
| | 1573 | |
| | 1574 | /* next item in the dictionary list */ |
| | 1575 | CTcDictEntry *nxt_; |
| | 1576 | |
| | 1577 | /* hash table containing the word entries */ |
| | 1578 | class CVmHashTable *hashtab_; |
| | 1579 | }; |
| | 1580 | |
| | 1581 | |
| | 1582 | /* |
| | 1583 | * entry in a dictionary list |
| | 1584 | */ |
| | 1585 | struct CTcPrsDictItem |
| | 1586 | { |
| | 1587 | CTcPrsDictItem(tc_obj_id obj, tc_prop_id prop) |
| | 1588 | { |
| | 1589 | obj_ = obj; |
| | 1590 | prop_ = prop; |
| | 1591 | nxt_ = 0; |
| | 1592 | } |
| | 1593 | |
| | 1594 | /* object */ |
| | 1595 | tc_obj_id obj_; |
| | 1596 | |
| | 1597 | /* property */ |
| | 1598 | tc_prop_id prop_; |
| | 1599 | |
| | 1600 | /* next entry in list */ |
| | 1601 | CTcPrsDictItem *nxt_; |
| | 1602 | }; |
| | 1603 | |
| | 1604 | /* |
| | 1605 | * Parser dictionary hash table entry |
| | 1606 | */ |
| | 1607 | class CVmHashEntryPrsDict: public CVmHashEntryCS |
| | 1608 | { |
| | 1609 | public: |
| | 1610 | CVmHashEntryPrsDict(const char *txt, size_t len, int copy) |
| | 1611 | : CVmHashEntryCS(txt, len, copy) |
| | 1612 | { |
| | 1613 | /* nothing in my list yet */ |
| | 1614 | list_ = 0; |
| | 1615 | } |
| | 1616 | |
| | 1617 | /* add an item to my list */ |
| | 1618 | void add_item(tc_obj_id obj, tc_prop_id prop); |
| | 1619 | |
| | 1620 | /* get the list head */ |
| | 1621 | struct CTcPrsDictItem *get_list() const { return list_; } |
| | 1622 | |
| | 1623 | protected: |
| | 1624 | /* list of object/property associations with this word */ |
| | 1625 | struct CTcPrsDictItem *list_; |
| | 1626 | }; |
| | 1627 | |
| | 1628 | /* ------------------------------------------------------------------------ */ |
| | 1629 | /* |
| | 1630 | * State save structure for parsing property expressions |
| | 1631 | */ |
| | 1632 | class CTcPrsPropExprSave |
| | 1633 | { |
| | 1634 | public: |
| | 1635 | unsigned int has_local_ctx_ : 1; |
| | 1636 | int local_ctx_var_num_; |
| | 1637 | size_t ctx_var_props_used_; |
| | 1638 | int next_ctx_arr_idx_; |
| | 1639 | int self_referenced_; |
| | 1640 | int full_method_ctx_referenced_; |
| | 1641 | int local_ctx_needs_self_; |
| | 1642 | int local_ctx_needs_full_method_ctx_; |
| | 1643 | struct CTcCodeBodyRef *cur_code_body_; |
| | 1644 | }; |
| | 1645 | |
| | 1646 | /* ------------------------------------------------------------------------ */ |
| | 1647 | /* |
| | 1648 | * Grammar production list entry |
| | 1649 | */ |
| | 1650 | class CTcGramProdEntry |
| | 1651 | { |
| | 1652 | public: |
| | 1653 | CTcGramProdEntry(class CTcSymObj *prod_obj); |
| | 1654 | |
| | 1655 | /* get my production object symbol */ |
| | 1656 | class CTcSymObj *get_prod_sym() const { return prod_sym_; } |
| | 1657 | |
| | 1658 | /* get/set the next item in the list */ |
| | 1659 | CTcGramProdEntry *get_next() const { return nxt_; } |
| | 1660 | void set_next(CTcGramProdEntry *nxt) { nxt_ = nxt; } |
| | 1661 | |
| | 1662 | /* add an alternative */ |
| | 1663 | void add_alt(class CTcGramProdAlt *alt); |
| | 1664 | |
| | 1665 | /* get the alternative list head */ |
| | 1666 | class CTcGramProdAlt *get_alt_head() const { return alt_head_; } |
| | 1667 | |
| | 1668 | /* write to an object file */ |
| | 1669 | void write_to_obj_file(class CVmFile *fp); |
| | 1670 | |
| | 1671 | /* load from an object file */ |
| | 1672 | static void load_from_obj_file(class CVmFile *fp, |
| | 1673 | const tctarg_prop_id_t *prop_xlat, |
| | 1674 | const ulong *enum_xlat, |
| | 1675 | class CTcSymObj *private_owner); |
| | 1676 | |
| | 1677 | /* move alternatives from my list to the given target list */ |
| | 1678 | void move_alts_to(CTcGramProdEntry *new_entry); |
| | 1679 | |
| | 1680 | /* get/set explicitly-declared flag */ |
| | 1681 | int is_declared() const { return is_declared_; } |
| | 1682 | void set_declared(int f) { is_declared_ = f; } |
| | 1683 | |
| | 1684 | protected: |
| | 1685 | /* associated production object symbol */ |
| | 1686 | class CTcSymObj *prod_sym_; |
| | 1687 | |
| | 1688 | /* next item in the list */ |
| | 1689 | CTcGramProdEntry *nxt_; |
| | 1690 | |
| | 1691 | /* head and tail of alternative list */ |
| | 1692 | class CTcGramProdAlt *alt_head_; |
| | 1693 | class CTcGramProdAlt *alt_tail_; |
| | 1694 | |
| | 1695 | /* |
| | 1696 | * flag: this production was explicitly declared (this means that we |
| | 1697 | * will consider it valid at link time even if it has no alternatives |
| | 1698 | * defined) |
| | 1699 | */ |
| | 1700 | unsigned int is_declared_ : 1; |
| | 1701 | }; |
| | 1702 | |
| | 1703 | /* |
| | 1704 | * Grammar production alternative. Each grammar production has one or |
| | 1705 | * more alternatives that, when matched, generate the production. |
| | 1706 | */ |
| | 1707 | class CTcGramProdAlt |
| | 1708 | { |
| | 1709 | public: |
| | 1710 | CTcGramProdAlt(class CTcSymObj *obj_sym, class CTcDictEntry *dict); |
| | 1711 | |
| | 1712 | /* get/set my score */ |
| | 1713 | int get_score() const { return score_; } |
| | 1714 | void set_score(int score) { score_ = score; } |
| | 1715 | |
| | 1716 | /* get/set my badness */ |
| | 1717 | int get_badness() const { return badness_; } |
| | 1718 | void set_badness(int badness) { badness_ = badness; } |
| | 1719 | |
| | 1720 | /* get my processor object symbol */ |
| | 1721 | class CTcSymObj *get_processor_obj() const { return obj_sym_; } |
| | 1722 | |
| | 1723 | /* get/set the next list element */ |
| | 1724 | CTcGramProdAlt *get_next() const { return nxt_; } |
| | 1725 | void set_next(CTcGramProdAlt *nxt) { nxt_ = nxt; } |
| | 1726 | |
| | 1727 | /* add a token to my list */ |
| | 1728 | void add_tok(class CTcGramProdTok *tok); |
| | 1729 | |
| | 1730 | /* get the head of my token list */ |
| | 1731 | class CTcGramProdTok *get_tok_head() const { return tok_head_; } |
| | 1732 | |
| | 1733 | /* write to an object file */ |
| | 1734 | void write_to_obj_file(class CVmFile *fp); |
| | 1735 | |
| | 1736 | /* load from an object file */ |
| | 1737 | static CTcGramProdAlt * |
| | 1738 | load_from_obj_file(class CVmFile *fp, |
| | 1739 | const tctarg_prop_id_t *prop_xlat, |
| | 1740 | const ulong *enum_xlat); |
| | 1741 | |
| | 1742 | /* get the dictionary in effect when the alternative was defined */ |
| | 1743 | class CTcDictEntry *get_dict() const { return dict_; } |
| | 1744 | |
| | 1745 | protected: |
| | 1746 | /* head and tail of our token list */ |
| | 1747 | class CTcGramProdTok *tok_head_; |
| | 1748 | class CTcGramProdTok *tok_tail_; |
| | 1749 | |
| | 1750 | /* dictionary in effect when alternative was defined */ |
| | 1751 | class CTcDictEntry *dict_; |
| | 1752 | |
| | 1753 | /* the processor object associated with this alternative */ |
| | 1754 | class CTcSymObj *obj_sym_; |
| | 1755 | |
| | 1756 | /* next alternative in our production */ |
| | 1757 | CTcGramProdAlt *nxt_; |
| | 1758 | |
| | 1759 | /* score */ |
| | 1760 | int score_; |
| | 1761 | |
| | 1762 | /* badness */ |
| | 1763 | int badness_; |
| | 1764 | }; |
| | 1765 | |
| | 1766 | /* grammar production token types */ |
| | 1767 | enum tcgram_tok_type |
| | 1768 | { |
| | 1769 | /* unknown */ |
| | 1770 | TCGRAM_UNKNOWN, |
| | 1771 | |
| | 1772 | /* match a production (given by the production object) */ |
| | 1773 | TCGRAM_PROD, |
| | 1774 | |
| | 1775 | /* match a part of speech (given by the dictionary property) */ |
| | 1776 | TCGRAM_PART_OF_SPEECH, |
| | 1777 | |
| | 1778 | /* match a literal string */ |
| | 1779 | TCGRAM_LITERAL, |
| | 1780 | |
| | 1781 | /* token-type match */ |
| | 1782 | TCGRAM_TOKEN_TYPE, |
| | 1783 | |
| | 1784 | /* free-floating end-of-string */ |
| | 1785 | TCGRAM_STAR, |
| | 1786 | |
| | 1787 | /* match one of several parts of speech */ |
| | 1788 | TCGRAM_PART_OF_SPEECH_LIST |
| | 1789 | }; |
| | 1790 | |
| | 1791 | /* |
| | 1792 | * Grammar production alternative token |
| | 1793 | */ |
| | 1794 | class CTcGramProdTok |
| | 1795 | { |
| | 1796 | public: |
| | 1797 | CTcGramProdTok() |
| | 1798 | { |
| | 1799 | /* not in a list yet */ |
| | 1800 | nxt_ = 0; |
| | 1801 | |
| | 1802 | /* no type yet */ |
| | 1803 | typ_ = TCGRAM_UNKNOWN; |
| | 1804 | |
| | 1805 | /* no property association yte */ |
| | 1806 | prop_assoc_ = TCTARG_INVALID_PROP; |
| | 1807 | } |
| | 1808 | |
| | 1809 | /* get/set my next element */ |
| | 1810 | CTcGramProdTok *get_next() const { return nxt_; } |
| | 1811 | void set_next(CTcGramProdTok *nxt) { nxt_ = nxt; } |
| | 1812 | |
| | 1813 | /* set me to match a production object */ |
| | 1814 | void set_match_prod(class CTcSymObj *obj) |
| | 1815 | { |
| | 1816 | /* remember the production object */ |
| | 1817 | typ_ = TCGRAM_PROD; |
| | 1818 | val_.obj_ = obj; |
| | 1819 | } |
| | 1820 | |
| | 1821 | /* set me to match a token type */ |
| | 1822 | void set_match_token_type(ulong enum_id) |
| | 1823 | { |
| | 1824 | /* remember the token enum ID */ |
| | 1825 | typ_ = TCGRAM_TOKEN_TYPE; |
| | 1826 | val_.enum_id_ = enum_id; |
| | 1827 | } |
| | 1828 | |
| | 1829 | /* set me to match a dictionary property */ |
| | 1830 | void set_match_part_of_speech(tctarg_prop_id_t prop) |
| | 1831 | { |
| | 1832 | /* remember the part of speech */ |
| | 1833 | typ_ = TCGRAM_PART_OF_SPEECH; |
| | 1834 | val_.prop_ = prop; |
| | 1835 | } |
| | 1836 | |
| | 1837 | /* |
| | 1838 | * set me to match a list of parts of speech; each part of speech must |
| | 1839 | * be separately added via add_match_part_ele() |
| | 1840 | */ |
| | 1841 | void set_match_part_list(); |
| | 1842 | |
| | 1843 | /* add an element to the part-of-speech match list */ |
| | 1844 | void add_match_part_ele(tctarg_prop_id_t prop); |
| | 1845 | |
| | 1846 | /* set me to match a literal string */ |
| | 1847 | void set_match_literal(const char *txt, size_t len) |
| | 1848 | { |
| | 1849 | /* remember the string */ |
| | 1850 | typ_ = TCGRAM_LITERAL; |
| | 1851 | val_.str_.txt_ = txt; |
| | 1852 | val_.str_.len_ = len; |
| | 1853 | } |
| | 1854 | |
| | 1855 | /* set me to match a free-floating end-of-string */ |
| | 1856 | void set_match_star() |
| | 1857 | { |
| | 1858 | /* set the type */ |
| | 1859 | typ_ = TCGRAM_STAR; |
| | 1860 | } |
| | 1861 | |
| | 1862 | /* get my type */ |
| | 1863 | tcgram_tok_type get_type() const { return typ_; } |
| | 1864 | |
| | 1865 | /* get my value */ |
| | 1866 | class CTcSymObj *getval_prod() const { return val_.obj_; } |
| | 1867 | tctarg_prop_id_t getval_part_of_speech() const { return val_.prop_; } |
| | 1868 | const char *getval_literal_txt() const { return val_.str_.txt_; } |
| | 1869 | const size_t getval_literal_len() const { return val_.str_.len_; } |
| | 1870 | ulong getval_token_type() const { return val_.enum_id_; } |
| | 1871 | size_t getval_part_list_len() const { return val_.prop_list_.len_; } |
| | 1872 | tctarg_prop_id_t getval_part_list_ele(size_t idx) const |
| | 1873 | { return val_.prop_list_.arr_[idx]; } |
| | 1874 | |
| | 1875 | /* |
| | 1876 | * get/set my property association - this is the property to which |
| | 1877 | * the actual match to the rule is assigned when we match the rule |
| | 1878 | */ |
| | 1879 | tctarg_prop_id_t get_prop_assoc() const { return prop_assoc_; } |
| | 1880 | void set_prop_assoc(tctarg_prop_id_t prop) { prop_assoc_ = prop; } |
| | 1881 | |
| | 1882 | /* write to an object file */ |
| | 1883 | void write_to_obj_file(class CVmFile *fp); |
| | 1884 | |
| | 1885 | /* load from an object file */ |
| | 1886 | static CTcGramProdTok * |
| | 1887 | load_from_obj_file(class CVmFile *fp, |
| | 1888 | const tctarg_prop_id_t *prop_xlat, |
| | 1889 | const ulong *enum_xlat); |
| | 1890 | |
| | 1891 | protected: |
| | 1892 | /* next token in my list */ |
| | 1893 | CTcGramProdTok *nxt_; |
| | 1894 | |
| | 1895 | /* my type - this specifies how this token matches */ |
| | 1896 | tcgram_tok_type typ_; |
| | 1897 | |
| | 1898 | /* match specification - varies according to my type */ |
| | 1899 | union |
| | 1900 | { |
| | 1901 | /* object - for matching a production */ |
| | 1902 | class CTcSymObj *obj_; |
| | 1903 | |
| | 1904 | /* property - for matching a part of speech */ |
| | 1905 | tctarg_prop_id_t prop_; |
| | 1906 | |
| | 1907 | /* token enum id - for matching a token type */ |
| | 1908 | ulong enum_id_; |
| | 1909 | |
| | 1910 | /* literal string */ |
| | 1911 | struct |
| | 1912 | { |
| | 1913 | const char *txt_; |
| | 1914 | size_t len_; |
| | 1915 | } str_; |
| | 1916 | |
| | 1917 | /* list of vocabulary elements */ |
| | 1918 | struct |
| | 1919 | { |
| | 1920 | /* number of array entries allocated */ |
| | 1921 | size_t alo_; |
| | 1922 | |
| | 1923 | /* number of array entries actually used */ |
| | 1924 | size_t len_; |
| | 1925 | |
| | 1926 | /* array of entries */ |
| | 1927 | tctarg_prop_id_t *arr_; |
| | 1928 | } prop_list_; |
| | 1929 | } val_; |
| | 1930 | |
| | 1931 | /* property association */ |
| | 1932 | tctarg_prop_id_t prop_assoc_; |
| | 1933 | }; |
| | 1934 | |
| | 1935 | /* ------------------------------------------------------------------------ */ |
| | 1936 | /* |
| | 1937 | * Exported symbol record |
| | 1938 | */ |
| | 1939 | class CTcPrsExport |
| | 1940 | { |
| | 1941 | public: |
| | 1942 | /* create with the given compiler symbol */ |
| | 1943 | CTcPrsExport(const char *sym, size_t sym_len) |
| | 1944 | { |
| | 1945 | /* remember my name */ |
| | 1946 | sym_ = sym; |
| | 1947 | sym_len_ = sym_len; |
| | 1948 | |
| | 1949 | /* |
| | 1950 | * we don't yet have an explicit external name, so export using |
| | 1951 | * the internal name |
| | 1952 | */ |
| | 1953 | ext_name_ = sym; |
| | 1954 | ext_len_ = sym_len; |
| | 1955 | |
| | 1956 | /* we're not in a list yet */ |
| | 1957 | nxt_ = 0; |
| | 1958 | } |
| | 1959 | |
| | 1960 | /* set the external name */ |
| | 1961 | void set_extern_name(const char *txt, size_t len) |
| | 1962 | { |
| | 1963 | ext_name_ = txt; |
| | 1964 | ext_len_ = len; |
| | 1965 | } |
| | 1966 | |
| | 1967 | /* get the symbol name and length */ |
| | 1968 | const char *get_sym() const { return sym_; } |
| | 1969 | size_t get_sym_len() const { return sym_len_; } |
| | 1970 | |
| | 1971 | /* get the external name and length */ |
| | 1972 | const char *get_ext_name() const { return ext_name_; } |
| | 1973 | size_t get_ext_len() const { return ext_len_; } |
| | 1974 | |
| | 1975 | /* get/set the next entry in the list */ |
| | 1976 | CTcPrsExport *get_next() const { return nxt_; } |
| | 1977 | void set_next(CTcPrsExport *nxt) { nxt_ = nxt; } |
| | 1978 | |
| | 1979 | /* write to an object file */ |
| | 1980 | void write_to_obj_file(class CVmFile *fp); |
| | 1981 | |
| | 1982 | /* read from an object file */ |
| | 1983 | static CTcPrsExport *read_from_obj_file(class CVmFile *fp); |
| | 1984 | |
| | 1985 | /* determine if my external name matches the given export's */ |
| | 1986 | int ext_name_matches(const CTcPrsExport *exp) const |
| | 1987 | { |
| | 1988 | return (exp->get_ext_len() == get_ext_len() |
| | 1989 | && memcmp(exp->get_ext_name(), get_ext_name(), |
| | 1990 | get_ext_len()) == 0); |
| | 1991 | } |
| | 1992 | |
| | 1993 | /* determine if my name matches the given string */ |
| | 1994 | int ext_name_matches(const char *txt) const |
| | 1995 | { |
| | 1996 | return (get_ext_len() == get_strlen(txt) |
| | 1997 | && memcmp(get_ext_name(), txt, get_ext_len()) == 0); |
| | 1998 | } |
| | 1999 | |
| | 2000 | /* determine if my symbol name matches the given export's */ |
| | 2001 | int sym_matches(const CTcPrsExport *exp) const |
| | 2002 | { |
| | 2003 | return (exp->get_sym_len() == get_sym_len() |
| | 2004 | && memcmp(exp->get_sym(), get_sym(), get_sym_len()) == 0); |
| | 2005 | } |
| | 2006 | |
| | 2007 | protected: |
| | 2008 | /* symbol name - this is the internal compiler symbol being exported */ |
| | 2009 | const char *sym_; |
| | 2010 | size_t sym_len_; |
| | 2011 | |
| | 2012 | /* external name - this is the name visible to the VM loader */ |
| | 2013 | const char *ext_name_; |
| | 2014 | size_t ext_len_; |
| | 2015 | |
| | 2016 | /* next in list */ |
| | 2017 | CTcPrsExport *nxt_; |
| | 2018 | }; |
| | 2019 | |
| | 2020 | |
| | 2021 | /* ------------------------------------------------------------------------ */ |
| | 2022 | /* |
| | 2023 | * Parser Symbol Table. The parser maintains a hierarchy of symbol |
| | 2024 | * tables; a local symbol table can be nested inside an enclosing |
| | 2025 | * scope's symbol table, and so on up to the top-level block scope, |
| | 2026 | * which is enclosed by the global scope. In addition, at function |
| | 2027 | * scope there's a separate table for "goto" labels. |
| | 2028 | */ |
| | 2029 | |
| | 2030 | /* find_or_def actions for undefined symbols */ |
| | 2031 | enum tcprs_undef_action |
| | 2032 | { |
| | 2033 | /* if undefined, add an "undefined" entry unconditionally */ |
| | 2034 | TCPRS_UNDEF_ADD_UNDEF, |
| | 2035 | |
| | 2036 | /* add a "property" entry unconditionally, but warn about it */ |
| | 2037 | TCPRS_UNDEF_ADD_PROP, |
| | 2038 | |
| | 2039 | /* add a "property" entry unconditionally, with no warning */ |
| | 2040 | TCPRS_UNDEF_ADD_PROP_NO_WARNING |
| | 2041 | }; |
| | 2042 | |
| | 2043 | /* parser symbol table */ |
| | 2044 | class CTcPrsSymtab |
| | 2045 | { |
| | 2046 | public: |
| | 2047 | CTcPrsSymtab(CTcPrsSymtab *parent_scope); |
| | 2048 | ~CTcPrsSymtab(); |
| | 2049 | |
| | 2050 | /* allocate parser symbol tables out of the parser memory pool */ |
| | 2051 | void *operator new(size_t siz); |
| | 2052 | |
| | 2053 | /* |
| | 2054 | * perform static initialization/termination - call once at program |
| | 2055 | * startup and shutdown (respectively) |
| | 2056 | */ |
| | 2057 | static void s_init(); |
| | 2058 | static void s_terminate(); |
| | 2059 | |
| | 2060 | /* get the enclosing scope's symbol table */ |
| | 2061 | CTcPrsSymtab *get_parent() const { return parent_; } |
| | 2062 | |
| | 2063 | /* find a symbol; returns null if the symbol isn't defined */ |
| | 2064 | class CTcSymbol *find(const textchar_t *sym, size_t len) |
| | 2065 | { return find(sym, len, 0); } |
| | 2066 | |
| | 2067 | class CTcSymbol *find(const textchar_t *sym) |
| | 2068 | { return find(sym, strlen(sym), 0); } |
| | 2069 | |
| | 2070 | /* |
| | 2071 | * Find a symbol; returns null if the symbol isn't defined. If |
| | 2072 | * symtab is not null, we'll fill it in with the actual symbol table |
| | 2073 | * in which we found the symbol; this might be an enclosing symbol |
| | 2074 | * table, since we search up the enclosing scope list. |
| | 2075 | */ |
| | 2076 | class CTcSymbol *find(const textchar_t *sym, size_t len, |
| | 2077 | CTcPrsSymtab **symtab); |
| | 2078 | |
| | 2079 | /* find a symbol directly in this table, without searching parents */ |
| | 2080 | class CTcSymbol *find_direct(const textchar_t *sym, size_t len); |
| | 2081 | |
| | 2082 | /* find a symbol without changing its referenced status */ |
| | 2083 | class CTcSymbol *find_noref(const textchar_t *sym, size_t len, |
| | 2084 | CTcPrsSymtab **symtab); |
| | 2085 | |
| | 2086 | /* |
| | 2087 | * Find a symbol; if the symbol isn't defined, log an error and add |
| | 2088 | * the symbol as type "undefined". Because we add a symbol entry if |
| | 2089 | * the symbol isn't defined, this *always* returns a valid symbol |
| | 2090 | * object. |
| | 2091 | */ |
| | 2092 | class CTcSymbol *find_or_def_undef(const char *sym, size_t len, |
| | 2093 | int copy_str) |
| | 2094 | { |
| | 2095 | return find_or_def(sym, len, copy_str, TCPRS_UNDEF_ADD_UNDEF); |
| | 2096 | } |
| | 2097 | |
| | 2098 | /* |
| | 2099 | * Find a symbol; if the symbol isn't defined, log a warning and |
| | 2100 | * define the symbol as type property. Because we add an entry if |
| | 2101 | * the symbol isn't defined, this *always* returns a valid symbol |
| | 2102 | * object. |
| | 2103 | */ |
| | 2104 | class CTcSymbol *find_or_def_prop(const char *sym, size_t len, |
| | 2105 | int copy_str) |
| | 2106 | { |
| | 2107 | return find_or_def(sym, len, copy_str, TCPRS_UNDEF_ADD_PROP); |
| | 2108 | } |
| | 2109 | |
| | 2110 | /* |
| | 2111 | * Find a symbol; if the symbol isn't defined, define the symbol as |
| | 2112 | * type property with no warning. This should be used when it is |
| | 2113 | * unambiguous that a symbol is meant as a property name. Because we |
| | 2114 | * add an entry if the symbol isn't defined, this *always* returns a |
| | 2115 | * valid symbol object. |
| | 2116 | */ |
| | 2117 | class CTcSymbol *find_or_def_prop_explicit(const char *sym, size_t len, |
| | 2118 | int copy_str) |
| | 2119 | { |
| | 2120 | return find_or_def(sym, len, copy_str, |
| | 2121 | TCPRS_UNDEF_ADD_PROP_NO_WARNING); |
| | 2122 | } |
| | 2123 | |
| | 2124 | /* |
| | 2125 | * Find a symbol. If the symbol isn't defined, and a "self" object |
| | 2126 | * is available, define the symbol as a property. If the symbol |
| | 2127 | * isn't defined an no "self" object is available, add an |
| | 2128 | * "undefined" entry for the symbol. |
| | 2129 | */ |
| | 2130 | class CTcSymbol *find_or_def_prop_implied(const char *sym, size_t len, |
| | 2131 | int copy_str, int is_self_avail) |
| | 2132 | { |
| | 2133 | return find_or_def(sym, len, copy_str, |
| | 2134 | is_self_avail |
| | 2135 | ? TCPRS_UNDEF_ADD_PROP : TCPRS_UNDEF_ADD_UNDEF); |
| | 2136 | } |
| | 2137 | |
| | 2138 | /* add a formal parameter symbol */ |
| | 2139 | void add_formal(const textchar_t *sym, size_t len, int formal_num, |
| | 2140 | int copy_str); |
| | 2141 | |
| | 2142 | /* add a local variable symbol */ |
| | 2143 | class CTcSymLocal *add_local(const textchar_t *sym, size_t len, |
| | 2144 | int local_num, int copy_str, |
| | 2145 | int init_assigned, int init_referenced); |
| | 2146 | |
| | 2147 | /* add a 'goto' symbol */ |
| | 2148 | class CTcSymLabel *add_code_label(const textchar_t *sym, size_t len, |
| | 2149 | int copy_str); |
| | 2150 | |
| | 2151 | /* add an entry to the table */ |
| | 2152 | void add_entry(class CTcSymbol *sym); |
| | 2153 | |
| | 2154 | /* remove an entry */ |
| | 2155 | void remove_entry(class CTcSymbol *sym); |
| | 2156 | |
| | 2157 | /* enumerate entries in the table through a callback */ |
| | 2158 | void enum_entries(void (*func)(void *, class CTcSymbol *), void *ctx); |
| | 2159 | |
| | 2160 | /* |
| | 2161 | * Scan the symbol table and check for unreferenced locals. Logs an |
| | 2162 | * error for each unreferenced or unassigned local. |
| | 2163 | */ |
| | 2164 | void check_unreferenced_locals(); |
| | 2165 | |
| | 2166 | /* |
| | 2167 | * Get/set my debugging list index - this is the index of this table |
| | 2168 | * in the list for this function or method. The index values start |
| | 2169 | * at 1 - a value of zero indicates that the symbol table isn't part |
| | 2170 | * of any list. |
| | 2171 | */ |
| | 2172 | int get_list_index() const { return list_index_; } |
| | 2173 | void set_list_index(int n) { list_index_ = n; } |
| | 2174 | |
| | 2175 | /* get/set the next entry in the linked list */ |
| | 2176 | CTcPrsSymtab *get_list_next() const { return list_next_; } |
| | 2177 | void set_list_next(CTcPrsSymtab *nxt) { list_next_ = nxt; } |
| | 2178 | |
| | 2179 | protected: |
| | 2180 | /* add an entry to a global symbol table */ |
| | 2181 | static void add_to_global_symtab(CTcPrsSymtab *tab, CTcSymbol *entry); |
| | 2182 | |
| | 2183 | /* get the underlying hash table */ |
| | 2184 | class CVmHashTable *get_hashtab() const { return hashtab_; } |
| | 2185 | |
| | 2186 | /* enumeration callback - check for unreferenced locals */ |
| | 2187 | static void unref_local_cb(void *ctx, class CTcSymbol *sym); |
| | 2188 | |
| | 2189 | /* |
| | 2190 | * find a symbol, or define a new symbol, according to the given |
| | 2191 | * action mode, if the symbol is undefined |
| | 2192 | */ |
| | 2193 | class CTcSymbol *find_or_def(const textchar_t *sym, size_t len, |
| | 2194 | int copy_str, tcprs_undef_action action); |
| | 2195 | |
| | 2196 | /* enclosing scope (parent) symbol table */ |
| | 2197 | CTcPrsSymtab *parent_; |
| | 2198 | |
| | 2199 | /* hash table */ |
| | 2200 | class CVmHashTable *hashtab_; |
| | 2201 | |
| | 2202 | /* hash function */ |
| | 2203 | static class CVmHashFunc *hash_func_; |
| | 2204 | |
| | 2205 | /* |
| | 2206 | * Next symbol table in local scope chain. For each function or |
| | 2207 | * method, we keep a simple linear list of the local scopes so that |
| | 2208 | * they can be written to the debugging records. We also keep an |
| | 2209 | * index value giving its position in the list, so that we can store |
| | 2210 | * references to the table using the list index. |
| | 2211 | */ |
| | 2212 | CTcPrsSymtab *list_next_; |
| | 2213 | int list_index_; |
| | 2214 | }; |
| | 2215 | |
| | 2216 | |
| | 2217 | /* ------------------------------------------------------------------------ */ |
| | 2218 | /* |
| | 2219 | * Debugger symbol table interface. This is an abstract interface that |
| | 2220 | * debuggers can implement to allow us to search for symbols that are |
| | 2221 | * obtained from a compiled program's debugger records. To keep the |
| | 2222 | * compiler independent of the target architecture and the debugger's |
| | 2223 | * own internal structures, we define this abstract interface that the |
| | 2224 | * debugger must implement. |
| | 2225 | * |
| | 2226 | * Since this type of symbol table is provided by a debugger as a view |
| | 2227 | * on the symbol information in a previously compiled program, the |
| | 2228 | * parser naturally has no need to add symbols to the table; hence the |
| | 2229 | * only required operations are symbol lookups. |
| | 2230 | */ |
| | 2231 | class CTcPrsDbgSymtab |
| | 2232 | { |
| | 2233 | public: |
| | 2234 | /* |
| | 2235 | * Get information on a symbol. Returns true if the symbol is |
| | 2236 | * found, false if not. If we find the symbol, fills in the |
| | 2237 | * information structure with the appropriate data. |
| | 2238 | */ |
| | 2239 | virtual int find_symbol(const textchar_t *sym, size_t len, |
| | 2240 | struct tcprsdbg_sym_info *info) = 0; |
| | 2241 | }; |
| | 2242 | |
| | 2243 | /* |
| | 2244 | * Debugger local symbol information structure |
| | 2245 | */ |
| | 2246 | struct tcprsdbg_sym_info |
| | 2247 | { |
| | 2248 | /* symbol type */ |
| | 2249 | enum tc_symtype_t sym_type; |
| | 2250 | |
| | 2251 | /* local/parameter number */ |
| | 2252 | uint var_id; |
| | 2253 | |
| | 2254 | /* context variable index - 0 if it's not a context local */ |
| | 2255 | int ctx_arr_idx; |
| | 2256 | |
| | 2257 | /* stack frame index */ |
| | 2258 | uint frame_idx; |
| | 2259 | }; |
| | 2260 | |
| | 2261 | |
| | 2262 | |
| | 2263 | /* ------------------------------------------------------------------------ */ |
| | 2264 | /* |
| | 2265 | * Parse Tree storage manager. |
| | 2266 | * |
| | 2267 | * The parse tree has some special characteristics that make it |
| | 2268 | * desirable to use a special memory manager for it. First, the parse |
| | 2269 | * tree consists of many small objects, so we would like to have as |
| | 2270 | * little overhead per object for memory tracking as possible. Second, |
| | 2271 | * parse tree objects all have a similar lifetime: we create the entire |
| | 2272 | * parse tree as we scan the source, then use it to generate target |
| | 2273 | * code, then discard the whole thing. |
| | 2274 | * |
| | 2275 | * To manage memory efficiently for the parse tree, we define our own |
| | 2276 | * memory manager for parse tree objects. The memory manager is very |
| | 2277 | * simple, fast, and has minimal per-object overhead. We simply |
| | 2278 | * maintain a list of large blocks, then suballocate requests out of the |
| | 2279 | * large blocks. Each time we run out of space in a block, we allocate |
| | 2280 | * a new block. We do not keep track of any extra tracking information |
| | 2281 | * per node, so a node cannot be individually freed; however, the entire |
| | 2282 | * block list can be freed at once, which is exactly the behavior we |
| | 2283 | * want. |
| | 2284 | */ |
| | 2285 | class CTcPrsMem |
| | 2286 | { |
| | 2287 | public: |
| | 2288 | CTcPrsMem(); |
| | 2289 | ~CTcPrsMem(); |
| | 2290 | |
| | 2291 | /* allocate storage */ |
| | 2292 | void *alloc(size_t siz); |
| | 2293 | |
| | 2294 | /* save the current pool state, for later resetting */ |
| | 2295 | void save_state(struct tcprsmem_state_t *state); |
| | 2296 | |
| | 2297 | /* |
| | 2298 | * reset the pool to the given state - delete all objects allocated |
| | 2299 | * in the pool since the state was saved |
| | 2300 | */ |
| | 2301 | void reset(const struct tcprsmem_state_t *state); |
| | 2302 | |
| | 2303 | /* reset to initial state */ |
| | 2304 | void reset(); |
| | 2305 | |
| | 2306 | private: |
| | 2307 | /* delete all parser memory */ |
| | 2308 | void delete_all(); |
| | 2309 | |
| | 2310 | /* allocate a new block */ |
| | 2311 | void alloc_block(); |
| | 2312 | |
| | 2313 | /* head of list of memory blocks */ |
| | 2314 | struct tcprsmem_blk_t *head_; |
| | 2315 | |
| | 2316 | /* tail of list and current memory block */ |
| | 2317 | struct tcprsmem_blk_t *tail_; |
| | 2318 | |
| | 2319 | /* current allocation offset in last block */ |
| | 2320 | char *free_ptr_; |
| | 2321 | |
| | 2322 | /* remaining space available in last block */ |
| | 2323 | size_t rem_; |
| | 2324 | }; |
| | 2325 | |
| | 2326 | /* |
| | 2327 | * state-saving structure |
| | 2328 | */ |
| | 2329 | struct tcprsmem_state_t |
| | 2330 | { |
| | 2331 | /* current tail of memory block list */ |
| | 2332 | struct tcprsmem_blk_t *tail; |
| | 2333 | |
| | 2334 | /* current allocation offset in last block */ |
| | 2335 | char *free_ptr; |
| | 2336 | |
| | 2337 | /* current remaining space in last block */ |
| | 2338 | size_t rem; |
| | 2339 | }; |
| | 2340 | |
| | 2341 | |
| | 2342 | /* |
| | 2343 | * Provide an overridden operator new for allocating objects explicitly |
| | 2344 | * from the pool |
| | 2345 | */ |
| | 2346 | inline void *operator new(size_t siz, CTcPrsMem *pool) |
| | 2347 | { |
| | 2348 | return pool->alloc(siz); |
| | 2349 | } |
| | 2350 | |
| | 2351 | /* |
| | 2352 | * provide an array operator new as well |
| | 2353 | */ |
| | 2354 | inline void *operator new[](size_t siz, CTcPrsMem *pool) |
| | 2355 | { |
| | 2356 | return pool->alloc(siz); |
| | 2357 | } |
| | 2358 | |
| | 2359 | |
| | 2360 | /* |
| | 2361 | * parse tree memory block |
| | 2362 | */ |
| | 2363 | struct tcprsmem_blk_t |
| | 2364 | { |
| | 2365 | /* next block in the list */ |
| | 2366 | tcprsmem_blk_t *next_; |
| | 2367 | |
| | 2368 | /* |
| | 2369 | * This block's byte array (the array extends off the end of the |
| | 2370 | * structure). |
| | 2371 | */ |
| | 2372 | char buf_[1]; |
| | 2373 | }; |
| | 2374 | |
| | 2375 | /* ------------------------------------------------------------------------ */ |
| | 2376 | /* |
| | 2377 | * Special array list subclass that uses parser memory |
| | 2378 | */ |
| | 2379 | class CPrsArrayList: public CArrayList |
| | 2380 | { |
| | 2381 | protected: |
| | 2382 | /* |
| | 2383 | * override the memory management functions to use parser memory |
| | 2384 | */ |
| | 2385 | |
| | 2386 | virtual void *alloc_mem(size_t siz) |
| | 2387 | { |
| | 2388 | /* allocate from the parser pool */ |
| | 2389 | return G_prsmem->alloc(siz); |
| | 2390 | } |
| | 2391 | |
| | 2392 | virtual void *realloc_mem(void *p, size_t oldsiz, size_t newsiz) |
| | 2393 | { |
| | 2394 | void *pnew; |
| | 2395 | |
| | 2396 | /* allocate a new block from the parser pool */ |
| | 2397 | pnew = G_prsmem->alloc(newsiz); |
| | 2398 | |
| | 2399 | /* copy from the old block to the new block */ |
| | 2400 | memcpy(pnew, p, oldsiz); |
| | 2401 | |
| | 2402 | /* return the new block */ |
| | 2403 | return pnew; |
| | 2404 | } |
| | 2405 | |
| | 2406 | virtual void free_mem(void *p) |
| | 2407 | { |
| | 2408 | /* |
| | 2409 | * do nothing - the parser pool automatically frees everything as a |
| | 2410 | * block when terminating the parser |
| | 2411 | */ |
| | 2412 | } |
| | 2413 | }; |
| | 2414 | |
| | 2415 | |
| | 2416 | /* ------------------------------------------------------------------------ */ |
| | 2417 | /* |
| | 2418 | * Expression Constant Value object. This object is used to express the |
| | 2419 | * value of a constant expression. |
| | 2420 | */ |
| | 2421 | class CTcConstVal |
| | 2422 | { |
| | 2423 | public: |
| | 2424 | CTcConstVal() |
| | 2425 | { |
| | 2426 | /* the type is unknown */ |
| | 2427 | typ_ = TC_CVT_UNK; |
| | 2428 | } |
| | 2429 | |
| | 2430 | /* |
| | 2431 | * determine if this is a constant value - it is a constant if it |
| | 2432 | * has any known value |
| | 2433 | */ |
| | 2434 | int is_const() const { return (typ_ != TC_CVT_UNK); } |
| | 2435 | |
| | 2436 | /* |
| | 2437 | * set the type to unknown - this indicates that there is no valid |
| | 2438 | * value, which generally means that the associated expression does |
| | 2439 | * not have a constant value |
| | 2440 | */ |
| | 2441 | void set_unknown() { typ_ = TC_CVT_UNK; } |
| | 2442 | |
| | 2443 | /* set from another value */ |
| | 2444 | void set(const CTcConstVal *val) |
| | 2445 | { |
| | 2446 | /* copy the type */ |
| | 2447 | typ_ = val->typ_; |
| | 2448 | |
| | 2449 | /* copy the value */ |
| | 2450 | val_ = val->val_; |
| | 2451 | } |
| | 2452 | |
| | 2453 | /* set an integer value */ |
| | 2454 | void set_int(long val) { typ_ = TC_CVT_INT; val_.intval_ = val; } |
| | 2455 | |
| | 2456 | /* set a floating-point value */ |
| | 2457 | void set_float(const char *val, size_t len) |
| | 2458 | { |
| | 2459 | typ_ = TC_CVT_FLOAT; |
| | 2460 | val_.floatval_.txt_ = val; |
| | 2461 | val_.floatval_.len_ = len; |
| | 2462 | } |
| | 2463 | |
| | 2464 | /* set an enumerator value */ |
| | 2465 | void set_enum(ulong val) { typ_ = TC_CVT_ENUM; val_.enumval_ = val; } |
| | 2466 | |
| | 2467 | /* set a single-quoted string value */ |
| | 2468 | void set_sstr(const char *val, size_t len); |
| | 2469 | |
| | 2470 | /* set a list value */ |
| | 2471 | void set_list(class CTPNList *lst); |
| | 2472 | |
| | 2473 | /* set an object reference value */ |
| | 2474 | void set_obj(ulong obj, enum tc_metaclass_t meta) |
| | 2475 | { |
| | 2476 | typ_ = TC_CVT_OBJ; |
| | 2477 | val_.objval_.id_ = obj; |
| | 2478 | val_.objval_.meta_ = meta; |
| | 2479 | } |
| | 2480 | |
| | 2481 | /* set a property pointer value */ |
| | 2482 | void set_prop(uint prop) |
| | 2483 | { |
| | 2484 | typ_ = TC_CVT_PROP; |
| | 2485 | val_.propval_ = prop; |
| | 2486 | } |
| | 2487 | |
| | 2488 | /* set a function pointer value */ |
| | 2489 | void set_funcptr(class CTcSymFunc *sym) |
| | 2490 | { |
| | 2491 | typ_ = TC_CVT_FUNCPTR; |
| | 2492 | val_.funcptrval_ = sym; |
| | 2493 | } |
| | 2494 | |
| | 2495 | /* set an anonymous function pointer value */ |
| | 2496 | void set_anon_funcptr(class CTPNCodeBody *code_body) |
| | 2497 | { |
| | 2498 | typ_ = TC_CVT_ANONFUNCPTR; |
| | 2499 | val_.codebodyval_ = code_body; |
| | 2500 | } |
| | 2501 | |
| | 2502 | /* set a nil/true value */ |
| | 2503 | void set_nil() { typ_ = TC_CVT_NIL; } |
| | 2504 | void set_true() { typ_ = TC_CVT_TRUE; } |
| | 2505 | |
| | 2506 | /* |
| | 2507 | * Set a vocabulary list placeholder. This has no actual value |
| | 2508 | * during compilation; instead, this is just a placeholder. During |
| | 2509 | * linking, we'll replace each of these with a list of strings |
| | 2510 | * giving the actual vocabulary for the property. |
| | 2511 | */ |
| | 2512 | void set_vocab_list() { typ_ = TC_CVT_VOCAB_LIST; } |
| | 2513 | |
| | 2514 | /* set a nil/true value based on a boolean value */ |
| | 2515 | void set_bool(int val) |
| | 2516 | { |
| | 2517 | typ_ = (val ? TC_CVT_TRUE : TC_CVT_NIL); |
| | 2518 | } |
| | 2519 | |
| | 2520 | /* get my type */ |
| | 2521 | tc_constval_type_t get_type() const { return typ_; } |
| | 2522 | |
| | 2523 | /* get my int value (no type checking) */ |
| | 2524 | long get_val_int() const { return val_.intval_; } |
| | 2525 | |
| | 2526 | /* get my floating point value (no type checking) */ |
| | 2527 | const char *get_val_float() const { return val_.floatval_.txt_; } |
| | 2528 | size_t get_val_float_len() const { return val_.floatval_.len_; } |
| | 2529 | |
| | 2530 | /* get my enumerator value (no type checking) */ |
| | 2531 | ulong get_val_enum() const { return val_.enumval_; } |
| | 2532 | |
| | 2533 | /* get my string value (no type checking) */ |
| | 2534 | const char *get_val_str() const { return val_.strval_.strval_; } |
| | 2535 | size_t get_val_str_len() const { return val_.strval_.strval_len_; } |
| | 2536 | |
| | 2537 | /* get my list value (no type checking) */ |
| | 2538 | class CTPNList *get_val_list() const { return val_.listval_; } |
| | 2539 | |
| | 2540 | /* get my object reference value (no type checking) */ |
| | 2541 | ulong get_val_obj() const |
| | 2542 | { return val_.objval_.id_; } |
| | 2543 | enum tc_metaclass_t get_val_obj_meta() const |
| | 2544 | { return val_.objval_.meta_; } |
| | 2545 | |
| | 2546 | /* get my property pointer value (no type checking) */ |
| | 2547 | uint get_val_prop() const { return val_.propval_; } |
| | 2548 | |
| | 2549 | /* get my function pointer symbol value (no type checking) */ |
| | 2550 | class CTcSymFunc *get_val_funcptr_sym() const |
| | 2551 | { return val_.funcptrval_; } |
| | 2552 | |
| | 2553 | /* get my anonymous function pointer value (no type checking) */ |
| | 2554 | class CTPNCodeBody *get_val_anon_func_ptr() const |
| | 2555 | { return val_.codebodyval_; } |
| | 2556 | |
| | 2557 | /* |
| | 2558 | * Determine if this value equals a given constant value. Returns |
| | 2559 | * true if so, false if not. We'll set (*can_compare) to true if |
| | 2560 | * the values are comparable, false if the comparison is not |
| | 2561 | * meaningful. |
| | 2562 | */ |
| | 2563 | int is_equal_to(const CTcConstVal *val) const; |
| | 2564 | |
| | 2565 | /* |
| | 2566 | * Convert an integer, nil, or true value to a string. Fills in the |
| | 2567 | * buffer with the result of the conversion if the value wasn't |
| | 2568 | * already a string. If the value is already a string, we'll simply |
| | 2569 | * return a pointer to the original string without making a copy. |
| | 2570 | * Returns null if the value is not convertible to a string. |
| | 2571 | */ |
| | 2572 | const char *cvt_to_str(char *buf, size_t bufl, size_t *result_len); |
| | 2573 | |
| | 2574 | /* |
| | 2575 | * Get my true/nil value. Returns false if the value is nil or zero, |
| | 2576 | * true if it's anything else. |
| | 2577 | */ |
| | 2578 | int get_val_bool() const |
| | 2579 | { |
| | 2580 | return !(typ_ == TC_CVT_NIL |
| | 2581 | || (typ_ == TC_CVT_INT && get_val_int() == 0)); |
| | 2582 | } |
| | 2583 | |
| | 2584 | private: |
| | 2585 | /* my type */ |
| | 2586 | tc_constval_type_t typ_; |
| | 2587 | |
| | 2588 | union |
| | 2589 | { |
| | 2590 | /* integer value (valid when typ_ == TC_CVT_INT) */ |
| | 2591 | long intval_; |
| | 2592 | |
| | 2593 | /* floating-point value (valid when typ_ == TC_CVT_FLOAT) */ |
| | 2594 | struct |
| | 2595 | { |
| | 2596 | const char *txt_; |
| | 2597 | size_t len_; |
| | 2598 | } |
| | 2599 | floatval_; |
| | 2600 | |
| | 2601 | /* enumerator value (valid when typ_ == TC_CVT_ENUM) */ |
| | 2602 | ulong enumval_; |
| | 2603 | |
| | 2604 | /* |
| | 2605 | * String value (valid when typ_ == TC_CVT_TYPE_SSTR). We need |
| | 2606 | * to know the length separately, because the underyling string |
| | 2607 | * may not be null-terminated. |
| | 2608 | */ |
| | 2609 | struct |
| | 2610 | { |
| | 2611 | const char *strval_; |
| | 2612 | size_t strval_len_; |
| | 2613 | } |
| | 2614 | strval_; |
| | 2615 | |
| | 2616 | /* my list value */ |
| | 2617 | class CTPNList *listval_; |
| | 2618 | |
| | 2619 | /* property ID value */ |
| | 2620 | uint propval_; |
| | 2621 | |
| | 2622 | /* object reference value */ |
| | 2623 | struct |
| | 2624 | { |
| | 2625 | ulong id_; |
| | 2626 | enum tc_metaclass_t meta_; |
| | 2627 | } |
| | 2628 | objval_; |
| | 2629 | |
| | 2630 | /* |
| | 2631 | * function pointer value - we store the underlying symbol, |
| | 2632 | * since function pointers are generally not resolved until late |
| | 2633 | * in the compilation |
| | 2634 | */ |
| | 2635 | class CTcSymFunc *funcptrval_; |
| | 2636 | |
| | 2637 | /* |
| | 2638 | * code body pointer value - we store the underlying code body |
| | 2639 | * for anonymous functions |
| | 2640 | */ |
| | 2641 | class CTPNCodeBody *codebodyval_; |
| | 2642 | } val_; |
| | 2643 | }; |
| | 2644 | |
| | 2645 | |
| | 2646 | /* ------------------------------------------------------------------------ */ |
| | 2647 | /* |
| | 2648 | * Assignment Types. |
| | 2649 | */ |
| | 2650 | |
| | 2651 | enum tc_asitype_t |
| | 2652 | { |
| | 2653 | /* simple assignment: x = 1 */ |
| | 2654 | TC_ASI_SIMPLE, |
| | 2655 | |
| | 2656 | /* add to: x += 1 */ |
| | 2657 | TC_ASI_ADD, |
| | 2658 | |
| | 2659 | /* subtract from: x -= 1 */ |
| | 2660 | TC_ASI_SUB, |
| | 2661 | |
| | 2662 | /* multiply by: x *= 1 */ |
| | 2663 | TC_ASI_MUL, |
| | 2664 | |
| | 2665 | /* divide by: x /= 1 */ |
| | 2666 | TC_ASI_DIV, |
| | 2667 | |
| | 2668 | /* modulo: x %= 1 */ |
| | 2669 | TC_ASI_MOD, |
| | 2670 | |
| | 2671 | /* bitwise-and with: x &= 1 */ |
| | 2672 | TC_ASI_BAND, |
| | 2673 | |
| | 2674 | /* bitwise-or with: x |= 1 */ |
| | 2675 | TC_ASI_BOR, |
| | 2676 | |
| | 2677 | /* bitwise-xor with: x ^= 1 */ |
| | 2678 | TC_ASI_BXOR, |
| | 2679 | |
| | 2680 | /* shift left: x <<= 1 */ |
| | 2681 | TC_ASI_SHL, |
| | 2682 | |
| | 2683 | /* shift right: x >>= 1 */ |
| | 2684 | TC_ASI_SHR, |
| | 2685 | |
| | 2686 | /* pre-increment */ |
| | 2687 | TC_ASI_PREINC, |
| | 2688 | |
| | 2689 | /* pre-decrement */ |
| | 2690 | TC_ASI_PREDEC, |
| | 2691 | |
| | 2692 | /* post-increment */ |
| | 2693 | TC_ASI_POSTINC, |
| | 2694 | |
| | 2695 | /* post-decrement */ |
| | 2696 | TC_ASI_POSTDEC |
| | 2697 | }; |
| | 2698 | |
| | 2699 | |
| | 2700 | /* ------------------------------------------------------------------------ */ |
| | 2701 | /* |
| | 2702 | * Formal parameter type list. For functions with declared formal |
| | 2703 | * parameter types (such as multi-methods), we use this class to keep the |
| | 2704 | * list of type names in the parameter list. |
| | 2705 | */ |
| | 2706 | class CTcFormalTypeList |
| | 2707 | { |
| | 2708 | public: |
| | 2709 | CTcFormalTypeList() |
| | 2710 | { |
| | 2711 | /* no entries in our type list yet */ |
| | 2712 | head_ = tail_ = 0; |
| | 2713 | |
| | 2714 | /* assume this isn't a varargs list */ |
| | 2715 | varargs_ = FALSE; |
| | 2716 | } |
| | 2717 | |
| | 2718 | ~CTcFormalTypeList() { } |
| | 2719 | |
| | 2720 | /* create the decorated name */ |
| | 2721 | void decorate_name(CTcToken *decorated_name, |
| | 2722 | const CTcToken *func_base_name); |
| | 2723 | |
| | 2724 | /* get the first parameter in the list */ |
| | 2725 | class CTcFormalTypeEle *get_first() const { return head_; } |
| | 2726 | |
| | 2727 | /* add a typed variable to the list */ |
| | 2728 | void add_typed_param(const CTcToken *tok); |
| | 2729 | |
| | 2730 | /* add an untyped variable to the list */ |
| | 2731 | void add_untyped_param(); |
| | 2732 | |
| | 2733 | /* add 'n' untyped variables to the list */ |
| | 2734 | void add_untyped_params(int n) |
| | 2735 | { |
| | 2736 | while (n-- > 0) |
| | 2737 | add_untyped_param(); |
| | 2738 | } |
| | 2739 | |
| | 2740 | /* add a trailing ellispsis (varargs indicator) */ |
| | 2741 | void add_ellipsis() { varargs_ = TRUE; } |
| | 2742 | |
| | 2743 | protected: |
| | 2744 | /* add a new list element */ |
| | 2745 | void add(class CTcFormalTypeEle *ele); |
| | 2746 | |
| | 2747 | /* add/tail of parameter list */ |
| | 2748 | class CTcFormalTypeEle *head_, *tail_; |
| | 2749 | |
| | 2750 | /* is this a varargs list? */ |
| | 2751 | int varargs_; |
| | 2752 | }; |
| | 2753 | |
| | 2754 | /* formal parameter type list entry */ |
| | 2755 | class CTcFormalTypeEle |
| | 2756 | { |
| | 2757 | public: |
| | 2758 | CTcFormalTypeEle() { name_ = 0; } |
| | 2759 | CTcFormalTypeEle(const char *name, size_t len); |
| | 2760 | ~CTcFormalTypeEle() |
| | 2761 | { |
| | 2762 | } |
| | 2763 | |
| | 2764 | /* next element in list */ |
| | 2765 | CTcFormalTypeEle *nxt_; |
| | 2766 | |
| | 2767 | /* type name */ |
| | 2768 | char *name_; |
| | 2769 | size_t name_len_; |
| | 2770 | }; |
| | 2771 | |
| | 2772 | |
| | 2773 | /* ------------------------------------------------------------------------ */ |
| | 2774 | /* |
| | 2775 | * Expression Operator Parsers. We construct a tree of these operator |
| | 2776 | * parsers so that we can express the expression grammar in a relatively |
| | 2777 | * compact and declarative notation. |
| | 2778 | */ |
| | 2779 | |
| | 2780 | /* |
| | 2781 | * basic operator parser |
| | 2782 | */ |
| | 2783 | class CTcPrsOp |
| | 2784 | { |
| | 2785 | public: |
| | 2786 | /* |
| | 2787 | * Parse an expression with this operator. Logs an error and |
| | 2788 | * returns non-zero if the expression is not valid; on success, |
| | 2789 | * returns zero. |
| | 2790 | * |
| | 2791 | * Fills in *val with the constant value, if any, of the expression. |
| | 2792 | * If the expression does not have a constant value, *val's type |
| | 2793 | * will be set to TC_CVT_UNKNOWN to indicate this. |
| | 2794 | * |
| | 2795 | * Returns a parse node if successful, or null if an error occurs |
| | 2796 | * and the operator parser is unable to make a guess about what was |
| | 2797 | * intended. |
| | 2798 | */ |
| | 2799 | virtual class CTcPrsNode *parse() const = 0; |
| | 2800 | }; |
| | 2801 | |
| | 2802 | /* |
| | 2803 | * generic left-associative binary operator |
| | 2804 | */ |
| | 2805 | class CTcPrsOpBin: public CTcPrsOp |
| | 2806 | { |
| | 2807 | public: |
| | 2808 | CTcPrsOpBin() |
| | 2809 | { |
| | 2810 | /* no left or right subexpression specified */ |
| | 2811 | left_ = right_ = 0; |
| | 2812 | |
| | 2813 | /* as-yet unknown operator token */ |
| | 2814 | op_tok_ = TOKT_INVALID; |
| | 2815 | } |
| | 2816 | |
| | 2817 | CTcPrsOpBin(tc_toktyp_t typ) |
| | 2818 | { |
| | 2819 | /* remember my operator token */ |
| | 2820 | op_tok_ = typ; |
| | 2821 | } |
| | 2822 | |
| | 2823 | CTcPrsOpBin(const CTcPrsOp *left, const CTcPrsOp *right, tc_toktyp_t typ) |
| | 2824 | { |
| | 2825 | /* remember my left and right sub-operators */ |
| | 2826 | left_ = left; |
| | 2827 | right_ = right; |
| | 2828 | |
| | 2829 | /* remember my operator token */ |
| | 2830 | op_tok_ = typ; |
| | 2831 | } |
| | 2832 | |
| | 2833 | /* parse the binary expression */ |
| | 2834 | class CTcPrsNode *parse() const; |
| | 2835 | |
| | 2836 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 2837 | virtual class CTcPrsNode |
| | 2838 | *build_tree(class CTcPrsNode *left, |
| | 2839 | class CTcPrsNode *right) const = 0; |
| | 2840 | |
| | 2841 | /* |
| | 2842 | * Try evaluating a constant result. If the two values can be |
| | 2843 | * combined with the operator to yield a constant value result, |
| | 2844 | * create a new parse node for the constant value (or update one of |
| | 2845 | * the given subnodes) and return it. If we can't provide a |
| | 2846 | * constant value, return null. |
| | 2847 | * |
| | 2848 | * By default, we'll indicate that the expression does not have a |
| | 2849 | * valid constant value. |
| | 2850 | */ |
| | 2851 | virtual class CTcPrsNode |
| | 2852 | *eval_constant(class CTcPrsNode *left, |
| | 2853 | class CTcPrsNode *right) const |
| | 2854 | { |
| | 2855 | /* indicate that we cannot synthesize a constant value */ |
| | 2856 | return 0; |
| | 2857 | } |
| | 2858 | |
| | 2859 | /* get/set my token */ |
| | 2860 | tc_toktyp_t get_op_tok() const { return op_tok_; } |
| | 2861 | void set_op_tok(tc_toktyp_t tok) { op_tok_ = tok; } |
| | 2862 | |
| | 2863 | protected: |
| | 2864 | /* operator that can be parsed for my left-hand side */ |
| | 2865 | const CTcPrsOp *left_; |
| | 2866 | |
| | 2867 | /* operator that can be parsed for my right-hand side */ |
| | 2868 | const CTcPrsOp *right_; |
| | 2869 | |
| | 2870 | /* my operator token */ |
| | 2871 | tc_toktyp_t op_tok_; |
| | 2872 | }; |
| | 2873 | |
| | 2874 | /* |
| | 2875 | * Binary Operator Group. This is a group of operators at a common |
| | 2876 | * precedence level. The group has an array of binary operators that |
| | 2877 | * are all at the same level of precedence; we'll evaluate the left |
| | 2878 | * suboperator, then check the token in the input stream against each of |
| | 2879 | * our group's operators, applying the one that matches, if one matches. |
| | 2880 | */ |
| | 2881 | class CTcPrsOpBinGroup: public CTcPrsOp |
| | 2882 | { |
| | 2883 | public: |
| | 2884 | CTcPrsOpBinGroup(const CTcPrsOp *left, const CTcPrsOp *right, |
| | 2885 | const class CTcPrsOpBin *const *ops) |
| | 2886 | { |
| | 2887 | /* remember my left and right suboperators */ |
| | 2888 | left_ = left; |
| | 2889 | right_ = right; |
| | 2890 | |
| | 2891 | /* remember the operators in my group */ |
| | 2892 | ops_ = ops; |
| | 2893 | } |
| | 2894 | |
| | 2895 | /* parse the expression */ |
| | 2896 | class CTcPrsNode *parse() const; |
| | 2897 | |
| | 2898 | protected: |
| | 2899 | /* find and apply an operator to the parsed left-hand side */ |
| | 2900 | int find_and_apply_op(CTcPrsNode **lhs) const; |
| | 2901 | |
| | 2902 | /* my left and right suboperators */ |
| | 2903 | const CTcPrsOp *left_; |
| | 2904 | const CTcPrsOp *right_; |
| | 2905 | |
| | 2906 | /* group of binary operators at this precedence level */ |
| | 2907 | const class CTcPrsOpBin *const *ops_; |
| | 2908 | }; |
| | 2909 | |
| | 2910 | /* |
| | 2911 | * Binary operator group for comparison operators. This is a similar to |
| | 2912 | * other binary groups, but also includes the special "is in" and "not |
| | 2913 | * in" operators. |
| | 2914 | */ |
| | 2915 | class CTcPrsOpBinGroupCompare: public CTcPrsOpBinGroup |
| | 2916 | { |
| | 2917 | public: |
| | 2918 | CTcPrsOpBinGroupCompare(const class CTcPrsOp *left, |
| | 2919 | const class CTcPrsOp *right, |
| | 2920 | const class CTcPrsOpBin *const *ops) |
| | 2921 | : CTcPrsOpBinGroup(left, right, ops) |
| | 2922 | { |
| | 2923 | } |
| | 2924 | |
| | 2925 | class CTcPrsNode *parse() const; |
| | 2926 | |
| | 2927 | protected: |
| | 2928 | /* parse the 'in' list portion of the expression */ |
| | 2929 | class CTPNArglist *parse_inlist() const; |
| | 2930 | }; |
| | 2931 | |
| | 2932 | /* comma operator */ |
| | 2933 | class CTcPrsOpComma: public CTcPrsOpBin |
| | 2934 | { |
| | 2935 | public: |
| | 2936 | CTcPrsOpComma(const CTcPrsOp *left, const CTcPrsOp *right) |
| | 2937 | : CTcPrsOpBin(left, right, TOKT_COMMA) { } |
| | 2938 | |
| | 2939 | /* evaluate constant result */ |
| | 2940 | class CTcPrsNode |
| | 2941 | *eval_constant(class CTcPrsNode *left, |
| | 2942 | class CTcPrsNode *right) const; |
| | 2943 | |
| | 2944 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 2945 | class CTcPrsNode |
| | 2946 | *build_tree(class CTcPrsNode *left, |
| | 2947 | class CTcPrsNode *right) const; |
| | 2948 | }; |
| | 2949 | |
| | 2950 | /* logical OR */ |
| | 2951 | class CTcPrsOpOr: public CTcPrsOpBin |
| | 2952 | { |
| | 2953 | public: |
| | 2954 | CTcPrsOpOr(const CTcPrsOp *left, const CTcPrsOp *right) |
| | 2955 | : CTcPrsOpBin(left, right, TOKT_OROR) { } |
| | 2956 | |
| | 2957 | /* evaluate constant result */ |
| | 2958 | class CTcPrsNode |
| | 2959 | *eval_constant(class CTcPrsNode *left, |
| | 2960 | class CTcPrsNode *right) const; |
| | 2961 | |
| | 2962 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 2963 | class CTcPrsNode |
| | 2964 | *build_tree(class CTcPrsNode *left, |
| | 2965 | class CTcPrsNode *right) const; |
| | 2966 | }; |
| | 2967 | |
| | 2968 | /* logical AND */ |
| | 2969 | class CTcPrsOpAnd: public CTcPrsOpBin |
| | 2970 | { |
| | 2971 | public: |
| | 2972 | CTcPrsOpAnd(const CTcPrsOp *left, const CTcPrsOp *right) |
| | 2973 | : CTcPrsOpBin(left, right, TOKT_ANDAND) { } |
| | 2974 | |
| | 2975 | /* evaluate constant result */ |
| | 2976 | class CTcPrsNode |
| | 2977 | *eval_constant(class CTcPrsNode *left, |
| | 2978 | class CTcPrsNode *right) const; |
| | 2979 | |
| | 2980 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 2981 | class CTcPrsNode |
| | 2982 | *build_tree(class CTcPrsNode *left, |
| | 2983 | class CTcPrsNode *right) const; |
| | 2984 | }; |
| | 2985 | |
| | 2986 | /* general magnitude comparison operators */ |
| | 2987 | class CTcPrsOpRel: public CTcPrsOpBin |
| | 2988 | { |
| | 2989 | public: |
| | 2990 | CTcPrsOpRel(tc_toktyp_t typ) : CTcPrsOpBin(typ) { } |
| | 2991 | |
| | 2992 | /* evaluate constant result */ |
| | 2993 | class CTcPrsNode |
| | 2994 | *eval_constant(class CTcPrsNode *left, |
| | 2995 | class CTcPrsNode *right) const; |
| | 2996 | |
| | 2997 | protected: |
| | 2998 | /* |
| | 2999 | * Get the result true/false value, given the result of the |
| | 3000 | * comparison. For example, if this is a greater-than operator, |
| | 3001 | * this should return TRUE if comp > 0, FALSE otherwise. |
| | 3002 | */ |
| | 3003 | virtual int get_bool_val(int comparison_value) const = 0; |
| | 3004 | }; |
| | 3005 | |
| | 3006 | /* comparison - greater than */ |
| | 3007 | class CTcPrsOpGt: public CTcPrsOpRel |
| | 3008 | { |
| | 3009 | public: |
| | 3010 | CTcPrsOpGt() : CTcPrsOpRel(TOKT_GT) { } |
| | 3011 | |
| | 3012 | /* get the boolean value for a comparison sense */ |
| | 3013 | int get_bool_val(int comp) const { return comp > 0; } |
| | 3014 | |
| | 3015 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 3016 | class CTcPrsNode |
| | 3017 | *build_tree(class CTcPrsNode *left, |
| | 3018 | class CTcPrsNode *right) const; |
| | 3019 | }; |
| | 3020 | |
| | 3021 | /* comparison - greater than or equal to */ |
| | 3022 | class CTcPrsOpGe: public CTcPrsOpRel |
| | 3023 | { |
| | 3024 | public: |
| | 3025 | CTcPrsOpGe() : CTcPrsOpRel(TOKT_GE) { } |
| | 3026 | |
| | 3027 | /* get the boolean value for a comparison sense */ |
| | 3028 | int get_bool_val(int comp) const { return comp >= 0; } |
| | 3029 | |
| | 3030 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 3031 | class CTcPrsNode |
| | 3032 | *build_tree(class CTcPrsNode *left, |
| | 3033 | class CTcPrsNode *right) const; |
| | 3034 | }; |
| | 3035 | |
| | 3036 | /* comparison - less than */ |
| | 3037 | class CTcPrsOpLt: public CTcPrsOpRel |
| | 3038 | { |
| | 3039 | public: |
| | 3040 | CTcPrsOpLt() : CTcPrsOpRel(TOKT_LT) { } |
| | 3041 | |
| | 3042 | /* get the boolean value for a comparison sense */ |
| | 3043 | int get_bool_val(int comp) const { return comp < 0; } |
| | 3044 | |
| | 3045 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 3046 | class CTcPrsNode |
| | 3047 | *build_tree(class CTcPrsNode *left, |
| | 3048 | class CTcPrsNode *right) const; |
| | 3049 | }; |
| | 3050 | |
| | 3051 | /* comparison - less than or equal to */ |
| | 3052 | class CTcPrsOpLe: public CTcPrsOpRel |
| | 3053 | { |
| | 3054 | public: |
| | 3055 | CTcPrsOpLe() : CTcPrsOpRel(TOKT_LE) { } |
| | 3056 | |
| | 3057 | /* get the boolean value for a comparison sense */ |
| | 3058 | int get_bool_val(int comp) const { return comp <= 0; } |
| | 3059 | |
| | 3060 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 3061 | class CTcPrsNode |
| | 3062 | *build_tree(class CTcPrsNode *left, |
| | 3063 | class CTcPrsNode *right) const; |
| | 3064 | }; |
| | 3065 | |
| | 3066 | /* |
| | 3067 | * Equality/inequality comparison |
| | 3068 | */ |
| | 3069 | class CTcPrsOpEqComp: public CTcPrsOpBin |
| | 3070 | { |
| | 3071 | public: |
| | 3072 | CTcPrsOpEqComp(tc_toktyp_t typ) : CTcPrsOpBin(typ) { } |
| | 3073 | |
| | 3074 | /* evaluate constant result */ |
| | 3075 | class CTcPrsNode |
| | 3076 | *eval_constant(class CTcPrsNode *left, |
| | 3077 | class CTcPrsNode *right) const; |
| | 3078 | |
| | 3079 | protected: |
| | 3080 | /* get the boolean value to use if the operands are equal */ |
| | 3081 | virtual int get_bool_val(int ops_equal) const = 0; |
| | 3082 | }; |
| | 3083 | |
| | 3084 | |
| | 3085 | /* |
| | 3086 | * Equality comparison |
| | 3087 | */ |
| | 3088 | class CTcPrsOpEq: public CTcPrsOpEqComp |
| | 3089 | { |
| | 3090 | public: |
| | 3091 | /* start out in C mode - use '==' operator by default */ |
| | 3092 | CTcPrsOpEq() |
| | 3093 | : CTcPrsOpEqComp(TOKT_EQEQ) { } |
| | 3094 | |
| | 3095 | /* set the current equality operator */ |
| | 3096 | void set_eq_op(tc_toktyp_t op) { op_tok_ = op; } |
| | 3097 | |
| | 3098 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 3099 | class CTcPrsNode |
| | 3100 | *build_tree(class CTcPrsNode *left, |
| | 3101 | class CTcPrsNode *right) const; |
| | 3102 | |
| | 3103 | /* get the boolean value to use if the operands are equal */ |
| | 3104 | virtual int get_bool_val(int ops_equal) const { return ops_equal; } |
| | 3105 | }; |
| | 3106 | |
| | 3107 | /* |
| | 3108 | * Inequality comparison |
| | 3109 | */ |
| | 3110 | class CTcPrsOpNe: public CTcPrsOpEqComp |
| | 3111 | { |
| | 3112 | public: |
| | 3113 | CTcPrsOpNe() : CTcPrsOpEqComp(TOKT_NE) { } |
| | 3114 | |
| | 3115 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 3116 | class CTcPrsNode |
| | 3117 | *build_tree(class CTcPrsNode *left, |
| | 3118 | class CTcPrsNode *right) const; |
| | 3119 | |
| | 3120 | /* get the boolean value to use if the operands are equal */ |
| | 3121 | virtual int get_bool_val(int ops_equal) const { return !ops_equal; } |
| | 3122 | }; |
| | 3123 | |
| | 3124 | /* |
| | 3125 | * binary arithmetic operators |
| | 3126 | */ |
| | 3127 | class CTcPrsOpArith: public CTcPrsOpBin |
| | 3128 | { |
| | 3129 | public: |
| | 3130 | CTcPrsOpArith(tc_toktyp_t typ) |
| | 3131 | : CTcPrsOpBin(typ) { } |
| | 3132 | |
| | 3133 | CTcPrsOpArith(const CTcPrsOp *left, const CTcPrsOp *right, |
| | 3134 | tc_toktyp_t typ) |
| | 3135 | : CTcPrsOpBin(left, right, typ) { } |
| | 3136 | |
| | 3137 | /* evaluate constant result */ |
| | 3138 | class CTcPrsNode |
| | 3139 | *eval_constant(class CTcPrsNode *left, |
| | 3140 | class CTcPrsNode *right) const; |
| | 3141 | |
| | 3142 | protected: |
| | 3143 | /* calculate the result */ |
| | 3144 | virtual long calc_result(long val1, long val2) const = 0; |
| | 3145 | }; |
| | 3146 | |
| | 3147 | /* bitwise OR */ |
| | 3148 | class CTcPrsOpBOr: public CTcPrsOpArith |
| | 3149 | { |
| | 3150 | public: |
| | 3151 | CTcPrsOpBOr(const CTcPrsOp *left, const CTcPrsOp *right) |
| | 3152 | : CTcPrsOpArith(left, right, TOKT_OR) { } |
| | 3153 | |
| | 3154 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 3155 | class CTcPrsNode |
| | 3156 | *build_tree(class CTcPrsNode *left, |
| | 3157 | class CTcPrsNode *right) const; |
| | 3158 | |
| | 3159 | protected: |
| | 3160 | /* calculate the result */ |
| | 3161 | virtual long calc_result(long val1, long val2) const |
| | 3162 | { return val1 | val2; } |
| | 3163 | }; |
| | 3164 | |
| | 3165 | /* bitwise XOR */ |
| | 3166 | class CTcPrsOpBXor: public CTcPrsOpArith |
| | 3167 | { |
| | 3168 | public: |
| | 3169 | CTcPrsOpBXor(const CTcPrsOp *left, const CTcPrsOp *right) |
| | 3170 | : CTcPrsOpArith(left, right, TOKT_XOR) { } |
| | 3171 | |
| | 3172 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 3173 | class CTcPrsNode |
| | 3174 | *build_tree(class CTcPrsNode *left, |
| | 3175 | class CTcPrsNode *right) const; |
| | 3176 | |
| | 3177 | protected: |
| | 3178 | /* calculate the result */ |
| | 3179 | virtual long calc_result(long val1, long val2) const |
| | 3180 | { return val1 ^ val2; } |
| | 3181 | }; |
| | 3182 | |
| | 3183 | /* bitwise AND */ |
| | 3184 | class CTcPrsOpBAnd: public CTcPrsOpArith |
| | 3185 | { |
| | 3186 | public: |
| | 3187 | CTcPrsOpBAnd(const CTcPrsOp *left, const CTcPrsOp *right) |
| | 3188 | : CTcPrsOpArith(left, right, TOKT_AND) { } |
| | 3189 | |
| | 3190 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 3191 | class CTcPrsNode |
| | 3192 | *build_tree(class CTcPrsNode *left, |
| | 3193 | class CTcPrsNode *right) const; |
| | 3194 | |
| | 3195 | protected: |
| | 3196 | /* calculate the result */ |
| | 3197 | virtual long calc_result(long val1, long val2) const |
| | 3198 | { return val1 & val2; } |
| | 3199 | }; |
| | 3200 | |
| | 3201 | /* |
| | 3202 | * shift left |
| | 3203 | */ |
| | 3204 | class CTcPrsOpShl: public CTcPrsOpArith |
| | 3205 | { |
| | 3206 | public: |
| | 3207 | CTcPrsOpShl() : CTcPrsOpArith(TOKT_SHL) { } |
| | 3208 | |
| | 3209 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 3210 | class CTcPrsNode |
| | 3211 | *build_tree(class CTcPrsNode *left, |
| | 3212 | class CTcPrsNode *right) const; |
| | 3213 | |
| | 3214 | protected: |
| | 3215 | long calc_result(long a, long b) const { return a << b; } |
| | 3216 | }; |
| | 3217 | |
| | 3218 | /* |
| | 3219 | * shift right |
| | 3220 | */ |
| | 3221 | class CTcPrsOpShr: public CTcPrsOpArith |
| | 3222 | { |
| | 3223 | public: |
| | 3224 | CTcPrsOpShr() : CTcPrsOpArith(TOKT_SHR) { } |
| | 3225 | |
| | 3226 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 3227 | class CTcPrsNode |
| | 3228 | *build_tree(class CTcPrsNode *left, |
| | 3229 | class CTcPrsNode *right) const; |
| | 3230 | |
| | 3231 | protected: |
| | 3232 | long calc_result(long a, long b) const { return a >> b; } |
| | 3233 | }; |
| | 3234 | |
| | 3235 | /* |
| | 3236 | * multiply |
| | 3237 | */ |
| | 3238 | class CTcPrsOpMul: public CTcPrsOpArith |
| | 3239 | { |
| | 3240 | public: |
| | 3241 | CTcPrsOpMul() : CTcPrsOpArith(TOKT_TIMES) { } |
| | 3242 | |
| | 3243 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 3244 | class CTcPrsNode |
| | 3245 | *build_tree(class CTcPrsNode *left, |
| | 3246 | class CTcPrsNode *right) const; |
| | 3247 | |
| | 3248 | protected: |
| | 3249 | long calc_result(long a, long b) const { return a * b; } |
| | 3250 | }; |
| | 3251 | |
| | 3252 | /* |
| | 3253 | * divide |
| | 3254 | */ |
| | 3255 | class CTcPrsOpDiv: public CTcPrsOpArith |
| | 3256 | { |
| | 3257 | public: |
| | 3258 | CTcPrsOpDiv() |
| | 3259 | : CTcPrsOpArith(TOKT_DIV) { } |
| | 3260 | |
| | 3261 | CTcPrsOpDiv(tc_toktyp_t tok) |
| | 3262 | : CTcPrsOpArith(tok) { } |
| | 3263 | |
| | 3264 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 3265 | class CTcPrsNode |
| | 3266 | *build_tree(class CTcPrsNode *left, |
| | 3267 | class CTcPrsNode *right) const; |
| | 3268 | |
| | 3269 | protected: |
| | 3270 | long calc_result(long a, long b) const; |
| | 3271 | }; |
| | 3272 | |
| | 3273 | |
| | 3274 | /* |
| | 3275 | * mod - inherit from divide operator to pick up divide-by-zero checking |
| | 3276 | */ |
| | 3277 | class CTcPrsOpMod: public CTcPrsOpDiv |
| | 3278 | { |
| | 3279 | public: |
| | 3280 | CTcPrsOpMod() : CTcPrsOpDiv(TOKT_MOD) { } |
| | 3281 | |
| | 3282 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 3283 | class CTcPrsNode |
| | 3284 | *build_tree(class CTcPrsNode *left, |
| | 3285 | class CTcPrsNode *right) const; |
| | 3286 | |
| | 3287 | protected: |
| | 3288 | long calc_result(long a, long b) const; |
| | 3289 | }; |
| | 3290 | |
| | 3291 | /* |
| | 3292 | * add |
| | 3293 | */ |
| | 3294 | class CTcPrsOpAdd: public CTcPrsOpArith |
| | 3295 | { |
| | 3296 | public: |
| | 3297 | CTcPrsOpAdd() : CTcPrsOpArith(TOKT_PLUS) { } |
| | 3298 | |
| | 3299 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 3300 | class CTcPrsNode |
| | 3301 | *build_tree(class CTcPrsNode *left, |
| | 3302 | class CTcPrsNode *right) const; |
| | 3303 | |
| | 3304 | /* evaluate constant result */ |
| | 3305 | class CTcPrsNode |
| | 3306 | *eval_constant(class CTcPrsNode *left, |
| | 3307 | class CTcPrsNode *right) const; |
| | 3308 | |
| | 3309 | protected: |
| | 3310 | long calc_result(long a, long b) const { return a + b; } |
| | 3311 | }; |
| | 3312 | |
| | 3313 | /* |
| | 3314 | * subtract |
| | 3315 | */ |
| | 3316 | class CTcPrsOpSub: public CTcPrsOpArith |
| | 3317 | { |
| | 3318 | public: |
| | 3319 | CTcPrsOpSub() : CTcPrsOpArith(TOKT_MINUS) { } |
| | 3320 | |
| | 3321 | /* build a new tree out of our left-hand and right-hand subtrees */ |
| | 3322 | class CTcPrsNode |
| | 3323 | *build_tree(class CTcPrsNode *left, |
| | 3324 | class CTcPrsNode *right) const; |
| | 3325 | |
| | 3326 | /* evaluate constant result */ |
| | 3327 | class CTcPrsNode |
| | 3328 | *eval_constant(class CTcPrsNode *left, |
| | 3329 | class CTcPrsNode *right) const; |
| | 3330 | |
| | 3331 | protected: |
| | 3332 | long calc_result(long a, long b) const { return a - b; } |
| | 3333 | }; |
| | 3334 | |
| | 3335 | /* |
| | 3336 | * Unary Operators |
| | 3337 | */ |
| | 3338 | class CTcPrsOpUnary: public CTcPrsOp |
| | 3339 | { |
| | 3340 | public: |
| | 3341 | class CTcPrsNode *parse() const; |
| | 3342 | |
| | 3343 | /* |
| | 3344 | * evaluate a constant subscript expression; returns a constant |
| | 3345 | * parse node expression if the subscript can be evaluated to a |
| | 3346 | * compile-time constant, or null if not |
| | 3347 | */ |
| | 3348 | static class CTcPrsNode |
| | 3349 | *eval_const_subscript(class CTcPrsNode *lhs, |
| | 3350 | class CTcPrsNode *subscript); |
| | 3351 | |
| | 3352 | /* |
| | 3353 | * evaluate a constant NOT expression; returns a constant parse node |
| | 3354 | * expression if the logical negation can be evaluated to a |
| | 3355 | * compile-time constant, or null if not |
| | 3356 | */ |
| | 3357 | static class CTcPrsNode *eval_const_not(class CTcPrsNode *lhs); |
| | 3358 | |
| | 3359 | /* parse a double-quoted string with embedded expressions */ |
| | 3360 | static class CTcPrsNode *parse_dstr_embed(); |
| | 3361 | |
| | 3362 | /* parse a list */ |
| | 3363 | static class CTcPrsNode *parse_list(); |
| | 3364 | |
| | 3365 | /* parse a primary expression */ |
| | 3366 | static class CTcPrsNode *parse_primary(); |
| | 3367 | |
| | 3368 | protected: |
| | 3369 | /* parse an anonymous function */ |
| | 3370 | static class CTcPrsNode *parse_anon_func(int short_form); |
| | 3371 | |
| | 3372 | /* parse a logical NOT operator */ |
| | 3373 | static class CTcPrsNode *parse_not(CTcPrsNode *sub); |
| | 3374 | |
| | 3375 | /* parse a bitwise NOT operator */ |
| | 3376 | static class CTcPrsNode *parse_bnot(CTcPrsNode *sub); |
| | 3377 | |
| | 3378 | /* parse an address-of operator */ |
| | 3379 | class CTcPrsNode *parse_addr() const; |
| | 3380 | |
| | 3381 | /* parse an arithmetic positive operator */ |
| | 3382 | static class CTcPrsNode *parse_pos(CTcPrsNode *sub); |
| | 3383 | |
| | 3384 | /* parse an arithmetic negative operator */ |
| | 3385 | static class CTcPrsNode *parse_neg(CTcPrsNode *sub); |
| | 3386 | |
| | 3387 | /* parse a pre- or post-increment operator */ |
| | 3388 | static class CTcPrsNode *parse_inc(int pre, CTcPrsNode *sub); |
| | 3389 | |
| | 3390 | /* parse a pre- or post-decrement operator */ |
| | 3391 | static class CTcPrsNode *parse_dec(int pre, CTcPrsNode *sub); |
| | 3392 | |
| | 3393 | /* parse a 'new' operator */ |
| | 3394 | static class CTcPrsNode *parse_new(CTcPrsNode *sub, int is_transient); |
| | 3395 | |
| | 3396 | /* parse a 'delete' operator */ |
| | 3397 | static class CTcPrsNode *parse_delete(CTcPrsNode *sub); |
| | 3398 | |
| | 3399 | /* parse a postfix expression */ |
| | 3400 | static class CTcPrsNode *parse_postfix(int allow_member_expr, |
| | 3401 | int allow_call_expr); |
| | 3402 | |
| | 3403 | /* parse a function or method call */ |
| | 3404 | static class CTcPrsNode *parse_call(CTcPrsNode *lhs); |
| | 3405 | |
| | 3406 | /* parse an argument list */ |
| | 3407 | static class CTPNArglist *parse_arg_list(); |
| | 3408 | |
| | 3409 | /* parse a subscript */ |
| | 3410 | static class CTcPrsNode *parse_subscript(CTcPrsNode *lhs); |
| | 3411 | |
| | 3412 | /* parse a member selection ('.' operator) */ |
| | 3413 | static class CTcPrsNode *parse_member(CTcPrsNode *lhs); |
| | 3414 | |
| | 3415 | /* parse an "inherited" expression */ |
| | 3416 | static class CTcPrsNode *parse_inherited(); |
| | 3417 | |
| | 3418 | /* parse a "delegated" expression */ |
| | 3419 | static class CTcPrsNode *parse_delegated(); |
| | 3420 | |
| | 3421 | /* local symbol enumeration callback for anonymous function setup */ |
| | 3422 | static void enum_for_anon(void *ctx, class CTcSymbol *sym); |
| | 3423 | |
| | 3424 | /* local symbol enumeration for anon function - follow-up */ |
| | 3425 | static void enum_for_anon2(void *ctx, class CTcSymbol *sym); |
| | 3426 | }; |
| | 3427 | |
| | 3428 | /* |
| | 3429 | * tertiary conditional operator |
| | 3430 | */ |
| | 3431 | class CTcPrsOpIf: public CTcPrsOp |
| | 3432 | { |
| | 3433 | public: |
| | 3434 | class CTcPrsNode *parse() const; |
| | 3435 | }; |
| | 3436 | |
| | 3437 | /* |
| | 3438 | * Assignment operators (including the regular assignment, "="/":=", |
| | 3439 | * plus all calculate-and-assign operators: "+=", "-=", etc) |
| | 3440 | */ |
| | 3441 | class CTcPrsOpAsi: public CTcPrsOp |
| | 3442 | { |
| | 3443 | public: |
| | 3444 | CTcPrsOpAsi() |
| | 3445 | { |
| | 3446 | /* start out with the C-mode simple assignment operator */ |
| | 3447 | asi_op_ = TOKT_EQ; |
| | 3448 | } |
| | 3449 | |
| | 3450 | /* parse an assignment */ |
| | 3451 | class CTcPrsNode *parse() const; |
| | 3452 | |
| | 3453 | /* set the current simple assignment operator */ |
| | 3454 | void set_asi_op(tc_toktyp_t tok) { asi_op_ = tok; } |
| | 3455 | |
| | 3456 | private: |
| | 3457 | /* current simple assignment operator */ |
| | 3458 | tc_toktyp_t asi_op_; |
| | 3459 | }; |
| | 3460 | |
| | 3461 | #endif /* TCPRS_H */ |
| | 3462 | |