Merge pull request #1004 from melex750/dev-cbot

Escape codes for strings in CBOT
1008-fix
krzys_h 2017-10-22 18:20:50 +02:00 committed by GitHub
commit 9448f6712f
14 changed files with 471 additions and 29 deletions

View File

@ -1770,6 +1770,24 @@ msgstr ""
msgid "This parameter needs a default value" msgid "This parameter needs a default value"
msgstr "" msgstr ""
msgid "Missing end quote"
msgstr ""
msgid "Unknown escape sequence"
msgstr ""
msgid "Octal value out of range"
msgstr ""
msgid "Missing hex digits after escape sequence"
msgstr ""
msgid "Hex value out of range"
msgstr ""
msgid "Invalid universal character name"
msgstr ""
msgid "Dividing by zero" msgid "Dividing by zero"
msgstr "" msgstr ""

View File

@ -680,6 +680,9 @@ msgstr "Anweisungen über das ausgewählte Objekt"
msgid "Help balloons\\Explain the function of the buttons" msgid "Help balloons\\Explain the function of the buttons"
msgstr "Hilfeblasen\\Hilfeblasen" msgstr "Hilfeblasen\\Hilfeblasen"
msgid "Hex value out of range"
msgstr ""
#, fuzzy #, fuzzy
msgid "Higher speed\\Doubles speed" msgid "Higher speed\\Doubles speed"
msgstr "Geschwindigkeit 2.0x\\Spielgeschwindigkeit doppelt so schnell" msgstr "Geschwindigkeit 2.0x\\Spielgeschwindigkeit doppelt so schnell"
@ -759,6 +762,9 @@ msgstr "Anweisungen\\Anweisungen für die Mission oder Übung"
msgid "Internal error - tell the developers" msgid "Internal error - tell the developers"
msgstr "Interner Fehler - Benachrichtige bitte die Entwickler" msgstr "Interner Fehler - Benachrichtige bitte die Entwickler"
msgid "Invalid universal character name"
msgstr ""
msgid "Invert\\Invert values on this axis" msgid "Invert\\Invert values on this axis"
msgstr "Invertieren\\Die Werte dieser Achse invertieren" msgstr "Invertieren\\Die Werte dieser Achse invertieren"
@ -869,6 +875,12 @@ msgstr "Verkleinern"
msgid "Mipmap level\\Mipmap level" msgid "Mipmap level\\Mipmap level"
msgstr "Mipmap-Level\\Mipmap-Level" msgstr "Mipmap-Level\\Mipmap-Level"
msgid "Missing end quote"
msgstr ""
msgid "Missing hex digits after escape sequence"
msgstr ""
msgid "Mission name" msgid "Mission name"
msgstr "Name der Mission" msgstr "Name der Mission"
@ -1031,6 +1043,9 @@ msgstr "OK\\Programm kompilieren"
msgid "Object too close" msgid "Object too close"
msgstr "Gegenstand zu nahe" msgstr "Gegenstand zu nahe"
msgid "Octal value out of range"
msgstr ""
msgid "One step" msgid "One step"
msgstr "Ein Schritt" msgstr "Ein Schritt"
@ -1632,6 +1647,9 @@ msgstr "Das Objekt existiert nicht"
msgid "Unknown command" msgid "Unknown command"
msgstr "Befehl unbekannt" msgstr "Befehl unbekannt"
msgid "Unknown escape sequence"
msgstr ""
msgid "Unknown function" msgid "Unknown function"
msgstr "Unbekannte Funktion" msgstr "Unbekannte Funktion"

View File

@ -677,6 +677,9 @@ msgstr "Instructions sur la sélection"
msgid "Help balloons\\Explain the function of the buttons" msgid "Help balloons\\Explain the function of the buttons"
msgstr "Bulles d'aide\\Bulles explicatives" msgstr "Bulles d'aide\\Bulles explicatives"
msgid "Hex value out of range"
msgstr ""
#, fuzzy #, fuzzy
msgid "Higher speed\\Doubles speed" msgid "Higher speed\\Doubles speed"
msgstr "Vitesse 2.0x\\Deux fois plus rapide" msgstr "Vitesse 2.0x\\Deux fois plus rapide"
@ -756,6 +759,9 @@ msgstr "Instructions mission\\Marche à suivre"
msgid "Internal error - tell the developers" msgid "Internal error - tell the developers"
msgstr "Erreur interne - contacter les développeurs" msgstr "Erreur interne - contacter les développeurs"
msgid "Invalid universal character name"
msgstr ""
msgid "Invert\\Invert values on this axis" msgid "Invert\\Invert values on this axis"
msgstr "Inversion\\Inverse les valeurs sur cet axe" msgstr "Inversion\\Inverse les valeurs sur cet axe"
@ -866,6 +872,12 @@ msgstr "Taille réduite"
msgid "Mipmap level\\Mipmap level" msgid "Mipmap level\\Mipmap level"
msgstr "Niveau de MIP mapping\\Niveau de MIP mapping" msgstr "Niveau de MIP mapping\\Niveau de MIP mapping"
msgid "Missing end quote"
msgstr ""
msgid "Missing hex digits after escape sequence"
msgstr ""
msgid "Mission name" msgid "Mission name"
msgstr "Nom de la mission" msgstr "Nom de la mission"
@ -1028,6 +1040,9 @@ msgstr "D'accord\\Compiler le programme"
msgid "Object too close" msgid "Object too close"
msgstr "Objet trop proche" msgstr "Objet trop proche"
msgid "Octal value out of range"
msgstr ""
msgid "One step" msgid "One step"
msgstr "Un pas" msgstr "Un pas"
@ -1629,6 +1644,9 @@ msgstr "Objet n'existe pas"
msgid "Unknown command" msgid "Unknown command"
msgstr "Commande inconnue" msgstr "Commande inconnue"
msgid "Unknown escape sequence"
msgstr ""
msgid "Unknown function" msgid "Unknown function"
msgstr "Routine inconnue" msgstr "Routine inconnue"

View File

@ -678,6 +678,9 @@ msgstr "Pomoc na temat zaznaczonego obiektu"
msgid "Help balloons\\Explain the function of the buttons" msgid "Help balloons\\Explain the function of the buttons"
msgstr "Dymki pomocy\\Wyjaśnia funkcje przycisków" msgstr "Dymki pomocy\\Wyjaśnia funkcje przycisków"
msgid "Hex value out of range"
msgstr ""
msgid "Higher speed\\Doubles speed" msgid "Higher speed\\Doubles speed"
msgstr "Zwiększ prędkość\\Podwaja prędkość" msgstr "Zwiększ prędkość\\Podwaja prędkość"
@ -756,6 +759,9 @@ msgstr "Rozkazy\\Pokazuje rozkazy dotyczące bieżącej misji"
msgid "Internal error - tell the developers" msgid "Internal error - tell the developers"
msgstr "Błąd wewnętrzny - powiadom twórców gry" msgstr "Błąd wewnętrzny - powiadom twórców gry"
msgid "Invalid universal character name"
msgstr ""
msgid "Invert\\Invert values on this axis" msgid "Invert\\Invert values on this axis"
msgstr "Odwróć\\Odwróć wartości na tej osi" msgstr "Odwróć\\Odwróć wartości na tej osi"
@ -852,6 +858,12 @@ msgstr "Pomniejsz"
msgid "Mipmap level\\Mipmap level" msgid "Mipmap level\\Mipmap level"
msgstr "Poziom mipmap\\Poziom mipmap" msgstr "Poziom mipmap\\Poziom mipmap"
msgid "Missing end quote"
msgstr ""
msgid "Missing hex digits after escape sequence"
msgstr ""
msgid "Mission name" msgid "Mission name"
msgstr "Nazwa misji" msgstr "Nazwa misji"
@ -1014,6 +1026,9 @@ msgstr "OK\\Zamyka edytor programu i powraca do gry"
msgid "Object too close" msgid "Object too close"
msgstr "Obiekt za blisko" msgstr "Obiekt za blisko"
msgid "Octal value out of range"
msgstr ""
msgid "One step" msgid "One step"
msgstr "Jeden krok" msgstr "Jeden krok"
@ -1614,6 +1629,9 @@ msgstr "Obiekt nieznany"
msgid "Unknown command" msgid "Unknown command"
msgstr "Nieznane polecenie" msgstr "Nieznane polecenie"
msgid "Unknown escape sequence"
msgstr ""
msgid "Unknown function" msgid "Unknown function"
msgstr "Funkcja nieznana" msgstr "Funkcja nieznana"

View File

@ -686,6 +686,9 @@ msgstr "Справка о выбранном объекте"
msgid "Help balloons\\Explain the function of the buttons" msgid "Help balloons\\Explain the function of the buttons"
msgstr "Подсказки\\Объяснение функций кнопок" msgstr "Подсказки\\Объяснение функций кнопок"
msgid "Hex value out of range"
msgstr ""
#, fuzzy #, fuzzy
msgid "Higher speed\\Doubles speed" msgid "Higher speed\\Doubles speed"
msgstr "Скорость 2.0х\\В два раза быстрее" msgstr "Скорость 2.0х\\В два раза быстрее"
@ -765,6 +768,9 @@ msgstr "Инструкции\\Показывает инструкции по т
msgid "Internal error - tell the developers" msgid "Internal error - tell the developers"
msgstr "Внутренняя ошибка - сообщите разработчикам" msgstr "Внутренняя ошибка - сообщите разработчикам"
msgid "Invalid universal character name"
msgstr ""
msgid "Invert\\Invert values on this axis" msgid "Invert\\Invert values on this axis"
msgstr "Инвертир.\\Инвертировать значения по этой оси" msgstr "Инвертир.\\Инвертировать значения по этой оси"
@ -875,6 +881,12 @@ msgstr "Свернуть"
msgid "Mipmap level\\Mipmap level" msgid "Mipmap level\\Mipmap level"
msgstr "Уровень уменьшающей фильтрации\\Уровень уменьшающей фильтрации" msgstr "Уровень уменьшающей фильтрации\\Уровень уменьшающей фильтрации"
msgid "Missing end quote"
msgstr ""
msgid "Missing hex digits after escape sequence"
msgstr ""
msgid "Mission name" msgid "Mission name"
msgstr "Название миссии" msgstr "Название миссии"
@ -1039,6 +1051,9 @@ msgstr "ОК\\Закрыть редактор программ и вернуть
msgid "Object too close" msgid "Object too close"
msgstr "Объект слишком близок" msgstr "Объект слишком близок"
msgid "Octal value out of range"
msgstr ""
msgid "One step" msgid "One step"
msgstr "Один шаг" msgstr "Один шаг"
@ -1645,6 +1660,9 @@ msgstr "Неизвестный объект"
msgid "Unknown command" msgid "Unknown command"
msgstr "Неизвестная команда" msgstr "Неизвестная команда"
msgid "Unknown escape sequence"
msgstr ""
msgid "Unknown function" msgid "Unknown function"
msgstr "Неизвестная функция" msgstr "Неизвестная функция"

View File

@ -241,6 +241,12 @@ enum CBotError : int
CBotErrNoClassName = 5046, //!< class name expected CBotErrNoClassName = 5046, //!< class name expected
CBotErrNoReturn = 5047, //!< non-void function needs "return;" CBotErrNoReturn = 5047, //!< non-void function needs "return;"
CBotErrDefaultValue = 5048, //!< this parameter needs a default value CBotErrDefaultValue = 5048, //!< this parameter needs a default value
CBotErrEndQuote = 5049, //!< missing end quote
CBotErrBadEscape = 5050, //!< unknown escape sequence
CBotErrOctalRange = 5051, //!< octal value out of range
CBotErrHexDigits = 5052, //!< missing hex digits after escape sequence
CBotErrHexRange = 5053, //!< hex value out of range
CBotErrUnicodeName = 5054, //!< invalid universal character name
// Runtime errors // Runtime errors
CBotErrZeroDiv = 6000, //!< division by zero CBotErrZeroDiv = 6000, //!< division by zero

View File

@ -42,15 +42,136 @@ CBotInstr* CBotExprLitString::Compile(CBotToken* &p, CBotCStack* pStack)
{ {
CBotCStack* pStk = pStack->TokenStack(); CBotCStack* pStk = pStack->TokenStack();
CBotExprLitString* inst = new CBotExprLitString(); std::string s = p->GetString();
inst->SetToken(p); auto it = s.cbegin();
p = p->GetNext(); if (++it != s.cend())
{
int pos = p->GetStart();
std::string valstring = "";
while (it != s.cend() && *it != '\"')
{
pStk->SetStartError(++pos);
if (*it != '\\') // not escape sequence ?
{
valstring += *(it++);
continue;
}
CBotVar* var = CBotVar::Create("", CBotTypString); if (++it == s.cend()) break;
pStk->SetVar(var);
return pStack->Return(inst, pStk); if (CharInList(*it, "01234567")) // octal
{
std::string octal = "";
for (int i = 0; i < 3; i++)
{
if (!CharInList(*it, "01234567")) break;
++pos;
octal += *it;
if (++it == s.cend()) break;
}
unsigned int val = std::stoi(octal, nullptr, 8);
if (val <= 255)
{
valstring.push_back(val);
continue;
}
pStk->SetError(CBotErrOctalRange, pos + 1);
}
else
{
++pos;
unsigned char c = *(it++);
if (c == '\"' || c == '\'' || c == '\\') valstring += c;
else if (c == 'a') valstring += '\a'; // alert bell
else if (c == 'b') valstring += '\b'; // backspace
else if (c == 'f') valstring += '\f'; // form feed
else if (c == 'n') valstring += '\n'; // new line
else if (c == 'r') valstring += '\r'; // carriage return
else if (c == 't') valstring += '\t'; // horizontal tab
else if (c == 'v') valstring += '\v'; // vertical tab
else if (c == 'x' || c == 'u' || c == 'U') // hex or unicode
{
if (it != s.cend())
{
std::string hex = "";
bool isHexCode = (c == 'x');
size_t maxlen = (c == 'u') ? 4 : 8;
for (size_t i = 0; isHexCode || i < maxlen; i++)
{
if (!CharInList(*it, "0123456789ABCDEFabcdef")) break;
++pos;
hex += *it;
if (++it == s.cend()) break;
}
if (!hex.empty())
{
unsigned int val = 0;
try
{
val = std::stoi(hex, nullptr, 16);
}
catch (const std::out_of_range& e)
{
pStk->SetError(CBotErrHexRange, pos + 1);
}
if (pStk->IsOk())
{
if (isHexCode) // hexadecimal
{
if (val <= 255)
{
valstring.push_back(val);
continue;
}
pStk->SetError(CBotErrHexRange, pos + 1);
}
else if (maxlen == hex.length()) // unicode character
{
if (val < 0xD800 || (0xDFFF < val && val < 0x110000))
{
valstring += CodePointToUTF8(val);
continue;
}
pStk->SetError(CBotErrUnicodeName, pos + 1);
}
}
}
}
pStk->SetError(CBotErrHexDigits, pos + 1);
}
else
pStk->SetError(CBotErrBadEscape, pos + 1); // unknown escape code
}
if (!pStk->IsOk()) break;
}
if (it == s.cend() || *it != '\"')
pStk->SetError(CBotErrEndQuote, p);
if (pStk->IsOk())
{
CBotExprLitString* inst = new CBotExprLitString();
inst->m_valstring.swap(valstring);
inst->SetToken(p);
p = p->GetNext();
CBotVar* var = CBotVar::Create("", CBotTypString);
pStk->SetVar(var);
return pStack->Return(inst, pStk);
}
}
pStk->SetError(CBotErrEndQuote, p);
return pStack->Return(nullptr, pStk);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -62,10 +183,7 @@ bool CBotExprLitString::Execute(CBotStack* &pj)
CBotVar* var = CBotVar::Create("", CBotTypString); CBotVar* var = CBotVar::Create("", CBotTypString);
std::string chaine = m_token.GetString(); var->SetValString(m_valstring);
chaine = chaine.substr(1, chaine.length()-2); // removes the quotes
var->SetValString(chaine); // value of the number
pile->SetVar(var); // put on the stack pile->SetVar(var); // put on the stack

View File

@ -58,6 +58,9 @@ public:
protected: protected:
virtual const std::string GetDebugName() override { return "CBotExprLitString"; } virtual const std::string GetDebugName() override { return "CBotExprLitString"; }
virtual std::string GetDebugData() override; virtual std::string GetDebugData() override;
private:
std::string m_valstring = "";
}; };
} // namespace CBot } // namespace CBot

View File

