diff --git a/CMakeLists.txt b/CMakeLists.txt index 8405235..adaf2f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ include_directories(include) set(QREDIS_SRC src/client.cpp src/lexer.cpp + src/parser.cpp src/request.cpp) # QRedis header files requiring MOC. @@ -31,6 +32,7 @@ qt5_wrap_cpp(QREDIS_MOC include/qredis/request.h src/client_p.h src/lexer.h + src/parser.h src/request_p.h) # Create the client library. diff --git a/src/lexer.cpp b/src/lexer.cpp index 85a0232..c926d31 100644 --- a/src/lexer.cpp +++ b/src/lexer.cpp @@ -25,6 +25,9 @@ void Lexer::readData() case ReadingUnsafeString: if(!readUnsafeString()) return; break; case ReadingSafeString: if(!readSafeString()) return; break; } + + if(state != ReadingSafeString) + state = DoingNothing; } } @@ -41,12 +44,9 @@ bool Lexer::readCharacter() { case '+': case '-': - case '*': - case ':': state = ReadingUnsafeString; break; + case ':': + case '*': state = ReadingUnsafeString; break; case '$': state = ReadingLength; break; - default: - emit warning(tr("Unexpected character '%1' encountered").arg(static_cast(c), 0, 16)); - return true; } emit character(c); diff --git a/src/lexer.h b/src/lexer.h index c4d3a0d..d4d7be3 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -19,8 +19,6 @@ class Lexer : public QObject void unsafeString(const QString &); void safeString(const QByteArray &); - void warning(const QString &); - private Q_SLOTS: void readData(); diff --git a/src/parser.cpp b/src/parser.cpp new file mode 100644 index 0000000..66525a3 --- /dev/null +++ b/src/parser.cpp @@ -0,0 +1,85 @@ +#include "parser.h" + +Parser::Parser(Lexer * lexer, QObject * parent) + : QObject(parent) +{ + connect(lexer, SIGNAL(character(char)), SLOT(readCharacter(char))); + connect(lexer, SIGNAL(unsafeString(QString)), SLOT(readUnsafeString(QString))); + connect(lexer, SIGNAL(safeString(QByteArray)), SLOT(readSafeString(QByteArray))); +} + +Parser::~Parser() +{ +} + +void Parser::readCharacter(char c) +{ + switch(c) + { + case '+': stack.append(Task(Task::ReadStatus)); break; + case '-': stack.append(Task(Task::ReadError)); break; + case ':': stack.append(Task(Task::ReadInteger)); break; + case '$': stack.append(Task(Task::ReadBulk)); break; + case '*': stack.append(Task(Task::ReadMultiBulk)); break; + default: + emit warning(tr("Skipping unexpected character '%1'").arg(static_cast(c), 0, 16)); + break; + } +} + +void Parser::readUnsafeString(const QString & value) +{ + if(tos().action == Task::ReadMultiBulk) + tos().count = value.toInt(); + else + { + stack.removeLast(); + + if(tos().action == Task::ReadStatus) + emit status(value); + else if(tos().action == Task::ReadError) + { + int pos = value.indexOf(' '); + emit error(value.left(pos), value.right(pos + 1)); + } + else if(!stack.count()) + emit integer(value.toLongLong()); + else + { + tos().value.toList().append(value); + descend(); + } + } +} + +void Parser::readSafeString(const QByteArray & value) +{ + stack.removeLast(); + + if(!stack.count()) + emit bulk(value); + else + { + tos().value.toList().append(value); + descend(); + } +} + +void Parser::descend() +{ + while(true) + { + if(tos().value.toList().count() < tos().count) + break; + + if(stack.count() == 1) + { + emit multiBulk(tos().value.toList()); + stack.removeLast(); + break; + } + + Task task = stack.takeLast(); + tos().value.toList().append(task.value); + } +} diff --git a/src/parser.h b/src/parser.h new file mode 100644 index 0000000..d93edfe --- /dev/null +++ b/src/parser.h @@ -0,0 +1,66 @@ +#ifndef PARSER_H +#define PARSER_H + +#include +#include +#include +#include + +#include "lexer.h" + +class Parser : public QObject +{ + Q_OBJECT + + public: + + Parser(Lexer *, QObject * = 0); + virtual ~Parser(); + + Q_SIGNALS: + + void status(const QString &); + void error(const QString &, const QString &); + void integer(qlonglong); + void bulk(const QByteArray &); + void multiBulk(const QVariantList &); + + void warning(const QString &); + + private Q_SLOTS: + + void readCharacter(char); + void readUnsafeString(const QString &); + void readSafeString(const QByteArray &); + + private: + + void descend(); + + class Task + { + public: + + enum Action { + ReadStatus, + ReadError, + ReadInteger, + ReadBulk, + ReadMultiBulk + }; + + enum { Unknown = -2 }; + + Task(Action action) : action(action), count(Unknown) {} + + Action action; + int count; + QVariant value; + }; + + QList stack; + + inline Task & tos() { return stack.last(); } +}; + +#endif // PARSER_H