Statements

Statements in CAOPLE can be primary statements, local variable declarations and structured control statements.

Statement ::=

{ PrimaryStatement | StructuredControlStatement | LocalVarDecl | ";" }

Local Variable Declarations

A local variable declaration can be at any place where a statement can be. It introduces a new variable and can have an optional assignment of an initial value to the variable. The scope of the variable is from the variable declaration to the end of its immediate enclosure statement.

When a local variable declaration is executed, a storage space is allocated to the variable and the initialisation expression, if any, is executed and the result value is assigned to the variable.

When the execution of the immediate enclosure statement finishes, the storage space allocated to the variable is discarded; and outside the scope, the variable is no longer accessible.

The variable identifier must not be in conflict with any other identifiers; i.e. the identifier must have not been used for any other entity.

The syntax of local variable declaration in EBNF is given below.

LocalVarDecl ::=

⟨VAR⟩ VarID=Identifier ":" TypeName [ ":=" Expression ] ";"

Primary Statements

There are four kinds of primary statements: agent operation statements, action invocation statement, assignment statement, and instruction statements.

PrimaryStatement ::=

AgentOperationStatement

| InstrStatement

| ActionInvoke

| Assignment

Assignment

The syntax of assignment statements is given below.

Assignment ::=

Identifier { "." FieldName | "[" AdditiveExpression "]" } ":=" Expression ";"

The identifier on the left hand side of the assignment symbol := must be either a variable (local or global), an agent parameter, or an action parameter.

When the assignment expression is executed, the expression on the right hand side of the assignment symbol := is evaluated and the result value is then assigned to the variable or its element as indicated by the .FieldName for a record type variable or by the [index] for a list data type variable, and so on.

The data type on the left hand side and right hand side of the assignment symbol must be identical.

Agent Operations

There are five different agent operation statements. Their syntax formats are defined as follows.

AgentOperationStatement ::=

CreationStm | JoinStm | QuitStm | SuspendStm | ResumeStm

CreationStm ::=

⟨CREATE⟩ [ AgentVar ⟨OF⟩] CasteName "(" [ ActualParamList]")" ]

[ ⟨FROM⟩ CasteLocation=StringTypeExpression ]

[ ⟨AT⟩ ComputerLocation=StringTypeExpression ] ";"

ActualParamList ::=Expression { "," Expression }

JoinStm ::=

⟨JOIN⟩CasteName "(" [ ActualParamList ] ")"

[ CasteLocation=StringTypeExpression ] ";"

QuitStm ::=⟨QUIT⟩[ CasteName ] ";"

SuspendStm ::=⟨SUSPEND⟩ [ CasteName ] ";"

ResumeStm ::=⟨RESUME⟩ [ CasteName ] ";"

DestroyStm ::=⟨DESTROY⟩ [ AgentVar ] ";"

  • Create and Destroy Statement

An agent creation statement creates an agent of a specified caste. The computer on which the agent to be located can be specified in the optional @- clause, and the computer from which the executable code of the caste is down- loaded can be specified in the optional FROM-clause. These locations must be string type expressions representing IP addresses. If any of the optional clauses is omitted, the default location is the local computer, i.e. the computer where the creation statement is executed.

The statement may also contain an agent variable in the optional Identifier-OF-clause. The identifier must be a variable of the agent type of the CasteName. The result of execution this statement is creating an agent and assign the agent ID to the variable.

The actual parameters, if any, are passed to the newly created agent to initialise the agent.

For example, an execution of the following statement will create an agent of caste Peer on the computer IP address "161.74.147.20". The universal unique identity of the newly created agent will be assigned to the variable researcher of type Peer.

Create researcher of Peer() @"161.74.147.20"

The destroy statement kills the agent when the agentID is given or kills itself when there is no agentID given. Consequently, the agent will terminate its execution and be removed from the system.

A distinctive feature of CAOPLE is that an agent can join or quit a caste dynamically. It can also suspend its membership of a caste for a while and then resume it. These are achieved via executing a join, quit, suspend or a resume statement, respectively.

The CasteName in these statements is the name of the caste that the agent joins, quits, suspends or resumes. If it is omitted, the default is the current caste that the statement is in. The caste name must be listed in the uses clause.

  • Join and Quit Statement

The Join statement makes the agent a member of a specified caste, thus it obtains the state variables, parameters, environment variables, actions and execution body declared in the caste. The agent is also registered to the caste as a member.

When an agent joins a caste that has parameters, the actual parameters in the form of a list of expressions must be given as the parameters of the join statement. The types of these expressions must be compatible with parameter types give in the caste declaration. When the join statement is executed, the expressions are evaluated, and their values are passed to the initialisation code and the body code of the caste.

