Exemplo n.º 1
0
/* Parse call to builtin symbol __builtin_va_start, which is the result of
 * calling va_start(arg, s). Return type depends on second input argument.
 */
static struct block *parse__builtin_va_start(struct block *block)
{
    const struct typetree *type;
    struct symbol *sym;
    struct token param;
    int is_invalid;

    consume('(');
    block = assignment_expression(block);
    consume(',');
    param = consume(IDENTIFIER);
    sym = sym_lookup(&ns_ident, param.strval);

    type = &current_func()->symbol->type;
    is_invalid = !sym || sym->depth != 1 || !is_function(type);
    is_invalid = is_invalid || !nmembers(type) || strcmp(
        get_member(type, nmembers(type) - 1)->name, param.strval);

    if (is_invalid) {
        error("Second parameter of va_start must be last function argument.");
        exit(1);
    }

    consume(')');
    block->expr = eval__builtin_va_start(block, block->expr);
    return block;
}
Exemplo n.º 2
0
void be_exception::GenerateAssignmentOperator (be_ClientImplementation& source)
{
   ostream & os = source.Stream ();
   DDS_StdString that ("");
   be_Type * btype;

   if (nmembers ())
   {
      that = " that";
   }

   os << ScopedName () << " & "
      << ScopedName () << "::operator = (const "
      << LocalName () << " &" << that << ")" << nl;
   os << "{" << nl;

   UTL_Scope * s = (UTL_Scope*)narrow((long) & UTL_Scope::type_id);
   assert (s);

   UTL_ScopeActiveIterator *it;

   // Iterate through decls

   for 
   (
      it = new UTL_ScopeActiveIterator (s, UTL_Scope::IK_decls);
      ! it->is_done ();
      it->next ()
   )
   {
      AST_Decl * adecl = it->item ();
      assert (adecl);

      be_field *bfield = (be_field *) adecl->narrow ((long) & be_field::type_id);

      if (bfield)
      {
         btype = bfield->get_be_type ();
         if (btype && btype->IsArrayType ())
         {
            // Need to copy array elements

            os << "   "
               << (char*) BE_Globals::RelativeScope (ScopedName (), bfield->StructMemberTypeName ())
               << "_copy (" << bfield->get_local_name () 
               << ", that." << bfield->get_local_name () << ");" << nl;
         }
         else
         {
            os << "   " << bfield->get_local_name () << " = that."
               << bfield->get_local_name() << ";" << nl;
         }
      }
   }

   delete it;

   os << "   return *this;" << nl;
   os << "}" << nl << nl;
}
Exemplo n.º 3
0
/* FOLLOW(parameter-list) = { ')' }, peek to return empty list; even though K&R
 * require at least specifier: (void)
 * Set parameter-type-list = parameter-list, including the , ...
 */
static struct typetree *parameter_list(const struct typetree *base)
{
    struct typetree *func = type_init(T_FUNCTION);
    func->next = base;

    while (peek().token != ')') {
        const char *name = NULL;
        struct typetree *type;

        type = declaration_specifiers(NULL);
        type = declarator(type, &name);
        if (is_void(type)) {
            if (nmembers(func)) {
                error("Incomplete type in parameter list.");
            }
            break;
        }

        type_add_member(func, name, type);
        if (peek().token != ',') {
            break;
        }

        consume(',');
        if (peek().token == ')') {
            error("Unexpected trailing comma in parameter list.");
            exit(1);
        } else if (peek().token == DOTS) {
            consume(DOTS);
            assert(!is_vararg(func));
            type_add_member(func, "...", NULL);
            assert(is_vararg(func));
            break;
        }
    }

