From 7e96145d27329ca3870f23d5b161785d10c9faf5 Mon Sep 17 00:00:00 2001 From: Aaron Seigo Date: Wed, 23 Dec 2015 16:43:45 +0100 Subject: Module -> SyntaxTree --- akonadi2_cli/CMakeLists.txt | 4 +- akonadi2_cli/main.cpp | 4 +- akonadi2_cli/module.cpp | 165 ---------------------------- akonadi2_cli/module.h | 72 ------------ akonadi2_cli/modules/core_syntax.cpp | 86 --------------- akonadi2_cli/modules/core_syntax.h | 30 ----- akonadi2_cli/repl/replStates.cpp | 8 +- akonadi2_cli/syntax_modules/core_syntax.cpp | 86 +++++++++++++++ akonadi2_cli/syntax_modules/core_syntax.h | 30 +++++ akonadi2_cli/syntaxtree.cpp | 165 ++++++++++++++++++++++++++++ akonadi2_cli/syntaxtree.h | 72 ++++++++++++ 11 files changed, 361 insertions(+), 361 deletions(-) delete mode 100644 akonadi2_cli/module.cpp delete mode 100644 akonadi2_cli/module.h delete mode 100644 akonadi2_cli/modules/core_syntax.cpp delete mode 100644 akonadi2_cli/modules/core_syntax.h create mode 100644 akonadi2_cli/syntax_modules/core_syntax.cpp create mode 100644 akonadi2_cli/syntax_modules/core_syntax.h create mode 100644 akonadi2_cli/syntaxtree.cpp create mode 100644 akonadi2_cli/syntaxtree.h diff --git a/akonadi2_cli/CMakeLists.txt b/akonadi2_cli/CMakeLists.txt index a07140e..a061ebb 100644 --- a/akonadi2_cli/CMakeLists.txt +++ b/akonadi2_cli/CMakeLists.txt @@ -5,8 +5,8 @@ find_package(Readline REQUIRED) set(akonadi2_cli_SRCS main.cpp - module.cpp - modules/core_syntax.cpp + syntaxtree.cpp + syntax_modules/core_syntax.cpp repl/repl.cpp repl/replStates.cpp state.cpp) diff --git a/akonadi2_cli/main.cpp b/akonadi2_cli/main.cpp index d23e070..f7b7b9f 100644 --- a/akonadi2_cli/main.cpp +++ b/akonadi2_cli/main.cpp @@ -22,7 +22,7 @@ #include #include -#include "module.h" +#include "syntaxtree.h" // #include "jsonlistener.h" #include "repl/repl.h" @@ -64,5 +64,5 @@ int main(int argc, char *argv[]) QStringList commands = app.arguments(); commands.removeFirst(); - return Module::self()->run(commands); + return SyntaxTree::self()->run(commands); } diff --git a/akonadi2_cli/module.cpp b/akonadi2_cli/module.cpp deleted file mode 100644 index 5fd68b4..0000000 --- a/akonadi2_cli/module.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2014 Aaron Seigo - * - * 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 2 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, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "module.h" - -#include -#include - -// TODO: needs a proper registry; making "core" modules plugins is -// almost certainly overkill, but this is not the way either -#include "modules/core_syntax.h" - -Module *Module::s_module = 0; - -Module::Syntax::Syntax() -{ -} - -Module::Syntax::Syntax(const QString &k, const QString &helpText, std::function l, Interactivity inter) - : keyword(k), - help(helpText), - interactivity(inter), - lambda(l) -{ -} - -Module::Module() -{ - QVector > syntaxModules; - syntaxModules << &CoreSyntax::syntax; - for (auto syntaxModule: syntaxModules) { - m_syntax += syntaxModule(); - } -} - -Module *Module::self() -{ - if (!s_module) { - s_module = new Module; - } - - return s_module; -} - -Module::SyntaxList Module::syntax() const -{ - return m_syntax; -} - -bool Module::run(const QStringList &commands) -{ - Command command = match(commands); - if (command.first && command.first->lambda) { - command.first->lambda(command.second, m_state); - if (command.first->interactivity == Syntax::EventDriven) { - return QCoreApplication::instance()->exec(); - } - - return true; - } - - return false; -} - -Module::Command Module::match(const QStringList &commandLine) const -{ - if (commandLine.isEmpty()) { - return Command(); - } - - QStringListIterator commandLineIt(commandLine); - - QVectorIterator syntaxIt(m_syntax); - const Syntax *lastFullSyntax = 0; - QStringList tailCommands; - while (commandLineIt.hasNext() && syntaxIt.hasNext()) { - const QString word = commandLineIt.next(); - while (syntaxIt.hasNext()) { - const Syntax &syntax = syntaxIt.next(); - if (word == syntax.keyword) { - lastFullSyntax = &syntax; - syntaxIt = syntax.children; - } - } - - if (!syntaxIt.hasNext()) { - break; - } - } - - if (lastFullSyntax && lastFullSyntax->lambda) { - while (commandLineIt.hasNext()) { - tailCommands << commandLineIt.next(); - } - - return std::make_pair(lastFullSyntax, tailCommands); - } - - return Command(); -} - -Module::SyntaxList Module::nearestSyntax(const QStringList &words, const QString &fragment) const -{ - SyntaxList matches; - - //qDebug() << "words are" << words; - if (words.isEmpty()) { - for (const Syntax &syntax: m_syntax) { - if (syntax.keyword.startsWith(fragment)) { - matches.push_back(syntax); - } - } - } else { - QStringListIterator wordIt(words); - QVectorIterator syntaxIt(m_syntax); - Syntax lastFullSyntax; - - while (wordIt.hasNext()) { - QString word = wordIt.next(); - while (syntaxIt.hasNext()) { - const Syntax &syntax = syntaxIt.next(); - if (word == syntax.keyword) { - lastFullSyntax = syntax; - syntaxIt = syntax.children; - } - } - } - - //qDebug() << "exiting with" << lastFullSyntax.keyword << words.last(); - if (lastFullSyntax.keyword == words.last()) { - syntaxIt = lastFullSyntax.children; - while (syntaxIt.hasNext()) { - Syntax syntax = syntaxIt.next(); - if (fragment.isEmpty() || syntax.keyword.startsWith(fragment)) { - matches.push_back(syntax); - } - } - } - } - - return matches; -} - -QStringList Module::tokenize(const QString &text) -{ - //TODO: properly tokenize (e.g. "foo bar" should not become ['"foo', 'bar"'] - return text.split(" "); -} - diff --git a/akonadi2_cli/module.h b/akonadi2_cli/module.h deleted file mode 100644 index 1149f4f..0000000 --- a/akonadi2_cli/module.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2014 Aaron Seigo - * - * 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 2 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, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#pragma once - -#include "state.h" - -#include -#include - -class Module -{ -public: - struct Syntax - { - enum Interactivity { - NotInteractive = 0, - EventDriven - }; - - Syntax(); - Syntax(const QString &keyword, - const QString &helpText = QString(), - std::function lambda = std::function(), - Interactivity interactivity = NotInteractive); - - QString keyword; - QString help; - Interactivity interactivity; - std::function lambda; - - QVector children; - }; - - typedef std::pair Command; - typedef QVector SyntaxList; - - static Module *self(); - - SyntaxList syntax() const; - Command match(const QStringList &commands) const; - SyntaxList nearestSyntax(const QStringList &words, const QString &fragment) const; - - bool run(const QStringList &commands); - - static QStringList tokenize(const QString &text); - -private: - Module(); - Command matches(const QStringList &commands) const; - - SyntaxList m_syntax; - State m_state; - static Module *s_module; -}; - diff --git a/akonadi2_cli/modules/core_syntax.cpp b/akonadi2_cli/modules/core_syntax.cpp deleted file mode 100644 index 944abbe..0000000 --- a/akonadi2_cli/modules/core_syntax.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2014 Aaron Seigo - * - * 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 2 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, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "core_syntax.h" - -#include // tr() -#include -#include - -namespace CoreSyntax -{ - -Module::SyntaxList syntax() -{ - Module::SyntaxList syntax; - syntax << Module::Syntax("exit", QObject::tr("Exits the application. Ctrl-d also works!"), &CoreSyntax::exit); - syntax << Module::Syntax(QObject::tr("help"), QObject::tr("Print command information: help [command]"), &CoreSyntax::showHelp); - return syntax; -} - -bool exit(const QStringList &, State &) -{ - ::exit(0); - return true; -} - -bool showHelp(const QStringList &commands, State &state) -{ - Module::Command command = Module::self()->match(commands); - if (commands.isEmpty()) { - state.printLine(QObject::tr("Welcome to the Akonadi2 command line tool!")); - state.printLine(QObject::tr("Top-level commands:")); - - QSet sorted; - for (auto syntax: Module::self()->syntax()) { - sorted.insert(syntax.keyword); - } - - for (auto keyword: sorted) { - state.printLine(keyword, 1); - } - } else if (const Module::Syntax *syntax = command.first) { - //TODO: get parent! - state.print(QObject::tr("Command `%1`").arg(syntax->keyword)); - - if (!syntax->help.isEmpty()) { - state.print(": " + syntax->help); - } - state.printLine(); - - if (!syntax->children.isEmpty()) { - state.printLine("Sub-commands:", 1); - QSet sorted; - for (auto childSyntax: syntax->children) { - sorted.insert(childSyntax.keyword); - } - - for (auto keyword: sorted) { - state.printLine(keyword, 1); - } - } - } else { - state.printError("Unknown command: " + commands.join(" ")); - } - - return true; -} - -} // namespace CoreSyntax - diff --git a/akonadi2_cli/modules/core_syntax.h b/akonadi2_cli/modules/core_syntax.h deleted file mode 100644 index beb8528..0000000 --- a/akonadi2_cli/modules/core_syntax.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2014 Aaron Seigo - * - * 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 2 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, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#pragma once - -#include "module.h" - -namespace CoreSyntax -{ - Module::SyntaxList syntax(); - bool exit(const QStringList &commands, State &state); - bool showHelp(const QStringList &commands, State &); -} - diff --git a/akonadi2_cli/repl/replStates.cpp b/akonadi2_cli/repl/replStates.cpp index 314cca8..0273aa2 100644 --- a/akonadi2_cli/repl/replStates.cpp +++ b/akonadi2_cli/repl/replStates.cpp @@ -29,7 +29,7 @@ #include #include -#include "module.h" +#include "syntaxtree.h" static char *akonadi2_cli_next_tab_complete_match(const char *text, int state); static char ** akonadi2_cli_tab_completion(const char *text, int start, int end); @@ -111,8 +111,8 @@ void EvalState::complete() if (!m_partial.isEmpty()) { //emit output("Processing ... " + command); - const QStringList commands = Module::tokenize(m_partial); - Module::self()->run(commands); + const QStringList commands = SyntaxTree::tokenize(m_partial); + SyntaxTree::self()->run(commands); m_partial.clear(); } @@ -149,7 +149,7 @@ static char **akonadi2_cli_tab_completion(const char *text, int start, int end) static char *akonadi2_cli_next_tab_complete_match(const char *text, int state) { - QVector nearest = Module::self()->nearestSyntax(tab_completion_full_state, QString(text)); + QVector nearest = SyntaxTree::self()->nearestSyntax(tab_completion_full_state, QString(text)); if (nearest.size() > state) { return qstrdup(nearest[state].keyword.toUtf8()); diff --git a/akonadi2_cli/syntax_modules/core_syntax.cpp b/akonadi2_cli/syntax_modules/core_syntax.cpp new file mode 100644 index 0000000..f71c1d6 --- /dev/null +++ b/akonadi2_cli/syntax_modules/core_syntax.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * 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 2 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, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "core_syntax.h" + +#include // tr() +#include +#include + +namespace CoreSyntax +{ + +SyntaxTree::SyntaxList syntax() +{ + SyntaxTree::SyntaxList syntax; + syntax << SyntaxTree::Syntax("exit", QObject::tr("Exits the application. Ctrl-d also works!"), &CoreSyntax::exit); + syntax << SyntaxTree::Syntax(QObject::tr("help"), QObject::tr("Print command information: help [command]"), &CoreSyntax::showHelp); + return syntax; +} + +bool exit(const QStringList &, State &) +{ + ::exit(0); + return true; +} + +bool showHelp(const QStringList &commands, State &state) +{ + SyntaxTree::Command command = SyntaxTree::self()->match(commands); + if (commands.isEmpty()) { + state.printLine(QObject::tr("Welcome to the Akonadi2 command line tool!")); + state.printLine(QObject::tr("Top-level commands:")); + + QSet sorted; + for (auto syntax: SyntaxTree::self()->syntax()) { + sorted.insert(syntax.keyword); + } + + for (auto keyword: sorted) { + state.printLine(keyword, 1); + } + } else if (const SyntaxTree::Syntax *syntax = command.first) { + //TODO: get parent! + state.print(QObject::tr("Command `%1`").arg(syntax->keyword)); + + if (!syntax->help.isEmpty()) { + state.print(": " + syntax->help); + } + state.printLine(); + + if (!syntax->children.isEmpty()) { + state.printLine("Sub-commands:", 1); + QSet sorted; + for (auto childSyntax: syntax->children) { + sorted.insert(childSyntax.keyword); + } + + for (auto keyword: sorted) { + state.printLine(keyword, 1); + } + } + } else { + state.printError("Unknown command: " + commands.join(" ")); + } + + return true; +} + +} // namespace CoreSyntax + diff --git a/akonadi2_cli/syntax_modules/core_syntax.h b/akonadi2_cli/syntax_modules/core_syntax.h new file mode 100644 index 0000000..0db6661 --- /dev/null +++ b/akonadi2_cli/syntax_modules/core_syntax.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * 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 2 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, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include "syntaxtree.h" + +namespace CoreSyntax +{ + SyntaxTree::SyntaxList syntax(); + bool exit(const QStringList &commands, State &state); + bool showHelp(const QStringList &commands, State &); +} + diff --git a/akonadi2_cli/syntaxtree.cpp b/akonadi2_cli/syntaxtree.cpp new file mode 100644 index 0000000..ea017db --- /dev/null +++ b/akonadi2_cli/syntaxtree.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * 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 2 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, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "syntaxtree.h" + +#include +#include + +// TODO: needs a proper registry; making "core" modules plugins is +// almost certainly overkill, but this is not the way either +#include "syntax_modules/core_syntax.h" + +SyntaxTree *SyntaxTree::s_module = 0; + +SyntaxTree::Syntax::Syntax() +{ +} + +SyntaxTree::Syntax::Syntax(const QString &k, const QString &helpText, std::function l, Interactivity inter) + : keyword(k), + help(helpText), + interactivity(inter), + lambda(l) +{ +} + +SyntaxTree::SyntaxTree() +{ + QVector > syntaxSyntaxTrees; + syntaxSyntaxTrees << &CoreSyntax::syntax; + for (auto syntaxSyntaxTree: syntaxSyntaxTrees) { + m_syntax += syntaxSyntaxTree(); + } +} + +SyntaxTree *SyntaxTree::self() +{ + if (!s_module) { + s_module = new SyntaxTree; + } + + return s_module; +} + +SyntaxTree::SyntaxList SyntaxTree::syntax() const +{ + return m_syntax; +} + +bool SyntaxTree::run(const QStringList &commands) +{ + Command command = match(commands); + if (command.first && command.first->lambda) { + command.first->lambda(command.second, m_state); + if (command.first->interactivity == Syntax::EventDriven) { + return QCoreApplication::instance()->exec(); + } + + return true; + } + + return false; +} + +SyntaxTree::Command SyntaxTree::match(const QStringList &commandLine) const +{ + if (commandLine.isEmpty()) { + return Command(); + } + + QStringListIterator commandLineIt(commandLine); + + QVectorIterator syntaxIt(m_syntax); + const Syntax *lastFullSyntax = 0; + QStringList tailCommands; + while (commandLineIt.hasNext() && syntaxIt.hasNext()) { + const QString word = commandLineIt.next(); + while (syntaxIt.hasNext()) { + const Syntax &syntax = syntaxIt.next(); + if (word == syntax.keyword) { + lastFullSyntax = &syntax; + syntaxIt = syntax.children; + } + } + + if (!syntaxIt.hasNext()) { + break; + } + } + + if (lastFullSyntax && lastFullSyntax->lambda) { + while (commandLineIt.hasNext()) { + tailCommands << commandLineIt.next(); + } + + return std::make_pair(lastFullSyntax, tailCommands); + } + + return Command(); +} + +SyntaxTree::SyntaxList SyntaxTree::nearestSyntax(const QStringList &words, const QString &fragment) const +{ + SyntaxList matches; + + //qDebug() << "words are" << words; + if (words.isEmpty()) { + for (const Syntax &syntax: m_syntax) { + if (syntax.keyword.startsWith(fragment)) { + matches.push_back(syntax); + } + } + } else { + QStringListIterator wordIt(words); + QVectorIterator syntaxIt(m_syntax); + Syntax lastFullSyntax; + + while (wordIt.hasNext()) { + QString word = wordIt.next(); + while (syntaxIt.hasNext()) { + const Syntax &syntax = syntaxIt.next(); + if (word == syntax.keyword) { + lastFullSyntax = syntax; + syntaxIt = syntax.children; + } + } + } + + //qDebug() << "exiting with" << lastFullSyntax.keyword << words.last(); + if (lastFullSyntax.keyword == words.last()) { + syntaxIt = lastFullSyntax.children; + while (syntaxIt.hasNext()) { + Syntax syntax = syntaxIt.next(); + if (fragment.isEmpty() || syntax.keyword.startsWith(fragment)) { + matches.push_back(syntax); + } + } + } + } + + return matches; +} + +QStringList SyntaxTree::tokenize(const QString &text) +{ + //TODO: properly tokenize (e.g. "foo bar" should not become ['"foo', 'bar"'] + return text.split(" "); +} + diff --git a/akonadi2_cli/syntaxtree.h b/akonadi2_cli/syntaxtree.h new file mode 100644 index 0000000..54b867f --- /dev/null +++ b/akonadi2_cli/syntaxtree.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * 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 2 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, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include "state.h" + +#include +#include + +class SyntaxTree +{ +public: + struct Syntax + { + enum Interactivity { + NotInteractive = 0, + EventDriven + }; + + Syntax(); + Syntax(const QString &keyword, + const QString &helpText = QString(), + std::function lambda = std::function(), + Interactivity interactivity = NotInteractive); + + QString keyword; + QString help; + Interactivity interactivity; + std::function lambda; + + QVector children; + }; + + typedef std::pair Command; + typedef QVector SyntaxList; + + static SyntaxTree *self(); + + SyntaxList syntax() const; + Command match(const QStringList &commands) const; + SyntaxList nearestSyntax(const QStringList &words, const QString &fragment) const; + + bool run(const QStringList &commands); + + static QStringList tokenize(const QString &text); + +private: + SyntaxTree(); + Command matches(const QStringList &commands) const; + + SyntaxList m_syntax; + State m_state; + static SyntaxTree *s_module; +}; + -- cgit v1.2.3