The Quit statement causes the agent to quit its caste membership for the given caste. When an agent quits from a caste, it will lost all components of the caste, including the state variables, actions and environment, and body.

When an agent quits a caste, it will also quit its a membership of the supercaste(s) (if any) of the quitted caste.

  • Suspend and Resume Statement

The Suspend statement statement causes the agent to suspend its membership to the caste. When an agent suspends its casteship to a caste, it will hold the values of the state variables of the caste as it is, but it will not execute the code of the caste until it resumes the casteship.

When an agent suspends a caste, it will also suspend its a membership of the super-caste(s) (if any) of the suspended caste.

The after an agent suspended its membership to a caste, the Resume statement can be executed to re-gain its membership to continue the execution of the body of the caste. If the suspension of membership is too long, the messages received during the suspended period may be lost.

  • Casteship: Caste Membership

An agent operation statement causes an agent to change its membership to a caste (i.e. the casteship). Thus, an agent can manage it casteship by performing these statements. However, the execution of such a statement is not always valid. In order to define the rules of valid casteship operation, we first define the notion of explicit caste membership and implicit caste membership.

An agent is an explicit member of a caste A, if and only if it is either created as an instance of the caste or joined the caste explicitly by executing a Join statement to this caste. By doing so, the agent also becomes a member of caste A’s super-castes, if any. In this case, the agent is an implicit member of the super-castes of caste A.

The following are the rules of caste membership management.

  1. An agent can change its state of explicit membership to a caste by executing a quit, suspend, resume statement, but not the state of its implicit membership, which are changed implicitly when the agent change the state of its explicit casteship.

  2. An agent can become an explicit member of a caste B by execution a "join B" statement, even if it is already an implicit member of B. When the agent subsequently quits from B, it will retain its implicit membership of caste B.

  3. An agent’s execution of a casteship management statement will have no effect if it is already in the membership state, e.g. it is already suspended to the caste.

Two implications of the above rules are:

  1. An agent can resume the membership to a caste A, if and only it is in the suspended state to caste A due to explicit suspending of the casteship by an execution of a "suspend A" statement.

  • An agent cannot resume the membership to a caste B that it was not created as an instance of the caste or not joined the caste explicitly, even if the caste is a super-caste of another caste that it is a member of.

The following is an example.

join GreetingPeer();

...

quit GreetingPeer();

In the above example, assume that the agent is not a member of GreetingPeer at the beginning. After executing the statement "join GreetingPeer", the agent will be a member of GreetingPeer caste. Assume that there is no quit statement between these two statements in the example. After executing the statement "quit GreetingPeer", the agent will no long be a member of the caste GreetingPeer.

Action Invocation Statement

The action invocation statement is in the same format of procedure call and method call statement in traditional programming languages. The syntax is given below.

ActionInvoke ::= ActionName "(" [ ActualParamList ] ")" ";"

When the action declaration contains parameters, an invocation of the action must have corresponding actual parameters in the form of a list of expressions. The types of the actual parameters must be the same as the corresponding parameters’ type as declared.

The execution of an action statement starts with the evaluation of the expressions as the actual parameters. The result values are assigned to the parameters. Then, the body statement given in the action declaration is executed. If the action is externally visible (i.e. not an internal action), when the execution of the body successfully completed, an event is generated such that the event name is the action name and the parameters are the values of the action parameters when the execution of the body finishes. This event can be captured by all agents who observe this agent’s behaviour.

For example, assume that say(word:string) is an action, where word is a parameter of type string, and in the body of the action, the value of the parameter word is not changed. For an invocation say("Hello, world!") of the action say, the value of the actual parameter is the string "Hello, world!". A successful execution of the action will generate an event with the event name being say and the actual parameter being "Hello world!".

Instruction Statements

CAOPLE has six instruction statements as primary statements. They are print, super, wait, Java library method call, Java object creation, and Java object method call. The syntax is given below.

InstrStm ::= ⟨WAIT⟩IntegerTypeExpression ";"

| ⟨PRINT⟩stringTypeExpression ";"

| ⟨SUPER⟩"(" ActualParamList ")" ";"

| ⟨JAVA_LIB_CALL⟩JarFileName MethodName ";"

| ⟨JAVA_OBJ_CREATE⟩ClassName ";"

| ⟨JAVA_OBJ_CALL⟩ClassName MethodName ";"

By executing a wait statement, for example, "wait 100", the agent will wait for 100 ms before progress to the next statement.

