1 //=============================================================================
2 //
3 // File : KviKvsParser_specialCommands.cpp
4 // Creation date : Thu 06 Now 2003 14.14 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 "KviKvsParserMacros.h"
30 #include "KviKvsObjectFunctionHandler.h"
31 #include "KviLocale.h"
32 #include "KviCommandFormatter.h"
33
34 /*
35 in fact this is not a fully special command
36 it is special only in the sense of parsing.
37 Once parsed, the command is routed to the perl module
38 with the interpreter code as FIRST parameter and the other parameters
39 of the command following.
40 the help page for perl.begin is in the perl module
41 the help page for python.begin is in the python module
42
43 perl.begin(context) <perl code> perl.end
44 python.begin <python code> python.end
45 */
46 #define IMPLEMENT_EXTERNAL_INTERPRETER_BEGIN(__name) \
47 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommand##__name##Begin() \
48 { \
49 const QChar * pBegin = KVSP_curCharPointer; \
50 \
51 skipSpaces(); \
52 KviKvsTreeNodeDataList * dl; \
53 if(KVSP_curCharUnicode == '(') \
54 { \
55 dl = parseCommaSeparatedParameterList(); \
56 if(!dl) \
57 return nullptr; \
58 } \
59 else \
60 { \
61 dl = new KviKvsTreeNodeDataList(pBegin); \
62 } \
63 \
64 if(!KVSP_curCharIsEndOfBuffer) \
65 KVSP_skipChar; \
66 \
67 if(!skipSpacesAndNewlines()) \
68 { \
69 delete dl; \
70 return nullptr; \
71 } \
72 \
73 /* allow a ';' after [interpreter].begin */ \
74 if(KVSP_curCharIsEndOfCommand && !KVSP_curCharIsEndOfBuffer) \
75 { \
76 KVSP_skipChar; \
77 if(!skipSpacesAndNewlines()) \
78 { \
79 delete dl; \
80 return nullptr; \
81 } \
82 } \
83 \
84 const QChar * pInterpreterBegin = KVSP_curCharPointer; \
85 QString szName; \
86 szName.sprintf("%s", #__name); \
87 szName = szName.toLower(); \
88 \
89 /* now look for [interpreter].end[terminator] */ \
90 QString szInterpreterEnd(szName); \
91 szInterpreterEnd += ".end"; \
92 const QChar * pInterpreterEnd; \
93 for(;;) \
94 { \
95 while(KVSP_curCharUnicode && (KVSP_curCharUnicode != 'p') && (KVSP_curCharUnicode != 'P')) \
96 KVSP_skipChar; \
97 if(KVSP_curCharIsEndOfBuffer) \
98 { \
99 delete dl; \
100 QString szErr = "Unexpected end of command buffer while looking for the "; \
101 szErr += szInterpreterEnd; \
102 szErr += " statement"; \
103 \
104 error(KVSP_curCharPointer, __tr2qs_ctx(szErr.toUtf8().data(), "kvs")); \
105 return nullptr; \
106 } \
107 pInterpreterEnd = KVSP_curCharPointer; \
108 \
109 if(KviQString::equalCIN(szInterpreterEnd, KVSP_curCharPointer, szInterpreterEnd.size())) \
110 { \
111 KVSP_skipNChars(szInterpreterEnd.size()); \
112 if(KVSP_curCharIsEndOfCommand || (KVSP_curCharUnicode == ' ') || (KVSP_curCharUnicode == '\t')) \
113 { \
114 /* yeah! */ \
115 QString szInterpreter(pInterpreterBegin, pInterpreterEnd - pInterpreterBegin); \
116 dl->prependItem(new KviKvsTreeNodeConstantData(pInterpreterBegin, new KviKvsVariant(szInterpreter))); \
117 while(!KVSP_curCharIsEndOfCommand) \
118 KVSP_skipChar; \
119 if(!KVSP_curCharIsEndOfBuffer) \
120 KVSP_skipChar; \
121 break; \
122 } \
123 else \
124 { \
125 KVSP_backNChars(szInterpreterEnd.size() - 1); \
126 } \
127 } \
128 else \
129 { \
130 KVSP_skipChar; \
131 } \
132 } \
133 \
134 return new KviKvsTreeNodeModuleSimpleCommand(pBegin, szName, "begin", dl); \
135 }
136
137 IMPLEMENT_EXTERNAL_INTERPRETER_BEGIN(Perl)
IMPLEMENT_EXTERNAL_INTERPRETER_BEGIN(Python)138 IMPLEMENT_EXTERNAL_INTERPRETER_BEGIN(Python)
139
140 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandBreak()
141 {
142 /*
143 @doc: break
144 @type:
145 command
146 @title:
147 break
148 @syntax:
149 break
150 @short:
151 Interrupts an iteration loop
152 @description:
153 Interrupts an iteration loop like [cmd]while[/cmd].[br]
154 This command always jumps out of a single code block.[br]
155 If called outside an iteration loop, will act just like [cmd]halt[/cmd]
156 has been called but has no additional semantics for events.[br]
157 */
158 const QChar * pBegin = KVSP_curCharPointer; // FIXME: this is not accurate at all : it may be even the end of the cmd
159 skipSpaces();
160 if(!KVSP_curCharIsEndOfCommand)
161 {
162 warning(KVSP_curCharPointer, __tr2qs_ctx("Trailing garbage at the end of the break command: ignored", "kvs"));
163 }
164
165 while(!KVSP_curCharIsEndOfCommand)
166 KVSP_skipChar;
167 if(!KVSP_curCharIsEndOfBuffer)
168 KVSP_skipChar;
169 return new KviKvsTreeNodeSpecialCommandBreak(pBegin);
170 }
171
parseSpecialCommandContinue()172 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandContinue()
173 {
174 /*
175 @doc: continue
176 @type:
177 command
178 @title:
179 continue
180 @syntax:
181 continue
182 @short:
183 Continues an iteration loop
184 @description:
185 Continues an iteration loop like [cmd]while[/cmd].[br]
186 This command always jumps to the next iteration in the code block.[br]
187 */
188 const QChar * pBegin = KVSP_curCharPointer; // FIXME: this is not accurate at all : it may be even the end of the cmd
189 skipSpaces();
190 if(!KVSP_curCharIsEndOfCommand)
191 {
192 warning(KVSP_curCharPointer, __tr2qs_ctx("Trailing garbage at the end of the continue command: ignored", "kvs"));
193 }
194
195 while(!KVSP_curCharIsEndOfCommand)
196 KVSP_skipChar;
197 if(!KVSP_curCharIsEndOfBuffer)
198 KVSP_skipChar;
199 return new KviKvsTreeNodeSpecialCommandContinue(pBegin);
200 }
201
parseSpecialCommandUnset()202 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandUnset()
203 {
204 /*
205 @doc: unset
206 @type:
207 command
208 @title:
209 unset
210 @syntax:
211 unset <variable_list>
212 @keyterms:
213 unsetting variables
214 @short:
215 Unsets a set of variables
216 @description:
217 Unsets the specified list of comma separated variables.
218 It is equivalent to assigning the default empty value
219 to each variable on its own: just does it all at once.
220 Note that KVIrc automatically frees the local variable memory
221 when they go out of scope and the global variable memory
222 when KVIrc terminates.
223 @examples:
224 [example]
225 %a = pippo
226 %b = 1
227 [cmd]echo[/cmd] %a %b
228 unset %a %b
229 [cmd]echo[/cmd] %a %b
230 [/example]
231 */
232
233 const QChar * pCmdBegin = KVSP_curCharPointer;
234
235 KviPointerList<KviKvsTreeNodeVariable> * pVarList = new KviPointerList<KviKvsTreeNodeVariable>;
236 pVarList->setAutoDelete(true);
237
238 while(KVSP_curCharUnicode == '%')
239 {
240 KviKvsTreeNodeVariable * d = parsePercent();
241 if(!d)
242 {
243 delete pVarList;
244 return nullptr;
245 }
246
247 pVarList->append(d);
248
249 skipSpaces();
250
251 if(KVSP_curCharUnicode == ',')
252 {
253 KVSP_skipChar;
254 skipSpaces();
255 }
256 }
257
258 if(!KVSP_curCharIsEndOfCommand)
259 {
260 delete pVarList;
261 warning(KVSP_curCharPointer, __tr2qs_ctx("The 'unset' command needs a variable list", "kvs"));
262 error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where a variable was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
263 return nullptr;
264 }
265
266 if(!KVSP_curCharIsEndOfBuffer)
267 KVSP_skipChar;
268
269 if(pVarList->count() < 1)
270 {
271 delete pVarList;
272 warning(KVSP_curCharPointer, __tr2qs_ctx("'unset' command used without a variable list", "kvs"));
273 return nullptr; // null unset ?
274 }
275 return new KviKvsTreeNodeSpecialCommandUnset(pCmdBegin, pVarList);
276 }
277
parseSpecialCommandGlobal()278 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandGlobal()
279 {
280 /*
281 @doc: global
282 @type:
283 command
284 @title:
285 global
286 @syntax:
287 global <variable_list>
288 @keyterms:
289 explicitly declaring global variables
290 @short:
291 Explicitly declares global variables
292 @description:
293 Declares a list of global variables.
294 Once a variable has been declared as global
295 it refers to the global KVIrc instance for the scope of the script.[br]
296 Global variables are shared between scripts and keep their
297 value until they are explicitly [cmd]unset[/cmd] or KVIrc quits.[br]
298 This command can be used to override the default behaviour of
299 declaring global variables by starting them with an uppercase letter
300 and declaring local variables by starting them with a lowercase one.[br][br]
301 In any particular instance instance where a global variable may interfere with desired operation,
302 you can specifically [b][cmd]unset[/cmd][/b] the desired variable.
303 @examples:
304 [example]
305 global %a, %b, %c;
306 [/example]
307 @seealso:
308 [fnc]$global[/fnc]
309 [cmd]unset[/cmd]
310 [fnc]$hash[/fnc]
311 */
312
313 while(KVSP_curCharUnicode == '%')
314 {
315 KVSP_skipChar;
316 const QChar * pBegin = KVSP_curCharPointer;
317
318 while((KVSP_curCharIsLetterOrNumber) || (KVSP_curCharUnicode == '_'))
319 KVSP_skipChar;
320
321 QString szIdentifier(pBegin, KVSP_curCharPointer - pBegin);
322
323 if(!m_pGlobals)
324 {
325 m_pGlobals = new KviPointerHashTable<QString, QString>(17, false);
326 m_pGlobals->setAutoDelete(true);
327 }
328 m_pGlobals->replace(szIdentifier, new QString());
329
330 skipSpaces();
331
332 if(KVSP_curCharUnicode == ',')
333 {
334 KVSP_skipChar;
335 skipSpaces();
336 }
337 }
338
339 if(!KVSP_curCharIsEndOfCommand)
340 {
341 warning(KVSP_curCharPointer, __tr2qs_ctx("The 'global' command needs a list of variables", "kvs"));
342 error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where a variable was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
343 return nullptr;
344 }
345
346 if(!KVSP_curCharIsEndOfBuffer)
347 KVSP_skipChar;
348 return nullptr;
349 }
350
parseSpecialCommandClass()351 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandClass()
352 {
353 /*
354 @doc: class
355 @title:
356 class
357 @short:
358 Defines a new object class
359 @keyterms:
360 defining an object class
361 @type:
362 command
363 @syntax:
364 class(<classname:string>[,<base_class_name:string>])
365 {
366 [internal] [function] <function_name>[([<parameter reminder>])]
367 {
368 <function body>
369 }
370
371 ...
372 }
373 @description:
374 Defines a new implementation of the class <classname>.
375 If an implementation of that class was already existing
376 it is removed with all the derived classes (and all the instances of this class
377 and the derived ones are destroyed).
378 <base_class_name> is the name of the class that the
379 new class has to inherit from.[br]
380 If <base_class_name> is omitted, the new class inherits automatically
381 from [class:object]object[/class].[br]
382 Note:[br]
383 The keywords "function" and "event" that were used in KVIrc versions
384 previous to 3.0.0 have been removed since they are "useless".[br]
385 The function keyword, however, is still permitted.
386 The keyword "internal" is useful when you want to hide
387 certain function from the outside world. An internal function
388 cannot be called by anyone else but the object instance itself. Note that
389 this is different from the C++ "protected" or "private" keywords
390 that refer to the object's class instead of the object instance.
391 The <parameter reminder> part is an optional string
392 that can be used to sign the parameters that the function expects;
393 it acts as a programmer reminder or comment and it has no other
394 meaning in KVIrc scripting. The <parameter reminder> respects the syntax
395 of an expression, so it is terminated by a closed parenthesis.
396 It's rather dangerous to use this command inside an object
397 function handler: if the class definition <class> was already
398 existing and it is a parent of the object's class, you might
399 end up executing "non-existent" code.[br]
400 As a thumb rule, use this command only outside object function handlers.[br]
401 [br][br]
402 Only for the curious: implementing protected and private access
403 list on members would have a considerable runtime overhead because
404 of the strange nature of the KVS language. Object member calls
405 are resolved completely at runtime (and that permits a lot of funny tricks
406 like [cmd]privateimpl[/cmd]) but unfortunately this also forces us
407 to check access lists at runtime. OK, this would be a relatively small footprint for the "private"
408 keyword where we need to run UP the called object inheritance hierarchy
409 but would have a significant performance footprint for the "protected"
410 keyword where we would need to traverse the [b]whole[/b] inheritance tree of the called and calling
411 objects... "internal" still allows hiding members in a lot of situations
412 and is really fast to verify at runtime: no inheritance tree traversal
413 is needed and only object pointers are compared.
414 @examples:
415 [example]
416 class(myclass,[class]object[/class])
417 {
418 constructor
419 {
420 [cmd]echo[/cmd] Hey this is my constructor
421 [cmd]echo[/cmd] I have been just created
422 }
423
424 destructor
425 {
426 [cmd]echo[/cmd] Oops! being destroyed
427 }
428
429 sayHello(this function expects no parameters)
430 {
431 [cmd]echo[/cmd] Hello world!
432 }
433 }
434 [/example]
435 @seealso:
436 [cmd]privateimpl[/cmd], [cmd]killclass[/cmd], [cmd]clearobjects[/cmd], [fnc]$classDefined[/fnc](),
437 [doc:objects]Objects documentation[/doc]
438 */
439
440 if(KVSP_curCharUnicode != '(')
441 {
442 error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where an open parenthesis was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
443 return nullptr;
444 }
445
446 const QChar * pBegin = KVSP_curCharPointer;
447
448 KviKvsTreeNodeDataList * l = parseCommaSeparatedParameterList();
449 if(!l)
450 return nullptr;
451
452 KviKvsTreeNodeSpecialCommandClass * pClass = new KviKvsTreeNodeSpecialCommandClass(pBegin, l);
453
454 if(!skipSpacesAndNewlines())
455 {
456 delete pClass;
457 return nullptr;
458 }
459
460 if(KVSP_curCharUnicode != '{')
461 {
462 errorBadChar(KVSP_curCharPointer, '{', "class");
463 delete pClass;
464 return nullptr;
465 }
466
467 KVSP_skipChar;
468
469 if(!skipSpacesAndNewlines())
470 {
471 delete pClass;
472 return nullptr;
473 }
474
475 while(KVSP_curCharUnicode != '}')
476 {
477 if((KVSP_curCharUnicode == '#') || (KVSP_curCharUnicode == '/'))
478 {
479 parseComment();
480 if(error())
481 {
482 delete pClass;
483 return nullptr;
484 }
485 if(!skipSpacesAndNewlines())
486 {
487 delete pClass;
488 return nullptr;
489 }
490 continue;
491 }
492
493 const QChar * pLabelBegin = KVSP_curCharPointer;
494
495 if(KVSP_curCharIsLetter)
496 {
497 KVSP_skipChar;
498 while(KVSP_curCharIsLetterOrNumber)
499 KVSP_skipChar;
500 }
501
502 if(KVSP_curCharIsEndOfBuffer)
503 {
504 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of buffer in class definition", "kvs"));
505 delete pClass;
506 return nullptr;
507 }
508
509 if(KVSP_curCharPointer == pLabelBegin)
510 {
511 error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where a function name was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
512 delete pClass;
513 return nullptr;
514 }
515
516 QString szLabel(pLabelBegin, KVSP_curCharPointer - pLabelBegin);
517
518 unsigned int uHandlerFlags = 0;
519
520 if(szLabel.toLower() == "internal")
521 {
522 uHandlerFlags |= KviKvsObjectFunctionHandler::Internal;
523 skipSpaces();
524 if(KVSP_curCharUnicode != '(')
525 {
526 pLabelBegin = KVSP_curCharPointer;
527
528 while(KVSP_curCharIsLetterOrNumber)
529 KVSP_skipChar;
530
531 if(KVSP_curCharIsEndOfBuffer)
532 {
533 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of buffer in class definition", "kvs"));
534 delete pClass;
535 return nullptr;
536 }
537
538 if(KVSP_curCharPointer == pLabelBegin)
539 {
540 error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where a function name was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
541 delete pClass;
542 return nullptr;
543 }
544 szLabel = QString(pLabelBegin, KVSP_curCharPointer - pLabelBegin);
545 }
546 }
547
548 if(szLabel.toLower() == "function")
549 {
550 skipSpaces();
551 if(KVSP_curCharUnicode != '(')
552 {
553 pLabelBegin = KVSP_curCharPointer;
554
555 while(KVSP_curCharIsLetterOrNumber)
556 KVSP_skipChar;
557
558 if(KVSP_curCharIsEndOfBuffer)
559 {
560 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of buffer in class definition", "kvs"));
561 delete pClass;
562 return nullptr;
563 }
564
565 if(KVSP_curCharPointer == pLabelBegin)
566 {
567 error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where a function name was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
568 delete pClass;
569 return nullptr;
570 }
571 szLabel = QString(pLabelBegin, KVSP_curCharPointer - pLabelBegin);
572 }
573 }
574
575 if(!skipSpacesAndNewlines())
576 {
577 delete pClass;
578 return nullptr;
579 }
580 QString szReminder;
581 if(KVSP_curCharUnicode == '(')
582 {
583 KVSP_skipChar;
584 const QChar * pReminderBegin = KVSP_curCharPointer;
585 while((!(KVSP_curCharIsEndOfBuffer)) && (KVSP_curCharUnicode != ')'))
586 KVSP_skipChar;
587
588 if(KVSP_curCharIsEndOfBuffer)
589 {
590 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of buffer in function parameter list reminder", "kvs"));
591 delete pClass;
592 return nullptr;
593 }
594 szReminder = QString(pReminderBegin, KVSP_curCharPointer - pReminderBegin);
595 KVSP_skipChar;
596
597 if(!skipSpacesAndNewlines())
598 {
599 delete pClass;
600 return nullptr;
601 }
602 }
603
604 if(KVSP_curCharIsEndOfBuffer)
605 {
606 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of buffer in class definition", "kvs"));
607 delete pClass;
608 return nullptr;
609 }
610
611 if(KVSP_curCharUnicode != '{')
612 {
613 errorBadChar(KVSP_curCharPointer, '{', "class");
614 delete pClass;
615 return nullptr;
616 }
617
618 pBegin = KVSP_curCharPointer;
619 KviKvsTreeNodeInstruction * pInstruction = parseInstruction();
620 if(!pInstruction)
621 {
622 // may be an empty instruction
623 if(error())
624 {
625 delete pClass;
626 return nullptr;
627 }
628 }
629 delete pInstruction;
630 int iLen = KVSP_curCharPointer - pBegin;
631 QString szInstruction;
632 if(iLen > 0)
633 {
634 szInstruction = QString(pBegin, KVSP_curCharPointer - pBegin);
635 KviCommandFormatter::bufferFromBlock(szInstruction);
636 }
637
638 pClass->addFunctionDefinition(new KviKvsTreeNodeSpecialCommandClassFunctionDefinition(pLabelBegin, szLabel, szInstruction, szReminder, uHandlerFlags));
639
640 if(!skipSpacesAndNewlines())
641 {
642 delete pClass;
643 return nullptr;
644 }
645 }
646
647 KVSP_skipChar;
648
649 return pClass;
650 }
651
parseSpecialCommandWhile()652 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandWhile()
653 {
654 /*
655 @doc: while
656 @type:
657 command
658 @title:
659 while
660 @syntax:
661 while (<condition>) <command>
662 @keyterms:
663 iteration commands, flow control commands
664 @short:
665 Iteration command
666 @description:
667 Executes <command> while the <condition> evaluates
668 to true (non zero result).[br]
669 <command> may be either a single command or a block of commands.[br]
670 It can contain the [cmd]break[/cmd] command: in that case the
671 execution of the <command> will be immediately interrupted and the control
672 transferred to the command following this while block.[br]
673 It is valid for <command> to be an empty command terminated with a [b];[/b].
674 <condition> is an expression as the ones evaluated by [doc:expressioneval]$(*)[/doc]
675 with the following extensions:[br]
676 If <condition> is a string, its length is evaluated: in this way a non-empty string
677 causes the <condition> to be true, an empty string causes it to be false.[br]
678 If <condition> is an array, its size is evaluated: in this way a non-empty array
679 causes the <condition> to be true, an empty array causes it to be false.[br]
680 If <condition> is a hash, the number of its entries is evaluated: in this way a non-empty hash
681 causes the <condition> to be true, an empty hash causes it to be false.[br]
682 @examples:
683 [example]
684 %i = 0;
685 while(%i < 100)%i++
686 while(%i > 0)
687 {
688 %i -= 10
689 if(%i < 20)break;
690 }
691 echo %i
692 [/example]
693 */
694
695 if(KVSP_curCharUnicode != '(')
696 {
697 warning(KVSP_curCharPointer, __tr2qs_ctx("The while command needs an expression enclosed in parenthesis", "kvs"));
698 error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where an open parenthesis was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
699 return nullptr;
700 }
701
702 const QChar * pBegin = KVSP_curCharPointer;
703
704 KVSP_skipChar;
705
706 KviKvsTreeNodeExpression * e = parseExpression(')');
707 if(!e)
708 {
709 // must be an error
710 return nullptr;
711 }
712
713 if(!skipSpacesAndNewlines())
714 {
715 delete e;
716 return nullptr;
717 }
718
719 if(KVSP_curCharUnicode == 0)
720 {
721 warning(pBegin, __tr2qs_ctx("The last while command in the buffer has no conditional instructions: it's senseless", "kvs"));
722 warning(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of script while looking for the instruction block of the while command", "kvs"));
723 }
724
725 KviKvsTreeNodeInstruction * i = parseInstruction();
726 if(!i)
727 {
728 if(error())
729 {
730 delete e;
731 return nullptr;
732 }
733 } // else, just an empty instruction
734
735 return new KviKvsTreeNodeSpecialCommandWhile(pBegin, e, i);
736 }
737
parseSpecialCommandDo()738 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandDo()
739 {
740 /*
741 @doc: do
742 @type:
743 command
744 @title:
745 do
746 @syntax:
747 do <command> while (<condition>)
748 @keyterms:
749 iteration commands, flow control commands
750 @short:
751 Iteration command
752 @description:
753 Executes <command> once then evaluates the <condition>.
754 If <condition> evaluates to true (non zero result) then repeats the execution again.[br]
755 <command> may be either a single command or a block of commands.[br]
756 It can contain the [cmd]break[/cmd] command: in that case the
757 execution of the <command> will be immediately interrupted and the control
758 transferred to the command following this while block.[br]
759 It is valid for <command> to be an empty command terminated with a ';'.
760 <condition> is an expression as the ones evaluated by [doc:expressioneval]$(*)[/doc]
761 with the following extensions:[br]
762 If <condition> is a string, its length is evaluated: in this way a non-empty string
763 causes the <condition> to be true, an empty string causes it to be false.[br]
764 If <condition> is an array, its size is evaluated: in this way a non-empty array
765 causes the <condition> to be true, an empty array causes it to be false.[br]
766 If <condition> is a hash, the number of its entries is evaluated: in this way a non-empty hash
767 causes the <condition> to be true, an empty hash causes it to be false.[br]
768 @examples:
769 [example]
770 %i = 0;
771 do %i++; while(%i < 100);
772 echo "After first execution: %i";
773 %i = 10
774 do
775 {
776 echo "Executed!";
777 %i++;
778 }
779 while(%i < 1)
780 echo "After second execution: %i";
781 [/example]
782 @seealso:
783 [cmd]while[/cmd]
784 */
785
786 const QChar * pBegin = KVSP_curCharPointer;
787
788 KviKvsTreeNodeInstruction * i = parseInstruction();
789 if(!i)
790 {
791 if(error())
792 return nullptr;
793 }
794
795 if(!skipSpacesAndNewlines())
796 {
797 if(i)
798 delete i;
799 return nullptr;
800 }
801
802 static const unsigned short while_chars[10] = { 'W', 'w', 'H', 'h', 'I', 'i', 'L', 'l', 'E', 'e' };
803
804 for(int j = 0; j < 10; j++)
805 {
806 if(KVSP_curCharUnicode != while_chars[j])
807 {
808 j++;
809 if(KVSP_curCharUnicode != while_chars[j])
810 {
811 if(KVSP_curCharIsEndOfBuffer)
812 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of command after the 'do' command block: expected 'while' keyword", "kvs"));
813 else
814 error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where a 'while' keyword was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
815 if(i)
816 delete i;
817 return nullptr;
818 }
819 }
820 else
821 j++;
822 KVSP_skipChar;
823 }
824
825 if(!skipSpacesAndNewlines())
826 {
827 if(i)
828 delete i;
829 return nullptr;
830 }
831
832 if(KVSP_curCharUnicode != '(')
833 {
834 warning(KVSP_curCharPointer, __tr2qs_ctx("The 'while' block of the 'do' command needs an expression enclosed in parenthesis", "kvs"));
835 errorBadChar(KVSP_curCharPointer, '(', "do");
836 if(i)
837 delete i;
838 return nullptr;
839 }
840
841 KVSP_skipChar;
842
843 KviKvsTreeNodeExpression * e = parseExpression(')');
844 if(!e)
845 {
846 // must be an error
847 if(i)
848 delete i;
849 return nullptr;
850 }
851
852 skipSpaces();
853
854 if(!KVSP_curCharIsEndOfCommand)
855 {
856 warning(KVSP_curCharPointer, __tr2qs_ctx("Garbage string after the expression in 'do' command: ignored", "kvs"));
857 while(!KVSP_curCharIsEndOfCommand)
858 KVSP_skipChar;
859 }
860
861 if(!KVSP_curCharIsEndOfBuffer)
862 KVSP_skipChar;
863
864 return new KviKvsTreeNodeSpecialCommandDo(pBegin, e, i);
865 }
866
parseSpecialCommandIf()867 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandIf()
868 {
869 /*
870 @doc: if
871 @type:
872 command
873 @title:
874 if
875 @syntax:
876 if (<condition>) <command1> [else <command2>]
877 @keyterms:
878 conditional commands, flow control commands
879 @short:
880 Flow control command
881 @description:
882 Executes <command1> if the <condition> evaluates to true (non zero result).[br]
883 If the [i]else part[/i] is given, <command2> is executed.[br]
884 If the <condition> evaluates to false (result == '0').[br]
885 the [b]<condition>[/b] is an expression evaluated in the same way as [doc:expressioneval]$(*)[/doc]
886 with the following extensions:[br]
887 If the [b]<condition>[/b] is a string, its length is evaluated - in this way a non-empty string
888 causes the <condition> to be true, and an empty string causes it to be false.[br]
889 If the [b]<condition>[/b] is an array, its size is evaluated - a non-empty array is true, an empty array is false.[br]
890 If the [b]<condition>[/b] is a hash, the number of its entries is evaluated - a non-empty hash is true, an empty hash is false.[br]
891 @examples:
892 [example]
893 if(%a != 10)
894 [cmd]echo[/cmd] \%a was != 10
895 else
896 [cmd]echo[/cmd] \%a was 10!
897 [/example]
898 @seealso:
899 [doc:expressioneval]Expression evaluation identifier[/doc]
900
901 */
902
903 if(KVSP_curCharUnicode != '(')
904 {
905 warning(KVSP_curCharPointer, __tr2qs_ctx("The 'if' command needs an expression enclosed in parenthesis", "kvs"));
906 errorBadChar(KVSP_curCharPointer, '(', "if");
907 return nullptr;
908 }
909
910 const QChar * pBegin = KVSP_curCharPointer;
911
912 KVSP_skipChar;
913
914 KviKvsTreeNodeExpression * e = parseExpression(')');
915 if(!e)
916 {
917 // must be an error
918 return nullptr;
919 }
920
921 if(!skipSpacesAndNewlines())
922 {
923 delete e;
924 return nullptr;
925 }
926
927 if(KVSP_curCharUnicode == 0)
928 {
929 warning(pBegin, __tr2qs_ctx("The last if command in the buffer has no conditional instructions: it's senseless", "kvs"));
930 warning(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of script while looking for the instruction block of the if command", "kvs"));
931 }
932
933 KviKvsTreeNodeInstruction * i = parseInstruction();
934 if(!i)
935 {
936 if(error())
937 {
938 delete e;
939 return nullptr;
940 }
941 } // else, just an empty instruction
942
943 if(!skipSpacesAndNewlines())
944 {
945 if(i)
946 delete i;
947 return nullptr;
948 }
949
950 const QChar * pElse = KVSP_curCharPointer;
951
952 if((KVSP_curCharUnicode != 'e') && (KVSP_curCharUnicode != 'E'))
953 return new KviKvsTreeNodeSpecialCommandIf(pBegin, e, i, nullptr);
954 KVSP_skipChar;
955 if((KVSP_curCharUnicode != 'l') && (KVSP_curCharUnicode != 'L'))
956 {
957 KVSP_setCurCharPointer(pElse);
958 return new KviKvsTreeNodeSpecialCommandIf(pBegin, e, i, nullptr);
959 }
960 KVSP_skipChar;
961 if((KVSP_curCharUnicode != 's') && (KVSP_curCharUnicode != 'S'))
962 {
963 KVSP_setCurCharPointer(pElse);
964 return new KviKvsTreeNodeSpecialCommandIf(pBegin, e, i, nullptr);
965 }
966 KVSP_skipChar;
967 if((KVSP_curCharUnicode != 'e') && (KVSP_curCharUnicode != 'E'))
968 {
969 KVSP_setCurCharPointer(pElse);
970 return new KviKvsTreeNodeSpecialCommandIf(pBegin, e, i, nullptr);
971 }
972 KVSP_skipChar;
973 if(KVSP_curCharIsLetterOrNumber)
974 {
975 if((KVSP_curCharUnicode == 'i') || (KVSP_curCharUnicode == 'I'))
976 {
977 KVSP_skipChar;
978 if((KVSP_curCharUnicode == 'f') || (KVSP_curCharUnicode == 'F'))
979 {
980 KVSP_skipChar;
981 if(!KVSP_curCharIsLetterOrNumber)
982 {
983 // this is an "elseif"
984 KVSP_backChar;
985 KVSP_backChar;
986 // point to if
987 goto handle_else_instruction;
988 }
989 KVSP_backChar;
990 }
991 KVSP_backChar;
992 }
993
994 KVSP_setCurCharPointer(pElse);
995 return new KviKvsTreeNodeSpecialCommandIf(pBegin, e, i, nullptr);
996 }
997
998 handle_else_instruction:
999 if(!skipSpacesAndNewlines())
1000 {
1001 delete e;
1002 if(i)
1003 delete i;
1004 return nullptr;
1005 }
1006
1007 KviKvsTreeNodeInstruction * i2 = parseInstruction();
1008 if(!i2)
1009 {
1010 if(error())
1011 {
1012 delete e;
1013 if(i)
1014 delete i;
1015 return nullptr;
1016 }
1017 } // else, just an empty instruction
1018
1019 return new KviKvsTreeNodeSpecialCommandIf(pBegin, e, i, i2);
1020 }
1021
skipToEndOfForControlBlock()1022 bool KviKvsParser::skipToEndOfForControlBlock()
1023 {
1024 bool bInString = false;
1025 int iParLevel = 0;
1026
1027 for(;;)
1028 {
1029 switch(KVSP_curCharUnicode)
1030 {
1031 case '"':
1032 bInString = !bInString;
1033 KVSP_skipChar;
1034 break;
1035 case '(':
1036 if(!bInString)
1037 iParLevel++;
1038 KVSP_skipChar;
1039 break;
1040 case ')':
1041 if(!bInString)
1042 {
1043 if(iParLevel == 0)
1044 return true;
1045 else
1046 iParLevel--;
1047 }
1048 KVSP_skipChar;
1049 break;
1050 case 0:
1051 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of buffer while looking for the closing ')' in the 'for' command", "kvs"));
1052 return false;
1053 break;
1054 //case '\n':
1055 // that's ok.. it may have a parenthesis on the next line
1056 //KVSP_skipChar;
1057 //break;
1058 default:
1059 KVSP_skipChar;
1060 break;
1061 }
1062 }
1063 // not reached
1064 KVSP_ASSERT(false);
1065 return false;
1066 }
1067
parseSpecialCommandFor()1068 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandFor()
1069 {
1070 /*
1071 @doc: for
1072 @type:
1073 command
1074 @title:
1075 for
1076 @syntax:
1077 for (<initialization>;<condition>;<update>) <command>
1078 @keyterms:
1079 iterational control commands
1080 @short:
1081 Iteration control command
1082 @description:
1083 Executes <initialization> once then runs the following iteration loop:
1084 if <condition> evaluates to true then <command> is executed followed
1085 by the <update> command. The iteration is repeated until <condition> evaluates to false.[br]
1086 <condition> is an expression as the ones evaluated by [doc:expressioneval]$(*)[/doc]
1087 with the following extensions:[br]
1088 If <condition> is a string, its length is evaluated: in this way a non-empty string
1089 causes the <condition> to be true, an empty string causes it to be false.[br]
1090 If <condition> is an array, its size is evaluated: in this way a non-empty array
1091 causes the <condition> to be true, an empty array causes it to be false.[br]
1092 If <condition> is a hash, the number of its entries is evaluated: in this way a non-empty hash
1093 causes the <condition> to be true, an empty hash causes it to be false.[br]
1094 @examples:
1095 [example]
1096 for(%a = 0;%a < 100;%a++)
1097 echo %a
1098 [/example]
1099 */
1100
1101 if(KVSP_curCharUnicode != '(')
1102 {
1103 warning(KVSP_curCharPointer, __tr2qs_ctx("The 'for' command needs an expression enclosed in parenthesis", "kvs"));
1104 errorBadChar(KVSP_curCharPointer, '(', "for");
1105 return nullptr;
1106 }
1107
1108 const QChar * pForBegin = KVSP_curCharPointer;
1109
1110 KVSP_skipChar;
1111
1112 skipSpaces();
1113
1114 KviKvsTreeNodeInstruction * i1 = parseInstruction();
1115 if(!i1)
1116 {
1117 if(error())
1118 return nullptr;
1119 } // else just empty instruction
1120
1121 skipSpaces();
1122
1123 KviKvsTreeNodeExpression * e = parseExpression(';');
1124 if(!e)
1125 {
1126 if(error())
1127 {
1128 if(i1)
1129 delete i1;
1130 return nullptr;
1131 }
1132 } // else just empty expression : assume true
1133
1134 skipSpaces();
1135
1136 // skip to the first non matching ')' that is not in a string
1137
1138 const QChar * pBegin = KVSP_curCharPointer;
1139
1140 if(!skipToEndOfForControlBlock())
1141 {
1142 if(error()) // <-- that's always true
1143 {
1144 if(i1)
1145 delete i1;
1146 if(e)
1147 delete e;
1148 return nullptr;
1149 }
1150 }
1151
1152 // HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
1153 // KVSP_curCharPointer is const!
1154 // we shouldn't be able to modify it
1155 QChar cSave = *(KVSP_curCharPointer);
1156
1157 QChar * pHack = (QChar *)KVSP_curCharPointer;
1158 *pHack = QChar('\n');
1159
1160 KVSP_curCharPointer = pBegin;
1161
1162 KviKvsTreeNodeInstruction * i2 = parseInstruction();
1163 *pHack = cSave;
1164
1165 KVSP_setCurCharPointer(pHack);
1166 // EOF HACK EOF HACK EOF HACK EOF HACK EOF HACK EOF HACK EOF HACK
1167
1168 if(!i2)
1169 {
1170 if(error())
1171 {
1172 if(i1)
1173 delete i1;
1174 if(e)
1175 delete e;
1176 return nullptr;
1177 }
1178 } // else just empty instruction
1179
1180 skipSpaces();
1181
1182 if(KVSP_curCharUnicode != ')')
1183 {
1184 error(KVSP_curCharPointer, __tr2qs_ctx("Found char %q (Unicode %x) while looking for the terminating ')' in 'for' command", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
1185 if(i1)
1186 delete i1;
1187 if(e)
1188 delete e;
1189 if(i2)
1190 delete i2;
1191 return nullptr;
1192 }
1193
1194 KVSP_skipChar;
1195
1196 if(!skipSpacesAndNewlines())
1197 {
1198 if(i1)
1199 delete i1;
1200 if(e)
1201 delete e;
1202 if(i2)
1203 delete i2;
1204 return nullptr;
1205 }
1206
1207 KviKvsTreeNodeInstruction * loop = parseInstruction();
1208 if(!loop)
1209 {
1210 if(error())
1211 {
1212 if(i1)
1213 delete i1;
1214 if(e)
1215 delete e;
1216 if(i2)
1217 delete i2;
1218 return nullptr;
1219 }
1220
1221 if((!i1) && (!e) && (!i2))
1222 {
1223 error(pForBegin, __tr2qs_ctx("Empty infinite 'for' loop: fix the script", "kvs"));
1224 return nullptr;
1225 }
1226 } // else just an empty instruction
1227
1228 return new KviKvsTreeNodeSpecialCommandFor(pForBegin, i1, e, i2, loop);
1229 }
1230
parseSpecialCommandForeach()1231 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandForeach()
1232 {
1233 /*
1234 @doc: foreach
1235 @type:
1236 command
1237 @title:
1238 foreach
1239 @syntax:
1240 foreach [-a] (<variable>,[<item>[,<item>[,<item>[...]]]) <command>
1241 @keyterms:
1242 iteration commands, flow control commands
1243 @switches:
1244 !sw: -a | --all
1245 Include empty variables in the iteration loop.
1246 @short:
1247 Iteration command
1248 @description:
1249 Executed <command> while assigning to <variable> each <item>.[br]
1250 <item> may be a constant, a variable, an array, a dictionary or a function returning
1251 either a constant string an array reference or a dictionary reference.[br]
1252 If <item> is an array, a dictionary or a function that returns a dictionary or array reference
1253 the iteration is done through all the dictionary/array items.[br]
1254 Please note that the iteration order of dictionary items is undefined.[br]
1255 You can always break from the loop by using the [cmd]break[/cmd] command,
1256 or skip to the next item with continue.[br]
1257 foreach doesn't iterate over empty scalar variables (i.e. the ones set to [fnc]$nothing[/fnc])
1258 unless you use the -a switch. (Note that an array with *some* empty entries is [b]not[/b] empty so
1259 the iteration is in fact done).
1260 @examples:
1261 [example]
1262 foreach(%i,1,2,3,4,5,6,7,8,9)[cmd]echo[/cmd] %i
1263 foreach(%chan,[fnc]$window.list[/fnc](channel))[cmd]me[/cmd] -r=%chan This is a test!
1264 [comment]# This will work too, and will do the same job[/comment]
1265 %windows[] = [fnc]$window.list[/fnc](channel)
1266 foreach(%chan,%windows[])[cmd]me[/cmd] -r=%chan This is a test!
1267 [comment]# And this too[/comment]
1268 %windows[] = [fnc]$window.list[/fnc](channel)
1269 foreach(%key,[fnc]$keys[/fnc](%windows[]))[cmd]me[/cmd] -r=%windows[%key] This is a test!
1270 [comment]# Another interesting example[/comment]
1271 [cmd]alias[/cmd](test){ [cmd]return[/cmd] [fnc]$hash[/fnc](1,a,2,b,3,c,4,d); };
1272 foreach(%x,[fnc]$keys[/fnc]($test)){ [cmd]echo[/cmd] %x, $test{%x}; }
1273 [/example]
1274 */
1275
1276 if(KVSP_curCharUnicode != '(')
1277 {
1278 warning(KVSP_curCharPointer, __tr2qs_ctx("The 'foreach' command needs an expression enclosed in parenthesis", "kvs"));
1279 errorBadChar(KVSP_curCharPointer, '(', "foreach");
1280 return nullptr;
1281 }
1282
1283 const QChar * pForeachBegin = KVSP_curCharPointer;
1284
1285 KVSP_skipChar;
1286
1287 skipSpaces();
1288
1289 if((KVSP_curCharUnicode != '%') && (KVSP_curCharUnicode != '$') && (KVSP_curCharUnicode != '@'))
1290 {
1291 warning(KVSP_curCharPointer, __tr2qs_ctx("The 'foreach' command expects a writable iteration variable as first parameter", "kvs"));
1292 error(KVSP_curCharPointer, __tr2qs_ctx("Found character '%q' (Unicode %x) where '%' or '$' was expected: see /help foreach for the command syntax", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
1293 return nullptr;
1294 }
1295
1296 KviKvsTreeNodeData * d = parsePercentOrDollar();
1297 if(!d)
1298 return nullptr;
1299
1300 if(d->isFunctionCall() || d->isReadOnly())
1301 {
1302 warning(KVSP_curCharPointer, __tr2qs_ctx("The 'foreach' command expects a writable iteration variable as first parameter", "kvs"));
1303 if(d->isFunctionCall())
1304 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected function call as 'foreach' iteration variable", "kvs"));
1305 else
1306 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected read-only variable as 'foreach' iteration variable", "kvs"));
1307 delete d;
1308 return nullptr;
1309 }
1310
1311 skipSpaces();
1312 if(KVSP_curCharUnicode != ',')
1313 {
1314 if(KVSP_curCharUnicode == ')')
1315 {
1316 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of 'foreach' parameters: at least one iteration data argument must be given", "kvs"));
1317 delete d;
1318 return nullptr;
1319 }
1320 warning(KVSP_curCharPointer, __tr2qs_ctx("The 'foreach' command expects a comma separated list of iteration data items after the first parameter", "kvs"));
1321 errorBadChar(KVSP_curCharPointer, ',', "foreach");
1322 return nullptr;
1323 }
1324
1325 KviKvsTreeNodeDataList * l = parseCommaSeparatedParameterList();
1326 if(!l)
1327 return nullptr;
1328
1329 if(!skipSpacesAndNewlines())
1330 {
1331 delete d;
1332 delete l;
1333 return nullptr;
1334 }
1335
1336 const QChar * pLoopBegin = KVSP_curCharPointer;
1337
1338 KviKvsTreeNodeInstruction * loop = parseInstruction();
1339 if(!loop)
1340 {
1341 if(error())
1342 return nullptr;
1343 warning(pLoopBegin, __tr2qs_ctx("Found empty 'foreach' execution block: maybe you need to fix your script?", "kvs"));
1344 loop = new KviKvsTreeNodeInstructionBlock(pLoopBegin);
1345 }
1346
1347 return new KviKvsTreeNodeSpecialCommandForeach(pForeachBegin, d, l, loop);
1348 }
1349
parseSpecialCommandSwitch()1350 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandSwitch()
1351 {
1352 /*
1353 @doc: switch
1354 @type:
1355 command
1356 @title:
1357 switch
1358 @syntax:
1359 switch [-p] (<expression>)
1360 {
1361 case(<value>)[:]<command>
1362 [break]
1363 case(<value>)[:]<command>
1364 [break]
1365 ....
1366 match(<wildcard_expression>)[:]<command>
1367 [break]
1368 ....
1369 regexp(<regular_expression>)[:]<command>
1370 [break]
1371 ....
1372 case(<value>)[:]<command>
1373 [break]
1374 ....
1375 default[:]<command>
1376 [break]
1377 }
1378 @switches:
1379 !sw: -p | --passthrough
1380 Use c-style 'break' flow control
1381 @short:
1382 Another flow control command
1383 @description:
1384 The switch command is based on the standard C 'switch' keyword.
1385 It conditionally executes groups of commands chosen from a larger set of command groups.[br]
1386 First <expression> is evaluated (<expression> is any arithmetic or string expression).[br]
1387 Then the [b]match[/b], [b]regexp[/b], [b]case[/b] and [b]default[/b] labels are evaluated sequentially
1388 in the order of appearance.[br]
1389 [b]case(<value>)[:]<command>[/b][br]
1390 The <value> is evaluated and is compared against the result of <expression>.
1391 String comparison is case insensitive.[br]
1392 If <value> is equal to <expression> then <command> is executed.
1393 Please note that <command> must be either a single instruction or an instruction block [b]enclosed in braces[/b].
1394 During or after <command> execution, if a [cmd]break[/cmd] statement is encountered the execution of the switch
1395 is terminated, otherwise the next label is evaluated.[br]
1396 If the -p (--passthrough) option is enabled, than the switch command will execute all the instructions blocks
1397 until a [cmd]break[/cmd] statement is found.[br]
1398 [b]match(<value>)[:]<command>[/b][br]
1399 The <value> is expected to be a wildcard expression (wildcard characters being [b]*[/b] and [b]?[/b])
1400 that is matched in a case insensitive fashion against <expression>.[br]
1401 If a match occurs, the related <command> is executed.
1402 The [cmd]break[/cmd] statement is treated as in the case label.[br]
1403 [b]regexp(<value>)[:]<command>[/b][br]
1404 The <value> is expected to be a complete standard regular expression
1405 that is matched in a case insensitive fashion against <expression>.[br]
1406 If a match occurs, the related <command> is executed.
1407 The [cmd]break[/cmd] statement is treated as in the case label.[br]
1408 [b]default[:]<command>[/b][br]
1409 The default label is executed unconditionally if no previous label terminated execution with the [cmd]break[/cmd] statement.[br]
1410 @examples:
1411 [comment]# Try to change the 1 below to 2 or 3 to see the results[/comment]
1412 [example]
1413 %tmp = 1
1414 switch(%tmp)
1415 {
1416 case(1):
1417 echo \%tmp was 1!
1418 break;
1419 case(2)
1420 echo \%tmp was 2!
1421 break;
1422 default:
1423 echo \%tmp was not 1 nor 2: it was %tmp!
1424 break;
1425 }
1426 [/example]
1427 [example]
1428 [comment]# A more complex example: change the 1 in 2 or 3[/comment]
1429 %tmp = 1
1430 switch(%tmp)
1431 {
1432 case(1):
1433 echo \%tmp was 1!
1434 case(2)
1435 echo \%tmp was 2!
1436 break;
1437 default:
1438 echo \%tmp was either 1 or something different from 2 (%tmp)
1439 break;
1440 }
1441 [/example]
1442 [example]
1443 [comment]# An example of the -p switch[/comment]
1444 %tmp = 1
1445 switch -p (%tmp)
1446 {
1447 case(1):
1448 echo \%tmp was 1!
1449 case(2)
1450 echo \%tmp was 1 or 2!
1451 break;
1452 default:
1453 echo \%tmp was not 1 or 2 (%tmp)
1454 break;
1455 }
1456 [/example]
1457 [example]
1458 [comment]# An example with strings[/comment]
1459 %tmp = "This is a test"
1460 %tmp2 = "This is not a test"
1461 switch(%tmp)
1462 {
1463 case(%tmp2)
1464 echo \%tmp == \%tmp2
1465 break;
1466 case(%tmp)
1467 {
1468 [comment]# do not break here[/comment]
1469 echo "Yeah... It's stupid... \%tmp == \%tmp :D"
1470 }
1471 match("*TEST"):
1472 echo "Matched *TEST"
1473 regexp("[a-zA-Z ]*test"):
1474 echo "Matched [a-zA-Z ]*text"
1475 regexp("[a-zA-Z ]*not[a-zA-Z ]*"):
1476 echo "Matched [a-zA-Z ]*not[a-zA-Z ]*"
1477 default:
1478 echo This is executed anyway (unless some break was called)
1479 break;
1480 }
1481 [/example]
1482 */
1483
1484 if(KVSP_curCharUnicode != '(')
1485 {
1486 warning(KVSP_curCharPointer, __tr2qs_ctx("The 'switch' command needs an expression enclosed in parenthesis", "kvs"));
1487 errorBadChar(KVSP_curCharPointer, '(', "switch");
1488 return nullptr;
1489 }
1490
1491 const QChar * pBegin = KVSP_curCharPointer;
1492
1493 KVSP_skipChar;
1494
1495 KviKvsTreeNodeExpression * e = parseExpression(')');
1496 if(!e)
1497 {
1498 // must be an error
1499 return nullptr;
1500 }
1501
1502 if(!skipSpacesAndNewlines())
1503 {
1504 delete e;
1505 return nullptr;
1506 }
1507
1508 if(KVSP_curCharUnicode != '{')
1509 {
1510 errorBadChar(KVSP_curCharPointer, '{', "switch");
1511 delete e;
1512 return nullptr;
1513 }
1514
1515 KVSP_skipChar;
1516
1517 if(!skipSpacesAndNewlines())
1518 {
1519 delete e;
1520 return nullptr;
1521 }
1522
1523 KviKvsTreeNodeSpecialCommandSwitch * pSwitch = new KviKvsTreeNodeSpecialCommandSwitch(pBegin, e);
1524
1525 KviKvsTreeNodeSpecialCommandSwitchLabel * pLabel = nullptr;
1526
1527 while(KVSP_curCharUnicode != '}')
1528 {
1529 // look for a 'case','match','default' or 'regexpr' label
1530
1531 const QChar * pLabelBegin = KVSP_curCharPointer;
1532 while(KVSP_curCharIsLetter)
1533 KVSP_skipChar;
1534
1535 if(KVSP_curCharIsEndOfBuffer)
1536 {
1537 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of buffer in switch condition block", "kvs"));
1538 delete pSwitch;
1539 return nullptr;
1540 }
1541
1542 if(KVSP_curCharPointer == pLabelBegin)
1543 {
1544 error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where a 'case','match','regexp','default' or 'break' label was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
1545 delete pSwitch;
1546 return nullptr;
1547 }
1548
1549 QString szLabel(pLabelBegin, KVSP_curCharPointer - pLabelBegin);
1550 QString szLabelLow = szLabel.toLower();
1551
1552 bool bNeedParam = true;
1553
1554 if(szLabelLow == "case")
1555 {
1556 pLabel = new KviKvsTreeNodeSpecialCommandSwitchLabelCase(pLabelBegin);
1557 }
1558 else if(szLabelLow == "match")
1559 {
1560 pLabel = new KviKvsTreeNodeSpecialCommandSwitchLabelMatch(pLabelBegin);
1561 }
1562 else if(szLabelLow == "regexp")
1563 {
1564 pLabel = new KviKvsTreeNodeSpecialCommandSwitchLabelRegexp(pLabelBegin);
1565 }
1566 else if(szLabelLow == "default")
1567 {
1568 pLabel = new KviKvsTreeNodeSpecialCommandSwitchLabelDefault(pLabelBegin);
1569 bNeedParam = false;
1570 }
1571 else if(szLabelLow == "break")
1572 {
1573 if(pLabel)
1574 {
1575 pLabel->setTerminatingBreak(true);
1576 skipSpaces();
1577 if(KVSP_curCharUnicode == ';')
1578 KVSP_skipChar;
1579 if(!skipSpacesAndNewlines())
1580 {
1581 delete pSwitch;
1582 delete pLabel;
1583 return nullptr;
1584 }
1585 continue;
1586 }
1587 else
1588 {
1589 error(pLabelBegin, __tr2qs_ctx("Found 'break' label where a 'case','match','regexp' or 'default' label was expected", "kvs"));
1590 delete pSwitch;
1591 return nullptr;
1592 }
1593 }
1594 else
1595 {
1596 error(pLabelBegin, __tr2qs_ctx("Found token '%Q' where a 'case','match','regexp','default' or 'break' label was expected", "kvs"), &szLabel);
1597 delete pSwitch;
1598 return nullptr;
1599 }
1600
1601 if(bNeedParam)
1602 {
1603 skipSpaces();
1604 if(KVSP_curCharUnicode != '(')
1605 {
1606 errorBadChar(KVSP_curCharPointer, '(', "switch");
1607 delete pSwitch;
1608 delete pLabel;
1609 return nullptr;
1610 }
1611 KVSP_skipChar;
1612
1613 KviKvsTreeNodeData * pParameter = parseSingleParameterInParenthesis();
1614 if(!pParameter)
1615 {
1616 delete pSwitch;
1617 delete pLabel;
1618 return nullptr;
1619 }
1620
1621 pLabel->setParameter(pParameter);
1622 }
1623
1624 skipSpaces();
1625 if(KVSP_curCharUnicode == ':')
1626 KVSP_skipChar;
1627 if(!skipSpacesAndNewlines())
1628 {
1629 delete pSwitch;
1630 delete pLabel;
1631 return nullptr;
1632 }
1633
1634 KviKvsTreeNodeInstruction * pInstruction = parseInstruction();
1635 if(!pInstruction)
1636 {
1637 // may be an empty instruction
1638 if(error())
1639 {
1640 delete pSwitch;
1641 delete pLabel;
1642 return nullptr;
1643 }
1644 }
1645
1646 pLabel->setInstruction(pInstruction);
1647 pSwitch->addLabel(pLabel);
1648
1649 if(!skipSpacesAndNewlines())
1650 {
1651 delete pSwitch;
1652 return nullptr;
1653 }
1654 }
1655
1656 KVSP_skipChar;
1657
1658 if(pSwitch->isEmpty())
1659 {
1660 error(pBegin, __tr2qs_ctx("Senseless empty switch command: fix the script", "kvs"));
1661 delete pSwitch;
1662 return nullptr;
1663 }
1664
1665 return pSwitch;
1666 }
1667
parseSpecialCommandDefpopupLabelPopup()1668 KviKvsTreeNodeSpecialCommandDefpopupLabelPopup * KviKvsParser::parseSpecialCommandDefpopupLabelPopup()
1669 {
1670 if(KVSP_curCharUnicode != '{')
1671 {
1672 errorBadChar(KVSP_curCharPointer, '{', "defpopup");
1673 return nullptr;
1674 }
1675
1676 std::unique_ptr<KviKvsTreeNodeSpecialCommandDefpopupLabelPopup> pPopup{new KviKvsTreeNodeSpecialCommandDefpopupLabelPopup(KVSP_curCharPointer)};
1677
1678 KVSP_skipChar;
1679
1680 if(!skipSpacesAndNewlines())
1681 return nullptr;
1682
1683 while(KVSP_curCharUnicode != '}')
1684 {
1685 // look for 'label', 'prologue', 'epilogue', 'popup', 'item', 'separator', 'separatorid' or 'extpopup' label
1686 const QChar * pLabelBegin = KVSP_curCharPointer;
1687 while(KVSP_curCharIsLetter)
1688 KVSP_skipChar;
1689
1690 if(KVSP_curCharIsEndOfBuffer)
1691 {
1692 error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of buffer in defpopup block", "kvs"));
1693 return nullptr;
1694 }
1695
1696 if(KVSP_curCharPointer == pLabelBegin)
1697 {
1698 error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where a 'prologue', 'separator', 'separatorid', 'label', 'popup', 'item', 'extpopup' or 'epilogue' label was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
1699 return nullptr;
1700 }
1701
1702 QString szLabel(pLabelBegin, KVSP_curCharPointer - pLabelBegin);
1703 QString szLabelLow = szLabel.toLower();
1704
1705 KviPointerList<QString> * pParameters = nullptr;
1706
1707 QString szCondition;
1708
1709 #define EXTRACT_POPUP_LABEL_PARAMETERS \
1710 if(!skipSpacesAndNewlines()) \
1711 return nullptr; \
1712 if(KVSP_curCharUnicode != '(') \
1713 { \
1714 errorBadChar(KVSP_curCharPointer, '(', "defpopup"); \
1715 return nullptr; \
1716 } \
1717 pParameters = parseCommaSeparatedParameterListNoTree(); \
1718 if(!pParameters) \
1719 return nullptr;
1720
1721 #define EXTRACT_POPUP_LABEL_CONDITION \
1722 if(!skipSpacesAndNewlines()) \
1723 return nullptr; \
1724 if(KVSP_curCharUnicode == '(') \
1725 { \
1726 const QChar * pBegin = KVSP_curCharPointer; \
1727 KVSP_skipChar; \
1728 KviKvsTreeNodeExpression * pExpression = parseExpression(')'); \
1729 if(!pExpression) \
1730 { \
1731 if(pParameters) \
1732 delete pParameters; \
1733 return nullptr; \
1734 } \
1735 int cLen = (KVSP_curCharPointer - pBegin) - 2; \
1736 if(cLen > 0) \
1737 szCondition.setUnicode(pBegin + 1, cLen); \
1738 delete pExpression; \
1739 if(!skipSpacesAndNewlines()) \
1740 { \
1741 if(pParameters) \
1742 delete pParameters; \
1743 return nullptr; \
1744 } \
1745 }
1746
1747 if((szLabelLow == "prologue") || (szLabelLow == "epilogue"))
1748 {
1749 bool bPrologue = (szLabelLow == "prologue");
1750 if(!skipSpacesAndNewlines())
1751 return nullptr;
1752
1753 if(KVSP_curCharUnicode == '(')
1754 {
1755 EXTRACT_POPUP_LABEL_PARAMETERS
1756 if(!skipSpacesAndNewlines())
1757 {
1758 if(pParameters)
1759 delete pParameters;
1760 return nullptr;
1761 }
1762 }
1763 const QChar * pBegin = KVSP_curCharPointer;
1764 KviKvsTreeNodeInstruction * pInstruction = parseInstruction();
1765 if(pInstruction)
1766 {
1767 // in fact we don't need it at all, we just need the code buffer...
1768 delete pInstruction;
1769 }
1770 else
1771 {
1772 // may be an empty instruction
1773 if(error())
1774 {
1775 // error
1776 if(pParameters)
1777 delete pParameters;
1778 return nullptr;
1779 }
1780 // empty instruction
1781 if(bPrologue)
1782 warning(pBegin, __tr2qs_ctx("Found empty prologue block: maybe you need to fix the script?", "kvs"));
1783 else
1784 warning(pBegin, __tr2qs_ctx("Found empty epilogue block: maybe you need to fix the script?", "kvs"));
1785 }
1786 int iLen = KVSP_curCharPointer - pBegin;
1787 if(iLen > 0)
1788 {
1789 QString szInstruction(pBegin, KVSP_curCharPointer - pBegin);
1790 KviCommandFormatter::bufferFromBlock(szInstruction);
1791 QString * pItemName = pParameters ? pParameters->first() : nullptr;
1792 QString szItemName = pItemName ? *pItemName : QString();
1793 if(bPrologue)
1794 pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelPrologue(pLabelBegin, szInstruction, szItemName));
1795 else
1796 pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelEpilogue(pLabelBegin, szInstruction, szItemName));
1797 } // else the instruction was empty anyway: we don't need it in fact
1798 if(pParameters)
1799 delete pParameters;
1800 }
1801 else if(szLabelLow == "separator")
1802 {
1803 EXTRACT_POPUP_LABEL_CONDITION
1804 if(KVSP_curCharUnicode == ';')
1805 KVSP_skipChar;
1806 QString szItemName = "dummySeparator";
1807 pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelSeparator(pLabelBegin, szCondition, szItemName));
1808 }
1809 else if(szLabelLow == "separatorid")
1810 {
1811 EXTRACT_POPUP_LABEL_PARAMETERS
1812 EXTRACT_POPUP_LABEL_CONDITION
1813 if(KVSP_curCharUnicode == ';')
1814 KVSP_skipChar;
1815 QString * pItemName = pParameters->first();
1816 pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelSeparator(pLabelBegin, szCondition, pItemName ? *pItemName : QString()));
1817 delete pParameters;
1818 }
1819 else if(szLabelLow == "label")
1820 {
1821 EXTRACT_POPUP_LABEL_PARAMETERS
1822 EXTRACT_POPUP_LABEL_CONDITION
1823
1824 QString * pText = pParameters->first();
1825 if(!pText)
1826 {
1827 error(pLabelBegin, __tr2qs_ctx("Unexpected empty <text> field in label parameters. See /help defpopup for the syntax", "kvs"));
1828 delete pParameters;
1829 return nullptr;
1830 }
1831 QString * pIcon = pParameters->next();
1832 if(KVSP_curCharUnicode == ';')
1833 KVSP_skipChar;
1834 QString * pItemName = pParameters->next();
1835 pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelLabel(pLabelBegin, szCondition, *pText, pIcon ? *pIcon : QString(), pItemName ? *pItemName : QString()));
1836 delete pParameters;
1837 }
1838 else if(szLabelLow == "popup")
1839 {
1840 EXTRACT_POPUP_LABEL_PARAMETERS
1841 EXTRACT_POPUP_LABEL_CONDITION
1842
1843 QString * pText = pParameters->first();
1844 if(!pText)
1845 {
1846 error(pLabelBegin, __tr2qs_ctx("Unexpected empty <text> field in extpopup parameters. See /help defpopup for the syntax", "kvs"));
1847 delete pParameters;
1848 return nullptr;
1849 }
1850 QString * pIcon = pParameters->next();
1851 QString * pItemName = pParameters->next();
1852
1853 KviKvsTreeNodeSpecialCommandDefpopupLabelPopup * pSubPopup = parseSpecialCommandDefpopupLabelPopup();
1854 if(!pSubPopup)
1855 {
1856 delete pParameters;
1857 return nullptr;
1858 }
1859
1860 pSubPopup->setCondition(szCondition);
1861 pSubPopup->setText(*pText);
1862 pSubPopup->setItemName(pItemName ? *pItemName : QString());
1863 if(pIcon)
1864 pSubPopup->setIcon(*pIcon);
1865 pPopup->addLabel(pSubPopup);
1866 delete pParameters;
1867 }
1868 else if(szLabelLow == "item")
1869 {
1870 EXTRACT_POPUP_LABEL_PARAMETERS
1871 EXTRACT_POPUP_LABEL_CONDITION
1872
1873 QString * pText = pParameters->first();
1874 if(!pText)
1875 {
1876 error(pLabelBegin, __tr2qs_ctx("Unexpected empty <text> field in extpopup parameters. See /help defpopup for the syntax", "kvs"));
1877 delete pParameters;
1878 return nullptr;
1879 }
1880 QString * pIcon = pParameters->next();
1881 QString * pItemName = pParameters->next();
1882
1883 const QChar * pBegin = KVSP_curCharPointer;
1884 KviKvsTreeNodeInstruction * pInstruction = parseInstruction();
1885 if(pInstruction)
1886 {
1887 // in fact we don't need it: we just need the code block
1888 delete pInstruction;
1889 }
1890 else
1891 {
1892 // empty instruction or error ?
1893 if(error())
1894 {
1895 // error
1896 delete pParameters;
1897 return nullptr;
1898 }
1899 // empty instruction
1900 warning(pBegin, __tr2qs_ctx("Found empty instruction for popup item: maybe you need to fix the script?", "kvs"));
1901 }
1902 int iLen = KVSP_curCharPointer - pBegin;
1903 if(iLen > 0)
1904 {
1905 QString szInstruction(pBegin, KVSP_curCharPointer - pBegin);
1906 KviCommandFormatter::bufferFromBlock(szInstruction);
1907 pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelItem(pLabelBegin, szCondition, *pText, pIcon ? *pIcon : QString(), szInstruction, pItemName ? *pItemName : QString()));
1908 }
1909 else
1910 {
1911 // zero length instruction, but still add the item
1912 QString szInstruction = "";
1913 KviCommandFormatter::bufferFromBlock(szInstruction);
1914 pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelItem(pLabelBegin, szCondition, *pText, pIcon ? *pIcon : QString(), szInstruction, pItemName ? *pItemName : QString()));
1915 }
1916 delete pParameters;
1917 }
1918 else if(szLabelLow == "extpopup")
1919 {
1920 EXTRACT_POPUP_LABEL_PARAMETERS
1921 EXTRACT_POPUP_LABEL_CONDITION
1922
1923 QString * pText = pParameters->first();
1924 if(!pText)
1925 {
1926 error(pLabelBegin, __tr2qs_ctx("Unexpected empty <text> field in extpopup parameters. See /help defpopup for the syntax", "kvs"));
1927 delete pParameters;
1928 return nullptr;
1929 }
1930 QString * pName = pParameters->next();
1931 if(!pName)
1932 {
1933 error(pLabelBegin, __tr2qs_ctx("Unexpected empty <name> field in extpopup parameters. See /help defpopup for the syntax", "kvs"));
1934 delete pParameters;
1935 return nullptr;
1936 }
1937 QString * pIcon = pParameters->next();
1938 QString * pItemName = pParameters->next();
1939 if(KVSP_curCharUnicode == ';')
1940 KVSP_skipChar;
1941 pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelExtpopup(pLabelBegin, szCondition, *pText, pIcon ? *pIcon : QString(), *pName, pItemName ? *pItemName : QString()));
1942 delete pParameters;
1943 }
1944 else
1945 {
1946 error(pLabelBegin, __tr2qs_ctx("Found token '%Q' where a 'prologue', 'separator', 'separatorid', 'label', 'popup', 'item', 'extpopup' or 'epilogue' label was expected", "kvs"), &szLabel);
1947 return nullptr;
1948 }
1949
1950 if(!skipSpacesAndNewlines())
1951 return nullptr;
1952 }
1953
1954 KVSP_skipChar;
1955 return pPopup.release();
1956 }
1957
parseSpecialCommandDefpopup()1958 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandDefpopup()
1959 {
1960 // FIXME: This SHOULD be renamed to popup.create (NOT popup.define!)
1961 // and internally aliased to defpopup as backward compat
1962 // There should be then also popup.destroy etc..
1963
1964 /*
1965 @doc: defpopup
1966 @type:
1967 command
1968 @title:
1969 defpopup
1970 @syntax:
1971 defpopup [-m] (<popup_name>)
1972 {
1973 prologue[(<id>)] <prologue_command>
1974
1975 epilogue[(<id>)] <epilogue_command>
1976
1977 label(<text>[,<id>])[(<expression>)][;]
1978
1979 item(<text>[,<icon>[,<id>]])[(<expression>)]<command>
1980
1981 popup(<text>[,<icon>[,<id>]])[(<expression>)]
1982 {
1983 <popup body>
1984 }
1985
1986 extpopup(<text>,<name>[,<icon>[,<id>]])[(<expression>)][;]
1987
1988 separator[(<expression>)][;]
1989 separatorid[(<id>)][(<expression>)][;]
1990 ...
1991 }
1992 @short:
1993 Defines a popup menu
1994 @switches:
1995 !sw: -m | --merge
1996 Merges the new popup contents with the current named popup.
1997 If the named popup does not exist, the popup is created.
1998 @description:
1999 Defines the popup menu <popup_name>. If the -m switch is [b]not[/b] used
2000 the previous contents of the popups are cleared before updating.[br]
2001 The popup is generated 'on the fly' when the [cmd]popup[/cmd] command
2002 is called.[br]
2003 The [i]item[/i] keyword adds a menu item with visible <text>,
2004 the optional <icon> and <command> as code to be executed when the item
2005 is clicked. <text> is a string that is evaluated at [cmd]popup[/cmd]
2006 call time and may contain identifiers and variables. If <expression>
2007 is given, it is evaluated at [cmd]popup[/cmd] call time and if the result
2008 is 0, the item is not shown in the physical popup.[br]
2009 The [i]popup[/i] keyword adds a submenu with visible <text>, the optional
2010 <icon> and a popup body that has identical syntax to the defpopup body.
2011 The <expression> has the same meaning as with the [i]item[/i] keyword.[br]
2012 The 'extpopup' keyword adds a submenu with visible <text>, the optional
2013 icon and a popup body that is defined by the popup menu <name>. This
2014 basically allows to nest popup menus and define their parts separately.
2015 <icon> and <expression> have the same meaning as with the [i]item[/i] keyword.[br]
2016 The [i]separator[/i] keyword adds a straight line between items (separator).[br]
2017 The 'separatorid' keyword adds a straight line between items, but permits to
2018 specify a separator id.[br]
2019 The [i]label[/i] keyword adds a descriptive label that acts like a separator.[br]
2020 The [i]prologue[/i] keyword adds a <prologue_command> to be executed
2021 just before the popup is filled at [cmd]popup[/cmd] command call.[br]
2022 The [i]epilogue[/i] keyword adds an <epilogue_command> to be executed
2023 just after the popup has been filled at [cmd]popup[/cmd] command call.[br]
2024 There can be multiple prologue and epilogue commands: their execution order
2025 is undefined.[br]
2026 <icon> is always an [doc:image_id]image identifier[/doc].[br]
2027 <id> is an unique identifier that can be used to remove single items
2028 by the means of [cmd]delpopupitem[/cmd]. If <id> is omitted
2029 then it is automatically generated.
2030 Please note that using this command inside the prologue, epilogue
2031 or item code of the modified popup menu is forbidden.
2032 In other words: self modification of popup menus is [b]not[/b] allowed.[br]
2033 To remove a popup menu use this command with an empty body:
2034 [example]
2035 defpopup(test){}
2036 [/example]
2037 This will remove the popup [i]test[/i] and free its memory.
2038 Popups have a special kind of local variables that have an extended lifetime,
2039 called [i]extended scope[/i] - this is described in the [doc:data_structures]Data structures documentation[/doc].[br]
2040 The syntax for an "extended scope" variable is:[br]
2041 [b]%:<variable name>[/b][br]
2042 This type of variable is maintained during the entire [i]visible lifetime[/i] of the popup -
2043 from the [cmd]popup[/cmd] command call to the moment in that the user selects an item and the
2044 corresponding code is executed (essentially from a [cmd]popup[/cmd] call to the next one).[br]
2045 This allows you to pre-calculate data and conditions in the prologue of the popup
2046 and then use it in the item handlers or item conditions.[br]
2047 @seealso:
2048 [cmd]popup[/cmd]
2049 @examples:
2050 */
2051
2052 if(KVSP_curCharUnicode != '(')
2053 {
2054 warning(KVSP_curCharPointer, __tr2qs_ctx("The 'defpopup' command needs an expression enclosed in parenthesis", "kvs"));
2055 errorBadChar(KVSP_curCharPointer, '(', "defpopup");
2056 return nullptr;
2057 }
2058
2059 const QChar * pBegin = KVSP_curCharPointer;
2060
2061 KVSP_skipChar;
2062
2063 KviKvsTreeNodeData * pPopupName = parseSingleParameterInParenthesis();
2064 if(!pPopupName)
2065 return nullptr;
2066
2067 if(!skipSpacesAndNewlines())
2068 {
2069 delete pPopupName;
2070 return nullptr;
2071 }
2072
2073 KviKvsTreeNodeSpecialCommandDefpopupLabelPopup * pMainPopup = parseSpecialCommandDefpopupLabelPopup();
2074 if(!pMainPopup)
2075 {
2076 delete pPopupName;
2077 return nullptr;
2078 }
2079
2080 return new KviKvsTreeNodeSpecialCommandDefpopup(pBegin, pPopupName, pMainPopup);
2081 }
2082
parseSpecialCommandHelp()2083 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandHelp()
2084 {
2085 // again not a fully special command: this routine just returns
2086 // a CoreSimpleCommand but parses the parameters as constants
2087
2088 // we handle a single big parameter, with whitespace stripped
2089 // This is because we want the identifiers to be preserved
2090 // as unevaluated (i.e $function).
2091
2092 skipSpaces();
2093
2094 const QChar * pBegin = KVSP_curCharPointer;
2095 while(!KVSP_curCharIsEndOfCommand)
2096 KVSP_skipChar;
2097
2098 if(!KVSP_curCharIsEndOfBuffer)
2099 KVSP_skipChar;
2100
2101 QString tmp(pBegin, KVSP_curCharPointer - pBegin);
2102 tmp = tmp.trimmed();
2103 const QString szHelpName("help");
2104
2105 KviKvsCoreSimpleCommandExecRoutine * r = KviKvsKernel::instance()->findCoreSimpleCommandExecRoutine(szHelpName);
2106 if(!r)
2107 return nullptr; // <--- internal error!
2108
2109 KviKvsTreeNodeDataList * p = new KviKvsTreeNodeDataList(pBegin);
2110 p->addItem(new KviKvsTreeNodeConstantData(pBegin, new KviKvsVariant(tmp)));
2111
2112 return new KviKvsTreeNodeCoreSimpleCommand(pBegin, szHelpName, p, r);
2113 }
2114