158 lines
3.5 KiB
Modula-2
158 lines
3.5 KiB
Modula-2
|
(*
|
||
|
* Generates some integer sequences. Each generator is a process that
|
||
|
* yields integers to the main process. ACK switches processes by
|
||
|
* saving and restoring the stack. It uses _lor_ and _str_ to save
|
||
|
* and restore the local base and frame pointer.
|
||
|
*)
|
||
|
MODULE SemaTest;
|
||
|
FROM Semaphores IMPORT Sema, NewSema, Down, Up, StartProcess;
|
||
|
FROM Storage IMPORT ALLOCATE;
|
||
|
FROM Test IMPORT fail, finished;
|
||
|
|
||
|
TYPE
|
||
|
Generator = POINTER TO GeneratorRecord;
|
||
|
GeneratorRecord = RECORD
|
||
|
resume: Sema; (* up when resuming generator *)
|
||
|
yield: Sema; (* up when yielding value *)
|
||
|
value: INTEGER;
|
||
|
END;
|
||
|
VAR
|
||
|
curgen: Generator; (* current generator *)
|
||
|
startLock: Sema; (* down when booting generator *)
|
||
|
startProc: PROC;
|
||
|
startSelf: Generator;
|
||
|
|
||
|
PROCEDURE BootGenerator;
|
||
|
VAR pr: PROC; self: Generator;
|
||
|
BEGIN
|
||
|
pr := startProc;
|
||
|
self := startSelf;
|
||
|
Up(startLock);
|
||
|
Down(self^.resume); (* wait for first Resume *)
|
||
|
pr();
|
||
|
END BootGenerator;
|
||
|
|
||
|
PROCEDURE StartGenerator(gen: Generator; pr: PROC);
|
||
|
BEGIN
|
||
|
gen^.resume := NewSema(0);
|
||
|
gen^.yield := NewSema(0);
|
||
|
Down(startLock);
|
||
|
startProc := pr;
|
||
|
startSelf := gen;
|
||
|
StartProcess(BootGenerator, 8192);
|
||
|
END StartGenerator;
|
||
|
|
||
|
PROCEDURE Resume(gen: Generator): INTEGER;
|
||
|
VAR self: Generator;
|
||
|
BEGIN
|
||
|
self := curgen;
|
||
|
curgen := gen;
|
||
|
Up(gen^.resume);
|
||
|
Down(gen^.yield); (* wait for Yield *)
|
||
|
curgen := self;
|
||
|
RETURN gen^.value
|
||
|
END Resume;
|
||
|
|
||
|
PROCEDURE Yield(i: INTEGER);
|
||
|
VAR self: Generator;
|
||
|
BEGIN
|
||
|
self := curgen;
|
||
|
self^.value := i;
|
||
|
Up(self^.yield); (* curgen becomes invalid *)
|
||
|
Down(self^.resume); (* wait for Resume *)
|
||
|
END Yield;
|
||
|
|
||
|
PROCEDURE YieldHalfOf(i: INTEGER);
|
||
|
BEGIN
|
||
|
Yield(i DIV 2);
|
||
|
END YieldHalfOf;
|
||
|
|
||
|
PROCEDURE Triangular;
|
||
|
(* Yields the triangular numbers, http://oeis.org/A000217 *)
|
||
|
VAR n: INTEGER;
|
||
|
BEGIN
|
||
|
n := 0;
|
||
|
LOOP
|
||
|
YieldHalfOf(n * (n + 1));
|
||
|
INC(n);
|
||
|
END;
|
||
|
END Triangular;
|
||
|
|
||
|
PROCEDURE Pentagonal;
|
||
|
(* Yields the pentagonal numbers, http://oeis.org/A000326 *)
|
||
|
VAR n: INTEGER;
|
||
|
BEGIN
|
||
|
n := 0;
|
||
|
LOOP
|
||
|
YieldHalfOf(n * (3 * n - 1));
|
||
|
INC(n);
|
||
|
END;
|
||
|
END Pentagonal;
|
||
|
|
||
|
PROCEDURE Odious;
|
||
|
(* Yields the odius numbers, http://oeis.org/A000069 *)
|
||
|
VAR b, i, n: INTEGER;
|
||
|
BEGIN
|
||
|
n := 1;
|
||
|
LOOP
|
||
|
(* b := count bits in n *)
|
||
|
b := 0;
|
||
|
i := n;
|
||
|
WHILE i # 0 DO
|
||
|
INC(b, i MOD 2);
|
||
|
i := i DIV 2;
|
||
|
END;
|
||
|
|
||
|
IF (b MOD 2) = 1 THEN
|
||
|
Yield(n);
|
||
|
END;
|
||
|
INC(n);
|
||
|
END;
|
||
|
END Odious;
|
||
|
|
||
|
TYPE
|
||
|
Triple = ARRAY[1..3] OF INTEGER;
|
||
|
PROCEDURE T(i1, i2, i3: INTEGER): Triple;
|
||
|
VAR t: Triple;
|
||
|
BEGIN
|
||
|
t[1] := i1; t[2] := i2; t[3] := i3; RETURN t
|
||
|
END T;
|
||
|
|
||
|
CONST
|
||
|
two28 = 268435456D; (* 0x1000_0000 *)
|
||
|
VAR
|
||
|
a: ARRAY [0..9] OF Triple;
|
||
|
tri, pen, odi: Generator;
|
||
|
i, g1, g2, g3: INTEGER;
|
||
|
BEGIN
|
||
|
startLock := NewSema(1);
|
||
|
|
||
|
ALLOCATE(tri, SIZE(GeneratorRecord));
|
||
|
ALLOCATE(pen, SIZE(GeneratorRecord));
|
||
|
ALLOCATE(odi, SIZE(GeneratorRecord));
|
||
|
StartGenerator(tri, Triangular);
|
||
|
StartGenerator(pen, Pentagonal);
|
||
|
StartGenerator(odi, Odious);
|
||
|
|
||
|
a[0] := T( 0, 0, 1);
|
||
|
a[1] := T( 1, 1, 2);
|
||
|
a[2] := T( 3, 5, 4);
|
||
|
a[3] := T( 6, 12, 7);
|
||
|
a[4] := T(10, 22, 8);
|
||
|
a[5] := T(15, 35, 11);
|
||
|
a[6] := T(21, 51, 13);
|
||
|
a[7] := T(28, 70, 14);
|
||
|
a[8] := T(36, 92, 16);
|
||
|
a[9] := T(45, 117, 19);
|
||
|
|
||
|
FOR i := 0 TO INTEGER(9) DO
|
||
|
g1 := Resume(tri);
|
||
|
g2 := Resume(pen);
|
||
|
g3 := Resume(odi);
|
||
|
IF g1 # a[i][1] THEN fail(1D * two28 + LONG(a[i][1])) END;
|
||
|
IF g2 # a[i][2] THEN fail(2D * two28 + LONG(a[i][2])) END;
|
||
|
IF g3 # a[i][3] THEN fail(3D * two28 + LONG(a[i][3])) END;
|
||
|
END;
|
||
|
finished;
|
||
|
END SemaTest.
|