The super statement can only be included in the initialisation part of the caste declaration when the caste is an extension of a super-caste. When the super statement is executed, an instance of the super-caste will be created and initialised with the actual parameter given in the super statement. The data types of the actual parameters must match the data types of the super-caste's parameters.

An execution of a print statement will display the value of the string type expression on the console screen of the machine.

A JavaLibCall statement will execute the method of the jar file specified in the statement.

A JavaObjCreate statement will create a Java object of the class of the jar file. This object is associated to the agent and accessed implicitly through the JavaObjCall statements executed by the agent.

A JavaObjCall statement is usually used together with a JavaObjCreate statement. An execution of the JavaObjCall statement will execute the method of the Java object previously created by a JavaObjCreate statement.

These three Java related statements provides a mechanism for wrapping Java code into agents.

The following is an example of using JavaLibCall statement.

caste Date(){

var currentTimeMS: int;

action Get_Current_TimeMS(){

JavaLibCall "Date.Date", "GET_CURRENT_TIME";

}

init {

Get_Current_TimeMS();

print "The current time is: " + currentTimeMS;

}

body {}

}

The following is the Java code for the above example.

package Date;

import obuaop.cavm.agent.*;

public class Date {

public void GET_CURRENT_TIME(Agent agt) {

Variable currentTimeMS;

currentTimeMS=agt.getVarByName("currentTimeMS");

currentTimeMS.value= (int) System.currentTimeMillis();

}

}

Structured Control Statements

CAOPLE has a number of traditional structured control statements that sup- port structured programming, such as IF-statement, FOR-Statement, WHILE- statement, REPEAT-statement, etc. It also has two new statements for concurrent and event-driven programming, which are WHEN-statement and TILL-statement to support event-driven computing and synchronisation between parallel executed agents. The WITH-statement in traditional programming languages, and its similar LET construct in functional programming languages, has been given a new semantics of atomicity, thus can be used to maintain the integrity of structured data. Two other structured control statements are introduced, the FORALL statement and SEARCH statement, for processing aggregate data in a more convenient way. They are particularly useful for processing list type of data.

StructuredControlStatement ::=

| IfStatement | CaseStatement

| ForStatement | WhileStatement | RepeatStatement | LoopStatement

| ForallStatement | SearchStatement

| WithStatement

| WhenStatement | TillStatement

If Statement

The syntax is:

IfStatement ::=

⟨IF⟩"(" Expression ")" "{" Statement "}" [ ⟨ELSE⟩"{" Statement "}" ]

The if-statement is the same as in traditional programming languages. It consists of a sequence of one or more (condition, statement) pairs, and a standalone optional “else” statement. Each condition is evaluated in turn until one such condition evaluates to true and then the corresponding statement is executed. If no conditions evaluate to true then the else statement is executed if it is exists; otherwise no statement is executed.

Case Statements

The syntax of case statement is:

CaseStatement ::=

⟨CASE⟩"(" Exp ")" "{"

{ Exp "->" "{" Statement "}" ";" }

[ ⟨ELSE⟩"{" Statement "}" ";" ] "}"

The case statement resembles the switch statement of traditional programming languages. It consists of a test expression, a sequence of (guard expression, statement) pairs, and an optional standalone “else” statement. The test expression is first evaluated and checked against the guard expressions one by one. If it is equal to a guard expression then the corresponding statement is executed. If more than one guard expression matches, then only the statement corresponding to the first is executed. If no guard expressions match, then the else statement is executed. If no guard expressions match and there is no else-clause, it is equivalent to skip.

The guard expressions must of the same datatype of the test expression.

For Statement

The syntax of for statement is:

ForStatement ::=

⟨FOR⟩"(" [ ⟨VAR⟩] Identifier ":=" AdditiveExpression ⟨TO⟩ AdditiveExpression ")"

"{" Statement "}"

An execution of a for-loop is a repeated execution of the loop body statement for a number of times. The number of times is determined by a control variable (of Integer type) together with a from-value and a to-value. The repeated execution of the loop body starts with the control variable being assigned with the from-value. After each execution of the loop body, the value of the control variable is increased by 1. The repeated execution of the loop body finishes when the control variable’s value is greater than the to-value.

When there is a keyword ⟨VAR⟩ before the step variable of the for-statement, the step variable is new and declared as of integer type.

While Statement

A while loop consists of a control condition and a loop body statement, where a control condition is a Boolean expression. The syntax is given below.

WhileStatement ::= ⟨WHILE⟩"(" Expression ")" "{" Statement "}"

The iteration of the loop body statements is determined by the control condition. First the control condition is evaluated. If condition is false, the loop terminates. If the condition is true, the body statement is executed and the while-loop iterates.

