Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Impressum sourceprovider-parser.y   Sprache: unbekannt

 
Spracherkennung für: .y vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/*TODO: check Exception, RuntimeException, XInterface defns */

%locations
%pure-parser

%{

#include <sal/config.h>

#include <o3tl/unreachable.hxx>
#include <o3tl/string_view.hxx>
#include <rtl/ustrbuf.hxx>
#include <unoidl/unoidl.hxx>

#include <algorithm>
#include <cassert>
#include <cerrno>
#include <cstddef>
#include <cstdlib>
#include <limits>
#include <new>
#include <utility>
#include <vector>

#include "sourceprovider-parser-requires.hxx"

%}

%union {
    sal_uInt64 ival;
    double fval;
    OString * sval;

    bool bval;
    std::vector<OUString> * excns;
    unoidl::detail::SourceProviderAccessDecls decls;
    unoidl::InterfaceTypeEntity::Method::Parameter::Direction dir;
    unoidl::detail::SourceProviderFlags flags;
    unoidl::detail::SourceProviderExpr expr;
    unoidl::detail::SourceProviderType * type;
    std::vector<unoidl::detail::SourceProviderType> * types;
}

/* TODO: %destructor { delete $$; } <sval> <excns> <type> <types> */

%lex-param {yyscan_t yyscanner}
%parse-param {yyscan_t yyscanner}

%{

#include <osl/file.h>
#include <osl/thread.h>
#include <sal/log.hxx>

#include "sourceprovider-scanner.hxx"

#define YYLLOC_DEFAULT(Current, Rhs, N) \
    do { (Current) = YYRHSLOC((Rhs), (N) ? 1 : 0); } while (0)

static void yyerror(YYLTYPE * locp, yyscan_t yyscanner, char const * msg) {
    assert(locp != nullptr);
    unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
    data->errorLine = *locp;
    data->parserError = OString(msg);
}

namespace {

void error(YYLTYPE location, yyscan_t yyscanner, OUString const & message) {
    unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
    data->errorLine = location;
    data->errorMessage = message;
}

OUString flagName(unoidl::detail::SourceProviderFlags flag) {
    switch (flag) {
    case unoidl::detail::FLAG_ATTRIBUTE:
        return u"attribute"_ustr;
    case unoidl::detail::FLAG_BOUND:
        return u"bound"_ustr;
    case unoidl::detail::FLAG_CONSTRAINED:
        return u"constrained"_ustr;
    case unoidl::detail::FLAG_MAYBEAMBIGUOUS:
        return u"maybeambiguous"_ustr;
    case unoidl::detail::FLAG_MAYBEDEFAULT:
        return u"maybedefault"_ustr;
    case unoidl::detail::FLAG_MAYBEVOID:
        return u"maybevoid"_ustr;
    case unoidl::detail::FLAG_OPTIONAL:
        return u"optional"_ustr;
    case unoidl::detail::FLAG_PROPERTY:
        return u"property"_ustr;
    case unoidl::detail::FLAG_READONLY:
        return u"readonly"_ustr;
    case unoidl::detail::FLAG_REMOVABLE:
        return u"removable"_ustr;
    case unoidl::detail::FLAG_TRANSIENT:
        return u"transient"_ustr;
    default:
        assert(false && "this cannot happen"); for (;;) { std::abort(); }
    }
}

OUString convertName(OString const * name) {
    assert(name != nullptr);
    OUString s(OStringToOUString(*name, RTL_TEXTENCODING_ASCII_US));
    delete name;
    return s;
}

OUString convertToFullName(
    unoidl::detail::SourceProviderScannerData const * data,
    OString const * identifier)
{
    assert(data != nullptr);
    OUString pref;
    if (!data->modules.empty()) {
        pref = data->modules.back() + ".";
    }
    return pref + convertName(identifier);
}

void convertToCurrentName(
    unoidl::detail::SourceProviderScannerData * data,
    OString const * identifier)
{
    assert(data != nullptr);
    assert(data->currentName.isEmpty());
    data->currentName = convertToFullName(data, identifier);
    assert(!data->currentName.isEmpty());
}

void clearCurrentState(unoidl::detail::SourceProviderScannerData * data) {
    assert(data != nullptr);
    data->currentName.clear();
    data->publishedContext = false;
}

unoidl::detail::SourceProviderEntity * getCurrentEntity(
    unoidl::detail::SourceProviderScannerData * data)
{
    assert(data != nullptr);
    assert(!data->currentName.isEmpty());
    std::map<OUString, unoidl::detail::SourceProviderEntity>::iterator i(
        data->entities.find(data->currentName));
    assert(i != data->entities.end());
    assert(i->second.kind == unoidl::detail::SourceProviderEntity::KIND_LOCAL);
    assert(i->second.pad.is());
    return &i->second;
}

template<typename T> rtl::Reference<T> getCurrentPad(
    unoidl::detail::SourceProviderScannerData * data)
{
    rtl::Reference<T> pad(dynamic_cast<T *>(getCurrentEntity(data)->pad.get()));
    assert(pad.is());
    return pad;
}

bool nameHasSameIdentifierAs(std::u16string_view name, std::u16string_view identifier)
{
    std::u16string_view::size_type pos = name.rfind('.');
    size_t i = (pos != std::u16string_view::npos) ? pos + 1 : 0;
    return identifier.size() == name.size() - i
        && o3tl::starts_with(name.substr(i), identifier);
}

bool coerce(
    YYLTYPE location, yyscan_t yyscanner,
    unoidl::detail::SourceProviderExpr * lhs,
    unoidl::detail::SourceProviderExpr * rhs)
{
    assert(lhs != nullptr);
    assert(rhs != nullptr);
    bool ok = bool(); // avoid warnings
    switch (lhs->type) {
    case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
        ok = rhs->type != unoidl::detail::SourceProviderExpr::TYPE_BOOL;
        break;
    case unoidl::detail::SourceProviderExpr::TYPE_INT:
        switch (rhs->type) {
        case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
            ok = false;
            break;
        case unoidl::detail::SourceProviderExpr::TYPE_INT:
            ok = true;
            break;
        case unoidl::detail::SourceProviderExpr::TYPE_UINT:
            if (lhs->ival >= 0) {
                lhs->type = unoidl::detail::SourceProviderExpr::TYPE_UINT;
                ok = true;
            } else if (rhs->uval <= SAL_MAX_INT64) {
                rhs->type = unoidl::detail::SourceProviderExpr::TYPE_INT;
                ok = true;
            } else {
                ok = false;
            }
            break;
        case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
            {
                auto tmp = lhs->ival;
                lhs->fval = tmp;
                ok = true;
            }
            break;
        }
        break;
    case unoidl::detail::SourceProviderExpr::TYPE_UINT:
        switch (rhs->type) {
        case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
            ok = false;
            break;
        case unoidl::detail::SourceProviderExpr::TYPE_INT:
            if (rhs->ival >= 0) {
                rhs->type = unoidl::detail::SourceProviderExpr::TYPE_UINT;
                ok = true;
            } else if (lhs->uval <= SAL_MAX_INT64) {
                lhs->type = unoidl::detail::SourceProviderExpr::TYPE_INT;
                ok = true;
            } else {
                ok = false;
            }
            break;
        case unoidl::detail::SourceProviderExpr::TYPE_UINT:
            ok = true;
            break;
        case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
            {
                auto nTmp = lhs->uval;
                lhs->fval = nTmp;
                ok = true;
            }
            break;
        }
        break;
    case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
        switch (rhs->type) {
        case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
            ok = false;
            break;
        case unoidl::detail::SourceProviderExpr::TYPE_INT:
            {
                auto tmp = rhs->ival;
                rhs->fval = tmp;
                ok = true;
            }
            break;
        case unoidl::detail::SourceProviderExpr::TYPE_UINT:
            {
                auto tmp = rhs->uval;
                rhs->fval = tmp;
                ok = true;
            }
            break;
        case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
            ok = true;
            break;
        }
        break;
    }
    if (!ok) {
        error(location, yyscanner, u"cannot coerce binary expression arguments"_ustr);
    }
    return ok;
}

unoidl::detail::SourceProviderEntity * findEntity_(
    unoidl::detail::SourceProviderScannerData * data, OUString * name)
{
    assert(data != nullptr);
    assert(name != nullptr);
    OUString n;
    if (!name->startsWith(".", &n)) {
        for (auto i(data->modules.rbegin()); i != data->modules.rend(); ++i) {
            n = *i + "." + *name;
            std::map<OUString, unoidl::detail::SourceProviderEntity>::iterator j(
                data->entities.find(n));
            if (j != data->entities.end()) {
                *name = n;
                return &j->second;
            }
            rtl::Reference<unoidl::Entity> ent(data->manager->findEntity(n));
            if (ent.is()) {
                std::map<OUString, unoidl::detail::SourceProviderEntity>::iterator
                    k(data->entities.emplace(
                              n,
                              unoidl::detail::SourceProviderEntity(
                                  unoidl::detail::SourceProviderEntity::KIND_EXTERNAL,
                                  ent)).
                      first);
                *name = n;
                return &k->second;
            }
        }
        n = *name;
    }
    std::map<OUString, unoidl::detail::SourceProviderEntity>::iterator i(
        data->entities.find(n));
    if (i != data->entities.end()) {
        *name = n;
        return &i->second;
    }
    rtl::Reference<unoidl::Entity> ent(data->manager->findEntity(n));
    if (ent.is()) {
        std::map<OUString, unoidl::detail::SourceProviderEntity>::iterator
            j(data->entities.emplace(
                      n,
                      unoidl::detail::SourceProviderEntity(
                          unoidl::detail::SourceProviderEntity::KIND_EXTERNAL,
                          ent)).
              first);
        *name = n;
        return &j->second;
    }
    return nullptr;
}

enum Found { FOUND_ERROR, FOUND_TYPE, FOUND_ENTITY };

Found findEntity(
    YYLTYPE location, yyscan_t yyscanner,
    unoidl::detail::SourceProviderScannerData * data,
    bool resolveInterfaceDefinitions, OUString * name,
    unoidl::detail::SourceProviderEntity const ** entity, bool * typedefed,
    unoidl::detail::SourceProviderType * typedefedType)
{
    //TODO: avoid recursion
    assert(data != nullptr);
    assert(name != nullptr);
    assert(entity != nullptr);
    unoidl::detail::SourceProviderEntity * e = findEntity_(data, name);
    OUString n(*name);
    OUString typeNucleus;
    std::size_t rank = 0;
    std::vector<unoidl::detail::SourceProviderType> args;
    for (;;) {
        if (e != nullptr) {
            switch (e->kind) {
            case unoidl::detail::SourceProviderEntity::KIND_LOCAL:
                if (e->pad.is()) {
                    break;
                }
                assert(e->entity.is());
                [[fallthrough]];
            case unoidl::detail::SourceProviderEntity::KIND_EXTERNAL:
                if (e->entity->getSort() == unoidl::Entity::SORT_TYPEDEF) {
                    if (typedefed != nullptr) {
                        *typedefed = true;
                    }
                    if (data->publishedContext
                        && !static_cast<unoidl::TypedefEntity *>(
                            e->entity.get())->isPublished())
                    {
                        error(
                            location, yyscanner,
                            ("type " + *name + " based on unpublished typedef "
                             + n + " used in published context"));
                        return FOUND_ERROR;
                    }
                    OUString t(
                        static_cast<unoidl::TypedefEntity *>(e->entity.get())
                        ->getType());
                    typeNucleus = t;
                    while (typeNucleus.startsWith("[]", &typeNucleus)) {
                        if (!args.empty()) {
                            error(
                                location, yyscanner,
                                ("inconsistent type manager: bad type " + *name
                                 + (" based on instantiated polymorphic struct"
                                    " type based on sequence type named ")
                                 + t));
                            return FOUND_ERROR;
                        }
                        if (rank == std::numeric_limits<std::size_t>::max()) {
                            error(
                                location, yyscanner,
                                ("bad type " + *name
                                 + " based on sequence type of too high rank"));
                            return FOUND_ERROR;
                        }
                        ++rank;
                    }
                    sal_Int32 i = typeNucleus.indexOf('<');
                    if (i != -1) {
                        if (!args.empty()) {
                            error(
                                location, yyscanner,
                                ("inconsistent type manager: bad type " + *name
                                 + (" based on instantiated polymorphic struct"
                                    " type based on instantiated polymorphic"
                                    " struct type named ")
                                 + t));
                            return FOUND_ERROR;
                        }
                        std::u16string_view tmpl(typeNucleus.subView(0, i));
                        do {
                            ++i; // skip '<' or ','
                            sal_Int32 j = i;
                            for (sal_Int32 level = 0;
                                 j != typeNucleus.getLength(); ++j)
                            {
                                sal_Unicode c = typeNucleus[j];
                                if (c == ',') {
                                    if (level == 0) {
                                        break;
                                    }
                                } else if (c == '<') {
                                    ++level;
                                } else if (c == '>') {
                                    if (level == 0) {
                                        break;
                                    }
                                    --level;
                                }
                            }
                            if (j != typeNucleus.getLength()) {
                                OUString argName(typeNucleus.copy(i, j - i));
                                unoidl::detail::SourceProviderEntity const *
                                    argEnt;
                                unoidl::detail::SourceProviderType argType;
                                switch (
                                    findEntity(
                                        location, yyscanner, data, false,
                                        &argName, &argEnt, nullptr, &argType))
                                {
                                case FOUND_ERROR:
                                    return FOUND_ERROR;
                                case FOUND_TYPE:
                                    break;
                                case FOUND_ENTITY:
                                    if (argEnt == nullptr) {
                                        error(
                                            location, yyscanner,
                                            (("inconsistent type manager: bad"
                                              " instantiated polymorphic struct"
                                              " type template type argument ")
                                             + argName));
                                        return FOUND_ERROR;
                                    } else {
                                        unoidl::detail::SourceProviderType::Type
                                            argT
                                            = unoidl::detail::SourceProviderType::Type();
                                            // avoid warnings
                                        switch (argEnt->kind) {
                                        case unoidl::detail::SourceProviderEntity::KIND_LOCAL:
                                            if (e->pad.is()) {
                                                error(
                                                    location, yyscanner,
                                                    (("inconsistent type"
                                                      " manager: bad"
                                                      " instantiated"
                                                      " polymorphic struct type"
                                                      " template type"
                                                      " argument ")
                                                     + argName));
                                                return FOUND_ERROR;
                                            }
                                            assert(e->entity.is());
                                            [[fallthrough]];
                                        case unoidl::detail::SourceProviderEntity::KIND_EXTERNAL:
                                            switch (e->entity->getSort()) {
                                            case unoidl::Entity::SORT_ENUM_TYPE:
                                                argT = unoidl::detail::SourceProviderType::TYPE_ENUM;
                                                break;
                                            case unoidl::Entity::SORT_PLAIN_STRUCT_TYPE:
                                                argT = unoidl::detail::SourceProviderType::TYPE_PLAIN_STRUCT;
                                                break;
                                            case unoidl::Entity::SORT_INTERFACE_TYPE:
                                                argT = unoidl::detail::SourceProviderType::TYPE_INTERFACE;
                                                break;
                                            default:
                                                error(
                                                    location, yyscanner,
                                                    (("inconsistent type"
                                                      "manager: bad"
                                                      " instantiated"
                                                      " polymorphic struct type"
                                                      " template type"
                                                      " argument ")
                                                     + argName));
                                                return FOUND_ERROR;
                                            }
                                            break;
                                        case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
                                        case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
                                            argT = unoidl::detail::SourceProviderType::TYPE_INTERFACE;
                                            break;
                                        case unoidl::detail::SourceProviderEntity::KIND_MODULE:
                                            assert(false && "this cannot happen");
                                        }
                                        argType
                                            = unoidl::detail::SourceProviderType(
                                                argT, argName, argEnt);
                                    }
                                    break;
                                }
                                args.push_back(argType);
                            }
                            i = j;
                        } while (i != typeNucleus.getLength()
                                 && typeNucleus[i] != '>');
                        if (i != typeNucleus.getLength() - 1
                            || typeNucleus[i] != '>')
                        {
                            error(
                                location, yyscanner,
                                ("inconsistent type manager: bad type name \""
                                 + t + "\""));
                            return FOUND_ERROR;
                        }
                        assert(!args.empty());
                        typeNucleus = tmpl;
                    }
                    if (typeNucleus.isEmpty()) {
                        error(
                            location, yyscanner,
                            ("inconsistent type manager: bad type name \"" + t
                             + "\""));
                        return FOUND_ERROR;
                    }
                    if (typeNucleus == "void") {
                        error(
                            location, yyscanner,
                            ("inconsistent type manager: bad type " + *name
                             + " based on void"));
                        return FOUND_ERROR;
                    }
                    if (typeNucleus == "boolean" || typeNucleus == "byte"
                        || typeNucleus == "short"
                        || typeNucleus == "unsigned short"
                        || typeNucleus == "long"
                        || typeNucleus == "unsigned long"
                        || typeNucleus == "hyper"
                        || typeNucleus == "unsigned hyper"
                        || typeNucleus == "float" || typeNucleus == "double"
                        || typeNucleus == "char" || typeNucleus == "string"
                        || typeNucleus == "type" || typeNucleus == "any")
                    {
                        if (!args.empty()) {
                            error(
                                location, yyscanner,
                                ("inconsistent type manager: bad type " + *name
                                 + (" based on instantiated polymorphic struct"
                                    " type based on ")
                                 + typeNucleus));
                            return FOUND_ERROR;
                        }
                        break;
                    }
                    n = "." + typeNucleus;
                    typeNucleus.clear();
                    e = findEntity_(data, &n);
                    continue;
                }
                break;
            case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
            case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
                if (resolveInterfaceDefinitions) {
                    rtl::Reference<unoidl::Entity> ent(
                        data->manager->findEntity(n));
                    // Do not allow ent to be of SORT_TYPEDEF:
                    if (!ent.is()
                        || (ent->getSort()
                            != unoidl::Entity::SORT_INTERFACE_TYPE))
                    {
                        error(
                            location, yyscanner,
                            (*name + " is based on interface declaration " + n
                             + " that is not an interface type entity"));
                        return FOUND_ERROR;
                    }
                    e->kind
                        = unoidl::detail::SourceProviderEntity::KIND_EXTERNAL;
                    e->entity = std::move(ent);
                }
                break;
            case unoidl::detail::SourceProviderEntity::KIND_MODULE:
                error(
                    location, yyscanner,
                    *name + " is based on module entity " + n);
                return FOUND_ERROR;
            }
        }
        if (!typeNucleus.isEmpty() || rank != 0 || !args.empty()) {
            if (typeNucleus.isEmpty() && e == nullptr) {
                // Found a type name based on an unknown entity:
                *entity = nullptr;
                return FOUND_ENTITY;
            }
            unoidl::detail::SourceProviderType t;
            if (args.empty()) {
                if (typeNucleus == "boolean") {
                    t = unoidl::detail::SourceProviderType(
                        unoidl::detail::SourceProviderType::TYPE_BOOLEAN);
                } else if (typeNucleus == "byte") {
                    t = unoidl::detail::SourceProviderType(
                        unoidl::detail::SourceProviderType::TYPE_BYTE);
                } else if (typeNucleus == "short") {
                    t = unoidl::detail::SourceProviderType(
                        unoidl::detail::SourceProviderType::TYPE_SHORT);
                } else if (typeNucleus == "unsigned short") {
                    t = unoidl::detail::SourceProviderType(
                        unoidl::detail::SourceProviderType::TYPE_UNSIGNED_SHORT);
                } else if (typeNucleus == "long") {
                    t = unoidl::detail::SourceProviderType(
                        unoidl::detail::SourceProviderType::TYPE_LONG);
                } else if (typeNucleus == "unsigned long") {
                    t = unoidl::detail::SourceProviderType(
                        unoidl::detail::SourceProviderType::TYPE_UNSIGNED_LONG);
                } else if (typeNucleus == "hyper") {
                    t = unoidl::detail::SourceProviderType(
                        unoidl::detail::SourceProviderType::TYPE_HYPER);
                } else if (typeNucleus == "unsigned hyper") {
                    t = unoidl::detail::SourceProviderType(
                        unoidl::detail::SourceProviderType::TYPE_UNSIGNED_HYPER);
                } else if (typeNucleus == "float") {
                    t = unoidl::detail::SourceProviderType(
                        unoidl::detail::SourceProviderType::TYPE_FLOAT);
                } else if (typeNucleus == "double") {
                    t = unoidl::detail::SourceProviderType(
                        unoidl::detail::SourceProviderType::TYPE_DOUBLE);
                } else if (typeNucleus == "char") {
                    t = unoidl::detail::SourceProviderType(
                        unoidl::detail::SourceProviderType::TYPE_CHAR);
                } else if (typeNucleus == "string") {
                    t = unoidl::detail::SourceProviderType(
                        unoidl::detail::SourceProviderType::TYPE_STRING);
                } else if (typeNucleus == "type") {
                    t = unoidl::detail::SourceProviderType(
                        unoidl::detail::SourceProviderType::TYPE_TYPE);
                } else if (typeNucleus == "any") {
                    t = unoidl::detail::SourceProviderType(
                        unoidl::detail::SourceProviderType::TYPE_ANY);
                } else {
                    assert(typeNucleus.isEmpty());
                    assert(e != nullptr);
                    switch (e->kind) {
                    case unoidl::detail::SourceProviderEntity::KIND_LOCAL:
                        if (e->pad.is()) {
                            if (dynamic_cast<unoidl::detail::SourceProviderEnumTypeEntityPad *>(
                                    e->pad.get())
                                != nullptr)
                            {
                                t = unoidl::detail::SourceProviderType(
                                    unoidl::detail::SourceProviderType::TYPE_ENUM,
                                    n, e);
                            } else if (dynamic_cast<unoidl::detail::SourceProviderPlainStructTypeEntityPad *>(
                                           e->pad.get())
                                       != nullptr)
                            {
                                t = unoidl::detail::SourceProviderType(
                                    unoidl::detail::SourceProviderType::TYPE_PLAIN_STRUCT,
                                    n, e);
                            } else if (dynamic_cast<unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *>(
                                           e->pad.get())
                                       != nullptr)
                            {
                                error(
                                    location, yyscanner,
                                    ("bad type " + *name
                                     + (" based on recursive reference to"
                                        " polymorphic struct type template ")
                                     + n));
                                return FOUND_ERROR;
                            } else if (dynamic_cast<unoidl::detail::SourceProviderExceptionTypeEntityPad *>(
                                           e->pad.get())
                                       != nullptr)
                            {
                                t = unoidl::detail::SourceProviderType(
                                    unoidl::detail::SourceProviderType::TYPE_EXCEPTION,
                                    n, e);
                            } else if (dynamic_cast<unoidl::detail::SourceProviderInterfaceTypeEntityPad *>(
                                           e->pad.get())
                                       != nullptr)
                            {
                                t = unoidl::detail::SourceProviderType(
                                    unoidl::detail::SourceProviderType::TYPE_INTERFACE,
                                    n, e);
                            } else {
                                error(
                                    location, yyscanner,
                                    ("bad type " + *name
                                     + " based on non-type entity " + n));
                                return FOUND_ERROR;
                            }
                            break;
                        }
                        assert(e->entity.is());
                        [[fallthrough]];
                    case unoidl::detail::SourceProviderEntity::KIND_EXTERNAL:
                        switch (e->entity->getSort()) {
                        case unoidl::Entity::SORT_ENUM_TYPE:
                            t = unoidl::detail::SourceProviderType(
                                unoidl::detail::SourceProviderType::TYPE_ENUM,
                                n, e);
                            break;
                        case unoidl::Entity::SORT_PLAIN_STRUCT_TYPE:
                            t = unoidl::detail::SourceProviderType(
                                unoidl::detail::SourceProviderType::TYPE_PLAIN_STRUCT,
                                n, e);
                            break;
                        case unoidl::Entity::SORT_POLYMORPHIC_STRUCT_TYPE_TEMPLATE:
                            error(
                                location, yyscanner,
                                ("bad type " + *name
                                 + " based on polymorphic struct type template "
                                 + n + " without type arguments"));
                            return FOUND_ERROR;
                        case unoidl::Entity::SORT_EXCEPTION_TYPE:
                            t = unoidl::detail::SourceProviderType(
                                unoidl::detail::SourceProviderType::TYPE_EXCEPTION,
                                n, e);
                            break;
                        case unoidl::Entity::SORT_INTERFACE_TYPE:
                            t = unoidl::detail::SourceProviderType(
                                unoidl::detail::SourceProviderType::TYPE_INTERFACE,
                                n, e);
                            break;
                        default:
                            error(
                                location, yyscanner,
                                ("bad type " + *name
                                 + " based on non-type entity " + n));
                            return FOUND_ERROR;
                        }
                        break;
                    case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
                    case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
                        t = unoidl::detail::SourceProviderType(
                            unoidl::detail::SourceProviderType::TYPE_INTERFACE,
                            n, e);
                        break;
                    case unoidl::detail::SourceProviderEntity::KIND_MODULE:
                        assert(false && "this cannot happen");
                    }
                }
            } else {
                assert(typeNucleus.isEmpty());
                assert(e != nullptr);
                switch (e->kind) {
                case unoidl::detail::SourceProviderEntity::KIND_LOCAL:
                    if (e->pad.is()) {
                        error(
                            location, yyscanner,
                            ("bad type " + *name
                             + (" based on instantiated polymorphic struct type"
                                " based on ")
                             + n
                             + (" that is either not a polymorphic struct type"
                                " template or a recursive reference to a"
                                " polymorphic struct type template")));
                        return FOUND_ERROR;
                    }
                    assert(e->entity.is());
                    [[fallthrough]];
                case unoidl::detail::SourceProviderEntity::KIND_EXTERNAL:
                    if (e->entity->getSort()
                        == unoidl::Entity::SORT_POLYMORPHIC_STRUCT_TYPE_TEMPLATE)
                    {
                        if (args.size()
                            != (static_cast<
                                    unoidl::PolymorphicStructTypeTemplateEntity *>(
                                        e->entity.get())
                                ->getTypeParameters().size()))
                        {
                            error(
                                location, yyscanner,
                                ("bad type " + *name
                                 + (" based on instantiated polymorphic struct"
                                    " type with ")
                                 + OUString::number(args.size())
                                 + (" type arguments based on polymorphic"
                                    " struct type template ")
                                 + n + " with "
                                 + OUString::number(
                                     static_cast<
                                         unoidl::PolymorphicStructTypeTemplateEntity *>(
                                             e->entity.get())
                                     ->getTypeParameters().size())
                                 + " type parameters"));
                            return FOUND_ERROR;
                        }
                        t = unoidl::detail::SourceProviderType(n, e, std::move(args));
                        break;
                    }
                    [[fallthrough]];
                case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
                case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
                    error(
                        location, yyscanner,
                        ("bad type " + *name
                         + (" based on instantiated polymorphic struct type"
                            " based on ")
                         + n
                         + " that is not a polymorphic struct type template"));
                    return FOUND_ERROR;
                case unoidl::detail::SourceProviderEntity::KIND_MODULE:
                    assert(false && "this cannot happen");
                }
            }
            if (typedefedType != nullptr) {
                for (std::size_t i = 0; i != rank; ++i) {
                    t = unoidl::detail::SourceProviderType(&t);
                }
                *typedefedType = std::move(t);
                typedefedType->typedefName = *name;
            }
            *entity = nullptr;
            return FOUND_TYPE;
        }
        *entity = e;
        return FOUND_ENTITY;
    }
}


bool checkTypeArgument(
    YYLTYPE location, yyscan_t yyscanner,
    unoidl::detail::SourceProviderType const & type)
{
    switch (type.type) {
    case unoidl::detail::SourceProviderType::TYPE_VOID:
    case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_SHORT:
    case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_LONG:
    case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_HYPER:
    case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
    case unoidl::detail::SourceProviderType::TYPE_PARAMETER: //TODO?
        error(
            location, yyscanner,
            u"bad instantiated polymorphic struct type argument"_ustr);
        return false;
    case unoidl::detail::SourceProviderType::TYPE_SEQUENCE:
        return checkTypeArgument(location, yyscanner, type.subtypes.front());
    default:
        return true;
    }
}

bool checkInstantiatedPolymorphicStructTypeArgument(
    unoidl::detail::SourceProviderType const & type, OUString const & name)
{
    if (type.type
        == unoidl::detail::SourceProviderType::TYPE_INSTANTIATED_POLYMORPHIC_STRUCT)
    {
        for (auto & i: type.subtypes) {
            if (checkInstantiatedPolymorphicStructTypeArgument(i, name)
                || i.getName() == name) // no need to worry about typedef
            {
                return true;
            }
        }
    }
    return false;
}

std::vector<OUString> annotations(bool deprecated) {
    std::vector<OUString> ann;
    if (deprecated) {
        ann.push_back(u"deprecated"_ustr);
    }
    return ann;
}

}

%}

