5 GRAMMAR

PbML grammar is fairly simple if you know C++/Java (and maybe Perl).

It is CASE SENSITIVE, and it describes an OO world, so there are classes, (interfaces), methods and attributes. As for this version PbML is a loosely typed language.

5.0 conventions

During the explanation of the syntax the following are used:

<bracketed_name> stands for a variable, an attribute, a method name, a class name etc...  
[...]? things between square brackets are optional  
[...]* things between square brackets may be repeated zero or more times  
[...]+ things between square brackets may be repeated one or more times  
A | B means that one can put or A or B, but not both  

5.1 BASICS

Usually identifiers are expressed just like in C/C++/Java, and so are constants.

Valid variable and list names: foo, f_56

Basic types are: "int", "float", "boolean" and "string" (and "scalar").
Object types are, for the moment being, just "object".

To make a variable/device of type "<type>" just write <type>[] ;
for example: boolean[] coins;

Special values are:

for boolean values:
"true", "false"
for object reference values:
"self" means the object whose method is being executed
"null" means no-object

Please see the examples.

5.2 SYNTAX

A PbML file is a sequence of class definitions and interface definitions.

Please note that the same class can be defined across several files. Each time one must redeclare the class header and then add new attributes and methods. The resulting class will be the union of every method, device and attribute found in every file under the definition of the same class.

interface definition: //TO BE WRITTEN

class definition:

class <class_name> [implements <interface_name>[,]+ ]?
{

vars:

<access_type> <type>[ "[]" ]? |<obj_type> [ "[]" ]? [ <attr_name> [,]]* ;
//more attribute definitions here

devices:
<obj_type> ["[]"]? <attr_name> [, <attr_name>]*;
//more device defs here

actions:
<method_name> ( [<type> <param_name> [,<type> <param_name> ]* ]) @ <phase_name>

{
//here method body
}
//more action here

commands:
<method_name> ( [<type> <param_name> [,<type> <param_name> ]* ]) @ <phase_name>

{
//here method body
}
//more commands here

routines:
<type> <method_name> ( [<type> <param_name> [,<type> <param_name> ]* ])

{
//here method body
}
//more routines here

}

Please note that each section (vars, actions, ...) may be repeated more than once (it can even by omitted) in a class definition. <method_name> <phase_name> <obj_name> <attr_name> <param_name> can be any valid C-like name.

<access_type> can be "private" (default if nothing is specified), "public" (other obejcts can read and write this attribute), "readable" (other obejcts can read this attribute), "writable" (other obejcts can write this attribute).

Thus it makes perfect sense to write:

int i=item1.subitem2.subsubitem.i;

as long as "subitem2" is a object readable attribute of "item1", "subsubitem" is a readable object attribute of "subitem2" and finally "i" is a readable scalar attribute od "subsubitem".

 


A method body is a collection of statements:

PbML features the typical statements, that are written and behave just like C++ ones:

automatic variable declaration (of type int, boolean, string, float, scalar, object, int[], object[],...)

if..then..else

while .... (no break statement yet)

return ....

assignment

Other statements deserve a little explanation:

5.2.1 METHOD INVOCATION

One can invoke a method of the same object whose code is executed, or of some other object.

The former is written in a typical way:

foo(4,5);

or mimicking some high-level languages, one can pass parameters in whatever order, provided one specifies the formal parameters name for each parameter:

foo(start=5, end=4); //if foo's prototype is foo(int start, int end);

to invoke a method of an object, or of a device we own, just write:

<device_name>.method(params);
<object_reference>.method(params);

PbML does support multiple indirection, if the attribute whose method is called is a readable object.

In theory only routines should be invoked. This means that in practice also actions and commands can be invoked. But beware!! This is a most-unnatural behaviour!.

5.2.2 LIBRARY CALL

A library call takes this form:

<library_name>::<service>(params);

We have now three libraries:

Yagga, Sys and Math.

Suche services are listed in appendix B.

Math is a one-to-one mapping of the java.lang.Math class, and has every usual method, max, min, sqrt, sin, cos, atan... in the more obvious form.

Library calls does not allow for the passing of parameters with their formal names:

Math::min(a=5,b=19); //wrong,
Math::max(5,8); //correct

5.2.3 LIST OPERATIONS

Lists are collections of devices, or collections of reference to objects, or plain lists of scalar values: The former are declared in the "devices:" section of a class definition, the latter can bedeclared in the "vars" section or in the body of a function.

5.2.3.0 objects and lists

Please note that lists contains only reference to objects, in an ordered or unordered way. By removing an object form a list one does not destroy the object.

To do so one must explicitly call the "destroy" operator on an object reference.

Moreover, assigning a list to an object reference makes perfect sense and simply assigns the first element of the list to the object element.

