forked from mirror/openmw-tes3mp
some general filter parser cleanup
This commit is contained in:
parent
c87a279444
commit
a61215dab1
3 changed files with 231 additions and 55 deletions
|
@ -1,7 +1,11 @@
|
||||||
|
|
||||||
#include "parser.hpp"
|
#include "parser.hpp"
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
#include "booleannode.hpp"
|
#include "booleannode.hpp"
|
||||||
|
|
||||||
|
@ -12,65 +16,239 @@ namespace CSMFilter
|
||||||
enum Type
|
enum Type
|
||||||
{
|
{
|
||||||
Type_EOS,
|
Type_EOS,
|
||||||
Type_None
|
Type_None,
|
||||||
|
Type_String,
|
||||||
|
Type_Number,
|
||||||
|
Type_Open,
|
||||||
|
Type_Close,
|
||||||
|
Type_OpenSquare,
|
||||||
|
Type_CloseSquare,
|
||||||
|
Type_Comma,
|
||||||
|
Type_Keyword_True, ///< \attention Keyword enums must be arranged continuously.
|
||||||
|
Type_Keyword_False,
|
||||||
|
Type_Keyword_And,
|
||||||
|
Type_Keyword_Or,
|
||||||
|
Type_Keyword_Not
|
||||||
};
|
};
|
||||||
|
|
||||||
Type mType;
|
Type mType;
|
||||||
|
std::string mString;
|
||||||
|
double mNumber;
|
||||||
|
|
||||||
Token (Type type);
|
Token (Type type);
|
||||||
|
|
||||||
|
Token (const std::string& string);
|
||||||
|
|
||||||
|
Token (double number);
|
||||||
|
|
||||||
|
operator bool() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
Token::Token (Type type) : mType (type) {}
|
Token::Token (Type type) : mType (type) {}
|
||||||
}
|
|
||||||
|
|
||||||
CSMFilter::Token CSMFilter::Parser::getNextToken (const std::string& filter, int& index) const
|
Token::Token (const std::string& string) : mType (Type_String), mString (string) {}
|
||||||
{
|
|
||||||
if (index>=static_cast<int> (filter.size()))
|
|
||||||
return Token (Token::Type_EOS);
|
|
||||||
|
|
||||||
return Token (Token::Type_None);
|
Token::Token (double number) : mType (Type_Number), mNumber (number) {}
|
||||||
}
|
|
||||||
|
|
||||||
bool CSMFilter::Parser::isEndState() const
|
Token::operator bool() const
|
||||||
{
|
|
||||||
return mState==State_End || mState==State_UnexpectedCharacter;
|
|
||||||
}
|
|
||||||
|
|
||||||
CSMFilter::Parser::Parser() : mState (State_Begin) {}
|
|
||||||
|
|
||||||
void CSMFilter::Parser::parse (const std::string& filter)
|
|
||||||
{
|
|
||||||
// reset
|
|
||||||
mState = State_Begin;
|
|
||||||
mFilter.reset();
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
while (!isEndState())
|
|
||||||
{
|
{
|
||||||
Token token = getNextToken (filter, index);
|
return mType!=Type_None;
|
||||||
|
}
|
||||||
|
|
||||||
switch (token.mType)
|
bool operator== (const Token& left, const Token& right)
|
||||||
|
{
|
||||||
|
if (left.mType!=right.mType)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (left.mType)
|
||||||
{
|
{
|
||||||
case Token::Type_None: mState = State_UnexpectedCharacter; break;
|
case Token::Type_String: return left.mString==right.mString;
|
||||||
case Token::Type_EOS: mState = State_End; break;
|
case Token::Type_Number: return left.mNumber==right.mNumber;
|
||||||
|
|
||||||
|
default: return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMFilter::Token CSMFilter::Parser::getStringToken()
|
||||||
|
{
|
||||||
|
std::string string;
|
||||||
|
|
||||||
|
int size = static_cast<int> (mInput.size());
|
||||||
|
|
||||||
|
for (; mIndex<size; ++mIndex)
|
||||||
|
{
|
||||||
|
char c = mInput[mIndex];
|
||||||
|
|
||||||
|
if (std::isalpha (c) || c=='_' || (!string.empty() && std::isdigit (c)) || c=='"' ||
|
||||||
|
(!string.empty() && string[0]=='"'))
|
||||||
|
string += c;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (c=='"' && string.size()>1)
|
||||||
|
{
|
||||||
|
++mIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!string.empty())
|
||||||
|
{
|
||||||
|
if (string[0]=='"' && (string[string.size()-1]!='"' || string.size()<2) )
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return Token (Token::Type_None);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string[0]!='"' && string[string.size()-1]=='"')
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return Token (Token::Type_None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mState==State_End && !mFilter)
|
return checkKeywords (string);
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMFilter::Token CSMFilter::Parser::getNumberToken()
|
||||||
|
{
|
||||||
|
std::string string;
|
||||||
|
|
||||||
|
int size = static_cast<int> (mInput.size());
|
||||||
|
|
||||||
|
bool hasDecimalPoint = false;
|
||||||
|
bool hasDigit = false;
|
||||||
|
|
||||||
|
for (; mIndex<size; ++mIndex)
|
||||||
|
{
|
||||||
|
char c = mInput[mIndex];
|
||||||
|
|
||||||
|
if (std::isdigit (c))
|
||||||
|
{
|
||||||
|
string += c;
|
||||||
|
hasDigit = true;
|
||||||
|
}
|
||||||
|
else if (c=='.' && !hasDecimalPoint)
|
||||||
|
{
|
||||||
|
string += c;
|
||||||
|
hasDecimalPoint = true;
|
||||||
|
}
|
||||||
|
else if (string.empty() && c=='-')
|
||||||
|
string += c;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasDigit)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return Token (Token::Type_None);
|
||||||
|
}
|
||||||
|
|
||||||
|
float value;
|
||||||
|
std::istringstream stream (string.c_str());
|
||||||
|
stream >> value;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMFilter::Token CSMFilter::Parser::checkKeywords (const Token& token)
|
||||||
|
{
|
||||||
|
static const char *sKeywords[] =
|
||||||
|
{
|
||||||
|
"true", "false",
|
||||||
|
"and", "or", "not",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string string = Misc::StringUtils::lowerCase (token.mString);
|
||||||
|
|
||||||
|
for (int i=0; sKeywords[i]; ++i)
|
||||||
|
if (sKeywords[i]==string)
|
||||||
|
return Token (static_cast<Token::Type> (i+Token::Type_Keyword_True));
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMFilter::Token CSMFilter::Parser::getNextToken()
|
||||||
|
{
|
||||||
|
int size = static_cast<int> (mInput.size());
|
||||||
|
|
||||||
|
char c = 0;
|
||||||
|
|
||||||
|
for (; mIndex<size; ++mIndex)
|
||||||
|
{
|
||||||
|
c = mInput[mIndex];
|
||||||
|
|
||||||
|
if (c!=' ')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mIndex>=size)
|
||||||
|
return Token (Token::Type_EOS);
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '(': ++mIndex; return Token (Token::Type_Open);
|
||||||
|
case ')': ++mIndex; return Token (Token::Type_Close);
|
||||||
|
case '[': ++mIndex; return Token (Token::Type_OpenSquare);
|
||||||
|
case ']': ++mIndex; return Token (Token::Type_CloseSquare);
|
||||||
|
case ',': ++mIndex; return Token (Token::Type_Comma);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c=='"' || c=='_' || std::isalpha (c))
|
||||||
|
return getStringToken();
|
||||||
|
|
||||||
|
if (c=='-' || c=='.' || std::isdigit (c))
|
||||||
|
return getNumberToken();
|
||||||
|
|
||||||
|
error();
|
||||||
|
return Token (Token::Type_None);
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseImp()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMFilter::Parser::error()
|
||||||
|
{
|
||||||
|
mError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMFilter::Parser::Parser() : mIndex (0), mError (false) {}
|
||||||
|
|
||||||
|
bool CSMFilter::Parser::parse (const std::string& filter)
|
||||||
|
{
|
||||||
|
// reset
|
||||||
|
mFilter.reset();
|
||||||
|
mError = false;
|
||||||
|
mInput = filter;
|
||||||
|
mIndex = 0;
|
||||||
|
|
||||||
|
boost::shared_ptr<Node> node = parseImp();
|
||||||
|
|
||||||
|
if (mError)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (node)
|
||||||
|
mFilter = node;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// Empty filter string equals to filter "true".
|
// Empty filter string equals to filter "true".
|
||||||
mFilter.reset (new BooleanNode (true));
|
mFilter.reset (new BooleanNode (true));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
CSMFilter::Parser::State CSMFilter::Parser::getState() const
|
return true;
|
||||||
{
|
|
||||||
return mState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::getFilter() const
|
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::getFilter() const
|
||||||
{
|
{
|
||||||
if (mState!=State_End)
|
if (mError)
|
||||||
throw std::logic_error ("No filter available");
|
throw std::logic_error ("No filter available");
|
||||||
|
|
||||||
return mFilter;
|
return mFilter;
|
||||||
|
|
|
@ -11,36 +11,36 @@ namespace CSMFilter
|
||||||
|
|
||||||
class Parser
|
class Parser
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
|
|
||||||
enum State
|
|
||||||
{
|
|
||||||
State_Begin,
|
|
||||||
State_UnexpectedCharacter,
|
|
||||||
State_End
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
State mState;
|
|
||||||
boost::shared_ptr<Node> mFilter;
|
boost::shared_ptr<Node> mFilter;
|
||||||
|
std::string mInput;
|
||||||
|
int mIndex;
|
||||||
|
bool mError;
|
||||||
|
|
||||||
Token getNextToken (const std::string& filter, int& index) const;
|
Token getStringToken();
|
||||||
|
|
||||||
bool isEndState() const;
|
Token getNumberToken();
|
||||||
///< This includes error states.
|
|
||||||
|
Token getNextToken();
|
||||||
|
|
||||||
|
Token checkKeywords (const Token& token);
|
||||||
|
///< Turn string token into keyword token, if possible.
|
||||||
|
|
||||||
|
boost::shared_ptr<Node> parseImp();
|
||||||
|
///< Will return a null-pointer, if there is nothing more to parse.
|
||||||
|
|
||||||
|
void error();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Parser();
|
Parser();
|
||||||
|
|
||||||
void parse (const std::string& filter);
|
bool parse (const std::string& filter);
|
||||||
///< Discards any previous calls to parse
|
///< Discards any previous calls to parse
|
||||||
|
///
|
||||||
State getState() const;
|
/// \return Success?
|
||||||
|
|
||||||
boost::shared_ptr<Node> getFilter() const;
|
boost::shared_ptr<Node> getFilter() const;
|
||||||
///< Throws an exception if getState()!=State_End
|
///< Throws an exception if the last call to parse did not return true.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,7 @@ CSVFilter::EditWidget::EditWidget (QWidget *parent)
|
||||||
|
|
||||||
void CSVFilter::EditWidget::textChanged (const QString& text)
|
void CSVFilter::EditWidget::textChanged (const QString& text)
|
||||||
{
|
{
|
||||||
mParser.parse (text.toUtf8().constData());
|
if (mParser.parse (text.toUtf8().constData()))
|
||||||
|
|
||||||
if (mParser.getState()==CSMFilter::Parser::State_End)
|
|
||||||
{
|
{
|
||||||
setPalette (mPalette);
|
setPalette (mPalette);
|
||||||
emit filterChanged (mParser.getFilter(), "");
|
emit filterChanged (mParser.getFilter(), "");
|
||||||
|
|
Loading…
Reference in a new issue