    return func;
}
Exemplo n.º 4
0
void be_exception::Generate (be_ClientImplementation& source)
{
   ostream & os = source.Stream();
   be_Tab tab (source);
   UTL_String * repID = get_decl_pragmas().get_repositoryID();
   const DDS_StdString corbaException = "DDS::Exception";

   // convenience constructor

   if (nmembers())
   {
      GenerateConvenienceConstructor(source);
   }

   // copy constructor

   GenerateCopyConstructor (source);

   // assignment operator

   GenerateAssignmentOperator (source);

   be_CodeGenerator::Generate (source);

   // name and id

   assert (repID);
   os << "const char * " << ScopedName () << "::m_name = \"" 
      << LocalName () << "\";" << nl << nl;
   os << "const char * " << ScopedName () << "::m_id = \"" 
      << repID->get_string () << "\";" << nl << nl;

   // downcast

   os << tab << ScopedName () << " * ";
   os << ScopedName () << "::";
   os << "_downcast (DDS::Exception * e)" << nl;
   os << tab << "{" << nl;
   tab.indent ();
   os << tab << "if (e && (e->_rep_id () == m_id))" << nl;
   os << tab << "{" << nl;
   tab.indent ();
   os << tab << "return static_cast < " << LocalName () << "*> (e);" << nl;
   tab.outdent ();
   os << tab << "}" << nl;
   os << tab << "return 0;" << nl;
   tab.outdent ();
   os << tab << "}" << nl << nl;

   // const downcast

   os << tab << "const " << ScopedName() << " * ";
   os << ScopedName () << "::";
   os << "_downcast (const DDS::Exception * e)" << nl;
   os << tab << "{" << nl;
   tab.indent ();
   os << tab << "if (e && (e->_rep_id () == m_id))" << nl;
   os << tab << "{" << nl;
   tab.indent ();
   os << tab << "return static_cast < " << LocalName () 
      << "*> ((" << LocalName () << "*) e);" << nl;
   tab.outdent ();
   os << tab << "}" << nl;
   os << tab << "return 0;" << nl;
   tab.outdent ();
   os << tab << "}" << nl << nl;

   // _clone

   os << tab << corbaException << " * " << ScopedName ();
   os << "::_clone () const" << nl;
   os << tab << "{" << nl;
   tab.indent ();
   os << tab << "return (" << corbaException << "*) new ";
   os << ScopedName () << " (*this);" << nl;
   tab.outdent ();
   os << tab << "}" << nl << nl;

   // raise

   os << tab << "void " << ScopedName () << "::_raise ("
      << XBE_Ev::arg (XBE_ENV_ARG1) << ") const" << nl;
   os << tab << "{" << nl;
   tab.indent ();
   os << tab;
   XBE_Ev::throwex (os, "*this");
   os << nl;
   tab.outdent ();
   os << tab << "}" << nl << nl;

   // factory

   os << tab << "DDS::Exception * " << ScopedName() << "::factory ()" << nl;
   os << tab << "{" << nl;
   tab.indent ();
   os << tab << "return new " << ScopedName () << ";" << nl;
   tab.outdent ();
   os << tab << "}" << nl << nl;

   // initilaizer

   os << "DDS::ExceptionInitializer " << ScopedName() << "::m_initializer (";
   os << "\"" << repID->get_string () << "\"";
   os << ", " << ScopedName () << "::factory);" << nl;
}
Exemplo n.º 5
0
void be_exception::Generate (be_ClientHeader & source)
{
   if (!Generated ())
   {
      ostream & os = source.Stream ();
      be_Tab tab (source);
      DDS_StdString lname = LocalName ();

      Generated (pbtrue);

      be_root::AddGlobalDeclarations (this);
      be_root::AddAnyOps (*this);
      be_root::AddStreamOps (*this);
      be_root::AddTypedef (*this);
      be_root::AddTypecode (*this);

      GenerateOpenClassDefinition (source);
      GenerateClassDeclarations (source);
      SetAccess (source, CA_PUBLIC);

      // now define nested types

      be_CodeGenerator::Generate (source);

      // generate _downcast

      os << tab << "static " << lname << "* _downcast ("
         << "DDS::Exception *);" << nl;
      os << tab << "static const " << lname << "* _downcast ("
         << "const DDS::Exception *);" << nl;

      // generate factory and builder

      os << tab << "static DDS::Exception * factory ();" << nl;
      os << tab << "static DDS::ExceptionInitializer m_initializer;" << nl << nl;

      // generate inline default constructor

      os << tab << lname << " () {};" << nl;

      // generate convenience constructor

      if (nmembers())
      {
         GenerateConvenienceConstructor (source);
      }

      // generate copy constructor

      os << tab << lname << " (const " << lname << " &);" << nl;

      // generate assignment operator

      os << tab << lname << "& operator = " << "(const " << lname << " &);" << nl;

      // generate duplicate

      os << tab << "virtual DDS::Exception * _clone () const;" << nl;

      // generate raise

      os << tab << "virtual void _raise (" << XBE_Ev::arg (XBE_ENV_ARG1)
         << ") const;" << nl;

      // generate name

      os << tab << "virtual const char * _name () const { return m_name; };" << nl;

      // generate repository id

      os << tab << "virtual const char * _rep_id () const { return m_id; };" << nl;

      // generate virtual destructor

      os << tab << "virtual ~" << lname << " () {}" << nl;

      GenerateMembers (source);
      SetAccess (source, CA_PRIVATE);

      os << tab << "static const char * m_name;" << nl;
      os << tab << "static const char * m_id;" << nl;

      GenerateCloseClassDefinition (source);

      be_root::GenerateDependants
      (
         source,
         SequenceMemberTypeName (),
         EnclosingScope ()
      );
   }
}
Exemplo n.º 6
0
void be_exception::GenerateConvenienceConstructor (be_ClientImplementation & source)
{
   ostream & os = source.Stream ();
   const char * argPrefix = "_";
   pbbool first = pbtrue;
   be_Tab tab (source);
   be_Type * btype;

   os << ScopedName () << "::" << LocalName () << " (";

   UTL_Scope * s = (UTL_Scope*)narrow ((long) & UTL_Scope::type_id);
   assert (s);

   // Iterate through decls

   UTL_ScopeActiveIterator * it;

   for
   (
      it = new UTL_ScopeActiveIterator (s, UTL_Scope::IK_decls);
      !it->is_done ();
      it->next ()
   )
   {
      AST_Decl * adecl = it->item ();
      assert (adecl);

      be_field *bfield = (be_field *)adecl->narrow ((long) & be_field::type_id);

      if (bfield)
      {
         btype = bfield->get_be_type ();

         if (!first)
         {
            os << ", ";
         }

         first = pbfalse;

         if (btype && btype->IsStringType ())
         {
            // Strings are special case

            os << (char*) BE_Globals::RelativeScope (ScopedName (), bfield->InTypeName ());
         }
         else
         {
            os << (char*) BE_Globals::RelativeScope (ScopedName (), bfield->StructMemberTypeName ());
         }

         os << " " << argPrefix << (char*) bfield->get_local_name ();
      }
   }
   delete it;

   os << ")" << nl;

   // Iterate thru members initializing

   if (nmembers ())
   {
      source.Indent ();

      first = pbtrue;

      for
      (
         it = new UTL_ScopeActiveIterator (s, UTL_Scope::IK_decls);
         !it->is_done ();
         it->next ()
      )
      {
         AST_Decl * adecl = it->item ();
         assert (adecl);

         be_field *bfield = (be_field *)adecl->narrow ((long) & be_field::type_id);

         if (bfield)
         {
            btype = bfield->get_be_type ();

            // Array members are assigned in body of method

            if (btype && ! btype->IsArrayType ())
            {
               if (first)
               {
                  first = pbfalse;
                  os << "   : " << nl;
               }
               else
               {
                  os << "," << nl;
               }

               os << "   " << (char*) bfield->get_local_name () << "(" << argPrefix
               << (char*) bfield->get_local_name () << ")";
            }
         }
      }
      delete it;

      source.Outdent ();
   }

   os << nl;
   os << tab << "{" << nl;

   for 
   (
      it = new UTL_ScopeActiveIterator (s, UTL_Scope::IK_decls);
      ! it->is_done ();
      it->next ()
   )
   {
      AST_Decl * adecl = it->item ();
      assert (adecl);

      be_field *bfield = (be_field *) adecl->narrow ((long) & be_field::type_id);

      if (bfield)
      {
         btype = bfield->get_be_type ();
         if (btype && btype->IsArrayType ())
         {
            // Need to copy array elements

            os << "   "
               << (char*) BE_Globals::RelativeScope (ScopedName (), bfield->StructMemberTypeName ())
               << "_copy (" << argPrefix << bfield->get_local_name () 
               << ", " << bfield->get_local_name () << ");" << nl;
         }
      }
   }
   delete it;

   os << "}" << nl << nl;
}
Exemplo n.º 7
0
static struct block *postfix_expression(struct block *block)
{
    struct var root;

