1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-16 16:29:55 +00:00

Allow comparison operators of arbitrary length

This commit is contained in:
unknown 2023-03-05 19:15:43 +01:00
parent 31ae1cd339
commit 868a398b86
3 changed files with 81 additions and 101 deletions

View file

@ -23,6 +23,7 @@
Bug #6645: Enemy block sounds align with animation instead of blocked hits Bug #6645: Enemy block sounds align with animation instead of blocked hits
Bug #6657: Distant terrain tiles become black when using FWIW mod Bug #6657: Distant terrain tiles become black when using FWIW mod
Bug #6661: Saved games that have no preview screenshot cause issues or crashes Bug #6661: Saved games that have no preview screenshot cause issues or crashes
Bug #6716: mwscript comparison operator handling is too restrictive
Bug #6807: Ultimate Galleon is not working properly Bug #6807: Ultimate Galleon is not working properly
Bug #6893: Lua: Inconsistent behavior with actors affected by Disable and SetDelete commands Bug #6893: Lua: Inconsistent behavior with actors affected by Disable and SetDelete commands
Bug #6939: OpenMW-CS: ID columns are too short Bug #6939: OpenMW-CS: ID columns are too short

View file

@ -10,6 +10,19 @@
#include <components/misc/strings/lower.hpp> #include <components/misc/strings/lower.hpp>
namespace
{
bool startsComparisonOperator(const Compiler::MultiChar& c)
{
return c == '=' || c == '<' || c == '>' || c == '!';
}
bool validComparisonOperatorCharacter(const Compiler::MultiChar& c)
{
return startsComparisonOperator(c) || c == ' ' || c == '\t';
}
}
namespace Compiler namespace Compiler
{ {
bool Scanner::get(MultiChar& c) bool Scanner::get(MultiChar& c)
@ -440,52 +453,68 @@ namespace Compiler
special = S_member; special = S_member;
} }
else if (c == '=') else if (startsComparisonOperator(c))
{ {
if (get(c)) TokenLoc loc = mLoc;
std::string op;
c.appendTo(op);
while (get(c))
{ {
/// \todo hack to allow a space in comparison operators (add option to disable) if (validComparisonOperatorCharacter(c))
if (c == ' ' && !get(c)) c.appendTo(op);
special = S_cmpEQ;
else if (c == '=')
special = S_cmpEQ;
else if (c == '>' || c == '<') // Treat => and =< as ==
{
special = S_cmpEQ;
mErrorHandler.warning(std::string("invalid operator =") + c.data() + ", treating it as ==", mLoc);
}
else else
{ {
special = S_cmpEQ;
putback(c); putback(c);
// return false; break;
/// Allow = as synonym for ==. \todo optionally disable for post-1.0 scripting improvements.
} }
} }
else size_t end = op.size();
while (end > 0 && (op[end - 1] == '\t' || op[end - 1] == ' '))
--end;
std::string_view trimmedOperator = std::string_view{ op }.substr(0, end);
switch (trimmedOperator[0])
{ {
putback(c); case '=':
return false; special = S_cmpEQ;
} if (trimmedOperator.size() != 2 || trimmedOperator[1] != '=')
} mErrorHandler.warning(
else if (c == '!') "invalid operator " + std::string(trimmedOperator) + ", treating it as ==", loc);
{ break;
if (get(c)) case '<':
{ special = S_cmpLT;
/// \todo hack to allow a space in comparison operators (add option to disable) if (trimmedOperator[1] == '=')
if (c == ' ' && !get(c)) {
return false; special = S_cmpLE;
if (trimmedOperator.size() != 2)
if (c == '=') mErrorHandler.warning(
"invalid operator " + std::string(trimmedOperator) + ", treating it as <=", loc);
}
else if (trimmedOperator.size() != 1)
mErrorHandler.warning(
"invalid operator " + std::string(trimmedOperator) + ", treating it as <", loc);
break;
case '>':
special = S_cmpGT;
if (trimmedOperator[1] == '=')
{
special = S_cmpGE;
if (trimmedOperator.size() != 2)
mErrorHandler.warning(
"invalid operator " + std::string(trimmedOperator) + ", treating it as >=", loc);
}
else if (trimmedOperator.size() != 1)
mErrorHandler.warning(
"invalid operator " + std::string(trimmedOperator) + ", treating it as >", loc);
break;
case '!':
special = S_cmpNE; special = S_cmpNE;
else if (trimmedOperator.size() != 2 || trimmedOperator[1] != '=')
{ mErrorHandler.warning(
putback(c); "invalid operator " + std::string(trimmedOperator) + ", treating it as !=", loc);
break;
default:
return false; return false;
}
} }
else
return false;
} }
else if (c.isMinusSign()) else if (c.isMinusSign())
{ {
@ -503,62 +532,6 @@ namespace Compiler
else else
special = S_minus; special = S_minus;
} }
else if (c == '<')
{
if (get(c))
{
/// \todo hack to allow a space in comparison operators (add option to disable)
if (c == ' ' && !get(c))
special = S_cmpLT;
else if (c == '=')
{
special = S_cmpLE;
if (get(c) && c != '=') // <== is a allowed as an alternative to <= :(
putback(c);
}
else if (c == '<' || c == '>') // Treat <> and << as <
{
special = S_cmpLT;
mErrorHandler.warning("Invalid operator, treating it as <", mLoc);
}
else
{
putback(c);
special = S_cmpLT;
}
}
else
special = S_cmpLT;
}
else if (c == '>')
{
if (get(c))
{
/// \todo hack to allow a space in comparison operators (add option to disable)
if (c == ' ' && !get(c))
special = S_cmpGT;
else if (c == '=')
{
special = S_cmpGE;
if (get(c) && c != '=') // >== is a allowed as an alternative to >= :(
putback(c);
}
else if (c == '<' || c == '>') // Treat >< and >> as >
{
special = S_cmpGT;
mErrorHandler.warning("Invalid operator, treating it as >", mLoc);
}
else
{
putback(c);
special = S_cmpGT;
}
}
else
special = S_cmpGT;
}
else if (c == '+') else if (c == '+')
special = S_plus; special = S_plus;
else if (c == '*') else if (c == '*')

