File: FeedbackImpl.mesa
Copyright Ó 1990 by Xerox Corporation. All rights reserved.
Last edited by: Eric Bier on September 5, 1987 1:44:49 pm PDT
Pier, October 23, 1987 3:23:36 pm PDT
Bier, May 1, 1990 11:14 pm PDT
Last tweaked by Mike Spreitzer on December 7, 1990 4:16 pm PST
DIRECTORY Ascii, Basics, Feedback, FeedbackConcreteTypes, FeedbackClasses, FeedbackSignals, FeedbackTypes,
IO, IOUtils, RefTab, Rope, SimpleFeedback;
FeedbackImpl:
CEDAR
PROGRAM
IMPORTS IO, IOUtils, RefTab, Rope
EXPORTS FeedbackTypes, Feedback, FeedbackClasses, FeedbackSignals
= BEGIN
Familiar Types
ROPE: TYPE = Rope.ROPE;
MsgType: TYPE = SimpleFeedback.MsgType;
MsgClass: TYPE = SimpleFeedback.MsgClass;
MsgRouter: TYPE = REF MsgRouterObj;
MsgRouterObj: PUBLIC TYPE = FeedbackConcreteTypes.MsgRouterObj;
MsgHandler: TYPE ~ REF MsgHandlerObj;
MsgHandlerObj: PUBLIC TYPE = FeedbackConcreteTypes.MsgHandlerObj;
The Problem
Problem: PUBLIC SIGNAL [msg: Rope.ROPE] ← CODE;
Router Operations By Name
typeBreaksAt:
PUBLIC
ARRAY MsgType
OF Feedback.Breaks ← [
oneLiner: [TRUE, TRUE],
begin: [TRUE, FALSE],
middle: [FALSE, FALSE],
end: [FALSE, TRUE]];
breaksToType:
PUBLIC
ARRAY
--begin:--
BOOL
OF
ARRAY
--end:--
BOOL
OF MsgType ← [
FALSE: [FALSE: middle, TRUE: end],
TRUE: [FALSE: begin, TRUE: oneLiner]];
AppendByName:
PUBLIC
PROC [routerName:
ATOM, msgType: MsgType, msgClass: MsgClass, msg: Rope.
ROPE] = {
router: MsgRouter ← EnsureRouter[routerName];
Append[router, msgType, msgClass, msg];
};
BlinkByName:
PUBLIC
PROC [routerName:
ATOM, msgClass: MsgClass ← $All] = {
router: MsgRouter ← EnsureRouter[routerName];
Blink[router, msgClass];
};
SetRouterOnByName:
PUBLIC
PROC [routerName:
ATOM, on:
BOOL] = {
router: MsgRouter ← EnsureRouter[routerName];
SetRouterOn[router, on];
};
GetRouterOnByName:
PUBLIC
PROC [routerName:
ATOM]
RETURNS [on:
BOOL] = {
router: MsgRouter ← EnsureRouter[routerName];
RETURN GetRouterOn[router]};
PutFByName:
PUBLIC
PROC [routerName:
ATOM, msgType: MsgType, msgClass: MsgClass, format: Rope.
ROPE ←
NIL, v1, v2, v3, v4, v5:
IO.Value ← [null[]] ] = {
router: MsgRouter ← EnsureRouter[routerName];
PutF[router, msgType, msgClass, format, v1, v2, v3, v4, v5];
};
ClearHeraldByName:
PUBLIC
PROC [routerName:
ATOM, msgClass: MsgClass ← $All] = {
router: MsgRouter ← EnsureRouter[routerName];
ClearHerald[router, msgClass];
};
Creating and Registering Routers
gRouters: RefTab.Ref ← RefTab.Create[]; -- all of the MsgRouters in the world
CreateRouter:
PUBLIC
PROC
RETURNS [router: MsgRouter] = {
router ← NEW[MsgRouterObj ← [defaultHandler: defaultHandler]];
RETURN};
RegisterRouter:
PUBLIC
PROC [router: MsgRouter, routerName:
ATOM]
RETURNS [oldRouter: MsgRouter ←
NIL] = {
found: BOOL ← FALSE;
val: REF;
[found, val] ← gRouters.Fetch[routerName];
IF found THEN oldRouter ← NARROW[val];
[] ← gRouters.Store[routerName, router];
};
RouterFromName:
PUBLIC
PROC [routerName:
ATOM]
RETURNS [router: MsgRouter ←
NIL] = {
found: BOOL ← FALSE;
val: REF;
[found, val] ← gRouters.Fetch[routerName];
IF found THEN router ← NARROW[val];
};
EnsureRouter:
PUBLIC
PROC [routerName:
ATOM]
RETURNS [router: MsgRouter] = {
router ← RouterFromName[routerName];
IF router =
NIL
THEN {
router ← CreateRouter[];
[] ← RegisterRouter[router, routerName];
};
};
ScanRouters:
PUBLIC
PROC [foreach:
PROC [
ATOM, MsgRouter]
RETURNS [stop:
BOOL ←
FALSE]]
RETURNS [result: Feedback.RouterScanResult ← []] ~ {
Pass:
PROC [key, val:
REF
ANY]
RETURNS [stop:
BOOL ←
FALSE]
--RefTab.EachPairAction-- ~ {
routerName: ATOM ~ NARROW[key];
router: MsgRouter ~ NARROW[val];
IF foreach[routerName, router] THEN result ← [TRUE, routerName, router];
RETURN [result.stopped]};
[] ← gRouters.Pairs[Pass];
RETURN};
Operating on Routers
Append:
PUBLIC
PROC [router: MsgRouter, msgType: MsgType, msgClass: MsgClass, msg:
ROPE] = {
mh: MsgHandler ~ GetEffectiveHandler[router, msgClass];
mh.PutF[mh, msgType, msgClass, "%g", [rope[msg]]];
RETURN};
PutF:
PUBLIC
PROC [router: MsgRouter, msgType: MsgType, msgClass: MsgClass, format:
ROPE ←
NIL, v1, v2, v3, v4, v5:
IO.Value ← [null[]] ] = {
mh: MsgHandler ~ GetEffectiveHandler[router, msgClass];
mh.PutF[mh, msgType, msgClass, format, v1, v2, v3, v4, v5];
RETURN};
Blink:
PUBLIC
PROC [router: MsgRouter, msgClass: MsgClass] = {
mh: MsgHandler ~ GetEffectiveHandler[router, msgClass];
mh.Blink[mh, msgClass];
};
SetRouterOn:
PUBLIC
PROC [router: MsgRouter, on:
BOOL] = {
router.on ← on;
};
GetRouterOn:
PUBLIC
PROC [router: MsgRouter]
RETURNS [on:
BOOL] = {
on ← router.on;
};
ClearHerald:
PUBLIC
PROC [router: MsgRouter, msgClass: MsgClass] = {
mh: MsgHandler ~ GetEffectiveHandler[router, msgClass];
mh.ClearHerald[mh, msgClass];
RETURN};
Directing Routers
CreateHandler:
PUBLIC
PROC
[
PutF: PROC [mh: MsgHandler, msgType: MsgType, msgClass: MsgClass, format: ROPE, v1, v2, v3, v4, v5: IO.Value ← [null[]] ],
ClearHerald: PROC [mh: MsgHandler, msgClass: MsgClass],
Blink: PROC [mh: MsgHandler, msgClass: MsgClass],
data: REF ANY ← NIL]
RETURNS [MsgHandler]
~ {RETURN [NEW [MsgHandlerObj ← [PutF, ClearHerald, Blink, data]]]};
GetHandlerData:
PUBLIC
PROC [mh: MsgHandler]
RETURNS [
REF
ANY]
~ {RETURN [mh.data]};
SetHandler:
PUBLIC
PROC [router: MsgRouter, msgClass: MsgClass, mh: MsgHandler]
RETURNS [previous: MsgHandler] ~ {
SELECT msgClass
FROM
$Default, $Every => {
varying: BOOL ~ msgClass=$Every AND router.c2h#NIL AND router.c2h.GetSize[]#0;
IF mh=NIL THEN mh ← defaultHandler;
IF varying THEN {router.c2h.Erase[]; previous ← NIL}
ELSE IF router.defaultHandler=defaultHandler THEN previous ← NIL
ELSE previous ← router.defaultHandler;
router.defaultHandler ← mh;
};
ENDCASE => {
IF router.c2h=NIL THEN router.c2h ← RefTab.Create[];
previous ← NARROW[router.c2h.Fetch[msgClass].val];
IF mh=NIL THEN [] ← router.c2h.Delete[msgClass]
ELSE [] ← router.c2h.Store[msgClass, mh];
};
RETURN};
SetMultiHandler:
PUBLIC
PROC [router: MsgRouter, msgClasses:
LIST
OF MsgClass, mh: MsgHandler] ~ {
FOR msgClasses ← msgClasses, msgClasses.rest
WHILE msgClasses#
NIL
DO
[] ← SetHandler[router, msgClasses.first, mh];
ENDLOOP;
RETURN};
GetHandler:
PUBLIC
PROC [router: MsgRouter, msgClass: MsgClass]
RETURNS [mh: MsgHandler] ~ {
SELECT msgClass
FROM
$Default, $Every => {
IF msgClass=$Every AND router.c2h#NIL AND router.c2h.GetSize[]#0 THEN RETURN [NIL];
IF router.defaultHandler=defaultHandler THEN RETURN [NIL];
RETURN [router.defaultHandler]};
ENDCASE => {
IF router.c2h=NIL THEN RETURN [NIL];
mh ← NARROW[router.c2h.Fetch[msgClass].val];
RETURN}};
GetEffectiveHandler:
PUBLIC
PROC [router: MsgRouter, msgClass: MsgClass]
RETURNS [mh: MsgHandler] ~ {
IF router=NIL OR NOT router.on THEN RETURN [doNothing];
IF msgClass=$Default OR msgClass=$Every THEN RETURN [router.defaultHandler];
IF router.c2h=NIL THEN RETURN [router.defaultHandler];
mh ← NARROW[router.c2h.Fetch[msgClass].val];
IF mh=NIL THEN mh ← router.defaultHandler;
RETURN};
ScanHandlers:
PUBLIC
PROC [router: MsgRouter, foreach:
PROC [MsgClass, MsgHandler]
RETURNS [stop:
BOOL ←
FALSE]]
RETURNS [result: Feedback.HandlerScanResult ← []] ~ {
Pass:
PROC [key, val:
REF
ANY]
RETURNS [stop:
BOOL ←
FALSE] ~ {
msgClass: MsgClass ~ NARROW[key];
mh: MsgHandler ~ NARROW[val];
IF foreach[msgClass, mh] THEN result ← [TRUE, msgClass, mh];
RETURN [result.stopped]};
IF router.c2h#NIL THEN [] ← router.c2h.Pairs[Pass];
IF router.defaultHandler#defaultHandler AND NOT result.stopped THEN [] ← Pass[$Default, router.defaultHandler];
RETURN};
GetHandledClasses:
PUBLIC
PROC [router: MsgRouter]
RETURNS [classes:
LIST
OF MsgClass ←
NIL] ~ {
Note:
PROC [msgClass: MsgClass, mh: MsgHandler]
RETURNS [stop:
BOOL ←
FALSE]
~ {classes ← CONS[msgClass, classes]};
[] ← ScanHandlers[router, Note];
RETURN};
The Default Handler
defaultHandler: PUBLIC MsgHandler ~ NEW [MsgHandlerObj ← [DefaultPutF, DefaultClearHerald, DefaultBlink, NIL]];
dh: MsgHandler ← NIL;
SetGlobalDefaultHandlersBehavior:
PUBLIC
PROC [newDH: Feedback.MsgHandler]
RETURNS [previous: Feedback.MsgHandler] ~ {
previous ← dh;
dh ← newDH};
DefaultPutF:
PROC [mh: MsgHandler, msgType: MsgType, msgClass: MsgClass, format: Rope.
ROPE, v1, v2, v3, v4, v5:
IO.Value ← [null[]] ] ~ {
dh.PutF[dh, msgType, msgClass, format, v1, v2, v3, v4, v5];
RETURN};
DefaultClearHerald:
PROC [mh: MsgHandler, msgClass: MsgClass] ~ {
dh.ClearHerald[dh, msgClass];
RETURN};
DefaultBlink:
PROC [mh: MsgHandler, msgClass: MsgClass] ~ {
dh.Blink[dh, msgClass];
RETURN};
An Algebra of Routers, Handlers and Streams
doNothing:
PUBLIC MsgHandler ~
NEW [MsgHandlerObj ← [
PutF: DontPutF,
ClearHerald: DontClearHerald,
Blink: DontBlink,
data: NIL]];
DontPutF: PROC [mh: MsgHandler, msgType: MsgType, msgClass: MsgClass, format: Rope.ROPE, v1, v2, v3, v4, v5: IO.Value ← [null[]] ] ~ {RETURN};
DontClearHerald: PROC [mh: MsgHandler, msgClass: MsgClass] ~ {RETURN};
DontBlink: PROC [mh: MsgHandler, msgClass: MsgClass] ~ {RETURN};
handleByProblem:
PUBLIC MsgHandler ~
NEW [MsgHandlerObj ← [
PutF: ProblemPutF,
ClearHerald: DontClearHerald,
Blink: DontBlink,
data: NIL]];
ProblemPutF:
PROC [mh: MsgHandler, msgType: MsgType, msgClass: MsgClass, format: Rope.
ROPE, v1, v2, v3, v4, v5:
IO.Value ← [null[]] ] ~ {
asRope: ROPE ~ IO.PutFR[format, v1, v2, v3, v4, v5];
SIGNAL Problem[asRope];
RETURN};
Splitter: TYPE ~ REF SplitterPrivate;
SplitterPrivate: TYPE ~ RECORD [h1, h2: MsgHandler];
CreateSplittingHandler:
PUBLIC
PROC [h1, h2: MsgHandler]
RETURNS [MsgHandler] ~ {
sr: Splitter ~ NEW [SplitterPrivate ← [h1, h2]];
RETURN [NEW [MsgHandlerObj ← [SplitPutF, SplitClearHerald, SplitBlink, sr]]]};
IsSplittingHandler:
PUBLIC
PROC [mh: MsgHandler]
RETURNS [is:
BOOL, h1, h2: MsgHandler] ~ {
WITH mh.data
SELECT
FROM
sr: Splitter => RETURN [TRUE, sr.h1, sr.h2];
ENDCASE => RETURN [FALSE, NIL, NIL]};
SplitPutF:
PROC [mh: MsgHandler, msgType: MsgType, msgClass: MsgClass, format: Rope.
ROPE, v1, v2, v3, v4, v5:
IO.Value ← [null[]] ] ~ {
sr: Splitter ~ NARROW[mh.data];
sr.h1.PutF[sr.h1, msgType, msgClass, format, v1, v2, v3, v4, v5];
sr.h2.PutF[sr.h2, msgType, msgClass, format, v1, v2, v3, v4, v5];
RETURN};
SplitClearHerald:
PROC [mh: MsgHandler, msgClass: MsgClass] ~ {
sr: Splitter ~ NARROW[mh.data];
sr.h1.ClearHerald[sr.h1, msgClass];
sr.h2.ClearHerald[sr.h2, msgClass];
RETURN};
SplitBlink:
PROC [mh: MsgHandler, msgClass: MsgClass] ~ {
sr: Splitter ~ NARROW[mh.data];
sr.h1.Blink[sr.h1, msgClass];
sr.h2.Blink[sr.h2, msgClass];
RETURN};
CreateHandlerOnRouter:
PUBLIC
PROC [router: MsgRouter]
RETURNS [MsgHandler] ~ {
RETURN [NEW [MsgHandlerObj ← [HorPutF, HorClearHerald, HorBlink, router]]]};
IsHandlerOnRouter:
PUBLIC
PROC [mh: MsgHandler]
RETURNS [is:
BOOL, router: MsgRouter] ~ {
WITH mh.data
SELECT
FROM
router: MsgRouter => RETURN [TRUE, router];
ENDCASE => RETURN [FALSE, NIL]};
HorPutF:
PROC [mh: MsgHandler, msgType: MsgType, msgClass: MsgClass, format: Rope.
ROPE, v1, v2, v3, v4, v5:
IO.Value ← [null[]] ] ~ {
router: MsgRouter ~ NARROW[mh.data];
PutF[router, msgType, msgClass, format, v1, v2, v3, v4, v5];
RETURN};
HorClearHerald:
PROC [mh: MsgHandler, msgClass: MsgClass] ~ {
router: MsgRouter ~ NARROW[mh.data];
ClearHerald[router, msgClass];
RETURN};
HorBlink:
PROC [mh: MsgHandler, msgClass: MsgClass] ~ {
router: MsgRouter ~ NARROW[mh.data];
Blink[router, msgClass];
RETURN};
HandlerOnStream: TYPE ~ REF HandlerOnStreamPrivate;
HandlerOnStreamPrivate:
TYPE ~
RECORD [
to: IO.STREAM,
nlAsap: BOOL,
Blink: PROC [IO.STREAM, MsgClass] ← NIL,
state: {before, after, chars} ← after
];
CreateHandlerOnStream:
PUBLIC
PROC [to:
IO.
STREAM, nlAsap:
BOOL,
Blink:
PROC [
IO.
STREAM, MsgClass] ←
NIL]
RETURNS [MsgHandler] ~ {
hos: HandlerOnStream ~ NEW [HandlerOnStreamPrivate ← [to, nlAsap, Blink]];
RETURN [NEW [MsgHandlerObj ← [StreamPutF, DontClearHerald, StreamBlink, hos]]]};
IsHandlerOnStream:
PUBLIC
PROC [mh: MsgHandler]
RETURNS [is:
BOOL, to:
IO.
STREAM, nlAsap:
BOOL,
Blink:
PROC [
IO.
STREAM, MsgClass]] ~ {
WITH mh.data
SELECT
FROM
hos: HandlerOnStream => RETURN [TRUE, hos.to, hos.nlAsap, hos.Blink];
ENDCASE => RETURN [FALSE, NIL, FALSE, NIL]};
StreamPutF:
PROC [mh: MsgHandler, msgType: MsgType, msgClass: MsgClass, format: Rope.
ROPE, v1, v2, v3, v4, v5:
IO.Value ← [null[]] ] ~ {
hos: HandlerOnStream ~ NARROW[mh.data];
IF hos.state=before OR (typeBreaksAt[msgType].begin AND hos.state#after) THEN hos.to.PutChar['\n];
hos.to.PutF[format, v1, v2, v3, v4, v5];
IF NOT typeBreaksAt[msgType].end THEN hos.state ← chars
ELSE IF hos.nlAsap THEN {hos.to.PutChar['\n]; hos.state ← after}
ELSE hos.state ← before;
RETURN};
StreamBlink:
PROC [mh: MsgHandler, msgClass: MsgClass] ~ {
hos: HandlerOnStream ~ NARROW[mh.data];
IF hos.Blink#NIL THEN hos.Blink[hos.to, msgClass];
RETURN};
SendAsciiBell: PUBLIC PROC [to: IO.STREAM, msgClass: MsgClass] ~ {to.PutChar[Ascii.BEL]};
StoringHandler: TYPE ~ REF StoringHandlerPrivate;
StoringHandlerPrivate:
TYPE ~
RECORD [
buffsize, avail: INT,
lastIsComplete: BOOL ← TRUE,
head, tail: LIST OF ROPE
];
CreateStoringHandler:
PUBLIC
PROC [buffsize:
INT ←
INT.
LAST]
RETURNS [MsgHandler] ~ {
head: LIST OF ROPE ~ LIST[NIL];
sh: StoringHandler ~ NEW [StoringHandlerPrivate ← [buffsize, buffsize, TRUE, head, head]];
RETURN [NEW [MsgHandlerObj ← [StoringPutF, DontClearHerald, DontBlink, sh]]]};
IsStoringHandler:
PUBLIC
PROC [mh: MsgHandler]
RETURNS [is:
BOOL, buffsize:
INT] ~ {
WITH mh.data
SELECT
FROM
sh: StoringHandler => RETURN [TRUE, sh.buffsize];
ENDCASE => RETURN [FALSE, 0]};
StoringPutF:
PROC [mh: MsgHandler, msgType: MsgType, msgClass: MsgClass, format: Rope.
ROPE, v1, v2, v3, v4, v5:
IO.Value ← [null[]] ] ~ {
sh: StoringHandler ~ NARROW[mh.data];
rope: ROPE ~ IO.PutFR[format, v1, v2, v3, v4, v5];
len: INT ~ rope.Length[];
IF len > sh.avail THEN RETURN;
sh.avail ← sh.avail - len;
IF sh.lastIsComplete
OR typeBreaksAt[msgType].begin
THEN sh.tail ← sh.tail.rest ← LIST[rope]
ELSE sh.tail.first ← Rope.Concat[sh.tail.first, rope];
sh.lastIsComplete ← typeBreaksAt[msgType].end;
RETURN};
GetStore:
PUBLIC
PROC [from: MsgHandler]
RETURNS [msgs:
LIST
OF
ROPE, lastIsComplete:
BOOL] ~ {
sh: StoringHandler ~ NARROW[from.data];
RETURN [sh.head.rest, sh.lastIsComplete]};
PlayStore:
PUBLIC
PROC [from, to: MsgHandler, withClass: MsgClass, thenClear:
BOOL] ~ {
sh: StoringHandler ~ NARROW[from.data];
FOR ml:
LIST
OF
ROPE ← sh.head.rest, ml.rest
WHILE ml#
NIL
DO
to.PutF[to, IF ml.rest#NIL OR sh.lastIsComplete THEN oneLiner ELSE begin, withClass, ml.first];
ENDLOOP;
IF thenClear THEN {sh.head.rest ← NIL; sh.lastIsComplete ← TRUE};
RETURN};
StreamOnRouter: TYPE ~ REF StreamOnRouterPrivate;
StreamOnRouterPrivate: TYPE ~ RECORD [r: MsgRouter, c: MsgClass];
sorProcs:
REF
IO.StreamProcs ~
IO.CreateStreamProcs[
variety: output,
class: $StreamOnRouter,
putChar: SorPutChar,
putBlock: SorPutBlock,
unsafePutBlock: SorUnsafePutBlock,
close: SorClose];
sorPfProcs: IOUtils.PFProcs ~ IOUtils.CopyPFProcs[NIL];
CreateStreamOnRouter:
PUBLIC
PROC [msgRouter: MsgRouter, msgClass: MsgClass]
RETURNS [s:
IO.
STREAM] ~ {
sor: StreamOnRouter ~ NEW [StreamOnRouterPrivate ← [msgRouter, msgClass]];
s ← IO.CreateStream[streamProcs: sorProcs, streamData: sor];
[] ← IOUtils.SetPFProcs[s, sorPfProcs];
RETURN};
IsStreamOnRouter:
PUBLIC
PROC [s:
IO.
STREAM]
RETURNS [is:
BOOL, msgRouter: MsgRouter, msgClass: MsgClass] ~ {
WITH s.streamData
SELECT
FROM
sor: StreamOnRouter => RETURN [TRUE, sor.r, sor.c];
ENDCASE => RETURN [FALSE, NIL, NIL]};
SorPutChar:
PROC [self:
IO.
STREAM, char:
CHAR] ~ {
sor: StreamOnRouter ~ NARROW[self.streamData];
IF char='\r
OR char='\l
THEN Append[sor.r, end, sor.c, NIL]
ELSE Append[sor.r, middle, sor.c, Rope.FromChar[char]];
RETURN};
SorPutBlock:
PROC [self:
IO.
STREAM, block:
REF
READONLY
TEXT, startIndex, count:
NAT] ~ {
sor: StreamOnRouter ~ NARROW[self.streamData];
IF startIndex >= block.length THEN RETURN;
{limit: NAT ~ MIN[block.length - startIndex, count] + startIndex;
WHILE startIndex < limit
DO
after: NAT ← startIndex;
c: CHAR;
WHILE after<limit AND (c ← block[after])#'\l AND c#'\r DO after ← after.SUCC ENDLOOP;
IF after>startIndex THEN Append[sor.r, middle, sor.c, Rope.FromRefText[block, startIndex, after-startIndex]];
WHILE after<limit
AND ((c ← block[after])='\l
OR c='\r)
DO
Append[sor.r, end, sor.c, NIL];
after ← after.SUCC;
ENDLOOP;
startIndex ← after;
ENDLOOP;
RETURN}};
SorUnsafePutBlock:
PROC [self:
IO.
STREAM, block: Basics.UnsafeBlock] ~
TRUSTED {
sor: StreamOnRouter ~ NARROW[self.streamData];
IF block.count <= 0 THEN RETURN;
{bytes: LONG POINTER TO Basics.RawBytes ~ LOOPHOLE[block.base];
limit: INT ~ block.startIndex + block.count;
startIndex: INT ← block.startIndex;
WHILE startIndex < limit
DO
after: NAT ← startIndex;
GiveChar:
PROC
RETURNS [c:
CHAR]
~ TRUSTED {c ← VAL[bytes[startIndex]]; startIndex ← startIndex.SUCC};
c: CHAR;
WHILE after<limit AND (c ← VAL[bytes[after]])#'\l AND c#'\r DO after ← after.SUCC ENDLOOP;
IF after>startIndex THEN Append[sor.r, middle, sor.c, Rope.FromProc[after-startIndex, GiveChar]];
WHILE after<limit
AND ((c ←
VAL[bytes[after]])='\l
OR c='\r)
DO
Append[sor.r, end, sor.c, NIL];
after ← after.SUCC;
ENDLOOP;
startIndex ← after;
ENDLOOP;
RETURN}};
SorPf:
PROC [stream:
IO.
STREAM, val:
IO.Value, format: IOUtils.Format, char:
CHAR]
--IOUtils.PFCodeProc-- ~ {
sor: StreamOnRouter ~ NARROW[stream.streamData];
last: INT ~ format.form.SkipTo[skip: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"];
PutF[sor.r, middle, sor.c, format.form.Substr[start: format.first-1, len: 2+last-format.first], val];
RETURN};
SorClose:
PROC [self:
IO.
STREAM, abort:
BOOL ←
FALSE] ~ {
IOUtils.AmbushStream[self: self, streamProcs: IOUtils.closedStreamProcs, streamData: NIL];
RETURN};
Start:
PROC = {
dh ← CreateStoringHandler[10000];
FOR c:
CHAR
IN ['a .. 'z]
DO
[] ← IOUtils.SetPFCodeProc[sorPfProcs, c, SorPf];
ENDLOOP;
RETURN};
Start[];
END.