PipalOps.mesa 
Copyright Ó 1988 by Xerox Corporation. All rights reserved.
Bertrand Serlet May 17, 1988 2:26:51 pm PDT
Louis Monier January 25, 1988 9:41:33 am PST
Barth, January 29, 1988 3:42:40 pm PST
DIRECTORY
Pipal, PipalInt, PipalReal,
RefTab;
PipalOps: CEDAR DEFINITIONS = BEGIN
Theory
This module contains many low level operations which depend upon enumeration.
Enumeration
enumerateMethod: Pipal.Method;
EachChildProc: TYPE = PROC [child: Pipal.Object] RETURNS [quit: BOOLFALSE];
EnumerateProc: TYPE = PROC [object: Pipal.Object, each: EachChildProc] RETURNS [quit: BOOLFALSE];
Enumerates all children of an object.
Order of enumeration should be the same for to successive calls.
HasEnumerate: PROC [object: Pipal.Object] RETURNS [BOOL];
Enumerate: EnumerateProc;
Short cut for applying the enumerate method. If none then try PipalInt.Enumerate and then PipalReal.Enumerate.
If both PipalOps and PipalInt (or a PipalReal) enumerations are supplied, the PipalOps enumeration should include all the objects enumerated by the PipalInt (or a PipalReal) enumeration.
CountChildren: PROC [object: Pipal.Object] RETURNS [count: NAT ← 0];
NthChild: PROC [object: Pipal.Object, rank: NAT ← 0] RETURNS [nthChild: Pipal.Object ← NIL];
Returns the Nth child, 0 being of course the first child, NIL if no such child.
Recast: PROC [object: Pipal.Object] RETURNS [Pipal.Object];
Information-lossy way to convert to a "simpler" object.
HashByEnumeration: Pipal.HashProc;
Uses Enumerate on children to compute hash.
CachedHashByEnumeration: Pipal.HashProc;
Same as previous, but caches its result.
EqualByEnumeration: Pipal.EqualProc;
Uses Enumerate on children to decide whether equal.
Transitive Closure
Transitive closure is a common operation that has to be efficient.
Reachable: PROC [root, candidate: Pipal.Object] RETURNS [BOOL];
Is candidate object reachable by enumeration from the root object?
AnyReachable: PROC [root: Pipal.Object, table: RefTab.Ref] RETURNS [BOOL];
Are any keys in table reachable by enumeration from the root object?
Replacement
replaceMethod: Pipal.Method;
MapProc: TYPE = PROC [old: Pipal.Object] RETURNS [new: Pipal.Object ← NIL];
new=old means no new object
ReplaceProc: TYPE = PROC [parent: Pipal.Object, map: MapProc] RETURNS [newParent: Pipal.Object];
Replaces each child of parent which is mapped. TransitiveReplace guarantees that such a function will only be called if some child of parent is in map.
A ReplaceProc can decide that even though some child changed, newParent is the same as parent. This is typically the case for objects which capture the notion of interface.
Some child of parent should always be found in table although the implementation is not required to check this. Every source object class must implement this method.
Replace: ReplaceProc;
Simply applies the replace method once.
No replace method => crash and burn.
Does not propagate events nor do transitive closure. For use only with care.
ReplaceFromRecast: ReplaceProc;
Information-lossy way to replace by replacing with the recasted object. Never use this for source objects!
ReplaceTable: TYPE = RefTab.Ref;
ReplaceTables map old objects to new ones.
ReplaceWithTable: PUBLIC PROC [oldRoot: Pipal.Object, table: ReplaceTable];
Same as Replace, but takes a table for convenience.
TransitiveReplace: PROC [root: Pipal.Object, table: ReplaceTable];
table maps old objects to new ones.
Replaces each old child by the corresponding new child everywhere reachable from root.
Recursively replaces each parent and puts them into table.
Path
This section supports the specification of individual objects from a root.
Path: TYPE = PRIVATE LIST OF PathBit;
PathBit: TYPE = PRIVATE RECORD [type: PathBitType, rank: NAT];
PathBitType: TYPE = {ops, real, int};
NIL designates the root.
The list specifies the child of the root, then the child of the former, etc ...
A more compact representation would be easy to introduce, of course.
ConcatPath: PROC [rootPath, childPath: Path] RETURNS [newPath: Path];
ExtendPath: PROC [currentPath: Path, type: PathBitType, rank: NAT] RETURNS [newPath: Path];
ApplyPath: PROC [root: Pipal.Object, path: Path] RETURNS [child: Pipal.Object];
ApplyRealPath: PROC [root: Pipal.Object, path: Path, transformation: PipalReal.Transformation] RETURNS [trans: PipalReal.Transformation, child: Pipal.Object];
Only applicable if path#NIL AND path.first.type=real.
ApplyIntPath: PROC [root: Pipal.Object, path: Path, transformation: PipalInt.Transformation] RETURNS [trans: PipalInt.Transformation, child: Pipal.Object];
Only applicable if path#NIL AND path.first.type=int.
ReplaceInPathWithTable: PROC [root: Pipal.Object, path: Path, table: ReplaceTable];
ReplaceInPath: PROC [root: Pipal.Object, path: Path, oldChild, newChild: Pipal.Object] RETURNS [table: ReplaceTable];
Replaces all the objects on the path from root to oldChild, and only those ones.
ReplaceWithTable is called (no broadcast). A table is built during that descent.
FindOpsPath: PROC [root, searched: Pipal.Object] RETURNS [path: Path, found: BOOL];
Returns the first ops path from root to child found, even if several would do.
FindRealPath: PROC [rootTrans: PipalReal.Transformation, root: Pipal.Object, searchedTrans: PipalReal.Transformation, searched: Pipal.Object] RETURNS [path: Path, found: BOOL];
Returns the first real path from root to child found, even if several would do.
FindIntPath: PROC [rootTrans: PipalInt.Transformation, root: Pipal.Object, searchedTrans: PipalInt.Transformation, searched: Pipal.Object] RETURNS [path: Path, found: BOOL];
Returns the first int path from root to child found, even if several would do.
Events and Notification
Events are analogoud to software interrupts. Some procedure might raise an event, which will cause all the registered handlers to be called.
WatchDog: TYPE = PROC [arg1, arg2, arg3: REFNIL, registrationData: REFNIL];
arg1, arg2, arg3: event-specific parameters passed through from Notify.
registrationData: as given at registration.
Can watchdog be applied twice?
RegisterWatchDog: PROC [event: ATOM, dog: WatchDog, registrationData: REFNIL];
Registers a procedure which is called when a specific event occurs.
ForgetWatchDog: PROC [event: ATOM, dog: WatchDog, registrationData: REFNIL, equal: PROC [REF, REF] RETURNS [BOOL] ← NIL];
Forgets a previously registered procedure. All parameters must be "equal" to registration parameters. If equal predicate is not given, pointer equality is assumed.
Broadcast: PROC [event: ATOM, arg1, arg2, arg3: REFNIL];
Calls all registered WatchDogs.
WatchDogs for a given event may be applied in any order.
Replacement Event
The replace event is called for all global replacement of objects.
replaceEvent: READONLY ATOM;
For that event, arg1 is a ReplaceTable.
ReplaceWatchDog: TYPE = PROC [root: Pipal.Object, table: ReplaceTable];
arg1 = table
registrationData = parent
RegisterReplaceWatchDog: PROC [root: Pipal.Object, dog: ReplaceWatchDog];
Registers a replace WatchDog called whenever replace is broadcast.
Short for RegisterWatchDog[replaceEvent, dog, parent] with the appropriate type convertions.
ForgetReplaceWatchDog: PROC [root: Pipal.Object, dog: ReplaceWatchDog];
Forgets a previously registered replace WatchDog. Parent must be identical to registration parent.
Short for ForgetWatchDog[replaceEvent, dog, parent] with the appropriate type convertions.
BroadcastReplace: PROC [table: ReplaceTable];
This procedure calls Broadcast.
TransitiveReplace should be used for local replacement.
Undo / Redo facilities
Event: TYPE = RECORD [message: Pipal.ROPE, state: REF];
Events: TYPE = LIST OF Event;
UndoRedo: TYPE = RECORD [
undo: Events ← NIL, -- first undoable event head of list
redo: Events ← NIL]; -- first redoable event head of list
Do: PROC [old: UndoRedo, message: Pipal.ROPE, oldState: REF] RETURNS [new: UndoRedo];
Empties the redo list and stacks a new event containing state on the undo list.
UndoRedoOp: TYPE = PROC [old: UndoRedo, oldState: REF] RETURNS [new: UndoRedo, newState: REF];
Undo: UndoRedoOp;
Undoes the last action, if any.
Redo: UndoRedoOp;
Redoes the next action, if any.
Reset: UndoRedoOp;
Undoes while possible.
Hacks
wDir: Pipal.ROPE;
Directory where Pipal has been started.
Shame on Cedar!
END.