The ModulaTor logo 7KB

The ModulaTor

Oberon-2 and Modula-2 Technical Publication

Ubaye's First Independent Modula-2 & Oberon-2 Journal! Nr. 1, Feb-1995


Using Coroutines in Oberon-2, Part I

by Günter Dotzel, ModulaWare

Some simple programming examples using coroutines are presented in Oberon-2 [Moes 93], [Poun 93], [Reis 92].

All examples were programmed by the author in Modula-2, before 1987. A. Schuhmacher transpiled the programs to Oberon-2, in Jan-1995.

The programs were compiled and tested on Alpha AXP/OpenVMS with ModulaWare's Oberon-2 compiler A2O. The Oberon-2 language does not feature coroutines. So the coroutine interface module is written in Modula-2, using ModulaWare's Modula-2 compiler MaX. It's implementation module contains two executable statements and is listed below.

The I/O modules used are from the ISO Modula-2 library [ISO 94]. The Oberon-2 interface definitions of the ISO Modula-2 library can be found in [Baum 92].

1. Odd word problem



MODULE OddWord; (* Uses coroutine programming technique to solve the Odd Word Problem (Dijkstra 1972): Remove blanks from input character stream and reverse the odd words (up to 'wl' character length); reverse word number 1,3,5,7... Reference: Martin, "Coroutines", Springer Verlag, New York GD: Sep-1982 (Modula-2 version) AS: Jan-1995 (Oberon-2 version) http://www.modulaware.com/ *) IMPORT SYSTEM, STextIO, CTR, M2C:=M2Coroutines; CONST cr=CHR(13); sp=' '; pnt='.'; wl=70; ll=78; TYPE wtype= ARRAY 1024 OF SYSTEM.WORD; VAR x: CHAR; cl, k: INTEGER; Word: ARRAY wl OF CHAR; W0, W1, W2: wtype (* work spaces *); PR, RWD, RSP, MAIN: M2C.PROCESS (* coroutines *); PROCEDURE PrintChar (c: CHAR); BEGIN STextIO.WriteChar(c); cl := cl MOD ll +1; END PrintChar; PROCEDURE ReadWord; BEGIN LOOP k:=0; WHILE (x # sp) & (x # pnt) & (x # cr) & (k < wl) DO INC(k); Word[k]:=x; STextIO.ReadChar(x); END; M2C.TRANSFER (RWD, PR); END; END ReadWord; PROCEDURE ReadSpace; BEGIN LOOP WHILE (x=sp) OR (x=cr) DO STextIO.ReadChar(x) END; IF x=pnt THEN PrintChar(pnt); M2C.TRANSFER( RSP, MAIN); (* EXIT *) ELSE PrintChar(sp); M2C.TRANSFER( RSP, RWD); END; END; END ReadSpace; PROCEDURE CheckLine; BEGIN IF (cl+k) >= ll THEN STextIO.WriteLn; cl:=0 END; END CheckLine; PROCEDURE Printer; VAR j: INTEGER; BEGIN LOOP CheckLine; FOR j := 1 TO k DO PrintChar(Word[j]) END; M2C.TRANSFER (PR, RSP); CheckLine; FOR j := k TO 1 BY -1 DO PrintChar(Word[j]) END; M2C.TRANSFER (PR, RSP); END; END Printer; PROCEDURE W(a: ARRAY OF CHAR); BEGIN STextIO.WriteLn; STextIO.WriteString(a); END W; BEGIN W('Odd Word Reversal Problem.'); W('The input text is echoed if a word is complete.'); W('Type words seperated by one ore more blanks and to exit type ".">'); W(""); STextIO.ReadChar(x); cl:=0; (* create coroutines (processes): *) M2C.NEWPROCESS( ReadWord, SYSTEM.ADR(W0), SIZE(wtype), RWD ); M2C.NEWPROCESS( ReadSpace, SYSTEM.ADR(W1), SIZE(wtype), RSP ); M2C.NEWPROCESS( Printer, SYSTEM.ADR(W2), SIZE(wtype), PR); M2C.TRANSFER( MAIN, RSP ); W("Final Point found. EXIT." ); W(""); END OddWord.

2. Grune's problem

Grune's problem is described in [Grun 77]: A process P1 copies characters from input to output with the proviso that where the input has "aa" the output will have "b" instead. And we have a similar process P2 which converts "bb" into "c". Now we want to connect these processes in series by feeding the output of P1 into P2.

This problem has been programmed by Lynning [Lynn 78] and Marlin [Marl 80] in different languages (Algol60, Simula, Algol68, ACL (A Coroutine Language), also see [Marl 80]).

The ACL version uses process instantiations with parameters for the converter. Here a modified version is shown, which uses modules and a more general method of supplying parameters to the instantiations P1 and P2. An array type of process elements is used.

There are three coroutines:

  1. 'InChar' reads the next character from input stream to 'ch'. It produces characters at level 0.
  2. 'Converter' has level 1 and upon request it receives from level 0 the input character 'ch'. It converts a double a in a single b.
  3. 'Converter' has level 2 and if it requires a new input character, it receives a character 'ch' from level 1. It converts a double b in a single c.
The level 2 is called from main process (level 3). After reactivation, it prints a character 'ch', produced by level 2.

The instantiations 1 and 2 obey the conversion rule parameter 'c1''c1' ---> 'c2'. A further instantation parameter (i,j), with i pointing to the next lower level and j pointing to the next higher level, is introduced:

i = PS [Level-1].P

j = PS [Level+1].P,

with 1 <= Level <= 2.

PS is an array of elements P of type PROCESS.

Instantiations are created with CREATE (Procedure, Level, c1, c2):

CREATE (Converter, 1, 'a', 'b');

CREATE (Converter, 2, 'b', 'c');

The workspace of the coroutines 'Converter' are now defined and initialized. The procedure code does exist in a single copy only.

Data stream example:

Input (Level 0) >ababababbbbbbxbaaaabxaaaaaabbaabbbx.

Output(Level 3) >abababacccxccxccccbx.

The following figure demonstrates the states of level 1 and level 2:


    +--<------------------------------<--+
    |                                    |
    |     c1             c1              |
    Q1 ---------> Q2 ----------> Q3/c2-->|
    \             |                      |
     \            |                      |
      \           |                      |
       \        Q4/c1                    |
        \         |                      |
         \        |                      |
          \       |                      |
           +--> Q5/cx ---------------->--+
Q1 is the state initial

Qm/cn means that a transition to state Qm will output "cn"

Level switching : Up/Down (TRANSFERUP/TRANSFERDOWN)

Q1: Down
Q2: Down
Q3: Up
Q4: Up
Q5: Up

Conversion: c1c1 ---> c2
cx ---> cx with x <> 1

The algorithm (see procedure Converter):

The variable 'ch' contains a input character after TRANSFERDOWN and a output character before TRANSFERUP.


  LOOP                                (*Init*)
    TRANSFERDOWN;                      (*Q1*)
    IF ch = c1 THEN
      TRANSFERDOWN;                    (*Q2*)
      IF ch = c1 THEN ch := c2
      ELSE 
        ch1 := ch;
        ch  := c1;
        TRANSFERUP;                    (*Q4*)
        ch  := ch1;
      END;
    END;
    TRANSFERUP;                     (*Q3*)(*Q5*)
  END;
Oberon-2 interface module to Modula-2 implementation of module M2COROUTINES:


MODULE M2COROUTINES; TYPE PROC * = PROCEDURE; PROCESS * = POINTER TO RECORD END; ADDRESS * = LONGINT; CARDINAL * = LONGINT; PROCEDURE NEWPROCESS * ( p: PROC; workSpaceAdr: ADDRESS; workSpaceSize: CARDINAL; VAR p1: PROCESS); END NEWPROCESS; PROCEDURE TRANSFER * ( VAR p1, p2: PROCESS); END TRANSFER; END M2COROUTINES.
MODULE Grune; (* Grune's problem in Oberon-2. written by GD/ModulaWare, Feb-1982 transpiled from Modula-2 to Oberon-2 by AS/Jan-1995. http://www.modulaware.com/ *) IMPORT SYSTEM, STextIO, M2C := M2COROUTINES; CONST Instance = 3; LevelMax = Instance+1; WorkSpace = 1024; TYPE ParType= RECORD c1, c2: CHAR; END; PROC = M2C.PROC; LevelType = LONGINT; wsptype = ARRAY WorkSpace OF SYSTEM.WORD; VAR ch: CHAR; Level: LevelType; PS: ARRAY LevelMax OF RECORD P: M2C.PROCESS; (* Process descriptor *) W: wsptype; Parameter: ParType; END; PROCEDURE TRANSFERDOWN ; BEGIN DEC (Level); M2C.TRANSFER ( PS [Level+1].P, PS [Level].P ); END TRANSFERDOWN; PROCEDURE TRANSFERUP ; BEGIN INC (Level); M2C.TRANSFER ( PS [Level-1].P, PS[Level].P ); END TRANSFERUP; PROCEDURE CREATE (Proc: PROC; Lev: LevelType; x,y: CHAR); BEGIN M2C.NEWPROCESS (Proc, SYSTEM.ADR(PS[Lev].W), SIZE(wsptype), PS[Lev].P); PS[Lev].Parameter.c1 := x; PS[Lev].Parameter.c2 := y; END CREATE; PROCEDURE InChar; BEGIN LOOP STextIO.ReadChar(ch); TRANSFERUP; END; END InChar; PROCEDURE Converter; VAR ch1: CHAR; BEGIN LOOP TRANSFERDOWN; IF ch = PS[Level].Parameter.c1 THEN TRANSFERDOWN; IF ch = PS[Level].Parameter.c1 THEN ch := PS[Level].Parameter.c2 ELSE ch1 := ch; ch := PS[Level].Parameter.c1; TRANSFERUP; ch := ch1; END; END; TRANSFERUP; END; END Converter; PROCEDURE W(a: ARRAY OF CHAR); BEGIN STextIO.WriteLn; STextIO.WriteString(a); END W; BEGIN Level := LEN(PS)-1; CREATE ( InChar, 0, CHR(0), CHR(0) ); CREATE (Converter, 1, 'a', 'b'); CREATE (Converter, 2, 'b', 'c'); W(" Grune's Problem, Text conversion: 'b' <--- 'aa'; 'c' <--- 'bb' >"); W(" terminate with "."); W(""); LOOP TRANSFERDOWN; STextIO.WriteChar(ch); IF ch = '.' THEN EXIT END; END; W(" Final Point found. "); W(""); END Grune.
Modula-2 definition and implementation module M2COROUTINES:


DEFINITION MODULE M2COROUTINES; IMPORT SYSTEM; TYPE PROCESS = SYSTEM.PROCESS; PROCEDURE NEWPROCESS ( p: PROC; workSpaceAdr: SYSTEM.ADDRESS; workSpaceSize: CARDINAL; VAR p1: PROCESS); PROCEDURE TRANSFER (VAR p1, p2: PROCESS); END M2COROUTINES.
IMPLEMENTATION MODULE M2COROUTINES; IMPORT SYSTEM; PROCEDURE NEWPROCESS ( p: PROC; workSpaceAdr: SYSTEM.ADDRESS; workSpaceSize: CARDINAL; VAR p1: PROCESS); BEGIN SYSTEM.NEWPROCESS(p,workSpaceAdr,workSpaceSize,p1); END NEWPROCESS; PROCEDURE TRANSFER (VAR p1, p2: PROCESS); BEGIN SYSTEM.TRANSFER(p1,p2); END TRANSFER; END M2COROUTINES.

3. Parameterized processes

The module CParameters allows one to pass a parameter to a coroutine to be created.



MODULE CParameters; (* Environment for Processes (Coroutines) with Parameters written by KM, GD/ModulaWare, 01-Jul-1983 transpiled from Modula-2 to Oberon-2 by AS/Jan-1995. http://www.modulaware.com/ *) IMPORT SYSTEM, M2C := M2COROUTINES,CTR; TYPE PType * = SYSTEM.WORD; CProc * = PROCEDURE ( a: PType ); VAR P: CProc; Q: SYSTEM.WORD; NEWPRO, MAIN: M2C.PROCESS; PROCEDURE X; BEGIN P(Q); END X; (* Replaces NEWPROCESS; creates and starts the process Proc with Parameter. *) PROCEDURE NewProcess * (Proc: CProc; Parameter: SYSTEM.WORD; WorkSpace: CTR.ADDRESS; Size: CTR.CARDINAL; VAR p1: M2C.PR OCESS); BEGIN P := Proc; Q := Parameter; (* create & start process X: *) M2C.NEWPROCESS(X, WorkSpace, Size, NEWPRO); M2C.TRANSFER(MAIN, NEWPRO); p1 := NEWPRO; END NewProcess; PROCEDURE Accept * ; (* Accepts Parameter, called once at coroutine entry *) BEGIN (* switch back: *) M2C.TRANSFER(NEWPRO, MAIN); END Accept; END CParameters.

3.1. Grune's problem revised



MODULE Green; (* Grune's problem revised, using parameterized processes. To declare processes with a parameter of type WORD (or POINTER TO any user defined data structure), the module CParameters is used. Here the process' parameter is of ParType. written by GD/ModulaWare, Feb-1982 modified by RS to use of CParameters, Dez-1986 transpiled from Modula-2 to Oberon-2 by AS/Jan-1995. http://www.modulaware.com/ *) IMPORT SYSTEM, CParameters, M2C:=M2Coroutines, STextIO, CPara := CParameters; CONST Instance = 3; LevelMax = Instance+1; WorkSpace = 1024; TYPE PPType *= POINTER TO ParType; ParType = RECORD c1, c2: CHAR; END; wsptype = ARRAY WorkSpace OF SYSTEM.WORD; LevelType = LONGINT; VAR ch: CHAR; Level: LevelType; PS * : ARRAY LevelMax OF RECORD P: M2C.PROCESS; (* Process descriptor *) W: wsptype; Param: ParType; END; PROCEDURE TRANSFERDOWN *; BEGIN DEC (Level); M2C.TRANSFER ( PS [Level+1].P, PS [Level].P ); END TRANSFERDOWN; PROCEDURE TRANSFERUP * ; BEGIN INC (Level); M2C.TRANSFER ( PS [Level-1].P, PS[Level].P ); END TRANSFERUP; PROCEDURE CREATE * (Proc: CPara.CProc; Lev: LevelType; x,y: CHAR); VAR ParamPtr: PPType; BEGIN PS[Lev].Param.c1 := x; PS[Lev].Param.c2 := y; ParamPtr := SYSTEM.VAL(PPType, SYSTEM.ADR (PS[Lev].Param)); CPara.NewProcess (Proc, SYSTEM.VAL(SYSTEM.WORD,ParamPtr), SYSTEM.ADR(PS[Lev].W), SIZE(wsptype), PS[Lev].P); END CREATE; PROCEDURE InChar; BEGIN LOOP STextIO.ReadChar(ch); TRANSFERUP; END; END InChar; PROCEDURE Converter (ParamPtr: PPType); VAR ch1: CHAR; c1, c2: CHAR; BEGIN CPara.Accept; c1 := ParamPtr^.c1; c2 := ParamPtr^.c2; LOOP TRANSFERDOWN; IF ch = c1 THEN TRANSFERDOWN; IF ch = c1 THEN ch := c2 ELSE ch1 := ch; ch := c1; TRANSFERUP; ch := ch1; END; END; TRANSFERUP; END; END Converter; BEGIN (* Body of Grunes *) Level := LEN (PS)-1; (* Body of Transfer *) (* Body of ReadChar *) M2C.NEWPROCESS (InChar, SYSTEM.ADR(PS[0].W), SIZE(wsptype), PS[0].P); CREATE (SYSTEM.VAL(CPara.CProc, Converter), 1, 'a', 'b');(* Body of Convert *) CREATE (SYSTEM.VAL(CPara.CProc, Converter), 2, 'b', 'c'); STextIO.WriteLn; STextIO.WriteString(" Grune's Problem,"); STextIO.WriteLn; STextIO.WriteString(" Text conversion: 'b' <--- 'aa'; 'c' <--- 'bb' >"); STextIO.WriteLn; LOOP TRANSFERDOWN; STextIO.WriteChar(ch); IF ch = '.' THEN EXIT END; END; STextIO.WriteLn; STextIO.WriteString(" Final Point found. "); STextIO.WriteLn; END Green.

4. Channel based scheduler Channels

In this chapter, the use of Occam's channel concept is illustrated. A reference is given in module Channels below.

If a multiprocess system consists of truly distributed processors connected with data channels rather than of processors accessing a common store, communication cannot be expressed using shared variables. One will prefer the scheme proposed by Hoare in CSP and embodied by the language Occam.

The translation of the terse Occam notation into equivalent form in Oberon-2 is shown in the definition module Channels:



MODULE Channels; (* NW 21.3.84 *) (* OCCAM channels (Hoare's CSP concept) in Oberon-2. Reference: [Wirt 84a] Schemes for multiprogramming and their implementation in Modula-2, by N. Wirth, ETH-Zuerich, Institut fuer Informatik, Report Nr. 59, Jun-1984. Ported to PDP-11 by R. Singer, ModulaWare GmbH, Erlangen, Dez-1986 Transpiled to Oberon-2/OpenVMS AXP by A. Schuhmacher, Jan-1987 http://www.modulaware.com *) IMPORT SYSTEM, M2C := M2COROUTINES,CTR; CONST WorkspaceSize = 1024; TYPE INTEGER * = CTR.INTEGER; PROC * = M2C.PROC; Message * = INTEGER; (* or an arbitrary data structure! *) Process = POINTER TO RingNode; ChannelObj = RECORD prod, cons: Process; END; Channel * = (* communication channel *) POINTER TO ChannelObj; ProcessState = CTR.ENUM8; CONST ready=0; waiting=1; terminated=2; TYPE PTR=POINTER TO RECORD m: Message;END; wsptype=ARRAY WorkspaceSize OF SYSTEM.WORD; RingNode = RECORD next, prev: Process; (*ring*) partner: Process; cor: M2C.PROCESS; state: ProcessState; msgAdr: PTR; main: Process; wsp: wsptype; END; VAR cp: Process; (*current process*) free: Process; (*chain of free process descriptors*) PROCEDURE Parallel * (P,Q: PROC); (* PAR P Q *) (* P and Q are parameterless procedures. To express PAR P Q R, Parallel (P, QR) is used, where QR is a procedure with body Parallel (Q, R). The creating process proceeds only if both offsprings (procedures P and Q) have terminated (i.e. have called the procedure Terminate in this implementation). *) VAR newP, newQ: Process; BEGIN IF free = NIL THEN NEW (newP); NEW (newQ); ELSE newP := free; newQ := free^.next; free := free^.next^.next; END; M2C.NEWPROCESS (P, SYSTEM.ADR (newP^.wsp), SIZE (wsptype), newP^.cor); newP^.next := cp^.next; newP^.prev := cp; newP^.state := ready; cp^.next := newP; newP^.next^.prev := newP; M2C.NEWPROCESS (Q, SYSTEM.ADR (newQ^.wsp), SIZE (wsptype), newQ^.cor); newQ^.next := cp^.next; newQ^.prev := cp; newQ^.state := ready; cp^.next := newQ; newQ^.next^.prev := newQ; newQ^.partner := newP; newP^.partner := newQ; newP^.main := cp; newQ^.main := cp; cp^.state := waiting; cp := newP; M2C.TRANSFER (cp^.main^.cor, cp^.cor); END Parallel; PROCEDURE Terminate * ; (* before the procedure P or Q terminate, each procedure has to call Terminate. Termination is delayed until the partner process also calls Terminate *) (* Each channel connects one sender with one receiver. The channel is truly a "wire" and has no implied buffering capability. It therefore forces the sender and receiver to a rendez-vous; the channel is not a mailbox, just a meeting point. *) VAR aux: Process; BEGIN aux := cp; IF cp^.partner^.state = terminated THEN cp^.prev^.next := cp^.next; cp^.next^.prev := cp^.prev; cp^.next := cp^.partner; cp^.partner^.prev^.next:=cp^.partner^.next; cp^.partner^.next^.prev:=cp^.partner^.prev; cp^.partner^.next := free; free := cp; cp := cp^.main; cp^.state := ready; ELSE REPEAT cp := cp^.next UNTIL cp^.state = ready; IF cp = aux THEN HALT(20); (*deadlock*) END; END; aux^.state := terminated; M2C.TRANSFER (aux^.cor, cp^.cor); END Terminate; PROCEDURE Send * (VAR ch: Channel; msg: Message); (* ch ! x *) (* if a sender outputs a message to a channel, it is delayed until a reveiver picks up the message (at the other end of the channel. No delay if a receiver was already waiting on the channel. *) VAR this: Process; BEGIN this := cp; IF ch^.cons # NIL THEN (*wakeup consumer*) cp := ch^.cons; ch^.cons := NIL; cp^.state := ready; cp^.msgAdr^.m := msg; ELSE (*wait for consumer*) IF ch^.prod # NIL THEN HALT(20); END; ch^.prod := cp; cp^.msgAdr := SYSTEM.VAL(PTR,SYSTEM.ADR (msg)); REPEAT cp := cp^.next UNTIL cp^.state = ready; this^.state := waiting; IF cp = this THEN HALT(20); (*deadlock*) END; END; M2C.TRANSFER (this^.cor, cp^.cor); END Send; PROCEDURE Receive * (VAR ch: Channel; VAR msg: Message); (* ch ? x *) (* if a receiver is expecting input from a channel, it gets delayed until a sender outputs a message on the channel. No delay if the sender was already waiting for his message to be picked up. *) VAR this: Process; BEGIN this := cp; IF ch^.prod # NIL THEN (*wakeup producer*) cp := ch^.prod; ch^.prod := NIL; cp^.state := ready; msg := cp^.msgAdr^.m; ELSE (*wait for producer*) IF ch^.cons # NIL THEN HALT(20); END; ch^.cons := cp; cp^.msgAdr := SYSTEM.VAL(PTR,SYSTEM.ADR (msg)); REPEAT cp := cp^.next UNTIL cp^.state = ready; this^.state := waiting; IF cp = this THEN HALT(20); (*deadlock*) END; END; M2C.TRANSFER (this^.cor, cp^.cor); END Receive; PROCEDURE SenderWaiting * (VAR ch: Channel): BOOLEAN; BEGIN RETURN ch^.prod # NIL; END SenderWaiting; PROCEDURE ReceiverWaiting * (VAR ch: Channel): BOOLEAN; BEGIN RETURN ch^.cons # NIL; END ReceiverWaiting; PROCEDURE InitChannel * (VAR ch: Channel); (* each channel used must be initialised *) BEGIN NEW (ch); ch^.prod := NIL; ch^.cons := NIL; END InitChannel; BEGIN free := NIL; NEW (cp); cp^.next := cp; cp^.prev := cp; cp^.state := ready; END Channels.

4.1. Grune's problem revised again

Grune's problem now serves to demonstrate the Channel concept:



MODULE GruneChannels; (* Grune's problem revised using OCCAM's channel concept defined by module Channels. written by RS/ModulaWare, Dec-1986. transpiled from Modula-2 to Oberon-2 by AS/03-Feb-1995. http://www.modulaware.com/ *) IMPORT Channels, STextIO, CTR ; CONST termChar = '.'; VAR terminal, input, screen: Channels.Channel; Params: ARRAY 2 OF RECORD c1, c2: CHAR; in, out: Channels.Channel; END; Level: CTR.CARDINAL; PROCEDURE InChar; VAR ch: CHAR; BEGIN LOOP STextIO.ReadChar(ch); Channels.Send (terminal, ORD(ch)); IF ch = termChar THEN Channels.Terminate; END; END; END InChar; PROCEDURE Converter; VAR level: CTR.CARDINAL; ch1, ch: Channels.Message; BEGIN level := Level; IF Level = 0 THEN INC (Level); END; LOOP Channels.Receive (Params [level].in, ch); IF CHR (ch) = Params [level].c1 THEN Channels.Receive (Params [level].in, ch); IF CHR (ch) = Params[level].c1 THEN ch := ORD(Params[level].c2); ELSE ch1 := ch; ch := ORD(Params[level].c1); Channels.Send (Params[level].out, ch); ch := ch1; END; END; Channels.Send (Params [level].out, ch); IF CHR (ch) = termChar THEN Channels.Terminate; END; END; END Converter; PROCEDURE Output; VAR ch: Channels.Message; BEGIN LOOP Channels.Receive (screen, ch); STextIO.WriteChar (CHR (ch)); IF CHR (ch) = termChar THEN Channels.Terminate; END; END; END Output; PROCEDURE Level0; BEGIN Channels.Parallel (Converter, Converter); Channels.Terminate; END Level0; PROCEDURE Level1; BEGIN Channels.Parallel (InChar, Level0); Channels.Terminate; END Level1; PROCEDURE InitParam; BEGIN Params[0].c1 := 'a'; Params[0].c2 := 'b'; Params[0].in := terminal; Params[0].out := input; Params[1].c1 := 'b'; Params[1].c2 := 'c'; Params[1].in := input; Params[1].out := screen; Level := 0; END InitParam; BEGIN STextIO.WriteLn; STextIO.WriteString(" Grune's Problem,"); STextIO.WriteLn; STextIO.WriteString(" Text conversion: 'b' <--- 'aa'; 'c' <--- 'bb' >"); STextIO.WriteLn; Channels.InitChannel (terminal); Channels.InitChannel (input); Channels.InitChannel (screen); InitParam; Channels.Parallel (Level1, Output); STextIO.WriteLn; STextIO.WriteString(" Final Point found. "); STextIO.WriteLn; END GruneChannels.
In the next issue:

The Oberon-2 coroutine examples are continued with part II in The ModulaTor, Mar-1995 with a presentation of the Lee algorithm. The next issue will also contain the bibliographic references.


IMPRESSUM: The ModulaTor is an unrefereed journal. Technical papers are to be taken as working papers and personal rather than organizational statements. Items are printed at the discretion of the Editor based upon his judgement on the interest and relevancy to the readership. Letters, announcements, and other items of professional interest are selected on the same basis. Office of publication. The Editor of The ModulaTor is Günter Dotzel; he can be reached at mailto:[email deleted due to spam]


Home | Site_index | Legal | OpenVMS_compiler | Alpha_Oberon_System | ModulaTor | Bibliography | Oberon[-2]_links | Modula-2_links |

Amazon.com [3KB] [Any browser]

Books Music Video Enter keywords...


Amazon.com logo

Webdesign by www.otolo.com/webworx, 16-Dec-1998