#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