You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kvirc/src/kvirc/kvs/kvi_kvs_parser_command.cpp

340 lines
9.4 KiB

//=============================================================================
//
// 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
// <namespace>::{<namespace>::}<alias_name>
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 '...<namespace>::<alias_name>' ?"));
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;
}