Next Previous Contents

3. Kaptain grammars

Kaptain reads the file which contains the specification of a context free grammar. The text generated by the grammar can be an executable command (or anything you can type at the shell prompt - pipes, scripts, ... - even multiline ones). This grammar describes the format of the command line and Kaptain creates the graphical dialog from that grammar, and then generates the command according to the user's manipulation on the dialog.

3.1 About grammar rules

Each grammar rule may contain tokens of the following types:

nonterminals

Nonterminal symbol, which stands for a complex text. It must start with a letter, and may contain numbers inside.

terminals

quotations

A quoted string which is fix text.

specials

Some special element for user input: always begin with @, for example @integer, @string, ... These will be substituted the value the user sets in the dialog.

others

(advanced) Regular expressions, substitution and translation expressions, these are described in the next section.

Each rule describes the meaning of a nonterminal:

start -> "My name is " name "." ;
name -> @string ;
Rules have a nonterminal on the left side, something on the right side of an arrow (->), and are followed by a semicolon (;)

The start symbol is the top level nonterminal, it must be present. The way texts are generated is beggining with start, and substituting all nonterminals with the appropriate sequence of symbols. When there are no nonterminals, the specials are substituted according to the user input, and the generated text is ready.

Rules may be non-deterministic:

start -> "It is " weather " today. The temperature is " temp "." ;
weather -> "sunny" | "raining" | how " cold." ;
how -> "a little" | "very" | @ ;
temp -> @integer ;
That veritcal bar symbol (|) means one and only one of the separated partitions should generate the text. In this case, the user will choose whether it is raining or it is sunny, or just cold, and if cold then how. It can be "very cold","a little cold", or simply "cold". So @ is an abbreviation for nothing :). He/she should also specify the temperature - @integer orders Kaptain to place a little spin box in the dialog. This symbol evaluates to the content of the spin box.

Kaptain grammars mustn't contain recursive definitions. Implicit recursion is also forbidden, thus

a -> b ;
b -> c ;
c -> a ;
is invalid. (The reason is that these cannot form dialog elements the way Kaptain works.)

3.2 Quoted strings

Kaptain accepts quotations very similar to Perl.

"double quotes"

Escape sequences may be included: \n, \t, \", \\ \$. Shell environmental variables are substituted ( $ONLYCHARS and ${any text in braces} ).

'single'

Literal string, cannot contain '

q%text%

(% can be replaced by any character except letters, numbers, underscore, hyphen and semicolon.) Literal string, no way to have the bounding character inside.

`back quotes`

A command, which is executed and the string is the standard output of that command. May not contain a backquote.

x%command%

(% can be replaced by any character except letters, numbers, underscore, hyphen and semicolon.) Literal string, no way to have the bounding character inside. The value is the output of the command.

To make multi line quotations easy, captain accepts the following form

... <<ANYLABEL
the
literal
text
ANYLABEL

3.3 Specials

The format of a special is

@name(param1, param2, ...)=initvalue
where param1, param2, ... and initvalue can be either nonterminals, quoted strings or integer values.

The following specials are available in the current release

@integer

An integer input from the user; you can specify valid range and inintial value if you like: @integer(-2,100)=4 means an integer between -2 and 100, initially 4. Either range or initial value can be omitted.

@float

Guess what it can be... Note that the initial value must be quoted! Ex.: @float="1.13".

@string

A text input field from the user; maximum length and initial value is optional: @string(15)="Hi, there!"

@infile

Input file. A text input field and a button from the user; a File Open dialog will appear if the button is pressed. Filter string and initial value are optional @infile("*.txt")="myfile.txt"

@outfile

The same as @infile, just with a Save as dialog.

@directory

The same as above, just for choosing directory. No filter string is accepted, only initial value. @directory="/home/joe"

@list, @combo, @combow

A simple listbox or combo box with elements from which the user should choose. @combow is read-write combo box, the others are read-only elements from the user's view. The returned value is the selected string. For example @list("cat","dog","lion","tiger").

If any of the given strings is multiline (it contains newline character), it is split to form different elements in the list. Thus @list(`ls`); would execute the command ls which gives the list of files, each on a different line, and the list box will contain the name of the files in the current directory.

The initial value, if it is integer n, makes the nth element to be selected. From 0.6 initial string value can be given. (Thanks to Luca Montecchiani [m.luca@iname.com])

As a new feature in 0.6, the first parameter of @list and @combo can be a regular expression based substitution expression, which is executed on each line before feeding it into the listbox. This makes it possible to have different text in the screen to the string that is evaluated from the corresponding element in the list. Read more about that in the next section.

@regexp

A text input field controlled by a regular expression: @string("^[ab]*$") only allows to type a and b letters in the field.

@button