Repeat Statement

The syntax of repeat statement is given below.

RepeatLoop ::= ⟨REPEAT⟩ "{" Statement "}" ⟨UNTILL⟩"(" BoolExp ")"

A repeat loop also consists of a loop body statement and a control condition. But, it differs from the while-loop in the way it controls the iteration. For the repeat statement, the loop body statements are executed first. Then, the control condition is evaluated. If the condition is false, the loop iterates. If the condition is true, the iteration terminates.

Loop Statement

A loop statement consists of a sequence of statements that is executed over and over unboundedly. This is useful for representing a continuous process that has to respond to periodical events.

LoopStatement ::= ⟨LOOP⟩ "{" Statement "}"

Forall Statement

The syntax of forall statement is as follows.

ForAllStatement ::=

⟨FORALL⟩ "(" [⟨VAR⟩] LocalVar=Identifier ⟨IN⟩ Expression

[ "|" BoolExpression ] ")" "{" Statement "}"

The expression in the forall statement must be a List type. When the keyword ⟨VAR⟩ is present, the forall-statement introduces a local variable identifier whose type is the element type of the Expression. Its scope is within the body of the forall-statement. Otherwise, the identifier must be an existing variable whose type is the element type of the expression. The forall statement executes the statement repeatedly with the local variable assigned with one element of the list a time to the list length.

For example, consider the follow fragment code. The value of variable MyList is the list [1,2,3,4,5] of integers, and integer type variable y with an initial value of 0. After an execution of the forall-statement, the value of variable y will be 1+2+3+4+5 = 15.

var y: int;

var myList: IntList;

y:=0;

myList := IntList: [1, 2, 3, 4, 5];

forall (var x in myList ) { y := y+x }

Note that, an element in a list could be undefined since not all elements in a list are required to be defined. For example, executing the following sequence of statements will print on the screen "y==100". However, without the if (x.isDefined) statement, it will have a runtime error since many elements between the 2nd to the 199th are not defined.

var y: int;

var myList: IntList;

y:=0;

myList[0] := 10;

myList[10]:= 40;

myList[200] := 50;

forall (var x in myList ) {

if (x.isDefined) {

y := y+x;

}

};

print "y==" + y;

If the BoolExpression is provided in a forall statement, the loop body will only be execute if the BoolExpression evaluates to ⟨TRUE⟩. A typical use of the BoolExpression is to filter out the elements in the list. The following is such an example.

y:=0;

forall (var x in myList | x.isDefined ) {

y := y+x;

};

print "y==" + y;

Search Statement

The syntax of Search statement is as follows.

SearchStatement ::=

⟨SEARCH⟩"("[⟨VAR⟩] LocalVar=Identifier ⟨IN⟩ Expression [ "|" BoolExpression ] ")"

"{" Statement "}" ⟨DEFAULT⟩"{" Statement "}"

The expression in a search statement must be a List type. When the keyword ⟨VAR⟩ is present, the search-statement introduces a new local variable identifier whose type is the element type of the Expression. Its scope is within the loop body of the search-statement. Otherwise, the identifier must be a variable whose type is the element type of the expression. The search statement assigns an element of the list to the variable one a time from the beginning of the list (i.e. at index 0) to the end (i.e. at the index of list length -1). It then executes the BoolExpression. If the BoolExpression evaluates to ⟨TRUE⟩, the body statement is executed and the whole search statement finishes. If no elements in the list make the Bool expression true, the default statement is executed if it presents, and the whole search statement finishes.

In other words, a search statement searches through a list data and find the first element in the list that meets the search criterion (i.e. the bool expression) to perform the body statement; if no element in the list that meets the criterion, it will do the default statement.

For example, consider the following code fragment. It will search for the first element in an integer list that is defined and whose value is greater than 10. Once the element is find, it will double the value. If no element in the list is greater than 10, it will add an element of value 12 to the list (at the end).

var myList: IntList;

...

Search (var x in myList | x.isDefined && x>10) {

x := x+x;

} Default {

myList.addElement(12);

}

With Statement

The with statement ensures the data integrity of structured data by ensuring the atomicity of updating the structured data. The syntax is:

WithStatement ::=

⟨WITH⟩ ⟨VAR⟩ Identifier "=" VariableName

{ "." FieldName | "[" IntegerExpression "]" }

"{" Statement "}" }

