The KiwiScript is declarative programming language for the user interface of the application program. This language has following features:
See KiwiScript Framework and KiwiEngine Framework for the implementation of this language.
The specification design and the implementation is still under construction.
This document is produced by Steel Wheels Project. This document and the other KiwiScript products are distributed under GNU public license or GNU lesser public license Ver2.1
The application has hierarchical tree structure based on Application Object Model. The node of the tree is defined by the frame object. Here is a sample frame object to present the GUI button whose title is "OK".
button: {
prototype: Button ;
title: "OK" ;
}
The "Button" is built in class in the KiwiScript with GUI system.
The slot of the frame can have event driven expression.
For example, the button is enabled when flag1
and flag2
are
true. The button state is immediately updated when the value of flag1
or flag2
is changed.
button: {
prototype: Button ;
enable: (bool) application.flag1
&& current_document().flag2 ;
}
In usually, the compiler estimate the type of the result of path expression. But, in the above case, the cast expression is used to tell it to the compiler.
You can define event-driven function. It is called when the one of the parameter of the function is changed. At the below example, the title of the button is switched to "close" when some documents are opened and "open" when there are no opened document.
button: {
title: watch(application.document.count) string {
uint count = application.document.count ;
string message ;
if(count > 0){
return "close" ;
} else {
return "open" ;
}
}
}
Almost of the light weight langage are weakly typed. But the KiwiScript is strongly typed.
Type name | Sample constant | Description |
---|---|---|
bool | true, false | Boolean value |
char | 'c', 0xc | 8bit unsigned integer value |
int | 1234, -1234 | 64bit signed integer |
uint | 1234u, 0x1234u | 64bit unsigned integer |
float | 1.234, -12.34 | Floating point value |
string | "Hello, world!!" | Unmodifiable array of Char |
array | [1234, 123], [] | Array of objects which has same data type |
frame | { a:0 ; b:"hello";} | Frame object |
The data type is attached for each objects. The assignment between different data type objects are not allowed. You have to use cast expression.
Attribute name | Sample Declaration | Description |
---|---|---|
readonly | readonly int a | The variable can be read access only |
fixed | { a : (fixed int) 0x1234 } | The slot can not be removed from the object |
The data attribute is attached for each variables (Some variables points same objects, but they have different type attributes). The assignment between different data attribute variable are restricted. The assignment from readonly variable into non-readonly variable is not allowed.
{
int a ;
readonly int b ;
readonly int c = a ; /* allowed */
readonly int d = b ; /* allowed */
int e = b ; /* not allowed */
}
The readonly
attribute is attached automatically.
The function parameter variables will have this attribute. See function.
The bool, char, int, uint, float
types are called number
type.
The context of string
object can not be modified.
The result of string operation alway generate new object instead of modifying the source.
Enumerated type can be defined in a frame.
Here is a sample code for the enumerated type value definition.
The "Week
" is a type name and "Sunday
", "Monday
"
are the identifier of the constant integer values.
{
Week : enum { Sunday : 0 ; Monday : 1, Tuesday : 2 } ;
}
The rule to inherit and lookup enum type and value is same with frame's rule. See Inheritance and Lookup for the frame.
All objects in an array must have same data types.
The frame object contains zero or more slots. The slot has following context:
Member | Description |
---|---|
Identifier | Identifier to distinguish this slot |
Object | Source object to define the value of the slot |
Member | Description |
---|---|
class | Define the name of frame to be referred by other frame |
prototype | Declare the class name to inherit it |
The rule to search, update and add slot is resembled to the rules for NewtonScript.
Following steps are used to search instance in the frame.
When setting a slot, the inheritance search is the same as for slot lookup, except that the slot is not always set where it is found. These are the rules for where a slot is set:
Use type
to define the new data type.
Here is examples fot type definition
{
days_t : type { user_t : array of int ; }
}
The rule to inherit and lookup the defined type is same with frame's rule. See Inheritance and Lookup for the frame.
ANSI-C like operators are supported.
Operator | Target type | Description |
---|---|---|
+ | number | Give + sign |
- | number | Negate the sign. The result value will have signed integer type |
! | boolean | Logical NOT |
~ | boolean | Bitwise NOT |
Operator | Target type | Description |
---|---|---|
+ | number | Add 2 numbers |
- | number | Sub R-val from L-val |
/ | number | Divide L-val by R-val |
% | number | Modulo L-val by R-val |
& | integer, boolean | Bitwise AND operation |
| | integer, boolean | Bitwise OR operation |
^ | integer, boolean | Bitwise XOR operation |
<< | integer | Bitwise left shift operation |
>> | integer | Bitwise right shift operation |
&& | boolean | Logical AND operation |
|| | boolean | Logical OR operation |
The path expression is used to point the object on hierarchical frame structure. It is described as the sequence of expression separated by the period.
/* system: */ {
application: {
func0 : sum(int a, int b){
return a + b ;
}
frame0: {
var0 : (int) 0 ;
}
main: (array of string argv) int {
int c = system.application.func0(10, 20) ;
application.frame0.var0 = self.frame0.var0 + 1 ;
}
}
}
The function which has the same name with the primitive
data type is used as case function.
The ANSI-C style cast operation (ex. ((int) a)
) is not supported.
float a ;
array of int b ;
int(a) ; // cast type of 'a' into 'int'
float(b) ; // semantics error
The assignment statement copies context of the primitive object.
But it copy only reference when the assign data set object.
It means the context of assigned object will be modified by the modification of source object.
To keep the assigned object unique, you have to use
/* a and b is different object */
int a = b ;
/* c points the same object with d */
array of int c = d ;
/* e and f points different objects */
array of int e = f.clone() ;
The syntax is restricted. The break
statement is required
for each case
and default
labels.
You can not describe fall through case.
switch(..){
case A: case B:
...
/* break for case is always required */
break ;
}
There are 2 ways to define function. First, the function is defined as a slot. Here is a sample code for the "div" function. It has two integer parameters and returns one double value.
{
div: func(readonly int a, readonly int b) double {
return double(a) / double(b) ;
}
}
Second, the function is also defined as an expression because the function is first class object.
div = func(readonly int a, readonly int b) double {
return double(a) / double(b) ;
}
double a = div(b, c) ;
Every parameters are called-by-value and they can no be modified in the called function. The readonly data attribute is attached for each parameters. (The attribute is attached by the compiler, So the user does not have to declare it.)
The function can return multiple values by using tuple.
div_and_mod = func(readonly int a, readonly int b) (int, int) {
return a / b, a % b ;
}
(int, int) a = div_and_mod(b, c) ;
Watch function observes the update of source path expressions. When the observer found the some update, the function body is executed. The returned value is treated as the slot value.
{
// calculate the width of this item by the
// parent width
width : watch(parent().width) float {
float width = parent().width ;
return width * 0.8 ;
}
}
The built-in class has the interface with KiwiScript but the function is implemented by the Objective-C.
application: {
main: func (array of string argv) int {
system.console.puts("Hello, world !!") ;
}
}
The following example display the window which contains "Hello, world !!" text.
application: {
window: {
prototype: Window ;
title: "Untitled" ;
text: {
prototype: TextField ;
value: "Hello, world !!" ;
}
}
}