A pushbutton which creates a new dialog when pressed. It accepts a nonterminal symbol as a parameter, which will form the dialog. The initial value is the text on the button. Ex.:@button(newdialog)="Settings...". @button is deprecated, use :dialog modifier instead. The argument is that it doesn't express the relation between grammatical symbols correctly.

@close

A button to close the current dialog. Closing the main dialog means exiting kaptain.

@action

A button for executing a command. If only a nonterminal is given as a parameter, it is evaluated and passed to the shell for execution. If several parameters are given (either quoted strings and nonterminals), the first one is considered to be a program and the others the parameters. For example @action(start) would evaluate the start symbol and execute it through the shell (usually bash). It is equivalent to @action("bash","-c",start). Another possibility is to use @action("perl","-e",start) to execute perl. In this case the text generated by the symbol start is meant to be perl code.

During the command is executed, the push button is disabled. When child process finishes, it is enabled again. To make Kaptain dialog disappear before executing command, use @exec.

@exec

Same as @action, but Kaptain quits immediately after starting the command.

@container

Accepts a nonterminal symbol as a parameter. It consists of a listbox and two buttons, Add and Remove. When Add is pressed, it evaluates the nonterminal and puts the text into the listbox. Remove removes the selected element from the listbox. It is useful when any number of arguments of the same type can be accepted (usually file names)

@icon

Given a file name parameter, it loads the graphic and shows it in the dialog. Has no effect in the generated text. If Kaptain was compiled with kde support, it looks for the file in the kde standard icon directories. Otherwise, full paths have to be specified.

@text

Information for the user slightly sunken in a frame. The parameter can be multiline text. Has no effect in the generated text.

@echo

Similar to @action, but it just prints the text to the standard output, never executes it. Useful when Kaptain is part of a pipe.

New features in 0.5:

@execclose

