/** * \file enum.cpp * \brief Enumeration/Enumerator * \author Stefan Reuther */ INTERFACE: #include #include "symbol_table.h" #include "type_rep.h" class Variable_symbol; class Enum_symbol : public Type_symbol { std::vector values; }; IMPLEMENTATION: #include #include "except.h" #include "downcast.h" #include "scope.h" #include "expr_annotator.h" #include "variable.h" #include "symbol_name.h" using namespace Opencxx; /** \class Enum_symbol \brief Enumeration type We use the following representation for enums: - the Enum_symbol is just a symbol-table placeholder to represent the type. - the individual enumerators are Variable_symbol's without address. - each of the enumerators is initialized with "0" (first one) or "previous_enum + 1" unless it is already initialized explicitly. */ PUBLIC Enum_symbol::Enum_symbol() { set_status(st_Declared); // enums can't be referenced } PUBLIC Enum_symbol::~Enum_symbol() { } PUBLIC Symbol::Kind Enum_symbol::get_kind() const { return k_Enum; } /** Process an enumerator definition. \param tree the enumerator definition. Either a symbol, or [symbol = expr] \param previous return value of process_value for previous enumerator, 0 on initial call \param scope scope to put identifiers into */ PUBLIC Ptree* Enum_symbol::process_value(Ptree* tree, Ptree* previous, Abstract_scope* scope) { /* split tree into symbol and value */ Ptree *name, *init; if (tree->IsLeaf()) { name = tree; if (!previous) init = new Leaf("0", 1); else init = new PtreeInfixExpr(previous, Ptree::List(new Leaf("+", 1), new Leaf("1", 1))); } else { expect_ptree(tree->Second(), '='); name = tree->First(); init = tree->Third(); } assert(name->IsLeaf()); /* annotate the initializer. This will always find a builtin operator. There cannot be any user-defined operator+ which takes a enum type on the lhs, because we're just defining the enum and the operator must be defined after the enum definition. There's no way to forward-declare an enum in ISO C++. */ Expr_result res = Expr_annotator(scope, &Source::instance()).visit(init); res.do_integral_promotions(); if (!res.get_type().is_int()) compile_error("enum initializer must be integer or enum"); Variable_symbol* vsym = new Variable_symbol(get_type(), // type s_None, // storage class res.get_tree(), // initializer 0, // bitsize false); // has no address scope->add_symbol(std::string(name->ToString()), vsym); values.push_back(vsym); Token t = { name->GetPosition(), name->GetLength(), ntName }; return new LeafName(t); } /** Parse an enumeration definition. Returns the generated type. Tree is ["enum" name-or-nil brace-or-missing] */ Type parse_enum(Ptree* tree, Abstract_scope* scope, Ptree* name_for_anon) { expect_ptree(tree->First(), "enum"); Ptree* nametree = tree->Second(); Ptree* content = tree->Third(); /* if there's no definition, it is a reference to an * already-defined enum */ if (!content) { if (!nametree) compile_error("stray `enum' in program"); Symbol_pair sym = Symbol_name(nametree, scope, false).lookup_for_use(false); if (!sym.tag || sym.tag->get_kind() != Symbol::k_Enum) compile_error("`enum " + std::string(nametree->ToString()) + "' not defined"); return downcast(sym.tag)->get_type(); } /* It is a definition. Figure out name. */ std::string name; if (!nametree) nametree = name_for_anon; Symbol_name sname(nametree, scope, false); if (sname.is_qualified()) compile_error("qualified name not allowed in definition"); if (sname.is_template()) compile_error("template enum"); name = sname.get_name(); Enum_symbol* sym; Symbol_pair pair; if ((pair = scope->lookup_here(name, true)) && pair.tag) { if (pair.tag->get_kind() != Symbol::k_Enum) compile_error("`" + name + "' already defined"); sym = downcast(pair.tag); } else { sym = new Enum_symbol(); scope->add_symbol(name, sym); } if (sym->is_defined()) compile_error("`enum " + name + "' already defined"); /* now parse content */ assert(!content->IsLeaf()); expect_ptree(content->First(), '{'); expect_ptree(content->Third(), '}'); Ptree* previous = 0; for (Ptree* p = content->Second(); p; (p = p->Cdr()) && (p = p->Cdr())) { previous = sym->process_value(p->Car(), previous, scope); } sym->set_defined(); return sym->get_type(); }