Initializing help system before first use

The Mosel Language

Topics covered in this chapter:

The Mosel language can be thought of as both a modeling language and a programming language. Like other modeling languages it offers the required facilities to declare and manipulate problems, decision variables, constraints and various data types and structures like sets and arrays. On the other hand, it also provides a complete set of functionalities proper to programming languages: it is compiled and optimized, all usual control flow constructs are supported (selection, loops) and can be extended by means of modules. Among these extensions, optimizers can be loaded just like any other type of modules and the functionality they offer may be used in the same way as any Mosel procedures or functions. These properties make of Mosel a powerful modeling, programming and solving language with which it is possible to write complex solution algorithms.

The syntax has been designed to be easy to learn and maintain. As a consequence, the set of reserved words and syntax constructs has deliberately been kept small avoiding shortcuts and `tricks' often provided by modeling languages. These facilities are sometimes useful to reduce the size of a model source (not its readability) but also are likely to introduce inconsistencies and ambiguities in the language itself, making it harder to understand and maintain.

Introduction

Comments

A comment is a part of the source file that is ignored by the compiler. It is usually used to explain what the program is supposed to do. Either single line comments or multi lines comments can be used in a source file. For the first case, the comment starts with the '!' character and terminates with the end of the line. A multi-line commentary must be inclosed in '(!' and '!)'. Note that it is possible to nest several multi-line commentaries.

! In a comment
 This text will be analyzed
 (! Start of a multi line
   (! Another comment
   blabla
   end of the second level comment !)
 end of the first level !) Analysis continues here 

Comments may appear anywhere in the source file.

Identifiers

Identifiers are used to name objects (variables, for instance). An identifier is an alphanumeric (plus '_') character string starting with an alphabetic character or '_'. All characters of an identifier are significant and the case is important (the identifier 'word' is not equivalent to 'Word').

Reserved words

The reserved words are identifiers with a particular meaning that determine a specific behaviour within the language. Because of their special role, these keywords cannot be used to name user defined objects (i.e. they cannot be redefined). The list of reserved words is:

and, array, as, boolean, break, case, constant, count, counter, declarations, div, do, dynamic, elif, else, end, evaluation, false, forall, forward, from, function, hashmap, if, imports, in, include, initialisations, initializations, integer, inter, is, is_binary, is_continuous,
is_free, is_integer, is_partint, is_semcont, is_semint, is_sos1, is_sos2, linctr, list, max, min, mod, model, mpvar, namespace, next, not, nsgroup, nssearch, of, options, or, package, parameters, procedure, public, prod, range, real, record, repeat, requirements, return, set, shared, string, sum, then, to, true, union, until, uses, version, while, with.

Note that, although the lexical analyzer of Mosel is case-sensitive, the reserved words are defined both as lower and upper case (i.e. AND and and are keywords but not And).

Separation of instructions, line breaking

In order to improve the readability of the source code, each statement may be split across several lines and indented using as many spaces or tabulations as required. However, as the line breaking is the expression terminator, if an expression is to be split, it must be cut after a symbol that implies a continuation like an operator ('+', '-', ...) or a comma (',') in order to warn the analyzer that the expression continues in the following line(s).

 A+B       ! Expression 1
 -C+D      ! Expression 2
 A+B-      ! Expression 3...
 C+D       ! ...end of expression 3 

Moreover, the character ';' can be used as an expression terminator.

 A+B ; -C+D  ! 2 expressions on the same line

Some users prefer to explicitly mark the end of each expression with a particular symbol. This is possible using the option explterm (see Section The compiler directives) which disables the default behaviour of the compiler. In that case, the line breaking is not considered any more as an expression separator and each statement finishing with an expression must be terminated by the symbol ';'.

 A+B;      ! Expression 1
 -C+D;     ! Expression 2
 A+B       ! Expression 3...
 -C+D;     ! ...end of expression 3 

Conventions in this document

In the following sections, the language syntax is explained. In all code templates, the following conventions are employed:

  • word: 'word' is a keyword and should be typed as is;
  • todo: 'todo' is to be replaced by something else that is explained later;
  • [ something ]: 'something' is optional and the entire block of instructions may be omitted;
  • [ something ...]: 'something' is optional but if used, it can be repeated several times.

Structure of the source file

The Mosel compiler may compile both models and packages source files. Once compiled, a model is ready for execution but a package is intended to be used by a model or another package (see Section The compiler directives).

The general structure of a model source file is as follows:

model model_name
[ Directives ]
[ Parameters ]
[ Body ]
end-model

The model statement marks the beginning the program and the statement end-model its end. Any text following this instruction is ignored (this can be used for adding plain text comments after the end of the program). The model name may be any quoted string or identifier, this name will be used as the model name in the Mosel model manager. An optional set of directives and a parameters block may follow. The actual program/model is described in the body of the source file which consists of a succession of declaration blocks, subroutine definitions and statements.

The structure of a package (see Section Packages) source file is similar to the one of a model:

package package_name
[ Directives ]
[ Parameters ]
[ Body ]
end-package

The package statement marks the beginning the library and the statement end-package its end. The package name must be a valid identifier.

It is important to understand that the language is procedural and not declarative: the declarations and statements are compiled and executed in the order of their appearance. As a consequence, it is not possible to refer to an identifier that is declared later in the source file or consider that a statement located later in the source file has already been executed. Moreover, the language is compiled and not interpreted: the entire source file is first translated — as a whole — into a binary form (the BIM file), then this binary form of the program is read again to be executed. During the compilation, except for some simple constant expressions, no action is actually performed. This is why only some errors can be detected during the compilation time, any others being detected when running the program.

The compiler directives

The compiler accepts four different types of directives: the uses statement, the imports statement, the options statement and the version statement. Namespace declarations are also expressed by means of directives, see Section Namespaces for further explanations.

Directive uses

The general form of a uses statement is:

uses libname1 [, libname2 ...][;]

This clause asks the compiler to load the listed modules or packages and import the symbols they define. Both modules and packages must still be available for running the model. If the source file being processed is a package, the bim files associated to the listed packages must be available for compiling another file using this package. It is also possible to merge bim files of several packages by using imports instead of uses when building packages.

By default the compiler tries first to find a package (the corresponding file is libname.bim) then, if this fails, it searches for a module (which file name is libname.dso). It is possible to indicate the type of library to look for by appending either ".bim" or ".dso" to the name (then the compiler does not try the alternative in case of failure). A package may also be specified by an extended file name (see Section File names and input/output drivers) including the IO driver in order to disable the automatic search (i.e. "a.bim" searches the file a.bim in the library path but ":a.bim" takes the file a.bim from the current directory).

For example,

uses 'mmsystem','mmxprs.dso','mypkg.bim'
uses ':/tmp/otherpkg.bim'

Both packages and modules are searched in a list of possible locations. Upon startup, Mosel uses as the default for this list the value of the environment variable MOSEL_DSO completed by a path deduced from the location (rtdir) of the Mosel runtime library (in the following # can be "32" on a 32bit system, "64" on a 64bit system or an empty string):

"rtdir\..\dso#"
Under Windows if rtdir terminates by "\bin#" and "rtdir\..\dso#" exists or
"rtdir/../dso#"
On Posix systems if rtdir terminates by "/lib#" and "rtdir/../dso#" exists or
"rtdir/dso#"
if this directory exists or
"rtdir"
if none of the above rules apply

The variable MOSEL_DSO is expected to be a list of paths conforming to the operating system conventions: for a Posix system the path separator is ':' (e.g. "/opt/Mosel/dso:/tmp") and it is ';' under Win32 (e.g. "E:\Mosel\Dso;C:\Temp"). The search path for modules and packages may also be set from the mosel command (using the -dp option, see Section Running Mosel) as well as inspected and modified from the Mosel Libraries (see functions XPRMgetdsopath and XPRMsetdsopath in the Mosel Libraries Reference Manual). Note however that Mosel will ignore modules not located in read-only locations when the restriction NoExec is active (see Section mosel command: restricted mode).

For locating packages Mosel will use the list of prefixes defined by the compiler option -bx (Section Running Mosel) or the environment variable MOSEL_BIM before proceeding to the search as decribed above. This parameter consists in a list of strings separated by the sequence || that are used as prefixes to the package name. For instance if the option -bx "bimdir/||tmp:" is used with the directive uses 'mypkg', the compiler will try to load the package "bimdir/mypkg.bim", then "tmp:mypkg.bim" before looking for "mypkg.bim" and "mypkg.dso" in the usual locations.

Directive imports

The general form of an imports statement is:

imports pkgname1 [, pkgname2 ...][;]

This clause is a special version of the uses directive that can only be used for packages: it asks the compiler to load the listed packages, import the symbols they define and incorporate the corresponding bim file. As a consequence, the generated file provides the functionality of the packages it imports. When used on a model file it removes the dynamic dependency on the listed packages (i.e. these packages are no longer required to run the model).

For example,

imports 'mypkg' 

Directive options

The compiler options may be used to modify the default behaviour of the compiler. The general form of an options statement is:

options optname1 [, optname2 ...]

The supported options are:

  • explterm: asks the compiler to expect explicit expression termination (see Section Separation of instructions, line breaking).
  • noimplicit: disables the implicit declarations (see Section About implicit declarations). This option can also be activated by using the '-ni' compiler flag (see Section Running Mosel)
  • noautofinal: by default initialization from blocks finalize sets they populate (section About automatic finalization). This option disables this behaviour that may be activated afterwards using the autofinal control parameter (cf. setparam).
  • keepassert: assertions (cf. assert) are compiled only in debug mode. With this option assertions are preserved regardless of the compilation mode.
  • xbim: store additional symbol information in the generated bim file (in particular array index names). This option can also be enabled by using the '-I' compiler flag (see Section Running Mosel).
  • fctasproc: by default return values of functions must be used such that a function call is not a valid statement. With this option functions can be used as procedures: when a statement consists in a function call its return value is silently ignored (see also asproc).
  • tagpriv: when the model is compiled with debug information private symbols are preserved. When this option is used these symbols are prefixed with '∼' such that they can be easily identified.
  • dynonly: this option can only be applied to a package: it marks the package as dynamic only such that it cannot be imported (see Section Directive imports).

For example,

options noimplicit,explterm 

Directive version

In addition to the model/package name, a file version number may be specified using this directive: a version number consists in 1, 2 or 3 integers between 0 and 999 separated by the character '.'.

version major [. minor [. release ]]

For example,

version 1.2 

The file version is stored in the BIM file and can be displayed from the Mosel console (command list) or retrieved using the Mosel Libraries (see function XPRMgetmodprop in the Mosel Libraries Reference Manual). From the model itself, the version number is recorded as a string in the control parameter model_version (see function getparam).

See also Section Advanced package version management.

The parameters block

A model parameter is a symbol, the value of which can be set just before running the model (optional parameter of the 'run' command of the command line interpreter). The general form of the parameters block is:

parameters
  ident1 = Expression1
[ ident2 = Expression2 ...]
end-parameters

where each identifier identi is the name of a parameter and the corresponding expression Expressioni its default value. This value is assigned to the parameter if no explicit value is provided at the start of the execution of the program (e.g. as a parameter of the 'run' command). Note that the type (integer, real, text string or Boolean) of a parameter is implied by its default value. Model parameters are manipulated as constants in the rest of the source file (it is not possible to alter their original value).

parameters
  size=12        ! Integer parameter
  R=12.67        ! Real parameter
  F="myfile"     ! Text string parameter
  B=true         ! Boolean parameter
end-parameters 

In addition to model parameters, Mosel and some modules and packages provide control parameters : they can be used to give information on the system (e.g. success of an I/O operation) or control its behaviour (e.g. select output format of real numbers). These parameters can be accessed and modified using the routines getparam and setparam. Refer to the documentation of these functions for a complete listing of available Mosel parameters. The documentation of the modules include the description of the parameters they publish.

Source file preprocessing

Source file character encoding

The Mosel compiler expects source files to be encoded in UTF-8 and will handle properly UTF-16 and UTF-32 encodings when the file begins with a BOM (Byte Order Mark). It is also possible to select an alternative encoding using the encoding annotation (see section Annotations).

For instance to notify the compiler that the the source file is encoded using ISO-8859-1, the following comment has to be copied at the beginning of the fie:

!@encoding:iso-8859-1

Source file inclusion

A Mosel program may be split into several source files by means of file inclusion. The 'include' instruction performs this task:

include filename

where filename is the name of the file to be included. This file name may contain environment variable references using the notation ${varname} (e.g. '${MOSEL}/examples/mymodel') that are expanded to generate the actual name. The 'include' instruction is replaced at compile time by the contents of the file filename.

Assuming the file a.mos contains:

model "Example for file inclusion"
  writeln('From the main file')
  include "b.mos"
end-model 

And the file b.mos:

  writeln('From an included file')

Due to the inclusion of b.mos, the file a.mos is equivalent to:

model "Example for file inclusion"
  writeln('From the main file')
  writeln('From an included file')
end-model 

If the compiler option -ix is used (Section Running Mosel) all file names used in the 'include' instruction will be prefixed as requested. For instance, if the option -ix "incdir/" is used with the compiler, the statement include "myfile.mos" will be replaced by the content of "incdir/myfile.mos".

Note that file inclusion cannot be used inside of blocks of instructions or before the body of the program (as a consequence, a file included cannot contain any of the following statements: uses, options or parameters).

Line control directives

In some cases it may be useful to process a Mosel source through an external preprocessor before compilation. For instance this may enable the use of facilities not supported by the Mosel compiler like macros, unrestricted file inclusion or conditional compilation. In order to generate meaningful error messages, the Mosel compiler supports line control directives: these directives are inserted by preprocessors (e.g. cpp or m4) to indicate the original location (file name and line number) of generated text.

#[line] linenum [filename]

To be properly interpreted, a line control directive must be the only statement of the line. Malformed directives and text following valid directives are silently ignored.

The declaration block

The role of the declaration block is to give a name, a type, and a structure to the entities that the processing part of the program/model will use. The type of a value defines its domain (for instance integer or real) and its structure, how it is organized, stored (for instance a reference to a single value or an ordered collection in the form of an array). The declaration block is composed of a list of declaration statements enclosed between the instructions declarations and end-declarations.

declarations
  Declare_stat
  [ Declare_stat ...]
end-declarations

Several declaration blocks may appear in a single source file but a symbol introduced in a given block cannot be used before that block. Once a name has been assigned to an entity, it cannot be reused for anything else.

Elementary types

Elementary objects are used to build up more complex data structures like sets or arrays. It is, of course, possible to declare an entity as a reference to a value of one of these elementary types. Such a declaration looks as follows:

ident1 [, ident2 ...]: [shared] type_name

where type_name is the type of the objects to create. Each of the identifiers identi is then declared as a reference to a value of the given type. The type name may be either a basic type (integer, real, string, boolean), an MP type (mpvar, linctr), an external type or a user defined type (see section User defined types). MP types are related to Mathematical Programming and allow declaration of decision variables and linear constraints. Note that the linear constraint objects can also be used to store linear expressions. External types are defined by modules (the documentation of each module describes how to use the type(s) it implements). The qualifier shared identifies variables that will be shared between several concurrent models (see Section Data sharing between models). Only entities of basic types and external types supporting sharing can be shared.

declarations
  i,j: integer
  str: string
  x,y,z: mpvar
end-declarations 

Basic types

The basic types are:

  • integer: an integer value between -2147483648 and 2147483647. Integers may also be expressed in hexadecimal by using the prefix 0x or 0X (e.g. 0x7b is the same as 123)
  • real: an approximation of a real value stored as a double precision floating-point number (values ranging between -1.7e+308 and 1.7e+308). Floating point numbers expressed in hexadecimal can also be used as real constants. The general form of such a constant is 0xh.hhhp[+/-]ddd where 'h' are hexadecimal digits and 'd' decimal digits (e.g. 0x1.9p+3 is the same as 12.5)
  • string: some text.
  • boolean: the result of a Boolean (logical) expression. The value of a Boolean entity is either the symbol true or the symbol false.

After its declaration, each entity receives an initial value of 0, an empty string, or false depending on its type.

MP types

Two special types are provided for mathematical programming.

  • mpvar: a decision variable
  • linctr: a linear constraint

Sets

Sets are used to group an unordered collection of elements of a given type. Set elements are unique: if an element is added several times it is only contained once in the set. Declaring a set consists of defining the type of elements to be collected.

The general form of a set declaration is:

ident1 [, ident2 ...] : [shared] [dynamic] set of [constant] type_name

where type_name is one of the elementary types. Each of the identifiers identi is then declared as a set of the given type. The qualifier shared identifies sets that will be shared between several concurrent models (see Section Data sharing between models). Only sets of basic types can be shared. If the qualifier dynamic is used the corresponding set(s) will never be finalized (see Section About automatic finalization and procedure finalize).

A set may collect references to constant elements of non-basic types: this kind of set will be created if the type name is preceded by the constant keyword. Only native types supporting the constant keyword and user defined types (see Section User defined types) referring to records including only basic types (see Section Records) or unions compatible only with basic types (see Section Unions) can be used in this context. As opposed to an ordinary set, a set of constant references behaves as if it was collecting values of the native type entities instead of their references. For instance adding 2 different variables of some native type to a normal set will always result in 2 elements added to the set. However a single element will be added to a set of constant references if these 2 variables have the same content (or the same textual representation). Moreover, since references are not directly collected, any change to a variable previously added to a set of constant references has no impact on the content of this set.

A particular set type is also available that should be preferred to the general form wherever possible because of its better efficiency: the range set is an ordered collection of consecutive integers in a given interval. The declaration of a range set is achieved by:

ident1 [, ident2 ...] : [shared] [dynamic] range [set of integer]

Each of the identifiers identi is then declared as a range set of integers. Every newly created set is empty.

declarations
  s1: set of string
  r1: range
  ds: set of constant date   ! type 'date' from module 'mmsystem'
end-declarations 

Lists

Lists are used to group a collection of elements of a given type. An element can be stored several times in a list and order of the elements is specified by construction. Declaring a list consists of defining the type of elements to be collected.

The general form of a list declaration is:

ident1 [, ident2 ...] : [shared] list of type_name

where type_name is one of the elementary types. Each of the identifiers identi is then declared as a list of the given type. The qualifier shared identifies lists that will be shared between several concurrent models (see Section Data sharing between models). Only lists of basic types can be shared.

Every newly created list is empty.

declarations
  l1: list of string
  l2: list of real
end-declarations 

A list element can be accessed using its order number. The first element has number 1 and index values inferior to 1 point to elements starting from the end of the list. For instance if l is a list, l(2) is the second element of this list, l(0) is the last element of the list and l(-1) its predecessor.

Arrays

An array is a collection of labelled objects of a given type. A label is defined by a list of indices taking their values in domains characterized by sets: the indexing sets. An array may be either dense or sparse: every possible index tuple is associated to a cell in a dense array while sparse arrays are created empty. The cells are then created explicitly (cf. procedure create) or when they are assigned a value (cf. Section Assignment) and the array may then grow `on demand'. It is also possible to delete some or all cells of a dynamic array using the procedure delcell. A cell that has not been created can be identified using the exists function and its value is the default initial value of the type of the array. The general form of an array declaration is:

