1 //=============================================================================
2 //
3 // File : KviKvsParser.cpp
4 // Creation date : Thu 25 Sep 2003 05.12 CEST by Szymon Stefanek
5 //
6 // This file is part of the KVIrc IRC client distribution
7 // Copyright (C) 2003-2010 Szymon Stefanek (pragma at kvirc dot net)
8 //
9 // This program is FREE software. You can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the HOPE that it will be USEFUL,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 // See the GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program. If not, write to the Free Software Foundation,
21 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 //=============================================================================
24
25 #include "KviKvsParser.h"
26 #include "KviKvsTreeNode.h"
27 #include "KviKvsReport.h"
28 #include "KviKvsKernel.h"
29 #include "KviKvsScript.h"
30 #include "KviKvsParserMacros.h"
31 #include "KviLocale.h"
32 #include "KviOptions.h"
33
34 //FIXME: @ == $$-> == $this->
35
KviKvsParser(KviKvsScript * pScript,KviWindow * pOutputWindow)36 KviKvsParser::KviKvsParser(KviKvsScript * pScript, KviWindow * pOutputWindow)
37 {
38 m_pGlobals = nullptr;
39 m_pScript = pScript;
40 m_pWindow = pOutputWindow;
41 }
42
~KviKvsParser()43 KviKvsParser::~KviKvsParser()
44 {
45 if(m_pGlobals)
46 delete m_pGlobals;
47 }
48
init()49 void KviKvsParser::init()
50 {
51 KviKvsKernel * pKern = KviKvsKernel::instance();
52
53 #define _REG_CNTRL_CMD(__cntrlCmdName, __parsingRoutine) \
54 { \
55 KviKvsSpecialCommandParsingRoutine * r = new KviKvsSpecialCommandParsingRoutine; \
56 r->proc = KVI_PTR2MEMBER(KviKvsParser::__parsingRoutine); \
57 pKern->registerSpecialCommandParsingRoutine(QString(__cntrlCmdName), r); \
58 }
59
60 _REG_CNTRL_CMD("break", parseSpecialCommandBreak);
61 _REG_CNTRL_CMD("class", parseSpecialCommandClass);
62 _REG_CNTRL_CMD("continue", parseSpecialCommandContinue);
63 _REG_CNTRL_CMD("defpopup", parseSpecialCommandDefpopup);
64 _REG_CNTRL_CMD("do", parseSpecialCommandDo);
65 _REG_CNTRL_CMD("for", parseSpecialCommandFor);
66 _REG_CNTRL_CMD("foreach", parseSpecialCommandForeach);
67 _REG_CNTRL_CMD("global", parseSpecialCommandGlobal);
68 _REG_CNTRL_CMD("help", parseSpecialCommandHelp);
69 _REG_CNTRL_CMD("if", parseSpecialCommandIf);
70 _REG_CNTRL_CMD("switch", parseSpecialCommandSwitch);
71 _REG_CNTRL_CMD("unset", parseSpecialCommandUnset);
72 _REG_CNTRL_CMD("while", parseSpecialCommandWhile);
73 #undef _REG_CNTRL_CMD
74 }
75
report(bool bError,const QChar * pLocation,const QString & szMsgFmt,kvi_va_list va)76 void KviKvsParser::report(bool bError, const QChar * pLocation, const QString & szMsgFmt, kvi_va_list va)
77 {
78 QString szMsg;
79 KviQString::vsprintf(szMsg, szMsgFmt, va);
80
81 KviPointerList<QString> * pCodeListing = nullptr;
82 QString szLocation;
83
84 if(pLocation)
85 {
86 pCodeListing = new KviPointerList<QString>;
87 pCodeListing->setAutoDelete(true);
88
89 int iLine, iCol;
90
91 KviKvsReport::findLineColAndListing(m_pBuffer, pLocation, iLine, iCol, pCodeListing);
92
93 szLocation = QString(__tr2qs_ctx("line %1, near character %2", "kvs")).arg(iLine).arg(iCol);
94 }
95 else
96 {
97 szLocation = __tr2qs_ctx("beginning of input", "kvs");
98 }
99
100 KviKvsReport rep(bError ? KviKvsReport::ParserError : KviKvsReport::ParserWarning, m_pScript->name(), szMsg, szLocation, m_pWindow);
101 if(pCodeListing)
102 rep.setCodeListing(pCodeListing);
103
104 KviKvsReport::report(&rep, m_pWindow);
105 }
106
errorBadChar(const QChar * pLocation,char cExpected,const char * szCommandName)107 void KviKvsParser::errorBadChar(const QChar * pLocation, char cExpected, const char * szCommandName)
108 {
109 if(pLocation->unicode())
110 error(pLocation, __tr2qs_ctx("Found character '%q' (Unicode 0x%x) where '%c' was expected: see \"/help %s\" for the command syntax", "kvs"),
111 pLocation, pLocation->unicode(), cExpected, szCommandName);
112 else
113 error(pLocation, __tr2qs_ctx("Found end of input where character '%c' was expected: see \"/help %s\" for the command syntax", "kvs"),
114 cExpected, szCommandName);
115 }
116
error(const QChar * pLocation,QString szMsgFmt,...)117 void KviKvsParser::error(const QChar * pLocation, QString szMsgFmt, ...)
118 {
119 m_bError = true;
120
121 kvi_va_list va;
122 kvi_va_start(va, szMsgFmt);
123 report(true, pLocation, szMsgFmt, va);
124 kvi_va_end(va);
125 }
126
warning(const QChar * pLocation,QString szMsgFmt,...)127 void KviKvsParser::warning(const QChar * pLocation, QString szMsgFmt, ...)
128 {
129 kvi_va_list va;
130 kvi_va_start(va, szMsgFmt);
131 report(false, pLocation, szMsgFmt, va);
132 kvi_va_end(va);
133 }
134
parse(const QChar * pBuffer,int iFlags)135 KviKvsTreeNodeInstruction * KviKvsParser::parse(const QChar * pBuffer, int iFlags)
136 {
137 m_iFlags = iFlags;
138
139 m_bError = false;
140 if(m_pGlobals)
141 m_pGlobals->clear(); // this shouldn't be needed since this is a one time parser
142
143 m_pBuffer = pBuffer;
144 m_ptr = pBuffer;
145
146 if(!pBuffer)
147 {
148 error(nullptr, __tr2qs_ctx("Empty script", "kvs"));
149 return nullptr;
150 }
151 return parseInstructionList();
152 }
153
parseAsExpression(const QChar * pBuffer,int iFlags)154 KviKvsTreeNodeInstruction * KviKvsParser::parseAsExpression(const QChar * pBuffer, int iFlags)
155 {
156 m_iFlags = iFlags;
157
158 m_bError = false;
159 if(m_pGlobals)
160 m_pGlobals->clear(); // this shouldn't be needed since this is a one time parser
161
162 m_pBuffer = pBuffer;
163 m_ptr = pBuffer;
164
165 if(!pBuffer)
166 {
167 error(nullptr, __tr2qs_ctx("Empty script", "kvs"));
168 return nullptr;
169 }
170
171 KviKvsTreeNodeExpression * expr = parseExpression(0);
172 if(!expr)
173 return nullptr;
174 return new KviKvsTreeNodeExpressionReturn(pBuffer, expr);
175 }
176
parseAsParameter(const QChar * pBuffer,int iFlags)177 KviKvsTreeNodeInstruction * KviKvsParser::parseAsParameter(const QChar * pBuffer, int iFlags)
178 {
179 m_iFlags = iFlags;
180
181 m_bError = false;
182 if(m_pGlobals)
183 m_pGlobals->clear(); // this shouldn't be needed since this is a one time parser
184
185 m_pBuffer = pBuffer;
186 m_ptr = pBuffer;
187
188 if(!pBuffer)
189 {
190 error(nullptr, __tr2qs_ctx("Empty script", "kvs"));
191 return nullptr;
192 }
193
194 KviKvsTreeNodeDataList * l = parseCommandParameterList();
195 if(!l)
196 return nullptr;
197
198 return new KviKvsTreeNodeParameterReturn(pBuffer, l);
199 }
200
201 //
202 // THE REAL KVS
203 //
204 // <script> ::= <instruction list> '\0'
205 //
206 // <instruction list> ::= <instruction> [ <instruction list> ]
207 // <instruction> ::= <instruction block> | <command> | <operation> | <comment>
208 // <instruction block> ::= '{' <instruction list> '}'
209 //
210 // <comment> ::= <bash style line comment> | <c++ style comment> | <c style comment>
211 // <bash style comment> ::='#' <any char not including newline or null> <newline or null>
212 // <c++ style comment> ::= '//' <any char not including newline or null> <newline or null>
213 // <c style comment> ::= '/*' <any char not including null or the sequence */> '*/'
214 //
215 // <command> ::= <simple command> | <callback command> | <control command>
216 // <simple command> ::= <core command> | <module command> | <alias command>
217 // <core command> ::= <command identifier> <switch list> <command parameter list> <command terminator>
218 // <switch list> ::= '-'<switch body> [<switch list>]
219 // <command parameter list> ::= <command parameter>[<space><command parameter>]
220 // <command parameter> ::= <command parameter part>[<command parameter>]
221 // <command parameter part> ::= <command literal parameter> | <string parameter> | <data evaluation>
222 // <command literal parameter> ::= <anything except space, null, newline, ;, ", $ or %>
223 // <string parameter> ::= '"'<string parameter body>'"'
224 // <string parameter body> ::= <anything except '"' or null>...
225 // <command terminator> ::= ';' | '\n' | '\0'
226 //
227
228 //
229 // <data> ::=
230 // <data_reference> ::= <function_call> | <structured_data>
231 // <structured_data> ::= <data_structure> | <variable> | <pointer_data>
232 // <data_structure> ::= <array> | <hash>
233 // <array> ::= '%'<identifier>'[]'
234 // <hash> ::= '%'<identifier>'{}'
235 // <variable> ::= '%'<identifier> | <array_element> | <hash_element>
236 // <array_element> ::= '%'<identifier>'['<expression>']'
237 // <hash_element> ::= '%'<identifier>'{'<key>'}'
238 // <function_call> ::= <simple_function_call> | <pointer_function_call>
239 // <simple_function_call> ::= '$'<identifier>'('<function_parameter_list>')'
240 // <pointer_function_call> ::= <variable>'->'<function_call> | <simple_function_call>'->'<function_call>
241 // <pointer_data> ::= <variable>'->'<data>' | <simple_function_call>'->'<data>
242 //
243
244 //
245 // This must evaluate SCOPE OBJECT operators!
246 // thus...first evaluate the <data> or <simple_function_call>
247 // If it was <simple_function_call> or <data> evaluation results in returning a <variable>
248 // then check if there is a scope operator
249 // if there is then take it as the top of the tree, the <variable> or <simple_function_call>
250 // go at the left param of the scope operator and re-evaluate the right side <data> or <simple_function_call>
251 //
252 //
253
254 /*
255 @doc: kvs_introduction
256 @type:
257 language
258 @keyterms:
259 kvs, compilation
260 @title:
261 KVIrc scripting language introduction
262 @short:
263 KVS scripting language introduction
264 @body:
265 [b]KVS[/b] is the [b]KV[/b]Irc [b]S[/b]cripting language.
266 It was inspired by C++, sh, Perl, PHP and mIRC scripting language implementations.
267 It is a compromise between flexibility and speed, a [i]workaround[/i] for many intrinsic
268 problems of an IRC-oriented scripting language.
269 [br]
270 KVS is semi-interpreted: the execution is done in two main stages.
271 The first stage is the compilation where a syntactic tree is built.
272 The second stage is the real execution and is performed by visiting the tree
273 in the proper order. The syntactic trees are cached in memory so
274 the next executions can jump directly into the second stage.
275 This two-stage approach has been introduced in version 3.0.0, the previous
276 versions of the language used a single-stage on-the-fly interpreter.
277 [br]
278 KVS allows you to:
279 [ul]
280 [li]Implement automated reactions to the events generated by an IRC network[/li]
281 [li]Add new complex commands[/li]
282 [li]Add interface elements like popups, toolbars, buttons...[/li]
283 [li]Add advanced interface elements like complete dialogs or even widgets integrated in KVIrc[/li]
284 [/ul]
285 KVS contains all the common constructs of structured programming.
286 You will find almost all the C control commands, sh/Perl-like variables, arrays and and functions.
287 There are also some object-oriented characteristics: you will find C++ like
288 objects with constructors, destructors and class inheritance.
289 There are also more exotic concepts like the signal-slots inter-object communication.
290 Obviously you will also find most of the RFC1459 IRC commands and
291 other tools to [i]play[/i] with an IRC connection.[br]
292 I'll try to explain the language by using examples instead of strict syntactic rules.[br]
293 I have even tried to write the rules... take a look [doc:syntactic_rules]here[/doc][br][br]
294 And please... forgive me for my [i]fantastic[/i] English :)
295 Szymon Stefanek
296 */
297
298 /*
299 @doc: kvs_basicconcepts
300 @type:
301 language
302 @keyterms:
303 script
304 @title:
305 KVS basic concepts
306 @short:
307 KVS basic concepts
308 @body:
309 [big]Scripts[/big]
310 You use KVS to implement [b]scripts[/b].
311 A script is basically a finite list of KVS instructions.
312 When you type a command in the KVIrc input window you in fact
313 execute a small one-line script. You can store
314 longer scripts in KVIrc memory and execute them at later time.
315 Scripts can be also read from external files by the means of the
316 [cmd]parse[/cmd] command.
317 [br]
318 There is an issue with the word [i]script[/i] that is worth clearing here.
319 It is common usage to call [i]script[/i] a thing that is something more
320 that a finite list of (some scripting language) instructions.
321 In fact a set of scripts, documentation files, graphics or other multimedia
322 files and sometimes executable binaries is still called a [i]script[/i]... just like
323 the [i]PrincoScript[/i] or [i]dynamirc[/i] (tough this last one should be categorized as [i]malware[/i] instead).
324 In KVIrc such a collection of items is called [i]addon[/i], but be prepared
325 for both usages of the word in this documentation and around the web.[br]
326 [doc:kvs_addons]Learn more about KVS addons[/doc].
327 [big]Hello world![/big]
328 This documentation contains a lot of script examples.
329 They will appear like the following block of code:
330 [example]
331 [cmd]echo[/cmd] Hello world!
332 [/example]
333 The best way to experiment is to execute the scripts from an external file.
334 Try to copy & paste the example above to a file and save it in
335 a known place. Then in the command input window type
336 [example]
337 [b]/[/b][cmd]parse[/cmd] <filename>
338 [/example]
339 where <filename> stands for the name of the file you just have saved.
340 Some simple examples (like the one above) can be also typed
341 directly in the command input window.
342 You must remember that the command input window needs
343 a leading slash ('/') character to recognize a script.
344 The command input window can also be put in multi-line mode by clicking
345 on the button on the right.
346 Another alternative for testing scripts is the code tester window.
347 You can access it by selecting [i]New code tester[/i] from the Scripting menu
348 at the top of the KVIrc window. You will soon have the opportunity to
349 experiment with all the methods. Read on.
350 [big]Basic syntax[/big]
351 A script contains a list of instructions separated by newlines or ';' characters.
352 Placing an instruction per line does not require a terminating character,
353 placing more instructions in a single line require them to be separated by ';'.
354 The most common instructions in KVS are [b]commands[/b]. A command is basically
355 a keyword followed by a list of space separator parameters.
356 The simplest (and the most useful) command in KVS is [cmd]echo[/cmd]; it prints
357 all its parameters to a KVIrc window.[br]
358 The following is an example of a valid script that uses only [cmd]echo[/cmd] commands.
359 [example]
360 echo "This is the first line"
361 ECHO This is the second line;
362 echo "This is the third line"; echo This is still on the third line;
363 eChO "This is the fourth line"; Echo "This is still on the fourth line"
364 [/example]
365 You have probably noticed that the terminating ';' character is optional
366 when the command is the last in a line.
367 The commands are [b]case insensitive[/b]; 'echo' is equivalent to 'Echo',
368 to 'ECHO' and to 'eChO'. In fact, most of KVS is case insensitive.
369 (There are obvious unavoidable exceptions for this rule; for example,
370 on UNIX systems file names are case sensitive and this must be
371 also reflected in KVS).
372 Another interesting thing is that when you execute the script you
373 don't see the enclosing quotes around the printed text: more about this
374 in the following sections.
375 [note]
376 Cryptic note (you may skip it for now):[br]
377 Yes, the command terminator is a problem for those that want to use ';)' at the end
378 of IRC commands like [cmd]msg[/cmd]. It is almost unavoidable (read: the cost for
379 avoiding it is too high). Note that using '|' or any other character as command terminator
380 will [b]not[/b] solve the problem: if the terminator is too difficult to type it will annoy the
381 scripters (and me), if it is too easy then there will be always someone that wants to use it
382 at the end (or in the middle) of a command with the original meaning.
383 The solution is to escape the [b];[/b] character:
384 [example]
385 [cmd]echo[/cmd] You can do it now \;)
386 [/example]
387 [/note]
388 [big]Parameter processing[/big]
389 Most of the commands accept (and sometimes require) a list of parameters.
390 For example, the [cmd]join[/cmd] command (that is used to join an IRC channel)
391 accepts two parameters: the first one is the channel to join and the second is
392 the password. The simplified syntax for join is:
393 [example]
394 [cmd]join[/cmd] <channel> [password]
395 [/example]
396 The line above is an example of syntax specification. All the commands
397 are described by such syntax lines. [cmd]join[/cmd] is the command and it stands exactly
398 for the literal string [i]join[/i] typed in a script. <channel> is in angular parenthesis
399 and represents a mandatory parameter: you must substitute a real channel name in its place
400 otherwise the command will fail and KVIrc will probably complain too.
401 [password] is still a parameter but the square parentheses indicate that it is
402 optional: if you specify it, then it will be interpreted as the channel password,
403 if you don't then no password will be used.
404 [note]
405 The syntax is written in a simplified BNF. I say simplified because it is not
406 totally strict around the KVIrc documentation. I just prefer the syntax to be
407 clear and easy to read instead of being formally perfect.
408 [/note]
409 You can finally join a channel by writing:
410 [example]
411 [cmd]join[/cmd] #kvirc kvircrocks
412 [/example]
413 or, since #kvirc usually has no password, by writing:
414 [example]
415 [cmd]join[/cmd] #kvirc
416 [/example]
417 In the example above the optional parameter [password] is omitted.
418 [note]
419 In fact it is not really omitted: KVIrc interprets it as an empty string that later
420 means [i]do not send the password to the server[/i].
421 Empty strings are equivalent to omitted ones.
422 [/note]
423 [big]Parameters, spaces and quotes[/big]
424 From the examples above is obvious that KVS command parameters are separated by spaces.
425 What is not totally obvious is that multiple spaces are allowed but KVIrc
426 will automatically reduce them to exactly one (just like HTML parsers or the shell
427 interpreters do). This is an useful behaviour in an IRC client since spaces usually
428 carry no information and in text oriented protocols make the parsing really harder (:D).
429 [br]
430 The spaces are simplified in normal processing but there are ways to force KVIrc
431 to interpret the spaces just as they are.
432 The first method are the quotation marks: all the spaces enclosed in quotation marks
433 will be preserved.
434 [example]
435 [cmd]echo[/cmd] This text will have spaces simplified
436 [cmd]echo[/cmd] But "this one not"
437 [/example]
438 The first example will print out with spaces simplified but the second not.
439 The quotes are also a nice trick to embed spaces into a single parameter that
440 would be obviously split in two or more.
441 [example]
442 [cmd]echo[/cmd] Parameter1 Parameter2 "Parameter 3 ( with spaces )" Parameter4
443 [/example]
444 By running the examples above you may have noticed that the spaces are preserved but the
445 quotes are then stripped! Yes, this is another tricky behaviour. But don't be afraid:
446 it is really easier to use than to explain.
447 There is obviously a method to preserve the quotes too and it is also another
448 method to preserve the spaces but that leads us to the next paragraph.
449 [big]Escape character[/big]
450 You may have already noticed that KVS treats some characters in a special way.
451 For example the double-quote characters can be used to enclose strings
452 and are stripped by the parser.
453 Another example of a special character is the command terminator (';'):
454 it has the [i]special[/i] meaning of terminating a command.
455 If you want to enclose a literal quote in your text, you need to [b]escape[/b] it.
456 Like in most other programming languages, the escaping character is the backslash ('\').
457 [example]
458 [cmd]echo[/cmd] You can smile this way too! \;)
459 [/example]
460 The above example will treat the ';' as a part of the parameters and print it.[br]
461 In some languages the action of [i]escaping[/i] a character is called [i]quoting[/i].
462 Although there is some confusion in this term, the meaning is to either use quotes
463 or to use the escape character to remove special meaning from some characters.
464 By quoting the spaces you can include them in a parameter, by escaping the quotes
465 you can include them in a command.
466 [example]
467 [cmd]echo[/cmd] "And he said \"Hello world!\""
468 [/example]
469 The example above will have the internal quotes preserved.
470 You can use the escape backslash to escape a newline:
471 [example]
472 [cmd]echo[/cmd] This text will be \
473 printed on a single line!
474 [/example]
475 After an escaped newline all the leading space and tab characters are skipped,
476 so you must include the needed spaces [b]before[/b] the escape character.
477 The previous example will be printed as:[br][br]
478 [i]This text will be printed on a single line[/i][br]
479 Another example:
480 [example]
481 [cmd]echo[/cmd] "The new KVIrc   \
482 IS OUT!"
483 [cmd]echo[/cmd] Check it out at http://www.kvi \
484 rc.net!
485 [/example]
486 This will be printed as:[br][br]
487 [i]
488 The new KVIrc IS OUT![br]
489 Check it out at http://www.kvirc.net!
490 [/i][br]
491 Finally, you can escape an escape character to include it literally in a parameter (and
492 this is really the end of these tricks :)
493 Later we will discover other common usages of the backslash escape, such
494 as preventing KVIrc from interpreting a literal percent character as a variable
495 or separating variable names from the text.
496 [big]Command switches[/big]
497 Many commands accept switch parameters.
498 [b]A switch modifies the behaviour of a command.[/b]
499 Any switch can optionally accept a parameter, that must
500 be specified after an equal ('=') sign.
501 [example]
502 [cmd]echo[/cmd] [b]-i = 2[/b] This text uses a specific color scheme
503 [/example]
504 The -i switch (just for example) changes the attributes
505 and the icon of the printed text.
506 [b]The switch must be specified immediately after the command keyword.[/b]
507 [example]
508 [cmd]echo[/cmd] This -i = 2 will obviously not work...
509 [/example]
510 If you want to start the first parameter of a command (that is not a switch)
511 with a literal '-' you must again escape it:
512 [example]
513 [cmd]echo[/cmd] \--- This text has three minus signs on the left
514 [/example]
515 or use the quotes:
516 [example]
517 [cmd]echo[/cmd] "--- This text has three minus signs on the left"
518 [/example]
519 [big]Command blocks[/big]
520 Commands can be 'grouped' in blocks by using the classic C++ braces.
521 Here is a single line example:
522 [example]
523 { [cmd]echo[/cmd] First command; [cmd]echo[/cmd] Second command; } [cmd]echo[/cmd] Third command
524 [/example]
525 Multi line example:
526 [example]
527 {
528 [cmd]echo[/cmd] First command
529 [cmd]echo[/cmd] Second command
530 }
531 [cmd]echo[/cmd] Third command
532 [/example]
533 [note]
534 Reminder: copy the example above to a text file
535 and then use /[cmd]parse[/cmd] <filename>
536 [/note]
537 In this case the command block has no special meaning
538 other than making the code more readable, but command blocks
539 will be useful later (see [cmd]if[/cmd],[cmd]while[/cmd]...).
540 [note]
541 Unlike in C or C++, the braces do [b]not[/b] automatically define a variable scope
542 (with few exceptions to this rule ... just to complicate the things a bit more).
543 You will recall this last assertion later, when reading about [doc:data_structures]data structures[/doc].
544 [/note]
545 [big]Comments[/big]
546 [p]
547 KVIrc supports comments in command sequences.[br]
548 A comment starts with the character '#' and terminates with a newline.
549 You can start a comment anywhere a command can start.
550 [example]
551 # This is a comment, it occupies the whole line
552 [cmd]echo[/cmd] After the comment!; # This is an end-line comment
553 [/example]
554 You can't escape newline characters in this case.
555 (or better: escape characters have no meaning in comments...
556 maybe one day I'll implement it).[br]
557 Starting from version 3.0.0, KVIrc supports also C++ single line and C multi-line comments.[br]
558 A C++ comment starts with two slashes [b]//[/b] and terminates with a newline.
559 A multi-line C comment starts with [b]/ *[/b] and ends at the first [b]* /[/b] encountered.
560 Since KVIrc has no pre-processor, the C/C++ comments usually can't be placed in the middle of a command:
561 they must start where a command would start and end before the begin of another command.
562 [big]Indentation[/big]
563 You [b]should[/b] use spaces or [b]tabs[/b] to [b]indent[/b] your code. Note that the [b]should[/b]
564 word is written in bold characters: I mean that you really should indent your code.
565 Indenting helps both you (the script writer) and the reader (any other user that will
566 read your script). A good indenting practice is the first step to become a great programmer :)
567 [note]
568 Please note that the command parameters should be separated by
569 space characters (ASCII 32). Tabs are not granted to work as parameter separators.[br]
570 [/note]
571 [example]
572 {
573 <tab>[cmd]echo[/cmd] Indented command
574 <tab>{
575 <tab><tab># Comment
576 <tab><tab>[cmd]echo[/cmd] Really Really long indented \
577 <tab><tab><tab>command
578 <tab>}
579 }
580 [/example]
581 Tabs behave better than spaces as indentation characters since other users can
582 adjust the tab size to match their taste. I personally prefer 4 character tabs
583 while most text/code editors usually come with 8 characters as default.
584 [big]And now?[/big]
585 [br]
586 You're now ready to really start experimenting with KVS. You can take
587 a look at the [doc:commands]command index[/doc] and start trying to use them
588 while keeping in mind the rules described in this document.
589 The next suggested lecture is the documentation about [doc:kvs_aliasesandfunctions]the aliases and the functions[/doc].
590 Have fun :)
591 [br]
592 */
593
594 /*
595 @doc: kvs_aliasesandfunctions
596 @type:
597 language
598 @keyterms:
599 aliases, functions
600 @title:
601 KVS Functions and aliases
602 @short:
603 KVS Functions and aliases
604 @body:
605 [big]Introduction[/big]
606 Since you're here, you should already have read about the [doc:kvs_basicconcepts]KVS basic concepts[/doc]
607 and have visited the [doc:commands]command index[/doc]. If you feel ready to take the next step
608 then read on.
609 [big]Functions[/big]
610 KVS has many internal [doc]functions[/doc] that can be used as command parameters.[br]
611 [b]All the function names start with a literal [b]$[/b] character.[/b]
612 [example]
613 [cmd]echo[/cmd] This window caption is [fnc]$window.caption[/fnc]
614 [/example]
615 The [fnc]$window.caption[/fnc] [doc:functions]function[/doc]
616 is evaluated before the command executes,
617 and it is changed into the current window caption text.
618 The [doc]functions[/doc] can be used also as switch parameters.
619 [example]
620 [cmd]echo[/cmd] -w = [fnc]$window[/fnc] This text will be surely \
621 printed in the current window
622 [/example]
623 The -w switch allows to redirect the echo text to a specified window --- in this
624 case the one that you are typing in.[br]
625 [i](Surprise: in this case the -w switch is useless,
626 since echo prints text to the current window by default...
627 but it will work correctly. :)[/i]
628 [br]
629 Normal function names can be made of [i]anycase[/i] letters, digits and underscores,
630 with the restriction that the first character is not a digit.[br]
631 Some kind of functions can contain a dot ([b].[/b]) character inside the name
632 and these are assumed to be module references (see [doc:modules]the modules documentation[/doc]).[br]
633 By now we have seen only simple functions, but there's more...[br]
634 The functions can accept parameters; the general syntax for a function call is:[br]
635 [b]$<function name>['('<parameter_list>')'][/b][br]
636 where <parameter_list> is a list of comma separated parameters,
637 eventually empty.
638 [example]
639 [cmd]echo[/cmd] The word 'neural' is [fnc]$str.len[/fnc](neural) characters long
640 [/example]
641 The function [fnc]$str.len[/fnc] accepts a single parameter and returns the
642 length in characters of the parameter string. The returned value is always
643 a string: in this case it can be also interpreted as a number.[br]
644 When passing an empty list you can avoid the parenthesis.
645 (And you have found the [i]simple[/i] functions shown above).
646 So the following two calls are equal:
647 [example]
648 [cmd]echo[/cmd] [fnc]$window.caption[/fnc]
649 [cmd]echo[/cmd] [fnc]$window.caption()[/fnc]
650 [/example]
651 If you want to pass an [i]empty[/i] string as the first parameter you have to use
652 the following syntax:
653 [example]
654 [cmd]echo[/cmd] [fnc]$str.len[/fnc]("")
655 [/example]
656 Obviously a function is valid as a function parameter.
657 [example]
658 [cmd]echo[/cmd] [fnc]$str.len[/fnc]([fnc]$window.caption[/fnc])
659 [/example]
660 If you want to place a literal '(' or ')' in the function parameters
661 you must escape it.
662 A special case for when you want to use 'matching' parentheses:
663 an opened '(' corresponds to a closed ')'.
664 In this case you can omit the 'escape' character.
665 [example]
666 [cmd]echo[/cmd] The length of '(a+b)' is : [fnc]$str.len[/fnc]( (a+b) )
667 [/example]
668 This is useful for algebraic and boolean expressions, like the ones
669 accepted by the special function $() (see next paragraphs).
670 [big]Aliases[/big]
671 An alias is a user defined command. It can be used to rename the builtin KVIrc commands or functions,
672 to automate complex tasks or as a means for structured programming.
673 Aliases can be created or destroyed by using the scriptcenter (graphic interface)
674 or from the commandline (or script) by using the [cmd]alias[/cmd] command.
675 Once created, an alias remains stored permanently in the KVIrc configuration files
676 until it is explicitly deleted.
677 A couple of examples will make the things clear.
678 join is a really commonly used command - it might be a good idea to rename it to
679 simply [b]j[/b] .. just to type it faster.
680 Nothing easier in KVIrc: just try this commandline:
681 [example]
682 [cmd]alias[/cmd](j){ [cmd]join[/cmd] $0-; };
683 [/example]
684 This will create the alias [b]j[/b]. From this moment you can use /j as if it was a normal command.
685 [example]
686 j #kvirc
687 [/example]
688 You may have noticed the strange $0- function in the alias body - it stands for
689 [i]all parameters passed to the alias[/i]. This means that when you call
690 [example]
691 j #kvirc testpassword
692 [/example]
693 then both the parameters (#kvirc and testpassword) are passed to the join command.
694 The $N functions are special functions that return the positional parameters passed
695 to the current script context. In an alias the script context is the script body and
696 it is the alias caller that generates the parameters.
697 $N (where N is a digit) returns the (N-1)-th positional parameter passed by the caller.
698 It returns the parameter numbered N-1 and not N since the parameters are indexed starting
699 from zero ($0 is the first parameter!).
700 $N-M returns the parameters from (N-1)-th to the (M-1)-th (a parameter range) and $N- returns
701 all the parameters from (N-1)-th to the last one. In the example above $0- stands for
702 all the parameters starting from the first one.
703 [br]
704 To remove an alias, use the alias command again with an empty body:
705 [example]
706 [cmd]alias[/cmd](j){}
707 [/example]
708 This will remove the alias [b]j[/b] defined above.
709 [br]
710 A common task in channel management is the kick & ban action.
711 You first ban a user from the channel and then eventually kick him
712 (obviously assuming that he is actually on the channel).
713 This involves using two commands - ban and then kick.
714 It could be a nice idea to have a single [b]kb[/b] command to perform this action.
715 Well...easy:
716 [example]
717 [cmd]alias[/cmd](kb){ [cmd]ban[/cmd] $0; [cmd]kick[/cmd] $0-; };
718 [/example]
719 This adds the [b]kb[/b] alias - it can be called as a normal command:
720 [example]
721 kb spammer You're not welcome here!
722 [/example]
723 This will first execute [i]ban spammer[/i] and then [i]kick spammer with; You're not welcome here[/i].
724 Our kb is a really simple example... it doesn't check for the validity of the parameters -
725 the server will warn us if the parameters passed to kb were empty.
726 [br]
727 The alias can be modified at any time by reusing the alias command.
728 Let's make our [b]kb[/b] a bit more intelligent and add a check for the parameters.
729 TIP: It is a good idea to write the following examples in a text file and then use /parse <filename> to execute it.
730 [example]
731 [cmd]alias[/cmd](kb)
732 {
733 [cmd]if[/cmd]("$0" == "")
734 {
735 [cmd]echo[/cmd] "Usage: /kb <nickname> <kick reason>"
736 [cmd]return[/cmd]
737 }
738 [cmd]ban[/cmd] $0
739 %reason = $1-
740 [cmd]if[/cmd]("%reason" == "")%reason = "You're not welcome here!"
741 [cmd]kick[/cmd] $0 %reason
742 }
743 [/example]
744 The example above will first check the validity of the <nickname> passed to kb -
745 if no nickname was passed, it will warn the user and stop.
746 The next step will be the [i]ban <nickname>[/i] call. Another enhancement is the [i]default reason[/i] -
747 we first assign the remaining parameters ($1- means [i]from $1 to the end[/i]) to a temporary variable,
748 and if the variable is empty, a default kick reason is assigned.
749 Finally the [i]kick <nickname> <reason>[/i] will be executed.
750 Get used to looking at the single command documentation pages, they will give
751 you the hints necessary to fully understand the above piece of code.
752 [br]
753 Aliases can be used as a means for structured programming.
754 In large scripts you will [b]surely[/b] have [i]common tasks[/i] to perform (like having
755 specially-colored output or calculating a value from a set of other values)...
756 Aliases are the way of writing the common tasks - they are equivalent to the [i]procedures[/i]
757 or [i]functions[/i] in many high-level programming languages.
758 The alias as a procedure (subroutine or sub-task) has been shown in the [b]kb[/b] example above -
759 it might be commonly called from more complex scripts or other aliases in the case that a
760 kick & ban action is needed.
761 [br]
762 Aliases can be used also as functions.
763 Assume that you often need to calculate the sum of three numbers - a function-alias is the way.
764 [example]
765 [cmd]alias[/cmd](sum3){ [cmd]return[/cmd] $($0 + $1 + $2); };
766 [/example]
767 This will add the alias [i]sum3[/i] and make it available both as a command and a function.
768 The [i]return[/i] command sets the return value of a sequence of commands
769 (an alias is a sequence of commands... remember?) and terminates the execution (by returning
770 the control to the caller).
771 So return $($0 + $1 + $2); will set the return value of the alias to the value
772 computed by $($0 + $1 + $2), which is the sum of the first three parameters passed.
773 You will then use it in the following way:
774 [example]
775 ...
776 %myfirstsum = $sum3(%somevalue,%someothervalue,4)
777 %anothersum = $sum3(12,%somevalue,%anothervalue)
778 ...
779 [/example]
780 Oops! I've used some variables without actually explaining them, please forgive me and read on.
781 This example is again really simple, but you might have more complex function-aliases.
782 The function-aliases are also normal aliases... you can use it as a command:
783 [example]
784 /sum3 1 2 3
785 [/example]
786 The above is a perfectly valid call, however there will be no visible results
787 (because a command call implies ignoring the return value.
788 In fact there is no difference at all between function-aliases and normal-aliases -
789 the caller makes the difference. By calling an alias as a command, the return value
790 just disappears into hyperspace, however if you call an alias as a function, the return value
791 is propagated (and in fact [i]used[/i]).
792 There are some [i]nice[/i] exceptions to this rule...but you don't need to care about it, for now.
793 If return is not called inside an alias body, the return value will be set to [fnc]$null[/fnc].
794 [br]
795 Aliases can accept switches just like any other command. The [fnc]$sw[/fnc] is there
796 exactly for that purpose. Check it out.
797 [big]Special functions[/big]
798 We have already seen the positional parameter functions.
799 The functions of type [b]$N[-[M]][/b] (where N and M are positive
800 numbers starting from 0 and N < M) evaluate to the sequence of
801 [b]positional parameters[/b] from Nth to Mth.[br]
802 If M is omitted, the function evaluate to the sequence of [b]positional
803 parameters[/b] from Nth to the last one. If the whole -M block is omitted
804 the function evaluate to the Nth positional parameter.
805 We will discover more on the [b]positional parameters[/b] when talking
806 of aliases and events.
807 [example]
808 $0 evaluates to the 1st positional parameter
809 $0-4 evaluates to the parameters from first to 5th
810 $41- evaluates to the parameters from 41st to the last available
811 [/example]
812 The function [b]$#[/b] evaluates to the number of positional parameters available.
813 The [b]positional parameter[/b] functions do not accept parameters.[br]
814 The special function [b]$(<expression>)[/b], called the
815 [i][doc:expressioneval]Expression evaluation identifier[/doc][/i], returns the result
816 of the evaluation of the <expression>. In previous versions of KVIrc this
817 function was called $calc().
818 [example]
819 [cmd]echo[/cmd] $(2 + (3 ^ 7) <= 1 * (3 && 2))
820 [/example]
821 The special function [b]${<command sequence>}[/b] evaluates to the
822 return value of the <command sequence>.[br]
823 The special function [b]$$[/b] evaluates to the current object ID,
824 but it is too early to explain it here...
825 */
826
827 /*
828 @doc: command_rebinding
829 @type:
830 language
831 @keyterms:
832 Rebinding commands to another window
833 @title:
834 Standard rebinding switch
835 @short:
836 Standard rebinding switch
837 @syntax:
838 <command> -r=<window_id> <parameters>
839 @body:
840 The -r switch is standardized along all the commands. It rebinds a command
841 to the windows specified by <window_id>. It is useful to launch commands
842 in windows that are not the current one. For example, you might want to
843 say something in a specific channel while processing an event bound to
844 a console, or say something in all the channels bound to the current IRC context.
845 The examples below will make everything clear.
846 @examples:
847 [example]
848 [comment]# Run a command in the console of the current IRC context[/comment]
849 [cmd]echo[/cmd] -r=$console This command is executed in the console ($window.caption)
850 [comment]# Say something to all the channels of the current IRC context[/comment]
851 [cmd]foreach[/cmd](%w,[fnc]$window.list[/fnc](channel))[cmd]say[/cmd] -r=%w Hi ppl on [fnc]$chan.name[/fnc]
852 [/example]
853 */
854
855 /*
856 @doc: window_naming_conventions
857 @type:
858 language
859 @title:
860 Window naming conventions
861 @keyterms:
862 IRC context, window ID, frame window, connection ID
863 @short:
864 KVIrc window structure and the window naming conventions
865 @body:
866 [big]Introduction[/big]
867 Starting from the release 3.0.0, KVIrc window structure has
868 grown in complexity. Older releases allowed one connection
869 per [i]frame window[/i] and thus had a dedicated command parser
870 for each connection. Finding a window in that scenario
871 was quite easy: it was enough to designate it by [i]name[/i]
872 (that was exactly the text displayed in the window caption).
873 It was sufficient to have an [i]unique[/i] name for ever window;
874 condition that was granted by the underlying IRC protocol
875 and by the KVIrc core design.[br]
876 In this version, the unique window names are impossible to be granted.
877 [big]Scenario[/big]
878 The command parser is now [i]global[/i] to the application.
879 There can be two or more consoles in each frame and the user
880 is able to join the same channel with two different nicknames
881 using two separate connections.
882 [ul]
883 [li]
884 Application (Unique command parser)
885 [ul]
886 [li]
887 Frame X
888 [ul]
889 [li]
890 Console M (IRC context)
891 [ul]
892 [li]Channel windows[/li]
893 [li]Query windows[/li]
894 [li]Other connection related windows[/li]
895 [/ul]
896 [/li]
897 [li]
898 Console N (IRC context)
899 [ul]
900 [li]Channel windows[/li]
901 [li]Query windows[/li]
902 [li]Other connection related windows[/li]
903 [/ul]
904 [/li]
905 [li]
906 Other windows
907 [/li]
908 [li]
909 ...
910 [/li]
911 [/ul]
912 [/li]
913 [li]
914 Frame Y
915 [ul]
916 [li]
917 Console O (IRC context)
918 [ul]
919 [li]Channel windows[/li]
920 [li]Query windows[/li]
921 [li]Other connection related windows[/li]
922 [/ul]
923 [/li]
924 [li]
925 Console P (IRC context)
926 [ul]
927 [li]Channel windows[/li]
928 [li]Query windows[/li]
929 [li]Other connection related windows[/li]
930 [/ul]
931 [/li]
932 [li]
933 Other windows
934 [/li]
935 [li]
936 ...
937 [/li]
938 [/ul]
939 [/li]
940 [li]
941 ...
942 [/li]
943 [/ul]
944 [/li]
945 [/ul]
946 [br]
947 A naming convention has become necessary to resolve ambiguities.
948 [big]Basic assumptions[/big]
949 Every KVIrc window has four main properties:[br]
950 -[b]an unique numeric identifier[/b][br]
951 -[b]the logical name[/b][br]
952 -[b]the type identifier[/b][br]
953 -[b]the caption text[/b][br]
954 The [b]numeric identifier[/b] is unique to the whole application,
955 and is the one returned by the [fnc]$window[/fnc] function.[br]
956 The identifier is assigned by KVIrc when the window is created
957 and is not changed until the window is destroyed.
958 This identifier will be referred as [b]window ID[/b].[br]
959 The [b]logical name[/b] is a property of some kind of windows.
960 It usually corresponds to the first part of the window caption.
961 For example, for channel windows it is the channel name, for
962 queries it is the list of the targets. For some other windows
963 the logical name corresponds to the caption text. This will be discussed later.[br]
964 The [b]type identifier[/b] describes the properties of a certain window.
965 For channel windows the type identifier is [i]channel[/i], for query windows is [i]query[/i],
966 for console windows it is [i]console[/i], etc.
967 [big]IRC Contexts[/big]
968 The KVIrc frame windows are numbered starting from 0 and named
969 [i]frame_<number>[/i]. Each frame can contain an unlimited number of consoles.[br]
970 Each console is bound to an [b]IRC context[/b]. (The part [i]is bound to[/i] could
971 be substituted by [i]defines[/i] or [i]is contained in[/i]).[br]
972 [i]An [b]IRC context[/b] is a set of resources that can deal with a single
973 IRC connection.[/i][br]
974 The association between an [b]IRC context[/b]
975 and a console is bijective: each [b]IRC context[/b] is associated
976 to a single console window.[br]
977 An [b]IRC context[/b] can be in connected or not-connected state.
978 When in connected state, it contains a set of windows beside the console:
979 mainly channels and query windows.
980 The channels and query windows can exist [b]only[/b] if the associated
981 [b]IRC context[/b] exists.[br]
982 Channels and queries have unique names inside a connection so
983 there is no way to confuse it. (Theoretically there can
984 be more than one query window with the same name, but in fact
985 all the windows refer to the same target so they are instances
986 of the same resource).
987 All this creates a sort of namespace: the channels and queries can be identified
988 as [i]bound[/i] to a specific [b]IRC context[/b].[br]
989 An [b]IRC context[/b] can [i]contain[/i] other windows, such as the [i]sockets[/i]
990 window or the [i]list[/i] window. KVIrc takes care of making them
991 unique inside the [b]IRC context[/b] namespace.[br]
992 Each [b]IRC context[/b] has its own unique [b]IRC context ID[/b] (see [fnc]$context[/fnc]).[br]
993 Since to a single [b]IRC context[/b] may correspond only a single IRC connection,
994 when in connected state, the [b]IRC context[/b] may be referred also as [b]connection[/b]
995 or [b]connection context[/b], and the associated [b]IRC context Id[/b] can be
996 referred as [b]connection ID[/b] or [b]connection context ID[/b].[br]
997 There are classes of windows that are not bound to any [b]IRC context[/b]:
998 this includes user created windows, DCC windows, browsers etc.[br]
999 KVIrc will try to keep that windows with unique logical names.
1000 [big]How to identify a window[/big]
1001 So what we have until now is:
1002 [ul]
1003 [li]Each window has its own unique [b]window ID[/b]: we
1004 will refer windows always using this identifier.[/li]
1005 [li]Each window has a set of properties including:
1006 window type, logical name.[/li]
1007 [li]Subsets of windows are bound to a single [b]IRC context[/b][/li]
1008 [/ul]
1009 The simplest (but also the less significant) method of looking for
1010 a window is to finding it by caption.[br]
1011 The [fnc]$window[/fnc] function finds the first KVIrc window matching
1012 the [i]caption text[/i] and returns its [b]window ID[/b].[br]
1013 This method will likely fail when there are more windows with the same
1014 caption text; for this reason several specific functions
1015 have been added to allow finding the correct window.[br]
1016 The [fnc]$console[/fnc] finds a console window bound to a specified
1017 [b]IRC context[/b].[br]
1018 The [fnc]$channel[/fnc] finds a channel window matching the specified
1019 name and bound to a specified [b]IRC context[/b].[br]
1020 The [fnc]$query[/fnc] finds a query window that has a specified target
1021 and is bound to a specified [b]IRC context[/b].[br]
1022 */
1023
1024 /*
1025 @doc: connection_dependent_commands
1026 @type:
1027 language
1028 @title:
1029 Connection dependent commands
1030 @keyterms:
1031 IRC context, connection dependent commands
1032 @body:
1033 Many KVIrc commands are connection dependent:
1034 you need an IRC connection to successfully execute them;
1035 usually because some data needs to be sent to the server.
1036 This includes commands like [cmd]whois[/cmd],[cmd]raw[/cmd],[cmd]query[/cmd],
1037 [cmd]msg[/cmd],[cmd]notice[/cmd],[cmd]op[/cmd],[cmd]ctcp[/cmd]...[br]
1038 These commands must be executed in a window that is bound to a
1039 [b]connected [doc:window_naming_conventions]IRC context[/doc][/b].
1040 You will obviously get an error message if you try to use them in a window
1041 that has no associated IRC connection.[br]
1042 For instance: [cmd]whois[/cmd] will work only if you execute it
1043 in a console, channel or query window.[br]
1044 If you want to use these commands in a window that is not associated to
1045 any IRC context you may use the [doc:command_rebinding]standard -r switch[/doc].
1046 You can use the same switch to execute a command in an [b]IRC context[/b] that is
1047 not the current one.
1048 */
1049
1050 /*
1051 @doc: aliases
1052 @type:
1053 language
1054 @keyterms:
1055 aliases
1056 @title:
1057 Aliases
1058 @short:
1059 Aliases : user definable command sequences
1060 @body:
1061 An alias is a user defined command. It can be used to rename the builtin KVIrc commands or functions,
1062 to automate complex tasks or as structured programming mean.
1063 Aliases can be created or destroyed by using the scriptcenter (graphic interface)
1064 or from the commandline (or script) by using the [cmd]alias[/cmd] command.
1065 Once created, an alias remains stored permanently in the KVIrc configuration files
1066 until it is explicitly deleted.
1067 A couple of examples will make the things clear.
1068 join is a really commonly used command. It might be a good idea to rename it to
1069 simply [b]j[/b] .. just to type it faster.
1070 Nothing easier in KVIrc: just try this commandline:
1071 [example]
1072 [cmd]alias[/cmd](j){ [cmd]join[/cmd] $0-; };
1073 [/example]
1074 [br]
1075 This will create the alias [b]j[/b]. From this moment you can use /j as it was a normal command.
1076 [example]
1077 j #kvirc
1078 [/example]
1079 You may have notices the strange $0- function in the alias body: it stands for
1080 [i]all parameters passed to the alias[/i]. This means that when you call
1081 [example]
1082 j #kvirc testpassword
1083 [/example]
1084 then both the parameters (#kvirc and testpassword) are passed to the join command.
1085 The $N functions are special functions that return the positional parameters passed
1086 to the current script context. In an alias the script context is the script body and
1087 it is the alias caller that generates the parameters.
1088 $N (where N is a digit) returns the (N-1)-th positional parameter passed by the caller.
1089 It returns the parameter numbered N-1 and not N since the parameters are indexed starting
1090 from zero ($0 is the first parameter!).
1091 $N-M returns the parameters from (N-1)-th to the (M-1)-th (a parameter range) and $N- returns
1092 all the parameters from (N-1)-th to the last one. In the example above $0- stands for
1093 all the parameters starting from the first one.
1094 [br]
1095 To remove an alias use again the alias command with an empty body:
1096 [example]
1097 [cmd]alias[/cmd](j){}
1098 [/example]
1099 This will remove the alias [b]j[/b] defined above.
1100 [br]
1101 A common task in channel management is the kick & ban action.
1102 You first ban a user from the channel and then eventually kick him
1103 (obviously assuming that he is actually on the channel).
1104 This involves using two commands: ban and then kick.
1105 It could be a nice idea to have a single [b]kb[/b] command to perform this action.
1106 Well...easy:
1107 [example]
1108 [cmd]alias[/cmd](kb){ [cmd]ban[/cmd] $0; [cmd]kick[/cmd] $0-; };
1109 [/example]
1110 This adds the [b]kb[/b] alias: it can be called as a normal command:
1111 [example]
1112 kb spammer You're not welcome here!
1113 [/example]
1114 This will first execute [i]ban spammer[/i] and then [i]kick spammer with; You're not welcome here[/i].
1115 Our kb is a really simple example... it doesn't check for the validity of the parameters:
1116 the server will warn us if the parameters passed to kb were empty.
1117 [br]
1118 The alias can be modified at any time by re-using the alias command.
1119 Let's make our [b]kb[/b] a bit more intelligent and add a check for the parameters.
1120 TIP: It is a good idea to write the following examples in a text file and then use /parse <filename> to execute it.
1121 [example]
1122 [cmd]alias[/cmd](kb)
1123 {
1124 [cmd]if[/cmd]("$0" == "")
1125 {
1126 [cmd]echo[/cmd] "Usage: /kb <nickname> <kick reason>"
1127 [cmd]return[/cmd]
1128 }
1129 [cmd]ban[/cmd] $0
1130 %reason = $1-
1131 [cmd]if[/cmd]("%reason" == "")%reason = "You're not welcome here!"
1132 [cmd]kick[/cmd] $0 %reason
1133 }
1134 [/example]
1135 The example above will first check the validity of the <nickname> passed to kb:
1136 if no nickname was passed, it will warn the user and stop.
1137 The next step will be the [i]ban <nickname>[/i] call. Another enhancement is the [i]default reason[/i]:
1138 we first assign the remaining parameters ($1- means [i]from $1 to the end[/i]) to a temporary variable,
1139 if the variable is empty, a default kick reason is assigned.
1140 Finally the [i]kick <nickname> <reason>[/i] will be executed.
1141 Get used to looking at the single command documentation pages, they will give
1142 you the hints necessary to fully understand the above piece of code.
1143 [br]
1144 Aliases can be used as a mean for structured programming.
1145 In large scripts you will [b]surely[/b] have [i]common tasks[/i] to perform (like having specially
1146 colored output or calculating a value from a set of other values)...
1147 Aliases are the way of writing the common tasks: they are equivalent to the [i]procedures[/i]
1148 or [i]functions[/i] in many high-level programming languages.
1149 The alias as a procedure (subroutine or sub-task) has been shown in the [b]kb[/b] example above:
1150 it might be commonly called from more complex scripts or other aliases in case that a
1151 kick & ban action is needed.
1152 [br]
1153 The aliases can be used also as functions.
1154 Assume that you need really often to calculate the sum of three numbers: a function-alias is the way.
1155 [example]
1156 [cmd]alias[/cmd](sum3){ [cmd]return[/cmd] $($0 + $1 + $2); };
1157 [/example]
1158 This will add the alias [i]sum3[/i] and make it available both as a command and a function.
1159 The [i]return[/i] command sets the return value of a sequence of commands
1160 (an alias is a sequence of commands...remember?) and terminates the execution (by returning
1161 the control to the caller).
1162 So return $($0 + $1 + $2); will set the return value of the alias to the value
1163 computed by $($0 + $1 + $2) that actually is the sum of the first three parameters passed.
1164 You will then use it in the following way:
1165 [example]
1166 ...
1167 %myfirstsum = $sum3(%somevalue,%someothervalue,4)
1168 %anothersum = $sum3(12,%somevalue,%anothervalue)
1169 ...
1170 [/example]
1171 Oops! I've used some variables without actually explaining them, hehe... please forgive me and read on.
1172 This example is again really simple, but you might have more complex function-aliases.
1173 The function-aliases are also normal aliases... you can use it as a command:
1174 [example]
1175 /sum3 1 2 3
1176 [/example]
1177 Is a perfectly valid call... it's just that it will have no visible results
1178 (just because a command call implies ignoring the return value.
1179 In fact there is no difference al all between function-aliases and normal-aliases:
1180 the caller makes the difference: by calling an alias as a command the return value
1181 just disappears in hyperspace, by calling an alias as a function, the return value
1182 is propagated (and in fact [i]used[/i]).
1183 (There are some [i]nice[/i] exceptions to this rule... but you don't need to care about it, for now).
1184 If return is not called inside an alias body, the return value will be just a null value.
1185 [br]
1186 Aliases can accept switches just like any other command. The [fnc]$sw[/fnc] is there
1187 exactly for that purpose. Check it out.
1188 [br]
1189 */
1190
1191 /*
1192 @doc: kvs_addons
1193 @type:
1194 language
1195 @keyterms:
1196 addons, addon
1197 @title:
1198 The KVIrc addon system
1199 @short:
1200 Writing KVIrc addons
1201 @body:
1202 [big]Introduction[/big]
1203 [br]
1204 An addon is basically a set of KVS scripts, multimedia, documentation
1205 and accessory files that implement a KVIrc feature.
1206 It might be a simple automatic-away subsystem, a GUI newsticker or a complex
1207 file sharing service (commonly called [i]fserve[/i]). Addons are sometimes called [i]scripts[/i].
1208 In fact a KVIrc addon is usually made of more than one KVS script.[br][br]
1209 KVIrc has a builtin addon management system that allows the users to create,
1210 install, configure and uninstall features with a nice graphical interface.
1211 The management system allows the addons to have documentation integrated in
1212 the KVIrc help and to be translated in several languages.
1213 [big]Addon installation[/big]
1214 The addons are usually shipped in compressed archives (.kva). KVIrc will look
1215 for the installer file called [i]install.kvs[/i] and executes it when the user will
1216 ask for your addon to be installed. The install.kvs contains the code for the
1217 [b]registration[/b] of your addon and will [cmd]include[/cmd] all the other
1218 necessary source files.
1219 [big]The minimal addon[/big]
1220 [br]
1221 The smallest addon that you can write is the one that does nothing.
1222 [example]
1223 [cmd]addon.register[/cmd]("MyAddon", \
1224 "1.0.0", \
1225 "My First Addon", \
1226 "An addon that is really cool but does
1227 simply nothing", \
1228 "4.0.0", \
1229 "MyAddon_32.png")
1230 {
1231 }
1232 [/example]
1233 The code above does nothing but registers the [i]MyAddon[/i] addon.
1234 [br]
1235 The first parameter is the internal addon ID which can be used to identify
1236 your addon inside KVIrc. The ID must be unique: two addons that share the same
1237 name cannot be installed. The second parameter is the addon version. It should
1238 be expressed in the classic format [major].[minor].[pathlevel] or something
1239 really similar (in fact KVIrc just expects the version to be a string composed
1240 of numbers separated by dots).[br][br]
1241 The version is compared when an addon is installed and KVIrc complains if the user
1242 tries to downgrade an addon (that is to install a less recent version over a more recent one).[br]
1243 The third parameter is the visible name of your addon: it will be displayed to the user in the
1244 addon management dialog. It can contain the [fnc]$tr[/fnc] function so you
1245 can have it translated to several languages. The fourth parameter
1246 is a short description of the feature that the addon implements; it can
1247 contain the $tr() function too. The fifth parameter is the minimal KVIrc
1248 version required to run the addon. The sixth parameter is the icon to show in
1249 the manager: it has to be 32x32 pixel big. There are also some switches that
1250 can be used to fiddle a little bit more :)
1251 [br]
1252 The callback instruction that follows the registration command is the
1253 uninstallation code. KVIrc will invoke it when the user will ask for your
1254 addon to be uninstalled. Don't assume that your addon will be never
1255 uninstalled: sooner or later it will be. For example, when upgrading an addon
1256 KVIrc will first uninstall the existing version and after that install the new
1257 one. The uninstallation process is a very important requisite for any program
1258 (in any programming language). In the example above there is nothing to
1259 uninstall (yet) so the callback code is empty, but if you continue reading we
1260 will soon fill it.
1261 To uninstall all files which are not automatically spotted by the installation
1262 process, you need to write an alias which handles them.
1263 [example]
1264 alias(MyAddon::uninstall::uninstall)
1265 {
1266 ...
1267 }
1268 [/example]
1269 [big]A typical addon layout[/big]
1270 [br]
1271 As stated above, the addons are usually shipped in a compressed archive.
1272 Once uncompressed, the installer will check the directory tree containing the
1273 addon code and all the related files.
1274 In order to have uniformity the installer complains if the structure is not
1275 respected.
1276 [br]
1277 [pre]
1278 [b]name-version[/b]
1279 +- init.kvs
1280 +- [b]src[/b]
1281 | +- source1.kvs
1282 | +- source2.kvs
1283 | \- ...
1284 +- [b]locale[/b]
1285 | +- name_it.mo
1286 | +- name_de.mo
1287 | \- ...
1288 +- [b]config[/b]
1289 | +- config1.kvc
1290 | +- config2.kvc
1291 | \- ...
1292 +- [b]sound[/b]
1293 | +- audio1.wav
1294 | +- audio2.wav
1295 | \- ...
1296 +- [b]pics[/b]
1297 | +- pic1.png
1298 | +- pic2.png
1299 | \- ...
1300 +- [b]help[/b]
1301 +- [b]en[/b]
1302 | +- index.html
1303 | +- hints.html
1304 | \- ...
1305 +- [b]it[/b]
1306 +- index.html
1307 +- hints.html
1308 \- ...
1309 [/pre]
1310 The entries in [b]bold[/b] are directories while the other are files.
1311 Please note that you need all of these directories or the routine that
1312 automagically creates the installer will fail.
1313 [br]
1314 The toplevel directory should be named with your addon name and version.
1315 Use no spaces in the directory entries (this will make the things simpler for
1316 people that want to use your addon).
1317 [br]
1318 Hint: Remember that your addon is going to be installed on different platforms
1319 (at least Linux, macOS and Windows based).
1320 The poor windows' notepad has serious problems with reading text
1321 files that contain only linefeeds as line separators. Keep it in mind...
1322 [br]
1323 The [b]initialization script[/b] has to be named init.kvs and must contain all the
1324 routines to register your addon.
1325 [example]
1326 [comment]# Register classes[/comment]
1327 MyAddon::classes::register
1328
1329 [comment]# Initialize events[/comment]
1330 MyAddon::events::init
1331
1332 [comment]# Load configuration[/comment]
1333 MyAddon::config::load
1334
1335 [comment]# Setup popups[/comment]
1336 defpopup("MyAddon")
1337 {
1338 item($tr("Something","MyAddon"),110)
1339 {
1340 ...
1341 }
1342 }
1343
1344 [comment]# Set options[/comment]
1345 option boolAutoAcceptDccSend 1
1346 option boolShowMinimizedDebugWindow 1
1347 [/example]
1348 [/p]
1349 [p]
1350 The main [b]source directory[/b] for your addon have to be named [i]src[/i] and must
1351 contain the implementation of the feature(s) you're going to provide.
1352 File names should contain the namespace of the addon, the optional subnamespace
1353 and the name of the feature like $addonNS_$subNS_[$subNS_[...]]$name.kvs
1354 [example]
1355 [comment]# A class which handles a database[/comment]
1356 MyAddon_classes_database.kvs
1357
1358 [comment]# A class which handles the options of our addon in a GUI[/comment]
1359 MyAddon_classes_gui_options.kvs
1360
1361 [comment]# A script containing some logging functions[/comment]
1362 MyAddon_functions_logging.kvs
1363 [/example]
1364 [br]
1365 The [b]locale[/b] directory should contain the *.mo files for your translations.
1366 The localization process of a script is explained in [doc:localization]this document[/doc].
1367 Your *.mo filenames should be prefixed by your addon name.
1368 [br]
1369 The [b]configuration directory[/b] [i]config[/i] should contains only the files
1370 which store the configuration of your addon and must end with the .kvc
1371 extension.
1372 [br]
1373 The [b]pics[/b] and [b]sound[/b] (if relevant) directories should contain
1374 your multimedia files. I's a good idea to have your pics file in PNG format
1375 and sound files in WAV format.
1376 [br]
1377 The [b]help[/b] directory should contain subdirectories for each language
1378 your help files are written in. The languages dirs should be named
1379 with the language code also used for the translation files (like [i]en[/i], [i]it[/i]
1380 etc...).
1381 Please note that English is the default language and KVIrc will
1382 fallback to the [i]en[/i] subdirectory when no other language is found around...
1383 [big]Some examples[/big]
1384 The code below is just an example of how to write a useful initialization of
1385 your own addon. The name of the classes refer to the ones described above.
1386 [example]
1387 [comment]# Register the classes[/comment]
1388 alias(MyAddon::classes::register)
1389 {
1390 [comment]# Create an array with all the classes of our addon.[/comment]
1391 [comment]# In this way it's easy to add or remove classes to registering routine[/comment]
1392 %classes[] = [fnc]$array[/fnc]( \
1393 MyAddon::classes::database, \
1394 MyAddon::classes::gui::options, \
1395 ...
1396 )
1397
1398 [comment]# Scan the array and register the classes[/comment]
1399 for(%i=0; %i < [fnc]$length[/fnc](%classes[]); %i++)
1400 {
1401 if([fnc]$classDefined[/fnc]("%classes[%i]"))
1402 {
1403 [cmd]objects.killclass[/cmd] %classes[%i]
1404 }
1405 [cmd]eval[/cmd] %classes[%i]
1406 }
1407 }
1408
1409 [comment]# Initialize events[/comment]
1410 alias(MyAddon::events::init)
1411 {
1412 event(OnKVIrcStartup,"MyAddon")
1413 {
1414 ...
1415 [comment]# Load the catalogue (translation) file [i]myaddon[/i] from the path provided[/comment]
1416 [cmd]trload[/cmd] myaddon [fnc]$file.localdir[/fnc]("locale/MyAddon")
1417 MyAddon::config::load
1418 ...
1419 }
1420 event(OnChannelMessage,"MyAddon_something")
1421 {
1422 ...
1423 }
1424 }
1425
1426 [comment]# Load configuration[/comment]
1427 alias(MyAddon::config::load)
1428 {
1429 [comment]# If the class ConfHandler is not defined, register all classes we have[/comment]
1430 if(![fnc]$classDefined[/fnc](MyAddon::classes::ConfHandler))
1431 {
1432 MyAddon::classes::register
1433 }
1434
1435 [comment]# Sets some variables[/comment]
1436 %MyAddonConfig = [fnc]$new[/fnc](MyAddon::classes::ConfHandler)
1437 %MyAddonConfigPath = [fnc]$file.localdir[/fnc](config/scripts/MyAddon)
1438
1439 [comment]# Open the configuration file and sets the section general[/comment]
1440 %c = [fnc]$config.open[/fnc](%MyAddonConfigPath/MyAddon.kvc,"r")
1441 [cmd]config.setsection[/cmd] %c general
1442
1443 [comment]# Store the value of the key "Key" in the global variable %Key[/comment]
1444 %Key = [fnc]$config.read[/fnc](%c,"Key",2)
1445 ...
1446 }
1447 [/example]
1448 [big]The help and configuration callbacks[/big]
1449 [br]
1450 Each addon can have a help and a configuration callback. These are set
1451 respectively by [cmd]addon.sethelpcallback[/cmd] and [cmd]addon.setconfigurecallback[/cmd].
1452 [br]
1453 The help callback will be invoked by KVIrc when the user will ask help for your addon (mainly
1454 from the addon management dialog, but not necessarily). It should call [cmd]help.open[/cmd]
1455 with the name of your documentation index html file (it should be relative
1456 to the help language directory: help.open myaddon/index.html will automatically
1457 lookup the right language). If you provide no help callback, the buttons
1458 for requesting help will be simply disabled. (A good an relatively complex addon
1459 *should* have at least a minimal help file explaining the features).
1460 [br]
1461 The configuration callback will be invoked when the user will try to configure
1462 your addon from the addon management dialog. This callback is useful
1463 mainly for more complex graphical scripts that can show up a dialog
1464 that allows configuring all of the addon features. To use this callback
1465 you will probably need some object scripting.
1466 [big]The real addon work[/big]
1467 [br]
1468 The real addon work is done by the scripts contained in the src directory.
1469 They will likely add aliases (maybe in a nice namespace named against your addon),
1470 register event handlers, create actions, timers, toolbars and object classes.
1471 You should install all of this stuff from your addon source files.
1472 Remember that your source files will [b]not[/b] be parsed every time KVIrc starts up:
1473 your stuff must be registered in KVIrc and be able to startup itself, if needed.
1474 Remember that you must clean up [b]everything[/b] in your uninstallation callback.
1475 This means that you must remove the aliases, unregister the event handlers,
1476 destroy the actions, kill the timers and the object classes you've created.
1477 Be a clean coder :)
1478 [big]Where to start[/big]
1479 [br]
1480 It is a good idea to start in the KVIrc scripts GitHub repository https://github.com/kvirc/kvirc-scripts. There are surely
1481 several addons to look at. Pick one that seems simple and analyze its layout and code
1482 (wow... amazing free software!). It will be easier to do than it was to explain it :D[br]
1483 Have fun! :)
1484 [br]
1485 */
1486
1487 /*
1488 @doc: kvs_codingtips
1489 @type:
1490 generic
1491 @title:
1492 Coding tips
1493 @keyterms:
1494 indentation,indent,readability
1495 @short:
1496 Generic coding tips for scripters (and not only)
1497 @body:
1498 Here comes a small list of [i]coding tips[/i].[br]
1499 These apply to programming in general, not only to KVIrc scripting.[br]
1500 [br]
1501 1. [b]Comment your code[/b][br]
1502 A well commented code is easy to maintain, and easy to read by others.[br]
1503 [br]
1504 2. [b]Indent your code[/b][br]
1505 Indentation increases the code readability; this is again for you and
1506 other developers that will be going to read your code.[br]
1507 [br]
1508 3. [b]Use TABS to indent your code[/b][br]
1509 ...and use [b]only tabs[/b] to indent.[br]
1510 Tabs are better than space since most code editors allow you
1511 to set the tab since and thus to have the indentation steps smaller or bigger.[br]
1512 This is really important since the indentation size is really a matter of personal taste.[br]
1513 Mixing spaces and tabs is Evil (tm), since it makes the code look really
1514 ugly in editors that have the tab size different than yours; in some cases the
1515 code gets really unreadable.[br]
1516 [br]
1517 4. [b]Use descriptive variable names[/b][br]
1518 Using 'foo' as variable name implies tracing its semantic the next
1519 time that you're going to read the code that uses it.[br]
1520 This is really annoying and time-consuming, especially if the project
1521 is getting large.[br]
1522 Obviously using [i]thisIsACounterVariable[/i] as name for a simple counter
1523 is also a suicide.[br]
1524 A good convention on variable names can speed up writing, debugging and maintaining code.[br]
1525 Encoding the type of the variable in the variable name might be also a good idea,
1526 but this is a matter of taste; personally I feel really well with that.[br]
1527 Just as example, here go my fundamental convention rules for C++:[br]
1528 [br]
1529 - The type of the variable is encoded at the beginning of the variable name:[br]
1530 [br]
1531 - b prefix for the boolean variables[br]
1532 - i prefix for signed integers[br]
1533 - u prefix for unsigned integers[br]
1534 - f and d prefixes for floating point stuff[br]
1535 - sz prefix for strings (this is rather for string classes)[br]
1536 - ...[br]
1537 [br]
1538 - Pointers have a [b]p[/b] prefix prepended[br]
1539 - Global variables start with a [b]g_[/b] prefix[br]
1540 - Member variables start with a [b]m_[/b] prefix[br]
1541 - Exception comes for local variables with obvious semantics[br]
1542 [br]
1543 - i,j,k,l for local loop counters[br]
1544 - [i]aux[/i] and [i]tmp[/i] for local obvious short-term temporary variables[br]
1545 [br]
1546 So actually by [b]only[/b] reading [i]g_pszQuitMessage[/i] I know that this is a global pointer to a string variable
1547 containing a quit message. :)[br]
1548 [/p]
1549 */
1550
1551 // FIXME: #warning "FINISH THE SYNTACTIC RULES DOC"
1552
1553 /*
1554 @doc: syntactic_rules
1555 @type:
1556 language
1557 @keyterms:
1558 productions
1559 @title:
1560 Syntactic rules
1561 @short:
1562 Syntactic rules of the KVIrc scripting language
1563 @body:
1564 In the following table you can find a good part of the
1565 KVIrc scripting language syntactic rules.[br]
1566 [br]
1567 <entity> indicates an [b]entity that can appear exactly one time[/b].[br]
1568 [<entity>] indicates an [b]optional entity[/b].[br]
1569 {<entity>} indicates an [b]entity that can appear one or more times[/b].[br]
1570 'entity' indicates a [b]literal entity[/b]: written exactly as it is.[br]
1571 <entity1>|<entity2> indicates mutually exclusive choices.[br]
1572 The mutually exclusive choices are often separated in two or more
1573 rules (productions), to improve readability.
1574 [table]
1575 [tr]
1576 [td]<command buffer>[/td]
1577 [td][<whitespace>][<command block>]{<command buffer>}[/td]
1578 [/tr]
1579 [tr]
1580 [td]<command buffer>[/td]
1581 [td][<whitespace>][<single command>]{<command buffer>}[/td]
1582 [/tr]
1583 [tr]
1584 [td]<whitespace>[/td]
1585 [td]{<space>|<tab>|<newline>}['\'<newline>][<whitespace>][/td]
1586 [/tr]
1587 [tr]
1588 [td]<space>[/td]
1589 [td]' '['\'<newline>][<space>] (ASCII space character)[/td]
1590 [/tr]
1591 [tr]
1592 [td]<tab>[/td]
1593 [td]'\t' (ASCII horizontal tabulation character)[/td]
1594 [/tr]
1595 [tr]
1596 [td]<newline>[/td]
1597 [td]'\n' (ASCII line feed (LF) character)[/td]
1598 [/tr]
1599 [tr]
1600 [td]<command block>[/td]
1601 [td]'{' <command buffer>[<whitespace>] '}'[/td]
1602 [/tr]
1603 [tr]
1604 [td]<single command>[/td]
1605 [td]<comment>[/td]
1606 [/tr]
1607 [tr]
1608 [td]<single command>[/td]
1609 [td]<lvalue command> <command terminator>[/td]
1610 [/tr]
1611 [tr]
1612 [td]<single command>[/td]
1613 [td]<rvalue command> <command terminator>[/td]
1614 [/tr]
1615 [tr]
1616 [td]<comment>[/td]
1617 [td]'#' {<non comment terminator>} <comment terminator>[/td]
1618 [/tr]
1619 [tr]
1620 [td]<comment terminator>[/td]
1621 [td]<newline> | <end of string>[/td]
1622 [/tr]
1623 [tr]
1624 [td]<end of string>[/td]
1625 [td]No character (internally ASCII character 0)[/td]
1626 [/tr]
1627 [tr]
1628 [td]<command terminator>[/td]
1629 [td]<newline> | <end of string> | ';'[/td]
1630 [/tr]
1631 [tr]
1632 [td]<non comment-terminator>[/td]
1633 [td]Any ASCII character except <newline> and <end of string>[/td]
1634 [/tr]
1635 [tr]
1636 [td]<simple command>[/td]
1637 [td][<module name>'.']<command name>[<switch list>]{<space>}<command dependent part>[/td]
1638 [/tr]
1639 [tr]
1640 [td]<lvalue command>[/td]
1641 [td]<variable>[<space>]<operation>[/td]
1642 [/tr]
1643 [tr]
1644 [td]<lvalue command>[/td]
1645 [td]<variable>'->'<object command>[/td]
1646 [/tr]
1647 [tr]
1648 [td]<lvalue command>[/td]
1649 [td]<identifier>'->'<object command>[/td]
1650 [/tr]
1651 [tr]
1652 [td]<operation>[/td]
1653 [td]<one op operator>[/td]
1654 [/tr]
1655 [tr]
1656 [td]<operation>[/td]
1657 [td]<two op operator>[<space>]<param string>[/td]
1658 [/tr]
1659 [tr]
1660 [td]<switch list>[/td]
1661 [td]{<space>}'-'<alpha char>[{<space>}'='<single parameter>][<switch list>][/td]
1662 [/tr]
1663 [tr]
1664 [td]<command name>[/td]
1665 [td]<alphanumeric char>{<alphanumeric char>}[/td]
1666 [/tr]
1667 [tr]
1668 [td]<module name>[/td]
1669 [td]<alphanumeric char>{<alphanumeric char>}[/td]
1670 [/tr]
1671 [tr]
1672 [td]<alphanumeric char>[/td]
1673 [td]Ascii characters 'A' to 'Z', 'a' to 'z', '0' to '9' and '_'[/td]
1674 [/tr]
1675 [tr]
1676 [td]<variable>[/td]
1677 [td]<global variable> | <local variable>[/td]
1678 [/tr]
1679 [tr]
1680 [td]<global variable>[/td]
1681 [td]'%' <uppercase letter> [<alphanumeric char>]['['<param string>']'][/td]
1682 [/tr]
1683 [tr]
1684 [td]<local variable>[/td]
1685 [td]'%' <lowercase letter> [<alphanumeric char>]['['<param string>']'][/td]
1686 [/tr]
1687 [tr]
1688 [td]<param string>[/td]
1689 [td][<single parameter>][<space>[<param string>]][/td]
1690 [/tr]
1691 [tr]
1692 [td]<single parameter>[/td]
1693 [td]<variable> | <identifier> | <nonterminator token> | <string>[/td]
1694 [/tr]
1695 [tr]
1696 [td]<nonterminator token>[/td]
1697 [td]<nonterminator char>['\'<newline><nonterminator char>][/td]
1698 [/tr]
1699 [tr]
1700 [td]<nonterminator char>[/td]
1701 [td]Any ascii character except <space> and <command terminator>[/td]
1702 [/tr]
1703 [tr]
1704 [td]<command dependent part>[/td]
1705 [td]Production in each command help page[/td]
1706 [/tr]
1707 [/table]
1708 To be continued...
1709 */
1710
1711 /*
1712 @doc: kvs_datatypes
1713 @type:
1714 language
1715 @keyterms:
1716 global variable, global variables, local variable, local variables,
1717 variables, variable, array, hash, dictionary, global variables, local variables, variable evaluation,
1718 associative arrays, scalars, data types, percent sign and extended scope
1719 @title:
1720 Variables and Data types
1721 @short:
1722 All about the KVS variable and datatype management
1723 @body:
1724 [title]Basic syntax[/title]
1725 [br]
1726 A variable identifier is composed by a '%' (percent) sign followed
1727 by a sequence of letters, digits or underscores.
1728 Examples of valid variable names are:
1729 [example]
1730 %i
1731 %variable
1732 %MyVar
1733 %1
1734 %thisisavar
1735 %2ndName
1736 %_hidden
1737 [/example]
1738 [br]
1739 Variables are created when you assign something to them: there is no need
1740 for a declaration (unlike other languages such as C/C++, Java or VB).
1741 [example]
1742 [comment]# create a variable named %X by assigning the value 10 to it[/comment]
1743 %X = 10
1744 [comment]# use the variable[/comment]
1745 echo "The value of X is" %X
1746 [/example]
1747 [title]Local and global variables[/title]
1748 [br]
1749 Variables can be local or global.
1750 Local variables preserve their contents only inside the [b]scope[/b] of a single script.
1751 Global variables are shared between all the scripts and preserve their contents
1752 until they are explicitly unset or until KVIrc quits.
1753 [br]
1754 Local variables start with a [b]lowercase letter[/b] while the global ones with an [b]uppercase letter[/b].
1755 [example]
1756 %var = 10; [comment]# this is a local variable[/comment]
1757 %Var = 10; [comment]# this is a global variable[/comment]
1758 [/example]
1759 [br]
1760 You can also force a variable that start with a lowercase letter to be global
1761 by pre-declaring it with the [cmd]global[/cmd] keyword.
1762 [example]
1763 [comment]# copy this script to a file and run /[cmd]parse[/cmd] <filename>[/comment]
1764 global %a
1765 %a = "The contents of the variable a"
1766 %b = "The contents of the variable b"
1767 [comment]# %a is a global variable now : all the other scripts can see its value[/comment]
1768 [comment]# %b is a local variable and no other scripts can see its value[/comment]
1769 [/example]
1770 [br]
1771 If you have executed the example above from a file (by the means of [cmd]parse[/cmd])
1772 then now you can type
1773 [example]
1774 [cmd]echo[/cmd] %a
1775 [/example]
1776 [br]
1777 in the commandline to see the contents of the variable %a.
1778 If you also try
1779 [example]
1780 [cmd]echo[/cmd] %b
1781 [/example]
1782 [br]
1783 you will see nothing printed since %b was local to the parsed script.
1784 [title]Data types[/title]
1785 [br]
1786 KVS has three main categories of data types: scalars, arrays and associative
1787 arrays (also known as dictionaries or hashes).
1788 [subtitle]Scalars[/subtitle]
1789 [br]
1790 The scalars are simple variables containing a single value (a string or an integer).
1791 [example]
1792 [comment]# %a is a scalar variable[/comment]
1793 %a = "This is a string"
1794 [cmd]echo[/cmd] %a
1795 %a = 24.5
1796 [cmd]echo[/cmd] %a
1797 [/example]
1798 [subtitle]Arrays[/subtitle]
1799 [br]
1800 Arrays are collections of items indexed by integers. The array items
1801 are selected by placing the index in square brackets just after the array name.
1802 [example]
1803 %arrayName[index]
1804 [/example]
1805 [br]
1806 An easy way to create an array is to use the [fnc]$array[/fnc] function.
1807 [example]
1808 %a = $array("element1","element2","element3"); [comment]# Create an array with 3 items[/comment]
1809 [cmd]for[/cmd](%i=0;%i<3;%i++)
1810 {
1811 echo %a[%i]; [comment]# Accessing the %i'th element of the array[/comment]
1812 }
1813 [/example]
1814 [br]
1815 Note that in the example above %a refers to the whole array while %a[%i] refers
1816 to one of its elements, in particular the one with index %i.
1817 You also create an array by explicitly assigning to one of its elements:
1818 [example]
1819 %a[9] = "This is an array element";
1820 [/example]
1821 [br]
1822 Array indexes are zero-based so in the example above you have created an array
1823 with 10 items. You can find out an array's length with the [fnc]$length[/fnc]() function.
1824 [example]
1825 %a[9] = "This is an array element";
1826 echo $length(%a)
1827 [/example]
1828 [br]
1829 Be aware that by making such an assignment you implicitly consume some memory for
1830 all the preceding array items (even if they are unset). This means that
1831 a simple instruction like the following may eat a huge amount of memory at once:
1832 [example]
1833 %a[1000000] = "An array element faaaaaar away...";
1834 echo $length(%a)
1835 [/example]
1836 [note]
1837 [big]Food for thought:[/big]
1838 KVIrc allocates a pointer for each item in the array. The pointer is
1839 empty when the item is unset and points to an additional block
1840 of memory when the item is set. The size of a pointer is platform
1841 dependent: on the platforms supported by KVIrc it's either 32 or 64-bit.
1842 The size of the additional block depends both on the platform
1843 and on the contents of the item... it's average value may
1844 be around 16 bytes. The array size is determined by the last SET element index.
1845 All this this means that in the worst case (64 bit assumption) an array in
1846 that the highest indexed item set is N eats up at least N*8+16 bytes of memory.
1847 [/note]
1848 Besides the traditional indexed looping method you
1849 can also use the [cmd]foreach[/cmd] command to iterate the items of an array.
1850 Be aware that [cmd]foreach[/cmd] will [b]not[/b] iterate over unset items in the
1851 array unless you use the -a switch.
1852 [example]
1853 %Array[0]=Pippo
1854 %Array[1]=Pluto
1855 %Array[2]=Paperino
1856 %Array[5]=Prova
1857 [cmd]foreach[/cmd](%item,%Array)[cmd]echo[/cmd] Got Item: %item
1858 [/example]
1859 [br]
1860 Note that the items 3 and 4 are simply skipped.
1861 [subtitle]Hashes[/subtitle]
1862 [br]
1863 The hashes are collections of items indexed by strings: the word [i]hash[/i]
1864 is in fact a shortcut for [i]hashtable[/i]. In literature hashes are also called
1865 [i]associative arrays[/i], [i]dictionaries[/i] or [i]key-value pair sets[i].
1866 The hash items are selected by placing the key in curly brackets
1867 just after the hash name.
1868 [example]
1869 %hashName{key}
1870 [/example]
1871 [br]
1872 An easy way to create a hash is to use the [fnc]$hash[/fnc] function.
1873 [/p]
1874 [example]
1875 %a = $hash("key1","value1","key2","value2","key3","value3")
1876 [cmd]foreach[/cmd](%key,[fnc]$keys[/fnc](%a))
1877 {
1878 echo "KEY:" %key "VALUE:" %a{%key};
1879 }
1880 [/example]
1881 [br]
1882 Note that in the example above %a refers to the whole hash while %a{%i} refers
1883 to one of its elements, in particular the one with the key %key.
1884 You also create a hash by explicitly assigning to one of its elements:
1885 [example]
1886 %a{"MyKey"} = "MyValue"
1887 [/example]
1888 [br]
1889 You may have already noticed that the [fnc]$keys[/fnc]() function returns
1890 the array of the hash keys: it is useful to iterate over the hash items.
1891 [title]Mutability of variables[/title]
1892 [br]
1893 KVS is not strictly typed: any variable can assume different type identities at different times,
1894 even in the same script.
1895 [example]
1896 [comment]# %a is a scalar[/comment]
1897 %a = "This is a string"
1898 [comment]# %a becomes an array with 3 elements[/comment]
1899 %a = $array("element1","element2","element3");
1900 [comment]# %a becomes a hash with two values[/comment]
1901 %a = $hash("key1","value1","key2","value2");
1902 [/example]
1903 [br]
1904 In literature this kind of variable is called [b]variant[/b] and this is the
1905 term that you will find all around the documentation when an explicit
1906 data type is not requested.
1907 [br]
1908 Note that array and hash items are variants too. This means that you can have arrays
1909 of arrays, hashes of arrays of hashes and any other multidimensional combination you like.
1910 However remember that hash keys are strings and not variants so you can't use an array as hash key.
1911 [example]
1912 [comment]# here we eat 256 locations of memory at once :)[/comment]
1913 %a[16][16] = 10
1914 [comment]# a hash of hashes: here we eat just two memory locations[/comment]
1915 %a{"16"}{"16"} = 10
1916 [/example]
1917 [br]
1918 In most cases the KVS engine manages automatically the conversion between data types.
1919 For example, when you put an array in a place where a scalar is requested, KVIrc
1920 automatically transforms it to a scalar string by joining all the items with a comma.
1921 [example]
1922 %a = $array("element1","element2","element3");
1923 echo %a; [comment]# echo expects its arguments to be scalar[/comment]
1924 [/example]
1925 [br]
1926 Conversely, when you put a scalar in place of an array, KVIrc automatically
1927 transforms it to an array with a single item. In this way a function like
1928 [fnc]$sort[/fnc] works also with a scalar.
1929 [br]
1930 In literature the conversions between data types are called [b]casts[/b]. When
1931 the conversion is automatic the cast is said to be [b]implicit[/b].
1932 [br]
1933 KVS handles also the other possible implicit casts: scalar->hash,hash->scalar,array->hash,hash->array.
1934 Experiment with it.
1935 [title]More about scalars[/title]
1936 [br]
1937 Internally KVS is implicitly typed: the [i]scalar[/i] data type is in fact
1938 a set of types that KVIrc manages silently. The types are: integer, string, real, boolean and hobject.
1939 [br]
1940 Integers are non-floating point numbers. Their allowable range depends on the underlying
1941 platform integer size: usually 32 or 64 bit.
1942 [br]
1943 Reals are floating point numbers. Their allowable range and precision depends on the underlying
1944 platform.
1945 [br]
1946 Booleans are either true or false values. For boolean constants, use [fnc]$true[/fnc] or [fnc]$false[/fnc].
1947 Don't use [i]true[/i] or [i]false[/i], because the engine sees them as non-empty strings, which always evaluate as true.
1948 [br]
1949 hobject stands for Handle to Object and it is a sort of a C++ pointer.
1950 Detailed description of objects is in [doc:objects]this document[/doc].
1951 [br]
1952 Basically anything else fails in the [i]string[/i] category.
1953 [br]
1954 In most cases KVS manages all the conversions between data types automatically.
1955 For example an integer becomes a true boolean when it's non zero and a false boolean
1956 otherwise, a real becomes an integer by truncating it's fractional part...
1957 [br]
1958 You can find out the type of a specified variable by using the [fnc]$typeof[/fnc]() function.
1959 [example]
1960 %a = 1
1961 echo $typeof(%a)
1962 %a = 1.1
1963 echo $typeof(%a)
1964 %a = $true
1965 echo $typeof(%a)
1966 %a = "test"
1967 echo $typeof(%a)
1968 [/example]
1969 [br]
1970 There is also another subtle type of scalar called [i]nothing[/i]. It stands for an empty (unset) variable.
1971 [example]
1972 %a = $nothing
1973 echo $typeof(%a)
1974 [/example]
1975 [br]
1976 Nothing is something in between a data type and a special value for all the other data types:
1977 it represents absence of information.
1978 This may look a bit confusing but realize that all the unreferenced KVS variable are in fact of type [i]nothing[/i]:
1979 they just don't exist. This means that you can use [fnc]$nothing[/fnc]() to effectively
1980 unset a variable.
1981 [br]
1982 Again, when possible, the conversion between nothing and the other data types is
1983 performed automatically. Nothing becomes an empty string, a null object handle or an empty array.
1984 [title]Explicit casts[/title]
1985 [br]
1986 You can make explicit conversions between some data types by using the casting functions.
1987 [fnc]$integer[/fnc]() will attempt to convert the variant parameter to an integer, [fnc]$real[/fnc]()
1988 will cast to a floating point value, [fnc]$boolean[/fnc]() will convert to a
1989 true/false value, [fnc]$string[/fnc]() will explicitly convert to a string,
1990 [fnc]$array[/fnc]() will convert to an array and [fnc]$hash[/fnc] will return
1991 a dictionary. By assigning the special [fnc]$nothing[/fnc]() value you will
1992 convert to the nothing data type (or simply unset the variable).
1993 The only explicit conversion that is not possible is to hobject.
1994 [br]
1995 As stated several times in this document, KVS tries to manage the casts automatically
1996 so you usually don't need to care about it. The explicit casts are provided for
1997 the very few cases where an automatic conversion would lead to an unexpected value (for your script)
1998 and for writer's clarity.
1999 [title]More about variables life-cycle[/title]
2000 [br]
2001 As stated above variables start their existence when you assign something to them.
2002 After a variable has been created it persists until it goes out of his scope (remember
2003 about local and global variables?) or you explicitly destroy it. You will usually
2004 not care about it and just leave the KVS engine to do his cleaning job but it's still worth
2005 knowing that you actually can force KVIrc to free the memory used by a variable.
2006 [br]
2007 The first method to explicitly destroy a variable is to call [cmd]unset[/cmd] on it.
2008 [cmd]unset[/cmd] in fact accepts a list of variables so you can destroy more variables at once.
2009 [example]
2010 %a = [fnc]$array[/fnc]("data","for","a","really","huge","array","of","items")
2011 %b = 10
2012 %c = "just a string that eats memory"
2013 [cmd]unset[/cmd] %a,%b,%c
2014 [/example]
2015 [br]
2016 The KVS engine treats unset variables just like empty strings. The opposite is also valid: empty
2017 strings behave like empty (unset) variables. This means that you can assign an empty string
2018 to a variable to unset it.
2019 [example]
2020 %a = "test"; [comment]# %a starts his existence[/comment]
2021 %b = "test2";
2022 %a = ""; [comment]# %a is in fact unset[/comment]
2023 %b = ; [comment]# syntactically this is just the same as above[/comment]
2024 [/example]
2025 [br]
2026 Note that because of mutability of variables (explained above) you can use the empty string
2027 assignment also to free arrays and hashes.
2028 [title]Extended scope variables[/title]
2029 [br]
2030 Beside local and global variables there is a third family of them.
2031 Variables that have a ':' character just after the leading '%' are [b]extended scope[/b] variables.
2032 [i]%:index[/i], [i]%:Hello[/i], [i]%:something.else[/i] are all valid special scope variable names.
2033 They're actually used in popups and in timers (but later I might find other usages as well :).
2034 "Extended scope" means that these variables are somewhere in the middle between
2035 global and local variables. They normally act as local, but in some cases their [b]lifetime[/b] and [b]visibility[/b]
2036 may be extended.
2037 [br]
2038 For example, in the popups, all the special scope variables
2039 are visible during all the "lifetime" of a popup (so from the prologue code call to
2040 the moment when the user selects an item and the corresponding code is executed).
2041 This allows you to pre-calculate some data or conditions in the popup prologue
2042 and use this data in the popup item conditions and item handlers.
2043 [title]Variable evaluation[/title]
2044 [br]
2045 A variable can appear in every place where a parameter
2046 is expected: so after the command name, after a switch or inside
2047 an identifier parameters. The KVS parser will try to extract the longest possible variable
2048 name after a literal percent '%' sign everywhere in the parameter string. So the command sequence
2049 [example]
2050 %number = 1st; echo this is my %number variable test
2051 [/example]
2052 [br]
2053 will first assign [i]1st[/i] to the variable [i]%number[/i] and then execute
2054 "echo this is my 1st variable test". The following example will [b]not[/b] work as expected.
2055 [example]
2056 %number = 1; echo this is my %numberst variable test
2057 [/example]
2058 [br]
2059 KVS will assign [b]1[/b] to %number in this case but the next variable
2060 name extracted will be [i]%numberst[/i] that is actually empty; so finally
2061 "echo this is my variable test" will be executed.
2062 To avoid this problem you can use the backslash escape character:
2063 [example]
2064 %number = 1; echo this is my %number\st variable test
2065 [/example]
2066 [title]Putting it all together[/title]
2067 [br]
2068 Variables can be either local, global or have an extended scope. Their start to exist
2069 when you first assign something to them and they disappear when they go out of their
2070 scope or you explicitly destroy them.
2071 [br]
2072 KVS has 8 builtin data types: string, integer, real, boolean, hobject, nothing, array and hash.
2073 The first 6 are scalar data types while the last two are not.
2074 [br]
2075 When possible, KVS manages all the conversions between data types silently.
2076 In the few cases in that an implicit conversion is not possible you have to manage the conversion
2077 manually otherwise KVS will complain.
2078 [br]
2079 */
2080
skipSpaces()2081 void KviKvsParser::skipSpaces()
2082 {
2083 while((KVSP_curCharUnicode == ' ') || (KVSP_curCharUnicode == '\t'))
2084 {
2085 KVSP_skipChar;
2086 }
2087
2088 if(KVSP_curCharUnicode == '\\')
2089 {
2090 KVSP_skipChar;
2091 if(KVSP_curCharUnicode == '\n')
2092 {
2093 KVSP_skipChar;
2094 skipSpaces();
2095 return;
2096 }
2097 else if(KVSP_curCharUnicode == '\r')
2098 {
2099 KVSP_skipChar;
2100 if(KVSP_curCharUnicode == '\n')
2101 {
2102 KVSP_skipChar;
2103 skipSpaces();
2104 return;
2105 }
2106 else
2107 {
2108 KVSP_backChar;
2109 KVSP_backChar;
2110 }
2111 }
2112 else
2113 {
2114 KVSP_backChar;
2115 }
2116 }
2117 }
2118
skipSpacesAndNewlines()2119 bool KviKvsParser::skipSpacesAndNewlines()
2120 {
2121 while((KVSP_curCharUnicode == ' ') || (KVSP_curCharUnicode == '\t') || (KVSP_curCharUnicode == '\n') || (KVSP_curCharUnicode == '\r'))
2122 {
2123 KVSP_skipChar;
2124 }
2125
2126 switch(KVSP_curCharUnicode)
2127 {
2128 case '\\':
2129 KVSP_skipChar;
2130 if(KVSP_curCharUnicode == '\n')
2131 {
2132 KVSP_skipChar;
2133 return skipSpacesAndNewlines();
2134 }
2135 else if(KVSP_curCharUnicode == '\r')
2136 {
2137 KVSP_skipChar;
2138 if(KVSP_curCharUnicode == '\n')
2139 {
2140 KVSP_skipChar;
2141 return skipSpacesAndNewlines();
2142 }
2143 else
2144 {
2145 KVSP_backChar;
2146 KVSP_backChar;
2147 }
2148 }
2149 else
2150 {
2151 KVSP_backChar;
2152 }
2153 break;
2154 case '#':
2155 case '/':
2156 // we allow comments too!
2157 (void)parseComment(); // this will return 0 anyway (and never trigger an error here)
2158 if(error())
2159 return false;
2160 return skipSpacesAndNewlines();
2161 break;
2162 }
2163 return true;
2164 }
2165
skipToNextLine()2166 void KviKvsParser::skipToNextLine()
2167 {
2168 while((KVSP_curCharUnicode != 0) && (KVSP_curCharUnicode != '\n'))
2169 KVSP_skipChar;
2170
2171 if(KVSP_curCharUnicode == '\n')
2172 KVSP_skipChar;
2173 }
2174
parseInstructionList()2175 KviKvsTreeNodeInstruction * KviKvsParser::parseInstructionList()
2176 {
2177 KviKvsTreeNodeInstructionBlock * l = new KviKvsTreeNodeInstructionBlock(KVSP_curCharPointer);
2178
2179 for(;;)
2180 {
2181 if(!skipSpacesAndNewlines())
2182 {
2183 delete l;
2184 return nullptr;
2185 }
2186
2187 if(KVSP_curCharUnicode != 0)
2188 {
2189 // instruction
2190 KviKvsTreeNodeInstruction * i = parseInstruction();
2191 if(i)
2192 l->addInstruction(i);
2193 else
2194 {
2195 if(error())
2196 {
2197 // ops...
2198 delete l;
2199 return nullptr;
2200 } // else empty instruction
2201 }
2202 }
2203 else
2204 {
2205 if(l->instructionCount() == 1)
2206 {
2207 // return the single instruction instead
2208 KviKvsTreeNodeInstruction * i = l->releaseFirst();
2209 delete l;
2210 return i;
2211 }
2212 // end of buffer
2213 return l;
2214 }
2215 }
2216
2217 // never here
2218 KVSP_ASSERT(false);
2219 return nullptr;
2220 }
2221
parseParameterPercentOrDollar()2222 KviKvsTreeNodeData * KviKvsParser::parseParameterPercentOrDollar()
2223 {
2224 KVSP_ASSERT((KVSP_curCharUnicode == '%') || (KVSP_curCharUnicode == '$') || (KVSP_curCharUnicode == '@'));
2225
2226 if(KVSP_curCharUnicode == '%')
2227 {
2228 KVSP_skipChar;
2229 if(!KVSP_curCharIsLetter && (KVSP_curCharUnicode != ':'))
2230 {
2231 // be flexible : allow an "alone" '%' char
2232 return new KviKvsTreeNodeConstantData(KVSP_curCharPointer - 1, new KviKvsVariant(QString("%")));
2233 }
2234 // this is surely a variable or function
2235 KVSP_backChar;
2236 }
2237 else if(KVSP_curCharUnicode == '$')
2238 {
2239 KVSP_skipChar;
2240 if(!KVSP_curCharIsFunctionStart)
2241 {
2242 // be flexible : allow an "alone" '$' char
2243 return new KviKvsTreeNodeConstantData(KVSP_curCharPointer - 1, new KviKvsVariant(QString("$")));
2244 }
2245 // this is surely a variable or function
2246 KVSP_backChar;
2247 }
2248
2249 return parsePercentOrDollar();
2250 }
2251
parsePercentOrDollar(bool bInObjScope)2252 KviKvsTreeNodeData * KviKvsParser::parsePercentOrDollar(bool bInObjScope)
2253 {
2254 KVSP_ASSERT((KVSP_curCharUnicode == '%') || (KVSP_curCharUnicode == '$') || (KVSP_curCharUnicode == '@'));
2255
2256 KviKvsTreeNodeData * r;
2257 const QChar * pBegin;
2258
2259 if(KVSP_curCharUnicode == '%')
2260 {
2261 r = parsePercent(bInObjScope);
2262 if(!r)
2263 return nullptr;
2264 }
2265 else if(KVSP_curCharUnicode == '$')
2266 {
2267 r = parseDollar(bInObjScope);
2268 if(!r)
2269 return nullptr;
2270 }
2271 else
2272 {
2273 // this is @
2274
2275 static QString szStrayAtRoutineName("@");
2276 static QString szMightBeStrayAtOrThisRoutineName("@?");
2277
2278 pBegin = KVSP_curCharPointer;
2279
2280 KVSP_skipChar;
2281
2282 if(bInObjScope || ((KVSP_curCharUnicode != '$') && (KVSP_curCharUnicode != '%')))
2283 {
2284 // we're sure this is just a stray @
2285 // we use a trick here: when @ is not supposed to be an object scope call
2286 // then we create a function that will return the @ itself as a string
2287 KviKvsCoreFunctionExecRoutine * pRoutine = KviKvsKernel::instance()->findCoreFunctionExecRoutine(szStrayAtRoutineName);
2288 r = new KviKvsTreeNodeCoreFunctionCall(KVSP_curCharPointer, szStrayAtRoutineName, pRoutine, new KviKvsTreeNodeDataList(KVSP_curCharPointer));
2289 //KVSP_skipChar;
2290 return r;
2291 }
2292 // we're not in object scope and cur char is either $ or %
2293 // check for the common syntax $0!$1@$2 seen in hostmasks
2294 // @$<digit> is non valid anyway
2295 if(KVSP_curCharUnicode == '$')
2296 {
2297 KVSP_skipChar;
2298 if(KVSP_curCharIsNumber)
2299 {
2300 // again a stray @
2301 KVSP_backChar;
2302 KviKvsCoreFunctionExecRoutine * pRoutine = KviKvsKernel::instance()->findCoreFunctionExecRoutine(szStrayAtRoutineName);
2303 r = new KviKvsTreeNodeCoreFunctionCall(KVSP_curCharPointer, szStrayAtRoutineName, pRoutine, new KviKvsTreeNodeDataList(KVSP_curCharPointer));
2304 return r;
2305 }
2306 KVSP_backChar;
2307 }
2308
2309 // now we're unsure: we will be able to decide only at runtime if it is a stray @ or the shortcut for $this
2310 // this design was a bit ugly.. I must admit it... but it is really useful when writing object classes...
2311 KviKvsCoreFunctionExecRoutine * pRoutine = KviKvsKernel::instance()->findCoreFunctionExecRoutine(szMightBeStrayAtOrThisRoutineName);
2312 // MUST BE THERE!
2313 // core function call
2314 r = new KviKvsTreeNodeCoreFunctionCall(pBegin, szMightBeStrayAtOrThisRoutineName, pRoutine, new KviKvsTreeNodeDataList(pBegin));
2315
2316 skipSpaces();
2317
2318 pBegin = KVSP_curCharPointer;
2319
2320 goto handle_scope_operator;
2321 }
2322
2323 pBegin = KVSP_curCharPointer;
2324
2325 while((KVSP_curCharUnicode == '[') || (KVSP_curCharUnicode == '{'))
2326 {
2327 if(KVSP_curCharUnicode == '[')
2328 {
2329 // array index
2330 KVSP_skipChar;
2331 skipSpaces();
2332 if(KVSP_curCharUnicode == ']')
2333 {
2334 KVSP_skipChar;
2335 if(KVSP_curCharUnicode == '#')
2336 {
2337 // count
2338 KVSP_skipChar;
2339 return new KviKvsTreeNodeArrayCount(pBegin, r);
2340 }
2341 else
2342 {
2343 // a hash reference assert
2344 return new KviKvsTreeNodeArrayReferenceAssert(pBegin, r);
2345 }
2346 }
2347
2348 KviKvsTreeNodeExpression * e = parseExpression(']');
2349 if(!e)
2350 {
2351 delete r;
2352 return nullptr;
2353 }
2354
2355 r = new KviKvsTreeNodeArrayElement(pBegin, r, e);
2356 }
2357 else
2358 {
2359 // hash key
2360 KVSP_skipChar;
2361 skipSpaces();
2362
2363 if(KVSP_curCharUnicode == '}')
2364 {
2365 // entire hash ?
2366 KVSP_skipChar;
2367 if(KVSP_curCharUnicode == '#')
2368 {
2369 KVSP_skipChar;
2370 return new KviKvsTreeNodeHashCount(pBegin, r);
2371 }
2372 return new KviKvsTreeNodeHashReferenceAssert(pBegin, r);
2373 }
2374
2375 KviKvsTreeNodeData * i = parseHashKey();
2376 if(!i)
2377 {
2378 // error
2379 delete r;
2380 return nullptr;
2381 }
2382
2383 KVSP_ASSERT(KVSP_curCharUnicode == '}');
2384
2385 KVSP_skipChar;
2386
2387 r = new KviKvsTreeNodeHashElement(pBegin, r, i);
2388 }
2389 }
2390
2391 if(KVSP_curCharUnicode != '-')
2392 {
2393 return r;
2394 }
2395
2396 if(!r->canEvaluateToObjectReference())
2397 return r; // FIXME: maybe print a warning ?
2398
2399 // might be a scope operator
2400
2401 KVSP_skipChar;
2402 if(KVSP_curCharUnicode != '>')
2403 {
2404 KVSP_backChar;
2405 return r;
2406 }
2407
2408 KVSP_skipChar;
2409 skipSpaces();
2410
2411 if((KVSP_curCharUnicode != '$') && (KVSP_curCharUnicode != '%'))
2412 {
2413 KVSP_setCurCharPointer(pBegin);
2414 return r;
2415 }
2416
2417 handle_scope_operator:
2418
2419 // hmmm... there really seems to be a scope operator there...
2420 if(KVSP_curCharUnicode == '%')
2421 {
2422 KVSP_skipChar;
2423 if(!KVSP_curCharIsLetter)
2424 {
2425 // be flexible : allow an "alone" '%' char
2426 KVSP_setCurCharPointer(pBegin);
2427 return r;
2428 }
2429 }
2430 else
2431 {
2432 KVSP_skipChar;
2433 if(!KVSP_curCharIsFunctionStart)
2434 {
2435 // be flexible : allow an "alone" '$' char
2436 KVSP_setCurCharPointer(pBegin);
2437 return r;
2438 }
2439 }
2440
2441 // ok : try the scope operator
2442 KVSP_backChar;
2443
2444 pBegin = KVSP_curCharPointer;
2445
2446 KviKvsTreeNodeData * r2 = parsePercentOrDollar(true);
2447
2448 if(!r2)
2449 {
2450 // must be an error
2451 delete r;
2452 return nullptr;
2453 }
2454
2455 if(!r2->canEvaluateInObjectScope())
2456 {
2457 // ops... it really wasn't
2458 delete r2;
2459 KVSP_setCurCharPointer(pBegin);
2460 return r;
2461 }
2462
2463 return new KviKvsTreeNodeScopeOperator(pBegin, r, r2);
2464 }
2465
parsePercent(bool bInObjScope)2466 KviKvsTreeNodeVariable * KviKvsParser::parsePercent(bool bInObjScope)
2467 {
2468 KVSP_ASSERT(KVSP_curCharUnicode == '%');
2469
2470 const QChar * pBegin = KVSP_curCharPointer;
2471
2472 KVSP_skipChar;
2473
2474 bool bExtScope;
2475
2476 if(KVSP_curCharUnicode == ':')
2477 {
2478 bExtScope = true;
2479 KVSP_skipChar;
2480 }
2481 else
2482 {
2483 bExtScope = false;
2484 }
2485
2486 if(!((KVSP_curCharIsLetterOrNumber) || (KVSP_curCharUnicode == '_')))
2487 {
2488 error(KVSP_curCharPointer, __tr2qs_ctx("Syntax error after '%' variable prefix. If you want to use a plain '%' in the code you need to escape it", "kvs"));
2489 return nullptr;
2490 }
2491
2492 const QChar * pIdBegin = KVSP_curCharPointer;
2493
2494 while((KVSP_curCharIsLetterOrNumber) || (KVSP_curCharUnicode == '_'))
2495 KVSP_skipChar;
2496
2497 QString szIdentifier(pIdBegin, KVSP_curCharPointer - pIdBegin);
2498
2499 //#warning "ADD A KviKvsTreeNodeBuiltinCleanupVariablesCommand on this KviKvsParser object"
2500 //#warning "KviKvsParser will append it to the script"
2501
2502 if(bExtScope)
2503 {
2504 if(bInObjScope)
2505 {
2506 error(KVSP_curCharPointer, __tr2qs_ctx("Objects have no extended scope variables", "kvs"));
2507 return nullptr;
2508 }
2509 return new KviKvsTreeNodeExtendedScopeVariable(pBegin, szIdentifier);
2510 }
2511
2512 if(bInObjScope)
2513 return new KviKvsTreeNodeObjectField(pBegin, szIdentifier);
2514
2515 // Check if explicitly declared as global
2516 if(m_pGlobals)
2517 {
2518 if(m_pGlobals->find(szIdentifier))
2519 return new KviKvsTreeNodeGlobalVariable(pBegin, szIdentifier);
2520 }
2521
2522 if(m_iFlags & AssumeLocals)
2523 return new KviKvsTreeNodeLocalVariable(pBegin, szIdentifier);
2524
2525 if(pIdBegin->category() == QChar::Letter_Uppercase)
2526 {
2527 //qDebug("Variable %s is global",szIdentifier.toUtf8().data());
2528 //if(m_iFlags & Pedantic)
2529 // warning(pIdBegin,__tr2qs_ctx("Declaring global variables with an uppercase letter is deprecated. Global variables should be declared with 'global'","kvs"));
2530 return new KviKvsTreeNodeGlobalVariable(pBegin, szIdentifier);
2531 }
2532
2533 return new KviKvsTreeNodeLocalVariable(pBegin, szIdentifier);
2534 }
2535
parseInstruction()2536 KviKvsTreeNodeInstruction * KviKvsParser::parseInstruction()
2537 {
2538 switch(KVSP_curCharUnicode)
2539 {
2540 case '#':
2541 case '/':
2542 (void)parseComment(); // this will return 0 anyway
2543 return nullptr;
2544 break;
2545 case 0: // empty instruction
2546 return nullptr;
2547 break;
2548 case '\n':
2549 case '\r':
2550 case ';': // empty instruction
2551 KVSP_skipChar;
2552 return nullptr;
2553 break;
2554 case '{': // command block
2555 return parseInstructionBlock();
2556 break;
2557 case '$':
2558 case '%':
2559 case '@':
2560 return parseVoidFunctionCallOrOperation();
2561 break;
2562 default:
2563 if(KVSP_curCharIsLetter || (KVSP_curCharUnicode == '_'))
2564 {
2565 // must be a command
2566 return parseCommand();
2567 }
2568 else
2569 {
2570 // what the heck is this ?
2571 error(KVSP_curCharPointer, __tr2qs_ctx("Found character '%q' (Unicode %x) where an instruction was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
2572 return nullptr;
2573 }
2574 break;
2575 }
2576 // never here
2577 KVSP_ASSERT(false);
2578 return nullptr;
2579 }
2580
parseInstructionBlock()2581 KviKvsTreeNodeInstruction * KviKvsParser::parseInstructionBlock()
2582 {
2583 KVSP_ASSERT(KVSP_curCharUnicode == '{');
2584
2585 KVSP_skipChar;
2586
2587 const QChar * pBegin = KVSP_curCharPointer;
2588
2589 KviKvsTreeNodeInstructionBlock * b = new KviKvsTreeNodeInstructionBlock(pBegin - 1);
2590
2591 for(;;)
2592 {
2593 if(!skipSpacesAndNewlines())
2594 {
2595 delete b;
2596 return nullptr;
2597 }
2598
2599 switch(KVSP_curCharUnicode)
2600 {
2601 case 0:
2602 delete b;
2603 warning(pBegin, __tr2qs_ctx("Unterminated instruction block", "kvs"));
2604 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of script in instruction block (missing closing brace)", "kvs"));
2605 return nullptr;
2606 break;
2607 case '}':
2608 KVSP_skipChar;
2609 if(b->instructionCount() <= 1)
2610 {
2611 if(b->instructionCount() < 1)
2612 {
2613 delete b;
2614 return nullptr; // just an empty block
2615 }
2616 // a single instruction
2617 KviKvsTreeNodeInstruction * i = b->releaseFirst();
2618 delete b;
2619 return i;
2620 }
2621 return b;
2622 break;
2623 default:
2624 // instruction
2625 KviKvsTreeNodeInstruction * i = parseInstruction();
2626 if(i)
2627 b->addInstruction(i);
2628 else
2629 {
2630 if(error())
2631 {
2632 // ops...
2633 delete b;
2634 return nullptr;
2635 } // else empty instruction
2636 }
2637 break;
2638 }
2639 }
2640 // never reached
2641 return nullptr;
2642 }
2643
parseCommandSwitchList()2644 KviKvsTreeNodeSwitchList * KviKvsParser::parseCommandSwitchList()
2645 {
2646 KVSP_ASSERT(KVSP_curCharUnicode == '-');
2647
2648 KviKvsTreeNodeSwitchList * sw = new KviKvsTreeNodeSwitchList(KVSP_curCharPointer);
2649
2650 while(KVSP_curCharUnicode == '-')
2651 {
2652 const QChar * pBegin = KVSP_curCharPointer;
2653 KVSP_skipChar;
2654
2655 bool bLong = false;
2656
2657 if(KVSP_curCharUnicode == '-')
2658 {
2659 // long switch
2660 pBegin = KVSP_curCharPointer;
2661 KVSP_skipChar;
2662 bLong = true;
2663 }
2664
2665 skipSpaces();
2666 if(!KVSP_curCharIsLetter)
2667 {
2668 if(KVSP_curCharIsNumber || KVSP_curCharIsEndOfCommand)
2669 {
2670 // a -digit : this is probably a negative number instead
2671 // or just a single dash (or couple of dashes)
2672 // go back to the initial dash and treat it as text...and return the current switch list
2673 KVSP_setCurCharPointer(pBegin);
2674 if(sw->isEmpty())
2675 {
2676 // not an error!
2677 delete sw;
2678 return nullptr;
2679 }
2680 return sw;
2681 }
2682 else
2683 {
2684 delete sw;
2685 warning(pBegin, __tr2qs_ctx("The dash after a command should be followed by a letter (switch), by a digit (negative number) or be escaped", "kvs"));
2686
2687 if(KVSP_curCharUnicode == 0)
2688 {
2689 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected character '%q' (Unicode %x) after a switch dash", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
2690 }
2691 else
2692 {
2693 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of script after a switch dash", "kvs"));
2694 }
2695 return nullptr;
2696 }
2697 }
2698
2699 const QChar * pSw = KVSP_curCharPointer;
2700
2701 KVSP_skipChar;
2702 while((KVSP_curCharIsLetterOrNumber) || (KVSP_curCharUnicode == '-'))
2703 KVSP_skipChar;
2704
2705 const QChar * pSwEnd = KVSP_curCharPointer;
2706
2707 skipSpaces();
2708
2709 if(KVSP_curCharUnicode == '=')
2710 {
2711 KVSP_skipChar;
2712 skipSpaces();
2713 KviKvsTreeNodeData * p = parseCommandParameter();
2714 if(!p)
2715 {
2716 // must be an error :(
2717 if(error())
2718 {
2719 error(pBegin, __tr2qs_ctx("The above problem might be related to the switch dash and the following equal sign", "kvs"));
2720 delete sw;
2721 return nullptr;
2722 }
2723 else
2724 {
2725 // assume empty string
2726 p = new KviKvsTreeNodeConstantData(KVSP_curCharPointer, new KviKvsVariant(QString("")));
2727 }
2728 }
2729
2730 skipSpaces();
2731
2732 if(bLong)
2733 sw->addLong(QString(pSw, pSwEnd - pSw), p);
2734 else
2735 sw->addShort(pSw->toLower().unicode(), p);
2736 }
2737 else
2738 {
2739 if(bLong)
2740 sw->addLong(QString(pSw, pSwEnd - pSw), new KviKvsTreeNodeConstantData(KVSP_curCharPointer, new KviKvsVariant(true))); // empty param
2741 else
2742 sw->addShort(pSw->toLower().unicode(), new KviKvsTreeNodeConstantData(KVSP_curCharPointer, new KviKvsVariant(true))); // empty param
2743 }
2744 }
2745
2746 return sw;
2747 }
2748
parseCommandParameterList()2749 KviKvsTreeNodeDataList * KviKvsParser::parseCommandParameterList()
2750 {
2751 KviKvsTreeNodeDataList * l = new KviKvsTreeNodeDataList(KVSP_curCharPointer);
2752
2753 for(;;)
2754 {
2755 skipSpaces();
2756 switch(KVSP_curCharUnicode)
2757 {
2758 case 0:
2759 return l;
2760 break;
2761 case '\r':
2762 case '\n':
2763 case ';':
2764 KVSP_skipChar;
2765 return l;
2766 break;
2767 default:
2768 // anything else is a parameter
2769 KviKvsTreeNodeData * p = parseCommandParameter();
2770 if(!p)
2771 {
2772 // this is an error
2773 delete l;
2774 return nullptr;
2775 }
2776 l->addItem(p);
2777 break;
2778 }
2779 }
2780
2781 // never here
2782 KVSP_ASSERT(false);
2783 return nullptr;
2784 }
2785
parseCommaSeparatedParameterListNoTree()2786 KviPointerList<QString> * KviKvsParser::parseCommaSeparatedParameterListNoTree()
2787 {
2788 KviPointerList<QString> * l = new KviPointerList<QString>;
2789 l->setAutoDelete(true);
2790
2791 KVSP_skipChar;
2792
2793 for(;;)
2794 {
2795 skipSpaces();
2796 switch(KVSP_curCharUnicode)
2797 {
2798 case 0:
2799 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of script in parameter list", "kvs"));
2800 delete l;
2801 return nullptr;
2802 break;
2803 case '\r':
2804 case '\n':
2805 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of line in parameter list", "kvs"));
2806 delete l;
2807 return nullptr;
2808 break;
2809 /*
2810 case ',':
2811 KVSP_skipChar;
2812 break;
2813 case ')':
2814 KVSP_skipChar;
2815 return l;
2816 break;
2817 */
2818 default:
2819 {
2820 // anything else is a parameter
2821 const QChar * pBegin = KVSP_curCharPointer;
2822 KviKvsTreeNodeData * p = parseCommaSeparatedParameter();
2823 if(!p)
2824 {
2825 // this is an error
2826 delete l;
2827 return nullptr;
2828 }
2829 delete p;
2830 QString * s = new QString(QString(pBegin, KVSP_curCharPointer - pBegin).trimmed());
2831 l->append(s);
2832
2833 switch(KVSP_curCharUnicode)
2834 {
2835 case ',':
2836 KVSP_skipChar;
2837 break;
2838 case ')':
2839 KVSP_skipChar;
2840 return l;
2841 break;
2842 }
2843 }
2844 break;
2845 }
2846 }
2847
2848 // never here
2849 KVSP_ASSERT(false);
2850 return nullptr;
2851 }
2852
parseCommaSeparatedParameterList()2853 KviKvsTreeNodeDataList * KviKvsParser::parseCommaSeparatedParameterList()
2854 {
2855 KviKvsTreeNodeDataList * l = new KviKvsTreeNodeDataList(KVSP_curCharPointer);
2856
2857 KVSP_skipChar;
2858
2859 for(;;)
2860 {
2861 skipSpaces();
2862 switch(KVSP_curCharUnicode)
2863 {
2864 case 0:
2865 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of script in parameter list", "kvs"));
2866 delete l;
2867 return nullptr;
2868 break;
2869 case '\r':
2870 case '\n':
2871 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of line in parameter list", "kvs"));
2872 delete l;
2873 return nullptr;
2874 break;
2875 /*
2876 case ',':
2877 KVSP_skipChar;
2878 break;
2879 case ')':
2880 KVSP_skipChar;
2881 return l;
2882 break;
2883 */
2884 default:
2885 // anything else is a parameter
2886 KviKvsTreeNodeData * p = parseCommaSeparatedParameter();
2887 if(!p)
2888 {
2889 // this is an error
2890 delete l;
2891 return nullptr;
2892 }
2893 l->addItem(p);
2894
2895 switch(KVSP_curCharUnicode)
2896 {
2897 case ',':
2898 KVSP_skipChar;
2899 break;
2900 case ')':
2901 KVSP_skipChar;
2902 return l;
2903 break;
2904 }
2905 break;
2906 }
2907 }
2908
2909 // never here
2910 KVSP_ASSERT(false);
2911 return nullptr;
2912 }
2913
2914 #define LITERAL_PARAM_PARSING_FUNCTION_BEGIN(__funcname) \
2915 KviKvsTreeNodeConstantData * KviKvsParser::__funcname() \
2916 { \
2917 QString szValue; \
2918 \
2919 [[maybe_unused]] const QChar * pStart = KVSP_curCharPointer; \
2920 const QChar * pBegin = KVSP_curCharPointer; \
2921 int iLen = 0; \
2922 [[maybe_unused]] int iNestedTerminators = 0; \
2923 \
2924 for(;;) \
2925 { \
2926 switch(KVSP_curCharUnicode) \
2927 {
2928
2929 #define LITERAL_PARAM_PARSING_FUNCTION_WARN_NESTED_TERMINATOR \
2930 if(!_OUTPUT_MUTE) \
2931 warning(KVSP_curCharPointer, __tr2qs_ctx("Nested character %q corresponding to expected terminator, this might confuse me a bit: it is a good idea to enclose it in quotes", "kvs"), KVSP_curCharPointer); \
2932 KVSP_skipChar; \
2933 iNestedTerminators++; \
2934 iLen++; \
2935 break;
2936
2937 #define LITERAL_PARAM_PARSING_FUNCTION_END_WITH_EXPECTED_TERMINATOR \
2938 if(iNestedTerminators > 0) \
2939 { \
2940 if(!_OUTPUT_MUTE) \
2941 warning(KVSP_curCharPointer, __tr2qs_ctx("Skipping nested terminator character %q", "kvs"), KVSP_curCharPointer); \
2942 KVSP_skipChar; \
2943 iNestedTerminators--; \
2944 iLen++; \
2945 } \
2946 else \
2947 { \
2948 if(iLen > 0) \
2949 szValue.append(QString(pBegin, iLen)); \
2950 { \
2951 bool bOk; \
2952 kvs_int_t iVal = szValue.toLong(&bOk); \
2953 if(bOk) \
2954 return new KviKvsTreeNodeConstantData(pBegin, new KviKvsVariant(iVal)); \
2955 kvs_real_t dVal = szValue.toDouble(&bOk); \
2956 if(bOk) \
2957 return new KviKvsTreeNodeConstantData(pBegin, new KviKvsVariant(dVal)); \
2958 } \
2959 return new KviKvsTreeNodeConstantData(pBegin, new KviKvsVariant(szValue)); \
2960 } \
2961 break;
2962
2963 #define LITERAL_PARAM_PARSING_FUNCTION_GENERIC_END \
2964 if(iLen > 0) \
2965 szValue.append(QString(pBegin, iLen)); \
2966 return new KviKvsTreeNodeConstantData(pBegin, new KviKvsVariant(szValue)); \
2967 break; \
2968 case '\\': \
2969 if(iLen > 0) \
2970 szValue.append(QString(pBegin, iLen)); \
2971 KVSP_skipChar; \
2972 switch(KVSP_curCharUnicode) \
2973 { \
2974 case 0: \
2975 warning(KVSP_curCharPointer - 1, __tr2qs_ctx("Stray backslash at the end of the script", "kvs")); \
2976 iLen = 0; \
2977 break; \
2978 case '\r': \
2979 case '\n': \
2980 KVSP_skipChar; \
2981 pBegin = KVSP_curCharPointer; \
2982 iLen = 0; \
2983 break; \
2984 case 'r': \
2985 KVSP_skipChar; \
2986 pBegin = KVSP_curCharPointer; \
2987 szValue.append(QChar('\r')); \
2988 iLen = 0; \
2989 break; \
2990 case 'n': \
2991 KVSP_skipChar; \
2992 pBegin = KVSP_curCharPointer; \
2993 szValue.append(QChar('\n')); \
2994 iLen = 0; \
2995 break; \
2996 case 't': \
2997 KVSP_skipChar; \
2998 pBegin = KVSP_curCharPointer; \
2999 szValue.append(QChar('\t')); \
3000 iLen = 0; \
3001 break; \
3002 default: \
3003 pBegin = KVSP_curCharPointer; \
3004 KVSP_skipChar; \
3005 iLen = 1; \
3006 break; \
3007 } \
3008 break; \
3009 default: \
3010 KVSP_skipChar; \
3011 iLen++; \
3012 break; \
3013 } \
3014 } \
3015 KVSP_ASSERT(false); \
3016 return nullptr; \
3017 }
3018
3019 LITERAL_PARAM_PARSING_FUNCTION_BEGIN(parseCommandLiteralParameter)
3020
3021 case 0:
3022 case '$':
3023 case '%':
3024 case '@':
3025 case '\r':
3026 case '\n':
3027 case '"':
3028 case ';':
3029 case ' ':
3030 case '\t':
3031 LITERAL_PARAM_PARSING_FUNCTION_GENERIC_END
3032
3033 LITERAL_PARAM_PARSING_FUNCTION_BEGIN(parseStringLiteralParameter)
3034
3035 case 0:
3036 case '$':
3037 case '%':
3038 case '@':
3039 case '\r':
3040 case '\n':
3041 case '"':
3042 LITERAL_PARAM_PARSING_FUNCTION_GENERIC_END
3043
3044 /*
3045 LITERAL_PARAM_PARSING_FUNCTION_BEGIN(parseArrayIndexLiteralParameter)
3046 case '\t':
3047 case ' ':
3048 case ']':
3049 LITERAL_PARAM_PARSING_FUNCTION_END
3050 */
3051
3052 LITERAL_PARAM_PARSING_FUNCTION_BEGIN(parseHashKeyLiteralParameter)
3053
3054 case '{':
3055 LITERAL_PARAM_PARSING_FUNCTION_WARN_NESTED_TERMINATOR
3056 case '}':
3057 LITERAL_PARAM_PARSING_FUNCTION_END_WITH_EXPECTED_TERMINATOR
3058 case 0:
3059 case '$':
3060 case '%':
3061 case '@':
3062 case '\r':
3063 case '\n':
3064 case '"':
3065 case '\t':
3066 case ' ':
3067 LITERAL_PARAM_PARSING_FUNCTION_GENERIC_END
3068
3069 LITERAL_PARAM_PARSING_FUNCTION_BEGIN(parseCommaSeparatedLiteralParameter)
3070
3071 case '(':
3072 LITERAL_PARAM_PARSING_FUNCTION_WARN_NESTED_TERMINATOR
3073 case ')':
3074 LITERAL_PARAM_PARSING_FUNCTION_END_WITH_EXPECTED_TERMINATOR
3075 case 0:
3076 case '$':
3077 case '%':
3078 case '@':
3079 case '\r':
3080 case '\n':
3081 case '"':
3082 case ',':
3083 case ' ':
3084 case '\t':
3085 LITERAL_PARAM_PARSING_FUNCTION_GENERIC_END
3086
3087 LITERAL_PARAM_PARSING_FUNCTION_BEGIN(parseSingleLiteralParameterInParenthesis)
3088
3089 case '(':
3090 LITERAL_PARAM_PARSING_FUNCTION_WARN_NESTED_TERMINATOR
3091 case ')':
3092 LITERAL_PARAM_PARSING_FUNCTION_END_WITH_EXPECTED_TERMINATOR
3093 case 0:
3094 case '$':
3095 case '%':
3096 case '@':
3097 case '\r':
3098 case '\n':
3099 case '"':
3100 case ' ':
3101 case '\t':
3102 LITERAL_PARAM_PARSING_FUNCTION_GENERIC_END
3103
3104 LITERAL_PARAM_PARSING_FUNCTION_BEGIN(parseBindingOperationLiteralParameter)
3105
3106 case 0:
3107 case '$':
3108 case '%':
3109 case '@':
3110 case '\r':
3111 case '\n':
3112 case '"':
3113 case '/':
3114 LITERAL_PARAM_PARSING_FUNCTION_GENERIC_END
3115
3116 /*
3117 KviKvsTreeNodeData * KviKvsParser::parseArrayIndex()
3118 {
3119 KviPointerList<KviKvsTreeNodeData> * l = new KviPointerList<KviKvsTreeNodeData>();
3120 l->setAutoDelete(true);
3121
3122 const QChar * pBegin = KVSP_curCharPointer;
3123
3124 //KVSP_skipChar;
3125
3126 for(;;)
3127 {
3128 switch(KVSP_curCharUnicode)
3129 {
3130 case 0:
3131 delete l;
3132 warning(pBegin,__tr2qs_ctx("Unterminated array index","kvs"));
3133 error(KVSP_curCharPointer,__tr2qs_ctx("Unexpected end of script in array index (missing ']' character?)","kvs"));
3134 return nullptr;
3135 break;
3136 case '\n':
3137 delete l;
3138 warning(pBegin,__tr2qs_ctx("Unterminated array index","kvs"));
3139 error(KVSP_curCharPointer,__tr2qs_ctx("Unexpected end of line in array index (missing ']' character or unescaped newline)","kvs"));
3140 return nullptr;
3141 break;
3142 case ' ':
3143 case '\t':
3144 skipSpaces();
3145 if(KVSP_curCharUnicode != ']')
3146 {
3147 delete l;
3148 warning(pBegin,__tr2qs_ctx("Unterminated array index","kvs"));
3149 switch(KVSP_curCharUnicode)
3150 {
3151 case 0:
3152 error(KVSP_curCharPointer,__tr2qs_ctx("Unexpected end of script in array index (missing ']' character?)","kvs"));
3153 break;
3154 case '\n':
3155 error(KVSP_curCharPointer,__tr2qs_ctx("Unexpected end of line in array index (missing ']' character or unescaped newline)","kvs"));
3156 break;
3157 default:
3158 error(KVSP_curCharPointer,__tr2qs_ctx("Unexpected character '%q' (Unicode %x) in array index: it should be already terminated","kvs"),KVSP_curCharPointer,KVSP_curCharUnicode);
3159 break;
3160 }
3161 return nullptr;
3162 }
3163 goto end_of_the_array_index;
3164 break;
3165 case '$':
3166 case '%':
3167 {
3168 // this may be a data reference
3169 KviKvsTreeNodeData * p = parseParameterPercentOrDollar();
3170 if(!p)
3171 {
3172 // this is an error
3173 delete l;
3174 return nullptr;
3175 }
3176 l->append(p);
3177 }
3178 break;
3179 case ']':
3180 {
3181 // end of the array index
3182 goto end_of_the_array_index;
3183 }
3184 break;
3185 case '"':
3186 {
3187 // string (should we parse strings in array indexes anyway ?).. well "1"$count might be a valid one in the end
3188 KviKvsTreeNodeData * p = parseStringParameter();
3189 if(!p)
3190 {
3191 // error
3192 delete l;
3193 return nullptr;
3194 }
3195 l->append(p);
3196 }
3197 break;
3198 default:
3199 {
3200 // anything else is a literal
3201 l->append(parseArrayIndexLiteralParameter());
3202 }
3203 break;
3204 }
3205 }
3206 end_of_the_array_index:
3207 if(l->count() > 1)
3208 {
3209 // complex parameter needed
3210 return new KviKvsTreeNodeCompositeData(l);
3211 } else {
3212 // a single parameter in the list
3213 l->setAutoDelete(false);
3214 KviKvsTreeNodeData * p = l->first();
3215 delete l;
3216 return p;
3217 }
3218
3219 }
3220 */
3221
parseHashKey()3222 KviKvsTreeNodeData * KviKvsParser::parseHashKey()
3223 {
3224 KviPointerList<KviKvsTreeNodeData> * l = new KviPointerList<KviKvsTreeNodeData>();
3225 l->setAutoDelete(true);
3226
3227 const QChar * pBegin = KVSP_curCharPointer;
3228
3229 //KVSP_skipChar;
3230
3231 for(;;)
3232 {
3233 switch(KVSP_curCharUnicode)
3234 {
3235 case 0:
3236 delete l;
3237 warning(pBegin, __tr2qs_ctx("Unterminated hash key", "kvs"));
3238 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of script in hash key (missing '}' character?)", "kvs"));
3239 return nullptr;
3240 break;
3241 case '\r':
3242 case '\n':
3243 delete l;
3244 warning(pBegin, __tr2qs_ctx("Unterminated hash key", "kvs"));
3245 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of line in hash key (missing '}' character or unescaped newline)", "kvs"));
3246 return nullptr;
3247 break;
3248 case ' ':
3249 case '\t':
3250 skipSpaces();
3251 if(KVSP_curCharUnicode != '}')
3252 {
3253 // separate by single spaces
3254 l->append(new KviKvsTreeNodeConstantData(KVSP_curCharPointer, new KviKvsVariant(QString(" "))));
3255 }
3256 else
3257 {
3258 goto end_of_the_hash_key;
3259 }
3260 break;
3261 case '$':
3262 case '%':
3263 case '@':
3264 {
3265 // this may be a data reference
3266 KviKvsTreeNodeData * p = parseParameterPercentOrDollar();
3267 if(!p)
3268 {
3269 // this is an error
3270 delete l;
3271 return nullptr;
3272 }
3273 l->append(p);
3274 }
3275 break;
3276 case '}':
3277 {
3278 // end of the array index
3279 goto end_of_the_hash_key;
3280 }
3281 break;
3282 case '"':
3283 {
3284 // string
3285 KviKvsTreeNodeData * p = parseStringParameter();
3286 if(!p)
3287 {
3288 // error
3289 delete l;
3290 return nullptr;
3291 }
3292 l->append(p);
3293 }
3294 break;
3295 default:
3296 {
3297 // anything else is a literal
3298 l->append(parseHashKeyLiteralParameter());
3299 }
3300 break;
3301 }
3302 }
3303 end_of_the_hash_key:
3304 if(l->count() > 1)
3305 {
3306 // complex parameter needed
3307 return new KviKvsTreeNodeCompositeData(pBegin, l);
3308 }
3309 else
3310 {
3311 // a single parameter in the list
3312 l->setAutoDelete(false);
3313 KviKvsTreeNodeData * p = l->first();
3314 delete l;
3315 return p;
3316 }
3317 // never reached
3318 return nullptr;
3319 }
3320
3321 /*
3322 PARENTHESIS_PARAMETER_PARSING_FUNCTION_BEGIN(parseCommaSeparatedParameter)
3323 case 0:
3324 case ',':
3325 case ')':
3326 case '\n':
3327 PARENTHESIS_PARAMETER_PARSING_FUNCTION_END()
3328
3329 #define PARENTHESIS_PARAMETER_PARSING_FUNCTION_BEGIN(_name) \
3330 */
3331
parseCommaSeparatedParameter()3332 KviKvsTreeNodeData * KviKvsParser::parseCommaSeparatedParameter()
3333 {
3334 KviPointerList<KviKvsTreeNodeData> * l = new KviPointerList<KviKvsTreeNodeData>;
3335 l->setAutoDelete(true);
3336
3337 const QChar * pBegin = KVSP_curCharPointer;
3338
3339 for(;;)
3340 {
3341 switch(KVSP_curCharUnicode)
3342 {
3343 case 0:
3344 case ',':
3345 case ')':
3346 case '\r':
3347 case '\n':
3348 // not a part of a parameter
3349 goto end_of_function_parameter;
3350 break;
3351 case '$':
3352 case '%':
3353 case '@':
3354 {
3355 // this may be a data reference
3356 KviKvsTreeNodeData * p = parseParameterPercentOrDollar();
3357 if(!p)
3358 {
3359 // this is an error
3360 delete l;
3361 return nullptr;
3362 }
3363 l->append(p);
3364 }
3365 break;
3366 case ' ':
3367 case '\t':
3368 skipSpaces();
3369 if((KVSP_curCharUnicode != ')') && (KVSP_curCharUnicode != ','))
3370 {
3371 // separate by single spaces
3372 l->append(new KviKvsTreeNodeConstantData(KVSP_curCharPointer, new KviKvsVariant(QString(" "))));
3373 }
3374 else
3375 {
3376 goto end_of_function_parameter;
3377 }
3378 break;
3379 case '"':
3380 {
3381 // this is a string
3382 KviKvsTreeNodeData * p = parseStringParameter();
3383 if(!p)
3384 {
3385 // this is an error
3386 delete l;
3387 return nullptr;
3388 }
3389 l->append(p);
3390 }
3391 break;
3392 default:
3393 {
3394 // anything else is a literal
3395 l->append(parseCommaSeparatedLiteralParameter());
3396 }
3397 break;
3398 }
3399 }
3400 end_of_function_parameter:
3401 if(l->count() > 1)
3402 {
3403 // complex parameter needed
3404 KviKvsTreeNodeData * p = new KviKvsTreeNodeCompositeData(pBegin, l);
3405 p->setEndingLocation(KVSP_curCharPointer);
3406 return p;
3407 }
3408 else
3409 {
3410 // a single parameter in the list, or no params at all
3411 l->setAutoDelete(false);
3412 KviKvsTreeNodeData * p = l->first();
3413 delete l;
3414 if(!p)
3415 p = new KviKvsTreeNodeConstantData(KVSP_curCharPointer, new KviKvsVariant());
3416 p->setEndingLocation(KVSP_curCharPointer);
3417 return p;
3418 }
3419 // never reached
3420 return nullptr;
3421 }
3422
parseSingleParameterInParenthesis()3423 KviKvsTreeNodeData * KviKvsParser::parseSingleParameterInParenthesis()
3424 {
3425 KviPointerList<KviKvsTreeNodeData> * l = new KviPointerList<KviKvsTreeNodeData>;
3426 l->setAutoDelete(true);
3427
3428 const QChar * pBegin = KVSP_curCharPointer;
3429
3430 for(;;)
3431 {
3432 switch(KVSP_curCharUnicode)
3433 {
3434 case ')':
3435 // not a part of a parameter
3436 KVSP_skipChar;
3437 goto end_of_function_parameter;
3438 break;
3439 case 0:
3440 case '\r':
3441 case '\n':
3442 // not a part of a parameter
3443 goto end_of_function_parameter;
3444 break;
3445 case '$':
3446 case '%':
3447 case '@':
3448 {
3449 // this may be a data reference
3450 KviKvsTreeNodeData * p = parseParameterPercentOrDollar();
3451 if(!p)
3452 {
3453 // this is an error
3454 delete l;
3455 return nullptr;
3456 }
3457 l->append(p);
3458 }
3459 break;
3460 case ' ':
3461 case '\t':
3462 skipSpaces();
3463 if((KVSP_curCharUnicode != ')') && (KVSP_curCharUnicode != ','))
3464 {
3465 // separate by single spaces
3466 l->append(new KviKvsTreeNodeConstantData(KVSP_curCharPointer, new KviKvsVariant(QString(" "))));
3467 }
3468 else
3469 {
3470 goto end_of_function_parameter;
3471 }
3472 break;
3473 case '"':
3474 {
3475 // this is a string
3476 KviKvsTreeNodeData * p = parseStringParameter();
3477 if(!p)
3478 {
3479 // this is an error
3480 delete l;
3481 return nullptr;
3482 }
3483 l->append(p);
3484 }
3485 break;
3486 default:
3487 {
3488 // anything else is a literal
3489 l->append(parseSingleLiteralParameterInParenthesis());
3490 }
3491 break;
3492 }
3493 }
3494 end_of_function_parameter:
3495 if(l->count() > 1)
3496 {
3497 // complex parameter needed
3498 KviKvsTreeNodeData * p = new KviKvsTreeNodeCompositeData(pBegin, l);
3499 p->setEndingLocation(KVSP_curCharPointer);
3500 return p;
3501 }
3502 else
3503 {
3504 // a single parameter in the list or list empty at all
3505 l->setAutoDelete(false);
3506 KviKvsTreeNodeData * p = l->first();
3507 if(p)
3508 p->setEndingLocation(KVSP_curCharPointer);
3509 delete l;
3510 return p;
3511 }
3512 // never reached
3513 return nullptr;
3514 }
3515
parseStringParameter()3516 KviKvsTreeNodeData * KviKvsParser::parseStringParameter()
3517 {
3518 KVSP_ASSERT(KVSP_curCharUnicode == '"');
3519
3520 KviPointerList<KviKvsTreeNodeData> * l = new KviPointerList<KviKvsTreeNodeData>();
3521 l->setAutoDelete(true);
3522
3523 const QChar * pBegin = KVSP_curCharPointer;
3524
3525 KVSP_skipChar;
3526
3527 for(;;)
3528 {
3529 switch(KVSP_curCharUnicode)
3530 {
3531 case 0:
3532 delete l;
3533 warning(pBegin, __tr2qs_ctx("Unterminated string constant", "kvs"));
3534 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of script in string constant (missing \" character?)", "kvs"));
3535 return nullptr;
3536 break;
3537 case '\r':
3538 case '\n':
3539 delete l;
3540 warning(pBegin, __tr2qs_ctx("Unterminated string constant", "kvs"));
3541 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of line in string constant (missing \" character or unescaped newline)", "kvs"));
3542 return nullptr;
3543 break;
3544 case '$':
3545 case '%':
3546 case '@':
3547 {
3548 // this may be a data reference
3549 KviKvsTreeNodeData * p = parseParameterPercentOrDollar();
3550 if(!p)
3551 {
3552 // this is an error
3553 delete l;
3554 return nullptr;
3555 }
3556 l->append(p);
3557 }
3558 break;
3559 case '"':
3560 {
3561 // end of the string
3562 KVSP_skipChar;
3563 goto end_of_the_string;
3564 }
3565 break;
3566 default:
3567 {
3568 // anything else is a literal
3569 l->append(parseStringLiteralParameter());
3570 }
3571 break;
3572 }
3573 }
3574 end_of_the_string:
3575 if(l->count() > 1)
3576 {
3577 // complex parameter needed
3578 // it is also an implicit string cast
3579 return new KviKvsTreeNodeCompositeData(pBegin, l);
3580 }
3581 else
3582 {
3583 if(l->count() > 0)
3584 {
3585 // a single parameter in the list
3586 // we need an explicit string cast here (it is the most common cast)
3587 l->setAutoDelete(false);
3588 KviKvsTreeNodeData * p = l->first();
3589 delete l;
3590 return new KviKvsTreeNodeStringCast(pBegin, p);
3591 }
3592 else
3593 {
3594 // no parameters at all.. return straight empty string (no need to cast)
3595 delete l;
3596 return new KviKvsTreeNodeConstantData(pBegin, new KviKvsVariant(new QString()));
3597 }
3598 }
3599 // never reached
3600 return nullptr;
3601 }
3602
parseCommandParameter(bool bPreferNumeric)3603 KviKvsTreeNodeData * KviKvsParser::parseCommandParameter(bool bPreferNumeric)
3604 {
3605 KviPointerList<KviKvsTreeNodeData> * l = new KviPointerList<KviKvsTreeNodeData>;
3606 l->setAutoDelete(true);
3607
3608 bool bGotLiteral = false;
3609
3610 const QChar * pBegin = KVSP_curCharPointer;
3611
3612 for(;;)
3613 {
3614 switch(KVSP_curCharUnicode)
3615 {
3616 case 0:
3617 case ' ':
3618 case '\t':
3619 case '\r':
3620 case '\n':
3621 case ';':
3622 // not a part of a parameter
3623 goto jumpout;
3624 break;
3625 case '$':
3626 case '%':
3627 case '@':
3628 {
3629 // this may be a data reference
3630 KviKvsTreeNodeData * p = parseParameterPercentOrDollar();
3631 if(!p)
3632 {
3633 // this is an error
3634 delete l;
3635 return nullptr;
3636 }
3637 l->append(p);
3638 }
3639 break;
3640 case '"':
3641 {
3642 // this is a string
3643 KviKvsTreeNodeData * p = parseStringParameter();
3644 if(!p)
3645 {
3646 // this is an error
3647 delete l;
3648 return nullptr;
3649 }
3650 l->append(p);
3651 }
3652 break;
3653 default:
3654 {
3655 bGotLiteral = true;
3656 // anything else is a literal
3657 l->append(parseCommandLiteralParameter());
3658 }
3659 break;
3660 }
3661 }
3662 jumpout:
3663 if(l->count() > 1)
3664 {
3665 // complex parameter needed
3666 KviKvsTreeNodeData * p = new KviKvsTreeNodeCompositeData(pBegin, l);
3667 p->setEndingLocation(KVSP_curCharPointer);
3668 return p;
3669 }
3670 else
3671 {
3672 // a single parameter in the list or empty list at all
3673 l->setAutoDelete(false);
3674 KviKvsTreeNodeData * p = l->first();
3675 delete l;
3676 if(p)
3677 {
3678 if(bGotLiteral)
3679 {
3680 // a single literal parameter
3681 if(bPreferNumeric)
3682 {
3683 // attempt to convert to a numeric format if this is a constant data item
3684 p->convertStringConstantToNumeric();
3685 }
3686 }
3687 p->setEndingLocation(KVSP_curCharPointer);
3688 }
3689 return p;
3690 }
3691 // never reached
3692 return nullptr;
3693 }
3694