Oberon || Compiler & Tools || Library || Module Index || Search Engine


Ulm's Oberon System:
genrem


NAME

genrem - generate RemoteObjects provider modules

SYNOPSIS

genrem abstraction

DESCRIPTION

Objects which are to be accessed via specific abstractions remotely need the support of a specific module which acts as a service provider (see Services and RemoteObjects). In most cases, this module can be more or less directly derived from the definition of the abstraction. Regrettably, lots of declarations and code are to be given for each of the operations of the abstraction and some care must be taken to avoid typing errors. genrem allows to generate such service providing modules from an abstraction definition (syntax see below) for the most common cases.

Restrictions

genrem imposes several restrictions on the abstraction:

Defining an Abstraction

Regrettably, the definition module of an abstraction is not sufficient to describe some of the semantics which are needed in the context of RemoteObjects. Therefore, a special syntax was defined which looks a little bit strange because it was designed to allow an easy implementation (genrem is implemented as perl script).

The grammar is given in EBNF (extended Backus-Naur form), nonterminals are set in italic, terminals either in double quotes or in bold (in case of keywords):

Abstraction =      AnyText "\n%%\n" TypeDecl [ProxyParams]
                   { Operation } "\n%%\n" AnyText .
ProxyParams =      PROXY PARAMS "\n" { ProxyParameter } .
ProxyParameter =   TransferType Identifier Type "\n" .
TransferType =     REF | COPY | COPYORNIL | LCOPY | BaseType .
TypeDecl =         [PERSISTENT] (SERIAL|PARALLEL) TYPE QualIdent "\n" .
Operation =        OPERATION Identifier "\n" { Parameter } .
Parameter =        ParamType Identifier Type "\n" .
ParamType =        (IN | OUT | INOUT | RVAL) TransferType |
                   RETURNS SUCCESS .
BaseType =         (* one of the basetypes which are supported by NetIO *)
Type =             Identifier | QualIdent | ARRAY OF CHAR .
AnyText =          (* Oberon declarations *) .

The text which defines an abstraction is divided into three parts which are separated by lines which just contain a %%-token (this construct is similar to yacc(1) and lex(1)). The first and the last part are plain Oberon text which are taken as provided without further checking. The part in the middle defines the abstraction and is replaced by the Oberon declarations and procedures which are generated by genrem. Note that (unlike as in Oberon) newlines are not considered as white space but as separators (as defined by the grammar).

The abstraction is defined by the type of the objects we are operating on together with some parameters of RemoteObjects (TypeDecl and ProxyParams) which is followed by an unlimited number of operations (Operation).

The optional keyword PERSISTENT must be given if the type is an extension of PersistentDisciplines.Object (extensions just of PersistentObjects.Object are not useful in this context). The selection SERIAL or PARALLEL is mapped to the associated access mode RemoteObjects.AccessMode of RemoteObjects. The type itself is specified by the qualified type identifier which is imported from the module which defines the abstraction. RemoteObjects allows the creation of proxy objects to be parameterized by the supporting module. These parameters may be optionally given as ProxyParams.

The rest of this section specifies the operations of the abstraction together with their parameters. The names of the operations are to be given as simple identifiers (not qualified) and must match exactly the name of the operation in the module which defines the abstraction. Each parameter is to be given on an extra line and is specified by its parameter type, its transfer type, its identifier, and its Oberon type. Note that the order of parameters is significant and must match exactly the order of the formal parameters of the associated procedure.

Oberon supports two parameter types: call by value and call by reference (so-called VAR-parameters). The semantics of call by reference cannot be hold in full for operations across address space boundaries. A substitute for this technique is call by value return where, on invocation, a copy is made of the parameter (as in the case of call by value), and, on returning, a copy of the possibly updated value of this parameter is assigned back to the actual parameter.

Following parameter types are supported by genrem:

IN      call by value
INOUT   call by value return
OUT     like call by value return but just returning a value
RVAL    like OUT but as RETURN-value
Note that the return value is handled like other parameters here except that it must be given as RVAL-parameter. Consequently, no more than one RVAL-parameter may be specified.

The transfer type specifies how values are to be converted into sequences of bytes. Currently, following transfer types are supported:

REF           RemoteObjects.Export  and  RemoteObjects.Import
COPY          PersistentObjects.Write  and  PersistentObjects.Read,
COPYORNIL     PersistentObjects.WriteObjectOrNIL and
              PersistentObjects.ReadObjectOrNIL
LCOPY         LinearizedStructures.Write  and  LinearizedStructures.Read
Byte          NetIO.WriteByte  and  NetIO.ReadByte
Char          NetIO.WriteChar  and  NetIO.ReadChar
Boolean       NetIO.WriteBoolean  and  NetIO.ReadBoolean
ShortInt      NetIO.WriteShortInt  and  NetIO.ReadShortInt
Integer       NetIO.WriteInteger  and  NetIO.ReadInteger
LongInt       NetIO.WriteLongInt  and  NetIO.ReadLongInt
Real          NetIO.WriteReal  and  NetIO.ReadReal
LongReal      NetIO.WriteLongReal  and  NetIO.ReadLongReal
Set           NetIO.WriteSet  and  NetIO.ReadSet
String        NetIO.WriteString  and  NetIO.ReadString
ConstString   NetIO.WriteConstString  and  NetIO.ReadConstString
Event         PersistentEvents.Write and PersistentEvents.Read

