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.
3828 lines
120 KiB
3828 lines
120 KiB
//=============================================================================
|
|
//
|
|
// File : kvi_kvs_parser.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_parser.h"
|
|
|
|
#include "kvi_kvs_treenode.h"
|
|
|
|
#include "kvi_kvs_report.h"
|
|
#include "kvi_kvs_kernel.h"
|
|
|
|
#include "kvi_kvs_script.h"
|
|
|
|
#include "kvi_kvs_parser_macros.h"
|
|
|
|
#include "kvi_locale.h"
|
|
|
|
#include "kvi_options.h"
|
|
|
|
//FIXME: @ == $$-> == $this->
|
|
|
|
KviKvsParser::KviKvsParser(KviKvsScript * pScript,KviWindow * pOutputWindow)
|
|
{
|
|
// no need to initialize m_pBuffer
|
|
// no need to initialize m_ptr
|
|
// no need to initialize m_bError
|
|
m_pGlobals = 0;
|
|
m_pScript = pScript;
|
|
m_pWindow = pOutputWindow;
|
|
}
|
|
|
|
KviKvsParser::~KviKvsParser()
|
|
{
|
|
if(m_pGlobals)delete m_pGlobals;
|
|
}
|
|
|
|
|
|
|
|
|
|
void KviKvsParser::init()
|
|
{
|
|
KviKvsKernel * pKern = KviKvsKernel::instance();
|
|
|
|
#define _REG_CNTRL_CMD(__cntrlCmdName,__parsingRoutine) \
|
|
{ \
|
|
KviKvsSpecialCommandParsingRoutine * r = new KviKvsSpecialCommandParsingRoutine; \
|
|
r->proc = KVI_PTR2MEMBER(KviKvsParser::__parsingRoutine); \
|
|
pKern->registerSpecialCommandParsingRoutine(TQString(__cntrlCmdName),r); \
|
|
}
|
|
|
|
_REG_CNTRL_CMD("if",parseSpecialCommandIf);
|
|
_REG_CNTRL_CMD("global",parseSpecialCommandGlobal);
|
|
_REG_CNTRL_CMD("while",parseSpecialCommandWhile);
|
|
_REG_CNTRL_CMD("break",parseSpecialCommandBreak);
|
|
_REG_CNTRL_CMD("do",parseSpecialCommandDo);
|
|
_REG_CNTRL_CMD("for",parseSpecialCommandFor);
|
|
_REG_CNTRL_CMD("foreach",parseSpecialCommandForeach);
|
|
_REG_CNTRL_CMD("switch",parseSpecialCommandSwitch);
|
|
_REG_CNTRL_CMD("defpopup",parseSpecialCommandDefpopup);
|
|
_REG_CNTRL_CMD("unset",parseSpecialCommandUnset);
|
|
_REG_CNTRL_CMD("class",parseSpecialCommandClass);
|
|
_REG_CNTRL_CMD("help",parseSpecialCommandHelp);
|
|
|
|
#undef _REG_CNTRL_CMD
|
|
}
|
|
|
|
void KviKvsParser::report(bool bError,const TQChar * pLocation,const TQString &szMsgFmt,kvi_va_list va)
|
|
{
|
|
TQString szMsg;
|
|
KviTQString::vsprintf(szMsg,szMsgFmt,va);
|
|
|
|
KviPointerList<TQString> * pCodeListing = 0;
|
|
TQString szLocation;
|
|
|
|
if(pLocation)
|
|
{
|
|
pCodeListing = new KviPointerList<TQString>;
|
|
pCodeListing->setAutoDelete(true);
|
|
|
|
int iLine,iCol;
|
|
|
|
KviKvsReport::findLineColAndListing(m_pBuffer,pLocation,iLine,iCol,pCodeListing);
|
|
|
|
KviTQString::sprintf(szLocation,__tr2qs("line %d, near character %d"),iLine,iCol);
|
|
} else {
|
|
szLocation = __tr2qs("beginning of input");
|
|
}
|
|
|
|
KviKvsReport rep(bError ? KviKvsReport::ParserError : KviKvsReport::ParserWarning,m_pScript->name(),szMsg,szLocation,m_pWindow);
|
|
if(pCodeListing)rep.setCodeListing(pCodeListing);
|
|
|
|
KviKvsReport::report(&rep,m_pWindow);
|
|
}
|
|
|
|
void KviKvsParser::errorBadChar(const TQChar * pLocation,char cExpected,const char * szCommandName)
|
|
{
|
|
if(pLocation->unicode())
|
|
error(pLocation,__tr2qs("Found character '%q' (unicode 0x%x) where '%c' was expected: see \"/help %s\" for the command syntax"),
|
|
pLocation,pLocation->unicode(),cExpected,szCommandName);
|
|
else
|
|
error(pLocation,__tr2qs("Found end of input where character '%c' was expected: see \"/help %s\" for the command syntax"),
|
|
cExpected,szCommandName);
|
|
}
|
|
|
|
void KviKvsParser::error(const TQChar * pLocation,const TQString &szMsgFmt,...)
|
|
{
|
|
m_bError = true;
|
|
|
|
kvi_va_list va;
|
|
kvi_va_start_by_reference(va,szMsgFmt);
|
|
report(true,pLocation,szMsgFmt,va);
|
|
kvi_va_end(va);
|
|
}
|
|
|
|
void KviKvsParser::warning(const TQChar * pLocation,const TQString &szMsgFmt,...)
|
|
{
|
|
kvi_va_list va;
|
|
kvi_va_start_by_reference(va,szMsgFmt);
|
|
report(false,pLocation,szMsgFmt,va);
|
|
kvi_va_end(va);
|
|
}
|
|
|
|
KviKvsTreeNodeInstruction * KviKvsParser::parse(const TQChar * pBuffer,int iFlags)
|
|
{
|
|
m_iFlags = iFlags;
|
|
|
|
m_bError = false;
|
|
if(m_pGlobals)m_pGlobals->clear(); // this shouldn't be needed since this is a one time parser
|
|
|
|
m_pBuffer = pBuffer;
|
|
m_ptr = pBuffer;
|
|
|
|
if(!pBuffer)
|
|
{
|
|
error(0,__tr2qs("Empty script"));
|
|
return 0;
|
|
}
|
|
return parseInstructionList();
|
|
}
|
|
|
|
KviKvsTreeNodeInstruction * KviKvsParser::parseAsExpression(const TQChar * pBuffer,int iFlags)
|
|
{
|
|
m_iFlags = iFlags;
|
|
|
|
m_bError = false;
|
|
if(m_pGlobals)m_pGlobals->clear(); // this shouldn't be needed since this is a one time parser
|
|
|
|
m_pBuffer = pBuffer;
|
|
m_ptr = pBuffer;
|
|
|
|
if(!pBuffer)
|
|
{
|
|
error(0,__tr2qs("Empty script"));
|
|
return 0;
|
|
}
|
|
|
|
KviKvsTreeNodeExpression * expr = parseExpression(0);
|
|
if(!expr)return 0;
|
|
return new KviKvsTreeNodeExpressionReturn(pBuffer,expr);
|
|
}
|
|
|
|
KviKvsTreeNodeInstruction * KviKvsParser::parseAsParameter(const TQChar * pBuffer,int iFlags)
|
|
{
|
|
m_iFlags = iFlags;
|
|
|
|
m_bError = false;
|
|
if(m_pGlobals)m_pGlobals->clear(); // this shouldn't be needed since this is a one time parser
|
|
|
|
m_pBuffer = pBuffer;
|
|
m_ptr = pBuffer;
|
|
|
|
if(!pBuffer)
|
|
{
|
|
error(0,__tr2qs("Empty script"));
|
|
return 0;
|
|
}
|
|
|
|
KviKvsTreeNodeDataList * l = parseCommandParameterList();
|
|
if(!l)return 0;
|
|
|
|
return new KviKvsTreeNodeParameterReturn(pBuffer,l);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// THE REAL KVS
|
|
//
|
|
// <script> ::= <instruction list> '\0'
|
|
//
|
|
// <instruction list> ::= <instruction> [ <instruction list> ]
|
|
// <instruction> ::= <instruction block> | <command> | <operation> | <comment>
|
|
// <instruction block> ::= '{' <instruction list> '}'
|
|
//
|
|
// <comment> ::= <bash style line comment> | <c++ style comment> | <c style comment>
|
|
// <bash style comment> ::='#' <any char not including newline or null> <newline or null>
|
|
// <c++ style comment> ::= '//' <any char not including newline or null> <newline or null>
|
|
// <c style comment> ::= '/*' <any char not including null or the sequence */> '*/'
|
|
//
|
|
// <command> ::= <simple command> | <callback command> | <control command>
|
|
// <simple command> ::= <core command> | <module command> | <alias command>
|
|
// <core command> ::= <command identifier> <switch list> <command parameter list> <command terminator>
|
|
// <switch list> ::= '-'<switch body> [<switch list>]
|
|
// <command parameter list> ::= <command parameter>[<space><command parameter>]
|
|
// <command parameter> ::= <command parameter part>[<command parameter>]
|
|
// <command parameter part> ::= <command literal parameter> | <string parameter> | <data evaluation>
|
|
// <command literal parameter> ::= <anything except space , null , newline , ; , " , $ or %>
|
|
// <string parameter> ::= '"'<string parameter body>'"'
|
|
// <string parameter body> ::= <anything except '"' or null>...
|
|
// <command terminator> ::= ';' | '\n' | '\0'
|
|
|
|
// <data> ::=
|
|
// <data_reference> ::= <function_call> | <structured_data>
|
|
// <structured_data> ::= <data_structure> | <variable> | <pointer_data>
|
|
// <data_structure> ::= <array> | <hash>
|
|
// <array> ::= '%'<identifier>'[]'
|
|
// <hash> ::= '%'<identifier>'{}'
|
|
// <variable> ::= '%'<identifier> | <array_element> | <hash_element>
|
|
// <array_element> ::= '%'<identifier>'['<expression>']'
|
|
// <hash_element> ::= '%'<identifier>'{'<key>'}'
|
|
// <function_call> ::= <simple_function_call> | <pointer_function_call>
|
|
// <simple_function_call> ::= '$'<identifier>'('<function_parameter_list>')'
|
|
// <pointer_function_call> ::= <variable>'->'<function_call> | <simple_function_call>'->'<function_call>
|
|
// <pointer_data> ::= <variable>'->'<data>' | <simple_function_call>'->'<data>
|
|
|
|
|
|
//
|
|
// This must evaluate SCOPE OBJECT operators!
|
|
// thus...first evaluate the <data> or <simple_function_call>
|
|
// If it was <simple_function_call> or <data> evaluation results in returning a <variable>
|
|
// then check if there is a scope operator
|
|
// if there is then take it as the top of the tree, the <variable> or <simple_function_call>
|
|
// go at the left param of the scope operator and re-evaluate the right side <data> or <simple_function_call>
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
@doc: kvs_introduction
|
|
@type:
|
|
language
|
|
@keyterms:
|
|
kvs, compilation
|
|
@title:
|
|
KVIrc scripting language introduction
|
|
@short:
|
|
KVIrc scripting language introduction
|
|
@body:
|
|
[p]
|
|
[b]KVS[/b] is the [b]KV[/b]irc [b]S[/b]cripting language.
|
|
It was inspired by C++,sh,perl,php and mIrc scripting language implementations.
|
|
It is a compromise between flexibility and speed, a 'workaround' for many intrinsic
|
|
problems of an IRC-oriented scripting language.
|
|
[/p]
|
|
[p]
|
|
KVS is semi-interpreted: the execution is done in two main stages.
|
|
The first stage is the compilation where a syntactic tree is built.
|
|
The second stage is the real execution and is performed by visiting the tree
|
|
in the proper order. The syntactic trees are cached in memory so
|
|
the next executions can jump directly into the second stage.
|
|
This two-stage approach has been introduced in version 3.0.0, the previous
|
|
versions of the language used a single-stage on-the-fly interpreter.
|
|
[/p]
|
|
[p]
|
|
KVS allows you to:[br]
|
|
[ul]
|
|
[li]Implement automated reactions to the events generated by an IRC network[/li]
|
|
[li]Add new complex commands[/li]
|
|
[li]Add interface elements like popups, toolbars, buttons...[/li]
|
|
[li]Add advanced interface elements like complete dialogs or even widgets integrated in KVIrc[/li]
|
|
[/ul]
|
|
[/p]
|
|
[p]
|
|
KVS contains all the common constructs of structured programming.
|
|
You will find almost all the C control commands, sh/perl-like variables, arrays and and functions.
|
|
There are also some object-oriented characteristics: you will find C++ like
|
|
objects with constructors, destructors and class inheritance.
|
|
There are also more exotic concepts like the signal-slots interobject-communication.
|
|
Obviously you will also find most of the RFC1459 IRC commands and
|
|
other tools to "play" with an IRC connection.
|
|
[/p]
|
|
[p]
|
|
I'll try to explain the language by using examples
|
|
instead of strict syntactic rules. (Actually I have even
|
|
tried to write the rules...take a look [doc:syntactic_rules]here[/doc][br][br]
|
|
And please...forgive me for my "fantastic" english :)
|
|
[/p]
|
|
Szymon Stefanek
|
|
*/
|
|
|
|
/*
|
|
@doc: kvs_basicconcepts
|
|
@type:
|
|
language
|
|
@keyterms:
|
|
script
|
|
@title:
|
|
KVS basic concepts
|
|
@short:
|
|
KVS basic concepts
|
|
@body:
|
|
[big]Scripts[/big]
|
|
[p]
|
|
You use KVS to implement [b]scripts[/b].
|
|
A script is basically a finite list of KVS instructions.
|
|
When you type a command in the KVIrc input window you in fact
|
|
execute a small one-line script. You can store
|
|
longer scripts in KVIrc memory and execute them at later time.
|
|
Scripts can be also read from external files by the means of the
|
|
[cmd]parse[/cmd] command.
|
|
[/p]
|
|
[p]
|
|
There is an issue with the word [i]script[/i] that is worth clearing here.
|
|
It is common usage to call [i]script[/i] a thing that is something more
|
|
that a finite list of (some scripting language) instructions.
|
|
In fact a set of scripts, documentation files, graphics or other multimedia
|
|
files and sometimes executable binaries is still called a [i]script[/i]...just like
|
|
the "PrincoScript" or "dynamirc" (tough this last one should be categorized as "malware" instead).
|
|
In KVIrc such a collection of items is called [i]addon[/i], but be prepared
|
|
for both usages of the word in this documentation and around the web.
|
|
[/p]
|
|
[p]
|
|
More about addons in this [doc:addons]document[/doc]
|
|
[/p]
|
|
|
|
[big]Hello world![/big]
|
|
[p]
|
|
This documentation contains a lot of script examples.
|
|
They will appear like the following block of code:
|
|
[example]
|
|
[cmd]echo[/cmd] Hello world!
|
|
[/example]
|
|
The best way to experiment is to execute the scripts from an external file.
|
|
Try to copy & paste the example above to a file and save it in
|
|
a known place. Then in the command input window type
|
|
[example]
|
|
[b]/[/b][cmd]parse[/cmd] <filename>
|
|
[/example]
|
|
where <filename> stands for the name of the file you just have saved.
|
|
Some simple examples (like the one above) can be also typed
|
|
directly in the command input window.
|
|
You must remember that the command input window needs
|
|
a leading slash ('/') character to recognize a script.
|
|
The command input window can also be put in multiline mode by clicking
|
|
on the button on the right.
|
|
Another alternative for testing scripts is the code tester window.
|
|
You can access it by selecting "New code tester" from the Scripting menu
|
|
at the top of the KVIrc window. You will soon have the opportunity to
|
|
experiment with all the methods. Read on.
|
|
[/p]
|
|
|
|
[big]Basic syntax[/big]
|
|
[p]
|
|
A script contains a list of instructions separated by newlines or ';' characters.
|
|
Placing an instruction per line does not require a terminating character,
|
|
placing more instructions in a single line require them to be separated by ';'.
|
|
The most common instructions in KVS are [b]commands[/b]. A command is basically
|
|
a keyword followed by a list of space separater parameters.
|
|
The simplest (and the most useful) command in KVS is [cmd]echo[/cmd]; it prints
|
|
all its parameters to a KVIrc window.[br]
|
|
The following is an example of a valid script that uses only [cmd]echo[/cmd] commands.
|
|
[example]
|
|
echo "This is the first line"
|
|
ECHO This is the second line;
|
|
echo "This is the third line"; echo This is still on the third line;
|
|
eChO "This is the fourth line"; Echo "This is still on the fourth line"
|
|
[/example]
|
|
You have probably noticed that the terminating ';' character is optional
|
|
when the command is the last in a line.
|
|
The commands are [b]case insensitive[/b]; 'echo' is equivalent to 'Echo',
|
|
to 'ECHO' and to 'eChO'. In fact, most of KVS is case insensitive.
|
|
(There are obvious unavoidable exceptions for this rule; for example,
|
|
on UNIX systems file names are case sensitive and this must be
|
|
also reflected in KVS).
|
|
Another interesting thing is that when you execute the script you
|
|
don't see the enclosing quotes around the printed text: more about this
|
|
in the following sections.[br]
|
|
[note]
|
|
Cryptic note (you may skip it for now):[br]
|
|
Yes, the command terminator is a problem for those that want to use ';)' at the end
|
|
of IRC commands like [cmd]msg[/cmd]. It is almost unavoidable (read: the cost for
|
|
avoiding it is too high). Note that using '|' or any other character as command terminator
|
|
will NOT solve the problem: if the terminator is too difficult to type it will annoy the
|
|
scripters (and me), if it is too easy then there will be always someone that wants to use it
|
|
at the end (or in the middle) of a command with the original meaning.
|
|
The solution is to escape the ';' character:
|
|
[example]
|
|
[cmd]echo[/cmd] You can do it now \;)
|
|
[/example]
|
|
[/note]
|
|
[/p]
|
|
|
|
[big]Parameter processing[/big]
|
|
[p]
|
|
Most of the commands accept (and sometimes require) a list of parameters.
|
|
For example, the [cmd]join[/cmd] command (that is used to join an IRC channel)
|
|
accepts two parameters: the first one is the channel to join and the second is
|
|
the password. The simplified syntax for join is:
|
|
[example]
|
|
[cmd]join[/cmd] <channel> [password]
|
|
[/example]
|
|
The line above is an example of syntax specification. All the commands
|
|
are described by such syntax lines. [cmd]join[/cmd] is the command and it stands exactly
|
|
for the literal string "join" typed in a script. <channel> is in angular parenthesis
|
|
and rappresents a mandatory parameter: you must substitute a real channel name in its place
|
|
otherwise the command will fail and KVIrc will probably complain too.
|
|
[password] is still a parameter but the square parentheses indicate that it is
|
|
optional: if you specify it, then it will be interpreted as the channel password,
|
|
if you don't then no password will be used.
|
|
[note]
|
|
The syntax is written in a simplified BNF. I say simplified because it is not
|
|
totally strict around the KVIrc documentation. I just prefer the syntax to be
|
|
clear and easy to read instead of being formally perfect.
|
|
[/note]
|
|
You can finally join a channel by writing:
|
|
[example]
|
|
[cmd]join[/cmd] #kvirc kvircrocks
|
|
[/example]
|
|
or , since #kvirc usually has no password , by writing:
|
|
[example]
|
|
[cmd]join[/cmd] #kvirc
|
|
[/example]
|
|
In the example above the optional parameter [password] is omitted.
|
|
[note]
|
|
In fact it is not really omitted: KVIrc interprets it as an empty string that later
|
|
means "do not send the password to the server".
|
|
Empty strings are equivalent to omitted ones.
|
|
[/note]
|
|
[/p]
|
|
|
|
[big]Parameters, spaces and quotes[/big]
|
|
[p]
|
|
From the examples above is obvious that KVS command parameters are separated by spaces.
|
|
What is not totally obvious is that multiple spaces are allowed but KVIrc
|
|
will automatically reduce them to exactly one (just like HTML parsers or the shell
|
|
interpreters do). This is an useful behaviour in an IRC client since spaces usually
|
|
carry no information and in text oriented protocols make the parsing really harder (:D).
|
|
[/p]
|
|
[p]
|
|
The spaces are simplified in normal processing but there are ways to force KVIrc
|
|
to interpret the spaces just as they are.
|
|
The first method are the quotation marks: all the spaces enclosed in quotation marks
|
|
will be preserved.
|
|
[example]
|
|
[cmd]echo[/cmd] This text will have spaces simplified
|
|
[cmd]echo[/cmd] But "this one not"
|
|
[/example]
|
|
The first example will print out with spaces simplified but the second not.
|
|
The quotes are also a nice trick to embed spaces into a single parameter that
|
|
would be obviously splitted in two or more.
|
|
[example]
|
|
[cmd]echo[/cmd] Parameter1 Parameter2 "Parameter 3 ( with spaces )" Parameter4
|
|
[/example]
|
|
By running the examples above you may have noticed that the spaces are preserved but the
|
|
quotes are then stripped! Yes, this is another tricky behaviour. But don't be afraid:
|
|
it is really easier to use than to explain.
|
|
There is obviously a method to preserve the quotes too and it is also another
|
|
method to preserve the spaces but that leads us to the next paragraph.
|
|
[/p]
|
|
|
|
[big]Escape character[/big]
|
|
[p]
|
|
You may have already noticed that KVS treats some characters in a special way.
|
|
For example the double-quote characters can be used to enclose strings
|
|
and are stripped by the parser.
|
|
Another example of a special character is the command terminator (';'):
|
|
it has the "special" meaning of terminating a command.
|
|
If you want to enclose a literal quote in your text, you need to [b]escape[/b] it.
|
|
Like in most other programming languages, the escaping character is the backslash ('\').
|
|
[example]
|
|
[cmd]echo[/cmd] You can smile this way too! \;)
|
|
[/example]
|
|
The above example will treat the ';' as a part of the parameters and print it.[br]
|
|
In some languages the action of "escaping" a character is called "quoting".
|
|
Altough there is some confusion in this term, the meaning is to either use quotes
|
|
or to use the escape character to remove special meaning from some characters.
|
|
By quoting the spaces you can include them in a parameter, by escaping the quotes
|
|
you can include them in a command.
|
|
[example]
|
|
[cmd]echo[/cmd] "And he said \"Hello world!\""
|
|
[/example]
|
|
The example above will have the internal quotes preserved.
|
|
You can use the escape backslash to escape a newline:
|
|
[example]
|
|
[cmd]echo[/cmd] This text will be \
|
|
printed on a single line!
|
|
[/example]
|
|
After an escaped newline all the leading space and tab characters are skipped,
|
|
so you must include the needed spaces [b]before[/b] the escape character.
|
|
The previous example will be printed as:[br][br]
|
|
[i]This text will be printed on a single line[/i][br]
|
|
Another example:[br]
|
|
[example]
|
|
[cmd]echo[/cmd] "The new kvirc   \
|
|
IS OUT!"
|
|
[cmd]echo[/cmd] Check it out at http://www.kvi\
|
|
rc.net!
|
|
[/example]
|
|
This will be printed as:[br][br]
|
|
[i]
|
|
The new kvirc IS OUT![br]
|
|
Check it out at http://www.kvirc.net!
|
|
[/i][br]
|
|
Finally, you can escape an escape character to include it literally in a parameter (and
|
|
this is really the end of these tricks :)
|
|
Later we will discover other common usages of the backslash escape, such
|
|
as preventing KVIrc from interpreting a literal percent character as a variable
|
|
or separating variable names from the text.
|
|
[/p]
|
|
|
|
[big]Command switches[/big]
|
|
[p]
|
|
Many commands accept switch parameters.
|
|
[b]A switch modifies the behaviour of a command.[/b]
|
|
Any switch can optionally accept a parameter, that must
|
|
be specified after an equal ('=') sign.
|
|
[example]
|
|
[cmd]echo[/cmd] [b]-i = 2[/b] This text uses a specific color scheme
|
|
[/example]
|
|
The -i switch (just for example) changes the attributes
|
|
and the icon of the printed text.
|
|
[b]The switch must be specified immediately after the command keyword.[/b]
|
|
[example]
|
|
[cmd]echo[/cmd] This -i = 2 will obviously not work...
|
|
[/example]
|
|
If you want to start the first parameter of a command (that is not a switch)
|
|
with a literal '-' you must again escape it:
|
|
[example]
|
|
[cmd]echo[/cmd] \--- This text has three minus signs on the left
|
|
[/example]
|
|
or use the quotes:
|
|
[example]
|
|
[cmd]echo[/cmd] "--- This text has three minus signs on the left"
|
|
[/example]
|
|
[/p]
|
|
|
|
[big]Command blocks[/big]
|
|
[p]
|
|
Commands can be 'grouped' in blocks by using the classic C++ braces.
|
|
Here is a single line example:[br]
|
|
[example]
|
|
{ [cmd]echo[/cmd] First command; [cmd]echo[/cmd] Second command; } [cmd]echo[/cmd] Third command
|
|
[/example]
|
|
Multi line example:[br]
|
|
[example]
|
|
{
|
|
[cmd]echo[/cmd] First command
|
|
[cmd]echo[/cmd] Second command
|
|
}
|
|
[cmd]echo[/cmd] Third command
|
|
[/example]
|
|
[note]
|
|
Reminder : copy the example above to a text file
|
|
and then use /[cmd]parse[/cmd] <filename>
|
|
[/note]
|
|
In this case the command block has no special meaning
|
|
other than making the code more readable , but command blocks
|
|
will be useful later (see [cmd]if[/cmd],[cmd]while[/cmd]...).[br]
|
|
[note]
|
|
Unlike in C or C++, the braces do NOT automatically define a variable scope
|
|
(with few exceptions to this rule ... just to complicate the things a bit more).
|
|
You will recall this last assertion later, when reading about [doc:data_structures]data structures[/doc].
|
|
[/note]
|
|
[/p]
|
|
|
|
[big]Comments[/big]
|
|
[p]
|
|
KVIrc supports comments in command sequences.[br]
|
|
A comment starts with the character '#' and terminates with a newline.
|
|
You can start a comment anywhere a command can start.[br]
|
|
[example]
|
|
# This is a comment , it occupies the whole line
|
|
[cmd]echo[/cmd] After the comment!; # This is an end-line comment
|
|
[/example]
|
|
You can't escape newline characters in this case.
|
|
(or better: escape characters have no meaning in comments...
|
|
maybe one day I'll implement it).[br]
|
|
Starting from version 3.0.0 kvirc supports also C++ single line and C multiline comments.[br]
|
|
A C++ comment starts with two slashes '//' and terminates with a newline.
|
|
A multiline C comment starts with '/*' and ends at the first '* /' encountered.
|
|
Since KVIrc has no pre-processor, the C/C++ comments usually can't be placed in the middle of a command:
|
|
they must start where a command would start and end before the begin of another command.[br]
|
|
[/p]
|
|
|
|
[big]Indentation[/big]
|
|
[p]
|
|
You [b]should[/b] use spaces or [b]tabs[/b] to [b]indent[/b] your code. Note that the [b]should[/b]
|
|
word is written in bold characters: I mean that you really should indent your code.
|
|
Indenting helps both you (the script writer) and the reader (any other user that will
|
|
read your script). A good indenting practice is the first step to become a great programmer :)
|
|
[note]
|
|
Please note that the command parameters should be separated by
|
|
space characters (ascii 32). Tabs are not granted to work as parameter separators.[br]
|
|
[/note]
|
|
[example]
|
|
{
|
|
<tab>[cmd]echo[/cmd] Indented command
|
|
<tab>{
|
|
<tab><tab># Comment
|
|
<tab><tab>[cmd]echo[/cmd] Really Really long indented \
|
|
<tab><tab><tab>command
|
|
<tab>}
|
|
}
|
|
[/example]
|
|
Tabs behave better than spaces as indentation characters since other users can
|
|
adjust the tab size to match their taste. I personally prefer 4 character tabs
|
|
while most text/code editors usually come with 8 characters as default.
|
|
[/p]
|
|
|
|
[big]And now ?[/big]
|
|
[p]
|
|
You're now ready to really start experimenting with KVS. You can take
|
|
a look at the [doc:commands]command index[/doc] and start trying to use them
|
|
while keeping in mind the rules described in this document.
|
|
The next suggested lecture is the documentation about [doc:kvs_aliasesandfunctions]the aliases and the functions[/doc].
|
|
Have fun :)
|
|
[/p]
|
|
*/
|
|
|
|
/*
|
|
@doc: kvs_aliasesandfunctions
|
|
@type:
|
|
language
|
|
@keyterms:
|
|
aliases, functions
|
|
@title:
|
|
KVS Functions and aliases
|
|
@short:
|
|
KVS Functions and aliases
|
|
@body:
|
|
[big]Introduction[/big]
|
|
[p]
|
|
Since you're here, you should already have readed about the [doc:kvs_basicconcepts]KVS basic concepts[/doc]
|
|
and have visited the [doc:commands]command index[/doc]. If you feel ready to take the next step
|
|
then read on.
|
|
[/p]
|
|
|
|
[big]Functions[/big][br]
|
|
[p]
|
|
KVS has many internal [doc]functions[/doc] that can be used as command parameters.[br]
|
|
[b]All the function names start with a literal '$' character.[/b][br]
|
|
[example]
|
|
[cmd]echo[/cmd] This window caption is [fnc]$window.caption[/fnc]
|
|
[/example]
|
|
The [fnc]$window.caption[/fnc] [doc:functions]function[/doc]
|
|
is evaluated before the command executes,
|
|
and it is changed into the current window caption text.[br]
|
|
The [doc]functions[/doc] can be used also as switch parameters.[br]
|
|
[example]
|
|
[cmd]echo[/cmd] -w = [fnc]$window[/fnc] This text will be surely \
|
|
printed in the current window
|
|
[/example]
|
|
The -w switch allows to redirect the echo text to a specified window --- in this
|
|
case the one that you are typing in.[br]
|
|
[i](Surprise: in this case the -w switch is useless ,
|
|
since echo prints text to the current window by default...
|
|
but it will work correctly. :)[/i]
|
|
[/p]
|
|
[p]
|
|
Normal function names can be made of "anycase" letters, digits and underscores,
|
|
with the restriction that the first character is not a digit.[br]
|
|
Some kind of functions can contain a dot '.' character inside the name
|
|
and these are assumed to be module references (see [doc:modules]the modules documentation[/doc]).[br]
|
|
[/p]
|
|
[p]
|
|
By now we have seen only simple functions, but there's more...[br]
|
|
The functions can accept parameters; the general syntax for a function call is:[br]
|
|
[b]$<function name>['('<parameter_list>')'][/b][br]
|
|
where <parameter_list> is a list of comma separated parameters,
|
|
eventually empty.
|
|
[example]
|
|
[cmd]echo[/cmd] The word 'neural' is [fnc]$str.len[/fnc](neural) characters long
|
|
[/example]
|
|
The function [fnc]$str.len[/fnc] accepts a single parameter and returns the
|
|
length in characters of the parameter string. The returned value is always
|
|
a string: in this case it can be also interpreted as a number.[br]
|
|
When passing an empty list you can avoid the parenthesis.
|
|
(And you have found the "simple" functions shown above).
|
|
So the followind two calls are equal:[br]
|
|
[example]
|
|
[cmd]echo[/cmd] [fnc]$window.caption[/fnc]
|
|
[cmd]echo[/cmd] [fnc]$window.caption()[/fnc]
|
|
[/example]
|
|
If you want to pass an "empty" string as the first parameter you have to use
|
|
the following syntax:[br]
|
|
[example]
|
|
[cmd]echo[/cmd] [fnc]$str.len[/fnc]("")
|
|
[/example]
|
|
Obviously a function is valid as a function parameter.[br]
|
|
[example]
|
|
[cmd]echo[/cmd] [fnc]$str.len[/fnc]([fnc]$window.caption[/fnc])
|
|
[/example]
|
|
If you want to place a literal '(' or ')' in the function parameters
|
|
you must escape it.
|
|
A special case for when you want to use 'matching' parentheses:
|
|
an opened '(' corresponds to a closed ')'.
|
|
In this case you can omit the 'escape' character.[br]
|
|
[example]
|
|
[cmd]echo[/cmd] The length of '(a+b)' is : [fnc]$str.len[/fnc]( (a+b) )
|
|
[/example]
|
|
This is useful for algebraic and boolean expressions , like the ones
|
|
accepted by the special function $() (see next paragraphs).[br]
|
|
[/p]
|
|
|
|
|
|
[big]Aliases[/big][br]
|
|
An alias is an user defined command. It can be used to rename the builtin kvirc commands or functions,
|
|
to automatize complex tasks or as structured programming mean.
|
|
Aliases can be created or destroyed by using the scriptcenter (graphic interface)
|
|
or from the commandline (or script) by using the [cmd]alias[/cmd] command.
|
|
Once created, an alias remains stored permanently in the KVIrc configuration files
|
|
until it is explicitly deleted.
|
|
A couple of examples will make the things clear.
|
|
join is a really commonly used command. It might be a good idea to rename it to
|
|
simply "j" .. just to type it faster.
|
|
Nothing easier in KVirc: just try this commandline:
|
|
[example]
|
|
[cmd]alias[/cmd](j){ [cmd]join[/cmd] $0-; };
|
|
[/example]
|
|
|
|
This will create the alias "j". From this moment you can use /j as it was a normal command.
|
|
[example]
|
|
j #kvirc
|
|
[/example]
|
|
You may have notices the strange $0- function in the alias body: it stands for
|
|
"all parameters passed to the alias". This means that when you call
|
|
[example]
|
|
j #kvirc testpassword
|
|
[/example]
|
|
then both the parameters (#kvirc and testpassword) are passed to the join command.
|
|
The $N functions are special functions that return the positional parameters passed
|
|
to the current script context. In an alias the script context is the script body and
|
|
it is the alias caller that generates the parameters.
|
|
$N (where N is a digit) returns the (N-1)-th positional parameter passed by the caller.
|
|
It returns the parameter numbered N-1 and not N since the parameters are indexed starting
|
|
from zero ($0 is the first parameter!).
|
|
$N-M returns the parameters from (N-1)-th to the (M-1)-th (a parameter range) and $N- returns
|
|
all the parameters from (N-1)-th to the last one. In the example above $0- stands for
|
|
all the parameters starting from the first one.
|
|
[/p]
|
|
[p]
|
|
To remove an alias use again the alias command with an empty body:
|
|
[example]
|
|
[cmd]alias[/cmd](j){}
|
|
[/example]
|
|
This will remove the alias "j" defined above.
|
|
[/p]
|
|
[p]
|
|
A common task in channel management is the kick & ban action.
|
|
You first ban an user from the channel and then eventually kick him
|
|
(obviously assuming that he is actually on the channel).
|
|
This involves using two commands: ban and then kick.
|
|
It could be a nice idea to have a single "kb" command to perform this action.
|
|
Well...easy:
|
|
[example]
|
|
[cmd]alias[/cmd](kb){ [cmd]ban[/cmd] $0; [cmd]kick[/cmd] $0-; };
|
|
[/example]
|
|
This adds the "kb" alias: it can be called as a normal command:
|
|
[example]
|
|
kb spammer You're not welcome here!
|
|
[/example]
|
|
This will first execute "ban spammer" and then "kick spammer You're not welcome here".
|
|
Our kb is a really simple example... it doesn't check for the validity of the parameters:
|
|
the server will warn us if the parameters passed to kb were empty.
|
|
[/p]
|
|
[p]
|
|
The alias can be modified at any time by re-using the alias command.
|
|
Let's make our "kb" a bit more intelligent and add a check for the parameters.
|
|
TIP: It is a good idea to write the following examples in a text file and then use /parse <filename> to execute it.
|
|
[example]
|
|
[cmd]alias[/cmd](kb)
|
|
{
|
|
[cmd]if[/cmd]("$0" == "")
|
|
{
|
|
[cmd]echo[/cmd] "Usage: /kb <nickname> <kick reason>"
|
|
[cmd]return[/cmd]
|
|
}
|
|
[cmd]ban[/cmd] $0
|
|
%reason = $1-
|
|
[cmd]if[/cmd]("%reason" == "")%reason = "You're not welcome here!"
|
|
[cmd]kick[/cmd] $0 %reason
|
|
}
|
|
[/example]
|
|
The example above will first check the validity of the <nickname> passed to kb:
|
|
if no nickname was passed , it will warn the user and stop.
|
|
The next step will be the "ban <nickname>" call. Another enchancement is the "default reason":
|
|
we first assign the remaining parameters ($1- means "from $1 to the end") to a temporary variable,
|
|
if the variable is empty , a default kick reason is assigned.
|
|
Finally the "kick <nickname> <reason>" will be executed.
|
|
Get used to looking at the single command documentation pages, they will give
|
|
you the hints necessary to fully understand the above piece of code.
|
|
[/p]
|
|
[p]
|
|
Aliases can be used as a mean for structured programming.
|
|
In large scripts you will SURELY have "common tasks" to perform (like having specially
|
|
colored output or calculating a value from a set of other values)...
|
|
Aliases are the way of writing the common tasks: they are equivalent to the "procedures"
|
|
or "functions" in many high-level programming languages.
|
|
The alias as a procedure (subroutine or sub-task) has been shown in the "kb" example above:
|
|
it might be commonly called from complexier scripts or other aliases in case that a
|
|
kick & ban action is needed.
|
|
[/p]
|
|
[p]
|
|
The aliases can be used also as functions.
|
|
Assume that you need really often to calculate the sum of three numbers: a function-alias is the way.
|
|
[example]
|
|
[cmd]alias[/cmd](sum3){ [cmd]return[/cmd] $($0 + $1 + $2); };
|
|
[/example]
|
|
This will add the alias "sum3" and make it available both as a command and a function.
|
|
The "return" command sets the return value of a sequence of commands
|
|
(an alias is a sequence of commands...remember ?) and terminates the execution (by returning
|
|
the control to the caller).
|
|
So return $($0 + $1 + $2); will set the return value of the alias to the value
|
|
computed by $($0 + $1 + $2) that actually is the sum of the first three parameters passed.
|
|
You will then use it in the following way:
|
|
[example]
|
|
...
|
|
%myfirstsum = $sum3(%somevalue,%someothervalue,4)
|
|
%anothersum = $sum3(12,%somevalue,%anothervalue)
|
|
...
|
|
[/example]
|
|
Ops.. I've used some variables without actually explaining them... hehe.. please forgive me and read on.
|
|
This example is again really simple , but you might have complexier function-aliases.
|
|
The function-aliases are also normal aliases.... you can use it as a command:
|
|
[example]
|
|
/sum3 1 2 3
|
|
[/example]
|
|
Is a perfectly valid call.... it's just that it will have no visible results
|
|
(just because a command call implies ignoring the return value.
|
|
In fact there is no difference al all between function-aliases and normal-aliases:
|
|
the caller makes the difference: by calling an alias as a command the return value
|
|
just disappears in hyperspace, by calling an alias as a function , the return value
|
|
is propagated (and in fact "used").
|
|
(There are some "nice" exceptions to this rule...but you don't need to care about it, for now).
|
|
If return is not called inside an alias body , the return value will be just a null value.
|
|
[/p]
|
|
[p]
|
|
Aliases can accept switches just like any other command. The [fnc]$sw[/fnc] is there
|
|
exactly for that purpose. Check it out.
|
|
[/p]
|
|
|
|
[big]Special functions[/big]
|
|
[p]
|
|
We have already seen the positional parameter functions.
|
|
The functions of type [b]$N[-[M]][/b] (where N and M are positive
|
|
numbers starting from 0 and N < M) evaluate to the sequence of
|
|
[b]positional parameters[/b] from Nth to Mth."[br]
|
|
If M is omitted , the function evaluate to the sequence of [b]positional
|
|
parameters[/b] from Nth to the last one. If the whole -M block is omitted
|
|
the function evaluate to the Nth positional parameter.
|
|
We will discover more on the [b]positional parameters[/b] when talking
|
|
of aliases and events.[br]
|
|
[example]
|
|
$0 evaluates to the 1st positional parameter
|
|
$0-4 evaluates to the parameters from first to 5th
|
|
$41- evaluates to the parameters from 41st to the last avaiable
|
|
[/example]
|
|
The function [b]$#[/b] evaluates to the number of positional parameters available.
|
|
The [b]positional parameter[/b] functions do not accept parameters.[br]
|
|
The special function [b]$(<expression>)[/b] returns the result
|
|
of the evaluation of the <expression>. In previous versions of KVIrc this
|
|
function was called [fnc]$calc[/fnc].[br]
|
|
[example]
|
|
[cmd]echo[/cmd] $(2 + (3 ^ 7) <= 1 * (3 && 2))
|
|
[/example]
|
|
The special function [b]${<command sequence>}[/b] evaluates to the
|
|
return value of the <command sequence>.[br]
|
|
The special function [b]$$[/b] evaluates to the current object id,
|
|
but it is too early to explain it here...[br]
|
|
*/
|
|
|
|
/*
|
|
@doc: command_rebinding
|
|
@type:
|
|
language
|
|
@keyterms:
|
|
Rebinding commands to another window
|
|
@title:
|
|
Standard rebinding switch
|
|
@short:
|
|
Standard rebinding switch
|
|
@syntax:
|
|
<command> -r=<window_id> <parameters>
|
|
@body:
|
|
The -r switch is standardized along all the commands. It rebinds a command
|
|
to the windows specified by <window_id>. It is useful to launch commands
|
|
in windows that are not the current one. For example, you might want to
|
|
say something in a specific channel while processing an event bound to
|
|
a console, or say something in all the channels bound to the current irc context.
|
|
The examples below will make everything clear.
|
|
@examples:
|
|
[example]
|
|
[comment]# Run a command in the console of the current IRC context[/comment]
|
|
[cmd]echo[/cmd] -r=$console This command is executed in the console ($window.caption)
|
|
[comment]# Say something to all the channels of the current IRC context[/comment]
|
|
[cmd]foreach[/cmd](%w,[fnc]$window.list[/fnc](channel))[cmd]say[/cmd] -r=%w Hi ppl on [fnc]$chan.name[/fnc]
|
|
[/example]
|
|
*/
|
|
|
|
/*
|
|
@doc: window_naming_conventions
|
|
@type:
|
|
language
|
|
@title:
|
|
Window naming conventions
|
|
@keyterms:
|
|
IRC context,window ID,frame window,connection ID
|
|
@short:
|
|
KVIrc window structure and the window naming conventions
|
|
@body:
|
|
[big]Introduction[/big][br]
|
|
Starting from the release 3.0.0 KVIrc window structure has
|
|
grown in complexity. Older releases allowed one connetion
|
|
per "frame window" and thus had a dedicated command parser
|
|
for each connection. Finding a window in that scenario
|
|
was quite easy: it was enough to designate it by "name"
|
|
(that was exactly the text displayed in the window caption).
|
|
It was sufficient to have an "unique" name for ever window;
|
|
condition that was granted by the underlying IRC protocol
|
|
and by the KVIrc core design.[br]
|
|
In this version, the unique window names are impossible to be granted.[br]
|
|
[big]Scenario[/big][br]
|
|
The command parser is now "global" to the application.
|
|
There can be two or more consoles in each frame and the user
|
|
is able to join the same channel with two different nicknames
|
|
using two separate connections.
|
|
[ul]
|
|
[li]
|
|
Application (Unique command parser)
|
|
[ul]
|
|
[li]
|
|
Frame X
|
|
[ul]
|
|
[li]
|
|
Console M (IRC context)
|
|
[ul]
|
|
[li]Channel windows[/li]
|
|
[li]Query windows[/li]
|
|
[li]Other connection related windows[/li]
|
|
[/ul]
|
|
[/li]
|
|
[li]
|
|
Console N (IRC context)
|
|
[ul]
|
|
[li]Channel windows[/li]
|
|
[li]Query windows[/li]
|
|
[li]Other connection related windows[/li]
|
|
[/ul]
|
|
[/li]
|
|
[li]
|
|
Other windows
|
|
[/li]
|
|
[li]
|
|
...
|
|
[/li]
|
|
[/ul]
|
|
[/li]
|
|
[li]
|
|
Frame Y
|
|
[ul]
|
|
[li]
|
|
Console O (IRC context)
|
|
[ul]
|
|
[li]Channel windows[/li]
|
|
[li]Query windows[/li]
|
|
[li]Other connection related windows[/li]
|
|
[/ul]
|
|
[/li]
|
|
[li]
|
|
Console P (IRC context)
|
|
[ul]
|
|
[li]Channel windows[/li]
|
|
[li]Query windows[/li]
|
|
[li]Other connection related windows[/li]
|
|
[/ul]
|
|
[/li]
|
|
[li]
|
|
Other windows
|
|
[/li]
|
|
[li]
|
|
...
|
|
[/li]
|
|
[/ul]
|
|
[/li]
|
|
[li]
|
|
...
|
|
[/li]
|
|
[/ul]
|
|
[/li]
|
|
[/ul]
|
|
[br]
|
|
A naming convention has becomed necessary to resolve ambiguities.[br]
|
|
[big]Basic assumptions[/big]
|
|
Every KVIrc window has four main properties:[br]
|
|
-[b]an unique numeric identifier[/b][br]
|
|
-[b]the logical name[/b][br]
|
|
-[b]the type identifier[/b][br]
|
|
-[b]the caption text[/b][br]
|
|
The [b]numeric identifier[/b] is unique to the whole application,
|
|
and is the one returned by the [fnc]$window[/fnc] function.[br]
|
|
The identifier is assigned by KVIrc when the window is created
|
|
and is not changed until the window is destroyed.
|
|
This identifier will be referred as [b]window ID[/b].[br]
|
|
The [b]logical name[/b] is a property of some kind of windows.
|
|
It usually corresponds to the first part of the window caption.
|
|
For example, for channel windows it is the channel name, for
|
|
queries it is the list of the targets. For some other windows
|
|
the logical name corresponds to the caption text. This will be discussed later.[br]
|
|
The [b]type identifier[/b] describes the properties of a certain window.
|
|
For channel windows the type identifier is "channel" , for query windows is "query" ,
|
|
for console windows it is "console", etc..[br]
|
|
|
|
[big]Irc contexts[/big][br]
|
|
The KVIrc frame windows are numbered starting from 0 and named
|
|
"frame_<number>". Each frame can contain an unlimited number of consoles.[br]
|
|
Each console is bound to an [b]IRC context[/b]. (The part "is bound to" could
|
|
be substituted by "defines" or "is contained in").[br]
|
|
[i]An [b]IRC context[/b] is a set of resources that can deal with a single
|
|
IRC connection.[/i][br]
|
|
The association between an [b]IRC context[/b]
|
|
and a console is bijective: each [b]IRC context[/b] is associated
|
|
to a single console window.[br]
|
|
An [b]IRC context[/b] can be in connected or not-connected state.
|
|
When in connected state, it contains a set of windows beside the console:
|
|
mainly channels and query windows.
|
|
The channels and query windows can exist ONLY if the associated
|
|
[b]IRC context[/b] exists.[br]
|
|
Channels and queries have unique names inside a connection so
|
|
there is no way to confuse it. (Theoretically there can
|
|
be more than one query window with the same name, but in fact
|
|
all the windows refer to the same target so they are instances
|
|
of the same resource).
|
|
All this creates a sort of namespace: the channels and queries can be identified
|
|
as "bound" to a specific [b]IRC context[/b].[br]
|
|
An [b]IRC context[/b] can "contain" other windows, such as the "sockets"
|
|
window or the "list" window. KVIrc takes care of making them
|
|
unique inside the [b]IRC context[/b] namespace.[br]
|
|
Each [b]IRC context[/b] has its own unique [b]IRC context ID[/b] (see [fnc]$context[/fnc]).[br]
|
|
Since to a single [b]IRC context[/b] may correspond only a single irc connection,
|
|
when in connected state, the [b]IRC context[/b] may be referred also as [b]connection[/b]
|
|
or [b]connection context[/b], and the associated [b]IRC context Id[/b] can be
|
|
referred as [b]connection ID[/b] or [b]connection context ID[/b].[br]
|
|
There are classes of windows that are not bound to any [b]IRC context[/b]:
|
|
this includes user created windows, DCC windows, browsers etc.[br]
|
|
KVIrc will try to keep that windows with unique logical names.[br]
|
|
[big]How to identify a window[/big][br]
|
|
So what we have until now is:[br]
|
|
[ul]
|
|
[li]Each window has its own unique [b]window ID[/b]: we
|
|
will refer windows always using this identifier.[/li]
|
|
[li]Each window has a set of properties including:
|
|
window type, logical name.[/li]
|
|
[li]Subsets of windows are bound to a single [b]IRC context[/b][/li]
|
|
[/ul]
|
|
The simplest (but also the less significant) method of looking for
|
|
a window is to finding it by caption.[br]
|
|
The [fnc]$window[/fnc] function finds the first KVIrc window matching
|
|
the "caption text" and returns its [b]window ID[/b].[br]
|
|
This method will likely fail when there are more windows with the same
|
|
caption text; for this reason several specific functions
|
|
have been added to allow finding the correct window.[br]
|
|
The [fnc]$console[/fnc] finds a console window bound to a specified
|
|
[b]IRC context[/b].[br]
|
|
The [fnc]$channel[/fnc] finds a channel window matching the specified
|
|
name and bound to a specified [b]IRC context[/b].[br]
|
|
The [fnc]$query[/fnc] finds a query window that has a specified target
|
|
and is bound to a specified [b]IRC context[/b].[br]
|
|
*/
|
|
|
|
|
|
/*
|
|
@doc: connection_dependant_commands
|
|
@type:
|
|
language
|
|
@title:
|
|
Connection dependant commands
|
|
@keyterms:
|
|
IRC context, connection dependant commands
|
|
@body:
|
|
Many KVIrc commands are connection dependant:
|
|
you need an IRC connection to succesfully execute them;
|
|
usually because some data needs to be sent to the server.
|
|
This includes commands like [cmd]whois[/cmd],[cmd]raw[/cmd],[cmd]query[/cmd],
|
|
[cmd]msg[/cmd],[cmd]notice[/cmd],[cmd]op[/cmd],[cmd]ctcp[/cmd]...[br]
|
|
These commands must be executed in a window that is bound to a
|
|
[b]connected [doc:window_naming_conventions]IRC context[/doc][/b].
|
|
You will obviously get an error message if you try to use them in a window
|
|
that has no associated IRC connection.[br]
|
|
For instance: [cmd]whois[/cmd] will work only if you execute it
|
|
in a console , channel or query window.[br]
|
|
If you want to use these commands in a window that is not associated to
|
|
any IRC context you may use the [doc:command_rebinding]standard -r switch[/doc].
|
|
You can use the same switch to execute a command in an [b]IRC context[/b] that is
|
|
not the current one.
|
|
*/
|
|
|
|
|
|
/*
|
|
@doc: aliases
|
|
@type:
|
|
language
|
|
@keyterms:
|
|
aliases
|
|
@title:
|
|
Aliases
|
|
@short:
|
|
Aliases : user definable command sequences
|
|
@body:
|
|
An alias is an user defined command. It can be used to rename the builtin kvirc commands or functions,
|
|
to automatize complex tasks or as structured programming mean.
|
|
Aliases can be created or destroyed by using the scriptcenter (graphic interface)
|
|
or from the commandline (or script) by using the [cmd]alias[/cmd] command.
|
|
Once created, an alias remains stored permanently in the KVIrc configuration files
|
|
until it is explicitly deleted.
|
|
A couple of examples will make the things clear.
|
|
join is a really commonly used command. It might be a good idea to rename it to
|
|
simply "j" .. just to type it faster.
|
|
Nothing easier in KVirc: just try this commandline:
|
|
[example]
|
|
[cmd]alias[/cmd](j){ [cmd]join[/cmd] $0-; };
|
|
[/example]
|
|
|
|
This will create the alias "j". From this moment you can use /j as it was a normal command.
|
|
[example]
|
|
j #kvirc
|
|
[/example]
|
|
You may have notices the strange $0- function in the alias body: it stands for
|
|
"all parameters passed to the alias". This means that when you call
|
|
[example]
|
|
j #kvirc testpassword
|
|
[/example]
|
|
then both the parameters (#kvirc and testpassword) are passed to the join command.
|
|
The $N functions are special functions that return the positional parameters passed
|
|
to the current script context. In an alias the script context is the script body and
|
|
it is the alias caller that generates the parameters.
|
|
$N (where N is a digit) returns the (N-1)-th positional parameter passed by the caller.
|
|
It returns the parameter numbered N-1 and not N since the parameters are indexed starting
|
|
from zero ($0 is the first parameter!).
|
|
$N-M returns the parameters from (N-1)-th to the (M-1)-th (a parameter range) and $N- returns
|
|
all the parameters from (N-1)-th to the last one. In the example above $0- stands for
|
|
all the parameters starting from the first one.
|
|
[/p]
|
|
[p]
|
|
To remove an alias use again the alias command with an empty body:
|
|
[example]
|
|
[cmd]alias[/cmd](j){}
|
|
[/example]
|
|
This will remove the alias "j" defined above.
|
|
[/p]
|
|
[p]
|
|
A common task in channel management is the kick & ban action.
|
|
You first ban an user from the channel and then eventually kick him
|
|
(obviously assuming that he is actually on the channel).
|
|
This involves using two commands: ban and then kick.
|
|
It could be a nice idea to have a single "kb" command to perform this action.
|
|
Well...easy:
|
|
[example]
|
|
[cmd]alias[/cmd](kb){ [cmd]ban[/cmd] $0; [cmd]kick[/cmd] $0-; };
|
|
[/example]
|
|
This adds the "kb" alias: it can be called as a normal command:
|
|
[example]
|
|
kb spammer You're not welcome here!
|
|
[/example]
|
|
This will first execute "ban spammer" and then "kick spammer You're not welcome here".
|
|
Our kb is a really simple example... it doesn't check for the validity of the parameters:
|
|
the server will warn us if the parameters passed to kb were empty.
|
|
[/p]
|
|
[p]
|
|
The alias can be modified at any time by re-using the alias command.
|
|
Let's make our "kb" a bit more intelligent and add a check for the parameters.
|
|
TIP: It is a good idea to write the following examples in a text file and then use /parse <filename> to execute it.
|
|
[example]
|
|
[cmd]alias[/cmd](kb)
|
|
{
|
|
[cmd]if[/cmd]("$0" == "")
|
|
{
|
|
[cmd]echo[/cmd] "Usage: /kb <nickname> <kick reason>"
|
|
[cmd]return[/cmd]
|
|
}
|
|
[cmd]ban[/cmd] $0
|
|
%reason = $1-
|
|
[cmd]if[/cmd]("%reason" == "")%reason = "You're not welcome here!"
|
|
[cmd]kick[/cmd] $0 %reason
|
|
}
|
|
[/example]
|
|
The example above will first check the validity of the <nickname> passed to kb:
|
|
if no nickname was passed , it will warn the user and stop.
|
|
The next step will be the "ban <nickname>" call. Another enchancement is the "default reason":
|
|
we first assign the remaining parameters ($1- means "from $1 to the end") to a temporary variable,
|
|
if the variable is empty , a default kick reason is assigned.
|
|
Finally the "kick <nickname> <reason>" will be executed.
|
|
Get used to looking at the single command documentation pages, they will give
|
|
you the hints necessary to fully understand the above piece of code.
|
|
[/p]
|
|
[p]
|
|
Aliases can be used as a mean for structured programming.
|
|
In large scripts you will SURELY have "common tasks" to perform (like having specially
|
|
colored output or calculating a value from a set of other values)...
|
|
Aliases are the way of writing the common tasks: they are equivalent to the "procedures"
|
|
or "functions" in many high-level programming languages.
|
|
The alias as a procedure (subroutine or sub-task) has been shown in the "kb" example above:
|
|
it might be commonly called from complexier scripts or other aliases in case that a
|
|
kick & ban action is needed.
|
|
[/p]
|
|
[p]
|
|
The aliases can be used also as functions.
|
|
Assume that you need really often to calculate the sum of three numbers: a function-alias is the way.
|
|
[example]
|
|
[cmd]alias[/cmd](sum3){ [cmd]return[/cmd] $($0 + $1 + $2); };
|
|
[/example]
|
|
This will add the alias "sum3" and make it available both as a command and a function.
|
|
The "return" command sets the return value of a sequence of commands
|
|
(an alias is a sequence of commands...remember ?) and terminates the execution (by returning
|
|
the control to the caller).
|
|
So return $($0 + $1 + $2); will set the return value of the alias to the value
|
|
computed by $($0 + $1 + $2) that actually is the sum of the first three parameters passed.
|
|
You will then use it in the following way:
|
|
[example]
|
|
...
|
|
%myfirstsum = $sum3(%somevalue,%someothervalue,4)
|
|
%anothersum = $sum3(12,%somevalue,%anothervalue)
|
|
...
|
|
[/example]
|
|
Ops.. I've used some variables without actually explaining them... hehe.. please forgive me and read on.
|
|
This example is again really simple , but you might have complexier function-aliases.
|
|
The function-aliases are also normal aliases.... you can use it as a command:
|
|
[example]
|
|
/sum3 1 2 3
|
|
[/example]
|
|
Is a perfectly valid call.... it's just that it will have no visible results
|
|
(just because a command call implies ignoring the return value.
|
|
In fact there is no difference al all between function-aliases and normal-aliases:
|
|
the caller makes the difference: by calling an alias as a command the return value
|
|
just disappears in hyperspace, by calling an alias as a function , the return value
|
|
is propagated (and in fact "used").
|
|
(There are some "nice" exceptions to this rule...but you don't need to care about it, for now).
|
|
If return is not called inside an alias body , the return value will be just a null value.
|
|
[/p]
|
|
[p]
|
|
Aliases can accept switches just like any other command. The [fnc]$sw[/fnc] is there
|
|
exactly for that purpose. Check it out.
|
|
[/p]
|
|
*/
|
|
|
|
|
|
/*
|
|
@doc: kvs_addons
|
|
@type:
|
|
language
|
|
@keyterms:
|
|
addons, addon
|
|
@title:
|
|
The KVIrc addon system
|
|
@short:
|
|
Writing KVIrc addons
|
|
@body:
|
|
[big]Introduction[/big]
|
|
[p]
|
|
An addon is basically a set of KVS scripts, multimedia, documentation
|
|
and accessory files that implement a KVIrc feature.
|
|
It might be a simple automatic-away subsystem, a GUI newsticker or a complex file sharing
|
|
service (commonly called "fserve"). Addons are sometimes called "scripts".
|
|
In fact a KVIrc addon is usually made of more than one KVS script.
|
|
[/p]
|
|
[p]
|
|
KVIrc has a builtin addon management system that allows the users
|
|
to install, configure and uninstall features with a nice graphical interface.
|
|
The management system allows the addons to have documentation integrated in the
|
|
KVIrc help and to be translated in several languages.
|
|
[/p]
|
|
|
|
[big]Addon installation[/big]
|
|
[p]
|
|
The addons are usually shipped in compressed archives (such as tar.gz "tarballs" or
|
|
zip files). Once uncompressed they should contain a KVS script file called "install.kvs".
|
|
KVIrc will look for and execute this file when the user will ask for your addon to
|
|
be installed. The install.kvs will usually contain the code for the [b]registration[/b]
|
|
of your addon and will [cmd]include[/cmd] all the other necessary source files.
|
|
[/p]
|
|
|
|
[big]The minimal addon[/big]
|
|
[p]
|
|
The smallest addon that you can write is the one that does nothing.
|
|
It just need to be writte in a file named install.kvs and contain code
|
|
similar to the following:
|
|
[example]
|
|
[cmd]addon.register[/cmd]("myaddon", \
|
|
"1.0.0", \
|
|
"My First Addon", \
|
|
"An addon that is really cool but does simply nothing", \
|
|
"3.2.0.99.20051230")
|
|
{
|
|
}
|
|
[/example]
|
|
The code above does nothing but registers the "myaddon" addon.
|
|
[/p]
|
|
[p]
|
|
The first parameter is the internal addon id which can be used to identify
|
|
your addon inside KVIrc. The id must be unique: two addons that share the same
|
|
name cannot be installed. The second parameter is the addon version. It should
|
|
be expressed in the classic format [major].[minor].[pathlevel] or something
|
|
really similar (in fact KVIrc just expects the version to be a string composed
|
|
of numbers separated by dots). The version is compared when an addon is installed
|
|
and KVIrc complains if the user tries to downgrade an addon (that is to install
|
|
a less recent version over a more recent one). The third parameter
|
|
is the visible name of your addon: it will be displayed to the user in the
|
|
addon management dialog. It can contain the [fnc]$tr[/fnc] function so you
|
|
can have it translated to several languages. The fourth parameter
|
|
is a short description of the feature that the addon implements; it can contain
|
|
the $tr() function too. The fifth parameter is the minimal KVIrc version
|
|
required to run the addon. There is also a sixth parameter (the icon) and
|
|
some switches that can be used to fiddle a little bit more :)
|
|
[/p]
|
|
[p]
|
|
The callback instruction that follows the registration command is the
|
|
uninstallation code. KVIrc will invoke it when the user will ask for
|
|
your addon to be uninstalled. Don't assume that your addon will be never uninstalled:
|
|
sooner or later it will be. For example, when upgrading an addon KVIrc
|
|
will first uninstall the existing version and after that install the new one.
|
|
The uninstallation process is a very important requisite for any program (in any
|
|
programming language). In the example above there is nothing to uninstall (yet)
|
|
so the callback code is empty, but if you continue reading we will soon fill it.
|
|
[/p]
|
|
|
|
[big]A typical addon layout[/big]
|
|
[p]
|
|
As stated above, the addons are usually shipped in a compressed archive.
|
|
Once uncompressed, the archive will expand into a small directory tree
|
|
containing the addon code and all the related files.
|
|
In order to have uniformity I encourage you to use the following directory structure.
|
|
[/p]
|
|
[p]
|
|
[pre]
|
|
[b]addonId-version/[/b]
|
|
install.kvs
|
|
INSTALL
|
|
[b]src[/b]
|
|
source1.kvs
|
|
source2.kvs
|
|
source3.kvs
|
|
...
|
|
[b]pics[/b]
|
|
addonId_pic1.png
|
|
addonId_pic2.png
|
|
addonId_pic3.png
|
|
...
|
|
[b]audio[/b]
|
|
addonId_audio1.png
|
|
addonId_audio2.png
|
|
addonId_audio3.png
|
|
...
|
|
[b]help[/b]
|
|
en
|
|
index.html
|
|
hints.html
|
|
...
|
|
it
|
|
index.html
|
|
hints.html
|
|
...
|
|
...
|
|
[b]locale[/b]
|
|
addonId_it.mo
|
|
addonId_ru.mo
|
|
addonId_de.mo
|
|
...
|
|
[b]...[/b]
|
|
...
|
|
...
|
|
...
|
|
...
|
|
[/pre]
|
|
[/p]
|
|
[p]
|
|
The entries in [b]bold[/b] are directories while the other are files.
|
|
Please note that this is a general layout for a huge and rather complex
|
|
addon: you might not need all of these directories. Remember: the minimal
|
|
addon has only an install.kvs file. Anyway, a really cool addon
|
|
will probably have all of them and maybe some more.
|
|
[/p]
|
|
[p]
|
|
The toplevel directory should be named with your addonId and version.
|
|
Try to use no spaces in the directory entries (this will make the things
|
|
simplier for people that want to use your addon). The toplevel
|
|
directory should contain your install.kvs script and a file with
|
|
a small description and the basic installation instructions.
|
|
This file can be named INSTALL or README.
|
|
[/p]
|
|
[p]
|
|
Hint: Remember that your addon is going to be installed on different platforms
|
|
(at least linux, macosx and windows based).
|
|
The poor windows notepad has serious problems with reading text
|
|
files that contain only linefeeds as line separators. Keep it in mind...
|
|
[/p]
|
|
[p]
|
|
The main source directory for your addon should be named "src" and
|
|
should contain the implementation of the feature(s) you're going to provide.
|
|
These files should be executed by the means of the [cmd]parse[/cmd]
|
|
command (or [cmd]include[/cmd] if you like the C/C++ style) from install.kvs.
|
|
[/p]
|
|
[p]
|
|
The "pics" and "audio" (if relevant) directories should contain your
|
|
multimedia files. It is a good idea to prefix the filenames with your addon id
|
|
in order to avoid collisions with other addons. The install.kvs script
|
|
should copy these files to the appropriate locations under the KVIrc local
|
|
directory returned by [fnc]$file.localdir[/fnc]().
|
|
[/p]
|
|
[p]
|
|
The "help" directory should contain subdirectories for each language that
|
|
your help files are written in. The languages dirs should be named
|
|
with the language code also used for the translation files (like "en","it" etc...).
|
|
Please note that english is the default language and KVIrc will
|
|
fallback to the "en" subdirectory when no other language is found around...
|
|
The help files (and subdirectories) should be copied to the "help" subdirectory of the KVIrc local
|
|
directory returned by [fnc]$file.localdir[/fnc]().
|
|
Since there will be many help files inside the language dirs, it is a good idea
|
|
to either prefix your help files with your addon id or (better) to create
|
|
a subdirectory named agains your addon inside the help language directory.
|
|
For an english file this would lead to something like...
|
|
[example]
|
|
file.copy index.html $file.localdir("help/en/myaddon")
|
|
[/example]
|
|
[/p]
|
|
[p]
|
|
The "locale" directory should contain the *.mo files for your tranlations.
|
|
The localization process of a script is explained in [doc:localization]this document[/doc].
|
|
The *.mo files should be copied to the "locale" subdirectory of the KVIrc local
|
|
directory returned by [fnc]$file.localdir[/fnc]().
|
|
Your *.mo filenames should be prefixed by your addon id (again to avoid collisions).
|
|
[/p]
|
|
|
|
[big]The help and configuration callbacks[/big]
|
|
[p]
|
|
Each addon can have a help and a configuration callback. These are set
|
|
respectively by [cmd]addon.sethelpcallback[/cmd] and [cmd]addon.setconfigurecallback[/cmd].
|
|
[/p]
|
|
[p]
|
|
The help callback will be invoked by KVIrc when the user will ask help for your addon (mainly
|
|
from the addon management dialog, but not necessairly). It should call [cmd]help.open[/cmd]
|
|
with the name of your documentation index html file (it should be relative
|
|
to the help language directory: help.open myaddon/index.html will automatically
|
|
lookup the right language). If you provide no help callback, the buttons
|
|
for requesting help will be simply disabled. (A good an relatively complex addon
|
|
*should* have at least a minimal help file explaining the features).
|
|
[/p]
|
|
[p]
|
|
The configuration callback will be invoked when the user will try to configure
|
|
your addon from the addon management dialog. This callback is useful
|
|
mainly for complexier graphical scripts that can show up a dialog
|
|
that allows configuring all of the addon features. To use this callback
|
|
you will probably need some object scripting.
|
|
[/p]
|
|
|
|
[big]The real addon work[/big]
|
|
[p]
|
|
The real addon work is done by the scripts contained in the src directory.
|
|
They will likely add aliases (maybe in a nice namespace named agains your addon),
|
|
register event handlers, create actions, timers, toolbars and object classes.
|
|
You should install all of this stuff from your addon source files.
|
|
Remember that your source files will NOT be parsed every time KVIrc starts up:
|
|
your stuff must be registered in KVIrc and be able to startup itself, if needed.
|
|
Remember that you must clean up [b]everything[/b] in your uninstallation callback.
|
|
This means that you must remove the aliases, unregister the event handlers,
|
|
destroy the actions, kill the timers and the object classes you've created.
|
|
Be a clean coder :)
|
|
[/p]
|
|
|
|
[big]Where to start[/big]
|
|
[p]
|
|
It is a good idea to start on the KVIrc web site. There are surely
|
|
several addons to look at. Pick one that seems simple and analyze its
|
|
layout and code (wow... the free software!). It will be easier to do than it was to explain it :D
|
|
[/p]
|
|
[p]
|
|
Have fun! :)
|
|
[/p]
|
|
*/
|
|
|
|
|
|
/*
|
|
@doc: kvs_codingtips
|
|
@type:
|
|
generic
|
|
@title:
|
|
Coding tips
|
|
@keyterms:
|
|
indentation,indent,readability
|
|
@short:
|
|
Generic coding tips for scripters (and not only)
|
|
@body:
|
|
Here comes a small list of "coding tips".[br]
|
|
These apply to programming in general , not only to KVIrc scripting.[br]
|
|
[br]
|
|
1. [b]Comment your code[/b][br]
|
|
A well commented code is easy to mantain, and easy to read by others.[br]
|
|
[br]
|
|
2. [b]Indent your code[/b][br]
|
|
Indentation increases the code readability; this is again for you and
|
|
other developers that will be going to read your code.[br]
|
|
[br]
|
|
3. [b]Use TABS to indent your code[/b][br]
|
|
...and use ONLY TABS to indent.[br]
|
|
Tabs are better than space since most code editors allow you
|
|
to set the tab sice and thus to have the indentation steps smaller or bigger.[br]
|
|
This is really important since the indentation size is really a matter of personal taste.[br]
|
|
Mixing spaces and tabs is Evil (tm), since it makes the code look really
|
|
ugly in editors that have the tab size different than yours; in some cases the
|
|
code gets really unreadable.[br]
|
|
[br]
|
|
4. [b]Use descriptive variable names[/b][br]
|
|
Using 'foo' as variable name implies tracing its semantic the next
|
|
time that you're going to read the code that uses it.[br]
|
|
This is really annoying and time-consuming, especially if the project
|
|
is getting large.[br]
|
|
Obviously using "thisIsACounterVariable" as name for a simple counter
|
|
is also a suicide.[br]
|
|
A good convention on variable names can speed up writing , debugging and mantaining code.[br]
|
|
Encoding the type of the variable in the variable name might be also a good idea,
|
|
but this is a matter of taste; personally I feel really well with that.[br]
|
|
Just as example, here go my fundamental convention rules for C++:[br]
|
|
[br]
|
|
- The type of the variable is encoded at the beginning of the variable name:[br]
|
|
[br]
|
|
- b prefix for the boolean varables[br]
|
|
- i prefix for signed integers[br]
|
|
- u prefix for unsigned integers[br]
|
|
- f and d prefixes for floating point stuff[br]
|
|
- sz prefix for strings (this is rather for string classes)[br]
|
|
- ...[br]
|
|
[br]
|
|
- Pointers have a "p" prefix prepended[br]
|
|
- Global variables start with a "g_" prefix[br]
|
|
- Member variables start with a "m_" prefix[br]
|
|
- Exception comes for local variables with obvious semantics[br]
|
|
[br]
|
|
- i,j,k,l for local loop counters[br]
|
|
- "aux" and "tmp" for local obvious short-term temporary variables[br]
|
|
[br]
|
|
So actually by ONLY reading "g_pszQuitMessage" I know that this is a global pointer to a string variable
|
|
containing a quit message. :)[br]
|
|
[/p]
|
|
*/
|
|
|
|
// FIXME: #warning "FINISH THE SYNTACTIC RULES DOC"
|
|
|
|
/*
|
|
@doc: syntactic_rules
|
|
@type:
|
|
language
|
|
@keyterms:
|
|
productions
|
|
@title:
|
|
Syntactic rules
|
|
@short:
|
|
Syntactic rules of the KVIrc scripting language
|
|
@body:
|
|
|
|
In the following table you can find a good part of the
|
|
KVIrc scripting language syntactic rules.[br]
|
|
[br]
|
|
<entity> indicates a ENTITY THAT CAN APPEAR EXACTLY ONE TIME.[br]
|
|
[<entity>] indicates an OPTIONAL ENTITY.[br]
|
|
{<entity>} indicates an ENTITY THAT CAN APPEAR ONE OR MORE TIMES.[br]
|
|
'entity' indicates a LITERAL ENTITY: written exactly as it is.[br]
|
|
<entity1>|<entity2> indicates mutually exclusive choices.[br]
|
|
The mutually exclusive choices are often separated in two or more
|
|
rules (productions), to improve readability.[br]
|
|
[table]
|
|
[tr]
|
|
[td]<command buffer>[/td]
|
|
[td][<whitespace>][<command block>]{<command buffer>}[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<command buffer>[/td]
|
|
[td][<whitespace>][<single command>]{<command buffer>}[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<whitespace>[/td]
|
|
[td]{<space>|<tab>|<newline>}['\'<newline>][<whitespace>][/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<space>[/td]
|
|
[td]' '['\'<newline>][<space>] (Ascii space character)[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<tab>[/td]
|
|
[td]'\t' (Ascii horizontal tabulation character)[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<newline>[/td]
|
|
[td]'\n' (Ascii line feed (LF) character)[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<command block>[/td]
|
|
[td]'{' <command buffer>[<whitespace>] '}'[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<single command>[/td]
|
|
[td]<comment>[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<single command>[/td]
|
|
[td]<lvalue command> <command terminator>[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<single command>[/td]
|
|
[td]<rvalue command> <command terminator>[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<comment>[/td]
|
|
[td]'#' {<non comment terminator>} <comment terminator>[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<comment terminator>[/td]
|
|
[td]<newline> | <end of string>[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<end of string>[/td]
|
|
[td]No character (internally Ascii character 0)[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<command terminator>[/td]
|
|
[td]<newline> | <end of string> | ';'[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<non comment-terminator>[/td]
|
|
[td]Any Ascii character except <newline> and <end of string>[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<simple command>[/td]
|
|
[td][<module name>'.']<command name>[<switch list>]{<space>}<command dependant part>[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<lvalue command>[/td]
|
|
[td]<variable>[<space>]<operation>[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<lvalue command>[/td]
|
|
[td]<variable>'->'<object command>[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<lvalue command>[/td]
|
|
[td]<identifier>'->'<object command>[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<operation>[/td]
|
|
[td]<one op operator>[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<operation>[/td]
|
|
[td]<two op operator>[<space>]<param string>[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<switch list>[/td]
|
|
[td]{<space>}'-'<alpha char>[{<space>}'='<single parameter>][<switch list>][/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<command name>[/td]
|
|
[td]<alphanumeric char>{<alphanumeric char>}[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<module name>[/td]
|
|
[td]<alphanumeric char>{<alphanumeric char>}[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<alphanumeric char>[/td]
|
|
[td]Ascii characters 'A' to 'Z' , 'a' to 'z' , '0' to '9' and '_'[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<variable>[/td]
|
|
[td]<global variable> | <local variable>[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<global variable>[/td]
|
|
[td]'%' <uppercase letter> [<alphanumeric char>]['['<param string>']'][/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<local variable>[/td]
|
|
[td]'%' <lowercase letter> [<alphanumeric char>]['['<param string>']'][/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<param string>[/td]
|
|
[td][<single parameter>][<space>[<param string>]][/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<single parameter>[/td]
|
|
[td]<variable> | <identifier> | <nonterminator token> | <string>[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<nonterminator token>[/td]
|
|
[td]<nonterminator char>['\'<newline><nonterminator char>][/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<nonterminator char>[/td]
|
|
[td]Any ascii character except <space> and <command terminator>[/td]
|
|
[/tr]
|
|
[tr]
|
|
[td]<command dependant part>[/td]
|
|
[td][b]Production in each command help page[/b][/td]
|
|
[/tr]
|
|
[/table]
|
|
To be continued...
|
|
*/
|
|
|
|
/*
|
|
@doc: command_rebinding
|
|
@type:
|
|
language
|
|
@keyterms:
|
|
Not supported
|
|
@title:
|
|
Standard -r switch no longer supported
|
|
@short:
|
|
Standard -r switch no longer supported
|
|
@body:
|
|
Starting from version 3.0.0 the standard -r switch to commands is no longer supported.
|
|
You should rebind your command sequences with [cmd]rebind[/cmd]
|
|
*/
|
|
|
|
/*
|
|
@doc: kvs_datatypes
|
|
@type:
|
|
language
|
|
@keyterms:
|
|
global variable, global variables, local variable, local variables,
|
|
variables, variable, array, hash, dictionary, global variables, local variables,variable evaluation,
|
|
associative arrays, scalars, data types, percent sign, extended scope
|
|
@title:
|
|
Variables and Data types
|
|
@short:
|
|
All about the KVS variable and datatype management
|
|
@body:
|
|
[title]Basic syntax[/title]
|
|
|
|
[p]
|
|
A variable identifier is composed by a '%' (percent) sign followed
|
|
by a sequence of letters, digits or underscores.
|
|
Examples of valid variable names are:
|
|
[/p]
|
|
|
|
[example]
|
|
%i
|
|
%variable
|
|
%MyVar
|
|
%1
|
|
%thisisavar
|
|
%2ndName
|
|
%_hidden
|
|
[/example]
|
|
|
|
[p]
|
|
Variables are created when you assign something to them: there is no need
|
|
for a declaration (unlike other languages such as C/C++, Java or VB).
|
|
[/p]
|
|
|
|
[example]
|
|
[comment]# create a variable named %X by assigning the value 10 to it[/comment]
|
|
%X = 10
|
|
[comment]# use the variable[/comment]
|
|
echo "The value of X is" %X
|
|
[/example]
|
|
|
|
[title]Local and global variables[/title]
|
|
|
|
[p]
|
|
Variables can be local or global.
|
|
Local variables preserve their contents only inside the [b]scope[/b] of a single script.
|
|
Global variables are shared between all the scripts and preserve their contents
|
|
until they are explicitly unset or until KVIrc quits.
|
|
[/p]
|
|
|
|
[p]
|
|
Local variables start with a [b]lowercase letter[/b] while the global ones with an [b]uppercase letter[/b].
|
|
[/p]
|
|
|
|
[example]
|
|
%var = 10; [comment]# this is a local variable[/comment]
|
|
%Var = 10; [comment]# this is a global variable[/comment]
|
|
[/example]
|
|
|
|
[p]
|
|
You can also force a variable that start with a lowercase letter to be global
|
|
by predeclaring it with the [cmd]global[/cmd] keyword.[br]
|
|
[/p]
|
|
|
|
[example]
|
|
[comment]# copy this script to a file and run /[cmd]parse[/cmd] <filename>[/comment]
|
|
global %a
|
|
%a = "The contents of the variable a"
|
|
%b = "The contents of the variable b"
|
|
[comment]# %a is a global variable now : all the other scripts can see its value[/comment]
|
|
[comment]# %b is a local variable and no other scripts can see its value[/comment]
|
|
[/example]
|
|
|
|
[p]
|
|
If you have executed the example above from a file (by the means of [cmd]parse[/cmd])
|
|
then now you can type
|
|
[/p]
|
|
|
|
[example]
|
|
[cmd]echo[/cmd] %a
|
|
[/example]
|
|
|
|
[p]
|
|
in the commandline to see the contents of the variable %a.
|
|
If you also try
|
|
[/p]
|
|
|
|
[example]
|
|
[cmd]echo[/cmd] %b
|
|
[/example]
|
|
|
|
[p]
|
|
you will see nothing printed since %b was local to the parsed script.
|
|
[/p]
|
|
|
|
[title]Data types[/title]
|
|
|
|
[p]
|
|
KVS has three main categories of data types: scalars, arrays and associative
|
|
arrays (also known as dictionaries or hashes).
|
|
[/p]
|
|
|
|
[subtitle]Scalars[/subtitle]
|
|
|
|
[p]
|
|
The scalars are simple variables containing a single value (a string or an integer).
|
|
[/p]
|
|
|
|
[example]
|
|
[comment]# %a is a scalar variable[/comment]
|
|
%a = "This is a string"
|
|
[cmd]echo[/cmd] %a
|
|
%a = 24.5
|
|
[cmd]echo[/cmd] %a
|
|
[/example]
|
|
|
|
[subtitle]Arrays[/subtitle]
|
|
|
|
[p]
|
|
Arrays are collections of items indexed by integers. The array items
|
|
are selected by placing the index in square brackets just after the array name.
|
|
[/p]
|
|
|
|
[example]
|
|
%arrayName[index]
|
|
[/example]
|
|
|
|
[p]
|
|
An easy way to create an array is to use the [fnc]$array[/fnc] function.
|
|
[/p]
|
|
|
|
[example]
|
|
%a = $array("element1","element2","element3"); [comment]# Create an array with 3 items[/comment]
|
|
[cmd]for[/cmd](%i=0;%i<3;%i++)
|
|
{
|
|
echo %a[%i]; [comment]# Accessing the %i'th element of the array[/comment]
|
|
}
|
|
[/example]
|
|
|
|
[p]
|
|
Note that in the example above %a refers to the whole array while %a[%i] refers
|
|
to one of its elements, in particular the one with index %i.
|
|
You also create an array by explicitly assigning to one of its elements:
|
|
[/p]
|
|
|
|
[example]
|
|
%a[9] = "This is an array element";
|
|
[/example]
|
|
|
|
[p]
|
|
Array indexes are zero-based so in the example above you have created an array
|
|
with 10 items. You can find out an array's length with the [fnc]$length[/fnc]() function.
|
|
[/p]
|
|
|
|
[example]
|
|
%a[9] = "This is an array element";
|
|
echo $length(%a)
|
|
[/example]
|
|
|
|
[p]
|
|
Be aware that by making such an assignment you implicitly consume some memory for
|
|
all the preceeding array items (even if they are unset). This means that
|
|
a simple instruction like the following may eat a huge amount of memory at once:
|
|
[/p]
|
|
|
|
[example]
|
|
%a[1000000] = "An array element faaaaaar away...";
|
|
echo $length(%a)
|
|
[/example]
|
|
|
|
[note]
|
|
[p]
|
|
Food for thoughts:
|
|
[/p]
|
|
[p]
|
|
KVIrc allocates a pointer for each item in the array. The pointer is
|
|
empty when the item is unset and points to an additional block
|
|
of memory when the item is set. The size of a pointer is platform
|
|
dependant: on the platforms supported by KVIrc it's either 32 or 64 bit.
|
|
The size of the additional block depends both on the platform
|
|
and on the contents of the item... it's average value may
|
|
be around 16 bytes. The array size is determined by the last SET element index.
|
|
All this this means that in the worst case (64 bit assumption) an array in
|
|
that the highest indexed item set is N eats up at least N*8+16 bytes of memory.
|
|
[/p]
|
|
[/note]
|
|
|
|
[p]
|
|
Besides the traditional indexed looping method you
|
|
can also use the [cmd]foreach[/cmd] command to iterate the items of an array.
|
|
Be aware that [cmd]foreach[/cmd] will NOT iterate over unset items in the
|
|
array unless you use the -a switch.
|
|
[/p]
|
|
|
|
[example]
|
|
%Array[0]=Pippo
|
|
%Array[1]=Pluto
|
|
%Array[2]=Paperino
|
|
%Array[5]=Prova
|
|
[cmd]foreach[/cmd](%item,%Array)[cmd]echo[/cmd] Got Item: %item
|
|
[/example]
|
|
|
|
[p]
|
|
Note that the items 3 and 4 are simply skipped.
|
|
[/p]
|
|
|
|
[subtitle]Hashes[/subtitle]
|
|
|
|
[p]
|
|
The hashes are collections of items indexed by strings: the word "hash"
|
|
is in fact a shortcut for "hashtable". In literature hashes are also called
|
|
"associative arrays", "dictionaries" or "key-value pair sets".
|
|
The hash items are selected by placing the key in curly brackets
|
|
just after the hash name.
|
|
[/p]
|
|
|
|
[example]
|
|
%hashName{key}
|
|
[/example]
|
|
|
|
[p]
|
|
An easy way to create a hash is to use the [fnc]$hash[/fnc] function.
|
|
[/p]
|
|
[example]
|
|
%a = $hash("key1","value1","key2","value2","key3","value3")
|
|
[cmd]foreach[/cmd](%key,[fnc]$keys[/fnc](%a))
|
|
{
|
|
echo "KEY:" %key "VALUE:" %a{%key};
|
|
}
|
|
[/example]
|
|
|
|
[p]
|
|
Note that in the example above %a refers to the whole hash while %a{%i} refers
|
|
to one of its elements, in particular the one with the key %key.
|
|
You also create a hash by explicitly assigning to one of its elements:
|
|
[/p]
|
|
|
|
[example]
|
|
%a{"MyKey"} = "MyValue"
|
|
[/example]
|
|
|
|
[p]
|
|
You may have already noticed that the [fnc]$key[/fnc]() function returns
|
|
the array of the hash keys: it is useful to iterate over the hash items.
|
|
[/p]
|
|
|
|
[title]Mutability of variables[/title]
|
|
|
|
[p]
|
|
KVS is not strictly typed: any variable can assume different type identities at different times,
|
|
even in the same script.
|
|
[/p]
|
|
|
|
[example]
|
|
[comment]# %a is a scalar[/comment]
|
|
%a = "This is a string"
|
|
[comment]# %a becomes an array with 3 elements[/comment]
|
|
%a = $array("element1","element2","element3");
|
|
[comment]# %a becomes a hash with two values[/comment]
|
|
%a = $hash("key1","value1","key2","value2");
|
|
[/example]
|
|
|
|
[p]
|
|
In literature this kind of variable is called [b]variant[/b] and this is the
|
|
term that you will find all around the documentation when an explicit
|
|
data type is not requested.
|
|
[/p]
|
|
|
|
[p]
|
|
Note that array and hash items are variants too. This means that you can have arrays
|
|
of arrays, hashes of arrays of hashes and any other multidimensional combination you like.
|
|
However remember that hash keys are strings and not variants so you can't use an array as hash key.
|
|
[/p]
|
|
|
|
[example]
|
|
[comment]# here we eat 256 locations of memory at once :)[/comment]
|
|
%a[16][16] = 10
|
|
[comment]# a hash of hashes: here we eat just two memory locations[/comment]
|
|
%a{"16"}{"16"} = 10
|
|
[/example]
|
|
|
|
|
|
[p]
|
|
In most cases the KVS engine manages automatically the conversion between data types.
|
|
For example, when you put an array in a place where a scalar is requested, KVIrc
|
|
automatically transforms it to a scalar string by joining all the items with a comma.
|
|
[/p]
|
|
|
|
[example]
|
|
%a = $array("element1","element2","element3");
|
|
echo %a; [comment]# echo expects its arguments to be scalar[/comment]
|
|
[/example]
|
|
|
|
[p]
|
|
Conversely, when you put a scalar in place of an array, KVIrc automatically
|
|
transforms it to an array with a single item. In this way a function like
|
|
[fnc]$sort[/fnc] works also with a scalar.
|
|
[/p]
|
|
|
|
[p]
|
|
In literature the conversions between data types are called [b]casts[/b]. When
|
|
the conversion is automatic the cast is said to be [b]implicit[/b].
|
|
[/p]
|
|
|
|
[p]
|
|
KVS handles also the other possible implicit casts: scalar->hash,hash->scalar,array->hash,hash->array.
|
|
Experiment with it.
|
|
[/p]
|
|
|
|
[title]More about scalars[/title]
|
|
|
|
[p]
|
|
Internally KVS is implicitly typed: the "scalar" data type is in fact
|
|
a set of types that KVIrc manages silently. The types are: integer, string, real, boolean and hobject.
|
|
[/p]
|
|
|
|
[p]
|
|
Integers are non-floating point numbers. Their allowable range depends on the underlying
|
|
platform integer size: usually 32 or 64 bit.
|
|
[/p]
|
|
|
|
[p]
|
|
Reals are floating point numbers. Their allowable range and precision depends on the underlying
|
|
platform.
|
|
[/p]
|
|
|
|
[p]
|
|
Booleans are either true or false values.
|
|
[/p]
|
|
|
|
[p]
|
|
Hobject stands for Handle to Object and it is a sort of a C++ pointer.
|
|
Detailed description of objects is in [doc:objects]this document[/doc].
|
|
[/p]
|
|
|
|
[p]
|
|
Basically anything else fails in the "string" category.
|
|
[/p]
|
|
|
|
[p]
|
|
In most cases KVS manages all the conversions between data types automatically.
|
|
For example an integer becomes a true boolean when it's non zero and a false boolean
|
|
otherwise, a real becomes an integer by truncating it's fractional part...
|
|
[/p]
|
|
|
|
[p]
|
|
You can find out the type of a specified variable by using the [fnc]$typeof[/fnc]() function.
|
|
[/p]
|
|
|
|
[example]
|
|
%a = 1
|
|
echo $typeof(%a)
|
|
%a = 1.1
|
|
echo $typeof(%a)
|
|
%a = $true
|
|
echo $typeof(%a)
|
|
%a = "test"
|
|
echo $typeof(%a)
|
|
[/example]
|
|
|
|
[p]
|
|
There is also another subtle type of scalar called "nothing". It stands for an
|
|
empty (unset) variable.
|
|
[/p]
|
|
|
|
[example]
|
|
%a = $nothing
|
|
echo $typeof(%a)
|
|
[/example]
|
|
|
|
[p]
|
|
Nothing is something in between a data type and a special value for all the other data types:
|
|
it rappresents absence of information.
|
|
This may look a bit confusing but realize that all the unreferenced KVS variable are in fact of type "nothing":
|
|
they just don't exist. This means that you can use [fnc]$nothing[/fnc]() to effectively
|
|
unset a variable.
|
|
[p]
|
|
|
|
[p]
|
|
Again, when possible, the conversion between nothing and the other data types is
|
|
performed automatically. Nothing becomes an empty string, a null object handle or an empty array.
|
|
[/p]
|
|
|
|
|
|
[title]Explicit casts[/title]
|
|
|
|
[p]
|
|
You can make explicit conversions between some data types by using the casting functions.
|
|
[fnc]$integer[/fnc]() will attempt to convert the variant parameter to an integer, [fnc]$real[/fnc]()
|
|
will cast to a floating point value, [fnc]$boolean[/fnc]() will convert to a
|
|
true/false value, [fnc]$string[/fnc]() will explicitly convert to a string,
|
|
[fnc]$array[/fnc]() will convert to an array and [fnc]$hash[/fnc] will return
|
|
a dictionary. By assigning the special [fnc]$nothing[/fnc]() value you will
|
|
convert to the nothing data type (or simply unset the variable).
|
|
The only explicit conversion that is not possible is to hobject.
|
|
[/p]
|
|
|
|
[p]
|
|
As stated several times in this document, KVS tries to manage the casts automatically
|
|
so you usually don't need to care about it. The explicit casts are provided for
|
|
the very few cases where an automatic conversion would lead to an unexpected value (for your script)
|
|
and for writer's clarity.
|
|
[/p]
|
|
|
|
[title]More about variables lifecycle[/title]
|
|
|
|
[p]
|
|
As stated above variables start their existence when you assign something to them.
|
|
After a variable has been created it persists until it goes out of his scope (remember
|
|
about local and global variables ?) or you explicitly destroy it. You will usually
|
|
not care about it and just leave the KVS engine to do his cleaning job but it's still worth
|
|
knowing that you actually can force KVIrc to free the memory used by a variable.
|
|
[/p]
|
|
|
|
[p]
|
|
The first method to explicitly destroy a variable is to call [cmd]unset[/cmd] on it.
|
|
[cmd]unset[/cmd] in fact accepts a list of variables so you can destroy more variables at once.
|
|
[/p]
|
|
|
|
[example]
|
|
%a = [fnc]$array[/fnc]("data","for","a","really","huge","array","of","items")
|
|
%b = 10
|
|
%c = "just a string that eats memory"
|
|
[cmd]unset[/cmd] %a,%b,%c
|
|
[/example]
|
|
|
|
[p]
|
|
The KVS engine treats unset variables just like empty strings. The opposite is also valid: empty
|
|
strings behave like empty (unset) variables. This means that you can assign an empty string
|
|
to a variable to unset it.
|
|
[/p]
|
|
|
|
[example]
|
|
%a = "test"; [comment]# %a starts his existence[/comment]
|
|
%b = "test2";
|
|
%a = ""; [comment]# %a is in fact unset[/comment]
|
|
%b = ; [comment]# syntactically this is just the same as above[/comment]
|
|
[/example]
|
|
|
|
[p]
|
|
Note that because of mutability of variables (explained above) you can use the empty string
|
|
assignment also to free arrays and hashes.
|
|
[/p]
|
|
|
|
[title]Extended scope variables[/title]
|
|
|
|
[p]
|
|
Beside local and global variables there is a third family of them.
|
|
Variables that have a ':' character just after the leading '%' are [b]extended scope[/b] variables.
|
|
"%:index" , "%:Hello" , "%:something.else" are all valid special scope variable names.
|
|
They're actually used in popups and in timers (but later I might find other usages as well :).
|
|
"Extended scope" means that these variables are somewhere in the middle between
|
|
global and local variables. They normally act as local , but in some cases their [b]lifetime[/b] and [b]visibility[/b]
|
|
may be extended.
|
|
[/p]
|
|
|
|
[p]
|
|
For example , in the popups , all the special scope variables
|
|
are visible during all the "lifetime" of a popup (so from the prologue code call to
|
|
the moment when the user selects an item and the corresponding code is executed).
|
|
This allows you to pre-calculate some data or conditions in the popup prologue
|
|
and use this data in the popup item conditions and item handlers.
|
|
[/p]
|
|
|
|
[title]Variable evaluation[/title]
|
|
|
|
[p]
|
|
A variable can appear in every place where a parameter
|
|
is expected: so after the command name, after a switch or inside
|
|
an identifier parameters. The KVS parser will try to extract the longest possible variable
|
|
name after a literal percent '%' sign everywhere in the parameter string. So the command sequence
|
|
[/p]
|
|
[example]
|
|
%number = 1st; echo this is my %number variable test
|
|
[/example]
|
|
[p]
|
|
will first assign "1st" to the variable "%number" and then execute
|
|
"echo this is my 1st variable test". The following example will NOT work as expected.
|
|
[/p]
|
|
[example]
|
|
%number = 1; echo this is my %numberst variable test
|
|
[/example]
|
|
[p]
|
|
KVS will assign "1" to %number in this case but the next variable
|
|
name extracted will be "%numberst" that is actually empty; so finally
|
|
"echo this is my variable test" will be executed.
|
|
To avoid this problem you can use the backslash escape character:
|
|
[/p]
|
|
[example]
|
|
%number = 1; echo this is my %number\st variable test
|
|
[/example]
|
|
|
|
[title]Putting it all together[/title]
|
|
|
|
[p]
|
|
Variables can be either local, global or have an extended scope. Their start to exist
|
|
when you first assign something to them and they disappear when they go out of their
|
|
scope or you explicitly destroy them.
|
|
[/p]
|
|
|
|
[p]
|
|
KVS has 8 builtin data types: string, integer, real, boolean, hobject, nothing, array and hash.
|
|
The first 6 are scalar data types while the last two are not.
|
|
[/p]
|
|
|
|
[p]
|
|
When possible, KVS manages all the conversions between data types silently.
|
|
In the few cases in that an implicit conversion is not possible you have to manage the conversion
|
|
manually otherwise KVS will complain.
|
|
[/p]
|
|
*/
|
|
|
|
|
|
|
|
void KviKvsParser::skipSpaces()
|
|
{
|
|
while((KVSP_curCharUnicode == ' ') || (KVSP_curCharUnicode == '\t'))
|
|
{
|
|
KVSP_skipChar;
|
|
}
|
|
|
|
if(KVSP_curCharUnicode == '\\')
|
|
{
|
|
KVSP_skipChar;
|
|
if(KVSP_curCharUnicode == '\n')
|
|
{
|
|
KVSP_skipChar;
|
|
skipSpaces();
|
|
return;
|
|
} else if(KVSP_curCharUnicode == '\r')
|
|
{
|
|
KVSP_skipChar;
|
|
if(KVSP_curCharUnicode == '\n')
|
|
{
|
|
KVSP_skipChar;
|
|
skipSpaces();
|
|
return;
|
|
} else {
|
|
KVSP_backChar;
|
|
KVSP_backChar;
|
|
}
|
|
} else {
|
|
KVSP_backChar;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool KviKvsParser::skipSpacesAndNewlines()
|
|
{
|
|
while((KVSP_curCharUnicode == ' ') || (KVSP_curCharUnicode == '\t') || (KVSP_curCharUnicode == '\n') || (KVSP_curCharUnicode == '\r'))
|
|
{
|
|
KVSP_skipChar;
|
|
}
|
|
|
|
switch(KVSP_curCharUnicode)
|
|
{
|
|
case '\\':
|
|
KVSP_skipChar;
|
|
if(KVSP_curCharUnicode == '\n')
|
|
{
|
|
KVSP_skipChar;
|
|
return skipSpacesAndNewlines();
|
|
} else if(KVSP_curCharUnicode == '\r')
|
|
{
|
|
KVSP_skipChar;
|
|
if(KVSP_curCharUnicode == '\n')
|
|
{
|
|
KVSP_skipChar;
|
|
return skipSpacesAndNewlines();
|
|
} else {
|
|
KVSP_backChar;
|
|
KVSP_backChar;
|
|
}
|
|
} else {
|
|
KVSP_backChar;
|
|
}
|
|
break;
|
|
case '#':
|
|
case '/':
|
|
// we allow comments too!
|
|
(void)parseComment(); // this will return 0 anyway (and never trigger an error here)
|
|
if(error())return false;
|
|
return skipSpacesAndNewlines();
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void KviKvsParser::skipToNextLine()
|
|
{
|
|
while((KVSP_curCharUnicode != 0) && (KVSP_curCharUnicode != '\n'))
|
|
KVSP_skipChar;
|
|
|
|
if(KVSP_curCharUnicode == '\n')KVSP_skipChar;
|
|
}
|
|
|
|
|
|
KviKvsTreeNodeInstruction * KviKvsParser::parseInstructionList()
|
|
{
|
|
KviKvsTreeNodeInstructionBlock * l = new KviKvsTreeNodeInstructionBlock(KVSP_curCharPointer);
|
|
|
|
|
|
for(;;)
|
|
{
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete l;
|
|
return 0;
|
|
}
|
|
|
|
if(KVSP_curCharUnicode != 0)
|
|
{
|
|
// instruction
|
|
KviKvsTreeNodeInstruction * i = parseInstruction();
|
|
if(i)l->addInstruction(i);
|
|
else {
|
|
if(error())
|
|
{
|
|
// ops...
|
|
delete l;
|
|
return 0;
|
|
} // else empty instruction
|
|
}
|
|
} else {
|
|
if(l->instructionCount() == 1)
|
|
{
|
|
// return the single instruction instead
|
|
KviKvsTreeNodeInstruction * i = l->releaseFirst();
|
|
delete l;
|
|
return i;
|
|
}
|
|
// end of buffer
|
|
return l;
|
|
}
|
|
}
|
|
|
|
// never here
|
|
KVSP_ASSERT(false);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
KviKvsTreeNodeData * KviKvsParser::parseParameterPercentOrDollar()
|
|
{
|
|
KVSP_ASSERT((KVSP_curCharUnicode == '%') || (KVSP_curCharUnicode == '$') || (KVSP_curCharUnicode == '@'));
|
|
|
|
if(KVSP_curCharUnicode == '%')
|
|
{
|
|
KVSP_skipChar;
|
|
if(!KVSP_curCharIsLetter && (KVSP_curCharUnicode != ':'))
|
|
{
|
|
// be flexible : allow an "alone" '%' char
|
|
return new KviKvsTreeNodeConstantData(KVSP_curCharPointer - 1,new KviKvsVariant(TQString("%")));
|
|
}
|
|
// this is surely a variable or function
|
|
KVSP_backChar;
|
|
} else if(KVSP_curCharUnicode == '$')
|
|
{
|
|
KVSP_skipChar;
|
|
if(!KVSP_curCharIsFunctionStart)
|
|
{
|
|
// be flexible : allow an "alone" '$' char
|
|
return new KviKvsTreeNodeConstantData(KVSP_curCharPointer - 1,new KviKvsVariant(TQString("$")));
|
|
}
|
|
// this is surely a variable or function
|
|
KVSP_backChar;
|
|
}
|
|
|
|
return parsePercentOrDollar();
|
|
}
|
|
|
|
|
|
|
|
KviKvsTreeNodeData * KviKvsParser::parsePercentOrDollar(bool bInObjScope)
|
|
{
|
|
KVSP_ASSERT((KVSP_curCharUnicode == '%') || (KVSP_curCharUnicode == '$') || (KVSP_curCharUnicode == '@'));
|
|
|
|
KviKvsTreeNodeData * r;
|
|
const TQChar * pBegin;
|
|
|
|
if(KVSP_curCharUnicode == '%')
|
|
{
|
|
r = parsePercent(bInObjScope);
|
|
if(!r)return 0;
|
|
} else if(KVSP_curCharUnicode == '$')
|
|
{
|
|
r = parseDollar(bInObjScope);
|
|
if(!r)return 0;
|
|
} else {
|
|
// this is @
|
|
static TQString szStrayAtRoutineName("@");
|
|
static TQString szMightBeStrayAtOrThisRoutineName("@?");
|
|
|
|
pBegin = KVSP_curCharPointer;
|
|
|
|
KVSP_skipChar;
|
|
|
|
if(bInObjScope || ((KVSP_curCharUnicode != '$') && (KVSP_curCharUnicode != '%')))
|
|
{
|
|
// we're sure this is just a stray @
|
|
// we use a trick here: when @ is not supposed to be an object scope call
|
|
// then we create a function that will return the @ itself as a string
|
|
KviKvsCoreFunctionExecRoutine * pRoutine = KviKvsKernel::instance()->findCoreFunctionExecRoutine(szStrayAtRoutineName);
|
|
r = new KviKvsTreeNodeCoreFunctionCall(KVSP_curCharPointer,szStrayAtRoutineName,pRoutine,new KviKvsTreeNodeDataList(KVSP_curCharPointer));
|
|
//KVSP_skipChar;
|
|
return r;
|
|
}
|
|
// we're not in object scope and cur char is either $ or %
|
|
// check for the common syntax $0!$1@$2 seen in hostmasks
|
|
// @$<digit> is non valid anyway
|
|
if(KVSP_curCharUnicode == '$')
|
|
{
|
|
KVSP_skipChar;
|
|
if(KVSP_curCharIsNumber)
|
|
{
|
|
// again a stray @
|
|
KVSP_backChar;
|
|
KviKvsCoreFunctionExecRoutine * pRoutine = KviKvsKernel::instance()->findCoreFunctionExecRoutine(szStrayAtRoutineName);
|
|
r = new KviKvsTreeNodeCoreFunctionCall(KVSP_curCharPointer,szStrayAtRoutineName,pRoutine,new KviKvsTreeNodeDataList(KVSP_curCharPointer));
|
|
return r;
|
|
}
|
|
KVSP_backChar;
|
|
}
|
|
|
|
// now we're unsure: we will be able to decide only at runtime if it is a stray @ or the shortcut for $this
|
|
// this design was a bit ugly.. I must admit it... but it is really useful when writing object classes...
|
|
KviKvsCoreFunctionExecRoutine * pRoutine = KviKvsKernel::instance()->findCoreFunctionExecRoutine(szMightBeStrayAtOrThisRoutineName);
|
|
// MUST BE THERE!
|
|
// core function call
|
|
r = new KviKvsTreeNodeCoreFunctionCall(pBegin,szMightBeStrayAtOrThisRoutineName,pRoutine,new KviKvsTreeNodeDataList(pBegin));
|
|
|
|
skipSpaces();
|
|
|
|
goto handle_scope_operator;
|
|
}
|
|
|
|
pBegin = KVSP_curCharPointer;
|
|
|
|
while((KVSP_curCharUnicode == '[') || (KVSP_curCharUnicode == '{'))
|
|
{
|
|
if(KVSP_curCharUnicode == '[')
|
|
{
|
|
// array index
|
|
KVSP_skipChar;
|
|
skipSpaces();
|
|
if(KVSP_curCharUnicode == ']')
|
|
{
|
|
KVSP_skipChar;
|
|
if(KVSP_curCharUnicode == '#')
|
|
{
|
|
// count
|
|
KVSP_skipChar;
|
|
return new KviKvsTreeNodeArrayCount(pBegin,r);
|
|
} else {
|
|
// a hash reference assert
|
|
return new KviKvsTreeNodeArrayReferenceAssert(pBegin,r);
|
|
}
|
|
}
|
|
|
|
KviKvsTreeNodeExpression * e = parseExpression(']');
|
|
if(!e)
|
|
{
|
|
delete r;
|
|
return 0;
|
|
}
|
|
|
|
r = new KviKvsTreeNodeArrayElement(pBegin,r,e);
|
|
} else {
|
|
// hash key
|
|
KVSP_skipChar;
|
|
skipSpaces();
|
|
|
|
if(KVSP_curCharUnicode == '}')
|
|
{
|
|
// entire hash ?
|
|
KVSP_skipChar;
|
|
if(KVSP_curCharUnicode == '#')
|
|
{
|
|
KVSP_skipChar;
|
|
return new KviKvsTreeNodeHashCount(pBegin,r);
|
|
}
|
|
return new KviKvsTreeNodeHashReferenceAssert(pBegin,r);
|
|
}
|
|
|
|
KviKvsTreeNodeData * i = parseHashKey();
|
|
if(!i)
|
|
{
|
|
// error
|
|
delete r;
|
|
return 0;
|
|
}
|
|
|
|
KVSP_ASSERT(KVSP_curCharUnicode == '}');
|
|
|
|
KVSP_skipChar;
|
|
|
|
r = new KviKvsTreeNodeHashElement(pBegin,r,i);
|
|
}
|
|
}
|
|
|
|
if(KVSP_curCharUnicode != '-')
|
|
{
|
|
return r;
|
|
}
|
|
|
|
if(!r->canEvaluateToObjectReference())return r; // FIXME: maybe print a warning ?
|
|
|
|
// might be a scope operator
|
|
|
|
KVSP_skipChar;
|
|
if(KVSP_curCharUnicode != '>')
|
|
{
|
|
KVSP_backChar;
|
|
return r;
|
|
}
|
|
|
|
|
|
KVSP_skipChar;
|
|
skipSpaces();
|
|
|
|
if((KVSP_curCharUnicode != '$') && (KVSP_curCharUnicode != '%'))
|
|
{
|
|
KVSP_setCurCharPointer(pBegin);
|
|
return r;
|
|
}
|
|
|
|
handle_scope_operator:
|
|
|
|
// hmmm... there really seems to be a scope operator there...
|
|
if(KVSP_curCharUnicode == '%')
|
|
{
|
|
KVSP_skipChar;
|
|
if(!KVSP_curCharIsLetter)
|
|
{
|
|
// be flexible : allow an "alone" '%' char
|
|
KVSP_setCurCharPointer(pBegin);
|
|
return r;
|
|
}
|
|
} else {
|
|
KVSP_skipChar;
|
|
if(!KVSP_curCharIsFunctionStart)
|
|
{
|
|
// be flexible : allow an "alone" '$' char
|
|
KVSP_setCurCharPointer(pBegin);
|
|
return r;
|
|
}
|
|
}
|
|
|
|
// ok : try the scope operator
|
|
KVSP_backChar;
|
|
|
|
pBegin = KVSP_curCharPointer;
|
|
|
|
KviKvsTreeNodeData * r2 = parsePercentOrDollar(true);
|
|
|
|
if(!r2)
|
|
{
|
|
// must be an error
|
|
delete r;
|
|
return 0;
|
|
}
|
|
|
|
if(!r2->canEvaluateInObjectScope())
|
|
{
|
|
// ops... it really wasn't
|
|
delete r2;
|
|
KVSP_setCurCharPointer(pBegin);
|
|
return r;
|
|
}
|
|
|
|
return new KviKvsTreeNodeScopeOperator(pBegin,r,r2);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
KviKvsTreeNodeVariable * KviKvsParser::parsePercent(bool bInObjScope)
|
|
{
|
|
KVSP_ASSERT(KVSP_curCharUnicode == '%');
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
KVSP_skipChar;
|
|
|
|
bool bExtScope;
|
|
|
|
if(KVSP_curCharUnicode == ':')
|
|
{
|
|
bExtScope = true;
|
|
KVSP_skipChar;
|
|
} else {
|
|
bExtScope = false;
|
|
}
|
|
|
|
if(!((KVSP_curCharIsLetterOrNumber) || (KVSP_curCharUnicode == '_')))
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Syntax error after '%' variable prefix. If you want to use a plain '%' in the code you need to escape it"));
|
|
return 0;
|
|
}
|
|
|
|
const TQChar * pIdBegin = KVSP_curCharPointer;
|
|
|
|
while((KVSP_curCharIsLetterOrNumber) || (KVSP_curCharUnicode == '_') || (KVSP_curCharUnicode == '.'))KVSP_skipChar;
|
|
|
|
TQString szIdentifier(pIdBegin,KVSP_curCharPointer - pIdBegin);
|
|
|
|
//#warning "ADD A KviKvsTreeNodeBuiltinCleanupVariablesCommand on this KviKvsParser object"
|
|
//#warning "KviKvsParser will append it to the script"
|
|
|
|
if(bExtScope)
|
|
{
|
|
if(bInObjScope)
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Objects have no extended scope variables"));
|
|
return 0;
|
|
}
|
|
return new KviKvsTreeNodeExtendedScopeVariable(pBegin,szIdentifier);
|
|
}
|
|
|
|
if(bInObjScope)
|
|
return new KviKvsTreeNodeObjectField(pBegin,szIdentifier);
|
|
|
|
if(m_pGlobals)
|
|
{
|
|
if(m_pGlobals->find(szIdentifier))return new KviKvsTreeNodeGlobalVariable(pBegin,szIdentifier);
|
|
}
|
|
|
|
if(m_iFlags & AssumeLocals)
|
|
return new KviKvsTreeNodeLocalVariable(pBegin,szIdentifier);
|
|
|
|
if(pIdBegin->category() & TQChar::Letter_Uppercase)
|
|
{
|
|
//if(m_iFlags & Pedantic)
|
|
// warning(pIdBegin,__tr2qs("Declaring global variables with an uppercase letter is deprecated. Global variables should be declared with 'global'"));
|
|
return new KviKvsTreeNodeGlobalVariable(pBegin,szIdentifier);
|
|
}
|
|
|
|
return new KviKvsTreeNodeLocalVariable(pBegin,szIdentifier);
|
|
}
|
|
|
|
KviKvsTreeNodeInstruction * KviKvsParser::parseInstruction()
|
|
{
|
|
switch(KVSP_curCharUnicode)
|
|
{
|
|
case '#':
|
|
case '/':
|
|
(void)parseComment(); // this will return 0 anyway
|
|
return 0;
|
|
break;
|
|
case 0: // empty instruction
|
|
return 0;
|
|
break;
|
|
case '\n':
|
|
case '\r':
|
|
case ';': // empty instruction
|
|
KVSP_skipChar;
|
|
return 0;
|
|
break;
|
|
case '{': // command block
|
|
return parseInstructionBlock();
|
|
break;
|
|
case '$':
|
|
case '%':
|
|
case '@':
|
|
return parseVoidFunctionCallOrOperation();
|
|
break;
|
|
default:
|
|
if(KVSP_curCharIsLetter || (KVSP_curCharUnicode == '_'))
|
|
{
|
|
// must be a command
|
|
return parseCommand();
|
|
} else {
|
|
// what the heck is this ?
|
|
error(KVSP_curCharPointer,__tr2qs("Found character '%q' (unicode %x) where an instruction was expected"),KVSP_curCharPointer,KVSP_curCharUnicode);
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// never here
|
|
KVSP_ASSERT(false);
|
|
return 0;
|
|
}
|
|
|
|
KviKvsTreeNodeInstruction * KviKvsParser::parseInstructionBlock()
|
|
{
|
|
KVSP_ASSERT(KVSP_curCharUnicode == '{');
|
|
|
|
KVSP_skipChar;
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
KviKvsTreeNodeInstructionBlock * b = new KviKvsTreeNodeInstructionBlock(pBegin - 1);
|
|
|
|
for(;;)
|
|
{
|
|
if(!skipSpacesAndNewlines())
|
|
{
|
|
delete b;
|
|
return 0;
|
|
}
|
|
|
|
switch(KVSP_curCharUnicode)
|
|
{
|
|
case 0:
|
|
delete b;
|
|
warning(pBegin,__tr2qs("Unterminated instruction block"));
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of script in instruction block (missing closing brace)"));
|
|
return 0;
|
|
break;
|
|
case '}':
|
|
KVSP_skipChar;
|
|
if(b->instructionCount() <= 1)
|
|
{
|
|
if(b->instructionCount() < 1)
|
|
{
|
|
delete b;
|
|
return 0; // just an empty block
|
|
}
|
|
// a single instruction
|
|
KviKvsTreeNodeInstruction * i = b->releaseFirst();
|
|
delete b;
|
|
return i;
|
|
}
|
|
return b;
|
|
break;
|
|
default:
|
|
// instruction
|
|
KviKvsTreeNodeInstruction * i = parseInstruction();
|
|
if(i)b->addInstruction(i);
|
|
else {
|
|
if(error())
|
|
{
|
|
// ops...
|
|
delete b;
|
|
return 0;
|
|
} // else empty instruction
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
// never reached
|
|
return 0;
|
|
}
|
|
|
|
KviKvsTreeNodeSwitchList * KviKvsParser::parseCommandSwitchList()
|
|
{
|
|
KVSP_ASSERT(KVSP_curCharUnicode == '-');
|
|
|
|
KviKvsTreeNodeSwitchList * sw = new KviKvsTreeNodeSwitchList(KVSP_curCharPointer);
|
|
|
|
while(KVSP_curCharUnicode == '-')
|
|
{
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
KVSP_skipChar;
|
|
|
|
bool bLong = false;
|
|
|
|
if(KVSP_curCharUnicode == '-')
|
|
{
|
|
// long switch
|
|
pBegin = KVSP_curCharPointer;
|
|
KVSP_skipChar;
|
|
bLong = true;
|
|
}
|
|
|
|
skipSpaces();
|
|
if(!KVSP_curCharIsLetter)
|
|
{
|
|
if(KVSP_curCharIsNumber || KVSP_curCharIsEndOfCommand)
|
|
{
|
|
// a -digit : this is probably a negative number instead
|
|
// or just a single dash (or couple of dashes)
|
|
// go back to the initial dash and treat it as text...and return the current switch list
|
|
KVSP_setCurCharPointer(pBegin);
|
|
if(sw->isEmpty())
|
|
{
|
|
// not an error!
|
|
delete sw;
|
|
return 0;
|
|
}
|
|
return sw;
|
|
} else {
|
|
delete sw;
|
|
warning(pBegin,__tr2qs("The dash after a command should be followed by a letter (switch), by a digit (negative number) or be escaped"));
|
|
|
|
if(KVSP_curCharUnicode == 0)
|
|
{
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected character '%q' (unicode %x) after a switch dash"),KVSP_curCharPointer,KVSP_curCharUnicode);
|
|
} else {
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of script after a switch dash"));
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
const TQChar * pSw = KVSP_curCharPointer;
|
|
|
|
KVSP_skipChar;
|
|
while((KVSP_curCharIsLetterOrNumber) || (KVSP_curCharUnicode == '-'))KVSP_skipChar;
|
|
|
|
const TQChar * pSwEnd = KVSP_curCharPointer;
|
|
|
|
skipSpaces();
|
|
|
|
if(KVSP_curCharUnicode == '=')
|
|
{
|
|
KVSP_skipChar;
|
|
skipSpaces();
|
|
KviKvsTreeNodeData * p = parseCommandParameter();
|
|
if(!p)
|
|
{
|
|
// must be an error :(
|
|
if(error())
|
|
{
|
|
error(pBegin,__tr2qs("The above problem might be related to the switch dash and the following equal sign"));
|
|
delete sw;
|
|
return 0;
|
|
} else {
|
|
// assume empty string
|
|
p = new KviKvsTreeNodeConstantData(KVSP_curCharPointer,new KviKvsVariant(TQString("")));
|
|
}
|
|
}
|
|
|
|
skipSpaces();
|
|
|
|
if(bLong)
|
|
sw->addLong(TQString(pSw,pSwEnd - pSw),p);
|
|
else
|
|
sw->addShort(pSw->lower().unicode(),p);
|
|
} else {
|
|
if(bLong)
|
|
sw->addLong(TQString(pSw,pSwEnd - pSw),new KviKvsTreeNodeConstantData(KVSP_curCharPointer,new KviKvsVariant(true))); // empty param
|
|
else
|
|
sw->addShort(pSw->lower().unicode(),new KviKvsTreeNodeConstantData(KVSP_curCharPointer,new KviKvsVariant(true))); // empty param
|
|
}
|
|
}
|
|
|
|
return sw;
|
|
}
|
|
|
|
|
|
|
|
KviKvsTreeNodeDataList * KviKvsParser::parseCommandParameterList()
|
|
{
|
|
KviKvsTreeNodeDataList * l = new KviKvsTreeNodeDataList(KVSP_curCharPointer);
|
|
|
|
for(;;)
|
|
{
|
|
skipSpaces();
|
|
switch(KVSP_curCharUnicode)
|
|
{
|
|
case 0:
|
|
return l;
|
|
break;
|
|
case '\r':
|
|
case '\n':
|
|
case ';':
|
|
KVSP_skipChar;
|
|
return l;
|
|
break;
|
|
default:
|
|
// anything else is a parameter
|
|
KviKvsTreeNodeData * p = parseCommandParameter();
|
|
if(!p)
|
|
{
|
|
// this is an error
|
|
delete l;
|
|
return 0;
|
|
}
|
|
l->addItem(p);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// never here
|
|
KVSP_ASSERT(false);
|
|
return 0;
|
|
}
|
|
|
|
|
|
KviPointerList<TQString> * KviKvsParser::parseCommaSeparatedParameterListNoTree()
|
|
{
|
|
KviPointerList<TQString> * l = new KviPointerList<TQString>;
|
|
l->setAutoDelete(true);
|
|
|
|
KVSP_skipChar;
|
|
|
|
for(;;)
|
|
{
|
|
skipSpaces();
|
|
switch(KVSP_curCharUnicode)
|
|
{
|
|
case 0:
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of script in parameter list"));
|
|
delete l;
|
|
return 0;
|
|
break;
|
|
case '\r':
|
|
case '\n':
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of line in parameter list"));
|
|
delete l;
|
|
return 0;
|
|
break;
|
|
/*
|
|
case ',':
|
|
KVSP_skipChar;
|
|
break;
|
|
case ')':
|
|
KVSP_skipChar;
|
|
return l;
|
|
break;
|
|
*/
|
|
default:
|
|
{
|
|
// anything else is a parameter
|
|
const TQChar *pBegin = KVSP_curCharPointer;
|
|
KviKvsTreeNodeData * p = parseCommaSeparatedParameter();
|
|
if(!p)
|
|
{
|
|
// this is an error
|
|
delete l;
|
|
return 0;
|
|
}
|
|
delete p;
|
|
TQString * s = new TQString(pBegin,KVSP_curCharPointer - pBegin);
|
|
s->stripWhiteSpace();
|
|
l->append(s);
|
|
|
|
switch(KVSP_curCharUnicode)
|
|
{
|
|
case ',':
|
|
KVSP_skipChar;
|
|
break;
|
|
case ')':
|
|
KVSP_skipChar;
|
|
return l;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// never here
|
|
KVSP_ASSERT(false);
|
|
return 0;
|
|
}
|
|
|
|
|
|
KviKvsTreeNodeDataList * KviKvsParser::parseCommaSeparatedParameterList()
|
|
{
|
|
KviKvsTreeNodeDataList * l = new KviKvsTreeNodeDataList(KVSP_curCharPointer);
|
|
|
|
KVSP_skipChar;
|
|
|
|
for(;;)
|
|
{
|
|
skipSpaces();
|
|
switch(KVSP_curCharUnicode)
|
|
{
|
|
case 0:
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of script in parameter list"));
|
|
delete l;
|
|
return 0;
|
|
break;
|
|
case '\r':
|
|
case '\n':
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of line in parameter list"));
|
|
delete l;
|
|
return 0;
|
|
break;
|
|
/*
|
|
case ',':
|
|
KVSP_skipChar;
|
|
break;
|
|
case ')':
|
|
KVSP_skipChar;
|
|
return l;
|
|
break;
|
|
*/
|
|
default:
|
|
// anything else is a parameter
|
|
KviKvsTreeNodeData * p = parseCommaSeparatedParameter();
|
|
if(!p)
|
|
{
|
|
// this is an error
|
|
delete l;
|
|
return 0;
|
|
}
|
|
l->addItem(p);
|
|
|
|
switch(KVSP_curCharUnicode)
|
|
{
|
|
case ',':
|
|
KVSP_skipChar;
|
|
break;
|
|
case ')':
|
|
KVSP_skipChar;
|
|
return l;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// never here
|
|
KVSP_ASSERT(false);
|
|
return 0;
|
|
}
|
|
|
|
|
|
#define LITERAL_PARAM_PARSING_FUNCTION_BEGIN(__funcname) \
|
|
KviKvsTreeNodeConstantData * KviKvsParser::__funcname() \
|
|
{ \
|
|
TQString szValue; \
|
|
\
|
|
const TQChar * pStart = KVSP_curCharPointer; \
|
|
const TQChar * pBegin = KVSP_curCharPointer; \
|
|
int iLen = 0; \
|
|
int iNestedTerminators = 0; \
|
|
\
|
|
for(;;) \
|
|
{ \
|
|
switch(KVSP_curCharUnicode) \
|
|
{
|
|
|
|
#define LITERAL_PARAM_PARSING_FUNCTION_WARN_NESTED_TERMINATOR \
|
|
if(!_OUTPUT_MUTE) \
|
|
warning(KVSP_curCharPointer,__tr2qs("Nested character %q corresponding to expected terminator, this might confuse me a bit: it is a good idea to enclose it in quotes"),KVSP_curCharPointer); \
|
|
KVSP_skipChar; \
|
|
iNestedTerminators++; \
|
|
iLen++; \
|
|
break;
|
|
|
|
#define LITERAL_PARAM_PARSING_FUNCTION_END_WITH_EXPECTED_TERMINATOR \
|
|
if(iNestedTerminators > 0) \
|
|
{ \
|
|
if(!_OUTPUT_MUTE) \
|
|
warning(KVSP_curCharPointer,__tr2qs("Skipping nested terminator character %q"),KVSP_curCharPointer); \
|
|
KVSP_skipChar; \
|
|
iNestedTerminators--; \
|
|
iLen++; \
|
|
} else { \
|
|
if(iLen > 0)szValue.append(TQString(pBegin,iLen)); \
|
|
{ \
|
|
bool bOk; \
|
|
kvs_int_t iVal = szValue.toLong(&bOk); \
|
|
if(bOk)return new KviKvsTreeNodeConstantData(pBegin,new KviKvsVariant(iVal)); \
|
|
kvs_real_t dVal = szValue.toDouble(&bOk); \
|
|
if(bOk)return new KviKvsTreeNodeConstantData(pBegin,new KviKvsVariant(dVal)); \
|
|
} \
|
|
return new KviKvsTreeNodeConstantData(pBegin,new KviKvsVariant(szValue)); \
|
|
} \
|
|
break;
|
|
|
|
#define LITERAL_PARAM_PARSING_FUNCTION_GENERIC_END \
|
|
if(iLen > 0) szValue.append(TQString(pBegin,iLen)); \
|
|
return new KviKvsTreeNodeConstantData(pBegin,new KviKvsVariant(szValue)); \
|
|
break; \
|
|
case '\\': \
|
|
if(iLen > 0)szValue.append(TQString(pBegin,iLen)); \
|
|
KVSP_skipChar; \
|
|
switch(KVSP_curCharUnicode) \
|
|
{ \
|
|
case 0: \
|
|
warning(KVSP_curCharPointer - 1,__tr2qs("Stray backslash at the end of the script")); \
|
|
iLen = 0; \
|
|
break; \
|
|
case '\r': \
|
|
case '\n': \
|
|
KVSP_skipChar; \
|
|
pBegin = KVSP_curCharPointer; \
|
|
iLen = 0; \
|
|
break; \
|
|
case 'r': \
|
|
KVSP_skipChar; \
|
|
pBegin = KVSP_curCharPointer; \
|
|
szValue.append(TQChar('\r')); \
|
|
iLen = 0; \
|
|
break; \
|
|
case 'n': \
|
|
KVSP_skipChar; \
|
|
pBegin = KVSP_curCharPointer; \
|
|
szValue.append(TQChar('\n')); \
|
|
iLen = 0; \
|
|
break; \
|
|
case 't': \
|
|
KVSP_skipChar; \
|
|
pBegin = KVSP_curCharPointer; \
|
|
szValue.append(TQChar('\t')); \
|
|
iLen = 0; \
|
|
break; \
|
|
default: \
|
|
pBegin = KVSP_curCharPointer; \
|
|
KVSP_skipChar; \
|
|
iLen = 1; \
|
|
break; \
|
|
} \
|
|
break; \
|
|
default: \
|
|
KVSP_skipChar; \
|
|
iLen++; \
|
|
break; \
|
|
} \
|
|
} \
|
|
KVSP_ASSERT(false); \
|
|
return 0; \
|
|
}
|
|
|
|
|
|
LITERAL_PARAM_PARSING_FUNCTION_BEGIN(parseCommandLiteralParameter)
|
|
case 0:
|
|
case '$':
|
|
case '%':
|
|
case '@':
|
|
case '\r':
|
|
case '\n':
|
|
case '"':
|
|
case ';':
|
|
case ' ':
|
|
case '\t':
|
|
LITERAL_PARAM_PARSING_FUNCTION_GENERIC_END
|
|
|
|
|
|
LITERAL_PARAM_PARSING_FUNCTION_BEGIN(parseStringLiteralParameter)
|
|
case 0:
|
|
case '$':
|
|
case '%':
|
|
case '@':
|
|
case '\r':
|
|
case '\n':
|
|
case '"':
|
|
LITERAL_PARAM_PARSING_FUNCTION_GENERIC_END
|
|
|
|
/*
|
|
LITERAL_PARAM_PARSING_FUNCTION_BEGIN(parseArrayIndexLiteralParameter)
|
|
case '\t':
|
|
case ' ':
|
|
case ']':
|
|
LITERAL_PARAM_PARSING_FUNCTION_END
|
|
*/
|
|
|
|
|
|
LITERAL_PARAM_PARSING_FUNCTION_BEGIN(parseHashKeyLiteralParameter)
|
|
case '{':
|
|
LITERAL_PARAM_PARSING_FUNCTION_WARN_NESTED_TERMINATOR
|
|
case '}':
|
|
LITERAL_PARAM_PARSING_FUNCTION_END_WITH_EXPECTED_TERMINATOR
|
|
case 0:
|
|
case '$':
|
|
case '%':
|
|
case '@':
|
|
case '\r':
|
|
case '\n':
|
|
case '"':
|
|
case '\t':
|
|
case ' ':
|
|
LITERAL_PARAM_PARSING_FUNCTION_GENERIC_END
|
|
|
|
|
|
LITERAL_PARAM_PARSING_FUNCTION_BEGIN(parseCommaSeparatedLiteralParameter)
|
|
case '(':
|
|
LITERAL_PARAM_PARSING_FUNCTION_WARN_NESTED_TERMINATOR
|
|
case ')':
|
|
LITERAL_PARAM_PARSING_FUNCTION_END_WITH_EXPECTED_TERMINATOR
|
|
case 0:
|
|
case '$':
|
|
case '%':
|
|
case '@':
|
|
case '\r':
|
|
case '\n':
|
|
case '"':
|
|
case ',':
|
|
case ' ':
|
|
case '\t':
|
|
LITERAL_PARAM_PARSING_FUNCTION_GENERIC_END
|
|
|
|
|
|
LITERAL_PARAM_PARSING_FUNCTION_BEGIN(parseSingleLiteralParameterInParenthesis)
|
|
case '(':
|
|
LITERAL_PARAM_PARSING_FUNCTION_WARN_NESTED_TERMINATOR
|
|
case ')':
|
|
LITERAL_PARAM_PARSING_FUNCTION_END_WITH_EXPECTED_TERMINATOR
|
|
case 0:
|
|
case '$':
|
|
case '%':
|
|
case '@':
|
|
case '\r':
|
|
case '\n':
|
|
case '"':
|
|
case ' ':
|
|
case '\t':
|
|
LITERAL_PARAM_PARSING_FUNCTION_GENERIC_END
|
|
|
|
LITERAL_PARAM_PARSING_FUNCTION_BEGIN(parseBindingOperationLiteralParameter)
|
|
case 0:
|
|
case '$':
|
|
case '%':
|
|
case '@':
|
|
case '\r':
|
|
case '\n':
|
|
case '"':
|
|
case '/':
|
|
LITERAL_PARAM_PARSING_FUNCTION_GENERIC_END
|
|
|
|
|
|
/*
|
|
KviKvsTreeNodeData * KviKvsParser::parseArrayIndex()
|
|
{
|
|
KviPointerList<KviKvsTreeNodeData> * l = new KviPointerList<KviKvsTreeNodeData>();
|
|
l->setAutoDelete(true);
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
//KVSP_skipChar;
|
|
|
|
for(;;)
|
|
{
|
|
switch(KVSP_curCharUnicode)
|
|
{
|
|
case 0:
|
|
delete l;
|
|
warning(pBegin,__tr2qs("Unterminated array index"));
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of script in array index (missing ']' character ?)"));
|
|
return 0;
|
|
break;
|
|
case '\n':
|
|
delete l;
|
|
warning(pBegin,__tr2qs("Unterminated array index"));
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of line in array index (missing ']' character or unescaped newline)"));
|
|
return 0;
|
|
break;
|
|
case ' ':
|
|
case '\t':
|
|
skipSpaces();
|
|
if(KVSP_curCharUnicode != ']')
|
|
{
|
|
delete l;
|
|
warning(pBegin,__tr2qs("Unterminated array index"));
|
|
switch(KVSP_curCharUnicode)
|
|
{
|
|
case 0:
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of script in array index (missing ']' character ?)"));
|
|
break;
|
|
case '\n':
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of line in array index (missing ']' character or unescaped newline)"));
|
|
break;
|
|
default:
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected character '%q' (unicode %x) in array index: it should be already terminated"),KVSP_curCharPointer,KVSP_curCharUnicode);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
goto end_of_the_array_index;
|
|
break;
|
|
case '$':
|
|
case '%':
|
|
{
|
|
// this may be a data reference
|
|
KviKvsTreeNodeData * p = parseParameterPercentOrDollar();
|
|
if(!p)
|
|
{
|
|
// this is an error
|
|
delete l;
|
|
return 0;
|
|
}
|
|
l->append(p);
|
|
}
|
|
break;
|
|
case ']':
|
|
{
|
|
// end of the array index
|
|
goto end_of_the_array_index;
|
|
}
|
|
break;
|
|
case '"':
|
|
{
|
|
// string (should we parse strings in array indexes anyway ?).. well "1"$count might be a valid one in the end
|
|
KviKvsTreeNodeData * p = parseStringParameter();
|
|
if(!p)
|
|
{
|
|
// error
|
|
delete l;
|
|
return 0;
|
|
}
|
|
l->append(p);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
// anything else is a literal
|
|
l->append(parseArrayIndexLiteralParameter());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
end_of_the_array_index:
|
|
if(l->count() > 1)
|
|
{
|
|
// complex parameter needed
|
|
return new KviKvsTreeNodeCompositeData(l);
|
|
} else {
|
|
// a single parameter in the list
|
|
l->setAutoDelete(false);
|
|
KviKvsTreeNodeData * p = l->first();
|
|
delete l;
|
|
return p;
|
|
}
|
|
|
|
}
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
KviKvsTreeNodeData * KviKvsParser::parseHashKey()
|
|
{
|
|
KviPointerList<KviKvsTreeNodeData> * l = new KviPointerList<KviKvsTreeNodeData>();
|
|
l->setAutoDelete(true);
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
//KVSP_skipChar;
|
|
|
|
for(;;)
|
|
{
|
|
switch(KVSP_curCharUnicode)
|
|
{
|
|
case 0:
|
|
delete l;
|
|
warning(pBegin,__tr2qs("Unterminated hash key"));
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of script in hash key (missing '}' character ?)"));
|
|
return 0;
|
|
break;
|
|
case '\r':
|
|
case '\n':
|
|
delete l;
|
|
warning(pBegin,__tr2qs("Unterminated hash key"));
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of line in hash key (missing '}' character or unescaped newline)"));
|
|
return 0;
|
|
break;
|
|
case ' ':
|
|
case '\t':
|
|
skipSpaces();
|
|
if(KVSP_curCharUnicode != '}')
|
|
{
|
|
// separate by single spaces
|
|
l->append(new KviKvsTreeNodeConstantData(KVSP_curCharPointer,new KviKvsVariant(TQString(" "))));
|
|
} else {
|
|
goto end_of_the_hash_key;
|
|
}
|
|
break;
|
|
case '$':
|
|
case '%':
|
|
case '@':
|
|
{
|
|
// this may be a data reference
|
|
KviKvsTreeNodeData * p = parseParameterPercentOrDollar();
|
|
if(!p)
|
|
{
|
|
// this is an error
|
|
delete l;
|
|
return 0;
|
|
}
|
|
l->append(p);
|
|
}
|
|
break;
|
|
case '}':
|
|
{
|
|
// end of the array index
|
|
goto end_of_the_hash_key;
|
|
}
|
|
break;
|
|
case '"':
|
|
{
|
|
// string
|
|
KviKvsTreeNodeData * p = parseStringParameter();
|
|
if(!p)
|
|
{
|
|
// error
|
|
delete l;
|
|
return 0;
|
|
}
|
|
l->append(p);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
// anything else is a literal
|
|
l->append(parseHashKeyLiteralParameter());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
end_of_the_hash_key:
|
|
if(l->count() > 1)
|
|
{
|
|
// complex parameter needed
|
|
return new KviKvsTreeNodeCompositeData(pBegin,l);
|
|
} else {
|
|
// a single parameter in the list
|
|
l->setAutoDelete(false);
|
|
KviKvsTreeNodeData * p = l->first();
|
|
delete l;
|
|
return p;
|
|
}
|
|
// never reached
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
PARENTHESIS_PARAMETER_PARSING_FUNCTION_BEGIN(parseCommaSeparatedParameter)
|
|
case 0:
|
|
case ',':
|
|
case ')':
|
|
case '\n':
|
|
PARENTHESIS_PARAMETER_PARSING_FUNCTION_END()
|
|
|
|
#define PARENTHESIS_PARAMETER_PARSING_FUNCTION_BEGIN(_name) \
|
|
*/
|
|
|
|
|
|
|
|
KviKvsTreeNodeData * KviKvsParser::parseCommaSeparatedParameter()
|
|
{
|
|
KviPointerList<KviKvsTreeNodeData> * l = new KviPointerList<KviKvsTreeNodeData>;
|
|
l->setAutoDelete(true);
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
for(;;)
|
|
{
|
|
switch(KVSP_curCharUnicode)
|
|
{
|
|
case 0:
|
|
case ',':
|
|
case ')':
|
|
case '\r':
|
|
case '\n':
|
|
// not a part of a parameter
|
|
goto end_of_function_parameter;
|
|
break;
|
|
case '$':
|
|
case '%':
|
|
case '@':
|
|
{
|
|
// this may be a data reference
|
|
KviKvsTreeNodeData * p = parseParameterPercentOrDollar();
|
|
if(!p)
|
|
{
|
|
// this is an error
|
|
delete l;
|
|
return 0;
|
|
}
|
|
l->append(p);
|
|
}
|
|
break;
|
|
case ' ':
|
|
case '\t':
|
|
skipSpaces();
|
|
if((KVSP_curCharUnicode != ')') && (KVSP_curCharUnicode != ','))
|
|
{
|
|
// separate by single spaces
|
|
l->append(new KviKvsTreeNodeConstantData(KVSP_curCharPointer,new KviKvsVariant(TQString(" "))));
|
|
} else {
|
|
goto end_of_function_parameter;
|
|
}
|
|
break;
|
|
case '"':
|
|
{
|
|
// this is a string
|
|
KviKvsTreeNodeData * p = parseStringParameter();
|
|
if(!p)
|
|
{
|
|
// this is an error
|
|
delete l;
|
|
return 0;
|
|
}
|
|
l->append(p);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
// anything else is a literal
|
|
l->append(parseCommaSeparatedLiteralParameter());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
end_of_function_parameter:
|
|
if(l->count() > 1)
|
|
{
|
|
// complex parameter needed
|
|
KviKvsTreeNodeData * p = new KviKvsTreeNodeCompositeData(pBegin,l);
|
|
p->setEndingLocation(KVSP_curCharPointer);
|
|
return p;
|
|
} else {
|
|
// a single parameter in the list, or no params at all
|
|
l->setAutoDelete(false);
|
|
KviKvsTreeNodeData * p = l->first();
|
|
delete l;
|
|
if(!p)p = new KviKvsTreeNodeConstantData(KVSP_curCharPointer,new KviKvsVariant());
|
|
p->setEndingLocation(KVSP_curCharPointer);
|
|
return p;
|
|
}
|
|
// never reached
|
|
return 0;
|
|
}
|
|
|
|
|
|
KviKvsTreeNodeData * KviKvsParser::parseSingleParameterInParenthesis()
|
|
{
|
|
KviPointerList<KviKvsTreeNodeData> * l = new KviPointerList<KviKvsTreeNodeData>;
|
|
l->setAutoDelete(true);
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
for(;;)
|
|
{
|
|
switch(KVSP_curCharUnicode)
|
|
{
|
|
case ')':
|
|
// not a part of a parameter
|
|
KVSP_skipChar;
|
|
goto end_of_function_parameter;
|
|
break;
|
|
case 0:
|
|
case '\r':
|
|
case '\n':
|
|
// not a part of a parameter
|
|
goto end_of_function_parameter;
|
|
break;
|
|
case '$':
|
|
case '%':
|
|
case '@':
|
|
{
|
|
// this may be a data reference
|
|
KviKvsTreeNodeData * p = parseParameterPercentOrDollar();
|
|
if(!p)
|
|
{
|
|
// this is an error
|
|
delete l;
|
|
return 0;
|
|
}
|
|
l->append(p);
|
|
}
|
|
break;
|
|
case ' ':
|
|
case '\t':
|
|
skipSpaces();
|
|
if((KVSP_curCharUnicode != ')') && (KVSP_curCharUnicode != ','))
|
|
{
|
|
// separate by single spaces
|
|
l->append(new KviKvsTreeNodeConstantData(KVSP_curCharPointer,new KviKvsVariant(TQString(" "))));
|
|
} else {
|
|
goto end_of_function_parameter;
|
|
}
|
|
break;
|
|
case '"':
|
|
{
|
|
// this is a string
|
|
KviKvsTreeNodeData * p = parseStringParameter();
|
|
if(!p)
|
|
{
|
|
// this is an error
|
|
delete l;
|
|
return 0;
|
|
}
|
|
l->append(p);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
// anything else is a literal
|
|
l->append(parseSingleLiteralParameterInParenthesis());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
end_of_function_parameter:
|
|
if(l->count() > 1)
|
|
{
|
|
// complex parameter needed
|
|
KviKvsTreeNodeData * p = new KviKvsTreeNodeCompositeData(pBegin,l);
|
|
p->setEndingLocation(KVSP_curCharPointer);
|
|
return p;
|
|
} else {
|
|
// a single parameter in the list or list empty at all
|
|
l->setAutoDelete(false);
|
|
KviKvsTreeNodeData * p = l->first();
|
|
if(p)p->setEndingLocation(KVSP_curCharPointer);
|
|
delete l;
|
|
return p;
|
|
}
|
|
// never reached
|
|
return 0;
|
|
}
|
|
|
|
|
|
KviKvsTreeNodeData * KviKvsParser::parseStringParameter()
|
|
{
|
|
KVSP_ASSERT(KVSP_curCharUnicode == '"');
|
|
|
|
KviPointerList<KviKvsTreeNodeData> * l = new KviPointerList<KviKvsTreeNodeData>();
|
|
l->setAutoDelete(true);
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
KVSP_skipChar;
|
|
|
|
for(;;)
|
|
{
|
|
switch(KVSP_curCharUnicode)
|
|
{
|
|
case 0:
|
|
delete l;
|
|
warning(pBegin,__tr2qs("Unterminated string constant"));
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of script in string constant (missing \" character ?)"));
|
|
return 0;
|
|
break;
|
|
case '\r':
|
|
case '\n':
|
|
delete l;
|
|
warning(pBegin,__tr2qs("Unterminated string constant"));
|
|
error(KVSP_curCharPointer,__tr2qs("Unexpected end of line in string constant (missing \" character or unescaped newline)"));
|
|
return 0;
|
|
break;
|
|
case '$':
|
|
case '%':
|
|
case '@':
|
|
{
|
|
// this may be a data reference
|
|
KviKvsTreeNodeData * p = parseParameterPercentOrDollar();
|
|
if(!p)
|
|
{
|
|
// this is an error
|
|
delete l;
|
|
return 0;
|
|
}
|
|
l->append(p);
|
|
}
|
|
break;
|
|
case '"':
|
|
{
|
|
// end of the string
|
|
KVSP_skipChar;
|
|
goto end_of_the_string;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
// anything else is a literal
|
|
l->append(parseStringLiteralParameter());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
end_of_the_string:
|
|
if(l->count() > 1)
|
|
{
|
|
// complex parameter needed
|
|
// it is also an implicit string cast
|
|
return new KviKvsTreeNodeCompositeData(pBegin,l);
|
|
} else {
|
|
if(l->count() > 0)
|
|
{
|
|
// a single parameter in the list
|
|
// we need an explicit string cast here (it is the most common cast)
|
|
l->setAutoDelete(false);
|
|
KviKvsTreeNodeData * p = l->first();
|
|
delete l;
|
|
return new KviKvsTreeNodeStringCast(pBegin,p);
|
|
} else {
|
|
// no parameters at all.. return straight empty string (no need to cast)
|
|
delete l;
|
|
return new KviKvsTreeNodeConstantData(pBegin,new KviKvsVariant(new TQString()));
|
|
}
|
|
}
|
|
// never reached
|
|
return 0;
|
|
}
|
|
|
|
KviKvsTreeNodeData * KviKvsParser::parseCommandParameter(bool bPreferNumeric)
|
|
{
|
|
KviPointerList<KviKvsTreeNodeData> * l = new KviPointerList<KviKvsTreeNodeData>;
|
|
l->setAutoDelete(true);
|
|
|
|
bool bGotLiteral = false;
|
|
|
|
const TQChar * pBegin = KVSP_curCharPointer;
|
|
|
|
for(;;)
|
|
{
|
|
switch(KVSP_curCharUnicode)
|
|
{
|
|
case 0:
|
|
case ' ':
|
|
case '\t':
|
|
case '\r':
|
|
case '\n':
|
|
case ';':
|
|
// not a part of a parameter
|
|
goto jumpout;
|
|
break;
|
|
case '$':
|
|
case '%':
|
|
case '@':
|
|
{
|
|
// this may be a data reference
|
|
KviKvsTreeNodeData * p = parseParameterPercentOrDollar();
|
|
if(!p)
|
|
{
|
|
// this is an error
|
|
delete l;
|
|
return 0;
|
|
}
|
|
l->append(p);
|
|
}
|
|
break;
|
|
case '"':
|
|
{
|
|
// this is a string
|
|
KviKvsTreeNodeData * p = parseStringParameter();
|
|
if(!p)
|
|
{
|
|
// this is an error
|
|
delete l;
|
|
return 0;
|
|
}
|
|
l->append(p);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
bGotLiteral = true;
|
|
// anything else is a literal
|
|
l->append(parseCommandLiteralParameter());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
jumpout:
|
|
if(l->count() > 1)
|
|
{
|
|
// complex parameter needed
|
|
KviKvsTreeNodeData * p = new KviKvsTreeNodeCompositeData(pBegin,l);
|
|
p->setEndingLocation(KVSP_curCharPointer);
|
|
return p;
|
|
} else {
|
|
// a single parameter in the list or empty list at all
|
|
l->setAutoDelete(false);
|
|
KviKvsTreeNodeData * p = l->first();
|
|
delete l;
|
|
if(p)
|
|
{
|
|
if(bGotLiteral)
|
|
{
|
|
// a single literal parameter
|
|
if(bPreferNumeric)
|
|
{
|
|
// attempt to convert to a numeric format if this is a constant data item
|
|
p->convertStringConstantToNumeric();
|
|
}
|
|
}
|
|
p->setEndingLocation(KVSP_curCharPointer);
|
|
}
|
|
return p;
|
|
}
|
|
// never reached
|
|
return 0;
|
|
}
|
|
|