summaryrefslogtreecommitdiffstats
path: root/src/kvirc/kvs/kvi_kvs_parser.cpp
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-24 02:13:59 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-24 02:13:59 +0000
commita6d58bb6052ac8cb01805a48c4ad2f129126116f (patch)
treedd867a099fcbb263a8009a9fb22695b87855dad6 /src/kvirc/kvs/kvi_kvs_parser.cpp
downloadkvirc-a6d58bb6052ac8cb01805a48c4ad2f129126116f.tar.gz
kvirc-a6d58bb6052ac8cb01805a48c4ad2f129126116f.zip
Added KDE3 version of kvirc
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kvirc@1095341 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/kvirc/kvs/kvi_kvs_parser.cpp')
-rw-r--r--src/kvirc/kvs/kvi_kvs_parser.cpp3827
1 files changed, 3827 insertions, 0 deletions
diff --git a/src/kvirc/kvs/kvi_kvs_parser.cpp b/src/kvirc/kvs/kvi_kvs_parser.cpp
new file mode 100644
index 0000000..37e462c
--- /dev/null
+++ b/src/kvirc/kvs/kvi_kvs_parser.cpp
@@ -0,0 +1,3827 @@
+//=============================================================================
+//
+// 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(QString(__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 QChar * pLocation,const QString &szMsgFmt,kvi_va_list va)
+{
+ QString szMsg;
+ KviQString::vsprintf(szMsg,szMsgFmt,va);
+
+ KviPointerList<QString> * pCodeListing = 0;
+ QString szLocation;
+
+ if(pLocation)
+ {
+ pCodeListing = new KviPointerList<QString>;
+ pCodeListing->setAutoDelete(true);
+
+ int iLine,iCol;
+
+ KviKvsReport::findLineColAndListing(m_pBuffer,pLocation,iLine,iCol,pCodeListing);
+
+ KviQString::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 QChar * 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 QChar * pLocation,const QString &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 QChar * pLocation,const QString &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 QChar * 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 QChar * 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 QChar * 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 &nbsp; &nbsp; text &nbsp; &nbsp; will &nbsp; &nbsp; have &nbsp; &nbsp; spaces &nbsp; &nbsp; simplified
+ [cmd]echo[/cmd] But &nbsp; &nbsp; "this &nbsp; &nbsp; &nbsp; one &nbsp; &nbsp; 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 \
+ &nbsp; &nbsp; 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 &nbsp; &nbsp; &nbsp \
+ &nbsp; &nbsp; IS OUT!"
+ [cmd]echo[/cmd] Check it out at http://www.kvi\
+ &nbsp; &nbsp; rc.net!
+ [/example]
+ This will be printed as:[br][br]
+ [i]
+ The new kvirc &nbsp; &nbsp; &nbsp; 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] &lt;filename&gt;
+ [/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]
+ {
+ &lt;tab&gt;[cmd]echo[/cmd] Indented command
+ &lt;tab&gt;{
+ &lt;tab&gt;&lt;tab&gt;# Comment
+ &lt;tab&gt;&lt;tab&gt;[cmd]echo[/cmd] Really Really long indented \
+ &lt;tab&gt;&lt;tab&gt;&lt;tab&gt;command
+ &lt;tab&gt;}
+ }
+ [/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 \
+ &nbsp; &nbsp; 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]
+ &nbsp; &nbsp; [b]addonId-version/[/b]
+ &nbsp; &nbsp; &nbsp; &nbsp; install.kvs
+ &nbsp; &nbsp; &nbsp; &nbsp; INSTALL
+ &nbsp; &nbsp; &nbsp; &nbsp; [b]src[/b]
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; source1.kvs
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; source2.kvs
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; source3.kvs
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...
+ &nbsp; &nbsp; &nbsp; &nbsp; [b]pics[/b]
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; addonId_pic1.png
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; addonId_pic2.png
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; addonId_pic3.png
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...
+ &nbsp; &nbsp; &nbsp; &nbsp; [b]audio[/b]
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; addonId_audio1.png
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; addonId_audio2.png
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; addonId_audio3.png
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...
+ &nbsp; &nbsp; &nbsp; &nbsp; [b]help[/b]
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; en
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; index.html
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hints.html
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; it
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; index.html
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hints.html
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...
+ &nbsp; &nbsp; &nbsp; &nbsp; [b]locale[/b]
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; addonId_it.mo
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; addonId_ru.mo
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; addonId_de.mo
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...
+ &nbsp; &nbsp; &nbsp; &nbsp; [b]...[/b]
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...
+ [/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(QString("%")));
+ }
+ // 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(QString("$")));
+ }
+ // 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 QChar * 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 QString szStrayAtRoutineName("@");
+ static QString 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 QChar * 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 QChar * pIdBegin = KVSP_curCharPointer;
+
+ while((KVSP_curCharIsLetterOrNumber) || (KVSP_curCharUnicode == '_') || (KVSP_curCharUnicode == '.'))KVSP_skipChar;
+
+ QString 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() & QChar::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 QChar * 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 QChar * 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 QChar * pSw = KVSP_curCharPointer;
+
+ KVSP_skipChar;
+ while((KVSP_curCharIsLetterOrNumber) || (KVSP_curCharUnicode == '-'))KVSP_skipChar;
+
+ const QChar * 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(QString("")));
+ }
+ }
+
+ skipSpaces();
+
+ if(bLong)
+ sw->addLong(QString(pSw,pSwEnd - pSw),p);
+ else
+ sw->addShort(pSw->lower().unicode(),p);
+ } else {
+ if(bLong)
+ sw->addLong(QString(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<QString> * KviKvsParser::parseCommaSeparatedParameterListNoTree()
+{
+ KviPointerList<QString> * l = new KviPointerList<QString>;
+ 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 QChar *pBegin = KVSP_curCharPointer;
+ KviKvsTreeNodeData * p = parseCommaSeparatedParameter();
+ if(!p)
+ {
+ // this is an error
+ delete l;
+ return 0;
+ }
+ delete p;
+ QString * s = new QString(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() \
+{ \
+ QString szValue; \
+\
+ const QChar * pStart = KVSP_curCharPointer; \
+ const QChar * 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(QString(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(QString(pBegin,iLen)); \
+ return new KviKvsTreeNodeConstantData(pBegin,new KviKvsVariant(szValue)); \
+ break; \
+ case '\\': \
+ if(iLen > 0)szValue.append(QString(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(QChar('\r')); \
+ iLen = 0; \
+ break; \
+ case 'n': \
+ KVSP_skipChar; \
+ pBegin = KVSP_curCharPointer; \
+ szValue.append(QChar('\n')); \
+ iLen = 0; \
+ break; \
+ case 't': \
+ KVSP_skipChar; \
+ pBegin = KVSP_curCharPointer; \
+ szValue.append(QChar('\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 QChar * 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 QChar * 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(QString(" "))));
+ } 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 QChar * 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(QString(" "))));
+ } 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 QChar * 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(QString(" "))));
+ } 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 QChar * 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 QString()));
+ }
+ }
+ // never reached
+ return 0;
+}
+
+KviKvsTreeNodeData * KviKvsParser::parseCommandParameter(bool bPreferNumeric)
+{
+ KviPointerList<KviKvsTreeNodeData> * l = new KviPointerList<KviKvsTreeNodeData>;
+ l->setAutoDelete(true);
+
+ bool bGotLiteral = false;
+
+ const QChar * 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;
+}
+