Go to the previous, next section.

The ISL Interface Language

We define an interface language called ISL (for Interface Specification Language), to describe ILU interfaces. This document describes the syntax and semantics of this language.

General Syntax

The conventional file suffix for ISL files is `.isl'. Some of the ILU tools rely on the name of the file being the same as the name of the interface defined in it, and rely on having only one interface defined in each `.isl' file.

An ISL interface contains four kinds of statements: the interface header, type declarations, exception declarations, and constant declarations. Each statement is terminated with a semi-colon.

Many statements in ISL contain lists: lists of the fields in a record, the types in a union, the methods in an object type. All lists in ISL are terminated with an END keyword, and the items in the list are separated by commas.

Comments may be placed in an ISL file. They are introduced with the character sequence (*, and terminated with *). Comments nest.

Identifiers

All identifiers that appear in ISL are alphanumeric, begin with an alphabetic character, and may contain hyphens.(2) Differences in case are not sufficient to distinguish between two identifiers; however, the case of an identifier may be preserved in its mapping to a specific programming language.

All ILU type names, exception names, and constant names have two parts, an interface identifier and a local identifier. When writing the full name, the interface identifier comes first, followed by a period, followed by the local identifier. If the interface identifier is ommitted in a name, it defaults to the interface identifier of the most recently encountered interface header.

Interface names, type names, exception names, and constant names occur in different name spaces. Thus is is possible to have a type and an exception with the same name.(3)

Reserved Words

The following words are reserved words in ISL: ARRAY, ASYNCHRONOUS, AUTHENTICATION, BOOLEAN, BRAND, CARDINAL, CHARACTER, CLASS, COLLECTIBLE, CONSTANT, DEFAULT, END, ENUMERATION, EXCEPTION, FALSE, FROM, FUNCTIONAL, IMPORTS, IN, INOUT, INTEGER, INTERFACE, LIMIT, LONG, METHODS, OBJECT, OF, OPTIONAL, OTHERS, OUT, RAISES, REAL, RECORD, SEQUENCE, SHORT, SIBLING, SINGLETON, SINK, SOURCE, SUPERCLASS, SUPERCLASSES, SUPERTYPES, TRUE, TYPE, UNION.

Reserved words may be used as identifiers, by placing them in double quotes, but may not be used as identifiers without quoting.

Other identifiers are worth avoiding, as they may cause problems with specific language implementations. The identifier t or T, for instance, causes problems with Common Lisp. Language-specific mappings of ISL should try to avoid these problems.

Statement Syntax

The Interface Header

Each interface is introduced with exactly one interface header of the form

INTERFACE interface-name [ BRAND brand ] [ IMPORTS list-of-imported-interfaces END ] ;

The interface-name is used by various language-specific productions to create name spaces in which the types, exceptions, and constants defined in the interface are declared. The optional list-of-imported-interfaces is a comma-separated list of fields, each of the form

interface-name [ FROM interface-file ]

where interface-file is the typical poorly defined string that names a file for your operating system (in our case, UNIX). Importing an interface allows the current interface to mention the types, exceptions, and constants defined in the imported interface, by referring to them as

interface-name.type-or-value-name

