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, END, ENUMERATION, EXCEPTION, FALSE, FROM, FUNCTIONAL, IMPORTS, IN, INOUT, INTEGER, INTERFACE, LIMIT, LONG, METHODS, OBJECT, OF, OPTIONAL, 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, but is reserved for the definition of "optional" record fields and method arguments; 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 = [ discriminator-type ] UNION types... END ;

where types is a comma-separated list of type specification of the form:

[ union-case-name : ] type-name [ = value-list END ]

where value-list is a comma-separated list of constant values of the type specified by discriminator-type, if that is present, or of type SHORT INTEGER, if no discriminator-type is specified. If a discriminator-type is specified, a value-list must be specified for each case type of the union, except optionally for exactly one of the union's case types, which then becomes the default case type of the union for any values of the discriminator-type not explicitly assigned to one of the union's case types. A simple example:

TYPE StringOrInt = UNION ilu.CString, CARDINAL END;

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

TYPE ColorType = ENUMERATION RGB, CMY, HSV, YIQ, HLS END;
TYPE U2 = ColorType
  UNION
    rgb-field : RGBObject = RGB END,
    others : COLORObject
  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 (illegal in ISL) declaration

TYPE type-name = UNION base-type-reference, NULL END ;

The difference is that OPTIONAL types are logically un-tagged; there is 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 ]
                        [ DOCUMENTATION documentation-string ]
                        [ COLLECTIBLE ]
                        [ AUTHENTICATION authentication-type ]
                        [ SUPERTYPES supertype-list END ]
                        [ METHODS method-list... END ]
                        [ BRAND string-constant ] ;

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.

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.

The authentication-type field of an object type definition defines which type of authentication is used to verify that a method can be handled. The supported values for this field are "SunRPCUNIX", "SunRPCDES", and "Kerberos5".

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

The method-list is a comma-separated list of procedure descriptions. All the methods of an object type have distinct names. This will be true even when we add multiple inheritance; this means independently-developed supertypes might not be usable together.

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.

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;

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-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.

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 - 65535, and must be unique within an interface. This is mainly supplied to allow description of existing services as object types.

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.

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 RPC life, 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.

Name mapping for target languages

This section outlines a proposal for name mappings and restrictions; this proposal is not yet accepted. (Thanks to external standards such as CORBA, this proposal cannot be implemented for some languages, such as ANSI C.)

This proposal is about how to name things in the various programming languages, in a way that avoids name clashes. It imposes no restrictions on the ISL source. However, the mappings will be more straightforward if the ISL source avoids two things: (1) two or more concsecutive hyphens in a name, and (2) starting an interface or type name with "ilu-" (in any casing).

The first step in mapping an ISL to a programming language is to scan type and interface names for the substring "ilu-" (in any casing); wherever it occurs, we insert a trailing digit zero.

