/** * \file symbol_name.cpp * \brief Symbol Names * \author Stefan Reuther * * This module contains the logic to parse a symbol name tree into * a higher-level data structure. */ INTERFACE: #include #include "type_rep.h" #include "symbol_table.h" #include "ptree_nodes.h" class Abstract_scope; class Symbol_name { public: enum Kind { k_Normal, ///< a normal name. k_Operator, ///< "operator foo". k_Alloc, ///< "new" and "delete". k_Constructor, ///< constructor. k_Destructor, ///< destructor. k_Conversion ///< "operator " (type conversion). }; /** Names for certain operators. */ static const char CONVERSION_OPERATOR_NAME[], CONSTRUCTOR_NAME[], DESTRUCTOR_NAME[], PTR_OPERATOR_NAME[], CALL_OPERATOR_NAME[], ASSIGNMENT_OPERATOR_NAME[], INDEX_OPERATOR_NAME[], NEW_OPERATOR_NAME[], ANEW_OPERATOR_NAME[], DELETE_OPERATOR_NAME[], ADELETE_OPERATOR_NAME[], COMMA_OPERATOR_NAME[]; private: std::string basename; ///< base name of symbol (sans scopes, mangled). Abstract_scope* scope; ///< scope in which symbol appears, if qualified name. bool qualified; ///< true iff this is a qualified name. Type conv_type; ///< target type for conversion operators, only if kind == k_Conversion Kind kind; ///< kind of symbol. Ptree* tpl_args; ///< template args, null if not a template. Format is "[< [a1 , a2] >]" Abstract_scope* start_scope; ///< starting scope for lookup. }; IMPLEMENTATION: #include #include #include #include "scope.h" #include "except.h" #include "decl_read.h" #include "template.h" #include "class.h" /** \class Symbol_name \brief A Symbol Name This represents a symbol name, such as `i', `NS::x', `Class::Class' or `Foo::operator='. If the symbol is a template, as in `vector', is_template() is true. Special case: the last component may also be "*", as in `Class::*', to represent member pointers. In addition, this class is a container for name mangling related stuff. */ /** This ctor is used in initialisation. */ PUBLIC Symbol_name::Symbol_name(std::string s, Abstract_scope* scope, Kind k) : basename(s), scope(scope), qualified(0), conv_type(), kind(k), tpl_args(0), start_scope(scope) { } /** Initialize symbol name. \param tree tree as obtained from OpenC++ \param scope scope in which this symbol might be looked up (needed for type names in conversion ops) \param is_fdecl true iff this is a declaration of a function. */ PUBLIC Symbol_name::Symbol_name(Ptree* tree, Abstract_scope* const scope, bool is_fdecl) : scope(0), qualified(false), tpl_args(0), start_scope(scope) { if (!tree) { /* anonymous object */ basename = gensym(); kind = k_Normal; this->scope = scope; } else if (!is_qualified_name(tree)) { /* it's a leaf. Leaf it so. */ init_name(scope, tree, is_fdecl); } else { /* qualified name */ qualified = true; Ptree* p = tree; bool look_global = true; Abstract_scope* current_scope = scope; #define ADVANCE(p) (p = p->Cdr(), assert(p != 0)) if (p->Car()->Eq("::")) { /* rooted lookup */ look_global = false; current_scope = current_scope->get_global_scope(); ADVANCE(p); } while (p && p->Cdr()) { Ptree* ele = p->Car(); // a scope name if (look_global) { Abstract_scope* s; while (current_scope && !(s = lookup_symbol_in_scope(ele, current_scope, start_scope))) current_scope = current_scope->get_parent(); if (!current_scope) compile_error("no such scope " + std::string(ele->ToString())); current_scope = s; } else { current_scope = lookup_symbol_in_scope(ele, current_scope, start_scope); if (!current_scope) compile_error("no such scope " + std::string(ele->ToString())); } look_global = false; ADVANCE(p); expect_ptree(p->Car(), "::"); ADVANCE(p); } /* now we have reached the final leaf */ init_name(current_scope, p->Car(), is_fdecl); } assert(this->scope); } PUBLIC Symbol_name::~Symbol_name() { } /** Helper: true iff p is a qualified name. Unqualified names might also have more than one component (operator, destructor, template). */ PUBLIC static bool Symbol_name::is_qualified_name(Ptree* p) { return (!p->IsLeaf() && p->Length() >= 2 && (p->First()->Eq("::") || p->Second()->Eq("::"))); } /** Helper: look up a scope. \param p the symbol (i.e. "std" or "vector") \param scope the scope in which we look up the symbol \param start_scope the scope in which we look up the template args, if any \return resulting scope, null if none found. Throws if it finds an invalid object. */ PUBLIC static Abstract_scope* Symbol_name::lookup_symbol_in_scope(Ptree* p, Abstract_scope* scope, Abstract_scope* start_scope) { Symbol_pair sp; if (p->IsLeaf()) { /* class or namespace */ sp = scope->lookup_here(p->ToString(), false); Abstract_scope* s; if (sp.tag && (s = sp.tag->get_scope())) return s; if (sp.untag && (s = sp.untag->get_scope())) return s; } else { /* template */ assert(p->Length() == 2); sp = scope->lookup_here(p->Car()->ToString(), false); Template_class_symbol* tcs = dynamic_cast(sp.tag); if (tcs) return tcs->get_specialisation_from_ptree(p->Second(), start_scope)->get_scope(); } if (sp.untag && sp.untag->get_kind() != Symbol::k_Function && sp.untag->get_kind() != Symbol::k_Variable) compile_error("not a scope name: " + std::string(p->ToString())); return 0; } /** Initialize this Symbol_name. \param s value for scope \param p symbol name (leaf, or [~ name], [operator X], [X ] \param is_fdecl true iff this is a function declaration/definition. Only in this mode, constructors are recognized. Rationale: looking up "A" in class A yields the class otherwise */ PRIVATE void Symbol_name::init_name(Abstract_scope* s, Ptree* p, bool is_fdecl) { if (!p->IsLeaf()) { if (p->Car()->Eq('~')) { if (!s->is_constructor(p->Second())) compile_error("invalid destructor name"); basename = DESTRUCTOR_NAME; kind = k_Destructor; scope = s; return; } if (p->Car()->Eq("operator")) { /* operator */ assert(p->Length() == 2); if (const char* pn = get_operator_name(p->Second())) { /* it's a normal operator */ basename = pn; if (pn == NEW_OPERATOR_NAME || pn == ANEW_OPERATOR_NAME || pn == DELETE_OPERATOR_NAME || pn == ADELETE_OPERATOR_NAME) kind = k_Alloc; else kind = k_Operator; scope = s; return; } /* conversion operator */ /* This sucks. parse.cc, rOperatorName: X normal operator Reserved(new), Reserved(delete) operator new [Reserved(new) \[ \]] array new/delete [( )] funcall op [\[ \]] index op => leaf of type RESERVED -or- list starting with RESERVED leaf or "(" or "[" -or- leaf containing operator (i.e. non-alphabetic) typename [typename [ptr-operators...]] ...where typname = type-specifier-list or name i.e. [operator [a [*]] [operator [:: a]] [operator [a [* *]]] [operator [[int] [[a :: *]]]] [operator a] [operator [a :: b]] [operator [[a :: b] [*]]] [operator [const [bool]]] [operator [[bool] const]] [operator [const a]] => single type if leaf => type + ptr-ops list of length 2 -and- first is not a "::" */ Ptree* opn = p->Second(); if (!opn->IsLeaf() && opn->Length() == 2 && !opn->Car()->Eq("::") && !opn->Second()->IsLeaf()) { /* this test doesn't wield out "const bool" etc. Use brute force. Bummer, maybe parse_type does assert or printBacktrace when something fails. Did I already say this sucks? */ try { Type t = parse_type(opn->First(), start_scope, 0, false); if (!parse_abstract_declarator(opn->Second(), start_scope, &t)) compile_error("invalid abstract-declarator for conversion operator"); conv_type = t; kind = k_Conversion; basename = CONVERSION_OPERATOR_NAME; scope = s; return; } catch(...) { } } /* we're here when it's just one type */ conv_type = parse_type(opn, start_scope, 0, false); kind = k_Conversion; basename = CONVERSION_OPERATOR_NAME; scope = s; return; } /* must be template */ if (p->Length() != 2) bogus_ptree_error("expected [name [< tplargs >]]", p); tpl_args = p->Second(); p = p->First(); } if (!p->IsLeaf()) bogus_ptree_error("expected leaf", p); if (is_fdecl && s->is_constructor(p)) { basename = CONSTRUCTOR_NAME; kind = k_Constructor; scope = s; } else { basename = p->ToString(); kind = k_Normal; scope = s; } } /** True iff this is a qualified symbol ("foo::bar"). */ PUBLIC bool Symbol_name::is_qualified() const { return qualified; } /** Get base name. This is the name under which the declared object will appear in the symbol table. */ PUBLIC inline std::string Symbol_name::get_name() const { return basename; } /** Get symbol kind. */ PUBLIC inline Symbol_name::Kind Symbol_name::get_kind() const { return kind; } /** Get scope. Qualified symbols are looked up in this scope only; unqualified ones are looked up here and in its parents. */ PUBLIC Abstract_scope* Symbol_name::get_scope() const { return scope; } /** Get target type if this is a conversion operator. */ PUBLIC Type Symbol_name::get_type() const { assert(get_kind() == k_Conversion); return conv_type; } /** True iff this is a templatized thing. */ PUBLIC inline bool Symbol_name::is_template() const { return tpl_args != 0; } /** Get template arguments. */ PUBLIC inline Ptree* Symbol_name::get_template_args() const { return tpl_args; } /** Look up symbol in order to use it. This uses the normal name lookup rules. \param qual true to force qualified lookup. This is required in expressions like "foo.bar" where bar, albeit unqualified, is looked up in scope foo only. */ PUBLIC Symbol_pair Symbol_name::lookup_for_use(bool qual) const { Symbol_pair sp; if (qual || is_qualified()) sp = get_scope()->lookup_here(get_name(), false); else sp = get_scope()->lookup_unqualified(get_name()); if (is_template()) { Template_class_symbol* tcs = dynamic_cast(sp.tag); if (!tcs) compile_error(get_name() + " is not a template"); Type_vector tv; parse_template_arg_list(tpl_args, start_scope, &tv); sp.tag = sp.untag = tcs->get_specialisation(tv); } return sp; } /** Look up symbol in order to declare it. */ PUBLIC Symbol_pair Symbol_name::lookup_for_decl() const { return get_scope()->lookup_here(get_name(), true); } /***************************** Name Mangling *****************************/ /** Generate a new, unused symbol name. */ PUBLIC static std::string Symbol_name::gensym() { static int counter = 0; ++counter; std::ostringstream s; s << "__" << counter; return s.str(); } /** Get name for nameless namespace. */ PUBLIC static std::string Symbol_name::get_unnamed_namespace_name() { // FIXME: g++ derives this name from the input file name, time, // and whatever. So should we do, too. return "__unnamed"; } /** Mangle symbol name into scope. For example, to determine the name for symbol "A::B::sym", use "get_mangled_symbol_name(A::B, sym)". */ PUBLIC static std::string Symbol_name::get_mangled_symbol_name(std::string scope, std::string name) { #ifdef OLD_MANGLER if (scope.length()) return scope + "::" + name; else return name; #else return name + scope; #endif } /** Convert symbol name into scope name. To determine the scope name for class "A::B", use "get_mangled_scope_from_symbol(A::B)". */ PUBLIC static std::string Symbol_name::get_mangled_scope_from_symbol(std::string name) { #ifdef OLD_MANGLER return name; #else std::ostringstream os; os << "?Q" << name.length() << name; return os.str(); #endif } /** Mangle function prototype into name. For example, to determine the name for "int X::func(void)", use "get_mangled_function_name(func, int(void))". */ PUBLIC static std::string Symbol_name::get_mangled_function_name(std::string fname, Type t, Type this_type) { #ifdef OLD_MANGLER return fname + "__" + t.make_pointer_type().get_encoded_type().substr(1); #else std::string type = t.get_function_signature(); if (this_type.is_valid()) { if (this_type.is_qualified(Type::q_Volatile)) type.insert(0, "V"); if (this_type.is_qualified(Type::q_Const)) type.insert(0, "C"); } std::ostringstream os; os << fname << "?F" << type.length() << type; return os.str(); #endif } /** Mangle template parameters into name. For example, to determine the name for "std::vector", use "get_mangled_template_name(vector, [int])". */ PUBLIC static std::string Symbol_name::get_mangled_template_name(std::string name, const Type_vector& types) { #ifdef OLD_MANGLER name += "__"; for (unsigned i = 0; i < types.size(); ++i) name += types[i].make_pointer_type().get_encoded_type().substr(1); return name; #else std::string type_sig; for (unsigned i = 0; i < types.size(); ++i) type_sig += types[i].make_pointer_type().get_encoded_type().substr(1); std::ostringstream os; os << name << "?T" << type_sig.length() << type_sig; return os.str(); #endif } /** Generate mangled block scope name. Given a function "fname", return the name of a new block. */ PUBLIC static std::string Symbol_name::get_mangled_block_scope(std::string fname) { return fname + get_block_name(); } PUBLIC static std::string Symbol_name::get_basename_from_symbol(std::string sym, std::string::size_type len) { #ifdef OLD_MANGLER return sym.substr(sym.length()-len, len); #else return sym.substr(0, len); #endif } /** Generate name for a block. */ PUBLIC static std::string Symbol_name::get_block_name() { static int counter = 0; ++counter; std::ostringstream s; #ifdef OLD_MANGLER s << "." << counter; #else s << "?" << counter; #endif return s.str(); } const char Symbol_name::CONVERSION_OPERATOR_NAME[] = "__cvt", Symbol_name::CONSTRUCTOR_NAME[] = "__ctor", Symbol_name::DESTRUCTOR_NAME[] = "__dtor", Symbol_name::PTR_OPERATOR_NAME[] = "__opar", Symbol_name::CALL_OPERATOR_NAME[] = "__opf", Symbol_name::ASSIGNMENT_OPERATOR_NAME[] = "__opas", Symbol_name::INDEX_OPERATOR_NAME[] = "__opi", Symbol_name::NEW_OPERATOR_NAME[] = "__opnew", Symbol_name::ANEW_OPERATOR_NAME[] = "__opanew", Symbol_name::DELETE_OPERATOR_NAME[] = "__opdel", Symbol_name::ADELETE_OPERATOR_NAME[] = "__opadel", Symbol_name::COMMA_OPERATOR_NAME[] = "__opcom"; /** Given an operator symbol, return its mangled name. */ PUBLIC static const char* Symbol_name::get_operator_name(Ptree* tree) { if (tree->IsLeaf()) { if (tree->Eq('+')) return "__opp"; // plus else if (tree->Eq('-')) return "__opm"; // minus else if (tree->Eq('*')) return "__opt"; // times else if (tree->Eq('/')) return "__opd"; // divide else if (tree->Eq('&')) return "__opa"; // and else if (tree->Eq('!')) return "__opn"; // not else if (tree->Eq('~')) return "__opc"; // complement else if (tree->Eq('%')) return "__opr"; // remainder else if (tree->Eq(',')) return COMMA_OPERATOR_NAME; // comma else if (tree->Eq('^')) return "__opx"; // xor else if (tree->Eq('|')) return "__opo"; // or else if (tree->Eq('<')) return "__oplt"; // less than else if (tree->Eq('>')) return "__opgt"; // greater than else if (tree->Eq('=')) return ASSIGNMENT_OPERATOR_NAME; // assign else if (tree->Eq("++")) return "__oppp"; // plusplus else if (tree->Eq("--")) return "__opmm"; // minusminus else if (tree->Eq("<<")) return "__opll"; // left left / less less else if (tree->Eq(">>")) return "__oprr"; // right right else if (tree->Eq("<=")) return "__ople"; else if (tree->Eq(">=")) return "__opge"; else if (tree->Eq("==")) return "__opeq"; else if (tree->Eq("!=")) return "__opne"; else if (tree->Eq("->")) return PTR_OPERATOR_NAME; // arrow else if (tree->Eq("&&")) return "__opaa"; // andand else if (tree->Eq("||")) return "__opoo"; // oror else if (tree->Eq("*=")) return "__optas"; // times-assign else if (tree->Eq("/=")) return "__opdas"; else if (tree->Eq("%=")) return "__opras"; else if (tree->Eq("+=")) return "__oppas"; else if (tree->Eq("-=")) return "__opmas"; else if (tree->Eq(">>=")) return "__oprras"; else if (tree->Eq("<<=")) return "__opllas"; else if (tree->Eq("&=")) return "__opaas"; else if (tree->Eq("^=")) return "__opxas"; else if (tree->Eq("|=")) return "__opoas"; else if (tree->Eq(".*")) return "__oppm"; else if (tree->Eq("->*")) return "__opppm"; else if (tree->Eq("new")) return NEW_OPERATOR_NAME; else if (tree->Eq("delete")) return DELETE_OPERATOR_NAME; else return 0; } else { Ptree* c = tree->Car(); if (c->Eq('[')) return INDEX_OPERATOR_NAME; // index else if (c->Eq('(')) return CALL_OPERATOR_NAME; // funcall else if (c->Eq("new")) return ANEW_OPERATOR_NAME; else if (c->Eq("delete")) return ADELETE_OPERATOR_NAME; else return 0; } }