Behaves like @exec or @action but closes only the current subdialog (or the application, if it's about a top level dialog).

@fork

Similar to @exec or @action but Kaptain just forks and is going on.

@dump

Just like @echo but then quits.

@execbuffer

Parameters and initial value are like those of @action. Places a button which executes the given command and catches the standard output of the execution. After that, when it comes to text generation, it evaluates to the standard output of the command which has been executed when the button was pressed the last time.

@password

Similar to @string, but shows only *'s

@preview

A button which creates a subdialog. That contains the text generated from the nonterminal given as a parameter. For example pressing the button made by @preview(start), you will see the generated text according to the current settings.

@fill

Inserts stretchable space.

Extensions in 0.6:

@multicol

Creates a multicolumn listview. Each row is created from the parameters of this special symbol just like in case of @list. The first parameter must be a regular expression in the form of m/regexp/ which contains subexpression. The second string parameter, if matches the pattern won't be insert into the list, it will be the header. For every line that matches the pattern each subexpression will be a separate column in the listview.

Consider the output of the command ls -l:

total 4103
-rw-r--r--    1 zsolt    zsolt        3269 Mar 30 21:45 index-1.html
-rw-r--r--    1 zsolt    zsolt        3324 Mar 30 21:45 index-2.html
-rw-r--r--    1 zsolt    zsolt       26819 Mar 30 21:45 index-3.html
-rw-r--r--    1 zsolt    zsolt        4710 Mar 30 21:45 index-4.html
-rw-r--r--    1 zsolt    zsolt        1289 Mar 30 21:45 index-5.html
-rw-r--r--    1 zsolt    zsolt        1237 Mar 30 21:45 index-6.html
-rw-r--r--    1 zsolt    zsolt        2100 Mar 30 21:45 index.html
-rw-r--r--    1 zsolt    zsolt         531 Sep 27  2000 index.nif
-rw-r--r--    1 zsolt    zsolt       34692 Mar 30 21:45 index.sgml
...
Suppose we want to insert these into a listview, but just the file name and the attributes. A regular expression that matches the first and the las column can be written in the following form:
m/^([^[:blank:]]+)[[:blank]].*[[:blank]]([^[:blank:]]+)$/
This might be complicated to understand but is easier to write. So the grammar for this:
start -> list @close="OK";
list -> @multicol( m/^([^[:blank:]]+).*[[:blank:]]([^[:blank:]]+)$/, "Attribute  Filename", `ls -l`);
It looks like this:
The only problem is that the command ls writes a line summarizing the list like `total 4103'. This can be avoided by modifying the pattern in a way that the first letter mustn't be t, which never occurs in as file attribute character: m/^([^t][^[:blank:]]+)...

If the second parameter of the @multicol special is a substitution expression, it is executed for each line before giving it for pattern matching. Thus in the case above, one can eliminate the line `total 4103' from the output of the command ls in the following way:

list -> @multicol( m/^([^[:blank:]]+).*[[:blank:]]([^[:blank:]]+)$/, s/^total.*$//, "Attribute  Filename", `ls -l`);
where s/^total.*$// simply deletes the lines beginning with the word `total'.

Finally, @multicol evaluates to the selected line, without the substitution if one was given as a second parameter.

@list

was improved. Now it's easy to create lists which can evaluate to and show different strings. See it's description above.

This list may improve in the future.

3.4 How the dialog is built

The fact is that the grammar itself contains enough information to build a user friendly dialog. Each nonterminal is connected to an area in the window.

For a conjunctive rule, like this

start -> first second third ;
the areas generated for first, second, third will be placed top-down. If you want them to be placed left to right, use the modifier :horizontal like this:
start :horizontal -> first second third ;
See next section for detailed information about modifiers.

For a disjunctive rule, Kaptain places radio buttons for choosing from several possibilities. That is for

start -> first | second | ! third | @;
either first, second, third will appear in the dialog with a radio button, and user can choose exactly one. That exclamation mark selects the default one. Modifiers are also avaiable just like above.

Sometimes its better to have check boxes if two or three possibilities are avalable (tristate check boxes also exist). Here

a -> "yes" | "no" ;
b -> "yes" | "maybe" | "no"
either a and b represents a check box.

Kaptain creates a standard check box if:

So to force Kaptain creating radio buttons for a double disjunction, write
a -> yes | no;
yes "Yes" -> "yes";
no "No" -> "no";

Kaptain creates a standard check box if:

Avoiding tristate check boxes can be done the with the same trick like above.

If you place a ! to one of the choices, the check box will be selected according to it.

3.5 Modifiers

Modifiers always begin with a colon and are related to the nonterminal after which they appear. For example:

start :framed :horizontal -> a b c ;
means that a, b and c are placed horizontally one by one, and each is in a frame.

Modifiers usually have effect on the appearence of the dialog.

:framed

Makes the child widgets to appear in a frame.

:tabbed

Makes the child widgets to appear on different tabs.

:horizontal

Makes the child widgets to appear horizontal one by one, instead of the default vertical arrangement.

:reverse

Makes the child widgets to appear in reverse order. That is down to top if :horizontal is not present, otherwise right to left.

:noeval

The nonterminal will evaluate to an empty string. It is useful when you use a @string input field by a @container, and you want to have only those items which were inserted into the container.

:double

Place child elements in two rows or columns. It is useful when you have many radio buttons.

:beside

Place child elements immediately to the right of the label of this rule (if it's a radio button or check box), not below as by default. Use it in the similar situations as this: number :beside "Number" -> @integer | @ ;

:dialog

Create a pushbutton, which if pressed, makes a subdialog to appear with the right side of the rule.

:wizard

Create a pushbutton, which stands for a subdialog, having all the nonterminals from the right side as pages in a so called "Wizard" dialog ( with "Next", "Back", "Cancel" and "Finish" buttons). Must be a conjunctive rule.

New modifiers in 0.5:

:tree

Places all children in a QListView object. If the children are also marked with :tree, they will appear on the secind level in the tree. Look at the example grammar for indent. This is a very good way to place a great amount of structured information in the dialog. Checkboxes and radio buttons can appear in the tree, but if other things are present, they will be placed under or beside the QListView object. Use it with :beside if you want to create things like KControlCenter. If the label of the tree item is like "{icons/my.gif}Text", then the picture mentioned between the braces will be placed beside the Text - this can be very attractive.

:detailed

Together with :tree, it makes a second column appear in the TreeView where the tooltip information is placed.

Notice that modifier :framed won't make the same nonterminal be in a frame, but all its children. Look at the example grammars to see what I'm talking about.

3.6 Text appearing in the dialog

The user won't see the names of the nonterminals or the text the grammar generates. You must specify some comment for every nonterminal which would be on the screen. The syntax is:

start "The title" -> a | b | c ;
a "Choice A" -> "--a";
b "Choice B" -> "--b";
c "Choice C" -> "--c";
In this case the title of the dialog is "The title" and the radio buttons have the labels "Choice A", "Choice B" and "Choice C". Without quotes, of course.

To include more information, you can use tooltips and whatsthis.

a "Choice A" "This is choice A" "When you choose A, ..." -> "--a";

So the rule syntax is:

<nonterminal> [<modifiers>] [<title> [<tooltip> [<whatsthis>]]]
  -> {<nonterminal> | <terminal> | <vertical bar> | @ | ! }* ;
I'm in trouble when I try to describe some kind of grammar rules with grammar rules :-) Look at the examples.

You can assign title, tooltip and whatsthis text to a nonterminal in a different place it is defined. If you had a rule

nonterm -> a b c;
insert the following anywhere below the rule:
nonterm :horizontal = "Title" "ToolTip" "This is WhatsThis" ;
This is useful because the grammar definition is separated from the description. You can also have multi line whatsthis:
nonterm = "" "ToolTip" <<THEEND
This is whatsthis.
You can read many things here.

Have a nice day!
THEEND
In this case, the title was empty, so if the nonterminal had a title, this would not overwrite it.

3.7 Dirty tricks

Multiple nonterminals

If a nonterminal symbol appears in the grammar several times on the right side, it is shown just for the first time. This helps you to achive different placement in the dialog to the generated text. For example:

start -> a b c d @exec(command)="OK" ;
command -> b a d c ;
a -> ...
Thus command is executed, which combines the dialog elements in a different order.

Similarly, if symbol a appears twice on the same right side,

start -> x " and " x ;
x -> @integer=19;
only one input field will be shown in the dialog, but the generated text will be 19 and 19.

Grammatical constraints

Suppose we have some text to generate, which might be enclosed in some kind of parentheses:

start -> open "any text" close ;
open -> "(" | "[" | "{" | "<" ;
close -> ")" | "]" | "}" | ">" ;
This leads to a dialog where you have to choose the opening and closing symbol. This allows the user to generate strange things like (any text}. Kaptain makes it easy to avoid this: after the rules just say
close <- open ;
which is a constraint for two disjunctive rules with the same number of choices on the right. This means the choice for close depends on the choice of open. If the nth possibility is choosen for open, then close will also evaluate to the nth disjunction. Thus only one radio button group is needed.

This feature allows the grammar to effect on different parts of the generated text. It somehow means much power than that of context-free grammars.

3.8 Example grammars

If you write your own grammars, and you think they might be useful for others, please send them to me terek@users.sourceforge.net, I will put them to the main Kaptain site.

Sound recording

Let's record from the microphone connected to the computer. The executable command is

% dd count=5 bs=8 < /dev/audio > myfile.au
It would be better to see a beautiful dialog. The grammar
#!/usr/local/bin/kaptain
start:framed -> "dd count=" count " bs=" size "k <" input " > " output ;
count "Number of blocks" -> @integer=5 ;
size "Size of blocks (k)" -> @integer=8 ;
input "Input device" -> audio | dsp ;
audio "Audio" -> "/dev/audio" ;
dsp "DSP" -> "/dev/dsp" ;
output "Output file" -> @outfile("*.au");

And this is the picture:

Creating archives

Here is a simple grammar, just witten in 5 minutes (may contain errors, check before you try to use it!)

#!/usr/bin/kaptn

start "Tar" -> tar buttons;

tar -> "tar " choice " " opt archive " " files;
opt:framed :horizontal  -> extropt createopt ;

createopt "Write options" -> dump remove verb comp;
extropt "Read options" -> noerr keep same abs;

dump "Dump files, not just symlinks" -> "--dereference " | @;

noerr"Ignore failed read" -> "--ignore-failed-read " | @;

keep "Keep existing files" -> "-k " | @;

same "Extract all protection information" -> "-p " | @;

abs "Absolute path names" -> "-P " | @;

remove "Remove files after adding to archive" -> "--remove-files " | @;

verb "Verbosely list files proceeded" -> "-v " | @;

comp -> compress | @;
compress "Compression" -> gzip | bzip;
gzip "gzip" -> "-z ";
bzip "bzip2" -> "--use-compress-program bzip2 ";

choice:framed  -> work;
work "Main choice" -> concat | ! create | diff | del | append | list | update | extract;
concat "Append to archive" -> "A";
create "Create archive" -> "c";
diff "Find differences between archive and file system" -> "d";
del "Delete from archive" -> "-delete";
append "Append to the end of an archive" -> "r";
list "List contents" -> "t";
update "Update archive" -> "u";
extract "Extract from archive" -> "x";

archive "Archive name" -> @infile;

files "Files to work with" -> @string ;

buttons :horizontal -> @action(tar)="OK" @close="Cancel";

It looks like this:

The state caught in the picture produced the command

tar c --remove-files --use-compress-program bzip2 files.tar *.cpp *.h

Calling perl

Kaptain can generate various texts, such as complicated pipes or a piece of perl code. To create your encrpyted password, use the perl command:

print crypt('passwd','sa')."\n";
Let's make the grammar:

#!/usr/local/bin/kaptain
start "Password encrypter" -> code @action("perl","-e",code)="Crypt";
code:framed -> "print crypt('" passw "','" sa q%')."\n"% ;
passw "Password" -> @string="notsimple";
sa "Random chars(2)" -> @string(2)="y4";

Notice that the @action has three parameters; the perl compiler is called without the shell, having two parameters: "-e" and the evaluated text of code;

Simple but great:

A complete example can be found here


Next Previous Contents