-- AbbrevExpandImpl.mesa
-- written by Bill Paxton, May 1981
-- last edit by Paxton, November 8, 1982 1:31 pm
-- Last Edited by: Plass, April 13, 1983 1:25 pm
-- This package implements abbreviation expansion in Tioga
DIRECTORY
AbbrevExpand,
RopeReader,
TextEdit,
EditSpan,
TextNode,
TextLooks,
PutGet,
CIFS,
Inline,
IO,
Rope,
RopeEdit,
RefTab,
Atom;
AbbrevExpandImpl:
CEDAR MONITOR
IMPORTS Atom, RopeReader, EditSpan, TextNode, Rope, RopeEdit, TextEdit, Inline, IO, RefTab, PutGet, CIFS
EXPORTS AbbrevExpand =
BEGIN
OPEN AbbrevExpand;
-- ***** Declarations
dictTable: RefTab.Ref ← RefTab.Create[zone: TextNode.pZone];
Entry: TYPE = REF EntryRec;
EntryRec:
TYPE =
RECORD [
next: Entry,
hash: CARDINAL,
keyRope: ROPE,
node: RefTextNode,
commands: LIST OF REF ANY
];
-- ***** Operations *****
Load:
PUBLIC
PROC
[fileName, dictName:
ROPE,
start: Offset ← 0, len: Offset ← MaxLen,
okToMapFile: BOOLEAN ← FALSE]
RETURNS [count: NAT] = {
rdr: RopeReader.Ref;
dict: ATOM = GetDictAtom[dictName];
root: Ref;
count ← 0;
[] ← RefTab.Store[dictTable, dict, NoDictAtom]; -- clears the dictionary
root ← PutGet.FromFile[fileName, start, len, okToMapFile];
rdr ← RopeReader.GetRopeReader[];
FOR n: Ref ← TextNode.FirstChild[root], TextNode.Next[n]
UNTIL n=
NIL
DO
node: RefTextNode ← TextNode.NarrowToTextNode[n];
IF node=NIL OR node.rope=NIL THEN LOOP;
AddToDict[root, node, dict, rdr];
count ← count+1;
ENDLOOP;
RopeReader.FreeRopeReader[rdr] };
AddToDict:
ENTRY
PROC [
root: Ref, node: RefTextNode, dictAtom: ATOM, rdr: RopeReader.Ref ← NIL] = {
ENABLE UNWIND => NULL;
dict: Entry ← GetDict[dictAtom];
keyRope: ROPE ← node.rope;
keyStart: Offset = 0;
hash: CARDINAL;
entry: Entry;
keyLen: NAT;
freeRdr: BOOLEAN ← FALSE;
resultStart: Offset;
commands: LIST OF REF ANY;
IF rdr=NIL THEN { rdr ← RopeReader.GetRopeReader[]; freeRdr ← TRUE };
keyLen ← FindKeyLen[keyRope,rdr];
IF keyLen=0 THEN { IF freeRdr THEN RopeReader.FreeRopeReader[rdr]; RETURN }; -- not an entry
hash ← KeyHash[keyRope,keyStart,keyLen,rdr];
entry ← LookupInternal[dict,hash,keyRope,keyStart,keyLen,rdr];
IF entry =
NIL
THEN {
-- add new entry
entry ← TextNode.pZone.NEW[EntryRec];
IF dict # NIL THEN { entry.next ← dict.next; dict.next ← entry }
ELSE [] ← RefTab.Store[dictTable,dictAtom,entry] };
entry.hash ← hash;
entry.keyRope ← RopeEdit.Substr[keyRope,keyStart,keyLen];
entry.node ← node;
[resultStart,commands] ← ParseCommands[keyRope,keyStart+keyLen,rdr];
entry.commands ← commands;
TextEdit.DeleteText[root,node,0,resultStart];
IF freeRdr THEN RopeReader.FreeRopeReader[rdr];
};
ParseCommands:
PROC [keyRope:
ROPE, start: Offset, rdr: RopeReader.Ref]
RETURNS [end: Offset, commands: LIST OF REF ANY] = { OPEN RopeReader;
ENABLE ReadOffEnd => GOTO Bad;
blank: BOOLEAN;
SetPosition[rdr, keyRope, start];
IF RopeEdit.BlankChar[Peek[rdr]] THEN { blank ← TRUE; [] ← Get[rdr] } ELSE blank ← FALSE;
IF Peek[rdr] = '(
THEN {
-- parse command list
h: IO.Handle ← IO.CreateInputStreamFromRope[RopeEdit.Substr[keyRope,start ← GetIndex[rdr]]];
commands ← NARROW[IO.GetRefAny[h]];
start ← start+IO.GetIndex[h];
IO.Close[h];
SetPosition[rdr, keyRope, start];
IF RopeEdit.BlankChar[Peek[rdr]] THEN { blank ← TRUE; [] ← Get[rdr] } ELSE blank ← FALSE;
};
IF Peek[rdr] = '=
THEN {
-- check for blank
[] ← Get[rdr];
IF blank AND Peek[rdr] = ' THEN [] ← Get[rdr]; -- skip blank after = if there was one before
end ← GetIndex[rdr];
}
ELSE end ← RopeEdit.Size[keyRope];
EXITS Bad => RETURN [RopeEdit.Size[keyRope],commands];
};
Clear:
PUBLIC
ENTRY
PROC [dictName:
ROPE] = {
-- clear the specified abbreviation dictionary
ENABLE UNWIND => NULL;
[] ← RefTab.Store[dictTable, GetDictAtom[dictName], NIL] };
IllFormedDef: PUBLIC ERROR = CODE;
GetDictAtom:
PROC [dictName:
ROPE]
RETURNS [dict:
ATOM] = {
-- force dictName lowercase before create the atom
force:
SAFE
PROC
RETURNS [c:
CHAR] =
CHECKED {
IF (c ← Rope.Fetch[dictName,i]) IN ['A..'Z] THEN c ← c-'A+'a;
i ← i+1 };
i: INT ← 0;
RETURN [Atom.MakeAtom[Rope.FromProc[Rope.Size[dictName],force]]];
};
Expand:
PUBLIC
PROC
[keyNode: RefTextNode, keyEnd: Offset, dict: ROPE, event: Event ← NIL]
RETURNS [foundIt:
BOOLEAN, keyDeterminesDict:
BOOLEAN, keyStart, keyLen, resultLen: Offset,
commands: LIST OF REF ANY] = {
FindEntry:
PROC [dictAtom:
ATOM]
RETURNS [
BOOLEAN] = {
dict: Entry ← GetDict[dictAtom];
IF dict#NIL THEN entry ← Lookup[dict,hash,keyRope,keyStart,kLen,rdr];
RETURN [entry # NIL] };
rdr: RopeReader.Ref;
dictAtom: ATOM = GetDictAtom[dict];
keyRope: ROPE ← keyNode.rope;
kLen: NAT;
kStart: Offset;
hash: CARDINAL;
entry: Entry;
foundIt ← keyDeterminesDict ← FALSE;
resultLen ← 0;
rdr ← RopeReader.GetRopeReader[];
keyEnd ← MAX[0,MIN[keyEnd,RopeEdit.Size[keyRope]]];
kStart ← keyStart ← FindKeyStart[keyRope,keyEnd,rdr];
kLen ← keyLen ← keyEnd-keyStart;
hash ← KeyHash[keyRope,keyStart,keyEnd,rdr];
-- see if have <dictName> '. before <keyName>
RopeReader.SetPosition[rdr,keyRope,keyStart];
IF keyStart > 0
AND RopeReader.Backwards[rdr]='.
THEN {
-- try to get the dictName from the key
nameStart: Offset ← FindKeyStart[keyRope,keyStart-1,rdr];
IF nameStart < keyStart-1
THEN {
dictName: ATOM ← Atom.MakeAtom[RopeEdit.Substr[keyRope,nameStart,keyStart-1-nameStart]];
[] ← FindEntry[dictName];
keyDeterminesDict ← TRUE;
keyLen ← keyEnd - (keyStart ← nameStart) }};
IF entry = NIL THEN [] ← FindEntry[dictAtom];
IF entry = NIL THEN { RopeReader.FreeRopeReader[rdr]; RETURN };
{ -- do it
CheckCaps:
PROC =
INLINE {
allcaps ← FALSE;
RopeReader.SetPosition[rdr,keyRope,kStart];
initialcap ← RopeReader.Peek[rdr] IN ['A..'Z];
FOR i:Offset
IN [0..kLen)
DO
IF RopeReader.Get[rdr] NOT IN ['A..'Z] THEN RETURN;
ENDLOOP;
allcaps ← TRUE };
node: RefTextNode = entry.node;
root: Ref = TextNode.Root[node];
keyRoot: Ref = TextNode.Root[keyNode];
nodeRope: ROPE ← node.rope;
looks: TextLooks.Looks ← TextEdit.FetchLooks[keyNode,kStart];
allcaps, initialcap: BOOLEAN ← FALSE;
type: TextNode.TypeName;
child: RefTextNode ← TextNode.NarrowToTextNode[TextNode.FirstChild[node]];
textLen: Offset ← RopeEdit.Size[nodeRope];
CheckCaps;
RopeReader.FreeRopeReader[rdr];
[,resultLen] ← TextEdit.ReplaceText[
destRoot: keyRoot, sourceRoot: root,
dest:keyNode, destStart:keyStart, destLen:keyLen,
source:node, sourceStart:0, sourceLen:textLen, event:event];
IF resultLen # textLen THEN ERROR;
IF looks # TextLooks.noLooks
THEN
TextEdit.AddLooks[keyRoot,keyNode,looks,keyStart,textLen,event];
IF allcaps THEN TextEdit.AllCaps[keyRoot,keyNode,keyStart,textLen,event]
ELSE
IF initialcap
AND textLen > 0
THEN
-- make first letter uppercase
TextEdit.AllCaps[keyRoot,keyNode,keyStart,1,event];
SELECT type ← TextNode.NodeType[node]
FROM
TextNode.nullTypeName, TextNode.NodeType[keyNode] => NULL;
ENDCASE => TextEdit.ChangeType[keyNode,type,event,keyRoot];
IF child #
NIL
THEN {
-- insert as children of keyNode
new: TextNode.Span ← EditSpan.Copy[keyRoot,root,TextNode.MakeNodeLoc[keyNode],
TextNode.MakeNodeSpan[child,TextNode.LastWithin[node]],
FALSE,after,1,event];
last: TextNode.RefTextNode ← TextNode.NarrowToTextNode[new.end.node];
IF last #
NIL
AND
keyStart+textLen < TextEdit.Size[keyNode] THEN -- move text after key to end of last new node
[] ← TextEdit.MoveText[
destRoot: keyRoot, sourceRoot: keyRoot, dest: last, destLoc: TextEdit.MaxLen,
source: keyNode, start: keyStart+textLen, len: TextEdit.MaxLen, event: event] };
foundIt ← TRUE;
commands ← entry.commands;
}};
KeyHash:
PROC
[keyRope: ROPE, keyStart, keyEnd: Offset, rdr: RopeReader.Ref]
RETURNS [h: CARDINAL] = {
-- hash must be independent of case of chars
len: NAT ← keyEnd-keyStart;
RopeReader.SetPosition[rdr,keyRope,keyStart];
h ← 0;
FOR i:
NAT
IN [1..
MIN[3,len])
DO
-- use the first 3 characters
h ← Inline.
BITXOR[Inline.
BITSHIFT[h,2],
RopeEdit.UpperCase[RopeReader.Get[rdr]]];
ENDLOOP;
IF len >= 2
THEN {
-- use the last 2 characters
RopeReader.SetPosition[rdr,keyRope,keyEnd];
FOR i:
NAT
IN [len-2..len)
DO
h ← Inline.
BITXOR[Inline.
BITSHIFT[h,2],
RopeEdit.UpperCase[RopeReader.Backwards[rdr]]];
ENDLOOP }};
Lookup:
ENTRY
PROC
[dict: Entry, hash:
CARDINAL, keyRope:
ROPE, keyStart: Offset, keyLen:
NAT,
rdr1, rdr2, rdr3: RopeReader.Ref ← NIL]
RETURNS [entry: Entry] = INLINE {
ENABLE UNWIND => NULL;
RETURN [LookupInternal[dict,hash,keyRope,keyStart,keyLen,rdr1,rdr2,rdr3]] };
LookupInternal:
PROC
[dict: Entry, hash:
CARDINAL, keyRope:
ROPE, keyStart: Offset, keyLen:
NAT,
rdr1, rdr2, rdr3: RopeReader.Ref ← NIL]
RETURNS [entry: Entry] = {
FreeReaders:
PROC =
INLINE {
IF free1 THEN RopeReader.FreeRopeReader[rdr1];
IF free2 THEN RopeReader.FreeRopeReader[rdr2];
IF free3 THEN RopeReader.FreeRopeReader[rdr3] };
free1, free2, free3: BOOLEAN ← FALSE;
pred: Entry ← NIL;
IF keyLen=0 THEN RETURN [NIL];
IF rdr1=NIL THEN { free1 ← TRUE; rdr1 ← RopeReader.GetRopeReader[] };
IF rdr2=NIL THEN { free2 ← TRUE; rdr2 ← RopeReader.GetRopeReader[] };
IF rdr3=NIL THEN { free3 ← TRUE; rdr3 ← RopeReader.GetRopeReader[] };
RopeReader.SetPosition[rdr1, keyRope, keyStart]; -- leave this at the start of the key
[] ← RopeReader.Peek[rdr1]; -- prime the reader for the key
entry ← NIL; -- will stay nil if don't find key in dict
FOR e: Entry ← dict, e.next
UNTIL e=
NIL
DO
IF e.hash = hash
AND RopeEdit.Size[e.keyRope] = keyLen
THEN {
-- check the ropes
rdr2^ ← rdr1^;
IF RopeReader.CompareSubstrs[keyRope,e.keyRope,
keyStart,keyLen,0,keyLen,rdr2,rdr3,
FALSE] = equal
THEN {
IF pred #
NIL
THEN {
-- move entry to front
pred.next ← e.next;
e.next ← dict.next;
dict.next ← e };
entry ← e; EXIT }};
pred ← e;
ENDLOOP;
FreeReaders[] };
FindKeyLen:
PROC [keyRope:
ROPE, rdr: RopeReader.Ref]
RETURNS [len:
NAT] = {
size: Offset ← RopeEdit.Size[keyRope];
IF size=0 THEN RETURN [0];
RopeReader.SetPosition[rdr,keyRope,0];
IF RopeEdit.PunctuationChar[RopeReader.Peek[rdr]] THEN RETURN [1];
len ← 0;
WHILE len<size
AND RopeEdit.AlphaNumericChar[RopeReader.Get[rdr]]
DO
len ← len+1; ENDLOOP };
FindKeyStart:
PROC [keyRope:
ROPE, keyEnd: Offset, rdr: RopeReader.Ref]
RETURNS [start: Offset] = {
IF keyRope.Length = 0 OR keyEnd = 0 THEN RETURN [0];
RopeReader.SetPosition[rdr,keyRope,keyEnd];
IF RopeEdit.PunctuationChar[RopeReader.PeekBackwards[rdr]] THEN RETURN [keyEnd-1];
start ← keyEnd;
WHILE start>0
AND RopeEdit.AlphaNumericChar[RopeReader.Backwards[rdr]]
DO
start ← start-1; ENDLOOP };
NoDictAtom: ATOM = $NoDictionaryAvailable;
GetDict:
PROC [dictName:
ATOM]
RETURNS [entry: Entry] = {
prop: REF ANY;
prop ← RefTab.Fetch[dictTable,dictName].val;
WITH prop
SELECT
FROM
x: Entry => RETURN [x];
x: ATOM => IF x = NoDictAtom THEN RETURN [NIL] ELSE ERROR;
ENDCASE => {
-- try to load it
r: ROPE ← Atom.GetPName[dictName];
fileName: ROPE ← RopeEdit.Concat[r,".Abbreviations"];
[] ← RefTab.Store[dictTable, dictName, NoDictAtom];
[] ← Load[fileName, r ! CIFS.Error => CHECKED {CONTINUE}];
RETURN [GetDict[dictName]] }};
-- ***** Initialization
Start:
PUBLIC
PROC = {
};
END.