-- ConflictAnalImpl.mesa January 17, 1986 3:53:44 pm PST -- Sturgis, June 2, 1986 8:16:54 pm PDT DIRECTORY Basics USING[LongNumber], GrammarBasic USING[GenProductionsForSymbol, GenTerminalSymbols, GetLength, Grammar, Production, Symbol, TestCanProduceEmpty, TestIfTerminal], HashTable USING[Create, Insert, Table], IO USING[int, STREAM, PutF], LR0ItemSetsBasic USING[CreateEmptyTentativeLR0ItemSet, FreezeTentativeLR0ItemSet, GenItemsInLR0ItemSet, GenItemsInTentativeLR0ItemSet, InsertItemInTentativeLR0ItemSet, LR0Item, LR0ItemSet, NarrowLR0ItemSet, TentativeLR0ItemSet], LR0Items USING[CompleteProduction, GenClose0Items, GenKernelGoTo0Items, GetFirstLR0Item, GetFirstSymbol, GetGrammarFromLR0Item, GetNextLR0Item, GetProductionAndPosition, GetStartLR0Item], LR1ItemSetsBasic USING[ClearLR1ItemSetsAssociatedNodes, CreateEmptyLR1ItemSet, FreezeLR1ItemSet, GenerateItemsFromLR1ItemSet, GenerateSubsetsFromLR1ItemSet, InsertItemInLR1ItemSet, LR1Item, LR1ItemSet, LR1ItemSubset, NarrowLR1ItemSet], LR1Items USING[GenCloseOfLR1ItemSubset, GenKernelGoTo1ItemSubsetsFromKernelItemSubset], LR1ItemSets USING[GenReductionRulesFromKernel], TerminalSequences USING[CreateEmptyTerminalSeqSet, FreezeTerminalSeqSet, GenFirstNonEmptyTerminalSeqs, GenTerminalSeqsFromSet, GetEmptyTerminalSeq, GetOneSymbolTerminalSeq, InsertTerminalSeqInSet, TerminalSeq, TerminalSeqSet, TestIfTerminalSeqInSet, TestIfTerminalSeqSetEmpty, TestNonEmptyIntersection], ParserGraphAlgorithms USING[Event], ParserGraphs USING[AppendAnArc, ClearAllUpperNodePointers, CreateEmptyGraph, CreateTentativeStage1Node, GenerateAllNodes, GenerateArcs, GetAnyStage1Node, GetAssociatedSet, GetBaseNode, GetGrammar, GetGraph, GetStartNode, GetUpperNode, Graph, InstallStage1Node, MarkAsStage2, MarkNodeToSplit, Node, SetUpperNode], ParserDisplay USING[ShowLR0ItemSet, ShowLR1ItemSet, ShowNode, ShowTerminalSeq, ShowLR0Item, ShowLR1Item], ConflictAnal USING[]; ConflictAnalImpl: CEDAR PROGRAM IMPORTS GrammarBasic, HashTable, IO, LR0ItemSetsBasic, LR0Items, LR1ItemSetsBasic, LR1Items, LR1ItemSets, TerminalSequences, ParserGraphs, ParserDisplay EXPORTS ConflictAnal = BEGIN OPEN GrammarBasic, IO, LR0ItemSetsBasic, LR0Items, LR1ItemSetsBasic, LR1Items, LR1ItemSets, TerminalSequences, ParserGraphAlgorithms, ParserGraphs, ParserDisplay, ConflictAnal; debug: BOOLEAN _ FALSE; -- Set TRUE for lots of debugging print out <> <> SignalUnresolvableConflict: SIGNAL = CODE; AnalyzeLALR1Conflicts: PUBLIC PROC[graph: Graph, on: IO.STREAM, noteEvent: PROC[Event]] RETURNS[--IsLR1-- BOOLEAN] = BEGIN grammar: Grammar _ GetGrammar[graph]; baseInfoGraph: Graph _ BuildBaseInfoGraph[graph]; conflictGraph: Graph _ CreateEmptyGraph[grammar, SplitNode]; -- SplitNode because there is no good class name at the moment isLR1: BOOLEAN _ TRUE; -- tentative seeOneNode: PROC[node: Node] RETURNS[BOOLEAN] = BEGIN terminalList: TerminalConflictList _ ObtainConflictsForOneNode[node]; IF debug AND terminalList # NIL THEN BEGIN IO.PutF[on, "\N\N\N\NThere are conflicting items at node "]; ShowNode[on, 5, node]; IO.PutF[on, "\N\N"]; FOR oneTerminal: TerminalConflictList _ terminalList, oneTerminal.next WHILE oneTerminal # NIL DO IO.PutF[on, "\N There are conflicting items for terminal sequence "]; ShowTerminalSeq[on, 0, oneTerminal.terminalSeq]; FOR oneConflictItem: ConflictList _ oneTerminal.conflicts, oneConflictItem.next WHILE oneConflictItem # NIL DO IO.PutF[on, "\N conflict item = \N"]; ShowLR1Item[on, 8, oneConflictItem.conflictItem]; ENDLOOP; ENDLOOP; END; FOR oneTerminal: TerminalConflictList _ terminalList, oneTerminal.next WHILE oneTerminal # NIL DO FOR oneConflictItem: ConflictList _ oneTerminal.conflicts, oneConflictItem.next WHILE oneConflictItem # NIL DO conflictNode: Node _ ProcessOneConflictItem[on, node, oneConflictItem.conflictItem, baseInfoGraph, conflictGraph, noteEvent]; oneConflictItem.node _ conflictNode; ENDLOOP; -- now look for possible conflict pairs FOR oneConflictItem: ConflictList _ oneTerminal.conflicts, oneConflictItem.next WHILE oneConflictItem # NIL DO IF LR0Items.CompleteProduction[oneConflictItem.conflictItem.lr0Item] # NIL THEN BEGIN -- we are looking at an item that produces a reduction FOR secondConflictItem: ConflictList _ oneTerminal.conflicts, secondConflictItem.next WHILE secondConflictItem # NIL DO IF oneConflictItem # secondConflictItem THEN VisitAccessableConflictPairs[grammar, on, oneConflictItem.conflictItem, oneConflictItem.node, secondConflictItem.conflictItem, secondConflictItem.node]; ENDLOOP; END; ENDLOOP; ENDLOOP; RETURN[TRUE]; END; GenerateAllNodes[graph, seeOneNode ! SignalUnresolvableConflict => {isLR1 _ FALSE; RESUME}]; RETURN[isLR1]; END; TerminalConflictList: TYPE = REF TerminalConflictListBody; TerminalConflictListBody: TYPE = RECORD[ terminalSeq: TerminalSeq, conflicts: ConflictList, next: TerminalConflictList]; ConflictList: TYPE = REF ConflictListBody; ConflictListBody: TYPE = RECORD[ conflictItem: LR1Item, node: Node, -- from a conflict graph next: ConflictList]; <<>> <<>> ObtainConflictsForOneNode: PROC[node: Node] RETURNS[TerminalConflictList] = BEGIN graph: Graph _ GetGraph[node]; grammar: Grammar _ GetGrammar[graph]; terminalList: TerminalConflictList _ NIL; seeOneConflict: PROC[terminalSeq: TerminalSeq, conflictItem: LR1Item] = BEGIN FOR oneTerminal: TerminalConflictList _ terminalList, oneTerminal.next WHILE oneTerminal # NIL DO IF oneTerminal.terminalSeq = terminalSeq THEN BEGIN <> oneTerminal.conflicts _ NEW[ConflictListBody_[conflictItem, NIL, oneTerminal.conflicts]]; RETURN; END; ENDLOOP; <<>> <> terminalList _ NEW[TerminalConflictListBody_[terminalSeq, NIL, terminalList]]; terminalList.conflicts _ NEW[ConflictListBody_[conflictItem, NIL]]; END; GenConflictItemsForNode[graph, node, seeOneConflict]; RETURN[terminalList]; END; ProdTermSeqCell: TYPE = REF ProdTermSeqCellBody; ProdTermSeqCellBody: TYPE = RECORD[ termSeq: TerminalSeq, production: Production, next: ProdTermSeqCell]; <> <> <<>> <> GenConflictItemsForNode: PROC[graph: Graph, node: Node, for: PROC[terminalSeq: TerminalSeq, conflictItem: LR1Item]] = BEGIN consistent: BOOLEAN _ TRUE; -- tentative grammar: Grammar _ GetGrammar[graph]; conflictingTerminals: TerminalSeqSet _ CreateEmptyTerminalSeqSet[grammar]; <> BEGIN terminalsSeen: TerminalSeqSet _ CreateEmptyTerminalSeqSet[grammar]; pairsSeen: ProdTermSeqCell _ NIL; NoteTheOutgoingArcs: PROC[s: Symbol, target: Node] RETURNS[--continue--BOOLEAN] = BEGIN IF TestIfTerminal[s] THEN [] _ InsertTerminalSeqInSet[terminalsSeen, GetOneSymbolTerminalSeq[s]]; RETURN[TRUE]; END; NoteTheReductionRules: PROC[terminalSeq: TerminalSeq, production: Production] = BEGIN IF GetLength[production] # 0 THEN BEGIN <> IF NOT InsertTerminalSeqInSet[terminalsSeen, terminalSeq] THEN BEGIN consistent _ FALSE; [] _ InsertTerminalSeqInSet[conflictingTerminals, terminalSeq] END; END ELSE BEGIN < pair more than once.>> FOR cell: ProdTermSeqCell _ pairsSeen, cell.next WHILE cell # NIL DO IF cell.termSeq = terminalSeq AND cell.production = production THEN RETURN; -- we have seen this pair before ENDLOOP; IF NOT InsertTerminalSeqInSet[terminalsSeen, terminalSeq] THEN BEGIN consistent _ FALSE; [] _ InsertTerminalSeqInSet[conflictingTerminals, terminalSeq] END; pairsSeen _ NEW[ProdTermSeqCellBody_[terminalSeq, production, pairsSeen]]; END; END; <<>> GenerateArcs[node, NoteTheOutgoingArcs]; GenReductionRulesFromKernel[NarrowLR1ItemSet[GetAssociatedSet[node]], NoteTheReductionRules]; IF consistent THEN RETURN; -- all is well END; -- of rough analysis <<>> <> <> <<>> <> BEGIN for1: PROC[kernalItem: LR1Item] = BEGIN for2: PROC[subSet: LR1ItemSubset] = BEGIN -- we are now looking at a subset of the closure of kernelItem <> p: Production _ LR0Items.CompleteProduction[subSet.lr0Item]; IF p # NIL THEN BEGIN <> <> for3: PROC[tSeq: TerminalSeq] = BEGIN IF TestIfTerminalSeqInSet[conflictingTerminals, tSeq] THEN for[tSeq, [subSet.lr0Item, tSeq]]; END; GenTerminalSeqsFromSet[subSet.terminals, for3]; END ELSE BEGIN <> <> s: Symbol _ GetFirstSymbol[subSet.lr0Item]; -- the first symbol after the "dot" tSeq: TerminalSeq _ IF TestIfTerminal[s] THEN GetOneSymbolTerminalSeq[s] ELSE NIL; for4: PROC[ftSeq: TerminalSeq] = {for[tSeq, [subSet.lr0Item, ftSeq]]}; IF tSeq # NIL AND TestIfTerminalSeqInSet[conflictingTerminals, tSeq] THEN GenTerminalSeqsFromSet[subSet.terminals, for4]; END; END; kernelItemTermSeqSet: TerminalSeqSet _ CreateEmptyTerminalSeqSet[grammar]; [] _ InsertTerminalSeqInSet[kernelItemTermSeqSet, kernalItem.terminalSeq]; [kernelItemTermSeqSet, ] _ FreezeTerminalSeqSet[kernelItemTermSeqSet]; GenCloseOfLR1ItemSubset[[kernalItem.lr0Item, kernelItemTermSeqSet], for2]; END; GenerateItemsFromLR1ItemSet[NarrowLR1ItemSet[GetAssociatedSet[node]], for1]; END; <<>> <<>> END; <> <> <<1) A node is a pair , where S is a canonical LR0 set and T is a set of kernel LR1 items. The core of each item in T is in S. If all possible LR1 items with the same core are in T, we simply represent them with the corresponding LR0 item.>> <<2) An arc is directed and labeled. It is a triple: <, , A>, where A is a symbol of the grammar and: 1) GOTO0(S1, A) = S2; 2) for all t1 in T1 there is a t2 in T2 such that t2 is in KernelGoTo1({t1}, A); and 3) if t1 is in Kernel(LALR1(S1)) and there is a t2 in T2 such that t2 is in KernelGoTO1({t1}, A), then t1 is in T1.>> <<3) If t is a conflicting item that has been presented to ProcessOneConflictItem, then there will be a node where S is the canonical LR0 set associated with the LALR(1) set containing the conflicting item, and were T is the set of kernel items in the LALR(1) set from which the conflicting item can be reached by LR(1) closure.>> <<>> <> <> <> <> ConflictGraphNodeInfo: TYPE = REF ConflictGraphNodeInfoBody; ConflictGraphNodeInfoBody: TYPE = RECORD[ node: Node, root: BOOLEAN, lr1KernelSet: LR1ItemSet, lr0KernelSet: LR0ItemSet, prevNodes: LIST OF Node, next: ConflictGraphNodeInfo]; ProcessOneConflictItem: PROC[on: IO.STREAM, initialLALRnode: Node, conflictingItem: LR1Item, baseInfoGraph: Graph, conflictGraph: Graph, noteEvent: PROC[Event]] RETURNS[initialConflictNodeForConflictingItem: Node] = BEGIN grammar: Grammar _ GetGrammar[baseInfoGraph]; rootLR0Item: LR0Item _ GetStartLR0Item[grammar]; -- following procedure is used for adding nodes to the conflict graph BuildOneConflictGraphNode: PROC[baseNode: Node, tentativeLR1KernelSet: LR1ItemSet, tentativeLR0KernelSet: TentativeLR0ItemSet] RETURNS[node: Node] = BEGIN baseNodeInfo: BaseNodeInfo _ NARROW[GetAssociatedSet[baseNode]]; lr1KernelSet: LR1ItemSet _ IF tentativeLR1KernelSet = NIL THEN NIL ELSE FreezeLR1ItemSet[tentativeLR1KernelSet, TRUE]; lr0KernelSet: LR0ItemSet _ IF tentativeLR0KernelSet = NIL THEN NIL ELSE FreezeTentativeLR0ItemSet[tentativeLR0KernelSet]; node _ NIL; -- tentative IF lr1KernelSet # NIL OR lr0KernelSet # NIL THEN BEGIN foundACandidate: BOOLEAN _ FALSE; FOR candidateNodeInfo: ConflictGraphNodeInfo _ baseNodeInfo.kernelGraphNodeChain, candidateNodeInfo.next WHILE candidateNodeInfo # NIL DO IF candidateNodeInfo.lr1KernelSet = lr1KernelSet AND candidateNodeInfo.lr0KernelSet = lr0KernelSet THEN BEGIN -- we have a successful candidate foundACandidate _ TRUE; node _ candidateNodeInfo.node; EXIT; END; ENDLOOP; IF NOT foundACandidate THEN BEGIN -- got to build a new one newNodeInfo: ConflictGraphNodeInfo _ NEW[ConflictGraphNodeInfoBody_[ NIL, FALSE, lr1KernelSet, lr0KernelSet, NIL, baseNodeInfo.kernelGraphNodeChain]]; CheckForRoot: PROC[item: LR0Item] = BEGIN IF item = rootLR0Item AND GetProductionAndPosition[item].position = 0 THEN newNodeInfo.root _ TRUE END; seeOneLR0Item: PROC[item: LR0Item] = {CheckForRoot[item]}; seeOneLR1Item: PROC[item: LR1Item] = {CheckForRoot[item.lr0Item]}; node _ CreateTentativeStage1Node[conflictGraph, newNodeInfo, baseNode]; newNodeInfo.node _ node; baseNodeInfo.kernelGraphNodeChain _ newNodeInfo; InstallStage1Node[node, FALSE]; IF lr0KernelSet # NIL THEN GenItemsInLR0ItemSet[lr0KernelSet, seeOneLR0Item]; IF NOT newNodeInfo.root AND lr1KernelSet # NIL THEN GenerateItemsFromLR1ItemSet[lr1KernelSet, seeOneLR1Item]; END; END; END; -- following special terminal sequences are used in testing whether all LR1 items with the same LR0 component will go into a constructed set, and hence can be represented by a single LR0 item, rather than a collection of distinct LR1 items. emptyTerminalSeq: TerminalSeq; singletonEmptyTerminalSeq: TerminalSeqSet; otherTerminalSeq: TerminalSeq _ NIL; singletonOtherTerminalSeq: TerminalSeqSet; -- set up the special terminal sequences BEGIN seeOneTerminal: PROC[t: Symbol] = BEGIN IF otherTerminalSeq = NIL THEN otherTerminalSeq _ GetOneSymbolTerminalSeq[t]; END; emptyTerminalSeq _ GetEmptyTerminalSeq[grammar]; singletonEmptyTerminalSeq _ CreateEmptyTerminalSeqSet[grammar]; [] _ InsertTerminalSeqInSet[singletonEmptyTerminalSeq, emptyTerminalSeq]; [singletonEmptyTerminalSeq, ] _ FreezeTerminalSeqSet[singletonEmptyTerminalSeq]; GenTerminalSymbols[grammar, seeOneTerminal]; singletonOtherTerminalSeq _ CreateEmptyTerminalSeqSet[grammar]; [] _ InsertTerminalSeqInSet[singletonOtherTerminalSeq, otherTerminalSeq]; [singletonOtherTerminalSeq, ] _ FreezeTerminalSeqSet[singletonOtherTerminalSeq]; END; -- build the initialConflictNode BEGIN initialBaseInfoNode: Node _ GetUpperNode[initialLALRnode]; initialBaseInfo: BaseNodeInfo _ NARROW[GetAssociatedSet[initialBaseInfoNode]]; tentativeLR1KernelSet: LR1ItemSet _ NIL; tentativeLR0KernelSet: TentativeLR0ItemSet _ NIL; seeOneLR0Item: PROC[candidateLR0Item: LR0Item] = BEGIN candidateLR0ItemIsIn: BOOLEAN _ FALSE; TryOneSyntheticLR1Item: PROC[trialItemSubset: LR1ItemSubset, trialItemSeq: TerminalSeq] = BEGIN seeOneClosureSubset: PROC[closureSubset: LR1ItemSubset] = BEGIN seeOneTerminalSeq: PROC[ts: TerminalSeq] = BEGIN IF candidateLR0ItemIsIn THEN RETURN; IF ts = conflictingItem.terminalSeq THEN BEGIN IF tentativeLR0KernelSet = NIL THEN tentativeLR0KernelSet _ CreateEmptyTentativeLR0ItemSet[grammar]; [] _ InsertItemInTentativeLR0ItemSet[tentativeLR0KernelSet, candidateLR0Item]; candidateLR0ItemIsIn _ TRUE; END; END; IF candidateLR0ItemIsIn THEN RETURN; IF closureSubset.lr0Item # conflictingItem.lr0Item THEN RETURN; GenTerminalSeqsFromSet[closureSubset.terminals, seeOneTerminalSeq]; END; GenCloseOfLR1ItemSubset[trialItemSubset, seeOneClosureSubset]; END; IF emptyTerminalSeq = conflictingItem.terminalSeq THEN TryOneSyntheticLR1Item[[candidateLR0Item, singletonOtherTerminalSeq], otherTerminalSeq] ELSE TryOneSyntheticLR1Item[[candidateLR0Item, singletonEmptyTerminalSeq], emptyTerminalSeq]; END; seeOneLR1Item: PROC[candidateLR1Item: LR1Item] = BEGIN candidateLR1ItemIsIn: BOOLEAN _ FALSE; seeOneClosureSubset: PROC[closureSubset: LR1ItemSubset] = BEGIN seeOneTerminalSeq: PROC[ts: TerminalSeq] = BEGIN IF candidateLR1ItemIsIn THEN RETURN; IF ts = conflictingItem.terminalSeq THEN BEGIN IF tentativeLR1KernelSet = NIL THEN tentativeLR1KernelSet _ CreateEmptyLR1ItemSet[grammar]; [] _ InsertItemInLR1ItemSet[tentativeLR1KernelSet, candidateLR1Item]; candidateLR1ItemIsIn _ TRUE; END; END; IF candidateLR1ItemIsIn THEN RETURN; IF closureSubset.lr0Item # conflictingItem.lr0Item THEN RETURN; GenTerminalSeqsFromSet[closureSubset.terminals, seeOneTerminalSeq]; END; GenCloseOfLR1Item[grammar, candidateLR1Item, seeOneClosureSubset]; END; -- first, we see if we can put in a pure LR0Item that represents all LR1 items with the same terminalSeq GenItemsInLR0ItemSet[initialBaseInfo.LR0Set, seeOneLR0Item]; -- now we try individual LR1 items GenerateItemsFromLR1ItemSet[initialBaseInfo.LALR1set, seeOneLR1Item]; -- and we build the node IF (initialConflictNodeForConflictingItem _ BuildOneConflictGraphNode[initialBaseInfoNode, tentativeLR1KernelSet, tentativeLR0KernelSet]) = NIL THEN ERROR; END; -- now we process stage1 nodes until there are no more, adding nodes as needed FOR stage1Node: Node _ GetAnyStage1Node[conflictGraph], GetAnyStage1Node[conflictGraph] WHILE stage1Node # NIL DO stage1NodeInfo: ConflictGraphNodeInfo _ NARROW[GetAssociatedSet[stage1Node]]; stage1BaseInfo: BaseNodeInfo _ NARROW[GetAssociatedSet[GetBaseNode[stage1Node]]]; FOR prevBaseInfoNodes: LIST OF Node _ stage1BaseInfo.predecessors, prevBaseInfoNodes.rest WHILE prevBaseInfoNodes # NIL DO prevBaseInfoNode: Node _ prevBaseInfoNodes.first; prevBaseInfo: BaseNodeInfo _ NARROW[GetAssociatedSet[prevBaseInfoNode]]; tentativeLR1KernelSet: LR1ItemSet _ NIL; tentativeLR0KernelSet: TentativeLR0ItemSet _ NIL; -- first we see if we shold make entries in tentativeLR0KernelSet, that is, if arbitrary LR1 items with the same core lead to items in the stage1 node. There are two cases, either "GoTo0" of an LR0 item leads to an LR0 item in the stage one node, or "GoTo1" of an "arbitrary" LR1 item (constructed from an lr0 item) leads to some LR1 item in the stage1 node. -- here we tackle the first case: "GoTo0" of an LR0 item leads to an LR0 item in the stage one node. BEGIN seeOneLr0Item: PROC[item: LR0Item] = BEGIN lr0ItemIsIn: BOOLEAN _ FALSE; seeOneKernelGoToItem: PROC[s: Symbol, goToItem: LR0Item] = BEGIN IF lr0ItemIsIn OR s # stage1BaseInfo.symbol THEN RETURN; IF stage1NodeInfo.lr0KernelSet # NIL AND TestIfLR0ItemInSet[goToItem, stage1NodeInfo.lr0KernelSet] THEN BEGIN IF tentativeLR0KernelSet = NIL THEN tentativeLR0KernelSet _ CreateEmptyTentativeLR0ItemSet[grammar]; [] _ InsertItemInTentativeLR0ItemSet[tentativeLR0KernelSet, item]; lr0ItemIsIn _ TRUE; END; END; seeOneClosureItem: PROC[closureItem: LR0Item] = {GenKernelGoTo0Items[closureItem, seeOneKernelGoToItem]}; GenClose0Items[item, seeOneClosureItem]; END; GenItemsInLR0ItemSet[prevBaseInfo.LR0Set, seeOneLr0Item]; END; -- and now the second case: "GoTo1" of an "arbitrary" LR1 item (constructed from an lr0 item) leads to some LR1 item in the stage1 node. The scheme is to generate the goto1 items from some specific lr1item and if there is a resulting terminal seq that wasn't the terminal seq of the trial item then "arbitrary" items will also generate the observed item. We need only try for two trial items. BEGIN seeOneLr0Item: PROC[lr0Item: LR0Item] = BEGIN lr0ItemIsIn: BOOLEAN _ FALSE; TryOneItem: PROC[trialItemSubset: LR1ItemSubset, trialItemSeq: TerminalSeq] = BEGIN seeOneGoToSubset: PROC[s: Symbol, goTo1Subset: LR1ItemSubset] = BEGIN -- now, we see if some element of goTo1Subset (whose follow terminalSeq is not the unique seq in trialItemSubset.terminals) is in stage1NodeInfo.lr1KernelSet seeOneTargetSubset: PROC[tSubset: LR1ItemSubset] = BEGIN seeOneTerminalSeq: PROC[tseq: TerminalSeq] = BEGIN IF lr0ItemIsIn THEN RETURN; IF tseq = trialItemSeq THEN RETURN; IF TestIfTerminalSeqInSet[tSubset.terminals, tseq] THEN BEGIN -- this lr0 item passes muster IF tentativeLR0KernelSet = NIL THEN tentativeLR0KernelSet _ CreateEmptyTentativeLR0ItemSet[grammar]; [] _ InsertItemInTentativeLR0ItemSet[tentativeLR0KernelSet, lr0Item]; lr0ItemIsIn _ TRUE; END; END; IF lr0ItemIsIn THEN RETURN; IF tSubset.lr0Item # goTo1Subset.lr0Item THEN RETURN; GenTerminalSeqsFromSet[goTo1Subset.terminals, seeOneTerminalSeq]; END; IF lr0ItemIsIn THEN RETURN; IF s # stage1BaseInfo.symbol THEN RETURN; GenerateSubsetsFromLR1ItemSet[stage1NodeInfo.lr1KernelSet, seeOneTargetSubset]; END; GenKernelGoTo1ItemSubsetsFromKernelItemSubset[trialItemSubset, seeOneGoToSubset]; END; IF TestIfLR0ItemInTentativeSet[lr0Item, tentativeLR0KernelSet] THEN RETURN; TryOneItem[[lr0Item, singletonEmptyTerminalSeq], emptyTerminalSeq]; IF lr0ItemIsIn THEN RETURN; TryOneItem[[lr0Item, singletonOtherTerminalSeq], otherTerminalSeq]; END; GenItemsInLR0ItemSet[prevBaseInfo.LR0Set, seeOneLr0Item]; END; -- we look for specific lr1 items that should be in the set we are building BEGIN seeOneLR1Item: PROC[lr1Item: LR1Item] = BEGIN lr1ItemIsIn: BOOLEAN _ FALSE; seeOneGenSubset: PROC[s: Symbol, subset: LR1ItemSubset] = BEGIN seeOneTargetSubset: PROC[tSubset: LR1ItemSubset] = BEGIN IF lr1ItemIsIn THEN RETURN; IF tSubset.lr0Item # subset.lr0Item THEN RETURN; IF TestNonEmptyIntersection[subset.terminals, tSubset.terminals] THEN BEGIN IF tentativeLR1KernelSet = NIL THEN tentativeLR1KernelSet _ CreateEmptyLR1ItemSet[grammar]; [] _ InsertItemInLR1ItemSet[tentativeLR1KernelSet, lr1Item]; lr1ItemIsIn _ TRUE; END; END; IF lr1ItemIsIn THEN RETURN; IF s # stage1BaseInfo.symbol THEN RETURN; GenerateSubsetsFromLR1ItemSet[stage1NodeInfo.lr1KernelSet, seeOneTargetSubset]; END; IF TestIfLR0ItemInTentativeSet[lr1Item.lr0Item, tentativeLR0KernelSet] THEN RETURN; GenGoTo1KernelSubsetsFromKernelItem[grammar, lr1Item, seeOneGenSubset]; END; GenerateItemsFromLR1ItemSet[prevBaseInfo.LALR1set, seeOneLR1Item]; END; -- now, if at least one of the sets is non empty, check to see if we have already built a node that satisfies the requirements, or build one. BEGIN node: Node _ BuildOneConflictGraphNode[prevBaseInfoNode, tentativeLR1KernelSet, tentativeLR0KernelSet]; IF node # NIL THEN stage1NodeInfo.prevNodes _ CONS[node, stage1NodeInfo.prevNodes]; END; ENDLOOP; MarkAsStage2[stage1Node]; ENDLOOP; END; BaseNodeInfo: TYPE = REF BaseNodeInfoBody; BaseNodeInfoBody: TYPE = RECORD[ predecessors: LIST OF Node _ NIL, symbol: Symbol _ NIL, LALR1set: LR1ItemSet _ NIL, LR0Set: LR0ItemSet _ NIL, kernelGraphNodeChain: ConflictGraphNodeInfo _ NIL]; BuildBaseInfoGraph: PROC[baseGraph: Graph] RETURNS[Graph] = BEGIN baseStart: Node _ GetStartNode[baseGraph]; grammar: Grammar _ GetGrammar[baseGraph]; newGraph: Graph _ CreateEmptyGraph[grammar, LALR1]; -- LALR1 because there is no good class name at the moment CreateOneNewNode: PROC[baseNode: Node] RETURNS[--continue--BOOLEAN] = BEGIN info: BaseNodeInfo _ NEW[BaseNodeInfoBody]; newNode: Node _ CreateTentativeStage1Node[newGraph, info, baseNode]; InstallStage1Node[newNode, baseNode=baseStart]; SetUpperNode[baseNode, newNode]; info.LALR1set _ NarrowLR1ItemSet[GetAssociatedSet[baseNode]]; info.LR0Set _ NarrowLR0ItemSet[GetAssociatedSet[GetBaseNode[baseNode]]]; RETURN[TRUE]; END; -- first clean up any set associations ClearLR1ItemSetsAssociatedNodes[grammar]; ClearAllUpperNodePointers[baseGraph]; -- next, we copy the structure of the given LR0Parser graph GenerateAllNodes[baseGraph, CreateOneNewNode]; FOR stage1Node: Node _ GetAnyStage1Node[newGraph], GetAnyStage1Node[newGraph] WHILE stage1Node # NIL DO baseSourceNode: Node _ GetBaseNode[stage1Node]; InstallNewArc: PROC[s: Symbol, baseArcTarget: Node]RETURNS[--continue-- BOOLEAN] = BEGIN target: Node _ GetUpperNode[baseArcTarget]; targetInfo: BaseNodeInfo _ NARROW[GetAssociatedSet[target]]; IF GetBaseNode[target] # baseArcTarget THEN ERROR; IF targetInfo.symbol # NIL AND targetInfo.symbol # s THEN ERROR; targetInfo.symbol _ s; AppendAnArc[stage1Node, s, target]; targetInfo.predecessors _ CONS[stage1Node, targetInfo.predecessors]; RETURN[TRUE]; END; GenerateArcs[baseSourceNode, InstallNewArc]; MarkAsStage2[stage1Node]; ENDLOOP; RETURN[newGraph]; END; <<>> <<>> <> ConflictPair: TYPE = REF ConflictPairBody; ConflictPairBody: TYPE = RECORD[ a, b: ConflictGraphNodeInfo]; AnUnresolvableConflict: SIGNAL = CODE; VisitAccessableConflictPairs: PROC[grammar: Grammar, showOn: IO.STREAM, itemA: LR1Item, nodeA: Node, itemB: LR1Item, nodeB: Node] = BEGIN firstPair: ConflictPair _ NEW[ConflictPairBody_[NARROW[GetAssociatedSet[nodeA]], NARROW[GetAssociatedSet[nodeB]]]]; visitedPairs: HashTable.Table _ HashTable.Create[equal: EqualPairs, hash: PairHash]; nEqualPairs: INT _ 0; nDifPairs: INT _ 0; conflicts: LIST OF LIST OF ConflictPair _ NIL; NoShow: PROC[tail: LIST OF ConflictPair] RETURNS[LIST OF ConflictPair] = {RETURN[tail]}; VisitPair: PROC[pair: ConflictPair, showChain: PROC[LIST OF ConflictPair] RETURNS[LIST OF ConflictPair]] = BEGIN ShowChain: PROC[tail: LIST OF ConflictPair] RETURNS[LIST OF ConflictPair] = BEGIN IF debug THEN ShowPair[showOn, pair]; RETURN[showChain[CONS[pair,tail]]]; END; UnresolvableConflict: PROC = BEGIN IO.PutF[showOn, "\N\N\NAn unresolvable conflict\N"]; IO.PutF[showOn, "between "]; ShowLR1Item[showOn, 0, itemA]; IO.PutF[showOn, "\N and "]; ShowLR1Item[showOn, 0, itemB]; IO.PutF[showOn, "\N\N"]; conflicts _ CONS[ShowChain[NIL], conflicts]; SignalUnresolvableConflict[]; END; IF HashTable.Insert[visitedPairs, pair, pair] THEN BEGIN MarkNodeToSplit[GetBaseNode[GetBaseNode[pair.a.node]]]; IF pair.a = pair.b THEN BEGIN nEqualPairs _ nEqualPairs + 1; IF pair.a.root THEN UnresolvableConflict[] ELSE BEGIN FOR prevAs: LIST OF Node _ pair.a.prevNodes, prevAs.rest WHILE prevAs # NIL DO prevA: Node _ prevAs.first; prevAInfo: ConflictGraphNodeInfo _ NARROW[GetAssociatedSet[prevA]]; VisitPair[NEW[ConflictPairBody_[prevAInfo, prevAInfo]], ShowChain]; ENDLOOP; END; END ELSE BEGIN nDifPairs _ nDifPairs + 1; IF pair.a.root AND pair.b.root THEN UnresolvableConflict[] ELSE FOR prevAs: LIST OF Node _ pair.a.prevNodes, prevAs.rest WHILE prevAs # NIL DO prevA: Node _ prevAs.first; prevAInfo: ConflictGraphNodeInfo _ NARROW[GetAssociatedSet[prevA]]; prevABase: Node _ GetBaseNode[prevA]; FOR prevBs: LIST OF Node _ pair.b.prevNodes, prevBs.rest WHILE prevBs # NIL DO prevB: Node _ prevBs.first; prevBInfo: ConflictGraphNodeInfo _ NARROW[GetAssociatedSet[prevB]]; prevBBase: Node _ GetBaseNode[prevB]; IF prevABase = prevBBase THEN VisitPair[NEW[ConflictPairBody_[prevAInfo, prevBInfo]], ShowChain]; ENDLOOP; ENDLOOP; END; END; END; VisitPair[firstPair, NoShow]; IF debug THEN IO.PutF[showOn, "\Nvisited %g pairs of unequals, and %g pairs of equals\N", IO.int[nDifPairs], IO.int[nEqualPairs]]; IF conflicts # NIL THEN BEGIN -- lets see if we can print more details FOR someConflicts: LIST OF LIST OF ConflictPair _ conflicts, someConflicts.rest WHILE someConflicts # NIL DO ShowConflictList[grammar, showOn, itemA, itemB, someConflicts.first]; ENDLOOP; END; END; <> ShowConflictList: PROC[grammar: Grammar, on: IO.STREAM, itemA, itemB: LR1Item, list: LIST OF ConflictPair] = BEGIN targetA: LIST OF Precursor _ NIL; targetB: LIST OF Precursor _ NIL; precListA: LIST OF Precursor _ NIL; precListB: LIST OF Precursor _ NIL; ConcatTruncatedPrecList: PROC[a, b: LIST OF Precursor] RETURNS[LIST OF Precursor] = {IF a.rest = NIL THEN RETURN[b] ELSE RETURN[CONS[a.first, ConcatTruncatedPrecList[a.rest, b]]]}; <> BEGIN TestItemA: PROC[candidate: Precursor] RETURNS[BOOLEAN] = {RETURN[candidate = [lr1[itemA]]]}; TestItemB: PROC[candidate: Precursor] RETURNS[BOOLEAN] = {RETURN[candidate = [lr1[itemB]]]}; listA: LIST OF Precursor _ FindPrecursors[grammar, list.first.a.lr0KernelSet, list.first.a.lr1KernelSet, TestItemA]; listB: LIST OF Precursor _ FindPrecursors[grammar, list.first.b.lr0KernelSet, list.first.b.lr1KernelSet, TestItemB]; --PrintPrecursorList[on, listA]; --PrintPrecursorList[on, listB]; targetA _ listA; targetB _ listB; precListA _ listA; precListB _ listB; END; <> FOR pairs: LIST OF ConflictPair _ list.rest, pairs.rest WHILE pairs # NIL DO TestItemA: PROC[candidate: Precursor] RETURNS[BOOLEAN] = BEGIN WITH candidate SELECT FROM c: lr0 Precursor => WITH targetA.first SELECT FROM t: lr0 Precursor => RETURN[t.item = GetNextLR0Item[c.item]]; t: lr1 Precursor => RETURN[FALSE]; ENDCASE => ERROR; c: lr1 Precursor => WITH targetA.first SELECT FROM t: lr0 Precursor => RETURN[FALSE]; t: lr1 Precursor => RETURN[t.item.lr0Item = GetNextLR0Item[c.item.lr0Item] AND t.item.terminalSeq = c.item.terminalSeq]; ENDCASE => ERROR; ENDCASE => ERROR; END; TestItemB: PROC[candidate: Precursor] RETURNS[BOOLEAN] = BEGIN WITH candidate SELECT FROM c: lr0 Precursor => WITH targetB.first SELECT FROM t: lr0 Precursor => RETURN[t.item = GetNextLR0Item[c.item]]; t: lr1 Precursor => RETURN[FALSE]; ENDCASE => ERROR; c: lr1 Precursor => WITH targetB.first SELECT FROM t: lr0 Precursor => RETURN[FALSE]; t: lr1 Precursor => RETURN[t.item.lr0Item = GetNextLR0Item[c.item.lr0Item] AND t.item.terminalSeq = c.item.terminalSeq]; ENDCASE => ERROR; ENDCASE => ERROR; END; listA: LIST OF Precursor _ FindPrecursors[grammar, pairs.first.a.lr0KernelSet, pairs.first.a.lr1KernelSet, TestItemA]; listB: LIST OF Precursor _ FindPrecursors[grammar, pairs.first.b.lr0KernelSet, pairs.first.b.lr1KernelSet, TestItemB]; --PrintPrecursorList[on, listA]; --PrintPrecursorList[on, listB]; targetA _ listA; targetB _ listB; precListA _ ConcatTruncatedPrecList[listA, precListA]; precListB _ ConcatTruncatedPrecList[listB, precListB]; ENDLOOP; ShowLR1Item[on, 0, itemA]; IO.PutF[on, "\N\Tcan be reached by\N\N"]; PrintPrecursorList[on, precListA]; IO.PutF[on, "\Nand \N"]; ShowLR1Item[on, 0, itemB]; IO.PutF[on, "\N\Tcan be reached by along the same path by\N\N"]; PrintPrecursorList[on, precListB]; IO.PutF[on, "\N\N\N"]; END; ShowPair: PROC[on: IO.STREAM, pair: ConflictPair] = BEGIN IO.PutF[on, "one conflicting pair\N"]; ShowConflictNode[on, pair.a]; ShowConflictNode[on, pair.b]; END; ShowConflictNode: PROC[on: IO.STREAM, info: ConflictGraphNodeInfo] = BEGIN IF info.lr1KernelSet = NIL THEN IO.PutF[on, " no lr1 items\N"] ELSE BEGIN IO.PutF[on, " lr1 items = \N"]; ShowLR1ItemSet[on, 10, info.lr1KernelSet]; IO.PutF[on, "\N"]; END; IF info.lr0KernelSet = NIL THEN IO.PutF[on, " no lr0 items\N"] ELSE BEGIN IO.PutF[on, " lr0 items = \N"]; ShowLR0ItemSet[on, 10, info.lr0KernelSet]; IO.PutF[on, "\N"]; END; IO.PutF[on, "\N"]; END; EqualPairs: PROC[a, b: REF ANY] RETURNS[BOOLEAN] = BEGIN pairA: ConflictPair _ NARROW[a]; pairB: ConflictPair _ NARROW[b]; RETURN[pairA.a = pairB.a AND pairA.b = pairB.b]; END; PairHash: PROC[a: REF ANY] RETURNS[CARDINAL] = BEGIN pairA: ConflictPair _ NARROW[a]; lna: Basics.LongNumber _ [lp[LOOPHOLE[pairA.a]]]; lnb: Basics.LongNumber _ [lp[LOOPHOLE[pairA.b]]]; RETURN[lna.lo+lna.hi+lnb.lo+lnb.hi]; END; Precursor: TYPE = RECORD[ SELECT case: * FROM lr0 => [item: LR0Item], lr1 => [item: LR1Item], ENDCASE]; FindPrecursors: PROC[grammar: Grammar, lr0KernelSet: LR0ItemSet, lr1KernelSet: LR1ItemSet, testOne: PROC[candidateImmediatePrecursor: Precursor] RETURNS[BOOLEAN]] RETURNS[LIST OF Precursor] = BEGIN visitedLR1Items: LR1ItemSet _ CreateEmptyLR1ItemSet[grammar]; visitedLR0Items: TentativeLR0ItemSet _ CreateEmptyTentativeLR0ItemSet[grammar]; returnChain: LIST OF Precursor _ NIL; found: BOOLEAN _ FALSE; -- initial SeeKernelLR0Item: PROC[kernelLR0Item: LR0Item] = {IF NOT found THEN [] _ ExploreFromLR0Item[kernelLR0Item]}; ExploreFromLR0Item: PROC[lr0Item: LR0Item] RETURNS[--stopGeneration-- BOOLEAN] = BEGIN IF found THEN RETURN[TRUE]; IF InsertItemInTentativeLR0ItemSet[visitedLR0Items, lr0Item] THEN BEGIN -- we havn't seen this one before IF testOne[[lr0[lr0Item]]] THEN -- we now have the immediate precursor BEGIN found _ TRUE; returnChain _ LIST[[lr0[lr0Item]]]; RETURN[TRUE] END ELSE BEGIN SeeItemWithEmptySeq: PROC[LR1Item] = {NULL}; GenOneSimpleLR0LR0CloseStep[lr0Item, ExploreFromLR0Item]; IF NOT found THEN BasicGenOneSimpleLR0LR1CloseStep[lr0Item, ExploreFromLR1Item, SeeItemWithEmptySeq]; IF found THEN -- this one belongs in the chain BEGIN returnChain _ CONS[[lr0[lr0Item]], returnChain]; RETURN[TRUE]; END; END; END; RETURN[FALSE]; END; SeeKernelLR1Item: PROC[kernelLR1Item: LR1Item] = {IF NOT found THEN [] _ ExploreFromLR1Item[kernelLR1Item]}; ExploreFromLR1Item: PROC[lr1Item: LR1Item] RETURNS[--stopGeneration-- BOOLEAN] = BEGIN IF found THEN RETURN[TRUE]; IF InsertItemInLR1ItemSet[visitedLR1Items, lr1Item] THEN BEGIN -- we havn't seen this one before IF testOne[[lr1[lr1Item]]] THEN -- we now have the immediate precursor BEGIN found _ TRUE; returnChain _ LIST[[lr1[lr1Item]]]; RETURN[TRUE] END ELSE BEGIN GenOneSimpleLR1LR1CloseStep[lr1Item, ExploreFromLR1Item]; IF found THEN -- this one belongs in the chain BEGIN returnChain _ CONS[[lr1[lr1Item]], returnChain]; RETURN[TRUE]; END; END; END; RETURN[FALSE]; END; IF lr0KernelSet # NIL THEN GenItemsInLR0ItemSet[lr0KernelSet, SeeKernelLR0Item]; IF found THEN RETURN[returnChain]; IF lr1KernelSet # NIL THEN GenerateItemsFromLR1ItemSet[lr1KernelSet, SeeKernelLR1Item]; IF found THEN RETURN[returnChain]; ERROR; END; TestIfLR0ItemInSet: PROC[item: LR0Item, set: LR0ItemSet] RETURNS[BOOLEAN]= BEGIN found: BOOLEAN _ FALSE; seeOneItem: PROC[setItem: LR0Item] = {IF setItem = item THEN found _ TRUE}; GenItemsInLR0ItemSet[set, seeOneItem]; RETURN[found]; END; TestIfLR0ItemInTentativeSet: PROC[item: LR0Item, set: TentativeLR0ItemSet] RETURNS[BOOLEAN]= BEGIN found: BOOLEAN _ FALSE; seeOneItem: PROC[setItem: LR0Item] = {IF setItem = item THEN found _ TRUE}; IF set = NIL THEN RETURN[FALSE]; GenItemsInTentativeLR0ItemSet[set, seeOneItem]; RETURN[found]; END; TestIfLR1ItemInSet: PROC[item: LR1Item, set: LR1ItemSet] RETURNS[BOOLEAN] = BEGIN yes: BOOLEAN _ FALSE; -- tentative seeOneSubset: PROC[subset: LR1ItemSubset] = BEGIN IF item.lr0Item # subset.lr0Item THEN RETURN; IF TestIfTerminalSeqInSet[subset.terminals, item.terminalSeq] THEN yes _ TRUE; END; IF set # NIL THEN GenerateSubsetsFromLR1ItemSet[set, seeOneSubset, TRUE]; RETURN[yes]; END; GenGoTo1KernelSubsetsFromKernelItem: PROC[grammar: Grammar, kernelItem: LR1Item, for: PROC[Symbol, LR1ItemSubset]] = BEGIN kernelItemTermSeqSet: TerminalSeqSet _ CreateEmptyTerminalSeqSet[grammar]; [] _ InsertTerminalSeqInSet[kernelItemTermSeqSet, kernelItem.terminalSeq]; [kernelItemTermSeqSet, ] _ FreezeTerminalSeqSet[kernelItemTermSeqSet]; GenKernelGoTo1ItemSubsetsFromKernelItemSubset[[kernelItem.lr0Item, kernelItemTermSeqSet], for]; END; GenCloseOfLR1Item: PROC[grammar: Grammar, item: LR1Item, for: PROC[LR1ItemSubset]] = BEGIN itemTermSeqSet: TerminalSeqSet _ CreateEmptyTerminalSeqSet[grammar]; [] _ InsertTerminalSeqInSet[itemTermSeqSet, item.terminalSeq]; [itemTermSeqSet, ] _ FreezeTerminalSeqSet[itemTermSeqSet]; GenCloseOfLR1ItemSubset[[item.lr0Item, itemTermSeqSet], for]; END; GenOneSimpleLR1LR1CloseStep: PROC[lr1Item: LR1Item, for: PROC[LR1Item] RETURNS[--stop-- BOOLEAN]] = BEGIN SeeItemWithEmptySeq: PROC[closureItem: LR1Item] = {[] _ for[[closureItem.lr0Item, lr1Item.terminalSeq]]}; BasicGenOneSimpleLR0LR1CloseStep[lr1Item.lr0Item, for, SeeItemWithEmptySeq]; END; <> <> BasicGenOneSimpleLR0LR1CloseStep: PROC[lr0Item: LR0Item, for: PROC[LR1Item]RETURNS[--stop-- BOOLEAN], seeItemWithEmptySeq: PROC[LR1Item]] = BEGIN grammar: Grammar _ GetGrammarFromLR0Item[lr0Item]; emptySeq: TerminalSeq _ GetEmptyTerminalSeq[grammar]; terminalSet: TerminalSeqSet _ CreateEmptyTerminalSeqSet[grammar]; emptySeqOccurs: BOOLEAN; -- first, build the terminal set that will follow each LR0 Item in the closure BEGIN remainder: LR0Item _ GetNextLR0Item[lr0Item]; emptyInPrev: BOOLEAN _ TRUE; -- initial value, lets the loop go around once WHILE remainder.ref # NIL AND emptyInPrev DO firstOfRemainder: Symbol _ GetFirstSymbol[remainder]; IF firstOfRemainder # NIL THEN BEGIN noSeqsSeen: BOOLEAN _ TRUE; -- tentative; SeeOneTerminalSeq: PROC[seq: TerminalSeq] = BEGIN IF seq # emptySeq THEN [] _ InsertTerminalSeqInSet[terminalSet, seq] ELSE emptyInPrev _ TRUE; noSeqsSeen _ FALSE; END; emptyInPrev _ FALSE; -- tentative value GenFirstNonEmptyTerminalSeqs[firstOfRemainder, SeeOneTerminalSeq]; IF noSeqsSeen THEN emptyInPrev _ TRUE; IF TestCanProduceEmpty[firstOfRemainder] THEN emptyInPrev _ TRUE; END ELSE emptyInPrev _ TRUE; remainder _ GetNextLR0Item[remainder]; ENDLOOP; emptySeqOccurs _ emptyInPrev; END; -- again, check for empty closure IF TestIfTerminalSeqSetEmpty[terminalSet] AND NOT emptySeqOccurs THEN RETURN; -- now generate the lr0 items for the closure, then generate the terminals. BEGIN stop: BOOLEAN _ FALSE; -- tentative HandleOneLR0ClosureItem: PROC[closureItem: LR0Item] RETURNS[--stop-- BOOLEAN] = BEGIN HandleOneTerminalSeq: PROC[ts: TerminalSeq] = {IF NOT stop AND for[[closureItem, ts]] THEN stop _ TRUE}; IF NOT stop THEN GenTerminalSeqsFromSet[terminalSet, HandleOneTerminalSeq]; IF stop THEN RETURN[TRUE]; IF emptySeqOccurs THEN seeItemWithEmptySeq[[closureItem, emptySeq]]; RETURN[FALSE]; END; GenOneSimpleLR0LR0CloseStep[lr0Item, HandleOneLR0ClosureItem]; END; END; <> GenOneSimpleLR0LR0CloseStep: PROC[lr0Item: LR0Item, for: PROC[LR0Item] RETURNS[--stop-- BOOLEAN]] = BEGIN leftSide: Symbol _ GetFirstSymbol[lr0Item]; -- the first symbol after the "dot" stop: BOOLEAN _ FALSE; -- tentative HandleOneProduction: PROC[production: Production] = {IF NOT stop AND for[GetFirstLR0Item[production]] THEN stop _ TRUE}; IF leftSide = NIL THEN RETURN; GenProductionsForSymbol[leftSide, HandleOneProduction]; END; GenElementsOfLR1ItemSubset: PROC[subset: LR1ItemSubset, for: PROC[LR1Item]] = BEGIN SeeOneTerminalSeq: PROC[ts: TerminalSeq] = {for[[subset.lr0Item, ts]]}; GenTerminalSeqsFromSet[subset.terminals, SeeOneTerminalSeq]; END; GenGoTo0KernelItemsFromKernelItem: PROC[kernelItem: LR0Item, for: PROC[Symbol, LR0Item]] = BEGIN seeCloseItem: PROC[closeItem: LR0Item] = {GenKernelGoTo0Items[closeItem, for]}; GenClose0Items[kernelItem, seeCloseItem]; END; DescribeConflictGraph: PROC[on: IO.STREAM, conflictGraph: Graph] = BEGIN nNodes: INT _ 0; nLR0Items: INT _ 0; nLR1Items: INT _ 0; nRootNodes: INT _ 0; seeOneNode: PROC[pnode: Node] RETURNS[BOOLEAN] = BEGIN info: ConflictGraphNodeInfo _ NARROW[GetAssociatedSet[pnode]]; seeOneLR1Item: PROC[item: LR1Item] = {nLR1Items _ nLR1Items + 1}; seeOneLR0Item: PROC[item: LR0Item] = {nLR0Items _ nLR0Items + 1}; IF info # NIL THEN BEGIN IF info.lr1KernelSet # NIL THEN GenerateItemsFromLR1ItemSet[info.lr1KernelSet, seeOneLR1Item]; IF info.lr0KernelSet # NIL THEN GenItemsInLR0ItemSet[info.lr0KernelSet, seeOneLR0Item]; nNodes _ nNodes + 1; IF info.root THEN BEGIN nRootNodes _ nRootNodes + 1; IO.PutF[on, "\NThis conflict graph contains a root node\N"]; ShowConflictNode[on, info]; END; END; RETURN[TRUE]; END; GenerateAllNodes[conflictGraph, seeOneNode]; IO.PutF[on, "\Nthere are %g nodes containing a total of %g lr0 items, a total of %g lr1 items, and %g of the nodes are root nodes\N", IO.int[nNodes], IO.int[nLR0Items], IO.int[nLR1Items], IO.int[nRootNodes]]; END; PrintPrecursorList: PROC[on: IO.STREAM, list: LIST OF Precursor] = BEGIN FOR items: LIST OF Precursor _ list, items.rest WHILE items # NIL DO WITH items.first SELECT FROM i: lr0 Precursor => ShowLR0Item[on, 5, i.item]; i: lr1 Precursor => ShowLR1Item[on, 5, i.item]; ENDCASE => ERROR; IO.PutF[on, "\N"]; ENDLOOP; END; END.