-- LR1ItemSetsBasicImpl.mesa -- last edit November 5, 1984 10:09:03 am PST -- Sturgis, March 12, 1986 11:16:41 am PST -- Remark: January 23, 1986 9:43:19 am PST -- LR1ItemSetsBasicImpl differs from LR1Parsing.LR1ItemSetsBasicImpl as follows: -- 1) InsertItemInLR1ItemSet and InsertSubsetInLR1ItemSet have both been modified to also make entries in the clean terminal sets, as well as the dirty sets -- 2) GenerateDirtyItemsFromLR1ItemSet and GenerateDirtySubsetsFromLR1ItemSet have been modified to refrain from re-inerting the previously dirty terminals in the clean terminals -- these changes are so that GenerateSubsetsFromLR1ItemSet will work, even for unfrozen item sets. Needed by new code for analyzing conflicts. After looking at all existing calls, I don't think it will have any bad effects. DIRECTORY GrammarBasic USING[Grammar, GetLR1ItemSetCollection, NoteLR1ItemSetCollection], TerminalSequences USING[CompareTermSeqSetWithSet, CompareTwoTerminalSeqSets, TerminalSeq, TerminalSeqSet, GenTerminalSeqsFromSet, InsertTerminalSeqInSet, InsertTermSeqSetInSet, RemoveTerminalSeqFromSet, TestIfTerminalSeqSetEmpty, FreezeTerminalSeqSet, CreateEmptyTerminalSeqSet, TestIfTerminalSeqInSet], LR0ItemSetsBasic USING[LR0Item], LR1ItemSetsBasic USING[LR1Item, LR1ItemSubset]; LR1ItemSetsBasicImpl: CEDAR PROGRAM IMPORTS GrammarBasic, TerminalSequences EXPORTS LR1ItemSetsBasic = BEGIN OPEN GrammarBasic, TerminalSequences, LR0ItemSetsBasic, LR1ItemSetsBasic; LR1ItemSetCollection: TYPE = REF LR1ItemSetCollectionBody; LR1ItemSetCollectionBody: TYPE = RECORD[ grammar: Grammar, firstSet: LR1ItemSet, hashTable: LR1HashTable]; LR1ItemSet: TYPE = REF LR1ItemSetBody; LR1ItemSetBody: PUBLIC TYPE = RECORD[ collection: LR1ItemSetCollection, frozen: BOOLEAN, marked: BOOLEAN, associatedNode: REF ANY, firstClean: LR1ItemSetChainCell, recentClean: LR1ItemSetChainCell _ NIL, firstDirty: LR1ItemSetChainCell, recentDirty: LR1ItemSetChainCell _ NIL, nextSet: LR1ItemSet, nextHashSet: LR1ItemSet _ NIL]; LR1ItemSetChainCell: TYPE = REF LR1ItemSetChainCellBody; LR1ItemSetChainCellBody: TYPE = RECORD[ lr0Item: LR0Item, next: LR1ItemSetChainCell, terminals: TerminalSeqSet]; LR1HashTable: TYPE = REF LR1HashTableBody; LR1HashTableBody: TYPE = RECORD[slots: SEQUENCE tableSize: CARDINAL OF LR1ItemSet]; ClearLR1ItemSetsAssociatedNodes: PUBLIC PROC[grammar: Grammar] = BEGIN collection: LR1ItemSetCollection _ GetCollection[grammar]; FOR set: LR1ItemSet _ collection.firstSet, set.nextSet WHILE set # NIL DO set.associatedNode _ NIL; ENDLOOP; END; MarkExistingLR1ItemSets: PUBLIC PROC[grammar: Grammar] = BEGIN collection: LR1ItemSetCollection _ GetCollection[grammar]; FOR set: LR1ItemSet _ collection.firstSet, set.nextSet WHILE set # NIL DO set.marked _ TRUE; ENDLOOP; END; ForgetUnmarkedLR1ItemSets: PUBLIC PROC[grammar: Grammar] = BEGIN collection: LR1ItemSetCollection _ GetCollection[grammar]; hashTable: LR1HashTable _ collection.hashTable; prevSet: LR1ItemSet _ NIL; nextSet: LR1ItemSet; -- first remove unmarked sets from the main chain FOR set: LR1ItemSet _ collection.firstSet, nextSet WHILE set # NIL DO nextSet _ set.nextSet; IF NOT set.marked THEN BEGIN IF prevSet = NIL THEN collection.firstSet _ set.nextSet ELSE prevSet.nextSet _ set.nextSet; set.nextSet _ NIL; END ELSE prevSet _ set; ENDLOOP; -- now remove unmarked sets from the hash chains FOR hashIndex: CARDINAL IN [0..hashTable.tableSize) DO prevSet _ NIL; FOR set: LR1ItemSet _ hashTable[hashIndex], nextSet WHILE set # NIL DO nextSet _ set.nextHashSet; IF NOT set.marked THEN BEGIN IF prevSet = NIL THEN hashTable[hashIndex] _ set.nextHashSet ELSE prevSet.nextHashSet _ set.nextHashSet; set.nextHashSet _ NIL; END ELSE prevSet _ set; ENDLOOP; ENDLOOP; END; CreateEmptyLR1ItemSet: PUBLIC PROC[grammar: Grammar] RETURNS[LR1ItemSet] = BEGIN set: LR1ItemSet _ NEW[LR1ItemSetBody _ [collection: GetCollection[grammar], frozen: FALSE, marked: FALSE]]; RETURN[set]; END; InsertItemInLR1ItemSet: PUBLIC PROC[set: LR1ItemSet, item: LR1Item] RETURNS[--newItem-- BOOLEAN]= BEGIN cleanCell: LR1ItemSetChainCell _ ObtainCleanCell[set, item.lr0Item]; dirtyCell: LR1ItemSetChainCell; IF set.frozen THEN ERROR; IF TestIfTerminalSeqInSet[cleanCell.terminals, item.terminalSeq] THEN RETURN[FALSE]; [] _ InsertTerminalSeqInSet[cleanCell.terminals, item.terminalSeq]; dirtyCell _ ObtainDirtyCell[set, item.lr0Item]; RETURN[InsertTerminalSeqInSet[dirtyCell.terminals, item.terminalSeq]]; END; InsertSubsetInLR1ItemSet: PUBLIC PROC[insert: LR1ItemSubset, into: LR1ItemSet] RETURNS[--someNew-- BOOLEAN] = BEGIN cleanCell: LR1ItemSetChainCell _ ObtainCleanCell[into, insert.lr0Item]; dirtyCell: LR1ItemSetChainCell; diff: TerminalSeqSet _ CompareTermSeqSetWithSet[testThese: insert.terminals, with: cleanCell.terminals]; IF into.frozen THEN ERROR; IF diff = NIL THEN RETURN[FALSE]; [] _ InsertTermSeqSetInSet[add: insert.terminals, to: cleanCell.terminals]; dirtyCell _ ObtainDirtyCell[into, insert.lr0Item]; RETURN[InsertTermSeqSetInSet[add: insert.terminals, to: dirtyCell.terminals]]; END; GenerateDirtyItemsFromLR1ItemSet: PUBLIC PROC[set: LR1ItemSet, for: PROC[LR1Item]] = BEGIN -- and mark them clean prevCell: LR1ItemSetChainCell _ NIL; nextCell: LR1ItemSetChainCell; IF set.frozen THEN ERROR; FOR dirtyCell: LR1ItemSetChainCell _ set.firstDirty, dirtyCell.next WHILE dirtyCell # NIL DO cleanCell: LR1ItemSetChainCell _ ObtainCleanCell[set, dirtyCell.lr0Item]; SeeTheTerminals: PROC[terminalSeq: TerminalSeq] = BEGIN RemoveTerminalSeqFromSet[dirtyCell.terminals, terminalSeq]; for[[dirtyCell.lr0Item, terminalSeq]]; END; GenTerminalSeqsFromSet[dirtyCell.terminals, SeeTheTerminals]; ENDLOOP; -- note, generation may have inserted new dirty items, which were not picked up during preceding scan, therefore, must make sure really is clean before discarding dirty item info. If there are new dirty items, the calling routine will have marked the appropriate node dirty for a later pass. FOR dirtyCell: LR1ItemSetChainCell _ set.firstDirty, nextCell WHILE dirtyCell # NIL DO nextCell _ dirtyCell.next; IF TestIfTerminalSeqSetEmpty[dirtyCell.terminals] THEN BEGIN IF prevCell # NIL THEN prevCell.next _ dirtyCell.next ELSE set.firstDirty _ dirtyCell.next; dirtyCell.next _ NIL; END ELSE prevCell _ dirtyCell; ENDLOOP; set.recentDirty _ NIL; END; GenerateDirtySubsetsFromLR1ItemSet: PUBLIC PROC[from: LR1ItemSet, for: PROC[LR1ItemSubset]] = BEGIN -- and make them clean prevCell: LR1ItemSetChainCell _ NIL; nextCell: LR1ItemSetChainCell; IF from.frozen THEN ERROR; FOR dirtyCell: LR1ItemSetChainCell _ from.firstDirty, dirtyCell.next WHILE dirtyCell # NIL DO cleanCell: LR1ItemSetChainCell _ ObtainCleanCell[from, dirtyCell.lr0Item]; dirtySeqs: TerminalSeqSet _ dirtyCell.terminals; dirtyCell.terminals _ CreateEmptyTerminalSeqSet[from.collection.grammar]; for[[dirtyCell.lr0Item, dirtySeqs]]; ENDLOOP; -- note, generation may have inserted new dirty items, which were not picked up during preceding scan, therefore, must make sure really is clean before discarding dirty item info. If there are new dirty items, the calling routine will have marked the appropriate node dirty for a later pass. FOR dirtyCell: LR1ItemSetChainCell _ from.firstDirty, nextCell WHILE dirtyCell # NIL DO nextCell _ dirtyCell.next; IF TestIfTerminalSeqSetEmpty[dirtyCell.terminals] THEN BEGIN IF prevCell # NIL THEN prevCell.next _ dirtyCell.next ELSE from.firstDirty _ dirtyCell.next; dirtyCell.next _ NIL; END ELSE prevCell _ dirtyCell; ENDLOOP; from.recentDirty _ NIL; END; FreezeLR1ItemSet: PUBLIC PROC[set: LR1ItemSet, allowDirty: BOOLEAN] RETURNS[LR1ItemSet] = BEGIN collection: LR1ItemSetCollection _ set.collection; hashCode: CARDINAL _ 0; hashIndex: CARDINAL; possibleSet: LR1ItemSet; IF set.frozen THEN ERROR; IF set.firstDirty # NIL THEN BEGIN dummy: PROC[LR1ItemSubset] = {NULL}; IF NOT allowDirty THEN ERROR; -- all entries should be clean GenerateDirtySubsetsFromLR1ItemSet[set, dummy]; -- cleans the set END; set.frozen _ TRUE; IF set.firstDirty # NIL THEN ERROR; -- WARNING: September 11, 1984 2:55:11 pm PDT: following comment was written to describe the old schem in which a copy was made. Must figure out how to correct for new scheme without a copy. -- note: following loop guarantees that the order of the lr0Items will be exactly the reverse of the order constructed in ObtainCleanCell, so that the order now conforms to the inverse of lr0Item.x FOR cell: LR1ItemSetChainCell _ set.firstClean, cell.next WHILE cell # NIL DO subHash: CARDINAL; [cell.terminals, subHash] _ FreezeTerminalSeqSet[cell.terminals]; hashCode _ hashCode + subHash; ENDLOOP; hashIndex _ hashCode MOD collection.hashTable.tableSize; FOR possibleSet _ collection.hashTable[hashIndex], possibleSet.nextHashSet WHILE possibleSet # NIL DO match: BOOLEAN _ TRUE; -- tentative -- we are about to scan through the subsets based on lr0Items, since the possibleSet just found, and the chain just constructed, have their lr0items in the same order, we need mearly look for the first difference. newCell: LR1ItemSetChainCell _ set.firstClean; possibleCell: LR1ItemSetChainCell _ possibleSet.firstClean; WHILE newCell # NIL AND possibleCell # NIL DO IF newCell.lr0Item # possibleCell.lr0Item THEN {match _ FALSE; EXIT}; IF newCell.terminals # possibleCell.terminals THEN {match _ FALSE; EXIT}; newCell _ newCell.next; possibleCell _ possibleCell.next; ENDLOOP; IF match AND newCell = NIL AND possibleCell = NIL THEN BEGIN -- we have a match RETURN[possibleSet]; END; ENDLOOP; -- existing one not found, so use the input set.nextHashSet _ collection.hashTable[hashIndex]; collection.hashTable[hashIndex] _ set; set.nextSet _ collection.firstSet; collection.firstSet _ set; RETURN[set]; END; TestIfLR1ItemSetFrozen: PUBLIC PROC[set: LR1ItemSet] RETURNS[BOOLEAN] = {RETURN[set.frozen]}; GenerateItemsFromLR1ItemSet: PUBLIC PROC[set: LR1ItemSet, for: PROC[LR1Item]] = BEGIN IF NOT set.frozen THEN ERROR; FOR cell: LR1ItemSetChainCell _ set.firstClean, cell.next WHILE cell # NIL DO ShowOneTerminalSeq: PROC[seq: TerminalSeq] = {for[[cell.lr0Item, seq]]}; GenTerminalSeqsFromSet[cell.terminals, ShowOneTerminalSeq]; ENDLOOP; END; GenerateSubsetsFromLR1ItemSet: PUBLIC PROC[set: LR1ItemSet, for: PROC[LR1ItemSubset], notFrozenOK: BOOLEAN] = BEGIN IF set = NIL THEN RETURN; IF NOT set.frozen AND NOT notFrozenOK THEN ERROR; FOR cell: LR1ItemSetChainCell _ set.firstClean, cell.next WHILE cell # NIL DO for[[cell.lr0Item, cell.terminals]]; ENDLOOP; END; SetLR1AssociatedNode: PUBLIC PROC[set: LR1ItemSet, node: REF ANY] = BEGIN IF set.associatedNode # NIL THEN ERROR; set.associatedNode _ node; END; GetLR1AssociatedNode: PUBLIC PROC[set: LR1ItemSet] RETURNS[REF ANY] = {RETURN[set.associatedNode]}; NarrowLR1ItemSet: PUBLIC PROC[ref: REF ANY] RETURNS[LR1ItemSet] = {RETURN[NARROW[ref]]}; CheckAnLR1ItemSet: PUBLIC PROC[ref: REF ANY] = BEGIN set: LR1ItemSet _ NARROW[ref]; FOR cell: LR1ItemSetChainCell _ set.firstClean, cell.next WHILE cell # NIL DO IF cell.next # NIL AND cell.lr0Item.x >= cell.next.lr0Item.x THEN ERROR; ENDLOOP; END; CompareTwoLR1ItemSets: PUBLIC PROC[ref1, ref2: REF ANY] RETURNS[BOOLEAN] = BEGIN set1: LR1ItemSet _ NARROW[ref1]; set2: LR1ItemSet _ NARROW[ref2]; cell1: LR1ItemSetChainCell _ set1.firstClean; cell2: LR1ItemSetChainCell _ set2.firstClean; IF NOT set1.frozen OR NOT set2.frozen THEN ERROR; WHILE cell1 # NIL AND cell2 # NIL DO IF cell1.lr0Item # cell2.lr0Item THEN RETURN[FALSE]; IF NOT CompareTwoTerminalSeqSets[cell1.terminals, cell2.terminals] THEN RETURN[FALSE]; cell1 _ cell1.next; cell2 _ cell2.next; ENDLOOP; IF cell1 = NIL AND cell2 = NIL THEN RETURN[TRUE]; RETURN[FALSE]; END; -- support code GetCollection: PROC[grammar: Grammar] RETURNS[LR1ItemSetCollection] = BEGIN collection: LR1ItemSetCollection _ NARROW[GetLR1ItemSetCollection[grammar]]; IF collection = NIL THEN BEGIN -- no collection in existence yet collection _ NEW[LR1ItemSetCollectionBody _ [grammar, NIL, NIL]]; collection.hashTable _ NEW[LR1HashTableBody[1000]]; FOR hx: CARDINAL IN[0..collection.hashTable.tableSize) DO collection.hashTable.slots[hx] _ NIL; ENDLOOP; NoteLR1ItemSetCollection[grammar, collection]; END; RETURN[collection]; END; -- note: this code guarantees the order of the lr0Items to conform to lr0Item.x ObtainCleanCell: PROCEDURE[set: LR1ItemSet, lr0Item: LR0Item] RETURNS[LR1ItemSetChainCell] = BEGIN IF set.recentClean # NIL AND set.recentClean.lr0Item = lr0Item THEN RETURN[set.recentClean]; FOR cell: LR1ItemSetChainCell _ set.firstClean, cell.next WHILE cell # NIL DO IF cell.lr0Item = lr0Item THEN {set.recentClean _ cell; RETURN[cell]}; IF cell.lr0Item.x > lr0Item.x THEN BEGIN -- insert new cell at fron of list newCell: LR1ItemSetChainCell _ NEW[LR1ItemSetChainCellBody_[lr0Item, cell, CreateEmptyTerminalSeqSet[set.collection.grammar]]]; set.firstClean _ set.recentClean _ newCell; RETURN[newCell]; END; IF cell.next = NIL OR cell.next.lr0Item.x > lr0Item.x THEN BEGIN -- insert new cell following current cell newCell: LR1ItemSetChainCell _ NEW[LR1ItemSetChainCellBody_[lr0Item, cell.next, CreateEmptyTerminalSeqSet[set.collection.grammar]]]; cell.next _ newCell; set.recentClean _ newCell; RETURN[newCell]; END; ENDLOOP; -- set must be empty, create first new cell BEGIN newCell: LR1ItemSetChainCell _ NEW[LR1ItemSetChainCellBody_[lr0Item, NIL, CreateEmptyTerminalSeqSet[set.collection.grammar]]]; IF set.recentClean # NIL THEN ERROR; set.firstClean _ set.recentClean _ newCell; RETURN[newCell]; END; END; ObtainDirtyCell: PROCEDURE[set: LR1ItemSet, lr0Item: LR0Item] RETURNS[LR1ItemSetChainCell] = BEGIN cell: LR1ItemSetChainCell; IF set.recentDirty # NIL AND set.recentDirty.lr0Item = lr0Item THEN RETURN[set.recentDirty]; FOR cell: LR1ItemSetChainCell _ set.firstDirty, cell.next WHILE cell # NIL DO IF cell.lr0Item = lr0Item THEN {set.recentDirty _ cell; RETURN[cell]}; ENDLOOP; cell _ set.recentDirty _ NEW[LR1ItemSetChainCellBody]; cell.lr0Item _ lr0Item; cell.next _ set.firstDirty; set.firstDirty _ cell; cell.terminals _ CreateEmptyTerminalSeqSet[set.collection.grammar]; RETURN[cell]; END; END.. -- Remark: September 8, 1984 12:11:53 pm PDT: There was a really nasty bug in GenerateDirtyItemsFromTentativeLR1ItemSet. I forgot to NIL out set.recentDirty, so a dirty cell could be removed, but still be pointed to by recentDirty. The result was the loss of subseqent items with the same LR0 part (as they would be placed into the dirty cell which would not be seen later.) -- Remark: September 11, 1984 3:09:15 pm PDT: conversion to have only one kind of terminal and LR1 set, i.e. no "tentative" versions. Also generate, insert etc subsets. -- Remark: September 12, 1984 3:38:39 pm PDT: really nasty bug in the insertion code. Obtaining clean cell did not put them in the correct order if the new item should go at the fron of the list. Same bug was in the LR0 code. -- RTE: November 1, 1984 5:17:55 pm PST: FreezeLR1ItemSet was not correctly scanning the sub cells of the tentative set. (started with NIL). Effect was to never see a match, so all sets were treated as never before seen. Did not show up until ran into a loop in among the splitting nodes.