In a similar way, we next scan the name for sequences of hyphens. Wherever two or more hyphens appear consecutively, the digit zero (`0') is inserted after every other one, starting with inserting a zero after the second hyphen.

The following steps assume the first two steps have already been done.

Where tuples <N1, N2, ... Nk> of ISL names must be mapped into a flat programming namespace, we concatenate the ISL names, with a double hyphen ("--") inserted between each.

Where ISL names (or tuples thereof) must be mapped, together with ILU-chosen names derived from the ISL names, into a flat programming namespace, the derived names begin with fixed strings specific to the derivation, where the fixed strings begin with "ilu-" (with any case), and a double hyphen is inserted between the fixed string and the ISL name.

Where ISL names (or tuples thereof), and possibly ILU-chosen names derived from the ISL names, must be mapped, together with a fixed set of ILU-chosen names, into a flat programming namespace, the fixed ILU-chosen names begin with "ilu-" (with any case) and do not include a double hyphen.

The final step is to translate hyphens to underscores, for programming languages that accept underscores but not hyphens in names.

Following is a specification of how names are mapped in each language. The notation "[N]" is used to denote the application of the first two steps and the last step. Examples of "[..]" are:

[Foo] => Foo
[foo-bar] => foo-bar
[wait----for--it-] => wait--0--0for--0-it-
[iluminate] => iluminate
[ilu--uli] => ilu-0--0uli
The mappings also use the notation "[[..]]" to denote the mapping of a type-reference.

C mapping

[ This mapping, while clean, will never be adopted because of the more problematic mapping specified by the OMG's CORBA document. ]

Item N from interface I is mapped to [I]__[N]. [[I.N]] = [I]__[N]; [[N]] = [I]__[N], where I is the current interface.

An enumerated value named V, of type T in interface I is mapped to [I]__[T]__[V].

A declaration of a record type T in interface I with fields F1:TR1, ... Fn:TRn is mapped to

typedef struct {[[TR1]] F1; ... [[TRn]] Fn} [I]__[T];

A declaration of a union type T in interface I of types TR1, ... TRn is mapped to

typedef enum {[[I.T]]__[[TR1]], ... [[I.T]]__[[TRn]]} ilu_tags__[[I.T]];
typedef struct {ilu_tags__[[I.T]] tag;
    union {
        [[TR1]] [[TR1]];
        ...
        [[TRn]] [[TRn]];
    } val;
} [[I.T]];

For passing exceptions through the method calls in interface I, the following auxiliary declaration is generated (supposing exceptions ER1:TR1, ... ER2:TR2 are raised):

typedef struct {
    ilu_Exception returnCode;
    union {
        [[TR1]] [[ER1]];
        ...
        [[TRn]] [[ERn]];
    } val;
} ilu_Status__[I];

An object type named T in interface I with methods M1, ... Mn maps to

typedef ilu_Ojbect [[I.T]];
[result-type-1] [I]__[T]__[M1]([[I.T]] ilu_self,
    [[arg-type-1-1]] [arg-name-1-1], ...
    [[arg-type-1-k]] [arg-name-1-k]);
...

C++ mapping

Item N from interface I is mapped to [I]__[N]. [[I.N]] = [I]__[N]; [[N]] = [I]__[N], where I is the current interface.

A declaration of an enumerated type named T in interface I containing values V1, ... Vn is mapped to typedef enum {[V1], ... [Vn]} [I]__[T].

Record and union declarations are mapped as for C. The exception status declaration is as for C.

Modula-3 mapping

ILU interface I is mapped to Modula-3 interface [I]; within an interface, item N is mapped to item [N]. [[I.N]] = [I].[N]; [[N]] = [N]. For Modula-3, we also use the notation "((a type-reference))", defined by ((N)) = [N] and ((I.N)) = [I]__[N].

A declaration of a record type T in interface I with fields F1:TR1, ... Fn:TRn is mapped to

TYPE [T] =  RECORD F1: [[TR1]]; ... Fn: [[TRn]]; END;

A declaration of a union type T in interface I of types TR1, ... TRn is mapped to

TYPE [T] = BRANDED OBJECT END;
TYPE [T]__((TR1)) = [T] BRANDED OBJECT v: [[TR1]] END;
...
TYPE [T]__((TRn)) = [T] BRANDED OBJECT v: [[TRn]] END;

A declaration of an enumerated type named T in interface I containing values V1, ... Vn is mapped to TYPE [T] = {[V1], ... [Vn]};

The type Ilu.Object has slots named ilu_is_surrogate, Ilu_Get_Server, Ilu_Get_Type, and Ilu_Close_Surrogate. Method names M are translated to [M]. For each object type T, the Modula-3 interface includes auxiliary procedures named Ilu_Sbh_Import__[T], Ilu_Name_Import__[T], and Ilu_Get_Type__[T].

CommonLisp mapping

Names from interface specifications are transformed into Lisp names (case-insensitive) by inserting hyphens at lower-to-upper case transitions. Hyphens that are already present are maintained as is. (Note: this causes problems; the ISL names "FooBar" and "foo-bar" map to the same Common Lisp name.)

A separate package is defined for each interface with defpackage. The name of this package is taken from the name of the interface. This package uses the packages common-lisp and ilu. The names of all entities defined in the ISL are exported from the package, including types, classes, constants, accessors, type predicates, generic functions, exceptions, etc. Such symbols are also shadowed, to avoid conflicts with used packages. For example, given the following interface:

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 = ??? "=" 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"
                | "COLLECTIBLE"
                | "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

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.