&kommander; New ParserMichalRudolfmrudolf@kdewebdev.orgEricLaffooneric@kdewebdev.org2005-2008Michal RudolfEric Laffoon&FDLNotice;New Parser Documentation
The new parser was introduced in &kommander; with version 1.2, released with
KDE 3.4. This document was originally released to show all the features of new parser.
As of &kommander; 1.3, released with KDE 3.5.9, the new parser is now the default, except for MainWindow applications created in &Qt; Designer. Because
the new parser is so much richer in ability, overcomes the limitations of nesting in the
old parser and adds so many new features we strongly recommend using it.
&kommander; itself will not be described here. Please refer to other documents to
see what is &kommander; for, how to create dialogs and how to manipulate widgets
on runtime.
Old parser
Here we compare the two parsers. While we advocate the new one for most purposes the old one is still
supported and useful, particularly when working with other scripting languages.
Old parser
The old parser was in fact macro parser. Only strings beginning with @ were
recognized, locally parsed and expanded.
@LineEdit1.setText(@ListBox.selection)
All the underlying functionality (local variables, expressions, file manipulation)
had to be done in another scripting language, such as Bash. While the intent with &kommander; is to support
all other scripting languages, and this is presently possible to some degree, there
was a need for a fast, native scripting language that was assured to be portable.
The biggest problem with the old parser is that the &kommander; specials are evaluated before the code is passed to the scripting language, making them impossible to use in loops and conditions.
The developers considered bash slow and not friendly to new users, and the old parser
had been initially bash calling DCOP. The paradox for &kommander; being language neutral
resulted in a need to do more than just functions natively.
New parser
The new parser is a full parser. It parses the whole script, not just functions. As we were interested
in GUI interaction, not the proliferation of scripting languages, we made compromises.
As a result you should find &kommander;'s scripting to be capable for most basic tasks
and natural and easy to use. There is also the Function Browser, which will help you
assemble statements. The Function Browser is intended to make &kommander; accessible to complete novice
programmers. It is similar to what you would find in KSpread to help you choose a function
and fill in the parameters.
If you want enhanced functionality found in other languages you can include
them in &kommander; script objects headed with a shebang. While in these scripts the Function
Browser will help you insert references to widgets. Just remember when using this functionality
that the parser makes one pass for the old parser functions and one pass for your script. So if you
try to change something in a widget and read it in the middle of a script you may not get what you expect.
#!/usr/bin/php
The following feature list is from version 1.2local and global variables and associative arraysnumerical expressionsstring manipulationvarious structure commands: if, while, for, foreachmost functions from old parserdirect widget manipulationmany additional functionsdecent execution speedreceive parameters from signals in script slotsThis list is from version 1.3pass parameters and receive them with script execute callsreturn a value from a scriptcreate widgets on the flyconnect signals and slots on the flyuse a variable alias for a widget namesimple indexed array functionsdirectly access a widgets slotsInvoking new parser
To enable new parser, set useInternalParser property of the dialog to
true. You can also enable new parser in a single script by putting
#!kommander
on the first line of the script. Also note if you are using another scripting language in
a script with a shebang that &kommander; automatically enables the old parser for interacting
with the dialog.
#!/bin/bash
echo @Self.item(0)
# returns first parameter passed to script
# echo $returnvalue passes back to calling script
New Parser FeaturesTypes
Each value is of one of three types: string, integer or double. Type conversion is
automatic and chooses most appropriate type (for example, if you add double to integer,
result will be double). If one of the values is string, result will be string too.
Places you can get into trouble here are getting a numerical value from a widget
and trying to perform a mathematical function on it. Since &kommander; uses +
to concatonate two strings of text it can treat LineEdit1.text + 2 as
22 instead of 2. See the conversion functions in
String functions to avoid problems.
Expressions
The following mathematical operators are supported: +, -, *, mod, . Standard brackets
are of course supported as well.
All kinds of comparisons are supported: <, >, <=,
>=, ==, !=. Instead of
!= you can also use <>.
Also, logical operators and, or, not
are supported, as well as their C equivalents (&&, ||, !).
For strings you can use + operator for string concatenation.
Some examples of valid expressions:
2+3
-5 * (2 - 13 mod 3)
"This list has " + 12 + "items."
Variables
Variables don't need to be declared. Once you use variable, it is considered declared.
Type of a variable is recognized automatically and can be changed later.
Associative arrays are supported as well. They map string keys onto values of any type. To declare
such array, you can just add some element to it, for example: A["Quanta"] = "Web editor".
Arrays are also handled by foreach command and
array functions.
Local and global variables are supported. Global variables are marked by leading underscore.
So, myVar is a local variable, but _myVar is global. The same applies
to arrays.
a = 5
b = 2 * 5 - (a + 1)
c = "[Item " + b + "]"
d["MyKey"] = "MyValue"
d["MyKey2"] = 5
Using variables for widgets works much as you would expect. This is useful when looping widgets into a table.
for i=0 to 10 do
mycombo = "ComboTable"+i
createWidget(mycombo, "ComboBox", "Form1")
end
Comments
You can use comments in &kommander; using the two traditional program language comment forms for line comments. For those users who are new to programming wondering what traditional form? see below. You can copy and paste the text below into a button or dialog initialization and see how comments behave in action.
// this is a comment for one line
message_info("Hello World") //traditional first program
// the above comment also ignored - the messagebox is not
# this is also a comment
message_info("This message will show")
Using the following multi-line comment will not work and will cause the rest of the widget execution to fail.
/*
Hi, I was supposed to be a comment
None of the script after this will execute
DON'T USE THIS TYPE OF COMMENT IN KOMMANDER!
*/
Built in Globals
&kommander; has some built in globals you may find handy.
_ARGS - the argument string passed to the dialog on opening
_ARGCOUNT - the count of arguments passed. These can be retrieved as ARG1 to ARGn where n is the total number of args passed
_KDDIR - the directory from which the dialog was run. &kommander; will default to your home directory, or a directory change if asked for it's current directory. This is useful for saving and reading files with the &kommander; file.
_NAME - there is no reason to use this so don't
_PID - the process id the current dialog is being run from - also available as just pid Avoid using this name for your variables!
_VERSION - this is handy if you want to display the version of &kommander; that is running
Passing arguments in &kommander;You can pass arguments via script parameters, signals and slots, command line parameters and DCOP. Let's look at scripts. Call your script like:
result = ScriptObject1.execute("Hello World")
debug(result)
Inside your script you might have the following
var = str_upper(Self.Item(0))
return(var)
Now you will get a return in your Stderr message log of HELLO WORLDReceiving a signal connected to a script slot works the same way. Self.Item(0) is parameter one and so on. You can retrieve the count of arguments passed with ScriptObject.count.
Command line parameters allow for named or unnamed arguments. Unnamed look like
kmdr-executor myprog.kmdr 100 red
Where you will find _ARG1 = 100 and _ARG2 = red. One quirk is passing strings with spaces as an argument means they need to be quoted. Using the dialog command complicates matters as the entire argument string must pass as one string, meaning in quotes.
dialog("mydialog.kmdr", 100+" \"Hello World\"")
This returns _ARG1 = 100 and _ARG2 = Hello World. Without the escaped quotes you would have _ARG2 = Hello and _ARG3 = World. Using Named Parameters is rather nice and potentially less confusing.
dialog("mydialog.kmdr", "xcount=100 xquote=Hello world")
And now you access those with _xcount and _xquote global variables.
DCOP can be complex, which is why we recommend using the tools we develop to enable creating DCOP for remote &kommander; dialogs with something like a function browser. Here is an example DCOP call issued from a dialog opened from a parent &kommander; window. Since it knows who its parent is it can send information back while it is open and freely access all its parent's functionality with the exception of slots. Of course that can be done internally with a script which can be called externally, so in practice there is no limit to what can be done.
dcop("kmdr-executor-"+parentPid, "KommanderIf", "setText(QString,QString)", "StatusBar8", "Hello")
Let's look at this piece by piece. First of all we add parentPid to "kmdr-executor-" as we make no assumption a &kommander; window was the caller. You could use this with Quanta or KSpread or whatever. Next we are addressing KommanderIf, which is a nice interface for end users which has been cleaned up. We hope eventually as KDE moves from DCOP to DBUS on KDE4 that more applications adopt a nice interface for integration. The next parameter, "setText(QString,QString)" is important because it prototypes the parameters allowed. Otherwise &kommander; could not validate the call. So without a definition of the DCOP call being used you will get an error. The remaining parameters are of course what is being passed. We recommend you look at applications with kdcop to see how this works and practice dcop calls from the shell to get your syntax right.
Commands
Various structure commands are supported. They can be freely nested.
There are also three special commands: exit, break and continue.
The first one ends script execution and returns. The second exits current block (while,
for or foreach and the third exits just a current step, restarting
from the beginning of the loop.
if
Command if has following syntax:
ifconditionthencodeelseifconditionthencodeelsecodeendif
Both elseif and else parts are optional. Condition
is any expression. Code is executed if condition is true. That means:
non-zero for integers and doublenon-empty for strings
if a * 2 > 7 then
b = 1
elseif a < 0 then
b = 2
elseif
b = 0
endif
whilewhileconditiondocodeendCondition is recalculated each time loop is executed.
while i < 15 do
i = i + a
end
for
Command for has following syntax:
forvariable=start valuetoend valuestepexpressiondocodeend
Loop is executed starting from start value and it is ended when variable's value is
bigger then end value. If step part is specified, on each step
variable's value is increased by given value instead of 1.
foreach i = 1 to 20 step 5 do
a = a + 2 * i
end
foreach
Command foreach has following syntax:
forvariableinarraydocodeend
Loop is executed for each key in given array. In each step variable is assigned the next key from the array.
sum = 0
foreach i in myArray do
sum = sum + myArray[i]
end
Functions
Most old parser functions are supported by new parser. Also, some new functions were added.
String functionsString functions are the same as in old parser, the only difference is that their names
are preceeded by str_ instead of @String.str_length(string) - returns length of stringstr_contains(string, text) - returns 1 if string contains textstr_find(string, text, start) - returns position of the first occurrence of text in string; optional start
specifies start of the search
str_find(string, text, start) - returns position of the last occurrence of text in string; optional start
specifies start of the search
str_left(string, count) - returns first count characters of stringstr_right(string, count) - returns last count characters of stringstr_right(string, start, count) - returns substring of string starting from start and containing count
characters (or everything to the end of the string if last parameter is not specified)
str_remove(string, text) - returns string with all substrings equal to text removed
str_replace(string, text, text2) - returns string with all substrings equal to text replaced with text2str_lower(string) - returns string converted to lowercase
str_upper(string) - returns string converted to uppercase
str_section(string, separator, start,
end) - returns substring containing appropriate sections of string determined
by separator; if no end is given, single start section is returned
str_args(string, ...) - returns string with %1, %2, %3 replaced with following parameters.
str_isnumber(string) - returns 1 if string is a valid number
str_isempty(string) - returns 1 if string is empty
str_toint(string, default) - returns string converted to integer; if conversion is not possible, optional default value is returned
str_todouble(string, default) - returns string converted to double; if conversion is not possible, optional default value is returned
&kommander; functions
Most &kommander; functions are supported; some (such as expr)
were obsoleted by new parser and are not available.
debug(string, ...) - writes all parameters on stderr
echo(string, ...) - writes all parameters on stdout
dcop(string, ...) - calls DCOP functionexec(string, shell) - executes external program
(using optional shell); block the execution of the current dialog until the program passed as the parameter exits; returns output of that program
i18n(string) - marks string for future translation
env(string) - returns a value of environmental variable
readSetting(key, default) - returns a value stored in config
file with given key; if there is no such value default is returned
writeSetting(key, value) - writes pair
key and value in config file
New in &kommander; 1.3execBackground(string, shell) - executes external program
(using optional shell) in the background, without blocking the current dialog; contrary to the above exec function, it will not return the output of the program.
return(value) - returns a value to the calling object (script, button...)
createWidget(widgetname, widgettype, parent) - creates a new widget. You can then place it in a table or toolbox, for example and use mywidget.show(true) to make it visible. If you are putting an new widget on the form you need to consider layout issues. &kommander; will not create layouts on the fly or edit pixel by pixel positioning (in most cases). This is confusing even in C++ development. We recommend you use a groupbox and do a layout in the dialog
for best control.
connect(sender, signal, receiver, slot) - connect a widget signal to a widget slot. See the connection dialog and select similar widgets for possibilities. If for instance a signal looks like looks like execute(const QString&) that is exactly what must be in quotes there.
disconnect(sender, signal, receiver, slot) - undo the connection as listed above. Again, exact syntax is essential.
widgetExists(widgetname) - remember you can use a variable name to reference a widget now. Use this when accessing created widgets to insure they are there. Calling a non-existant widget obviously will throw an error.
Array functions
Most array functions are supported; some (such as value)
were obsoleted by new parser and are not available. The only difference is that their names
are preceeded by array_ instead of @Array.Due to parser limitation, name of array has to be specified as string now; for example
array_count("MyArray").array_clear(array) - removes all elements from arrayarray_count(array) - returns number of elements in arrayarray_keys(array) - returns string containing EOL-separated keys of array - note that if you had imported a scalar (keys without values, see below for an example) into an array with &kommander; you would not be able to access it with array_values("myarray") as you might think (since it seems to only have values) but would instead need to use array_keys("myarray"). You might find a better choice for this is to use the new indexed arrays described below.
array_values(array) - returns string containing EOL-separated values of arrayarray_tostring(array) - returns string containing whole array
as EOL-separated pairs containing key and value separated with TAB character
array_fromstring(array, string) - reads array from string (usually provided by array_tostring function)
array_remove(array, key) - removes item with key
key from arrayHere is an example for array manipulation:
array_fromstring("myArray", "1\tA\nsecond\tB\n3\tC")
foreach key in myArray do
debug("myArray[" + key + "]= " + myArray[key])
end
This will print out the following to the stderr. It is visible that there is no guarantee about the order of array elements, as well that the keys are strings, not numbers.
myArray[1]= A
myArray[3]= C
myArray[second]= B
Another example for keyless arrays:
array_fromstring("myArray", "A\nB\nC")
foreach key in myArray do
debug(key)
end
debug("Array elements:\n" + array_keys("myArray"))
This results in:
A
B
C
Array elements:
A
B
C
New in &kommander; 1.3array_indexedFromString(array, string, separator) - this compensates for &kommander; not having indexed arrays. it creates an array with a zero based sequential index. Remember to use quotes on the array name and any strings not represented by a variable. The separator argument is optional and defaults to "\t" [TAB] which is used to separate fields reading and writing tables, arrays or detail widgets. Remember this array index does not enforce any rules on its self. It is just like you created it with a for loop, just more convenient.array_indexedInsertElements(array, key, string, separator) - this function is part of the indexed array suite and enables you to insert elements in your array while maintaining an index that is sequential, contiguous and unique. Set the index key to start at and the text string and how it is separated. The elements will be added shifting all the index numbers after by the number added.
array_indexedRemoveElements(array, key start, number) - this enables you to remove elements from an indexed array and avoid gaps in your index. Specify the key to start at and optionally how many to remove. The default is one. You will end up with a re-indexed array less the removed elements.
array_indexedToString(array, separator) - this enables you to convert your indexed array back into a string, particularly useful for detail widgets. For instance if you are displaying a database query result in TreeWidget1 and it has six columns you can use TreeWidget1.selection to get the selected row. it will be separated by tabs and you could look at a the fifth element by using str_section(TreeWidget1.selection, "\t", 4) (remember it is zero based). That's nice for reading a value, but if you want to change it you can see you have a lot more work to do. After you split that string you have to reassemble with val1+"\t"+val2... Using indexed arrays you could edit that fifth element like so...
idx = TreeWidget1.currentItem
array_indexedFromString("z", TreeWidget1.selection)
z[4] = "new value"
TreeWidget1.removeItem(idx)
TreeWidget1.insertItem(array_indexedToString("z"), idx)
Note that only two short lines were added to accomplish this! This was very welcome for database use.
File functions
All file functions are supported, the only difference is that their names
are preceeded by file_ instead of @File.file_read(name) - returns content of file namefile_write(name, ...) - writes all arguments
to file namefile_append(name, ...) - appends all arguments
to file nameInput functions
These functions show some dialog allowing user to enter some value. They are accessible in the old parser using @Input.. For most functions all parameters are optional, exception is
input_text which requires 2 parameters and input_value which requires 5 parameters.
input_color(caption, default) - returns color in #RRGGBB format
input_text(caption, label, default) - returns text entered by user
input_value(caption, label, default,
min, max, step) - returns value entered by user
input_directory(startdir, filter, caption) - returns directory selected by user
input_openfile(caption, label, default) - returns existing file entered by user
input_savefile(caption, label, default) - returns file entered by user (if file exists, confirmation will be required)
input_openfiles(caption, label, default) - returns string of EOL-separated existing files entered by user
Message functions
These functions show some message for user or ask user to confirm some action. In the old parser use @Message. instead.
message_info(text, caption) - shows information text
message_error(text, caption) - shows error text
message_warning(text, caption, button1,
button2, button3) - shows question with warning and up to three buttons; number
of chosen button is returned; if no button names are specified, Yes and No will be displayed
message_question(text, caption, button1,
button2, button3) - shows question and up to three buttons; number
of chosen button is returned; if no button names are specified, Yes and No will be displayed