If the optional "FROM interface-file" is not specified for an imported interface, a sensible site-dependent search policy is followed in an attempt to locate that interface, typically looking down a path (environment variable ILUPATH on POSIX systems) of directories for a file with the name `interface-name.isl'.

Type Declarations

In general, a type is defined with a statement of the form

TYPE type-name = type-reference | construction ;

The form TYPE type-name = type-reference is used when you want to rename an existing type to make its usage clear or give it a name in the current interface. A type-reference is just a type-name, or a reference to a type name defined in another interface: interface-name.type-name.

Primitive types

The following type "names" are pre-defined:

There is also a special type NULL, which cannot be used directly; it has a single value, NULL.

Constructor overview

The form TYPE type-name = construction is used when a user needs to define a new type. Several simple constructors for more complex data types are specified:

In addition, the automatically-imported interface ILU defines the short sequence CString of short character.

Array Declarations

An ARRAY is a fixed-length N-dimensional array of some type. It is defined with a declaration of the form

TYPE type-name = ARRAY OF dimension-list base-type-reference ;

where dimension-list is a comma-separated list of non-negative integers, each integer specifying the size of a dimension of the array, and base-type-reference is a type-reference to some other ILU type. For example,

TYPE SymbolTable = ARRAY OF 400 Symbol;
TYPE Matrix3030 = ARRAY OF 30, 30 REAL;

The total number of elements in the array may not exceed 4294967295 (2^32-1).

Sequence Declarations

A sequence is a variable-length one-dimensional array of some type. It is defined with a declaration of the form

TYPE type-name = [ SHORT ] SEQUENCE OF base-type-reference [ LIMIT size ] ;

where base-type-reference is a type-reference to some other ILU type. If the LIMIT parameter size is used, it limits the sequences to having at most size elements; otherwise the sequences are limited to having at most 4294967295 (2^32-1) elements. Use of the SHORT modifier is shorthand for a LIMIT of 65535 (2^16-1). Use of the LONG modifier is not defined for sequences.

Generalized Array Declarations

This is a proposed language change, not yet accepted.

The existing language has a weakness: it cannot express coordinated multidimensional variable-length arrays. Coordinated means that there is only one length per dimension, regardless of how many arrays there are at that level. An example is a bitmap of variable height and width: all rows are the same length, and all columns are the same length.

A generalized array type is defined with a declaration of the form

TYPE type-name = ARRAY dim , ... dim OF base-type-reference ;
where each dim is of the form
length | [ LIMIT maxlen | SHORT ]

A dimension can be given a fixed length by simply specifying that length. A variable-length dimension is either left blank (meaning the maximum length is 2**32-1), specified as SHORT (meaning the maximum length is 2**16-1), or given an explicit maximum length.

Note that putting the dimensions after the OF would create a syntactic ambiguity in some cases, concerning grouping of a SHORT.

Record Declarations

TYPE type-name = RECORD fields... END ;

where fields is a comma-separated list of field, which has the form

field-name : field-type-reference

A sample record declaration:

TYPE Symbol = RECORD
  name : string,
  type : TypeInfo,
  address : cardinal
END;

Union Declarations

A union is a type which may take on values of several different types. To be compliant with the CORBA notion of unions, the union declaration is much more baroque and complicated than it really should be. The declaration has the form:

TYPE type-name = [ tag-type ] UNION arm-list END [ OTHERS ] ;

where arm-list is a comma-separated list of arm, each of the form:

[ union-case-name : ] type-name [ arm-valuator ]

where each arm-valuator is either of the form

= DEFAULT
or of the form
= value-list END
and where a value-list is a comma-separated list of constant values of the tag type. The tag type must be one of: SHORT INTEGER, SHORT CARDINAL, INTEGER, CARDINAL, BYTE, BOOLEAN, or an enumerated type. (We should also allow SHORT CHARACTER and CHARACTER.) The tag type is SHORT INTEGER if not explicitly specified.

A arm-valuator must be given for either all or none of the arms; if none, the arms are assigned single integral values, starting with 0. arm-valuators must be given if the tag type isn't numeric. All the values appearing in the value-lists of a union must be different from one another. DEFAULT can appear in at most one arm of a union type construction. DEFAULT and OTHERS cannot both appear in the same union.

A union value consists of a tag value, possibly paired with a second value. When the tag value is one that appears in, or is implicitly assigned to, an arm of the union type construction, the second value is of the type named in that arm. Otherwise, the union value is well-formed only if DEFAULT or OTHERS appears in the union type construction. If an arm is valued with DEFAULT, the second value is of that arm's type. If OTHERS appears, there is no second value; it is as if there were a default arm of some trivial type (like C's void or ML's unit).

A simple example:

TYPE StringOrInt = UNION ilu.CString, CARDINAL END;

A more complex example, that uses an explicit tag type, union case names, and a default arm:

TYPE ColorType = ENUMERATION RGB, CMY, HSV, YIQ, HLS END;
TYPE U2 = ColorType
  UNION
    rgb-field : RGBObject = RGB END,
    others : COLORObject = DEFAULT
  END;

The union case name is not guaranteed to be present in language-specific mappings.

ISL unions are logically (and sometimes actually, depending on the programming language) tagged. There is a difference between

TYPE T1 = UNION Bar, Baz END;
TYPE T2 = UNION Foo, T1 END;
and
TYPE T1 = UNION Bar, Baz END;
TYPE T2 = UNION Foo, Bar, Baz END;

Optional Declarations

A variable of type OPTIONAL Foo can have either a value of Foo or of type NULL. It is declared with the form

TYPE type-name = OPTIONAL base-type-reference ;

This should be thought of as roughly equivalent to the declaration

TYPE type-name = BOOLEAN UNION base-type-reference = TRUE END END OTHERS ;

The difference is that OPTIONAL types are logically un-tagged. An optional value is not a pair of (BOOLEAN, base-type-reference); rather it is a single value, either a special, distinguished, "null" value or a value of the base-type-reference. There is thus no difference between

TYPE Bar = OPTIONAL Foo;
TYPE Baz = OPTIONAL Bar;
and
TYPE Bar = OPTIONAL Foo;
TYPE Baz = OPTIONAL Foo;

Enumeration Declarations

An enumeration is an abstract type whose values are explicitly enumerated. It is declared with the form

TYPE type-name = ENUMERATION values... END ;

where values is a comma-separated list of value names, with optional value ID's that are constants of type SHORT CARDINAL that specify the value used to represent the enumeration value "on the wire".(4) Use of value ID's is deprecated.

value-name [ = value-id ]

For example,

TYPE TapeAction = ENUMERATION
  SkipRecord = 1,
  Rewind = 23,
  Backspace = 49,
  WriteEOF = 0
END;

All value-names and value-IDs must be unique within an enumeration. If value-IDs are not assigned explicitly, appropriate values will be assigned automatically in some unspecified way. An enumeration may have at most 65535 (2^16-1) values.

Object Type Declarations

Object types are described in the following way:

TYPE type-name = OBJECT
                        [ SINGLETON protocol-description-string ]
                        [ DOCUMENTATION documentation-string ]
                        [ COLLECTIBLE ]
                        [ OPTIONAL ]
                        [ AUTHENTICATION authentication-type ]
                        [ SUPERTYPES supertype-list END ]
                        [ METHODS method-list... END ]
                        [ BRAND string-constant ] ;

The keyword CLASS is a deprecated synonym for OBJECT, and SUPERCLASSES is a deprecated synonym for SUPERTYPES. Also,

                        [ SUPERCLASS supertype-name ]
is a deprecated equivalent to
                        [ SUPERTYPES supertype-name END ]

The SINGLETON keyword specifies that instances of this type are singleton servers, and implies that the discriminator object (the subject of the call) should not be implicitly marshalled as the first argument in an RPC. This is typically used in describing an instance of an existing RPC service, which is to be modelled in ILU. The argument to SINGLETON is a string in the form of ILU "protocol-info", which specifies particular protocol-specific parameters to be used in implementing this object type `on the wire'. For example, the Sun RPC calendar manager would use a protocol-description-string of "sunrpc_2_100068_3", indicating that it uses a Sun RPC program number of 100068 and a Sun RPC version of 3.