%token TOK_ELLIPSIS
%token TOK_COLONS
%token TOK_LEFTSHIFT
%token TOK_RIGHTSHIFT

%token TOK_FALSE
%token TOK_TRUE
%token TOK_ANY
%token TOK_ATTRIBUTE
%token TOK_BOOLEAN
%token TOK_BOUND
%token TOK_BYTE
%token TOK_CHAR
%token TOK_CONST
%token TOK_CONSTANTS
%token TOK_CONSTRAINED
%token TOK_DOUBLE
%token TOK_ENUM
%token TOK_EXCEPTION
%token TOK_FLOAT
%token TOK_GET
%token TOK_HYPER
%token TOK_IN
%token TOK_INOUT
%token TOK_INTERFACE
%token TOK_LONG
%token TOK_MAYBEAMBIGUOUS
%token TOK_MAYBEDEFAULT
%token TOK_MAYBEVOID
%token TOK_MODULE
%token TOK_OPTIONAL
%token TOK_OUT
%token TOK_PROPERTY
%token TOK_PUBLISHED
%token TOK_RAISES
%token TOK_READONLY
%token TOK_REMOVABLE
%token TOK_SEQUENCE
%token TOK_SERVICE
%token TOK_SET
%token TOK_SHORT
%token TOK_SINGLETON
%token TOK_STRING
%token TOK_STRUCT
%token TOK_TRANSIENT
%token TOK_TYPE
%token TOK_TYPEDEF
%token TOK_UNSIGNED
%token TOK_VOID

%token<sval> TOK_IDENTIFIER
%token<ival> TOK_INTEGER
%token<fval> TOK_FLOATING

%token TOK_DEPRECATED

%token TOK_ERROR

%type<sval> identifier name singleInheritance singleInheritance_opt
%type<bval> ctors_opt deprecated_opt ellipsis_opt published_opt
%type<decls> attributeAccessDecl attributeAccessDecls
%type<dir> direction
%type<excns> exceptionSpec exceptionSpec_opt exceptions
%type<flags> flag flagSection flagSection_opt flags
%type<expr> addExpr andExpr expr multExpr orExpr primaryExpr shiftExpr unaryExpr
  xorExpr
%type<type> type
%type<types> typeArguments

%initial-action { yylloc = 1; }

%%

definitions:
  definitions definition
| /* empty */
;

definition:
  moduleDecl
| enumDefn
| plainStructDefn
| polymorphicStructTemplateDefn
| exceptionDefn
| interfaceDefn
| typedefDefn
| constantGroupDefn
| singleInterfaceBasedServiceDefn
| accumulationBasedServiceDefn
| interfaceBasedSingletonDefn
| serviceBasedSingletonDefn
| interfaceDecl
;

moduleDecl:
  TOK_MODULE identifier
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      OUString name(convertToFullName(data, $2));
      data->modules.push_back(name);
      std::pair<std::map<OUString, unoidl::detail::SourceProviderEntity>::iterator, bool> p(
          data->entities.emplace(
                  name,
                  unoidl::detail::SourceProviderEntity(
                      unoidl::detail::SourceProviderEntity::KIND_MODULE)));
      if (!p.second
          && (p.first->second.kind
              != unoidl::detail::SourceProviderEntity::KIND_MODULE))
      {
          error(@2, yyscanner, "multiple entities named " + name);
          YYERROR;
      }
  }
  '{' definitions '}' ';' { yyget_extra(yyscanner)->modules.pop_back(); }
;

enumDefn:
  deprecated_opt published_opt TOK_ENUM identifier
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      data->publishedContext = $2;
      convertToCurrentName(data, $4);
      if (!data->entities.emplace(
                  data->currentName,
                  unoidl::detail::SourceProviderEntity(
                      new unoidl::detail::SourceProviderEnumTypeEntityPad(
                          $2))).
          second)
      {
          error(@4, yyscanner, "multiple entities named " + data->currentName);
          YYERROR;
      }
  }
  '{' enumMembers '}' ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
      unoidl::detail::SourceProviderEnumTypeEntityPad * pad =
          dynamic_cast<unoidl::detail::SourceProviderEnumTypeEntityPad *>(
              ent->pad.get());
      assert(pad != nullptr);
      ent->entity = new unoidl::EnumTypeEntity(
          pad->isPublished(), std::move(pad->members), annotations($1));
      ent->pad.clear();
      clearCurrentState(data);
  }
;

enumMembers:
| enumMembers ',' enumMember
| enumMember
;

enumMember:
  deprecated_opt identifier
  {
      OUString id(convertName($2));
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      rtl::Reference<unoidl::detail::SourceProviderEnumTypeEntityPad> pad(
          getCurrentPad<unoidl::detail::SourceProviderEnumTypeEntityPad>(data));
      sal_Int32 v;
      if (pad->members.empty()) {
          v = 0;
      } else {
          v = pad->members.back().value;
          if (v == SAL_MAX_INT32) {
              error(
                  @2, yyscanner,
                  ("enum " + data->currentName + " member " + id
                   + " would have out-of-range value 2^31"));
              YYERROR;
          }
          ++v;
      }
      pad->members.emplace_back(id, v, annotations($1));
  }
| deprecated_opt identifier '=' expr
  {
      OUString id(convertName($2));
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      rtl::Reference<unoidl::detail::SourceProviderEnumTypeEntityPad> pad(
          getCurrentPad<unoidl::detail::SourceProviderEnumTypeEntityPad>(data));
      sal_Int32 v;
      switch ($4.type) {
      case unoidl::detail::SourceProviderExpr::TYPE_INT:
          if ($4.ival < SAL_MIN_INT32 || $4.ival > SAL_MAX_INT32) {
              error(
                  @4, yyscanner,
                  ("out-of-range enum " + data->currentName + " member " + id
                   + " value " + OUString::number($4.ival)));
              YYERROR;
          }
          v = static_cast<sal_Int32>($4.ival);
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_UINT:
          if ($4.uval > SAL_MAX_INT32) {
              error(
                  @4, yyscanner,
                  ("out-of-range enum " + data->currentName + " member " + id
                   + " value " + OUString::number($4.uval)));
              YYERROR;
          }
          v = static_cast<sal_Int32>($4.uval);
          break;
      default:
          error(
              @4, yyscanner,
              ("non-integer enum " + data->currentName + " member " + id
               + " value"));
          YYERROR;
          break;
      }
      pad->members.emplace_back(id, v, annotations($1));
  }
