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.
1989 lines
55 KiB
1989 lines
55 KiB
//=============================================================================
|
|
//
|
|
// File : kvi_kvs_parser_specialcommands.cpp
|
|
// Creation date : Thu 06 Now 2003 14.14 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_kvs_object_functionhandler.h"
|
|
|
|
#include "kvi_locale.h"
|
|
|
|
#include "kvi_cmdformatter.h"
|
|
|
|
|
|
KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandPerlBegin()
|
|
{
|
|
// in fact this is not a fully special command
|
|
// it is special only in the sense of parsing.
|
|
// Once parsed, the command is routed to the perl module
|
|
// with the perl code as FIRST parameter and the other parameters
|
|
// of the command following.
|
|
// the help page for perl.begin is in the perl module
|
|
|
|
// perl.begin(context) <perl code> perl.end
|
|
//
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
skipSpaces();
|
|
KviKvsTreeNodeDataList * dl;
|
|
if(KVSP_curCharUnicode == '(')
|
|
{
|
|
dl = parseCommaSeparatedParameterList();
|
|
if(!dl)return 0;
|
|
} else {
|
|
dl = new KviKvsTreeNodeDataList(pBegin);
|
|
}
|
|
|
|
//while(!KVSP_curCharIsEndOfCommand)KVSP_skipChar;
|
|
if(!KVSP_curCharIsEndOfBuffer)KVSP_skipChar;
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete dl;
|
|
return 0;
|
|
}
|
|
|
|
// allow a ';' after perl.begin
|
|
if(KVSP_curCharIsEndOfCommand && !KVSP_curCharIsEndOfBuffer)
|
|
{
|
|
KVSP_skipChar;
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete dl;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
const TQChar * pPerlBegin = KVSP_curCharPointer;
|
|
|
|
// now look for perl.end[terminator]
|
|
static TQString szPerlEnd("perl.end");
|
|
const TQChar * pPerlEnd;
|
|
for(;;)
|
|
{
|
|
while(KVSP_curCharUnicode && (KVSP_curCharUnicode != 'p') && (KVSP_curCharUnicode != 'P'))
|
|
KVSP_skipChar;
|
|
if(KVSP_curCharIsEndOfBuffer)
|
|
{
|
|
delete dl;
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of command buffer while looking for the \"perl.end\" statement"));
|
|
return 0;
|
|
}
|
|
pPerlEnd = KVSP_curCharPointer;
|
|
if(KviTQString::equalCIN(szPerlEnd,KVSP_curCharPointer,8))
|
|
{
|
|
KVSP_skipNChars(8);
|
|
if(KVSP_curCharIsEndOfCommand || (KVSP_curCharUnicode == ' ') || (KVSP_curCharUnicode == '\t'))
|
|
{
|
|
// yeah!
|
|
TQString szPerl(pPerlBegin,pPerlEnd - pPerlBegin);
|
|
dl->prependItem(new KviKvsTreeNodeConstantData(pPerlBegin,new KviKvsVariant(szPerl)));
|
|
while(!KVSP_curCharIsEndOfCommand)KVSP_skipChar;
|
|
if(!KVSP_curCharIsEndOfBuffer)KVSP_skipChar;
|
|
break;
|
|
} else {
|
|
KVSP_backNChars(7);
|
|
}
|
|
} else {
|
|
KVSP_skipChar;
|
|
}
|
|
}
|
|
|
|
return new KviKvsTreeNodeModuleSimpleCommand(pBegin,"perl","begin",dl);
|
|
}
|
|
|
|
KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandBreak()
|
|
{
|
|
/*
|
|
@doc: break
|
|
@type:
|
|
command
|
|
@title:
|
|
break
|
|
@syntax:
|
|
break
|
|
@short:
|
|
Interrupts an iteration loop
|
|
@description:
|
|
Interrupts an iteration loop like [cmd]while[/cmd].[br]
|
|
This command always jumps out of a single code block.[br]
|
|
If called outside an iteration loop , will act just like [cmd]halt[/cmd]
|
|
has been called but has no additional semantics for events.[br]
|
|
*/
|
|
const TQChar * pBegin = KVSP_curCharPointer; // FIXME: this is not accurate at all : it may be even the end of the cmd
|
|
skipSpaces();
|
|
if(!KVSP_curCharIsEndOfCommand)
|
|
{
|
|
warning(KVSP_curCharPointer,__tr2qs("Trailing garbage at the end of the break command: ignored"));
|
|
}
|
|
|
|
while(!KVSP_curCharIsEndOfCommand)KVSP_skipChar;
|
|
if(!KVSP_curCharIsEndOfBuffer)KVSP_skipChar;
|
|
return new KviKvsTreeNodeSpecialCommandBreak(pBegin);
|
|
}
|
|
|
|
KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandUnset()
|
|
{
|
|
/*
|
|
@doc: unset
|
|
@type:
|
|
command
|
|
@title:
|
|
unset
|
|
@syntax:
|
|
unset <variable_list>
|
|
@keyterms:
|
|
unsetting variables
|
|
@short:
|
|
Unsets a set of variables
|
|
@description:
|
|
Unsets the specified list of comma separated variables.
|
|
It is equivalent to assigning the default empty value
|
|
to each variable on its own: just does it all at aonce.
|
|
Note that KVIrc automatically frees the local variable memory
|
|
when they go out of scope and the global variable memory
|
|
when KVIrc terminates.
|
|
@examples:
|
|
[example]
|
|
%a = pippo
|
|
%b = 1
|
|
[cmd]echo[/cmd] %a %b
|
|
unset %a %b
|
|
[cmd]echo[/cmd] %a %b
|
|
[/example]
|
|
*/
|
|
|
|
const TQChar * pCmdBegin = KVSP_curCharPointer;
|
|
|
|
KviPointerList<KviKvsTreeNodeVariable> * pVarList = new KviPointerList<KviKvsTreeNodeVariable>;
|
|
pVarList->setAutoDelete(true);
|
|
|
|
while(KVSP_curCharUnicode == '%')
|
|
{
|
|
KviKvsTreeNodeVariable * d = parsePercent();
|
|
if(!d)return 0;
|
|
|
|
pVarList->append(d);
|
|
|
|
skipSpaces();
|
|
|
|
if(KVSP_curCharUnicode == ',')
|
|
{
|
|
KVSP_skipChar;
|
|
skipSpaces();
|
|
}
|
|
}
|
|
|
|
if(!KVSP_curCharIsEndOfCommand)
|
|
{
|
|
warning(KVSP_curCharPointer,__tr2qs("The 'unset' command needs a variable list"));
|
|
error(KVSP_curCharPointer,__tr2qs("Found character %q (tqunicode %x) where a variable was expected"),KVSP_curCharPointer,KVSP_curCharUnicode);
|
|
return 0;
|
|
}
|
|
|
|
if(!KVSP_curCharIsEndOfBuffer)KVSP_skipChar;
|
|
|
|
if(pVarList->count() < 1)
|
|
{
|
|
delete pVarList;
|
|
warning(KVSP_curCharPointer,__tr2qs("'unset' command used without a variable list"));
|
|
return 0; // null unset ?
|
|
}
|
|
return new KviKvsTreeNodeSpecialCommandUnset(pCmdBegin,pVarList);
|
|
}
|
|
|
|
KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandGlobal()
|
|
{
|
|
/*
|
|
@doc: global
|
|
@type:
|
|
command
|
|
@title:
|
|
global
|
|
@syntax:
|
|
global <variable_list>
|
|
@keyterms:
|
|
explicitly declaring global variables
|
|
@short:
|
|
Explicitly declares global variables
|
|
@description:
|
|
Declares a list of global variables.
|
|
Once a variable has been declared as global
|
|
it refers to the global kvirc instance for the scope of the script.
|
|
Global variables are shared between scripts and keep their
|
|
value until they are explicitly unset or kvirc quits.
|
|
This command can be used to override the default behaviour of
|
|
declaring global variables by starting them with an uppercase letter
|
|
and declaring local variables by starting them with a lowercase one.
|
|
@examples:
|
|
global %a,%b,%c;
|
|
*/
|
|
while(KVSP_curCharUnicode == '%')
|
|
{
|
|
KVSP_skipChar;
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
|
|
while((KVSP_curCharIsLetterOrNumber) || (KVSP_curCharUnicode == '_'))KVSP_skipChar;
|
|
|
|
TQString szIdentifier(pBegin,KVSP_curCharPointer - pBegin);
|
|
|
|
if(!m_pGlobals)
|
|
{
|
|
m_pGlobals = new KviPointerHashTable<TQString,TQString>(17,false);
|
|
m_pGlobals->setAutoDelete(true);
|
|
}
|
|
m_pGlobals->tqreplace(szIdentifier,new TQString());
|
|
|
|
skipSpaces();
|
|
|
|
if(KVSP_curCharUnicode == ',')
|
|
{
|
|
KVSP_skipChar;
|
|
skipSpaces();
|
|
}
|
|
}
|
|
|
|
if(!KVSP_curCharIsEndOfCommand)
|
|
{
|
|
warning(KVSP_curCharPointer,__tr2qs("The 'global' command needs a variable list"));
|
|
error(KVSP_curCharPointer,__tr2qs("Found character %q (tqunicode %x) where a variable was expected"),KVSP_curCharPointer,KVSP_curCharUnicode);
|
|
return 0;
|
|
}
|
|
|
|
if(!KVSP_curCharIsEndOfBuffer)KVSP_skipChar;
|
|
return 0;
|
|
}
|
|
|
|
KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandClass()
|
|
{
|
|
/*
|
|
@doc: class
|
|
@title:
|
|
class
|
|
@short:
|
|
Defines a new object class
|
|
@keyterms:
|
|
defining an object class
|
|
@type:
|
|
command
|
|
@syntax:
|
|
class(<classname:string>[,<base_class_name:string>])
|
|
{
|
|
[internal] [function] <function_name>[([<parameter reminder>])]
|
|
{
|
|
<function body>
|
|
}
|
|
|
|
...
|
|
}
|
|
@description:
|
|
Defines a new implementation of the class <classname>.
|
|
If an implementation of that class was already existing
|
|
it is removed with all the derived classes (and all the instances of this class
|
|
and the derived ones are destroyed).
|
|
<base_class_name> is the name of the class that the
|
|
new class has to inherit from.[br]
|
|
If <base_class_name> is omitted, the new class inherits automatically
|
|
from [class:object]object[/class].[br]
|
|
Note:[br]
|
|
The keywords "function" and "event" that were used in KVIrc versions
|
|
previous to 3.0.0 have been removed since "useless".[br]
|
|
The function keyword, however, is still permitted.
|
|
The keyword "internal" is useful when you want to hide
|
|
certain function from the outside world. An internal function
|
|
cannot be called by anyone else but the object instance itself. Note that
|
|
this is different from the C++ "protected" or "private" keywords
|
|
that refer to the object's class instead of the object instance.
|
|
The <parameter reminder> part is an optional string
|
|
that can be used to sign the parameters that the function expects;
|
|
it acts as a programmer reminder or comment and it has no other
|
|
meaning in KVIrc scripting. The <parameter reminder> respects the syntax
|
|
of an expression, so it is terminated by a closed parenthesis.
|
|
It's rather dangerous to use this command inside an object
|
|
function handler: if the class definition <class> was already
|
|
existing and it is a tqparent of the object's class, you might
|
|
end up executing "inexisting" code.[br]
|
|
As a thumb rule, use this command only outside object function handlers.[br]
|
|
[br][br]
|
|
Only for the curious: implementing protected and private access
|
|
list on members would have a considerable runtime overhead because
|
|
of the strange nature of the KVS language. Object member calls
|
|
are resolved completly at runtime (and that permits a lot of funny tricks
|
|
like [cmd]privateimpl[/cmd]) but unfortunately this also forces us
|
|
to check access lists at runtime. Ok, this would be a relatively small footprint for the "private"
|
|
keyword where we need to run UP the called object inheritance hierarchy
|
|
but would have a significant performance footprint for the "protected"
|
|
keyword where we would need to traverse the WHOLE inheritance tree of the called and calling
|
|
objects... "internal" still allows hiding members in a lot of situations
|
|
and is really fast to verify at runtime: no inheritance tree traversal
|
|
is needed and only object pointers are compared.
|
|
@examples:
|
|
[example]
|
|
class(myclass,[class]object[/class])
|
|
{
|
|
constructor
|
|
{
|
|
[cmd]echo[/cmd] Hey this is my constructor
|
|
[cmd]echo[/cmd] I have been just created
|
|
}
|
|
|
|
destructor
|
|
{
|
|
[cmd]echo[/cmd] Ops...being destroyed
|
|
}
|
|
|
|
sayHello(this function expects no parameters)
|
|
{
|
|
[cmd]echo[/cmd] Hello world!
|
|
}
|
|
}
|
|
[/example]
|
|
@seealso:
|
|
[cmd]privateimpl[/cmd], [cmd]killclass[/cmd], [cmd]clearobjects[/cmd], [fnc]$classDefined[/fnc](),
|
|
[doc:objects]Objects documentation[/doc]
|
|
*/
|
|
|
|
if(KVSP_curCharUnicode != '(')
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Found character %q (tqunicode %x) where an open parenthesis was expected"),KVSP_curCharPointer,KVSP_curCharUnicode);
|
|
return 0;
|
|
}
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
KviKvsTreeNodeDataList * l = parseCommaSeparatedParameterList();
|
|
if(!l)return 0;
|
|
|
|
KviKvsTreeNodeSpecialCommandClass * pClass = new KviKvsTreeNodeSpecialCommandClass(pBegin,l);
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
|
|
if(KVSP_curCharUnicode != '{')
|
|
{
|
|
errorBadChar(KVSP_curCharPointer,'{',"class");
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
|
|
KVSP_skipChar;
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
|
|
while(KVSP_curCharUnicode != '}')
|
|
{
|
|
if((KVSP_curCharUnicode == '#') || (KVSP_curCharUnicode == '/'))
|
|
{
|
|
parseComment();
|
|
if(error())
|
|
{
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
const TQChar * pLabelBegin = KVSP_curCharPointer;
|
|
|
|
if(KVSP_curCharIsLetter)
|
|
{
|
|
KVSP_skipChar;
|
|
while(KVSP_curCharIsLetterOrNumber)KVSP_skipChar;
|
|
}
|
|
|
|
if(KVSP_curCharIsEndOfBuffer)
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of buffer in class definition"));
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
|
|
if(KVSP_curCharPointer == pLabelBegin)
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Found character %q (tqunicode %x) where a function name was expected"),KVSP_curCharPointer,KVSP_curCharUnicode);
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
|
|
TQString szLabel(pLabelBegin,KVSP_curCharPointer - pLabelBegin);
|
|
|
|
unsigned int uHandlerFlags = 0;
|
|
|
|
if(szLabel.lower() == "internal")
|
|
{
|
|
uHandlerFlags |= KviKvsObjectFunctionHandler::Internal;
|
|
skipSpaces();
|
|
if(KVSP_curCharUnicode != '(')
|
|
{
|
|
pLabelBegin = KVSP_curCharPointer;
|
|
|
|
while(KVSP_curCharIsLetterOrNumber)KVSP_skipChar;
|
|
|
|
if(KVSP_curCharIsEndOfBuffer)
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of buffer in class definition"));
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
|
|
if(KVSP_curCharPointer == pLabelBegin)
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Found character %q (tqunicode %x) where a function name was expected"),KVSP_curCharPointer,KVSP_curCharUnicode);
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
szLabel = TQString(pLabelBegin,KVSP_curCharPointer - pLabelBegin);
|
|
}
|
|
}
|
|
|
|
|
|
if(szLabel.lower() == "function")
|
|
{
|
|
skipSpaces();
|
|
if(KVSP_curCharUnicode != '(')
|
|
{
|
|
pLabelBegin = KVSP_curCharPointer;
|
|
|
|
while(KVSP_curCharIsLetterOrNumber)KVSP_skipChar;
|
|
|
|
if(KVSP_curCharIsEndOfBuffer)
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of buffer in class definition"));
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
|
|
if(KVSP_curCharPointer == pLabelBegin)
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Found character %q (tqunicode %x) where a function name was expected"),KVSP_curCharPointer,KVSP_curCharUnicode);
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
szLabel = TQString(pLabelBegin,KVSP_curCharPointer - pLabelBegin);
|
|
}
|
|
}
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
|
|
if(KVSP_curCharUnicode == '(')
|
|
{
|
|
while((!(KVSP_curCharIsEndOfBuffer)) && (KVSP_curCharUnicode != ')'))
|
|
KVSP_skipChar;
|
|
|
|
if(KVSP_curCharIsEndOfBuffer)
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of buffer in function parameter list reminder"));
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
|
|
KVSP_skipChar;
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if(KVSP_curCharIsEndOfBuffer)
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of buffer in class definition"));
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
|
|
if(KVSP_curCharUnicode != '{')
|
|
{
|
|
errorBadChar(KVSP_curCharPointer,'{',"class");
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
|
|
pBegin = KVSP_curCharPointer;
|
|
KviKvsTreeNodeInstruction * pInstruction = parseInstruction();
|
|
if(!pInstruction)
|
|
{
|
|
// may be an empty instruction
|
|
if(error())
|
|
{
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
}
|
|
delete pInstruction;
|
|
int iLen = KVSP_curCharPointer - pBegin;
|
|
TQString szInstruction;
|
|
if(iLen > 0)
|
|
{
|
|
szInstruction = TQString(pBegin,KVSP_curCharPointer - pBegin);
|
|
KviCommandFormatter::bufferFromBlock(szInstruction);
|
|
}
|
|
|
|
pClass->addFunctionDefinition(new KviKvsTreeNodeSpecialCommandClassFunctionDefinition(pLabelBegin,szLabel,szInstruction,uHandlerFlags));
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete pClass;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
KVSP_skipChar;
|
|
|
|
return pClass;
|
|
}
|
|
|
|
|
|
KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandWhile()
|
|
{
|
|
/*
|
|
@doc: while
|
|
@type:
|
|
command
|
|
@title:
|
|
while
|
|
@syntax:
|
|
while (<condition>) <command>
|
|
@keyterms:
|
|
iteration commands, flow control commands
|
|
@short:
|
|
Iteration command
|
|
@description:
|
|
Executes <command> while the <condition> evaluates
|
|
to true (non zero result).[br]
|
|
<command> may be either a single command or a block of commands.[br]
|
|
It can contain the [cmd]break[/cmd] command: in that case the
|
|
execution of the <command> will be immediately interrupted and the control
|
|
transferred to the command following this while block.[br]
|
|
It is valid for <command> to be an empty command terminated with a ';'.
|
|
<condition> is an expression as the ones evaluated by [doc:expressioneval]$(*)[/doc]
|
|
with the following extensions:[br]
|
|
If <condition> is a string, its length is evaluated: in this way a non-empty string
|
|
causes the <condition> to be true, an empty string causes it to be false.[br]
|
|
If <condition> is an array, its size is evaluated: in this way a non-empty array
|
|
causes the <condition> to be true, an empty array causes it to be false.[br]
|
|
If <condition> is a hash, the number of its entries is evaluated: in this way a non-empty hash
|
|
causes the <condition> to be true, an empty hash causes it to be false.[br]
|
|
@examples:
|
|
[example]
|
|
%i = 0;
|
|
while(%i < 100)%i++
|
|
while(%i > 0)
|
|
{
|
|
%i -= 10
|
|
if(%i < 20)break;
|
|
}
|
|
echo %i
|
|
[/example]
|
|
*/
|
|
|
|
if(KVSP_curCharUnicode != '(')
|
|
{
|
|
warning(KVSP_curCharPointer,__tr2qs("The while command needs an expression enclosed in parenthesis"));
|
|
error(KVSP_curCharPointer,__tr2qs("Found character %q (tqunicode %x) where an open parenthesis was expected"),KVSP_curCharPointer,KVSP_curCharUnicode);
|
|
return 0;
|
|
}
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
KVSP_skipChar;
|
|
|
|
KviKvsTreeNodeExpression * e = parseExpression(')');
|
|
if(!e)
|
|
{
|
|
// must be an error
|
|
return 0;
|
|
}
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete e;
|
|
return 0;
|
|
}
|
|
|
|
if(KVSP_curCharUnicode == 0)
|
|
{
|
|
warning(pBegin,__tr2qs("The last while command in the buffer has no conditional instructions: it's senseless"));
|
|
warning(KVSP_curCharPointer,__tr2qs("Unexpected end of script while looking for the instruction block of the while command"));
|
|
}
|
|
|
|
KviKvsTreeNodeInstruction * i = parseInstruction();
|
|
if(!i)
|
|
{
|
|
if(error())
|
|
{
|
|
delete e;
|
|
return 0;
|
|
}
|
|
} // else , just an empty instruction
|
|
|
|
return new KviKvsTreeNodeSpecialCommandWhile(pBegin,e,i);
|
|
}
|
|
|
|
KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandDo()
|
|
{
|
|
/*
|
|
@doc: do
|
|
@type:
|
|
command
|
|
@title:
|
|
do
|
|
@syntax:
|
|
do <command> while (<condition>)
|
|
@keyterms:
|
|
iteration commands, flow control commands
|
|
@short:
|
|
Iteration command
|
|
@description:
|
|
Executes <command> once then evaluates the <condition>.
|
|
If <condition> evaluates to true (non zero result) then repeats the execution again.[br]
|
|
<command> may be either a single command or a block of commands.[br]
|
|
It can contain the [cmd]break[/cmd] command: in that case the
|
|
execution of the <command> will be immediately interrupted and the control
|
|
transferred to the command following this while block.[br]
|
|
It is valid for <command> to be an empty command terminated with a ';'.
|
|
<condition> is an expression as the ones evaluated by [doc:expressioneval]$(*)[/doc]
|
|
with the following extensions:[br]
|
|
If <condition> is a string, its length is evaluated: in this way a non-empty string
|
|
causes the <condition> to be true, an empty string causes it to be false.[br]
|
|
If <condition> is an array, its size is evaluated: in this way a non-empty array
|
|
causes the <condition> to be true, an empty array causes it to be false.[br]
|
|
If <condition> is a hash, the number of its entries is evaluated: in this way a non-empty hash
|
|
causes the <condition> to be true, an empty hash causes it to be false.[br]
|
|
@examples:
|
|
[example]
|
|
%i = 0;
|
|
do %i++; while(%i < 100);
|
|
echo "After first execution: %i";
|
|
%i = 10
|
|
do {
|
|
echo "Executed!";
|
|
%i++;
|
|
} while(%i < 1)
|
|
echo "After second execution: %i";
|
|
[/example]
|
|
@seealso:
|
|
[cmd]while[/cmd]
|
|
*/
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
KviKvsTreeNodeInstruction * i = parseInstruction();
|
|
if(!i)
|
|
{
|
|
if(error())return 0;
|
|
}
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
if(i)delete i;
|
|
return 0;
|
|
}
|
|
|
|
static const unsigned short while_chars[10] = { 'W','w','H','h','I','i','L','l','E','e' };
|
|
|
|
for(int j=0;j<10;j++)
|
|
{
|
|
if(KVSP_curCharUnicode != while_chars[j])
|
|
{
|
|
j++;
|
|
if(KVSP_curCharUnicode != while_chars[j])
|
|
{
|
|
if(KVSP_curCharIsEndOfBuffer)
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of command after the 'do' command block: expected 'while' keyword"));
|
|
else
|
|
error(KVSP_curCharPointer,__tr2qs("Found character %q (tqunicode %x) where a 'while' keyword was expected"),KVSP_curCharPointer,KVSP_curCharUnicode);
|
|
if(i)delete i;
|
|
return 0;
|
|
}
|
|
} else j++;
|
|
KVSP_skipChar;
|
|
}
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
if(i)delete i;
|
|
return 0;
|
|
}
|
|
|
|
if(KVSP_curCharUnicode != '(')
|
|
{
|
|
warning(KVSP_curCharPointer,__tr2qs("The 'while' block of the 'do' command needs an expression enclosed in parenthesis"));
|
|
errorBadChar(KVSP_curCharPointer,'(',"do");
|
|
if(i)delete i;
|
|
return 0;
|
|
}
|
|
|
|
KVSP_skipChar;
|
|
|
|
KviKvsTreeNodeExpression * e = parseExpression(')');
|
|
if(!e)
|
|
{
|
|
// must be an error
|
|
if(i)delete i;
|
|
return 0;
|
|
}
|
|
|
|
skipSpaces();
|
|
|
|
if(!KVSP_curCharIsEndOfCommand)
|
|
{
|
|
warning(KVSP_curCharPointer,__tr2qs("Garbage string after the expression in 'do' command: ignored"));
|
|
while(!KVSP_curCharIsEndOfCommand)KVSP_skipChar;
|
|
}
|
|
|
|
if(!KVSP_curCharIsEndOfBuffer)KVSP_skipChar;
|
|
|
|
return new KviKvsTreeNodeSpecialCommandDo(pBegin,e,i);
|
|
}
|
|
|
|
|
|
|
|
|
|
KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandIf()
|
|
{
|
|
/*
|
|
@doc: if
|
|
@type:
|
|
command
|
|
@title:
|
|
if
|
|
@syntax:
|
|
if (<condition>) <command1> [else <command2>]
|
|
@keyterms:
|
|
conditional commands, flow control commands
|
|
@short:
|
|
Flow control command
|
|
@description:
|
|
Executes <command1> if the <condition> evaluates
|
|
to true (non zero result).
|
|
If the "else part" is given <command2> is executed
|
|
if the <condition> evaluates to false (result == '0')
|
|
<condition> is an expression as the ones evaluated by [doc:expressioneval]$(*)[/doc]
|
|
with the following extensions:[br]
|
|
If <condition> is a string, its length is evaluated: in this way a non-empty string
|
|
causes the <condition> to be true, an empty string causes it to be false.[br]
|
|
If <condition> is an array, its size is evaluated: in this way a non-empty array
|
|
causes the <condition> to be true, an empty array causes it to be false.[br]
|
|
If <condition> is a hash, the number of its entries is evaluated: in this way a non-empty hash
|
|
causes the <condition> to be true, an empty hash causes it to be false.[br]
|
|
@examples:
|
|
if(%a != 10)[cmd]echo[/cmd] \%a was != 10
|
|
else [cmd]echo[/cmd] \%a was 10!
|
|
*/
|
|
|
|
if(KVSP_curCharUnicode != '(')
|
|
{
|
|
warning(KVSP_curCharPointer,__tr2qs("The 'if' command needs an expression enclosed in parenthesis"));
|
|
errorBadChar(KVSP_curCharPointer,'(',"if");
|
|
return 0;
|
|
}
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
KVSP_skipChar;
|
|
|
|
|
|
KviKvsTreeNodeExpression * e = parseExpression(')');
|
|
if(!e)
|
|
{
|
|
// must be an error
|
|
return 0;
|
|
}
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete e;
|
|
return 0;
|
|
}
|
|
|
|
if(KVSP_curCharUnicode == 0)
|
|
{
|
|
warning(pBegin,__tr2qs("The last if command in the buffer has no conditional instructions: it's senseless"));
|
|
warning(KVSP_curCharPointer,__tr2qs("Unexpected end of script while looking for the instruction block of the if command"));
|
|
}
|
|
|
|
KviKvsTreeNodeInstruction * i = parseInstruction();
|
|
if(!i)
|
|
{
|
|
if(error())
|
|
{
|
|
delete e;
|
|
return 0;
|
|
}
|
|
} // else , just an empty instruction
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
if(i)delete i;
|
|
return 0;
|
|
}
|
|
|
|
const TQChar * pElse = KVSP_curCharPointer;
|
|
|
|
if((KVSP_curCharUnicode != 'e') && (KVSP_curCharUnicode != 'E'))
|
|
return new KviKvsTreeNodeSpecialCommandIf(pBegin,e,i,0);
|
|
KVSP_skipChar;
|
|
if((KVSP_curCharUnicode != 'l') && (KVSP_curCharUnicode != 'L'))
|
|
{
|
|
KVSP_setCurCharPointer(pElse);
|
|
return new KviKvsTreeNodeSpecialCommandIf(pBegin,e,i,0);
|
|
}
|
|
KVSP_skipChar;
|
|
if((KVSP_curCharUnicode != 's') && (KVSP_curCharUnicode != 'S'))
|
|
{
|
|
KVSP_setCurCharPointer(pElse);
|
|
return new KviKvsTreeNodeSpecialCommandIf(pBegin,e,i,0);
|
|
}
|
|
KVSP_skipChar;
|
|
if((KVSP_curCharUnicode != 'e') && (KVSP_curCharUnicode != 'E'))
|
|
{
|
|
KVSP_setCurCharPointer(pElse);
|
|
return new KviKvsTreeNodeSpecialCommandIf(pBegin,e,i,0);
|
|
}
|
|
KVSP_skipChar;
|
|
if(KVSP_curCharIsLetterOrNumber)
|
|
{
|
|
if((KVSP_curCharUnicode == 'i') || (KVSP_curCharUnicode == 'I'))
|
|
{
|
|
KVSP_skipChar;
|
|
if((KVSP_curCharUnicode == 'f') || (KVSP_curCharUnicode == 'F'))
|
|
{
|
|
KVSP_skipChar;
|
|
if(!KVSP_curCharIsLetterOrNumber)
|
|
{
|
|
// this is an "elseif"
|
|
KVSP_backChar;
|
|
KVSP_backChar;
|
|
// point to if
|
|
goto handle_else_instruction;
|
|
}
|
|
KVSP_backChar;
|
|
}
|
|
KVSP_backChar;
|
|
}
|
|
|
|
KVSP_setCurCharPointer(pElse);
|
|
return new KviKvsTreeNodeSpecialCommandIf(pBegin,e,i,0);
|
|
}
|
|
|
|
handle_else_instruction:
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete e;
|
|
if(i)delete i;
|
|
return 0;
|
|
}
|
|
|
|
KviKvsTreeNodeInstruction * i2 = parseInstruction();
|
|
if(!i2)
|
|
{
|
|
if(error())
|
|
{
|
|
delete e;
|
|
if(i)delete i;
|
|
return 0;
|
|
}
|
|
} // else , just an empty instruction
|
|
|
|
return new KviKvsTreeNodeSpecialCommandIf(pBegin,e,i,i2);
|
|
}
|
|
|
|
bool KviKvsParser::skipToEndOfForControlBlock()
|
|
{
|
|
bool bInString = false;
|
|
int iParLevel = 0;
|
|
|
|
for(;;)
|
|
{
|
|
switch(KVSP_curCharUnicode)
|
|
{
|
|
case '"':
|
|
bInString = !bInString;
|
|
KVSP_skipChar;
|
|
break;
|
|
case '(':
|
|
if(!bInString)iParLevel++;
|
|
KVSP_skipChar;
|
|
break;
|
|
case ')':
|
|
if(!bInString)
|
|
{
|
|
if(iParLevel == 0)return true;
|
|
else iParLevel--;
|
|
}
|
|
KVSP_skipChar;
|
|
break;
|
|
case 0:
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of buffer while looking for the closing ')' in the 'for' command"));
|
|
return false;
|
|
break;
|
|
//case '\n':
|
|
// that's ok.. it may have a parenthesis on the next line
|
|
//KVSP_skipChar;
|
|
//break;
|
|
default:
|
|
KVSP_skipChar;
|
|
break;
|
|
}
|
|
}
|
|
// not reached
|
|
KVSP_ASSERT(false);
|
|
return false;
|
|
}
|
|
|
|
KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandFor()
|
|
{
|
|
/*
|
|
@doc: for
|
|
@type:
|
|
command
|
|
@title:
|
|
for
|
|
@syntax:
|
|
for (<initialization>;<condition>;<update>) <command>
|
|
@keyterms:
|
|
iterational control commands
|
|
@short:
|
|
Iteration control command
|
|
@description:
|
|
Executes <initialization> once then runs the following iteration loop:
|
|
if <condition> evaluates to true then <command> is executed followed
|
|
by the <update> command. The iteration is repeated until <condition> evaluates to false.[br]
|
|
<condition> is an expression as the ones evaluated by [doc:expressioneval]$(*)[/doc]
|
|
with the following extensions:[br]
|
|
If <condition> is a string, its length is evaluated: in this way a non-empty string
|
|
causes the <condition> to be true, an empty string causes it to be false.[br]
|
|
If <condition> is an array, its size is evaluated: in this way a non-empty array
|
|
causes the <condition> to be true, an empty array causes it to be false.[br]
|
|
If <condition> is a hash, the number of its entries is evaluated: in this way a non-empty hash
|
|
causes the <condition> to be true, an empty hash causes it to be false.[br]
|
|
@examples:
|
|
for(%a = 0;%a < 100;%a++)echo %a
|
|
*/
|
|
|
|
if(KVSP_curCharUnicode != '(')
|
|
{
|
|
warning(KVSP_curCharPointer,__tr2qs("The 'for' command needs an expression enclosed in parenthesis"));
|
|
errorBadChar(KVSP_curCharPointer,'(',"for");
|
|
return 0;
|
|
}
|
|
|
|
const TQChar * pForBegin = KVSP_curCharPointer;
|
|
|
|
KVSP_skipChar;
|
|
|
|
skipSpaces();
|
|
|
|
KviKvsTreeNodeInstruction * i1 = parseInstruction();
|
|
if(!i1)
|
|
{
|
|
if(error())return 0;
|
|
} // else just empty instruction
|
|
|
|
skipSpaces();
|
|
|
|
KviKvsTreeNodeExpression * e = parseExpression(';');
|
|
if(!e)
|
|
{
|
|
if(error())
|
|
{
|
|
if(i1)delete i1;
|
|
return 0;
|
|
}
|
|
} // else just empty expression : assume true
|
|
|
|
skipSpaces();
|
|
|
|
// skip to the first non matching ')' that is not in a string
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
if(!skipToEndOfForControlBlock())
|
|
{
|
|
if(error()) // <-- that's always true
|
|
{
|
|
if(i1)delete i1;
|
|
if(e)delete e;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
// HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
|
|
// KVSP_curCharPointer is const!
|
|
// we shouldn't be able to modify it
|
|
TQChar cSave = *(KVSP_curCharPointer);
|
|
|
|
TQChar * pHack = (TQChar *)KVSP_curCharPointer;
|
|
*pHack = TQChar('\n');
|
|
|
|
KVSP_curCharPointer = pBegin;
|
|
|
|
KviKvsTreeNodeInstruction * i2 = parseInstruction();
|
|
*pHack = cSave;
|
|
|
|
KVSP_setCurCharPointer(pHack);
|
|
// EOF HACK EOF HACK EOF HACK EOF HACK EOF HACK EOF HACK EOF HACK
|
|
|
|
|
|
if(!i2)
|
|
{
|
|
if(error())
|
|
{
|
|
if(i1)delete i1;
|
|
if(e)delete e;
|
|
return 0;
|
|
}
|
|
} // else just empty instruction
|
|
|
|
skipSpaces();
|
|
|
|
if(KVSP_curCharUnicode != ')')
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Found char %q (tqunicode %x) while looking for the terminating ')' in 'for' command"),KVSP_curCharPointer,KVSP_curCharUnicode);
|
|
if(i1)delete i1;
|
|
if(e)delete e;
|
|
if(i2)delete i2;
|
|
return 0;
|
|
}
|
|
|
|
KVSP_skipChar;
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
if(i1)delete i1;
|
|
if(e)delete e;
|
|
if(i2)delete i2;
|
|
return 0;
|
|
}
|
|
|
|
KviKvsTreeNodeInstruction * loop = parseInstruction();
|
|
if(!loop)
|
|
{
|
|
if(error())
|
|
{
|
|
if(i1)delete i1;
|
|
if(e)delete e;
|
|
if(i2)delete i2;
|
|
return 0;
|
|
}
|
|
|
|
if((!i1) && (!e) && (!i2))
|
|
{
|
|
error(pForBegin,__tr2qs("Empty infinite 'for' loop: fix the script"));
|
|
if(i1)delete i1;
|
|
if(e)delete e;
|
|
if(i2)delete i2;
|
|
return 0;
|
|
}
|
|
} // else just an empty instruction
|
|
|
|
return new KviKvsTreeNodeSpecialCommandFor(pForBegin,i1,e,i2,loop);
|
|
}
|
|
|
|
|
|
|
|
KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandForeach()
|
|
{
|
|
/*
|
|
@doc: foreach
|
|
@type:
|
|
command
|
|
@title:
|
|
foreach
|
|
@syntax:
|
|
foreach [-a] (<variable>,[<item>[,<item>[,<item>[...]]]) <command>
|
|
@keyterms:
|
|
iteration commands, flow control commands
|
|
@switches:
|
|
!sw: -a | --all
|
|
Include empty variables in the iteration loop.
|
|
@short:
|
|
Iteration command
|
|
@description:
|
|
Executed <command> while assigning to <variable> each <item>.[br]
|
|
<item> may be a constant , a variable , an array , a dictionary or a function returning
|
|
either a constant string an array reference or a dictionary reference.[br]
|
|
If <item> is an array , a dictionary or a function that returns a dictionary or array reference
|
|
the iteration is done through all the dictionary/array items.[br]
|
|
Please note that the iteration order of dictionary items is undefined.[br]
|
|
You can always break from the loop by using the [cmd]break[/cmd] command.[br]
|
|
foreach doesn't iterate over empty scalar variables (i.e. the ones set to [fnc]$nothing[/fnc])
|
|
unless you use the -a switch. (Note that an array with *some* empty entries is NOT empty so
|
|
the iteration is in fact done).
|
|
@examples:
|
|
[example]
|
|
foreach(%i,1,2,3,4,5,6,7,8,9)[cmd]echo[/cmd] %i
|
|
foreach(%chan,[fnc]$window.list[/fnc](channel))[cmd]me[/cmd] -r=%chan This is a test!
|
|
[comment]# This will work too, and will do the same job[/comment]
|
|
%windows[] = [fnc]$window.list[/fnc](channel)
|
|
foreach(%chan,%windows[])[cmd]me[/cmd] -r=%chan This is a test!
|
|
[comment]# And this too[/comment]
|
|
%windows[] = [fnc]$window.list[/fnc](channel)
|
|
foreach(%key,[fnc]$keys[/fnc](%windows[]))[cmd]me[/cmd] -r=%windows[%key] This is a test!
|
|
[comment]# Another interesting example[/comment]
|
|
[cmd]alias[/cmd](test){ [cmd]return[/cmd] [fnc]$hash[/fnc](1,a,2,b,3,c,4,d); };
|
|
foreach(%x,[fnc]$keys[/fnc]($test)){ [cmd]echo[/cmd] %x, $test{%x}; }
|
|
[/example]
|
|
*/
|
|
|
|
if(KVSP_curCharUnicode != '(')
|
|
{
|
|
warning(KVSP_curCharPointer,__tr2qs("The 'foreach' command needs an expression enclosed in parenthesis"));
|
|
errorBadChar(KVSP_curCharPointer,'(',"foreach");
|
|
return 0;
|
|
}
|
|
|
|
const TQChar * pForeachBegin = KVSP_curCharPointer;
|
|
|
|
KVSP_skipChar;
|
|
|
|
skipSpaces();
|
|
|
|
|
|
if((KVSP_curCharUnicode != '%') && (KVSP_curCharUnicode != '$') && (KVSP_curCharUnicode != '@'))
|
|
{
|
|
warning(KVSP_curCharPointer,__tr2qs("The 'foreach' command expects a writeable iteration variable as first parameter"));
|
|
error(KVSP_curCharPointer,__tr2qs("Found character '%q' (tqunicode %x) where '%' or '$' was expected: see /help foreach for the command syntax"),KVSP_curCharPointer,KVSP_curCharUnicode);
|
|
return 0;
|
|
}
|
|
|
|
KviKvsTreeNodeData * d = parsePercentOrDollar();
|
|
if(!d)return 0;
|
|
|
|
if(d->isFunctionCall() || d->isReadOnly())
|
|
{
|
|
warning(KVSP_curCharPointer,__tr2qs("The 'foreach' command expects a writeable iteration variable as first parameter"));
|
|
if(d->isFunctionCall())
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected function call as 'foreach' iteration variable"));
|
|
else
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected read-only variable as 'foreach' iteration variable"));
|
|
delete d;
|
|
return 0;
|
|
}
|
|
|
|
skipSpaces();
|
|
if(KVSP_curCharUnicode != ',')
|
|
{
|
|
if(KVSP_curCharUnicode == ')')
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of 'foreach' parameters: at least one iteration data argument must be given"));
|
|
delete d;
|
|
return 0;
|
|
}
|
|
warning(KVSP_curCharPointer,__tr2qs("The 'foreach' command expects a comma separated list of iteration data items after the first parameter"));
|
|
errorBadChar(KVSP_curCharPointer,',',"foreach");
|
|
return 0;
|
|
}
|
|
|
|
KviKvsTreeNodeDataList * l = parseCommaSeparatedParameterList();
|
|
if(!l)return 0;
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete d;
|
|
delete l;
|
|
return 0;
|
|
}
|
|
|
|
const TQChar * pLoopBegin = KVSP_curCharPointer;
|
|
|
|
KviKvsTreeNodeInstruction * loop = parseInstruction();
|
|
if(!loop)
|
|
{
|
|
if(error())return 0;
|
|
warning(pLoopBegin,__tr2qs("Found empty 'foreach' execution block: maybe you need to fix your script ?"));
|
|
loop = new KviKvsTreeNodeInstructionBlock(pLoopBegin);
|
|
}
|
|
|
|
return new KviKvsTreeNodeSpecialCommandForeach(pForeachBegin,d,l,loop);
|
|
}
|
|
|
|
|
|
KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandSwitch()
|
|
{
|
|
/*
|
|
@doc: switch
|
|
@type:
|
|
command
|
|
@title:
|
|
switch
|
|
@syntax:
|
|
switch(<expression>)
|
|
{
|
|
case(<value>)[:]<command>
|
|
[break]
|
|
case(<value>)[:]<command>
|
|
[break]
|
|
....
|
|
match(<wildcard_expression>)[:]<command>
|
|
[break]
|
|
....
|
|
regexp(<regular_expression>)[:]<command>
|
|
[break]
|
|
....
|
|
case(<value>)[:]<command>
|
|
[break]
|
|
....
|
|
default[:]<command>
|
|
[break]
|
|
}
|
|
@short:
|
|
Another flow control command
|
|
@description:
|
|
The switch command is based on the standard C 'switch' keyword.
|
|
It executes conditionally groups of commands choosen from
|
|
a larger set of command groups.[br]
|
|
First <expression> is evaluated (<expression> is any arithmetic or string expression).[br]
|
|
Then the 'match','regexp','case' and 'default' labels are evaluated sequentially
|
|
in the order of appearance.[br]
|
|
[b]case(<value>)[:]<command>[/b][br]
|
|
The <value> is evaluated and is compared against the result of <expression>.
|
|
The comparison is case insensitive (if the values are strings).[br]
|
|
If <value> is equal to <expression> then <command> is executed.
|
|
Please note that <command> must be either a single instruction or an instruction block [b]enclosed in braces[/b].
|
|
If <command> contains a [cmd]break[/cmd] statement inside or if [cmd]break[/cmd]
|
|
is specified just after the <command> then the execution of the switch is terminated
|
|
otherwise the nex label is evaluated.[br]
|
|
[b]match(<value>)[:]<command>[/b][br]
|
|
The <value> is expected to be a wildcard expression (containing '*' and '?' wildcards)
|
|
that is matched against <expression>.[br]
|
|
If there is a match (a complete case insensitive match!) then the related <command>
|
|
is executed. [cmd]brea[/cmd] is treated just like in the case label.[br]
|
|
[b]regexp(<value>)[:]<command>[/b][br]
|
|
The <value> is expected to be a complete standard regular expression
|
|
that is matched agains <expression>.[br]
|
|
If there is a match (a complete case insensitive match!) then the related <command>
|
|
is executed. [cmd]brea[/cmd] is treated just like in the case label.[br]
|
|
[b]default[:]<command>[/b][br]
|
|
The default label is executed unconditionally (unless there was a previous label
|
|
that terminated the execution with break).[br]
|
|
@examples:
|
|
[comment]# Try to change the 1 below to 2 or 3 to see the results[/comment]
|
|
%tmp = 1
|
|
switch(%tmp)
|
|
{
|
|
case(1):
|
|
echo \%tmp was 1!
|
|
break;
|
|
case(2)
|
|
echo \%tmp was 2!
|
|
break;
|
|
default:
|
|
echo \%tmp was not 1 nor 2: it was %tmp!
|
|
break;
|
|
}
|
|
[comment]# A complexier example: change the 1 in 2 or 3[/comment]
|
|
%tmp = 1
|
|
switch(%tmp)
|
|
{
|
|
case(1):
|
|
echo \%tmp was 1!
|
|
case(2)
|
|
echo \%tmp was 2!
|
|
break;
|
|
default:
|
|
echo \%tmp was either 1 or something different from 2 (%tmp)
|
|
break;
|
|
}
|
|
[comment]# An example with strings[/comment]
|
|
%tmp = "This is a test"
|
|
%tmp2 = "This is not a test"
|
|
switch(%tmp)
|
|
{
|
|
case(%tmp2)
|
|
echo \%tmp == \%tmp2
|
|
break;
|
|
case(%tmp)
|
|
{
|
|
# do not break here
|
|
echo "Yeah.. it's stupid.. \%tmp == \%tmp :D"
|
|
}
|
|
match("*TEST"):
|
|
echo "Matched *TEST"
|
|
regexp("[a-zA-Z ]*test"):
|
|
echo "Matched [a-zA-Z ]*text"
|
|
regexp("[a-zA-Z ]*not[a-zA-Z ]*"):
|
|
echo "Matched [a-zA-Z ]*not[a-zA-Z ]*"
|
|
default:
|
|
echo This is executed anyway (unless some break was called)
|
|
break;
|
|
}
|
|
*/
|
|
|
|
if(KVSP_curCharUnicode != '(')
|
|
{
|
|
warning(KVSP_curCharPointer,__tr2qs("The 'switch' command needs an expression enclosed in parenthesis"));
|
|
errorBadChar(KVSP_curCharPointer,'(',"switch");
|
|
return 0;
|
|
}
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
KVSP_skipChar;
|
|
|
|
KviKvsTreeNodeExpression * e = parseExpression(')');
|
|
if(!e)
|
|
{
|
|
// must be an error
|
|
return 0;
|
|
}
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete e;
|
|
return 0;
|
|
}
|
|
|
|
if(KVSP_curCharUnicode != '{')
|
|
{
|
|
errorBadChar(KVSP_curCharPointer,'{',"switch");
|
|
delete e;
|
|
return 0;
|
|
}
|
|
|
|
KVSP_skipChar;
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete e;
|
|
return 0;
|
|
}
|
|
|
|
KviKvsTreeNodeSpecialCommandSwitch * pSwitch = new KviKvsTreeNodeSpecialCommandSwitch(pBegin,e);
|
|
|
|
KviKvsTreeNodeSpecialCommandSwitchLabel * pLabel = 0;
|
|
|
|
while(KVSP_curCharUnicode != '}')
|
|
{
|
|
// look for a 'case','match','default' or 'regexpr' label
|
|
|
|
const TQChar * pLabelBegin = KVSP_curCharPointer;
|
|
while(KVSP_curCharIsLetter)KVSP_skipChar;
|
|
|
|
if(KVSP_curCharIsEndOfBuffer)
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of buffer in switch condition block"));
|
|
delete pSwitch;
|
|
return 0;
|
|
}
|
|
|
|
if(KVSP_curCharPointer == pLabelBegin)
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Found character %q (tqunicode %x) where a 'case','match','regexp','default' or 'break' label was expected"),KVSP_curCharPointer,KVSP_curCharUnicode);
|
|
delete pSwitch;
|
|
return 0;
|
|
}
|
|
|
|
TQString szLabel(pLabelBegin,KVSP_curCharPointer - pLabelBegin);
|
|
TQString szLabelLow = szLabel.lower();
|
|
|
|
bool bNeedParam = true;
|
|
|
|
if(szLabelLow == "case")
|
|
{
|
|
pLabel = new KviKvsTreeNodeSpecialCommandSwitchLabelCase(pLabelBegin);
|
|
} else if(szLabelLow == "match")
|
|
{
|
|
pLabel = new KviKvsTreeNodeSpecialCommandSwitchLabelMatch(pLabelBegin);
|
|
} else if(szLabelLow == "regexp")
|
|
{
|
|
pLabel = new KviKvsTreeNodeSpecialCommandSwitchLabelRegexp(pLabelBegin);
|
|
} else if(szLabelLow == "default")
|
|
{
|
|
pLabel = new KviKvsTreeNodeSpecialCommandSwitchLabelDefault(pLabelBegin);
|
|
bNeedParam = false;
|
|
} else if(szLabelLow == "break")
|
|
{
|
|
if(pLabel)
|
|
{
|
|
pLabel->setTerminatingBreak(true);
|
|
skipSpaces();
|
|
if(KVSP_curCharUnicode == ';')KVSP_skipChar;
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete pSwitch;
|
|
delete pLabel;
|
|
return 0;
|
|
}
|
|
continue;
|
|
} else {
|
|
error(pLabelBegin,__tr2qs("Found 'break' label where a 'case','match','regexp' or 'default' label was expected"));
|
|
delete pSwitch;
|
|
return 0;
|
|
}
|
|
} else {
|
|
error(pLabelBegin,__tr2qs("Found token '%Q' where a 'case','match','regexp','default' or 'break' label was expected"),&szLabel);
|
|
delete pSwitch;
|
|
return 0;
|
|
}
|
|
|
|
if(bNeedParam)
|
|
{
|
|
skipSpaces();
|
|
if(KVSP_curCharUnicode != '(')
|
|
{
|
|
errorBadChar(KVSP_curCharPointer,'(',"switch");
|
|
delete pSwitch;
|
|
delete pLabel;
|
|
return 0;
|
|
}
|
|
KVSP_skipChar;
|
|
|
|
KviKvsTreeNodeData * pParameter = parseSingleParameterInParenthesis();
|
|
if(!pParameter)
|
|
{
|
|
delete pSwitch;
|
|
delete pLabel;
|
|
return 0;
|
|
}
|
|
|
|
pLabel->setParameter(pParameter);
|
|
}
|
|
|
|
skipSpaces();
|
|
if(KVSP_curCharUnicode == ':')KVSP_skipChar;
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete pSwitch;
|
|
delete pLabel;
|
|
return 0;
|
|
}
|
|
|
|
KviKvsTreeNodeInstruction * pInstruction = parseInstruction();
|
|
if(!pInstruction)
|
|
{
|
|
// may be an empty instruction
|
|
if(error())
|
|
{
|
|
delete pSwitch;
|
|
delete pLabel;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
pLabel->setInstruction(pInstruction);
|
|
pSwitch->addLabel(pLabel);
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete pSwitch;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
KVSP_skipChar;
|
|
|
|
if(pSwitch->isEmpty())
|
|
{
|
|
error(pBegin,__tr2qs("Senseless empty switch command: fix the script"));
|
|
delete pSwitch;
|
|
return 0;
|
|
}
|
|
|
|
return pSwitch;
|
|
}
|
|
|
|
KviKvsTreeNodeSpecialCommandDefpopupLabelPopup * KviKvsParser::parseSpecialCommandDefpopupLabelPopup()
|
|
{
|
|
if(KVSP_curCharUnicode != '{')
|
|
{
|
|
errorBadChar(KVSP_curCharPointer,'{',"defpopup");
|
|
return 0;
|
|
}
|
|
|
|
KviKvsTreeNodeSpecialCommandDefpopupLabelPopup * pPopup = new KviKvsTreeNodeSpecialCommandDefpopupLabelPopup(KVSP_curCharPointer);
|
|
|
|
KVSP_skipChar;
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete pPopup;
|
|
return 0;
|
|
}
|
|
|
|
while(KVSP_curCharUnicode != '}')
|
|
{
|
|
// look for 'label', 'prologue', 'epilogue', 'popup', 'item', 'separator' or 'extpopup' label
|
|
const TQChar * pLabelBegin = KVSP_curCharPointer;
|
|
while(KVSP_curCharIsLetter)KVSP_skipChar;
|
|
|
|
if(KVSP_curCharIsEndOfBuffer)
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of buffer in defpopup block"));
|
|
delete pPopup;
|
|
return 0;
|
|
}
|
|
|
|
if(KVSP_curCharPointer == pLabelBegin)
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Found character %q (tqunicode %x) where a 'prologue','separator','label','popup','item','extpopup' or 'epilogue' label was expected"),KVSP_curCharPointer,KVSP_curCharUnicode);
|
|
delete pPopup;
|
|
return 0;
|
|
}
|
|
|
|
TQString szLabel(pLabelBegin,KVSP_curCharPointer - pLabelBegin);
|
|
TQString szLabelLow = szLabel.lower();
|
|
|
|
KviPointerList<TQString> * pParameters = 0;
|
|
|
|
TQString szCondition;
|
|
|
|
|
|
#define EXTRACT_POPUP_LABEL_PARAMETERS \
|
|
if(!skipSpacesAndNewlines()) \
|
|
{ \
|
|
delete pPopup; \
|
|
return 0; \
|
|
} \
|
|
if(KVSP_curCharUnicode != '(') \
|
|
{ \
|
|
errorBadChar(KVSP_curCharPointer,'(',"defpopup"); \
|
|
delete pPopup; \
|
|
return 0; \
|
|
} \
|
|
pParameters = parseCommaSeparatedParameterListNoTree(); \
|
|
if(!pParameters)return 0;
|
|
|
|
|
|
#define EXTRACT_POPUP_LABEL_CONDITION \
|
|
if(!skipSpacesAndNewlines()) \
|
|
{ \
|
|
delete pPopup; \
|
|
return 0; \
|
|
} \
|
|
if(KVSP_curCharUnicode == '(') \
|
|
{ \
|
|
const TQChar * pBegin = KVSP_curCharPointer; \
|
|
KVSP_skipChar; \
|
|
KviKvsTreeNodeExpression * pExpression = parseExpression(')'); \
|
|
if(!pExpression) \
|
|
{ \
|
|
if(pParameters)delete pParameters; \
|
|
delete pPopup; \
|
|
return 0; \
|
|
} \
|
|
int cLen = (KVSP_curCharPointer - pBegin) - 2; \
|
|
if(cLen > 0) \
|
|
{ \
|
|
szCondition.setUnicode(pBegin + 1,cLen); \
|
|
} \
|
|
delete pExpression; \
|
|
if(!skipSpacesAndNewlines()) \
|
|
{ \
|
|
if(pParameters)delete pParameters; \
|
|
delete pPopup; \
|
|
return 0; \
|
|
} \
|
|
}
|
|
|
|
|
|
|
|
if((szLabelLow == "prologue") || (szLabelLow == "epilogue"))
|
|
{
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool bPrologue = (szLabelLow == "prologue");
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete pPopup;
|
|
return 0;
|
|
}
|
|
|
|
if(KVSP_curCharUnicode == '(')
|
|
{
|
|
EXTRACT_POPUP_LABEL_PARAMETERS
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
if(pParameters)delete pParameters;
|
|
delete pPopup;
|
|
return 0;
|
|
}
|
|
}
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
KviKvsTreeNodeInstruction * pInstruction = parseInstruction();
|
|
if(pInstruction)
|
|
{
|
|
// in fact we don't need it at all, we just need the code buffer...
|
|
delete pInstruction;
|
|
} else {
|
|
// may be an empty instruction
|
|
if(error())
|
|
{
|
|
// error
|
|
if(pParameters)delete pParameters;
|
|
delete pPopup;
|
|
return 0;
|
|
}
|
|
// empty instruction
|
|
if(bPrologue)
|
|
warning(pBegin,__tr2qs("Found empty prologue block: maybe you need to fix the script?"));
|
|
else
|
|
warning(pBegin,__tr2qs("Found empty epilogue block: maybe you need to fix the script?"));
|
|
}
|
|
int iLen = KVSP_curCharPointer - pBegin;
|
|
if(iLen > 0)
|
|
{
|
|
TQString szInstruction(pBegin,KVSP_curCharPointer - pBegin);
|
|
KviCommandFormatter::bufferFromBlock(szInstruction);
|
|
TQString * pItemName = pParameters ? pParameters->first() : 0;
|
|
TQString szItemName = pItemName ? *pItemName : TQString();
|
|
if(bPrologue)
|
|
pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelPrologue(pLabelBegin,szInstruction,szItemName));
|
|
else
|
|
pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelEpilogue(pLabelBegin,szInstruction,szItemName));
|
|
} // else the instruction was empty anyway: we don't need it in fact
|
|
if(pParameters)delete pParameters;
|
|
} else if(szLabelLow == "separator")
|
|
{
|
|
// FIXME: Separators can't have labels here :(((((
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
EXTRACT_POPUP_LABEL_CONDITION
|
|
if(KVSP_curCharUnicode == ';')KVSP_skipChar;
|
|
TQString szItemName = "dummySeparator"; // <------- FIXME!
|
|
pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelSeparator(pLabelBegin,szCondition,szItemName));
|
|
|
|
} else if(szLabelLow == "label")
|
|
{
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
EXTRACT_POPUP_LABEL_PARAMETERS
|
|
EXTRACT_POPUP_LABEL_CONDITION
|
|
|
|
TQString * pText = pParameters->first();
|
|
if(!pText)
|
|
{
|
|
error(pLabelBegin,__tr2qs("Unexpected empty <text> field in label parameters. See /help defpopup for the syntax"));
|
|
delete pParameters;
|
|
delete pPopup;
|
|
return 0;
|
|
}
|
|
TQString * pIcon = pParameters->next();
|
|
if(KVSP_curCharUnicode == ';')KVSP_skipChar;
|
|
TQString * pItemName = pParameters->next();
|
|
pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelLabel(pLabelBegin,szCondition,*pText,pIcon ? *pIcon : TQString(),pItemName ? *pItemName : TQString()));
|
|
delete pParameters;
|
|
} else if(szLabelLow == "popup")
|
|
{
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
EXTRACT_POPUP_LABEL_PARAMETERS
|
|
EXTRACT_POPUP_LABEL_CONDITION
|
|
|
|
TQString * pText = pParameters->first();
|
|
if(!pText)
|
|
{
|
|
error(pLabelBegin,__tr2qs("Unexpected empty <text> field in extpopup parameters. See /help defpopup for the syntax"));
|
|
delete pParameters;
|
|
delete pPopup;
|
|
return 0;
|
|
}
|
|
TQString * pIcon = pParameters->next();
|
|
TQString * pItemName = pParameters->next();
|
|
|
|
KviKvsTreeNodeSpecialCommandDefpopupLabelPopup * pSubPopup = parseSpecialCommandDefpopupLabelPopup();
|
|
if(!pSubPopup)
|
|
{
|
|
delete pParameters;
|
|
delete pPopup;
|
|
return 0;
|
|
}
|
|
|
|
pSubPopup->setCondition(szCondition);
|
|
pSubPopup->setText(*pText);
|
|
pSubPopup->setItemName(pItemName ? *pItemName : TQString());
|
|
if(pIcon)pSubPopup->setIcon(*pIcon);
|
|
pPopup->addLabel(pSubPopup);
|
|
delete pParameters;
|
|
} else if(szLabelLow == "item")
|
|
{
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
EXTRACT_POPUP_LABEL_PARAMETERS
|
|
EXTRACT_POPUP_LABEL_CONDITION
|
|
|
|
TQString * pText = pParameters->first();
|
|
if(!pText)
|
|
{
|
|
error(pLabelBegin,__tr2qs("Unexpected empty <text> field in extpopup parameters. See /help defpopup for the syntax"));
|
|
delete pParameters;
|
|
delete pPopup;
|
|
return 0;
|
|
}
|
|
TQString * pIcon = pParameters->next();
|
|
TQString * pItemName = pParameters->next();
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
KviKvsTreeNodeInstruction * pInstruction = parseInstruction();
|
|
if(pInstruction)
|
|
{
|
|
// in fact we don't need it: we just need the code block
|
|
delete pInstruction;
|
|
} else {
|
|
// empty instruction or error ?
|
|
if(error())
|
|
{
|
|
// error
|
|
delete pParameters;
|
|
delete pPopup;
|
|
return 0;
|
|
}
|
|
// empty instruction
|
|
warning(pBegin,__tr2qs("Found empty instruction for popup item: maybe you need to fix the script?"));
|
|
}
|
|
int iLen = KVSP_curCharPointer - pBegin;
|
|
if(iLen > 0)
|
|
{
|
|
TQString szInstruction(pBegin,KVSP_curCharPointer - pBegin);
|
|
KviCommandFormatter::bufferFromBlock(szInstruction);
|
|
pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelItem(pLabelBegin,szCondition,*pText,pIcon ? *pIcon : TQString(),szInstruction,pItemName ? *pItemName : TQString()));
|
|
} else {
|
|
// zero length instruction, but still add the item
|
|
TQString szInstruction = "";
|
|
KviCommandFormatter::bufferFromBlock(szInstruction);
|
|
pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelItem(pLabelBegin,szCondition,*pText,pIcon ? *pIcon : TQString(),szInstruction,pItemName ? *pItemName : TQString()));
|
|
}
|
|
delete pParameters;
|
|
} else if(szLabelLow == "extpopup")
|
|
{
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
EXTRACT_POPUP_LABEL_PARAMETERS
|
|
EXTRACT_POPUP_LABEL_CONDITION
|
|
|
|
TQString * pText = pParameters->first();
|
|
if(!pText)
|
|
{
|
|
error(pLabelBegin,__tr2qs("Unexpected empty <text> field in extpopup parameters. See /help defpopup for the syntax"));
|
|
delete pParameters;
|
|
delete pPopup;
|
|
return 0;
|
|
}
|
|
TQString * pName = pParameters->next();
|
|
if(!pName)
|
|
{
|
|
error(pLabelBegin,__tr2qs("Unexpected empty <name> field in extpopup parameters. See /help defpopup for the syntax"));
|
|
delete pParameters;
|
|
delete pPopup;
|
|
return 0;
|
|
}
|
|
TQString * pIcon = pParameters->next();
|
|
TQString * pItemName = pParameters->next();
|
|
if(KVSP_curCharUnicode == ';')KVSP_skipChar;
|
|
pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelExtpopup(pLabelBegin,szCondition,*pText,pIcon ? *pIcon : TQString(),*pName,pItemName ? *pItemName : TQString()));
|
|
delete pParameters;
|
|
} else {
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
error(pLabelBegin,__tr2qs("Found token '%Q' where a 'prologue','separator','label','popup','item','extpopup' or 'epilogue' label was expected"),&szLabel);
|
|
delete pPopup;
|
|
return 0;
|
|
}
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete pPopup;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
KVSP_skipChar;
|
|
return pPopup;
|
|
}
|
|
|
|
|
|
KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandDefpopup()
|
|
{
|
|
// FIXME: This SHOULD be renamed to popup.create (NOT popup.define!)
|
|
// and internally aliased to defpopup as backward compat
|
|
// There should be then also popup.destroy etc..
|
|
|
|
/*
|
|
@doc: defpopup
|
|
@type:
|
|
command
|
|
@title:
|
|
defpopup
|
|
@syntax:
|
|
defpopup [-m] (<popup_name>)
|
|
{
|
|
prologue[(<id>)] <prologue_command>
|
|
|
|
epilogue[(<id>)] <epilogue_command>
|
|
|
|
label(<text>[,<id>])[(<expression>)][;]
|
|
|
|
item(<text>[,<icon>[,<id>]])[(<expression>)]<command>
|
|
|
|
popup(<text>[,<icon>[,<id>]])[(<expression>)]
|
|
{
|
|
<popup body>
|
|
}
|
|
|
|
extpopup(<text>,<name>[,<icon>[,<id>]])[(<expression>)][;]
|
|
|
|
separator[(<expression>)][;]
|
|
...
|
|
}
|
|
@short:
|
|
Defines a popup menu
|
|
@switches:
|
|
!sw: -m | --merge
|
|
Merges the new popup contents with an older popup version
|
|
@description:
|
|
Defines the popup menu <popup_name>. If the -m switch is NOT used
|
|
the previous contents of the popups are cleared, otherwise are preserved.[br]
|
|
The popup is generated 'on the fly' when the [cmd]popup[/cmd] command
|
|
is called.[br]
|
|
The 'item' keyword adds a menu item with visible <text> ,
|
|
the optional <icon> and <command> as code to be executed when the item
|
|
is clicked. <text> is a string that is evaluated at [cmd]popup[/cmd]
|
|
call time and may contain identifiers and variables. If <expression>
|
|
is given, it is evaluated at [cmd]popup[/cmd] call time and if the result
|
|
is 0, the item is not shown in the physical popup.[br]
|
|
The 'popup' keyword adds a submenu with visible <text> , the optional
|
|
<icon> and a popup body that follows exactly the same syntax
|
|
as the defpopup body. The <expression> has the same meaning as with the
|
|
'item' keyword.[br]
|
|
The 'extpopup' keyword adds a submenu with visible <text> , the optional
|
|
icon and a popup body that is defined by the popup menu <name>. This
|
|
basically allows to nest popup menus and define their parts separately.
|
|
<icon> and <expression> have the same meaning as with the 'item' keyword.[br]
|
|
The 'separator' keyword adds a straight line between items (separator).[br]
|
|
The 'label' keywork adds a descriptive label that acts like a separator.[br]
|
|
The 'prologue' keyword adds a <prologue_command> to be executed
|
|
just before the popup is filled at [cmd]popup[/cmd] command call.[br]
|
|
The 'epilogue' keyword adds an <epilogue_command> to be executed
|
|
just after the popup has been filled at [cmd]popup[/cmd] command call.[br]
|
|
There can be multiple prologue and epilogue commands: their execution order
|
|
is undefined.[br]
|
|
<icon> is always an [doc:image_id]image identifier[/doc].[br]
|
|
<id> is an unique identifier that can be used to remove single items
|
|
by the means of [cmd]delpopupitem[/cmd]. If <id> is omitted
|
|
then it is automatically generated.
|
|
Please note that using this command inside the prologue , epilogue
|
|
or item code of the modified popup menu is forbidden.
|
|
In other words: self modification of popup menus is NOT allowed.[br]
|
|
To remove a popup menu use this command with an empty body:[br]
|
|
[example]
|
|
defpopup(test){}
|
|
[/example]
|
|
This will remove the popup 'test' and free its memory.
|
|
Popups have a special kind of local variables that have an extended lifetime:
|
|
these are called "extended scope variables" and are described in the [doc:data_structures]Data structures documentation[/doc].[br]
|
|
The syntax for these variables is:[br]
|
|
[b]%:<variable name>[/b][br]
|
|
These variables are visible during all the "visible lifetime" of the popup:
|
|
from the [cmd]popup[/cmd] command call to the moment in that the user selects an item
|
|
and the corresponding code is executed (substantially from a [cmd]popup[/cmd] call to the next one).[br]
|
|
This allows you to pre-calculate data and conditions in the porologue of the popup
|
|
and then use it in the item handlers or item conditions.[br]
|
|
@seealso:
|
|
[cmd]popup[/cmd]
|
|
@examples:
|
|
*/
|
|
|
|
if(KVSP_curCharUnicode != '(')
|
|
{
|
|
warning(KVSP_curCharPointer,__tr2qs("The 'defpopup' command needs an expression enclosed in parenthesis"));
|
|
errorBadChar(KVSP_curCharPointer,'(',"defpopup");
|
|
return 0;
|
|
}
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
KVSP_skipChar;
|
|
|
|
KviKvsTreeNodeData * pPopupName = parseSingleParameterInParenthesis();
|
|
if(!pPopupName)return 0;
|
|
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete pPopupName;
|
|
return 0;
|
|
}
|
|
|
|
KviKvsTreeNodeSpecialCommandDefpopupLabelPopup * pMainPopup = parseSpecialCommandDefpopupLabelPopup();
|
|
if(!pMainPopup)
|
|
{
|
|
delete pPopupName;
|
|
return 0;
|
|
}
|
|
|
|
return new KviKvsTreeNodeSpecialCommandDefpopup(pBegin,pPopupName,pMainPopup);
|
|
}
|
|
|
|
|
|
KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandHelp()
|
|
{
|
|
// again not a fully special command: this routine just returns
|
|
// a CoreSimpleCommand but parses the parameters as constants
|
|
|
|
// we handle a single big parameter, with whitespace stripped
|
|
// This is because we want the identifiers to be preserved
|
|
// as unevaluated (i.e $function).
|
|
|
|
skipSpaces();
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
while(!KVSP_curCharIsEndOfCommand)KVSP_skipChar;
|
|
|
|
if(!KVSP_curCharIsEndOfBuffer)KVSP_skipChar;
|
|
|
|
TQString tmp(pBegin,KVSP_curCharPointer - pBegin);
|
|
tmp.stripWhiteSpace();
|
|
|
|
const TQString szHelpName("help");
|
|
|
|
KviKvsCoreSimpleCommandExecRoutine * r = KviKvsKernel::instance()->findCoreSimpleCommandExecRoutine(szHelpName);
|
|
if(!r)return 0; // <--- internal error!
|
|
|
|
KviKvsTreeNodeDataList * p = new KviKvsTreeNodeDataList(pBegin);
|
|
p->addItem(new KviKvsTreeNodeConstantData(pBegin,new KviKvsVariant(tmp)));
|
|
|
|
return new KviKvsTreeNodeCoreSimpleCommand(pBegin,szHelpName,p,r);
|
|
}
|