The optional documentation-string is a quoted string, which is passed on to language-specific bindings where possible, such as with the doc-string capability in Common Lisp.

The COLLECTIBLE keyword specifies that instances of this type are meant to be garbage collectible, and that methods necessary for this should be automatically added to its method suite. For an object type to be collectible, all ancestor object types must also be collectible.

The OPTIONAL keyword specifies that the language-specific nil value may be passed, instead of an instance of this object type, anywhere this object type is used. This is a CORBA mis-feature, and its use is strongly deprecated. Better to explicitly use a different type constructed with the ILU OPTIONAL keyword.

The authentication-type field of an object type definition defines which type of authentication is used to verify that a method can be handled. No values for this field are currently supported.

The optional supertype-list defines an inheritance relationship between the object types named in the list and the type type-name.

The string-constant in the BRAND clause, if any, contributes an arbitrary tag to the structure of the type; omitting the BRAND clause is equivalent to giving one with the empty string. Branding gives the programmer a way to make two types distinct despite their otherwise having the same structure. See an earlier subsection for more details.

The method-list is a comma-separated list of procedure descriptions. All the methods of an object type have distinct names. This means that independently-developed supertypes might not be usable together.

Methods have the syntax:

[ FUNCTIONAL ] [ ASYNCHRONOUS ] method-name ( [ args... ] )
        [ : return-type-reference ]
        [ RAISES exceptions... END ]
        [ = procedure-id ]
        [ documentation-string ]