5.2.3.1 list iteration

To iterate over such collections one must use the following construct:

foreach <obj_reference> | <scalar_name> in <list_name>
// statement here

for example:

object[] ships;
ships=Yagga::getAll("Ship");
foreach ship in ships
{
   ship.doDamage(5);
}

In the body of the foreach statement the variable "ship" is given every iteration the value of one element in the list, from first to last.

5.2.3.2 list broadcast

To call a method on every object contained in a list one can write

foreach item in list
   item.method();

a much compact syntax is:

list.method();

This will broadcast the method() call to every object in the list. This can simulate user defined events, and the code executes more quickly than the foreach construct.

Please note that if an object in the list does not have the method method(), Yagga will issue a warning but will keep on executing the cycle.

5.2.3.3 list assignment

One can initialize or assign elements to a list in several ways.

The simplier are:

object[] list=list_old; //assign the values of another

object[] list=(item1,item2,list3); //assign a list of values, that can be single objects or lists

Please note that list and list_old will reference the same objects.

If you destroy an object from list_old, it won't be accessible from list. If you, instead, remove an object from a list it will keep on existing on the other list.

5.2.3.4 List subsetting

Llists can be restricted by mean of the "subset" operator.

This operator has the form

list=old_list[a==5];

or

item=old_list[index];

In the first case list will be assigned every object in old_list that has the attribute "a" with the value of 5. In the second case item will be assigned the value of the (index+1)-th element (lists start counting from zero!!!) of the list.

Generically the subset operator must always follow this syntax:

list[<item_side_expression> <relational_operator> <current_context_expression>]

or

list[<current_context_expression>]

where:

<item_side_expression> is an expression evaluated in the "scope" of each item of the list. It can thus contains only constants or name of object's attributes or devices (or method calls, provided methods are defined in every object in the list).

<relational_operator> MUST be one of : "==" "!=" ">" ">=" "<" "<="

<current_context_expression> is an expression evaluated in the "scope" of the current object. It can use local variables, constants, obeject references or calls etc... as usual. It can, obviously, not refer to attrbutes or methods of a particular object in the list. If this appears alone between the square brackets, than it is taken as the index of the element to be returned.

Please note that in the first syntax the relational operator must always be present, even if it seems superfluous.

So for example:

list=old_list[visible]; //visible is a boolean attrib of elements in the list

the compiler won't compile.

Instead one must explicitly specify:

list=old_list[visible==true];

5.2.3.5 list methods

Lists have several methods predefined.

They have a slightly different from standard method invocation, but these methods have parameters and return values.

These methods are called with the following syntax:

object list=old_list;
list=>method(params,params,...);

length

int length()

returns the number of items referenced by the list.

contains

boolean contains(object item)

returns true if "item" is referenced in the list, false otherwise

remove

object remove(int index)
object remove(object item)

removes from the list the index-th element (or the reference to the "item" object). If removal is successful returns the removed object.

Please note that:

list=list=>remove(item);

is equivalent to just

list=>remove(item);

pop push

object pop()
object popend()

pops the rightmost element of the list. Returns the popped element or null.

object popbegin()

pops the leftmost element of the list. Returns the popped element or null.

object[] push(object item)
object[] pushend(object item)

pushes the obejct passed as a paramater in the list, on the right side. Returns the new list.

object[] pushbegin(object item)

pushes the object passed as a paramater in the list, on the left side

sort

object[] sort(string key1, string key2, ....)

sorts ascendingly elements in the list according to the ordering based on the attributes names passed as parameters.

If list[] contains object of class "Automobile" with attributes "numberOfWheels", "maxSpeed" and "4Wd" then

list=>sort("maxSpeed")

will sort list[] with cars sorted from slower to faster, whreas

list=>sort("4Wd","numberOfWheels")

will sort cars by 4Wd and numberOfWheels, i.e. first came 2Wd (and among 2Wd cars are sorted by ascending number of wheels) then came 4Wd (and among these, cars are sorted by number of wheels).

If an attribute is not present in an object results are impredictable.

Please ensure that every object in the list has the attributes used for sorting. Returns the sorted list.

object[] downsort(string key1, string key2, ....)

sorts descendingly elements in the list according to the ordering based on the attributes names passed as parameters. Returns the sorted list.

min/max

scalar min(string attribute)

returns the lowest value that the attribute specified in "attribute" has among objects in the list. "scalar" means it can return a string, a float, an integer or even a boolean.

scalar max(string attribute)

returns the highest value that the attribute specified in "attrbiute" has among objects in ther list.

minOn / maxOn

object[] maxOn(string attrib)

will return a list of objects that have tha maximum value of the attribute specified in "attrib" among the objects in the list. The returned list can contain a single element or even an empty list (with zero elemens).

object[] minOn(string attrib)