;

plainStructDefn:
  deprecated_opt published_opt TOK_STRUCT identifier singleInheritance_opt
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      data->publishedContext = $2;
      convertToCurrentName(data, $4);
      OUString baseName;
      rtl::Reference<unoidl::PlainStructTypeEntity> baseEnt;
      if ($5 != nullptr) {
          baseName = convertName($5);
          unoidl::detail::SourceProviderEntity const * p;
          if (findEntity(
                  @5, yyscanner, data, false, &baseName, &p, nullptr, nullptr)
              == FOUND_ERROR)
          {
              YYERROR;
          }
          if (p == nullptr || !p->entity.is()
              || p->entity->getSort() != unoidl::Entity::SORT_PLAIN_STRUCT_TYPE)
          {
              error(
                  @5, yyscanner,
                  ("plain struct type " + data->currentName + " base "
                   + baseName
                   + " does not resolve to an existing plain struct type"));
              YYERROR;
          }
          baseEnt = static_cast<unoidl::PlainStructTypeEntity *>(
              p->entity.get());
          if ($2 && !baseEnt->isPublished()) {
              error(
                  @5, yyscanner,
                  ("published plain struct type " + data->currentName + " base "
                   + baseName + " is unpublished"));
              YYERROR;
          }
      }
      if (!data->entities.emplace(
                  data->currentName,
                  unoidl::detail::SourceProviderEntity(
                      new unoidl::detail::SourceProviderPlainStructTypeEntityPad(
                          $2, baseName, baseEnt))).
          second)
      {
          error(@4, yyscanner, "multiple entities named " + data->currentName);
          YYERROR;
      }
  }
  '{' structMembers '}' ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
      unoidl::detail::SourceProviderPlainStructTypeEntityPad * pad =
          dynamic_cast<
              unoidl::detail::SourceProviderPlainStructTypeEntityPad *>(
                  ent->pad.get());
      assert(pad != nullptr);
      ent->entity = new unoidl::PlainStructTypeEntity(
          pad->isPublished(), pad->baseName, std::move(pad->members), annotations($1));
      ent->pad.clear();
      clearCurrentState(data);
  }
;

polymorphicStructTemplateDefn:
  deprecated_opt published_opt TOK_STRUCT identifier '<'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      data->publishedContext = $2;
      convertToCurrentName(data, $4);
      if (!data->entities.emplace(
                  data->currentName,
                  unoidl::detail::SourceProviderEntity(
                      new unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad(
                          $2))).
          second)
      {
          error(@4, yyscanner, "multiple entities named " + data->currentName);
          YYERROR;
      }
  }
  typeParameters '>' '{' structMembers '}' ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
      unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *
          pad = dynamic_cast<
              unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *>(
                  ent->pad.get());
      assert(pad != nullptr);
      ent->entity = new unoidl::PolymorphicStructTypeTemplateEntity(
          pad->isPublished(), std::move(pad->typeParameters), std::move(pad->members),
          annotations($1));
      ent->pad.clear();
      clearCurrentState(data);
  }
;

typeParameters:
  typeParameters ',' identifier
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      rtl::Reference<unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad>
          pad(getCurrentPad<unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad>(
                  data));
      OUString id(convertName($3));
      if (std::find(pad->typeParameters.begin(), pad->typeParameters.end(), id)
          != pad->typeParameters.end())
      {
          error(
              @3, yyscanner,
              ("polymorphic struct type template " + data->currentName
               + " type parameter " + id
               + " has same identifier as another type parameter"));
          YYERROR;
      }
      pad->typeParameters.push_back(id);
  }
| identifier
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      rtl::Reference<unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad>
          pad(getCurrentPad<unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad>(
                  data));
      OUString id(convertName($1));
      assert(pad->typeParameters.empty());
      pad->typeParameters.push_back(id);
  }
;

exceptionDefn:
  deprecated_opt published_opt TOK_EXCEPTION identifier singleInheritance_opt
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      data->publishedContext = $2;
      convertToCurrentName(data, $4);
      OUString baseName;
      rtl::Reference<unoidl::ExceptionTypeEntity> baseEnt;
      if ($5 != nullptr) {
          baseName = convertName($5);
          unoidl::detail::SourceProviderEntity const * p;
          if (findEntity(
                  @5, yyscanner, data, false, &baseName, &p, nullptr, nullptr)
              == FOUND_ERROR)
          {
              YYERROR;
          }
          if (p == nullptr || !p->entity.is()
              || p->entity->getSort() != unoidl::Entity::SORT_EXCEPTION_TYPE)
          {
              error(
                  @5, yyscanner,
                  ("exception type " + data->currentName + " base " + baseName
                   + " does not resolve to an existing exception type"));
              YYERROR;
          }
          baseEnt = static_cast<unoidl::ExceptionTypeEntity *>(
              p->entity.get());
          if ($2 && !baseEnt->isPublished()) {
              error(
                  @5, yyscanner,
                  ("published exception type " + data->currentName + " base "
                   + baseName + " is unpublished"));
              YYERROR;
          }
      }
      if (!data->entities.emplace(
                  data->currentName,
                  unoidl::detail::SourceProviderEntity(
                      new unoidl::detail::SourceProviderExceptionTypeEntityPad(
                          $2, baseName, baseEnt))).
          second)
      {
          error(@4, yyscanner, "multiple entities named " + data->currentName);
          YYERROR;
      }
  }
 '{' structMembers '}' ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
      unoidl::detail::SourceProviderExceptionTypeEntityPad * pad =
          dynamic_cast<unoidl::detail::SourceProviderExceptionTypeEntityPad *>(
              ent->pad.get());
      assert(pad != nullptr);
      ent->entity = new unoidl::ExceptionTypeEntity(
          pad->isPublished(), pad->baseName, std::move(pad->members), annotations($1));
      ent->pad.clear();
      clearCurrentState(data);
  }
;

structMembers:
  structMembers structMember
| /* empty */
;

structMember:
  deprecated_opt type identifier ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      unoidl::detail::SourceProviderType t(*$2);
      delete $2;
      OUString id(convertName($3));
      switch (t.type) {
      case unoidl::detail::SourceProviderType::TYPE_VOID:
      case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
          error(
              @2, yyscanner,
              ("illegal struct/exception type " + data->currentName
               + " direct member " + id + " type"));
          YYERROR;
          break;
      default:
          break;
      }
      if (t.type != unoidl::detail::SourceProviderType::TYPE_PARAMETER
          && t.getName() == data->currentName) // no need to worry about typedef
      {
          error(
              @2, yyscanner,
              ("struct/exception type " + data->currentName + " direct member "
               + id + " has same type as the type itself"));
          YYERROR;
      }
      if (checkInstantiatedPolymorphicStructTypeArgument(t, data->currentName))
      {
          error(
              @2, yyscanner,
              ("struct/exception type " + data->currentName + " direct member "
               + id
               + (" has instantiated polymorphic struct type that uses the type"
                  " itself as an argument")));
          YYERROR;
      }
      if (nameHasSameIdentifierAs(data->currentName, id)) {
          error(
              @3, yyscanner,
              ("struct/exception type " + data->currentName + " direct member "
               + id + " has same unqualified identifier as the type itself"));
          YYERROR;
      }
      unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
      unoidl::detail::SourceProviderPlainStructTypeEntityPad * p1 =
          dynamic_cast<unoidl::detail::SourceProviderPlainStructTypeEntityPad *>(
              ent->pad.get());
      if (p1 != nullptr) {
          for (const auto & i: p1->members) {
              if (id == i.name) {
                  error(
                      @3, yyscanner,
                      ("plain struct type " + data->currentName
                       + " direct member " + id
                       + " has same identifier as another direct member"));
                  YYERROR;
              }
          }
          if (p1->baseEntity.is()) {
              OUString baseName(p1->baseName);
              for (auto baseEnt(p1->baseEntity);;) {
                  if (nameHasSameIdentifierAs(baseName, id)) {
                      error(
                          @3, yyscanner,
                          ("plain struct type " + data->currentName
                           + " direct member " + id
                           + " has same unqalified identifier as base "
                           + baseName));
                      YYERROR;
                  }
                  for (auto & i: baseEnt->getDirectMembers()) {
                      if (id == i.name) {
                          error(
                              @3, yyscanner,
                              ("plain struct type " + data->currentName
                               + " direct member " + id
                               + " has same identifier as a member of base "
                               + baseName));
                          YYERROR;
                      }
                  }
                  baseName = baseEnt->getDirectBase();
                  if (baseName.isEmpty()) {
                      break;
                  }
                  unoidl::detail::SourceProviderEntity const * p;
                  if (findEntity(
                          @2, yyscanner, data, false, &baseName, &p, nullptr,
                          nullptr)
                      == FOUND_ERROR)
                  {
                      YYERROR;
                  }
                  if (p == nullptr || !p->entity.is()
                      || (p->entity->getSort()
                          != unoidl::Entity::SORT_PLAIN_STRUCT_TYPE))
                  {
                      error(
                          @2, yyscanner,
                          ("inconsistent type manager: plain struct type "
                           + data->currentName + " base " + baseName
                           + (" does not resolve to an existing plain struct"
                              " type")));
                      YYERROR;
                  }
                  baseEnt = static_cast<unoidl::PlainStructTypeEntity *>(
                      p->entity.get());
              }
          }
          p1->members.emplace_back(id, t.getName(), annotations($1));
      } else {
          unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *
              p2 = dynamic_cast<unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *>(
                  ent->pad.get());
          if (p2 != nullptr) {
              for (const auto & i: p2->members) {
                  if (id == i.name) {
                      error(
                          @3, yyscanner,
                          ("polymorphic struct type template "
                           + data->currentName + " direct member " + id
                           + " has same identifier as another direct member"));
                      YYERROR;
                  }
              }
              p2->members.emplace_back(
                  id, t.getName(),
                  t.type == unoidl::detail::SourceProviderType::TYPE_PARAMETER,
                  annotations($1));
          } else {
              unoidl::detail::SourceProviderExceptionTypeEntityPad * p3
                  = dynamic_cast<unoidl::detail::SourceProviderExceptionTypeEntityPad *>(
                      ent->pad.get());
              assert(p3 != nullptr);
              for (const auto & i: p3->members) {
                  if (id == i.name) {
                      error(
                          @3, yyscanner,
                          ("exception type " + data->currentName
                           + " direct member " + id
                           + " has same identifier as another direct member"));
                      YYERROR;
                  }
              }
              if (p3->baseEntity.is()) {
                  OUString baseName(p3->baseName);
                  for (auto baseEnt(p3->baseEntity);;) {
                      if (nameHasSameIdentifierAs(baseName, id)) {
                          error(
                              @3, yyscanner,
                              ("exception type " + data->currentName
                               + " direct member " + id
                               + " has same unqalified identifier as base "
                               + baseName));
                          YYERROR;
                      }
                      for (auto & i: baseEnt->getDirectMembers()) {
                          if (id == i.name) {
                              error(
                                  @3, yyscanner,
                                  ("exception type " + data->currentName
                                   + " direct member " + id
                                   + " has same identifier as a member of base "
                                   + baseName));
                              YYERROR;
                          }
                      }
                      baseName = baseEnt->getDirectBase();
                      if (baseName.isEmpty()) {
                          break;
                      }
                      unoidl::detail::SourceProviderEntity const * p;
                      if (findEntity(
                              @2, yyscanner, data, false, &baseName, &p,
                              nullptr, nullptr)
                          == FOUND_ERROR)
                      {
                          YYERROR;
                      }
                      if (p == nullptr || !p->entity.is()
                          || (p->entity->getSort()
                              != unoidl::Entity::SORT_EXCEPTION_TYPE))
                      {
                          error(
                              @2, yyscanner,
                              ("inconsistent type manager: exception type "
                               + data->currentName + " base " + baseName
                               + (" does not resolve to an existing exception"
                                  " type")));
                          YYERROR;
                      }
                      baseEnt = static_cast<unoidl::ExceptionTypeEntity *>(
                          p->entity.get());
                  }
              }
              p3->members.emplace_back(id, t.getName(), annotations($1));
          }
      }
  }
;

interfaceDefn:
  deprecated_opt published_opt TOK_INTERFACE identifier singleInheritance_opt
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      data->publishedContext = $2;
      convertToCurrentName(data, $4);
      OUString baseName;
      rtl::Reference<unoidl::InterfaceTypeEntity> baseEnt;
      if ($5 != nullptr) {
          baseName = convertName($5);
          unoidl::detail::SourceProviderEntity const * p;
          if (findEntity(
                  @5, yyscanner, data, true, &baseName, &p, nullptr, nullptr)
              == FOUND_ERROR)
          {
              YYERROR;
          }
          if (p == nullptr || !p->entity.is()
              || p->entity->getSort() != unoidl::Entity::SORT_INTERFACE_TYPE)
          {
              error(
                  @5, yyscanner,
                  ("interface type " + data->currentName + " direct base "
                   + baseName
                   + " does not resolve to an existing interface type"));
              YYERROR;
          }
          baseEnt = static_cast<unoidl::InterfaceTypeEntity *>(p->entity.get());
          if ($2 && !baseEnt->isPublished()) {
              error(
                  @5, yyscanner,
                  ("published interface type " + data->currentName
                   + " direct base " + baseName + " is unpublished"));
              YYERROR;
          }
      }
      std::map<OUString, unoidl::detail::SourceProviderEntity>::iterator i(
          data->entities.find(data->currentName));
      if (i != data->entities.end()) {
          switch (i->second.kind) {
          case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
              break;
          case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
              if (!$2) {
                  error(
                      @4, yyscanner,
                      ("unpublished interface type " + data->currentName
                       + " has been declared published"));
                  YYERROR;
              }
              break;
          default:
              error(
                  @4, yyscanner,
                  "multiple entities named " + data->currentName);
              YYERROR;
              break;
          }
      }
      rtl::Reference<unoidl::detail::SourceProviderInterfaceTypeEntityPad> pad(
          new unoidl::detail::SourceProviderInterfaceTypeEntityPad(
              $2, baseEnt.is()));
      if (baseEnt.is()
          && !pad->addDirectBase(
              @4, yyscanner, data,
              unoidl::detail::SourceProviderInterfaceTypeEntityPad::DirectBase(
                  baseName, baseEnt, std::vector<OUString>()),
              false))
      {
          YYERROR;
      }
      data->entities[data->currentName] = unoidl::detail::SourceProviderEntity(
          pad);
  }
  '{' interfaceMembers '}' ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
      unoidl::detail::SourceProviderInterfaceTypeEntityPad * pad =
          dynamic_cast<unoidl::detail::SourceProviderInterfaceTypeEntityPad *>(
              ent->pad.get());
      assert(pad != nullptr);
      if (pad->directMandatoryBases.empty()
          && data->currentName != "com.sun.star.uno.XInterface")
      {
          OUString base(u".com.sun.star.uno.XInterface"_ustr);
          unoidl::detail::SourceProviderEntity const * p;
          if (findEntity(@4, yyscanner, data, true, &base, &p, nullptr, nullptr)
              == FOUND_ERROR)
          {
              YYERROR;
          }
          if (p == nullptr || !p->entity.is()
              || p->entity->getSort() != unoidl::Entity::SORT_INTERFACE_TYPE)
          {
              error(
                  @3, yyscanner,
                  ("interface type " + data->currentName
                   + " implicit direct base " + base
                   + " does not resolve to an existing interface type"));
              YYERROR;
          }
          if (!pad->addDirectBase(
                  @3, yyscanner, data,
                  unoidl::detail::SourceProviderInterfaceTypeEntityPad::DirectBase(
                      base,
                      static_cast<unoidl::InterfaceTypeEntity *>(
                          p->entity.get()),
                      std::vector<OUString>()),
                  false))
          {
              YYERROR;
          }
      }
      std::vector<unoidl::AnnotatedReference> mbases;
      for (auto & i: pad->directMandatoryBases) {
          mbases.emplace_back(i.name, std::move(i.annotations));
      }
      std::vector<unoidl::AnnotatedReference> obases;
      for (auto & i: pad->directOptionalBases) {
          obases.emplace_back(i.name, std::move(i.annotations));
      }
      ent->entity = new unoidl::InterfaceTypeEntity(
          pad->isPublished(), std::move(mbases), std::move(obases), std::move(pad->directAttributes),
          std::move(pad->directMethods), annotations($1));
      ent->pad.clear();
      clearCurrentState(data);
  }
;

interfaceMembers:
 interfaceMembers interfaceMember
| /* empty */
;

interfaceMember:
  interfaceBase
| interfaceAttribute
| interfaceMethod
;

