MpsIdlSpecification |
MPS uses a simple language to define the interface that an MPS server object publishes to its client objects. Interface definitions written in this language are then compiled by the MPS IDL compiler, mpsidl, into C++, Java, or both.
This is a small example of an MPS IDL file (simple.mps):
import "../libmps/namingservice.mps"; namespace Simple { interface Simple { bool not(bool arg); void dump(org::hebe::mps::naming::NamingService ns); } }
Namespaces are hierarchic, and map to nested namespaces in C++ and multi-segmented package names in Java.
Interfaces are defined with the "interface" keyword, and appear to the client as the programming interface to the remote object. No state is mentioned explicitly in an interface definition - interfaces are pure behaviour.
Structures are defined with the "struct" keyword, as below, and appear as state with no behaviour. Once created, an instance of a structure is immutable. They exist as pure value types.
The following MPS IDL file is the one used to define the naming service used by the MPS runtime itself (namingservice.mps):
namespace org { namespace hebe { namespace mps { namespace naming { struct Result { bool bound; string objectName; string resolvedName; }
interface NamingService { Result resolve(string objectName); Result bind(string objectName, string resolvedName, bool replace); bool unbind(string objectName);
Result[] enumerate(); } } } } }
2 Formal Definition2.1 Overall File structure
MPS IDL files consist of an optional sequence of import statements, followed by a single namespace declaration.
import "mps-idl-filename"; import "../other-mps-idl-filename";
namespace SomeNamespace { ... }
Comments may be single-line, a la C++ or Java:
// single-line comment
or multi-line, like C:
/* this is a multi-line comment */
Comments may be inserted at any point in the file.
import "filename";
Imports are used to allow complex interface and data-structure definitions to be split across multiple MPS IDL files (and therefore across compilation units in languages like C++).
When code is generated from an MPS IDL file, the only code that is generated is for the namespace specified in the file named on the command-line. Namespaces declared in imported files do not have code generated for them. You will have to specify the imported MPS IDL files on the command-line of a separate invocation of the mpsidl compiler.
namespace NamespaceName { // Namespace contents go here ... }
Namespaces help avoid problems where two separate meanings are attached to the same name. Namespaces are hierarchic - you can nest a namespace within a namespace. All identifiers in an MPS IDL file are bound within a namespace.
2.5 The Scope Override Operator
Take this fragment of MPS IDL:
namespace foo { namespace bar { namespace baz { struct quux { int blort; } } interface zot { quux myfunction(); int myotherfunction(quux q); } } }
Note that there is a reference to type "quux" as the result-type of function zot::myfunction(). This code, as it stands, will not compile, because "quux" is not defined at the same scoping level as zot::myfunction(). What you need to do is use the scope override operator:
... interface zot { baz::quux myfunction(); int myotherfunction(foo::bar::baz::quux q); } ...
which lets the compiler figure out which instance of "quux" you are talking about. The scope override operator, "::", works much the same way as in C++, and is similar to fully specifying the name of a class in Java. Note that the two ways of referring to "quux" - absolute (starting with "foo") and relative (starting with "baz") - are interchangeable in this context.
struct StructName { // Member variable definitions go here; for example... int member1; string member2; OtherStruct [] member3; Interface member4; }
Structures are used in MPS IDL to transfer a collection of information of various types using a single object. They are immutable objects: once created, there is no way to alter the values of any of the member variables. They are also behaviour-free: they are pure data. Contrast this with interfaces, which have behaviour but no state/data.
interface InterfaceName { // Member function definitions go here; for example int myFunction(string foo); OtherInterface myFactoryMethod(); StructureType myResultGatheringMethod(int searchTerm); }
interface Extension extends InterfaceName { string anotherFunction(StructureType [] someValues); }
Interfaces are used to define the functions that remote objects can perform. They are extensible - MPS IDL provides type inheritance (but not code inheritance - that is up to the implementation language). All data types are legal as parameters and return types.
MPS IDL supports a few basic primitive types, plus arrays of other types (including arrays, structures, and interfaces), user-defined structures, and user-defined interfaces.
The primitive types MPS IDL supports are:
Variable declarations are used to define the body of structures. They are very similar to C or Java-style variable declarations, with the base type written first, followed by optional pairs of brackets ('[]') to denote (nested) arrays, and the variable name written last.
Method declarations follow the C and Java pattern as well, with the return-type listed first, followed by the method name, followed by a parameter list enclosed in parentheses and separated by commas.
int x; float y; string z; float [] vec; float [][] matrix; string [] names;
float pow10(float x); string concat(string a, string b); string repeat(string x, int numberOfRepeats); int[] addScalar(int[] a, int b);