will return a list of objects that have tha minimum value of the attribute specified in "attrib" among the objects in the list.

5.2.4 ACTION TERMINATION

As we have seen in section 2.1.1 Actions should terminate with one of the following statements, which must appear in the same place a "return" statement would appear. These statements tell the engine if, and how, recall this action for a particular object. They have the same flow-control meaning as a return, i.e. they terminate the current action execution whenever they appear in the code being executed.

terminate
action must not be recalled during current phase/event
canTerminate
action can be recalled during current phase/event
recallMe
action must be recalled during current phase/event

If none is specified, than an action returns with a default code of "terminate".

5.2.5 CREATING NEW OBJECT

New objects can be created. The instruction is:

object <obj_ref> = new <class_name>;

after this instruction the object exists until one destroys it. After an object creation the OWNER is copied from the creating object to the created object. One can then modify this value.

5.2.5.1 CREATING FROM PROTOTYPES

New object can be created from special template stock object called prototypes.

These prototypes are objects of an existing PbML class with certain default values for their attributes and devices.

These prototypes are defined in prototype files. Please see appendix A.4 for further details on the file format.

To create an object from a prototype:

object <obj_reference>= new <class_name>(<expression>);

<expression> is an expression yelding a string representing a prototype name:

string name="Fokker DR II";
object a=new Airplane(name);
a.setX(4);
....

Please note the following:

-The owner of the newly created object is the same as the owner of the object in wich the code is being executed.

-Scalar attributes are copied exactly from the prototype's attributes to the newly created object

-Objects and devices follows a particular behaviour:

1) in the prototype definition an object reference or device must be assigned, as a value, the name of another prototype.

2) this prototype is created in cascade, i.e. a side effect of the creation of an object from prototype is the cascading creation of every referenced object/device.

3a) if this secondary object is assigned to a device, it is made unique. i.e. every instance of the parent object (created from a prototype) references a different device created from the same prototype. The owner of these cacading object is the same as the owner of the parent object.

3b) if the secondary object is assigned to an object reference (in the vars: block) then it is made global, i.e. every instance of the parent object (created from a prototype) references the SAME object created from the same prototype.

The owner of these "global" objects is a generic "__UNIVERSE__", but of course is no particular player.

Example:

Prototypes:

[Move m1]
....

[Move m2]
....

[Guns Vickers]
...

[Airplane Fokker]
...
myMove<=m1,m2;
//in the PbML for Airplane,
//myMove[] is defined in the vars: block

guns=Vickers;
//in the PbML for Airplane,
//guns is defines in the devices: block

if one instantiates:

object a1=new Airplane("Fokker");
object a2=new Airplane("Fokker");

two instances of Guns (from proto Vickers) are created (let's call them 'Guns0001' and 'Guns0002', and one instance of Move m1 and one of Move m2 (from protos) are created with name 'm1' and 'm2';

'guns' of 'a1' will refer to the object of class Guns named 'Guns0001' and 'guns' of 'a2' will refere to 'Guns0002', whereas the list 'myMove', in both airplanes, will have two items, each of one refering to the same Moves: 'm1', 'm2'.

It is then clear that building objects from prototypes has a strong effect: devices are actual components of the parent object, whereas objects as variables are just references to objects, objects that can and are shared among different objects.

So please use object instantiation in prototypes with caution. This behaviour is only found in object instantiation from prototypes with cascading creation of other objects. If one, in the code does write:

object myM1=new Move("m1");
object myM2=new Move("m2");
a1.addMove(myM1); //modify the myMove list
a1.addMove(myM2);

then a1's myMove will point to newly created (and different from anybody's else) list of moves.

5.2.6 DESTROYING OBJECT

To delete an object from the game simply write:

destroy <obj_ref>;

The deletion will take place at the end of the current phase.

5.2.7 CREATING VIEWS

Views are created in order to give to the player who owns an object information over some aspect of the simulated world.

Information is passed without any predefined form or meaning, except the one the designer wants to give to information received by the client.

To create a view an object sends to its owner (or to every player if broadcst is specified) a list of pairs, each pair consist of an item and a value assigned to this item.

create [broadcast]? view
{
pair, pair, ...
};

each pair is

name=value,

where value can be any valid expression, a constant, a variable or an attribute. If it is an object reference or a list the compiler will give error. In such a case, please supply a suitable string that describes the obejct.

For example:

create view
{
type="introspection",
gear=gear,
max=max,
fuel=fuel*10
};

Please see chapter 7 and appendix B for the creation of the default introspective view.

5.2.8 OWNERS

Each object in the world has an "owner", a string representing the player who "owns" this object.

This doesn't mean that a player automatically knows its object, she still needs to receive a view about a particular object in order to know it.

This is a default attribute: 'OWNER'.