interfaceBase:
  deprecated_opt flagSection_opt TOK_INTERFACE name ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      OUString name(convertName($4));
      rtl::Reference<unoidl::detail::SourceProviderInterfaceTypeEntityPad> pad(
          getCurrentPad<unoidl::detail::SourceProviderInterfaceTypeEntityPad>(
              data));
      if (pad->singleBase) {
          error(
              @3, yyscanner,
              u"single-inheritance interface cannot have additional bases"_ustr);
          YYERROR;
      }
      if (($2 & ~unoidl::detail::FLAG_OPTIONAL) != 0) {
          error(
              @2, yyscanner,
              u"interface base can only be flagged as [optional]"_ustr);
          YYERROR;
      }
      bool opt = ($2 & unoidl::detail::FLAG_OPTIONAL) != 0;
      OUString orgName(name);
      unoidl::detail::SourceProviderEntity const * p;
      bool typedefed = false;
      if (findEntity(@4, yyscanner, data, true, &name, &p, &typedefed, nullptr)
          == FOUND_ERROR)
      {
          YYERROR;
      }
      if (p == nullptr || !p->entity.is()
          || p->entity->getSort() != unoidl::Entity::SORT_INTERFACE_TYPE)
      {
          error(
              @4, yyscanner,
              ("interface type " + data->currentName + " direct base " + name
               + " does not resolve to an existing interface type"));
          YYERROR;
      }
      if (typedefed) {
          error(
              @4, yyscanner,
              ("interface type " + data->currentName + " direct base " + orgName
               + " is a typedef"));
          YYERROR;
      }
      rtl::Reference<unoidl::InterfaceTypeEntity> ent(
          static_cast<unoidl::InterfaceTypeEntity *>(p->entity.get()));
      if (data->publishedContext && !ent->isPublished()) {
          error(
              @4, yyscanner,
              ("published interface type " + data->currentName + " direct base "
               + name + " is unpublished"));
          YYERROR;
      }
      if (!pad->addDirectBase(
              @4, yyscanner, data,
              unoidl::detail::SourceProviderInterfaceTypeEntityPad::DirectBase(
                  name, ent, annotations($1)),
              opt))
      {
          YYERROR;
      }
  }
;

interfaceAttribute:
  deprecated_opt flagSection type identifier
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      unoidl::detail::SourceProviderType t(*$3);
      delete $3;
      OUString id(convertName($4));
      if (($2 & unoidl::detail::FLAG_ATTRIBUTE) == 0) {
          error(
              @2, yyscanner,
              u"interface attribute must be flagged as [attribute]"_ustr);
          YYERROR;
      }
      if (($2
           & ~(unoidl::detail::FLAG_ATTRIBUTE | unoidl::detail::FLAG_BOUND
               | unoidl::detail::FLAG_READONLY))
          != 0)
      {
          error(
              @2, yyscanner,
              (u"interface attribute can only be flagged as [attribute,"
               " bound, readonly]"_ustr));
          YYERROR;
      }
      switch (t.type) {
      case unoidl::detail::SourceProviderType::TYPE_VOID:
      case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
          error(
              @3, yyscanner,
              ("illegal interface type " + data->currentName
               + " direct attribute " + id + " type"));
          YYERROR;
          break;
      default:
          break;
      }
      rtl::Reference<unoidl::detail::SourceProviderInterfaceTypeEntityPad> pad(
          getCurrentPad<unoidl::detail::SourceProviderInterfaceTypeEntityPad>(
              data));
      if (!pad->addDirectMember(@4, yyscanner, data, id)) {
          YYERROR;
      }
      pad->directAttributes.emplace_back(
          id, t.getName(), ($2 & unoidl::detail::FLAG_BOUND) != 0,
          ($2 & unoidl::detail::FLAG_READONLY) != 0,
          std::vector<OUString>(), std::vector<OUString>(), annotations($1));
  }
  attributeAccessDecls_opt ';'
;

attributeAccessDecls_opt:
  '{' attributeAccessDecls '}'
| /* empty */
;

attributeAccessDecls:
  attributeAccessDecls attributeAccessDecl
  {
      if (($1 & $2) != 0) {
          error(
              @2, yyscanner, u"duplicate get/set attribute access declaration"_ustr);
          YYERROR;
      }
      $$ = unoidl::detail::SourceProviderAccessDecls($1 | $2);
  }
| /* empty */ { $$ = unoidl::detail::SourceProviderAccessDecls(0); }
;

attributeAccessDecl:
  TOK_GET exceptionSpec ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      rtl::Reference<unoidl::detail::SourceProviderInterfaceTypeEntityPad>
          pad(getCurrentPad<unoidl::detail::SourceProviderInterfaceTypeEntityPad>(
              data));
      assert(!pad->directAttributes.empty());
      pad->directAttributes.back().getExceptions = *$2;
      delete $2;
      $$ = unoidl::detail::ACCESS_DECL_GET;
  }
| TOK_SET exceptionSpec ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      rtl::Reference<unoidl::detail::SourceProviderInterfaceTypeEntityPad>
          pad(getCurrentPad<unoidl::detail::SourceProviderInterfaceTypeEntityPad>(
              data));
      assert(!pad->directAttributes.empty());
      pad->directAttributes.back().setExceptions = *$2;
      delete $2;
      if (pad->directAttributes.back().readOnly) {
          error(
              @1, yyscanner,
              ("interface type " + data->currentName
               + " direct read-only attribute "
               + pad->directAttributes.back().name
               + " cannot have set access declaration"));
          YYERROR;
      }
      $$ = unoidl::detail::ACCESS_DECL_SET;
  }
;

interfaceMethod:
  deprecated_opt type identifier
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      unoidl::detail::SourceProviderType t(*$2);
      delete $2;
      OUString id(convertName($3));
      if (t.type == unoidl::detail::SourceProviderType::TYPE_EXCEPTION) {
          error(
              @3, yyscanner,
              ("illegal interface type " + data->currentName
               + " direct method " + id + " return type"));
          YYERROR;
      }
      rtl::Reference<unoidl::detail::SourceProviderInterfaceTypeEntityPad> pad(
          getCurrentPad<unoidl::detail::SourceProviderInterfaceTypeEntityPad>(
              data));
      if (!pad->addDirectMember(@3, yyscanner, data, id)) {
          YYERROR;
      }
      pad->directMethods.emplace_back(
          id, t.getName(),
          std::vector<unoidl::InterfaceTypeEntity::Method::Parameter>(),
          std::vector<OUString>(), annotations($1));
  }
  '(' methodParams_opt ')' exceptionSpec_opt ';'
  {
      if ($8 != nullptr) {
          unoidl::detail::SourceProviderScannerData * data
              = yyget_extra(yyscanner);
          rtl::Reference<unoidl::detail::SourceProviderInterfaceTypeEntityPad>
              pad(getCurrentPad<unoidl::detail::SourceProviderInterfaceTypeEntityPad>(
                  data));
          assert(!pad->directMethods.empty());
          pad->directMethods.back().exceptions = *$8;
          delete $8;
      }
  }
;

methodParams_opt:
  methodParams
| /* empty */
;

methodParams:
  methodParams ',' methodParam
| methodParam
;

methodParam:
  '[' direction ']' type identifier
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      unoidl::detail::SourceProviderType t(*$4);
      delete $4;
      OUString id(convertName($5));
      rtl::Reference<unoidl::detail::SourceProviderInterfaceTypeEntityPad>
          pad(getCurrentPad<unoidl::detail::SourceProviderInterfaceTypeEntityPad>(
              data));
      assert(!pad->directMethods.empty());
      switch (t.type) {
      case unoidl::detail::SourceProviderType::TYPE_VOID:
      case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
          error(
              @4, yyscanner,
              ("illegal interface type " + data->currentName
               + " direct method " + pad->directMethods.back().name
               + " parameter " + id + " type"));
          YYERROR;
          break;
      default:
          break;
      }
      for (const auto & i: pad->directMethods.back().parameters) {
          if (id == i.name) {
              error(
                  @5, yyscanner,
                  ("interface type " + data->currentName + " direct method "
                   + pad->directMethods.back().name + " parameter " + id
                   + " has same identifier as another parameter"));
              YYERROR;
          }
      }
      pad->directMethods.back().parameters.emplace_back(id, t.getName(), $2);
  }
;

direction:
  TOK_IN { $$ = unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN; }
| TOK_OUT
  { $$ = unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_OUT; }
| TOK_INOUT
  { $$ = unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN_OUT; }
;

typedefDefn:
  deprecated_opt published_opt TOK_TYPEDEF type identifier ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      data->publishedContext = $2;
      unoidl::detail::SourceProviderType t(*$4);
      delete $4;
      OUString name(convertToFullName(data, $5));
      // There is no good reason to forbid typedefs to VOID, to instantiated
      // polymorphic struct types, and to exception types, but some old client
      // code of registry data expects this typedef restriction (like the
      // assert(false) default in handleTypedef in
      // codemaker/source/javamaker/javatype.cxx), so forbid them for now:
      switch (t.type) {
      case unoidl::detail::SourceProviderType::TYPE_VOID:
      case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
      case unoidl::detail::SourceProviderType::TYPE_INSTANTIATED_POLYMORPHIC_STRUCT:
          error(@4, yyscanner, u"bad typedef type"_ustr);
          YYERROR;
          break;
      case unoidl::detail::SourceProviderType::TYPE_ENUM:
      case unoidl::detail::SourceProviderType::TYPE_PLAIN_STRUCT:
      case unoidl::detail::SourceProviderType::TYPE_INTERFACE:
          if ($2) {
              bool unpub = false;
              switch (t.entity->kind) {
              case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
                  unpub = true;
                  break;
              case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
                  break;
              case unoidl::detail::SourceProviderEntity::KIND_MODULE:
                  O3TL_UNREACHABLE;
              default:
                  assert(t.entity->entity.is() || t.entity->pad.is());
                  unpub
                      = !(t.entity->entity.is()
                          ? static_cast<unoidl::PublishableEntity *>(
                              t.entity->entity.get())->isPublished()
                          : t.entity->pad->isPublished());
                  break;
              }
              if (unpub) {
                  error(
                      @4, yyscanner,
                      "published typedef " + name + " type is unpublished");
                  YYERROR;
              }
          }
          break;
      case unoidl::detail::SourceProviderType::TYPE_PARAMETER:
          O3TL_UNREACHABLE;
      default:
          break;
      }
      if (!data->entities.emplace(
                  name,
                  unoidl::detail::SourceProviderEntity(
                      unoidl::detail::SourceProviderEntity::KIND_LOCAL,
                      new unoidl::TypedefEntity(
                          $2, t.getName(), annotations($1)))).
          second)
      {
          error(@5, yyscanner, "multiple entities named " + name);
          YYERROR;
      }
      clearCurrentState(data);
  }
;

constantGroupDefn:
  deprecated_opt published_opt TOK_CONSTANTS identifier
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      data->publishedContext = $2;
      convertToCurrentName(data, $4);
      if (!data->entities.emplace(
                  data->currentName,
                  unoidl::detail::SourceProviderEntity(
                      new unoidl::detail::SourceProviderConstantGroupEntityPad(
                          $2))).
          second)
      {
          error(@4, yyscanner, "multiple entities named " + data->currentName);
          YYERROR;
      }
  }
  '{' constants '}' ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
      unoidl::detail::SourceProviderConstantGroupEntityPad * pad =
          dynamic_cast<unoidl::detail::SourceProviderConstantGroupEntityPad *>(
              ent->pad.get());
      assert(pad != nullptr);
      ent->entity = new unoidl::ConstantGroupEntity(
          pad->isPublished(), std::move(pad->members), annotations($1));
      ent->pad.clear();
      clearCurrentState(data);
  }
;

constants:
  constants constant
| /* empty */
;

constant:
  deprecated_opt TOK_CONST type identifier '=' expr ';'
  {
      OUString id(convertName($4));
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      rtl::Reference<unoidl::detail::SourceProviderConstantGroupEntityPad> pad(
          getCurrentPad<unoidl::detail::SourceProviderConstantGroupEntityPad>(
              data));
      unoidl::detail::SourceProviderType t(*$3);
      delete $3;
      unoidl::ConstantValue v(false); // dummy value
      switch (t.type) {
      case unoidl::detail::SourceProviderType::TYPE_BOOLEAN:
          if ($6.type != unoidl::detail::SourceProviderExpr::TYPE_BOOL) {
              error(
                  @6, yyscanner,
                  ("bad value of boolean-typed constant " + data->currentName
                   + "." + id));
              YYERROR;
          }
          v = unoidl::ConstantValue($6.bval);
          break;
      case unoidl::detail::SourceProviderType::TYPE_BYTE:
          switch ($6.type) {
          case unoidl::detail::SourceProviderExpr::TYPE_INT:
              if ($6.ival < SAL_MIN_INT8 || $6.ival > SAL_MAX_INT8) {
                  error(
                      @6, yyscanner,
                      ("out-of-range byte-typed constant " + data->currentName
                       + "." + id + " value " + OUString::number($6.ival)));
                  YYERROR;
              }
              v = unoidl::ConstantValue(static_cast<sal_Int8>($6.ival));
              break;
          case unoidl::detail::SourceProviderExpr::TYPE_UINT:
              if ($6.uval > SAL_MAX_INT8) {
                  error(
                      @6, yyscanner,
                      ("out-of-range byte-typed constant " + data->currentName
                       + "." + id + " value " + OUString::number($6.uval)));
                  YYERROR;
              }
              v = unoidl::ConstantValue(static_cast<sal_Int8>($6.uval));
              break;
          default:
              error(
                  @6, yyscanner,
                  ("bad value of byte-typed constant " + data->currentName + "."
                   + id));
              YYERROR;
              break;
          }
          break;
      case unoidl::detail::SourceProviderType::TYPE_SHORT:
          switch ($6.type) {
          case unoidl::detail::SourceProviderExpr::TYPE_INT:
              if ($6.ival < SAL_MIN_INT16 || $6.ival > SAL_MAX_INT16) {
                  error(
                      @6, yyscanner,
                      ("out-of-range short-typed constant " + data->currentName
                       + "." + id + " value " + OUString::number($6.ival)));
                  YYERROR;
              }
              v = unoidl::ConstantValue(static_cast<sal_Int16>($6.ival));
              break;
          case unoidl::detail::SourceProviderExpr::TYPE_UINT:
              if ($6.uval > SAL_MAX_INT16) {
                  error(
                      @6, yyscanner,
                      ("out-of-range short-typed constant " + data->currentName
                       + "." + id + " value " + OUString::number($6.uval)));
                  YYERROR;
              }
              v = unoidl::ConstantValue(static_cast<sal_Int16>($6.uval));
              break;
          default:
              error(
                  @6, yyscanner,
                  ("bad value of short-typed constant " + data->currentName
                   + "." + id));
              YYERROR;
              break;
          }
          break;
      case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_SHORT:
          switch ($6.type) {
          case unoidl::detail::SourceProviderExpr::TYPE_INT:
              if ($6.ival < 0 || $6.ival > SAL_MAX_UINT16) {
                  error(
                      @6, yyscanner,
                      ("out-of-range unsigned-short-typed constant "
                       + data->currentName + "." + id + " value "
                       + OUString::number($6.ival)));
                  YYERROR;
              }
              v = unoidl::ConstantValue(static_cast<sal_uInt16>($6.ival));
              break;
          case unoidl::detail::SourceProviderExpr::TYPE_UINT:
              if ($6.uval > SAL_MAX_UINT16) {
                  error(
                      @6, yyscanner,
                      ("out-of-range unsigned-short-typed constant "
                       + data->currentName + "." + id + " value "
                       + OUString::number($6.uval)));
                  YYERROR;
              }
              v = unoidl::ConstantValue(static_cast<sal_uInt16>($6.uval));
              break;
          default:
              error(
                  @6, yyscanner,
                  ("bad value of unsigned-short-typed constant "
                   + data->currentName + "." + id));
              YYERROR;
              break;
          }
          break;
      case unoidl::detail::SourceProviderType::TYPE_LONG:
          switch ($6.type) {
          case unoidl::detail::SourceProviderExpr::TYPE_INT:
              if ($6.ival < SAL_MIN_INT32 || $6.ival > SAL_MAX_INT32) {
                  error(
                      @6, yyscanner,
                      ("out-of-range long-typed constant " + data->currentName
                       + "." + id + " value " + OUString::number($6.ival)));
                  YYERROR;
              }
              v = unoidl::ConstantValue(static_cast<sal_Int32>($6.ival));
              break;
          case unoidl::detail::SourceProviderExpr::TYPE_UINT:
              if ($6.uval > SAL_MAX_INT32) {
                  error(
                      @6, yyscanner,
                      ("out-of-range long-typed constant " + data->currentName
                       + "." + id + " value " + OUString::number($6.uval)));
                  YYERROR;
              }
              v = unoidl::ConstantValue(static_cast<sal_Int32>($6.uval));
              break;
          default:
              error(
                  @6, yyscanner,
                  ("bad value of long-typed constant " + data->currentName
                   + "." + id));
              YYERROR;
              break;
          }
          break;
      case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_LONG:
          switch ($6.type) {
          case unoidl::detail::SourceProviderExpr::TYPE_INT:
              if ($6.ival < 0 || $6.ival > SAL_MAX_UINT32) {
                  error(
                      @6, yyscanner,
                      ("out-of-range unsigned-long-typed constant "
                       + data->currentName + "." + id + " value "
                       + OUString::number($6.ival)));
                  YYERROR;
              }
              v = unoidl::ConstantValue(static_cast<sal_uInt32>($6.ival));
              break;
          case unoidl::detail::SourceProviderExpr::TYPE_UINT:
              if ($6.uval > SAL_MAX_UINT32) {
                  error(
                      @6, yyscanner,
                      ("out-of-range unsigned-long-typed constant "
                       + data->currentName + "." + id + " value "
                       + OUString::number($6.uval)));
                  YYERROR;
              }
              v = unoidl::ConstantValue(static_cast<sal_uInt32>($6.uval));
              break;
          default:
              error(
                  @6, yyscanner,
                  ("bad value of unsigned-long-typed constant "
                   + data->currentName + "." + id));
              YYERROR;
              break;
          }
          break;
      case unoidl::detail::SourceProviderType::TYPE_HYPER:
          switch ($6.type) {
          case unoidl::detail::SourceProviderExpr::TYPE_INT:
              v = unoidl::ConstantValue($6.ival);
              break;
          case unoidl::detail::SourceProviderExpr::TYPE_UINT:
              if ($6.uval > SAL_MAX_INT64) {
                  error(
                      @6, yyscanner,
                      ("out-of-range hyper-typed constant " + data->currentName
                       + "." + id + " value " + OUString::number($6.uval)));
                  YYERROR;
              }
              v = unoidl::ConstantValue(static_cast<sal_Int64>($6.uval));
              break;
          default:
              error(
                  @6, yyscanner,
                  ("bad value of hyper-typed constant " + data->currentName
                   + "." + id));
              YYERROR;
              break;
          }
          break;
      case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_HYPER:
          switch ($6.type) {
          case unoidl::detail::SourceProviderExpr::TYPE_INT:
              if ($6.ival < 0) {
                  error(
                      @6, yyscanner,
                      ("out-of-range unsigned-hyper-typed constant "
                       + data->currentName + "." + id + " value "
                       + OUString::number($6.ival)));
                  YYERROR;
              }
              v = unoidl::ConstantValue(static_cast<sal_uInt64>($6.ival));
              break;
          case unoidl::detail::SourceProviderExpr::TYPE_UINT:
              v = unoidl::ConstantValue($6.uval);
              break;
          default:
              error(
                  @6, yyscanner,
                  ("bad value of unsigned-hyper-typed constant "
                   + data->currentName + "." + id));
              YYERROR;
              break;
          }
          break;
      case unoidl::detail::SourceProviderType::TYPE_FLOAT:
          switch ($6.type) {
          case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
              error(
                  @6, yyscanner,
                  ("bad boolean value of float-typed constant "
                   + data->currentName + "." + id));
              YYERROR;
              break;
          case unoidl::detail::SourceProviderExpr::TYPE_INT:
              v = unoidl::ConstantValue(static_cast<float>($6.ival));
              break;
          case unoidl::detail::SourceProviderExpr::TYPE_UINT:
              v = unoidl::ConstantValue(static_cast<float>($6.uval));
              break;
          case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
              v = unoidl::ConstantValue(static_cast<float>($6.fval));
              break;
          }
          break;
      case unoidl::detail::SourceProviderType::TYPE_DOUBLE:
          switch ($6.type) {
          case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
              error(
                  @6, yyscanner,
                  ("bad boolean value of double-typed constant "
                   + data->currentName + "." + id));
              YYERROR;
              break;
          case unoidl::detail::SourceProviderExpr::TYPE_INT:
              v = unoidl::ConstantValue(static_cast<double>($6.ival));
              break;
          case unoidl::detail::SourceProviderExpr::TYPE_UINT:
              v = unoidl::ConstantValue(static_cast<double>($6.uval));
              break;
          case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
              v = unoidl::ConstantValue($6.fval);
              break;
          }
          break;
      default:
          error(
              @3, yyscanner,
              "bad type for constant " + data->currentName + "." + id);
          YYERROR;
          break;
      }
      pad->members.emplace_back(id, v, annotations($1));
  }