    block = primary_expression(block);
    root = block->expr;

    while (1) {
        const struct member *field;
        const struct typetree *type;
        struct var expr, copy, *arg;
        struct token tok;
        int i, j;

        switch ((tok = peek()).token) {
        case '[':
            do {
                /* Evaluate a[b] = *(a + b). The semantics of pointer arithmetic
                 * takes care of multiplying b with the correct width. */
                consume('[');
                block = expression(block);
                root = eval_expr(block, IR_OP_ADD, root, block->expr);
                root = eval_deref(block, root);
                consume(']');
            } while (peek().token == '[');
            break;
        case '(':
            type = root.type;
            if (is_pointer(root.type) && is_function(root.type->next))
                type = type_deref(root.type);
            else if (!is_function(root.type)) {
                error("Expression must have type pointer to function, was %t.",
                    root.type);
                exit(1);
            }
            consume('(');
            arg = calloc(nmembers(type), sizeof(*arg));
            for (i = 0; i < nmembers(type); ++i) {
                if (peek().token == ')') {
                    error("Too few arguments, expected %d but got %d.",
                        nmembers(type), i);
                    exit(1);
                }
                block = assignment_expression(block);
                arg[i] = block->expr;
                /* todo: type check here. */
                if (i < nmembers(type) - 1) {
                    consume(',');
                }
            }
            while (is_vararg(type) && peek().token != ')') {
                consume(',');
                arg = realloc(arg, (i + 1) * sizeof(*arg));
                block = assignment_expression(block);
                arg[i] = block->expr;
                i++;
            }
            consume(')');
            for (j = 0; j < i; ++j)
                param(block, arg[j]);
            free(arg);
            root = eval_call(block, root);
            break;
        case '.':
            consume('.');
            tok = consume(IDENTIFIER);
            field = find_type_member(root.type, tok.strval);
            if (!field) {
                error("Invalid field access, no member named '%s'.",
                    tok.strval);
                exit(1);
            }
            root.type = field->type;
            root.offset += field->offset;
            break;
        case ARROW:
            consume(ARROW);
            tok = consume(IDENTIFIER);
            if (is_pointer(root.type) && is_struct_or_union(root.type->next)) {
                field = find_type_member(type_deref(root.type), tok.strval);
                if (!field) {
                    error("Invalid field access, no member named '%s'.",
                        tok.strval);
                    exit(1);
                }

                /* Make it look like a pointer to the field type, then perform
                 * normal dereferencing. */
                root.type = type_init(T_POINTER, field->type);
                root = eval_deref(block, root);
                root.offset = field->offset;
            } else {
                error("Invalid field access.");
                exit(1);
            }
            break;
        case INCREMENT:
            consume(INCREMENT);
            copy = create_var(root.type);
            eval_assign(block, copy, root);
            expr = eval_expr(block, IR_OP_ADD, root, var_int(1));
            eval_assign(block, root, expr);
            root = copy;
            break;
        case DECREMENT:
            consume(DECREMENT);
            copy = create_var(root.type);
            eval_assign(block, copy, root);
            expr = eval_expr(block, IR_OP_SUB, root, var_int(1));
            eval_assign(block, root, expr);
            root = copy;
            break;
        default:
            block->expr = root;
            return block;
        }
    }
}
Exemplo n.º 8
0
/* Cover both external declarations, functions, and local declarations (with
 * optional initialization code) inside functions.
 */