ident1 [, ident2 ...] : [shared] [dynamic|hashmap] array(list_of_sets) of type_name

where list_of_sets is a list of set declarations/expressions separated by commas and type_name is one of the elementary types. Each of the identifiers identi is then declared as an array of the given type and indexed by the given sets. In the list of indexing sets, a set declaration can be anonymous (i.e. rs:set of real can be replaced by set of real if no reference to rs is required) or shortened to the type of the set (i.e. set of real can be replaced by real in that context). An anonymous indexing set is attached to the array: it is created at the same time as the array and, if its reference has not been saved separately, it will be cleared when the array is reset (see reset) and deleted when the array is released. Note also that different arrays sharing the same declaration including anonymous sets will not use the same indexing sets. For instance, in the following declaration the arrays a1 and a2 do not have the same first indexing set:

declarations
  a1,a2:array ( range, S:set of string ) of real
end-declarations 

The qualifier shared identifies arrays that will be shared between several concurrent models (see Section Data sharing between models). Only arrays of basic types and indiced by shared or constant sets of basic types can be shared.

declarations
  e: set of string
  t1:array ( e, rs:set of real, range, integer ) of real
  t2:array ( {"i1","i2"}, 1..3 ) of integer
end-declarations 

By default an array is dense (or static). For best performane it is better to index static arrays with constant sets or initialize and finalize indexing sets as soon as possible (cf. procedure finalize). An array is sparse (or dynamic) and created empty if either the qualifier dynamic or hashmap is used. Arrays declared with either of these qualifiers behave the same but their internal representation differ: the dynamic representation requires less memory and is faster for linear enumeration while the hashmap representation is faster for random access.

Note that once a set is employed as an indexing set, Mosel makes sure that its size is never reduced in order to guarantee that no entry of any array becomes inaccessible. Such a set is called fixed.

Special case of dynamic arrays of a type not supporting assignment

Certain types do not have assignment operators: for instance, writing x:=1 is a syntax error if x is of type mpvar. If an array of such a type is defined as dynamic the corresponding cells are not created. In that case, it is required to create each of the relevant entries of the array by using the procedure create since entries cannot be defined by assignment.

Records

A record is a finite collection of objects of any type. Each component of a record is called a field and is characterized by its name (an identifier) and its type. The general form of a record declaration is:

ident1 [, ident2 ...] : record
  field1 [, field2 ...]: type_name
  [...]
end-record

where fieldi are the identifiers of the fields of the record and type_name one of the elementary types. Each of the identifiers identi is then declared as a record including the listed fields.

Example:

declarations
  r1: record
       i,j:integer
       r:real
      end-record
end-declarations 

Each record declaration is considered unique by the compiler. In the following example, although r1 and r2 have the same definitions, they are not of the same type (but r3 is of course of the type of r2):

declarations
  r1: record
       i,j:integer
      end-record
  r2,r3: record
       i,j:integer
      end-record
end-declarations

Subroutine references

Subroutines are typically used to execute predefined operations, for instance, the function cos computes a cosine value while the procedure write displays some text on the console. A subroutine reference is an entity that is able to store a reference to a procedure or a function. The general form of a subroutine reference declaration is:

ident1 [, ident2 ...] : procedure [(List_of_params)]
or
ident1 [, ident2 ...] : function [(List_of_params)]: Type

Where List_of_params and Type are the properties of a compatible subroutine (see section Procedures and functions). After its creation a subroutine reference does not refer to any actual routine and is considered as undefined (use isdefined to identify this state); it can only be used (see section Expressions and Procedure call) once it has been associated to a proper routine via an assignment (see section Assignment of subroutine references). This association may be cancelled using reset.

The following example declares myfunc as a reference to a function returning a real and expecting a real as parameter; this symbol could be used to hold any function similar to cos or arctan for instance:

declarations
  myfunct: function (real):real
end-declarations 

Unions

A union is a container capable of holding an object of one of a predefined set of types. Defining an entity of this kind consists in specifying this set of compatible types or using the predefined union type any than can handle any type without restriction. The general form of a union declaration is:

ident1 [, ident2 ...] : type1 or type2 [or type3...]
or
ident1 [, ident2 ...] : any

where typei is one of the compatible types for each of the identfiers identi. Although this enumeration is not ordered the first type (type1) has a special meaning as it defines the representation to use when the union is initialized from some textual form (see Section Initialization block). In the case of the predefined union type any the textual representation is handled by a string. If one of the compatible types is also a union the resulting set of compatible types will include all types of this union.

Example:

declarations
  u: string or integer or boolean
  ua: text or any
 end-declarations

In the example above the identifier 'u' can contain either a string or an integer or a boolean (but not several values of these types at the same time). The identifier 'ua' can receive a value of any type as if it was declared as an 'any' except that the type 'text' will be used for textual initialisation (instead of 'string' as implied by 'any').

In its initial state a union is undefined: it gets a value (and a type) either via assignment (see Section Assignment of unions) or with the help of the create function. The state of a union can be checked with isdefined and the function reset may be used to clear a union and restore its initial state.

Constants

A constant is an identifier for which the value is known at declaration time and that will never be modified. The general form of a constant declaration is:

identifier = Expression

where identifier is the name of the constant and Expression its initial and only value. The expression must be of one of the basic types, a set or a list of one of these types, a native type supporting constant definition, or a record type containing only basic types.

Example:

declarations
  STR='my const string'
  I1=12
  R=1..10          ! constant range
  S={2.3,5.6,7.01} ! constant set
  L=[2,4,6]        ! constant list
end-declarations 

The compiler supports two kinds of constants: a compile time constant is a constant which value can be computed by the compiler. A run time constant will be known only when the model is run.

Example:

parameters
  P=0
end-parameters
declarations
  I=1/3            ! compile time constant
  J=P*2            ! run time constant
end-declarations 

A set or list can be turned into a constant using the procedure finalize.

User defined types

Naming new types

A new type may be defined by associating an identifier to a type declaration. The general form of a type definition is:

identifier = Type_def

where Type_def is a type (elementary, set, list, array or record) to be associated to the symbol identifier. After such a definition, the new type may be used wherever a type name is required.

Example:

declarations
  entier=integer
  setint=set of entier
  arr=array(range) of integer
  myany=string or integer
  i:entier           ! <=> i:integer
  s:setint           ! <=> s:set of integer
  a:arr              ! <=> a:array(range) of integer
end-declarations 

Combining types

Thanks to user defined types one can create complex data structures by combining structures offered by the language. For instance an array of sets may be defined as follows:

declarations
  typset=set of integer
  a1:array(1..10) of typset
end-declarations 

In order to simplify the description of complex data structures, the Mosel compiler can generate automatically the intermediate user types. Using this property, the example above can be written as follows (both arrays a1 and a2 are of the same type):

declarations
  a2:array(1..10) of set of integer
end-declarations 

Expressions

Expressions are, together with the keywords, the major building blocks of a language. This section summarizes the different basic operators and connectors used to build expressions.

Expressions are constructed using constants, operators and identifiers (of objects or functions). If an identifier appears in an expression its value is the value referenced by this identifier. In the case of a set, a list, an array or a record, it is the whole structure. To access a single cell of an array, it is required to 'dereference' this array. The dereferencing of an array is denoted as follows:

array_ident (Exp1 [, Exp2 ...])

where array_ident is the name of the array and Expi an expression of the type of the ith indexing set of the array. The type of such an expression is the type of the array and its value the value stored in the array with the label 'Exp1 [, Exp2 ...]'. In order to access the cell of an array of arrays, additional indexing sequences may be appended or the list of indices for the second array can be added to the list of indices of the first array. For instance, the array a:array(1..10) of array(1..10) of integer can be dereferenced with a(1)(2) or a(1,2).

Indexing sets of an array may be accessed using the following syntax:

array_ident.index(i) [.type_name]
or
array_ident.index(i).range

Where array_ident is a reference to an array and i an integer indicating which index to consider (e.g. 1 for the first index set, see getnbdim). The value of this expression is a set which type will be defined only when the index set number is a compile time constant. The type type_name (or the keyword range) might be added to the expression in order to enforce a type when it cannot be detected at compile time (a runtime error will be raised if this type does not correspond to the set or if the index number is not valid).

Similarly, to access the field of a record, it is required to 'dereference' this record. The dereferencing of a record is denoted as follows:

record_ident.field_ident

where record_ident is the name of the record and field_ident the name of the required field.

Dereferencing arrays of records is achieved by combining the syntax for the two structures. For instance a(1).b

Accessing the value of a union is about the same as dereferencing a record except that field names are replaced by type names:

union_ident.type_name

where union_ident is the name of the union and type_name the name of one of its compatible types (for instance u.integer). Trying to access a type that does not correspond to the actual value stored in the union will cause the execution of the program to terminate on an error: the properties of the union to inspect must be checked prior to its dereferencing (see functions gettypeid, getstruct and geteltype or Section Boolean expressions regarding the is operator). However, dereferencing a union for an assignment (see Section Assignment) will impose the type of the union: if the union is already of the designed type, its value will be updated, otherwise the current entity held in the union will be released and a new instance of the requested type will be created.

Unions may also be accessed via the expected structure they hold:

union_ident.struct_name[.type_name]

Where struct_name is either array, set, range or list. With only a structure this expression is untyped and can be only used in operations where knowing the structure is sufficient (e.g. with the function getsize or the construct index on an array as described above). Except for range it is necessary to append a type name to get a fully defined expression (for instance u.set.integer).

Arrays and lists held in a union can be directly dereferenced as follows:

union_ident.array(Exp1 [, Exp2 ...]).type_name
or
union_ident.list(Exp1).type_name

As for the preceding options for derefrencing unions, the structure and type of the entity stored in the union must match the expression otherwise a runtime error is raised.

Each type is associated with an identification number. This ID, a positive integer, can be retrieved using the following notation:

type_name.id

where type_name is the name of a type. The elementary types integer, real, string, boolean, mpvar and linctr have a constant ID (respectively 1,2,3,4,5 and 6) that never changes but all other types (like native types published by modules or user defined types) are assigned their ID at the beginning of the execution of the model such that it may change between executions (but this value is constant during a given execution).

A function call is denoted as follows:

function_ident
or
function_ident (Exp1 [, Exp2 ...])

where function_ident is either the name of a function or a function reference and Expi the ith parameter required by this function (note that function parameters are evaluated from right to left). The first form is for a function requiring no parameter.

The special function if is an operator that allows one to make a selection among expressions. Its syntax is the following:

if (Bool_expr, Exp1, Exp2)

which evaluates to Exp1 if Bool_expr is true or Exp2 otherwise. The type of this expression is the type of Exp1 and Exp2 which must be of the same type.

Evaluation order: Parentheses may be used to modify the predefined evaluation order of the operators (see Table Priority and evaluation order of operators in Mosel, operators of the same priority are evaluated in the sense indicated in the table, operators with lower priority values take precedence over operators with a high priority value) or simply to group subexpressions. For example:

sum(i in 1..10) A(i)*10 + 20  ! Same as:  (sum(i in 1..10) (A(i)*10)) + 20
max(j in 1..5) C(j)*D(j) - 10 ! Same as:  (max(j in 1..5) (C(j)*D(j))) - 10
prod(j in 1..5) C(j)*10 + 15  ! Same as:  (prod(j in 1..5) C(j)) * 10 + 15
i<=10 and j>5 or j=5          ! Same as:  (j<=10 and j>5) or (j=5)