;

singleInterfaceBasedServiceDefn:
  deprecated_opt published_opt TOK_SERVICE identifier singleInheritance
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      data->publishedContext = $2;
      convertToCurrentName(data, $4);
      OUString base(convertName($5));
      unoidl::detail::SourceProviderEntity const * p;
      if (findEntity(@5, yyscanner, data, false, &base, &p, nullptr, nullptr)
          == FOUND_ERROR)
      {
          YYERROR;
      }
      bool ifcBase = false;
      bool pubBase = false;
      if (p != nullptr) {
          switch (p->kind) {
          case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
              ifcBase = true;
              pubBase = false;
              break;
          case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
              ifcBase = true;
              pubBase = true;
              break;
          default:
              if (p->entity.is()
                  && (p->entity->getSort()
                      == unoidl::Entity::SORT_INTERFACE_TYPE))
              {
                  ifcBase = true;
                  pubBase = static_cast<unoidl::InterfaceTypeEntity *>(
                      p->entity.get())->isPublished();
              }
              break;
          }
      }
      if (!ifcBase) {
          error(
              @5, yyscanner,
              ("single-interface--based service " + data->currentName + " base "
               + base + " does not resolve to an interface type"));
          YYERROR;
      }
      if ($2 && !pubBase) {
          error(
              @5, yyscanner,
              ("published single-interface--based service " + data->currentName
               + " base " + base + " is unpublished"));
          YYERROR;
      }
      if (!data->entities.emplace(
                  data->currentName,
                  unoidl::detail::SourceProviderEntity(
                      new unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad(
                          $2, base))).
          second)
      {
          error(@4, yyscanner, "multiple entities named " + data->currentName);
          YYERROR;
      }
  }
  ctors_opt ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
      unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad * pad =
          dynamic_cast<unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad *>(
              ent->pad.get());
      assert(pad != nullptr);
      std::vector<unoidl::SingleInterfaceBasedServiceEntity::Constructor> ctors;
      if ($7) {
          for (const auto & i: pad->constructors) {
              std::vector<unoidl::SingleInterfaceBasedServiceEntity::Constructor::Parameter> parms;
              for (auto & j: i.parameters) {
                  parms.emplace_back(j.name, j.type.getName(), j.rest);
              }
              ctors.push_back(
                  unoidl::SingleInterfaceBasedServiceEntity::Constructor(
                      i.name, std::move(parms), std::vector(i.exceptions), std::vector(i.annotations)));
          }
      } else {
          assert(pad->constructors.empty());
          ctors.push_back(
              unoidl::SingleInterfaceBasedServiceEntity::Constructor());
      }
      ent->entity = new unoidl::SingleInterfaceBasedServiceEntity(
          pad->isPublished(), pad->base, std::move(ctors), annotations($1));
      ent->pad.clear();
      clearCurrentState(data);
  }
;

ctors_opt:
  '{' ctors '}' { $$ = true; }
| /* empty */ { $$ = false; }
;

ctors:
  ctors ctor
| /* empty */
;

ctor:
  deprecated_opt identifier
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      OUString id(convertName($2));
      rtl::Reference<unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad>
          pad(getCurrentPad<unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad>(
                  data));
      for (const auto & i: pad->constructors) {
          if (id == i.name) {
              error(
                  @2, yyscanner,
                  ("single-interface--based service " + data->currentName
                   + " constructor " + id
                   + " has same identifier as another constructor"));
              YYERROR;
          }
      }
      pad->constructors.push_back(
          unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad::Constructor(
              id, annotations($1)));
  }
  '(' ctorParams_opt ')' exceptionSpec_opt ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      rtl::Reference<unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad>
          pad(getCurrentPad<unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad>(
                  data));
      assert(!pad->constructors.empty());
      if ($7 != nullptr) {
          pad->constructors.back().exceptions = *$7;
          delete $7;
      }
      for (auto i(pad->constructors.begin()); i != pad->constructors.end() - 1;
           ++i)
      {
          if (i->parameters.size()
              == pad->constructors.back().parameters.size())
          {
              bool same = true;
              for (auto
                       j(i->parameters.begin()),
                       k(pad->constructors.back().parameters.begin());
                   j != i->parameters.end(); ++j, ++k)
              {
                  if (!j->type.equals(k->type) || j->rest != k->rest) {
                      same = false;
                      break;
                  }
              }
              if (same) {
                  error(
                      @2, yyscanner,
                      ("single-interface--based service " + data->currentName
                       + " constructor " + pad->constructors.back().name
                       + " has similar parameter list to constructor "
                       + i->name));
                  YYERROR;
              }
          }
      }
  }
;

ctorParams_opt:
  ctorParams
| /* empty */
;

ctorParams:
  ctorParams ',' ctorParam
| ctorParam
;

ctorParam:
  '[' direction ']' type ellipsis_opt identifier
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      unoidl::detail::SourceProviderType t(*$4);
      delete $4;
      OUString id(convertName($6));
      rtl::Reference<unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad>
          pad(getCurrentPad<unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad>(
              data));
      assert(!pad->constructors.empty());
      if ($2 != unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN) {
          error(
              @4, yyscanner,
              ("single-interface--based service " + data->currentName
               + " constructor " + pad->constructors.back().name + " parameter "
               + id + " direction must be [in]"));
          YYERROR;
      }
      switch (t.type) {
      case unoidl::detail::SourceProviderType::TYPE_VOID:
      case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
          error(
              @4, yyscanner,
              ("illegal single-interface--based service " + data->currentName
               + " constructor " + pad->constructors.back().name + " parameter "
               + id + " type"));
          YYERROR;
          break;
      default:
          break;
      }
      if ($5) {
          if (t.type != unoidl::detail::SourceProviderType::TYPE_ANY) {
              error(
                  @4, yyscanner,
                  ("illegal single-interface--based service "
                   + data->currentName + " constructor "
                   + pad->constructors.back().name + " rest parameter " + id
                   + " non-any type"));
              YYERROR;
          }
          if (!pad->constructors.back().parameters.empty()) {
              error(
                  @5, yyscanner,
                  ("single-interface--based service " + data->currentName
                   + " constructor " + pad->constructors.back().name
                   + " rest parameter " + id + " must be first parameter"));
              YYERROR;
          }
      } else if (!pad->constructors.back().parameters.empty()
                 && pad->constructors.back().parameters.back().rest)
      {
          error(
              @1, yyscanner,
              ("single-interface--based service " + data->currentName
               + " constructor " + pad->constructors.back().name
               + " rest parameter must be last parameter"));
          YYERROR;
      }
      for (const auto & i: pad->constructors.back().parameters) {
          if (id == i.name) {
              error(
                  @6, yyscanner,
                  ("single-interface--based service " + data->currentName
                   + " constructor " + pad->constructors.back().name
                   + " parameter " + id
                   + " has same identifier as another parameter"));
              YYERROR;
          }
      }
      pad->constructors.back().parameters.push_back(
          unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad::Constructor::Parameter(
              id, std::move(t), $5));
  }
;

ellipsis_opt:
  TOK_ELLIPSIS { $$ = true; }
| /* empty */ { $$ = false; }

accumulationBasedServiceDefn:
  deprecated_opt published_opt TOK_SERVICE identifier
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      data->publishedContext = $2;
      convertToCurrentName(data, $4);
      if (!data->entities.emplace(
                  data->currentName,
                  unoidl::detail::SourceProviderEntity(
                      new unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad(
                          $2))).
          second)
      {
          error(@4, yyscanner, "multiple entities named " + data->currentName);
          YYERROR;
      }
  }
  '{' serviceMembers '}' ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
      unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad * pad =
          dynamic_cast<unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad *>(
              ent->pad.get());
      assert(pad != nullptr);
      ent->entity = new unoidl::AccumulationBasedServiceEntity(
          pad->isPublished(), std::move(pad->directMandatoryBaseServices),
          std::move(pad->directOptionalBaseServices), std::move(pad->directMandatoryBaseInterfaces),
          std::move(pad->directOptionalBaseInterfaces), std::move(pad->directProperties),
          annotations($1));
      ent->pad.clear();
      clearCurrentState(data);
  }
;

serviceMembers:
  serviceMembers serviceMember
| /* empty */
;

serviceMember:
  serviceBase
| serviceInterfaceBase
| serviceProperty
;

serviceBase:
  deprecated_opt flagSection_opt TOK_SERVICE name ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      OUString name(convertName($4));
      rtl::Reference<unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad> pad(
          getCurrentPad<unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad>(
              data));
      if (($2 & ~unoidl::detail::FLAG_OPTIONAL) != 0) {
          error(
              @2, yyscanner,
              u"service base can only be flagged as [optional]"_ustr);
          YYERROR;
      }
      bool opt = ($2 & unoidl::detail::FLAG_OPTIONAL) != 0;
      unoidl::detail::SourceProviderEntity const * p;
      if (findEntity(@4, yyscanner, data, false, &name, &p, nullptr, nullptr)
          == FOUND_ERROR)
      {
          YYERROR;
      }
      if (p == nullptr || !p->entity.is()
          || (p->entity->getSort()
              != unoidl::Entity::SORT_ACCUMULATION_BASED_SERVICE))
      {
          error(
              @4, yyscanner,
              ("accumulation-based service " + data->currentName
               + " direct base service " + name
               + " does not resolve to an accumulation-based service"));
          YYERROR;
      }
      if (data->publishedContext
          && !static_cast<unoidl::AccumulationBasedServiceEntity *>(
              p->entity.get())->isPublished())
      {
          error(
              @4, yyscanner,
              ("published accumulation-based service " + data->currentName
               + " direct base service " + name + " is unpublished"));
          YYERROR;
      }
      std::vector<unoidl::AnnotatedReference> & v(
          opt
          ? pad->directOptionalBaseServices : pad->directMandatoryBaseServices);
      for (const auto & i: v) {
          if (name == i.name) {
              error(
                  @4, yyscanner,
                  ("accumulation-based service " + data->currentName
                   + " duplicate direct base service " + name));
              YYERROR;
          }
      }
      v.emplace_back(name, annotations($1));
  }
;

serviceInterfaceBase:
  deprecated_opt flagSection_opt TOK_INTERFACE name ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      OUString name(convertName($4));
      rtl::Reference<unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad> pad(
          getCurrentPad<unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad>(
              data));
      if (($2 & ~unoidl::detail::FLAG_OPTIONAL) != 0) {
          error(
              @2, yyscanner,
              u"interface base can only be flagged as [optional]"_ustr);
          YYERROR;
      }
      bool opt = ($2 & unoidl::detail::FLAG_OPTIONAL) != 0;
      unoidl::detail::SourceProviderEntity const * p;
      if (findEntity(@4, yyscanner, data, false, &name, &p, nullptr, nullptr)
          == FOUND_ERROR)
      {
          YYERROR;
      }
      bool ifcBase = false;
      bool pubBase = false;
      if (p != nullptr) {
          switch (p->kind) {
          case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
              ifcBase = true;
              pubBase = false;
              break;
          case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
              ifcBase = true;
              pubBase = true;
              break;
          default:
              if (p->entity.is()
                  && (p->entity->getSort()
                      == unoidl::Entity::SORT_INTERFACE_TYPE))
              {
                  ifcBase = true;
                  pubBase = static_cast<unoidl::InterfaceTypeEntity *>(
                      p->entity.get())->isPublished();
              }
              break;
          }
      }
      if (!ifcBase) {
          error(
              @4, yyscanner,
              ("accumulation-based service " + data->currentName
               + " direct base interface " + name
               + " does not resolve to an interface type"));
          YYERROR;
      }
      if (data->publishedContext && !opt && !pubBase) {
          error(
              @4, yyscanner,
              ("published accumulation-based service " + data->currentName
               + " direct base interface " + name + " is unpublished"));
          YYERROR;
      }
      std::vector<unoidl::AnnotatedReference> & v(
          opt
          ? pad->directOptionalBaseInterfaces
          : pad->directMandatoryBaseInterfaces);
      for (const auto & i: v) {
          if (name == i.name) {
              error(
                  @4, yyscanner,
                  ("accumulation-based service " + data->currentName
                   + " duplicate direct base interface " + name));
              YYERROR;
          }
      }
      v.emplace_back(name, annotations($1));
  }
;

serviceProperty:
  deprecated_opt flagSection type identifier ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      unoidl::detail::SourceProviderType t(*$3);
      delete $3;
      OUString id(convertName($4));
      if (($2 & unoidl::detail::FLAG_PROPERTY) == 0) {
          error(
              @2, yyscanner,
              (u"accumulation-based service property must be flagged as"
               " [property]"_ustr));
          YYERROR;
      }
      if (($2
           & ~(unoidl::detail::FLAG_BOUND | unoidl::detail::FLAG_CONSTRAINED
               | unoidl::detail::FLAG_MAYBEAMBIGUOUS
               | unoidl::detail::FLAG_MAYBEDEFAULT
               | unoidl::detail::FLAG_MAYBEVOID | unoidl::detail::FLAG_OPTIONAL
               | unoidl::detail::FLAG_PROPERTY | unoidl::detail::FLAG_READONLY
               | unoidl::detail::FLAG_REMOVABLE
               | unoidl::detail::FLAG_TRANSIENT))
          != 0)
      {
          error(
              @2, yyscanner,
              (u"accumulation-based service property can only be flagged as"
               " [property, bound, constrained, maybeambiguous, maybedefault,"
               " maybevoid, optional, readonly, removable, transient]"_ustr));
          YYERROR;
      }
      int att = 0;
      if (($2 & unoidl::detail::FLAG_BOUND) != 0) {
          att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_BOUND;
      }
      if (($2 & unoidl::detail::FLAG_CONSTRAINED) != 0) {
          att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_CONSTRAINED;
      }
      if (($2 & unoidl::detail::FLAG_MAYBEAMBIGUOUS) != 0) {
          att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_MAYBE_AMBIGUOUS;
      }
      if (($2 & unoidl::detail::FLAG_MAYBEDEFAULT) != 0) {
          att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_MAYBE_DEFAULT;
      }
      if (($2 & unoidl::detail::FLAG_MAYBEVOID) != 0) {
          att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_MAYBE_VOID;
      }
      if (($2 & unoidl::detail::FLAG_OPTIONAL) != 0) {
          att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_OPTIONAL;
      }
      if (($2 & unoidl::detail::FLAG_READONLY) != 0) {
          att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_READ_ONLY;
      }
      if (($2 & unoidl::detail::FLAG_REMOVABLE) != 0) {
          att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_REMOVABLE;
      }
      if (($2 & unoidl::detail::FLAG_TRANSIENT) != 0) {
          att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_TRANSIENT;
      }
      switch (t.type) {
      case unoidl::detail::SourceProviderType::TYPE_VOID:
      case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
          error(
              @3, yyscanner,
              ("illegal accumulation-based service " + data->currentName
               + " direct property " + id + " type"));
          YYERROR;
          break;
      default:
          break;
      }
      rtl::Reference<unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad>
          pad(getCurrentPad<unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad>(
                  data));
      for (const auto & i: pad->directProperties) {
          if (id == i.name) {
              error(
                  @4, yyscanner,
                  ("accumulation-based service " + data->currentName
                   + " duplicate direct property " + id));
              YYERROR;
          }
      }
      pad->directProperties.emplace_back(
          id, t.getName(),
          unoidl::AccumulationBasedServiceEntity::Property::Attributes(att),
          annotations($1));
  }
