3. O3PRM project structure

The O3PRM language is made of compilation units that are placed into modules. It is possible to encode in a single file an entire project but it is not recommended. A package matches a specific file in which we we can find at least one compilation unit. The following is a sample O3PRM project:

fr \
  | lip6 \
  |   | printers \
  |   |   | types.o3prm // types definition
  |   |   | powersupply.o3prm // class PowerSupply definition
  |   |   | equipment.o3prm // class Equipment definition
  |   |   | room.o3prm // class Room definition
  |   |   | computer.o3prm // class Computer definition
  |   |   | printer.o3prm // class Printer definition
  |   |   | example.o3prm // system Example definition
  |   |   | query.o3prm // request query definition

File extensions must be used as an indicator of the file’s compilation unit nature. The following extensions are allowed: .o3prmr for queries, o3prm for everything else (types, classes, interfaces and systems). It is good practice to name a file with the compilation unit it holds, for example in computer.o3prm should contain the definition for the Computer class. If the file contains several compilation units, you should name the file according to the unit with the highest order. For example, if you define types, classes and a system in one file, you should use the system’s name.

3.1. Compilation units

There exist four different compilation units in the O3PRM language. A compilation unit declares a specific element in the modeling process and can either be: an attribute’s type, a class, an interface, a system or even a query. Each compilation unit can start with a header. Headers are the locations where you declare import statements.

<o3prm> ::= [<header>] <compilation_unit> [(<compilation_unit>)]
<header> ::= <import>
<compilation_unit> ::= <type_unit>      |
                       <class_unit>     |
                       <interface_unit> |
                       <system_unit>

3.2. Header syntax

Each compilation unit is declared in a module defined by the path from the project’s root to the file’s name in which it is declared. Directory separators are represented using dots. For example the file fr/lip6/printers/types.o3prm defines the namespace fr.lip6.printers.types.

Namespaces can be used to import all of the compilation units defined in them, since many compilation units will need units defined in other files. In such cases, we say that a given compilation unit has dependencies which are declared using the import keyword. The syntax is:

<import>  ::= import <path> ";"
<path>    ::= <word> [ ( "." <word> ) ]

A <word> is an alphanumerical identifier. You can find its proper definition in the full BNF.

An example:

import fr.lip6.printers.computer;

The O3PRM interpreter should use an environment variable to know which directory to lookup for resolving compilations units. You should check your O3PRM interpreter to know which environment variable is used.

A compilation unit can be accessed through two different names:

  • Its simple name: the name given to it in the file where it is declared (for example Computer).
  • Its full name: defined by its namespace and its simple name (for example fr.lip6.printers.Computer).

In most cases, referring to a compilation unit using its simple name will work, you will need full names only to prevent name collisions/ambiguities. Name collisions happen when two compilation units have the same name but are declared in different namespaces. In such situations, the O3PRM interpreter cannot resolve the name and will raise an interpretation error.

Note that no matter how you refer to a compilation unit (either by its simple name or full name) you must always import it using the package complete name.

Finally, note that compilation units are case sensitive regardless of the operating system.