summaryrefslogtreecommitdiffstats
path: root/akonadish/syntaxtree.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'akonadish/syntaxtree.cpp')
-rw-r--r--akonadish/syntaxtree.cpp216
1 files changed, 216 insertions, 0 deletions
diff --git a/akonadish/syntaxtree.cpp b/akonadish/syntaxtree.cpp
new file mode 100644
index 0000000..495ad22
--- /dev/null
+++ b/akonadish/syntaxtree.cpp
@@ -0,0 +1,216 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "syntaxtree.h"
21
22#include <QCoreApplication>
23#include <QDebug>
24
25SyntaxTree *SyntaxTree::s_module = 0;
26
27Syntax::Syntax()
28{
29}
30
31Syntax::Syntax(const QString &k, const QString &helpText, std::function<bool(const QStringList &, State &)> l, Interactivity inter)
32 : keyword(k),
33 help(helpText),
34 interactivity(inter),
35 lambda(l)
36{
37}
38
39SyntaxTree::SyntaxTree()
40{
41}
42
43int SyntaxTree::registerSyntax(std::function<Syntax::List()> f)
44{
45 m_syntax += f();
46 return m_syntax.size();
47}
48
49SyntaxTree *SyntaxTree::self()
50{
51 if (!s_module) {
52 s_module = new SyntaxTree;
53 }
54
55 return s_module;
56}
57
58Syntax::List SyntaxTree::syntax() const
59{
60 return m_syntax;
61}
62
63bool SyntaxTree::run(const QStringList &commands)
64{
65 bool success = false;
66 m_timeElapsed.start();
67 Command command = match(commands);
68 if (command.first) {
69 if (command.first->lambda) {
70 success = command.first->lambda(command.second, m_state);
71 if (success && command.first->interactivity == Syntax::EventDriven) {
72 success = m_state.commandStarted();
73 }
74 } else if (command.first->children.isEmpty()) {
75 m_state.printError(QObject::tr("Broken command... sorry :("), "st_broken");
76 } else {
77 QStringList keywordList;
78 for (auto syntax: command.first->children) {
79 keywordList << syntax.keyword;
80 }
81 const QString keywords = keywordList.join(" " );
82 m_state.printError(QObject::tr("Command requires additional arguments, one of: %1").arg(keywords));
83 }
84 } else {
85 m_state.printError(QObject::tr("Unknown command"), "st_unknown");
86 }
87
88 if (m_state.commandTiming()) {
89 m_state.printLine(QObject::tr("Time elapsed: %1").arg(m_timeElapsed.elapsed()));
90 }
91 return false;
92}
93
94SyntaxTree::Command SyntaxTree::match(const QStringList &commandLine) const
95{
96 if (commandLine.isEmpty()) {
97 return Command();
98 }
99
100 QStringListIterator commandLineIt(commandLine);
101
102 QVectorIterator<Syntax> syntaxIt(m_syntax);
103 const Syntax *lastFullSyntax = 0;
104 QStringList tailCommands;
105 while (commandLineIt.hasNext() && syntaxIt.hasNext()) {
106 const QString word = commandLineIt.next();
107 while (syntaxIt.hasNext()) {
108 const Syntax &syntax = syntaxIt.next();
109 if (word == syntax.keyword) {
110 lastFullSyntax = &syntax;
111 syntaxIt = syntax.children;
112 break;
113 }
114 }
115 }
116
117 if (lastFullSyntax) {
118 while (commandLineIt.hasNext()) {
119 tailCommands << commandLineIt.next();
120 }
121
122 return std::make_pair(lastFullSyntax, tailCommands);
123 }
124
125 return Command();
126}
127
128Syntax::List SyntaxTree::nearestSyntax(const QStringList &words, const QString &fragment) const
129{
130 Syntax::List matches;
131
132 //qDebug() << "words are" << words;
133 if (words.isEmpty()) {
134 for (const Syntax &syntax: m_syntax) {
135 if (syntax.keyword.startsWith(fragment)) {
136 matches.push_back(syntax);
137 }
138 }
139 } else {
140 QStringListIterator wordIt(words);
141 QVectorIterator<Syntax> syntaxIt(m_syntax);
142 Syntax lastFullSyntax;
143
144 while (wordIt.hasNext()) {
145 const QString &word = wordIt.next();
146 while (syntaxIt.hasNext()) {
147 const Syntax &syntax = syntaxIt.next();
148 if (word == syntax.keyword) {
149 lastFullSyntax = syntax;
150 syntaxIt = syntax.children;
151 break;
152 }
153 }
154 }
155
156 //qDebug() << "exiting with" << lastFullSyntax.keyword << words.last();
157 if (lastFullSyntax.keyword == words.last()) {
158 syntaxIt = lastFullSyntax.children;
159 while (syntaxIt.hasNext()) {
160 Syntax syntax = syntaxIt.next();
161 if (fragment.isEmpty() || syntax.keyword.startsWith(fragment)) {
162 matches.push_back(syntax);
163 }
164 }
165 }
166 }
167
168 return matches;
169}
170
171QStringList SyntaxTree::tokenize(const QString &text)
172{
173 //TODO: properly tokenize (e.g. "foo bar" should not become ['"foo', 'bar"']a
174 static const QVector<QChar> quoters = QVector<QChar>() << '"' << '\'';
175 QStringList tokens;
176 QString acc;
177 QChar closer;
178 for (int i = 0; i < text.size(); ++i) {
179 const QChar c = text.at(i);
180 if (c == '\\') {
181 ++i;
182 if (i < text.size()) {
183 acc.append(text.at(i));
184 }
185 } else if (!closer.isNull()) {
186 if (c == closer) {
187 acc = acc.trimmed();
188 if (!acc.isEmpty()) {
189 tokens << acc;
190 }
191 acc.clear();
192 closer = QChar();
193 } else {
194 acc.append(c);
195 }
196 } else if (c.isSpace()) {
197 acc = acc.trimmed();
198 if (!acc.isEmpty()) {
199 tokens << acc;
200 }
201 acc.clear();
202 } else if (quoters.contains(c)) {
203 closer = c;
204 } else {
205 acc.append(c);
206 }
207 }
208
209 acc = acc.trimmed();
210 if (!acc.isEmpty()) {
211 tokens << acc;
212 }
213
214 return tokens;
215}
216