View file

@ -48,25 +48,31 @@ namespace Compiler
return -1; return -1;
} }
bool operator==(const char ch) { return mData[0] == ch && mData[1] == 0 && mData[2] == 0 && mData[3] == 0; } bool operator==(const char ch) const
{
return mData[0] == ch && mData[1] == 0 && mData[2] == 0 && mData[3] == 0;
}
bool operator==(const MultiChar& ch) bool operator==(const MultiChar& ch) const
{ {
return mData[0] == ch.mData[0] && mData[1] == ch.mData[1] && mData[2] == ch.mData[2] return mData[0] == ch.mData[0] && mData[1] == ch.mData[1] && mData[2] == ch.mData[2]
&& mData[3] == ch.mData[3]; && mData[3] == ch.mData[3];
} }
bool operator!=(const char ch) { return mData[0] != ch || mData[1] != 0 || mData[2] != 0 || mData[3] != 0; } bool operator!=(const char ch) const
{
return mData[0] != ch || mData[1] != 0 || mData[2] != 0 || mData[3] != 0;
}
bool isWhitespace() bool isWhitespace() const
{ {
return (mData[0] == ' ' || mData[0] == '\t' || mData[0] == ',') && mData[1] == 0 && mData[2] == 0 return (mData[0] == ' ' || mData[0] == '\t' || mData[0] == ',') && mData[1] == 0 && mData[2] == 0
&& mData[3] == 0; && mData[3] == 0;
} }
bool isDigit() { return std::isdigit(mData[0]) && mData[1] == 0 && mData[2] == 0 && mData[3] == 0; } bool isDigit() const { return std::isdigit(mData[0]) && mData[1] == 0 && mData[2] == 0 && mData[3] == 0; }
bool isMinusSign() bool isMinusSign() const
{ {
if (mData[0] == '-' && mData[1] == 0 && mData[2] == 0 && mData[3] == 0) if (mData[0] == '-' && mData[1] == 0 && mData[2] == 0 && mData[3] == 0)
return true; return true;
@ -74,7 +80,7 @@ namespace Compiler
return mData[0] == '\xe2' && mData[1] == '\x80' && mData[2] == '\x93' && mData[3] == 0; return mData[0] == '\xe2' && mData[1] == '\x80' && mData[2] == '\x93' && mData[3] == 0;
} }
bool isAlpha() bool isAlpha() const
{ {
if (isMinusSign()) if (isMinusSign())
return false; return false;
@ -82,13 +88,13 @@ namespace Compiler
return std::isalpha(mData[0]) || mData[1] != 0 || mData[2] != 0 || mData[3] != 0; return std::isalpha(mData[0]) || mData[1] != 0 || mData[2] != 0 || mData[3] != 0;
} }
void appendTo(std::string& str) void appendTo(std::string& str) const
{ {
for (int i = 0; i <= mLength; i++) for (int i = 0; i <= mLength; i++)
str += mData[i]; str += mData[i];
} }
void putback(std::istream& in) void putback(std::istream& in) const
{ {
for (int i = mLength; i >= 0; i--) for (int i = mLength; i >= 0; i--)
in.putback(mData[i]); in.putback(mData[i]);
@ -157,7 +163,7 @@ namespace Compiler
mLength = -1; mLength = -1;
} }
std::string data() std::string data() const
{ {
// NB: mLength is the number of the last element in the array // NB: mLength is the number of the last element in the array
return std::string(mData, mLength + 1); return std::string(mData, mLength + 1);