Table 2.1: Priority and evaluation order of operators in Mosel (smaller values indicate higher priority)
Priority Operators Sense of evaluation
1 ()  {}  []  .  if  count  function calls   type conversions
2 -unary  ^  is
3 *  /  div  mod  prod  inter
4 +  -binary  sum  union  max  min
5 <  <=  >  >=  =  <>  in  not in 
6 not
7 and
8 or
9 array

Operators that result in statements are discussed in other sections:

Table Meaning of operators for different expression types summarizes the meaning and applicability of operators that are discussed for the different expression types in the following sections.

Table 2.2: Meaning of operators for different expression types
Type Operators Description
All expressions ()  Changing the evaluation order
All except linear constraints if  Inline 'if'
Unions is  is not Test properties
Symbols ->  'reference to' operator
Arithmetic expressions count  Counter
^ Exponential operator
*  /  prod Multiplication and division
div  mod  Integer division and modulo
+  -  sum  Addition and substraction
max  min Maximum and minimum value
<  <=  >  >=  =  <>  Comparators
String expressions Concatenation
Difference
<  <=  >  >=  =  <>  Comparators (see Section Boolean expressions)
Set expressions {}  Constant set definition
*  inter Intersection
+  union  Union
- Difference
<=  Subset
>=  Superset
=  <> Equality and difference of contents
in  not in Set element
range  set  Constructors; clone operators
List expressions []  Constant list definition
+  sum  Concatenation
Difference
=  <> Equality and difference of contents
list Constructor; clone operator
Boolean expressions not Negation
and Logic 'and'
or Logic 'or'
Linear constraint expressions * Multiplication (one operand must be of numerical type)
+  -  sum  Addition / subtraction
<=  >=  =  Relational operators
Automatic arrays ::  Inline array initialization (see Section Inline initialization)
=  <>  Equality and difference of contents
array Constructor

Type conversions and constructors

The Mosel compiler operates automatic conversions to the type required by a given operator in the following cases:

  • in the dereference list of an array:
    integerreal;
  • in a function or procedure parameter list:
    integerreal, linctr;
    reallinctr;
    mpvarlinctr;
  • anywhere else:
    integerreal, string, linctr;
    realstring, linctr;
    mpvarlinctr;
    booleanstring.

It is possible to force a basic type conversion using the type name as a function (i.e. integer, real, string, boolean). In the case of string, the result is the textual representation of the converted expression. Note that the conversion of a real to a string depends on the control parameters realfmt, zerotol and txtztol (see setparam). In the case of boolean, for numerical values, the result is true if the value is nonzero and for strings the result is true if the string is the word `true'. When converting a real to an integer the result is the integral part of the number (no rounding is performed). Note that explicit conversions are not defined for MP types, and structured types (e.g. linctr(x) is a syntax error).
For generating numerical values from strings it is in general preferrable to use the subroutines parseint/parsereal that provide error handling functionality in place of the basic conversion available through integer/real.

! Assuming A=3.6, B=2
  integer(A+B)         ! = 5
  string(A-B)          ! = "1.6"
  real(integer(A+B))   ! = 5.6 (because the compiler simplifies
                                the expression) 

Some native and record types might be used as function names to create new instances of the corresponding type (see the documentation of each individual type for a list of possible constructors). If the only argument of such a function call is an entity of the same type the result is a copy of the argument (the type must support assignment). For record types a specific syntax makes it possible to create and initialize each field of the newly created entity in a single operation:

type_name (.field1:=val1 [, .field2:=val2 ...])

where type_name is the name of a record type and fieldi one of its fields to be initialised with the corresponding vali value.

A local instance of a given type can be obtained with the following syntax:

(type_name)

Where type_name is the name of a non basic type (i.e. any type except integer, real, string and boolean). The result of this expression is a newly created instance of the type local to the current expression.

The constructor array (see Automatic arrays) for creating new arrays ad-hoc is an aggregate operator, via list (see List expressions), set and range (see Set expressions) it is possible to perform transformations between set and list structures.

Union constructors and subroutine parameters

Similarly to native types a union type may be used as a function that takes a single parameter. As a result of such a construct a new instance of the specified union type is returned taking the same value and type as the given parameter. If this parameter is not of a basic type (like a native object) the newly created entity will contain a reference to this object. Moreover if the parameter is another union the resulting entity will receive a reference to the value of this union (instead of a reference to the union) if this value is also a reference. However it will receive a copy of this value if it is a basic type. Note that in this case the compiler may not be able to detect a type incompatibility and a runtime error may happen if the value of the entity given as parameter is not compatible with the target union.

This wrapping mechanism is also used when a subroutine expects a union but it receives a compatible type or a different union type: the compiler creates a local union of the required type initialized with the provided value or reference.

Aggregate operators

Table 2.3: Aggregate operators in Mosel with default values for empty expressions
Operator Description Default values
count  Counter 0
prod Product 1
inter Intersection of sets empty set
sum Sum (arithmetic); concatenation (list, text) arithmetic: 0, list: empty list
union Union of sets; concatenation of lists empty set or list
max Maximum value real: -MAX_REAL, integer: -MAX_INT-1
min Minimum value real: MAX_REAL, integer: MAX_INT
and Logic 'and' true
or Logic 'or' false
array Array creation dynamic empty array

An operator is said to be aggregate when it is associated to a list of indices for each of which a set or list of values is defined. This operator is then applied to its operands for each possible tuple of values (e.g. the summation operator sum is an aggregate operator). The general form of an aggregate operator is:

Aggregate_ident (Iterator1 [, Iterator2 ...]) Expression
or
count (Iterator1 [, Iterator2 ...])

where the Aggregate_ident is the name of the operator and Expression an expression compatible with this operator (see below for the different available operators). The type of the result of such an aggregate expression is the type of Expression. The count operator does not require an additional expression: its value, an integer, corresponds to the number of times the expression of another aggregate operator used with the same iterator list would be evaluated (i.e. it is equivalent to sum(iteratorlist) 1).

An iterator is one of the following constructs:

SetList_expr
or
ident1 [, ident2 ...] in SetList_expr [| Bool_expr]
or
ident = Expression [| Bool_expr]
or
ident as counter

The first form gives the list of the values to be taken without specifying an index name. With the second form, the indices named identi take successively all values of the set or list defined by SetList_expr. With the third form, the index ident is assigned a single value (which must be a scalar). For the second and third cases, the scope of the created identifier is limited to the scope of the operator (i.e. it exists only for the following iterators and for the operand of the aggregate operator). Moreover, an optional condition can be stated by means of Bool_expr which can be used as a filter to select the relevant elements of the domain of the index. It is important to note that this condition is evaluated as early as possible. As a consequence, a Boolean expression that does not depend on any of the defined indices in the considered iterator list is evaluated only once, namely before the aggregate operator itself and not for each possible tuple of indices. The last form of an iterator declares a counter for the operator: the value of the corresponding symbol is incremented each time the operator's expression is evaluated. For this case, if ident has been declared before, it must be integer or real and its value is not reset. Otherwise, as for indices, the scope of the created integer identifier is limited to the scope of the operator and its initial value is 0. There can be only one counter for a given aggregate operator.

The Mosel compiler performs loop optimization when function exists is used as the first factors of the condition in order to enumerate only those tuples of indices that correspond to actual cells in the array instead of all possible tuples. To be effective, this optimization requires that sets used to declare the array on which the exist condition applies must be named and the same sets must be used to define the index domains. Moreover, the maximum speedup is obtained when order of indices is respected and all indices are defined in the same aggregate operator.

An index is considered to be a constant: it is not possible to change explicitly the value of a named index (using an assignment for instance).

Arithmetic expressions

Numerical constants can be written using the common scientific notation. Arithmetic expressions are naturally expressed by means of the usual operators (+, -, *, / division, unary -, unary +, ^ raise to the power). For integer values, the operators mod (remainder of division) and div (integral division) are also defined. Note that mpvar objects are handled like real values in expression.

The sum (summation) aggregate operators is defined on integers, real and mpvar. The aggregate operators prod (product), min (minimum) and max (maximum) can be used on integer and real values.

x*5.5+(2+z)^4+cos(12.4)
sum(i in 1..10) (min(j in s) t(i)*(a(j) mod 2)) 

String expressions

Constant strings of characters must be quoted with single (') or double quote (") and may extend over several lines. Strings enclosed in double quotes may contain C-like escape sequences introduced by the 'backslash' character (\a \b \f \n \r \t \v \xxx \uhhhh with xxx being the character code as an octal number and hhhh a Unicode code as a four hexadecimal digits number).

Each sequence is replaced by the corresponding control character (e.g. \n is the `new line' command) or, if no control character exists, by the second character of the sequence itself (e.g. \\ is replaced by '\').

The escape sequences are not interpreted if they are contained in strings that are enclosed in single quotes.

Example:

'c:\ddd1\ddd2\ddd3' is understood as c:\ddd1\ddd2\ddd3
"c:\ddd1\ddd2\ddd3" is understood as c:ddd1ddd2ddd3

There are two basic operators for strings: the concatenation, written '+' and the difference, written '-'.

"a1b2c3d5"+"e6"    ! = "a1b2c3d5e6"
'a1b2c3d5'-"3d5"   ! = "a1b2c" 

A constant string may also take 2 additional forms: initialised from the content of an external file or as a portion of the current input file. For the first case, a text string enclosed in backquotes will be replaced by the content of the file identified by this enclosed text (this file name is handled in the same way as for source file inclusion—see Section Source file inclusion—in particular the same rules apply regarding the file location and the expansion of environment variables). For the second case, a line ending by the backquote character optionally followed by some label (consisting in any sequence of characters not including backquote) will be interpreted as the beginning of a text block. The end of this text block is marked by a line starting with the previously used label (if any) followed by the backquote character.

Example:

`afile.txt` ! This string is the content of "afile.txt"
`MyMarker
line1
line2
MyMarker`  ! This string is equivalent to "line1\nline2\n"

Set expressions

Constant sets are described using one of the following constructs:

{[ Exp1 [, Exp2 ...]]}
or
Integer_exp1 .. Integer_exp2

The first form enumerates all the values contained in the set and the second form, restricted to sets of integers, gives an interval of integer values. This form implicitly defines a range set.

The basic operators on sets are the union written +, the difference written - and the intersection written *.

The aggregate operators union and inter can also be used to build up set expressions.

{1,2,3}+{4,5,6}-(5..8)*{6,10}             ! = {1,2,3,4,5}
{'a','b','c'}*{'b','c','d'}               ! = {'b','c'}
union(i in 1..4|i<>2) {i*3}               ! = {3,9,12}
inter(i in [2,3]) union(j in 1..10) {i*j} ! = {6,12,18} 

If several range sets are combined in the same expression, the result is either a range or a set of integers depending on the continuity of the produced domain. If range sets and sets of integers of more than one element are combined in an expression, the result is a set of integers. It is however possible to convert a set of integers to a range by using the notation range(setexpr) where setexpr is a set expression which result is either a set of integers or a range. Similarly stating set(lstexpr) will generate a set from the elements of the list expression lstexpr.

List expressions

A constant list consist in a list of expressions enclosed in square brackets:

[[ Exp1 [, Exp2 ...]]]

There are two basic operators for lists: the concatenation, written '+' and the difference, written '-'. The aggregate operator sum can also be used to build up list expressions (the operator union behaves like sum when applied to lists).

[1,2,3]+[1,2,3]        ! = [1,2,3,1,2,3]
[1,2,3,4]-[3,4]        ! = [1,2]
sum(i in 1..3) [i*3]   ! = [3,6,9] 

List expressions are untyped when they include values of different types. When possible the compiler will convert elements of an untyped list to the required type. For instance list of a mix of integers and reals (or a list of integers) will be converted to a list of reals to perform an assignment to a list of reals. Otherwise untyped lists are converted to lists of any (see Section Unions).

A list can also be constructed from the elements of a set using the syntax list(setexpr) where setexpr is a set expression.

Boolean expressions

A Boolean expression is an expression whose result is either true or false. The traditional comparators are defined on integer and real values: <, <=, =, <> (not equal), >=, >. Comparisons are performed within the tolerance specified by the control parameter "zerotol" (see setparam) when these operators are applied to reals. For instance, two real values a and b will be considered equal if abs(a-b)≤zerotol.

These comparison operators are also defined for string expressions. In that case, the order is defined by the ISO-8859-1 character set (i.e. roughly: punctuation < digits < capitals < lower case letters < accented letters).

