
<<SMCommentTableImpl.mesa>>
<<Copyright  1985 by Xerox Corporation.  All rights reserved.>>
<<last edit by Satterthwaite, May 12, 1986 11:46:07 am PDT>>

DIRECTORY
SMCommentTable: TYPE USING [
BreakSequence, BreakTab, Index, Node, Ref, Table, TableNode, Text],
SMCommentTableOps: TYPE USING [];

SMCommentTableImpl: CEDAR PROGRAM
EXPORTS SMCommentTableOps ~ {
OPEN SMCommentTable;

CommentManager: PUBLIC TYPE ~ RECORD[
z: ZONE_,
commentTable: SMCommentTable.Table_NIL,
breakTable: SMCommentTable.BreakTab_NIL,
ending: INT_0];

CommentM: TYPE ~ REF CommentManager;

TableOverflow: PUBLIC ERROR ~ CODE;
TableOrder: PUBLIC ERROR ~ CODE;

Create: PUBLIC PROC[zone: ZONE] RETURNS[CommentM] ~ {
RETURN[zone.NEW[CommentManager _ [z~zone]]]};

Reset: PUBLIC PROC[cm: CommentM] ~ {
IF cm.commentTable # NIL THEN {
cm.commentTable.size _ 0;
cm.commentTable.last _ NIL;
cm.commentTable.lastx _ 0};
IF cm.breakTable # NIL THEN cm.breakTable.size _ 0;
cm.ending _ 0};

Add: PUBLIC PROC[cm: CommentM, start: Index, text: Text, lastToken, prefix: Index] ~ {
ref: Ref ~ (cm.z).NEW[Node _ [start, text, lastToken, prefix]];
size: NAT ~ (IF cm.commentTable = NIL THEN 0 ELSE cm.commentTable.size);
max: NAT ~ (IF cm.commentTable = NIL THEN 0 ELSE cm.commentTable.max);
last: Ref ~ (IF size > 0 THEN cm.commentTable[size-1] ELSE NIL);
IF lastToken > start THEN RETURN;
IF last # NIL THEN {
IF last.start > start OR lastToken < last.lastToken THEN RETURN;
IF last.start = start THEN {
<<merge the blank line comment with the actual comment>>
IF last.text # NIL THEN RETURN;
ref.prefix _ prefix + last.prefix;
cm.commentTable[size-1] _ ref;
RETURN};
};
IF size >= max THEN {
newMax: NAT ~ (IF max > (NAT.LAST/2 - 2) THEN NAT.LAST ELSE max*2 + 4);
IF newMax < size THEN ERROR TableOverflow;
{newTable: Table ~ (cm.z).NEW[TableNode[newMax]];
newTable.last _ NIL;
FOR i: NAT IN [0..size) DO
newTable[i] _ cm.commentTable[i];
cm.commentTable[i] _ NIL;
ENDLOOP;
cm.commentTable _ newTable;
}};
cm.commentTable[size] _ ref;
cm.commentTable.size _ size + 1};

AddBreakHint: PUBLIC PROC[cm: CommentM, index: Index] ~ {
size: NAT ~ (IF cm.breakTable = NIL THEN 0 ELSE cm.breakTable.size);
max: NAT ~ (IF cm.breakTable = NIL THEN 0 ELSE cm.breakTable.max);
last: Index ~ (IF size = 0 THEN 0 ELSE cm.breakTable[size-1]);
IF index <= last THEN RETURN;
IF size >= max THEN {
new: BreakTab _ NIL;
nMax: NAT _ MAX[200, IF max < NAT.LAST/2 THEN max+max ELSE NAT.LAST];
IF nMax <= max THEN ERROR TableOverflow;
new _ (cm.z).NEW[BreakSequence[nMax]];
FOR i: NAT IN [0..size) DO new[i] _ cm.breakTable[i] ENDLOOP;
cm.breakTable _ new};
cm.breakTable[size] _ index;
cm.breakTable.size _ size + 1};

FindNext: PUBLIC PROC[cm: CommentM, notBefore: Index] RETURNS[Ref] ~ {
IF cm.commentTable # NIL AND cm.commentTable.size > 0 THEN {
low: NAT _ 0;
high: NAT _ cm.commentTable.size-1;
last: Ref _ cm.commentTable.last;
lastx: NAT _ cm.commentTable.lastx;
lval: Index _ 0;
IF last # NIL THEN {
lval _ last.start;
IF notBefore = lval THEN RETURN[last];
IF notBefore < lval THEN high _ lastx ELSE low _ lastx+1};
WHILE low < high DO
lval _ (last _ cm.commentTable[lastx _ (low+high)/2]).start;
IF notBefore < lval THEN high _ lastx
ELSE IF notBefore > lval THEN low _ lastx+1
ELSE {
cm.commentTable.lastx _ lastx;
RETURN[cm.commentTable.last _ last]};
ENDLOOP;
IF high # lastx OR last = NIL THEN
lval _ (last _ cm.commentTable[lastx _ high]).start;
IF notBefore <= lval THEN {
cm.commentTable.lastx _ lastx;
RETURN[cm.commentTable.last _ last]};
};
RETURN[NIL]};

TestBreakHint: PUBLIC PROC[cm: CommentM, start: Index, next: Index] RETURNS[BOOL] ~ {
IF start = 0 OR next <= start THEN RETURN[TRUE];
IF cm.breakTable = NIL OR cm.breakTable.size <= 1 THEN RETURN[FALSE];
{low: NAT _ 0;
high: NAT _ cm.breakTable.size - 1;
DO
last: NAT _ (low+high)/2;
lval: Index _ cm.breakTable[last];
IF low = high THEN RETURN[start < lval AND next > lval];
IF start < lval THEN {
IF next > lval THEN RETURN[TRUE];
high _ last}
ELSE IF start >= lval THEN low _ last+1;
ENDLOOP;
};
};

Explode: PUBLIC PROC[ref: Ref] RETURNS[start: Index, text: Text, lastToken, prefix: Index] ~ {
RETURN (IF ref = NIL
THEN [Index.LAST, NIL, Index.LAST, 0]
ELSE [ref.start, ref.text, ref.lastToken, ref.prefix])
};

SetEnding: PUBLIC PROC[cm: CommentM, end: Index] ~ {cm.ending _ end};

GetEnding: PUBLIC PROC[cm: CommentM] RETURNS[Index] ~ {RETURN[cm.ending]};

}.