;

interfaceBasedSingletonDefn:
  deprecated_opt published_opt TOK_SINGLETON identifier singleInheritance ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      data->publishedContext = $2;
      OUString name(convertToFullName(data, $4));
      OUString base(convertName($5));
      unoidl::detail::SourceProviderEntity const * p;
      if (findEntity(@5, yyscanner, data, false, &base, &p, nullptr, nullptr)
          == FOUND_ERROR)
      {
          YYERROR;
      }
      bool ifcBase = false;
      bool pubBase = false;
      if (p != nullptr) {
          switch (p->kind) {
          case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
              ifcBase = true;
              pubBase = false;
              break;
          case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
              ifcBase = true;
              pubBase = true;
              break;
          default:
              if (p->entity.is()
                  && (p->entity->getSort()
                      == unoidl::Entity::SORT_INTERFACE_TYPE))
              {
                  ifcBase = true;
                  pubBase = static_cast<unoidl::InterfaceTypeEntity *>(
                      p->entity.get())->isPublished();
              }
              break;
          }
      }
      if (!ifcBase) {
          error(
              @5, yyscanner,
              ("interface-based singleton " + name + " base " + base
               + " does not resolve to an interface type"));
          YYERROR;
      }
      if ($2 && !pubBase) {
          error(
              @5, yyscanner,
              ("published interface-based singleton " + name + " base " + base
               + " is unpublished"));
          YYERROR;
      }
      if (!data->entities.emplace(
                  name,
                  unoidl::detail::SourceProviderEntity(
                      unoidl::detail::SourceProviderEntity::KIND_LOCAL,
                      new unoidl::InterfaceBasedSingletonEntity(
                          $2, base, annotations($1)))).
          second)
      {
          error(@4, yyscanner, "multiple entities named " + name);
          YYERROR;
      }
      clearCurrentState(data);
  }
;

serviceBasedSingletonDefn:
  deprecated_opt published_opt TOK_SINGLETON identifier '{' TOK_SERVICE name ';'
  '}' ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      data->publishedContext = $2;
      OUString name(convertToFullName(data, $4));
      OUString base(convertName($7));
      unoidl::detail::SourceProviderEntity const * p;
      if (findEntity(@7, yyscanner, data, false, &base, &p, nullptr, nullptr)
          == FOUND_ERROR)
      {
          YYERROR;
      }
      if (p == nullptr
          || !p->entity.is()
          || (p->entity->getSort()
              != unoidl::Entity::SORT_ACCUMULATION_BASED_SERVICE))
      {
          error(
              @7, yyscanner,
              ("service-based singleton " + name + " base " + base
               + " does not resolve to an accumulation-based service"));
          YYERROR;
      }
      if ($2
          && !static_cast<unoidl::AccumulationBasedServiceEntity *>(
              p->entity.get())->isPublished())
      {
          error(
              @7, yyscanner,
              ("published service-based singleton " + name + " base " + base
               + " is unpublished"));
          YYERROR;
      }
      if (!data->entities.emplace(
                  name,
                  unoidl::detail::SourceProviderEntity(
                      unoidl::detail::SourceProviderEntity::KIND_LOCAL,
                      new unoidl::ServiceBasedSingletonEntity(
                          $2, base, annotations($1)))).
          second)
      {
          error(@4, yyscanner, "multiple entities named " + name);
          YYERROR;
      }
      clearCurrentState(data);
  }
;

singleInheritance_opt:
  singleInheritance
| /* empty */ { $$ = nullptr; }
;

singleInheritance: ':' name { $$ = $2; }
;

exceptionSpec_opt:
  exceptionSpec
| /* empty */ { $$ = nullptr; }
;

exceptionSpec: TOK_RAISES '(' exceptions ')' { $$ = $3; }
;

exceptions:
  exceptions ',' name
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      OUString name(convertName($3));
      unoidl::detail::SourceProviderEntity const * p;
      if (findEntity(@3, yyscanner, data, false, &name, &p, nullptr, nullptr)
          == FOUND_ERROR)
      {
          delete $1; /* see commented-out %destructor above */
          YYERROR;
      }
      if (p == nullptr
          || !p->entity.is()
          || (p->entity->getSort() != unoidl::Entity::SORT_EXCEPTION_TYPE))
      {
          delete $1; /* see commented-out %destructor above */
          error(
              @3, yyscanner,
              ("exception " + name + " does not resolve to an exception type"));
          YYERROR;
      }
      if (data->publishedContext
          && !(static_cast<unoidl::ExceptionTypeEntity *>(p->entity.get())
               ->isPublished()))
      {
          delete $1; /* see commented-out %destructor above */
          error(
              @3, yyscanner,
              ("unpublished exception " + name + " used in published context"));
          YYERROR;
      }
      if (std::find($1->begin(), $1->end(), name) != $1->end()) {
          delete $1; /* see commented-out %destructor above */
          error(
              @3, yyscanner, ("exception " + name + " listed more than once"));
          YYERROR;
      }
      $1->push_back(name);
      $$ = $1;
  }
| name
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      OUString name(convertName($1));
      unoidl::detail::SourceProviderEntity const * p;
      if (findEntity(@1, yyscanner, data, false, &name, &p, nullptr, nullptr)
          == FOUND_ERROR)
      {
          YYERROR;
      }
      if (p == nullptr
          || !p->entity.is()
          || (p->entity->getSort() != unoidl::Entity::SORT_EXCEPTION_TYPE))
      {
          error(
              @1, yyscanner,
              ("exception " + name + " does not resolve to an exception type"));
          YYERROR;
      }
      if (data->publishedContext
          && !(static_cast<unoidl::ExceptionTypeEntity *>(p->entity.get())
               ->isPublished()))
      {
          error(
              @1, yyscanner,
              ("unpublished exception " + name + " used in published context"));
          YYERROR;
      }
      $$ = new std::vector<OUString>; $$->push_back(name);
  }
;

interfaceDecl:
  deprecated_opt/*ignored*/ published_opt TOK_INTERFACE identifier ';'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      data->publishedContext = $2;
      OUString name(convertToFullName(data, $4));
      std::pair<std::map<OUString, unoidl::detail::SourceProviderEntity>::iterator, bool> p(
          data->entities.emplace(
                  name,
                  unoidl::detail::SourceProviderEntity(
                      $2
                      ? unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL
                      : unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL)));
      if (!p.second) {
          switch (p.first->second.kind) {
          case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
              if ($2) {
                  p.first->second.kind
                      = unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL;
              }
              break;
          case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
              break;
          default:
              assert(p.first->second.entity.is());
              if (p.first->second.entity->getSort()
                  != unoidl::Entity::SORT_INTERFACE_TYPE)
              {
                  error(
                      @4, yyscanner,
                      "multiple entities named " + data->currentName);
                  YYERROR;
              }
              if ($2
                  && !static_cast<unoidl::InterfaceTypeEntity *>(
                      p.first->second.entity.get())->isPublished())
              {
                  error(
                      @4, yyscanner,
                      ("published interface type declaration "
                       + data->currentName + " has been defined unpublished"));
                  YYERROR;
              }
          }
      }
      clearCurrentState(data);
  }
;

published_opt:
  TOK_PUBLISHED { $$ = true; }
| /* empty */ { $$ = false; }
;

flagSection_opt:
  flagSection
| /* empty */ { $$ = unoidl::detail::SourceProviderFlags(0); }
;

flagSection: '[' flags ']' { $$ = $2; }
;

flags:
  flags ',' flag
  {
      if (($1 & $3) != 0) {
          error(@3, yyscanner, "duplicate flag " + flagName($3));
          YYERROR;
      }
      $$ = unoidl::detail::SourceProviderFlags($1 | $3);
  }
| flag
;

flag:
  TOK_ATTRIBUTE { $$ = unoidl::detail::FLAG_ATTRIBUTE; }
| TOK_BOUND { $$ = unoidl::detail::FLAG_BOUND; }
| TOK_CONSTRAINED { $$ = unoidl::detail::FLAG_CONSTRAINED; }
| TOK_MAYBEAMBIGUOUS { $$ = unoidl::detail::FLAG_MAYBEAMBIGUOUS; }
| TOK_MAYBEDEFAULT { $$ = unoidl::detail::FLAG_MAYBEDEFAULT; }
| TOK_MAYBEVOID { $$ = unoidl::detail::FLAG_MAYBEVOID; }
| TOK_OPTIONAL { $$ = unoidl::detail::FLAG_OPTIONAL; }
| TOK_PROPERTY { $$ = unoidl::detail::FLAG_PROPERTY; }
| TOK_READONLY { $$ = unoidl::detail::FLAG_READONLY; }
| TOK_REMOVABLE { $$ = unoidl::detail::FLAG_REMOVABLE; }
| TOK_TRANSIENT { $$ = unoidl::detail::FLAG_TRANSIENT; }
;

expr: orExpr
;

orExpr:
  orExpr '|' xorExpr
  {
      if (!coerce(@1, yyscanner, &$1, &$3)) {
          YYERROR;
      }
      switch ($1.type) {
      case unoidl::detail::SourceProviderExpr::TYPE_INT:
          $$ = unoidl::detail::SourceProviderExpr::Int($1.ival | $3.ival);
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_UINT:
          $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval | $3.uval);
          break;
      default:
          error(@1, yyscanner, u"arguments of non-integer type to \"|\""_ustr);
          YYERROR;
          break;
      }
  }
| xorExpr
;

xorExpr:
  xorExpr '^' andExpr
  {
      if (!coerce(@1, yyscanner, &$1, &$3)) {
          YYERROR;
      }
      switch ($1.type) {
      case unoidl::detail::SourceProviderExpr::TYPE_INT:
          $$ = unoidl::detail::SourceProviderExpr::Int($1.ival ^ $3.ival);
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_UINT:
          $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval ^ $3.uval);
          break;
      default:
          error(@1, yyscanner, u"arguments of non-integer type to \"^\""_ustr);
          YYERROR;
          break;
      }
  }
| andExpr
;

andExpr:
  andExpr '&' shiftExpr
  {
      if (!coerce(@1, yyscanner, &$1, &$3)) {
          YYERROR;
      }
      switch ($1.type) {
      case unoidl::detail::SourceProviderExpr::TYPE_INT:
          $$ = unoidl::detail::SourceProviderExpr::Int($1.ival & $3.ival);
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_UINT:
          $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval & $3.uval);
          break;
      default:
          error(@1, yyscanner, u"arguments of non-integer type to \"&\""_ustr);
          YYERROR;
          break;
      }
  }
| shiftExpr
;

shiftExpr:
  shiftExpr TOK_LEFTSHIFT addExpr
  {
      int n;
      switch ($3.type) {
      case unoidl::detail::SourceProviderExpr::TYPE_INT:
          if ($3.ival < 0 || $3.ival > 63) {
              error(
                  @3, yyscanner,
                  ("out-of-range shift argument " + OUString::number($3.ival)
                   + " to \"<<\" "));
              YYERROR;
          }
          n = static_cast<int>($3.ival);
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_UINT:
          if ($3.uval > 63) {
              error(
                  @3, yyscanner,
                  ("out-of-range shift argument " + OUString::number($3.uval)
                   + " to \"<<\" "));
              YYERROR;
          }
          n = static_cast<int>($3.uval);
          break;
      default:
          error(@3, yyscanner, u"right argument of non-integer type to \"<<\""_ustr);
          YYERROR;
      }
      switch ($1.type) {
      case unoidl::detail::SourceProviderExpr::TYPE_INT:
          if ($1.ival < 0) {
              error(
                  @1, yyscanner,
                  ("cannot left-shift negative argument "
                   + OUString::number($1.ival)));
              YYERROR;
          }
          $$ = unoidl::detail::SourceProviderExpr::Int($1.ival << n);
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_UINT:
          $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval << n);
          break;
      default:
          error(@1, yyscanner, u"left argument of non-integer type to \"<<\""_ustr);
          YYERROR;
          break;
      }
  }
| shiftExpr TOK_RIGHTSHIFT addExpr
  {
      int n;
      switch ($3.type) {
      case unoidl::detail::SourceProviderExpr::TYPE_INT:
          if ($3.ival < 0 || $3.ival > 63) {
              error(
                  @3, yyscanner,
                  ("out-of-range shift argument " + OUString::number($3.ival)
                   + " to \">>\" "));
              YYERROR;
          }
          n = static_cast<int>($3.ival);
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_UINT:
          if ($3.uval > 63) {
              error(
                  @3, yyscanner,
                  ("out-of-range shift argument " + OUString::number($3.uval)
                   + " to \">>\" "));
              YYERROR;
          }
          n = static_cast<int>($3.uval);
          break;
      default:
          error(@3, yyscanner, u"right argument of non-integer type to \">>\""_ustr);
          YYERROR;
          break;
      }
      switch ($1.type) {
      case unoidl::detail::SourceProviderExpr::TYPE_INT:
          $$ = unoidl::detail::SourceProviderExpr::Int($1.ival >> n);
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_UINT:
          $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval >> n);
          break;
      default:
          error(@1, yyscanner, u"left argument of non-integer type to \">>\""_ustr);
          YYERROR;
          break;
      }
  }
| addExpr
;

addExpr:
  addExpr '+' multExpr
  {
      if (!coerce(@1, yyscanner, &$1, &$3)) {
          YYERROR;
      }
      switch ($1.type) {
      case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
          error(@1, yyscanner, u"arguments of boolean type to binary \"+\""_ustr);
          YYERROR;
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_INT:
          $$ = unoidl::detail::SourceProviderExpr::Int($1.ival + $3.ival); //TODO: overflow
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_UINT:
          $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval + $3.uval); //TODO: overflow
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
          $$ = unoidl::detail::SourceProviderExpr::Float($1.fval + $3.fval);
          break;
      }
  }
| addExpr '-' multExpr
  {
      if (!coerce(@1, yyscanner, &$1, &$3)) {
          YYERROR;
      }
      switch ($1.type) {
      case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
          error(@1, yyscanner, u"arguments of boolean type to binary \"-\""_ustr);
          YYERROR;
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_INT:
          $$ = unoidl::detail::SourceProviderExpr::Int($1.ival - $3.ival); //TODO: overflow
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_UINT:
          $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval - $3.uval); //TODO: overflow
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
          $$ = unoidl::detail::SourceProviderExpr::Float($1.fval - $3.fval);
          break;
      }
  }
| multExpr
;

multExpr:
  multExpr '*' unaryExpr
  {
      if (!coerce(@1, yyscanner, &$1, &$3)) {
          YYERROR;
      }
      switch ($1.type) {
      case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
          error(@1, yyscanner, u"arguments of boolean type to \"*\""_ustr);
          YYERROR;
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_INT:
          $$ = unoidl::detail::SourceProviderExpr::Int($1.ival * $3.ival); //TODO: overflow
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_UINT:
          $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval * $3.uval); //TODO: overflow
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
          $$ = unoidl::detail::SourceProviderExpr::Float($1.fval * $3.fval);
          break;
      }
  }
| multExpr '/' unaryExpr
  {
      if (!coerce(@1, yyscanner, &$1, &$3)) {
          YYERROR;
      }
      switch ($1.type) {
      case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
          error(@1, yyscanner, u"arguments of boolean type to \"/\""_ustr);
          YYERROR;
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_INT:
          if ($3.ival == 0) {
              error(@3, yyscanner, u"cannot divide by zero"_ustr);
              YYERROR;
          }
          $$ = unoidl::detail::SourceProviderExpr::Int($1.ival / $3.ival);
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_UINT:
          if ($3.uval == 0) {
              error(@3, yyscanner, u"cannot divide by zero"_ustr);
              YYERROR;
          }
          $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval / $3.uval);
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
          if ($3.fval == 0) {
              error(@3, yyscanner, u"cannot divide by zero"_ustr);
              YYERROR;
          }
          $$ = unoidl::detail::SourceProviderExpr::Float($1.fval - $3.fval);
          break;
      }
  }
| multExpr '%' unaryExpr
  {
      if (!coerce(@1, yyscanner, &$1, &$3)) {
          YYERROR;
      }
      switch ($1.type) {
      case unoidl::detail::SourceProviderExpr::TYPE_INT:
          if ($3.ival == 0) {
              error(@3, yyscanner, u"cannot divide by zero"_ustr);
              YYERROR;
          }
          $$ = unoidl::detail::SourceProviderExpr::Int($1.ival % $3.ival);
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_UINT:
          if ($3.uval == 0) {
              error(@3, yyscanner, u"cannot divide by zero"_ustr);
              YYERROR;
          }
          $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval % $3.uval);
          break;
      default:
          error(@1, yyscanner, u"arguments of non-integer type to \"%\""_ustr);
          YYERROR;
          break;
      }
  }
| unaryExpr
;

unaryExpr:
  '+' primaryExpr
  {
      if ($2.type == unoidl::detail::SourceProviderExpr::TYPE_BOOL) {
          error(@2, yyscanner, u"argument of boolean type to unary \"+\""_ustr);
          YYERROR;
      }
      $$ = $2;
  }
