cfad47cfa3/t3compiler/tads3/tcprs.cpp
Commiter: Nikos Chantziaras
Author: Nikos Chantziaras
Revision: cfad47cfa3
File Size: 235 KB
(June 01, 2009 20:54 UTC) Almost 3 years ago
Initial commit.
Showing without highlighting since it looks like a big file and may slow your browser - show with highlighting
Show/hide line numbers#ifdef RCSID
static char RCSid[] =
"$Header: d:/cvsroot/tads/tads3/TCPRS.CPP,v 1.5 1999/07/11 00:46:53 MJRoberts Exp $";
#endif
/*
* Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved.
*
* Please see the accompanying license file, LICENSE.TXT, for information
* on using and copying this software.
*/
/*
Name
tcprs.cpp - TADS 3 Compiler Parser
Function
This parser module contains code required for any parser usage, both
for a full compiler and for a debugger.
Notes
Modified
04/30/99 MJRoberts - Creation
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "os.h"
#include "t3std.h"
#include "tcprs.h"
#include "tctarg.h"
#include "tcgen.h"
#include "vmhash.h"
#include "tcmain.h"
#include "vmfile.h"
#include "tctok.h"
/* ------------------------------------------------------------------------ */
/*
* Expression Operator parser objects. These objects are linked
* together in a network that defines the order of precedence for
* expression operators.
*
* These objects use static storage. This is acceptable, even though
* these objects aren't all "const" qualified, because the compiler uses
* a single global parser object; since there's only the one parser,
* there only needs to be a single network of these objects.
*/
/* unary operator parser */
static CTcPrsOpUnary S_op_unary;
/* factor group */
static const CTcPrsOpMul S_op_mul;
static const CTcPrsOpDiv S_op_div;
static const CTcPrsOpMod S_op_mod;
static const CTcPrsOpBin *const
S_grp_factor[] = { &S_op_mul, &S_op_div, &S_op_mod, 0 };
static const CTcPrsOpBinGroup S_op_factor(&S_op_unary, &S_op_unary,
S_grp_factor);
/* term group */
static const CTcPrsOpAdd S_op_add;
static const CTcPrsOpSub S_op_sub;
static const CTcPrsOpBin *const S_grp_term[] = { &S_op_add, &S_op_sub, 0 };
static const CTcPrsOpBinGroup S_op_term(&S_op_factor, &S_op_factor,
S_grp_term);
/* shift group */
static const CTcPrsOpShl S_op_shl;
static const CTcPrsOpShr S_op_shr;
static const CTcPrsOpBin *const S_grp_shift[] = { &S_op_shl, &S_op_shr, 0 };
static const CTcPrsOpBinGroup S_op_shift(&S_op_term, &S_op_term,
S_grp_shift);
/* magnitude comparisons group */
static const CTcPrsOpGt S_op_gt;
static const CTcPrsOpGe S_op_ge;
static const CTcPrsOpLt S_op_lt;
static const CTcPrsOpLe S_op_le;
static const CTcPrsOpBin *const
S_grp_relcomp[] = { &S_op_gt, &S_op_ge, &S_op_lt, &S_op_le, 0 };
static const CTcPrsOpBinGroup S_op_relcomp(&S_op_shift, &S_op_shift,
S_grp_relcomp);
/*
* Equality/inequality comparison group. Note that the equality operator
* is non-const because we want the option to change the operator on the
* fly based on syntax mode - '==' in C-mode, '=' in TADS traditional mode.
* (This was a feature of tads 2, but we have since deprecated it in tads
* 3, so this ability is actually just vestigial at this point. No harm in
* keeping around the code internally for it, though, since it's pretty
* simple.)
*
* Note also that this is a special binary group - this one recognizes the
* non-keyword operators 'is in' and 'not in'.
*/
static CTcPrsOpEq S_op_eq;
static const CTcPrsOpNe S_op_ne;
static const CTcPrsOpBin *const
S_grp_eqcomp[] = { &S_op_eq, &S_op_ne, 0 };
static const CTcPrsOpBinGroupCompare
S_op_eqcomp(&S_op_relcomp, &S_op_relcomp, S_grp_eqcomp);
/* bitwise AND operator */
static const CTcPrsOpBAnd S_op_band(&S_op_eqcomp, &S_op_eqcomp);
/* bitwise XOR operator */
static const CTcPrsOpBXor S_op_bxor(&S_op_band, &S_op_band);
/* bitwise OR operator */
static const CTcPrsOpBOr S_op_bor(&S_op_bxor, &S_op_bxor);
/* logical AND operator */
static const CTcPrsOpAnd S_op_and(&S_op_bor, &S_op_bor);
/* logical OR operator */
static const CTcPrsOpOr S_op_or(&S_op_and, &S_op_and);
/* conditional operator */
static const CTcPrsOpIf S_op_if;
/*
* assignment operator - note that this is non-const, because we must be
* able to change the operator - '=' in C-mode, and ':=' in TADS
* traditional mode
*/
static CTcPrsOpAsi S_op_asi;
/* comma operator */
static const CTcPrsOpComma S_op_comma(&S_op_asi, &S_op_asi);
/* ------------------------------------------------------------------------ */
/*
* Main Parser
*/
/*
* initialize the parser
*/
CTcParser::CTcParser()
{
size_t i;
/* we don't have any module information yet */
module_name_ = 0;
module_seqno_ = 0;
/* start out in normal mode */
pp_expr_mode_ = FALSE;
src_group_mode_ = FALSE;
/* create the global symbol table */
global_symtab_ = new CTcPrsSymtab(0);
/* no enclosing statement yet */
enclosing_stm_ = 0;
/* no source location yet */
cur_desc_ = 0;
cur_linenum_ = 0;
/* no dictionaries yet */
dict_cur_ = 0;
dict_head_ = dict_tail_ = 0;
/* no object file dictionary list yet */
obj_dict_list_ = 0;
obj_file_dict_idx_ = 0;
/* no dictionary properties yet */
dict_prop_head_ = 0;
/* no grammar productions yet */
gramprod_head_ = gramprod_tail_ = 0;
/* no object file symbol list yet */
obj_sym_list_ = 0;
obj_file_sym_idx_ = 0;
/* no object templates yet */
template_head_ = template_tail_ = 0;
/* allocate some initial template parsing space */
template_expr_max_ = 16;
template_expr_ = (CTcObjTemplateInst *)G_prsmem->
alloc(sizeof(template_expr_[0]) * template_expr_max_);
/* no locals yet */
local_cnt_ = max_local_cnt_ = 0;
/* no local or goto symbol table yet */
local_symtab_ = 0;
enclosing_local_symtab_ = 0;
goto_symtab_ = 0;
/* no debugger local symbol table yet */
debug_symtab_ = 0;
/* not in a preprocessor constant expression */
pp_expr_mode_ = FALSE;
/* assume we're doing full compilation */
syntax_only_ = FALSE;
/* no nested top-level statements yet */
nested_stm_head_ = 0;
nested_stm_tail_ = 0;
/* no anonymous objects yet */
anon_obj_head_ = 0;
anon_obj_tail_ = 0;
/* no non-symbol objects yet */
nonsym_obj_head_ = 0;
nonsym_obj_tail_ = 0;
/* allocate an initial context variable property array */
ctx_var_props_size_ = 50;
ctx_var_props_ = (tctarg_prop_id_t *)
t3malloc(ctx_var_props_size_ * sizeof(tctarg_prop_id_t));
/* no context variable properties assigned yet */
ctx_var_props_cnt_ = 0;
ctx_var_props_used_ = 0;
/*
* no context variable indices assigned yet - start at one higher
* than the index at which we always store 'self'
*/
next_ctx_arr_idx_ = TCPRS_LOCAL_CTX_METHODCTX + 1;
/* 'self' isn't valid yet */
self_valid_ = FALSE;
/* start at enum ID 1 (let 0 serve as an invalid value) */
next_enum_id_ = 1;
/* the '+' property is not yet defined */
plus_prop_ = 0;
/* no exported symbols yet */
exp_head_ = exp_tail_ = 0;
/* allocate an initial '+' stack */
plus_stack_alloc_ = 32;
plus_stack_ = (CTPNStmObject **)
t3malloc(plus_stack_alloc_ * sizeof(*plus_stack_));
/* clear out the stack */
for (i = 0 ; i < plus_stack_alloc_ ; ++i)
plus_stack_[i] = 0;
/* there's no current code body (function/method body) yet */
cur_code_body_ = 0;
/* nothing in the local context has been referenced yet */
self_referenced_ = FALSE;
local_ctx_needs_self_ = FALSE;
full_method_ctx_referenced_ = FALSE;
local_ctx_needs_full_method_ctx_ = FALSE;
}
/*
* Initialize. This must be called after the code generator is set up.
*/
void CTcParser::init()
{
static const char construct_name[] = "construct";
static const char finalize_name[] = "finalize";
static const char objcall_name[] = ".objcall";
static const char graminfo_name[] = "grammarInfo";
static const char miscvocab_name[] = "miscVocab";
static const char lexical_parent_name[] = "lexicalParent";
static const char src_order_name[] = "sourceTextOrder";
static const char src_group_name[] = "sourceTextGroup";
static const char src_group_mod_name[] = "sourceTextGroupName";
static const char src_group_seq_name[] = "sourceTextGroupOrder";
tctarg_prop_id_t graminfo_prop_id;
tctarg_prop_id_t lexpar_prop_id;
tctarg_prop_id_t src_order_prop_id;
tctarg_prop_id_t src_group_prop_id;
tctarg_prop_id_t src_group_mod_prop_id;
tctarg_prop_id_t src_group_seq_prop_id;
CTcSymProp *sym;
/* allocate and note our special property ID's */
constructor_prop_ = G_cg->new_prop_id();
finalize_prop_ = G_cg->new_prop_id();
objcall_prop_ = G_cg->new_prop_id();
graminfo_prop_id = G_cg->new_prop_id();
miscvocab_prop_ = G_cg->new_prop_id();
lexpar_prop_id = G_cg->new_prop_id();
src_order_prop_id = G_cg->new_prop_id();
src_group_prop_id = G_cg->new_prop_id();
src_group_mod_prop_id = G_cg->new_prop_id();
src_group_seq_prop_id = G_cg->new_prop_id();
/* add a "construct" property for constructors */
sym = new CTcSymProp(construct_name, sizeof(construct_name) - 1,
FALSE, (tctarg_prop_id_t)constructor_prop_);
sym->mark_referenced();
global_symtab_->add_entry(sym);
constructor_sym_ = sym;
/* add a "finalize" property for finalizers */
sym = new CTcSymProp(finalize_name, sizeof(finalize_name) - 1,
FALSE, (tctarg_prop_id_t)finalize_prop_);
sym->mark_referenced();
global_symtab_->add_entry(sym);
/* add an "object call" property for anonymous functions */
sym = new CTcSymProp(objcall_name, sizeof(objcall_name) - 1,
FALSE, (tctarg_prop_id_t)objcall_prop_);
sym->mark_referenced();
global_symtab_->add_entry(sym);
/* add a "grammarInfo" property for grammar production match objects */
graminfo_prop_ = new CTcSymProp(graminfo_name, sizeof(graminfo_name) - 1,
FALSE,
(tctarg_prop_id_t)graminfo_prop_id);
graminfo_prop_->mark_referenced();
global_symtab_->add_entry(graminfo_prop_);
/* add a "miscVocab" property for miscellaneous vocabulary words */
sym = new CTcSymProp(miscvocab_name, sizeof(miscvocab_name) - 1,
FALSE, miscvocab_prop_);
sym->mark_referenced();
global_symtab_->add_entry(sym);
/* add a "lexicalParent" property for a nested object's parent */
lexical_parent_sym_ = new CTcSymProp(lexical_parent_name,
sizeof(lexical_parent_name) - 1,
FALSE,
(tctarg_prop_id_t)lexpar_prop_id);
lexical_parent_sym_->mark_referenced();
global_symtab_->add_entry(lexical_parent_sym_);
/* add a "sourceTextOrder" property */
src_order_sym_ = new CTcSymProp(src_order_name,
sizeof(src_order_name) - 1,
FALSE,
(tctarg_prop_id_t)src_order_prop_id);
src_order_sym_->mark_referenced();
global_symtab_->add_entry(src_order_sym_);
/* start the sourceTextOrder index at 1 */
src_order_idx_ = 1;
/* add a "sourceTextGroup" property */
src_group_sym_ = new CTcSymProp(src_group_name,
sizeof(src_group_name) - 1,
FALSE,
(tctarg_prop_id_t)src_group_prop_id);
src_group_sym_->mark_referenced();
global_symtab_->add_entry(src_group_sym_);
/* we haven't created the sourceTextGroup referral object yet */
src_group_id_ = TCTARG_INVALID_OBJ;
/* add a "sourceTextGroupName" property */
src_group_mod_sym_ = new CTcSymProp(
src_group_mod_name, sizeof(src_group_mod_name) - 1, FALSE,
(tctarg_prop_id_t)src_group_mod_prop_id);
src_group_mod_sym_->mark_referenced();
global_symtab_->add_entry(src_group_mod_sym_);
/* add a "sourceTextGroupOrder" property */
src_group_seq_sym_ = new CTcSymProp(
src_group_seq_name, sizeof(src_group_seq_name) - 1, FALSE,
(tctarg_prop_id_t)src_group_seq_prop_id);
src_group_seq_sym_->mark_referenced();
global_symtab_->add_entry(src_group_seq_sym_);
}
/*
* destroy the parser
*/
CTcParser::~CTcParser()
{
/*
* Note that we don't have to delete certain objects, because we
* allocated them out of the parser memory pool and will be
* automatically deleted when the pool is deleted. For example, we
* don't have to delete any symbol tables, including the global
* symbol table.
*/
/* delete the module name, if it's known */
lib_free_str(module_name_);
/* delete the object file symbol fixup list, if present */
if (obj_sym_list_ != 0)
t3free(obj_sym_list_);
/* delete the object file dictionary fixup list, if present */
if (obj_dict_list_ != 0)
t3free(obj_dict_list_);
/* delete the context variable property list */
if (ctx_var_props_ != 0)
t3free(ctx_var_props_);
/* delete the export list */
while (exp_head_ != 0)
{
CTcPrsExport *nxt;
/* remember the next entry, since we're deleting our pointer to it */
nxt = exp_head_->get_next();
/* delete this entry */
delete exp_head_;
/* move on to the next */
exp_head_ = nxt;
}
/* delete the '+' stack */
t3free(plus_stack_);
}
/* ------------------------------------------------------------------------ */
/*
* Set the module information
*/
void CTcParser::set_module_info(const char *name, int seqno)
{
/* if we have a name stored already, delete the old one */
lib_free_str(module_name_);
/* store the new name and sequence number */
module_name_ = lib_copy_str(name);
module_seqno_ = seqno;
}
/*
* Change the #pragma C mode. On changing this mode, we'll change the
* assignment operator and equality operator tokens. If 'mode' is true,
* we're in C mode; otherwise, we're in traditional TADS mode.
*
* #pragma C+: assignment is '=', equality is '=='
*. #pragma C-: assignment is ':=', equality is '='.
*/
void CTcParser::set_pragma_c(int mode)
{
/* set the assignment operator */
S_op_asi.set_asi_op(mode ? TOKT_EQ : TOKT_ASI);
/* set the equality comparison operator */
S_op_eq.set_eq_op(mode ? TOKT_EQEQ : TOKT_EQ);
}
/*
* Parse an expression. This parses a top-level comma expression.
*/
CTcPrsNode *CTcParser::parse_expr()
{
/* parse a comma expression */
return S_op_comma.parse();
}
/*
* Parse a condition expression. Warns if the outermost operator is a
* simple assignment.
*/
CTcPrsNode *CTcParser::parse_cond_expr()
{
CTcPrsNode *cond;
/* parse the expression */
cond = parse_expr();
/*
* if the outermost operator is a simple assignment, display an
* error
*/
if (cond != 0 && cond->is_simple_asi() && !G_prs->get_syntax_only())
G_tok->log_warning(TCERR_ASI_IN_COND);
/* return the result */
return cond;
}
/*
* Parse an assignment expression.
*/
CTcPrsNode *CTcParser::parse_asi_expr()
{
/* parse an assignment expression */
return S_op_asi.parse();
}
/*
* Parse an expression or a double-quoted string expression
*/
CTcPrsNode *CTcParser::parse_expr_or_dstr(int allow_comma_expr)
{
/*
* parse the appropriate kind of expression - if a comma expression is
* allowed, parse that, otherwise parse an assignment expression (as
* that's the next thing down the hierarchy from the comma operator)
*/
return (allow_comma_expr ? S_op_comma.parse() : S_op_asi.parse());
}
/*
* Parse a required semicolon
*/
int CTcParser::parse_req_sem()
{
const char eof_str[] = "<end of file>";
/* check to see if we found the semicolon */
if (G_tok->cur() == TOKT_SEM)
{
/* success - skip the semicolon and tell the caller to proceed */
G_tok->next();
return 0;
}
/*
* check what we have; the type of error we want to log depends on
* what we find next
*/
switch(G_tok->cur())
{
case TOKT_RPAR:
/* log the extra ')' error */
G_tok->log_error(TCERR_EXTRA_RPAR);
/*
* we're probably in an expression that ended before the user
* thought it should; skip the extraneous material up to the
* next semicolon
*/
return skip_to_sem();
case TOKT_RBRACK:
/* log the error */
G_tok->log_error(TCERR_EXTRA_RBRACK);
/* skip up to the next semicolon */
return skip_to_sem();
case TOKT_EOF:
/*
* missing semicolon at end of file - log the missing-semicolon
* error and tell the caller not to proceed, since there's
* nothing left to parse
*/
G_tok->log_error(TCERR_EXPECTED_SEMI,
(int)sizeof(eof_str)-1, eof_str);
return 1;
default:
/*
* the source is probably just missing a semicolon; log the
* error, and tell the caller to proceed from the current
* position
*/
G_tok->log_error_curtok(TCERR_EXPECTED_SEMI);
return 0;
}
}
/*
* Skip to the next semicolon
*/
int CTcParser::skip_to_sem()
{
/* keep going until we find a semicolon or some other reason to stop */
for (;;)
{
/* see what we have next */
switch(G_tok->cur())
{
case TOKT_EOF:
/* end of file - tell the caller not to proceed */
return 1;
case TOKT_SEM:
/*
* it's the semicolon at last - skip it and tell the caller
* to proceed
*/
G_tok->next();
return 0;
case TOKT_LBRACE:
case TOKT_RBRACE:
/*
* Don't skip past braces - the caller probably simply left
* out a semicolon at the end of a statement, and we've now
* reached the next block start or end. Stop here and tell
* the caller to proceed.
*/
return 0;
default:
/* skip anything else */
G_tok->next();
break;
}
}
}
/*
* Create a symbol node
*/
CTcPrsNode *CTcParser::create_sym_node(const textchar_t *sym, size_t sym_len)
{
CTcSymbol *entry;
CTcPrsSymtab *symtab;
/*
* First, look up the symbol in local scope. Local scope symbols
* can always be resolved during parsing, because the language
* requires that local scope items be declared before their first
* use.
*/
entry = local_symtab_->find(sym, sym_len, &symtab);
/* if we found it in local scope, return a resolved symbol node */
if (entry != 0 && symtab != global_symtab_)
return new CTPNSymResolved(entry);
/* if there's a debugger local scope, look it up there */
if (debug_symtab_ != 0)
{
tcprsdbg_sym_info info;
/* look it up in the debug symbol table */
if (debug_symtab_->find_symbol(sym, sym_len, &info))
{
/* found it - return a debugger local variable */
return new CTPNSymDebugLocal(&info);
}
}
/*
* We didn't find it in local scope, so the symbol cannot be resolved
* until code generation - return an unresolved symbol node. Note a
* possible implicit self-reference, since this could be a property of
* 'self'.
*/
set_self_referenced(TRUE);
return new CTPNSym(sym, sym_len);
}
/*
* Find or add a dictionary symbol
*/
CTcDictEntry *CTcParser::declare_dict(const char *txt, size_t len)
{
CTcSymObj *sym;
CTcDictEntry *entry = 0;
/* look up the symbol */
sym = (CTcSymObj *)global_symtab_->find(txt, len);
/* if it's not defined, add it; otherwise, make sure it's correct */
if (sym == 0)
{
/* it's not yet defined - define it as a dictionary */
sym = new CTcSymObj(G_tok->getcur()->get_text(),
G_tok->getcur()->get_text_len(),
FALSE, G_cg->new_obj_id(), FALSE,
TC_META_DICT, 0);
/* add it to the global symbol table */
global_symtab_->add_entry(sym);
/* create a new dictionary entry */
entry = create_dict_entry(sym);
}
else
{
/* make sure it's an object of metaclass 'dictionary' */
if (sym->get_type() != TC_SYM_OBJ)
{
/* log an error */
G_tok->log_error_curtok(TCERR_REDEF_AS_OBJ);
/* forget the symbol - it's not even an object */
sym = 0;
}
else if (sym->get_metaclass() != TC_META_DICT)
{
/* it's an object, but not a dictionary - log an error */
G_tok->log_error_curtok(TCERR_REDEF_AS_DICT);
/* forget the symbol */
sym = 0;
}
/* find it in our dictionary list */
entry = get_dict_entry(sym);
/*
* if we didn't find the entry, we must have loaded the symbol
* from a symbol file - add the dictionary list entry now
*/
if (entry == 0)
entry = create_dict_entry(sym);
}
/* return the new dictionary object */
return entry;
}
/*
* Find or add a grammar production symbol. Returns the master
* CTcGramProdEntry object for the grammar production.
*/
CTcGramProdEntry *CTcParser::declare_gramprod(const char *txt, size_t len)
{
CTcSymObj *sym;
CTcGramProdEntry *entry;
/* find or define the grammar production object symbol */
sym = find_or_def_gramprod(txt, len, &entry);
/* return the entry */
return entry;
}
/*
* Find or add a grammar production symbol.
*/
CTcSymObj *CTcParser::find_or_def_gramprod(const char *txt, size_t len,
CTcGramProdEntry **entryp)
{
CTcSymObj *sym;
CTcGramProdEntry *entry;
/* look up the symbol */
sym = (CTcSymObj *)global_symtab_->find(txt, len);
/* if it's not defined, add it; otherwise, make sure it's correct */
if (sym == 0)
{
/* it's not yet defined - define it as a grammar production */
sym = new CTcSymObj(G_tok->getcur()->get_text(),
G_tok->getcur()->get_text_len(),
FALSE, G_cg->new_obj_id(), FALSE,
TC_META_GRAMPROD, 0);
/* add it to the global symbol table */
global_symtab_->add_entry(sym);
/* create a new production list entry */
entry = create_gramprod_entry(sym);
}
else
{
/* make sure it's an object of metaclass 'grammar production' */
if (sym->get_type() != TC_SYM_OBJ)
{
/* log an error */
G_tok->log_error_curtok(TCERR_REDEF_AS_OBJ);
/* forget the symbol - it's not even an object */
sym = 0;
}
else if (sym->get_metaclass() != TC_META_GRAMPROD)
{
/* it's an object, but not a production - log an error */
G_tok->log_error_curtok(TCERR_REDEF_AS_GRAMPROD);
/* forget the symbol */
sym = 0;
}
/*
* If we found the symbol, make sure it's not marked as external,
* since we're actually defining a rule for this production. If
* the production is exported from any other modules, we'll share
* the production object with the other modules.
*/
if (sym != 0)
sym->set_extern(FALSE);
/* get the existing production object, if any */
entry = get_gramprod_entry(sym);
/*
* if we didn't find the entry, we must have loaded the symbol
* from a symbol file - add the entry now
*/
if (entry == 0)
entry = create_gramprod_entry(sym);
}
/*
* if the caller is interested in knowing the associated grammar rule
* list entry, return it
*/
if (entryp != 0)
*entryp = entry;
/* return the new symbol */
return sym;
}
/*
* Add a symbol loaded from an object file
*/
void CTcParser::add_sym_from_obj_file(uint idx, class CTcSymbol *sym)
{
/*
* add the entry to the object file index list - adjust from the
* 1-based index used in the file to an array index
*/
obj_sym_list_[idx - 1] = sym;
}
/*
* Get an object file symbol, ensuring that it's an object symbol
*/
CTcSymObj *CTcParser::get_objfile_objsym(uint idx)
{
CTcSymObj *sym;
/* get the object based on the index */
sym = (CTcSymObj *)get_objfile_sym(idx);
/* make sure it's an object - if it isn't, return null */
if (sym == 0 || sym->get_type() != TC_SYM_OBJ)
return 0;
/* it checks out - return it */
return sym;
}
/*
* Add a dictionary symbol loaded from an object file
*/
void CTcParser::add_dict_from_obj_file(CTcSymObj *sym)
{
CTcDictEntry *entry;
/* get the current entry, if any */
entry = get_dict_entry(sym);
/* if there's no current entry, create a new one */
if (entry == 0)
{
/* create the entry */
entry = create_dict_entry(sym);
}
/* add the entry to the object file index list */
obj_dict_list_[obj_file_dict_idx_++] = entry;
}
/*
* create a new dictionary list entry
*/
CTcDictEntry *CTcParser::create_dict_entry(CTcSymObj *sym)
{
CTcDictEntry *entry;
/* allocate the new entry */
entry = new (G_prsmem) CTcDictEntry(sym);
/* link the new entry into our list */
if (dict_tail_ != 0)
dict_tail_->set_next(entry);
else
dict_head_ = entry;
dict_tail_ = entry;
/*
* set the metaclass-specific extra data in the object symbol to
* point to the dictionary list entry
*/
sym->set_meta_extra(entry);
/* return the new entry */
return entry;
}
/*
* find a dictionary list entry for a given object symbol
*/
CTcDictEntry *CTcParser::get_dict_entry(CTcSymObj *obj)
{
/* the dictionary list entry is the metaclass-specific data pointer */
return (obj == 0 ? 0 : (CTcDictEntry *)obj->get_meta_extra());
}
/*
* Create a grammar production list entry. This creates a
* CTcGramProdEntry object associated with the given grammar production
* symbol, and links the new entry into the master list of grammar
* production entries.
*/
CTcGramProdEntry *CTcParser::create_gramprod_entry(CTcSymObj *sym)
{
CTcGramProdEntry *entry;
/* allocate the new entry */
entry = new (G_prsmem) CTcGramProdEntry(sym);
/* link the new entry into our list of anonymous match entries */
if (gramprod_tail_ != 0)
gramprod_tail_->set_next(entry);
else
gramprod_head_ = entry;
gramprod_tail_ = entry;
/*
* set the metaclass-specific data in the object symbol to point to
* the grammar production list entry
*/
if (sym != 0)
sym->set_meta_extra(entry);
/* return the new entry */
return entry;
}
/*
* find a grammar entry for a given (GrammarProd) object symbol
*/
CTcGramProdEntry *CTcParser::get_gramprod_entry(CTcSymObj *obj)
{
/* the grammar entry is the metaclass-specific data pointer */
return (obj == 0 ? 0 : (CTcGramProdEntry *)obj->get_meta_extra());
}
/*
* Add a nested top-level statement to our list
*/
void CTcParser::add_nested_stm(CTPNStmTop *stm)
{
/* link it into our list */
if (nested_stm_tail_ != 0)
nested_stm_tail_->set_next_stm_top(stm);
else
nested_stm_head_ = stm;
nested_stm_tail_ = stm;
}
/*
* Add an anonymous object to our list
*/
void CTcParser::add_anon_obj(CTcSymObj *sym)
{
/* link it into our list */
if (anon_obj_tail_ != 0)
anon_obj_tail_->nxt_ = sym;
else
anon_obj_head_ = sym;
anon_obj_tail_ = sym;
/* it's the last one */
sym->nxt_ = 0;
/* mark the symbol as anonymous */
sym->set_anon(TRUE);
}
/*
* Add a non-symbolic object to our list
*/
void CTcParser::add_nonsym_obj(tctarg_obj_id_t id)
{
tcprs_nonsym_obj *obj;
/* allocate a link structure */
obj = new (G_prsmem) tcprs_nonsym_obj(id);
/* link it into our list */
if (nonsym_obj_tail_ != 0)
nonsym_obj_tail_->nxt_ = obj;
else
nonsym_obj_head_ = obj;
nonsym_obj_tail_ = obj;
}
/*
* Basic routine to read a length-prefixed symbol. Uses the given
* temporary buffer, then stores the text in tokenizer memory (which
* remains valid and available throughout compilation). If the length
* exceeds the temporary buffer length, we'll flag the given error and
* return null. The length return pointer can be null if the caller wants
* the results null-terminated rather than returned with a counted length.
* If the length pointer is given, the result will not be null-terminated.
*
*/
const char *CTcParser::read_len_prefix_str
(CVmFile *fp, char *tmp_buf, size_t tmp_buf_len, size_t *ret_len,
int err_if_too_long)
{
size_t read_len;
size_t alloc_len;
/* read the length to read from the file */
read_len = (size_t)fp->read_uint2();
/* if we need null termination, add a byte to the allocation length */
alloc_len = read_len + (ret_len == 0 ? 1 : 0);
/* if it won't fit in the temporary buffer, it's an error */
if (alloc_len > tmp_buf_len)
{
/* log the error and return failure */
G_tcmain->log_error(0, 0, TC_SEV_ERROR, err_if_too_long);
return 0;
}
/* read the bytes into the temporary buffer */
fp->read_bytes(tmp_buf, read_len);
/* add null termination if required, or set the return length if not */
if (ret_len == 0)
tmp_buf[read_len] = '\0';
else
*ret_len = read_len;
/* store the result in the tokenizer's text list and return the result */
return G_tok->store_source(tmp_buf, alloc_len);
}
/*
* Read a length prefixed string into a given buffer. Returns zero on
* success, non-zero on failure.
*/
int CTcParser::read_len_prefix_str(CVmFile *fp, char *buf, size_t buf_len,
int err_if_too_long)
{
size_t read_len;
size_t alloc_len;
/* read the length to read from the file */
read_len = (size_t)fp->read_uint2();
/* add a byte for null termination */
alloc_len = read_len + 1;
/* if it won't fit in the temporary buffer, it's an error */
if (alloc_len > buf_len)
{
/* log the error and return failure */
G_tcmain->log_error(0, 0, TC_SEV_ERROR, err_if_too_long);
return 1;
}
/* read the bytes into the caller's buffer */
fp->read_bytes(buf, read_len);
/* add null termination */
buf[read_len] = '\0';
/* success */
return 0;
}
/* ------------------------------------------------------------------------ */
/*
* Constant Value
*/
/*
* set a string value
*/
void CTcConstVal::set_sstr(const char *val, size_t len)
{
/* store the type */
typ_ = TC_CVT_SSTR;
/* store a pointer to the string */
val_.strval_.strval_ = val;
val_.strval_.strval_len_ = len;
/* for image file layout purposes, record the length of this string */
G_cg->note_str(len);
}
/*
* set a list value
*/
void CTcConstVal::set_list(CTPNList *lst)
{
/* set the type */
typ_ = TC_CVT_LIST;
/* remember the list */
val_.listval_ = lst;
/* for image file layout purposes, record the length of this list */
G_cg->note_list(lst->get_count());
}
/*
* Convert a value to a string
*/
const char *CTcConstVal::cvt_to_str(char *buf, size_t bufl,
size_t *result_len)
{
/* check my type */
switch(typ_)
{
case TC_CVT_NIL:
/* the result is "nil" */
if (bufl < 4)
return 0;
strcpy(buf, "nil");
*result_len = 3;
return buf;
case TC_CVT_TRUE:
/* the result is "true" */
if (bufl < 5)
return 0;
strcpy(buf, "true");
*result_len = 4;
return buf;
case TC_CVT_SSTR:
/* it's already a string */
*result_len = get_val_str_len();
return get_val_str();
case TC_CVT_INT:
/* convert our signed integer value */
if (bufl < 12)
return 0;
sprintf(buf, "%ld", get_val_int());
*result_len = strlen(buf);
return buf;
case TC_CVT_FLOAT:
/* we store these as strings */
*result_len = get_val_float_len();
return get_val_float();
default:
/* can't convert other types */
return 0;
}
}
/*
* Compare for equality to another constant value
*/
int CTcConstVal::is_equal_to(const CTcConstVal *val) const
{
CTPNListEle *ele1;
CTPNListEle *ele2;
/*
* if the types are not equal, the values are not equal; otherwise,
* check the various types
*/
if (typ_ != val->get_type())
{
/* the types aren't equal, so the values are not equal */
return FALSE;
}
/* the types are the same; do the comparison based on the type */
switch(typ_)
{
case TC_CVT_UNK:
/* unknown type; unknown values can never be equal */
return FALSE;
case TC_CVT_TRUE:
case TC_CVT_NIL:
/*
* nil==nil and true==true; since we know the types are the
* same, the values are the same
*/
return TRUE;
case TC_CVT_INT:
/* compare the integers */
return (get_val_int() == val->get_val_int());
case TC_CVT_SSTR:
/* compare the strings */
return (get_val_str_len() == val->get_val_str_len()
&& memcmp(get_val_str(), val->get_val_str(),
get_val_str_len()) == 0);
case TC_CVT_LIST:
/*
* if the lists don't have the same number of elements, they're
* not equal
*/
if (get_val_list()->get_count() != val->get_val_list()->get_count())
return FALSE;
/*
* compare each element of each list; if they're all the same,
* the values are the same
*/
ele1 = get_val_list()->get_head();
ele2 = val->get_val_list()->get_head();
for ( ; ele1 != 0 && ele2 != 0 ;
ele1 = ele1->get_next(), ele2 = ele2->get_next())
{
/* if these elements aren't equal, the lists aren't equal */
if (!ele1->get_expr()->get_const_val()
->is_equal_to(ele2->get_expr()->get_const_val()))
return FALSE;
}
/* we didn't find any differences, so the lists are equal */
return TRUE;
case TC_CVT_OBJ:
/* if the object values are the same, the values match */
return (get_val_obj() == val->get_val_obj());
case TC_CVT_PROP:
/* if the property values are the same, the values match */
return (get_val_prop() == val->get_val_prop());
case TC_CVT_FUNCPTR:
/*
* if both symbols are the same, the values match; otherwise,
* they refer to different functions
*/
return (get_val_funcptr_sym() == val->get_val_funcptr_sym());
default:
/* unknown type; return unequal */
return FALSE;
}
}
/* ------------------------------------------------------------------------ */
/*
* Operator Parsers
*/
/* ------------------------------------------------------------------------ */
/*
* Parse a left-associative binary operator
*/
CTcPrsNode *CTcPrsOpBin::parse() const
{
CTcPrsNode *lhs;
CTcPrsNode *rhs;
/* parse our left side - if that fails, return failure */
if ((lhs = left_->parse()) == 0)
return 0;
/* keep going as long as we find our operator */
for (;;)
{
/* check my operator */
if (G_tok->cur() == get_op_tok())
{
CTcPrsNode *const_tree;
/* skip the matching token */
G_tok->next();
/* parse the right-hand side */
if ((rhs = right_->parse()) == 0)
return 0;
/* try folding our subnodes into a constant value, if possible */
const_tree = eval_constant(lhs, rhs);
/*
* if we couldn't calculate a constant value, build the tree
* normally
*/
if (const_tree == 0)
{
/*
* Build my tree, then proceed to parse any additional
* occurrences of our operator, with the result of
* applying this occurrence of the operator as the
* left-hand side of the new operator.
*/
lhs = build_tree(lhs, rhs);
}
else
{
/* we got a constant value - use it as the result directly */
lhs = const_tree;
}
}
else
{
/*
* it's not my operator - return what we thought might have
* been our left-hand side
*/
return lhs;
}
}
}
/* ------------------------------------------------------------------------ */
/*
* Parse a group of left-associative binary operators at the same
* precedence level
*/
CTcPrsNode *CTcPrsOpBinGroup::parse() const
{
CTcPrsNode *lhs;
/* parse our left side - if that fails, return failure */
if ((lhs = left_->parse()) == 0)
return 0;
/* keep going as long as we find one of our operators */
while (find_and_apply_op(&lhs)) ;
/* return the expression tree */
return lhs;
}
/*
* Find an apply one of our operators to the already-parsed left-hand
* side. Returns true if we found an operator, false if not.
*/
int CTcPrsOpBinGroup::find_and_apply_op(CTcPrsNode **lhs) const
{
const CTcPrsOpBin *const *op;
CTcPrsNode *rhs;
/* check each operator at this precedence level */
for (op = ops_ ; *op != 0 ; ++op)
{
/* check this operator's token */
if (G_tok->cur() == (*op)->get_op_tok())
{
CTcPrsNode *const_tree;
/* skip the operator token */
G_tok->next();
/* parse the right-hand side */
if ((rhs = right_->parse()) == 0)
{
/* error - cancel the entire expression */
*lhs = 0;
return FALSE;
}
/* try folding our subnodes into a constant value */
const_tree = (*op)->eval_constant(*lhs, rhs);
/*
* if we couldn't calculate a constant value, build the tree
* normally
*/
if (const_tree == 0)
{
/*
* build my tree, replacing the original left-hand side
* with the new expression
*/
*lhs = (*op)->build_tree(*lhs, rhs);
}
else
{
/* we got a constant value - use it as the result */
*lhs = const_tree;
}
/*
* Tell the caller to proceed to parse any additional
* occurrences of our operator - this will apply the next
* occurrence of the operator as the left-hand side of the
* new operator.
*/
return TRUE;
}
}
/*
* if we got here, we didn't find an operator - tell the caller that
* we've reached the end of this operator's possible span
*/
return FALSE;
}
/* ------------------------------------------------------------------------ */
/*
* Comparison operator group
*/
CTcPrsNode *CTcPrsOpBinGroupCompare::parse() const
{
CTcPrsNode *lhs;
/* parse our left side - if that fails, return failure */
if ((lhs = left_->parse()) == 0)
return 0;
/* keep going as long as we find one of our operators */
for (;;)
{
CTPNArglist *rhs;
/*
* try one of our regular operators - if we find it, go back for
* another round to see if there's another operator following
* the next expression
*/
if (find_and_apply_op(&lhs))
continue;
/*
* check for the 'is in' operator - 'is' and 'in' aren't
* keywords, so we must check for symbol tokens with the text of
* these context-sensitive keywords
*/
if (G_tok->cur() == TOKT_SYM
&& G_tok->getcur()->text_matches("is", 2))
{
/* we have 'is' - get the next token and check if it's 'in' */
if (G_tok->next() == TOKT_SYM
&& G_tok->getcur()->text_matches("in", 2))
{
/* scan the expression list */
rhs = parse_inlist();
if (rhs == 0)
return 0;
/* build the node */
lhs = new CTPNIsIn(lhs, rhs);
/*
* we've applied the 'is in' operator - go back for
* another operator from the comparison group
*/
continue;
}
else
{
/* it's not 'is in' - throw back the token and keep looking */
G_tok->unget();
}
}
/*
* Check for the 'not in' operator
*/
if (G_tok->cur() == TOKT_SYM
&& G_tok->getcur()->text_matches("not", 3))
{
/* we have 'is' - get the next token and check if it's 'in' */
if (G_tok->next() == TOKT_SYM
&& G_tok->getcur()->text_matches("in", 2))
{
/* scan the expression list */
rhs = parse_inlist();
if (rhs == 0)
return 0;
/* build the node */
lhs = new CTPNNotIn(lhs, rhs);
/*
* we've applied the 'is in' operator - go back for
* another operator from the comparison group
*/
continue;
}
else
{
/* it's not 'is in' - throw back the token and keep looking */
G_tok->unget();
}
}
/* we didn't find any of our operators - we're done */
break;
}
/* return the expression */
return lhs;
}
/*
* parse the list for the right-hand side of an 'is in' or 'not in'
* expression
*/
CTPNArglist *CTcPrsOpBinGroupCompare::parse_inlist() const
{
int argc;
CTPNArg *arg_head;
CTPNArg *arg_tail;
/* skip the second keyword token, and check for an open paren */
if (G_tok->next() == TOKT_LPAR)
{
/* skip the paren */
G_tok->next();
}
else
{
/*
* log an error, and keep going on the assumption that it was
* merely omitted and the rest of the list is well-formed
*/
G_tok->log_error_curtok(TCERR_IN_REQ_LPAR);
}
/* keep going until we find the close paren */
for (argc = 0, arg_head = arg_tail = 0 ;; )
{
CTcPrsNode *expr;
CTPNArg *arg_cur;
/* if this is the close paren, we're done */
if (G_tok->cur() == TOKT_RPAR)
break;
/* parse this expression */
expr = S_op_asi.parse();
if (expr == 0)
return 0;
/* count the argument */
++argc;
/* create a new argument node */
arg_cur = new CTPNArg(expr);
/*
* link the new node at the end of our list (this preserves the
* order of the original list)
*/
if (arg_tail != 0)
arg_tail->set_next_arg(arg_cur);
else
arg_head = arg_cur;
arg_tail = arg_cur;
/* we need to be looking at a comma or right paren */
if (G_tok->cur() == TOKT_RPAR)
{
/* that's the end of the list */
break;
}
else if (G_tok->cur() == TOKT_COMMA)
{
/* skip the comma and parse the next argument */
G_tok->next();
}
else
{
/*
* If we're at the end of the file, there's no point
* proceding, so return failure. If we've reached something
* that looks like a statement separator (semicolon, curly
* brace), also return failure, since the problem is clearly
* a missing right paren. Otherwise, assume that a comma
* was missing and continue as though we have another
* argument.
*/
switch(G_tok->cur())
{
default:
/* log an error */
G_tok->log_error_curtok(TCERR_EXPECTED_IN_COMMA);
/*
* if we're at the end of file, return what we have so
* far; otherwise continue, assuming that they merely
* left out a comma between two argument expressions
*/
if (G_tok->cur() == TOKT_EOF)
return new CTPNArglist(argc, arg_head);
break;
case TOKT_SEM:
case TOKT_LBRACE:
case TOKT_RBRACE:
case TOKT_DSTR_MID:
case TOKT_DSTR_END:
/*
* we're apparently at the end of the statement; flag
* the error as a missing right paren, and return what
* we have so far
*/
G_tok->log_error_curtok(TCERR_EXPECTED_IN_RPAR);
return new CTPNArglist(argc, arg_head);
}
}
}
/* skip the closing paren */
G_tok->next();
/* create and return the argument list descriptor */
return new CTPNArglist(argc, arg_head);
}
/* ------------------------------------------------------------------------ */
/*
* Comma Operator
*/
/*
* try to evaluate a constant expression
*/
CTcPrsNode *CTcPrsOpComma::eval_constant(CTcPrsNode *left,
CTcPrsNode *right) const
{
/*
* if both sides are constants, the result is the constant on the
* right side; we can't simply fold down to a right-side constant if
* the left side is not constant, though, because we must still
* evaluate the left side at run-time for any possible side effects
*/
if (left->is_const() && right->is_const())
{
/* both are constants - simply return the right constant value */
return right;
}
else
{
/*
* one or the other is non-constant, so we can't fold the
* expression - return null to so indicate
*/
return 0;
}
}
/*
* build a subtree for the comma operator
*/
CTcPrsNode *CTcPrsOpComma::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNComma(left, right);
}
/* ------------------------------------------------------------------------ */
/*
* logical OR operator
*/
/*
* try to evaluate a constant expression
*/
CTcPrsNode *CTcPrsOpOr::eval_constant(CTcPrsNode *left,
CTcPrsNode *right) const
{
/* check for constants */
if (left->is_const())
{
CTcPrsNode *ret;
/*
* Check for constants. If the first expression is constant,
* the result will always be either 'true' (if the first
* expression's constant value is true), or the value of the
* second expression (if the first expression's constant value
* is 'nil').
*
* Note that it doesn't matter whether or not the right side is
* a constant. If the left is true, the right will never be
* executed because of the short-circuit logic; if the left is
* nil, the result will always be the result of the right value.
*/
if (left->get_const_val()->get_val_bool())
{
/*
* the left is true, so the result is always true, and the
* right never gets executed
*/
ret = left;
}
else
{
/* the left is nil, so the result is the right value */
ret = right;
}
/* ensure the result is a boolean value */
if (ret->is_const())
{
/* make it a true/nil constant value */
ret->get_const_val()
->set_bool(ret->get_const_val()->get_val_bool());
}
else
{
/* boolean-ize the value at run-time as needed */
ret = new CTPNBoolize(ret);
}
/* return the result */
return ret;
}
else
{
/*
* one or the other is non-constant, so we can't fold the
* expression - return null to so indicate
*/
return 0;
}
}
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpOr::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNOr(left, right);
}
/* ------------------------------------------------------------------------ */
/*
* logical AND operator
*/
/*
* try to evaluate a constant expression
*/
CTcPrsNode *CTcPrsOpAnd::eval_constant(CTcPrsNode *left,
CTcPrsNode *right) const
{
/*
* Check for constants. If the first expression is constant, the
* result will always be either 'nil' (if the first expression's
* constant value is nil), or the value of the second expression (if
* the first expression's constant value is 'true').
*
* Note that it doesn't matter whether or not the right side is a
* constant. If the left is nil, the right will never be executed
* because of the short-circuit logic; if the left is true, the
* result will always be the result of the right value.
*/
if (left->is_const())
{
CTcPrsNode *ret;
/*
* The left value is a constant, so the result is always know.
* If the left value is nil, the result is nil; otherwise, it's
* the right half.
*/
if (left->get_const_val()->get_val_bool())
{
/* the left side is true - the result is the right side */
ret = right;
}
else
{
/*
* The left side is nil - the result is nil, and the right
* side never gets executed.
*/
ret = left;
}
/* ensure the result is a boolean value */
if (ret->is_const())
{
/* make it a true/nil constant value */
ret->get_const_val()
->set_bool(ret->get_const_val()->get_val_bool());
}
else
{
/* boolean-ize the value at run-time as needed */
ret = new CTPNBoolize(ret);
}
/* return the result */
return ret;
}
else
{
/*
* one or the other is non-constant, so we can't fold the
* expression - return null to so indicate
*/
return 0;
}
}
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpAnd::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNAnd(left, right);
}
/* ------------------------------------------------------------------------ */
/*
* Generic Comparison Operator parser base class
*/
/*
* evaluate a constant expression
*/
CTcPrsNode *CTcPrsOpRel::eval_constant(CTcPrsNode *left,
CTcPrsNode *right) const
{
/* check for constants */
if (left->is_const() && right->is_const())
{
tc_constval_type_t typ1, typ2;
int sense;
/* get the types */
typ1 = left->get_const_val()->get_type();
typ2 = right->get_const_val()->get_type();
/* determine what we're comparing */
if (typ1 == TC_CVT_INT && typ2 == TC_CVT_INT)
{
long val1, val2;
/* get the values */
val1 = left->get_const_val()->get_val_int();
val2 = right->get_const_val()->get_val_int();
/* calculate the sense of the integer comparison */
sense = (val1 < val2 ? -1 : val1 == val2 ? 0 : 1);
}
else if (typ1 == TC_CVT_SSTR && typ2 == TC_CVT_SSTR)
{
/* compare the string values */
sense = strcmp(left->get_const_val()->get_val_str(),
right->get_const_val()->get_val_str());
}
else if (typ1 == TC_CVT_FLOAT || typ2 == TC_CVT_FLOAT)
{
/* we can't compare floats at compile time, but it's legal */
return 0;
}
else
{
/* these types are incomparable */
G_tok->log_error(TCERR_CONST_BAD_COMPARE,
G_tok->get_op_text(get_op_tok()));
return 0;
}
/* set the result in the left value */
left->get_const_val()->set_bool(get_bool_val(sense));
/* return the updated left value */
return left;
}
else
{
/*
* one or the other is non-constant, so we can't fold the
* expression - return null to so indicate
*/
return 0;
}
}
/* ------------------------------------------------------------------------ */
/*
* greater-than operator
*/
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpGt::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNGt(left, right);
}
/* ------------------------------------------------------------------------ */
/*
* less-than operator
*/
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpLt::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNLt(left, right);
}
/* ------------------------------------------------------------------------ */
/*
* greater-or-equal operator
*/
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpGe::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNGe(left, right);
}
/* ------------------------------------------------------------------------ */
/*
* less-or-equal operator
*/
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpLe::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNLe(left, right);
}
/* ------------------------------------------------------------------------ */
/*
* General equality/inequality operators base class
*/
/*
* evaluate a constant expression
*/
CTcPrsNode *CTcPrsOpEqComp::eval_constant(CTcPrsNode *left,
CTcPrsNode *right) const
{
int ops_equal;
/* check for constants */
if (left->is_const() && right->is_const())
{
/* both sides are constants - determine if they're equal */
ops_equal = left->get_const_val()
->is_equal_to(right->get_const_val());
/* set the result in the left value */
left->get_const_val()->set_bool(get_bool_val(ops_equal));
/* return the updated left value */
return left;
}
else if (left->is_addr() && right->is_addr())
{
CTcConstVal cval;
int comparable;
/*
* both sides are addresses - if they're both addresses of the
* same subexpression, then the values are comparable as
* compile-time constants
*/
ops_equal = ((CTPNAddr *)left)
->is_addr_eq((CTPNAddr *)right, &comparable);
/* if they're not comparable, we can't fold this as a constant */
if (!comparable)
return 0;
/* generate the appropriate boolean result for the comparison */
cval.set_bool(get_bool_val(ops_equal));
/* return a new constant node with the result */
return new CTPNConst(&cval);
}
else
{
/*
* one or the other is non-constant, so we can't fold the
* expression - return null to so indicate
*/
return 0;
}
}
/* ------------------------------------------------------------------------ */
/*
* equality operator
*/
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpEq::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNEq(left, right);
}
/* ------------------------------------------------------------------------ */
/*
* inequality operator
*/
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpNe::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNNe(left, right);
}
/* ------------------------------------------------------------------------ */
/*
* 'is in' operator
*/
/*
* construct
*/
CTPNIsInBase::CTPNIsInBase(CTcPrsNode *lhs, class CTPNArglist *rhs)
: CTPNBin(lhs, rhs)
{
/* presume we don't have a constant value */
const_true_ = FALSE;
}
/*
* fold constants
*/
CTcPrsNode *CTPNIsInBase::fold_binop()
{
CTPNArglist *lst;
CTPNArg *arg;
CTPNArg *prv;
CTPNArg *nxt;
/* if the left-hand side isn't constant, there's nothing to do */
if (!left_->is_const())
return this;
/* the right side is always an argument list */
lst = (CTPNArglist *)right_;
/* look for the value in the arguments */
for (prv = 0, arg = lst->get_arg_list_head() ; arg != 0 ; arg = nxt)
{
/* remember the next argument, in case we eliminate this one */
nxt = arg->get_next_arg();
/* check to see if this argument is a constant */
if (arg->is_const())
{
/*
* This one's a constant, so check to see if we found the
* left side value. If the left side equals this value,
* note that we found the value.
*/
if (left_->get_const_val()->is_equal_to(arg->get_const_val()))
{
/*
* The values are equal, so the result of the expression
* is definitely 'true'.
*/
const_true_ = TRUE;
/*
* Because the 'is in' operator only evaluates operands
* from the 'in' list until it finds one that matches,
* any remaining operands will simply never be
* evaluated. We can thus discard the rest of the
* argument list.
*/
nxt = 0;
}
/*
* We now know whether the left side equals this constant
* list element. This is never going to change because both
* values are constant, so there's no point in making this
* same comparison over and over again at run-time. We can
* thus eliminate this argument from the list.
*/
lst->set_argc(lst->get_argc() - 1);
if (prv == 0)
lst->set_arg_list_head(nxt);
else
prv->set_next_arg(nxt);
}
}
/*
* If the argument list is now completely empty, the result of the
* expression is a constant.
*/
if (lst->get_arg_list_head() == 0)
{
/* set the left operand's value to our result */
left_->get_const_val()->set_bool(const_true_);
/* return the constant value in place of the entire expression */
return left_;
}
/* we're not a constant, to return myself unchanged */
return this;
}
/* ------------------------------------------------------------------------ */
/*
* 'not in' operator
*/
/*
* construct
*/
CTPNNotInBase::CTPNNotInBase(CTcPrsNode *lhs, class CTPNArglist *rhs)
: CTPNBin(lhs, rhs)
{
/* presume we don't have a constant value */
const_false_ = FALSE;
}
/*
* fold constants for binary operator
*/
CTcPrsNode *CTPNNotInBase::fold_binop()
{
CTPNArglist *lst;
CTPNArg *arg;
CTPNArg *prv;
CTPNArg *nxt;
/* if the left-hand side isn't constant, there's nothing to do */
if (!left_->is_const())
return this;
/* the right side is always an argument list */
lst = (CTPNArglist *)right_;
/* look for the value in the arguments */
for (prv = 0, arg = lst->get_arg_list_head() ; arg != 0 ; arg = nxt)
{
/* remember the next argument, in case we eliminate this one */
nxt = arg->get_next_arg();
/* check to see if this argument is a constant */
if (arg->is_const())
{
/*
* This one's a constant, so check to see if we found the
* left side value. If the left side equals this value,
* note that we found the value.
*/
if (left_->get_const_val()->is_equal_to(arg->get_const_val()))
{
/*
* The values are equal, so the result of the expression
* is definitely 'nil'.
*/
const_false_ = TRUE;
/*
* Because the 'not in' operator only evaluates operands
* from the 'in' list until it finds one that matches,
* any remaining operands will simply never be
* evaluated. We can thus discard the rest of the
* argument list.
*/
nxt = 0;
}
/*
* We now know whether the left side equals this constant
* list element. This is never going to change because both
* values are constant, so there's no point in making this
* same comparison over and over again at run-time. We can
* thus eliminate this argument from the list.
*/
lst->set_argc(lst->get_argc() - 1);
if (prv == 0)
lst->set_arg_list_head(nxt);
else
prv->set_next_arg(nxt);
}
}
/*
* If the argument list is now completely empty, the result of the
* expression is a constant.
*/
if (lst->get_arg_list_head() == 0)
{
/* set the left operand's value to our result */
left_->get_const_val()->set_bool(!const_false_);
/* return the constant value in place of the entire expression */
return left_;
}
/* we're not a constant, to return myself unchanged */
return this;
}
/* ------------------------------------------------------------------------ */
/*
* General arithmetic operator base class
*/
/*
* evaluate constant value
*/
CTcPrsNode *CTcPrsOpArith::eval_constant(CTcPrsNode *left,
CTcPrsNode *right) const
{
/* check for constants */
if (left->is_const() && right->is_const())
{
/* require that both values are integers or floats */
if (left->get_const_val()->get_type() == TC_CVT_FLOAT
|| right->get_const_val()->get_type() == TC_CVT_FLOAT)
{
/* can't do it at compile time, but it's legal */
return 0;
}
else if (left->get_const_val()->get_type() != TC_CVT_INT
|| right->get_const_val()->get_type() != TC_CVT_INT)
{
/* incompatible types - log an error */
G_tok->log_error(TCERR_CONST_BINARY_REQ_NUM,
G_tok->get_op_text(get_op_tok()));
return 0;
}
else
{
long result;
/* calculate the result */
result = calc_result(left->get_const_val()->get_val_int(),
right->get_const_val()->get_val_int());
/* assign the result back to the left operand */
left->get_const_val()->set_int(result);
}
/* return the updated left value */
return left;
}
else
{
/*
* one or the other is non-constant, so we can't fold the
* expression - return null to so indicate
*/
return 0;
}
}
/* ------------------------------------------------------------------------ */
/*
* bitwise OR operator
*/
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpBOr::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNBOr(left, right);
}
/* ------------------------------------------------------------------------ */
/*
* bitwise AND operator
*/
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpBAnd::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNBAnd(left, right);
}
/* ------------------------------------------------------------------------ */
/*
* bitwise XOR operator
*/
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpBXor::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNBXor(left, right);
}
/* ------------------------------------------------------------------------ */
/*
* shift left operator
*/
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpShl::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNShl(left, right);
}
/* ------------------------------------------------------------------------ */
/*
* shift right operator
*/
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpShr::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNShr(left, right);
}
/* ------------------------------------------------------------------------ */
/*
* multiplication operator
*/
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpMul::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNMul(left, right);
}
/* ------------------------------------------------------------------------ */
/*
* division operator
*/
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpDiv::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNDiv(left, right);
}
/*
* evaluate constant result
*/
long CTcPrsOpDiv::calc_result(long a, long b) const
{
/* check for divide-by-zero */
if (b == 0)
{
/* log a divide-by-zero error */
G_tok->log_error(TCERR_CONST_DIV_ZERO);
/* the result isn't really meaningful, but return something anyway */
return 1;
}
else
{
/* return the result */
return a / b;
}
}
/* ------------------------------------------------------------------------ */
/*
* modulo operator
*/
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpMod::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNMod(left, right);
}
/*
* evaluate constant result
*/
long CTcPrsOpMod::calc_result(long a, long b) const
{
/* check for divide-by-zero */
if (b == 0)
{
/* log a divide-by-zero error */
G_tok->log_error(TCERR_CONST_DIV_ZERO);
/* the result isn't really meaningful, but return something anyway */
return 1;
}
else
{
/* return the result */
return a % b;
}
}
/* ------------------------------------------------------------------------ */
/*
* subtraction operator
*/
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpSub::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNSub(left, right);
}
/*
* evaluate a constant value
*/
CTcPrsNode *CTcPrsOpSub::eval_constant(CTcPrsNode *left,
CTcPrsNode *right) const
{
if (left->is_const() && right->is_const())
{
tc_constval_type_t typ1, typ2;
/* get the types */
typ1 = left->get_const_val()->get_type();
typ2 = right->get_const_val()->get_type();
/* check our types */
if (typ1 == TC_CVT_INT && typ2 == TC_CVT_INT)
{
/* calculate the integer sum */
left->get_const_val()
->set_int(left->get_const_val()->get_val_int()
- right->get_const_val()->get_val_int());
}
else if (typ1 == TC_CVT_FLOAT || typ2 == TC_CVT_FLOAT)
{
/* can't fold float constants at compile time */
return 0;
}
else if (typ1 == TC_CVT_LIST)
{
CTPNList *lst;
/* get the original list */
lst = left->get_const_val()->get_val_list();
/*
* if the right side is a list, remove each element of that
* list from the list on the left; otherwise, remove the
* value on the right from the list on the left
*/
if (typ2 == TC_CVT_LIST)
{
/* remove each element of the rhs list from the lhs list */
CTPNListEle *ele;
/* scan the list, adding each element */
for (ele = right->get_const_val()
->get_val_list()->get_head() ;
ele != 0 ; ele = ele->get_next())
{
/* add this element's underlying expression value */
lst->remove_element(ele->get_expr()->get_const_val());
}
}
else
{
/* remove the rhs value from the lhs list */
lst->remove_element(right->get_const_val());
}
}
else
{
/* these types are incompatible - log an error */
G_tok->log_error(TCERR_CONST_BINMINUS_INCOMPAT);
return 0;
}
/* return the updated left side */
return left;
}
else
{
/* they're not constant - we can't generate a constant result */
return 0;
}
}
/* ------------------------------------------------------------------------ */
/*
* addition operator
*/
/*
* evaluate constant value
*/
CTcPrsNode *CTcPrsOpAdd::eval_constant(CTcPrsNode *left,
CTcPrsNode *right) const
{
/* check for constants */
if (left->is_const() && right->is_const())
{
tc_constval_type_t typ1, typ2;
/* get the types */
typ1 = left->get_const_val()->get_type();
typ2 = right->get_const_val()->get_type();
/* check our types */
if (typ1 == TC_CVT_INT && typ2 == TC_CVT_INT)
{
/* calculate the integer sum */
left->get_const_val()
->set_int(left->get_const_val()->get_val_int()
+ right->get_const_val()->get_val_int());
}
else if (typ1 == TC_CVT_FLOAT || typ2 == TC_CVT_FLOAT)
{
/* can't fold float constants at compile time */
return 0;
}
else if (typ1 == TC_CVT_LIST)
{
CTPNList *lst;
/* get the original list */
lst = left->get_const_val()->get_val_list();
/*
* if the right side is also a list, concatenate it onto the
* left list; otherwise, just add the right side as a new
* element to the existing list
*/
if (typ2 == TC_CVT_LIST)
{
CTPNListEle *ele;
/* scan the list, adding each element */
for (ele = right->get_const_val()
->get_val_list()->get_head() ;
ele != 0 ; ele = ele->get_next())
{
/* add this element's underlying expression value */
lst->add_element(ele->get_expr());
}
}
else
{
/* add a new list element for the right side */
lst->add_element(right);
}
/*
* this list is longer than the original(s); tell the parser
* about it in case it's the longest list yet
*/
G_cg->note_list(lst->get_count());
}
else if (typ1 == TC_CVT_SSTR || typ2 == TC_CVT_SSTR)
{
char buf1[128];
char buf2[128];
const char *str1, *str2;
size_t len1, len2;
char *new_str;
/* if the second value is a list, we can't make a constant */
if (typ2 == TC_CVT_LIST)
return 0;
/* convert both values to strings if they're not already */
str1 = left->get_const_val()
->cvt_to_str(buf1, sizeof(buf1), &len1);
str2 = right->get_const_val()
->cvt_to_str(buf2, sizeof(buf2), &len2);
/*
* if we couldn't convert one or the other, leave the result
* non-constant
*/
if (str1 == 0 || str2 == 0)
return 0;
/*
* allocate space in the node pool for the concatenation of
* the two strings - if that fails, don't bother with the
* concatenation
*/
new_str = (char *)G_prsmem->alloc(len1 + len2 + 1);
if (new_str == 0)
return 0;
/* copy the two string values into the new space */
memcpy(new_str, str1, len1);
memcpy(new_str + len1, str2, len2);
new_str[len1 + len2] = '\0';
/* set the new value in the left node */
left->get_const_val()->set_sstr(new_str, len1 + len2);
}
else
{
/* these types are incompatible - log an error */
G_tok->log_error(TCERR_CONST_BINPLUS_INCOMPAT);
return 0;
}
/* return the updated left value */
return left;
}
else
{
/* the values aren't constant, so the result isn't constant */
return 0;
}
}
/*
* build the subtree
*/
CTcPrsNode *CTcPrsOpAdd::build_tree(CTcPrsNode *left,
CTcPrsNode *right) const
{
return new CTPNAdd(left, right);
}
/* ------------------------------------------------------------------------ */
/*
* Assignment Operator Group
*/
/*
* parse an assignment expression
*/
CTcPrsNode *CTcPrsOpAsi::parse() const
{
CTcPrsNode *lhs;
CTcPrsNode *rhs;
tc_toktyp_t curtyp;
/* start by parsing a conditional subexpression */
lhs = S_op_if.parse();
if (lhs == 0)
return 0;
/* get the next operator */
curtyp = G_tok->cur();
/* check to see if it's an assignment operator of some kind */
switch(curtyp)
{
case TOKT_PLUSEQ:
case TOKT_MINEQ:
case TOKT_TIMESEQ:
case TOKT_DIVEQ:
case TOKT_MODEQ:
case TOKT_ANDEQ:
case TOKT_OREQ:
case TOKT_XOREQ:
case TOKT_SHLEQ:
case TOKT_SHREQ:
/* it's an assignment operator - process it */
break;
default:
/* check against the current simple-assignment operator */
if (curtyp == asi_op_)
{
/* it's an assignment operator - process it */
break;
}
else
{
/*
* it's not an assignment - return the original
* subexpression with no further elaboration
*/
return lhs;
}
}
/* check for a valid lvalue */
if (!lhs->check_lvalue())
{
/* log an error but continue parsing */
G_tok->log_error(TCERR_INVALID_LVALUE,
G_tok->get_op_text(G_tok->cur()));
}
/* skip the assignment operator */
G_tok->next();
/*
* Recursively parse an assignment subexpression. Do this
* recursively rather than iteratively, because assignment operators
* group right-to-left. By recursively parsing an assignment, our
* right-hand side will contain all remaining assignment expressions
* incorporated into it.
*/
rhs = parse();
if (rhs == 0)
return 0;
/* build and return the result based on the operator type */
switch(curtyp)
{
case TOKT_PLUSEQ:
lhs = new CTPNAddAsi(lhs, rhs);
break;
case TOKT_MINEQ:
lhs = new CTPNSubAsi(lhs, rhs);
break;
case TOKT_TIMESEQ:
lhs = new CTPNMulAsi(lhs, rhs);
break;
case TOKT_DIVEQ:
lhs = new CTPNDivAsi(lhs, rhs);
break;
case TOKT_MODEQ:
lhs = new CTPNModAsi(lhs, rhs);
break;
case TOKT_ANDEQ:
lhs = new CTPNBAndAsi(lhs, rhs);
break;
case TOKT_OREQ:
lhs = new CTPNBOrAsi(lhs, rhs);
break;
case TOKT_XOREQ:
lhs = new CTPNBXorAsi(lhs, rhs);
break;
case TOKT_SHLEQ:
lhs = new CTPNShlAsi(lhs, rhs);
break;
case TOKT_SHREQ:
lhs = new CTPNShrAsi(lhs, rhs);
break;
default:
/* plain assignment operator */
lhs = new CTPNAsi(lhs, rhs);
break;
}
/* return the result */
return lhs;
}
/* ------------------------------------------------------------------------ */
/*
* Tertiary Conditional Operator
*/
CTcPrsNode *CTcPrsOpIf::parse() const
{
CTcPrsNode *first;
CTcPrsNode *second;
CTcPrsNode *third;
/* parse the conditional part */
first = S_op_or.parse();
if (first == 0)
return 0;
/* if we're not looking at the '?' operator, we're done */
if (G_tok->cur() != TOKT_QUESTION)
return first;
/* skip the '?' operator */
G_tok->next();
/*
* parse the second part, which can be any expression, including a
* double-quoted string expression or a comma expression (even though
* the '?:' operator overall has higher precedence than ',', we can't
* steal away operands from a ',' before our ':' because that would
* leave the ':' with nothing to go with)
*/
second = G_prs->parse_expr_or_dstr(TRUE);
if (second == 0)
return 0;
/* make sure we have the ':' after the second part */
if (G_tok->cur() != TOKT_COLON)
{
/*
* log the error, but continue parsing as though we found the
* ':' - if the ':' is simply missing, this will allow us to
* recover and continue parsing the rest of the expression
*/
G_tok->log_error(TCERR_QUEST_WITHOUT_COLON);
/* if we're at the end of file, there's no point in continuing */
if (G_tok->cur() == TOKT_EOF)
return 0;
}
/* skip the ':' */
G_tok->next();
/*
* parse the third part, which can be any other expression, including a
* double-quoted string expression - but not a comma expression, since
* we have higher precedence than ','
*/
third = G_prs->parse_expr_or_dstr(FALSE);
if (third == 0)
return 0;
/*
* If the condition is constant, we can choose the second or third
* expression directly. It doesn't matter whether or not the second
* and/or third parts are themselves constant, because a constant
* condition means that we'll always execute only one of the
* alternatives.
*/
if (first->is_const())
{
/*
* evaluate the conditional value as a true/false value, and
* return the second part's constant if the condition is true,
* or the third part's constant if the condition is false
*/
return (first->get_const_val()->get_val_bool()
? second : third);
}
else
{
/* it's not a constant value - return a new conditional node */
return new CTPNIf(first, second, third);
}
}
/* ------------------------------------------------------------------------ */
/*
* Unary Operator Parser
*/
CTcPrsNode *CTcPrsOpUnary::parse() const
{
CTcPrsNode *sub;
tc_toktyp_t op;
/* get the current token, which may be a prefix operator */
op = G_tok->cur();
/* check for prefix operators */
switch(op)
{
case TOKT_AND:
/* skip the '&' */
G_tok->next();
/* parse the address expression */
return parse_addr();
case TOKT_NOT:
case TOKT_BNOT:
case TOKT_PLUS:
case TOKT_MINUS:
case TOKT_INC:
case TOKT_DEC:
case TOKT_DELETE:
/* skip the operator */
G_tok->next();
/*
* recursively parse the unary expression to which to apply the
* operator
*/
sub = parse();
if (sub == 0)
return 0;
/* apply the operator */
switch(op)
{
case TOKT_NOT:
/* apply the NOT operator */
return parse_not(sub);
case TOKT_BNOT:
/* apply the bitwise NOT operator */
return parse_bnot(sub);
case TOKT_PLUS:
/* apply the unary positive operator */
return parse_pos(sub);
case TOKT_MINUS:
/* apply the unary negation operator */
return parse_neg(sub);
case TOKT_INC:
/* apply the pre-increment operator */
return parse_inc(TRUE, sub);
case TOKT_DEC:
/* apply the pre-decrement operator */
return parse_dec(TRUE, sub);
case TOKT_DELETE:
/* apply the deletion operator */
return parse_delete(sub);
default:
break;
}
default:
/* it's not a unary prefix operator - parse a postfix expression */
return parse_postfix(TRUE, TRUE);
}
}
/*
* parse a unary NOT expression
*/
CTcPrsNode *CTcPrsOpUnary::parse_not(CTcPrsNode *subexpr)
{
CTcPrsNode *ret;
/* try folding a constant value */
ret = eval_const_not(subexpr);
/*
* if we got a constant result, return it; otherwise, create a NOT
* node for code generation
*/
if (ret != 0)
return ret;
else
return new CTPNNot(subexpr);
}
/*
* evaluate a constant NOT expression
*/
CTcPrsNode *CTcPrsOpUnary::eval_const_not(CTcPrsNode *subexpr)
{
/*
* if the underlying expression is a constant value, apply the
* operator
*/
if (subexpr->is_const())
{
/* set the new value */
subexpr->get_const_val()
->set_bool(!subexpr->get_const_val()->get_val_bool());
/* return the modified constant value */
return subexpr;
}
/* the result is not constant */
return 0;
}
/*
* parse a unary bitwise NOT expression
*/
CTcPrsNode *CTcPrsOpUnary::parse_bnot(CTcPrsNode *subexpr)
{
/*
* if the underlying expression is a constant value, apply the
* operator
*/
if (subexpr->is_const())
{
/* we need an integer - log an error if it's not */
if (subexpr->get_const_val()->get_type() != TC_CVT_INT)
G_tok->log_error(TCERR_CONST_UNARY_REQ_NUM,
G_tok->get_op_text(TOKT_BNOT));
else
subexpr->get_const_val()
->set_int(~subexpr->get_const_val()->get_val_int());
/* return the updated value */
return subexpr;
}
/* create the bitwise NOT node */
return new CTPNBNot(subexpr);
}
/*
* parse a unary address expression
*/
CTcPrsNode *CTcPrsOpUnary::parse_addr() const
{
CTcPrsNode *subexpr;
/*
* if it's a simple symbol, create an unresolved symbol node for it;
* otherwise parse the entire expression
*/
if (G_tok->cur() == TOKT_SYM)
{
const CTcToken *tok;
/*
* create an unresolved symbol node - we'll resolve this during
* code generation
*/
tok = G_tok->getcur();
subexpr = new CTPNSym(tok->get_text(), tok->get_text_len());
/*
* The address operator implies that the symbol is a property, so
* define the property symbol and mark it as referenced if we
* haven't already.
*/
G_prs->get_global_symtab()->find_or_def_prop_explicit(
tok->get_text(), tok->get_text_len(), FALSE);
/* skip the symbol */
G_tok->next();
}
else
{
/* parse an expression */
subexpr = parse();
if (subexpr == 0)
return 0;
}
/*
* The underlying expression must be something that has an address;
* if it's not, it's an error.
*/
if (!subexpr->has_addr())
{
/*
* can't take the address of the subexpression - log an error,
* but continue parsing the expression anyway
*/
G_tok->log_error(TCERR_NO_ADDRESS);
}
/* create the address node */
return new CTPNAddr(subexpr);
}
/*
* parse a unary arithmetic positive expression
*/
CTcPrsNode *CTcPrsOpUnary::parse_pos(CTcPrsNode *subexpr)
{
/*
* if the underlying expression is a constant value, apply the
* operator
*/
if (subexpr->is_const())
{
/* if it's a float, a unary '+' has no effect at all */
if (subexpr->get_const_val()->get_type() == TC_CVT_FLOAT)
return subexpr;
/* we need an integer - log an error if it's not */
if (subexpr->get_const_val()->get_type() != TC_CVT_INT)
G_tok->log_error(TCERR_CONST_UNARY_REQ_NUM,
G_tok->get_op_text(TOKT_PLUS));
/*
* positive-ing a value doesn't change the value, so return the
* original constant
*/
return subexpr;
}
/* create the unary positive node */
return new CTPNPos(subexpr);
}
/*
* parse a unary arithmetic negation expression
*/
CTcPrsNode *CTcPrsOpUnary::parse_neg(CTcPrsNode *subexpr)
{
/*
* if the underlying expression is a constant value, apply the
* operator
*/
if (subexpr->is_const())
{
/* we need an integer or float */
if (subexpr->get_const_val()->get_type() == TC_CVT_INT)
{
/* set the value negative in the subexpression */
subexpr->get_const_val()
->set_int(-(subexpr->get_const_val()->get_val_int()));
}
else if (subexpr->get_const_val()->get_type() == TC_CVT_FLOAT)
{
CTcConstVal *cval = subexpr->get_const_val();
char *new_txt;
/* allocate a buffer for a copy of the float text plus a '-' */
new_txt = (char *)G_prsmem->alloc(cval->get_val_float_len() + 1);
/* insert the minus sign */
new_txt[0] = '-';
/* add the original string */
memcpy(new_txt + 1, cval->get_val_float(),
cval->get_val_float_len());
/* update the subexpression's constant value to the new text */
cval->set_float(new_txt, cval->get_val_float_len() + 1);
}
else
{
/* log the error */
G_tok->log_error(TCERR_CONST_UNARY_REQ_NUM,
G_tok->get_op_text(TOKT_MINUS));
}
/* return the modified constant value */
return subexpr;
}
/* create the unary negation node */
return new CTPNNeg(subexpr);
}
/*
* parse a pre-increment expression
*/
CTcPrsNode *CTcPrsOpUnary::parse_inc(int pre, CTcPrsNode *subexpr)
{
/* require an lvalue */
if (!subexpr->check_lvalue())
{
/* log an error, but continue parsing */
G_tok->log_error(TCERR_INVALID_UNARY_LVALUE,
G_tok->get_op_text(TOKT_INC));
}
/* apply the increment operator */
if (pre)
return new CTPNPreInc(subexpr);
else
return new CTPNPostInc(subexpr);
}
/*
* parse a pre-decrement expression
*/
CTcPrsNode *CTcPrsOpUnary::parse_dec(int pre, CTcPrsNode *subexpr)
{
/* require an lvalue */
if (!subexpr->check_lvalue())
{
/* log an error, but continue parsing */
G_tok->log_error(TCERR_INVALID_UNARY_LVALUE,
G_tok->get_op_text(TOKT_INC));
}
/* apply the pre-increment operator */
if (pre)
return new CTPNPreDec(subexpr);
else
return new CTPNPostDec(subexpr);
}
/*
* parse a unary allocation expression
*/
CTcPrsNode *CTcPrsOpUnary::parse_new(CTcPrsNode *subexpr, int is_transient)
{
/* create the allocation node */
return new CTPNNew(subexpr, is_transient);
}
/*
* parse a unary deletion expression
*/
CTcPrsNode *CTcPrsOpUnary::parse_delete(CTcPrsNode *subexpr)
{
/* the delete operator is obsolete in TADS 3 - warn about it */
if (!G_prs->get_syntax_only())
G_tok->log_warning(TCERR_DELETE_OBSOLETE);
/* create the deletion node */
return new CTPNDelete(subexpr);
}
/*
* parse a postfix expression
*/
CTcPrsNode *CTcPrsOpUnary::parse_postfix(int allow_member_expr,
int allow_call_expr)
{
CTcPrsNode *sub;
/* parse a primary expression */
sub = parse_primary();
if (sub == 0)
return 0;
/* keep going as long as we find postfix operators */
for (;;)
{
tc_toktyp_t op;
/* check for a postfix operator */
op = G_tok->cur();
switch(op)
{
case TOKT_LPAR:
/* left paren - function or method call */
if (allow_call_expr)
{
/* parse the call expression */
sub = parse_call(sub);
}
else
{
/* call expressions aren't allowed - stop here */
return sub;
}
break;
case TOKT_LBRACK:
/* left square bracket - subscript */
sub = parse_subscript(sub);
break;
case TOKT_DOT:
/*
* Dot - member selection. If a member expression is allowed
* by the caller, parse it; otherwise, just return the
* expression up to this point.
*/
if (allow_member_expr)
{
/*
* it's allowed - parse it and continue to look for other
* postfix expressions following the member expression
*/
sub = parse_member(sub);
}
else
{
/*
* member expressions aren't allowed - stop here,
* returning the expression up to this point
*/
return sub;
}
break;
case TOKT_INC:
/* post-increment */
G_tok->next();
sub = parse_inc(FALSE, sub);
break;
case TOKT_DEC:
/* post-decrement */
G_tok->next();
sub = parse_dec(FALSE, sub);
break;
default:
/* it's not a postfix operator - return the result */
return sub;
}
/* if the last parse failed, return failure */
if (sub == 0)
return 0;
}
}
/*
* Parse an argument list
*/
CTPNArglist *CTcPrsOpUnary::parse_arg_list()
{
int argc;
CTPNArg *arg_head;
/* skip the open paren */
G_tok->next();
/* keep going until we find the close paren */
for (argc = 0, arg_head = 0 ;; )
{
CTcPrsNode *expr;
CTPNArg *arg_cur;
/* if this is the close paren, we're done */
if (G_tok->cur() == TOKT_RPAR)
break;
/* parse this actual parameter expression */
expr = S_op_asi.parse();
if (expr == 0)
return 0;
/* count the argument */
++argc;
/* create a new argument node */
arg_cur = new CTPNArg(expr);
/* check to see if the argument is followed by an ellipsis */
if (G_tok->cur() == TOKT_ELLIPSIS)
{
/* skip the ellipsis */
G_tok->next();
/* mark the argument as a list-to-varargs parameter */
arg_cur->set_varargs(TRUE);
}
/*
* Link the new node in at the beginning of our list - this will
* ensure that the list is built in reverse order, which is the
* order in which we push the arguments onto the stack.
*/
arg_cur->set_next_arg(arg_head);
arg_head = arg_cur;
/* we need to be looking at a comma, right paren, or ellipsis */
if (G_tok->cur() == TOKT_RPAR)
{
/* that's the end of the list */
break;
}
else if (G_tok->cur() == TOKT_COMMA)
{
/* skip the comma and parse the next argument */
G_tok->next();
}
else
{
/*
* If we're at the end of the file, there's no point
* proceding, so return failure. If we've reached something
* that looks like a statement separator (semicolon, curly
* brace), also return failure, since the problem is clearly
* a missing right paren. Otherwise, assume that a comma
* was missing and continue as though we have another
* argument.
*/
switch(G_tok->cur())
{
default:
/* log an error */
G_tok->log_error_curtok(TCERR_EXPECTED_ARG_COMMA);
/*
* if we're at the end of file, return what we have so
* far; otherwise continue, assuming that they merely
* left out a comma between two argument expressions
*/
if (G_tok->cur() == TOKT_EOF)
return new CTPNArglist(argc, arg_head);
break;
case TOKT_SEM:
case TOKT_LBRACE:
case TOKT_RBRACE:
case TOKT_DSTR_MID:
case TOKT_DSTR_END:
/*
* we're apparently at the end of the statement; flag
* the error as a missing right paren, and return what
* we have so far
*/
G_tok->log_error_curtok(TCERR_EXPECTED_ARG_RPAR);
return new CTPNArglist(argc, arg_head);
}
}
}
/* skip the closing paren */
G_tok->next();
/* create and return the argument list descriptor */
return new CTPNArglist(argc, arg_head);
}
/*
* Parse a function call expression
*/
CTcPrsNode *CTcPrsOpUnary::parse_call(CTcPrsNode *lhs)
{
CTPNArglist *arglist;
/* parse the argument list */
arglist = parse_arg_list();
if (arglist == 0)
return 0;
/* build and return the function call node */
return new CTPNCall(lhs, arglist);
}
/*
* Parse a subscript expression
*/
CTcPrsNode *CTcPrsOpUnary::parse_subscript(CTcPrsNode *lhs)
{
CTcPrsNode *subscript;
CTcPrsNode *cval;
/* skip the '[' */
G_tok->next();
/* parse the expression within the brackets */
subscript = S_op_comma.parse();
if (subscript == 0)
return 0;
/* check for the matching ']' */
if (G_tok->cur() == TOKT_RBRACK)
{
/* got it - skip it */
G_tok->next();
}
else
{
/* log an error, and forgive the missing ']' */
G_tok->log_error_curtok(TCERR_EXPECTED_SUB_RBRACK);
}
/* try folding constants */
cval = eval_const_subscript(lhs, subscript);
/*
* if that worked, use the result; otherwise, build an expression
* node to generate code for the subscript operator
*/
if (cval != 0)
return cval;
else
return new CTPNSubscript(lhs, subscript);
}
/*
* Evaluate a constant subscript value
*/
CTcPrsNode *CTcPrsOpUnary::eval_const_subscript(CTcPrsNode *lhs,
CTcPrsNode *subscript)
{
/*
* if we're subscripting a constant list by a constant index value,
* we can evaluate a constant result
*/
if (lhs->is_const() && subscript->is_const())
{
long idx;
CTcPrsNode *ele;
/*
* make sure the index value is an integer and the value being
* indexed is a list; if either type is wrong, the indexing
* expression is invalid
*/
if (subscript->get_const_val()->get_type() != TC_CVT_INT)
{
/* we can't use a non-integer expression as a list index */
G_tok->log_error(TCERR_CONST_IDX_NOT_INT);
}
else if (lhs->get_const_val()->get_type() != TC_CVT_LIST)
{
/* we can't index any constant type other than list */
G_tok->log_error(TCERR_CONST_IDX_INV_TYPE);
}
else
{
/* get the index value */
idx = subscript->get_const_val()->get_val_int();
/* ask the list to look up the item by index */
ele = lhs->get_const_val()->get_val_list()->get_const_ele(idx);
/* if we got a valid result, return it */
if (ele != 0)
return ele;
}
}
/* we couldn't fold it to a constant expression */
return 0;
}
/*
* Parse a member selection ('.') expression. If no '.' is actually
* present, then '.targetprop' is implied.
*/
CTcPrsNode *CTcPrsOpUnary::parse_member(CTcPrsNode *lhs)
{
CTcPrsNode *rhs;
int rhs_is_expr;
/*
* If a '.' is present, skip it; otherwise, '.targetprop' is implied.
*/
if (G_tok->cur() == TOKT_DOT)
{
/* we have an explicit property expression - skip the '.' */
G_tok->next();
/* assume the property will be a simple symbol, not an expression */
rhs_is_expr = FALSE;
/* we could have a symbol or a parenthesized expression */
switch(G_tok->cur())
{
case TOKT_SYM:
/*
* It's a simple property name - create a symbol node. Note
* that we must explicitly create an unresolved symbol node,
* since we want to ignore any local variable with the same
* name and look only in the global symbol table for a
* property; we must hence defer resolving the symbol until
* code generation.
*/
rhs = new CTPNSym(G_tok->getcur()->get_text(),
G_tok->getcur()->get_text_len());
/* skip the symbol token */
G_tok->next();
/* proceed to check for an argument list */
break;
case TOKT_LPAR:
/*
* It's a parenthesized expression - parse it. First, skip
* the open paren - we don't want the sub-expression to go
* beyond the close paren (if we didn't skip the open paren,
* the open paren would be part of the sub-expression, hence
* any postfix expression after the close paren would be
* considered part of the sub-expression; this would be
* invalid, since we might want to find a postfix actual
* parameter list).
*/
G_tok->next();
/* remember that it's an expression */
rhs_is_expr = TRUE;
/* parse the sub-expression */
rhs = S_op_comma.parse();
if (rhs == 0)
return 0;
/* require the close paren */
if (G_tok->cur() == TOKT_RPAR)
{
/* skip the closing paren */
G_tok->next();
}
else
{
/* log the error */
G_tok->log_error_curtok(TCERR_EXPR_MISSING_RPAR);
/*
* if we're at a semicolon or end of file, we must be on
* to the next statement - stop trying to parse this one
* if so; otherwise, continue on the assumption that they
* merely left out the close paren and what follows is
* more expression for us to process
*/
if (G_tok->cur() == TOKT_SEM || G_tok->cur() == TOKT_EOF)
return lhs;
}
break;
case TOKT_TARGETPROP:
/*
* it's an unparenthesized "targetprop" expression - skip the
* keyword
*/
G_tok->next();
/*
* the property value is the result of evaluating
* "targetprop", which is an expression
*/
rhs = new CTPNTargetprop();
rhs_is_expr = TRUE;
/* note the reference to the extended method context */
G_prs->set_full_method_ctx_referenced(TRUE);
/* go parse the rest */
break;
default:
/* anything else is invalid - log an error */
G_tok->log_error_curtok(TCERR_INVALID_PROP_EXPR);
/* skip the errant token so we don't loop on it */
G_tok->next();
/* return what we have so far */
return lhs;
}
}
else
{
/* there's no property specified, so '.targetprop' is implied */
rhs = new CTPNTargetprop();
rhs_is_expr = TRUE;
/*
* note the reference to the full method context (since
* 'targetprop' is part of the extended method context beyond
* 'self')
*/
G_prs->set_full_method_ctx_referenced(TRUE);
}
/* check for an argument list */
if (G_tok->cur() == TOKT_LPAR)
{
CTPNArglist *arglist;
/* parse the argument list */
arglist = parse_arg_list();
if (arglist == 0)
return 0;
/* create and return a member-with-arguments node */
return new CTPNMemArg(lhs, rhs, rhs_is_expr, arglist);
}
else
{
/*
* there's no argument list - create and return a simple member
* node
*/
return new CTPNMember(lhs, rhs, rhs_is_expr);
}
}
/*
* Parse a double-quoted string with an embedded expression. We treat
* this type of expression as though it were a comma expression.
*/
CTcPrsNode *CTcPrsOpUnary::parse_dstr_embed()
{
CTcPrsNode *cur;
/*
* First, create a node for the initial part of the string. This is
* just an ordinary double-quoted string node. If the initial part of
* the string is zero-length, don't create an initial node at all,
* since this would just generate do-nothing code.
*/
if (G_tok->getcur()->get_text_len() != 0)
{
/* create the node for the initial part of the string */
cur = new CTPNDstr(G_tok->getcur()->get_text(),
G_tok->getcur()->get_text_len());
}
else
{
/*
* the initial part of the string is empty, so we don't need a node
* for this portion
*/
cur = 0;
}
/* skip the dstring */
G_tok->next();
/* keep going until we find the end of the string */
for (;;)
{
CTcPrsNode *sub;
int done;
/*
* parse the embedded expression, which can be any ordinary
* expression type, including a double-quoted string expression
*/
sub = G_prs->parse_expr_or_dstr(TRUE);
if (sub == 0)
return 0;
/* build an embedding node for the expression */
sub = new CTPNDstrEmbed(sub);
/*
* after the expression, we must find either another string
* segment with another embedded expression following, or the
* final string segment; anything else is an error
*/
do_next_segment:
switch(G_tok->cur())
{
case TOKT_DSTR_MID:
/*
* It's a string with yet another embedded expression.
* Simply continue to the next segment.
*/
done = FALSE;
break;
case TOKT_DSTR_END:
/*
* It's the last segment of the string. We can stop after
* processing this segment.
*/
done = TRUE;
break;
default:
/*
* anything else is invalid - we must find the end of the
* string. Log an error.
*/
G_tok->log_error_curtok(TCERR_EXPECTED_DSTR_CONT);
/*
* if this is the end of the file, there's no point in
* continuing; return what we have so far
*/
if (G_tok->cur() == TOKT_EOF)
return (cur != 0 ? cur : sub);
/* tell the tokenizer to assume the missing '>>' */
G_tok->assume_missing_dstr_cont();
/* go back and try it again */
goto do_next_segment;
}
/*
* Build a node representing everything so far: do this by
* combining the sub-expression with everything preceding, using a
* comma operator. This isn't necessary if there's nothing
* preceding the sub-expression, since this means the
* sub-expression itself is everything so far.
*/
if (cur != 0)
cur = new CTPNComma(cur, sub);
else
cur = sub;
/*
* Combine the part so far with the next string segment, using a
* comma operator. If the next string segment is empty, there's no
* need to add anything for it.
*/
if (G_tok->getcur()->get_text_len() != 0)
{
CTcPrsNode *newstr;
/* create a node for the new string segment */
newstr = new CTPNDstr(G_tok->getcur()->get_text(),
G_tok->getcur()->get_text_len());
/* combine it into the part so far with a comma operator */
cur = new CTPNComma(cur, newstr);
}
/* skip this string part */
G_tok->next();
/* if that was the last segment, this is the final result */
if (done)
return cur;
}
}
/*
* Parse a primary expression
*/
CTcPrsNode *CTcPrsOpUnary::parse_primary()
{
CTcPrsNode *sub;
CTcConstVal cval;
/* keep going until we find something interesting */
for (;;)
{
/* determine what we have */
switch(G_tok->cur())
{
case TOKT_LBRACE:
/* short form of anonymous function */
return parse_anon_func(TRUE);
case TOKT_FUNCTION:
/* anonymous function requires 'new' */
G_tok->log_error(TCERR_ANON_FUNC_REQ_NEW);
/*
* parse it as an anonymous function anyway, even though the
* syntax isn't quite correct - the rest of it might still
* be okay, so we can at least continue parsing from here to
* find out
*/
return parse_anon_func(FALSE);
case TOKT_NEW:
/* skip the operator and check for 'function' */
if (G_tok->next() == TOKT_FUNCTION)
{
/* it's an anonymous function definition - go parse it */
sub = parse_anon_func(FALSE);
}
else
{
int trans;
/* check for the 'transient' keyword */
trans = (G_tok->cur() == TOKT_TRANSIENT);
if (trans)
G_tok->next();
/*
* it's an ordinary 'new' expression - parse the primary
* making up the name
*/
sub = parse_primary();
/* if there's an argument list, parse the argument list */
if (G_tok->cur() == TOKT_LPAR)
sub = parse_call(sub);
/* create the 'new' node */
sub = parse_new(sub, trans);
}
return sub;
case TOKT_LPAR:
/* left parenthesis - skip it */
G_tok->next();
/* parse the expression */
sub = S_op_comma.parse();
if (sub == 0)
return 0;
/* require the matching right parenthesis */
if (G_tok->cur() == TOKT_RPAR)
{
/* skip the right paren */
G_tok->next();
}
else
{
/*
* log an error; assume that the paren is simply
* missing, so continue with our work
*/
G_tok->log_error_curtok(TCERR_EXPR_MISSING_RPAR);
}
/* return the parenthesized expression */
return sub;
case TOKT_NIL:
/* nil - the result is the constant value nil */
cval.set_nil();
return_constant:
/* skip the token */
G_tok->next();
/* return a constant node */
return new CTPNConst(&cval);
case TOKT_TRUE:
/* true - the result is the constant value true */
cval.set_true();
goto return_constant;
case TOKT_INT:
/* integer - the result is a constant integer value */
cval.set_int(G_tok->getcur()->get_int_val());
goto return_constant;
case TOKT_FLOAT:
/* floating point number */
cval.set_float(G_tok->getcur()->get_text(),
G_tok->getcur()->get_text_len());
goto return_constant;
case TOKT_SSTR:
handle_sstring:
/* single-quoted string - the result is a constant string value */
cval.set_sstr(G_tok->getcur()->get_text(),
G_tok->getcur()->get_text_len());
goto return_constant;
case TOKT_DSTR:
/*
* if we're in preprocessor expression mode, treat this the
* same as a single-quoted string
*/
if (G_prs->get_pp_expr_mode())
goto handle_sstring;
/*
* a string implicitly references 'self', because we could run
* through the default output method on the current object
*/
G_prs->set_self_referenced(TRUE);
/* build a double-quoted string node */
sub = new CTPNDstr(G_tok->getcur()->get_text(),
G_tok->getcur()->get_text_len());
/* skip the string */
G_tok->next();
/* return the new node */
return sub;
case TOKT_DSTR_START:
/* a string implicitly references 'self' */
G_prs->set_self_referenced(TRUE);
/* parse the embedding expression */
return parse_dstr_embed();
case TOKT_LBRACK:
/* parse the list */
return parse_list();
case TOKT_SYM:
/*
* symbol - the meaning of the symbol is not yet known, so
* create an unresolved symbol node
*/
sub = G_prs->create_sym_node(G_tok->getcur()->get_text(),
G_tok->getcur()->get_text_len());
/* skip the symbol token */
G_tok->next();
/* return the new node */
return sub;
case TOKT_SELF:
/* note the explicit self-reference */
G_prs->set_self_referenced(TRUE);
/* generate the "self" node */
G_tok->next();
return new CTPNSelf();
case TOKT_REPLACED:
/* generate the "replaced" node */
G_tok->next();
return new CTPNReplaced();
case TOKT_TARGETPROP:
/* note the explicit extended method context reference */
G_prs->set_full_method_ctx_referenced(TRUE);
/* generate the "targetprop" node */
G_tok->next();
return new CTPNTargetprop();
case TOKT_TARGETOBJ:
/* note the explicit extended method context reference */
G_prs->set_full_method_ctx_referenced(TRUE);
/* generate the "targetobj" node */
G_tok->next();
return new CTPNTargetobj();
case TOKT_DEFININGOBJ:
/* note the explicit extended method context reference */
G_prs->set_full_method_ctx_referenced(TRUE);
/* generate the "definingobj" node */
G_tok->next();
return new CTPNDefiningobj();
case TOKT_ARGCOUNT:
/* generate the "argcount" node */
G_tok->next();
return new CTPNArgc();
case TOKT_INHERITED:
/* parse the "inherited" operation */
return parse_inherited();
case TOKT_DELEGATED:
/* parse the "delegated" operation */
return parse_delegated();
case TOKT_RPAR:
/* extra right paren - log an error */
G_tok->log_error(TCERR_EXTRA_RPAR);
/* skip it and go back for more */
G_tok->next();
break;
case TOKT_RBRACK:
/* extra right square bracket - log an error */
G_tok->log_error(TCERR_EXTRA_RBRACK);
/* skip it and go back for more */
G_tok->next();
break;
case TOKT_DSTR_MID:
case TOKT_DSTR_END:
case TOKT_SEM:
case TOKT_RBRACE:
/*
* this looks like the end of the statement, but we expected
* an operand - note the error and end the statement
*/
G_tok->log_error_curtok(TCERR_EXPECTED_OPERAND);
/*
* Synthesize a constant zero as the operand value. Do not
* skip the current token, because it's almost certainly not
* meant to be part of the expression; we want to stay put
* so that the caller can resynchronize on this token.
*/
cval.set_int(G_tok->getcur()->get_int_val());
return new CTPNConst(&cval);
default:
/* invalid primary expression - log the error */
G_tok->log_error_curtok(TCERR_BAD_PRIMARY_EXPR);
/*
* Skip the token that's causing the problem; this will
* ensure that we don't loop indefinitely trying to figure
* out what this token is about, and return a constant zero
* value as the primary.
*/
G_tok->next();
/* synthesize a constant zero as the operand value */
cval.set_int(G_tok->getcur()->get_int_val());
goto return_constant;
}
}
}
/*
* Parse an "inherited" expression
*/
CTcPrsNode *CTcPrsOpUnary::parse_inherited()
{
CTcPrsNode *lhs;
/* skip the "inherited" keyword and check what follows */
switch(G_tok->next())
{
case TOKT_SYM:
/*
* it's an "inherited superclass..." expression - set up the
* "inherited superclass" node
*/
lhs = new CTPNInhClass(G_tok->getcur()->get_text(),
G_tok->getcur()->get_text_len());
/* skip the superclass token */
G_tok->next();
/* parse the member expression portion normally */
break;
case TOKT_LT:
/*
* '<' - this is the start of a multi-method type list. Parse the
* list: type1 ',' type2 ',' ... '>', then the argument list to the
* 'inherited' call.
*/
{
/* create the formal type list */
CTcFormalTypeList *tl = new (G_prsmem)CTcFormalTypeList();
/* skip the '<' */
G_tok->next();
/* parse each list element */
for (int done = FALSE ; !done ; )
{
switch (G_tok->cur())
{
case TOKT_GT:
/* end of the list - skip the '>', and we're done */
G_tok->next();
done = TRUE;
break;
case TOKT_ELLIPSIS:
/* '...' */
tl->add_ellipsis();
/* this has to be the end of the list */
if (G_tok->next() == TOKT_GT)
G_tok->next();
else
G_tok->log_error_curtok(TCERR_MMINH_MISSING_GT);
/* assume the list ends here in any case */
done = TRUE;
break;
case TOKT_SYM:
/* a type token - add it to the list */
tl->add_typed_param(G_tok->getcur());
finish_type:
/* skip the type token */
switch (G_tok->next())
{
case TOKT_COMMA:
/* another type follows */
G_tok->next();
break;
case TOKT_GT:
G_tok->next();
done = TRUE;
break;
case TOKT_SYM:
case TOKT_ELLIPSIS:
case TOKT_TIMES:
/* probably just a missing comma */
G_tok->log_error_curtok(TCERR_MMINH_MISSING_COMMA);
break;
default:
/* anything else is an error */
G_tok->log_error_curtok(TCERR_MMINH_MISSING_COMMA);
G_tok->next();
break;
}
break;
case TOKT_TIMES:
/* '*' indicates an untyped parameter */
tl->add_untyped_param();
goto finish_type;
case TOKT_LPAR:
/* probably a missing '>' */
G_tok->log_error_curtok(TCERR_MMINH_MISSING_GT);
done = TRUE;
break;
case TOKT_COMMA:
/* probably a missing type */
G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_TYPE);
G_tok->next();
break;
case TOKT_SEM:
case TOKT_RPAR:
case TOKT_EOF:
/* all of these indicate a premature end of the list */
G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_TYPE);
return 0;
default:
/* anything else is an error */
G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_TYPE);
G_prs->skip_to_sem();
return 0;
}
}
/* the left-hand side is an "inherited" node, with the arg list */
lhs = new CTPNInh();
((CTPNInh *)lhs)->set_typelist(tl);
/* an inherited<> expression must have an argument list */
if (G_tok->cur() != TOKT_LPAR)
{
G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_LIST);
G_prs->skip_to_sem();
return 0;
}
}
break;
default:
/*
* There's no explicit superclass name listed, so the left-hand
* side of the '.' expression is the simple "inherited" node.
*/
lhs = new CTPNInh();
/*
* Since we don't have an explicit superclass, we'll need the
* method context at run-time to establish the next class in
* inheritance order. Flag the need for the full method context.
*/
G_prs->set_full_method_ctx_referenced(TRUE);
/* parse the member expression portion normally */
break;
}
/* parse and return the member expression */
return parse_member(lhs);
}
/*
* Parse a "delegated" expression
*/
CTcPrsNode *CTcPrsOpUnary::parse_delegated()
{
CTcPrsNode *lhs;
CTcPrsNode *target;
/* 'delegated' always references 'self' */
G_prs->set_self_referenced(TRUE);
/* skip the "delegated" keyword */
G_tok->next();
/*
* Parse a postfix expression giving the delegatee. Don't allow
* nested member subexpressions (unless they're enclosed in
* parentheses, of course) - our implicit '.' postfix takes
* precedence. Also, don't allow call subexpressions (unless enclosed
* in parens), since a postfix argument list binds to the 'delegated'
* expression, not to a subexpression involving a function/method
* call.
*/
target = parse_postfix(FALSE, FALSE);
/* set up the "delegated" node */
lhs = new CTPNDelegated(target);
/* return the rest as a normal member expression */
return parse_member(lhs);
}
/*
* Parse a list
*/
CTcPrsNode *CTcPrsOpUnary::parse_list()
{
CTPNList *lst;
CTcPrsNode *ele;
/* skip the opening '[' */
G_tok->next();
/*
* create the list expression -- we'll add elements to the list as
* we parse the elements
*/
lst = new CTPNList();
/* scan all list elements */
for (;;)
{
/* check what we have */
switch(G_tok->cur())
{
case TOKT_RBRACK:
/*
* that's the end of the list - skip the closing bracket and
* return the finished list
*/
G_tok->next();
goto done;
case TOKT_EOF:
case TOKT_RBRACE:
case TOKT_SEM:
case TOKT_DSTR_MID:
case TOKT_DSTR_END:
/*
* these would all seem to imply that the closing ']' was
* missing from the list; flag the error and end the list
* now
*/
G_tok->log_error_curtok(TCERR_LIST_MISSING_RBRACK);
goto done;
case TOKT_RPAR:
/*
* extra right paren - log an error, but then skip the paren
* and try to keep parsing
*/
G_tok->log_error(TCERR_LIST_EXTRA_RPAR);
G_tok->next();
break;
default:
/* it must be the next element expression */
break;
}
/*
* Attempt to parse another list element expression. Parse just
* below a comma expression, because commas can be used to
* separate list elements.
*/
ele = S_op_asi.parse();
if (ele == 0)
return 0;
/* add the element to the list */
lst->add_element(ele);
/* check what follows the element */
switch(G_tok->cur())
{
case TOKT_COMMA:
/* skip the comma introducing the next element */
G_tok->next();
/* if a close bracket follows, we seem to have an extra comma */
if (G_tok->cur() == TOKT_RBRACK)
{
/*
* log an error about the missing element, then end the
* list here
*/
G_tok->log_error_curtok(TCERR_LIST_EXPECT_ELEMENT);
goto done;
}
break;
case TOKT_RBRACK:
/*
* we're done with the list - skip the bracket and return
* the finished list
*/
G_tok->next();
goto done;
case TOKT_EOF:
case TOKT_LBRACE:
case TOKT_RBRACE:
case TOKT_SEM:
case TOKT_DSTR_MID:
case TOKT_DSTR_END:
/*
* these would all seem to imply that the closing ']' was
* missing from the list; flag the error and end the list
* now
*/
G_tok->log_error_curtok(TCERR_LIST_MISSING_RBRACK);
goto done;
default:
/*
* Anything else is an error - note that we expected a
* comma, then proceed with parsing from the current token
* as though we had found the comma (in all likelihood, the
* comma was accidentally omitted). If we've reached the
* end of the file, return what we have so far, since it's
* pointless to keep looping.
*/
G_tok->log_error_curtok(TCERR_LIST_EXPECT_COMMA);
/* give up on end of file, otherwise keep going */
if (G_tok->cur() == TOKT_EOF)
goto done;
break;
}
}
done:
/* tell the parser to note this list, in case it's the longest yet */
G_cg->note_list(lst->get_count());
/* return the list */
return lst;
}
/* ------------------------------------------------------------------------ */
/*
* Parse Allocation Object
*/
/*
* memory allocator for parse nodes
*/
void *CTcPrsAllocObj::operator new(size_t siz)
{
/* allocate the space out of the node pool */
return G_prsmem->alloc(siz);
}
/* ------------------------------------------------------------------------ */
/*
* Parse Tree space manager
*/
/*
* create
*/
CTcPrsMem::CTcPrsMem()
{
/* we have no blocks yet */
head_ = tail_ = 0;
/* allocate our first block */
alloc_block();
}
CTcPrsMem::~CTcPrsMem()
{
/* delete all objects in our pool */
delete_all();
}
/*
* Save state, for later resetting
*/
void CTcPrsMem::save_state(tcprsmem_state_t *state)
{
/* save the pool information in the state structure */
state->tail = tail_;
state->free_ptr = free_ptr_;
state->rem = rem_;
}
/*
* Reset to initial state
*/
void CTcPrsMem::reset()
{
/* delete all blocks */
delete_all();
/* re-allocate the initial block */
alloc_block();
}
/*
* Reset. This deletes all objects allocated out of the parser pool
* since the state was saved.
*/
void CTcPrsMem::reset(const tcprsmem_state_t *state)
{
tcprsmem_blk_t *cur;
/*
* delete all of the blocks that were allocated after the last block
* that existed when the state was saved
*/
for (cur = state->tail->next_ ; cur != 0 ; )
{
tcprsmem_blk_t *nxt;
/* remember the next block */
nxt = cur->next_;
/* delete this block */
t3free(cur);
/* move on to the next one */
cur = nxt;
}
/* re-establish the saved last block */
tail_ = state->tail;
/* make sure the list is terminated at the last block */
tail_->next_ = 0;
/* re-establish the saved allocation point in the last block */
free_ptr_ = state->free_ptr;
rem_ = state->rem;
}
/*
* Delete all parser memory. This deletes all objects allocated out of
* parser memory, so the caller must be sure that all of these objects
* are unreferenced.
*/
void CTcPrsMem::delete_all()
{
/* free all blocks */
while (head_ != 0)
{
tcprsmem_blk_t *nxt;
/* remember the next block after this one */
nxt = head_->next_;
/* free this block */
t3free(head_);
/* move on to the next one */
head_ = nxt;
}
/* there's no tail now */
tail_ = 0;
}
/*
* allocate a block
*/
void CTcPrsMem::alloc_block()
{
tcprsmem_blk_t *blk;
/*
* block size - pick a size that's large enough that we won't be
* unduly inefficient (in terms of having tons of blocks), but still
* friendly to 16-bit platforms (i.e., under 64k)
*/
const size_t BLOCK_SIZE = 65000;
/* allocate space for the block */
blk = (tcprsmem_blk_t *)t3malloc(sizeof(tcprsmem_blk_t) + BLOCK_SIZE - 1);
/* if that failed, throw an error */
if (blk == 0)
err_throw(TCERR_NO_MEM_PRS_TREE);
/* link in the block at the end of our list */
blk->next_ = 0;
if (tail_ != 0)
tail_->next_ = blk;
else
head_ = blk;
/* the block is now the last block in the list */
tail_ = blk;
/*
* Set up to allocate out of our block. Make sure the free pointer
* starts out on a worst-case alignment boundary; normally, the C++
* compiler will properly align our "buf_" structure member on a
* worst-case boundary, so this calculation won't actually change
* anything, but this will help ensure portability even to weird
* compilers.
*/
free_ptr_ = (char *)osrndpt((unsigned char *)blk->buf_);
/*
* get the amount of space remaining in the block (in the unlikely
* event that worst-case alignment actually moved the free pointer
* above the start of the buffer, we'll have lost a little space in
* the buffer for the alignment offset)
*/
rem_ = BLOCK_SIZE - (free_ptr_ - blk->buf_);
}
/*
* Allocate space
*/
void *CTcPrsMem::alloc(size_t siz)
{
char *ret;
size_t space_used;
/* if there's not enough space available, allocate a new block */
if (siz > rem_)
{
/* allocate a new block */
alloc_block();
/*
* if there's still not enough room, the request must exceed the
* largest block we can allocate
*/
if (siz > rem_)
G_tok->throw_internal_error(TCERR_PRS_BLK_TOO_BIG, (ulong)siz);
}
/* return the free pointer */
ret = free_ptr_;
/* advance the free pointer past the space, rounding for alignment */
free_ptr_ = (char *)osrndpt((unsigned char *)free_ptr_ + siz);
/* deduct the amount of space we consumed from the available space */
space_used = free_ptr_ - ret;
if (space_used > rem_)
rem_ = 0;
else
rem_ -= space_used;
/* return the allocated space */
return ret;
}
/* ------------------------------------------------------------------------ */
/*
* parse node base class
*/
/*
* By default, an expression cannot be used as a debugger expression
*/
CTcPrsNode *CTcPrsNodeBase::adjust_for_debug(const tcpn_debug_info *info)
{
err_throw(VMERR_INVAL_DBG_EXPR);
AFTER_ERR_THROW(return 0;)
}
/* ------------------------------------------------------------------------ */
/*
* constant node
*/
/*
* adjust for debugger use
*/
CTcPrsNode *CTPNConstBase::adjust_for_debug(const tcpn_debug_info *info)
{
/* convert to a debugger-constant */
return new CTPNDebugConst(&val_);
}
/* ------------------------------------------------------------------------ */
/*
* List parse node
*/
/*
* add an element to a list
*/
void CTPNListBase::add_element(CTcPrsNode *expr)
{
CTPNListEle *ele;
/* create a list element object for the new element */
ele = new CTPNListEle(expr);
/* count the new entry */
++cnt_;
/* add the element to our linked list */
ele->set_prev(tail_);
if (tail_ != 0)
tail_->set_next(ele);
else
head_ = ele;
tail_ = ele;
/*
* if the new element does not have a constant value, the list no
* longer has a constant value (if it did before)
*/
if (!expr->is_const())
is_const_ = FALSE;
}
/*
* remove each occurrence of a given constant value from the list
*/
void CTPNListBase::remove_element(const CTcConstVal *val)
{
CTPNListEle *cur;
/* scan the list */
for (cur = head_ ; cur != 0 ; cur = cur->get_next())
{
/*
* if this element is constant, compare it to the value to be
* removed; if it matches, remove it
*/
if (cur->get_expr()->is_const()
&& cur->get_expr()->get_const_val()->is_equal_to(val))
{
/* set the previous element's forward pointer */
if (cur->get_prev() == 0)
head_ = cur->get_next();
else
cur->get_prev()->set_next(cur->get_next());
/* set the next element's back pointer */
if (cur->get_next() == 0)
tail_ = cur->get_prev();
else
cur->get_next()->set_prev(cur->get_prev());
/* decrement our element counter */
--cnt_;
}
}
}
/*
* Get the constant value of the element at the given index. Logs an
* error and returns null if there's no such element.
*/
CTcPrsNode *CTPNListBase::get_const_ele(int index)
{
CTPNListEle *ele;
/* if the index is negative, it's out of range */
if (index < 1)
{
/* log the error and return failure */
G_tok->log_error(TCERR_CONST_IDX_RANGE);
return 0;
}
/* scan the list for the given element */
for (ele = head_ ; ele != 0 && index > 1 ;
ele = ele->get_next(), --index) ;
/* if we ran out of elements, the index is out of range */
if (ele == 0 || index != 1)
{
G_tok->log_error(TCERR_CONST_IDX_RANGE);
return 0;
}
/* return the element's constant value */
return ele->get_expr();
}
/*
* Fold constants
*/
CTcPrsNode *CTPNListBase::fold_constants(CTcPrsSymtab *symtab)
{
CTPNListEle *cur;
int all_const;
/*
* if the list is already constant, there's nothing extra we need to
* do
*/
if (is_const_)
return this;
/* presume the result will be all constants */
all_const = TRUE;
/* run through my list and fold each element */
for (cur = head_ ; cur != 0 ; cur = cur->get_next())
{
/* fold this element */
cur->fold_constants(symtab);
/*
* if this element is not a constant, the whole list cannot be
* constant
*/
if (!cur->get_expr()->is_const())
all_const = FALSE;
}
/* if every element was a constant, the overall list is constant */
if (all_const)
is_const_ = TRUE;
/* return myself */
return this;
}
/*
* Adjust for debugging
*/
CTcPrsNode *CTPNListBase::adjust_for_debug(const tcpn_debug_info *info)
{
CTPNListEle *cur;
/* run through my list and adjust each element */
for (cur = head_ ; cur != 0 ; cur = cur->get_next())
{
/* adjust this element */
cur->adjust_for_debug(info);
}
/*
* force the list to be non-constant - in debugger mode, we have to
* build the value we push as a dynamic object, never as an actual
* constant, to ensure that the generated code can be deleted
* immediately after being executed
*/
is_const_ = FALSE;
/* return myself */
return this;
}
/* ------------------------------------------------------------------------ */
/*
* conditional operator node base class
*/
/*
* fold constants
*/
CTcPrsNode *CTPNIfBase::fold_constants(CTcPrsSymtab *symtab)
{
/* fold constants in the subnodes */
first_ = first_->fold_constants(symtab);
second_ = second_->fold_constants(symtab);
third_ = third_->fold_constants(symtab);
/*
* if the first is now a constant, we can fold this entire
* expression node by choosing the second or third based on its
* value; otherwise, return myself unchanged
*/
if (first_->is_const())
{
/*
* the condition is a constant - the result is the 'then' or
* 'else' part, based on the condition's value
*/
return (first_->get_const_val()->get_val_bool()
? second_ : third_);
}
else
{
/* we can't fold this node any further - return it unchanged */
return this;
}
}
/* ------------------------------------------------------------------------ */
/*
* Double-quoted string node - base class
*/
/*
* create a double-quoted string node
*/
CTPNDstrBase::CTPNDstrBase(const char *str, size_t len)
{
/* remember the string */
str_ = str;
len_ = len;
/*
* note the length in the parser, in case it's the longest string
* we've seen so far
*/
G_cg->note_str(len);
}
/*
* adjust for debugger use
*/
CTcPrsNode *CTPNDstrBase::adjust_for_debug(const tcpn_debug_info *info)
{
/*
* don't allow dstring evaluation in speculative mode, since we
* can't execute anything with side effects in this mode
*/
if (info->speculative)
err_throw(VMERR_BAD_SPEC_EVAL);
/* return a debugger dstring node */
return new CTPNDebugDstr(str_, len_);
}
/* ------------------------------------------------------------------------ */
/*
* Address-of parse node
*/
/*
* fold constants
*/
CTcPrsNode *CTPNAddrBase::fold_constants(CTcPrsSymtab *symtab)
{
CTcPrsNode *ret;
/* ask the symbol to generate a constant expression for its address */
ret = get_sub_expr()->fold_addr_const(symtab);
/*
* if we got a constant value, return it; otherwise, return myself
* unchanged
*/
return (ret != 0 ? ret : this);
}
/*
* determine if my address equals that of another node
*/
int CTPNAddrBase::is_addr_eq(const CTPNAddr *node, int *comparable) const
{
/*
* If both sides are symbols, the addresses are equal if and only if
* the symbols are identical. One symbol has exactly one meaning in
* a given context, and no two symbols can have the same meaning.
* (It's important that we be able to state this for all symbols,
* because we can't necessarily know during parsing the meaning of a
* given symbol, since the symbol could be a forward reference.)
*/
if (get_sub_expr()->get_sym_text() != 0
&& node->get_sub_expr()->get_sym_text() != 0)
{
CTcPrsNode *sym1;
CTcPrsNode *sym2;
/* they're both symbols, so they're comparable */
*comparable = TRUE;
/* they're the same if both symbols have the same text */
sym1 = get_sub_expr();
sym2 = node->get_sub_expr();
return (sym1->get_sym_text_len() == sym2->get_sym_text_len()
&& memcmp(sym1->get_sym_text(), sym2->get_sym_text(),
sym1->get_sym_text_len()) == 0);
}
/* they're not comparable */
*comparable = FALSE;
return FALSE;
}
/* ------------------------------------------------------------------------ */
/*
* Symbol parse node base class
*/
/*
* fold constants
*/
CTcPrsNode *CTPNSymBase::fold_constants(CTcPrsSymtab *symtab)
{
CTcSymbol *sym;
CTcPrsNode *ret;
/*
* Look up my symbol. At this stage, don't assume a definition;
* merely look to see if it's already known. We don't have enough
* information to determine how we should define the symbol, so
* leave it undefined until code generation if it's not already
* known.
*/
sym = symtab->find(get_sym_text(), get_sym_text_len());
if (sym != 0)
{
/* ask the symbol to do the folding */
ret = sym->fold_constant();
/* if that succeeded, return it; otherwise, return unchanged */
return (ret != 0 ? ret : this);
}
else
{
/* not defined - return myself unchanged */
return this;
}
}
/*
* Fold my address to a constant node. If I have no address value, I'll
* simply return myself unchanged. Note that it's an error if I have no
* constant value, but we'll count on the code generator to report the
* error, and simply ignore it for now.
*/
CTcPrsNode *CTPNSymBase::fold_addr_const(CTcPrsSymtab *symtab)
{
CTcSymbol *sym;
/* look up my symbol; if we don't find it, don't define it */
sym = symtab->find(get_sym_text(), get_sym_text_len());
if (sym != 0)
{
/* we got a symbol - ask it to do the folding */
return sym->fold_addr_const();
}
else
{
/* undefined symbol - there's no constant address value */
return 0;
}
}
/*
* Determine if I have a return value when called
*/
int CTPNSymBase::has_return_value_on_call() const
{
CTcSymbol *sym;
/* try resolving my symbol */
sym = G_prs->get_global_symtab()->find(sym_, len_);
/*
* if we found a symbol, let it resolve the call; otherwise, assume
* that we do have a return value
*/
if (sym != 0)
return sym->has_return_value_on_call();
else
return TRUE;
}
/*
* Determine if I am a valid lvalue
*/
int CTPNSymBase::check_lvalue_resolved(class CTcPrsSymtab *symtab) const
{
CTcSymbol *sym;
/* look up the symbol in the given scope */
sym = symtab->find(get_sym_text(), get_sym_text_len());
if (sym != 0)
{
/* ask the symbol what it thinks */
return sym->check_lvalue();
}
else
{
/* it's undefined - can't be an lvalue */
return FALSE;
}
}
/*
* Adjust for debugger use
*/
CTcPrsNode *CTPNSymBase::adjust_for_debug(const tcpn_debug_info *)
{
/*
* If this symbol isn't defined in the global symbol table, we can't
* evaluate this expression in the debugger - new symbols can never
* be defined in the debugger, so there's no point in trying to hold
* a forward reference as we normally would for an undefined symbol.
* We need look only in the global symbol table because local
* symbols will already have been resolved.
*/
if (G_prs->get_global_symtab()->find(sym_, len_) == 0)
{
/* log the error, to generate an appropriate message */
G_tok->log_error(TCERR_UNDEF_SYM, (int)len_, sym_);
/* throw the error as well */
err_throw_a(TCERR_UNDEF_SYM, 2,
ERR_TYPE_INT, (int)len_, ERR_TYPE_TEXTCHAR, sym_);
}
/* return myself unchanged */
return this;
}
/* ------------------------------------------------------------------------ */
/*
* Resolved Symbol parse node base class
*/
/*
* fold constants
*/
CTcPrsNode *CTPNSymResolvedBase::fold_constants(CTcPrsSymtab *symtab)
{
CTcPrsNode *ret;
/* ask the symbol to generate the folded constant value */
ret = sym_->fold_constant();
/* if that succeeded, return it; otherwise, return myself unchanged */
return (ret != 0 ? ret : this);
}
/*
* Fold my address to a constant node. If I have no address value, I'll
* simply return myself unchanged. Note that it's an error if I have no
* constant value, but we'll count on the code generator to report the
* error, and simply ignore it for now.
*/
CTcPrsNode *CTPNSymResolvedBase::fold_addr_const(CTcPrsSymtab *symtab)
{
/* ask my symbol to generate the folded constant value */
return sym_->fold_addr_const();
}
/* ------------------------------------------------------------------------ */
/*
* Debugger local variable resolved symbol
*/
/*
* construction
*/
CTPNSymDebugLocalBase::CTPNSymDebugLocalBase(const tcprsdbg_sym_info *info)
{
/* save the type information */
switch(info->sym_type)
{
case TC_SYM_LOCAL:
var_id_ = info->var_id;
ctx_arr_idx_ = info->ctx_arr_idx;
frame_idx_ = info->frame_idx;
is_param_ = FALSE;
break;
case TC_SYM_PARAM:
var_id_ = info->var_id;
ctx_arr_idx_ = 0;
frame_idx_ = info->frame_idx;
is_param_ = TRUE;
break;
default:
/* other types are invalid */
assert(FALSE);
break;
}
}
/* ------------------------------------------------------------------------ */
/*
* Argument List parse node base class
*/
/*
* fold constants
*/
CTcPrsNode *CTPNArglistBase::fold_constants(CTcPrsSymtab *symtab)
{
CTPNArg *cur;
/* fold each list element */
for (cur = get_arg_list_head() ; cur != 0 ; cur = cur->get_next_arg())
cur->fold_constants(symtab);
/* return myself with no further folding */
return this;
}
/*
* adjust for debugger use
*/
CTcPrsNode *CTPNArglistBase::adjust_for_debug(const tcpn_debug_info *info)
{
CTPNArg *arg;
/* adjust each argument list member */
for (arg = list_ ; arg != 0 ; arg = arg->get_next_arg())
{
/*
* adjust this argument; assume the argument node itself isn't
* affected
*/
arg->adjust_for_debug(info);
}
/* return myself otherwise unchanged */
return this;
}
/* ------------------------------------------------------------------------ */
/*
* Argument List Entry parse node base class
*/
/*
* fold constants
*/
CTcPrsNode *CTPNArgBase::fold_constants(CTcPrsSymtab *symtab)
{
/* fold my argument expression */
arg_expr_ = arg_expr_->fold_constants(symtab);
/* return myself unchanged */
return this;
}
/* ------------------------------------------------------------------------ */
/*
* Member with no arguments
*/
/*
* adjust for debugger use
*/
CTcPrsNode *CTPNMemberBase::adjust_for_debug(const tcpn_debug_info *info)
{
/* adjust the object and property expressions */
obj_expr_ = obj_expr_->adjust_for_debug(info);
prop_expr_ = prop_expr_->adjust_for_debug(info);
/* return myself otherwise unchanged */
return this;
}
/* ------------------------------------------------------------------------ */
/*
* Member with Arguments parse node base class
*/
/*
* fold constants
*/
CTcPrsNode *CTPNMemArgBase::fold_constants(CTcPrsSymtab *symtab)
{
/* fold constants in the object and property expressions */
obj_expr_ = obj_expr_->fold_constants(symtab);
prop_expr_ = prop_expr_->fold_constants(symtab);
/*
* fold constants in the argument list; an argument list node never
* changes to a new node type during constant folding, so we don't
* need to update the member
*/
arglist_->fold_constants(symtab);
/* return myself with no further evaluation */
return this;
}
/*
* adjust for debugger use
*/
CTcPrsNode *CTPNMemArgBase::adjust_for_debug(const tcpn_debug_info *info)
{
/* don't allow in speculative mode due to possible side effects */
if (info->speculative)
err_throw(VMERR_BAD_SPEC_EVAL);
/*
* adjust my object expression, property expression, and argument
* list
*/
obj_expr_ = obj_expr_->adjust_for_debug(info);
prop_expr_ = prop_expr_->adjust_for_debug(info);
arglist_->adjust_for_debug(info);
/* return myself otherwise unchanged */
return this;
}
/* ------------------------------------------------------------------------ */
/*
* Function/Method Call parse node base class
*/
/*
* fold constants
*/
CTcPrsNode *CTPNCallBase::fold_constants(CTcPrsSymtab *symtab)
{
/* fold my function expression */
func_ = func_->fold_constants(symtab);
/* fold my argument list */
arglist_->fold_constants(symtab);
/* return myself unchanged */
return this;
}
/*
* adjust for debugger use
*/
CTcPrsNode *CTPNCallBase::adjust_for_debug(const tcpn_debug_info *info)
{
/* don't allow in speculative mode because of side effects */
if (info->speculative)
err_throw(VMERR_BAD_SPEC_EVAL);
/* adjust the function expression */
func_ = func_->adjust_for_debug(info);
/* adjust the argument list (assume it doesn't change) */
arglist_->adjust_for_debug(info);
/* return myself otherwise unchanged */
return this;
}
/* ------------------------------------------------------------------------ */
/*
* Parser Symbol Table implementation
*/
/* static hash function */
CVmHashFunc *CTcPrsSymtab::hash_func_ = 0;
/*
* allocate parser symbol tables out of the parser memory pool
*/
void *CTcPrsSymtab::operator new(size_t siz)
{
return G_prsmem->alloc(siz);
}
/*
* static initialization
*/
void CTcPrsSymtab::s_init()
{
/* create our static hash function */
if (hash_func_ == 0)
hash_func_ = new CVmHashFuncCS();
}
/*
* static termination
*/
void CTcPrsSymtab::s_terminate()
{
/* delete our static hash function */
if (hash_func_ != 0)
{
delete hash_func_;
hash_func_ = 0;
}
}
/*
* initialize
*/
CTcPrsSymtab::CTcPrsSymtab(CTcPrsSymtab *parent_scope)
{
size_t hash_table_size;
/*
* Create the hash table. If we're at global scope (parent_scope ==
* 0), create a large hash table, since we'll probably add lots of
* symbols; otherwise, it's just a local table, which probably won't
* have many entries, so create a small table.
*
* Note that we always use the static hash function object, hence
* the table doesn't own the object.
*/
hash_table_size = (parent_scope == 0 ? 512 : 32);
hashtab_ = new (G_prsmem)
CVmHashTable(hash_table_size, hash_func_, FALSE,
new (G_prsmem) CVmHashEntry *[hash_table_size]);
/* remember the parent scope */
parent_ = parent_scope;
/* we're not in a debugger frame list yet */
list_index_ = 0;
list_next_ = 0;
}
/*
* delete
*/
CTcPrsSymtab::~CTcPrsSymtab()
{
/* delete our underlying hash table */
delete hashtab_;
}
/*
* Find a symbol, marking it as referenced if we find it.
*/
CTcSymbol *CTcPrsSymtab::find(const textchar_t *sym, size_t len,
CTcPrsSymtab **symtab)
{
CTcSymbol *entry;
/* find the symbol */
entry = find_noref(sym, len, symtab);
/* if we found an entry, mark it as referenced */
if (entry != 0)
entry->mark_referenced();
/* return the result */
return entry;
}
/*
* Find a symbol. This base version does not affect the 'referenced'
* status of the symbol we look up.
*/
CTcSymbol *CTcPrsSymtab::find_noref(const textchar_t *sym, size_t len,
CTcPrsSymtab **symtab)
{
CTcPrsSymtab *curtab;
/*
* Look for the symbol. Start in the current symbol table, and work
* outwards to the outermost enclosing table.
*/
for (curtab = this ; curtab != 0 ; curtab = curtab->get_parent())
{
CTcSymbol *entry;
/* look for the symbol in this table */
if ((entry = curtab->find_direct(sym, len)) != 0)
{
/*
* found it - if the caller wants to know about the symbol
* table in which we actually found the symbol, return that
* information
*/
if (symtab != 0)
*symtab = curtab;
/* return the symbol table entry we found */
return entry;
}
}
/* we didn't find the symbol - return failure */
return 0;
}
/*
* Find a symbol directly in this table
*/
CTcSymbol *CTcPrsSymtab::find_direct(const textchar_t *sym, size_t len)
{
/* return the entry from our hash table */
return (CTcSymbol *)get_hashtab()->find(sym, len);
}
/*
* Add a formal parameter symbol
*/
void CTcPrsSymtab::add_formal(const textchar_t *sym, size_t len,
int formal_num, int copy_str)
{
CTcSymLocal *lcl;
/*
* Make sure it's not already defined in our own symbol table - if
* it is, log an error and ignore the redundant definition. (We
* only care about our own scope, not enclosing scopes, since it's
* perfectly fine to hide variables in enclosing scopes.)
*/
if (get_hashtab()->find(sym, len) != 0)
{
/* log an error */
G_tok->log_error(TCERR_FORMAL_REDEF, (int)len, sym);
/* don't add the symbol again */
return;
}
/* create the symbol entry */
lcl = new CTcSymLocal(sym, len, copy_str, TRUE, formal_num);
/* add it to the table */
get_hashtab()->add(lcl);
}
/*
* Add a symbol to the table
*/
void CTcPrsSymtab::add_entry(CTcSymbol *sym)
{
/* add it to the table */
get_hashtab()->add(sym);
}
/*
* remove a symbol from the table
*/
void CTcPrsSymtab::remove_entry(CTcSymbol *sym)
{
/* remove it from the underyling hash table */
get_hashtab()->remove(sym);
}
/*
* Add a local variable symbol
*/
CTcSymLocal *CTcPrsSymtab::add_local(const textchar_t *sym, size_t len,
int local_num, int copy_str,
int init_assigned, int init_referenced)
{
CTcSymLocal *lcl;
/*
* make sure the symbol isn't already defined in this scope; if it
* is, log an error
*/
if (get_hashtab()->find(sym, len) != 0)
{
/* log the error */
G_tok->log_error(TCERR_LOCAL_REDEF, (int)len, sym);
/* don't create the symbol again - return the original definition */
return 0;
}
/* create the symbol entry */
lcl = new CTcSymLocal(sym, len, copy_str, FALSE, local_num);
/*
* if the symbol is initially to be marked as referenced or
* assigned, mark it now
*/
if (init_assigned)
lcl->set_val_assigned(TRUE);
if (init_referenced)
lcl->set_val_used(TRUE);
/* add it to the table */
get_hashtab()->add(lcl);
/* return the new local */
return lcl;
}
/*
* Add a code label ('goto') symbol
*/
CTcSymLabel *CTcPrsSymtab::add_code_label(const textchar_t *sym, size_t len,
int copy_str)
{
CTcSymLabel *lbl;
/*
* make sure the symbol isn't already defined in this scope; if it
* is, log an error
*/
if (get_hashtab()->find(sym, len) != 0)
{
/* log the error */
G_tok->log_error(TCERR_CODE_LABEL_REDEF, (int)len, sym);
/* don't create the symbol again - return the original definition */
return 0;
}
/* create the symbol entry */
lbl = new CTcSymLabel(sym, len, copy_str);
/* add it to the table */
get_hashtab()->add(lbl);
/* return the new label */
return lbl;
}
/*
* Find a symbol; if the symbol isn't defined, add a new entry according
* to the action flag. Because we add a symbol entry if the symbol
* isn't defined, this *always* returns a valid symbol object.
*/
CTcSymbol *CTcPrsSymtab::find_or_def(const textchar_t *sym, size_t len,
int copy_str, tcprs_undef_action action)
{
CTcSymbol *entry;
CTcPrsSymtab *curtab;
/*
* Look for the symbol. Start in the current symbol table, and work
* outwards to the outermost enclosing table.
*/
for (curtab = this ; ; curtab = curtab->get_parent())
{
/* look for the symbol in this table */
entry = (CTcSymbol *)curtab->get_hashtab()->find(sym, len);
if (entry != 0)
{
/* mark the entry as referenced */
entry->mark_referenced();
/* found it - return the symbol */
return entry;
}
/*
* If there's no parent symbol table, the symbol is undefined.
* Add a new symbol according to the action parameter. Note
* that we always add the new symbol at global scope, hence we
* add it to 'curtab', not 'this'.
*/
if (curtab->get_parent() == 0)
{
/* check which action we're being asked to perform */
switch(action)
{
case TCPRS_UNDEF_ADD_UNDEF:
/* add an "undefined" entry - log an error */
G_tok->log_error(TCERR_UNDEF_SYM, (int)len, sym);
/* create a new symbol of type undefined */
entry = new CTcSymUndef(sym, len, copy_str);
/* finish up */
goto add_entry;
case TCPRS_UNDEF_ADD_PROP:
/* add a new "property" entry - log a warning */
G_tok->log_warning(TCERR_ASSUME_SYM_PROP, (int)len, sym);
/* create a new symbol of type property */
entry = new CTcSymProp(sym, len, copy_str,
G_cg->new_prop_id());
/* finish up */
goto add_entry;
case TCPRS_UNDEF_ADD_PROP_NO_WARNING:
/* add a new property entry with no warning */
entry = new CTcSymProp(sym, len, copy_str,
G_cg->new_prop_id());
/* finish up */
goto add_entry;
add_entry:
/* add the new entry to the global table */
add_to_global_symtab(curtab, entry);
/* return the new entry */
return entry;
}
}
}
}
/*
* Enumerate the entries in a symbol table
*/
void CTcPrsSymtab::enum_entries(void (*func)(void *, CTcSymbol *),
void *ctx)
{
/*
* Ask the hash table to perform the enumeration. We know that all
* of our entries in the symbol table are CTcSymbol objects, so we
* can coerce the callback function to the appropriate type without
* danger.
*/
get_hashtab()->enum_entries((void (*)(void *, CVmHashEntry *))func, ctx);
}
/*
* Scan the symbol table for unreferenced local variables
*/
void CTcPrsSymtab::check_unreferenced_locals()
{
/* skip the check if we're only parsing for syntax */
if (!G_prs->get_syntax_only())
{
/* run the symbols through our unreferenced local check callback */
enum_entries(&unref_local_cb, this);
}
}
/*
* Enumeration callback to check for unreferenced locals
*/
void CTcPrsSymtab::unref_local_cb(void *, CTcSymbol *sym)
{
/* check the symbol */
sym->check_local_references();
}
/* ------------------------------------------------------------------------ */
/*
* Comma node
*/
/*
* fold constants
*/
CTcPrsNode *CTPNCommaBase::fold_binop()
{
/* use the normal addition folder */
return S_op_comma.eval_constant(left_, right_);
}
/* ------------------------------------------------------------------------ */
/*
* Addition parse node
*/
/*
* Fold constants. We override the default fold_constants() for
* addition nodes because addition constancy can be affected by symbol
* resolution. In particular, if we resolve symbols in a list, the list
* could turn constant, which could in turn make the result of an
* addition operator with the list as an operand turn constant.
*/
CTcPrsNode *CTPNAddBase::fold_binop()
{
/* use the normal addition folder */
return S_op_add.eval_constant(left_, right_);
}
/* ------------------------------------------------------------------------ */
/*
* Subtraction parse node
*/
/*
* Fold constants. We override the default fold_constants() for the
* subtraction node because subtraction constancy can be affected by
* symbol resolution. In particular, if we resolve symbols in a list,
* the list could turn constant, which could in turn make the result of
* a subtraction operator with the list as an operand turn constant.
*/
CTcPrsNode *CTPNSubBase::fold_binop()
{
/* use the normal addition folder */
return S_op_sub.eval_constant(left_, right_);
}
/* ------------------------------------------------------------------------ */
/*
* Equality Comparison parse node
*/
/*
* fold constants
*/
CTcPrsNode *CTPNEqBase::fold_binop()
{
/* use the normal addition folder */
return S_op_eq.eval_constant(left_, right_);
}
/* ------------------------------------------------------------------------ */
/*
* Inequality Comparison parse node
*/
/*
* fold constants
*/
CTcPrsNode *CTPNNeBase::fold_binop()
{
/* use the normal addition folder */
return S_op_ne.eval_constant(left_, right_);
}
/* ------------------------------------------------------------------------ */
/*
* Logical AND parse node
*/
/*
* fold constants
*/
CTcPrsNode *CTPNAndBase::fold_binop()
{
/* use the normal addition folder */
return S_op_and.eval_constant(left_, right_);
}
/* ------------------------------------------------------------------------ */
/*
* Logical OR parse node
*/
/*
* fold constants
*/
CTcPrsNode *CTPNOrBase::fold_binop()
{
/* use the normal addition folder */
return S_op_or.eval_constant(left_, right_);
}
/* ------------------------------------------------------------------------ */
/*
* NOT parse node
*/
/*
* fold constants
*/
CTcPrsNode *CTPNNotBase::fold_unop()
{
/* use the normal addition folder */
return S_op_unary.eval_const_not(sub_);
}
/* ------------------------------------------------------------------------ */
/*
* Subscript parse node
*/
/*
* Fold constants. We override the default fold_constants() for
* subscript nodes because subscript constancy can be affected by symbol
* resolution. In particular, if we resolve symbols in a list, the list
* could turn constant, which could in turn make the result of a
* subscript operator with the list as an operand turn constant.
*/
/* ------------------------------------------------------------------------ */
/*
* Equality Comparison parse node
*/
/*
* fold constants
*/
CTcPrsNode *CTPNSubscriptBase::fold_binop()
{
/* use the normal addition folder */
return S_op_unary.eval_const_subscript(left_, right_);
}
/* ------------------------------------------------------------------------ */
/*
* Parser Symbol Table Entry base class
*/
/*
* Allocate symbol objects from the parse pool, since these objects have
* all of the lifespan characteristics of pool objects.
*/
void *CTcSymbolBase::operator new(size_t siz)
{
return G_prsmem->alloc(siz);
}
/*
* Write to a symbol file.
*/
int CTcSymbolBase::write_to_sym_file(CVmFile *fp)
{
/* do the basic writing */
return write_to_file_gen(fp);
}
/*
* Write to a file. This is a generic base routine that can be used for
* writing to a symbol or object file.
*/
int CTcSymbolBase::write_to_file_gen(CVmFile *fp)
{
/* write my type */
fp->write_int2((int)get_type());
/* write my name */
return write_name_to_file(fp);
}
/*
* Write the symbol name to a file
*/
int CTcSymbolBase::write_name_to_file(CVmFile *fp)
{
/* write the length of my symbol name, followed by the symbol name */
fp->write_int2((int)get_sym_len());
/* write the symbol string */
fp->write_bytes(get_sym(), get_sym_len());
/* we wrote the symbol */
return TRUE;
}
/*
* Read a symbol from a symbol file
*/
CTcSymbol *CTcSymbolBase::read_from_sym_file(CVmFile *fp)
{
tc_symtype_t typ;
/*
* read the type - this is the one thing we know is always present
* for every symbol (the rest of the data might vary per subclass)
*/
typ = (tc_symtype_t)fp->read_uint2();
/* create the object based on the type */
switch(typ)
{
case TC_SYM_FUNC:
return CTcSymFunc::read_from_sym_file(fp);
case TC_SYM_OBJ:
return CTcSymObj::read_from_sym_file(fp);
case TC_SYM_PROP:
return CTcSymProp::read_from_sym_file(fp);
case TC_SYM_ENUM:
return CTcSymEnum::read_from_sym_file(fp);
default:
/* other types should not be in a symbol file */
G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_SYMEXP_INV_TYPE);
return 0;
}
}
/*
* Read the basic information from the symbol file
*/
const char *CTcSymbolBase::base_read_from_sym_file(CVmFile *fp)
{
char buf[TOK_SYM_MAX_LEN + 1];
/* read, null-terminate, and return the string */
return CTcParser::read_len_prefix_str(fp, buf, sizeof(buf), 0,
TCERR_SYMEXP_SYM_TOO_LONG);
}
/*
* Write to an object file.
*/
int CTcSymbolBase::write_to_obj_file(CVmFile *fp)
{
/* do the basic writing */
return write_to_file_gen(fp);
}
/* ------------------------------------------------------------------------ */
/*
* function symbol entry base
*/
/*
* fold function name into a function address
*/
CTcPrsNode *CTcSymFuncBase::fold_constant()
{
CTcConstVal cval;
/* set up the function pointer constant */
cval.set_funcptr((CTcSymFunc *)this);
/* return a constant node with the function pointer */
return new CTPNConst(&cval);
}
/*
* Write to a symbol file
*/
int CTcSymFuncBase::write_to_sym_file(CVmFile *fp)
{
char buf[5];
CTcSymFunc *cur;
int ext_modify;
/* scan for the bottom of our modify stack */
for (cur = get_mod_base() ; cur != 0 && cur->get_mod_base() != 0 ;
cur = cur->get_mod_base()) ;
/* we modify an external if the bottom of our modify stack is external */
ext_modify = (cur != 0 && cur->is_extern());
/*
* If we're external, don't bother writing to the file - if we're
* importing a function, we don't want to export it as well. Note that
* a function that is replacing or modifying an external function is
* fundamentally external itself, because the function must be defined
* in another file to be replaceable/modifiable.
*
* As an exception, if this is a multi-method base symbol, and a
* multi-method with this name is defined in this file, export it even
* though it's technically an extern symbol. We don't export most
* extern symbols because we count on the definer to export them, but
* in the case of multi-method base symbols, there is no definer - the
* base symbol is basically a placeholder to be filled in by the
* linker. So *someone* has to export these. The logical place to
* export them is from any file that defines a multi-method based on
* the base symbol.
*/
if ((is_extern_ || ext_replace_ || ext_modify) && !mm_def_)
return FALSE;
/* inherit default */
CTcSymbol::write_to_sym_file(fp);
/* write our argument count, varargs flag, and return value flag */
oswp2(buf, argc_);
buf[2] = (varargs_ != 0);
buf[3] = (has_retval_ != 0);
buf[4] = (is_multimethod_ ? 1 : 0)
| (is_multimethod_base_ ? 2 : 0);
fp->write_bytes(buf, 5);
/* we wrote the symbol */
return TRUE;
}
/*
* add an absolute fixup to my list
*/
void CTcSymFuncBase::add_abs_fixup(CTcDataStream *ds, ulong ofs)
{
/* ask the code body to add the fixup */
CTcAbsFixup::add_abs_fixup(&fixups_, ds, ofs);
}
/*
* add an absolute fixup at the current stream offset
*/
void CTcSymFuncBase::add_abs_fixup(CTcDataStream *ds)
{
/* ask the code body to add the fixup */
CTcAbsFixup::add_abs_fixup(&fixups_, ds, ds->get_ofs());
}
/*
* Read from a symbol file
*/
CTcSymbol *CTcSymFuncBase::read_from_sym_file(CVmFile *fp)
{
char symbuf[4096];
const char *sym;
char info[5];
int argc;
int varargs;
int has_retval;
int is_multimethod, is_multimethod_base;
/*
* Read the symbol name. Use a custom reader instead of the base
* reader, because function symbols can be quite long, due to
* multimethod name decoration.
*/
if ((sym = CTcParser::read_len_prefix_str(
fp, symbuf, sizeof(symbuf), 0, TCERR_SYMEXP_SYM_TOO_LONG)) == 0)
return 0;
/* read the argument count, varargs flag, and return value flag */
fp->read_bytes(info, 5);
argc = osrp2(info);
varargs = (info[2] != 0);
has_retval = (info[3] != 0);
is_multimethod = ((info[4] & 1) != 0);
is_multimethod_base = ((info[4] & 2) != 0);
/* create and return the new symbol */
return new CTcSymFunc(sym, strlen(sym), FALSE, argc,
varargs, has_retval,
is_multimethod, is_multimethod_base, TRUE);
}
/*
* Write to an object file
*/
int CTcSymFuncBase::write_to_obj_file(CVmFile *fp)
{
char buf[10];
CTcSymFunc *cur;
CTcSymFunc *last_mod;
int mod_body_cnt;
int ext_modify;
/*
* If it's external, and we have no fixups, don't bother writing it to
* the object file. If there are no fixups, we don't have any
* references to the function, hence there's no need to include it in
* the object file.
*/
if (is_extern_ && fixups_ == 0)
return FALSE;
/*
* If we have a modified base function, scan down the chain of modified
* bases until we reach the last one. If it's external, we need to
* note this, and we need to store the fixup list for the external
* symbol so that we can explicitly link it to the imported symbol at
* link time.
*/
for (mod_body_cnt = 0, last_mod = 0, cur = get_mod_base() ; cur != 0 ;
last_mod = cur, cur = cur->get_mod_base())
{
/* if this one has an associated code body, count it */
if (cur->get_code_body() != 0 && !cur->get_code_body()->is_replaced())
++mod_body_cnt;
}
/* we modify an external if the last in the modify chain is external */
ext_modify = (last_mod != 0 && last_mod->is_extern());
/* inherit default */
CTcSymbol::write_to_obj_file(fp);
/*
* write our argument count, varargs flag, return value, extern flags,
* and the number of our modified base functions with code bodies
*/
oswp2(buf, argc_);
buf[2] = (varargs_ != 0);
buf[3] = (has_retval_ != 0);
buf[4] = (is_extern_ != 0);
buf[5] = (ext_replace_ != 0);
buf[6] = (ext_modify != 0);
buf[7] = (is_multimethod_ ? 1 : 0)
| (is_multimethod_base_ ? 2 : 0);
oswp2(buf + 8, mod_body_cnt);
fp->write_bytes(buf, 10);
/* if we modify an external, save its fixup list */
if (ext_modify)
CTcAbsFixup::write_fixup_list_to_object_file(fp, last_mod->fixups_);
/* write the code stream offsets of the modified base function bodies */
for (cur = get_mod_base() ; cur != 0 ; cur = cur->get_mod_base())
{
/* if this one has a code body, write its code stream offset */
if (cur->get_code_body() != 0)
fp->write_int4(cur->get_anchor()->get_ofs());
}
/*
* If we're defined as external, write our fixup list. Since this
* is an external symbol, it will have no anchor in the code stream,
* hence we need to write our fixup list with the symbol and not
* with the anchor.
*/
if (is_extern_)
CTcAbsFixup::write_fixup_list_to_object_file(fp, fixups_);
/* we wrote the symbol */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* local variable symbol entry base
*/
/*
* initialize
*/
CTcSymLocalBase::CTcSymLocalBase(const char *str, size_t len, int copy,
int is_param, int var_num)
: CTcSymbol(str, len, copy, (is_param ? TC_SYM_PARAM : TC_SYM_LOCAL))
{
/* remember the information */
var_num_ = var_num;
is_param_ = is_param;
/* presume it's a regular stack variable (not a context local) */
is_ctx_local_ = FALSE;
ctx_orig_ = 0;
ctx_var_num_ = 0;
ctx_level_ = 0;
ctx_var_num_set_ = FALSE;
/* so far, the value isn't used anywhere */
val_used_ = FALSE;
val_assigned_ = FALSE;
/* the symbol has not been referenced so far */
referenced_ = FALSE;
/* remember where the symbol is defined in the source file */
G_tok->get_last_pos(&src_desc_, &src_linenum_);
}
/*
* Mark the value of the variable as used
*/
void CTcSymLocalBase::set_val_used(int f)
{
/* note the new status */
val_used_ = f;
/* if we have now assigned the value, propagate to the original */
if (f && ctx_orig_ != 0)
ctx_orig_->set_val_used(TRUE);
}
/*
* Mark the value of the variable as assigned
*/
void CTcSymLocalBase::set_val_assigned(int f)
{
/* note the new status */
val_assigned_ = f;
/* if we have now assigned the value, propagate to the original */
if (f && ctx_orig_ != 0)
ctx_orig_->set_val_assigned(TRUE);
}
/*
* Check for references to this local
*/
void CTcSymLocalBase::check_local_references()
{
int err;
tc_severity_t sev = TC_SEV_WARNING;
/*
* if this isn't an original, but is simply a copy of a variable
* inherited from an enclosing scope (such as into an anonymous
* function), don't bother even checking for errors - we'll let the
* original symbol take care of reporting its own errors
*/
if (ctx_orig_ != 0)
return;
/*
* if it's unreferenced or unassigned (or both), log an error; note
* that a formal parameter is always assigned, since the value is
* assigned by the caller
*/
if (!get_val_used() && (!get_val_assigned() && !is_param()))
{
/* the variable is never used at all */
err = TCERR_UNREFERENCED_LOCAL;
}
else if (!get_val_used())
{
if (is_param() || is_list_param())
{
/*
* it's a parameter, or a local that actually contains a
* varargs parameter list - generate only a pedantic error
*/
sev = TC_SEV_PEDANTIC;
err = TCERR_UNREFERENCED_PARAM;
}
else
{
/* this local is assigned a value that's never used */
err = TCERR_UNUSED_LOCAL_ASSIGNMENT;
}
}
else if (!get_val_assigned() && !is_param())
{
/* it's used but never assigned */
err = TCERR_UNASSIGNED_LOCAL;
}
else
{
/* no error */
return;
}
/*
* display the warning message, showing the error location as the
* source line where the local was defined
*/
G_tcmain->log_error(get_src_desc(), get_src_linenum(),
sev, err, (int)get_sym_len(), get_sym());
}
/*
* create a new context variable copy of this symbol
*/
CTcSymbol *CTcSymLocalBase::new_ctx_var() const
{
CTcSymLocal *lcl;
/* create a new local with the same name */
lcl = new CTcSymLocal(get_sym(), get_sym_len(), FALSE, FALSE, 0);
/* refer the copy back to the original (i.e., me) */
lcl->set_ctx_orig((CTcSymLocal *)this);
/* set up the context variable information */
if (!is_ctx_local_)
{
/*
* The original is a true local - we're at the first context
* level, and we don't yet have a property assigned, since we
* don't know if this variable is actually going to be
* referenced.
*/
lcl->set_ctx_level(1);
}
else
{
/*
* The original was already a context variable - we're at one
* higher context level in this function, and we use the same
* context property as the original did.
*/
lcl->set_ctx_level(ctx_level_ + 1);
}
/* return the new symbol */
return lcl;
}
/*
* Apply context variable conversion
*/
int CTcSymLocalBase::apply_ctx_var_conv(CTcPrsSymtab *symtab,
CTPNCodeBody *code_body)
{
/*
* if this symbol isn't referenced, simply delete it from the table,
* so that it doesn't get entered in the debug records; and there's
* no need to propagate it back to the enclosing scope as a context
* variable, since it's not referenced from this enclosed scope
*/
if (!referenced_)
{
/* remove the symbol from the table */
symtab->remove_entry(this);
/* this variable doesn't need to be converted */
return FALSE;
}
/*
* convert the symbol in the enclosing scope to a context local, if
* it's not already
*/
if (ctx_orig_ != 0)
{
/* convert the original to a context symbol */
ctx_orig_->convert_to_ctx_var(get_val_used(), get_val_assigned());
/*
* ask the code body for the context object's local variable for
* our recursion level
*/
ctx_var_num_ = code_body->get_or_add_ctx_var_for_level(ctx_level_);
/* note that we've set our context variable ID */
ctx_var_num_set_ = TRUE;
/* this variable was converted */
return TRUE;
}
/* this variable wasn't converted */
return FALSE;
}
/*
* convert this variable to a context variable
*/
void CTcSymLocalBase::convert_to_ctx_var(int val_used, int val_assigned)
{
/* if I'm not already a context local, mark me as a context local */
if (!is_ctx_local_)
{
/* mark myself as a context local */
is_ctx_local_ = TRUE;
/*
* we haven't yet assigned our local context variable, since
* we're still processing the inner scope at this point; just
* store placeholders for now so we know to come back and fix
* this up
*/
ctx_var_num_ = 0;
ctx_arr_idx_ = 0;
}
/* note that I've been referenced */
mark_referenced();
/* propagate the value-used and value-assigned flags */
if (val_used)
set_val_used(TRUE);
if (val_assigned)
set_val_assigned(TRUE);
/* propagate the conversion to the original symbol */
if (ctx_orig_ != 0)
ctx_orig_->convert_to_ctx_var(val_used, val_assigned);
}
/*
* finish the context variable conversion
*/
void CTcSymLocalBase::finish_ctx_var_conv()
{
/*
* If this isn't already marked as a context variable, there's
* nothing to do - this variable must not have been referenced from
* an anonymous function yet, and hence can be kept in the stack.
*
* Similarly, if my context local variable number has been assigned
* already, there's nothing to do - we must have been set to refer
* back to a context variable in an enclosing scope (this can happen
* in a nested anonymous function).
*/
if (!is_ctx_local_ || ctx_var_num_set_)
return;
/*
* tell the parser to create a local context for this scope, if it
* hasn't already
*/
G_prs->init_local_ctx();
/* use the local context variable specified by the parser */
ctx_var_num_ = G_prs->get_local_ctx_var();
ctx_var_num_set_ = TRUE;
/* assign our array index */
if (ctx_arr_idx_ == 0)
ctx_arr_idx_ = G_prs->alloc_ctx_arr_idx();
}
/*
* Get my context variable array index
*/
int CTcSymLocalBase::get_ctx_arr_idx() const
{
/*
* if I'm based on an original symbol from another scope, use the
* same property ID as the original symbol
*/
if (ctx_orig_ != 0)
return ctx_orig_->get_ctx_arr_idx();
/* return my context property */
return ctx_arr_idx_;
}
/* ------------------------------------------------------------------------ */
/*
* object symbol entry base
*/
/*
* fold the symbol as a constant
*/
CTcPrsNode *CTcSymObjBase::fold_constant()
{
CTcConstVal cval;
/* set up the object constant */
cval.set_obj(get_obj_id(), get_metaclass());
/* return a constant node */
return new CTPNConst(&cval);
}
/*
* Write to a symbol file
*/
int CTcSymObjBase::write_to_sym_file(CVmFile *fp)
{
int result;
/*
* If we're external, don't bother writing to the file - if we're
* importing an object, we don't want to export it as well. If it's
* modified, don't write it either, because modified symbols cannot
* be referenced directly by name (the symbol for a modified object
* is a fake symbol anyway). In addition, don't write the symbol if
* it's a 'modify' or 'replace' definition that applies to an
* external base object - instead, we'll pick up the symbol from the
* other symbol file with the original definition.
*/
if (is_extern_ || modified_ || ext_modify_ || ext_replace_)
return FALSE;
/* inherit default */
result = CTcSymbol::write_to_sym_file(fp);
/* if that was successful, write additional object-type-specific data */
if (result)
{
/* write the metaclass ID */
fp->write_int2((int)metaclass_);
/* if it's of metaclass tads-object, write superclass information */
if (metaclass_ == TC_META_TADSOBJ)
{
char c;
size_t cnt;
CTPNSuperclass *sc;
/*
* set up our flags: indicate whether or not we're explicitly
* based on the root object class, and if we're a 'class'
* object
*/
c = ((sc_is_root() ? 1 : 0)
| (is_class() ? 2 : 0)
| (is_transient() ? 4 : 0));
fp->write_bytes(&c, 1);
/* count the declared superclasses */
for (cnt = 0, sc = sc_name_head_ ; sc != 0 ;
sc = sc->nxt_, ++cnt) ;
/*
* write the number of declared superclasses followed by the
* names of the superclasses
*/
fp->write_int2(cnt);
for (sc = sc_name_head_ ; sc != 0 ; sc = sc->nxt_)
{
/* write the counted-length identifier */
fp->write_int2(sc->get_sym_len());
fp->write_bytes(sc->get_sym_txt(), sc->get_sym_len());
}
}
}
/* return the result */
return result;
}
/*
* Read from a symbol file
*/
CTcSymbol *CTcSymObjBase::read_from_sym_file(CVmFile *fp)
{
const char *txt;
tc_metaclass_t meta;
CTcSymObj *sym;
char c;
size_t cnt;
size_t i;
/* read the symbol name */
if ((txt = base_read_from_sym_file(fp)) == 0)
return 0;
/* read the metaclass ID */
meta = (tc_metaclass_t)fp->read_uint2();
/*
* If it's a dictionary object, check to see if it's already defined -
* a dictionary object can be exported from multiple modules without
* error, since dictionaries are shared across modules.
*
* The same applies to grammar productions, since a grammar production
* can be implicitly created in multiple files.
*/
if (meta == TC_META_DICT || meta == TC_META_GRAMPROD)
{
CTcSymbol *old_sym;
/* look for a previous instance of the symbol */
old_sym = G_prs->get_global_symtab()->find(txt, strlen(txt));
if (old_sym != 0
&& old_sym->get_type() == TC_SYM_OBJ
&& ((CTcSymObj *)old_sym)->get_metaclass() == meta)
{
/*
* the dictionary is already in the symbol table - return the
* existing one, since there's no conflict with importing the
* dictionary from multiple places
*/
return old_sym;
}
}
/* create the new symbol */
sym = new CTcSymObj(txt, strlen(txt), FALSE, G_cg->new_obj_id(),
TRUE, meta, 0);
/* if the metaclass is tads-object, read additional information */
if (meta == TC_META_TADSOBJ)
{
/* read the root-object-superclass flag and the class-object flag */
fp->read_bytes(&c, 1);
sym->set_sc_is_root((c & 1) != 0);
sym->set_is_class((c & 2) != 0);
if ((c & 4) != 0)
sym->set_transient();
/* read the number of superclasses, and read the superclass names */
cnt = fp->read_uint2();
for (i = 0 ; i < cnt ; ++i)
{
char buf[TOK_SYM_MAX_LEN + 1];
const char *sc_txt;
size_t sc_len;
/* read the symbol */
sc_txt = CTcParser::read_len_prefix_str(
fp, buf, sizeof(buf), &sc_len, TCERR_SYMEXP_SYM_TOO_LONG);
/* add the superclass list entry to the symbol */
sym->add_sc_name_entry(sc_txt, sc_len);
}
}
/* return the symbol */
return sym;
}
/*
* Add a superclass name entry.
*/
void CTcSymObjBase::add_sc_name_entry(const char *txt, size_t len)
{
CTPNSuperclass *entry;
/* create the entry object */
entry = new CTPNSuperclass(txt, len);
/* link it into our list */
if (sc_name_tail_ != 0)
sc_name_tail_->nxt_ = entry;
else
sc_name_head_ = entry;
sc_name_tail_ = entry;
}
/*
* Check to see if I have a given superclass.
*/
int CTcSymObjBase::has_superclass(class CTcSymObj *sc_sym) const
{
CTPNSuperclass *entry;
/*
* Scan my direct superclasses. For each one, check to see if my
* superclass matches the given superclass, or if my superclass
* inherits from the given superclass.
*/
for (entry = sc_name_head_ ; entry != 0 ; entry = entry->nxt_)
{
CTcSymObj *entry_sym;
/* look up this symbol */
entry_sym = (CTcSymObj *)G_prs->get_global_symtab()->find(
entry->get_sym_txt(), entry->get_sym_len());
/*
* if the entry's symbol doesn't exist or isn't an object symbol,
* skip it
*/
if (entry_sym == 0 || entry_sym->get_type() != TC_SYM_OBJ)
continue;
/*
* if it matches the given superclass, we've found the given
* superclass among our direct superclasses, so we definitely have
* the given superclass
*/
if (entry_sym == sc_sym)
return TRUE;
/*
* ask the symbol if the given class is among its direct or
* indirect superclasses - if it's a superclass of my superclass,
* it's also my superclass
*/
if (entry_sym->has_superclass(sc_sym))
return TRUE;
}
/*
* we didn't find the given class anywhere among our superclasses or
* their superclasses, so it must not be a superclass of ours
*/
return FALSE;
}
/*
* Synthesize a placeholder symbol for a modified object.
*
* The new symbol is not for use by the source code; we add it merely as
* a placeholder. Build its name starting with a space so that it can
* never be reached from source code, and use the object number to
* ensure it's unique within the file.
*/
CTcSymObj *CTcSymObjBase::synthesize_modified_obj_sym(int anon)
{
char nm[TOK_SYM_MAX_LEN + 1];
const char *stored_nm;
tc_obj_id mod_id;
CTcSymObj *mod_sym;
size_t len;
/* create a new ID for the modified object */
mod_id = G_cg->new_obj_id();
/* build the name */
if (anon)
{
/* it's anonymous - we don't need a real name */
stored_nm = ".anon";
len = strlen(nm);
}
else
{
/* synthesize a name */
synthesize_modified_obj_name(nm, mod_id);
len = strlen(nm);
/* store it in tokenizer memory */
stored_nm = G_tok->store_source(nm, len);
}
/* create the object */
mod_sym = new CTcSymObj(stored_nm, len, FALSE, mod_id, FALSE,
TC_META_TADSOBJ, 0);
/* mark it as modified */
mod_sym->set_modified(TRUE);
/* add it to the symbol table, if it has a name */
if (!anon)
G_prs->get_global_symtab()->add_entry(mod_sym);
else
G_prs->add_anon_obj(mod_sym);
/* return the new symbol */
return mod_sym;
}
/*
* Build the name of a synthesized placeholder symbol for a given object
* number. The buffer should be TOK_SYM_MAX_LEN + 1 bytes long.
*/
void CTcSymObjBase::
synthesize_modified_obj_name(char *buf, tctarg_obj_id_t obj_id)
{
/*
* Build the fake name, based on the object ID to ensure uniqueness
* and so that we can look it up based on the object ID. Start it
* with a space so that no source token can ever refer to it.
*/
sprintf(buf, " %lx", (ulong)obj_id);
}
/*
* Add a deleted property entry
*/
void CTcSymObjBase::add_del_prop_to_list(CTcObjPropDel **list_head,
CTcSymProp *prop_sym)
{
CTcObjPropDel *entry;
/* create the new entry */
entry = new CTcObjPropDel(prop_sym);
/* link it into my list */
entry->nxt_ = *list_head;
*list_head = entry;
}
/*
* Add a self-reference fixup
*/
void CTcSymObjBase::add_self_ref_fixup(CTcDataStream *stream, ulong ofs)
{
/* add a fixup to our list */
CTcIdFixup::add_fixup(&fixups_, stream, ofs, obj_id_);
}
/*
* Write to a object file
*/
int CTcSymObjBase::write_to_obj_file(CVmFile *fp)
{
/*
* If the object is external and has never been referenced, don't
* bother writing it.
*
* In addition, if the object is marked as modified, don't write it.
* We write modified base objects specially, because we must control
* the order in which a modified base object is written relative its
* modifying object.
*/
if ((is_extern_ && !ref_) || modified_)
return FALSE;
/* if the object has already been written, don't write it again */
if (written_to_obj_)
{
/*
* if we've never been counted in the object file before, we
* must have been written indirectly in the course of writing
* another symbol - in this case, return true to indicate that
* we are in the file, even though we're not actually writing
* anything now
*/
if (!counted_in_obj_)
{
/* we've now been counted in the object file */
counted_in_obj_ = TRUE;
/* indicate that we have been written */
return TRUE;
}
else
{
/* we've already been written and counted - don't write again */
return FALSE;
}
}
/* do the main part of the writing */
return write_to_obj_file_main(fp);
}
/*
* Write the object symbol to an object file. This main routine does
* most of the actual work, once we've decided that we're actually going
* to write the symbol.
*/
int CTcSymObjBase::write_to_obj_file_main(CVmFile *fp)
{
char buf[32];
uint cnt;
CTcObjPropDel *delprop;
/* take the next object file index */
set_obj_file_idx(G_prs->get_next_obj_file_sym_idx());
/*
* if I have a dictionary object, make sure it's in the object file
* before I am - we need to be able to reference the object during
* load, so it has to be written before me
*/
if (dict_ != 0)
dict_->write_sym_to_obj_file(fp);
/*
* if I'm not anonymous, write the basic header information for the
* symbol (don't do this for anonymous objects, since they don't
* have a name to write)
*/
if (!anon_)
write_to_file_gen(fp);
/*
* write my object ID, so that we can translate from the local
* numbering system in the object file to the new numbering system
* in the image file
*/
oswp4(buf, obj_id_);
/* write the flags */
buf[4] = (is_extern_ != 0);
buf[5] = (ext_replace_ != 0);
buf[6] = (modified_ != 0);
buf[7] = (mod_base_sym_ != 0);
buf[8] = (ext_modify_ != 0);
buf[9] = (obj_stm_ != 0 && obj_stm_->is_class());
buf[10] = (transient_ != 0);
/* add the metaclass type */
oswp2(buf + 11, (int)metaclass_);
/* add the dictionary's object file index, if we have one */
if (dict_ != 0)
oswp2(buf + 13, dict_->get_obj_idx());
else
oswp2(buf + 13, 0);
/*
* add my object file index (we store this to eliminate any
* dependency on the load order - this allows us to write other
* symbols recursively without worrying about exactly where the
* recursion occurs relative to assigning the file index)
*/
oswp2(buf + 15, get_obj_file_idx());
/* write the data to the file */
fp->write_bytes(buf, 17);
/* if we're not external, write our stream address */
if (!is_extern_)
fp->write_int4(stream_ofs_);
/* if we're modifying another object, store some extra information */
if (mod_base_sym_ != 0)
{
/*
* Write our list of properties to be deleted from base objects
* at link time. First, count the properties in the list.
*/
for (cnt = 0, delprop = first_del_prop_ ; delprop != 0 ;
++cnt, delprop = delprop->nxt_) ;
/* write the count */
fp->write_int2(cnt);
/* write the deleted property list */
for (delprop = first_del_prop_ ; delprop != 0 ;
delprop = delprop->nxt_)
{
/*
* write out this property symbol (we write the symbol
* rather than the ID, because when we load the object file,
* we'll need to adjust the ID to new global numbering
* system in the image file; the easiest way to do this is
* to write the symbol and look it up at load time)
*/
fp->write_int2(delprop->prop_sym_->get_sym_len());
fp->write_bytes(delprop->prop_sym_->get_sym(),
delprop->prop_sym_->get_sym_len());
}
}
/* write our self-reference fixup list */
CTcIdFixup::write_to_object_file(fp, fixups_);
/*
* If this is a modifying object, we must write the entire chain of
* modified base objects immediately after this object. When we're
* reading the symbol table, this ensures that we can read each
* modified base object recursively as we read its modifiers, which
* is necessary so that we can build up the same modification chain
* on loading the object file.
*/
if (mod_base_sym_ != 0)
{
/* write the main part of the definition */
mod_base_sym_->write_to_obj_file_main(fp);
}
/* mark the object as written to the file */
written_to_obj_ = TRUE;
/* written */
return TRUE;
}
/*
* Write cross-references to the object file
*/
int CTcSymObjBase::write_refs_to_obj_file(CVmFile *fp)
{
CTPNSuperclass *sc;
uint cnt;
long cnt_pos;
long end_pos;
CTcVocabEntry *voc;
/*
* if this symbol wasn't written to the object file in the first
* place, we obviously don't want to include any extra data for it
*/
if (!written_to_obj_)
return FALSE;
/* write my symbol index */
fp->write_int4(get_obj_file_idx());
/* write a placeholder superclass count */
cnt_pos = fp->get_pos();
fp->write_int2(0);
/* write my superclass list */
for (sc = (obj_stm_ != 0 ? obj_stm_->get_first_sc() : 0), cnt = 0 ;
sc != 0 ; sc = sc->nxt_)
{
CTcSymObj *sym;
/* look up this superclass symbol */
sym = (CTcSymObj *)sc->get_sym();
if (sym != 0 && sym->get_type() == TC_SYM_OBJ)
{
/* write the superclass symbol index */
fp->write_int4(sym->get_obj_file_idx());
/* count it */
++cnt;
}
}
/* go back and write the superclass count */
end_pos = fp->get_pos();
fp->set_pos(cnt_pos);
fp->write_int2(cnt);
fp->set_pos(end_pos);
/* count my vocabulary words */
for (cnt = 0, voc = vocab_ ; voc != 0 ; ++cnt, voc = voc->nxt_) ;
/* write my vocabulary words */
fp->write_int2(cnt);
for (voc = vocab_ ; voc != 0 ; voc = voc->nxt_)
{
/* write the text of the word */
fp->write_int2(voc->len_);
fp->write_bytes(voc->txt_, voc->len_);
/* write the property ID */
fp->write_int2(voc->prop_);
}
/* indicate that we wrote the symbol */
return TRUE;
}
/*
* Load references from the object file
*/
void CTcSymObjBase::load_refs_from_obj_file(CVmFile *fp, const char *,
tctarg_obj_id_t *,
tctarg_prop_id_t *prop_xlat)
{
uint i;
uint cnt;
CTcObjScEntry *sc_tail;
/* read the superclass count */
cnt = fp->read_uint2();
/* read the superclass list */
for (sc_tail = 0, i = 0 ; i < cnt ; ++i)
{
ulong idx;
CTcSymObj *sym;
CTcObjScEntry *sc;
/* read the next index */
idx = fp->read_uint4();
/* get the symbol */
sym = (CTcSymObj *)G_prs->get_objfile_sym(idx);
if (sym->get_type() != TC_SYM_OBJ)
sym = 0;
/* create a new list entry */
sc = new (G_prsmem) CTcObjScEntry(sym);
/* link it in at the end of the my superclass list */
if (sc_tail != 0)
sc_tail->nxt_ = sc;
else
sc_ = sc;
/* this is now the last entry in my superclass list */
sc_tail = sc;
}
/* load the vocabulary words */
cnt = fp->read_uint2();
for (i = 0 ; i < cnt ; ++i)
{
size_t len;
char *txt;
tctarg_prop_id_t prop;
/* read the length of this word's text */
len = fp->read_uint2();
/* allocate parser memory for the word's text */
txt = (char *)G_prsmem->alloc(len);
/* read the word into the allocated text buffer */
fp->read_bytes(txt, len);
/* read the property */
prop = (tctarg_prop_id_t)fp->read_uint2();
/* translate the property to the new numbering system */
prop = prop_xlat[prop];
/* add the word to our vocabulary */
add_vocab_word(txt, len, prop);
}
}
/*
* Add a word to my vocabulary
*/
void CTcSymObjBase::add_vocab_word(const char *txt, size_t len,
tctarg_prop_id_t prop)
{
CTcVocabEntry *entry;
/* create a new vocabulary entry */
entry = new (G_prsmem) CTcVocabEntry(txt, len, prop);
/* link it into my list */
entry->nxt_ = vocab_;
vocab_ = entry;
}
/*
* Delete a vocabulary property from my list (for 'replace')
*/
void CTcSymObjBase::delete_vocab_prop(tctarg_prop_id_t prop)
{
CTcVocabEntry *entry;
CTcVocabEntry *prv;
CTcVocabEntry *nxt;
/* scan my list and delete each word defined for the given property */
for (prv = 0, entry = vocab_ ; entry != 0 ; entry = nxt)
{
/* remember the next entry */
nxt = entry->nxt_;
/* if this entry is for the given property, unlink it */
if (entry->prop_ == prop)
{
/*
* it matches - unlink it from the list (note that we don't
* have to delete the entry, because it's allocated in
* parser memory and thus will be deleted when the parser is
* deleted)
*/
if (prv != 0)
prv->nxt_ = nxt;
else
vocab_ = nxt;
/*
* this entry is no longer in any list (we don't really have
* to clear the 'next' pointer here, since nothing points to
* 'entry' any more, but doing so will make it obvious that
* the entry was removed from the list, which could be handy
* during debugging from time to time)
*/
entry->nxt_ = 0;
}
else
{
/*
* this entry is still in the list, so it's now the previous
* entry for our scan
*/
prv = entry;
}
}
}
/*
* Build my dictionary
*/
void CTcSymObjBase::build_dictionary()
{
CTcVocabEntry *entry;
/* if I don't have a dictionary, there's nothing to do */
if (dict_ == 0)
return;
/*
* if I'm a class, there's nothing to do, since vocabulary defined
* in a class is only entered in the dictionary for the instances of
* the class, not for the class itself
*/
if (is_class_)
return;
/* add inherited words from my superclasses to my list */
inherit_vocab();
/* add each of my words to the dictionary */
for (entry = vocab_ ; entry != 0 ; entry = entry->nxt_)
{
/* add this word to my dictionary */
dict_->add_word(entry->txt_, entry->len_, FALSE,
obj_id_, entry->prop_);
}
}
/*
* Add my words to the dictionary, associating the words with the given
* object. This can be used to add my own words to the dictionary or to
* add my words to a subclass's dictionary.
*/
void CTcSymObjBase::inherit_vocab()
{
CTcObjScEntry *sc;
/*
* if I've already inherited my superclass vocabulary, there's
* nothing more we need to do
*/
if (vocab_inherited_)
return;
/* make a note that I've inherited my superclass vocabulary */
vocab_inherited_ = TRUE;
/* inherit words from each superclass */
for (sc = sc_ ; sc != 0 ; sc = sc->nxt_)
{
/* make sure this superclass has built its inherited list */
sc->sym_->inherit_vocab();
/* add this superclass's words to my list */
sc->sym_->add_vocab_to_subclass((CTcSymObj *)this);
}
}
/*
* Add my vocabulary words to the given subclass's vocabulary list
*/
void CTcSymObjBase::add_vocab_to_subclass(CTcSymObj *sub)
{
CTcVocabEntry *entry;
/* add each of my words to the subclass */
for (entry = vocab_ ; entry != 0 ; entry = entry->nxt_)
{
/* add this word to my dictionary */
sub->add_vocab_word(entry->txt_, entry->len_, entry->prop_);
}
}
/*
* Set my base 'modify' object. This tells us the object that we're
* modifying.
*/
void CTcSymObjBase::set_mod_base_sym(CTcSymObj *sym)
{
/* remember the object I'm modifying */
mod_base_sym_ = sym;
/*
* set the other object's link back to me, so it knows that I'm the
* object that's modifying it
*/
if (sym != 0)
sym->set_modifying_sym((CTcSymObj *)this);
}
/*
* Get the appropriate stream for a given metaclass
*/
CTcDataStream *CTcSymObjBase::get_stream_from_meta(tc_metaclass_t meta)
{
switch(meta)
{
case TC_META_TADSOBJ:
/* it's the regular object stream */
return G_os;
case TC_META_ICMOD:
/* intrinsic class modifier stream */
return G_icmod_stream;
default:
/* other metaclasses have no stream */
return 0;
}
}
/*
* Add a class-specific template
*/
void CTcSymObjBase::add_template(CTcObjTemplate *tpl)
{
/* link it in at the tail of our list */
if (template_tail_ != 0)
template_tail_->nxt_ = tpl;
else
template_head_ = tpl;
template_tail_ = tpl;
}
/*
* Create a grammar rule list object
*/
CTcGramProdEntry *CTcSymObjBase::create_grammar_entry(
const char *prod_sym, size_t prod_sym_len)
{
CTcSymObj *sym;
/* look up the grammar production symbol */
sym = G_prs->find_or_def_gramprod(prod_sym, prod_sym_len, 0);
/* create a new grammar list associated with the production */
grammar_entry_ = new (G_prsmem) CTcGramProdEntry(sym);
/* return the new grammar list */
return grammar_entry_;
}
/* ------------------------------------------------------------------------ */
/*
* metaclass symbol
*/
/*
* add a property
*/
void CTcSymMetaclassBase::add_prop(const char *txt, size_t len,
const char *obj_fname, int is_static)
{
CTcSymProp *prop_sym;
/* see if this property is already defined */
prop_sym = (CTcSymProp *)G_prs->get_global_symtab()->find(txt, len);
if (prop_sym != 0)
{
/* it's already defined - make sure it's a property */
if (prop_sym->get_type() != TC_SYM_PROP)
{
/*
* it's something other than a property - log the
* appropriate type of error, depending on whether we're
* loading this from an object file or from source code
*/
if (obj_fname == 0)
{
/* creating from source - note the code location */
G_tok->log_error_curtok(TCERR_REDEF_AS_PROP);
}
else
{
/* loading from an object file */
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
TCERR_OBJFILE_REDEF_SYM_TYPE,
(int)len, txt, "property", obj_fname);
}
/* forget the symbol - it's not a property */
prop_sym = 0;
}
}
else
{
/* add the property definition */
prop_sym = new CTcSymProp(txt, len, FALSE, G_cg->new_prop_id());
G_prs->get_global_symtab()->add_entry(prop_sym);
}
/*
* if we found a valid property symbol, add it to the metaclass
* property list
*/
if (prop_sym != 0)
{
/*
* mark the symbol as referenced - even if we don't directly
* make use of it, the metaclass table references this symbol
*/
prop_sym->mark_referenced();
/* add the property to the metaclass list */
add_prop(prop_sym, is_static);
}
}
/*
* add a property
*/
void CTcSymMetaclassBase::add_prop(class CTcSymProp *prop, int is_static)
{
CTcSymMetaProp *entry;
/* create a new list entry for the property */
entry = new (G_prsmem) CTcSymMetaProp(prop, is_static);
/* link it at the end of our list */
if (prop_tail_ != 0)
prop_tail_->nxt_ = entry;
else
prop_head_ = entry;
prop_tail_ = entry;
/* count the addition */
++prop_cnt_;
}
/*
* write some additional data to the object file
*/
int CTcSymMetaclassBase::write_to_obj_file(class CVmFile *fp)
{
CTcSymMetaProp *cur;
char buf[16];
/* inherit default */
CTcSymbol::write_to_obj_file(fp);
/* write my metaclass index, class object ID, and property count */
fp->write_int2(meta_idx_);
fp->write_int4(class_obj_);
fp->write_int2(prop_cnt_);
/* write my property symbol list */
for (cur = prop_head_ ; cur != 0 ; cur = cur->nxt_)
{
/* write this symbol name */
fp->write_int2(cur->prop_->get_sym_len());
fp->write_bytes(cur->prop_->get_sym(), cur->prop_->get_sym_len());
/* set up the flags */
buf[0] = 0;
if (cur->is_static_)
buf[0] |= 1;
/* write the flags */
fp->write_bytes(buf, 1);
}
/* write our modifying object flag */
buf[0] = (mod_obj_ != 0);
fp->write_bytes(buf, 1);
/* if we have a modifier object chain, write it out */
if (mod_obj_ != 0)
mod_obj_->write_to_obj_file_as_modified(fp);
/* written */
return TRUE;
}
/*
* get the nth property in our table
*/
CTcSymMetaProp *CTcSymMetaclassBase::get_nth_prop(int n) const
{
CTcSymMetaProp *prop;
/* traverse the list to the desired index */
for (prop = prop_head_ ; prop != 0 && n != 0 ; prop = prop->nxt_, --n) ;
/* return the property */
return prop;
}
/* ------------------------------------------------------------------------ */
/*
* property symbol entry base
*/
/*
* fold an address constant
*/
CTcPrsNode *CTcSymPropBase::fold_addr_const()
{
CTcConstVal cval;
/* set up the property pointer constant */
cval.set_prop(get_prop());
/* return a constant node */
return new CTPNConst(&cval);
}
/*
* Read from a symbol file
*/
CTcSymbol *CTcSymPropBase::read_from_sym_file(CVmFile *fp)
{
const char *sym;
CTcSymbol *old_entry;
/* read the symbol name */
if ((sym = base_read_from_sym_file(fp)) == 0)
return 0;
/*
* If this property is already defined, this is a harmless
* redefinition - every symbol file can define the same property
* without any problem. Indicate the harmless redefinition by
* returning the original symbol.
*/
old_entry = G_prs->get_global_symtab()->find(sym, strlen(sym));
if (old_entry != 0 && old_entry->get_type() == TC_SYM_PROP)
return old_entry;
/* create and return the new symbol */
return new CTcSymProp(sym, strlen(sym), FALSE, G_cg->new_prop_id());
}
/*
* Write to an object file
*/
int CTcSymPropBase::write_to_obj_file(CVmFile *fp)
{
/*
* If the property has never been referenced, don't bother writing
* it. We must have picked up the definition from an external
* symbol set we loaded but have no references of our own to the
* property.
*/
if (!ref_)
return FALSE;
/* inherit default */
CTcSymbol::write_to_obj_file(fp);
/*
* write my local property ID value - when we load the object file,
* we'll need to figure out the translation from our original
* numbering system to the new numbering system used in the image
* file
*/
fp->write_int4((ulong)prop_);
/* written */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* Enumerator symbol base
*/
/*
* fold the symbol as a constant
*/
CTcPrsNode *CTcSymEnumBase::fold_constant()
{
CTcConstVal cval;
/* set up the enumerator constant */
cval.set_enum(get_enum_id());
/* return a constant node */
return new CTPNConst(&cval);
}
/*
* Write to a symbol file
*/
int CTcSymEnumBase::write_to_sym_file(CVmFile *fp)
{
int result;
char buf[32];
/* inherit default */
result = CTcSymbol::write_to_sym_file(fp);
/* write the 'token' flag */
if (result)
{
/* clear the flags */
buf[0] = 0;
/* set the 'token' flag if appropriate */
if (is_token_)
buf[0] |= 1;
/* write the flags */
fp->write_bytes(buf, 1);
}
/* return the result */
return result;
}
/*
* Read from a symbol file
*/
CTcSymbol *CTcSymEnumBase::read_from_sym_file(CVmFile *fp)
{
const char *sym;
CTcSymEnum *old_entry;
char buf[32];
int is_token;
/* read the symbol name */
if ((sym = base_read_from_sym_file(fp)) == 0)
return 0;
/* read the 'token' flag */
fp->read_bytes(buf, 1);
is_token = ((buf[0] & 1) != 0);
/*
* If this enumerator is already defined, this is a harmless
* redefinition - every symbol file can define the same enumerator
* without any problem. Indicate the harmless redefinition by
* returning the original symbol.
*/
old_entry = (CTcSymEnum *)
G_prs->get_global_symtab()->find(sym, strlen(sym));
if (old_entry != 0 && old_entry->get_type() == TC_SYM_ENUM)
{
/* if this is a 'token' enum, mark the old entry as such */
if (is_token)
old_entry->set_is_token(TRUE);
/* return the original entry */
return old_entry;
}
/* create and return the new symbol */
return new CTcSymEnum(sym, strlen(sym), FALSE,
G_prs->new_enum_id(), is_token);
}
/*
* Write to an object file
*/
int CTcSymEnumBase::write_to_obj_file(CVmFile *fp)
{
char buf[32];
/*
* If the enumerator has never been referenced, don't bother writing
* it. We must have picked up the definition from an external
* symbol set we loaded but have no references of our own to the
* enumerator.
*/
if (!ref_)
return FALSE;
/* inherit default */
CTcSymbol::write_to_obj_file(fp);
/*
* write my local enumerator ID value - when we load the object file,
* we'll need to figure out the translation from our original
* numbering system to the new numbering system used in the image
* file
*/
fp->write_int4((ulong)enum_id_);
/* clear the flags */
buf[0] = 0;
/* set the 'token' flag if appropriate */
if (is_token_)
buf[0] |= 1;
/* write the flags */
fp->write_bytes(buf, 1);
/* written */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* Built-in function symbol base
*/
/*
* Write to a object file
*/
int CTcSymBifBase::write_to_obj_file(CVmFile *fp)
{
char buf[10];
/* inherit default */
CTcSymbol::write_to_obj_file(fp);
/* write the varargs and return value flags */
buf[0] = (varargs_ != 0);
buf[1] = (has_retval_ != 0);
/* write the argument count information */
oswp2(buf+2, min_argc_);
oswp2(buf+4, max_argc_);
/*
* write the function set ID and index - these are required to match
* those used in all other object files that make up a single image
* file
*/
oswp2(buf+6, func_set_id_);
oswp2(buf+8, func_idx_);
fp->write_bytes(buf, 10);
/* written */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* Parser dictionary hash table entry
*/
/*
* add an item to my list of object associations
*/
void CVmHashEntryPrsDict::add_item(tc_obj_id obj, tc_prop_id prop)
{
CTcPrsDictItem *item;
/* search my list for an existing association to the same obj/prop */
for (item = list_ ; item != 0 ; item = item->nxt_)
{
/* if it matches, we don't need to add this one again */
if (item->obj_ == obj && item->prop_ == prop)
return;
}
/* not found - create a new item */
item = new (G_prsmem) CTcPrsDictItem(obj, prop);
/* link it into my list */
item->nxt_ = list_;
list_ = item;
}
/* ------------------------------------------------------------------------ */
/*
* Dictionary entry - each dictionary object gets one of these objects
* to track it
*/
/*
* construction
*/
CTcDictEntry::CTcDictEntry(CTcSymObj *sym)
{
const size_t hash_table_size = 128;
/* remember my object symbol and word truncation length */
sym_ = sym;
/* no object file index yet */
obj_idx_ = 0;
/* not in a list yet */
nxt_ = 0;
/* create my hash table */
hashtab_ = new (G_prsmem)
CVmHashTable(hash_table_size,
new (G_prsmem) CVmHashFuncCI(), TRUE,
new (G_prsmem) CVmHashEntry *[hash_table_size]);
}
/*
* Add a word to the table
*/
void CTcDictEntry::add_word(const char *txt, size_t len, int copy,
tc_obj_id obj, tc_prop_id prop)
{
CVmHashEntryPrsDict *entry;
/* search for an existing entry */
entry = (CVmHashEntryPrsDict *)hashtab_->find(txt, len);
/* if there's no entry, create a new one */
if (entry == 0)
{
/* create a new item */
entry = new (G_prsmem) CVmHashEntryPrsDict(txt, len, copy);
/* add it to the table */
hashtab_->add(entry);
}
/* add this object/property association to the word's hash table entry */
entry->add_item(obj, prop);
}
/*
* Write my symbol to an object file
*/
void CTcDictEntry::write_sym_to_obj_file(CVmFile *fp)
{
/* if I already have a non-zero index value, I've already been written */
if (obj_idx_ != 0)
return;
/* assign myself an object file dictionary index */
obj_idx_ = G_prs->get_next_obj_file_dict_idx();
/* write my symbol to the object file */
sym_->write_to_obj_file(fp);
}
/* ------------------------------------------------------------------------ */
/*
* Grammar production list entry
*/
CTcGramProdEntry::CTcGramProdEntry(CTcSymObj *prod_sym)
{
/* remember my object symbol */
prod_sym_ = prod_sym;
/* not in a list yet */
nxt_ = 0;
/* no alternatives yet */
alt_head_ = alt_tail_ = 0;
/* not explicitly declared yet */
is_declared_ = FALSE;
}
/*
* Add an alternative
*/
void CTcGramProdEntry::add_alt(CTcGramProdAlt *alt)
{
/* link it at the end of my list */
if (alt_tail_ != 0)
alt_tail_->set_next(alt);
else
alt_head_ = alt;
alt_tail_ = alt;
/* this is now the last element in our list */
alt->set_next(0);
}
/*
* Move my alternatives to a new owner
*/
void CTcGramProdEntry::move_alts_to(CTcGramProdEntry *new_entry)
{
CTcGramProdAlt *alt;
CTcGramProdAlt *nxt;
/* move each of my alternatives */
for (alt = alt_head_ ; alt != 0 ; alt = nxt)
{
/* remember the next alternative, since we're unlinking this one */
nxt = alt->get_next();
/* unlink this one from the list */
alt->set_next(0);
/* link this one into the new owner's list */
new_entry->add_alt(alt);
}
/* there's nothing left in our list */
alt_head_ = alt_tail_ = 0;
}
/*
* Write to an object file
*/
void CTcGramProdEntry::write_to_obj_file(CVmFile *fp)
{
ulong cnt;
CTcGramProdAlt *alt;
ulong flags;
/* write the object file index of my production object symbol */
fp->write_int4(prod_sym_ == 0 ? 0 : prod_sym_->get_obj_file_idx());
/* set up the flags */
flags = 0;
if (is_declared_)
flags |= 1;
/* write the flags */
fp->write_int4(flags);
/* count my alternatives */
for (cnt = 0, alt = alt_head_ ; alt != 0 ;
++cnt, alt = alt->get_next()) ;
/* write the count */
fp->write_int4(cnt);
/* write each alternative */
for (alt = alt_head_ ; alt != 0 ; alt = alt->get_next())
alt->write_to_obj_file(fp);
}
/* ------------------------------------------------------------------------ */
/*
* Grammar production alternative
*/
CTcGramProdAlt::CTcGramProdAlt(CTcSymObj *obj_sym, CTcDictEntry *dict)
{
/* remember the associated processor object */
obj_sym_ = obj_sym;
/* remember the default dictionary currently in effect */
dict_ = dict;
/* nothing in our token list yet */
tok_head_ = tok_tail_ = 0;
/* we don't have a score or badness yet */
score_ = 0;
badness_ = 0;
/* we're not in a list yet */
nxt_ = 0;
}
void CTcGramProdAlt::add_tok(CTcGramProdTok *tok)
{
/* link the token at the end of my list */
if (tok_tail_ != 0)
tok_tail_->set_next(tok);
else
tok_head_ = tok;
tok_tail_ = tok;
/* there's nothing after this token */
tok->set_next(0);
}
/*
* Write to an object file
*/
void CTcGramProdAlt::write_to_obj_file(CVmFile *fp)
{
ulong cnt;
CTcGramProdTok *tok;
/* write my score and badness */
fp->write_int2(score_);
fp->write_int2(badness_);
/* write the index of my processor object symbol */
fp->write_int4(obj_sym_ == 0 ? 0 : obj_sym_->get_obj_file_idx());
/* write the dictionary index */
fp->write_int4(dict_ == 0 ? 0 : dict_->get_obj_idx());
/* count my tokens */
for (cnt = 0, tok = tok_head_ ; tok != 0 ;
++cnt, tok = tok->get_next()) ;
/* write my token count */
fp->write_int4(cnt);
/* write the tokens */
for (tok = tok_head_ ; tok != 0 ; tok = tok->get_next())
tok->write_to_obj_file(fp);
}
/* ------------------------------------------------------------------------ */
/*
* Grammar production token object
*/
/*
* write to an object file
*/
void CTcGramProdTok::write_to_obj_file(CVmFile *fp)
{
size_t i;
/* write my type */
fp->write_int2((int)typ_);
/* write my data */
switch(typ_)
{
case TCGRAM_PROD:
/* write my object's object file index */
fp->write_int4(val_.obj_ != 0
? val_.obj_->get_obj_file_idx() : 0);
break;
case TCGRAM_TOKEN_TYPE:
/* write my enum token ID */
fp->write_int4(val_.enum_id_);
break;
case TCGRAM_PART_OF_SPEECH:
/* write my property ID */
fp->write_int2(val_.prop_);
break;
case TCGRAM_LITERAL:
/* write my string */
fp->write_int2(val_.str_.len_);
fp->write_bytes(val_.str_.txt_, val_.str_.len_);
break;
case TCGRAM_STAR:
/* no additional value data */
break;
case TCGRAM_PART_OF_SPEECH_LIST:
/* write the length */
fp->write_int2(val_.prop_list_.len_);
/* write each element */
for (i = 0 ; i < val_.prop_list_.len_ ; ++i)
fp->write_int2(val_.prop_list_.arr_[i]);
/* done */
break;
case TCGRAM_UNKNOWN:
/* no value - there's nothing extra to write */
break;
}
/* write my property association */
fp->write_int2(prop_assoc_);
}
/*
* Initialize with a part-of-speech list
*/
void CTcGramProdTok::set_match_part_list()
{
const size_t init_alo = 10;
/* remember the type */
typ_ = TCGRAM_PART_OF_SPEECH_LIST;
/* we have nothing in the list yet */
val_.prop_list_.len_ = 0;
/* set the initial allocation size */
val_.prop_list_.alo_ = init_alo;
/* allocate the initial list */
val_.prop_list_.arr_ = (tctarg_prop_id_t *)G_prsmem->alloc(
init_alo * sizeof(val_.prop_list_.arr_[0]));
}
/*
* Add a property to our part-of-speech match list
*/
void CTcGramProdTok::add_match_part_ele(tctarg_prop_id_t prop)
{
/* if necessary, re-allocate the array at a larger size */
if (val_.prop_list_.len_ == val_.prop_list_.alo_)
{
tctarg_prop_id_t *oldp;
/* bump up the size a bit */
val_.prop_list_.alo_ += 10;
/* remember the current list long enough to copy it */
oldp = val_.prop_list_.arr_;
/* reallocate it */
val_.prop_list_.arr_ = (tctarg_prop_id_t *)G_prsmem->alloc(
val_.prop_list_.alo_ * sizeof(val_.prop_list_.arr_[0]));
/* copy the old list into the new one */
memcpy(val_.prop_list_.arr_, oldp,
val_.prop_list_.len_ * sizeof(val_.prop_list_.arr_[0]));
}
/*
* we now know we have space for the new element, so add it, bumping up
* the length counter to account for the addition
*/
val_.prop_list_.arr_[val_.prop_list_.len_++] = prop;
}
/* ------------------------------------------------------------------------ */
/*
* Code Body Parse Node
*/
/*
* instantiate
*/
CTPNCodeBodyBase::CTPNCodeBodyBase(CTcPrsSymtab *lcltab,
CTcPrsSymtab *gototab, CTPNStm *stm,
int argc, int varargs,
int varargs_list,
CTcSymLocal *varargs_list_local,
int local_cnt, int self_valid,
CTcCodeBodyRef *enclosing_code_body)
{
/* remember the data in the code body */
lcltab_ = lcltab;
gototab_ = gototab;
stm_ = stm;
argc_ = argc;
varargs_ = varargs;
varargs_list_ = varargs_list;
varargs_list_local_ = varargs_list_local;
local_cnt_ = local_cnt;
self_valid_ = self_valid;
self_referenced_ = FALSE;
full_method_ctx_referenced_ = FALSE;
/* remember the enclosing code body */
enclosing_code_body_ = enclosing_code_body;
/* presume we won't need a local context object */
has_local_ctx_ = FALSE;
local_ctx_arr_size_ = 0;
ctx_head_ = ctx_tail_ = 0;
local_ctx_needs_self_ = FALSE;
local_ctx_needs_full_method_ctx_ = FALSE;
/* presume we have an internal fixup list */
fixup_owner_sym_ = 0;
fixup_list_anchor_ = &internal_fixups_;
/* no internal fixups yet */
internal_fixups_ = 0;
/* we haven't been replaced yet */
replaced_ = FALSE;
/*
* remember the source location of the closing brace, which should
* be the current location when we're instantiated
*/
end_desc_ = G_tok->get_last_desc();
end_linenum_ = G_tok->get_last_linenum();
}
/*
* fold constants
*/
CTcPrsNode *CTPNCodeBodyBase::fold_constants(class CTcPrsSymtab *)
{
/*
* fold constants in our compound statement, in the scope of our
* local symbol table
*/
if (stm_ != 0)
stm_->fold_constants(lcltab_);
/* we are not directly changed by this operation */
return this;
}
/*
* Check for unreferenced labels
*/
void CTPNCodeBodyBase::check_unreferenced_labels()
{
/*
* enumerate our labels - skip this check if we're only parsing the
* program for syntax
*/
if (gototab_ != 0 && !G_prs->get_syntax_only())
gototab_->enum_entries(&unref_label_cb, this);
}
/*
* Callback for enumerating labels for checking for unreferenced labels
*/
void CTPNCodeBodyBase::unref_label_cb(void *, CTcSymbol *sym)
{
/* if it's a label, check it out */
if (sym->get_type() == TC_SYM_LABEL)
{
CTcSymLabel *lbl = (CTcSymLabel *)sym;
/*
* get its underlying statement, and make sure it has a
* control-flow flag for goto, continue, or break
*/
if (lbl->get_stm() != 0)
{
ulong flags;
/*
* get the explicit control flow flags for this statement --
* these flags indicate the use of the label in a goto,
* break, or continue statement
*/
flags = lbl->get_stm()->get_explicit_control_flow_flags();
/*
* if the flags aren't set for at least one of the explicit
* label uses, the label is unreferenced
*/
if ((flags & (TCPRS_FLOW_GOTO | TCPRS_FLOW_BREAK
| TCPRS_FLOW_CONT)) == 0)
lbl->get_stm()->log_warning(TCERR_UNREFERENCED_LABEL,
(int)lbl->get_sym_len(),
lbl->get_sym());
}
}
}
/*
* add an absolute fixup to my list
*/
void CTPNCodeBodyBase::add_abs_fixup(CTcDataStream *ds, ulong ofs)
{
/* ask the code body to add the fixup */
CTcAbsFixup::add_abs_fixup(fixup_list_anchor_, ds, ofs);
}
/*
* add an absolute fixup at the current stream offset
*/
void CTPNCodeBodyBase::add_abs_fixup(CTcDataStream *ds)
{
/* ask the code body to add the fixup */
CTcAbsFixup::add_abs_fixup(fixup_list_anchor_, ds, ds->get_ofs());
}
/*
* Get the context variable for a given level
*/
int CTPNCodeBodyBase::get_or_add_ctx_var_for_level(int level)
{
CTcCodeBodyCtx *ctx;
/* scan our list to see if the level is already assigned */
for (ctx = ctx_head_ ; ctx != 0 ; ctx = ctx->nxt_)
{
/* if we've already set up this level, return its variable */
if (ctx->level_ == level)
return ctx->var_num_;
}
/* we didn't find it - allocate a new level structure */
ctx = new (G_prsmem) CTcCodeBodyCtx();
/* set up its level and allocate a new variable and property for it */
ctx->level_ = level;
ctx->var_num_ = G_prs->alloc_ctx_holder_var();
/*
* allocating a new variable probably increased the maximum local
* variable count - update our information from the parser
*/
local_cnt_ = G_prs->get_max_local_cnt();
/* link it into our list */
ctx->prv_ = ctx_tail_;
ctx->nxt_ = 0;
if (ctx_tail_ != 0)
ctx_tail_->nxt_ = ctx;
else
ctx_head_ = ctx;
ctx_tail_ = ctx;
/* return the variable for the new level */
return ctx->var_num_;
}
/*
* Find a local context for a given level
*/
int CTPNCodeBodyBase::get_ctx_var_for_level(int level, int *varnum)
{
CTcCodeBodyCtx *ctx;
/* if they want level zero, it's our local context */
if (level == 0)
{
/* set the variable ID to our local context variable */
*varnum = local_ctx_var_;
/* return true only if we actually have a local context */
return has_local_ctx_;
}
/* scan our list to see if the level is already assigned */
for (ctx = ctx_head_ ; ctx != 0 ; ctx = ctx->nxt_)
{
/* if we've already set up this level, return its variable */
if (ctx->level_ == level)
{
/* set the caller's variable number */
*varnum = ctx->var_num_;
/* indicate that we found it */
return TRUE;
}
}
/* didn't find it */
return FALSE;
}
/*
* Get the immediately enclosing code body
*/
CTPNCodeBody *CTPNCodeBodyBase::get_enclosing() const
{
/*
* if we have no enclosing code body reference, we have no enclosing
* code body
*/
if (enclosing_code_body_ == 0)
return 0;
/* get the code body from my enclosing code body reference object */
return enclosing_code_body_->ptr;
}
/*
* Get the outermost enclosing code body
*/
CTPNCodeBody *CTPNCodeBodyBase::get_outermost_enclosing() const
{
CTPNCodeBody *cur;
CTPNCodeBody *nxt;
/*
* scan each enclosing code body until we find one without an enclosing
* code body
*/
for (cur = 0, nxt = get_enclosing() ; nxt != 0 ;
cur = nxt, nxt = nxt->get_enclosing()) ;
/* return what we found */
return cur;
}
/*
* Get the base function symbol for a code body defining a modified
* function (i.e., 'modify <funcname>...'). This is the function to which
* 'replaced' refers within this code body and within nested code bodies.
*/
class CTcSymFunc *CTPNCodeBodyBase::get_replaced_func() const
{
CTcSymFunc *b;
CTPNCodeBody *enc;
/* if we have an associated function symbol, it's the base function */
if ((b = get_func_sym()) != 0)
return b->get_mod_base();
/*
* if we have an enclosing code body, then 'replaced' here means the
* same thing it does there, since we don't explicitly replace anything
* here
*/
if ((enc = get_enclosing()) != 0)
return enc->get_replaced_func();
/* if we haven't found anything yet, we don't have a base function */
return 0;
}
/* ------------------------------------------------------------------------ */
/*
* Generic statement node
*/
/*
* initialize at the tokenizer's current source file position
*/
CTPNStmBase::CTPNStmBase()
{
/* get the current source location from the parser */
init(G_prs->get_cur_desc(), G_prs->get_cur_linenum());
}
/*
* log an error at this statement's source file position
*/
void CTPNStmBase::log_error(int errnum, ...) const
{
va_list marker;
/* display the message */
va_start(marker, errnum);
G_tcmain->v_log_error(file_, linenum_, TC_SEV_ERROR, errnum, marker);
va_end(marker);
}
/*
* log a warning at this statement's source file position
*/
void CTPNStmBase::log_warning(int errnum, ...) const
{
va_list marker;
/* display the message */
va_start(marker, errnum);
G_tcmain->v_log_error(file_, linenum_, TC_SEV_WARNING, errnum, marker);
va_end(marker);
}
/*
* Generate code for a sub-statement
*/
void CTPNStmBase::gen_code_substm(CTPNStm *substm)
{
/* set the error reporting location to refer to the sub-statement */
G_tok->set_line_info(substm->get_source_desc(),
substm->get_source_linenum());
/* generate code for the sub-statement */
substm->gen_code(TRUE, TRUE);
/* restore the error reporting location to the main statement */
G_tok->set_line_info(get_source_desc(), get_source_linenum());
}
/* ------------------------------------------------------------------------ */
/*
* Object Definition Statement
*/
/*
* fold constants
*/
CTcPrsNode *CTPNStmObjectBase::fold_constants(CTcPrsSymtab *symtab)
{
CTPNObjProp *prop;
/* fold constants in each of our property list entries */
for (prop = first_prop_ ; prop != 0 ; prop = prop->nxt_)
prop->fold_constants(symtab);
/* we're not changed directly by this */
return this;
}
/* ------------------------------------------------------------------------ */
/*
* superclass record
*/
/*
* get my symbol
*/
CTcSymbol *CTPNSuperclass::get_sym() const
{
/* if we know the symbol already, return it directly */
if (sym_ != 0)
return sym_;
/* look up my symbol by name in the global symbol table */
return G_prs->get_global_symtab()->find(sym_txt_, sym_len_);
}
/*
* am I a subclass of the given class?
*/
int CTPNSuperclass::is_subclass_of(const CTPNSuperclass *other) const
{
CTcSymObj *sym;
CTPNSuperclass *sc;
/*
* if my name matches, we're a subclass (we are a subclass of
* ourselves)
*/
if (other->sym_len_ == sym_len_
&& memcmp(other->sym_txt_, sym_txt_, sym_len_) == 0)
return TRUE;
/*
* We're a subclass if any of our superclasses are subclasses of the
* given object. Get my object symbol, and make sure it's really a
* tads-object - if it's not, we're definitely not a subclass of
* anything.
*/
sym = (CTcSymObj *)get_sym();
if (sym == 0
|| sym->get_type() != TC_SYM_OBJ
|| sym->get_metaclass() != TC_META_TADSOBJ)
return FALSE;
/* scan our symbol's superclass list for a match */
for (sc = sym->get_sc_name_head() ; sc != 0 ; sc = sc->nxt_)
{
/*
* if this one's a subclass of the given class, we're a subclass
* as well, since we're a subclass of this superclass
*/
if (sc->is_subclass_of(other))
return TRUE;
}
/*
* we didn't find any superclass that's a subclass of the given
* class, so we're not a subclass of the given class
*/
return FALSE;
}
/* ------------------------------------------------------------------------ */
/*
* 'return' statement
*/
/*
* fold constants
*/
CTcPrsNode *CTPNStmReturnBase::fold_constants(CTcPrsSymtab *symtab)
{
/* set our location for any errors that occur */
G_tok->set_line_info(get_source_desc(), get_source_linenum());
/* fold constants in the expression, if we have one */
if (expr_ != 0)
expr_ = expr_->fold_constants(symtab);
/* we are not directly changed by this operation */
return this;
}
/* ------------------------------------------------------------------------ */
/*
* Formal type list
*/
/*
* add a typed parameter to the list - 'tok' is the symbol giving the type
* name
*/
void CTcFormalTypeList::add_typed_param(const CTcToken *tok)
{
add(new (G_prsmem) CTcFormalTypeEle(tok->get_text(), tok->get_text_len()));
}
/* add an untyped parameter to the list */
void CTcFormalTypeList::add_untyped_param()
{
add(new (G_prsmem) CTcFormalTypeEle());
}
/* add a list element */
void CTcFormalTypeList::add(CTcFormalTypeEle *ele)
{
/* link it into our list */
if (tail_ != 0)
tail_->nxt_ = ele;
else
head_ = ele;
tail_ = ele;
ele->nxt_ = 0;
}
/*
* create a decorated name token for the multi-method defined by the given
* function name and our type list
*/
void CTcFormalTypeList::decorate_name(CTcToken *decorated_name,
const CTcToken *func_base_name)
{
CTcFormalTypeEle *ele;
size_t len;
const char *p;
/* figure out how much space we need for the decorated name */
for (len = func_base_name->get_text_len() + 1, ele = head_ ;
ele != 0 ; ele = ele->nxt_)
{
/* add this type name's length, if there's a name */
if (ele->name_ != 0)
len += ele->name_len_;
/* add a semicolon after the type */
len += 1;
}
/* add "...;" if it's varargs */
if (varargs_)
len += 4;
/* allocate space for the name */
G_tok->reserve_source(len);
/* start with the function name */
p = G_tok->store_source_partial(func_base_name->get_text(),
func_base_name->get_text_len());
/* add a "*" separator for the multi-method indicator */
G_tok->store_source_partial("*", 1);
/* add each type name */
for (ele = head_ ; ele != 0 ; ele = ele->nxt_)
{
/* add the type, if it has one (if not, leave the type empty) */
if (ele->name_ != 0)
G_tok->store_source_partial(ele->name_, ele->name_len_);
/* add a semicolon to terminate the parameter name */
G_tok->store_source_partial(";", 1);
}
/* add the varargs indicator ("...;"), if applicable */
if (varargs_)
G_tok->store_source_partial("...;", 4);
/* null-terminate it */
G_tok->store_source_partial("\0", 1);
/* set the decorated token name */
decorated_name->settyp(TOKT_SYM);
decorated_name->set_text(p, len);
}
/* formal list element - construction */
CTcFormalTypeEle::CTcFormalTypeEle(const char *name, size_t len)
{
name_ = new (G_prsmem) char[len + 1];
memcpy(name_, name, len);
name_len_ = len;
} |