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.
340 lines
9.4 KiB
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;
|
|
}
|
|
|
|
|