The execution of a with-statement will create a new variable with the identifier on the left-hand side of the "=" symbol. The expression on the right-hand side will be evaluates to a structured data to be processed by the statement. The value of the expression is assigned to the newly created variable. When the statement finishes, the value of the newly created local variable will be assigned back to the structured data. The update of the designated data will be atomic. Thus, the with-statement will maintain the integrity of the structured data.

For example, assume that variable x is of data type Person. The following with statement will ensure the data of various fields of DoB be updated at the same time so that the data integrity can be maintain all the time even when the statement is executed in parallel with the statements of other agents who read the same data.

with var y = x.DoB {

y.day := 9;

y.month := 2;

y.year := 1961;

}

When Statement

The when statement is specifically design for event-drive programming. The syntax is:

WhenStatement ::=

⟨WHEN⟩ EventExpression "{" Statement "}"

EventExpression ::=

⟨EXIST⟩ Identifier ⟨IN⟩ CasteName ":" ActionPattern

| AgentID ":" ActionPattern

A when statement consists of a scenario-statement pair. The execution of a when-statement is first to evaluate the scenario (i.e. the EventExpression). If it is true, then the corresponding statement is executed; otherwise, the statement is skipped.

A scenario describes a situation in the environment in terms of the events happened due to the performance of actions by the agents in the environment. There are two formats of scenarios that CAOPLE supports directly. The first is when a particular agent has performed a certain pattern of action. The second is when there is an agent in a caste has performed a certain pattern of action.

For example, the exist-scenario in the following code introduces a variable x of agent type of caste C. Variable x’s scope is within the corresponding statement of the when-statement.

when exist x in C:ActionPattern { statement }

An action pattern consists of the name of the action followed by parameters. The syntax definition is given below.

ActionPattern ::= ActionName "(" [ ParaList ] ")"

ParaList ::= PatternParameter { "," PatternParameter }

PatternParameter ::= Expression | ⟨RCV⟩ Variable

A pattern parameter can be either variable or an expression. When it is an expression (note: a special case is a variable without the keyword ⟨RCV⟩), the value of the expression is used to match the parameter of the action event. If there is a keyword ⟨RCV⟩ before a variable, the value of the parameter in the event is assigned to the variable, when the event is matched to the action pattern.

For example, let something be a string type variable, Agt be an agent variable of type Peer. The following are some examples of scenarios.

Agt: say("Welcome to the world!")

Agt: say(rcv something)

Agt: say(something)

exist x in Peer: say("Welcome to the world!")

exist x in Peer: say( rcv something)

exist x in Peer: say(something)

In the above examples, the 1st is true when agent Agt said "Welcome to the world!". The 2nd is true when the agent Agt said something and what Agt said will be assigned to variable something when the event expression is evaluate. The 3rd is true when the agent Agt said a word that is equal to the value of the variable something. The 4th is true when there is an agent in the caste Peer said "Welcome to the world!". The 5th is true when there is an agent in the caste Peer said something, and what that agent said is assigned to variable something when the event expression is evaluated. Finally, the 6th is true when there is an agent in the caste Peer said a word that is equal to the value of the variable something.

Till Statement

The till statement is designed for synchronisation in event-driven computing. The syntax is defined as follows.

TillStatement ::=

⟨TILL⟩ EventExpression "{" Statement "}"

{ "||" EventExpression "{" Statement "}" }

[ ⟨TIMEOUT⟩ "(" Expression ")" "{" Statement "}" ] ";"

The execution of a till statement will cause the agent to wait for a specified period of time (given in the timeout clause) until one of the event expressions becomes true to progress to the corresponding statement, or timeout (that is, the specified period of time expires) and progress for the timeout statement. It will then finish the execution of the statement and progress to the next statement.

The expression in the timeout clause must be of integer type. The value of the expression is the length of the period of time for timeout in milliseconds, i.e. 1/1000 seconds.

For example,

Till exist Agt in Peer: Say("Hello world!") {

Say("Welcome to the world.")

}

|| exist Agt in Peer: Say("Welcome to the world.") {

Say("My name is " + name +". How are you?")

}

timeout (3000) {

Say("Good Bye!")

};

In the above example, the agent will wait for a period of 3 seconds (i.e. 3000 milliseconds). In this period of time, it will take the action Say("Welcome to the world."), if some agent of the caste Peer took an action Say("Hello world!"), then progress to the next statement; or, it will take an action Say("My name is " + name +". How are you?"), if some agent in the caste of Peer Say("Welcome to the world."), and then progress to the next statement. If no such event happens in the period of 3 seconds, it will take the action Say("Good Bye!") when timeout (i.e. at the end of 3 seconds), then progress to the next statement.

When a Till statement does not contain a timeout-clause, it will wait indefinitely until one of the events happens.