Instead of the parameter type and the transfer type it is possible to specify RETURNS SUCCESS in cases of a BOOLEAN-valued RETURN-type.

The Oberon type of the parameter may be given as type identifier (for one of the Oberon base types) or as qualified identifier (if it is defined elsewhere) or as ARRAY OF CHAR for strings.

Abstraction-specific Oberon Code

The header and trailer of the resulting module are to be given in Oberon in the first and third part of the input file which is given to genrem. The header contains the module declaration (i.e. MODULE, followed by the name of the module) and the import list. The import list not only covers all modules mentioned in the definition of the abstraction but also has to give all other modules which are used by the text which is generated by genrem:

The trailer must contain

CreateProxy needs and is allowed to use the Services.Type of the proxy object type type and the filled interface record if. Both variables are global and initialized by InitIFs. Further, CreateProxy should call RelatedEvents.QueueEvents for the created proxy object.

If proxy parameters were specified by PROXY PARAMS, genrem generates a type declaration for ProxyParameters and the necessary interface procedures for PersistentObjects. Note that CreateProxyParams should be used then to create the proxy parameter object in GetParams.

In some cases it may be necessary to add more declarations and procedures to the resulting module text. Additional declarations with the exception of procedures may be given in the header. Further procedures may be added to trailer but not to the header.

Invocation

genrem reads the abstraction (in the syntax as given above) from the given file and generates the module (not the definition) on standard output. Usually, the file name of the abstraction is (with suffix ``.rm'') named after the service providing module which, in turn, is by convention named RemoteXXX is the module which defines the abstraction is called XXX. Therefore, the typical invocation of genrem looks like:
genrem RemoteXXX.rm >RemoteXXX.om
This rule can be easily put into makefiles for GNU-make (see mmo):
GenremSrc :=            $(shell echo *.rm)
GenremTargets :=        $(patsubst %.rm,%.om,$(GenremSrc))
$(GenremTargets):       %.om: %.rm
                        genrem $^ >$@

EXAMPLE

Following definition defines an abstraction for integer sequences:
DEFINITION IntSequences;

   IMPORT Objects, Services;

   TYPE
      IntSequence = POINTER TO IntSequenceRec;
      IntSequenceRec = RECORD (Services.ObjectRec) END;

      NextProc = PROCEDURE (is: IntSequence) : INTEGER;
      SkipProc = PROCEDURE (is: IntSequence; positions: INTEGER);
      RewindProc = PROCEDURE (is: IntSequence);
      Interface = POINTER TO InterfaceRec;
      InterfaceRec =
         RECORD
            (Objects.ObjectRec)
            next: NextProc;
            skip: SkipProc;
            rewind: RewindProc;
         END;

   PROCEDURE Next(is: IntSequence) : INTEGER;
   PROCEDURE Skip(is: IntSequence; positions: INTEGER);
   PROCEDURE Rewind(is: IntSequence);

   PROCEDURE Init(is: IntSequence; if: Interface);

END IntSequences.
An associated input file for genrem:
MODULE RemoteIntSequences;

   IMPORT IntSequences, Messages, NetIO, RelatedEvents, RemoteObjects,
      Services, Streams;

%%

   SERIAL TYPE IntSequences.IntSequence

   OPERATION Next
      RVAL Integer nextval INTEGER

   OPERATION Skip
      IN Integer positions INTEGER

   OPERATION Rewind

%%

   PROCEDURE CreateProxy(VAR object: Services.Object;
                         params: PersistentObjects.Object);
      VAR
         is: IntSequence;
   BEGIN
      NEW(is);
      Services.Init(is, type);
      IntSequences.Init(is, if);
      RelatedEvents.QueueEvents(is);
      object := is;
   END CreateProxy;

BEGIN
   InitIFs;
END RemoteIntSequences.

DIAGNOSTICS

genrem generates only some error messages on its own -- most errors are detected and reported by the Oberon compiler. The output of genrem is rather readable and should allow to track errors back to the original source.

Following error messages may be generated by genrem:

No %% seen
This indicates that the %%-token which separates the three parts are missing.
2nd %% missing
Just one %%-token was found but no second.
TYPE declaration expected
The declaration of the object type of the abstraction is missing.
only one TYPE declaration expected
Exactly one type declaration of the object type of the abstraction is to be given.
only one PROXY PARAMS declaration expected
Only one proxy parameter section may be given.
PROXY PARAMS must be given before OPERATION-declarations
A proxy parameter section was given after the definition of operations.
qualified identifier expected
A simple or invalid identifier was given where a qualified identifier was expected. Note that, for example, the object type of the abstraction must be given as qualified identifier.
invalid qualified identifier
An invalid identifier was found.
unknown transfer type
A transfer type was given which is not mentioned in the table above.
OPERATION Identifier expected
The name of the operation is missing.
more than one return construct
Just one return construct may be given per operation.
valid declaration expected
Catch-all message for all other problems to understand a declaration.

SEE ALSO

oc
Oberon Compiler
RemoteObjects
remote object invocations
RemotePersistentObjects
delegates operations of PersistentObjects

BUGS

Only up to 1023 characters are transferred of parameters which are specified as ARRAY OF CHAR. If the given character array is longer it gets silently truncated.
Edited by: borchert, last change: 1998/02/03, revision: 1.5, converted to HTML: 2000/03/29

Oberon || Compiler & Tools || Library || Module Index || Search Engine