//============================================================================= // // File : kvi_kvs_parser_command.cpp // Creation date : Thu 03 Nov 2003 13.23 CEST by Szymon Stefanek // // This file is part of the KVirc irc client distribution // Copyright (C) 2003 Szymon Stefanek (pragma at kvirc dot net) // // 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 opinion) 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. // //============================================================================= #define __KVIRC__ #include "kvi_kvs_parser.h" #include "kvi_kvs_treenode.h" #include "kvi_kvs_report.h" #include "kvi_kvs_kernel.h" #include "kvi_kvs_parser_macros.h" #include "kvi_locale.h" #include "kvi_kvs_script.h" #include "kvi_cmdformatter.h" KviKvsTreeNodeCommand * KviKvsParser::parseCommand() { KVSP_ASSERT(KVSP_curCharIsLetter || (KVSP_curCharUnicode == '_')); const TQChar * pIdentifier = KVSP_curCharPointer; while(KVSP_curCharIsLetterOrNumber || (KVSP_curCharUnicode == '_'))KVSP_skipChar; int iIdentifierLen = KVSP_curCharPointer - pIdentifier; const TQChar * pSecondPart = 0; int iSecondPartLen = 0; bool bHasNamespaceSoMustBeAlias = false; if(KVSP_curCharUnicode == '.') { // a module command KVSP_skipChar; pSecondPart = KVSP_curCharPointer; if(!KVSP_curCharIsLetter) { warning(KVSP_curCharPointer - 1,__tr2qs("Stray dot ('.') character or invalid following module command name")); error(KVSP_curCharPointer,__tr2qs("Syntax error: malformed module command identifier")); return 0; } KVSP_skipChar; while(KVSP_curCharIsLetterOrNumber || (KVSP_curCharUnicode == '_'))KVSP_skipChar; iSecondPartLen = KVSP_curCharPointer - pSecondPart; } else while(KVSP_curCharUnicode == ':') { // an alias with namespace(s) ? // here we allow the syntax of the form // ::{::} bHasNamespaceSoMustBeAlias = true; KVSP_skipChar; if(KVSP_curCharUnicode == ':') { KVSP_skipChar; if(!KVSP_curCharIsLetter) { warning(KVSP_curCharPointer - 1,__tr2qs("Stray '::' sequence or invalid following alias name")); error(KVSP_curCharPointer,__tr2qs("Syntax error: malformed alias identifier")); return 0; } KVSP_skipChar; while(KVSP_curCharIsLetterOrNumber || (KVSP_curCharUnicode == '_'))KVSP_skipChar; } else { warning(KVSP_curCharPointer - 1,__tr2qs("Stray ':' character: did you mean '...::' ?")); error(KVSP_curCharPointer,__tr2qs("Syntax error: malformed (alias?) command identifier")); return 0; } iIdentifierLen = KVSP_curCharPointer - pIdentifier; } TQString szIdentifier(pIdentifier,iIdentifierLen); skipSpaces(); KviKvsTreeNodeSwitchList * sw = 0; KviKvsTreeNodeData * pRebindData; if(KVSP_curCharUnicode == '-') { // extract the switch sw = parseCommandSwitchList(); if(!sw) { if(error()) return 0; // else it might be a negative number or something that does not seem // to be a switch anyway pRebindData = 0; } else { pRebindData = sw->getStandardRebindingSwitch(); } } else { pRebindData = 0; } KviKvsTreeNodeCommand * cmd; if(!bHasNamespaceSoMustBeAlias) { // perl.begin has a *really* half special parsing routine if(iIdentifierLen == 4) { if(pIdentifier->lower().unicode() == 'p') { if(KviTQString::equalCI(szIdentifier,"perl")) { if(pSecondPart) { TQString szSecondPart(pSecondPart,iSecondPartLen); if(KviTQString::equalCI(szSecondPart,"begin")) { // yep, that's perl.begin cmd = parseSpecialCommandPerlBegin(); if(!cmd) { // might be an error , but might be not... // it is an error only if error() returns true // but since the caller will take care of it // we just return 0 if(sw)delete sw; if(pRebindData)delete pRebindData; return 0; } cmd->setLocation(pIdentifier); if(sw) { cmd->setSwitchList(sw); // cmd becomes child of the rebinding switch if(pRebindData)return new KviKvsTreeNodeRebindingSwitch(pRebindData->location(),pRebindData,cmd); } return cmd; } } } } } if(!pSecondPart) { // is this a special command ? // Here theoretically we could also lookup special commands composed of two parts but we actually don't need it. // Looking up only the first part if there is a second part, instead, // is dangerous since it may generate infinite loops (help.open vs help) KviKvsSpecialCommandParsingRoutine * ccpr = KviKvsKernel::instance()->findSpecialCommandParsingRoutine(szIdentifier); if(ccpr) { cmd = (this->*(ccpr->proc))(); if(!cmd) { // might be an error , but might be not... // it is an error only if error() returns true // but since the caller will take care of it // we just return 0 if(sw)delete sw; if(pRebindData)delete pRebindData; return 0; } cmd->setLocation(pIdentifier); if(sw) { cmd->setSwitchList(sw); // cmd becomes child of the rebinding switch if(pRebindData)return new KviKvsTreeNodeRebindingSwitch(pRebindData->location(),pRebindData,cmd); } return cmd; } } // is it a callback command ? if(KVSP_curCharUnicode == '(') { // core callback command // module callback command KviKvsTreeNodeDataList * dl = parseCommaSeparatedParameterList(); if(!dl) { if(sw)delete sw; if(pRebindData)delete pRebindData; return 0; } if(!skipSpacesAndNewlines()) { if(sw)delete sw; if(pRebindData)delete pRebindData; delete dl; return 0; } const TQChar * pClbkBegin = KVSP_curCharPointer; KviKvsTreeNodeInstruction * ins = parseInstruction(); if(!ins) { if(error()) { if(sw)delete sw; if(pRebindData)delete pRebindData; return 0; } // actually we need empty callbacks (for alias() at least) // the single command implementations should take care of checking it /*else { warning(pIdentifier,__tr2qs("Callback command called with an empty callback instruction")); error(KVSP_curCharPointer,__tr2qs("Callback commands must have a callback instruction")); if(sw)delete sw; delete dl; return 0; }*/ } else { delete ins; // in fact we don't need it, it will be reparsed the first time it is called // Q: Couldn't we actually use the already parsed tree ? // A: No: the tree must be reparsed in a new parser context // since we're keeping track of global and local variables... // The locals of this context are NOT the same as the locals // of the other context. } TQString szCallbackName = szIdentifier; szCallbackName += " callback"; TQString szBlock(pClbkBegin,KVSP_curCharPointer - pClbkBegin); KviCommandFormatter::bufferFromBlock(szBlock); KviKvsScript * clbk = new KviKvsScript(szCallbackName,szBlock); if(pSecondPart) { cmd = new KviKvsTreeNodeModuleCallbackCommand(pIdentifier,szIdentifier,TQString(pSecondPart,iSecondPartLen),dl,clbk); } else { KviKvsCoreCallbackCommandExecRoutine * r = KviKvsKernel::instance()->findCoreCallbackCommandExecRoutine(szIdentifier); if(r) { cmd = new KviKvsTreeNodeCoreCallbackCommand(pIdentifier,szIdentifier,dl,r,clbk); } else { error(KVSP_curCharPointer,__tr2qs("Unknown callback command \"%Q\""),&szIdentifier); if(sw)delete sw; if(pRebindData)delete pRebindData; delete dl; delete clbk; return 0; } } if(sw) { cmd->setSwitchList(sw); // cmd becomes child of the rebinding switch if(pRebindData)return new KviKvsTreeNodeRebindingSwitch(pRebindData->location(),pRebindData,cmd); } return cmd; } } // must be core simple command, module simple command or alias KviKvsTreeNodeDataList * pl = parseCommandParameterList(); if(!pl) { if(sw)delete sw; if(pRebindData)delete pRebindData; return 0; // this MUST be an error } if(bHasNamespaceSoMustBeAlias) { // alias for sure, bind at runtime cmd = new KviKvsTreeNodeAliasSimpleCommand(pIdentifier,szIdentifier,pl); } else { if(pSecondPart) { cmd = new KviKvsTreeNodeModuleSimpleCommand(pIdentifier,szIdentifier,TQString(pSecondPart,iSecondPartLen),pl); } else { KviKvsCoreSimpleCommandExecRoutine * r = KviKvsKernel::instance()->findCoreSimpleCommandExecRoutine(szIdentifier); if(r) { cmd = new KviKvsTreeNodeCoreSimpleCommand(pIdentifier,szIdentifier,pl,r); } else { // must be an alias in root namespace, bind at runtime cmd = new KviKvsTreeNodeAliasSimpleCommand(pIdentifier,szIdentifier,pl); } } } if(sw) { cmd->setSwitchList(sw); // cmd becomes child of the rebinding switch if(pRebindData)return new KviKvsTreeNodeRebindingSwitch(pRebindData->location(),pRebindData,cmd); } return cmd; }