| '-' primaryExpr
  {
      switch ($2.type) {
      case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
          error(@2, yyscanner, u"argument of boolean type to unary \"-\""_ustr);
          YYERROR;
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_INT:
          if ($2.ival == SAL_MIN_INT64) {
              error(@2, yyscanner, u"cannot negate -2^63"_ustr);
              YYERROR;
          }
          $$ = unoidl::detail::SourceProviderExpr::Int(-$2.ival);
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_UINT:
          if ($2.uval == SAL_CONST_UINT64(0x8000000000000000)) {
              $$ = unoidl::detail::SourceProviderExpr::Int(SAL_MIN_INT64);
          } else {
              if ($2.uval > SAL_MAX_INT64) {
                  error(
                      @2, yyscanner,
                      ("cannot negate out-of-range value "
                       + OUString::number($2.uval)));
                  YYERROR;
              }
              $$ = unoidl::detail::SourceProviderExpr::Int(
                  -static_cast<sal_Int64>($2.uval));
          }
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
          $$ = unoidl::detail::SourceProviderExpr::Float(-$2.fval);
          break;
      }
  }
| '~' primaryExpr
  {
      switch ($2.type) {
      case unoidl::detail::SourceProviderExpr::TYPE_INT:
          $$ = unoidl::detail::SourceProviderExpr::Int(~$2.ival);
          break;
      case unoidl::detail::SourceProviderExpr::TYPE_UINT:
          $$ = unoidl::detail::SourceProviderExpr::Uint(~$2.uval);
          break;
      default:
          error(@2, yyscanner, u"argument of non-integer type to \"~\""_ustr);
          YYERROR;
          break;
      }
  }
| primaryExpr
;

primaryExpr:
  '(' expr ')' { $$ = $2; }
| TOK_FALSE { $$ = unoidl::detail::SourceProviderExpr::Bool(false); }
| TOK_TRUE { $$ = unoidl::detail::SourceProviderExpr::Bool(true); }
| TOK_INTEGER { $$ = unoidl::detail::SourceProviderExpr::Uint($1); }
| TOK_FLOATING { $$ = unoidl::detail::SourceProviderExpr::Float($1); }
| name
  {
      OUString name(convertName($1));
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      unoidl::ConstantValue v(false); // dummy value
      bool found = false;
      bool unpub = false;
      sal_Int32 i = name.lastIndexOf('.');
      if (i == -1) {
          rtl::Reference<unoidl::detail::SourceProviderEntityPad> pad(
              getCurrentEntity(data)->pad);
          unoidl::detail::SourceProviderEnumTypeEntityPad * p1 = dynamic_cast<
              unoidl::detail::SourceProviderEnumTypeEntityPad *>(pad.get());
          if (p1 != nullptr) {
              for (const auto & j: p1->members) {
                  if (j.name == name) {
                      v = unoidl::ConstantValue(j.value);
                      found = true;
                      break;
                  }
              }
          } else {
              unoidl::detail::SourceProviderConstantGroupEntityPad * p2
                  = dynamic_cast<
                      unoidl::detail::SourceProviderConstantGroupEntityPad *>(
                          pad.get());
              if (p2 != nullptr) {
                  for (const auto & j: p2->members) {
                      if (j.name == name) {
                          v = j.value;
                          found = true;
                          break;
                      }
                  }
              }
          }
      } else {
          OUString scope(name.copy(0, i));
          unoidl::detail::SourceProviderEntity const * ent;
          if (findEntity(
                  @1, yyscanner, data, false, &scope, &ent, nullptr, nullptr)
              == FOUND_ERROR)
          {
              YYERROR;
          }
          if (ent != nullptr) {
              std::u16string_view id(name.subView(i + 1));
              // No need to check for enum members here, as they cannot be
              // referenced in expressions by qualified name (TODO: is that true?):
              if (ent->entity.is()) {
                  if (ent->entity->getSort()
                      == unoidl::Entity::SORT_CONSTANT_GROUP)
                  {
                      std::vector<unoidl::ConstantGroupEntity::Member> const &
                          mems(
                              static_cast<unoidl::ConstantGroupEntity *>(
                                  ent->entity.get())->
                              getMembers());
                      for (auto & j: mems) {
                          if (j.name == id) {
                              v = j.value;
                              found = true;
                              unpub
                                  = !static_cast<unoidl::ConstantGroupEntity *>(
                                      ent->entity.get())->isPublished();
                              break;
                          }
                      }
                  }
              } else if (ent->pad.is()) {
                  unoidl::detail::SourceProviderConstantGroupEntityPad * pad
                      = dynamic_cast<
                          unoidl::detail::SourceProviderConstantGroupEntityPad *>(
                              ent->pad.get());
                  if (pad != nullptr) {
                      for (const auto & j: pad->members) {
                          if (j.name == id) {
                              v = j.value;
                              found = true;
                              unpub = !ent->pad->isPublished();
                              break;
                          }
                      }
                  }
              }
          }
      }
      if (!found) {
          error(
              @1, yyscanner,
              (name
               + (" does not resolve to neither a constant nor an unqualified"
                  " enum member")));
          YYERROR;
      }
      if (data->publishedContext && unpub) {
          error(
              @1, yyscanner,
              "unpublished value " + name + " used in published context");
          YYERROR;
      }
      switch (v.type) {
      case unoidl::ConstantValue::TYPE_BOOLEAN:
          $$ = unoidl::detail::SourceProviderExpr::Bool(v.booleanValue);
          break;
      case unoidl::ConstantValue::TYPE_BYTE:
          $$ = unoidl::detail::SourceProviderExpr::Int(v.byteValue);
          break;
      case unoidl::ConstantValue::TYPE_SHORT:
          $$ = unoidl::detail::SourceProviderExpr::Int(v.shortValue);
          break;
      case unoidl::ConstantValue::TYPE_UNSIGNED_SHORT:
          $$ = unoidl::detail::SourceProviderExpr::Uint(v.unsignedShortValue);
          break;
      case unoidl::ConstantValue::TYPE_LONG:
          $$ = unoidl::detail::SourceProviderExpr::Int(v.longValue);
          break;
      case unoidl::ConstantValue::TYPE_UNSIGNED_LONG:
          $$ = unoidl::detail::SourceProviderExpr::Uint(v.unsignedLongValue);
          break;
      case unoidl::ConstantValue::TYPE_HYPER:
          $$ = unoidl::detail::SourceProviderExpr::Int(v.hyperValue);
          break;
      case unoidl::ConstantValue::TYPE_UNSIGNED_HYPER:
          $$ = unoidl::detail::SourceProviderExpr::Uint(v.unsignedHyperValue);
          break;
      case unoidl::ConstantValue::TYPE_FLOAT:
          $$ = unoidl::detail::SourceProviderExpr::Float(v.floatValue);
          break;
      case unoidl::ConstantValue::TYPE_DOUBLE:
          $$ = unoidl::detail::SourceProviderExpr::Float(v.doubleValue);
          break;
      }
  }
;

typeArguments:
  typeArguments ',' type
  {
      unoidl::detail::SourceProviderType t(*$3);
      delete $3;
      if (!checkTypeArgument(@3, yyscanner, t)) {
          delete $1; /* see commented-out %destructor above */
          YYERROR;
      }
      $1->push_back(t);
      $$ = $1;
  }
| type
  {
      unoidl::detail::SourceProviderType t(*$1);
      delete $1;
      if (!checkTypeArgument(@1, yyscanner, t)) {
          YYERROR;
      }
      $$ = new std::vector<unoidl::detail::SourceProviderType>;
      $$->push_back(t);
  }
;

type:
  TOK_VOID
  {
      $$ = new unoidl::detail::SourceProviderType(
          unoidl::detail::SourceProviderType::TYPE_VOID);
  }
| TOK_BOOLEAN
  {
      $$ = new unoidl::detail::SourceProviderType(
          unoidl::detail::SourceProviderType::TYPE_BOOLEAN);
  }
| TOK_BYTE
  {
      $$ = new unoidl::detail::SourceProviderType(
          unoidl::detail::SourceProviderType::TYPE_BYTE);
  }
| TOK_SHORT
  {
      $$ = new unoidl::detail::SourceProviderType(
          unoidl::detail::SourceProviderType::TYPE_SHORT);
  }
| TOK_UNSIGNED TOK_SHORT
  {
      $$ = new unoidl::detail::SourceProviderType(
          unoidl::detail::SourceProviderType::TYPE_UNSIGNED_SHORT);
  }
| TOK_LONG
  {
      $$ = new unoidl::detail::SourceProviderType(
          unoidl::detail::SourceProviderType::TYPE_LONG);
  }
| TOK_UNSIGNED TOK_LONG
  {
      $$ = new unoidl::detail::SourceProviderType(
          unoidl::detail::SourceProviderType::TYPE_UNSIGNED_LONG);
  }
| TOK_HYPER
  {
      $$ = new unoidl::detail::SourceProviderType(
          unoidl::detail::SourceProviderType::TYPE_HYPER);
  }
| TOK_UNSIGNED TOK_HYPER
  {
      $$ = new unoidl::detail::SourceProviderType(
          unoidl::detail::SourceProviderType::TYPE_UNSIGNED_HYPER);
  }
| TOK_FLOAT
  {
      $$ = new unoidl::detail::SourceProviderType(
          unoidl::detail::SourceProviderType::TYPE_FLOAT);
  }
| TOK_DOUBLE
  {
      $$ = new unoidl::detail::SourceProviderType(
          unoidl::detail::SourceProviderType::TYPE_DOUBLE);
  }
| TOK_CHAR
  {
      $$ = new unoidl::detail::SourceProviderType(
          unoidl::detail::SourceProviderType::TYPE_CHAR);
  }
| TOK_STRING
  {
      $$ = new unoidl::detail::SourceProviderType(
          unoidl::detail::SourceProviderType::TYPE_STRING);
  }
| TOK_TYPE
  {
      $$ = new unoidl::detail::SourceProviderType(
          unoidl::detail::SourceProviderType::TYPE_TYPE);
  }
| TOK_ANY
  {
      $$ = new unoidl::detail::SourceProviderType(
          unoidl::detail::SourceProviderType::TYPE_ANY);
  }
| TOK_SEQUENCE '<' type '>'
  {
      switch ($3->type) {
      case unoidl::detail::SourceProviderType::TYPE_VOID:
      case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
      case unoidl::detail::SourceProviderType::TYPE_PARAMETER: //TODO?
          error(@3, yyscanner, u"illegal sequence type component type"_ustr);
          YYERROR;
          break;
      default:
          break;
      }
      $$ = new unoidl::detail::SourceProviderType($3);
      delete $3;
  }
| name
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      OUString name(convertName($1));
      bool done = false;
      if (name.indexOf('.') == -1 && !data->currentName.isEmpty()) {
          unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
          unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *
              pad = dynamic_cast<
                  unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *>(
                      ent->pad.get());
          if (pad != nullptr
              && (std::find(
                      pad->typeParameters.begin(), pad->typeParameters.end(),
                      name)
                  != pad->typeParameters.end()))
          {
              $$ = new unoidl::detail::SourceProviderType(name);
              done = true;
          }
      }
      if (!done) {
          unoidl::detail::SourceProviderEntity const * ent;
          unoidl::detail::SourceProviderType t;
          switch (findEntity(
                      @1, yyscanner, data, false, &name, &ent, nullptr, &t))
          {
          case FOUND_ERROR:
              YYERROR;
              break;
          case FOUND_TYPE:
              $$ = new unoidl::detail::SourceProviderType(t);
              break;
          case FOUND_ENTITY:
              if (ent == nullptr) {
                  error(@1, yyscanner, "unknown entity " + name);
                  YYERROR;
              }
              bool ok = false;
              switch (ent->kind) {
              case unoidl::detail::SourceProviderEntity::KIND_LOCAL:
                  if (ent->pad.is()) {
                      if (data->publishedContext && !ent->pad->isPublished()) {
                          error(
                              @1, yyscanner,
                              ("unpublished entity " + name
                               + " used in published context"));
                          YYERROR;
                      }
                      if (dynamic_cast<unoidl::detail::SourceProviderEnumTypeEntityPad *>(
                              ent->pad.get())
                          != nullptr)
                      {
                          $$ = new unoidl::detail::SourceProviderType(
                              unoidl::detail::SourceProviderType::TYPE_ENUM,
                              name, ent);
                          ok = true;
                      } else if (dynamic_cast<unoidl::detail::SourceProviderPlainStructTypeEntityPad *>(
                                     ent->pad.get())
                                 != nullptr)
                      {
                          $$ = new unoidl::detail::SourceProviderType(
                              unoidl::detail::SourceProviderType::TYPE_PLAIN_STRUCT,
                              name, ent);
                          ok = true;
                      } else if (dynamic_cast<unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *>(
                                     ent->pad.get())
                                 != nullptr)
                      {
                          error(
                              @1, yyscanner,
                              (("recursive reference to polymorphic struct type"
                                " template ")
                               + name));
                          YYERROR;
                      } else if (dynamic_cast<unoidl::detail::SourceProviderExceptionTypeEntityPad *>(
                                     ent->pad.get())
                                 != nullptr)
                      {
                          $$ = new unoidl::detail::SourceProviderType(
                              unoidl::detail::SourceProviderType::TYPE_EXCEPTION,
                              name, ent);
                          ok = true;
                      } else if (dynamic_cast<unoidl::detail::SourceProviderInterfaceTypeEntityPad *>(
                                     ent->pad.get())
                                 != nullptr)
                      {
                          $$ = new unoidl::detail::SourceProviderType(
                              unoidl::detail::SourceProviderType::TYPE_INTERFACE,
                              name, ent);
                          ok = true;
                      }
                      break;
                  }
                  assert(ent->entity.is());
                  [[fallthrough]];
              case unoidl::detail::SourceProviderEntity::KIND_EXTERNAL:
                  if (data->publishedContext
                      && ent->entity->getSort() != unoidl::Entity::SORT_MODULE
                      && !static_cast<unoidl::PublishableEntity *>(
                          ent->entity.get())->isPublished())
                  {
                      error(
                          @1, yyscanner,
                          ("unpublished entity " + name
                           + " used in published context"));
                      YYERROR;
                  }
                  switch (ent->entity->getSort()) {
                  case unoidl::Entity::SORT_ENUM_TYPE:
                      $$ = new unoidl::detail::SourceProviderType(
                          unoidl::detail::SourceProviderType::TYPE_ENUM, name,
                          ent);
                      ok = true;
                      break;
                  case unoidl::Entity::SORT_PLAIN_STRUCT_TYPE:
                      $$ = new unoidl::detail::SourceProviderType(
                          unoidl::detail::SourceProviderType::TYPE_PLAIN_STRUCT,
                          name, ent);
                      ok = true;
                      break;
                  case unoidl::Entity::SORT_POLYMORPHIC_STRUCT_TYPE_TEMPLATE:
                      error(
                          @1, yyscanner,
                          ("polymorphic struct type template " + name
                           + " without type arguments"));
                      YYERROR;
                      break;
                  case unoidl::Entity::SORT_EXCEPTION_TYPE:
                      $$ = new unoidl::detail::SourceProviderType(
                          unoidl::detail::SourceProviderType::TYPE_EXCEPTION,
                          name, ent);
                      ok = true;
                      break;
                  case unoidl::Entity::SORT_INTERFACE_TYPE:
                      $$ = new unoidl::detail::SourceProviderType(
                          unoidl::detail::SourceProviderType::TYPE_INTERFACE,
                          name, ent);
                      ok = true;
                      break;
                  case unoidl::Entity::SORT_TYPEDEF:
                      O3TL_UNREACHABLE;
                  default:
                      break;
                  }
                  break;
              case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
                  if (data->publishedContext) {
                      error(
                          @1, yyscanner,
                          ("unpublished entity " + name
                           + " used in published context"));
                      YYERROR;
                  }
                  [[fallthrough]];
              case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
                  $$ = new unoidl::detail::SourceProviderType(
                      unoidl::detail::SourceProviderType::TYPE_INTERFACE, name,
                      ent);
                  ok = true;
                  break;
              case unoidl::detail::SourceProviderEntity::KIND_MODULE:
                  assert(false && "this cannot happen");
              }
              if (!ok) {
                  error(@1, yyscanner, "non-type entity " + name);
                  YYERROR;
              }
              break;
          }
      }
  }
| name '<' typeArguments '>'
  {
      unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
      OUString name(convertName($1));
      std::vector<unoidl::detail::SourceProviderType> args(*$3);
      delete $3;
      unoidl::detail::SourceProviderEntity const * ent;
      if (findEntity(@1, yyscanner, data, false, &name, &ent, nullptr, nullptr)
          == FOUND_ERROR)
      {
          YYERROR;
      }
      if (ent == nullptr) {
          error(@1, yyscanner, "unknown entity " + name);
          YYERROR;
      }
      bool ok = false;
      switch (ent->kind) {
      case unoidl::detail::SourceProviderEntity::KIND_LOCAL:
          if (ent->pad.is()) {
              if (dynamic_cast<unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *>(
                      ent->pad.get())
                  != nullptr)
              {
                  error(
                      @1, yyscanner,
                      (("recursive reference to polymorphic struct type"
                        " template ")
                       + name));
                  YYERROR;
              }
              break;
          }
          assert(ent->entity.is());
          [[fallthrough]];
      case unoidl::detail::SourceProviderEntity::KIND_EXTERNAL:
          if (ent->entity->getSort()
              == unoidl::Entity::SORT_POLYMORPHIC_STRUCT_TYPE_TEMPLATE)
          {
              rtl::Reference<unoidl::PolymorphicStructTypeTemplateEntity> e(
                  static_cast<unoidl::PolymorphicStructTypeTemplateEntity *>(
                      ent->entity.get()));
              if (args.size() != e->getTypeParameters().size()) {
                  error(
                      @1, yyscanner,
                      ("bad number of polymorphic struct type template " + name
                       + " type arguments"));
                  YYERROR;
              }
              if (data->publishedContext && !e->isPublished()) {
                  error(
                      @1, yyscanner,
                      ("unpublished polymorphic struct type template " + name
                       + " used in published context"));
                  YYERROR;
              }
              $$ = new unoidl::detail::SourceProviderType(name, ent, std::move(args));
              ok = true;
          }
          break;
      case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
      case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
          break;
      case unoidl::detail::SourceProviderEntity::KIND_MODULE:
          assert(false && "this cannot happen");
      }
      if (!ok) {
          error(@1, yyscanner, "non-type entity " + name);
          YYERROR;
      }
  }