where the discriminator (the implicit first argument to the method, the subject of the call, an instance of the object type in question) is not explicitly listed in the signature. Each method has zero or more arguments in a comma-separated list, each element of which is a colon-separated two-ple

[ argument-direction ] argument-name : [ SIBLING ] argument-type-reference
The SIBLING keyword may only appear on arguments of an object type, to indicate that the argument should be a sibling object to the discriminator of the method. The FUNCTIONAL keyword indicates that the method, for a given set of arguments, is idempotent (i.e., the side effects of one call are the same as the side effects of more than one call) and will always return the same result (or raise the same exception); this information may be used for caching of return values in the client side stubs. The optional argument-direction information is one of the three keywords IN, OUT, INOUT, specifying whether the parameter is being used as an input parameter, an output parameter, or both.

A method return type is allowed (again separated from the procedure argument list by a colon), and a list of possible exceptions may be specified as a comma-separated list of exception names, bracketed with the keywords RAISES and END.

The optional procedure-id field allows a service description to specify the procedure code that is used in the RPC request packet for this method. Procedure ID's are restricted to the range [0,65279], and must be unique within an interface. This may only be used in methods on objects marked with the SINGLETON attribute.

If a method is marked with the ASYNCHRONOUS keyword and does not return a value or raise an exception, the RPC method call of a surrogate instance will return after sending the request packet to the RPC partner, as the success of the call does not depend on the completion of the associated code. Other RPC methods will block in such a way as to allow the scheduler to handle other events while it is waiting for the call to complete, if the user has registered the appropriate scheduler hooks with the ILU runtime.

The optional documentation-string is a quoted string, which is passed on to language bindings for which it is meaningful, such as the doc-string capability in Common Lisp.

For example:

TYPE FancyString = OBJECT
  METHODS
    FUNCTIONAL Length () : cardinal,
    Substring (start : cardinal, end : cardinal) : string
      RAISES StartGreaterThanEnd, StartTooLarge, EndTooLarge END,
    Char (index : cardinal) : character
      RAISES BadIndex END
  END;

Note that the object language in ILU is not intended to be used to fully define an object type, but rather to describe it in a simple language that can be transformed into the different object type definition systems of several other languages.

Exception Declarations

Exceptions in ILU are raised by ILU methods. They allow error conditions to be signalled back to the calling code. They are declared with a statement of the form:

EXCEPTION exception-name [ : type-reference ] [ documentation-string ] ;

The optional type-reference part of the declaration allows the exception to have an associated value, to be used in interpretation of the exception. For example, an exception BadFilename might have the type ilu.CString, so that the actual bad filename can be associated with the exception:

The optional documentation-string is a quoted string, which is passed on to language bindings for which it is meaningful, such as the doc-string capability in Common Lisp.

