#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