<> <> <> <> <> <> <> <> <<>> <> <<>> <> <<>> DIRECTORY IO USING [atom, BreakProc, EndOfStream, GetTokenRope, IDProc, PutFR, RIS, rope, STREAM], LoganBerry USING [Entry, Open], LoganQuery USING [AttributePatterns, ComplexCursor, NextEntry, QueryEntries, ReadAttributePatterns, SyntaxError], NodeProps USING [GetProp, PutProp], Rope USING [Concat, ROPE, Size], TextLooks USING [Runs], TextNode USING [Body, Location, MaxLen, NodeItself, NodeProps, Ref, RefTextNode, Span]; QueryTextNodeImpl: CEDAR PROGRAM IMPORTS IO, LoganBerry, LoganQuery, NP: NodeProps, Rope EXPORTS TextNode = BEGIN OPEN TextNode; ROPE: TYPE ~ Rope.ROPE; MakeNodeLoc: PUBLIC PROC [n: Ref] RETURNS [Location] = { RETURN [[node: n, where: NodeItself]] }; MakeNodeSpan: PUBLIC PROC [first, last: Ref] RETURNS [Span] = { RETURN [[MakeNodeLoc[first], MakeNodeLoc[last]]] }; NarrowToTextNode: PUBLIC PROC [n: Ref] RETURNS [txt: RefTextNode] ~ {RETURN [n]}; <> NewTextNode: PUBLIC PROC RETURNS [txt: RefTextNode] = { txt _ NEW[Body]; txt.last _ TRUE }; Parent: PUBLIC PROC [n: Ref] RETURNS [Ref] = { RETURN [InlineParent[n]] }; InlineParent: PROC [n: Ref] RETURNS [Ref] = INLINE { DO IF n=NIL OR n.deleted THEN RETURN [NIL]; IF n.last THEN RETURN [QueryToText[n.next]]; n _ n.next; ENDLOOP; }; Root: PUBLIC PROC [n: Ref] RETURNS [Ref] = { <> p: Ref; DO IF (p _ InlineParent[n])=NIL THEN RETURN [IF n=NIL OR n.deleted THEN NIL ELSE n]; n _ p; ENDLOOP; }; Next: PUBLIC PROC [n: Ref] RETURNS [Ref] = { RETURN[IF n=NIL OR n.last OR n.deleted THEN NIL ELSE QueryToText[n.next]]; }; Previous: PUBLIC PROC [n: Ref, parent: Ref _ NIL] RETURNS [nx: Ref] = { nx2: Ref; IF parent=NIL THEN parent _ InlineParent[n]; IF n=NIL OR parent=NIL OR (nx _ parent.child)=n THEN RETURN [NIL]; DO IF (nx2_nx.next)=n THEN RETURN [QueryToLastText[nx]]; nx _ nx2; ENDLOOP; }; Forward: PUBLIC PROC [node: Ref] RETURNS [nx: Ref, levelDelta: INTEGER] = { [nx, levelDelta] _ InlineForward[node]; }; InlineForward: PROC [node: Ref] RETURNS [nx: Ref, levelDelta: INTEGER] = INLINE { <> child: Ref; IF node=NIL THEN RETURN [NIL, 0]; IF (child _ node.child) # NIL THEN RETURN [QueryToText[child, node], 1]; -- descend in the tree levelDelta _ 0; DO -- move to next node, sibling or up* then sibling IF NOT node.last THEN RETURN [QueryToText[node.next], levelDelta]; -- the sibling IF (node _ node.next) = NIL THEN RETURN [NIL, levelDelta]; -- the parent levelDelta _ levelDelta-1; ENDLOOP; }; RawInlineForward: PROC [node: Ref] RETURNS [nx: Ref, levelDelta: INTEGER] = INLINE { <> child: Ref; IF node=NIL THEN RETURN [NIL, 0]; IF (child _ node.child) # NIL THEN RETURN [child, 1]; -- descend in the tree levelDelta _ 0; DO -- move to next node, sibling or up* then sibling IF NOT node.last THEN RETURN [node.next, levelDelta]; -- the sibling IF (node _ node.next) = NIL THEN RETURN [NIL, levelDelta]; -- the parent levelDelta _ levelDelta-1; ENDLOOP; }; Backward: PUBLIC PROC [node: Ref, parent: Ref _ NIL] RETURNS [back, backparent: Ref, levelDelta: INTEGER] = { <> child, child2: Ref; IF parent = NIL THEN parent _ InlineParent[node]; IF parent = NIL OR node = NIL THEN RETURN [NIL, NIL, 0]; IF (child _ parent.child) = node THEN RETURN [parent, Parent[parent], -1]; DO IF child.last THEN ERROR; -- incorrect value supplied for parent IF (child2 _ child.next)=node THEN EXIT; child _ child2; ENDLOOP; levelDelta _ 0; DO IF (child2 _ LastChild[child]) = NIL THEN RETURN [QueryToLastText[child], parent, levelDelta]; levelDelta _ levelDelta+1; parent _ child; child _ child2; ENDLOOP; }; StepForward: PUBLIC PROC [node: Ref] RETURNS [Ref] = { RETURN[Forward[node].nx] }; <> StepBackward: PUBLIC PROC [node: Ref, parent: Ref _ NIL] RETURNS [Ref] = { RETURN[Backward[node, parent].back] }; <> Level: PUBLIC PROC [node: Ref] RETURNS [level: INTEGER] = { <> level _ 0; UNTIL (node _ InlineParent[node])=NIL DO level _ level+1 ENDLOOP; }; ForwardClipped: PUBLIC PROC [ node: Ref, maxLevel: INTEGER, nodeLevel: INTEGER _ 0] RETURNS [nx: Ref, nxLevel: INTEGER] = { <> <> <> child: Ref; IF node=NIL THEN RETURN [NIL, 0]; IF nodeLevel <= 0 THEN nodeLevel _ Level[node]; IF nodeLevel < maxLevel AND (child _ node.child) # NIL THEN RETURN [QueryToText[child, node], nodeLevel+1]; -- return the child DO -- move to next node, sibling or up* then sibling IF NOT node.last THEN RETURN [QueryToText[node.next], nodeLevel]; -- return the sibling IF (node _ node.next) = NIL THEN RETURN [NIL, 0]; -- go to the parent nodeLevel _ nodeLevel-1; ENDLOOP; }; BackwardClipped: PUBLIC PROC [ node: Ref, maxLevel: INTEGER, parent: Ref _ NIL, nodeLevel: INTEGER _ 0] RETURNS [back, backparent: Ref, backLevel: INTEGER] = { <> <> child, child2: Ref; IF parent = NIL THEN parent _ InlineParent[node]; IF parent = NIL OR node = NIL THEN RETURN [NIL, NIL, 0]; IF nodeLevel <= 0 THEN nodeLevel _ Level[node]; IF (child _ parent.child) = node THEN RETURN [parent, InlineParent[parent], nodeLevel-1]; DO -- search for sibling just before node IF child.last THEN ERROR; -- incorrect value supplied for parent IF (child2 _ child.next)=node THEN EXIT; child _ child2; ENDLOOP; DO -- go deeper in tree until reach maxLevel IF nodeLevel >= maxLevel OR (child2 _ LastChild[child]) = NIL THEN RETURN [QueryToLastText[child], parent, nodeLevel]; nodeLevel _ nodeLevel+1; parent _ child; child _ child2; ENDLOOP; }; LocRelative: PUBLIC PROC [location: Location, count: INT _ 0, break: NAT _ 1, skipCommentNodes: BOOL _ FALSE] RETURNS [Location] = { n: Ref _ location.node; size, lastSize, where: INT _ 0; init: Ref _ n; lastTxt: RefTextNode; IF count=0 AND InlineParent[n]=NIL THEN RETURN [[FirstChild[n], 0]]; -- avoid returning root node where _ MAX[location.where, 0]; -- where we are in the current node WHILE n # NIL DO IF n # NIL AND (NOT skipCommentNodes OR NOT n.comment) THEN { lastSize _ size _ Rope.Size[n.rope]; IF (count _ count-(size-where)) <= 0 THEN RETURN [[n, MAX[0, count+size]]]; lastTxt _ n; count _ count-break; }; [n, ] _ InlineForward[n]; where _ 0; ENDLOOP; IF lastTxt # NIL THEN RETURN [[lastTxt, lastSize]]; -- end of last text node RETURN [[init, 0]]; }; LocWithin: PUBLIC PROC [n: Ref, count: INT, break: NAT _ 1, skipCommentNodes: BOOL _ FALSE] RETURNS [Location] = { RETURN[LocRelative[[n, 0], count, break, skipCommentNodes]] }; BadArgs: PUBLIC ERROR = CODE; LocOffset: PUBLIC PROC [loc1, loc2: Location, break: NAT _ 1, skipCommentNodes: BOOL _ FALSE] RETURNS [count: INT _ 0] = { <> node: Ref _ loc2.node; n: Ref _ loc1.node; count _ IF loc2.where # NodeItself THEN loc2.where ELSE 0; count _ count - MAX[loc1.where, 0]; DO -- add in counts for text nodes before location SELECT n FROM node => RETURN; NIL => ERROR BadArgs; ENDCASE; IF n # NIL AND (NOT skipCommentNodes OR NOT n.comment) THEN { size: INT _ IF n.formatName#$query THEN Rope.Size[n.rope] ELSE 300; count _ count+size+break; }; [n, ] _ RawInlineForward[n]; ENDLOOP; }; LocNumber: PUBLIC PROC [at: Location, break: NAT _ 1, skipCommentNodes: BOOL _ FALSE] RETURNS [count: INT] = { RETURN[LocOffset[[Root[at.node], 0], at, break, skipCommentNodes]] }; <> FirstSibling: PUBLIC PROC [n: Ref] RETURNS [Ref] = { RETURN[FirstChild[Parent[n]]]; }; LastSibling: PUBLIC PROC [n: Ref] RETURNS [Ref] = { IF n=NIL THEN RETURN [NIL]; UNTIL n.last DO n _ n.next ENDLOOP; RETURN[QueryToLastText[n]]; }; FirstChild: PUBLIC PROC [n: Ref] RETURNS [Ref] = { RETURN[IF n=NIL THEN NIL ELSE QueryToText[n.child, n]]; }; LastChild: PUBLIC PROC [n: Ref] RETURNS [Ref] = { RETURN[LastSibling[FirstChild[n]]]; }; LastWithin: PUBLIC PROC [n: Ref] RETURNS [Ref] = { nxt: Ref; IF n=NIL THEN RETURN [NIL]; IF (nxt _ n.child)=NIL THEN RETURN [n]; n _ nxt; DO -- keep going to child of last sibling IF n.last THEN { IF (nxt _ n.child)=NIL THEN RETURN [QueryToLastText[n]]; n _ nxt } ELSE n _ n.next; ENDLOOP; }; LastLocWithin: PUBLIC PROC [n: Ref] RETURNS [Location] = { last: Ref _ LastWithin[n]; where: INT _ IF last # NIL THEN Rope.Size[last.rope] ELSE NodeItself; RETURN [[last, where]]; }; NthChild: PUBLIC PROC [n: Ref, location: INT _ 0] RETURNS [child: Ref] = { <> IF n=NIL OR (child _ n.child)=NIL THEN RETURN; DO IF location=0 THEN RETURN [QueryToText[child]]; child _ QueryToText[child]; IF child.last THEN RETURN [NIL]; child _ child.next; location _ location-1; ENDLOOP; }; NthSibling: PUBLIC PROC [n: Ref, cnt: INT _ 0] RETURNS [Ref] = { <> IF n=NIL THEN RETURN [NIL]; DO IF cnt=0 THEN RETURN [QueryToText[n]]; n _ QueryToText[n]; IF n.last THEN RETURN [NIL]; n _ n.next; cnt _ cnt-1; ENDLOOP; }; CountChildren: PUBLIC PROC [n: Ref] RETURNS [count: INT _ 0] = { child: Ref; IF (child _ FirstChild[n])=NIL THEN RETURN; DO count _ count+1; child _ QueryToText[child]; IF child.last THEN RETURN; child _ child.next; ENDLOOP; }; CountFollowing: PUBLIC PROC [n: Ref] RETURNS [count: INT _ 0] = { IF n=NIL THEN RETURN; UNTIL n.last DO n _ n.next; count _ count+1; n _ QueryToText[n]; ENDLOOP; }; CountToParent: PUBLIC PROC [n: Ref] RETURNS [count: INT _ 0, parent: Ref] = { IF n=NIL THEN RETURN; UNTIL n.last DO n _ n.next; count _ count+1; n _ QueryToText[n]; ENDLOOP; parent _ n.next; parent _ QueryToText[parent]; }; CountToChild: PUBLIC PROC [parent, child: Ref] RETURNS [count: INT _ 0] = { <> n: Ref; IF parent=NIL OR child=NIL THEN RETURN; n _ parent.child; DO SELECT n FROM child => RETURN [count]; NIL => RETURN [MaxLen]; ENDCASE; n _ Next[n]; count _ count+1; ENDLOOP; }; NodeRope: PUBLIC PROC [n: RefTextNode] RETURNS [ROPE] = { RETURN[IF n=NIL THEN NIL ELSE n.rope]; }; NodeRuns: PUBLIC PROC [n: RefTextNode] RETURNS [TextLooks.Runs] = { RETURN[IF n=NIL THEN NIL ELSE n.runs]; }; Props: PUBLIC PROC [n: Ref] RETURNS [NodeProps] = { RETURN[IF n=NIL THEN NIL ELSE n.props]; }; NodeFormat: PUBLIC PROC [n: Ref] RETURNS [ATOM] = { RETURN[IF n=NIL THEN NIL ELSE n.formatName]; }; IsLastSibling: PUBLIC PROC [n: Ref] RETURNS [BOOL] = { RETURN[IF n=NIL THEN FALSE ELSE n.last]; }; EndPos: PUBLIC PROC [n: Ref] RETURNS [INT] = { IF n=NIL THEN RETURN [0]; RETURN [MAX[Rope.Size[n.rope], 1]-1]; }; <> <<>> -- remember last query and its cursor to improve performance lastq: Ref _ NIL; cursor: REF LoganQuery.ComplexCursor _ NIL; QueryToText: PROC [q, p: Ref _ NIL] RETURNS [t: Ref] ~ { <> <> entry: LoganBerry.Entry; IF q=NIL OR q.formatName # $query THEN RETURN[t: q]; <> IF q # lastq THEN { -- get new cursor lastq _ q; cursor _ NARROW[NP.GetProp[q, $LBCursor]]; IF cursor = NIL THEN { cursor _ InitiateQuery[q.rope]; IF cursor = NIL THEN { -- bad query specification q.formatName _ $block; q.rope _ Rope.Concat["Bad Query: ", q.rope]; RETURN[q]; }; NP.PutProp[q, $LBCursor, cursor]; }; }; entry _ LoganQuery.NextEntry[cursor: cursor^]; IF entry = NIL THEN { -- query completed q.formatName _ $block; q.rope _ NIL; RETURN[q]; }; <> t _ NEW[Body _ q^]; -- t inherits q's properties t.child _ NIL; t.formatName _ $block; t.last _ FALSE; t.rope _ EntryToRope[entry]; <> IF p=NIL THEN { p _ q; WHILE NOT p.last DO p _ p.next; ENDLOOP; p _ p.next; -- p is now q's parent IF p.child#q THEN { p _ p.child; -- get to first sibling WHILE p.next # q DO p _ p.next; ENDLOOP; }; }; t.next _ q; IF p.child=q THEN p.child _ t; IF p.next=q THEN p.next _ t; }; QueryToLastText: PROC [q: Ref] RETURNS [t: Ref] ~ { <> <> IF q=NIL OR q.formatName # $query THEN RETURN[t: q]; t _ QueryToText[q]; WHILE t.rope # NIL DO t _ QueryToText[t.next, t]; ENDLOOP; }; InitiateQuery: PROC [rope: Rope.ROPE] RETURNS [c: REF LoganQuery.ComplexCursor] ~ { dbname: Rope.ROPE; ap: LoganQuery.AttributePatterns; s: IO.STREAM _ IO.RIS[rope]; dbname _ IO.GetTokenRope[s, IO.IDProc ! IO.EndOfStream => GOTO End].token; ap _ LoganQuery.ReadAttributePatterns[s ! LoganQuery.SyntaxError => GOTO End]; c _ NEW[LoganQuery.ComplexCursor _ LoganQuery.QueryEntries[db: LoganBerry.Open[dbName: dbname], patterns: ap].cursor]; EXITS End => RETURN[NIL]; }; EntryToRope: PROC [e: LoganBerry.Entry] RETURNS [r: Rope.ROPE] ~ { r _ NIL; FOR l: LoganBerry.Entry _ e, l.rest WHILE l # NIL DO r _ IO.PutFR["%g%g: %g\n", IO.rope[r], IO.atom[l.first.type], IO.rope[l.first.value]]; ENDLOOP; }; END.