TYPE Filename = ilu.CString;
EXCEPTION BadFileName : Filename "The value is the bad filename";

Because of the uncertain nature of life in distributed systems, the pre-defined exception ilu.ProtocolError (defined in the ILU interface) may be raised by any method, to indicate that the method could not be handled, for some reason. It has the following form:

TYPE ProtocolErrorDetail = ENUMERATION
                NoSuchClassAtServer = 1,
                BrandMismatch = 2,
                NoSuchMethodOnClass = 3,
                InvalidArguments = 4,
                UnknownObjectInstance = 5,
                UnreachableModule = 6,
                RequestRejectedByModule = 7,
                TimeoutOnRequest = 8,
                UnknownError = 9
        END;

EXCEPTION ProtocolError : ProtocolErrorDetail;

Signalling of ProtocolError is never done by user-written server code; it is reserved to the transport and runtime layers of ILU.

Constant Declarations

For convenience of interface design, constant values for certain simple types may be defined in ISL with statements of the form

CONSTANT constant-name : constant-type = constant-value ;

Integer, Cardinal, and Byte Constants

A constant-value for types that are sub-types of INTEGER, CARDINAL, or BYTE is specified with the syntax

[ sign ] [ base-indicator ] digits

where the optional base-indicator allows selection of bases 2, 8, 10 or 16. It is a digit '0' (zero) followed by either the character 'B' for base 2, 'X' for base 16, 'O' (oh) for base 8, or 'D' for base 10. The sign is only valid for subtypes of INTEGER; it is either '+' or '-'; if not specified, '+' is assumed. The base-indicator and digits fields are case-insensitive.

Real Constants

A constant-value for subtypes of REAL has the syntax:

[ sign ] integer.fraction [ e exponent ]

where integer and fraction are sequences of decimal digits, sign is either '+' or '-' ('+' is the default), and exponent is the power of 10 which the rest of the value is multiplied by (defaults to 0).

ilu.CString Constants

A constant-value for a sub-type of ilu.CString has the form

"characters"

where characters are any ISO-Latin-1 characters except for 8_000. The escape character is defined to be '#' (hash). The escape character may occur in the string only in the following ways:

#" -- a single double-quote character
## -- a single escape character
#hex-digithex-digit -- the octet 16_hex-digithex-digit
#n -- newline
#r -- carriage return

Examples of Constants

CONSTANT Newline : byte = 10;
CONSTANT Pi : short real = 3.14159;
CONSTANT Big : long real = -1.1349e27;  (*  -1.1349 * 10**27   *)
TYPE Filename = ilu.CString;
CONSTANT MyLogin : Filename = "~/.login";
CONSTANT Prompt : ilu.CString = "OK#n ";
CONSTANT HeapBound : cardinal = 0xFFFF39a0;
CONSTANT Pattern1 : cardinal = 0b000001000001;

ilu.isl

The standard interface ilu can be found in the file `ILUHOME/interfaces/ilu.isl'; it is maintained as `ILUHOME/src/stubbers/parser/ilu.isl'. Here are its contents:

INTERFACE ilu BRAND "v1";

TYPE CString = SEQUENCE OF SHORT CHARACTER;

