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_script.cpp

357 lines
9.9 KiB

//=============================================================================
//
// File : kvi_kvs_script.cpp
// Creation date : Thu 25 Sep 2003 05.12 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_script.h"
#include "kvi_kvs_parser.h"
#include "kvi_kvs_report.h"
#include "kvi_kvs_runtimecontext.h"
#include "kvi_kvs_treenode_instruction.h"
#include "kvi_kvs_variantlist.h"
#include "kvi_kvs_kernel.h"
#include "kvi_locale.h"
#include "kvi_out.h"
#include "kvi_mirccntrl.h"
#include "kvi_window.h"
#include "kvi_app.h"
class KVIRC_API KviKvsScriptData
{
friend class KviKvsScript;
protected:
unsigned int m_uRefs; // Reference count for this structure
TQString m_szName; // script context name
TQString m_szBuffer; // NEVER TOUCH THIS
const TQChar * m_pBuffer; // this points to m_szBuffer: use it to extract string data
KviKvsScript::ScriptType m_eType; // the type of the code in m_szBuffer
KviKvsTreeNodeInstruction * m_pTree; // syntax tree
unsigned int m_uLock; // this is increased while the script is being executed
};
//#warning "THERE IS SOME MESS WITH m_szBuffer and m_pBuffer : with some script copying we may get errors with negative char indexes!"
KviKvsScript::KviKvsScript(const TQString &szName,const TQString &szBuffer,ScriptType eType)
{
m_pData = new KviKvsScriptData;
m_pData->m_uRefs = 1;
m_pData->m_szName = szName;
m_pData->m_eType = eType;
m_pData->m_szBuffer = szBuffer;
if(m_pData->m_szBuffer.isNull())m_pData->m_szBuffer = "";
//KviTQString::detach(*(m_pData->m_pszBuffer));
m_pData->m_pBuffer = KviTQString::nullTerminatedArray(m_pData->m_szBuffer); // never 0
m_pData->m_uLock = 0;
m_pData->m_pTree = 0;
}
KviKvsScript::KviKvsScript(const TQString &szName,const TQString &szBuffer,KviKvsTreeNodeInstruction * pPreparsedTree,ScriptType eType)
{
m_pData = new KviKvsScriptData;
m_pData->m_uRefs = 1;
m_pData->m_szName = szName;
m_pData->m_szBuffer = szBuffer;
m_pData->m_eType = eType;
if(m_pData->m_szBuffer.isNull())m_pData->m_szBuffer = "";
//KviTQString::detach(*(m_pData->m_pszBuffer));
m_pData->m_pBuffer = KviTQString::nullTerminatedArray(m_pData->m_szBuffer); // never 0
m_pData->m_uLock = 0;
m_pData->m_pTree = pPreparsedTree;
}
KviKvsScript::KviKvsScript(const KviKvsScript &src)
{
m_pData = src.m_pData;
m_pData->m_uRefs++;
}
KviKvsScript::~KviKvsScript()
{
if(m_pData->m_uRefs < 2)
{
if(m_pData->m_uLock)debug("WARNING: Destroying a locked KviKvsScript");
if(m_pData->m_pTree)delete m_pData->m_pTree;
delete m_pData;
} else {
m_pData->m_uRefs--;
}
}
void KviKvsScript::setName(const TQString &szName)
{
if(m_pData->m_uRefs > 1)detach();
m_pData->m_szName = szName;
}
const TQString & KviKvsScript::name() const
{
return m_pData->m_szName;
}
const TQString & KviKvsScript::code() const
{
return m_pData->m_szBuffer;
}
bool KviKvsScript::locked() const
{
return m_pData->m_uLock > 0;
}
void KviKvsScript::dump(const char * prefix)
{
if(m_pData->m_pTree)m_pData->m_pTree->dump(prefix);
else debug("%s KviKvsScript : no tree to dump",prefix);
}
void KviKvsScript::detach()
{
if(m_pData->m_uRefs <= 1)return;
m_pData->m_uRefs--;
KviKvsScriptData * d = new KviKvsScriptData;
d->m_uRefs = 1;
d->m_eType = m_pData->m_eType;
d->m_szBuffer = m_pData->m_szBuffer;
if(d->m_szBuffer.isNull())d->m_szBuffer = "";
KviTQString::detach(d->m_szBuffer);
d->m_pBuffer = KviTQString::nullTerminatedArray(d->m_szBuffer); // never 0
d->m_uLock = 0;
d->m_pTree = 0;
m_pData = d;
}
const TQChar * KviKvsScript::buffer() const
{
return m_pData->m_pBuffer;
}
int KviKvsScript::run(const TQString &szCode,KviWindow * pWindow,KviKvsVariantList * pParams,KviKvsVariant * pRetVal)
{
// static helper
KviKvsScript s("kvirc::corecall(run)",szCode);
return s.run(pWindow,pParams,pRetVal,PreserveParams);
}
int KviKvsScript::evaluate(const TQString &szCode,KviWindow * pWindow,KviKvsVariantList * pParams,KviKvsVariant * pRetVal)
{
// static helper
KviKvsScript s("kvirc::corecall(evalutate)",szCode,Parameter);
return s.run(pWindow,pParams,pRetVal,PreserveParams);
}
int KviKvsScript::evaluateAsString(const TQString &szCode,KviWindow * pWindow,KviKvsVariantList * pParams,TQString &szRetVal)
{
// static helper
KviKvsVariant ret;
KviKvsScript s("kvirc::corecall(evalutate)",szCode,Parameter);
int iRet = s.run(pWindow,pParams,&ret,PreserveParams);
ret.asString(szRetVal);
return iRet;
}
int KviKvsScript::run(KviWindow * pWnd,KviKvsVariantList * pParams,TQString &szRetVal,int iRunFlags,KviKvsExtendedRunTimeData * pExtData)
{
KviKvsVariant retVal;
int iRet = run(pWnd,pParams,&retVal,iRunFlags,pExtData);
retVal.asString(szRetVal);
return iRet;
}
//static long int g_iTreeCacheHits = 0;
//static long int g_iTreeCacheMisses = 0;
int KviKvsScript::run(KviWindow * pWnd,KviKvsVariantList * pParams,KviKvsVariant * pRetVal,int iRunFlags,KviKvsExtendedRunTimeData * pExtData)
{
if(!m_pData->m_pTree)
{
//g_iTreeCacheMisses++;
//debug("CREATING TREE FOR SCRIPT %s",name().latin1());
//debug("TREE CACHE STATS: HITS=%d, MISSES=%d",g_iTreeCacheHits,g_iTreeCacheMisses);
if(!parse(pWnd,iRunFlags))
{
if(pParams && !(iRunFlags & PreserveParams))delete pParams;
return Error;
}
} else {
//g_iTreeCacheHits++;
//debug("USING A CACHED TREE FOR SCRIPT %s",name().latin1());
//debug("TREE CACHE STATS: HITS=%d, MISSES=%d",g_iTreeCacheHits,g_iTreeCacheMisses);
}
return execute(pWnd,pParams,pRetVal,iRunFlags,pExtData);
}
int KviKvsScript::run(KviKvsRunTimeContext * pContext,int iRunFlags)
{
if(!m_pData->m_pTree)
{
//g_iTreeCacheMisses++;
//debug("CREATING TREE FOR SCRIPT %s",name().latin1());
//debug("TREE CACHE STATS: HITS=%d, MISSES=%d",g_iTreeCacheHits,g_iTreeCacheMisses);
if(!parse(pContext->window(),iRunFlags))
return Error;
} else {
//g_iTreeCacheHits++;
//debug("USING A CACHED TREE FOR SCRIPT %s",name().latin1());
//debug("TREE CACHE STATS: HITS=%d, MISSES=%d",g_iTreeCacheHits,g_iTreeCacheMisses);
}
int iRet;
if(iRunFlags & Quiet)
{
bool bMustReEnable = !(pContext->reportingDisabled());
pContext->disableReporting();
iRet = executeInternal(pContext);
if(bMustReEnable)pContext->enableReporting();
} else {
iRet = executeInternal(pContext);
}
return iRet;
}
bool KviKvsScript::parse(KviWindow * pOutput,int iRunFlags)
{
if(m_pData->m_pTree)
{
// there is already a tree
// if we have more than one ref, detach!
if(m_pData->m_uRefs > 1)
{
// mmmh.. more than one ref! .. detach
detach();
} else {
// only a single ref: we're the owner of the tree
if(m_pData->m_uLock)
{
// ops... someone is locked in THIS script object
debug("WARNING: Trying to reparse a locked KviKvsScript!");
return false;
}
if(m_pData->m_pTree)delete m_pData->m_pTree;
m_pData->m_pTree = 0;
}
} // else there is no tree at all, nobody can be locked inside
KviKvsParser p(this,(iRunFlags & Quiet) ? 0 : pOutput);
// parse never blocks
int iFlags = iRunFlags & AssumeLocals ? KviKvsParser::AssumeLocals : 0;
if(iRunFlags & Pedantic)iFlags |= KviKvsParser::Pedantic;
switch(m_pData->m_eType)
{
case Expression:
m_pData->m_pTree = p.parseAsExpression(m_pData->m_pBuffer,iFlags);
break;
case Parameter:
m_pData->m_pTree = p.parseAsParameter(m_pData->m_pBuffer,iFlags);
break;
case InstructionList:
default:
m_pData->m_pTree = p.parse(m_pData->m_pBuffer,iFlags);
break;
}
//debug("\n\nDUMPING SCRIPT");
//dump("");
//debug("END OF SCRIPT DUMP\n\n");
return !p.error();
}
int KviKvsScript::executeInternal(KviKvsRunTimeContext * pContext)
{
// lock this script
m_pData->m_uLock++;
int iRunStatus = Success;
if(!m_pData->m_pTree->execute(pContext))
{
if(pContext->error())iRunStatus = Error;
else {
// else just a halt, return or sth like that
if(pContext->haltCalled())
iRunStatus |= HaltEncountered;
}
}
// we can't block any longer: unlock
m_pData->m_uLock--;
return iRunStatus;
}
int KviKvsScript::execute(KviWindow * pWnd,KviKvsVariantList * pParams,KviKvsVariant * pRetVal,int iRunFlags,KviKvsExtendedRunTimeData * pExtData)
{
bool bDeleteParams = !(iRunFlags & PreserveParams);
// do we have a parsed tree ?
if(!m_pData->m_pTree)
{
if(pParams && bDeleteParams)delete pParams;
// this is intended for developers only
pWnd->outputNoFmt(KVI_OUT_PARSERERROR,"[developer error]: you must succesfully call KviKvsScript::parse() before KviKvsScript::execute()");
return Error;
}
// do we need to pass dummy params ?
if(!pParams)
{
pParams = KviKvsKernel::instance()->emptyParameterList();
bDeleteParams = false;
}
bool bDeleteRetVal = false;
if(!pRetVal)
{
pRetVal = new KviKvsVariant();
bDeleteRetVal = true;
}
KviKvsRunTimeContext ctx(this,pWnd,pParams,pRetVal,pExtData);
if(iRunFlags & Quiet)
ctx.disableReporting();
int iRunStatus = executeInternal(&ctx);
// don't forget to delete the params
if(bDeleteParams)delete pParams;
if(bDeleteRetVal)delete pRetVal;
pParams = 0; pRetVal = 0;
return iRunStatus;
}