-- Test3Impl.mesa -- written by Bill Paxton, April 1981 -- last edit by Bill Paxton, 28-Jul-81 10:12:47 DIRECTORY EditTest, EditSpan, UndoEvent, TextNode, RandomLongInt, TreeCheck; Test3Impl: PROGRAM IMPORTS EditTest, EditSpan, TreeCheck, TextNode, RandomLongInt, UndoEvent EXPORTS EditTest = BEGIN OPEN EditTest, spanI:EditSpan, randLI:RandomLongInt, undoI:UndoEvent, nodeI:TextNode; Span: TYPE = nodeI.Span; -- ***** Node Edit operations PickNodeDest: PROC [tree: Ref] RETURNS [dest: Location, start, loc, size: Offset, where: Place, after, before: Ref] = { [dest, loc, size] ← PickNodeLoc[tree]; IF size=0 THEN { dest ← nodeI.MakeNodeLoc[tree]; loc ← -1; where ← child } ELSE where ← PickPlace[]; IF where#before THEN { after ← nodeI.StepForward[before ← dest.node]; start ← loc+1 } ELSE { before ← nodeI.StepBackward[after ← dest.node]; start ← loc }}; CheckSpan: PROC [span: Span, spanStart, spanLen, treeSize: Offset, beforeSpan, afterSpan: Ref] = { start, len, size: Offset; before, after: Ref; [start, len, size] ← LocateSpan[span]; IF size # treeSize OR start # spanStart OR len # spanLen THEN ERROR; IF (before ← nodeI.StepForward[beforeSpan]) # span.start.node THEN ERROR; IF afterSpan # (after ← nodeI.StepForward[span.end.node]) THEN ERROR }; ReplaceNodes: PUBLIC PROC = { CheckReplace: PROC = { TreeCheck.Verify[destTree]; TreeCheck.Verify[nodeI.Root[dest.start.node]]; CheckSpan[result, destStart, sourceLen, resultSize, beforeDest, afterDest] }; sourceTree, destTree: Ref; source, dest, result: Span; beforeDest, afterDest: Ref; destStart, destLen, destSize, sourceStart, sourceLen, sourceSize: Offset; resultSize: Offset; [sourceTree, destTree] ← PickTrees[]; [source, sourceStart, sourceLen, sourceSize] ← PickNodeSpan[sourceTree]; [dest, destStart, destLen, destSize] ← PickNodeSpan[destTree]; beforeDest ← nodeI.StepBackward[dest.start.node]; afterDest ← nodeI.StepForward[dest.end.node]; undoI.Reset[event]; result ← spanI.Replace[dest, source, FALSE, event]; resultSize ← destSize+sourceLen-destLen; CheckReplace; undoI.Reset[undoEvent]; undoI.Undo[event, undoEvent]; TreeCheck.Verify[destTree]; TreeCheck.Verify[nodeI.Root[result.start.node]]; CheckSpan[dest, destStart, destLen, destSize, beforeDest, afterDest]; undoI.Undo[undoEvent]; -- undo the previous undo CheckReplace; AdjustTree[destTree, resultSize] }; DeleteFromTree: PUBLIC PROC [tree: Ref, size, num: Offset] RETURNS [newsize: Offset] = { newsize ← size; WHILE num > 0 DO start: Location; last, before, after: Ref; len, destLoc, destSize: Offset; [start, destLoc, destSize] ← PickNodeLoc[tree]; IF (len ← MIN[num,destSize-destLoc])=0 THEN ERROR; last ← FindLastNode[start.node, len]; after ← nodeI.StepForward[last]; before ← nodeI.StepBackward[start.node]; [] ← spanI.Delete[[start,nodeI.MakeNodeLoc[last]]]; newsize ← newsize-len; num ← num-len; CheckDelete[tree, before, after, start.node, newsize]; ENDLOOP }; CheckDelete: PROC [destTree, beforeDest, afterDest, inDest: Ref, resultSize: Offset] = { size: Offset; next: Ref; TreeCheck.Verify[destTree]; TreeCheck.Verify[nodeI.Root[inDest]]; IF (size ← TreeSize[destTree]) # resultSize THEN ERROR; IF (next ← nodeI.StepForward[beforeDest]) # afterDest THEN ERROR }; DeleteNodes: PUBLIC PROC = { destTree: Ref; dest: Span; beforeDest, afterDest: Ref; destStart, destLen, destSize: Offset; resultSize: Offset; destTree ← PickTree[]; [dest, destStart, destLen, destSize] ← PickNodeSpan[destTree]; beforeDest ← nodeI.StepBackward[dest.start.node]; afterDest ← nodeI.StepForward[dest.end.node]; undoI.Reset[event]; spanI.Delete[dest, event]; resultSize ← destSize-destLen; CheckDelete[destTree, beforeDest, afterDest, dest.start.node, resultSize]; undoI.Reset[undoEvent]; undoI.Undo[event, undoEvent]; -- undo the delete TreeCheck.Verify[destTree]; CheckSpan[dest, destStart, destLen, destSize, beforeDest, afterDest]; undoI.Undo[undoEvent]; -- undo the previous undo CheckDelete[destTree, beforeDest, afterDest, dest.start.node, resultSize]; AdjustTree[destTree, resultSize] }; InsertInTree: PUBLIC PROC [tree: Ref, size, num: Offset] RETURNS [newsize: Offset] = { newsize ← size; WHILE num > 0 DO newsize ← InsertOne[tree]; num ← num-1; ENDLOOP }; InsertNode: PUBLIC PROC = { tree: Ref ← PickTree[]; size: Offset ← InsertOne[tree]; AdjustTree[tree, size] }; InsertOne: PROC [tree: Ref] RETURNS [size: Offset] = { dest: Location; destStart, destLoc, destSize: Offset; new, afterDest, beforeDest: Ref; where: Place; [dest,destStart,destLoc,destSize,where,afterDest,beforeDest] ← PickNodeDest[tree]; new ← spanI.Insert[dest.node, where]; TreeCheck.Verify[tree]; CheckSpan[nodeI.MakeNodeSpan[new,new], destStart, 1, destSize+1, beforeDest, afterDest]; RETURN [destSize+1] }; CopyNodes: PUBLIC PROC = { sourceTree, destTree: Ref; source, result: Span; dest: Location; beforeDest, afterDest: Ref; destStart, destLoc, destSize, sourceStart, sourceLen, sourceSize: Offset; undoLoc, undoSize, resultSize: Offset; where: Place; [sourceTree, destTree] ← PickTrees[]; [source, sourceStart, sourceLen, sourceSize] ← PickNodeSpan[sourceTree]; [dest,destStart,destLoc,destSize,where,afterDest,beforeDest] ← PickNodeDest[destTree]; undoI.Reset[event]; result ← spanI.Copy[dest, source, FALSE, where, 0, event]; TreeCheck.Verify[destTree]; CheckSpan[result, destStart, sourceLen, resultSize ← destSize+sourceLen, beforeDest, afterDest]; undoI.Reset[undoEvent]; undoI.Undo[event, undoEvent]; -- undo the copy TreeCheck.Verify[destTree]; [undoLoc, undoSize] ← LocateNode[dest.node]; IF undoSize # destSize OR undoLoc # destLoc THEN ERROR; undoI.Undo[undoEvent]; -- undo the previous undo TreeCheck.Verify[destTree]; CheckSpan[result, destStart, sourceLen, resultSize, beforeDest, afterDest]; AdjustTree[destTree, resultSize] }; MoveNodes: PUBLIC PROC = { CheckMove: PROC = { next: Ref; size: Offset; TreeCheck.Verify[destTree]; IF result # source THEN ERROR; IF (next ← nodeI.StepForward[beforeSource]) # afterSource THEN ERROR; IF sourceTree=destTree THEN { resultSize ← sourceSize; resultStart ← IF destStart > sourceStart THEN destStart-sourceLen ELSE destStart } ELSE { TreeCheck.Verify[sourceTree]; IF (size ← TreeSize[sourceTree]) # sourceSize-sourceLen THEN ERROR; resultSize ← destSize+sourceLen; resultStart ← destStart }; CheckSpan[result, resultStart, sourceLen, resultSize, beforeDest, afterDest] }; sourceTree, destTree, beforeDest, afterDest, beforeSource, afterSource: Ref; moveToEnd: BOOLEAN; source, result: Span; dest: Location; sourceStart, sourceLen, sourceSize, destStart, destLoc, destSize: Offset; where: Place; undoLoc, undoSize, resultStart, resultSize: Offset; [sourceTree, destTree] ← PickTrees[]; [source, sourceStart, sourceLen, sourceSize] ← PickNodeSpan[sourceTree]; IF sourceTree=destTree THEN { destSize ← sourceSize; IF moveToEnd←RandomBoolean[] THEN { -- move toward end sourceEnd: Offset ← sourceStart+sourceLen; IF sourceSize=sourceEnd THEN RETURN; -- cannot move toward end where ← IF RandomBoolean[] THEN after ELSE child; destLoc ← randLI.Choose[sourceEnd,sourceSize-1] } ELSE { -- move toward front IF sourceStart=0 THEN RETURN; -- cannot move toward front where ← before; destLoc ← randLI.Choose[0,sourceStart-1] }; dest ← [FindTreeNode[destTree,destLoc],nodeI.NodeItself]; IF where#before THEN { afterDest ← nodeI.StepForward[beforeDest ← dest.node]; destStart ← destLoc+1 } ELSE { beforeDest ← nodeI.StepBackward[afterDest ← dest.node]; destStart ← destLoc }} ELSE [dest,destStart,destLoc,destSize,where,afterDest,beforeDest] ← PickNodeDest[destTree]; afterSource ← nodeI.StepForward[source.end.node]; beforeSource ← nodeI.StepBackward[source.start.node]; undoI.Reset[event]; result ← spanI.Move[dest, source, FALSE, where, 0, event]; CheckMove; undoI.Reset[undoEvent]; undoI.Undo[event, undoEvent]; -- undo the move TreeCheck.Verify[destTree]; IF sourceTree#destTree THEN TreeCheck.Verify[sourceTree]; [undoLoc, undoSize] ← LocateNode[dest.node]; IF undoSize # destSize OR undoLoc # destLoc THEN ERROR; CheckSpan[source, sourceStart, sourceLen, sourceSize, beforeSource, afterSource]; undoI.Undo[undoEvent]; -- undo the previous undo CheckMove; IF sourceTree#destTree THEN { AdjustTree[destTree, destSize+sourceLen]; AdjustTree[sourceTree, sourceSize-sourceLen] }}; MoveNodesOnto: PUBLIC PROC = { CheckMoveOnto: PROC = { IF result # source THEN ERROR; TreeCheck.Verify[destTree]; IF sourceTree = destTree THEN { -- check for overlap or adjacent IF destEnd=sourceStart THEN CheckSpan[result, destStart, sourceLen, sourceSize-destLen, beforeDest, afterSource] ELSE IF sourceEnd=destStart THEN CheckSpan[result, sourceStart, sourceLen, sourceSize-destLen, beforeSource, afterDest] ELSE IF sourceStart IN [destStart..destEnd) THEN IF sourceEnd IN [destStart..destEnd] THEN CheckSpan[result, destStart, sourceLen, sourceSize+sourceLen-destLen, beforeDest, afterDest] ELSE CheckSpan[result, destStart, sourceLen, sourceSize+destStart-sourceStart, beforeDest, afterSource] ELSE IF sourceEnd IN [destStart..destEnd) THEN CheckSpan[result, sourceStart, sourceLen, sourceSize+sourceEnd-destEnd, beforeSource, afterDest] ELSE IF sourceStart < destStart THEN IF sourceEnd >= destEnd THEN CheckSpan[result, sourceStart, sourceLen, sourceSize, beforeSource, afterSource] ELSE CheckSpan[result, destStart-sourceLen, sourceLen, sourceSize-destLen, beforeDest, afterDest] ELSE CheckSpan[result, destStart, sourceLen, sourceSize-destLen, beforeDest, afterDest] } ELSE { size: Offset; next: Ref; TreeCheck.Verify[sourceTree]; IF (size ← TreeSize[sourceTree]) # sourceSize-sourceLen THEN ERROR; IF (next ← nodeI.StepForward[beforeSource]) # afterSource THEN ERROR; CheckSpan[result, destStart, sourceLen, destSize+sourceLen-destLen, beforeDest, afterDest] }}; sourceTree, destTree: Ref; source, dest, result: Span; beforeDest, afterDest, beforeSource, afterSource: Ref; destStart, destLen, destSize, sourceStart, sourceLen, sourceSize: Offset; destEnd, sourceEnd: Offset; [sourceTree, destTree] ← PickTrees[]; [source, sourceStart, sourceLen, sourceSize] ← PickNodeSpan[sourceTree]; sourceEnd ← sourceStart+sourceLen; [dest, destStart, destLen, destSize] ← PickNodeSpan[destTree]; destEnd ← destStart+destLen; beforeDest ← nodeI.StepBackward[dest.start.node]; afterDest ← nodeI.StepForward[dest.end.node]; beforeSource ← nodeI.StepBackward[source.start.node]; afterSource ← nodeI.StepForward[source.end.node]; undoI.Reset[event]; result ← spanI.MoveOnto[dest, source, FALSE, event]; CheckMoveOnto; undoI.Reset[undoEvent]; undoI.Undo[event, undoEvent]; -- undo the move TreeCheck.Verify[destTree]; IF sourceTree # destTree THEN TreeCheck.Verify[sourceTree]; CheckSpan[dest, destStart, destLen, destSize, beforeDest, afterDest]; CheckSpan[source, sourceStart, sourceLen, sourceSize, beforeSource, afterSource]; undoI.Undo[undoEvent]; -- undo the previous undo CheckMoveOnto; IF sourceTree # destTree THEN { AdjustTree[destTree, destSize+sourceLen-destLen]; AdjustTree[sourceTree, sourceSize-sourceLen] } ELSE AdjustTree[destTree, TreeSize[destTree]] }; TransposeNodes: PUBLIC PROC = { CheckTranspose: PROC = { TreeCheck.Verify[alphaTree]; IF alphaTree = betaTree THEN { IF ~alphaFirst THEN { CheckSpan[alpha, betaStart, alphaLen, alphaSize, beforeBeta, afterBeta]; CheckSpan[beta, alphaStart+alphaLen-betaLen, betaLen, alphaSize, beforeAlpha, afterAlpha] } ELSE { CheckSpan[alpha, betaStart-alphaLen+betaLen, alphaLen, alphaSize, beforeBeta, afterBeta]; CheckSpan[beta, alphaStart, betaLen, alphaSize, beforeAlpha, afterAlpha] }} ELSE { TreeCheck.Verify[betaTree]; CheckSpan[alpha, betaStart, alphaLen, betaSize+alphaLen-betaLen, beforeBeta, afterBeta]; CheckSpan[beta, alphaStart, betaLen, alphaSize+betaLen-alphaLen, beforeAlpha, afterAlpha] }}; alphaTree, betaTree: Ref; alpha, beta, alphaResult, betaResult: Span; beforeAlpha, afterAlpha, beforeBeta, afterBeta: Ref; alphaStart, alphaLen, alphaSize, betaStart, betaLen, betaSize: Offset; alphaEnd, betaEnd: Offset; alphaFirst: BOOLEAN; [alphaTree, betaTree] ← PickTrees[]; [alpha, alphaStart, alphaLen, alphaSize] ← PickNodeSpan[alphaTree]; alphaEnd ← alphaStart+alphaLen; beforeAlpha ← nodeI.StepBackward[alpha.start.node]; afterAlpha ← nodeI.StepForward[alpha.end.node]; IF alphaTree = betaTree THEN { betaFirst, betaLast: Ref; betaSize ← alphaSize; IF alphaFirst←RandomBoolean[] THEN { IF alphaEnd+1 >= alphaSize THEN RETURN; -- nothing after alpha [betaStart,betaEnd] ← ChooseTwo[alphaEnd+1,alphaSize] } ELSE { -- pick beta section to left of alpha IF alphaStart <= 1 THEN RETURN; -- nothing before alpha [betaStart,betaEnd] ← ChooseTwo[0,alphaStart-1] }; IF (betaLen ← betaEnd-betaStart) = 0 THEN RETURN; betaFirst ← FindTreeNode[betaTree, betaStart]; betaLast ← FindTreeNode[betaTree, betaEnd-1]; beta ← nodeI.MakeNodeSpan[betaFirst, betaLast] } ELSE [beta, betaStart, betaLen, betaSize] ← PickNodeSpan[betaTree]; beforeBeta ← nodeI.StepBackward[beta.start.node]; afterBeta ← nodeI.StepForward[beta.end.node]; undoI.Reset[event]; [alphaResult,betaResult] ← spanI.Transpose[alpha, beta, FALSE, event]; IF alpha # alphaResult OR beta # betaResult THEN ERROR; CheckTranspose; undoI.Reset[undoEvent]; undoI.Undo[event, undoEvent]; -- undo the transpose TreeCheck.Verify[alphaTree]; IF alphaTree # betaTree THEN TreeCheck.Verify[betaTree]; CheckSpan[alpha, alphaStart, alphaLen, alphaSize, beforeAlpha, afterAlpha]; CheckSpan[beta, betaStart, betaLen, betaSize, beforeBeta, afterBeta]; undoI.Undo[undoEvent]; -- undo the previous undo CheckTranspose; IF alphaTree # betaTree THEN { AdjustTree[alphaTree, alphaSize+betaLen-alphaLen]; AdjustTree[betaTree, betaSize+alphaLen-betaLen] } ELSE AdjustTree[alphaTree, alphaSize] }; SplitNode: PUBLIC PROC = { CheckSplit: PROC = { TreeCheck.Verify[destTree]; CheckSpan[nodeI.MakeNodeSpan[newNode,newNode], destLoc, 1, destSize+1, before, splitNode] }; destTree: Ref ← PickTree[]; destLoc, destSize, splitLoc: Offset; dest: Location; splitNode: Node; before, after, newNode: Ref; [dest, destLoc, destSize] ← PickNodeLoc[destTree]; after ← nodeI.StepForward[dest.node]; before ← nodeI.StepBackward[dest.node]; splitLoc ← PickOne[splitNode ← nodeI.NarrowToTextNode[dest.node]]; undoI.Reset[event]; newNode ← spanI.Split[[splitNode,splitLoc], event]; CheckSplit; undoI.Reset[undoEvent]; undoI.Undo[event, undoEvent]; -- undo the split TreeCheck.Verify[destTree]; CheckSpan[nodeI.MakeNodeSpan[splitNode,splitNode], destLoc, 1, destSize, before, after]; undoI.Undo[undoEvent]; -- undo the undo CheckSplit; AdjustTree[destTree, destSize+1] }; MergeNodes: PUBLIC PROC = { CheckMerge: PROC = { TreeCheck.Verify[destTree]; IF mergeNode # before THEN ERROR; CheckSpan[nodeI.MakeNodeSpan[mergeNode,mergeNode], destLoc-1, 1, destSize-1, nodeI.StepBackward[before], after] }; destTree: Ref ← PickTree[]; destLoc, destSize: Offset; dest: Location; before, after, destNode, mergeNode: Ref; mergeLoc: Location; [dest, destLoc, destSize] ← PickNodeLoc[destTree]; IF destLoc=0 THEN RETURN; -- no predecessor to merge with destNode ← dest.node; after ← nodeI.StepForward[destNode]; before ← nodeI.StepBackward[destNode]; undoI.Reset[event]; mergeLoc ← spanI.Merge[destNode, event]; mergeNode ← mergeLoc.node; CheckMerge; undoI.Reset[undoEvent]; undoI.Undo[event, undoEvent]; -- undo the merge TreeCheck.Verify[destTree]; CheckSpan[nodeI.MakeNodeSpan[destNode,destNode], destLoc, 1, destSize, before, after]; undoI.Undo[undoEvent]; -- undo the undo CheckMerge; AdjustTree[destTree, destSize-1] }; NestNodes: PUBLIC PROC = { ChangeNesting[1] }; UnNestNodes: PUBLIC PROC = { ChangeNesting[-1] }; ChangeNesting: PROC [change: INTEGER] = { destTree: Ref; dest, result: Span; before, after: Ref; destStart, destLen, destSize: Offset; destTree ← PickTree[]; [dest, destStart, destLen, destSize] ← PickNodeSpan[destTree]; before ← nodeI.StepBackward[dest.start.node]; after ← nodeI.StepForward[dest.end.node]; undoI.Reset[event]; result ← spanI.ChangeNesting[dest, change, event]; IF result # dest THEN ERROR; CheckSpan[dest, destStart, destLen, destSize, before, after]; undoI.Reset[undoEvent]; undoI.Undo[event, undoEvent]; -- undo the change in nesting TreeCheck.Verify[destTree]; CheckSpan[dest, destStart, destLen, destSize, before, after]; undoI.Undo[undoEvent]; -- undo the previous undo CheckSpan[dest, destStart, destLen, destSize, before, after] }; END.