With sets, the comparators <= (`is subset of'), >= (`is superset of'), = (`equality of contents') and <> (`difference of contents') are defined. These comparators must be used with two sets of the same type. Moreover, the operator `expr in Set_expr' is true if the expression expr is contained in the set Set_expr. The opposite, the operator not in is also defined.

With lists, the comparators = (`equality of contents') and <> (`difference of contents') are defined. These comparators must be used with two lists of the same type.

With arrays, the comparators = (`equality of contents') and <> (`difference of contents') are defined. These comparators must be used with two arrays of the same type and this type must support the requested operator (for instance arrays of mpvar cannot be compared).

With records, the comparators = (`equality of contents') and <> (`difference of contents') are defined. These comparators must be used with two records of the same type and all fields of this record type must support the requested operator (for instance records including mpvar entries cannot be compared).

With unions, the comparators = (`equality of contents') and <> (`difference of contents') are defined. These comparators can be used either between two unions (not necessarily from the same definition) or between a union and an entity of one of its compatible types. The result of the comparison is true if the two operands are of the same type, can be compared and have the same value. Two unions are also considered equal if they are both uninitialized.

The operator is (as well as its opposite is not) is also defined on unions: its left operand must be a union and its right operand can be either a type name (like integer or string) or a structure name (array, set, range, list, record, procedure or function) optionally followed by of and a type specification (for instance u is list of real). The value of the test is true if the type ID of the union (see gettypeid) corresponds to the specified type or if the union is of the specified structure (see getstruct).

To combine Boolean expressions, the operators and (logical and) and or (logical or) as well as the unary operator not (logical negation) can be used. The evaluation of an arithmetic expression stops as soon as its value is known.

The aggregate operators and and or are the natural extension of their binary counterparts.

3<=v1 and v2>=45 or t<>r and not r in {1..10}
and(i in 1..10) 3<=arr(i) 

Linear constraint expressions

Linear constraints are built up using linear expressions on the decision variables (type mpvar).

The different forms of constraints are:

Linear_expr
or
Linear_expr1 Ctr_cmp Linear_expr2
or
Linear_expr SOS_type
or
mpvar_ref mpvar_type1
or
mpvar_ref mpvar_type2 Arith_expr

In the case of the first form, the constraint is unconstrained and is just a linear expression. For the second form, the valid comparators are <=, >=, = (range constraints may be created using the procedure setrange). The third form is used to declare special ordered sets. The types are then is_sos1 and is_sos2. The coefficients of the variables in the linear expression are used as weights for the SOS (as a consequence, a 0-weighted variable cannot be represented this way, procedure makesos1 or makesos2 has to be used instead).

The last two types are used to set up special types for decision variables. The first series does not require any extra information: is_continuous (default), is_integer, is_binary, is_free. Continuous and integer variables have the default lower bound 0, binary variables only take the values 0 or 1, and 'free' means that the variable is unbounded (i.e. ranging from -∞ to +∞). The second series of types is associated with a threshold value stated by an arithmetic expressions: is_partint for partial integer, the value indicates the limit up to which the variable must be integer, above which it is continuous. For is_semcont (semi-continuous) and is_semint (semi-continuous integer) the value gives the semi-continuous limit of the variable (that is, the lower bound on the part of its domain that is continuous or consecutive integers respectively). Note that these constraints on single variables are also considered as common linear constraints.

3*y+sum(i in 1..10) x(i)*i >= z-t
x is_free                        ! Define an unbounded variable
x <= -2                          ! Upper bound on x
t is_integer                     ! Define an integer variable t=0,1,2,...
t >= -7                          ! Change lower bound on t: t=-7,-6,-5,...
sum(i in 1..10) i*x(i) is_sos1   ! SOS1 {x(1),x(2),...} with
                                 ! weights 1,2,...
y is_semint 5                    ! y=0 or y=5,6,...
y <= 20                          ! Upper bound on y: y=0 or y=5,6,...,20 

Internally all linear constraints are stored in the same form: a linear expression (including a constant term) and a constraint type (the right hand side is always 0). This means, the constraint expression 3*x>=5*y-10 is internally represented by: 3*x-5*y+10 and the type `greater than or equal to'. When a reference to a linear constraint appears in an expression, its value is the linear expression it contains. For example, if the identifier ctl refers to the linear constraint 3*x>=5*y-10, the expression z-x+ctl is equal to: z-2*x-5*y+10.

Note that the value of a unary constraint of the type x is_type threshold is x-threshold.

Automatic arrays

The array keyword can be used as an aggregate operator in order to create an array that will exist only for the duration of the expression.

array (Iterator1 [, Iterator2 ...]) Expression

here, the iterators define the indices of the array and the expression, the associated values.

This automatic array may be used wherever a reference to an array is expected: for instance to save the solution values of an array of decision variables in an initialization block (see Section Initialization block).

initializations to "mydata.txt"
  evaluation of array(i in 1..10) x(i).sol as "mylabel"
end-initializations

Operator -> (reference to)

The reference to operator creates a reference to its operand.

->NameRef
or
->(Expr)

Where NameRef is the name of a subroutine, the name of a global entity of any type, or any dereferencing of a non-basic type, and Expr an expression of a non-basic type.

When the reference to operator is applied to a subroutine name or to a subroutine reference, it returns a subroutine reference instead of trying to call the routine (see section Subroutine references). This subroutine reference can be used wherever the corresponding type is expected, in particular for an assignment that initialises another subroutine reference (see section Assignment of subroutine references). Note that the compiler does not guarantee which routine is returned if the name of a subroutine refers to several implementations (i.e. the routine has been overloaded, see section Overloading) and the context does not make it possible to select a particular version.

In all other cases this operator is essentially used to create aliases (see section Assignment of unions).

Statements

Four types of statements are supported by the Mosel language. The simple statements can be seen as elementary operations. The initialization block is used to load data from a file or save data to a file. Selection statements allow one to choose between different sets of statements depending on conditions. Finally, the loop statements are used to repeat operations.

Each of these constructs is considered as a single statement. A list of statements is a succession of statements. No particular statement separator is required between statements except if a statement terminates by an expression. In that case, the expression must be finished by either a line break or the symbol ';'.

Simple statements

Assignment

An assignment consists in changing the value associated to an identifier. The general form of an assignment is:

ident_ref := Expression
or
ident_ref += Expression
or
ident_ref -= Expression

where ident_ref is a reference to a value (i.e. an identifier or an array/record dereference) and Expression is an expression of a compatible type with ident_ref. The direct assignment, denoted := replaces the value associated with ident_ref by the value of the expression. The additive assignment, denoted +=, and the subtractive assignment, denoted -=, are basically combinations of a direct assignment with an addition or a subtraction. They require an expression of a type that supports these operators (for instance it is not possible to use additive assignment with Boolean objects).

The additive and subtractive assignments have a special meaning with linear constraints in the sense that they preserve the constraint type of the assigned identifier: normally a constraint used in an expression has the value of the linear expression it contains, the constraint type is ignored.

c:= 3*x+y >= 5
c+= y              ! Implies c is 3*x+2*y-5 >= 0
c:= 3*x+y >= 5
c:= c + y          ! Implies c is 3*x+2*y-5 (c becomes unconstrained) 

Assignment of structured types

The direct assignment := can also be used with sets, lists, arrays and records under certain conditions. For sets and lists, reference and value must be of the same type, the system performing no conversion on structures. For instance it is not possible to assign a set of integers to a set of reals although assigning an integer value to a real object is valid.

When assigning records, reference and value must be of the same type and this type must be assignment compatible: two records having identical definitions are not considered to be the same type by the compiler. In most cases it will be necessary to employ a user type to declare the objects. A record is assignment compatible if all the fields it includes can be assigned a value. For instance a record including a decision variable (type mpvar) cannot be used in an assignment: copying a value of such a type has to be performed one field at a time skiping those fields that cannot be assigned.

Two arrays can be used in an assignment if they have strictly the same definition and are assignment compatible (i.e. their type supports assignment).

Assignment of subroutine references

Only the direct assignment := can be used with subroutine references, the value assigned to the entity must have a compatible type and will usually be the result of the -> operator to prevent calling of the operand. As the result of such a statement both subroutine references refer to the same routine and, if the source reference is undefined the destination of the assignment will also become undefined.

declarations
  myfunc,fct2,fct0:function (real):real
end-declarations
myfunc:=->cos                   ! 'myfunc' refers to 'cos'
fct2:=->myfunc                  ! as well as 'fct2'
writeln(myfunc(0),"=",fct2(0))  ! => 1=1
fct2:=->fct0                    ! fct2 becomes undefined
writeln(isdefined(->fct2))      ! => false

Assignment of unions

Only the direct assignment := can be used with union entities: the value associated with the union is replaced by a copy of the value of the expression that must have a type compatible with the union (otherwise a compilation error is raised). If the current value of the union is of the same type as the expression then a direct assignment between the entity stored in the union and the provided expression is performed. Otherwise, the current value of the union is released and a new instance of the required type is created before a direct assignment is performed. Note that although no implicit type conversion is performed, assigning an integer to a union compatible only with reals will succeed (i.e. the integer value will be automatically converted to real).

If the expression is also a union then the assignment operation is executed on the value stored in this union, if its type is not compatible with the destination of the assignment a runtime error will be raised. If the expression is an undefined union then the destination entity will also become undefined (i.e. the current value of the union is released and the entity returns to its initial state).

If the expression is the result of the -> operator then the union will become an alias to the source expression instead of receiving a copy of its value.

declarations
  S:set of integer
  u:any
end-declarations
u:=->S                          ! 'u' and 'S' refer to the same set
S:={1,2,3}
writeln(u)                      ! => {1,2,3}
u.set.integer+={4}
writeln(u)                      ! => {1,2,3,4}
writeln(S)                      ! => {1,2,3,4}

About implicit declarations

Each symbol should be declared before being used. However, an implicit declaration is issued when a new symbol is assigned a value the type of which is unambiguous.

! Assuming A,S,SE are unknown symbols
A:= 1              ! A is automatically defined
                   !   as an integer reference
S:={1,2,3}         ! S is automatically defined
                   !   as a set of integers
SE:={}             ! This produces a parser error as
                   ! the type of SE is unknown 

In the case of arrays, the implicit declaration should be avoided or used with particular care as Mosel tries to deduce the indexing sets from the context and decides automatically whether the created array must be dynamic. The result is not necessarily what is expected.

A(1):=1            ! Implies: A:array(1..1) of integer
A(t):=2.5          ! Assuming "t in 1..10|f(t) > 0"
                   ! implies: A:dynamic array(range) of real 

The option noimplicit disables implicit declarations (see Section Directive options).

Inline initialization

Using inline initialization it is possible to assign several cells of an array in a single statement. The general form of an inline initialization is:

ident_ref ::[ Exp1 [, Exp2 ...] ]
or
ident_ref ::(Ind1 [, Ind2 ...] )[ Exp1 [, Exp2 ...] ]

where ident_ref is the object to initialize (array, set or list) and Expi are expressions of a compatible type with ident_ref. The first form of this statement may be used with lists, sets and arrays indiced by ranges: the list of expressions is used to initialize the object. In the case of lists and sets this operation is similar to a direct assignment, with an array, the first index of each dimension is the lower bound of the indexing range or 1 if the range is empty.

The second form is used to initialize regions of arrays or arrays indiced by general sets: each Indi expression indicates the index or list of indices for the corresponding dimension. An index list can be a constant, a list of constants (e.g. ['a','b','c']) or a constant range (e.g. 1..10) but all values must be known at compile time.

declarations
 T:array(1..10) of integer
 U:array(1..9,{'a','b','c'}) of integer
end-declarations
T::[2,4,6,8]           ! <=> T(1):=2; T(2):=4;...
T::(2..5)[7,8,9,19]    ! <=> T(2):=7; T(3):=8;...
U::([1,3,6],'b')[1,2,3]! <=> U(1,'b'):=1; U(3,'b'):=2;... 

Linear constraint expression

A linear constraint expression can be assigned to an identifier but can also be stated on its own. In that case, the constraint is said to be anonymous and is added to the set of already defined constraints. The difference from a named constraint is that it is not possible to refer to an anonymous constraint again, for instance to modify it.

10<=x; x<=20
x is_integer 

Procedure call

Not all required actions are coded in a given source file. The Mosel language comes with a set of predefined procedures that perform specific actions (like displaying a message). It is also possible to import procedures from external locations by using modules or packages (cf. Section The compiler directives).

The general form of a procedure call is:

procedure_ident
procedure_ident (Exp1 [, Exp2 ...])

where procedure_ident is either the name of a procedure or a procedure reference and, if required, Expi is the ith parameter for the call (note that parameters of procedures are evaluated from right to left). Refer to Chapter Predefined functions and procedures of this manual for a comprehensive listing of the predefined procedures.The modules documentation should also be consulted for explanations about the procedures provided by each module.

writeln("hello!")     ! Displays the message: hello!

Initialization block

The initialization block may be used to initialize objects (scalars, arrays, lists or sets) of basic type from files or to save the values of such objects to files. Scalars and arrays of external/user types supporting this feature may also be initialized using this facility.

The first form of an initialization block is used to initialize data from a file:

initializations from Filename
  item1 [ as Label1]
  or
  [itemT11, itemT12 [ ,IdentT13 ...]] as LabelT1
[
    item2 [ as Label2]
    or
    [itemT21, itemT22 [ ,IdentT23 ...]] as LabelT2
...]
end-initializations

where Filename, a string expression, is the name of the file to read, itemi any object identifier and itemTij an array identifier. Each identifier is automatically associated to a label: by default this label is the identifier itself but a different name may be specified explicitly using a string expression Labeli. If a given item is of a record type, the operation is permitted only if all fields it contains can be initialized. For instance, if one of the fields is a decision variable (type mpvar), the compilation will fail. Alternatively, the fields to be initialized can be listed using the following syntax as an item:

Identifier(field1 [ ,filedi ...])

If a given item is a namespace (see Section Namespaces) all the identifiers it includes at the time of parsing the statement are implicitly added to the block with the exception of namespaces and entities that are not compatible with initializations (like decision variables). The associated labels are the fully qualified names of the objects (i.e. the identifier prefixed by the namespace) unless a label is specified for this record: in this case it is used as a replacement for the default prefix when generating the per entity labels such that an empty string will remove entirely the prefix. Using the compiler option -wi makes it possible to get a list of included identifiers by means of a compiler warning (see Section Running Mosel).

When an initialization block is executed, the given file is opened and the requested labels are searched for in this file to initialize the corresponding objects. Several arrays may be initialized with a single record. In this case they must be all indexed by the same sets, have scalar types and the label is obligatory. After the execution of an initializations from block, the control parameter nbread reports the number of items actually read in. Moreover, if control parameter readcnt is set to true before the execution of the block, counting is also achieved at the label level: the number of items actually read in for each label may be obtained using function getreadcnt.

An initialization file must contain one or several records of the following form:

Label: value

where Label is a text string and value either a constant of a basic type (integer, real, string or boolean) or a collection of values separated by spaces and enclosed in square brackets. Collections of values are used to initialize lists, sets records or arrays — if such a record is requested for a scalar, then the first value of the collection is selected. When used for arrays, indices enclosed in round brackets may be inserted in the list of values to specify a location in the corresponding array.

Note also that:

  • no particular formatting is required: spaces, tabulations, and line breaks are just normal separators
  • the special value '*' implies a no-operation (i.e. the corresponding entity is not initialized)
  • the special value '?' (nil value) implies a reset of the corresponding entity (the reset procedure is applied to references, 0,false or and empty string is assigned to variables of basic types)
  • single line comments are supported (i.e. starting with '!' and terminated by the end of the line)
  • Boolean constants are either the identifiers false (FALSE) and true (TRUE) or the numerical constants 0 and 1
  • all text strings (including the labels) may be quoted using either single or double quotes. In the latter case, escape sequences are interpreted (i.e. use of '\').

By default Mosel expects that initialization files are encoded in UTF-8 and it can handle UTF-16 and UTF-32 when a BOM (Byte Order Mark) is used. To process files in another encoding, a special encoding comment line must be put at the beginning of the file (see section Source file character encoding). For instance a data file encoded with CP1252 should start with the following comment line:

!@encoding:CP1252

The second form of an initialization block is used to save data to a file:

initializations to Filename
  item1 [as Label1]
  or
  [itemT11, itemT12 [ ,IdentT13 ...]] as LabelT1
[
    item2 [ as Label2]
    or     [itemT21, itemT22 [ ,IdentT23 ...]] as LabelT2
...]
end-initializations

In this form, any itemi can be replaced by the value of an expression using the following construct (Labeli is mandatory in this case):

evaluation of expression

When this second form is executed, the value of all provided labels is updated with the current value of the corresponding identifier—A copy of the original file is saved prior to the update (i.e. the original version of fname can be found in fname ∼).— in the given file. If a label cannot be found, a new record is appended to the end of the file and the file is created if it does not yet exist.

For example, assuming the file a.dat contains:

! Example of the use of initialization blocks
t:[ (1 un) [10 11] (2 deux) [* 22] (3 trois) [30 33]]
t2:[ 10 (4) 30 40 ]
'nb used': ? 

consider the following program:

model "Example initblk"
declarations
 nb_used:integer
 s: set of string
 ta,tb: dynamic array(1..3,s) of real
 t2: array(1..5) of integer
end-declarations

initializations from 'a.dat'
 [ta,tb] as 't'   ! ta=[(1,'un',10),(3,'trois',30)]
                  ! tb=[(1,'un',11),(2,'deux',22),(3,'trois',33)]
 t2               ! t2=[10,0,0,30,40]
 nb_used as "nb used" ! nb_used=0
end-initializations

nb_used+=1
ta(2,"quatre"):=1000

initializations to 'a.dat'
 [ta,tb] as 't'
 nb_used as "nb used"
 s
end-initializations
end-model 

After the execution of this model, the data file contains:

! Example of the use of initialization blocks
t:[(1 'un') [10 11] (2 'deux') [* 22] (2 'quatre') [1000 *]
  (3 'trois') [30 33]]
t2:[ 10 (4) 30 40 ]
'nb used': 1
's': ['un' 'deux' 'trois' 'quatre'] 

In case of error (e.g. file not found, corrupted data format) during the processing of an initialization block, the execution of the model is interrupted. However if the value of control parameter ioctrl is true, executions continues. It is up to the user to verify whether data has been properly transfered by checking the value of control parameter iostatus.

Handling of unions

When initializing unions the procedure will consider only scalar values of basic types: integers, reals and booleans are assigned to the union; textual values are used to initialize the entity employing the first type from the list of compatible types specified for this union (for the union 'any' this is a string). An I/O error will be raised if this type does not support initialization.

When exporting unions, any non-scalar value and type that does not support conversion to string will result in a NIL value in the generated file.

About automatic finalization

During the execution of an initializations from block all sets are automatically finalized just after having been initialized (unless they have been explicitly declared as dynamic). This also applies to sets indirectly initialized through the non-dynamic arrays for which they are index sets. In addition, such an array is created as a static array if it has not been used before the initialization block.

This behaviour is controled by the autofinal control parameter which value may be changed using the setparam procedure (i.e. it is therefore possible to have automatic finalization active for only some initializations blocks). The compiler option noautofinal (see section Directive options) allows to disable this feature from the beginning of the model (although it can be re-enabled as required using the control parameter).

Selections

If statement

The general form of the if statement is:

if Bool_exp_1
then Statement_list_1
[
  elif Bool_exp_2
  then Statement_list_2
...]
[ else Statement_list_E ]
end-if

The selection is executed as follows: if Bool_exp_1 is true then Statement_list_1 is executed and the process continues after the end-if instruction. Otherwise, if there are elif statements, they are executed in the same manner as the if instruction itself. If, all boolean expressions evaluated are false and there is an else instruction, then Statement_list_E are executed; otherwise no statement is executed and the process continues after the end-if keyword.

if c=1
then writeln('c=1')
elif c=2
then writeln('c=2')
else writeln('c<>1 and c<>2')
end-if 

A shorter syntax for this statement is also supported:

if Bool_exp : Statement

With this form the only statement Statement is executed if the condition Bool_exp is true.

Case statement

The general form of the case statement is:

case Expression_0 of
Expression_1: Statement_1
or
Expression_1: do Statement_list_1 end-do
[
  Expression_2: Statement_2
  or
  Expression_2: do Statement_list_2 end-do
...]
[ else Statement_list_E ]
end-case

The selection is executed as follows: Expression_0 (that must be of type integer, real, string or boolean) is evaluated and compared sequentially with each expression of the list Expression_i until a match is found. Then the statement Statement_i (resp. list of statements Statement_list_i) corresponding to the matching expression is executed and the execution continues after the end-case instruction. If no matching is found and an else statement is present, the list of statements Statement_list_E is executed, otherwise the execution continues after the end-case instruction. Note that, each of the expression lists Expression_i can be either a scalar, a set or a list of expressions separated by commas. In the last two cases, the matching succeeds if the expression Expression_0 corresponds to an element of the set or an entry of the list.

case c of
  1     : writeln('c=1')
  2..5  : writeln('c in 2..5')
  6,8,10: writeln('c in {6,8,10}')
  else writeln('c in {7,9} or c >10 or c <1')
end-case 

Loops

Forall loop

The general form of the forall statement is:

forall (Iterator_list) Statement
or
forall (Iterator_list) do Statement_list end-do

The statement Statement (resp. list of statements Statement_list) is repeated for each possible index tuple generated by the iterator list (cf. Section Aggregate operators).

forall (i in 1..10, j in 1..10 | i<>j) do
  write(' (' , i, ',' , j, ')')
  if isodd(i*j) then s+={i*j}
  end-if
end-do 

While loop

The general form of the while statement is:

while (Bool_expr) Statement
or
while (Bool_expr) do Statement_list end-do

The statement Statement (resp. list of statements Statement_list) is repeated as long as the condition Bool_expr is true. If the condition is false at the first evaluation, the while statement is entirely skipped.

i:=1
while(i<=10) do
  write(' ',i)
  if isodd(i) then s+={i}
  end-if
  i+=1
end-do 

Repeat loop

The general form of the repeat statement is:

repeat
Statement1
[ Statement2 ...]
until Bool_expr

The list of statements enclosed in the instructions repeat and until is repeated until the condition Bool_expr is true. As opposed to the while loop, the statement(s) is (are) executed at least once.

i:=1
repeat
  write(' ',i)
  if isodd(i) then s+={i}
  end-if
  i+=1
until i>10 

break and next statements

The statements break and next are respectively used to interrupt and jump to the next iteration of a loop. The general form of the break and next statements is:

break [n|label]
or
next [n|label]

where n is an optional integer constant: n-1 nested loops are stopped before applying the operation. This optional argument may also be a label (in the form an identifier or a string constant): in this case the loop to consider is identified by a label that must be defined just before the corresponding loop using the following syntax:

label :

The label can be either an identifier (that is not associated to any entity) or a constant string. The scope of each label is limited to the loop it identifies.

! in this example only the loop controls are shown
L1:                 ! 1: Define label "L1"
repeat              ! 2: Loop L1
 forall (i in S) do ! 3: Loop L2
  while (C3) do     ! 4: Loop L3
   break 3          ! 5: Stop the 3 loops and continue after line 12
   next             ! 6: Go to next iteration of L3 (line 4)
   next 2           ! 7: Stop L3 and go to next 'i' (line 3)
  end-do            ! 8: End of L3
  next "L1"         ! 9: Stop L2, go to next iteration of L1 (line 12)
  break             !10: Stop L2 and continue after line 11
 end-do             !11: End of L2
until C1            !12: End of L1 

with statement

The general syntax of this statement is:

with locdef_1 [, locdef_2...] do
  Statement
  [ Statement ...]
end-do

Where locdef_i are local definitions of the following form:

ident=exp
or
ident(idndx_1 [, idndx_2...])=id_arr

In the first form ident is defined as an alias to the given expression exp and in the second form ident is equivalent to the array reference id_arr and each idndx_i are the corresponding indexing sets.

Although the with statement is not a loop it is handled like a single iteration forall loop such that it is possible to use the break and next statements within the block of instructions.

! in this example LR is an array of records
with r=LR(10) do
 r.x:=10         ! update LR(10).x
 r.y:=20         ! update LR(10).y
end-do

Procedures and functions

It is possible to group sets of statements and declarations in the form of subroutines that, once defined, can be called several times during the execution of the model. There are two kinds of subroutines in Mosel, procedures and functions. Procedures are used in the place of statements (e.g. writeln("Hi!")) and functions as part of expressions (because a value is returned, e.g. round(12.3)). Procedures and functions may both receive arguments, define local data and call themselves recursively.

Definition

Defining a subroutine consists of describing its external properties (i.e. its name and arguments) and the actions to be performed when it is executed (i.e. the statements to perform). The general form of a procedure definition is:

procedure name_proc [(list_of_parms)]
  Proc_body
end-procedure

where name_proc is the name of the procedure and list_of_parms its list of formal parameters (if any). This list is composed of symbol declarations (cf. Section The declaration block) separated by commas. The only differences from usual declarations are that the variable name can be omitted (the declaration states only the type of the entity) and no constants or expressions are allowed, including in the indexing list of an array (for instance A=12 or t1:array(1..4) of real are not valid parameter declarations). The body of the procedure is the usual list of statements and declaration blocks except that no type, procedure or function definition can be included.

procedure myproc
  writeln("In myproc")
end-procedure

procedure withparams(a:array(r:range) of real, i,j:integer)
  writeln("I received: i=",i," j=",j)
  forall(n in r) writeln("a(",n,")=",a(n))
end-procedure

declarations
  mytab:array(1..10) of real
end-declarations

myproc                                 ! Call myproc
withparams(mytab,23,67)                ! Call withparams 

The definition of a function is very similar to the one of a procedure:

function name_func [(List_of_params)]: Type
  Func_body
end-function

The only difference with a procedure is that the function type must be specified: it can be any type. Inside the body of a function, a special variable of the type of the function is automatically defined: returned. This variable is used as the return value of the function, it must therefore be assigned a value during the execution of the function.

function multiply_by_3(i:integer):integer
  returned:=i*3
end-function

writeln("3*12=", multiply_by_3(12))    ! Call the function 

Normally all statements of a subroutine are executed in sequence. It is however possible to interrupt the execution and return to the caller by using the special statement return.

Variable number of parameters

A subroutine may accept a variable number of parameters. To declare a routine of this kind, the last parameter of the list of parameters must have the special type ...: this parameter will receive all the additional arguments as a list of any (see Section Unions) when the routine is called.

procedure printall(prefix:string,argv:...)
 if argv.size=0 then
  writeln(prefix)                     ! just the prefix
 else
  forall(p in argv) writeln(prefix,p) ! prefix and other arguments
 end-if
end-procedure

printall("nothing")
printall("data ",1,"a",true)

The above code extract displays:

nothing
data 1
data a
data true

Formal parameters: passing convention

Formal Parameters of basic types are passed by value and all other types are passed by reference. In practice, when a parameter is passed by value, the subroutine receives a copy of the information so, if the subroutine modifies this parameter, the effective parameter remains unchanged. But if a parameter is passed by reference, the subroutine receives the parameter itself. As a consequence, if the parameter is modified during the process of the subroutine, the effective parameter is also affected.

procedure alter(s:set of integer,i:integer)
  i+=1
  s+={i}
end-procedure

gs:={1}
gi:=5
alter(gs,gi)
writeln(gs," ",gi)                     ! Displays: {1,6} 5 

Local declarations

Several declaration blocks may be used in a subroutine and all identifiers declared are local to this subroutine. This means that all of these symbols exist only in the scope of the subroutine (i.e. between the declaration and the end-procedure or end-function statement) and all of the resource they use is released once the subroutine terminates its execution unless they are referenced outside of the routine (e.g. member of a set defined globally). As a consequence, active constraints (linctr that are not just linear expressions) declared inside a subroutine and the variables they employ are still effective after the termination of the subroutine (because they are part of the current problem) even if the symbols used to name the related objects are not defined any more. Note also that a local declaration may hide a global symbol.

declarations                    ! Global definition
  i,j:integer
end-declarations

procedure myproc
  declarations
     i:string                   ! This declaration hides the global symbol
  end-declarations
  i:="a string"                 ! Local 'i'
  j:=4
  writeln("Inside of myproc, i=",i," j=",j)
end-procedure

i:=45                           ! Global 'i'
j:=10
myproc
writeln("Outside of myproc, i=",i," j=",j)

This code extract displays:

Inside of myproc, i=a string j=4
Outside of myproc, i=45 j=4 

Overloading

Mosel supports overloading of procedures and functions. One can define the same function several times with different sets of parameters and the compiler decides which subroutine to use depending on the parameter list. This also applies to predefined procedures and functions.

! Returns a randomly generated integer in the interval [1,limit]
function random(limit:integer):integer
  returned:=round(.5+random*limit)          ! Use the predefined
                                            ! 'random' function
end-function 

It is important to note that:

  • a procedure cannot overload a function and vice versa;
  • it is not possible to redefine any identifier; this rule also applies to procedures and functions. A subroutine definition can be used to overload another subroutine only if it differs for at least one parameter. This means, a difference in the type of the return value of a function is not sufficient.

Forward declaration

During the compilation phase of a source file, only symbols that have been previously declared can be used at any given point. If two procedures call themselves recursively (cross recursion), it is therefore necessary to be able to declare one of the two procedures in advance. Moreover, for the sake of clarity it is sometimes useful to group all procedure and function definitions at the end of the source file. A forward declaration is provided for these uses: it consists of stating only the header of a subroutine that will be defined later. The general form of a forward declaration is:

forward procedure Proc_name [(List_of_params)]
or
forward function Func_name [(List_of_params)]: Type

where the procedure or function Func_name will be defined later in the source file. Alternatively a subroutine can be declared by stating its header inside of a declarations block. Note that a forward declaration for which no actual definition can be found is considered as an error by Mosel.

forward function f2(x:integer):integer

function f1(x:integer):integer
  returned:=x+if(x>0,f2(x-1),0)             ! f1 needs to know f2
end-function

function f2(x:integer):integer
  returned:=x+if(x>0,f1(x-1),0)             ! f2 needs to know f1
end-function 

Suffix notation

Functions which name begins with get and taking a single argument may be called using a suffix notation. This alternative syntax is constructed by appending to the variable name (the intended function parameter) a dot followed by the function name without its prefix get. For instance the call getsol(x) is the same as x.sol. The compiler performing internally the translation from the suffix notation to the usual function call notation, the two syntaxes are equivalent.

Similarly, calls to procedures which name begins with set and taking two arguments may be written as an assignment combined with a suffix notation. In this case the statement can be replaced by the variable name (the intended first procedure parameter) followed by a dot and the procedure name without its prefix set then the assignment sign := and the value corresponding to the second parameter. For instance the statement sethidden(ctl,true) can also be written ctl.hidden:=true. As for the other alternative notation, the compiler performs the rewriting internally and the two syntaxes are equivalent.

Problems

In Mosel terms, a problem is a container holding various attributes and entities. The nature of the information stored is characterised by a problem type. The core system of Mosel provides the mpproblem problem type for the representation of mathematical programming problems with linear constraints. Other types may be published by modules either as entirely new problem types or as problem type extensions. An extension adds extra functionality or properties to an existing type; for instance, mpproblem.xprs provided by the module mmxprs adds support for solving mpproblem problems while the type mpproblem.nl of mmnl makes it possible to include non-linear constraints in an mpproblem.

When the execution of the model starts, an instance of each of the available problem types is created: this main problem constitutes the default problem context. As a consequence, all problem related operations (e.g., add constraints, solve...) refer to this context. Further problem instances may be declared just like any other symbol using a declarations section. The specification of a problem type (that is used as an elementary type in a declaration) has two forms:

problem_type
or
problem_type1 and problem_type2 [and problem_typen ...]

where problem_type* are problem type names. The second syntax allows to define a problem instance that refers to several problem types: this can be useful if a particular problem consists in the combination of several problem types. Note also that the main problem can be seen as an instance of the combination of all available problem types.

The with construct can be used to switch to a different problem context for the duration of a block of instructions. The general form of this construct is:

with prob do
  Statement
  [ Statement ...]
end-do

where prob is a problem reference or a problem type specification. In the first case the referenced problem is selected, in the second case, a new problem instance is created for the duration of the block (i.e., it is released after the block has been processed). Both statements and declaration blocks as well as other with constructs may be included in this section: they are all executed in the context of the selected problem.

declarations
  p1,p2:mpproblem
  p3:mpproblem and mypb      ! assuming 'mypb' is a problem type
  PT=mpproblem and mypb      ! user defined problem type
  a:array(1..10) of PT
  x,y:mpvar
end-declarations
with p1 do
 x+y>=0
end-do
with p2 do
 x-y=1
end-do

Some problem types support assignment (operator :=) and additive assignment (operator +=). These operators can be used between objects of same type but also when the right parameter of the operator is a component of the assigned object. For instance, assuming the declarations of the previous example we could state p3:=p2 meaning that the mpproblem part of p3 must be replaced by a copy of p2, the mypb part of p3 remaining unchanged. From the same context, the assignment p2:=p3 produces a compilation error.

The mpproblem type

An mpproblem instance basically consists in a set of linear constraints (the decision variables defined anywhere in a model are shared by all problems). A constraint is incorporated into a problem when it is expressed, so having the declaration of a linctr identifier in the context of a problem is not sufficient to attach it to this problem. The association will occur when the symbol is assigned its first value. Afterwards, the constraint will remain part of the same problem even if it is altered from within the context of another problem (a constraint cannot belong to several problems at the same time).

with p1 do
 C1:=x+y+z>=0
 x is_integer
end-do
with p2 do
 2*x-3*z=0    ! here we state constraints of p2
 ...
 minimize(z)
 C1+= x.sol*z.sol
end-do

In the example above, the constraint C1 is part of problem p1. From the context of a second problem p2 the constraint C1 is modified using solution information of p2: this change affects only the first problem since the constraint does not belong to the current context. Note that since is_integer is a (unary) constraint, the decision variable x is integer for problem p1 but it is a continuous variable in p2.

When a problem is released or reset (see reset), all its constraints are detached. Constraints which are not referenced (anonymous constraints) are released at the same time, named constraints however are not freed, they become available to be associated to some other problem.

with mpproblem do
 C1:=x+y+z>=0  ! (1)
 x-2*y=10      ! (2)
 x is_integer  ! (3)
end-do
with p1 do
 C1
end-do

In this example, at the end of the first with block, the local problem is released. As a consequence the constraint C1 is detached from this problem (but remains unchanged) and the 2 other constraints are freed. The following statements add C1 to the problem p1.

The type mpproblem supports both assignment (operator :=) and additive assignment (operator +=).

The public qualifier

Once a source file has been compiled, the identifiers used to designate the objects of the model become useless for Mosel. In order to access information after a model has been executed (for instance using the print command of the interractive debugger), a table of symbols is saved in the BIM file.

The qualifier public can be used in declaration and definition of objects to mark those identifiers (including subroutines) that must be published in the table of symbols. Without this qualifier a symbol is considered to be private and it is not recorded in the table of symbols (unless the source is compiled with debugging information).

public declarations
  e:integer                                 ! e is published
  f:integer                                 ! f is published
end-declarations

declarations
  public a,b,c:integer                      ! a,b and c are published
  d:real                                    ! d is private
end-declarations

forward public procedure myproc(i:integer)  ! 'myproc' is published

This qualifier can also be used when declaring record types in order to select the fields of the record that can be accessed from outside of the file making the definitions: this allows to make available only a few fields of a record, hidding what is considered to be internal data.

declarations
 public t1=record
     i:integer            ! t1.i is private
     public j:real        ! t1.j is public
    end-record
 public t2=public record
     i:integer            ! t2.i is public
     j:real               ! t2.j is public
    end-record
end-declarations

Note that a public record type can only contain public types even if it does not publish its fields.

Packages

Declarations may be stored in a package: once compiled, the package can be used by any model by means of the uses or import statements (see Section Directive uses). Except for its beginning and termination (keyword model is replaced by package) a package source is similar to a normal model source. The following points should be noticed:

  • all statements and declarations outside procedure or function definitions are used as an initialization routine: they are automatically executed before statements of the model using the package;
  • symbols that should be published by the package must be made explicitly public using the public qualifier (see Section The public qualifier);
  • model parameters of a package are automatically added to the list of parameters of the model using the package;
  • a package cannot be imported several times by a model and packages publish symbols of packages they import. For instance, assuming package P1 imports package P2, a model using P1 cannot import or use explicitly P2 but has access to the functionality of P2 via P1.

Version management

When a package defines a version number (see Section Directive version) Mosel implements a compatility rule similar to the one used for modules: a package version A can be used in place of package version B if major(A) = major(B) and minor(A) ≥ minor(B). This mechanism applies at compile time (when using different packages with the same dependencies) and at runtime when loading a model (see also Section Advanced package version management). It is recommended to update package version numbers as follows:

  • major number: any changes that will require recompilation of models using the package (in particular removal of public functionality from the package)
  • minor number: addition of new functionality that does not influence the behavior of existing uses of the package
  • release number: bug fixes

The requirements block

Requirements are symbols a package requires for its processing but does not define. These required symbols are declared in requirement blocks which are a special kind of declaration blocks in which constants are not allowed but procedure/functions can be declared. The symbols of such a block have to be defined when the model using the package is compiled: the definitions may appear either in the model or in another package but cannot come from a module. Several packages used by a given model may have the same requirements (i.e. same identifier and same declaration). It is also worth noting that a package inherits the requirements of the packages it uses.

requirements
  an_int:integer
  s0: set of string
  bigar: array(S0) of real
  procedure doit(i:integer)
end-requirements

The declaration of a record type in a requirements block will have all its fields implicitly public. When trying to satisfy the requirement, the system will check the beginning of the definition of the record that must match (i.e. same name, type and position of each field) and it will ignore any additional entry that might be contained by the actual definition. As a consequence a record declared as a requirement may have no field: this means that the package needs such a record type but it does not access it directly. For instance in the following declaration the package expects a record type named props and 2 associated routines getprop and setprop to access this datastructure:

requirements
  props=record
        end-record
  function getprop(p:props,name:string):integer
  procedure setprop(p:props,name:string,value:integer)
end-requirements

Control parameters

Packages may define control parameters that can be used just like those of modules via routines getparam and setparam. A control parameter is defined in the parameters block (see Section The parameters block) using the following syntax:

pname: type_name

where pname is the name of the parameter as a constant string and type_name its type (either integer, real, string or boolean). In addition to this declaration accessor routines must be defined: for handling integer parameters the public function pkgname∼getiparam(pname:string):integer and the public procedure pkgname∼setparam(pname:string,v:integer) must be defined (pkgname being the name of the current package). The function will be called by getparam to retrieve the value of the specified parameter (as a string in lower case) and the procedure will be used by setparam to change the parameter value. Similar definitions will be required for the other types (assuming the package declares parameters of the corresponding types), namely getrparam (real parameters), getsparam (string parameters) and getbparam (Boolean parameters) as well as the associated procedures setparam. The following example shows the required definitions for the package mypkg to publish real parameters p1 and p2:

parameters
  "p1":real
  "p2":real
end-parameters

declarations
 myp1,myp2:real    ! private variables to hold parameter values
end-declarations

! get value function for real parameters
public function mypkg~getrparam(p:string):real
 case p of
  "p1": returned:=myp1
  "p2": returned:=myp2
 end-case
end-function

! set value procedure for real parameters
public procedure mypkg~setparam(p:string,v:real)
 case p of
  "p1": myp1:=v
  "p2": myp2:=v
 end-case
end-procedure

Namespaces

All identifiers (variables and subroutines names) of a program are implicitly collected in a global dictionary shared by the program itself and all packages and modules it uses. It is also possible to group certain identifiers under a namespace that is characterised by a name. A given identifier may appear in several namespaces and each of its occurrences refers to a different entity, as a consequence an entity is unambiguously identified by its name and the namespace to which it is a member: this is the fully qualified name of this entity that is noted:

nspc∼ident

Where nspc is a namespace name and ident an identifier in this namespace.

A namespace's name is also an identifier that must be declared before being used even if it is defined by a package already loaded. This declaration is achieved using the namespace compiler directive:

namespace ns1 [, ns2 ...]

Where nsi are the names of the namespaces that will be used in the program. As an identifier a namespace may be declared as part of another namespace and any of the nsi may have the form nsx∼nsy to declare nsy as a namespace included in nsx. Several namespace directives may be stated.

When the compilation starts a namespace is automatically created: it is used to collect all private symbols of the program. When compiling a model this namespace has an empty name (i.e. a fully qualified name of this namespace is of the form ∼ident) and for a package it has the same name as the package.

When looking for an identifier (that is not fully qualified) the compiler tries first to find it in the global dictionary and then searches in a predefined list of namespaces. This list is initialised with the namespace of private symbols and may be extended using the nssearch directive:

nssearch ns1 [, ns2 ...]

This statement adds the specified namespaces to the search list. If the directive is stated several times, each added list is appended to the current list of namespaces. The namespace search is not recursive: it is not sufficient to add a namespace to the search list to have all its included namespaces to be also part of the search list (e.g. if ns1 includes ns11 and ns12, the 3 names ns1,ns1∼ns11 and ns1∼ns12 must be put into the search list for all the identifiers to be searchable). Note that namespaces listed in a nssearch directive do not need to be declared in a namespace directive.

Any namespace defined in a package is available to any model or package using it (and all the identifiers it includes are implicitly public). Defining a namespace group makes it possible to allow only certain packages to access a given namespace. The definition of such a group requires the use of a dedicated compiler directive:

nsgroup nspc: pkg1 [, pkg2 ...]
or
nsgroup nspc

Where pkgi are the package names (as constant strings) that will be allowed to access namespace nspc. By default the automatic namespace containing the private symbols of a package has a group containing only the package itself such that it cannot be used by any external component. It is however possible to redefine this initial group with a nsgroup directive, in particular the second form of the directive (without specifying any package) makes the corresponding namespace available to any package. Note that namespaces listed in a nsgroup directive do not need to be declared in a namespace directive.

Annotations

Annotations are meta data expressed in the Mosel source file that are stored in the resulting bim file after compilation. Thanks to a dedicated API it is possible to retrieve the information both from the model itself during its execution (see getannotations) or before/after execution from a host application (see function XPRMgetannotations in the Mosel Libraries Reference Manual).

Syntax

Annotations are organised in categories. A category groups a set of annotations and other categories (or sub-categories). When expressing a full annotation name, categories are separated by the '.' symbol. For instance:

doc.name

will be used to select the annotation name that is a member of the doc category. Similarly:

mycat1.cat2.info

will reference the annotation info recorded in the category cat2 that is itself part of category mycat1. Annotations and annotation categories must be valid Mosel identifiers: their names can only use alpha-numeric symbols plus '_'.

Some predefined categories are available at the beginning of the compilation:

  • the default category (its name is empty) collects annotations that are not explicitly member of any particular category. For instance the annotation myannot will be recorded in the default category. This annotation may also be referenced by its full name .myannot
  • mc (for Mosel Compiler) is used to pass information to the compiler during the compilation. For example, the mc.def annotation makes it possible to declare an annotation type (see section Declaration)
  • doc can be used to document a model or package file (see section Documenting models using annotations)

In the Mosel source file annotations are included in special comments. A single-line annotation is of the form:

!@ name value

Here name is the name of the annotation (spaces between '@' and the name are ignored) and the following text (up to the end of line) its corresponding value. The separation character between the name and the value can be a space, ':' or '=' (there must be no space between the name and the symbol). There is no restriction on the content of the value: it can be any kind of text (unless the annotation is typed—see section Declaration).

A multi-line annotation is of the form:

(!@name value
  ...
  @name2 value2
  ...
!)

where name is an annotation name while the text following this name is its associated value. With this syntax the value may spread over several lines, its termination is marked either by the end of the comment block or by a new annotation specification. In this context, a new annotation must start with the '@' symbol at the beginning of a new line (leading spaces are ignored). As for a one-line annotation, symbols ':' and '=' can be used instead of a space to separate the name and its value.

If several annotations of the same category have to be defined in the same block, a current category may be defined such that following annotation names can be shortened. This mechanism is activated by specifying the category name terminated by a dot (the remainder of this line is ignored) before the first annotation statement. The category selection is effective for the current comment block only and remains active until the next selection. Using a dot in place of a category name restores the default behaviour (i.e. the full path must be used for annotation reference). For instance:

(!@doc.        Switch to 'doc' category (this text is ignored)
  @name:my_function
  @type:integer
  @mycat.cat1. Switch to 'mycat.cat1'
  @memb1 10
  @memb2 20
  @.           Unselect current category
  @glb=useless
!)

Is equivalent to:

(!@doc.name:my_function
  @doc.type:integer
  @mycat.cat1.memb1 10
  @mycat.cat1.memb2 20
  @glb=useless
!)

By default any new annotation name is added to the internal dictionary and no checking is applied to the provided value. If a given annotation is defined several times only the last assignment is preserved. The compiler will however emit a warning if an attempt is made to assign a value to a category or to use an annotation as a category. For instance:

!@mycat.memb1 10
!@mycat.memb1.memb2 20

The second definition will fail to use mycat.memb1 as a category because the first one has already implicitly declared it as an annotation.

Symbol association

An annotation is either global or associated with a specific public symbol (see section The public qualifier). The association depends on the location of the definition in the source code:

  • annotations preceding a subroutine declaration (forward statement) or definition are associated with the subroutine name
  • annotations preceding a declarations block are distributed to all the symbols declared in the block
  • inside of a declarations block: annotations preceding or terminating the line of a declaration are associated with the corresponding symbols

In all other cases the annotations are global (i.e. not associated with any particular symbol) — in particular trying to associate annotations to private symbols will result in global annotations.

Annotations that precede a subroutine declaration, a declarations block or an entity in a declarations block can be turned into global annotations by inserting the compiler annotation mc.flush between the annotation and the following code.

Declaration

Declaration of annotations is achieved via the mc.def compiler annotation. Once an annotation is declared, the compiler checks the validity of definitions and rejects those that are not compliant, issuing a warning message (invalid annotations will not make the compilation fail unless the flag strict is used).

The general syntax of the annotation declaration statement is:

!@mc.def aname [prop1[,prop2...]]

Where aname is an annotation name and prop? a property keyword. The possible keywords are:

alias name1 name2...
aname Defining an alias to name1, name2...
text|integer|real|boolean
Type of the annotation value (default: text).
last|first|merge|multi
Handling of multiple definitions of an annotation (default: last)
  • last: the last definition is kept
  • first: keep the first definition (the following ones are ignored)
  • merge: definitions are concatenated (separated by new lines)
  • multi: all definitions are kept
global|specific
By default, the association of annotations depends on the location of the definition. If global is stated, the annotation is always global; with option specific, the annotation will be kept only if it can be associated with a symbol (otherwise it is ignored instead of being stored as a global one).
values=v1 v2 v3...
If used, this option must be the last one of the definition and it cannot be combined with range. It defines a list of possible values for the annotation.
range=lb ub
If used, this option must be the last one of the definition, it requires the type to be specified ( integer or real) and it cannot be combined with values. It defines a range of possible values.
strict
When this option has been stated any error detected on this annotation (or path when applied to a category) will make the compilation fail
noimplicit
Applied to a category this option indicates that any additional annotation of this category must be first declared (using an mc.def construct) before being used. Note that sub-categories are not concerned by this flag: if required each sub-category has also to be tagged.

Example:

!@mc.def person.name text,first,specific
!@mc.def person.age integer,first,specific,range=0 150
!@mc.def person.gender values=male female

Categories are implicitly declared by the annotations they include (for instance declaring @mycat.myann implies the creation of mycat as a category). It is also possible to explicitly declare an empty category (i.e. containing no annotation) using the mc.def construct by appending a dot to the category name (the only supported properties are strict and noimplicit). For instance:

!@mc.def mycat. noimplicit

For a given annotation the declaration may be stated several times but the properties of an annotation cannot be changed. For instance, the following declarations can be used in the same source:

!@mc.def myann
!@mc.def myann text,last

But the following declaration cannot be combined with any of the two preceding ones as they both result in the annotation type text:

!@mc.def myann integer

Declarations included in models are not exported to the bim file (i.e. they are only used during the compilation procedure) but declarations stated in packages are published if they are relative to a user defined category: any model using the package inherits the annotation declarations of the package.

Additional properties can be set using the mc.set compiler annotation. The general syntax of this special statement is:

!@mc.set name flag

Where name is an annotation or category name and flag one of the following keywords:

complete
Applied to a category this flag indicates that no other annotation can be added to this category (ignored for an annotation). It is however still possible to declare aliases. Note that sub-categories are not concerned by this flag: if required each sub-category has also to be tagged.
disable
Disable the named category or annotation. From the point where this flag has been set onwards, all definitions deriving from the provided name are silently ignored.
enable
Revert the effect of disable.
unpublish
Disable the automatic publication of the specified declaration.
publish
Publish the specified declaration.

Note that mc.set expects a full explicit name: for this command ann refers to category ann and not to annotation .ann as in other places.

Advanced package version management

When loading dynamically a package the system relies on the version number stated by the version compiler directive (see Section Directive version) to decide whether the provided package is compatible with the one required by the model or package loading it. During compilation the version information of each required package (and module) is recorded in the bim file: by default the package version ap.bp.cp is compatible with the required package version ar.br.cr if ap=ar and bp≥br.

This default rule can be changed by issuing the annotation mc.version.compatible:

!@mc.version.compatible a[.b[.c]] 

This directive defines the minimum version number that is compatible with the package (this version number must be smaller than the package version). It is the responsability of the developper to make sure that the package is effectively compatible with the older versions (i.e. the newer version must not include any breaking change, in particular, it must not remove any previously available functionality).

For instance, consider the following declarations:

 package P
 version 2.0.0
 !@mc.version.compatible 1.0

This declares a package P with version 2.0.0 that is compatible with versions from 1.0.0 onwards: a bim file requiring the version 1.0 of this package can use this newer version instead.

The annotation mc.version.since makes it possible to alter the version information stored in a bim file:

!@mc.version.since a[.b[.c]] 

Applied to a subroutine it defines the package version that introduced (a particular overloaded version of) the subroutine. This information is used by the compiler to generate the version requirement of a model or package using the package. Note that in order to use this annotation the mc.version.compatible declaration must have been stated. Any version number specified with mc.version.since must lie between the compatible version and the current version of the package.

Consider this extended form of the preceding example that adds two subroutines along with mc.version.since markup on one of them:

 package P
 version 2.0.0
 !@mc.version.compatible 1.0

 !@mc.version.since 1.1.1
 public procedure myproc1
 ...
 public procedure myproc2

The bim file of a model using this package P will record a dependency on version 1.1.1 if only the procedure myproc1 is used. However, a dependency on version 2.0.0 will be logged if procedure myproc2 is called.

In case a new version introduces an incompatibility that cannot be characterised by a subroutine (by adding a new data type for instance) the mc.version.since declarations should be removed in order to avoid generating an invalid dependency version. As an alternative to this removal the mc.version.atleast annotation can be used:

!@mc.version.atleast a[.b[.c]] 

This definition specifies a minimum version dependency.

Assuming that we are publishing a new version of our package that introduces a new type. The beginning of the package source might look as follows:

 package P
 version 3.1
 !@mc.version.compatible 1.0
 !@mc.version.atleast 3.0
 ...
 !@mc.version.since 3.0.2
 public procedure myproc3

This declares a package P with version 3.1.0 that is compatible with version 1.0.0. Independently of further mc.version.since declarations, compiling a model with this package will generate a dependency of at least version 3.0.0, calling procedure myproc3 will raise the version dependency to 3.0.2.

A subroutine about to be removed can be tagged as deprecated such that the compiler can report a warning when this routine is used. This tagging is acheived by the mc.version.deprecated annotation:

!@mc.version.deprecated a[.b[.c]] 

This definition specifies the version from which the routine is deprecated.

Coming back to the previous example, let us consider that a new version deprecates the procedure myproc1 and introduces (as a replacement) the procedure myproc1bis:

 package P
 version 4
 !@mc.version.compatible 1.0
 !@mc.version.atleast 3.0
 ...
 !@mc.version.since 1.1.1
 !@mc.version.deprecated 4.0
 public procedure myproc1
 ...
 public procedure myproc1bis

With the declarations above the compiler will emit a deprecation warning if myproc1 is called and the required package version is greater than 4.0. For instance this will happen if myproc1bis is used.

File names and input/output drivers

Mosel handles data streams using I/O drivers: a driver is an interface between Mosel and a physical data source. Its role is to expose the data source in a standard way such that from the user perspective, all data sources can be accessed using the same methods (i.e. initializations blocks, file handling functions). Drivers are specified in file names: all Mosel functions supporting I/O operations through drivers can be given an extended file name. This type of name is composed of the pair driver_name:file_name. When Mosel needs to access a file, it looks for the specified driver in the table of available drivers. This table contains all predefined drivers as well as drivers published by modules currently loaded in memory. If the driver is provided by a module, the module name may also be indicated in the extended file name: module_name.driver_name:file_name. Using this notation, Mosel loads the required module if necessary (otherwise the file operation fails if the module is not already loaded). For instance it is better to use mmodbc.odbc:database than odbc:database.

The file_name part of the extended file name is specific to the driver and its structure and meaning depends on the driver. For instance, the sysfd driver expects a numerical file descriptor so file sysfd:1 is a valid name but sysfd:myfile cannot work. A driver may act as a filter and expects as file_name another extended file name (e.g. zlib.deflate:mem:myblk).

When no driver name is specified, Mosel uses the default driver which name is an empty string (myfile is equivalent to :myfile). This driver relies on OS functions to access files from the file system. Note that on the Windows operating system Mosel does not support relative paths on a specified drive (i.e. the file "C:myfile" is equivalent to "C:\myfile", the behaviour may be different in other environments).

The tmp driver is an extension to the default driver: it locates the specified file in the temporary directory used by Mosel (i.e. "tmp:toto" is equivalent to the expression getparam("tmpdir")+"/toto").

The null driver can be used to disable a stream: whatever written to file "null:" is ignored and reading from it is like reading from an empty file.

The mem driver uses a memory block instead of a file handled by the operating system. A file name for this driver is of the form mem:label[/minsize[/incstep]] where label is an identifier whose first character is a letter and minsize an optional initial amount of memory to be reserved (size is expressed in bytes, in kilobytes with suffix "k" or in megabytes with suffix "m"). The label being recorded in the dictionary of the model symbols it cannot be identical to any of the identifiers of the model (the function newmuid might be used to generate a unique identifier). The memory block is allocated dynamically and resized as necessary. By default the size of the memory block is increased by pages of 4 kilobytes: the optional parameter incstep may be used to change this page size (i.e. the default setting is "label/0/4k"). The special value 0 modifies the allocation policy: instead of being increased of a fixed amount, the block size is doubled. In all cases unused memory is released when the file is closed.
The mem driver may also be used to exchange data with an application using the Mosel libraries (refer to the Mosel Libraries Reference Manual for further explanation).

The tee driver can only be open for writing and expects as file name a list of up to 6 extended file names separated with `&': it opens all the specified files and duplicates what it receives to each of them. If only one file is given or if the string terminates with `&', output is also sent to the default output stream (or error stream if the file is used for errors). For instance, writing to the file "tee:log1&log2&" has the effect of writing at the same time to files "log1" and "log2" as well as sending a copy to the console.

The bin driver can only be used for initializations blocks as a replacement of the default driver: it allows to write (and read) data files in a platform independent binary format. This file format is generally smaller than its ASCII equivalent and preserves accuracy of floating point numbers. This driver can be used in 2 different ways: a single file including all records of the initialisations block is produced if a file name is provided. For instance, in the following example the file "mydata" will contain both A and B:

initialisations to "bin:mydata"
  A
  B
end-initialisations

With the second form (without file name) one file is generated for each record of the block. The following example produces 2 files: "mydata_A" to contain the values of record A and "mydata_B" for values of B:

initialisations to "bin:"
  A as "mydata_A"
  B as "mydata_B"
end-initialisations

When using this form in an initialisations to block, the option append may be specified such that files are open in append mode.

The other predefined drivers (sysfd, cb and raw) are useful when interfacing Mosel with a host application. They are described in detail in the Mosel Libraries Reference Manual.

I/O drivers provided by modules of the Mosel distribution are documented with the corresponding module (see Part Modules of this manual).

Character encoding of text files

Mosel uses UTF-8 for its internal representation of text strings and this is also the default character encoding for text files. It is however possible to read and write text files in different encodings: for model source and initialization block files the selection can be achieved by means of a special comment (see sections Source file character encoding and Initialization block) but the encoding may also be specified at the time of opening a file by prefixing its name with the "enc:" prefix:

enc:encoding [+unix|+dos|+sys] [+bom|+nobom],filename

Mosel supports natively the encodings UTF-8, UTF-16, UTF-32, ISO-8859-1, ISO-8859-15, CP1252 and US-ASCII. For UTF-16 and UTF-32 the byte ordering depends on the architecture of the running system (e.g. this is Little Endian on an x86 processor) but it can also be specified by appending LE (Little Endian) or BE (Big Endian) to the encoding name (e.g. UTF-16LE). The availability and names of other encodings depends on the operating system.

The following aliases may also be used in place of an encoding name: RAW (no encoding), SYS (default system encoding), WCHAR (wide character for the C library), FNAME (encoding used for file names), TTY (encoding of the output stream of the console), TTYIN (encoding of the input stream of the console), STDIN, STDOUT, STDERR (encoding of the default input/output/error stream).

In addition to the encoding name a couple of options might be applied: +unix and +dos select the line termination (note that +dos is automatically used when writing to a physical file on Windows). Options +bom and +nobom decides whether a Byte Order Mark is to be inserted at the beginning of the file (this option only applies to UTF encodings when the file is not open in appending mode). By default a BOM is inserted when the encoding is UTF-16 or UTF-32, the option +nobom disables this insertion. The option +bom implies the insertion of a BOM on UTF-8 encoded files (this is usually not required for this encoding but often used on Windows systems). The option +sys selects the line termination and BOM convention of the running system (i.e. it is equivalent to +unix on a Posix system and +dos+bom on a Windows machine).

Working directory and temporary directory

Except for absolute path names, file or path name expansion are relative to the current working directory. By default this reference location corresponds to the operating system current working directory which usually is the directory from which Mosel has been started. Since the working directory is an execution parameter, a model may be running with a current working directory which might be different from the one used by the operating system. It is therefore recommended to use absolute file names when a Mosel model communicates with an external component (for instance when a file name is part of the DSN to be used for an ODBC connection).

In addition to the current working directory, Mosel creates a temporary directory that is shared by all models for storing temporary data handled as physical files. This directory is identified by the environment variable MOSEL_TMP or located in the system temporary directory as specified by one of the environment variables TMP, TEMP or USERPROFILE under Windows and TMPDIR on Posix systems. If none of these environment variables is defined, the default base directory will be "C:\" on Windows and "/tmp" on Posix systems. The Mosel temporary directory is automatically created when needed and deleted at program termination.

The path names of the working directory and the temporary directory are identified respectively by the "workdir" and "tmpdir" control parameters and can be retrieved using the getparam function. It is possible to change the current working directory of a running model by updating the "workdir" parameter using setparam.

Handling of input/output

At the start of the execution of a program/model, three text streams are created automatically: the standard input, output and error streams. The standard output stream is used by the procedures writing text (write, writeln, fflush). The standard input stream is used by the procedures reading text (read, readln, fskipline). The standard error stream is the destination of error messages reported by Mosel during its execution. These streams are inherited from the environment in which Mosel is being run: usually using an output procedure implies printing something to the console and using an input procedure implies expecting something to be typed by the user.

The procedures fopen and fclose make it possible to associate text files to the input, output and error streams: in this case the IO functions can be used to read from or write to files. Note that when a file is opened, it is automatically made the active input, output or error stream (according to its opening status) but the file that was previously assigned to the corresponding stream remains open. It is however possible to switch between different open files using the procedure fselect in combination with the function getfid.

model "test IO"
 def_out:=getfid(F_OUTPUT)     ! Save file ID of default output
 fopen("mylog.txt",F_OUTPUT)   ! Switch output to 'mylog.txt'
 my_out:=getfid(F_OUTPUT)      ! Save ID of current output stream

 repeat
  fselect(def_out)             ! Select default ouput...
  write("Text? ")              ! ...to print a message
  text:=''
  readln(text)                 ! Read a string from the default input
  fselect(my_out)              ! Select the file 'mylog.txt'
  writeln(text)                ! Write the string into the file
 until text=''
 fclose(F_OUTPUT)              ! Close current output (='mylog.txt')
 writeln("Finished!")          ! Display message to default output
end-model 

Deploying models

Once a model has been compiled to a BIM file it can be deployed in a variety of ways. It may

  • be run from some remote code using the remote invocation library XPRD (see the XPRD reference manual),
  • be integrated in an application through the Mosel libraries (see Mosel libraries reference manual),
  • form part of an Xpress Insight application (see the Xpress Insight Developer Guide), or
  • simply be invoked from a command window or shell.

For the last option the usual approach consists in using the mosel command line tool (see section Running Mosel) with the run command. For instance, the following command may be used to run the model mycmd.bim:

> mosel run mycmd.bim

The aim of the deploy module is to ease the use of a model published this way. In particular it gives access to the command line parameters that are stated after the '--' marker. For instance, assuming our model is executed with the following command:

> mosel run mycmd.bim -- -f toto

The model will be able to retrieve the arguments '-f' and 'toto' through the routines argc and argv. The deploy module also makes it possible to generate an executable program from the BIM file. Moreover, it gives the model access to the command line arguments and exposes a method for embedding configuration files into the resulting program. The deploy module is usually used through one of its I/O drivers: the driver csrc generates a C program (based on the Mosel libraries) from a BIM file and a second driver, exe, produces directly the executable by running a C compiler on the generated C source (this requires the availability of a C compiler on the system). For example, the following command will create the program runmycmd (or runmycmd.exe on Windows) from the model mycmd.mos:

> mosel comp mycmd.mos -o deploy.exe:runmycmd

The example mosdeploy.mos included in the distribution shows how to generate a script (Windows bat-file or Unix shell script) embedding a bim-file: this can be a convenient alternative to the above approach when no C compiler is available.

In addition to its I/O drivers, the deploy module publishes two functions for accessing the program arguments: argc returns the number of parameters passed to the command (counting the command itself as the first) and argv(i) returns the ith argument (as a string). As an example, the following model displays the arguments it receives:

model mycmd
uses 'deploy'

writeln("My arguments:")
forall(i in 1..argc) writeln(argv(i))
end-model

After compiling this example into an executable with the command shown above, an execution of the command runmycmd a b c will display:

My arguments:
runmycmd
a
b
c

In addition to giving access to command line arguments, deploy makes it possible to embed files into the resulting executable (note that this can also be done using datablock). File locations are passed via model parameters. The following example outputs its source when the program is called with the argument 'src' — otherwise it reports an error message:

model mycmd2
uses 'deploy','mmsystem'

parameters
 SRC="null:"
end-parameters

if argc<>2 or argv(2)<>"src" then
 writeln("Usage: ", argv(1), " src")
 exit(1)
else
 writeln("Source:")
 fcopy(SRC,"")
end-if
end-model

In this example, the source file is identified by the model parameter SRC. To generate the program, the following command has to be issued:

> mosel comp mycmd2.mos -o deploy.exe:runmycmd2,SRC=mycmd2.mos

With the command above, the file mycmd2.mos is included in the executable and the SRC parameter is redefined such that the model can access the file through memory. Note that the model file can also be included in the executable in compressed form. To enable this feature, the parameter name has to be suffixed with -z in the compilation command:

> mosel comp mycmd2.mos -o deploy.exe:runmycmd2,SRC-z=mycmd2.mos

Documenting models using annotations

The predefined doc annotation category can be used to document a Mosel file. Using a dedicated set of annotations the model author can add descriptions to the various entities defined in the source, the user-defined descriptions are completed by definitions automatically generated by the Mosel compiler.

From a bim file that includes such definitions a documentation processor may produce a complete document: as an example, the Xpress distribution comes with the moseldoc processor that generates an HTML documentation from an annotated bim file.

doc annotation category

Unlike other annotation categories, the doc annotation category is disabled by default such that the corresponding annotations are silently ignored. To generate a documentation-enabled bim file the compiler has to be run with the option -D. In addition to enabling the doc category, this flag also activates the automatic generation of certain documentation annotations by the compiler. Alternatively to using this flag, a model may define the following annotations:

!@mc.set doc enable
!@doc.autogen=true

Note that these special annotations can also be used in the source file as a means to exclude some definitions from the documentation, setting doc.autogen to false right before the definitions to be excluded and back to true immediately after.

Global definitions

The following global annotations are automatically generated by the compiler:

@doc.name
Name of the package or model
@doc.version
Version number as stated by the 'version' statement
@doc.date
Current date
@doc.ispkg
Set to 'true' if the file is a package

All automatic annotations can also be defined explicitly in the Mosel source to overwrite their default values.

The following annotations may be added to complete the general appearance of the document to be produced (they are used by the moseldoc documentation processor):

@doc.title
Title of the document
@doc.subtitle
Subtitle of the document
@doc.xmlheader
Header of the XML document
@doc.xmlroot
Name of the XML element containing the documentation
@doc.id
Prefix used to generate IDs of chapters, sections, and subsections. If the documentation for several packages is generated from a single master model then a unique ID must be explicitly defined in each of the packages in order to avoid ID collision

The @doc category is complete (i.e. it is not possible to create new doc.X annotations), however, the category @doc.ext can be used to define further information assuming a particular documentation processor can exploit it.

Document structure

Optionally, the resulting document may be organised in chapters, sections and subsections. Each of these constructs can contain both text paragraphs and entity descriptions (declarations and subroutines). To enter a new documentation component, one of the following annotations has to be defined:

@doc.chapter
Start a chapter
@doc.section
Start a section inside of a chapter
@doc.subsection
Start a subsection inside of a section

Chapters and sections may also be taken from external files: the annotation @doc.include specifies a file name that must be a valid XML document including either chapters (tag <chapter>) or sections (tag <section>). The Mosel compiler will record the location of the inclusion that will be executed by the moseldoc processor.

In addition to the provided title a short title might also be defined (using @doc.shorttitle) that will be used in place of the (long) title in the table of contents. Whenever a new division starts, a unique ID is automatically generated based on the section number and any defined prefix specified in the header of the document with @doc.id. It is also possible to explicitly define an ID using @doc.id just after entering the section (this is required when the section has to be referenced using a <ref> tag).

From inside of any of these divisions a new paragraph is added with the @doc.p annotation. By default any new addition (paragraph or entity description) is appended to the current component but it is possible to select an alternative location. A target location has first to be defined using the annotation @doc.location: this creates a label associated with the current section. Defining the annotation @doc.relocate with this target elsewhere in the source file will move all subsequent additions to the target location; this relocation will continue up to the next division marker or relocation definition. Note that defining an empty relocation reverts to the effective current location. Example:

(!@doc.
  @chapter My first chapter
  @p some text related to the first chapter
  @location first_chap
  @section first section of first chapter
  @p something about the section
  @relocate first_chap
  @p this paragraph will be inserted directly under first chapter
  @relocate
  @p but this one will remain in the section
!)

Symbol definitions

The following sections list the various documentation annotations that can be defined depending on the kind of the entity (parameter, variable, type or subroutine) to be documented. Some of these annotations are automatically defined by the compiler: in the case of values (like the value of a constant) the automatic definition may not be performed if the value is the result of a calculation that cannot be evaluated at compile time ("runtime constant"). In this case it is required to explicitly specify the text that should be retained in the documentation.

Parameters

@doc.descr
Description (1-2 text lines)
@doc.default
Default value (automatically generated)
@doc.type
Type (automatically generated)
@doc.value
Possible value and explanation of its meaning (may be defined several times)
@doc.info
Some more detailed explanations (may be defined several times)
@doc.ignore
The symbol will be ignored by the documentation processor
@doc.deprecated
The parameter is deprecated and should no longer be used (an explanation can be given)

Types, constants and variables
This set of annotations apply to symbols declared in declarations blocks. Record fields (both for a type declaration and for a variable) can be described using @doc.recflddescr: the value of this annotation consists in the name of the field followed by its description (a space should separate these two components)

@doc.descr
Description (1-2 text lines)
@doc.const
For a constant: value (automatically generated)
@doc.type
Type (automatically generated)
@doc.typedef
For a type definition: type (automatically generated)
@doc.value
Possible value and explanation of its meaning (may be defined several times)
@doc.info
Some more detailed explanations (may be defined several times)
@doc.setby
Name of subroutines modifying this entity
@doc.recfldtype
Type of a record field (automatically generated)
@doc.recflddescr
Description of a record field
@doc.ignore
The symbol will be ignored by the documentation processor
@doc.reqmt
The symbol is a requirement (automatically generated)
@doc.deprecated
The type or variable is deprecated and should no longer be used (an explanation can be given)

Procedures and functions
Information from different overloaded versions of a given subroutine is merged automatically. The @doc.group annotation may be used to merge information of routines with different names but used for a similar task (up to 3 different subroutine names can be grouped). The @doc.param annotation is used to describe the parameters of the routine: the value of this annotation consists in the name of the parameter followed by its description (a space should separate these two components)

@doc.group
Name of another subroutine that this one should be grouped with
@doc.descr
Description (1-2 text lines)
@doc.shortdescr
Shortened description for table of contents and list display
@doc.syntax
Routine signature (automatically generated)
@doc.param
Name and meaning of a subroutine argument (may be defined several times)
@doc.paramval
Possible value and meaning of a subroutine argument (may be defined several times). The value of this annotation is the name of the parameter (as specified with a preceding @doc.param) followed by the value and the explanation
@doc.return
For functions only: what is returned
@doc.err
Possible error code (may be defined several times)
@doc.example
Example of use (may be defined several times)
@doc.info
Some more detailed explanations (may be defined several times)
@doc.related
List of related symbols
@doc.ignore
The subroutine will be ignored by the documentation processor
@doc.reqmt
The subroutine is a requirement (automatically generated)
@doc.deprecated
The subroutine is deprecated and should no longer be used (an explanation can be given)

Annotation definitions

A special set of annotations (category @doc.annot) is available for documenting annotation definitions in Mosel packages (not supported for Mosel models). The annotations for documenting annotation definitions are global annotations, their value must start with an annotation name in order to associate them with the corresponding annotation definition.

@doc.annot.descr
Annotation name followed by a short description (1-2 text lines)
@doc.annot.default
Annotation name and default value
@doc.annot.value
Annotation name, possible value and explanation of its meaning (may be defined several times)
@doc.annot.type
Annotation type
@doc.annot.info
Annotation name and some more detailed explanations (may be defined several times)
@doc.annot.deprecated
Annotation name and optionally some explanatory text
@doc.annotcat
Annotation category to document (may be defined several times), if undefined all categories are documented
@doc.annotloc
Insertion point (specified via @doc.location) for annotations documentation

Package control parameters

A special set of annotations (category @doc.cparam) is available for documenting control parameters of Mosel packages. The annotations for documenting control parameters are global annotations, their value must start with a parameter name in order to associate them with the corresponding control parameter.

@doc.cparam.descr
Parameter name followed by a short description (1-2 text lines)
@doc.cparam.default
Parameter name and default value
@doc.cparam.value
Parameter name, possible value and explanation of its meaning (may be defined several times)
@doc.cparam.type
Parameter type (automatically generated by the compiler)
@doc.cparam.info
Parameter name and some more detailed explanations (may be defined several times)
@doc.cparam.deprecated
Parameter name and optionally some explanatory text
@doc.cparamloc
Insertion point (specified via @doc.location) for control parameters documentation

moseldoc documentation processor

Running moseldoc

The moseldoc program takes as input either a bim file produced from a Mosel model compiled with the -D compiler option or directly a Mosel source file (in which case a compilation step is automatically executed). Typically the generation of the documentation from a source file will be obtained with the following command:

>moseldoc mymodel.mos

The result of this process is an XML file ("mymodel_doc.xml") and a directory containing an HTML version of the documentation ("mymodel_html"). The program will produce only the XML file (from a bim or source file) if option -xml is used and only the HTML output (from an XML file) if -html is selected. The option -f is required to force the replacement of existing files.

As a Mosel program available in source form, moseldoc can be adapted to fit specific requirements. To re-generate the executable use this compilation command:

>mosel comp -s moseldoc.mos -o deploy.exe:moseldoc,css-z=moseldoc.css

Structure of the generated document

The resulting document respects the structure defined by the dedicated annotations (chapter, section, subsection). In each of these divisions, the paragraphs are exposed first, then the parameters and variables and finally the list of subroutines. If no structural elements have been defined, a chapter per entity type is automatically created to group similar objects (Parameters, Constants, Types, Variables and Subroutines).

Processing of annotation values

Values associated with descriptive text annotations (like section titles or descriptions) are interpreted as XML. Paragraphs (@doc.p) and examples (@doc.example) are handled in a specific way: by default the value is inserted as XML but, if the value starts with [TXT], the content is treated as plain text; if it starts with [SRC], the value is considered to be some example code and it is reproduced preserving spacing. If it starts with [NOD], it is interpreted as a self-contained XML node (i.e. it is not inserted in a paragraph block). In an XML block of text, the markers ref (chapter/section/subsection reference), fctRef (subroutine reference) and entRef (entity reference) are processed such that in the HTML document they are turned into hyperlinks to the corresponding objects. Similarly, the tt element type is replaced by an appropriate style for displaying code samples.

Message translation

Mosel supports a message translation mechanism that makes it possible to display messages in the current language of the operating environment. This system requires that all messages are originally written in English and identified as messages to be translated (it is usually not desirable to translate all text strings of a model). The Mosel compiler can then collect all messages to be translated for building message catalogs. Each message catalog file contains the translations of the messages for a given language: Mosel will select the appropriate file for the current language during its execution to use the right set of translations. The system is designed such that it will not fail if a translation or an entire language is missing: in such a case the original English text is used.

Preparing the model source

Most often, not all text strings occurring in a program are to be translated to native language. This is why it is necessary to tag each message to be translated such that the automatic message translation system can process only the relevant texts. The tagging is achieved by using the operators _c(), _() or the modified procedures write_(), writeln_(), fwrite_() and fwriteln_().

The operator _c() is used to identify constant strings that should be collected for translation but the string will not be translated at the place where it is used. This operator can be applied to a list of string constants. A similar effect can be obtained with the annotation mc.msgid.

The function _() applies to both constant strings and variables: it replaces its argument by the translated string. As with the operator _c() constant strings are collected for the message catalogs, but they will also be replaced by their translation at the place where the operator is applied to the string.

The write_ and writeln_ procedures are equivalent to their normal versions except that they process the constant strings they have to display for translation.

All translations of a model (or package) are grouped under a domain: this identifier is used to name the message catalog files. The default domain name is the model (or package) name after having replaced spaces and non-ascii characters by underscores (for instance the domain name of the model "my mod" is "my_mod"). The domain name can also be specified using the mc.msgdom annotation.

The following model example shows the use of the various markers:

model translate
! The message domain is 'trs' (default name would be the model name 'translate')
!@mc.msgdom trs

 declarations
  ! The elements of 'nums' are kept in English, but collected for translation
  nums=[_c("one","two","three")]
  ! Add 'four' to the message catalogs (although it is not used here)
  !@mc.msgid:four
 end-declarations

 ! Translate the message text, without translating 'nums'
 writeln_("all numbers (in English): ", nums)
 n:=getfirst(nums)
 ! Translate the message text and the first occurrence of 'n', but not
 ! its second occurrence
 writeln_("the first number is: ", _(n), " (in English:", n, ")")
end-model

Building the message catalogs

Once the model source has been prepared, the list of messages to be translated can be extracted. This operation is performed by the Mosel compiler when executed with the option -x:

>mosel comp -x mymod.mos -o trs.pot

The output of this command is a Portable Object Template (POT): this is a text file consisting of a list of pairs msgid (message to translate), msgstr (translation) for which only the first entry is populated.

With the example model from the previous section the generated POT file results in the following:

# Created by Mosel v4.0.0 from 'translate.mos'
# Domain name: trs

msgid "all numbers (in English): %L\n"
msgstr ""

msgid "four"
msgstr ""

msgid "one"
msgstr ""

msgid "the first number is: %s (in English:%s)\n"
msgstr ""

msgid "three"
msgstr ""

msgid "two"
msgstr ""

For each of the supported languages a separate PO (Portable Object) file that will contain the corresponding translations has to be created from this template. The command xprnls is used for this task (for further details please refer to the XPRNLS Reference Manual). For instance the following command will create the file for the Italian translations of the messages:

>xprnls init -o trs.it.po trs.pot

Here we name the file domain.language.po in order to ease the management of these translation files (where language stands for the ISO639 language code).

The generated file is a copy of the template with an additional header that should be completed by the translator (it is pre-populated with information obtained from the system), in particular the language (property "Language") and the encoding (property "Content-Type"). For each of the msgid records the translation in the language associated to the file has to be provided in the msgstr record. Note that some messages include escape sequences (like "\n") and format markers (e.g. "%s"): the corresponding translation must include the same format markers as the original text and they must appear in the same order (otherwise the translation will be ignored).

The beginning of the translation file of our example for French (named "trs.fr.po") should be similar to the following (the extract below shows only the header and the translation of the first message):

msgid ""
msgstr ""
"Project-Id-Version: My translation example\n"
"POT-Creation-Date: 2015-12-04 18:16+0100\n"
"PO-Revision-Date: 2015-12-04 18:16+0100\n"
"Last-Translator: Jules Verne\n"
"Language: fr\n"
"Content-Type: text/plain; charset=ISO8859-15\n"

msgid "all numbers (in English): %L\n"
msgstr "tous les nombres (en anglais): %L\n"

The message catalogs for the PO files are obtained by running once more the xprnls command, this time using the option mogen:

>xprnls mogen -d locale trs.*.po

This command will compile each of the PO files into a Machine Object (MO) file named trs.mo that will be saved under the directory locale/lang/LC_MESSAGES. This directory tree must be distributed along with the model file for the automatic translation to work.

Model execution

During the execution of the model the message catalogs for the current language (as indicated by the operating system) are loaded automatically from the 'locale' directory. This location is defined by the "localedir" control parameter (by default this is "./locale"). If no message catalog can be found for the requested language then the original English text is used. This will also be the case if a translation is missing (e.g. if the message catalog has not been updated after some model source change).

When run on a computer configured for French our example displays:

tous les nombres (en anglais): [`one',`two',`three']
le premier nombre est: un (en anglais:one)

© 2001-2024 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.