-- DependenciesImpl.mesa -- last edit July 13, 1984 1:18:59 pm PDT Sturgis DIRECTORY Dependencies USING[DependencySet, DependencySetBody, Action, ActionBody, DataItem, DataItemBody, ListCell, ListCellBody]; DependenciesImpl: PROGRAM EXPORTS Dependencies = BEGIN OPEN Dependencies; freeListCells: ListCell ← NIL; debugging: BOOLEAN ← FALSE; CreateDependencySet: PUBLIC PROCEDURE RETURNS[DependencySet] = {RETURN[NEW[DependencySetBody ← [FALSE, NIL, NIL, FALSE, NIL, NIL]]]}; DefineAction: PUBLIC PROCEDURE[inSet: DependencySet, act: PROC[Action, REF ANY], actInfo: REF ANY] RETURNS[Action] = BEGIN action: Action ← NEW[ActionBody ← [ act: act, info: actInfo, dirty: FALSE, dependentData: NIL, firstDataList: NIL, set: inSet, sorting: FALSE, sorted: FALSE, isSource: FALSE, prevUnsorted: NIL, nextUnsorted: inSet.unSortedList, prevSorted: NIL, nextSorted: NIL]]; IF inSet.unSortedList # NIL THEN inSet.unSortedList.prevUnsorted ← action; inSet.unSortedList ← action; inSet.changed ← TRUE; Verify[inSet]; RETURN[action]; END; DefineDataItem: PUBLIC PROCEDURE[inSet: DependencySet] RETURNS[DataItem] = BEGIN dataItem: DataItem ← NEW[DataItemBody ← [ dirty: FALSE, set: inSet, nextItem: inSet.dataItems, prevItem: NIL, nextDirtyItem: NIL, prevDirtyItem: NIL, dependentActions: NIL, firstActionList: NIL ]]; IF inSet.dataItems # NIL THEN inSet.dataItems.prevItem ← dataItem; inSet.dataItems ← dataItem; inSet.changed ← TRUE; Verify[inSet]; RETURN[dataItem]; END; NoteThatDataItemIsReadByAction: PUBLIC PROCEDURE[dataItem: DataItem, action: Action] = BEGIN cell: ListCell; IF dataItem = NIL OR action = NIL THEN RETURN; IF dataItem.set # action.set THEN MisMatchedDependencySets; IF freeListCells # NIL THEN {cell ← freeListCells; freeListCells ← cell.nextOnList} ELSE cell ← NEW[ListCellBody]; cell↑ ← [ action: action, dataItem: dataItem, prevOnList: NIL, nextOnList: dataItem.dependentActions, prevList: NIL, nextList: action.firstDataList ]; IF dataItem.dependentActions # NIL THEN dataItem.dependentActions.prevOnList ← cell; dataItem.dependentActions ← cell; IF action.firstDataList # NIL THEN action.firstDataList.prevList ← cell; action.firstDataList ← cell; dataItem.set.changed ← TRUE; END; NoteThatActionModifiesDataItem: PUBLIC PROCEDURE[action: Action, dataItem: DataItem] = BEGIN cell: ListCell; IF dataItem = NIL OR action = NIL THEN RETURN; IF dataItem.set # action.set THEN MisMatchedDependencySets; IF freeListCells # NIL THEN {cell ← freeListCells; freeListCells ← cell.nextOnList} ELSE cell ← NEW[ListCellBody]; cell↑ ← [ action: action, dataItem: dataItem, prevOnList: NIL, nextOnList: action.dependentData, prevList: NIL, nextList: dataItem.firstActionList ]; IF action.dependentData # NIL THEN action.dependentData.prevOnList ← cell; action.dependentData ← cell; IF dataItem.firstActionList # NIL THEN dataItem.firstActionList.prevList ← cell; dataItem.firstActionList ← cell; action.set.changed ← TRUE; Verify[action.set]; END; DeactivateAction: PUBLIC PROCEDURE[action: Action] = BEGIN set: DependencySet; nextCell: ListCell ← NIL; IF action = NIL THEN RETURN; set ← action.set; -- remove the action from the set lists IF set.sortedList = action THEN set.sortedList ← action.nextSorted; IF action.prevSorted # NIL THEN action.prevSorted.nextSorted ← action.nextSorted; IF action.nextSorted # NIL THEN action.nextSorted.prevSorted ← action.prevSorted; IF (set.unSortedList = action) # (action.prevUnsorted = NIL) THEN ERROR; IF set.unSortedList = action THEN set.unSortedList ← action.nextUnsorted; IF action.prevUnsorted # NIL THEN action.prevUnsorted.nextUnsorted ← action.nextUnsorted; IF action.nextUnsorted # NIL THEN action.nextUnsorted.prevUnsorted ← action.prevUnsorted; -- now remove the action from all dependent lists hanging off of data items FOR cell: ListCell ← action.firstDataList, nextCell WHILE cell # NIL DO nextCell ← cell.nextList; IF cell.prevOnList = NIL THEN cell.dataItem.dependentActions ← cell.nextOnList; RemoveCell[cell]; ENDLOOP; -- now erase the list of dependent data items FOR cell: ListCell ← action.dependentData, nextCell WHILE cell # NIL DO nextCell ← cell.nextOnList; IF cell.prevList = NIL THEN cell.dataItem.firstActionList ← cell.nextList; RemoveCell[cell]; ENDLOOP; action↑ ← [ act: NIL, info: NIL, dirty: FALSE, dependentData: NIL, firstDataList: NIL, set: NIL, sorting: FALSE, sorted: FALSE, isSource: FALSE, prevUnsorted: NIL, nextUnsorted: NIL, prevSorted: NIL, nextSorted: NIL]; set.changed ← TRUE; Verify[set]; END; DeactivateDataItem: PUBLIC PROCEDURE[dataItem: DataItem] = BEGIN set: DependencySet; nextCell: ListCell ← NIL; IF dataItem = NIL THEN RETURN; set ← dataItem.set; -- remove the data item from the set lists IF set.dataItems = dataItem THEN set.dataItems ← dataItem.nextItem; IF dataItem.prevItem # NIL THEN dataItem.prevItem.nextItem ← dataItem.nextItem; IF dataItem.nextItem # NIL THEN dataItem.nextItem.prevItem ← dataItem.prevItem; IF set.dirtyDataItems = dataItem THEN set.dirtyDataItems ← dataItem.nextDirtyItem; IF dataItem.prevDirtyItem # NIL THEN dataItem.prevDirtyItem.nextDirtyItem ← dataItem.nextDirtyItem; IF dataItem.nextDirtyItem # NIL THEN dataItem.nextDirtyItem.prevDirtyItem ← dataItem.prevDirtyItem; -- now remove the data item from all dependent lists hanging off of actions FOR cell: ListCell ← dataItem.firstActionList, nextCell WHILE cell # NIL DO nextCell ← cell.nextList; IF cell.prevOnList = NIL THEN cell.action.dependentData ← cell.nextOnList; RemoveCell[cell]; ENDLOOP; -- now erase the list of dependent actions FOR cell: ListCell ← dataItem.dependentActions, nextCell WHILE cell # NIL DO nextCell ← cell.nextOnList; IF cell.prevList = NIL THEN cell.action.firstDataList ← cell.nextList; RemoveCell[cell]; ENDLOOP; dataItem↑ ← [ dirty: FALSE, set: NIL, nextItem: NIL, prevItem: NIL, nextDirtyItem: NIL, prevDirtyItem: NIL, dependentActions: NIL, firstActionList: NIL]; set.changed ← TRUE; Verify[set]; END; SortDependencySet: PUBLIC PROCEDURE[set: DependencySet] = BEGIN IF NOT set.changed THEN RETURN; set.sortedList ← NIL; FOR action: Action ← set.unSortedList, action.nextUnsorted WHILE action # NIL DO action.isSource ← TRUE; -- tentative action.sorting ← action.sorted ← FALSE; ENDLOOP; FOR action: Action ← set.unSortedList, action.nextUnsorted WHILE action # NIL DO MarkNotSource: PROC[act: Action] = {act.isSource ← FALSE}; EnumerateActionDependencies[action, MarkNotSource]; ENDLOOP; FOR action: Action ← set.unSortedList, action.nextUnsorted WHILE action # NIL DO IF action.isSource THEN SortOneUnsortedActionAndDependencies[action]; ENDLOOP; FOR action: Action ← set.unSortedList, action.nextUnsorted WHILE action # NIL DO IF NOT action.sorted THEN ERROR LoopInDependencyGraph; ENDLOOP; FOR dItem: DataItem ← set.dataItems, dItem.nextItem WHILE dItem # NIL DO IF dItem.firstActionList # NIL AND dItem.firstActionList.nextList # NIL THEN ERROR MultipleAssignmentToOneDataItem[]; ENDLOOP; set.changed ← FALSE; END; MarkDataItemDirty: PUBLIC PROCEDURE[dataItem: DataItem] = BEGIN IF dataItem = NIL THEN RETURN; IF NOT dataItem.dirty THEN BEGIN dataItem.dirty ← TRUE; dataItem.nextDirtyItem ← dataItem.set.dirtyDataItems; dataItem.set.dirtyDataItems ← dataItem; END; END; MarkActionDirty: PUBLIC PROCEDURE[action: Action] = BEGIN IF action = NIL THEN RETURN; action.dirty ← TRUE; action.set.dirtyActionsExist ← TRUE; END; DataItemIsSource: PUBLIC PROCEDURE[dataItem: DataItem] RETURNS[--yes-- BOOLEAN] = BEGIN RETURN[dataItem.firstActionList = NIL]; END; PerformDirtyActions: PUBLIC PROCEDURE[set: DependencySet] = BEGIN nextDataItem: DataItem; IF set.dirtyDataItems = NIL AND NOT set.dirtyActionsExist THEN RETURN; IF set.changed THEN SortDependencySet[set]; -- first process the dirty data items FOR dataItem: DataItem ← set.dirtyDataItems, nextDataItem WHILE dataItem # NIL DO nextDataItem ← dataItem.nextDirtyItem; dataItem.nextDirtyItem ← NIL; dataItem.dirty ← FALSE; FOR actionCell: ListCell ← dataItem.dependentActions, actionCell.nextOnList WHILE actionCell # NIL DO actionCell.action.dirty ← TRUE; ENDLOOP; ENDLOOP; set.dirtyDataItems ← NIL; -- now process the dirty actions, and subsequent dirty actions FOR action: Action ← set.sortedList, action.nextSorted WHILE action # NIL DO IF action.dirty THEN BEGIN MarkAsDirty: PROC[act: Action] = {act.dirty ← TRUE}; action.act[action, action.info]; EnumerateActionDependencies[action, MarkAsDirty]; action.dirty ← FALSE; END; ENDLOOP; set.dirtyActionsExist ← FALSE; END; MisMatchedDependencySets: PUBLIC ERROR = CODE; LoopInDependencyGraph: PUBLIC ERROR = CODE; MultipleAssignmentToOneDataItem: PUBLIC ERROR = CODE; GetActionInfo: PUBLIC PROCEDURE[action: Action] RETURNS[DependencySet, PROC[Action, REF ANY], REF ANY] = BEGIN RETURN[action.set, action.act, action.info]; END; Verify: PUBLIC PROCEDURE[dSet: DependencySet] = BEGIN prevDataItem: DataItem ← NIL; prevAction: Action ← NIL; IF NOT debugging THEN RETURN; -- scan each data item and see if its lists are ok FOR dataItem: DataItem ← dSet.dataItems, dataItem.nextItem WHILE dataItem # NIL DO IF dataItem.prevItem # prevDataItem THEN ERROR; CheckList[dataItem.dependentActions]; CheckListChain[dataItem.firstActionList]; prevDataItem ← dataItem; ENDLOOP; -- scan each action and see if some of its chains are ok FOR action: Action ← dSet.unSortedList, action.nextUnsorted WHILE action # NIL DO IF action.prevUnsorted # prevAction THEN ERROR; CheckList[action.dependentData]; CheckListChain[action.firstDataList]; prevAction ← action; ENDLOOP; END; -- local procedures RemoveCell: PROCEDURE[cell: ListCell] = BEGIN IF cell.prevOnList # NIL THEN cell.prevOnList.nextOnList ← cell.nextOnList; IF cell.nextOnList # NIL THEN cell.nextOnList.prevOnList ← cell.prevOnList; IF cell.prevList # NIL THEN cell.prevList.nextList ← cell.nextList; IF cell.nextList # NIL THEN cell.nextList.prevList ← cell.prevList; cell↑ ← [ action: NIL, dataItem: NIL, prevOnList: NIL, nextOnList: freeListCells, prevList: NIL, nextList: NIL]; freeListCells ← cell; END; SortOneUnsortedActionAndDependencies: PROCEDURE[action: Action] = BEGIN IF action.sorting THEN LoopInDependencyGraph; IF NOT action.sorted THEN BEGIN action.sorting ← TRUE; EnumerateActionDependencies[action, SortOneUnsortedActionAndDependencies]; action.nextSorted ← action.set.sortedList; action.set.sortedList ← action; action.sorting ← FALSE; action.sorted ← TRUE; END; END; EnumerateActionDependencies: PROCEDURE[action: Action, p: PROC[Action]] = BEGIN FOR dataCell: ListCell ← action.dependentData, dataCell.nextOnList WHILE dataCell # NIL DO FOR actionCell: ListCell ← dataCell.dataItem.dependentActions, actionCell.nextOnList WHILE actionCell # NIL DO p[actionCell.action]; ENDLOOP; ENDLOOP; END; CheckList: PROCEDURE[cell: ListCell] = BEGIN prev: ListCell ← NIL; nSeen: CARDINAL ← 0; FOR cell ← cell, cell.nextOnList WHILE cell # NIL DO IF cell.prevOnList # prev THEN ERROR; IF cell.action = NIL THEN ERROR; IF cell.dataItem = NIL THEN ERROR; prev ← cell; nSeen ← nSeen + 1; IF nSeen = 0 THEN ERROR; ENDLOOP; END; CheckListChain: PROCEDURE[cell: ListCell] = BEGIN prev: ListCell ← NIL; nSeen: CARDINAL ← 0; FOR cell ← cell, cell.nextList WHILE cell # NIL DO IF cell.prevList # prev THEN ERROR; IF cell.action = NIL THEN ERROR; IF cell.dataItem = NIL THEN ERROR; prev ← cell; nSeen ← nSeen + 1; IF nSeen = 0 THEN ERROR; ENDLOOP; END; END.. -- 14-Feb-82 15:09:52: Sturgis, started DependenciesImpl.mesa -- 14-Feb-82 17:07:26: compiles -- 16-Feb-82 17:25:02: re-wrote to match the redesigned interface (that involves explicit recognition of data Items) (about 2 to 3 hours) -- RTE: 19-Feb-82 12:26:11: define data item did not set certain fields, and the compiler defaulted them to NIL!!!, rather than generating an error. -- RTE: 19-Feb-82 13:51:33: PerformDirtyDataActions had wrong sign on the test for the existince of dirty data items. -- change: 4-Mar-82 17:23:26: add GetactionInfo for debugging -- change: 5-Mar-82 11:55:47: add Verify -- RTE: 5-Mar-82 12:04:56: creating an action did not properly insert it on the unSOrtedlist. similar for creating a dataitem. -- RTE: 5-Mar-82 12:22:24: when deactivating items, and removing cells on assorted lists, forgot to handle the case of chaning through the first cell on a list, the pointer in the controlling object must be nilled. -- RTE: 5-Mar-82 12:36:16: prev error got half of the cases, but there are two back pointers that can be nil, not one. -- RTE: 5-Mar-82 13:01:27: add verify calls to many structure change actions. -- RTE: 5-Mar-82 13:25:07: when deleting an action, forgot to nil out its back pointer if it was head of unsorted list, due to an editing error when creating the code by copy. -- Change: 9-Mar-82 16:09:44: add a cehck to see taht all actions are sorted after a sort. Looking for a mysterious bug wherein rows do not recompute when a column is deleted. -- RTE: 9-Mar-82 16:23:20: adding the above check led to a loop. Seems that I had not been marking the actions unsorted before starting the sort, so ol actions did not end up sorted. while adding the check code I also set the actions unserted. This led to the observation that I wasnt nilling the set.sorted list before starting the sort, so the last sorted action ended up pointing to the first old (and unsroted) action. -- RTE: 11-Mar-82 17:31:26: the deactivate code worked incorrectly when removing a cell from the first of a list, it set the pointer in the action or data item hoding the list to NIL, rather than to the next cell in the appropriate list. -- change: 12-Mar-82 10:40:48: add MarkActionDirty, and DataItemIsSource -- RTE: 12-Mar-82 12:49:10: got the sign wrong on DataItemIsSOurce. -- RTE: August 2, 1982 4:04 pm: allow Deactivate routines to accept NIL items. -- change: August 2, 1982 4:16 pm: notice any changes to the set, and if set is marked changed when PerformDirtyActions is called, first sort the set. Also, modify sort procedure to return if no changes have been made. -- RTE: August 8, 1982 3:34 pm: generate LoopInDependencyGraph as expected, rather than simply ERROR, when finding such a loop. -- August 8, 1982 3:59 pm: add catch for multiple assignment to one data item. -- change: October 6, 1982 3:05 pm: add a global flag to turn the verifier on and off. Nominally off. -- change: November 13, 1982 3:28 pm: tighten up verify to check for nil entries in cells. -- RTE: November 13, 1982 3:49 pm: When deactivating a data item, it remained on the dirtyDataItems chain (no back pointer to remove it with). Moreover, we left its dependentActions list pointing into the FreeCells chain, because the cells on its dependentAction list had been "freed", and we did not nil the corresponding pointer in the DataItem. -- March 2, 1983 9:18 am: make sure deactivation routines nil out all pointers. -- Change: July 13, 1984 1:18:17 pm PDT: after placing many nil tests in client code, I have finally decided to put the NIL tests in the mark dirty routines and not dependence routines.