TYPE ProtocolErrorDetail =
  ENUMERATION
    NoSuchClassAtServer,     (* server doesn't handle specified class *)
    BrandMismatch,           (* versions out of sync *)
    NoSuchMethodOnClass,     (* invalid method, or method not implemented *)
    InvalidArguments,        (* bad arguments passed *)
    UnknownObjectInstance,   (* specified instance not on server *)
    UnreachableModule,       (* no path to handler *)
    RequestRejectedByModule, (* request not looked at, for some reason *)
    TimeoutOnRequest,        (* no response from server within timeout *)
    UnknownError             (* catchall error *)
  END;

EXCEPTION ProtocolError : ProtocolErrorDetail;

The declarations of ProtocolErrorDetail and ProtocolError don't belong here, and will be eliminated in favor of a reference manual section explaining the possible errors.

ISL Grammar

In this grammar, parentheses are used for grouping, vertical-bar indicates selection, braces indicated optionality, quotation marks indicate literal keywords or literal punctuation.

No whitespace is allowed between the parts of a radix, number, or quoted-string. Aside from that, whitespace is used to separate fields where necessary, and excess whitespace is ignored outside of quoted-strings.

Three primitives are used:

interface = interface-def | interface interface-def

interface-def = interface-declaration other-declarations

interface-declaration = "INTERFACE" name-string
                        [ "BRAND" brand-string ]
                        [ "IMPORTS" import-list "END" ]
                        ";"

import-name = name-string [ "FROM" filename ]

import-list = import-name | import-list "," import-name

other-declarations = constant-decl | exception-decl | type-decl

constant-decl = "CONSTANT" name-string ":" ( integer-const
                                           | cardinal-const
                                           | byte-const
                                           | float-const
                                           | string-const ) ";"

integer-const = [ "SHORT" | "LONG" ] "INTEGER" "=" [ sign ] number

cardinal-const = [ "SHORT" | "LONG" ] "CARDINAL" "=" number

byte-const = "BYTE" "=" number

float-const = [ "SHORT" | "LONG" ] "REAL" "="
              [sign] digits [ "." digits ] [ "e" digits ]

number = [ radix ] digits

radix = "0" ( binary | octal | hexadecimal )

binary = "b"

octal = "o"

hexadecimal = "x"

string-const = "ilu.CString" "=" quoted-string

exception-decl = "EXCEPTION" excp-name [ ":" type-name ] [ doc-string ] ";"

excp-name = name-string

type-decl = "TYPE" type-name "=" ( type-name | new-type-decl ) ";"

type-name = primitive-type-name | name-string

primitive-type-name = "BYTE"
                    | [ "SHORT" | "LONG" ] "CARDINAL"
                    | [ "SHORT" | "LONG" ] "INTEGER"
                    | [ "SHORT" | "LONG" ] "REAL"
                    | [ "SHORT" ] "CHARACTER"
                    | "BOOLEAN"

new-type-decl =   record-decl
                | array-decl
                | sequence-decl
                | union-decl
                | optional-decl
                | object-decl

record-decl = "RECORD" field-list "END"

field-list = field | field-list "," field

field = name-string ":" type-name

sequence-decl = [ "SHORT" ] "SEQUENCE" "OF" type-name [ "LIMIT" number ]

array-decl = "ARRAY" "OF" dimensions-list type-name

dimensions-list = number | dimensions-list "," number

union-decl = "UNION" field-list "END"

optional-decl = "OPTIONAL" type-name

object-decl = "OBJECT" object-attributes

object-attributes = object-feature | object-attributes object-feature

object-feature =  "SINGLETON" singleton-protocol-info
                | "COLLECTIBLE"
                | "OPTIONAL"
                | "DOCUMENTATION" doc-string
                | "AUTHORIZATION" auth-type
                | "BRAND" brand-string
                | "SUPERTYPES" supertype-list "END"
                | "METHODS" method-list "END"

supertype-list = type-name | supertype-list "," type-name

singleton-protocol-info = quoted-string

auth-type = quoted-string

method-list = method | method-list "," method

method = [ "FUNCTIONAL" | "ASYNCHRONOUS" ] name-string
         arguments [ ":" return-type ] [ "RAISES" exception-list "END"]
         [ doc-string ]

return-type = type-name

exception-list = excp-name | exception-list "," excp-name

arguments = "(" argument-list ")"

argument-list = argument | argument-list "," argument

argument = [ "IN" | "OUT" | "INOUT" ] name-string ":" [ "SIBLING" ] type-name

doc-string = quoted-string

quoted-string = "\"" string "\""

Go to the previous, next section.