Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

changes in CommandExecutor and CommandEvaluation

File size: 24.8 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        CommandExecutor::parseIfNeeded(command);
104        return CommandExecutor::getEvaluation().execute();
105    }
106
107    std::string CommandExecutor::complete(const std::string& command)
108    {
109        CommandExecutor::parseIfNeeded(command);
110
111        if (!CommandExecutor::getEvaluation().bNewCommand_)
112            CommandExecutor::parse(CommandExecutor::getEvaluation().command_, false);
113        else
114            CommandExecutor::getEvaluation().bNewCommand_ = false;
115
116        return CommandExecutor::getEvaluation().complete();
117    }
118
119    std::string CommandExecutor::hint(const std::string& command)
120    {
121        CommandExecutor::parseIfNeeded(command);
122        return CommandExecutor::getEvaluation().hint();
123    }
124
125    CommandEvaluation CommandExecutor::evaluate(const std::string& command)
126    {
127        CommandExecutor::parse(command);
128        CommandExecutor::getEvaluation().evaluateParams();
129        return CommandExecutor::getEvaluation();
130    }
131
132    void CommandExecutor::parseIfNeeded(const std::string& command)
133    {
134        if ((CommandExecutor::getEvaluation().originalCommand_ != command) || (CommandExecutor::getEvaluation().state_ == CS_Uninitialized))
135            CommandExecutor::parse(command);
136    }
137
138    void CommandExecutor::parse(const std::string& command, bool bInitialize)
139    {
140std::cout << "parse: command: >" << command << "<" << std::endl;
141        if (bInitialize)
142            CommandExecutor::getEvaluation().initialize(command);
143
144        CommandExecutor::getEvaluation().commandTokens_.split(command, " ", SubString::WhiteSpaces, false, '\\', false, '"', false, '(', ')', false, '\0');
145        CommandExecutor::getEvaluation().command_ = command;
146
147        switch (CommandExecutor::getEvaluation().state_)
148        {
149            case CS_Uninitialized:
150            {
151std::cout << "parse: state: CS_Uninitialized" << std::endl;
152                // Impossible
153                break;
154            }
155            case CS_Empty:
156            {
157std::cout << "parse: state: CS_Empty" << std::endl;
158                CommandExecutor::createListOfPossibleIdentifiers(CommandExecutor::getArgument(0));
159                CommandExecutor::createListOfPossibleFunctions(CommandExecutor::getArgument(0));
160
161                if (CommandExecutor::argumentsGiven() > 0)
162                {
163                    CommandExecutor::getEvaluation().state_ = CS_ShortcutOrIdentifier;
164                    CommandExecutor::parse(command, false);
165                    return;
166                }
167                break;
168            }
169            case CS_ShortcutOrIdentifier:
170            {
171std::cout << "parse: state: CS_ShortcutOrIdentifier" << std::endl;
172                if (CommandExecutor::argumentsFinished() > 0 || !CommandExecutor::getEvaluation().bNewCommand_)
173                {
174                    // There's already a finished first argument - check if it's function or a classname
175                    CommandExecutor::getEvaluation().functionclass_ = CommandExecutor::getPossibleIdentifier(CommandExecutor::getArgument(0));
176                    CommandExecutor::getEvaluation().function_ = CommandExecutor::getPossibleCommand(CommandExecutor::getArgument(0));
177
178                    if (CommandExecutor::getEvaluation().function_)
179                    {
180                        // It's a shortcut - continue parsing
181                        CommandExecutor::getEvaluation().state_ = CS_Shortcut_Params;
182                        if (CommandExecutor::argumentsFinished() > 0 )
183                            CommandExecutor::parse(command, false);
184                        else
185                            CommandExecutor::parse(command + " ", false);
186                        return;
187                    }
188                    else if (CommandExecutor::getEvaluation().functionclass_)
189                    {
190                        // It's a classname - continue parsing
191                        CommandExecutor::getEvaluation().state_ = CS_Function;
192                        if (CommandExecutor::argumentsFinished() > 0 )
193                            CommandExecutor::parse(command, false);
194                        else
195                            CommandExecutor::parse(command + " ", false);
196                        return;
197                    }
198                }
199
200                unsigned int numIdentifiers = CommandExecutor::getEvaluation().listOfPossibleIdentifiers_.size();
201                unsigned int numCommands = CommandExecutor::getEvaluation().listOfPossibleFunctions_.size();
202
203                if (CommandExecutor::argumentsFinished() == 0)
204                {
205                    // There is no finished first argument
206                    if (numCommands == 1 && numIdentifiers == 0)
207                    {
208                        // It must be this command
209                        const std::string* possibleCommand = (*CommandExecutor::getEvaluation().listOfPossibleFunctions_.begin()).second;
210                        CommandExecutor::getEvaluation().state_ = CS_Shortcut_Params;
211                        CommandExecutor::getEvaluation().function_ = CommandExecutor::getPossibleCommand(*possibleCommand);
212                        CommandExecutor::parse(*possibleCommand + " ", false);
213                        return;
214                    }
215                    else if (numIdentifiers == 1 && numCommands == 0)
216                    {
217                        // It must be this classname
218                        const std::string* possibleIdentifier = (*CommandExecutor::getEvaluation().listOfPossibleIdentifiers_.begin()).second;
219                        CommandExecutor::getEvaluation().state_ = CS_Function;
220                        CommandExecutor::getEvaluation().functionclass_ = CommandExecutor::getPossibleIdentifier(*possibleIdentifier);
221                        CommandExecutor::parse(*possibleIdentifier + " ", false);
222                        return;
223                    }
224                }
225
226                if (numCommands == 0 && numIdentifiers == 0)
227                {
228                    // It's not a shortcut nor a classname
229                    CommandExecutor::getEvaluation().state_ = CS_Error;
230                    AddLanguageEntry("commandexecutorunknownfirstargument", "is not a shortcut nor a classname");
231                    CommandExecutor::getEvaluation().errorMessage_ = "Error: " + CommandExecutor::getArgument(0) + " " + GetLocalisation("commandexecutorunknownfirstargument") + ".";
232                }
233                break;
234            }
235            case CS_Function:
236std::cout << "parse: state: CS_Function" << std::endl;
237            {
238                if (CommandExecutor::getEvaluation().functionclass_ && CommandExecutor::argumentsGiven() > 0)
239                {
240                    if (CommandExecutor::argumentsFinished() > 1 || !CommandExecutor::getEvaluation().bNewCommand_)
241                    {
242                        // There is already a second argument - check if it's a valid function
243                        CommandExecutor::getEvaluation().function_ = CommandExecutor::getPossibleCommand(CommandExecutor::getArgument(1), CommandExecutor::getEvaluation().functionclass_);
244
245                        if (CommandExecutor::getEvaluation().function_)
246                        {
247                            // It's a shortcut - continue parsing
248                            CommandExecutor::getEvaluation().state_ = CS_Function_Params;
249                            if (CommandExecutor::argumentsFinished() > 1 )
250                                CommandExecutor::parse(command, false);
251                            else
252                                CommandExecutor::parse(command + " ", false);
253                            return;
254                        }
255                        else if (CommandExecutor::argumentsFinished() > 1)
256                        {
257                            // It's not a function
258                            AddLanguageEntry("commandexecutorunknowncommand", "is not a valid commandname");
259                            CommandExecutor::getEvaluation().errorMessage_ = "Error: " + CommandExecutor::getArgument(1) + " " + GetLocalisation("commandexecutorunknowncommand") + ".";
260                            CommandExecutor::getEvaluation().state_ = CS_Error;
261                        }
262                    }
263
264                    CommandExecutor::createListOfPossibleFunctions(CommandExecutor::getArgument(1), CommandExecutor::getEvaluation().functionclass_);
265
266                    if (CommandExecutor::argumentsFinished() <= 1)
267                    {
268                        // There is no finished second argument
269                        unsigned int numFunctions = CommandExecutor::getEvaluation().listOfPossibleFunctions_.size();
270
271                        if (numFunctions == 1)
272                        {
273                            // It must be this command
274                            const std::string* possibleCommand = (*CommandExecutor::getEvaluation().listOfPossibleFunctions_.begin()).second;
275                            CommandExecutor::getEvaluation().state_ = CS_Function_Params;
276                            CommandExecutor::getEvaluation().function_ = CommandExecutor::getPossibleCommand(*possibleCommand, CommandExecutor::getEvaluation().functionclass_);
277                            CommandExecutor::parse(CommandExecutor::getArgument(0) + " " + *possibleCommand + " ", false);
278                            return;
279                        }
280                        else if (numFunctions == 0)
281                        {
282                            // It's not a function
283                            AddLanguageEntry("commandexecutorunknowncommand", "is not a valid commandname");
284                            CommandExecutor::getEvaluation().errorMessage_ = "Error: " + CommandExecutor::getArgument(1) + " " + GetLocalisation("commandexecutorunknowncommand") + ".";
285                            CommandExecutor::getEvaluation().state_ = CS_Error;
286                        }
287                    }
288
289                    // It's ambiguous
290                    return;
291                }
292
293                // Bad state
294                CommandExecutor::getEvaluation().state_ = CS_Error;
295                break;
296            }
297            case CS_Shortcut_Params:
298std::cout << "parse: state: CS_Shortcut_Params" << std::endl;
299            case CS_Function_Params:
300            {
301std::cout << "parse: state: CS_Function_Params" << std::endl;
302                if (CommandExecutor::getEvaluation().function_)
303                {
304                    unsigned int startindex = 0;
305                    if (CommandExecutor::getEvaluation().state_ == CS_Shortcut_Params)
306                        startindex = 1;
307                    else if (CommandExecutor::getEvaluation().state_ == CS_Function_Params)
308                        startindex = 2;
309
310                    if (CommandExecutor::argumentsGiven() >= startindex)
311                    {
312                        if ((CommandExecutor::argumentsGiven() == CommandExecutor::argumentsFinished() || !CommandExecutor::getEvaluation().bNewCommand_) && CommandExecutor::enoughArgumentsGiven(CommandExecutor::getEvaluation().function_))
313                        {
314                            if (CommandExecutor::getEvaluation().state_ == CS_Shortcut_Params)
315                                CommandExecutor::getEvaluation().state_ = CS_Shortcut_Finished;
316                            else if (CommandExecutor::getEvaluation().state_ == CS_Function_Params)
317                                CommandExecutor::getEvaluation().state_ = CS_Function_Finished;
318
319                            return;
320                        }
321                        else
322                        {
323                            CommandExecutor::createListOfPossibleArguments(CommandExecutor::getLastArgument(), CommandExecutor::getEvaluation().function_, CommandExecutor::getEvaluation().commandTokens_.size() - startindex);
324                            unsigned int numArguments = CommandExecutor::getEvaluation().listOfPossibleArguments_.size();
325
326                            if (numArguments == 1)
327                            {
328                                // There is exactly one possible argument
329                                const std::string* possibleArgument = (*CommandExecutor::getEvaluation().listOfPossibleArguments_.begin()).second;
330                                if (CommandExecutor::argumentsGiven() > CommandExecutor::argumentsFinished())
331                                    CommandExecutor::parse(CommandExecutor::getEvaluation().commandTokens_.subSet(0, CommandExecutor::getEvaluation().commandTokens_.size() - 1).join() + " " + (*possibleArgument) + " ", false);
332                                else
333                                    CommandExecutor::parse(CommandExecutor::getEvaluation().commandTokens_.subSet(0, CommandExecutor::getEvaluation().commandTokens_.size()).join() + " " + (*possibleArgument) + " ", false);
334
335                                return;
336                            }
337
338                            if ((CommandExecutor::argumentsGiven() > CommandExecutor::argumentsFinished()) && (!CommandExecutor::getEvaluation().bNewCommand_))
339                            {
340                                // There is more than one argument, but the user wants to use this - check if there is a perfect match
341                                const std::string* possibleArgument = CommandExecutor::getPossibleArgument(CommandExecutor::getLastArgument(), CommandExecutor::getEvaluation().function_, CommandExecutor::getEvaluation().commandTokens_.size() - startindex);
342                                if (possibleArgument)
343                                {
344                                    // There is such an argument - use it
345                                    CommandExecutor::parse(command + " ", false);
346                                    return;
347                                }
348                            }
349                        }
350
351                        // Nothing to do
352                        return;
353                    }
354                }
355
356                // Bad state
357                CommandExecutor::getEvaluation().state_ = CS_Error;
358                break;
359            }
360            case CS_Shortcut_Finished:
361std::cout << "parse: state: CS_Shortcut_Finished" << std::endl;
362                break;
363            case CS_Function_Finished:
364std::cout << "parse: state: CS_Function_Finished" << std::endl;
365                break;
366            case CS_Error:
367std::cout << "parse: state: CS_Error" << std::endl;
368                break;
369        }
370    }
371
372    unsigned int CommandExecutor::argumentsFinished()
373    {
374        if (CommandExecutor::getEvaluation().command_.size() > 0)
375        {
376            if (CommandExecutor::getEvaluation().command_[CommandExecutor::getEvaluation().command_.size() - 1] == ' ')
377                return CommandExecutor::getEvaluation().commandTokens_.size();
378            else if (CommandExecutor::getEvaluation().commandTokens_.size() > 0)
379                return CommandExecutor::getEvaluation().commandTokens_.size() - 1;
380        }
381        return 0;
382    }
383
384    unsigned int CommandExecutor::argumentsGiven()
385    {
386        return CommandExecutor::getEvaluation().commandTokens_.size();
387    }
388
389    bool CommandExecutor::enoughArgumentsGiven(ConsoleCommand* command)
390    {
391        if (CommandExecutor::getEvaluation().functionclass_)
392            return (CommandExecutor::argumentsGiven() >= (2 + command->getParamCount()));
393        else
394            return (CommandExecutor::argumentsGiven() >= (1 + command->getParamCount()));
395    }
396
397    std::string CommandExecutor::getArgument(unsigned int index)
398    {
399        if ((index >= 0) && index < (CommandExecutor::getEvaluation().commandTokens_.size()))
400            return CommandExecutor::getEvaluation().commandTokens_[index];
401        else
402            return "";
403    }
404
405    std::string CommandExecutor::getLastArgument()
406    {
407        if (CommandExecutor::getEvaluation().commandTokens_.size() > 0)
408            if (CommandExecutor::getEvaluation().commandTokens_.size() > 0 && CommandExecutor::getEvaluation().command_[CommandExecutor::getEvaluation().command_.size() - 1] != ' ')
409                return CommandExecutor::getEvaluation().commandTokens_[CommandExecutor::getEvaluation().commandTokens_.size() - 1];
410
411        return "";
412    }
413
414    void CommandExecutor::createListOfPossibleIdentifiers(const std::string& fragment)
415    {
416        CommandExecutor::getEvaluation().listOfPossibleIdentifiers_.clear();
417        std::string lowercase = getLowercase(fragment);
418        for (std::map<std::string, Identifier*>::const_iterator it = Identifier::getLowercaseIdentifierMapBegin(); it != Identifier::getLowercaseIdentifierMapEnd(); ++it)
419            if ((*it).second->hasConsoleCommands())
420                if ((*it).first.find(lowercase) == 0 || fragment == "")
421                    CommandExecutor::getEvaluation().listOfPossibleIdentifiers_.push_back(std::pair<const std::string*, const std::string*>(&(*it).first, &(*it).second->getName()));
422
423        CommandExecutor::getEvaluation().listOfPossibleIdentifiers_.sort(CommandExecutor::compareStringsInList);
424    }
425
426    void CommandExecutor::createListOfPossibleFunctions(const std::string& fragment, Identifier* identifier)
427    {
428        CommandExecutor::getEvaluation().listOfPossibleFunctions_.clear();
429        std::string lowercase = getLowercase(fragment);
430        if (!identifier)
431        {
432            for (std::map<std::string, ConsoleCommand*>::const_iterator it = CommandExecutor::getLowercaseConsoleCommandShortcutMapBegin(); it != CommandExecutor::getLowercaseConsoleCommandShortcutMapEnd(); ++it)
433                if ((*it).first.find(lowercase) == 0 || fragment == "")
434                    CommandExecutor::getEvaluation().listOfPossibleFunctions_.push_back(std::pair<const std::string*, const std::string*>(&(*it).first, &(*it).second->getName()));
435        }
436        else
437        {
438            for (std::map<std::string, ConsoleCommand*>::const_iterator it = identifier->getLowercaseConsoleCommandMapBegin(); it != identifier->getLowercaseConsoleCommandMapEnd(); ++it)
439                if ((*it).first.find(lowercase) == 0 || fragment == "")
440                    CommandExecutor::getEvaluation().listOfPossibleFunctions_.push_back(std::pair<const std::string*, const std::string*>(&(*it).first, &(*it).second->getName()));
441        }
442
443        CommandExecutor::getEvaluation().listOfPossibleFunctions_.sort(CommandExecutor::compareStringsInList);
444    }
445
446    void CommandExecutor::createListOfPossibleArguments(const std::string& fragment, ConsoleCommand* command, unsigned int param)
447    {
448        CommandExecutor::getEvaluation().listOfPossibleArguments_.clear();
449        std::string lowercase = getLowercase(fragment);
450        for (std::list<std::pair<std::string, std::string> >::const_iterator it = command->getArgumentCompletionListBegin(param); it != command->getArgumentCompletionListEnd(param); ++it)
451            if ((*it).first.find(lowercase) == 0 || fragment == "")
452                CommandExecutor::getEvaluation().listOfPossibleArguments_.push_back(std::pair<const std::string*, const std::string*>(&(*it).first, &(*it).second));
453
454        CommandExecutor::getEvaluation().listOfPossibleArguments_.sort(CommandExecutor::compareStringsInList);
455    }
456
457    Identifier* CommandExecutor::getPossibleIdentifier(const std::string& name)
458    {
459        std::string lowercase = getLowercase(name);
460        std::map<std::string, Identifier*>::const_iterator it = Identifier::getLowercaseIdentifierMap().find(lowercase);
461        if ((it != Identifier::getLowercaseIdentifierMapEnd()) && (*it).second->hasConsoleCommands())
462            return (*it).second;
463
464        return 0;
465    }
466
467    ConsoleCommand* CommandExecutor::getPossibleCommand(const std::string& name, Identifier* identifier)
468    {
469        std::string lowercase = getLowercase(name);
470        if (!identifier)
471        {
472            std::map<std::string, ConsoleCommand*>::const_iterator it = CommandExecutor::getLowercaseConsoleCommandShortcutMap().find(lowercase);
473            if (it != CommandExecutor::getLowercaseConsoleCommandShortcutMapEnd())
474                return (*it).second;
475        }
476        else
477        {
478            std::map<std::string, ConsoleCommand*>::const_iterator it = identifier->getLowercaseConsoleCommandMap().find(lowercase);
479            if (it != identifier->getLowercaseConsoleCommandMapEnd())
480                return (*it).second;
481        }
482        return 0;
483    }
484
485    const std::string* CommandExecutor::getPossibleArgument(const std::string& name, ConsoleCommand* command, unsigned int param)
486    {
487        std::string lowercase = getLowercase(name);
488        for (std::list<std::pair<std::string, std::string> >::const_iterator it = command->getArgumentCompletionListBegin(param); it != command->getArgumentCompletionListEnd(param); ++it)
489            if ((*it).first == lowercase)
490                return &(*it).second;
491
492        return 0;
493    }
494
495    bool CommandExecutor::compareStringsInList(const std::pair<const std::string*, const std::string*>& first, const std::pair<const std::string*, const std::string*>& second)
496    {
497        return ((*first.first) < (*second.first));
498    }
499}
Note: See TracBrowser for help on using the repository browser.