Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/console/src/core/CommandExecutor.cc @ 1390

Last change on this file since 1390 was 1390, checked in by landauf, 16 years ago

only girls sleep at this time :P

(started new implementation of ConsoleExecutor parser, but it's still buggy)

File size: 21.6 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "CommandExecutor.h"
30#include "ConsoleCommand.h"
31#include "util/String.h"
32#include "util/Convert.h"
33#include "Identifier.h"
34#include "Language.h"
35#include "Debug.h"
36#include "TclBind.h"
37
38namespace orxonox
39{
40    CommandExecutor& CommandExecutor::getInstance()
41    {
42        static CommandExecutor instance;
43        return instance;
44    }
45
46    CommandEvaluation& CommandExecutor::getEvaluation()
47    {
48        return CommandExecutor::getInstance().evaluation_;
49    }
50
51    const CommandEvaluation& CommandExecutor::getLastEvaluation()
52    {
53        return CommandExecutor::getInstance().evaluation_;
54    }
55
56    ConsoleCommand& CommandExecutor::addConsoleCommandShortcut(ConsoleCommand* command)
57    {
58        std::map<std::string, ConsoleCommand*>::const_iterator it = CommandExecutor::getInstance().consoleCommandShortcuts_.find(command->getName());
59        if (it != CommandExecutor::getInstance().consoleCommandShortcuts_.end())
60        {
61            COUT(2) << "Warning: Overwriting console-command shortcut with name " << command->getName() << "." << std::endl;
62        }
63
64
65        CommandExecutor::getInstance().consoleCommandShortcuts_[command->getName()] = command;
66        CommandExecutor::getInstance().consoleCommandShortcuts_LC_[getLowercase(command->getName())] = command;
67        return (*command);
68    }
69
70    /**
71        @brief Returns the executor of a console command shortcut with given name.
72        @brief name The name of the requested console command shortcut
73        @return The executor of the requested console command shortcut
74    */
75    ConsoleCommand* CommandExecutor::getConsoleCommandShortcut(const std::string& name)
76    {
77        std::map<std::string, ConsoleCommand*>::const_iterator it = CommandExecutor::getInstance().consoleCommandShortcuts_.find(name);
78        if (it != CommandExecutor::getInstance().consoleCommandShortcuts_.end())
79            return (*it).second;
80        else
81            return 0;
82    }
83
84    /**
85        @brief Returns the executor of a console command shortcut with given name in lowercase.
86        @brief name The name of the requested console command shortcut in lowercase
87        @return The executor of the requested console command shortcut
88    */
89    ConsoleCommand* CommandExecutor::getLowercaseConsoleCommandShortcut(const std::string& name)
90    {
91        std::map<std::string, ConsoleCommand*>::const_iterator it = CommandExecutor::getInstance().consoleCommandShortcuts_LC_.find(name);
92        if (it != CommandExecutor::getInstance().consoleCommandShortcuts_LC_.end())
93            return (*it).second;
94        else
95            return 0;
96    }
97
98    bool CommandExecutor::execute(const std::string& command, bool useTcl)
99    {
100        if (useTcl)
101            return TclBind::eval(command);
102
103        if ((CommandExecutor::getEvaluation().getCommand() != command) || (CommandExecutor::getEvaluation().getState() == CS_Uninitialized))
104            CommandExecutor::parse(command);
105
106        return CommandExecutor::getEvaluation().execute();
107    }
108
109    std::string CommandExecutor::complete(const std::string& command)
110    {
111        if ((CommandExecutor::getEvaluation().getCommand() != command) || (CommandExecutor::getEvaluation().getState() == CS_Uninitialized))
112            CommandExecutor::parse(command);
113
114        return CommandExecutor::getEvaluation().complete();
115    }
116
117    std::string CommandExecutor::hint(const std::string& command)
118    {
119        if ((CommandExecutor::getEvaluation().getCommand() != command) || (CommandExecutor::getEvaluation().getState() == CS_Uninitialized))
120            CommandExecutor::parse(command);
121
122        return CommandExecutor::getEvaluation().hint();
123    }
124
125    CommandEvaluation CommandExecutor::evaluate(const std::string& command)
126    {
127        CommandExecutor::parse(command, true);
128        CommandExecutor::getEvaluation().evaluateParams();
129        return CommandExecutor::getEvaluation();
130    }
131
132    void CommandExecutor::parse(const std::string& command, bool bInitialize)
133    {
134        if (bInitialize)
135            CommandExecutor::getEvaluation().initialize(command);
136        else
137            CommandExecutor::getEvaluation().setNewCommand(false);
138
139        CommandExecutor::getEvaluation().setTokens(command + COMMAND_EXECUTOR_CURSOR);
140        CommandExecutor::getEvaluation().setCommand(command);
141
142        switch (CommandExecutor::getEvaluation().getState())
143        {
144            case CS_Uninitialized:
145                // Impossible
146                break;
147            case CS_Empty:
148                CommandExecutor::createListOfPossibleIdentifiers(CommandExecutor::getArgument(0));
149                CommandExecutor::createListOfPossibleFunctions(CommandExecutor::getArgument(0));
150
151                if (CommandExecutor::argumentsGiven() > 0)
152                {
153                    CommandExecutor::getEvaluation().setState(CS_ShortcutOrIdentifier);
154                    CommandExecutor::parse(command, false);
155                    return;
156                }
157                break;
158            case CS_ShortcutOrIdentifier:
159                if (CommandExecutor::argumentsFinished() > 0 || !CommandExecutor::getEvaluation().isNewCommand())
160                {
161                    // There's already a finished first argument - check if it's function or a classname
162                    CommandExecutor::getEvaluation().setIdentifier(CommandExecutor::getPossibleIdentifier(CommandExecutor::getArgument(0)));
163                    CommandExecutor::getEvaluation().setFunction(CommandExecutor::getPossibleCommand(CommandExecutor::getArgument(0)));
164
165                    if (CommandExecutor::getEvaluation().getFunction())
166                    {
167                        // It's a shortcut - continue parsing
168                        CommandExecutor::getEvaluation().setState(CS_Shortcut_Params);
169                        if (CommandExecutor::argumentsFinished() > 0 )
170                            CommandExecutor::parse(command, false);
171                        else
172                            CommandExecutor::parse(command + " ", false);
173                        return;
174                    }
175                    else if (CommandExecutor::getEvaluation().getIdentifier())
176                    {
177                        // It's a classname - continue parsing
178                        CommandExecutor::getEvaluation().setState(CS_Function);
179                        if (CommandExecutor::argumentsFinished() > 0 )
180                            CommandExecutor::parse(command, false);
181                        else
182                            CommandExecutor::parse(command + " ", false);
183                        return;
184                    }
185                }
186
187                if (CommandExecutor::argumentsFinished() == 0)
188                {
189                    // There is no finished first argument
190                    unsigned int numIdentifiers = CommandExecutor::getEvaluation().getListOfPossibleIdentifiers().size();
191                    unsigned int numCommands = CommandExecutor::getEvaluation().getListOfPossibleFunctions().size();
192
193                    if (numCommands == 1 && numIdentifiers == 0)
194                    {
195                        // It must be this command
196                        const std::string* possibleCommand = (*CommandExecutor::getEvaluation().getListOfPossibleFunctions().begin()).second;
197                        CommandExecutor::getEvaluation().setState(CS_Shortcut_Params);
198                        CommandExecutor::getEvaluation().setFunction(CommandExecutor::getPossibleCommand(*possibleCommand));
199                        CommandExecutor::parse(*possibleCommand + " ", false);
200                        return;
201                    }
202                    else if (numIdentifiers == 1 && numCommands == 0)
203                    {
204                        // It must be this classname
205                        const std::string* possibleIdentifier = (*CommandExecutor::getEvaluation().getListOfPossibleIdentifiers().begin()).second;
206                        CommandExecutor::getEvaluation().setState(CS_Function);
207                        CommandExecutor::getEvaluation().setIdentifier(CommandExecutor::getPossibleIdentifier(*possibleIdentifier));
208                        CommandExecutor::parse(*possibleIdentifier + " ", false);
209                        return;
210                    }
211                }
212
213                // It's not a shortcut nor a classname
214                CommandExecutor::getEvaluation().setState(CS_Error);
215                AddLanguageEntry("commandexecutorunknownfirstargument", "is not a shortcut nor a classname");
216                CommandExecutor::getEvaluation().setError("Error: " + CommandExecutor::getArgument(0) + " " + GetLocalisation("commandexecutorunknownfirstargument") + ".");
217                break;
218            case CS_Function:
219                if (CommandExecutor::getEvaluation().getIdentifier() && CommandExecutor::argumentsGiven() > 1)
220                {
221                    if (CommandExecutor::argumentsFinished() > 1 || !CommandExecutor::getEvaluation().isNewCommand())
222                    {
223                        // There is already a second argument - check if it's a valid function
224                        CommandExecutor::getEvaluation().setFunction(CommandExecutor::getPossibleCommand(CommandExecutor::getArgument(1), CommandExecutor::getEvaluation().getIdentifier()));
225
226                        if (CommandExecutor::getEvaluation().getFunction())
227                        {
228                            // It's a shortcut - continue parsing
229                            CommandExecutor::getEvaluation().setState(CS_Function_Params);
230                            if (CommandExecutor::argumentsFinished() > 1 )
231                                CommandExecutor::parse(command, false);
232                            else
233                                CommandExecutor::parse(command + " ", false);
234                            return;
235                        }
236                    }
237
238                    if (CommandExecutor::argumentsFinished() <= 1)
239                    {
240                        // There is no finished second argument
241                        CommandExecutor::createListOfPossibleFunctions(CommandExecutor::getArgument(0), CommandExecutor::getEvaluation().getIdentifier());
242                        unsigned int numFunctions = CommandExecutor::getEvaluation().getListOfPossibleFunctions().size();
243
244                        if (numFunctions == 1)
245                        {
246                            // It must be this command
247                            const std::string* possibleCommand = (*CommandExecutor::getEvaluation().getListOfPossibleFunctions().begin()).second;
248                            CommandExecutor::getEvaluation().setState(CS_Function_Params);
249                            CommandExecutor::getEvaluation().setFunction(CommandExecutor::getPossibleCommand(*possibleCommand));
250                            CommandExecutor::parse(*possibleCommand + " ", false);
251                            return;
252                        }
253                    }
254
255                    // It's not a function
256                    AddLanguageEntry("commandexecutorunknowncommand", "is not a valid commandname");
257                    CommandExecutor::getEvaluation().setError("Error: " + CommandExecutor::getArgument(1) + " " + GetLocalisation("commandexecutorunknowncommand") + ".");
258                }
259
260                // Bad state
261                CommandExecutor::getEvaluation().setState(CS_Error);
262                break;
263            case CS_Shortcut_Params:
264            case CS_Function_Params:
265                if (CommandExecutor::getEvaluation().getFunction())
266                {
267                    unsigned int startindex = 0;
268                    if (CommandExecutor::getEvaluation().getState() == CS_Shortcut_Params)
269                        startindex = 1;
270                    else if (CommandExecutor::getEvaluation().getState() == CS_Function_Params)
271                        startindex = 2;
272
273                    if (CommandExecutor::argumentsGiven() >= startindex)
274                    {
275                        if (CommandExecutor::enoughArgumentsGiven(CommandExecutor::getEvaluation().getFunction()))
276                        {
277                            if (CommandExecutor::getEvaluation().getState() == CS_Shortcut_Params)
278                                CommandExecutor::getEvaluation().setState(CS_Shortcut_Finished);
279                            else if (CommandExecutor::getEvaluation().getState() == CS_Function_Params)
280                                CommandExecutor::getEvaluation().setState(CS_Function_Finished);
281                            return;
282                        }
283                        else
284                        {
285                            CommandExecutor::createListOfPossibleArguments(CommandExecutor::getLastArgument(), CommandExecutor::getEvaluation().getFunction(), CommandExecutor::getEvaluation().getOriginalTokens().size() - startindex);
286                            unsigned int numArguments = CommandExecutor::getEvaluation().getListOfPossibleArguments().size();
287
288                            if (numArguments == 1)
289                            {
290                                // There is exactly one possible argument
291                                const std::string* possibleArgument = (*CommandExecutor::getEvaluation().getListOfPossibleArguments().begin()).second;
292                                CommandExecutor::parse(CommandExecutor::getEvaluation().getTokens().subSet(0, CommandExecutor::getEvaluation().getTokens().size() - 1 - startindex).join() + " " + (*possibleArgument) + " ");
293                                return;
294                            }
295
296                            if (!CommandExecutor::getEvaluation().isNewCommand())
297                            {
298                                // There is more than one argument, but the user wants to use this - check if there is a perfect match
299                                const std::string* possibleArgument = CommandExecutor::getPossibleArgument(CommandExecutor::getLastArgument(), CommandExecutor::getEvaluation().getFunction(), CommandExecutor::getEvaluation().getOriginalTokens().size() - startindex);
300                                if (possibleArgument)
301                                {
302                                    // There is such an argument - use it
303                                    CommandExecutor::parse(command + " ", false);
304                                    return;
305                                }
306                            }
307                        }
308                    }
309                }
310
311                // Bad state
312                CommandExecutor::getEvaluation().setState(CS_Error);
313                break;
314            case CS_Shortcut_Finished:
315            case CS_Function_Finished:
316            case CS_Error:
317                break;
318        }
319    }
320
321    unsigned int CommandExecutor::argumentsFinished()
322    {
323        // Because we added a cursor we have +1 arguments
324        if (CommandExecutor::getEvaluation().getTokens().size() >= 1)
325            return (CommandExecutor::getEvaluation().getTokens().size() - 1);
326        else
327            return 0;
328    }
329
330    unsigned int CommandExecutor::argumentsGiven()
331    {
332        return CommandExecutor::getEvaluation().getOriginalTokens().size();
333    }
334
335    bool CommandExecutor::enoughArgumentsGiven(ConsoleCommand* command)
336    {
337        if (CommandExecutor::getEvaluation().getIdentifier())
338            return (CommandExecutor::argumentsGiven() >= (2 + command->getParamCount()));
339        else
340            return (CommandExecutor::argumentsGiven() >= (1 + command->getParamCount()));
341    }
342
343    std::string CommandExecutor::getArgument(unsigned int index)
344    {
345        if ((index >= 0) && index < (CommandExecutor::getEvaluation().getOriginalTokens().size()))
346            return CommandExecutor::getEvaluation().getOriginalTokens()[index];
347        else
348            return "";
349    }
350
351    std::string CommandExecutor::getLastArgument()
352    {
353        if (CommandExecutor::getEvaluation().getOriginalTokens().size() > 0)
354            return CommandExecutor::getEvaluation().getOriginalTokens()[0];
355        else
356            return "";
357    }
358
359    void CommandExecutor::createListOfPossibleIdentifiers(const std::string& fragment)
360    {
361        for (std::map<std::string, Identifier*>::const_iterator it = Identifier::getLowercaseIdentifierMapBegin(); it != Identifier::getLowercaseIdentifierMapEnd(); ++it)
362            if ((*it).second->hasConsoleCommands())
363                if ((*it).first.find(getLowercase(fragment)) == 0 || fragment == "")
364                    CommandExecutor::getEvaluation().getListOfPossibleIdentifiers().push_back(std::pair<const std::string*, const std::string*>(&(*it).first, &(*it).second->getName()));
365
366        CommandExecutor::getEvaluation().getListOfPossibleIdentifiers().sort(CommandExecutor::compareStringsInList);
367    }
368
369    void CommandExecutor::createListOfPossibleFunctions(const std::string& fragment, Identifier* identifier)
370    {
371        if (!identifier)
372        {
373            for (std::map<std::string, ConsoleCommand*>::const_iterator it = CommandExecutor::getLowercaseConsoleCommandShortcutMapBegin(); it != CommandExecutor::getLowercaseConsoleCommandShortcutMapEnd(); ++it)
374                if ((*it).first.find(getLowercase(fragment)) == 0 || fragment == "")
375                    CommandExecutor::getEvaluation().getListOfPossibleFunctions().push_back(std::pair<const std::string*, const std::string*>(&(*it).first, &(*it).second->getName()));
376        }
377        else
378        {
379            for (std::map<std::string, ConsoleCommand*>::const_iterator it = identifier->getLowercaseConsoleCommandMapBegin(); it != identifier->getLowercaseConsoleCommandMapEnd(); ++it)
380                if ((*it).first.find(getLowercase(fragment)) == 0 || fragment == "")
381                    CommandExecutor::getEvaluation().getListOfPossibleFunctions().push_back(std::pair<const std::string*, const std::string*>(&(*it).first, &(*it).second->getName()));
382        }
383        CommandExecutor::getEvaluation().getListOfPossibleFunctions().sort(CommandExecutor::compareStringsInList);
384    }
385
386    void CommandExecutor::createListOfPossibleArguments(const std::string& fragment, ConsoleCommand* command, unsigned int param)
387    {
388        for (std::list<std::pair<std::string, std::string> >::const_iterator it = command->getArgumentCompletionListBegin(param); it != command->getArgumentCompletionListEnd(param); ++it)
389            if ((*it).first.find(getLowercase(fragment)) == 0 || fragment == "")
390                CommandExecutor::getEvaluation().getListOfPossibleArguments().push_back(std::pair<const std::string*, const std::string*>(&(*it).first, &(*it).second));
391
392        CommandExecutor::getEvaluation().getListOfPossibleIdentifiers().sort(CommandExecutor::compareStringsInList);
393
394    }
395
396    Identifier* CommandExecutor::getPossibleIdentifier(const std::string& name)
397    {
398        std::map<std::string, Identifier*>::const_iterator it = Identifier::getLowercaseIdentifierMap().find(getLowercase(name));
399        if ((it != Identifier::getLowercaseIdentifierMapEnd()) && (*it).second->hasConsoleCommands())
400            return (*it).second;
401
402        return 0;
403    }
404
405    ConsoleCommand* CommandExecutor::getPossibleCommand(const std::string& name, Identifier* identifier)
406    {
407        if (!identifier)
408        {
409            std::map<std::string, ConsoleCommand*>::const_iterator it = CommandExecutor::getLowercaseConsoleCommandShortcutMap().find(getLowercase(name));
410            if (it != CommandExecutor::getLowercaseConsoleCommandShortcutMapEnd())
411                return (*it).second;
412        }
413        else
414        {
415            std::map<std::string, ConsoleCommand*>::const_iterator it = identifier->getLowercaseConsoleCommandMap().find(getLowercase(name));
416            if (it != identifier->getLowercaseConsoleCommandMapEnd())
417                return (*it).second;
418        }
419        return 0;
420    }
421
422    const std::string* CommandExecutor::getPossibleArgument(const std::string& name, ConsoleCommand* command, unsigned int param)
423    {
424        std::string lowercasename = getLowercase(name);
425        for (std::list<std::pair<std::string, std::string> >::const_iterator it = command->getArgumentCompletionListBegin(param); it != command->getArgumentCompletionListEnd(param); ++it)
426            if ((*it).first == lowercasename)
427                return &(*it).second;
428
429        return 0;
430    }
431
432    bool CommandExecutor::compareStringsInList(const std::pair<const std::string*, const std::string*>& first, const std::pair<const std::string*, const std::string*>& second)
433    {
434        return ((*first.first) < (*second.first));
435    }
436}
Note: See TracBrowser for help on using the repository browser.