dps/src/Linked2Tree.m3


Copyright (C) 1994, Digital Equipment Corp.

MODULE Linked2Tree;

PROCEDURE Prepend (t: T; e: E) =
  BEGIN
  LOCK t DO PrependWhileLocked (t, e); END;
  END Prepend;

PROCEDURE PrependWhileLocked (t: T; e: E) =
  BEGIN
  <* ASSERT (e.parent = NIL) *>  e.parent := t;
  IF t.firstChild=NIL THEN
    t.firstChild := e; t.lastChild := e;
    e.nextSibling := NIL; e.previousSibling := NIL;
   ELSE
    t.firstChild.previousSibling := e; e.nextSibling := t.firstChild;
    e.previousSibling := NIL; t.firstChild := e;
    END;
  END PrependWhileLocked;

PROCEDURE Append (t: T; e: E) =
  BEGIN
  LOCK t DO AppendWhileLocked (t, e); END;
  END Append;

PROCEDURE AppendWhileLocked (t: T; e: E) =
  BEGIN
  <* ASSERT (e.parent = NIL) *>  e.parent := t;
  IF t.firstChild=NIL THEN
    t.firstChild := e; t.lastChild := e;
    e.nextSibling := NIL; e.previousSibling := NIL;
   ELSE
    t.lastChild.nextSibling := e; e.previousSibling := t.lastChild;
    e.nextSibling := NIL; t.lastChild := e;
    END;
  END AppendWhileLocked;

PROCEDURE InsertBefore (t: T; e, before: E) =
  BEGIN
  LOCK t DO InsertBeforeWhileLocked (t, e, before); END;
  END InsertBefore;

PROCEDURE InsertBeforeWhileLocked (t: T; e, before: E) =
  BEGIN
  IF before = NIL THEN AppendWhileLocked (t, e); RETURN; END;
  <* ASSERT (before.parent = t) *>
  IF before.previousSibling=NIL THEN PrependWhileLocked (t, e); RETURN; END;
  <* ASSERT (e.parent = NIL) *>  e.parent := t;
  before.previousSibling.nextSibling := e;
  e.previousSibling := before.previousSibling; e.nextSibling := before;
  before.previousSibling := e;
  END InsertBeforeWhileLocked;

PROCEDURE InsertAfter (t: T; e, after: E) =
  BEGIN
  LOCK t DO InsertAfterWhileLocked (t, e, after); END;
  END InsertAfter;

PROCEDURE InsertAfterWhileLocked (t: T; e, after: E) =
  BEGIN
  IF after = NIL THEN AppendWhileLocked (t, e); RETURN; END;
  <* ASSERT (after.parent = t) *>
  IF after.nextSibling=NIL THEN AppendWhileLocked (t, e); RETURN; END;
  <* ASSERT (e.parent = NIL) *>  e.parent := t;
  after.nextSibling.previousSibling := e;
  e.previousSibling := after; e.nextSibling := after.nextSibling;
  after.nextSibling := e;
  END InsertAfterWhileLocked;

PROCEDURE Remove (e: E) =
 VAR t: T;
  BEGIN
  t := e.parent;
  t.RemoveChild (e);
  END Remove;

PROCEDURE RemoveChild (t: T; e: E) =
  BEGIN
  RemoveInternal (t, e);
  END RemoveChild;

PROCEDURE RemoveInternal (t: T; e: E) =
  BEGIN
  LOCK t DO RemoveWhileLocked (t, e); END;
  END RemoveInternal;

PROCEDURE RemoveWhileLocked (t: T; e: E) =
 VAR done: BOOLEAN;
 VAR past: E;
  BEGIN
  e.parent := NIL;
  done := FALSE;
  IF t.firstChild=e THEN
    t.firstChild := e.nextSibling;
    IF t.firstChild#NIL THEN t.firstChild.previousSibling := NIL; END;
    done := TRUE;
    END;
  IF t.lastChild=e THEN
    t.lastChild := e.previousSibling;
    IF t.lastChild#NIL THEN t.lastChild.nextSibling := NIL; END;
    done := TRUE;
    END;
  IF NOT done THEN
    past := e.nextSibling;
    e.nextSibling.previousSibling := e.previousSibling;
    e.previousSibling.nextSibling := past;
    END;
  END RemoveWhileLocked;

PROCEDURE First (t: T): E =
  BEGIN
  LOCK t DO RETURN t.firstChild; END;
  END First;

PROCEDURE Next (e: E): E =
  BEGIN (* Should lock e.parent? *)
  LOCK e DO RETURN e.nextSibling; END;
  END Next;

PROCEDURE Last (t: T): E =
  BEGIN
  LOCK t DO RETURN t.lastChild; END;
  END Last;

PROCEDURE Previous (e: E): E =
  BEGIN (* Should lock e.parent? *)
  LOCK e DO RETURN e.previousSibling; END;
  END Previous;

PROCEDURE InternalMoveToFirst (t: T; e: E): E =
 BEGIN
  LOCK t DO
    IF e = t.firstChild THEN RETURN NIL; END;
    RemoveWhileLocked (t, e);
    PrependWhileLocked (t, e);
    RETURN e.parent; (* Hmmm. *)
    END;
  END InternalMoveToFirst;

PROCEDURE InternalMoveToLast (t: T; e: E): E =
  BEGIN
  LOCK t DO
    IF e = t.lastChild THEN RETURN NIL; END;
    RemoveWhileLocked (t, e);
    AppendWhileLocked (t, e);
    RETURN e;
    END;
  END InternalMoveToLast;

PROCEDURE MoveToFirst (e: E): E =
 VAR p: T;
  BEGIN
  p := e.parent;
  IF p=NIL THEN RETURN NIL; ELSE RETURN p.MakeChildFirst(e); END;
  END MoveToFirst;

PROCEDURE MakeChildFirst (t: T; e: E): E =
  BEGIN
  RETURN InternalMoveToFirst(t, e);
  END MakeChildFirst;

PROCEDURE MoveToLast (e: E): E =
 VAR p: T;
  BEGIN
  p := e.parent;
  IF p=NIL THEN RETURN NIL; ELSE RETURN p.MakeChildLast(e); END;
  END MoveToLast;

PROCEDURE MakeChildLast (t: T; e: E): E =
  BEGIN
  RETURN InternalMoveToLast(t, e);
  END MakeChildLast;

  BEGIN

  END Linked2Tree.