struct block *declaration(struct block *parent)
{
    struct typetree *base;
    enum symtype symtype;
    enum linkage linkage;
    int stc = '$';

    base = declaration_specifiers(&stc);
    switch (stc) {
    case EXTERN:
        symtype = SYM_DECLARATION;
        linkage = LINK_EXTERN;
        break;
    case STATIC:
        symtype = SYM_TENTATIVE;
        linkage = LINK_INTERN;
        break;
    case TYPEDEF:
        symtype = SYM_TYPEDEF;
        linkage = LINK_NONE;
        break;
    default:
        if (!ns_ident.current_depth) {
            symtype = SYM_TENTATIVE;
            linkage = LINK_EXTERN;
        } else {
            symtype = SYM_DEFINITION;
            linkage = LINK_NONE;
        }
        break;
    }

    while (1) {
        struct definition *def;
        const char *name = NULL;
        const struct typetree *type;
        struct symbol *sym;

        type = declarator(base, &name);
        if (!name) {
            consume(';');
            return parent;
        }

        sym = sym_add(&ns_ident, name, type, symtype, linkage);
        if (ns_ident.current_depth) {
            assert(ns_ident.current_depth > 1);
            def = current_func();
            def->locals = sym_list_add(def->locals, sym);
        }

        switch (peek().token) {
        case ';':
            consume(';');
            return parent;
        case '=':
            if (sym->symtype == SYM_DECLARATION) {
                error("Extern symbol '%s' cannot be initialized.", sym->name);
                exit(1);
            }
            if (!sym->depth && sym->symtype == SYM_DEFINITION) {
                error("Symbol '%s' was already defined.", sym->name);
                exit(1);
            }
            consume('=');
            sym->symtype = SYM_DEFINITION;
            if (sym->linkage == LINK_NONE) {
                assert(parent);
                parent = initializer(parent, var_direct(sym));
            } else {
                assert(sym->depth || !parent);
                def = push_back_definition(sym);
                initializer(def->body, var_direct(sym));
            }
            assert(size_of(&sym->type) > 0);
            if (peek().token != ',') {
                consume(';');
                return parent;
            }
            break;
        case '{': {
            int i;
            if (!is_function(&sym->type) || sym->depth) {
                error("Invalid function definition.");
                exit(1);
            }
            assert(!parent);
            assert(sym->linkage != LINK_NONE);
            sym->symtype = SYM_DEFINITION;
            def = push_back_definition(sym);
            push_scope(&ns_ident);
            define_builtin__func__(sym->name);
            for (i = 0; i < nmembers(&sym->type); ++i) {
                name = get_member(&sym->type, i)->name;
                type = get_member(&sym->type, i)->type;
                symtype = SYM_DEFINITION;
                linkage = LINK_NONE;
                if (!name) {
                    error("Missing parameter name at position %d.", i + 1);
                    exit(1);
                }
                def->params = sym_list_add(def->params,
                    sym_add(&ns_ident, name, type, symtype, linkage));
            }
            parent = block(def->body);
            pop_scope(&ns_ident);
            return parent;
        }
        default:
            break;
        }
        consume(',');
    }
}
Exemplo n.º 9
0
static struct block *object_initializer(struct block *block, struct var target)
{
    int i,
        filled = target.offset;
    const struct typetree *type = target.type;