;

name:
  name TOK_COLONS identifier { *$1 += "." + *$3; delete $3; $$ = $1; }
| TOK_COLONS identifier { *$2 = "." + *$2; $$ = $2; }
| identifier
;

identifier:
  TOK_IDENTIFIER
| TOK_GET { $$ = new OString("get"_ostr); }
| TOK_PUBLISHED { $$ = new OString("published"_ostr); }
| TOK_SET { $$ = new OString("set"_ostr); }
;

deprecated_opt:
  TOK_DEPRECATED { $$ = true; }
| /* empty */ { $$ = false; }
;

%%

namespace unoidl::detail {

OUString SourceProviderType::getName() const {
    if (!typedefName.isEmpty()) {
        return typedefName;
    }
    switch (type) {
    case unoidl::detail::SourceProviderType::TYPE_VOID:
        return u"void"_ustr;
    case unoidl::detail::SourceProviderType::TYPE_BOOLEAN:
        return u"boolean"_ustr;
    case unoidl::detail::SourceProviderType::TYPE_BYTE:
        return u"byte"_ustr;
    case unoidl::detail::SourceProviderType::TYPE_SHORT:
        return u"short"_ustr;
    case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_SHORT:
        return u"unsigned short"_ustr;
    case unoidl::detail::SourceProviderType::TYPE_LONG:
        return u"long"_ustr;
    case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_LONG:
        return u"unsigned long"_ustr;
    case unoidl::detail::SourceProviderType::TYPE_HYPER:
        return u"hyper"_ustr;
    case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_HYPER:
        return u"unsigned hyper"_ustr;
    case unoidl::detail::SourceProviderType::TYPE_FLOAT:
        return u"float"_ustr;
    case unoidl::detail::SourceProviderType::TYPE_DOUBLE:
        return u"double"_ustr;
    case unoidl::detail::SourceProviderType::TYPE_CHAR:
        return u"char"_ustr;
    case unoidl::detail::SourceProviderType::TYPE_STRING:
        return u"string"_ustr;
    case unoidl::detail::SourceProviderType::TYPE_TYPE:
        return u"type"_ustr;
    case unoidl::detail::SourceProviderType::TYPE_ANY:
        return u"any"_ustr;
    case unoidl::detail::SourceProviderType::TYPE_SEQUENCE:
        assert(subtypes.size() == 1);
        return "[]" + subtypes.front().getName();
    case unoidl::detail::SourceProviderType::TYPE_ENUM:
    case unoidl::detail::SourceProviderType::TYPE_PLAIN_STRUCT:
    case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
    case unoidl::detail::SourceProviderType::TYPE_INTERFACE:
    case unoidl::detail::SourceProviderType::TYPE_PARAMETER:
        return name;
    case unoidl::detail::SourceProviderType::TYPE_INSTANTIATED_POLYMORPHIC_STRUCT:
        {
            OUStringBuffer n(512);
            n.append(name + "<");
            for (auto i(subtypes.begin()); i != subtypes.end(); ++i) {
                if (i != subtypes.begin()) {
                    n.append(",");
                }
                n.append(i->getName());
            }
            return n.append(">").makeStringAndClear();
        }
    default:
        assert(false && "this cannot happen"); for (;;) { std::abort(); }
    }
}

bool SourceProviderType::equals(SourceProviderType const & other) const {
    if (type != other.type || name != other.name
        || subtypes.size() != other.subtypes.size())
    {
        return false;
    }
    for (auto i(subtypes.begin()), j(other.subtypes.begin());
         i != subtypes.end(); ++i, ++j)
    {
        if (!i->equals(*j)) {
            return false;
        }
    }
    return true;
}

bool SourceProviderInterfaceTypeEntityPad::addDirectBase(
    YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
    DirectBase const & base, bool optional)
{
    std::set<OUString> seen;
    if (!(checkBaseClashes(
              location, yyscanner, data, base.name, base.entity, true, optional,
              optional, &seen)
          && addBase(
              location, yyscanner, data, base.name, base.name, base.entity,
              true, optional)))
    {
        return false;
    }
    if (optional) {
        addOptionalBaseMembers(
            location, yyscanner, data, base.name, base.entity);
    }
    (optional ? directOptionalBases : directMandatoryBases).push_back(base);
    return true;
}

bool SourceProviderInterfaceTypeEntityPad::addDirectMember(
    YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
    OUString const & name)
{
    assert(data != nullptr);
    if (!checkMemberClashes(location, yyscanner, data, u"", name, true)) {
        return false;
    }
    allMembers.emplace(name, Member(data->currentName));
    return true;
}

bool SourceProviderInterfaceTypeEntityPad::checkBaseClashes(
    YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
    OUString const & name,
    rtl::Reference<unoidl::InterfaceTypeEntity> const & entity, bool direct,
    bool optional, bool outerOptional, std::set<OUString> * seen) const
{
    assert(data != nullptr);
    assert(entity.is());
    assert(seen != nullptr);
    if (direct || optional || seen->insert(name).second) {
        std::map<OUString, BaseKind>::const_iterator i(allBases.find(name));
        if (i != allBases.end()) {
            switch (i->second) {
            case BASE_INDIRECT_OPTIONAL:
                if (direct && optional) {
                    error(
                        location, yyscanner,
                        ("interface type " + data->currentName
                         + " duplicate base " + name));
                    return false;
                }
                break;
            case BASE_DIRECT_OPTIONAL:
                if (direct || !outerOptional) {
                    error(
                        location, yyscanner,
                        ("interface type " + data->currentName
                         + " duplicate base " + name));
                    return false;
                }
                return true;
            case BASE_INDIRECT_MANDATORY:
                if (direct) {
                    error(
                        location, yyscanner,
                        ("interface type " + data->currentName
                         + " duplicate base " + name));
                    return false;
                }
                return true;
            case BASE_DIRECT_MANDATORY:
                if (direct || (!optional && !outerOptional)) {
                    error(
                        location, yyscanner,
                        ("interface type " + data->currentName
                         + " duplicate base " + name));
                    return false;
                }
                return true;
            }
        }
        if (direct || !optional) {
            for (auto & j: entity->getDirectMandatoryBases()) {
                OUString n("." + j.name);
                unoidl::detail::SourceProviderEntity const * p;
                if (findEntity(
                        location, yyscanner, data, true, &n, &p, nullptr,
                        nullptr)
                    == FOUND_ERROR)
                {
                    return false;
                }
                if (p == nullptr || !p->entity.is()
                    || (p->entity->getSort()
                        != unoidl::Entity::SORT_INTERFACE_TYPE))
                {
                    error(
                        location, yyscanner,
                        ("inconsistent type manager: interface type "
                         + data->currentName + " base " + n
                         + " does not resolve to an existing interface type"));
                    return false;
                }
                if (!checkBaseClashes(
                        location, yyscanner, data, n,
                        static_cast<unoidl::InterfaceTypeEntity *>(
                            p->entity.get()),
                        false, false, outerOptional, seen))
                {
                    return false;
                }
            }
            for (auto & j: entity->getDirectOptionalBases()) {
                OUString n("." + j.name);
                unoidl::detail::SourceProviderEntity const * p;
                if (findEntity(
                        location, yyscanner, data, true, &n, &p, nullptr,
                        nullptr)
                    == FOUND_ERROR)
                {
                    return false;
                }
                if (p == nullptr || !p->entity.is()
                    || (p->entity->getSort()
                        != unoidl::Entity::SORT_INTERFACE_TYPE))
                {
                    error(
                        location, yyscanner,
                        ("inconsistent type manager: interface type "
                         + data->currentName + " base " + n
                         + " does not resolve to an existing interface type"));
                    return false;
                }
                if (!checkBaseClashes(
                        location, yyscanner, data, n,
                        static_cast<unoidl::InterfaceTypeEntity *>(
                            p->entity.get()),
                        false, true, outerOptional, seen))
                {
                    return false;
                }
            }
            for (auto & j: entity->getDirectAttributes()) {
                if (!checkMemberClashes(
                        location, yyscanner, data, name, j.name,
                        !outerOptional))
                {
                    return false;
                }
            }
            for (auto & j: entity->getDirectMethods()) {
                if (!checkMemberClashes(
                        location, yyscanner, data, name, j.name,
                        !outerOptional))
                {
                    return false;
                }
            }
        }
    }
    return true;
}

bool SourceProviderInterfaceTypeEntityPad::checkMemberClashes(
    YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
    std::u16string_view interfaceName, OUString const & memberName,
    bool checkOptional) const
{
    std::map<OUString, Member>::const_iterator i(allMembers.find(memberName));
    if (i != allMembers.end()) {
        if (!i->second.mandatory.isEmpty()) {
            // For a direct member, interfaceName will be empty, so this will
            // catch two direct members with the same name:
            if (i->second.mandatory != interfaceName) {
                error(
                    location, yyscanner,
                    ("interface type " + data->currentName
                     + " duplicate member " + memberName));
                return false;
            }
        } else if (checkOptional) {
            for (auto & j: i->second.optional) {
                if (j != interfaceName) {
                    error(
                        location, yyscanner,
                        ("interface type " + data->currentName
                         + " duplicate member " + memberName));
                    return false;
                }
            }
        }
    }
    return true;
}

bool SourceProviderInterfaceTypeEntityPad::addBase(
    YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
    OUString const & directBaseName, OUString const & name,
    rtl::Reference<unoidl::InterfaceTypeEntity> const & entity, bool direct,
    bool optional)
{
    assert(data != nullptr);
    assert(entity.is());
    BaseKind kind = optional
        ? direct ? BASE_DIRECT_OPTIONAL : BASE_INDIRECT_OPTIONAL
        : direct ? BASE_DIRECT_MANDATORY : BASE_INDIRECT_MANDATORY;
    std::pair<std::map<OUString, BaseKind>::iterator, bool> p(
        allBases.emplace(name, kind));
    bool seen = !p.second && p.first->second >= BASE_INDIRECT_MANDATORY;
    if (!p.second && kind > p.first->second) {
        p.first->second = kind;
    }
    if (!optional && !seen) {
        for (auto & i: entity->getDirectMandatoryBases()) {
            OUString n("." + i.name);
            unoidl::detail::SourceProviderEntity const * q;
            if (findEntity(
                    location, yyscanner, data, true, &n, &q, nullptr, nullptr)
                == FOUND_ERROR)
            {
                return false;
            }
            if (q == nullptr || !q->entity.is()
                || q->entity->getSort() != unoidl::Entity::SORT_INTERFACE_TYPE)
            {
                error(
                    location, yyscanner,
                    ("inconsistent type manager: interface type "
                     + data->currentName + " base " + n
                     + " does not resolve to an existing interface type"));
                return false;
            }
            if (!addBase(
                    location, yyscanner, data, directBaseName, n,
                    static_cast<unoidl::InterfaceTypeEntity *>(q->entity.get()),
                    false, false))
            {
                return false;
            }
        }
        for (auto & i: entity->getDirectOptionalBases())
        {
            OUString n("." + i.name);
            unoidl::detail::SourceProviderEntity const * q;
            if (findEntity(
                    location, yyscanner, data, true, &n, &q, nullptr, nullptr)
                == FOUND_ERROR)
            {
                return false;
            }
            if (q == nullptr || !q->entity.is()
                || q->entity->getSort() != unoidl::Entity::SORT_INTERFACE_TYPE)
            {
                error(
                    location, yyscanner,
                    ("inconsistent type manager: interface type "
                     + data->currentName + " base " + n
                     + " does not resolve to an existing interface type"));
                return false;
            }
            if (!addBase(
                    location, yyscanner, data, directBaseName, n,
                    static_cast<unoidl::InterfaceTypeEntity *>(q->entity.get()),
                    false, true))
            {
                return false;
            }
        }
        for (auto & i: entity->getDirectAttributes()) {
            allMembers.emplace(i.name, Member(name));
        }
        for (auto & i: entity->getDirectMethods()) {
            allMembers.emplace(i.name, Member(name));
        }
    }
    return true;
}

bool SourceProviderInterfaceTypeEntityPad::addOptionalBaseMembers(
    YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
    OUString const & name,
    rtl::Reference<unoidl::InterfaceTypeEntity> const & entity)
{
    assert(entity.is());
    for (auto & i: entity->getDirectMandatoryBases()) {
        OUString n("." + i.name);
        unoidl::detail::SourceProviderEntity const * p;
        if (findEntity(
                location, yyscanner, data, true, &n, &p, nullptr, nullptr)
            == FOUND_ERROR)
        {
            return false;
        }
        if (p == nullptr || !p->entity.is()
            || p->entity->getSort() != unoidl::Entity::SORT_INTERFACE_TYPE)
        {
            error(
                location, yyscanner,
                ("inconsistent type manager: interface type "
                 + data->currentName + " base " + n
                 + " does not resolve to an existing interface type"));
            return false;
        }
        if (!addOptionalBaseMembers(
                location, yyscanner, data, n,
                static_cast<unoidl::InterfaceTypeEntity *>(p->entity.get())))
        {
            return false;
        }
    }
    for (auto & i: entity->getDirectAttributes()) {
        Member & m(
            allMembers.emplace(i.name, Member(u""_ustr))
            .first->second);
        if (m.mandatory.isEmpty()) {
            m.optional.insert(name);
        }
    }
    for (auto & i: entity->getDirectMethods()) {
        Member & m(
            allMembers.emplace(i.name, Member(u""_ustr))
            .first->second);
        if (m.mandatory.isEmpty()) {
            m.optional.insert(name);
        }
    }
    return true;
}

bool parse(OUString const & uri, SourceProviderScannerData * data) {
    assert(data != nullptr);
    oslFileHandle handle;
    oslFileError e = osl_openFile(uri.pData, &handle, osl_File_OpenFlag_Read);
    switch (e) {
    case osl_File_E_None:
        break;
    case osl_File_E_NOENT:
        return false;
    default:
        throw FileFormatException(uri, "cannot open: " + OUString::number(e));
    }
    sal_uInt64 size;
    e = osl_getFileSize(handle, &size);
    if (e != osl_File_E_None) {
        oslFileError e2 = osl_closeFile(handle);
        SAL_WARN_IF(
            e2 != osl_File_E_None, "unoidl",
            "cannot close " << uri << ": " << +e2);
        throw FileFormatException(
            uri, "cannot get size: " + OUString::number(e));
    }
    void * address;
    e = osl_mapFile(handle, &address, size, 0, osl_File_MapFlag_RandomAccess);
    if (e != osl_File_E_None) {
        oslFileError e2 = osl_closeFile(handle);
        SAL_WARN_IF(
            e2 != osl_File_E_None, "unoidl",
            "cannot close " << uri << ": " << +e2);
        throw FileFormatException(uri, "cannot mmap: " + OUString::number(e));
    }
    try {
        data->setSource(address, size);
        yyscan_t yyscanner;
        if (yylex_init_extra(data, &yyscanner) != 0) {
            // Checking errno for the specific EINVAL, ENOMEM documented for
            // yylex_init_extra would not work as those values are not defined
            // by the C++ Standard:
            int e2 = errno;
            throw FileFormatException(
                uri,
                "yylex_init_extra failed with errno " + OUString::number(e2));
        }
        int e2 = yyparse(yyscanner);
        yylex_destroy(yyscanner);
        switch (e2) {
        case 0:
            break;
        default:
            O3TL_UNREACHABLE;
        case 1:
            throw FileFormatException(
                uri,
                ("cannot parse"
                 + (data->errorLine == 0
                    ? OUString() : " line " + OUString::number(data->errorLine))
                 + (data->parserError.isEmpty()
                    ? OUString()
                    : (", "
                       + OStringToOUString(
                           data->parserError, osl_getThreadTextEncoding())))
                 + (data->errorMessage.isEmpty()
                    ? OUString() : ": \"" + data->errorMessage + "\"")));
        case 2:
            throw std::bad_alloc();
        }
    } catch (...) {
        e = osl_unmapMappedFile(handle, address, size);
        SAL_WARN_IF(e != osl_File_E_None, "unoidl", "cannot unmap: " << +e);
        e = osl_closeFile(handle);
        SAL_WARN_IF(e != osl_File_E_None, "unoidl", "cannot close: " << +e);
        throw;
    }
    e = osl_unmapMappedFile(handle, address, size);
    SAL_WARN_IF(e != osl_File_E_None, "unoidl", "cannot unmap: " << +e);
    e = osl_closeFile(handle);
    SAL_WARN_IF(e != osl_File_E_None, "unoidl", "cannot close: " << +e);
    return true;
}

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

[Seitenstruktur0.97Druckenetwas mehr zur Ethik2026-05-05]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge