Add binary and character literals in CBOT

blender-script
melex750 2019-04-11 05:14:11 -04:00
parent bc572aa52f
commit 1058a326ad
18 changed files with 436 additions and 7 deletions

View File

@ -1795,6 +1795,9 @@ msgstr ""
msgid "Invalid universal character name"
msgstr ""
msgid "Empty character constant"
msgstr ""
msgid "Dividing by zero"
msgstr ""

View File

@ -507,6 +507,9 @@ msgstr "Upravit vybraný program"
msgid "Egg"
msgstr "Vejce"
msgid "Empty character constant"
msgstr ""
msgid "End of block missing"
msgstr "Chybí konec bloku"

View File

@ -508,6 +508,9 @@ msgstr "Gewähltes Programm bearbeiten"
msgid "Egg"
msgstr "Ei"
msgid "Empty character constant"
msgstr ""
msgid "End of block missing"
msgstr "Es fehlt eine geschlossene geschweifte Klammer \"}\" (Ende des Blocks)"

View File

@ -510,6 +510,9 @@ msgstr "Éditer le programme sélectionné"
msgid "Egg"
msgstr "Oeuf"
msgid "Empty character constant"
msgstr ""
msgid "End of block missing"
msgstr "Il manque la fin du bloc"

View File

@ -506,6 +506,9 @@ msgstr "Edytuj zaznaczony program"
msgid "Egg"
msgstr "Jajo"
msgid "Empty character constant"
msgstr ""
msgid "End of block missing"
msgstr "Brak końca bloku"

View File

@ -505,6 +505,9 @@ msgstr "Alterar o programa selecionado"
msgid "Egg"
msgstr "Ovo"
msgid "Empty character constant"
msgstr ""
msgid "End of block missing"
msgstr "Fim do bloco ausente"

View File

@ -514,6 +514,9 @@ msgstr "Изменить выбранную программу"
msgid "Egg"
msgstr "Яйцо"
msgid "Empty character constant"
msgstr ""
msgid "End of block missing"
msgstr "Отсутствует конец блока"

View File

@ -182,7 +182,8 @@ enum TokenType
TokenTypNum = 2, //!< number
TokenTypString = 3, //!< string
TokenTypVar = 4, //!< a variable name
TokenTypDef = 5 //!< value according DefineNum
TokenTypDef = 5, //!< value according DefineNum
TokenTypChar = 6, //!< character literal
};
/**
@ -252,6 +253,7 @@ enum CBotError : int
CBotErrHexDigits = 5052, //!< missing hex digits after escape sequence
CBotErrHexRange = 5053, //!< hex value out of range
CBotErrUnicodeName = 5054, //!< invalid universal character name
CBotErrCharEmpty = 5055, //!< empty character constant
// Runtime errors
CBotErrZeroDiv = 6000, //!< division by zero

View File

@ -0,0 +1,151 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2018, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "CBot/CBotInstr/CBotExprLitChar.h"
#include "CBot/CBotStack.h"
#include "CBot/CBotCStack.h"
#include "CBot/CBotVar/CBotVar.h"
namespace CBot
{
CBotExprLitChar::CBotExprLitChar()
{
}
CBotExprLitChar::~CBotExprLitChar()
{
}
CBotInstr* CBotExprLitChar::Compile(CBotToken* &p, CBotCStack* pStack)
{
CBotCStack* pStk = pStack->TokenStack();
const auto& s = p->GetString();
auto it = s.cbegin();
if (++it != s.cend())
{
if (*it != '\'') // not empty quotes ?
{
uint32_t valchar = 0;
int pos = p->GetStart() + 1;
if (*it != '\\') valchar = *(it++); // not escape sequence ?
else if (++it != s.cend())
{
pStk->SetStartError(pos++);
unsigned char c = *(it++);
if (c == '\"' || c == '\'' || c == '\\') valchar = c;
else if (c == 'a') valchar = '\a'; // alert bell
else if (c == 'b') valchar = '\b'; // backspace
else if (c == 'f') valchar = '\f'; // form feed
else if (c == 'n') valchar = '\n'; // new line
else if (c == 'r') valchar = '\r'; // carriage return
else if (c == 't') valchar = '\t'; // horizontal tab
else if (c == 'v') valchar = '\v'; // vertical tab
else if (c == 'u' || c == 'U') // unicode escape
{
if (it != s.cend())
{
std::string hex = "";
size_t maxlen = (c == 'u') ? 4 : 8;
for (size_t i = 0; i < maxlen; i++)
{
if (!CharInList(*it, "0123456789ABCDEFabcdef")) break;
++pos;
hex += *it;
if (++it == s.cend()) break;
}
if (maxlen == hex.length()) // unicode character
{
try
{
valchar = std::stoi(hex, nullptr, 16);
if (0x10FFFF < valchar || (0xD7FF < valchar && valchar < 0xE000))
pStk->SetError(CBotErrUnicodeName, pos + 1);
}
catch (const std::out_of_range& e)
{
pStk->SetError(CBotErrHexRange, pos + 1);
}
}
else
pStk->SetError(CBotErrHexDigits, pos + 1);
}
else
pStk->SetError(CBotErrHexDigits, pos + 1);
}
else
pStk->SetError(CBotErrBadEscape, pos + 1);
}
if (it == s.cend() || *it != '\'')
pStk->SetError(CBotErrEndQuote, p);
if (pStk->IsOk())
{
CBotExprLitChar* inst = new CBotExprLitChar();
inst->m_valchar = valchar;
inst->SetToken(p);
p = p->GetNext();
CBotVar* var = CBotVar::Create("", CBotTypChar);
pStk->SetVar(var);
return pStack->Return(inst, pStk);
}
}
pStk->SetError(CBotErrCharEmpty, p);
}
pStk->SetError(CBotErrEndQuote, p);
return pStack->Return(nullptr, pStk);
}
bool CBotExprLitChar::Execute(CBotStack* &pj)
{
CBotStack* pile = pj->AddStack(this);
if (pile->IfStep()) return false;
CBotVar* var = CBotVar::Create("", CBotTypChar);
var->SetValChar(m_valchar);
pile->SetVar(var);
return pj->Return(pile);
}
void CBotExprLitChar::RestoreState(CBotStack* &pj, bool bMain)
{
if (bMain) pj->RestoreStack(this);
}
std::string CBotExprLitChar::GetDebugData()
{
return m_token.GetString();
}
} // namespace CBot

View File

@ -0,0 +1,60 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2018, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#pragma once
#include "CBot/CBotInstr/CBotInstr.h"
namespace CBot
{
/**
* \brief A character literal
* \verbatim 'a', '\n', '\t', '\uFFFD', '\U0000FFFD', etc. \endverbatim
*/
class CBotExprLitChar : public CBotInstr
{
public:
CBotExprLitChar();
~CBotExprLitChar();
/*!
* \brief Compile a character literal
*/
static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack);
/*!
* \brief Execute, returns the corresponding char.
*/
bool Execute(CBotStack* &pj) override;
/*!
* \brief RestoreState
*/
void RestoreState(CBotStack* &pj, bool bMain) override;
protected:
virtual const std::string GetDebugName() override { return "CBotExprLitChar"; }
virtual std::string GetDebugData() override;
private:
uint32_t m_valchar = 0;
};
} // namespace CBot

View File

@ -42,7 +42,7 @@ CBotInstr* CBotExprLitString::Compile(CBotToken* &p, CBotCStack* pStack)
{
CBotCStack* pStk = pStack->TokenStack();
std::string s = p->GetString();
const auto& s = p->GetString();
auto it = s.cbegin();
if (++it != s.cend())
@ -51,7 +51,7 @@ CBotInstr* CBotExprLitString::Compile(CBotToken* &p, CBotCStack* pStack)
std::string valstring = "";
while (it != s.cend() && *it != '\"')
{
pStk->SetStartError(++pos);
++pos;
if (*it != '\\') // not escape sequence ?
{
valstring += *(it++);
@ -59,6 +59,7 @@ CBotInstr* CBotExprLitString::Compile(CBotToken* &p, CBotCStack* pStack)
}
if (++it == s.cend()) break;
pStk->SetStartError(pos);
if (CharInList(*it, "01234567")) // octal
{

View File

@ -21,6 +21,7 @@
#include "CBot/CBotInstr/CBotExpression.h"
#include "CBot/CBotInstr/CBotExprLitBool.h"
#include "CBot/CBotInstr/CBotExprLitChar.h"
#include "CBot/CBotInstr/CBotExprLitNan.h"
#include "CBot/CBotInstr/CBotExprLitNull.h"
#include "CBot/CBotInstr/CBotExprLitNum.h"
@ -169,6 +170,13 @@ CBotInstr* CBotParExpr::CompileLitExpr(CBotToken* &p, CBotCStack* pStack)
return pStack->Return(inst, pStk);
}
// is this a character?
if (p->GetType() == TokenTypChar)
{
CBotInstr* inst = CBotExprLitChar::Compile(p, pStk);
return pStack->Return(inst, pStk);
}
// is this a chaine?
if (p->GetType() == TokenTypString)
{

View File

@ -304,12 +304,53 @@ CBotToken* CBotToken::NextToken(const char*& program, bool first)
stop = true;
}
// special case for characters
if (token[0] == '\'')
{
if (c == '\\') // escape sequence
{
token += c;
c = *(program++);
if (c == 'u' || c == 'U') // unicode escape
{
int maxlen = (c == 'u') ? 4 : 8;
token += c;
c = *(program++);
for (int i = 0; i < maxlen; i++)
{
if (c == 0 || !CharInList(c, hexnum)) break;
token += c;
c = *(program++);
}
}
else if (c != 0 && !CharInList(c, nch)) // other escape char
{
token += c;
c = *(program++);
}
}
else if (c != 0 && c != '\'' && !CharInList(c, nch)) // single character
{
token += c;
c = *(program++);
}
if (c == '\'') // close quote
{
token += c;
c = *(program++);
}
stop = true;
}
// special case for numbers
if ( CharInList(token[0], num ))
{
bool bdot = false; // found a point?
bool bexp = false; // found an exponent?
char bin[] = "01";
char* liste = num;
if (token[0] == '0' && c == 'x') // hexadecimal value?
{
@ -317,6 +358,12 @@ CBotToken* CBotToken::NextToken(const char*& program, bool first)
c = *(program++); // next character
liste = hexnum;
}
else if (token[0] == '0' && c == 'b') // binary literal
{
liste = bin;
token += c;
c = *(program++);
}
cw:
while (c != 0 && CharInList(c, liste))
{
@ -399,6 +446,7 @@ bis:
if (CharInList(token[0], num )) t->m_type = TokenTypNum;
if (token[0] == '\"') t->m_type = TokenTypString;
if (token[0] == '\'') t->m_type = TokenTypChar;
if (first) t->m_type = TokenTypNone;
t->m_keywordId = GetKeyWord(token);

View File

@ -155,6 +155,18 @@ long GetNumInt(const std::string& str)
break;
}
}
else if (*p == 'b')
{
while (*++p != 0)
{
if (*p == '0' || *p == '1')
{
num = (num << 1) + *p - '0';
continue;
}
break;
}
}
return num;
}

View File

@ -44,6 +44,8 @@ set(SOURCES
CBotInstr/CBotEmpty.h
CBotInstr/CBotExprLitBool.cpp
CBotInstr/CBotExprLitBool.h
CBotInstr/CBotExprLitChar.cpp
CBotInstr/CBotExprLitChar.h
CBotInstr/CBotExprLitNan.cpp
CBotInstr/CBotExprLitNan.h
CBotInstr/CBotExprLitNull.cpp

View File

@ -742,6 +742,7 @@ void InitializeRestext()
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::CBotErrCharEmpty] = TR("Empty character constant");
stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero");
stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized");

View File

@ -589,16 +589,17 @@ void CScript::UpdateList(Ui::CList* list)
list->SetState(Ui::STATE_ENABLE);
}
// Colorize a string literal with escape sequences also colored
// Colorize a string or character literal with escape sequences also colored
static 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;
auto it = s.cbegin();
char endQuote = *(it++);
++start;
while (it != s.cend() && *it != '\"')
while (it != s.cend() && *it != endQuote)
{
if (*(it++) != '\\') // not escape sequence
{
@ -697,7 +698,7 @@ void CScript::ColorizeScript(Ui::CEdit* edit, int rangeStart, int rangeEnd)
{
color = Gfx::FONT_HIGHLIGHT_STRING;
}
else if (type == CBot::TokenTypString) // string literals
else if (type == CBot::TokenTypString || type == CBot::TokenTypChar) // string literals and character literals
{
HighlightString(edit, token, cursor1);
bt = bt->GetNext();

View File

@ -619,6 +619,27 @@ TEST_F(CBotUT, IntegerMathNearLimits_Issue993)
);
}
TEST_F(CBotUT, BinaryLiterals)
{
ExecuteTest(
"extern void TestBinaryLiterals() {\n"
" ASSERT( 8 == 0b00001000);\n"
" ASSERT( 12 == 0b00001100);\n"
" ASSERT( 16 == 0b00010000);\n"
" ASSERT( 24 == 0b00011000);\n"
" ASSERT( 32 == 0b00100000);\n"
" ASSERT( 48 == 0b00110000);\n"
" ASSERT( 64 == 0b01000000);\n"
" ASSERT( 96 == 0b01100000);\n"
" ASSERT(128 == 0b10000000);\n"
" ASSERT(192 == 0b11000000);\n"
" ASSERT(256 == 0b100000000);\n"
" ASSERT(384 == 0b110000000);\n"
" ASSERT(512 == 0b1000000000);\n"
"}\n"
);
}
TEST_F(CBotUT, ToString)
{
ExecuteTest(
@ -1752,6 +1773,107 @@ TEST_F(CBotUT, StringFunctions)
);
}
TEST_F(CBotUT, LiteralCharacters)
{
ExecuteTest(
"extern void TestCharValue()\n"
"{\n"
" ASSERT('A' == 65);\n"
" ASSERT('B' == 66);\n"
" ASSERT('C' == 67);\n"
" ASSERT('\\a' == 0x07);\n"
" ASSERT('\\b' == 0x08);\n"
" ASSERT('\\t' == 0x09);\n"
" ASSERT('\\n' == 0x0A);\n"
" ASSERT('\\v' == 0x0B);\n"
" ASSERT('\\f' == 0x0C);\n"
" ASSERT('\\r' == 0x0D);\n"
" ASSERT('\\\"' == 0x22);\n"
" ASSERT('\\\'' == 0x27);\n"
" ASSERT('\\\\' == 0x5C);\n"
"}\n"
"extern void TestCharUnicodeEscape()\n"
"{\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"
"extern void AssignCharToString_ToUTF_8()\n"
"{\n"
" string test = '\\u00A9';\n"
" test += '\\u00AE';\n"
" ASSERT(test == \"\\xC2\\xA9\\xC2\\xAE\");\n"
"}\n"
"extern void AddCharToString_ToUTF_8()\n"
"{\n"
" ASSERT(\"\" + 'A' + 'B' + 'C' == \"ABC\");\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"
" ASSERT(\"\" + '\\U0010FFFF' == \"\\xF4\\x8F\\xBF\\xBF\");\n"
"}\n"
);
ExecuteTest(
"extern void MissingEndQuote()\n"
"{\n"
" '\n"
"}\n",
CBotErrEndQuote
);
ExecuteTest(
"extern void MissingEndQuote()\n"
"{\n"
" 'a\n"
"}\n",
CBotErrEndQuote
);
ExecuteTest(
"extern void EmptyQuotes()\n"
"{\n"
" '';\n"
"}\n",
CBotErrCharEmpty
);
ExecuteTest(
"extern void UnknownEscapeSequence()\n"
"{\n"
" '\\p';\n"
"}\n",
CBotErrBadEscape
);
ExecuteTest(
"extern void MissingHexDigits()\n"
"{\n"
" '\\u';\n"
"}\n",
CBotErrHexDigits
);
ExecuteTest(
"extern void BadUnicodeCharacterName()\n"
"{\n"
" '\\U00110000';\n"
"}\n",
CBotErrUnicodeName
);
}
TEST_F(CBotUT, TestNANParam_Issue642)
{
ExecuteTest(