    assert(!is_tagged(type));

    consume('{');
    target.lvalue = 1;
    switch (type->type) {
    case T_UNION:
        /* C89 states that only the first element of a union can be
         * initialized. Zero the whole thing first if there is padding. */
        if (size_of(get_member(type, 0)->type) < type->size) {
            target.type =
                (type->size % 8) ?
                    type_init(T_ARRAY, &basic_type__char, type->size) :
                    type_init(T_ARRAY, &basic_type__long, type->size / 8);
            zero_initialize(block, target);
        }
        target.type = get_member(type, 0)->type;
        block = initializer(block, target);
        if (peek().token != '}') {
            error("Excess elements in union initializer.");
            exit(1);
        }
        break;
    case T_STRUCT:
        for (i = 0; i < nmembers(type); ++i) {
            target.type = get_member(type, i)->type;
            target.offset = filled + get_member(type, i)->offset;
            block = initializer(block, target);
            if (peek().token == ',') {
                consume(',');
            } else break;
            if (peek().token == '}') {
                break;
            }
        }
        while (++i < nmembers(type)) {
            target.type = get_member(type, i)->type;
            target.offset = filled + get_member(type, i)->offset;
            zero_initialize(block, target);
        }
        break;
    case T_ARRAY:
        target.type = type->next;
        for (i = 0; !type->size || i < type->size / size_of(type->next); ++i) {
            target.offset = filled + i * size_of(type->next);
            block = initializer(block, target);
            if (peek().token == ',') {
                consume(',');
            } else break;
            if (peek().token == '}') {
                break;
            }
        }
        if (!type->size) {
            assert(!target.symbol->type.size);
            assert(is_array(&target.symbol->type));

            /* Incomplete array type can only be in the root level of target
             * type tree, overwrite type directly in symbol. */
            ((struct symbol *) target.symbol)->type.size =
                (i + 1) * size_of(type->next);
        } else {
            while (++i < type->size / size_of(type->next)) {
                target.offset = filled + i * size_of(type->next);
                zero_initialize(block, target);
            }
        }
        break;
    default:
        error("Block initializer only apply to aggregate or union type.");
        exit(1);
    }

