Newer
Older
libj / modules / x-tokenizer / src / string_util.cpp
#include <sstream>
#include <array>
#include <unordered_map>

#include <string_util.hpp>

namespace xtokenizer
{

    // エスケープマップを生成
    constexpr std::array<char, 256> createEscapeMap()
    {
        std::array<char, 256> map = {'\0'};
        map['a'] = '\a';
        map['b'] = '\b';
        map['f'] = '\f';
        map['n'] = '\n';
        map['r'] = '\r';
        map['t'] = '\t';
        map['v'] = '\v';
        map['"'] = '"';
        map['\''] = '\'';
        map['\\'] = '\\';
        map['?'] = '?';
        map['0'] = '\0';
        return map;
    }

    // ESCAPE のマップ
    constexpr std::array<char, 256> ESCAPE_MAP = createEscapeMap();

    /**
     * 指定された文字をエスケープ文字に変換します。
     *
     * @param c エスケープ文字の文字シンボル (例 '\n' の場合、 'n' を指定する)
     * @return エスケープされた文字 (例 'n' が指定された場合、'\n' を返す)
     */
    constexpr char toEscape(char c)
    {
        return ESCAPE_MAP[static_cast<unsigned char>(c)];
    }

    /**
     * 文字列 str の offset 位置からの文字列が、prefix で始まるか否かを返します。
     *
     * @param str 文字列
     * @param prefix プレフィックス
     * @param offset オフセット
     * @return prefix で始まる場合、true
     */
    bool startsWith(const std::string &str, const char *prefix, int offset)
    {
        int prefixLen = std::strlen(prefix);
        if ((static_cast<int>(str.size()) < prefixLen + offset) || (offset < 0))
        {
            return false;
        }
        return std::equal(prefix, prefix + prefixLen, str.begin() + offset);
    }

    /**
     * 文字列 str の offset 位置からの文字列が、prefix で始まるか否かを返します。
     *
     * @param str 文字列
     * @param prefix プレフィックス
     * @param offset オフセット
     * @return prefix で始まる場合、true
     */
    bool startsWith(const std::string &str, const std::string &prefix, int offset)
    {
        if ((str.size() < prefix.size() + offset) || (offset < 0))
        {
            return false;
        }
        return std::equal(prefix.begin(), prefix.end(), str.begin() + offset);
    }

    /**
     * 指定された文字列中の中で neelde が現れる位置を検索します。
     *
     * @param str 文字列
     * @param needle 検索文字列
     * @param offset オフセット
     * @return needle が現れる位置
     */
    int indexOf(const std::string &str, const std::string &needle, int offset)
    {
        if (str.length() == 0)
        {
            return offset;
        }
        std::size_t pos = str.find(needle, offset);
        if (pos != std::string::npos)
        {
            return static_cast<int>(pos);
        }
        return -1;
    }

    /**
     * 指定された文字列中の中で neelde が現れる位置を検索します。
     * エスケープされている文字は除外されます。
     *
     * @param str 文字列
     * @param needle 検索文字列
     * @param offset オフセット
     * @return needle が現れる位置
     */
    int unescapedIndexOf(const std::string &str, const std::string &needle, int offset)
    {
        bool inEscape = false;
        size_t needleLen = needle.length();

        for (size_t idx = offset; idx < str.length(); idx++)
        {
            if (str[idx] == '\\')
            {
                inEscape = !inEscape;
            }
            else
            {
                if ((str.substr(idx, needleLen) == needle) && !inEscape)
                {
                    return idx;
                }
                inEscape = false;
            }
        }
        return -1;
    }

    /**
     * 指定された文字列の offset より length 長分をエスケープされた文字列に変換します。
     *
     * @param str 変換する文字列
     * @param offset オフセット
     * @param length 長さ
     * @return エスケープされた文字列
     */
    std::string escapedString(const std::string &str, int offset, int length)
    {
        std::string result;
        result.reserve(str.size());

        int end = std::min(static_cast<int>(str.length()), offset + length);
        for (int idx = offset; idx < end; idx++)
        {
            if ((str[idx] == '\\') && (idx + 1) < end)
            {
                char escapedChar = toEscape(str[idx + 1]);
                if (escapedChar != '\0')
                {
                    result.push_back(escapedChar);
                    idx += 1;
                }
                else if ((str[idx + 1] == 'x') && ((idx + 3) < end) && std::isdigit(str[idx + 2]) && std::isdigit(str[idx + 3]))
                {
                    std::stringstream ss;
                    ss << std::hex << str.substr(idx + 2, 2);
                    int hexValue;
                    ss >> hexValue;
                    result.push_back(static_cast<char>(hexValue));
                    idx += 3;
                }
                else
                {
                    result.push_back(str[idx]);
                }
            }
            else
            {
                result.push_back(str[idx]);
            }
        }
        return result;
    }

} // namespace kscript