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 <cctype>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "booleannode.hpp"
|
||||
|
||||
|
@ -12,65 +16,239 @@ namespace CSMFilter
|
|||
enum Type
|
||||
{
|
||||
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;
|
||||
std::string mString;
|
||||
double mNumber;
|
||||
|
||||
Token (Type type);
|
||||
|
||||
Token (const std::string& string);
|
||||
|
||||
Token (double number);
|
||||
|
||||
operator bool() const;
|
||||
};
|
||||
|
||||
Token::Token (Type type) : mType (type) {}
|
||||
}
|
||||
|
||||
CSMFilter::Token CSMFilter::Parser::getNextToken (const std::string& filter, int& index) const
|
||||
{
|
||||
if (index>=static_cast<int> (filter.size()))
|
||||
return Token (Token::Type_EOS);
|
||||
Token::Token (const std::string& string) : mType (Type_String), mString (string) {}
|
||||
|
||||
return Token (Token::Type_None);
|
||||
}
|
||||
Token::Token (double number) : mType (Type_Number), mNumber (number) {}
|
||||
|
||||
bool CSMFilter::Parser::isEndState() 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::operator bool() const
|
||||
{
|
||||
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_EOS: mState = State_End; break;
|
||||
case Token::Type_String: return left.mString==right.mString;
|
||||
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".
|
||||
mFilter.reset (new BooleanNode (true));
|
||||
}
|
||||
}
|
||||
|
||||
CSMFilter::Parser::State CSMFilter::Parser::getState() const
|
||||
{
|
||||
return mState;
|
||||
return true;
|
||||
}
|
||||
|
||||
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::getFilter() const
|
||||
{
|
||||
if (mState!=State_End)
|
||||
if (mError)
|
||||
throw std::logic_error ("No filter available");
|
||||
|
||||
return mFilter;
|
||||
|
|
|
@ -11,36 +11,36 @@ namespace CSMFilter
|
|||
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
|
||||
enum State
|
||||
{
|
||||
State_Begin,
|
||||
State_UnexpectedCharacter,
|
||||
State_End
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
State mState;
|
||||
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;
|
||||
///< This includes error states.
|
||||
Token getNumberToken();
|
||||
|
||||
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:
|
||||
|
||||
Parser();
|
||||
|
||||
void parse (const std::string& filter);
|
||||
bool parse (const std::string& filter);
|
||||
///< Discards any previous calls to parse
|
||||
|
||||
State getState() const;
|
||||
///
|
||||
/// \return Success?
|
||||
|
||||
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)
|
||||
{
|
||||
mParser.parse (text.toUtf8().constData());
|
||||
|
||||
if (mParser.getState()==CSMFilter::Parser::State_End)
|
||||
if (mParser.parse (text.toUtf8().constData()))
|
||||
{
|
||||
setPalette (mPalette);
|
||||
emit filterChanged (mParser.getFilter(), "");
|
||||
|
|
Loading…
Reference in a new issue