    consume('}');
    return block;
}
Exemplo n.º 10
0
static void member_declaration_list(struct typetree *type)
{
    struct namespace ns = {0};
    struct typetree *decl_base, *decl_type;
    const char *name;

    push_scope(&ns);

    do {
        decl_base = declaration_specifiers(NULL);

        do {
            name = NULL;
            decl_type = declarator(decl_base, &name);

            if (!name) {
                error("Missing name in member declarator.");
                exit(1);
            } else if (!size_of(decl_type)) {
                error("Field '%s' has incomplete type '%t'.", name, decl_type);
                exit(1);
            } else {
                sym_add(&ns, name, decl_type, SYM_DECLARATION, LINK_NONE);
                type_add_member(type, name, decl_type);
            }

            if (peek().token == ',') {
                consume(',');
                continue;
            }
        } while (peek().token != ';');

        consume(';');
    } while (peek().token != '}');

    pop_scope(&ns);
}

static struct typetree *struct_or_union_declaration(void)
{
    struct symbol *sym = NULL;
    struct typetree *type = NULL;
    enum type kind =
        (next().token == STRUCT) ? T_STRUCT : T_UNION;

    if (peek().token == IDENTIFIER) {
        const char *name = consume(IDENTIFIER).strval;
        sym = sym_lookup(&ns_tag, name);
        if (!sym) {
            type = type_init(kind);
            sym = sym_add(&ns_tag, name, type, SYM_TYPEDEF, LINK_NONE);
        } else if (is_integer(&sym->type)) {
            error("Tag '%s' was previously declared as enum.", sym->name);
            exit(1);
        } else if (sym->type.type != kind) {
            error("Tag '%s' was previously declared as %s.",
                sym->name, (sym->type.type == T_STRUCT) ? "struct" : "union");
            exit(1);
        }

        /* Retrieve type from existing symbol, possibly providing a complete
         * definition that will be available for later declarations. Overwrites
         * existing type information from symbol table. */
        type = &sym->type;
        if (peek().token == '{' && type->size) {
            error("Redefiniton of '%s'.", sym->name);
            exit(1);
        }
    }

    if (peek().token == '{') {
        if (!type) {
            /* Anonymous structure; allocate a new standalone type,
             * not part of any symbol. */
            type = type_init(kind);
        }

        consume('{');
        member_declaration_list(type);
        assert(type->size);
        consume('}');
    }

    /* Return to the caller a copy of the root node, which can be overwritten
     * with new type qualifiers without altering the tag registration. */
    return (sym) ? type_tagged_copy(&sym->type, sym->name) : type;
}

static void enumerator_list(void)
{
    struct var val;
    struct symbol *sym;
    int enum_value = 0;

    consume('{');
    do {
        const char *name = consume(IDENTIFIER).strval;

        if (peek().token == '=') {
            consume('=');
            val = constant_expression();
            if (!is_integer(val.type)) {
                error("Implicit conversion from non-integer type in enum.");
            }
            enum_value = val.imm.i;
        }

        sym = sym_add(
            &ns_ident,
            name,
            &basic_type__int,
            SYM_ENUM_VALUE,
            LINK_NONE);
        sym->enum_value = enum_value++;

        if (peek().token != ',')
            break;
        consume(',');
    } while (peek().token != '}');
    consume('}');
}

static struct typetree *enum_declaration(void)
{
    struct typetree *type = type_init(T_SIGNED, 4);

    consume(ENUM);
    if (peek().token == IDENTIFIER) {
        struct symbol *tag = NULL;
        const char *name = consume(IDENTIFIER).strval;

        tag = sym_lookup(&ns_tag, name);
        if (!tag || tag->depth < ns_tag.current_depth) {
            tag = sym_add(&ns_tag, name, type, SYM_TYPEDEF, LINK_NONE);
        } else if (!is_integer(&tag->type)) {
            error("Tag '%s' was previously defined as aggregate type.",
                tag->name);
            exit(1);
        }

        /* Use enum_value as a sentinel to represent definition, checked on 
         * lookup to detect duplicate definitions. */
        if (peek().token == '{') {
            if (tag->enum_value) {
                error("Redefiniton of enum '%s'.", tag->name);
            }
            enumerator_list();
            tag->enum_value = 1;
        }
    } else {
        enumerator_list();
    }

    /* Result is always integer. Do not care about the actual enum definition,
     * all enums are ints and no type checking is done. */
    return type;
}

static struct typetree get_basic_type_from_specifier(unsigned short spec)
{
    switch (spec) {
    case 0x0001: /* void */
        return basic_type__void;
    case 0x0002: /* char */
    case 0x0012: /* signed char */
        return basic_type__char;
    case 0x0022: /* unsigned char */
        return basic_type__unsigned_char;
    case 0x0004: /* short */
    case 0x0014: /* signed short */
    case 0x000C: /* short int */
    case 0x001C: /* signed short int */
        return basic_type__short;
    case 0x0024: /* unsigned short */
    case 0x002C: /* unsigned short int */
        return basic_type__unsigned_short;
    case 0x0008: /* int */
    case 0x0010: /* signed */
    case 0x0018: /* signed int */
        return basic_type__int;
    case 0x0020: /* unsigned */
    case 0x0028: /* unsigned int */
        return basic_type__unsigned_int;
    case 0x0040: /* long */
    case 0x0050: /* signed long */
    case 0x0048: /* long int */
    case 0x0058: /* signed long int */
    case 0x00C0: /* long long */
    case 0x00D0: /* signed long long */
    case 0x00D8: /* signed long long int */
        return basic_type__long;
    case 0x0060: /* unsigned long */
    case 0x0068: /* unsigned long int */
    case 0x00E0: /* unsigned long long */
    case 0x00E8: /* unsigned long long int */
        return basic_type__unsigned_long;
    case 0x0100: /* float */
        return basic_type__float;
    case 0x0200: /* double */
    case 0x0240: /* long double */
        return basic_type__double;
    default:
        error("Invalid type specification.");
        exit(1); 
    }
}

/* Parse type, qualifiers and storage class. Do not assume int by default, but
 * require at least one type specifier. Storage class is returned as token
 * value, unless the provided pointer is NULL, in which case the input is parsed
 * as specifier-qualifier-list.
 */
struct typetree *declaration_specifiers(int *stc)
{
    struct typetree *type = NULL;
    struct token tok;
    int done = 0;

    /* Use a compact bit representation to hold state about declaration 
     * specifiers. Initialize storage class to sentinel value. */
    unsigned short spec = 0x0000;
    enum qualifier qual = Q_NONE;
    if (stc)       *stc =    '$';

    #define set_specifier(d) \
        if (spec & d) error("Duplicate type specifier '%s'.", tok.strval); \
        next(); spec |= d;

