summaryrefslogtreecommitdiffstats
path: root/sinksh/syntaxtree.cpp
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2016-01-20 19:07:07 +0100
committerChristian Mollekopf <chrigi_1@fastmail.fm>2016-01-20 19:07:07 +0100
commitbdb01c2c068df326f5a8328ed1492ab1bea388c5 (patch)
tree25c2ee1b29bc481b6914c244ed9ca194b1415d16 /sinksh/syntaxtree.cpp
parent17e7ee40c9185c0505883853345fd6024c675b1a (diff)
downloadsink-bdb01c2c068df326f5a8328ed1492ab1bea388c5.tar.gz
sink-bdb01c2c068df326f5a8328ed1492ab1bea388c5.zip
Renamed Akonadi2 to Sink
(except for documentation).
Diffstat (limited to 'sinksh/syntaxtree.cpp')
-rw-r--r--sinksh/syntaxtree.cpp221
1 files changed, 221 insertions, 0 deletions
diff --git a/sinksh/syntaxtree.cpp b/sinksh/syntaxtree.cpp
new file mode 100644
index 0000000..4860582
--- /dev/null
+++ b/sinksh/syntaxtree.cpp
@@ -0,0 +1,221 @@
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
171State &SyntaxTree::state()
172{
173 return m_state;
174}
175
176QStringList SyntaxTree::tokenize(const QString &text)
177{
178 //TODO: properly tokenize (e.g. "foo bar" should not become ['"foo', 'bar"']a
179 static const QVector<QChar> quoters = QVector<QChar>() << '"' << '\'';
180 QStringList tokens;
181 QString acc;
182 QChar closer;
183 for (int i = 0; i < text.size(); ++i) {
184 const QChar c = text.at(i);
185 if (c == '\\') {
186 ++i;
187 if (i < text.size()) {
188 acc.append(text.at(i));
189 }
190 } else if (!closer.isNull()) {
191 if (c == closer) {
192 acc = acc.trimmed();
193 if (!acc.isEmpty()) {
194 tokens << acc;
195 }
196 acc.clear();
197 closer = QChar();
198 } else {
199 acc.append(c);
200 }
201 } else if (c.isSpace()) {
202 acc = acc.trimmed();
203 if (!acc.isEmpty()) {
204 tokens << acc;
205 }
206 acc.clear();
207 } else if (quoters.contains(c)) {
208 closer = c;
209 } else {
210 acc.append(c);
211 }
212 }
213
214 acc = acc.trimmed();
215 if (!acc.isEmpty()) {
216 tokens << acc;
217 }
218
219 return tokens;
220}
221