<commit_before>/*
 *    Copyright (c) 2021, The OpenThread Authors.
 *    All rights reserved.
 *
 *    Redistribution and use in source and binary forms, with or without
 *    modification, are permitted provided that the following conditions are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *    3. Neither the name of the copyright holder nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *    POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file
 *   The file implements the DNS-SD Discovery Proxy.
 */

#if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY

#define OTBR_LOG_TAG "DPROXY"

#include "agent/discovery_proxy.hpp"

#include <algorithm>
#include <string>

#include <assert.h>

#include <openthread/dnssd_server.h>

#include "common/code_utils.hpp"
#include "common/dns_utils.hpp"
#include "common/logging.hpp"

namespace otbr {
namespace Dnssd {

DiscoveryProxy::DiscoveryProxy(Ncp::ControllerOpenThread &aNcp, Mdns::Publisher &aPublisher)
    : mNcp(aNcp)
    , mMdnsPublisher(aPublisher)
{
}

void DiscoveryProxy::Start(void)
{
    otDnssdQuerySetCallbacks(mNcp.GetInstance(), &DiscoveryProxy::OnDiscoveryProxySubscribe,
                             &DiscoveryProxy::OnDiscoveryProxyUnsubscribe, this);

    mMdnsPublisher.SetSubscriptionCallbacks(
        [this](const std::string &aType, const Mdns::Publisher::DiscoveredInstanceInfo &aInstanceInfo) {
            OnServiceDiscovered(aType, aInstanceInfo);
        },

        [this](const std::string &aHostName, const Mdns::Publisher::DiscoveredHostInfo &aHostInfo) {
            OnHostDiscovered(aHostName, aHostInfo);
        });

    otbrLogInfo("started");
}

void DiscoveryProxy::Stop(void)
{
    otDnssdQuerySetCallbacks(mNcp.GetInstance(), nullptr, nullptr, nullptr);
    mMdnsPublisher.SetSubscriptionCallbacks(nullptr, nullptr);

    otbrLogInfo("stopped");
}

void DiscoveryProxy::OnDiscoveryProxySubscribe(void *aContext, const char *aFullName)
{
    reinterpret_cast<DiscoveryProxy *>(aContext)->OnDiscoveryProxySubscribe(aFullName);
}

void DiscoveryProxy::OnDiscoveryProxySubscribe(const char *aFullName)
{
    std::string                    fullName(aFullName);
    otbrError                      error = OTBR_ERROR_NONE;
    MdnsSubscriptionList::iterator it;
    DnsNameInfo                    nameInfo = SplitFullDnsName(fullName);

    otbrLogInfo("subscribe: %s", fullName.c_str());

    it = std::find_if(mSubscriptions.begin(), mSubscriptions.end(), [&](const MdnsSubscription &aSubscription) {
        return aSubscription.Matches(nameInfo.mInstanceName, nameInfo.mServiceName, nameInfo.mHostName,
                                     nameInfo.mDomain);
    });

    VerifyOrExit(it == mSubscriptions.end(), it->mSubscriptionCount++);

    mSubscriptions.emplace_back(nameInfo.mInstanceName, nameInfo.mServiceName, nameInfo.mHostName, nameInfo.mDomain);

    {
        MdnsSubscription &subscription = mSubscriptions.back();

        otbrLogDebug("subscriptions: %sx%d", subscription.ToString().c_str(), subscription.mSubscriptionCount);

        if (GetServiceSubscriptionCount(nameInfo.mInstanceName, nameInfo.mServiceName, nameInfo.mHostName) == 1)
        {
            if (subscription.mHostName.empty())
            {
                mMdnsPublisher.SubscribeService(nameInfo.mServiceName, nameInfo.mInstanceName);
            }
            else
            {
                mMdnsPublisher.SubscribeHost(nameInfo.mHostName);
            }
        }
    }

exit:
    if (error != OTBR_ERROR_NONE)
    {
        otbrLogWarning("failed to subscribe %s: %s", fullName.c_str(), otbrErrorString(error));
    }
}

void DiscoveryProxy::OnDiscoveryProxyUnsubscribe(void *aContext, const char *aFullName)
{
    reinterpret_cast<DiscoveryProxy *>(aContext)->OnDiscoveryProxyUnsubscribe(aFullName);
}

void DiscoveryProxy::OnDiscoveryProxyUnsubscribe(const char *aFullName)
{
    std::string                    fullName(aFullName);
    otbrError                      error = OTBR_ERROR_NONE;
    MdnsSubscriptionList::iterator it;
    DnsNameInfo                    nameInfo = SplitFullDnsName(fullName);

    otbrLogInfo("unsubscribe: %s", fullName.c_str());

    it = std::find_if(mSubscriptions.begin(), mSubscriptions.end(), [&](const MdnsSubscription &aSubscription) {
        return aSubscription.Matches(nameInfo.mInstanceName, nameInfo.mServiceName, nameInfo.mHostName,
                                     nameInfo.mDomain);
    });

    VerifyOrExit(it != mSubscriptions.end(), error = OTBR_ERROR_NOT_FOUND);

    {
        MdnsSubscription &subscription = *it;

        subscription.mSubscriptionCount--;
        assert(subscription.mSubscriptionCount >= 0);

        if (subscription.mSubscriptionCount == 0)
        {
            mSubscriptions.erase(it);
        }

        otbrLogDebug("service subscriptions: %sx%d", it->ToString().c_str(), it->mSubscriptionCount);

        if (GetServiceSubscriptionCount(nameInfo.mInstanceName, nameInfo.mServiceName, nameInfo.mHostName) == 0)
        {
            if (subscription.mHostName.empty())
            {
                mMdnsPublisher.UnsubscribeService(nameInfo.mServiceName, nameInfo.mInstanceName);
            }
            else
            {
                mMdnsPublisher.UnsubscribeHost(nameInfo.mHostName);
            }
        }
    }
exit:
    if (error != OTBR_ERROR_NONE)
    {
        otbrLogWarning("failed to unsubscribe %s: %s", fullName.c_str(), otbrErrorString(error));
    }
}

void DiscoveryProxy::OnServiceDiscovered(const std::string &                            aType,
                                         const Mdns::Publisher::DiscoveredInstanceInfo &aInstanceInfo)
{
    otDnssdServiceInstanceInfo instanceInfo;

    otbrLogInfo("service discovered: %s, instance %s hostname %s addresses %zu port %d priority %d "
                "weight %d",
                aType.c_str(), aInstanceInfo.mName.c_str(), aInstanceInfo.mHostName.c_str(),
                aInstanceInfo.mAddresses.size(), aInstanceInfo.mPort, aInstanceInfo.mPriority, aInstanceInfo.mWeight);

    CheckServiceNameSanity(aType);
    CheckHostnameSanity(aInstanceInfo.mHostName);

    instanceInfo.mAddressNum = aInstanceInfo.mAddresses.size();

    if (!aInstanceInfo.mAddresses.empty())
    {
        instanceInfo.mAddresses = reinterpret_cast<const otIp6Address *>(&aInstanceInfo.mAddresses[0]);
    }
    else
    {
        instanceInfo.mAddresses = nullptr;
    }

    instanceInfo.mPort      = aInstanceInfo.mPort;
    instanceInfo.mPriority  = aInstanceInfo.mPriority;
    instanceInfo.mWeight    = aInstanceInfo.mWeight;
    instanceInfo.mTxtLength = static_cast<uint16_t>(aInstanceInfo.mTxtData.size());
    instanceInfo.mTxtData   = aInstanceInfo.mTxtData.data();
    instanceInfo.mTtl       = CapTtl(aInstanceInfo.mTtl);

    std::for_each(mSubscriptions.begin(), mSubscriptions.end(), [&](const MdnsSubscription &aSubscription) {
        if (aSubscription.MatchesServiceInstance(aType, aInstanceInfo.mName))
        {
            std::string serviceFullName  = aType + "." + aSubscription.mDomain;
            std::string hostName         = TranslateDomain(aInstanceInfo.mHostName, aSubscription.mDomain);
            std::string instanceFullName = aInstanceInfo.mName + "." + serviceFullName;

            instanceInfo.mFullName = instanceFullName.c_str();
            instanceInfo.mHostName = hostName.c_str();

            otDnssdQueryHandleDiscoveredServiceInstance(mNcp.GetInstance(), serviceFullName.c_str(), &instanceInfo);
        }
    });
}

void DiscoveryProxy::OnHostDiscovered(const std::string &                        aHostName,
                                      const Mdns::Publisher::DiscoveredHostInfo &aHostInfo)
{
    otDnssdHostInfo hostInfo;

    otbrLogInfo("host discovered: %s hostname %s addresses %zu", aHostName.c_str(), aHostInfo.mHostName.c_str(),
                aHostInfo.mAddresses.size());

    CheckHostnameSanity(aHostInfo.mHostName);

    hostInfo.mAddressNum = aHostInfo.mAddresses.size();
    if (!aHostInfo.mAddresses.empty())
    {
        hostInfo.mAddresses = reinterpret_cast<const otIp6Address *>(&aHostInfo.mAddresses[0]);
    }
    else
    {
        hostInfo.mAddresses = nullptr;
    }

    hostInfo.mTtl = CapTtl(aHostInfo.mTtl);

    std::for_each(mSubscriptions.begin(), mSubscriptions.end(), [&](const MdnsSubscription &aSubscription) {
        if (aSubscription.MatchesHost(aHostName))
        {
            std::string hostFullName = TranslateDomain(aHostInfo.mHostName, aSubscription.mDomain);

            otDnssdQueryHandleDiscoveredHost(mNcp.GetInstance(), hostFullName.c_str(), &hostInfo);
        }
    });
}

std::string DiscoveryProxy::TranslateDomain(const std::string &aName, const std::string &aTargetDomain)
{
    std::string targetName;
    std::string hostName, domain;

    VerifyOrExit(OTBR_ERROR_NONE == SplitFullHostName(aName, hostName, domain), targetName = aName);
    VerifyOrExit(domain == "local.", targetName = aName);

    targetName = hostName + "." + aTargetDomain;

exit:
    otbrLogDebug("translate domain: %s => %s", aName.c_str(), targetName.c_str());
    return targetName;
}

int DiscoveryProxy::GetServiceSubscriptionCount(const std::string &aInstanceName,
                                                const std::string &aServiceName,
                                                const std::string &aHostName)
{
    return std::accumulate(
        mSubscriptions.begin(), mSubscriptions.end(), 0, [&](int aAccum, const MdnsSubscription &aSubscription) {
            return aAccum + ((aSubscription.mInstanceName == aInstanceName &&
                              aSubscription.mServiceName == aServiceName && aSubscription.mHostName == aHostName)
                                 ? aSubscription.mSubscriptionCount
                                 : 0);
        });
}

void DiscoveryProxy::CheckServiceNameSanity(const std::string &aType)
{
    size_t dotpos;

    OTBR_UNUSED_VARIABLE(aType);
    OTBR_UNUSED_VARIABLE(dotpos);

    assert(aType.length() > 0);
    assert(aType[aType.length() - 1] != '.');
    dotpos = aType.find_first_of('.');
    assert(dotpos != std::string::npos);
    assert(dotpos == aType.find_last_of('.'));
}

void DiscoveryProxy::CheckHostnameSanity(const std::string &aHostName)
{
    OTBR_UNUSED_VARIABLE(aHostName);

    assert(aHostName.length() > 0);
    assert(aHostName[aHostName.length() - 1] == '.');
}

uint32_t DiscoveryProxy::CapTtl(uint32_t aTtl)
{
    return std::min(aTtl, static_cast<uint32_t>(kServiceTtlCapLimit));
}

std::string DiscoveryProxy::MdnsSubscription::ToString(void) const
{
    std::string str;

    if (!mHostName.empty())
    {
        str = mHostName + "." + mDomain;
    }
    else if (!mInstanceName.empty())
    {
        str = mInstanceName + "." + mServiceName + "." + mDomain;
    }
    else
    {
        str = mServiceName + "." + mDomain;
    }

    return str;
}

bool DiscoveryProxy::MdnsSubscription::Matches(const std::string &aInstanceName,
                                               const std::string &aServiceName,
                                               const std::string &aHostName,
                                               const std::string &aDomain) const
{
    return mInstanceName == aInstanceName && mServiceName == aServiceName && mHostName == aHostName &&
           mDomain == aDomain;
}

bool DiscoveryProxy::MdnsSubscription::MatchesServiceInstance(const std::string &aType,
                                                              const std::string &aInstanceName) const
{
    return mServiceName == aType && (mInstanceName.empty() || mInstanceName == aInstanceName);
}

bool DiscoveryProxy::MdnsSubscription::MatchesHost(const std::string &aHostName) const
{
    return mHostName == aHostName;
}

} // namespace Dnssd
} // namespace otbr