    #define set_qualifier(d) \
        if (qual & d) error("Duplicate type qualifier '%s'.", tok.strval); \
        next(); qual |= d;

    #define set_storage_class(t) \
        if (!stc) error("Unexpected storage class in qualifier list."); \
        else if (*stc != '$') error("Multiple storage class specifiers."); \
        next(); *stc = t;

    do {
        switch ((tok = peek()).token) {
        case VOID:      set_specifier(0x001); break;
        case CHAR:      set_specifier(0x002); break;
        case SHORT:     set_specifier(0x004); break;
        case INT:       set_specifier(0x008); break;
        case SIGNED:    set_specifier(0x010); break;
        case UNSIGNED:  set_specifier(0x020); break;
        case LONG:
            if (spec & 0x040) {
                set_specifier(0x080);
            } else {
                set_specifier(0x040);   
            }
            break;
        case FLOAT:     set_specifier(0x100); break;
        case DOUBLE:    set_specifier(0x200); break;
        case CONST:     set_qualifier(Q_CONST); break;
        case VOLATILE:  set_qualifier(Q_VOLATILE); break;
        case IDENTIFIER: {
            struct symbol *tag = sym_lookup(&ns_ident, tok.strval);
            if (tag && tag->symtype == SYM_TYPEDEF && !type) {
                consume(IDENTIFIER);
                type = type_init(T_STRUCT);
                *type = tag->type;
            } else {
                done = 1;
            }
            break;
        }
        case UNION:
        case STRUCT:
            if (!type) {
                type = struct_or_union_declaration();
            } else {
                done = 1;
            }
            break;
        case ENUM:
            if (!type) {
                type = enum_declaration();
            } else {
                done = 1;
            }
            break;
        case AUTO:
        case REGISTER:
        case STATIC:
        case EXTERN:
        case TYPEDEF:
            set_storage_class(tok.token);
            break;
        default:
            done = 1;
            break;
        }

        if (type && spec) {
            error("Invalid combination of declaration specifiers.");
            exit(1);
        }
    } while (!done);

    #undef set_specifier
    #undef set_qualifier
    #undef set_storage_class

    if (type) {
        if (qual & type->qualifier) {
            error("Duplicate type qualifier:%s%s.",
                (qual & Q_CONST) ? " const" : "",
                (qual & Q_VOLATILE) ? " volatile" : "");
        }
    } else if (spec) {
        type = type_init(T_STRUCT);
        *type = get_basic_type_from_specifier(spec);
    } else {
        error("Missing type specifier.");
        exit(1);
    }

    type->qualifier |= qual;
    return type;
}

/* Set var = 0, using simple assignment on members for composite types. This
 * rule does not consume any input, but generates a series of assignments on the
 * given variable. Point is to be able to zero initialize using normal simple
 * assignment rules, although IR can become verbose for large structures.
 */
static void zero_initialize(struct block *block, struct var target)
{
    int i;
    struct var var;
    assert(target.kind == DIRECT);

    switch (target.type->type) {
    case T_STRUCT:
    case T_UNION:
        target.type = unwrapped(target.type);
        var = target;
        for (i = 0; i < nmembers(var.type); ++i) {
            target.type = get_member(var.type, i)->type;
            target.offset = var.offset + get_member(var.type, i)->offset;
            zero_initialize(block, target);
        }
        break;
    case T_ARRAY:
        assert(target.type->size);
        var = target;
        target.type = target.type->next;
        assert(is_struct(target.type) || !target.type->next);
        for (i = 0; i < var.type->size / var.type->next->size; ++i) {
            target.offset = var.offset + i * var.type->next->size;
            zero_initialize(block, target);
        }
        break;
    case T_POINTER:
        var = var_zero(8);
        var.type = type_init(T_POINTER, &basic_type__void);
        eval_assign(block, target, var);
        break;
    case T_UNSIGNED:
    case T_SIGNED:
        var = var_zero(target.type->size);
        eval_assign(block, target, var);
        break;
    default:
        error("Invalid type to zero-initialize, was '%t'.", target.type);
        exit(1);
    }
}