@ -241,23 +241,13 @@ void CBotToken::SetPos(int start, int end)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool CharInList(const char c, const char* list)
{
int i = 0;
while (true) static char sep1[] = " \r\n\t,:()[]{}-+*/=;><!~^|&%.\"\'?";
{
if (c == list[i++]) return true;
if (list[i] == 0) return false;
}
}
static char sep1[] = " \r\n\t,:()[]{}-+*/=;><!~^|&%.";
static char sep2[] = " \r\n\t"; // only separators static char sep2[] = " \r\n\t"; // only separators
static char sep3[] = ",:()[]{}-+*/=;<>!~^|&%."; // operational separators static char sep3[] = ",:()[]{}-+*/=;<>!~^|&%.?"; // operational separators
static char num[] = "0123456789"; // point (single) is tested separately static char num[] = "0123456789"; // point (single) is tested separately
static char hexnum[] = "0123456789ABCDEFabcdef"; static char hexnum[] = "0123456789ABCDEFabcdef";
static char nch[] = "\"\r\n\t"; // forbidden in chains static char nch[] = "\r\n\t"; // forbidden in chains
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
CBotToken* CBotToken::NextToken(const char*& program, bool first) CBotToken* CBotToken::NextToken(const char*& program, bool first)
@ -278,14 +268,13 @@ CBotToken* CBotToken::NextToken(const char*& program, bool first)
// special case for strings // special case for strings
if (token[0] == '\"' ) if (token[0] == '\"' )
{ {
while (c != 0 && !CharInList(c, nch)) while (c != 0 && c != '\"' && !CharInList(c, nch))
{ {
if ( c == '\\' ) if ( c == '\\' )
{ {
c = *(program++); // next character token += c;
if ( c == 'n' ) c = '\n'; c = *(program++);
if ( c == 'r' ) c = '\r'; if (c == 0 || CharInList(c, nch)) break;
if ( c == 't' ) c = '\t';
} }
token += c; token += c;
c = *(program++); c = *(program++);

View File

@ -236,4 +236,49 @@ float GetNumFloat(const std::string& str)
return static_cast<float>(num); return static_cast<float>(num);
} }
bool CharInList(const char c, const char* list)
{
int i = 0;
while (list[i] != 0)
{
if (c == list[i++]) return true;
}
return false;
}
std::string CodePointToUTF8(unsigned int val)
{
std::string s = "";
if (val < 0xD800 || (0xDFFF < val && val < 0x110000))
{
if (val < 0x80)
{
s.push_back(val);
}
else if (val < 0x800)
{
s.push_back(0xC0 + (val >> 6));
s.push_back(0x80 + (val & 0x3F));
}
else if (val < 0x10000)
{
s.push_back(0xE0 + (val >> 12));
s.push_back(0x80 + ((val >> 6) & 0x3F));
s.push_back(0x80 + (val & 0x3F));
}
else
{
s.push_back(0xF0 + (val >> 18));
s.push_back(0x80 + ((val >> 12) & 0x3F));
s.push_back(0x80 + ((val >> 6) & 0x3F));
s.push_back(0x80 + (val & 0x3F));
}
}
return s;
}
} // namespace CBot } // namespace CBot

View File

@ -96,6 +96,21 @@ long GetNumInt(const std::string& str);
*/ */
float GetNumFloat(const std::string& str); float GetNumFloat(const std::string& str);
/*!
* \brief Search a null-terminated string for a char value.
* \param c The char to find.
* \param list The string to search.
* \return true if the char is found.
*/
bool CharInList(const char c, const char* list);
/*!
* \brief Converts a Unicode code point to UTF-8 encoded character.
* \param val Code point value.
* \return UTF-8 encoded string or empty string.
*/
std::string CodePointToUTF8(unsigned int val);
template<typename T> class CBotLinkedList template<typename T> class CBotLinkedList
{ {
public: public:

View File

@ -731,6 +731,12 @@ void InitializeRestext()
stringsCbot[CBot::CBotErrNoClassName] = TR("Class name expected"); stringsCbot[CBot::CBotErrNoClassName] = TR("Class name expected");
stringsCbot[CBot::CBotErrNoReturn] = TR("Non-void function needs \"return;\""); stringsCbot[CBot::CBotErrNoReturn] = TR("Non-void function needs \"return;\"");
stringsCbot[CBot::CBotErrDefaultValue] = TR("This parameter needs a default value"); stringsCbot[CBot::CBotErrDefaultValue] = TR("This parameter needs a default value");
stringsCbot[CBot::CBotErrEndQuote] = TR("Missing end quote");
stringsCbot[CBot::CBotErrBadEscape] = TR("Unknown escape sequence");
stringsCbot[CBot::CBotErrOctalRange] = TR("Octal value out of range");
stringsCbot[CBot::CBotErrHexDigits] = TR("Missing hex digits after escape sequence");
stringsCbot[CBot::CBotErrHexRange] = TR("Hex value out of range");
stringsCbot[CBot::CBotErrUnicodeName] = TR("Invalid universal character name");
stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero"); stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero");
stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized"); stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized");

View File

@ -589,6 +589,56 @@ void CScript::UpdateList(Ui::CList* list)
list->SetState(Ui::STATE_ENABLE); list->SetState(Ui::STATE_ENABLE);
} }
// Colorize a string literal with escape sequences also colored
void HighlightString(Ui::CEdit* edit, const std::string& s, int start)
{
edit->SetFormat(start, start + 1, Gfx::FONT_HIGHLIGHT_STRING);
auto it = s.cbegin() + 1;
++start;
while (it != s.cend() && *it != '\"')
{
if (*(it++) != '\\') // not escape sequence
{
edit->SetFormat(start, start + 1, Gfx::FONT_HIGHLIGHT_STRING);
++start;
continue;
}
if (it == s.cend()) break;
int end = start + 2;
if (CBot::CharInList(*it, "01234567")) // octal escape sequence
{
for (int i = 0; ++it != s.cend() && i < 2; i++, end++)
{
if (!CBot::CharInList(*it, "01234567")) break;
}
}
else if (*it == 'x' || *it == 'u' || *it == 'U') // hex or unicode escape
{
bool isHexCode = (*it == 'x');
int maxlen = (*it == 'u') ? 4 : 8;
for (int i = 0; ++it != s.cend(); i++, end++)
{
if (!isHexCode && i >= maxlen) break;
if (!CBot::CharInList(*it, "0123456789ABCDEFabcdef")) break;
}
}
else // n, r, t, etc.
++it;
edit->SetFormat(start, end, Gfx::FONT_HIGHLIGHT_NONE);
start = end;
}
if (it != s.cend())
edit->SetFormat(start, start + 1, Gfx::FONT_HIGHLIGHT_STRING);
}
// Colorize the text according to syntax. // Colorize the text according to syntax.
@ -643,10 +693,16 @@ void CScript::ColorizeScript(Ui::CEdit* edit, int rangeStart, int rangeEnd)
{ {
color = Gfx::FONT_HIGHLIGHT_CONST; color = Gfx::FONT_HIGHLIGHT_CONST;
} }
else if (type == CBot::TokenTypString || type == CBot::TokenTypNum) // string literals and numbers else if (type == CBot::TokenTypNum) // numbers
{ {
color = Gfx::FONT_HIGHLIGHT_STRING; color = Gfx::FONT_HIGHLIGHT_STRING;
} }
else if (type == CBot::TokenTypString) // string literals
{
HighlightString(edit, token, cursor1);
bt = bt->GetNext();
continue;
}
assert(cursor1 < cursor2); assert(cursor1 < cursor2);
edit->SetFormat(cursor1, cursor2, color); edit->SetFormat(cursor1, cursor2, color);