#endif // OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
<commit_msg>[dnssd] fix printing string after its destruction (#857)<commit_after>/*
 *    Copyright (c) 2021, The OpenThread Authors.
 *    All rights reserved.
 *
 *    Redistribution and use in source and binary forms, with or without
 *    modification, are permitted provided that the following conditions are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *    3. Neither the name of the copyright holder nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *    POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file
 *   The file implements the DNS-SD Discovery Proxy.
 */

#if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY

#define OTBR_LOG_TAG "DPROXY"

#include "agent/discovery_proxy.hpp"

#include <algorithm>
#include <string>

#include <assert.h>

#include <openthread/dnssd_server.h>

#include "common/code_utils.hpp"
#include "common/dns_utils.hpp"
#include "common/logging.hpp"

namespace otbr {
namespace Dnssd {

DiscoveryProxy::DiscoveryProxy(Ncp::ControllerOpenThread &aNcp, Mdns::Publisher &aPublisher)
    : mNcp(aNcp)
    , mMdnsPublisher(aPublisher)
{
}

void DiscoveryProxy::Start(void)
{
    otDnssdQuerySetCallbacks(mNcp.GetInstance(), &DiscoveryProxy::OnDiscoveryProxySubscribe,
                             &DiscoveryProxy::OnDiscoveryProxyUnsubscribe, this);

    mMdnsPublisher.SetSubscriptionCallbacks(
        [this](const std::string &aType, const Mdns::Publisher::DiscoveredInstanceInfo &aInstanceInfo) {
            OnServiceDiscovered(aType, aInstanceInfo);
        },

        [this](const std::string &aHostName, const Mdns::Publisher::DiscoveredHostInfo &aHostInfo) {
            OnHostDiscovered(aHostName, aHostInfo);
        });

    otbrLogInfo("started");
}

void DiscoveryProxy::Stop(void)
{
    otDnssdQuerySetCallbacks(mNcp.GetInstance(), nullptr, nullptr, nullptr);
    mMdnsPublisher.SetSubscriptionCallbacks(nullptr, nullptr);

    otbrLogInfo("stopped");
}

void DiscoveryProxy::OnDiscoveryProxySubscribe(void *aContext, const char *aFullName)
{
    reinterpret_cast<DiscoveryProxy *>(aContext)->OnDiscoveryProxySubscribe(aFullName);
}

void DiscoveryProxy::OnDiscoveryProxySubscribe(const char *aFullName)
{
    std::string                    fullName(aFullName);
    otbrError                      error = OTBR_ERROR_NONE;
    MdnsSubscriptionList::iterator it;
    DnsNameInfo                    nameInfo = SplitFullDnsName(fullName);

    otbrLogInfo("subscribe: %s", fullName.c_str());

    it = std::find_if(mSubscriptions.begin(), mSubscriptions.end(), [&](const MdnsSubscription &aSubscription) {
        return aSubscription.Matches(nameInfo.mInstanceName, nameInfo.mServiceName, nameInfo.mHostName,
                                     nameInfo.mDomain);
    });

    VerifyOrExit(it == mSubscriptions.end(), it->mSubscriptionCount++);

    mSubscriptions.emplace_back(nameInfo.mInstanceName, nameInfo.mServiceName, nameInfo.mHostName, nameInfo.mDomain);

    {
        MdnsSubscription &subscription = mSubscriptions.back();

        otbrLogDebug("subscriptions: %sx%d", subscription.ToString().c_str(), subscription.mSubscriptionCount);

        if (GetServiceSubscriptionCount(nameInfo.mInstanceName, nameInfo.mServiceName, nameInfo.mHostName) == 1)
        {
            if (subscription.mHostName.empty())
            {
                mMdnsPublisher.SubscribeService(nameInfo.mServiceName, nameInfo.mInstanceName);
            }
            else
            {
                mMdnsPublisher.SubscribeHost(nameInfo.mHostName);
            }
        }
    }

exit:
    if (error != OTBR_ERROR_NONE)
    {
        otbrLogWarning("failed to subscribe %s: %s", fullName.c_str(), otbrErrorString(error));
    }
}

void DiscoveryProxy::OnDiscoveryProxyUnsubscribe(void *aContext, const char *aFullName)
{
    reinterpret_cast<DiscoveryProxy *>(aContext)->OnDiscoveryProxyUnsubscribe(aFullName);
}

void DiscoveryProxy::OnDiscoveryProxyUnsubscribe(const char *aFullName)
{
    std::string                    fullName(aFullName);
    otbrError                      error = OTBR_ERROR_NONE;
    MdnsSubscriptionList::iterator it;
    DnsNameInfo                    nameInfo = SplitFullDnsName(fullName);

    otbrLogInfo("unsubscribe: %s", fullName.c_str());

    it = std::find_if(mSubscriptions.begin(), mSubscriptions.end(), [&](const MdnsSubscription &aSubscription) {
        return aSubscription.Matches(nameInfo.mInstanceName, nameInfo.mServiceName, nameInfo.mHostName,
                                     nameInfo.mDomain);
    });

    VerifyOrExit(it != mSubscriptions.end(), error = OTBR_ERROR_NOT_FOUND);

    {
        MdnsSubscription &subscription = *it;

        subscription.mSubscriptionCount--;
        assert(subscription.mSubscriptionCount >= 0);

        otbrLogDebug("service subscriptions: %sx%d", it->ToString().c_str(), it->mSubscriptionCount);

        if (subscription.mSubscriptionCount == 0)
        {
            mSubscriptions.erase(it);
        }

        if (GetServiceSubscriptionCount(nameInfo.mInstanceName, nameInfo.mServiceName, nameInfo.mHostName) == 0)
        {
            if (subscription.mHostName.empty())
            {
                mMdnsPublisher.UnsubscribeService(nameInfo.mServiceName, nameInfo.mInstanceName);
            }
            else
            {
                mMdnsPublisher.UnsubscribeHost(nameInfo.mHostName);
            }
        }
    }
exit:
    if (error != OTBR_ERROR_NONE)
    {
        otbrLogWarning("failed to unsubscribe %s: %s", fullName.c_str(), otbrErrorString(error));
    }
}

void DiscoveryProxy::OnServiceDiscovered(const std::string &                            aType,
                                         const Mdns::Publisher::DiscoveredInstanceInfo &aInstanceInfo)
{
    otDnssdServiceInstanceInfo instanceInfo;

    otbrLogInfo("service discovered: %s, instance %s hostname %s addresses %zu port %d priority %d "
                "weight %d",
                aType.c_str(), aInstanceInfo.mName.c_str(), aInstanceInfo.mHostName.c_str(),
                aInstanceInfo.mAddresses.size(), aInstanceInfo.mPort, aInstanceInfo.mPriority, aInstanceInfo.mWeight);

    CheckServiceNameSanity(aType);
    CheckHostnameSanity(aInstanceInfo.mHostName);

    instanceInfo.mAddressNum = aInstanceInfo.mAddresses.size();

    if (!aInstanceInfo.mAddresses.empty())
    {
        instanceInfo.mAddresses = reinterpret_cast<const otIp6Address *>(&aInstanceInfo.mAddresses[0]);
    }
    else
    {
        instanceInfo.mAddresses = nullptr;
    }

    instanceInfo.mPort      = aInstanceInfo.mPort;
    instanceInfo.mPriority  = aInstanceInfo.mPriority;
    instanceInfo.mWeight    = aInstanceInfo.mWeight;
    instanceInfo.mTxtLength = static_cast<uint16_t>(aInstanceInfo.mTxtData.size());
    instanceInfo.mTxtData   = aInstanceInfo.mTxtData.data();
    instanceInfo.mTtl       = CapTtl(aInstanceInfo.mTtl);

    std::for_each(mSubscriptions.begin(), mSubscriptions.end(), [&](const MdnsSubscription &aSubscription) {
        if (aSubscription.MatchesServiceInstance(aType, aInstanceInfo.mName))
        {
            std::string serviceFullName  = aType + "." + aSubscription.mDomain;
            std::string hostName         = TranslateDomain(aInstanceInfo.mHostName, aSubscription.mDomain);
            std::string instanceFullName = aInstanceInfo.mName + "." + serviceFullName;

            instanceInfo.mFullName = instanceFullName.c_str();
            instanceInfo.mHostName = hostName.c_str();

            otDnssdQueryHandleDiscoveredServiceInstance(mNcp.GetInstance(), serviceFullName.c_str(), &instanceInfo);
        }
    });
}

void DiscoveryProxy::OnHostDiscovered(const std::string &                        aHostName,
                                      const Mdns::Publisher::DiscoveredHostInfo &aHostInfo)
{
    otDnssdHostInfo hostInfo;

    otbrLogInfo("host discovered: %s hostname %s addresses %zu", aHostName.c_str(), aHostInfo.mHostName.c_str(),
                aHostInfo.mAddresses.size());

    CheckHostnameSanity(aHostInfo.mHostName);

    hostInfo.mAddressNum = aHostInfo.mAddresses.size();
    if (!aHostInfo.mAddresses.empty())
    {
        hostInfo.mAddresses = reinterpret_cast<const otIp6Address *>(&aHostInfo.mAddresses[0]);
    }
    else
    {
        hostInfo.mAddresses = nullptr;
    }

    hostInfo.mTtl = CapTtl(aHostInfo.mTtl);

    std::for_each(mSubscriptions.begin(), mSubscriptions.end(), [&](const MdnsSubscription &aSubscription) {
        if (aSubscription.MatchesHost(aHostName))
        {
            std::string hostFullName = TranslateDomain(aHostInfo.mHostName, aSubscription.mDomain);

            otDnssdQueryHandleDiscoveredHost(mNcp.GetInstance(), hostFullName.c_str(), &hostInfo);
        }
    });
}

std::string DiscoveryProxy::TranslateDomain(const std::string &aName, const std::string &aTargetDomain)
{
    std::string targetName;
    std::string hostName, domain;

    VerifyOrExit(OTBR_ERROR_NONE == SplitFullHostName(aName, hostName, domain), targetName = aName);
    VerifyOrExit(domain == "local.", targetName = aName);

    targetName = hostName + "." + aTargetDomain;

exit:
    otbrLogDebug("translate domain: %s => %s", aName.c_str(), targetName.c_str());
    return targetName;
}

int DiscoveryProxy::GetServiceSubscriptionCount(const std::string &aInstanceName,
                                                const std::string &aServiceName,
                                                const std::string &aHostName)
{
    return std::accumulate(
        mSubscriptions.begin(), mSubscriptions.end(), 0, [&](int aAccum, const MdnsSubscription &aSubscription) {
            return aAccum + ((aSubscription.mInstanceName == aInstanceName &&
                              aSubscription.mServiceName == aServiceName && aSubscription.mHostName == aHostName)
                                 ? aSubscription.mSubscriptionCount
                                 : 0);
        });
}

void DiscoveryProxy::CheckServiceNameSanity(const std::string &aType)
{
    size_t dotpos;

    OTBR_UNUSED_VARIABLE(aType);
    OTBR_UNUSED_VARIABLE(dotpos);

    assert(aType.length() > 0);
    assert(aType[aType.length() - 1] != '.');
    dotpos = aType.find_first_of('.');
    assert(dotpos != std::string::npos);
    assert(dotpos == aType.find_last_of('.'));
}

void DiscoveryProxy::CheckHostnameSanity(const std::string &aHostName)
{
    OTBR_UNUSED_VARIABLE(aHostName);

    assert(aHostName.length() > 0);
    assert(aHostName[aHostName.length() - 1] == '.');
}

uint32_t DiscoveryProxy::CapTtl(uint32_t aTtl)
{
    return std::min(aTtl, static_cast<uint32_t>(kServiceTtlCapLimit));
}

std::string DiscoveryProxy::MdnsSubscription::ToString(void) const
{
    std::string str;

    if (!mHostName.empty())
    {
        str = mHostName + "." + mDomain;
    }
    else if (!mInstanceName.empty())
    {
        str = mInstanceName + "." + mServiceName + "." + mDomain;
    }
    else
    {
        str = mServiceName + "." + mDomain;
    }

    return str;
}

bool DiscoveryProxy::MdnsSubscription::Matches(const std::string &aInstanceName,
                                               const std::string &aServiceName,
                                               const std::string &aHostName,
                                               const std::string &aDomain) const
{
    return mInstanceName == aInstanceName && mServiceName == aServiceName && mHostName == aHostName &&
           mDomain == aDomain;
}

bool DiscoveryProxy::MdnsSubscription::MatchesServiceInstance(const std::string &aType,
                                                              const std::string &aInstanceName) const
{
    return mServiceName == aType && (mInstanceName.empty() || mInstanceName == aInstanceName);
}

bool DiscoveryProxy::MdnsSubscription::MatchesHost(const std::string &aHostName) const
{
    return mHostName == aHostName;
}

} // namespace Dnssd
} // namespace otbr

#endif // OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
<|endoftext|>


<commit_before>#include "compiler/build_tables/build_parse_table.h"
#include <algorithm>
#include <map>
#include <set>
#include <string>
#include <unordered_map>
#include <utility>
#include "compiler/parse_table.h"
#include "compiler/build_tables/parse_conflict_manager.h"
#include "compiler/build_tables/remove_duplicate_states.h"
#include "compiler/build_tables/parse_item.h"
#include "compiler/build_tables/item_set_closure.h"
#include "compiler/lexical_grammar.h"
#include "compiler/syntax_grammar.h"
#include "compiler/rules/symbol.h"
#include "compiler/rules/built_in_symbols.h"
#include "compiler/build_tables/recovery_tokens.h"

namespace tree_sitter {
namespace build_tables {

using std::find;
using std::pair;
using std::vector;
using std::set;
using std::map;
using std::string;
using std::to_string;
using std::unordered_map;
using std::make_shared;
using rules::Symbol;

class ParseTableBuilder {
  const SyntaxGrammar grammar;
  const LexicalGrammar lexical_grammar;
  ParseConflictManager conflict_manager;
  unordered_map<Symbol, ParseItemSet> recovery_states;
  unordered_map<ParseItemSet, ParseStateId, ParseItemSet::Hash> parse_state_ids;
  vector<pair<ParseItemSet, ParseStateId>> item_sets_to_process;
  ParseTable parse_table;
  std::set<string> conflicts;
  ParseItemSet null_item_set;
  std::set<const Production *> fragile_productions;
  bool allow_any_conflict;

 public:
  ParseTableBuilder(const SyntaxGrammar &grammar,
                    const LexicalGrammar &lex_grammar)
      : grammar(grammar),
        lexical_grammar(lex_grammar),
        allow_any_conflict(false) {}

  pair<ParseTable, CompileError> build() {
    Symbol start_symbol = Symbol(0, grammar.variables.empty());
    Production start_production({
      ProductionStep(start_symbol, 0, rules::AssociativityNone),
    });

    add_parse_state(ParseItemSet({
      {
        ParseItem(rules::START(), start_production, 0),
        LookaheadSet({ rules::END_OF_INPUT() }),
      },
    }));

    CompileError error = process_part_state_queue();
    if (error.type != TSCompileErrorTypeNone) {
      return { parse_table, error };
    }

    add_out_of_context_parse_states();
    allow_any_conflict = true;
    process_part_state_queue();
    allow_any_conflict = false;

    for (ParseStateId state = 0; state < parse_table.states.size(); state++) {
      add_shift_extra_actions(state);
      add_reduce_extra_actions(state);
    }

    mark_fragile_actions();
    remove_duplicate_parse_states();

    return { parse_table, CompileError::none() };
  }

 private:
  CompileError process_part_state_queue() {
    while (!item_sets_to_process.empty()) {
      auto pair = item_sets_to_process.back();
      ParseItemSet item_set = item_set_closure(pair.first, grammar);

      ParseStateId state_id = pair.second;
      item_sets_to_process.pop_back();

      add_reduce_actions(item_set, state_id);
      add_shift_actions(item_set, state_id);

      if (!conflicts.empty()) {
        return CompileError(TSCompileErrorTypeParseConflict,
                            "Unresolved conflict.\n\n" + *conflicts.begin());
      }
    }

    return CompileError::none();
  }

  void add_out_of_context_parse_states() {
    for (const Symbol &symbol : recovery_tokens(lexical_grammar)) {
      add_out_of_context_parse_state(symbol);
    }

    for (size_t i = 0; i < grammar.variables.size(); i++) {
      Symbol symbol(i, false);
      add_out_of_context_parse_state(symbol);
    }

    parse_table.error_state.actions[rules::END_OF_INPUT()].push_back(
      ParseAction::Shift(0, PrecedenceRange()));
  }

  void add_out_of_context_parse_state(const rules::Symbol &symbol) {
    const ParseItemSet &item_set = recovery_states[symbol];
    if (!item_set.entries.empty()) {
      ParseStateId state = add_parse_state(item_set);
      parse_table.error_state.actions[symbol].push_back(
        ParseAction::Shift(state, PrecedenceRange()));
    }
  }

  ParseStateId add_parse_state(const ParseItemSet &item_set) {
    auto pair = parse_state_ids.find(item_set);
    if (pair == parse_state_ids.end()) {
      ParseStateId state_id = parse_table.add_state();

      parse_state_ids[item_set] = state_id;
      item_sets_to_process.push_back({ item_set, state_id });
      return state_id;
    } else {
      return pair->second;
    }
  }

  void add_shift_actions(const ParseItemSet &item_set, ParseStateId state_id) {
    for (const auto &transition : item_set.transitions()) {
      const Symbol &symbol = transition.first;
      const ParseItemSet &next_item_set = transition.second.first;
      const PrecedenceRange &precedence = transition.second.second;

      ParseAction *new_action = add_action(
        state_id, symbol, ParseAction::Shift(0, precedence), item_set);

      if (!allow_any_conflict)
        recovery_states[symbol].add(next_item_set);

      if (new_action)
        new_action->state_index = add_parse_state(next_item_set);
    }
  }

  void add_reduce_actions(const ParseItemSet &item_set, ParseStateId state_id) {
    for (const auto &pair : item_set.entries) {
      const ParseItem &item = pair.first;
      const auto &lookahead_symbols = pair.second;

      ParseItem::CompletionStatus status = item.completion_status();
      if (status.is_done) {
        ParseAction action;
        if (item.lhs() == rules::START()) {
          if (state_id == 1) {
            action = ParseAction::Accept();
          } else {
            continue;
          }
        } else {
          action = ParseAction::Reduce(Symbol(item.variable_index),
                                       item.step_index, status.precedence,
                                       status.associativity, *item.production);
        }

        for (const auto &lookahead_sym : *lookahead_symbols.entries)
          add_action(state_id, lookahead_sym, action, item_set);
      }
    }
  }

  void add_shift_extra_actions(ParseStateId state_id) {
    ParseAction action = ParseAction::ShiftExtra();
    for (const Symbol &extra_symbol : grammar.extra_tokens)
      add_action(state_id, extra_symbol, action, null_item_set);
  }

  void add_reduce_extra_actions(ParseStateId state_id) {
    const ParseState &state = parse_table.states[state_id];

    for (const Symbol &extra_symbol : grammar.extra_tokens) {
      const auto &actions_for_symbol = state.actions.find(extra_symbol);
      if (actions_for_symbol == state.actions.end())
        continue;

      for (const ParseAction &action : actions_for_symbol->second)
        if (action.type == ParseActionTypeShift && !action.extra) {
          size_t dest_state_id = action.state_index;
          ParseAction reduce_extra = ParseAction::ReduceExtra(extra_symbol);
          for (const auto &pair : state.actions)
            add_action(dest_state_id, pair.first, reduce_extra, null_item_set);
        }
    }
  }

  void mark_fragile_actions() {
    for (ParseState &state : parse_table.states) {
      set<Symbol> symbols_with_multiple_actions;

      for (auto &entry : state.actions) {
        if (entry.second.size() > 1)
          symbols_with_multiple_actions.insert(entry.first);

        for (ParseAction &action : entry.second) {
          if (action.type == ParseActionTypeReduce && !action.extra) {
            if (has_fragile_production(action.production))
              action.fragile = true;

            action.production = NULL;
            action.precedence_range = PrecedenceRange();
            action.associativity = rules::AssociativityNone;
          }
        }

        for (auto i = entry.second.begin(); i != entry.second.end();) {
          bool erased = false;
          for (auto j = entry.second.begin(); j != i; j++) {
            if (*j == *i) {
              entry.second.erase(i);
              erased = true;
              break;
            }
          }
          if (!erased)
            ++i;
        }
      }

      if (!symbols_with_multiple_actions.empty()) {
        for (auto &entry : state.actions) {
          if (!entry.first.is_token) {
            set<Symbol> first_set = get_first_set(entry.first);
            for (const Symbol &symbol : symbols_with_multiple_actions) {
              if (first_set.count(symbol)) {
                entry.second[0].can_hide_split = true;
                break;
              }
            }
          }
        }
      }
    }
  }

  void remove_duplicate_parse_states() {
    auto replacements =
      remove_duplicate_states<ParseState, ParseAction>(&parse_table.states);

    parse_table.error_state.each_advance_action(
      [&replacements](ParseAction *action) {
        auto replacement = replacements.find(action->state_index);
        if (replacement != replacements.end())
          action->state_index = replacement->second;
      });
  }

  ParseAction *add_action(ParseStateId state_id, Symbol lookahead,
                          const ParseAction &new_action,
                          const ParseItemSet &item_set) {
    const auto &current_actions = parse_table.states[state_id].actions;
    const auto &current_entry = current_actions.find(lookahead);
    if (current_entry == current_actions.end())
      return &parse_table.set_action(state_id, lookahead, new_action);
    if (allow_any_conflict)
      return &parse_table.add_action(state_id, lookahead, new_action);

    const ParseAction old_action = current_entry->second[0];
    auto resolution = conflict_manager.resolve(new_action, old_action);

    switch (resolution.second) {
      case ConflictTypeNone:
        if (resolution.first)
          return &parse_table.set_action(state_id, lookahead, new_action);
        break;

      case ConflictTypeResolved: {
        if (resolution.first) {
          if (old_action.type == ParseActionTypeReduce)
            fragile_productions.insert(old_action.production);
          return &parse_table.set_action(state_id, lookahead, new_action);
        } else {
          if (new_action.type == ParseActionTypeReduce)
            fragile_productions.insert(new_action.production);
          break;
        }
      }

      case ConflictTypeUnresolved: {
        if (handle_unresolved_conflict(item_set, lookahead)) {
          if (old_action.type == ParseActionTypeReduce)
            fragile_productions.insert(old_action.production);
          if (new_action.type == ParseActionTypeReduce)
            fragile_productions.insert(new_action.production);
          return &parse_table.add_action(state_id, lookahead, new_action);
        }
        break;
      }
    }

    return nullptr;
  }

  bool handle_unresolved_conflict(const ParseItemSet &item_set,
                                  const Symbol &lookahead) {
    set<Symbol> involved_symbols;
    set<ParseItem> reduce_items;
    set<ParseItem> core_shift_items;
    set<ParseItem> other_shift_items;

    for (const auto &pair : item_set.entries) {
      const ParseItem &item = pair.first;
      const LookaheadSet &lookahead_set = pair.second;

      Symbol next_symbol = item.next_symbol();
      if (next_symbol == rules::NONE()) {
        if (lookahead_set.contains(lookahead)) {
          involved_symbols.insert(item.lhs());
          reduce_items.insert(item);
        }
      } else {
        if (item.step_index > 0) {
          set<Symbol> first_set = get_first_set(next_symbol);
          if (first_set.count(lookahead)) {
            involved_symbols.insert(item.lhs());
            core_shift_items.insert(item);
          }
        } else if (next_symbol == lookahead) {
          other_shift_items.insert(item);
        }
      }
    }

    for (const auto &conflict_set : grammar.expected_conflicts)
      if (involved_symbols == conflict_set)
        return true;

    string description = "Lookahead symbol: " + symbol_name(lookahead) + "\n";

    if (!reduce_items.empty()) {
      description += "Reduce items:\n";
      for (const ParseItem &item : reduce_items)
        description += "  " + item_string(item) + "\n";
    }

    if (!core_shift_items.empty()) {
      description += "Core shift items:\n";
      for (const ParseItem &item : core_shift_items)
        description += "  " + item_string(item) + "\n";
    }

    if (!other_shift_items.empty()) {
      description += "Other shift items:\n";
      for (const ParseItem &item : other_shift_items)
        description += "  " + item_string(item) + "\n";
    }

    conflicts.insert(description);
    return false;
  }

  string item_string(const ParseItem &item) const {
    string result = symbol_name(item.lhs()) + " ->";
    size_t i = 0;
    for (const ProductionStep &step : *item.production) {
      if (i == item.step_index)
        result += " \u2022";
      result += " " + symbol_name(step.symbol);
      i++;
    }
    if (i == item.step_index)
      result += " \u2022";

    result += "   (prec " + to_string(item.precedence());

    switch (item.associativity()) {
      case rules::AssociativityNone:
        result += ")";
        break;
      case rules::AssociativityLeft:
        result += ", assoc left)";
        break;
      case rules::AssociativityRight:
        result += ", assoc right)";
        break;
    }

    return result;
  }

  set<Symbol> get_first_set(const Symbol &start_symbol) {
    set<Symbol> result;
    vector<Symbol> symbols_to_process({ start_symbol });

    while (!symbols_to_process.empty()) {
      Symbol symbol = symbols_to_process.back();
      symbols_to_process.pop_back();
      if (result.insert(symbol).second)
        for (const Production &production : grammar.productions(symbol))
          if (!production.empty())
            symbols_to_process.push_back(production[0].symbol);
    }

    return result;
  }

  string symbol_name(const rules::Symbol &symbol) const {
    if (symbol.is_built_in()) {
      if (symbol == rules::END_OF_INPUT())
        return "END_OF_INPUT";
      else
        return "";
    } else if (symbol.is_token) {
      const Variable &variable = lexical_grammar.variables[symbol.index];
      if (variable.type == VariableTypeNamed)
        return variable.name;
      else
        return "'" + variable.name + "'";
    } else {
      return grammar.variables[symbol.index].name;
    }
  }

  bool has_fragile_production(const Production *production) {
    auto end = fragile_productions.end();
    return std::find(fragile_productions.begin(), end, production) != end;
  }
};

pair<ParseTable, CompileError> build_parse_table(
  const SyntaxGrammar &grammar, const LexicalGrammar &lex_grammar) {
  return ParseTableBuilder(grammar, lex_grammar).build();
}

}  // namespace build_tables
}  // namespace tree_sitter
<commit_msg>Include shift-extra actions alongside other actions in recovery states<commit_after>#include "compiler/build_tables/build_parse_table.h"
#include <algorithm>
#include <map>
#include <set>
#include <string>
#include <unordered_map>
#include <utility>
#include "compiler/parse_table.h"
#include "compiler/build_tables/parse_conflict_manager.h"
#include "compiler/build_tables/remove_duplicate_states.h"
#include "compiler/build_tables/parse_item.h"
#include "compiler/build_tables/item_set_closure.h"
#include "compiler/lexical_grammar.h"
#include "compiler/syntax_grammar.h"
#include "compiler/rules/symbol.h"
#include "compiler/rules/built_in_symbols.h"
#include "compiler/build_tables/recovery_tokens.h"

namespace tree_sitter {
namespace build_tables {

using std::find;
using std::pair;
using std::vector;
using std::set;
using std::map;
using std::string;
using std::to_string;
using std::unordered_map;
using std::make_shared;
using rules::Symbol;

class ParseTableBuilder {
  const SyntaxGrammar grammar;
  const LexicalGrammar lexical_grammar;
  ParseConflictManager conflict_manager;
  unordered_map<Symbol, ParseItemSet> recovery_states;
  unordered_map<ParseItemSet, ParseStateId, ParseItemSet::Hash> parse_state_ids;
  vector<pair<ParseItemSet, ParseStateId>> item_sets_to_process;
  ParseTable parse_table;
  std::set<string> conflicts;
  ParseItemSet null_item_set;
  std::set<const Production *> fragile_productions;
  bool allow_any_conflict;

 public:
  ParseTableBuilder(const SyntaxGrammar &grammar,
                    const LexicalGrammar &lex_grammar)
      : grammar(grammar),
        lexical_grammar(lex_grammar),
        allow_any_conflict(false) {}

  pair<ParseTable, CompileError> build() {
    Symbol start_symbol = Symbol(0, grammar.variables.empty());
    Production start_production({
      ProductionStep(start_symbol, 0, rules::AssociativityNone),
    });

    add_parse_state(ParseItemSet({
      {
        ParseItem(rules::START(), start_production, 0),
        LookaheadSet({ rules::END_OF_INPUT() }),
      },
    }));

    CompileError error = process_part_state_queue();
    if (error.type != TSCompileErrorTypeNone)
      return { parse_table, error };

    add_out_of_context_parse_states();

    allow_any_conflict = true;
    process_part_state_queue();
    allow_any_conflict = false;

    for (ParseStateId state = 0; state < parse_table.states.size(); state++)
      add_reduce_extra_actions(state);

    mark_fragile_actions();
    remove_duplicate_parse_states();

    return { parse_table, CompileError::none() };
  }

 private:
  CompileError process_part_state_queue() {
    while (!item_sets_to_process.empty()) {
      auto pair = item_sets_to_process.back();
      ParseItemSet item_set = item_set_closure(pair.first, grammar);

      ParseStateId state_id = pair.second;
      item_sets_to_process.pop_back();

      add_reduce_actions(item_set, state_id);
      add_shift_actions(item_set, state_id);
      add_shift_extra_actions(state_id);

      if (!conflicts.empty()) {
        return CompileError(TSCompileErrorTypeParseConflict,
                            "Unresolved conflict.\n\n" + *conflicts.begin());
      }
    }

    return CompileError::none();
  }

  void add_out_of_context_parse_states() {
    for (const Symbol &symbol : recovery_tokens(lexical_grammar)) {
      add_out_of_context_parse_state(symbol);
    }

    for (size_t i = 0; i < grammar.variables.size(); i++) {
      Symbol symbol(i, false);
      add_out_of_context_parse_state(symbol);
    }

    parse_table.error_state.actions[rules::END_OF_INPUT()].push_back(
      ParseAction::Shift(0, PrecedenceRange()));
  }

  void add_out_of_context_parse_state(const rules::Symbol &symbol) {
    const ParseItemSet &item_set = recovery_states[symbol];
    if (!item_set.entries.empty()) {
      ParseStateId state = add_parse_state(item_set);
      parse_table.error_state.actions[symbol].push_back(
        ParseAction::Shift(state, PrecedenceRange()));
    }
  }

  ParseStateId add_parse_state(const ParseItemSet &item_set) {
    auto pair = parse_state_ids.find(item_set);
    if (pair == parse_state_ids.end()) {
      ParseStateId state_id = parse_table.add_state();

      parse_state_ids[item_set] = state_id;
      item_sets_to_process.push_back({ item_set, state_id });
      return state_id;
    } else {
      return pair->second;
    }
  }

  void add_shift_actions(const ParseItemSet &item_set, ParseStateId state_id) {
    for (const auto &transition : item_set.transitions()) {
      const Symbol &symbol = transition.first;
      const ParseItemSet &next_item_set = transition.second.first;
      const PrecedenceRange &precedence = transition.second.second;

      ParseAction *new_action = add_action(
        state_id, symbol, ParseAction::Shift(0, precedence), item_set);

      if (!allow_any_conflict)
        recovery_states[symbol].add(next_item_set);

      if (new_action)
        new_action->state_index = add_parse_state(next_item_set);
    }
  }

  void add_reduce_actions(const ParseItemSet &item_set, ParseStateId state_id) {
    for (const auto &pair : item_set.entries) {
      const ParseItem &item = pair.first;
      const auto &lookahead_symbols = pair.second;

      ParseItem::CompletionStatus status = item.completion_status();
      if (status.is_done) {
        ParseAction action;
        if (item.lhs() == rules::START()) {
          if (state_id == 1) {
            action = ParseAction::Accept();
          } else {
            continue;
          }
        } else {
          action = ParseAction::Reduce(Symbol(item.variable_index),
                                       item.step_index, status.precedence,
                                       status.associativity, *item.production);
        }

        for (const auto &lookahead_sym : *lookahead_symbols.entries)
          add_action(state_id, lookahead_sym, action, item_set);
      }
    }
  }

  void add_shift_extra_actions(ParseStateId state_id) {
    ParseAction action = ParseAction::ShiftExtra();
    for (const Symbol &extra_symbol : grammar.extra_tokens)
      add_action(state_id, extra_symbol, action, null_item_set);
  }

  void add_reduce_extra_actions(ParseStateId state_id) {
    const ParseState &state = parse_table.states[state_id];

    for (const Symbol &extra_symbol : grammar.extra_tokens) {
      const auto &actions_for_symbol = state.actions.find(extra_symbol);
      if (actions_for_symbol == state.actions.end())
        continue;

      for (const ParseAction &action : actions_for_symbol->second)
        if (action.type == ParseActionTypeShift && !action.extra) {
          size_t dest_state_id = action.state_index;
          ParseAction reduce_extra = ParseAction::ReduceExtra(extra_symbol);
          for (const auto &pair : state.actions)
            add_action(dest_state_id, pair.first, reduce_extra, null_item_set);
        }
    }
  }

  void mark_fragile_actions() {
    for (ParseState &state : parse_table.states) {
      set<Symbol> symbols_with_multiple_actions;

      for (auto &entry : state.actions) {
        if (entry.second.size() > 1)
          symbols_with_multiple_actions.insert(entry.first);

        for (ParseAction &action : entry.second) {
          if (action.type == ParseActionTypeReduce && !action.extra) {
            if (has_fragile_production(action.production))
              action.fragile = true;

            action.production = NULL;
            action.precedence_range = PrecedenceRange();
            action.associativity = rules::AssociativityNone;
          }
        }

        for (auto i = entry.second.begin(); i != entry.second.end();) {
          bool erased = false;
          for (auto j = entry.second.begin(); j != i; j++) {
            if (*j == *i) {
              entry.second.erase(i);
              erased = true;
              break;
            }
          }
          if (!erased)
            ++i;
        }
      }

      if (!symbols_with_multiple_actions.empty()) {
        for (auto &entry : state.actions) {
          if (!entry.first.is_token) {
            set<Symbol> first_set = get_first_set(entry.first);
            for (const Symbol &symbol : symbols_with_multiple_actions) {
              if (first_set.count(symbol)) {
                entry.second[0].can_hide_split = true;
                break;
              }
            }
          }
        }
      }
    }
  }

  void remove_duplicate_parse_states() {
    auto replacements =
      remove_duplicate_states<ParseState, ParseAction>(&parse_table.states);

    parse_table.error_state.each_advance_action(
      [&replacements](ParseAction *action) {
        auto replacement = replacements.find(action->state_index);
        if (replacement != replacements.end())
          action->state_index = replacement->second;
      });
  }

  ParseAction *add_action(ParseStateId state_id, Symbol lookahead,
                          const ParseAction &new_action,
                          const ParseItemSet &item_set) {
    const auto &current_actions = parse_table.states[state_id].actions;
    const auto &current_entry = current_actions.find(lookahead);
    if (current_entry == current_actions.end())
      return &parse_table.set_action(state_id, lookahead, new_action);
    if (allow_any_conflict)
      return &parse_table.add_action(state_id, lookahead, new_action);

    const ParseAction old_action = current_entry->second[0];
    auto resolution = conflict_manager.resolve(new_action, old_action);

    switch (resolution.second) {
      case ConflictTypeNone:
        if (resolution.first)
          return &parse_table.set_action(state_id, lookahead, new_action);
        break;

      case ConflictTypeResolved: {
        if (resolution.first) {
          if (old_action.type == ParseActionTypeReduce)
            fragile_productions.insert(old_action.production);
          return &parse_table.set_action(state_id, lookahead, new_action);
        } else {
          if (new_action.type == ParseActionTypeReduce)
            fragile_productions.insert(new_action.production);
          break;
        }
      }

      case ConflictTypeUnresolved: {
        if (handle_unresolved_conflict(item_set, lookahead)) {
          if (old_action.type == ParseActionTypeReduce)
            fragile_productions.insert(old_action.production);
          if (new_action.type == ParseActionTypeReduce)
            fragile_productions.insert(new_action.production);
          return &parse_table.add_action(state_id, lookahead, new_action);
        }
        break;
      }
    }

    return nullptr;
  }

  bool handle_unresolved_conflict(const ParseItemSet &item_set,
                                  const Symbol &lookahead) {
    set<Symbol> involved_symbols;
    set<ParseItem> reduce_items;
    set<ParseItem> core_shift_items;
    set<ParseItem> other_shift_items;

    for (const auto &pair : item_set.entries) {
      const ParseItem &item = pair.first;
      const LookaheadSet &lookahead_set = pair.second;

      Symbol next_symbol = item.next_symbol();
      if (next_symbol == rules::NONE()) {
        if (lookahead_set.contains(lookahead)) {
          involved_symbols.insert(item.lhs());
          reduce_items.insert(item);
        }
      } else {
        if (item.step_index > 0) {
          set<Symbol> first_set = get_first_set(next_symbol);
          if (first_set.count(lookahead)) {
            involved_symbols.insert(item.lhs());
            core_shift_items.insert(item);
          }
        } else if (next_symbol == lookahead) {
          other_shift_items.insert(item);
        }
      }
    }

    for (const auto &conflict_set : grammar.expected_conflicts)
      if (involved_symbols == conflict_set)
        return true;

    string description = "Lookahead symbol: " + symbol_name(lookahead) + "\n";

    if (!reduce_items.empty()) {
      description += "Reduce items:\n";
      for (const ParseItem &item : reduce_items)
        description += "  " + item_string(item) + "\n";
    }

    if (!core_shift_items.empty()) {
      description += "Core shift items:\n";
      for (const ParseItem &item : core_shift_items)
        description += "  " + item_string(item) + "\n";
    }

    if (!other_shift_items.empty()) {
      description += "Other shift items:\n";
      for (const ParseItem &item : other_shift_items)
        description += "  " + item_string(item) + "\n";
    }

    conflicts.insert(description);
    return false;
  }

  string item_string(const ParseItem &item) const {
    string result = symbol_name(item.lhs()) + " ->";
    size_t i = 0;
    for (const ProductionStep &step : *item.production) {
      if (i == item.step_index)
        result += " \u2022";
      result += " " + symbol_name(step.symbol);
      i++;
    }
    if (i == item.step_index)
      result += " \u2022";

    result += "   (prec " + to_string(item.precedence());

    switch (item.associativity()) {
      case rules::AssociativityNone:
        result += ")";
        break;
      case rules::AssociativityLeft:
        result += ", assoc left)";
        break;
      case rules::AssociativityRight:
        result += ", assoc right)";
        break;
    }

    return result;
  }

  set<Symbol> get_first_set(const Symbol &start_symbol) {
    set<Symbol> result;
    vector<Symbol> symbols_to_process({ start_symbol });

    while (!symbols_to_process.empty()) {
      Symbol symbol = symbols_to_process.back();
      symbols_to_process.pop_back();
      if (result.insert(symbol).second)
        for (const Production &production : grammar.productions(symbol))
          if (!production.empty())
            symbols_to_process.push_back(production[0].symbol);
    }

    return result;
  }

  string symbol_name(const rules::Symbol &symbol) const {
    if (symbol.is_built_in()) {
      if (symbol == rules::END_OF_INPUT())
        return "END_OF_INPUT";
      else
        return "";
    } else if (symbol.is_token) {
      const Variable &variable = lexical_grammar.variables[symbol.index];
      if (variable.type == VariableTypeNamed)
        return variable.name;
      else
        return "'" + variable.name + "'";
    } else {
      return grammar.variables[symbol.index].name;
    }
  }

  bool has_fragile_production(const Production *production) {
    auto end = fragile_productions.end();
    return std::find(fragile_productions.begin(), end, production) != end;
  }
};

pair<ParseTable, CompileError> build_parse_table(
  const SyntaxGrammar &grammar, const LexicalGrammar &lex_grammar) {
  return ParseTableBuilder(grammar, lex_grammar).build();
}

}  // namespace build_tables
}  // namespace tree_sitter
<|endoftext|>


<commit_before>#include "hopsanc.h"
#include <iostream>

#include "HopsanCore.h"
#include "HopsanEssentials.h"
#include "ComponentSystem.h"

static hopsan::ComponentSystem *spCoreComponentSystem = nullptr;
static hopsan::HopsanEssentials gHopsanCore;

static double startTime, stopTime;

//! @brief Prints all waiting messages
//! @param[in] printDebug Should debug messages also be printed
void printWaitingMessages(hopsan::HopsanEssentials& hopsanCore, bool printDebug, bool silent)
{
    if(silent) return;

    hopsan::HString msg, type, tag;
    while (hopsanCore.checkMessage() > 0) {
        hopsanCore.getMessage(msg,type,tag);
        if (type == "debug") {
            if (printDebug) {
                std::cout << msg.c_str() << "\n";
            }
        }
        else {
            std::cout << msg.c_str() << "\n";
        }
    }
}

int loadModel(const char* path) {
    spCoreComponentSystem = gHopsanCore.loadHMFModelFile(path, startTime, stopTime);
    if(!spCoreComponentSystem) {
        std::cout << "Failed to instantiate model!\n";
        printWaitingMessages(gHopsanCore, false, false);
        return -1;
    }
    const hopsan::HString modelName = spCoreComponentSystem->getName();
    spCoreComponentSystem->addSearchPath(modelName+"-resources");
    std::cout << "Loaded model: " << modelName.c_str() << "\n";
    printWaitingMessages(gHopsanCore, false, false);
    return 0;
}

//! @brief Returns specified data vector as C array
//! @param [in] variable Variable name ("component.port.variable")
//! @param [out] size Size of returned data array
//! @returns Pointer to data array
double *getDataVector(const char* variable, int &size)
{
    if(!spCoreComponentSystem) {
        std::cout << "No model is loaded.\n";
        size = -1;
        return nullptr;
    }

    //Parse variable string
    hopsan::HString varStr(variable);
    hopsan::HVector<hopsan::HString>splitVar = varStr.split('.');
    if(splitVar.size() < 3) {
        std::cout << "Component name, port name and variable name must be specified.\n";
        size = -1;
        return nullptr;
    }

    //Find component
    hopsan::Component *pComp = spCoreComponentSystem->getSubComponent(splitVar[0]);
    if(!pComp) {
        std::cout << "No such component: " << splitVar[0].c_str() << "\n";
        size = -1;
        return nullptr;
    }

    //Find port
    hopsan::Port *pPort = pComp->getPort(splitVar[1]);
    if(!pPort) {
        std::cout << "No such port: " << splitVar[1].c_str() << "\n";
        size = -1;
        return nullptr;
    }

    int varId = pPort->getNodeDataIdFromName(splitVar[2]);
    if(varId < 0) {
        std::cout << "No such variable: " << splitVar[2].c_str() << "\n";
        size = -1;
        return nullptr;
    }

    size = int(spCoreComponentSystem->getNumActuallyLoggedSamples());
    double *data = new double[size];
    std::vector< std::vector<double> > *pLogData = pPort->getLogDataVectorPtr();
    for (size_t t=0; t<spCoreComponentSystem->getNumActuallyLoggedSamples(); ++t) {
        data[t] = (*pLogData)[t][size_t(varId)];
    }

    return data;
}

int loadLibrary(const char *path)
{
    if(!gHopsanCore.loadExternalComponentLib(path)) {
        printWaitingMessages(gHopsanCore, false, false);
        return -1;
    };
    printWaitingMessages(gHopsanCore, false, false);
    return 0;
}

int setStartTime(double value)
{
    startTime = value;
    return 0;
}

int setTimeStep(double value)
{
    if(!spCoreComponentSystem) {
        std::cout << "Error: No model is loaded!\n";
        return -1;
    }
    spCoreComponentSystem->setDesiredTimestep(value);
    return 0;
}

int setStopTime(double value)
{
    stopTime = value;
    return 0;
}

int simulate()
{
    if(!spCoreComponentSystem) {
        std::cout << "Error: No model is loaded!\n";
        return -1;
    }
    std::cout << "Checking model... ";
    if (spCoreComponentSystem->checkModelBeforeSimulation()) {
        std::cout << "Success!\n";
    }
    else {
        std::cout << "Failed!\n";
        printWaitingMessages(gHopsanCore, false, false);
        return -1;
    }

    std::cout << "Initializing model... ";
    if(spCoreComponentSystem->initialize(startTime, stopTime)) {
        std::cout << "Success!\n";
    }
    else {
        std::cout << "Failed!\n";
        printWaitingMessages(gHopsanCore, false, false);
        return -1;
    }
    printWaitingMessages(gHopsanCore, false, false);

    std::cout << "Simulating... ";
    spCoreComponentSystem->simulate(stopTime);
    std::cout << "Finished!\n";

    printWaitingMessages(gHopsanCore, false, false);
    return 0;
}

double *getTimeVector(int &size)
{
    if(!spCoreComponentSystem) {
        size = -1;
        return nullptr;
    }
    size = int(spCoreComponentSystem->getNumActuallyLoggedSamples());
    return spCoreComponentSystem->getLogTimeVector()->data();
}

int setParameter(const char *name, const char *value)
{
    if(!spCoreComponentSystem) {
        std::cout << "No model is loaded.\n";
        return -1;
    }

    hopsan::HString nameStr(name);
    hopsan::HVector<hopsan::HString> nameVec = nameStr.split('.');

    if(nameVec.size() == 1) {
        if(spCoreComponentSystem->setParameterValue(nameVec[0], hopsan::HString(value))) {
            return 0;
        }
        else {
            std::cout << "Failed to set parameter value: " << nameVec[0].c_str() << "\n";
            return -1;
        }
    }
    else if(nameVec.size() == 2) {
        hopsan::Component *pComp = spCoreComponentSystem->getSubComponent(nameVec[0]);
        if(!pComp) {
            std::cout << "No such component: " << nameVec[0].c_str() << "\n";
            return -1;
        }
        if(pComp->setParameterValue(nameVec[1], hopsan::HString(value))) {
            return 0;
        }
        else {
            std::cout << "Failed to set parameter value: " << nameVec[1].c_str() << "\n";
            return -1;
        }
    }

    std::cout << "Wrong number of arguments.\n";
    return -1;
}

int setLogSamples(unsigned long value)
{
    if(!spCoreComponentSystem) {
        std::cout << "Error: No model is loaded.\n";
        return -1;
    }
    std::cout << "Setting samples to " << size_t(value) << "\n";
    spCoreComponentSystem->setNumLogSamples(value);
    return 0;
}
<commit_msg>Support subsystems in C wrapper<commit_after>#include "hopsanc.h"
#include <iostream>

#include "HopsanCore.h"
#include "HopsanEssentials.h"
#include "ComponentSystem.h"

static hopsan::ComponentSystem *spCoreComponentSystem = nullptr;
static hopsan::HopsanEssentials gHopsanCore;

static double startTime, stopTime;

//! @brief Prints all waiting messages
//! @param[in] printDebug Should debug messages also be printed
void printWaitingMessages(hopsan::HopsanEssentials& hopsanCore, bool printDebug, bool silent)
{
    if(silent) return;

    hopsan::HString msg, type, tag;
    while (hopsanCore.checkMessage() > 0) {
        hopsanCore.getMessage(msg,type,tag);
        if (type == "debug") {
            if (printDebug) {
                std::cout << msg.c_str() << "\n";
            }
        }
        else {
            std::cout << msg.c_str() << "\n";
        }
    }
}

int loadModel(const char* path) {
    spCoreComponentSystem = gHopsanCore.loadHMFModelFile(path, startTime, stopTime);
    if(!spCoreComponentSystem) {
        std::cout << "Failed to instantiate model!\n";
        printWaitingMessages(gHopsanCore, false, false);
        return -1;
    }
    const hopsan::HString modelName = spCoreComponentSystem->getName();
    spCoreComponentSystem->addSearchPath(modelName+"-resources");
    std::cout << "Loaded model: " << modelName.c_str() << "\n";
    printWaitingMessages(gHopsanCore, false, false);
    return 0;
}

//! @brief Returns specified data vector as C array
//! @param [in] variable Variable name ("component.port.variable")
//! @param [out] size Size of returned data array
//! @returns Pointer to data array
double *getDataVector(const char* variable, int &size)
{
    if(!spCoreComponentSystem) {
        std::cout << "No model is loaded.\n";
        size = -1;
        return nullptr;
    }

    //Parse variable string
    hopsan::HString varStr(variable);
    hopsan::HVector<hopsan::HString> splitSys = varStr.split('|');
    hopsan::HVector<hopsan::HString> splitVar = splitSys.last().split('.');
    splitSys.resize(splitSys.size()-1);
    if(splitVar.size() < 3) {
        std::cout << "Component name, port name and variable name must be specified.\n";
        size = -1;
        return nullptr;
    }

    //Find system
    hopsan::ComponentSystem *pSystem = spCoreComponentSystem;
    for(size_t i=0; i<splitSys.size(); ++i) {
        pSystem = pSystem->getSubComponentSystem(splitSys[i]);
        if(!pSystem) {
            std::cout << "Error: Subsystem not found: " << splitSys[i].c_str() << "\n";
            size = -1;
            return nullptr;
        }
    }

    //Find component
    hopsan::Component *pComp = pSystem->getSubComponent(splitVar[0]);
    if(!pComp) {
        std::cout << "No such component: " << splitVar[0].c_str() << "\n";
        size = -1;
        return nullptr;
    }

    //Find port
    hopsan::Port *pPort = pComp->getPort(splitVar[1]);
    if(!pPort) {
        std::cout << "No such port: " << splitVar[1].c_str() << "\n";
        size = -1;
        return nullptr;
    }

    int varId = pPort->getNodeDataIdFromName(splitVar[2]);
    if(varId < 0) {
        std::cout << "No such variable: " << splitVar[2].c_str() << "\n";
        size = -1;
        return nullptr;
    }

    size = int(spCoreComponentSystem->getNumActuallyLoggedSamples());
    double *data = new double[size];
    std::vector< std::vector<double> > *pLogData = pPort->getLogDataVectorPtr();
    for (size_t t=0; t<spCoreComponentSystem->getNumActuallyLoggedSamples(); ++t) {
        data[t] = (*pLogData)[t][size_t(varId)];
    }

    return data;
}

int loadLibrary(const char *path)
{
    if(!gHopsanCore.loadExternalComponentLib(path)) {
        printWaitingMessages(gHopsanCore, false, false);
        return -1;
    };
    printWaitingMessages(gHopsanCore, false, false);
    return 0;
}

int setStartTime(double value)
{
    startTime = value;
    return 0;
}

int setTimeStep(double value)
{
    if(!spCoreComponentSystem) {
        std::cout << "Error: No model is loaded!\n";
        return -1;
    }
    spCoreComponentSystem->setDesiredTimestep(value);
    return 0;
}

int setStopTime(double value)
{
    stopTime = value;
    return 0;
}

int simulate()
{
    if(!spCoreComponentSystem) {
        std::cout << "Error: No model is loaded!\n";
        return -1;
    }
    std::cout << "Checking model... ";
    if (spCoreComponentSystem->checkModelBeforeSimulation()) {
        std::cout << "Success!\n";
    }
    else {
        std::cout << "Failed!\n";
        printWaitingMessages(gHopsanCore, false, false);
        return -1;
    }

    std::cout << "Initializing model... ";
    if(spCoreComponentSystem->initialize(startTime, stopTime)) {
        std::cout << "Success!\n";
    }
    else {
        std::cout << "Failed!\n";
        printWaitingMessages(gHopsanCore, false, false);
        return -1;
    }
    printWaitingMessages(gHopsanCore, false, false);

    std::cout << "Simulating... ";
    spCoreComponentSystem->simulate(stopTime);
    std::cout << "Finished!\n";

    printWaitingMessages(gHopsanCore, false, false);
    return 0;
}

double *getTimeVector(int &size)
{
    if(!spCoreComponentSystem) {
        size = -1;
        return nullptr;
    }
    size = int(spCoreComponentSystem->getNumActuallyLoggedSamples());
    return spCoreComponentSystem->getLogTimeVector()->data();
}

int setParameter(const char *name, const char *value)
{
    if(!spCoreComponentSystem) {
        std::cout << "No model is loaded.\n";
        return -1;
    }

    //Parse arguments
    hopsan::HString nameStr(name);
    hopsan::HVector<hopsan::HString> sysVec = nameStr.split('|');
    hopsan::HVector<hopsan::HString> nameVec = sysVec.last().split('.');
    sysVec.resize(sysVec.size()-1);

    //Generate component name and parameter name
    hopsan::HString compName, parName;
    if(nameVec.size() == 1) {   //System parameter
        compName = "";
        parName = nameVec[0];
    }
    else if(nameVec.size() == 2) { //Constant
        compName = nameVec[0];
        parName = nameVec[1];
    }
    else if(nameVec.size() == 3) { //Input variable
        compName = nameVec[0];
        parName = nameVec[1]+"#"+nameVec[2];
    }
    else {
        std::cout << "Error: Parameter name not specified.\n";
        return -1;
    }

    //Find system
    hopsan::ComponentSystem *pSystem = spCoreComponentSystem;
    for(size_t i=0; i<sysVec.size(); ++i) {
        pSystem = pSystem->getSubComponentSystem(sysVec[i]);
        if(!pSystem) {
            std::cout << "Error: Subsystem not found: " << sysVec[i].c_str() << "\n";
            return -1;
        }
    }

    if(compName.empty()) {   //Set system parameter
        if(pSystem->setParameterValue(parName, hopsan::HString(value))) {
            return 0;
        }
        else {
            std::cout << "Error: Failed to set parameter value: " << parName.c_str() << "\n";
            return -1;
        }
    }
    else if(nameVec.size() == 2 || nameVec.size() == 3) { //Set constant or input variable
        hopsan::Component *pComp = pSystem->getSubComponent(compName);
        if(!pComp) {
            std::cout << "Error: No such component: " << compName.c_str() << "\n";
            return -1;
        }
        if(pComp->setParameterValue(parName, hopsan::HString(value))) {
            return 0;
        }
        else {
            std::cout << "Error: Failed to set parameter value: " << parName.c_str() << "\n";
            return -1;
        }
    }

    std::cout << "Wrong number of arguments.\n";
    return -1;
}

int setLogSamples(unsigned long value)
{
    if(!spCoreComponentSystem) {
        std::cout << "Error: No model is loaded.\n";
        return -1;
    }
    std::cout << "Setting samples to " << size_t(value) << "\n";
    spCoreComponentSystem->setNumLogSamples(value);
    return 0;
}
<|endoftext|>


<commit_before>#include "interface.h"
#include "def.h"
#include "CoordSimilarity.h"
#include "TimeSimilarity.h"
#include "param.h"

#include <string>
#include <time.h>
#include <windows.h>
#include <vector>
#include <iostream>
#include <chrono>
#include <functional>
#include <sstream>

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem.hpp>

using namespace std;

//#define UNICODE

vector<string> csvname;
Boost_Sock bm(IP, REQUESTPORT, nullptr, "Request Sock");
Boost_Sock bmpop(IP, PARAMPORT, paramop_coord, "Param Sock");

std::string ws2s(const std::wstring& ws)
{
	std::string curLocale = setlocale(LC_ALL, NULL);        // curLocale = "C";
	setlocale(LC_ALL, "chs");
	const wchar_t* _Source = ws.c_str();
	size_t _Dsize = 2 * ws.size() + 1;
	char *_Dest = new char[_Dsize];
	memset(_Dest, 0, _Dsize);
	wcstombs(_Dest, _Source, _Dsize);
	std::string result = _Dest;
	delete[] _Dest;
	setlocale(LC_ALL, curLocale.c_str());
	return result;
}

std::wstring s2ws(const std::string& s)
{
	setlocale(LC_ALL, "chs");
	const char* _Source = s.c_str();
	size_t _Dsize = s.size() + 1;
	wchar_t *_Dest = new wchar_t[_Dsize];
	wmemset(_Dest, 0, _Dsize);
	mbstowcs(_Dest, _Source, _Dsize);
	std::wstring result = _Dest;
	delete[] _Dest;
	setlocale(LC_ALL, "C");
	return result;
}

vector<Point> read_csv(wstring path) {
	int record_count = 0;
	HANDLE hfile = CreateFile(path.c_str(), GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL);
	DWORD dwFileSize = GetFileSize(hfile, NULL);
	HANDLE hmapping = CreateFileMapping(hfile, 0, PAGE_READONLY, 0, 0, 0);
	LPVOID buffer_void = MapViewOfFile(hmapping, FILE_MAP_READ, 0, 0, 0);
	// Attetion to that not all csv are UNICODE, in our case, they are ANSI
	PCHAR abuf = (PCHAR)buffer_void;
	//setlocale(LC_ALL, "");

	vector<Point> vp;
	PCHAR rows = abuf, cols = abuf;
	bool beheaded = false;
	double x = 0, y = 0;
	for (DWORD offset = 0; offset < dwFileSize; offset++)
	{
		if (*abuf == '\r') {

		}
		else if (*abuf == '\n') {
			if (beheaded) {
				y = atof(string(cols, abuf - 1).c_str());
				// add point
				vp.push_back(Point(x, y));
			}
			// renew rows and cols
			rows = abuf + 1;
			cols = abuf + 1;
			beheaded = true;
		}
		else if (beheaded && *abuf == ',') {
			x = atof(string(cols, abuf).c_str());
			// renew cols
			cols = abuf + 1;
		}
		abuf++;
	}
	UnmapViewOfFile(buffer_void);
	CloseHandle(hmapping);
	CloseHandle(hfile);
	return vp;
}

vector<TPoint> read_csv_time(wstring path) {
	int record_count = 0;
	HANDLE hfile = CreateFile(path.c_str(), GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL);
	DWORD dwFileSize = GetFileSize(hfile, NULL);
	HANDLE hmapping = CreateFileMapping(hfile, 0, PAGE_READONLY, 0, 0, 0);
	LPVOID buffer_void = MapViewOfFile(hmapping, FILE_MAP_READ, 0, 0, 0);
	// Attetion to that not all csv are UNICODE, in our case, they are ANSI
	PCHAR abuf = (PCHAR)buffer_void;

	vector<TPoint> vp;
	PCHAR rows = abuf, cols = abuf;
	bool beheaded = false;
	double x = 0, y = 0;
	unsigned long long t = 0;
	int state = 0;
	for (DWORD offset = 0; offset < dwFileSize; offset++)
	{
		if (*abuf == '\r') {

		}
		else if (*abuf == '\n') {
			if (beheaded) {
				tm tmtime; tmtime.tm_sec = 0; tmtime.tm_isdst = -1;
				sscanf(cols, "%04d/%02d/%02d %02d:%02d\r", &tmtime.tm_year, &tmtime.tm_mon, &tmtime.tm_mday, &tmtime.tm_hour, &tmtime.tm_min);
				tmtime.tm_year -= 1900;
				t = mktime(&tmtime);
				// add point
				vp.push_back(TPoint(x, y, t));
			}
			// renew rows and cols
			rows = abuf + 1;
			cols = abuf + 1;
			// renew state
			state = 0;
			beheaded = true;
		}
		else if (beheaded && *abuf == ',') {
			if (state == 0)
				x = atof(string(cols, abuf).c_str());
			else if (state == 1)
				y = atof(string(cols, abuf).c_str());
			// renew cols
			cols = abuf + 1;
			// renew state
			state++;
		}
		abuf++;
	}
	UnmapViewOfFile(buffer_void);
	CloseHandle(hmapping);
	CloseHandle(hfile);
	return vp;
}

void get_all_csv(string fullpath = "../../case/origin") {
	namespace fs = boost::filesystem;
	fs::path full_path(fullpath, fs::native);
	if (!fs::exists(fullpath)) {
		return;
	}
	fs::directory_iterator end_iter;
	for (fs::directory_iterator iter(fullpath); iter != end_iter; iter++) {
		try {

			if (fs::extension(*iter) == ".csv") {
				csvname.push_back(iter->path().filename().string());
				//csvname.push_back(iter->path().string());
			}
		}
		catch (const std::exception & ex) {
			std::cerr << ex.what() << std::endl;
			continue;
		}
	}
}

string find_coord(string tracename) {
	using namespace std;
	vector<vector<Point>> tofind;
	vector<Point> trace;
	if (csvname.empty())
	{
		get_all_csv();
	}
	for (int i = 0; i < csvname.size(); i++)
	{
		if (tracename == csvname[i])
		{
			trace = read_csv(s2ws(csvname[i]));
		}
		else {
			tofind.push_back(read_csv(s2ws(csvname[i])));
		}
	}
	CoordSimilarityList coord_list = CoordSort(trace, tofind);
	string res1;
	for (int i = 0;i < min(5, (int)coord_list.similarities.size());i++) {
		res1 += tracename;
		int j, k = coord_list.similarities[i].first;
		for (j = 0;j < coord_list.trace_sections.size();j++) {
			if (coord_list.trace_sections[j].index == k) break;
		}
		if (j < coord_list.trace_sections.size()) {
			res1 += '&';
			string tmp;
			stringstream ss;
			ss << coord_list.trace_sections[j].t1_begin;
			ss >> tmp;
			res1 += tmp;
			res1 += '!';
			ss << coord_list.trace_sections[j].t1_end;
			ss >> tmp;
			res1 += tmp;
			res1 += '*';
			res1 += csvname[k];
			res1 += '&';
			ss << coord_list.trace_sections[j].t2_begin;
			ss >> tmp;
			res1 += tmp;
			res1 += '!';
			ss << coord_list.trace_sections[j].t2_end;
			ss >> tmp;
			res1 += tmp;
		}
	}
	return res1;
}

TimeSimilarityList find_time(string tracename) {
	using namespace std;
	vector<vector<TPoint>> tofind;
	vector<TPoint> trace;
	if (csvname.empty())
	{
		get_all_csv();
	}
	for (int i = 0; i < csvname.size(); i++)
	{
		if (tracename == csvname[i])
		{
			trace = read_csv_time(s2ws(csvname[i]));
		}
		else {
			tofind.push_back(read_csv_time(s2ws(csvname[i])));
		}
	}
	return TimeSort(trace, tofind);
}

string cmp_coord(string tracename1, string tracename2) {
	using namespace std;
	vector<Point> trace1, trace2;
	trace1 = read_csv(s2ws(tracename1));
	trace2 = read_csv(s2ws(tracename2));
	CoordSimilarity coord_simi = CoordCompare(trace1, trace2);
	string res1 = tracename1;
	res1 += '&';
	string tmp;
	stringstream ss;
	ss << coord_simi.trace_sections[0].t1_begin;
	ss >> tmp;
	res1 += tmp;
	res1 += '!';
	ss << coord_simi.trace_sections[0].t1_end;
	ss >> tmp;
	res1 += tmp;
	res1 += '*';
	res1 += tracename2;
	res1 += '&';
	ss << coord_simi.trace_sections[0].t2_begin;
	ss >> tmp;
	res1 += tmp;
	res1 += '!';
	ss << coord_simi.trace_sections[0].t2_end;
	ss >> tmp;
	res1 += tmp;
	return res1;
}

TimeSimilarity cmp_time(string tracename1, string tracename2) {
	using namespace std;
	vector<TPoint> trace1, trace2;
	trace1 = read_csv_time(s2ws(tracename1));
	trace2 = read_csv_time(s2ws(tracename2));
	return TimeCompare(trace1, trace2);
}

/*
	Flow Line
	* get_all_csv
	find_coord/find_time
	result_encode(+4)
	xxx_return
*/

int wmain(int argc, TCHAR* argv[], TCHAR* env[]) {
	//return_by_socket();
	//paramop_return();
	//get_all_csv();

	//vector<Point> trace_coord[4];
	//vector<TPoint> trace_time[4];

	//for (int i = 0;i < 4;i++) {
	//	wstring path = L"../../case/origin/";
	//	wstring patht = L"../../case/origin/";
	//	path += (i + '0');
	//	path += L".csv";
	//	patht += (i + '0');
	//	patht += L"t.csv";
	//	//cout << elapse_time(read_csv, path) << endl;
	//	trace_coord[i] = read_csv(path);
	//	trace_time[i] = read_csv_time(patht);
	//}

	cout << cmp_coord("../../case/origin/1.csv", "../../case/oringin/2.csv") << endl;

	//printf("Sort Test");
	//std::vector< std::vector<Point> > traces;
	//for (int i = 0;i < 3;i++) traces.push_back(trace_coord[i]);
	//CoordSimilarityList coordlist = CoordSort(trace_coord[3], traces);
	//for (int i = 0;i < coordlist.similarities.size();i++) {
	//	cout << coordlist.similarities[i].first << " ";
	//	printf("%.2f%%\n", coordlist.similarities[i].second * 100);
	//}
	//puts("");
	//for (int i = 0;i < coordlist.trace_sections.size();i++) {
	//	cout << "mine between ";
	//	cout << coordlist.trace_sections[i].t1_begin << " ";
	//	cout << coordlist.trace_sections[i].t1_end << " ";
	//	cout << "with trace " << coordlist.trace_sections[i].index << " between ";
	//	cout << coordlist.trace_sections[i].t2_begin << " ";
	//	cout << coordlist.trace_sections[i].t2_end << " ";
	//	printf("%.2f%%\n", coordlist.trace_sections[i].coord_sim * 100);
	//}
	//puts("");

	cout << "׼" << endl;
	bmpop.msg_loop();
	bm.msg_loop();

	system("pause");
	return 0;
}<commit_msg>update<commit_after>#include "interface.h"
#include "def.h"
#include "CoordSimilarity.h"
#include "TimeSimilarity.h"
#include "param.h"

#include <string>
#include <time.h>
#include <windows.h>
#include <vector>
#include <iostream>
#include <chrono>
#include <functional>
#include <sstream>

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem.hpp>

using namespace std;

//#define UNICODE

vector<string> csvname;
Boost_Sock bm(IP, REQUESTPORT, nullptr, "Request Sock");
Boost_Sock bmpop(IP, PARAMPORT, paramop_coord, "Param Sock");

std::string ws2s(const std::wstring& ws)
{
	std::string curLocale = setlocale(LC_ALL, NULL);        // curLocale = "C";
	setlocale(LC_ALL, "chs");
	const wchar_t* _Source = ws.c_str();
	size_t _Dsize = 2 * ws.size() + 1;
	char *_Dest = new char[_Dsize];
	memset(_Dest, 0, _Dsize);
	wcstombs(_Dest, _Source, _Dsize);
	std::string result = _Dest;
	delete[] _Dest;
	setlocale(LC_ALL, curLocale.c_str());
	return result;
}

std::wstring s2ws(const std::string& s)
{
	setlocale(LC_ALL, "chs");
	const char* _Source = s.c_str();
	size_t _Dsize = s.size() + 1;
	wchar_t *_Dest = new wchar_t[_Dsize];
	wmemset(_Dest, 0, _Dsize);
	mbstowcs(_Dest, _Source, _Dsize);
	std::wstring result = _Dest;
	delete[] _Dest;
	setlocale(LC_ALL, "C");
	return result;
}

vector<Point> read_csv(wstring path) {
	int record_count = 0;
	HANDLE hfile = CreateFile(path.c_str(), GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL);
	DWORD dwFileSize = GetFileSize(hfile, NULL);
	HANDLE hmapping = CreateFileMapping(hfile, 0, PAGE_READONLY, 0, 0, 0);
	LPVOID buffer_void = MapViewOfFile(hmapping, FILE_MAP_READ, 0, 0, 0);
	// Attetion to that not all csv are UNICODE, in our case, they are ANSI
	PCHAR abuf = (PCHAR)buffer_void;
	//setlocale(LC_ALL, "");

	vector<Point> vp;
	PCHAR rows = abuf, cols = abuf;
	bool beheaded = false;
	double x = 0, y = 0;
	for (DWORD offset = 0; offset < dwFileSize; offset++)
	{
		if (*abuf == '\r') {

		}
		else if (*abuf == '\n') {
			if (beheaded) {
				y = atof(string(cols, abuf - 1).c_str());
				// add point
				vp.push_back(Point(x, y));
			}
			// renew rows and cols
			rows = abuf + 1;
			cols = abuf + 1;
			beheaded = true;
		}
		else if (beheaded && *abuf == ',') {
			x = atof(string(cols, abuf).c_str());
			// renew cols
			cols = abuf + 1;
		}
		abuf++;
	}
	UnmapViewOfFile(buffer_void);
	CloseHandle(hmapping);
	CloseHandle(hfile);
	return vp;
}

vector<TPoint> read_csv_time(wstring path) {
	int record_count = 0;
	HANDLE hfile = CreateFile(path.c_str(), GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL);
	DWORD dwFileSize = GetFileSize(hfile, NULL);
	HANDLE hmapping = CreateFileMapping(hfile, 0, PAGE_READONLY, 0, 0, 0);
	LPVOID buffer_void = MapViewOfFile(hmapping, FILE_MAP_READ, 0, 0, 0);
	// Attetion to that not all csv are UNICODE, in our case, they are ANSI
	PCHAR abuf = (PCHAR)buffer_void;

	vector<TPoint> vp;
	PCHAR rows = abuf, cols = abuf;
	bool beheaded = false;
	double x = 0, y = 0;
	unsigned long long t = 0;
	int state = 0;
	for (DWORD offset = 0; offset < dwFileSize; offset++)
	{
		if (*abuf == '\r') {

		}
		else if (*abuf == '\n') {
			if (beheaded) {
				tm tmtime; tmtime.tm_sec = 0; tmtime.tm_isdst = -1;
				sscanf(cols, "%04d/%02d/%02d %02d:%02d\r", &tmtime.tm_year, &tmtime.tm_mon, &tmtime.tm_mday, &tmtime.tm_hour, &tmtime.tm_min);
				tmtime.tm_year -= 1900;
				t = mktime(&tmtime);
				// add point
				vp.push_back(TPoint(x, y, t));
			}
			// renew rows and cols
			rows = abuf + 1;
			cols = abuf + 1;
			// renew state
			state = 0;
			beheaded = true;
		}
		else if (beheaded && *abuf == ',') {
			if (state == 0)
				x = atof(string(cols, abuf).c_str());
			else if (state == 1)
				y = atof(string(cols, abuf).c_str());
			// renew cols
			cols = abuf + 1;
			// renew state
			state++;
		}
		abuf++;
	}
	UnmapViewOfFile(buffer_void);
	CloseHandle(hmapping);
	CloseHandle(hfile);
	return vp;
}

void get_all_csv(string fullpath = "../../case/origin") {
	namespace fs = boost::filesystem;
	fs::path full_path(fullpath, fs::native);
	if (!fs::exists(fullpath)) {
		return;
	}
	fs::directory_iterator end_iter;
	for (fs::directory_iterator iter(fullpath); iter != end_iter; iter++) {
		try {

			if (fs::extension(*iter) == ".csv") {
				csvname.push_back(iter->path().filename().string());
				//csvname.push_back(iter->path().string());
			}
		}
		catch (const std::exception & ex) {
			std::cerr << ex.what() << std::endl;
			continue;
		}
	}
}

string find_coord(string tracename) {
	using namespace std;
	vector<vector<Point>> tofind;
	vector<Point> trace;
	if (csvname.empty()) {
		get_all_csv();
	}
	for (int i = 0; i < csvname.size(); i++) {
		if (tracename == csvname[i]) {
			trace = read_csv(s2ws(csvname[i]));
		}
		else {
			tofind.push_back(read_csv(s2ws(csvname[i])));
		}
	}
	CoordSimilarityList coord_list = CoordSort(trace, tofind);
	string res1;
	for (int i = 0;i < min(5, (int)coord_list.similarities.size());i++) {
		if (i > 0) res1 += ':';
		res1 += tracename;
		int j, k = coord_list.similarities[i].first;
		for (j = 0;j < coord_list.trace_sections.size();j++) {
			if (coord_list.trace_sections[j].index == k) break;
		}
		if (j < coord_list.trace_sections.size()) {
			res1 += '&';
			string tmp;
			stringstream ss;
			ss.clear();
			ss << coord_list.trace_sections[j].t1_begin;
			ss >> tmp;
			res1 += tmp + '!';
			ss.clear();
			ss << coord_list.trace_sections[j].t1_end;
			ss >> tmp;
			res1 += tmp + '*' + csvname[k] + '&';
			ss.clear();
			ss << coord_list.trace_sections[j].t2_begin;
			ss >> tmp;
			res1 += tmp + '!';
			ss.clear();
			ss << coord_list.trace_sections[j].t2_end;
			ss >> tmp;
			res1 += tmp + '*';
			ss.clear();
			ss << coord_list.similarities[i].second;
			ss >> tmp;
			res1 += tmp;
		}
	}
	return res1;
}

TimeSimilarityList find_time(string tracename) {
	using namespace std;
	vector<vector<TPoint>> tofind;
	vector<TPoint> trace;
	if (csvname.empty())
	{
		get_all_csv();
	}
	for (int i = 0; i < csvname.size(); i++)
	{
		if (tracename == csvname[i])
		{
			trace = read_csv_time(s2ws(csvname[i]));
		}
		else {
			tofind.push_back(read_csv_time(s2ws(csvname[i])));
		}
	}
	return TimeSort(trace, tofind);
}

string cmp_coord(string tracename1, string tracename2) {
	using namespace std;
	vector<Point> trace1, trace2;
	trace1 = read_csv(s2ws(tracename1));
	trace2 = read_csv(s2ws(tracename2));
	CoordSimilarity coord_simi = CoordCompare(trace1, trace2);
	string res1 = tracename1;
	res1 += '&';
	string tmp;
	stringstream ss;
	ss.clear();
	ss << coord_simi.trace_sections[0].t1_begin;
	ss >> tmp;
	res1 += tmp + '!';
	ss.clear();
	ss << coord_simi.trace_sections[0].t1_end;
	ss >> tmp;
	res1 += tmp + '*' + tracename2 + '&';
	ss.clear();
	ss << coord_simi.trace_sections[0].t2_begin;
	ss >> tmp;
	res1 += tmp + '!';
	ss.clear();
	ss << coord_simi.trace_sections[0].t2_end;
	ss >> tmp;
	res1 += tmp;
	return res1;
}

TimeSimilarity cmp_time(string tracename1, string tracename2) {
	using namespace std;
	vector<TPoint> trace1, trace2;
	trace1 = read_csv_time(s2ws(tracename1));
	trace2 = read_csv_time(s2ws(tracename2));
	return TimeCompare(trace1, trace2);
}

/*
	Flow Line
	* get_all_csv
	find_coord/find_time
	result_encode(+4)
	xxx_return
*/

int wmain(int argc, TCHAR* argv[], TCHAR* env[]) {
	//return_by_socket();
	//paramop_return();
	//get_all_csv();

	vector<Point> trace_coord[4];
	//vector<TPoint> trace_time[4];

	//for (int i = 0;i < 4;i++) {
	//	wstring path = L"../../case/origin/";
	//	wstring patht = L"../../case/origin/";
	//	path += (i + '0');
	//	path += L".csv";
	//	patht += (i + '0');
	//	patht += L"t.csv";
	//	//cout << elapse_time(read_csv, path) << endl;
	//	trace_coord[i] = read_csv(path);
	//	trace_time[i] = read_csv_time(patht);
	//}

	string s1 = "../../case/origin/1.csv";
	string s2 = "../../case/origin/2.csv";
	cout << cmp_coord(s1, s2) << endl;
	//cout << find_coord("1.csv") << endl;

	//printf("Sort Test");
	//std::vector< std::vector<Point> > traces;
	//for (int i = 0;i < 3;i++) traces.push_back(trace_coord[i]);
	//CoordSimilarityList coordlist = CoordSort(trace_coord[3], traces);
	//for (int i = 0;i < coordlist.similarities.size();i++) {
	//	cout << coordlist.similarities[i].first << " ";
	//	printf("%.2f%%\n", coordlist.similarities[i].second * 100);
	//}
	//puts("");
	//for (int i = 0;i < coordlist.trace_sections.size();i++) {
	//	cout << "mine between ";
	//	cout << coordlist.trace_sections[i].t1_begin << " ";
	//	cout << coordlist.trace_sections[i].t1_end << " ";
	//	cout << "with trace " << coordlist.trace_sections[i].index << " between ";
	//	cout << coordlist.trace_sections[i].t2_begin << " ";
	//	cout << coordlist.trace_sections[i].t2_end << " ";
	//	printf("%.2f%%\n", coordlist.trace_sections[i].coord_sim * 100);
	//}
	//puts("");

	cout << "׼" << endl;
	bmpop.msg_loop();
	bm.msg_loop();

	system("pause");
	return 0;
}<|endoftext|>


<commit_before>#include "compiler/prepare_grammar/extract_tokens.h"
#include <map>
#include <vector>
#include <set>
#include <string>
#include "tree_sitter/compiler.h"
#include "compiler/prepared_grammar.h"
#include "compiler/rules/visitor.h"
#include "compiler/rules/symbol.h"
#include "compiler/rules/string.h"
#include "compiler/rules/metadata.h"
#include "compiler/rules/pattern.h"
#include "compiler/prepare_grammar/token_description.h"
#include "compiler/prepare_grammar/is_token.h"

namespace tree_sitter {
namespace prepare_grammar {

using std::pair;
using std::tuple;
using std::string;
using std::map;
using std::to_string;
using std::vector;
using std::set;
using std::make_shared;
using std::make_tuple;
using std::dynamic_pointer_cast;
using rules::rule_ptr;
using rules::Symbol;
using rules::SymbolOptionToken;
using rules::SymbolOptionAuxToken;

class UsedSymbols : public rules::IdentityRuleFn {
  set<Symbol> used_symbols_;

  rules::rule_ptr apply(rules::Symbol *sym) {
    used_symbols_.insert(*sym);
    return sym->copy();
  }
};

class SymbolInliner : public rules::IdentityRuleFn {
  map<Symbol, Symbol> replacements;
  using rules::IdentityRuleFn::apply_to;

  int new_index_for_symbol(const Symbol &symbol) {
    int result = symbol.index;
    for (const auto &pair : replacements)
      if (pair.first.index < symbol.index &&
          pair.first.is_auxiliary() == symbol.is_auxiliary())
        result--;
    return result;
  }

  rule_ptr apply_to(const Symbol *rule) { return replace_symbol(*rule).copy(); }

 public:
  Symbol replace_symbol(const Symbol &rule) {
    if (rule.is_built_in())
      return rule;
    auto replacement_pair = replacements.find(rule);
    if (replacement_pair != replacements.end())
      return replacement_pair->second;
    else
      return Symbol(new_index_for_symbol(rule), rule.options);
  }

  explicit SymbolInliner(const map<Symbol, Symbol> &replacements)
      : replacements(replacements) {}
};

class TokenExtractor : public rules::IdentityRuleFn {

  rule_ptr apply_to_token(const rules::Rule *input) {
    auto rule = input->copy();
    for (size_t i = 0; i < tokens.size(); i++)
      if (tokens[i].second->operator==(*rule))
        return make_shared<Symbol>(i, SymbolOptionAuxToken);
    size_t index = tokens.size();
    tokens.push_back({ token_description(rule), rule });
    return make_shared<Symbol>(index, SymbolOptionAuxToken);
  }

  rule_ptr default_apply(const rules::Rule *rule) {
    auto result = rule->copy();
    if (is_token(rule->copy())) {
      return apply_to_token(rule);
    } else {
      return result;
    }
  }

  rule_ptr apply_to(const rules::Metadata *rule) {
    auto result = rule->copy();
    if (is_token(rule->copy())) {
      return apply_to_token(rule);
    } else {
      return rules::IdentityRuleFn::apply_to(rule);
    }
  }

 public:
  vector<pair<string, rule_ptr>> tokens;
};

static const GrammarError *ubiq_token_err(const string &msg) {
  return new GrammarError(GrammarErrorTypeInvalidUbiquitousToken, msg);
}

tuple<SyntaxGrammar, LexicalGrammar, const GrammarError *> extract_tokens(
    const Grammar &grammar) {
  vector<pair<string, rule_ptr>> rules, tokens, aux_rules, aux_tokens;
  vector<rule_ptr> separators;
  set<Symbol> ubiquitous_tokens;
  map<Symbol, Symbol> symbol_replacements;

  TokenExtractor extractor;

  for (size_t i = 0; i < grammar.rules().size(); i++) {
    auto pair = grammar.rules()[i];
    if (is_token(pair.second)) {
      tokens.push_back(pair);
      symbol_replacements.insert(
          { Symbol(i), Symbol(tokens.size() - 1, SymbolOptionToken) });
    } else {
      rules.push_back({ pair.first, extractor.apply(pair.second) });
    }
  }

  aux_tokens.insert(aux_tokens.end(), extractor.tokens.begin(),
                    extractor.tokens.end());

  SymbolInliner inliner(symbol_replacements);
  for (auto &pair : rules)
    pair.second = inliner.apply(pair.second);

  for (auto rule : grammar.ubiquitous_tokens()) {
    if (is_token(rule)) {
      separators.push_back(rule);
    } else {
      auto sym = dynamic_pointer_cast<const Symbol>(extractor.apply(rule));
      if (!sym.get())
        return make_tuple(SyntaxGrammar(), LexicalGrammar(),
                 ubiq_token_err("Not a token: " + rule->to_string()));

      Symbol symbol = inliner.replace_symbol(*sym);
      if (!symbol.is_token())
        return make_tuple(SyntaxGrammar(), LexicalGrammar(),
                 ubiq_token_err("Not a token: " + symbol.to_string()));

      ubiquitous_tokens.insert(symbol);
    }
  }

  return make_tuple(SyntaxGrammar(rules, aux_rules, ubiquitous_tokens),
           LexicalGrammar(tokens, aux_tokens, separators), nullptr);
}

}  // namespace prepare_grammar
}  // namespace tree_sitter
<commit_msg>Remove unused code in extract_tokens.cc<commit_after>#include "compiler/prepare_grammar/extract_tokens.h"
#include <map>
#include <vector>
#include <set>
#include <string>
#include "tree_sitter/compiler.h"
#include "compiler/prepared_grammar.h"
#include "compiler/rules/visitor.h"
#include "compiler/rules/symbol.h"
#include "compiler/rules/string.h"
#include "compiler/rules/metadata.h"
#include "compiler/rules/pattern.h"
#include "compiler/prepare_grammar/token_description.h"
#include "compiler/prepare_grammar/is_token.h"

namespace tree_sitter {
namespace prepare_grammar {

using std::dynamic_pointer_cast;
using std::make_shared;
using std::make_tuple;
using std::map;
using std::pair;
using std::set;
using std::string;
using std::tuple;
using std::vector;
using rules::rule_ptr;
using rules::Symbol;
using rules::SymbolOptionToken;
using rules::SymbolOptionAuxToken;

class SymbolInliner : public rules::IdentityRuleFn {
  map<Symbol, Symbol> replacements;
  using rules::IdentityRuleFn::apply_to;

  int new_index_for_symbol(const Symbol &symbol) {
    int result = symbol.index;
    for (const auto &pair : replacements)
      if (pair.first.index < symbol.index &&
          pair.first.is_auxiliary() == symbol.is_auxiliary())
        result--;
    return result;
  }

  rule_ptr apply_to(const Symbol *rule) { return replace_symbol(*rule).copy(); }

 public:
  Symbol replace_symbol(const Symbol &rule) {
    if (rule.is_built_in())
      return rule;
    auto replacement_pair = replacements.find(rule);
    if (replacement_pair != replacements.end())
      return replacement_pair->second;
    else
      return Symbol(new_index_for_symbol(rule), rule.options);
  }

  explicit SymbolInliner(const map<Symbol, Symbol> &replacements)
      : replacements(replacements) {}
};

class TokenExtractor : public rules::IdentityRuleFn {
  rule_ptr apply_to_token(const rules::Rule *input) {
    auto rule = input->copy();
    for (size_t i = 0; i < tokens.size(); i++)
      if (tokens[i].second->operator==(*rule))
        return make_shared<Symbol>(i, SymbolOptionAuxToken);
    size_t index = tokens.size();
    tokens.push_back({ token_description(rule), rule });
    return make_shared<Symbol>(index, SymbolOptionAuxToken);
  }

  rule_ptr default_apply(const rules::Rule *rule) {
    auto result = rule->copy();
    if (is_token(rule->copy())) {
      return apply_to_token(rule);
    } else {
      return result;
    }
  }

  rule_ptr apply_to(const rules::Metadata *rule) {
    auto result = rule->copy();
    if (is_token(rule->copy())) {
      return apply_to_token(rule);
    } else {
      return rules::IdentityRuleFn::apply_to(rule);
    }
  }

 public:
  vector<pair<string, rule_ptr>> tokens;
};

static const GrammarError *ubiq_token_err(const string &msg) {
  return new GrammarError(GrammarErrorTypeInvalidUbiquitousToken, msg);
}

tuple<SyntaxGrammar, LexicalGrammar, const GrammarError *> extract_tokens(
    const Grammar &grammar) {
  vector<pair<string, rule_ptr>> rules, tokens, aux_rules, aux_tokens;
  vector<rule_ptr> separators;
  set<Symbol> ubiquitous_tokens;
  map<Symbol, Symbol> symbol_replacements;

  TokenExtractor extractor;

  for (size_t i = 0; i < grammar.rules().size(); i++) {
    auto pair = grammar.rules()[i];
    if (is_token(pair.second)) {
      tokens.push_back(pair);
      symbol_replacements.insert(
          { Symbol(i), Symbol(tokens.size() - 1, SymbolOptionToken) });
    } else {
      rules.push_back({ pair.first, extractor.apply(pair.second) });
    }
  }

  aux_tokens.insert(aux_tokens.end(), extractor.tokens.begin(),
                    extractor.tokens.end());

  SymbolInliner inliner(symbol_replacements);
  for (auto &pair : rules)
    pair.second = inliner.apply(pair.second);

  for (auto rule : grammar.ubiquitous_tokens()) {
    if (is_token(rule)) {
      separators.push_back(rule);
    } else {
      auto sym = dynamic_pointer_cast<const Symbol>(extractor.apply(rule));
      if (!sym.get())
        return make_tuple(SyntaxGrammar(), LexicalGrammar(),
                 ubiq_token_err("Not a token: " + rule->to_string()));

      Symbol symbol = inliner.replace_symbol(*sym);
      if (!symbol.is_token())
        return make_tuple(SyntaxGrammar(), LexicalGrammar(),
                 ubiq_token_err("Not a token: " + symbol.to_string()));

      ubiquitous_tokens.insert(symbol);
    }
  }

  return make_tuple(SyntaxGrammar(rules, aux_rules, ubiquitous_tokens),
           LexicalGrammar(tokens, aux_tokens, separators), nullptr);
}

}  // namespace prepare_grammar
}  // namespace tree_sitter
<|endoftext|>