The following is a simple CG/PL program:
|
An object can also be a mailbox handle, a task handle, or any other special object defined within a particular environment.
A special type of object is a null-value (null value or "no object" value).
When processing logical (boolean) values, a null-value is used as a false-value, and a string "YES" is used as a true-value.
The language lexemes are:
A comment is a double-slash (//) sign and all following symbols up to and including the next EOL (end-of-line) symbol.
white-space is a sequence of 1 or more space symbols, tabulation symbols, EOL symbols, and/or comments. At least one white space is required between a name and a keyword, other lexemes can be separated with zero or more white spaces.
Variables are local to the function/procedure instance (invocation) where they were created. All their values are destroyed when the function/procedure exits (returns).
The following unary operations are supported:
The unary operations have a higher priority than binary and ternary operations. The following expression has the value of -1, not -3:
The language implements binary operations. Binary operations are specified as
The following binary operations are supported, listed in the descending priority order:
The binary operations of the same priority group left-to-right: X op Y op Z is the same as ( X op Y ) op Z .
The binary operations have a higher priority than the ternary operations.
The language implements one ternary operation:
The ternary operation groups right-to-left: A ? B : C ? D : E is the same as A ? B : (C ? D : E)
The language implements the indexing operation:
If the object expression value is a dictionary,
the operation references the dictionary key number index
(the first key has the number 0).
Retrieving a key that does not exist results in a null-value.
An attempt to assign a new value to a referenced key results in a program exception.
If SquareDictionary value is {"one" = 1; "two" = "four"; "three"=9;},
the following expression has the value of "three":
The language implements the key, or dictionary-element operation:
The language implements the computed key, or dictionary-element operation:
The language implements function calls. A function call is specified as a name followed
by parentheses optionally containing a list of expressions (parameter-expressions).
The name used should be a name of a built-in function or an already
defined function. Parameter expressions (if any) are computed, and the function
code is executed using the parameter expression values.
A function result is an object, and the indexing and dictionary-element operations
can be applied to a function result.
If MyFunction function has 2 parameters and its returned value is an array (1,"four",9),
the following expression has the value of "four":
The keywords null and false can be used in expressions to specify a null-value, while the keyword true can be used to specify a true-value.
An empty operator performs no action:
A null operator consists of the keyword null. It performs no action:
An assignment operator consists of a data container reference (a variable, an array element, or a dictionary element), the = sign and an expression that will be computed and assigned to the specified data container.
If a data container reference is an array element, that element must exist or it must be the first non-existent array element, i.e. if an array has 3 elements, you can assign values to elements number 0,1,2, and 3. In the last case, a new element is added to the array.
If a data container reference is a dictionary element, assigning a null-value effectively removes the element from the dictionary.
A procedure call operator is specified in the same way as a function call expression.
The name used should be a name of a built-in procedure or an already
defined procedure.
If MyProc procedure has 2 parameters, the following operator is a correct procedure call:
A stop operator terminates the Task execution. It consists of the stop keyword:
A return operator finishes function or procedure execution.
A return operator within a function consists of the return keyword and an expression.
This expression is computed and its value becomes the value of the function call.
A conditional operator consists of the if keyword followed by an expression (if-expression),
the end keyword, an operator sequence (if-sequence), and the end keyword optionally
followed by the if keyword.
The if-expression is computed, and if its value is not a null-value, the if-sequence is executed, and the conditional operation execution ends.
The following example increases the myCount variable value by 2
if its value is less than 10:
The loop operator consists of the loop keyword, optionally prefixed with the while keyword and an expression (while-expression), an operator sequence (initial sequence), and the end keyword optionally followed by the loop keyword.
If a while-expression is specified, it is computed, and if its value is null the loop operator
execution ends. Otherwise the operator sequence is executed, and the loop operator execution repeats.
The following example checks the myCount variable value and keeps increasing it by 2
while that value is less than 10:
The loop operator can optionally contain one or more exitif-portions between the initial sequence
and the end keyword. Each exitif-portion consists of the exitif keyword,
followed by an expression (exitif-expression), the semicolon (';') sign, and an operator sequence.
After the initial sequence is executed, the first exitif-expression is computed. If its value is not null, the loop operator
execution ends. Otherwise the exitif operator sequence is executed, and the next exitif expression is computed.
After the last exitif operator sequence is executed, the loop operator execution repeats.
The following example appends the "aaa" string to the myWord variable,
checks the variable lengths, and if the length is less than 20, appends the "bbb" string to the myWord variable,
and repeats the process:
Some operators in a sequence may be presented in an alternative form. Operators in an alternative form are not followed by the semicolon sign.
The alternative form of a conditional operator consists of the if keyword followed by an expression (if-expression), and an operator sequence (if-sequence) enclosed in left ({) and right (}) brace signs.
The alternative form of a conditional operator can optionally contain one or more elif-portions after the enclosed if-sequence. Each elif-portion consists of the elif keyword, an expression (elif-expression), and an operator sequence (elif-sequence), enclosed into braces.
The alternative form of a conditional operator can optionally contain an else-portion.
It is specified after the enclosed if-sequence and after all optional elif-portions.
The else-portion consists of the else keyword and
an operator sequence (else-sequence) enclosed in braces.
The following example increases the myCount variable value by 2
if the value is less than 10,
it decreases the variable value by 3
if the value is not less than 10,
but it is less than 20,
and the variable value is multiplied by 4 in all other cases:
Note: it is not required to use parentheses to enclose if-expressions or elif-expressions.
The alternative form of a loop operator consists of the while keyword,
an expression (while-expression), a left brace ('{') sign,
an operator sequence (initial sequence),
zero or more exitif-portions and a right brace ('}') sign.
Each optional exitif-portion consists of the exitif keyword,
followed by an expression (exitif-expression), the semicolon (';') sign, and an operator sequence.
The following example appends the "aaa" string to the myWord variable,
checks the variable lengths, and if the length is less than 20, appends the "bbb" string to the myWord variable,
and repeats the process:
Note: it is not required to use parentheses to enclose while-expressions or exitif-expressions.
The procedures and functions can be called recursively: a procedure or a function can call itself - directly or via calling some other procedures or functions.
All code sections are reenterabale: the same code section can be used by several program activations or Tasks at the same time.
Code sections must be declared before they can be used. Declarations include forward-declarations, external-declarations, and definitions.
A program code should contain exactly one external-declaration or exactly one
definition (but not both) of each code section used.
A program code may contain not more than one forward-declaration of
a code section, specified before its definition.
Forward-definitions are used in programs containing two or more code sections
calling each other, so it is not possible to define each section before it is used
in the other code section.
External-declarations allow code sections to call code sections defined
in separate program code modules.
External-declarations are allowed only in the environments that support them,
such as the Real-Time Application environments.
See the environment description for more details.
The code section name and its parameter names specified in an external-declaration
must match the code section name and its parameter names specified in
the code section definition given in an external program code module.
The code section definition can specify more parameters than an external-declaration.
The missing parameters are assigned null-values when such an external-declaration is used.
An entry declaration consists of the entry keyword, the entry name, followed by one of the following:
If a running program reaches the end of an entry section operator sequence, an implicit stop operator is executed.
The following example shows an entry for a Real-Time Application. It tries to accept an incoming call, and if succeeds, it plays a sound. The entry code ends, quitting the program and thus finishing the call:
Or, in the alternative form, and using the alternative form of the conditional operator:
A procedure declaration consists of the procedure keyword, the procedure name, the left parenthesis, an optional list of parameter names, the right parenthesis, followed by one of the following:
If a running program reaches the end of a procedure operator sequence, an implicit return operator is executed.
The following example shows a Real-Time Application procedure that speaks the specified number:
If a forward-declaration is used, the procedure definition must have exactly the same parameters as its forward-declaration.
Example:
A function declaration is the same as a procedure declaration, but the function keyword is used instead of the procedure keyword.
All program control paths in a function code section must end with a return or a stop operator.
The following example defines a function calculating the factorial of its argument:
Below is the same function in the alternative form, using the ternary operator:
All code section (entry, procedure, and function) names used within the same program code must be unique.
The value of Same("J" + "ack","Jack") value is a null-value.
In the following example:
If the from is non-negative, the substring starts at
the from position (the first symbol of the string has the position 0),
If from is a negative value, then the substring ends at the 1-from position from the
string end (to include the last str symbol, from should be -1).
If the from value (or 1-from value) is equal to or greater than the str value length,
the result is an empty string.
If the from + len (or 1-from + len) value is greater than the str value length,
the resulting string is shorter than len.
In all other cases this function returns a null-value.
If the myArray value is (1,4,9,16,25), the RemoveElement(myArray,2) changes the myArray value to (1,4,16,25).
If the myArray value is (1,4,9,16,25), the InsertElement(myArray,2,"Jack") changes the myArray value to (1,4,"Jack",9,16,25).
Mailbox handles are internal objects representing a Mailbox.
In the following example, a simple text message is sent.
In the following example, a multipart/mixed message is sent. It contains an HTML text and a binary attachment.
Task handles are internal objects representing a Task. In a Cluster environment, a Task handler includes a reference to the cluster member running the Task.
A program (a running Task) can create a new Task by using the spawning expression. It is
specified using the spawn keyword followed by a name of an entry code section. A new
Task is created and it starts to run concurrently with the task that used the spawning expression,
executing the specified entry code section.
The spawning expression value is a task handle for the newly created task, or null if
the system failed to create a new task.
In the following example, a program executing the Main entry code section creates a new task that starts to execute the DoBackup entry code section, which copies files "file1","file2",...."file100" into "backup1","backup2",..., files.
Tasks do not share any variables, even when a Task directly creates a new Task using the spawning expression.
Tasks can exchange data by sending Events:
The Meeting mechanism allows a Task to make itself known to other Tasks associated with the same Account.
Each Account can have several named Meeting Sets and an unnamed Default Meeting Set. Several named Meetings can be created in any Meeting Set. Each Meeting is a dictionary object that can contain zero or one task handle.
The Queue mechanism allows a Task to make itself known to other Tasks associated with the same Account. When a Task registered in a Queue is found by some other Task, the found Task is removed from the Queue.
Note: this function removes the first Task from the Queue. Unless the retrieved Task re-enqueues itself into the same Queue, no other Task will find it in that Queue.
variable ::= name
basicData ::= variable | funcCall
dataRef ::= basicData | dataRef [ expr ] | dataRef . name | dataRef . ( expr )
spawnExpr ::= spawn name
basicExpr ::= string | number | dataRef | null | false | true | spawnExpr
unaryOp ::= ! | not | - | +
unary ::= basicExpr | unaryOp unary | ( expr )
multOp ::= * | / | %
multBinary ::= unary | multBinary multOp unary
addOp ::= + | -
addBinary ::= multBinary | addBinary addOp multBinary
cmpOp ::= < | <= | == | != | >= | >
cmpBinary ::= addBinary | cmpBinary cmpOp addBinary
logicOp ::= & | and | | | or | && | and then | || | or else
logicBinary ::= cmpBinary | logicBinary logOp cmpBinary
ternary ::= logicBinary | logicBinary ? logicBinary : ternary
expr ::= ternary
argList ::= expr 0*(, expr)
funcCall ::= name ( [argList] )
procCall ::= name ( [argList] )
leftSide ::= variable | dataRef [ expr ] | dataRef . name | dataRef . ( expr )
letOper ::= letOper = expr
nullOper ::= | null
stopOper ::= stop
returnOper ::= return [ expr ]
ifOper ::= if expr then opSequence 0*( elif expr then opSequence ) [ else opSequence ] end [ if ]
altIfOper ::= if expr { opSequence } 0*( elif expr { opSequence } ) [ else { opSequence } ]
loopOper ::= [while expr ] loop opSequence 0*( exitif expr ; opSequence) end [ loop ]
altLoopOper ::= while expr { opSequence 0*( exitif expr ; opSequence) }
oper ::= nullOper | procCall | letOper | returnOper | stopOper | ifOper | loopOper |
altOper ::= altIfOper | altLoopOper
seqOper ::= oper ; | altOper
opSequence ::= 0*( seqOper )
entryBody ::= forward ; | is opSequence end [ entry ] ; | { opSequence }
procBody ::= forward ; | external ; | is opSequence end [ procedure ] ; | { opSequence }
funcBody ::= forward ; | external ; | is opSequence end [ function ] ; | { opSequence }
parmList ::= name 0*(, name)
entry ::= entry name entryBody
procedure ::= procedure name ( [ paramlist] ) procBody
function ::= function name ( [ paramlist] ) funcBody
program ::= 1*(entry | procedure | function)