<> <> <> <> <> DIRECTORY AbbrevExpand, Atom, Basics, EditSpan, FS, IO, PutGet, RefTab, Rope, RopeEdit, RopeReader, TextEdit, TextLooks, TextNode; AbbrevExpandImpl: CEDAR MONITOR IMPORTS Atom, EditSpan, FS, Basics, IO, PutGet, RefTab, Rope, RopeEdit, RopeReader, TextEdit, TextNode EXPORTS AbbrevExpand = BEGIN OPEN AbbrevExpand; <<-- ***** Declarations>> dictTable: RefTab.Ref _ RefTab.Create[mod: 67]; 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] 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]; 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: BOOL _ 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: BOOL; 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.STREAM _ IO.RIS[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: BOOL, keyDeterminesDict: BOOL, keyStart, keyLen, resultLen: Offset, commands: LIST OF REF ANY] = { FindEntry: PROC [dictAtom: ATOM] RETURNS [BOOL] = { 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 '. before >> 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: BOOL _ 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 _ Basics.BITXOR[Basics.BITSHIFT[h,2], RopeEdit.UpperCase[RopeReader.Get[rdr]]-0C]; ENDLOOP; IF len >= 2 THEN { -- use the last 2 characters RopeReader.SetPosition[rdr,keyRope,keyEnd]; FOR i: NAT IN [len-2..len) DO h _ Basics.BITXOR[Basics.BITSHIFT[h,2], RopeEdit.UpperCase[RopeReader.Backwards[rdr]]-0C]; 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: BOOL _ 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 len0 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 ! FS.Error => CHECKED {CONTINUE}]; RETURN [GetDict[dictName]] }}; <<-- ***** Initialization>> Start: PUBLIC PROC = { }; END.