View File

@ -1533,6 +1533,120 @@ TEST_F(CBotUT, String)
" ASSERT(c == \"Colobot!\");\n" " ASSERT(c == \"Colobot!\");\n"
"}\n" "}\n"
); );
ExecuteTest(
"extern void MissingEndQuote()\n"
"{\n"
" \"Colobot...\n"
"}\n",
CBotErrEndQuote
);
}
TEST_F(CBotUT, StringEscapeCodes)
{
ExecuteTest(
"extern void HexEscapeCodes()\n"
"{\n"
" ASSERT(\" \\x07 \" == \" \\a \");\n"
" ASSERT(\" \\x08 \" == \" \\b \");\n"
" ASSERT(\" \\x09 \" == \" \\t \");\n"
" ASSERT(\" \\x0A \" == \" \\n \");\n"
" ASSERT(\" \\x0B \" == \" \\v \");\n"
" ASSERT(\" \\x0C \" == \" \\f \");\n"
" ASSERT(\" \\x0D \" == \" \\r \");\n"
" ASSERT(\" \\x22 \" == \" \\\" \");\n"
" ASSERT(\" \\x27 \" == \" \\\' \");\n"
" ASSERT(\" \\x5C \" == \" \\\\ \");\n"
" string test = \"\\x31 \\x32 \\x33\";\n"
" ASSERT(test == \"1 2 3\");\n"
"}\n"
"extern void OctalEscapeCodes()\n"
"{\n"
" ASSERT(\" \\000 \" == \" \\x00 \");\n"
" ASSERT(\" \\007 \" == \" \\x07 \");\n"
" ASSERT(\" \\010 \" == \" \\x08 \");\n"
" ASSERT(\" \\011 \" == \" \\x09 \");\n"
" ASSERT(\" \\012 \" == \" \\x0A \");\n"
" ASSERT(\" \\013 \" == \" \\x0B \");\n"
" ASSERT(\" \\014 \" == \" \\x0C \");\n"
" ASSERT(\" \\015 \" == \" \\x0D \");\n"
" ASSERT(\" \\042 \" == \" \\x22 \");\n"
" ASSERT(\" \\047 \" == \" \\x27 \");\n"
" ASSERT(\" \\134 \" == \" \\x5C \");\n"
" string test = \"\\101 \\102 \\103\";\n"
" ASSERT(test == \"A B C\");\n"
"}\n"
"extern void UnicodeEscapeCodesToUTF_8()\n"
"{\n"
" ASSERT(\" \\u0000 \" == \" \\0 \");\n"
" ASSERT(\" \\u0007 \" == \" \\a \");\n"
" ASSERT(\" \\u0008 \" == \" \\b \");\n"
" ASSERT(\" \\u0009 \" == \" \\t \");\n"
" ASSERT(\" \\u000A \" == \" \\n \");\n"
" ASSERT(\" \\u000B \" == \" \\v \");\n"
" ASSERT(\" \\u000C \" == \" \\f \");\n"
" ASSERT(\" \\u000D \" == \" \\r \");\n"
" ASSERT(\" \\u0022 \" == \" \\\" \");\n"
" ASSERT(\" \\u0027 \" == \" \\\' \");\n"
" ASSERT(\" \\u005C \" == \" \\\\ \");\n"
"\n"
" ASSERT(\"\\u00A9\" == \"\\xC2\\xA9\");\n"
" ASSERT(\"\\u00AE\" == \"\\xC2\\xAE\");\n"
" ASSERT(\"\\u262E\" == \"\\xE2\\x98\\xAE\");\n"
" ASSERT(\"\\u262F\" == \"\\xE2\\x98\\xAF\");\n"
" ASSERT(\"\\U0001F60E\" == \"\\xF0\\x9F\\x98\\x8E\");\n"
" ASSERT(\"\\U0001F61C\" == \"\\xF0\\x9F\\x98\\x9C\");\n"
" ASSERT(\"\\U0001F6E0\" == \"\\xF0\\x9F\\x9B\\xA0\");\n"
"}\n"
"extern void UnicodeMaxCharacterNameToUTF_8()\n"
"{\n"
" ASSERT(\"\\U0010FFFF\" == \"\\xF4\\x8F\\xBF\\xBF\");\n"
"}\n"
);
}
TEST_F(CBotUT, StringEscapeCodeErrors)
{
ExecuteTest(
"extern void UnknownEscapeSequence()\n"
"{\n"
" \"Unknown: \\p \";\n"
"}\n",
CBotErrBadEscape
);
ExecuteTest(
"extern void MissingHexDigits()\n"
"{\n"
" \" \\x \";\n"
"}\n",
CBotErrHexDigits
);
ExecuteTest(
"extern void HexValueOutOfRange()\n"
"{\n"
" \" \\x100 \";\n"
"}\n",
CBotErrHexRange
);
ExecuteTest(
"extern void OctalValueOutOfRange()\n"
"{\n"
" \" \\400 \";\n"
"}\n",
CBotErrOctalRange
);
ExecuteTest(
"extern void BadUnicodeCharacterName()\n"
"{\n"
" \" \\U00110000 \";\n"
"}\n",
CBotErrUnicodeName
);
} }
// TODO: not implemented, see issue #694 // TODO: not implemented, see issue #694