-- GrammarBasicImpl.mesa -- last edit September 10, 1984 3:49:59 pm PDT -- Sturgis, April 1, 1986 1:23:25 pm PST DIRECTORY GenOneCasabaParserPrivate USING[GetErrorInfo, KillParserGen], GrammarBasic USING[TerminalVariety], IO USING[int, PutF, rope, STREAM], Rope USING[Equal, Fetch, Length, ROPE]; GrammarBasicImpl: CEDAR PROGRAM IMPORTS GenOneCasabaParserPrivate, IO, Rope EXPORTS GrammarBasic = BEGIN OPEN GenOneCasabaParserPrivate, GrammarBasic, Rope, IO; -- first the basic grammar stuff Grammar: TYPE = REF GrammarBody; GrammarBody: PUBLIC TYPE = RECORD[ startProduction: Production _ NIL, productions: Production _ NIL, symbols: Symbol _ NIL, symbolHashTable: SymbolHashTable, terminalCount: CARDINAL _ 0, terminalList: SymbolCell, -- following entries record info for clients terminalSeqCollection: REF ANY, lr0ItemSetCollection: REF ANY, lr0ItemCollection: REF ANY, lr1ItemSetCollection: REF ANY ]; Symbol: TYPE = REF SymbolBody; SymbolBody: PUBLIC TYPE = RECORD[ text: ROPE, terminalVariety: TerminalVariety _ unique, -- used for terminals spellingOrClass: ROPE, -- used for terminals terminal: BOOLEAN, producesTerminals: BOOLEAN, reachable: BOOLEAN, next: Symbol, -- following entries record info for clients productionList: REF ANY, terminalSeq: REF ANY, canProduceEmpty: BOOLEAN, canBeFirstList: REF ANY ]; Production: TYPE = REF ProductionBody; ProductionBody: PUBLIC TYPE = RECORD[ leftSymbol: Symbol, rightSide: RightSide, index: CARDINAL, next: Production _ NIL, -- following entries record info for clients firstLR0Item: REF ANY _ NIL, associatedLR0Closure: REF ANY _ NIL, associatedLR1Closure: REF ANY _ NIL ]; RightSide: TYPE = REF RightSideBody; RightSideBody: TYPE = RECORD[ symbols: SEQUENCE nRightSymbols: CARDINAL OF Symbol]; SymbolCell: TYPE = REF SymbolCellBody; SymbolCellBody: TYPE = RECORD[ symbol: Symbol, next: SymbolCell]; BuildAGrammar: PUBLIC PROC[ genTheGrammar: PROC[ oneUniqueTerminal: PROC[name: ROPE, spelling: ROPE], -- spelling = NIL if the same spelling as name oneGenericTerminal: PROC[name: ROPE, class: ROPE], oneNonTerminal: PROC[name: ROPE], oneLeftSide: PROC[ruleIndex: CARDINAL, symb: ROPE], aRightSideSymb: PROC[ROPE]]] RETURNS[Grammar] = BEGIN kill: BOOLEAN _ FALSE; -- tentative grammar: Grammar _ NEW[GrammarBody]; lastSymbol: Symbol _ NIL; lastTerminal: SymbolCell _ NIL; nTerminals: CARDINAL _ 0; firstProduction: Production _ NIL; lastProduction: Production _ NIL; currentIndex: CARDINAL; currentLeftSide: Symbol _ NIL; currentRightSideList: SymbolCell _ NIL; lastRightSideCell: SymbolCell _ NIL; currentRightSideSymbolCount: CARDINAL _ 0; SeeOneUniqueTerminal: PROC[name: ROPE, spelling: ROPE] = BEGIN IF spelling = NIL OR Length[spelling] = 0 THEN spelling _ name; HandleOneTerminal[name, unique, spelling]; END; SeeOneGenericTerminal: PROC[name: ROPE, class: ROPE] = {HandleOneTerminal[name, generic, class]}; HandleOneTerminal: PROC[name: ROPE, variety: TerminalVariety, spellingOrClass: ROPE] = BEGIN symbol: Symbol; terminalCell: SymbolCell; symbol _ EnterTerminalSymbol[grammar.symbolHashTable, name, variety, spellingOrClass]; IF lastSymbol # NIL THEN lastSymbol.next _ symbol ELSE grammar.symbols _ symbol; lastSymbol _ symbol; nTerminals _ nTerminals + 1; terminalCell _ NEW[SymbolCellBody _ [symbol, NIL]]; IF lastTerminal = NIL THEN grammar.terminalList _ terminalCell ELSE lastTerminal.next _ terminalCell; lastTerminal _ terminalCell; END; SeeOneNonTerminal: PROC[name: ROPE] = BEGIN symbol: Symbol; symbol _ EnterNonTerminalSymbol[grammar.symbolHashTable, name]; IF lastSymbol # NIL THEN lastSymbol.next _ symbol ELSE grammar.symbols _ symbol; lastSymbol _ symbol; END; CleanUpPrevious: PROC = BEGIN IF currentLeftSide # NIL THEN BEGIN rightSide: RightSide _ NEW[RightSideBody[currentRightSideSymbolCount]]; production: Production _ NEW[ProductionBody _ [currentLeftSide, rightSide, currentIndex]]; FOR I: CARDINAL IN [0..currentRightSideSymbolCount) DO rightSide.symbols[I] _ currentRightSideList.symbol; currentRightSideList _ currentRightSideList.next; ENDLOOP; IF lastProduction = NIL THEN firstProduction _ production ELSE lastProduction.next _ production; IF grammar.startProduction = NIL THEN grammar.startProduction _ production; lastProduction _ production; currentLeftSide _ NIL; currentRightSideList _ lastRightSideCell _ NIL; currentRightSideSymbolCount _ 0; END; END; SeeLeftSide: PROC[ruleIndex: CARDINAL, symb: ROPE] = BEGIN CleanUpPrevious[]; currentIndex _ ruleIndex; currentLeftSide _ LookUpSymbol[grammar.symbolHashTable, symb]; END; SeeARightSideSymbol: PROC[symbolText: ROPE] = BEGIN symbol: Symbol; cell: SymbolCell; symbol _ LookUpSymbol[grammar.symbolHashTable, symbolText]; cell _ NEW[SymbolCellBody _ [symbol, NIL]]; IF lastRightSideCell = NIL THEN currentRightSideList _ cell ELSE lastRightSideCell.next _ cell; lastRightSideCell _ cell; currentRightSideSymbolCount _ currentRightSideSymbolCount + 1; END; grammar.symbolHashTable _ BuildSymbolHashTable[]; genTheGrammar[SeeOneUniqueTerminal, SeeOneGenericTerminal, SeeOneNonTerminal, SeeLeftSide, SeeARightSideSymbol ! GetErrorInfo => BEGIN stream: IO.STREAM; position: INT; [stream, position] _ GetErrorInfo[]; kill _ TRUE; RESUME[stream, position] END]; CleanUpPrevious[]; grammar.productions _ firstProduction; grammar.terminalCount _ nTerminals; IF kill THEN RETURN[NIL]; PrepareTheGrammar[grammar]; RETURN[grammar]; END; GetLength: PUBLIC PROC[production: Production] RETURNS[CARDINAL] = {RETURN[production.rightSide.nRightSymbols]}; GetStartProduction: PUBLIC PROC[grammar: Grammar] RETURNS[Production] = {RETURN[grammar.startProduction]}; GetRightSideSymbol: PUBLIC PROC[production: Production, I: CARDINAL] RETURNS[Symbol] = {RETURN[production.rightSide.symbols[I]]}; GetLeftSideSymbol: PUBLIC PROC[production: Production] RETURNS[Symbol] = {RETURN[production.leftSymbol]}; GenProductions: PUBLIC PROC[grammar: Grammar, for: PROC[Production]] = BEGIN FOR production: Production _ grammar.productions, production.next WHILE production # NIL DO for[production]; ENDLOOP; END; GenAllSymbols: PUBLIC PROC[grammar: Grammar, for: PROC[Symbol]] = BEGIN FOR symbol: Symbol _ grammar.symbols, symbol.next WHILE symbol # NIL DO for[symbol]; ENDLOOP; END; GetSymbolText: PUBLIC PROC[symbol: Symbol] RETURNS[ROPE] = {RETURN[symbol.text]}; GetTerminalVariety: PUBLIC PROC[symbol: Symbol] RETURNS[TerminalVariety] = BEGIN IF NOT symbol.terminal THEN ERROR; RETURN[symbol.terminalVariety]; END; GetTerminalSpelling: PUBLIC PROC[symbol: Symbol] RETURNS[ROPE] = BEGIN IF NOT symbol.terminal THEN SymbolError[symbol, "was expected to be a terminal"]; IF NOT symbol.terminalVariety = unique THEN SymbolError[symbol, "should not be generic"]; RETURN[symbol.spellingOrClass]; END; GetTerminalClass: PUBLIC PROC[symbol: Symbol] RETURNS[ROPE] = BEGIN IF NOT symbol.terminal THEN SymbolError[symbol, "was expected to be a terminal"]; IF NOT symbol.terminalVariety = generic THEN SymbolError[symbol, "should be generic"]; RETURN[symbol.spellingOrClass]; END; GetProductionIndex: PUBLIC PROC[production: Production] RETURNS[CARDINAL] = {RETURN[production.index]}; GetGrammarStats: PUBLIC PROC[grammar: Grammar] RETURNS[ nProductions, nRightSideItems, nTerminals, nNonTerminals: INT] = BEGIN SeeOneProduction: PROC[p: Production] = BEGIN nProductions_nProductions+1; nRightSideItems_nRightSideItems+GetLength[p]; END; SeeOneSymbol: PROC[s: Symbol] = BEGIN IF TestIfTerminal[s] THEN nTerminals _ nTerminals+1 ELSE nNonTerminals _ nNonTerminals+1; END; nProductions _ nRightSideItems _ nTerminals _ nNonTerminals _ 0; GenProductions[grammar, SeeOneProduction]; GenAllSymbols[grammar, SeeOneSymbol]; END; -- then the recorded info to help higher level code NoteTerminalSeqCollection: PUBLIC PROC[grammar: Grammar, ref: REF ANY] = {grammar.terminalSeqCollection _ ref}; GetTerminalSeqCollection: PUBLIC PROC[grammar: Grammar] RETURNS[REF ANY] = {RETURN[grammar.terminalSeqCollection]}; NoteTerminalSeq: PUBLIC PROC[s: Symbol, ref: REF ANY] = {s.terminalSeq _ ref}; GetTerminalSeq: PUBLIC PROC[s: Symbol] RETURNS[REF ANY] = {RETURN[s.terminalSeq]}; NoteLR0ItemSetCollection: PUBLIC PROC[grammar: Grammar, ref: REF ANY] = {grammar.lr0ItemSetCollection _ ref}; GetLR0ItemSetCollection: PUBLIC PROC[grammar: Grammar] RETURNS[REF ANY] = {RETURN[grammar.lr0ItemSetCollection]}; NoteFirstLR0Item: PUBLIC PROC[production: Production, ref: REF ANY] = {production.firstLR0Item _ ref}; GetFirstLR0Item: PUBLIC PROC[production: Production] RETURNS[REF ANY] = {RETURN[production.firstLR0Item]}; NoteLR0ItemCollection: PUBLIC PROC[grammar: Grammar, ref: REF ANY] = {grammar.lr0ItemCollection _ ref}; GetLR0ItemCollection: PUBLIC PROC[grammar: Grammar] RETURNS[REF ANY] = {RETURN[grammar.lr0ItemCollection]}; NoteAssociatedLR0Closure: PUBLIC PROC[production: Production, ref: REF ANY] = {production.associatedLR0Closure _ ref}; GetAssociatedLR0Closure: PUBLIC PROC[production: Production] RETURNS[REF ANY] = {RETURN[production.associatedLR0Closure]}; NoteLR1ItemSetCollection: PUBLIC PROC[grammar: Grammar, ref: REF ANY] = {grammar.lr1ItemSetCollection _ ref}; GetLR1ItemSetCollection: PUBLIC PROC[grammar: Grammar] RETURNS[REF ANY] = {RETURN[grammar.lr1ItemSetCollection]}; NoteAssociatedLR1Closure: PUBLIC PROC[production: Production, ref: REF ANY] = {production.associatedLR1Closure _ ref}; GetAssociatedLR1Closure: PUBLIC PROC[production: Production] RETURNS[REF ANY] = {RETURN[production.associatedLR1Closure]}; -- these procedures are used by the derivitive procedures and their support NoteProductionList: PROC[s: Symbol, list: REF ANY] = {s.productionList _ list}; GetProductionList: PROC[s: Symbol] RETURNS[REF ANY] = {RETURN[s.productionList]}; GetNTerminals: PROC[grammar: Grammar] RETURNS[CARDINAL] = {RETURN[grammar.terminalCount]}; GetTerminalFlag: PROC[s: Symbol] RETURNS[BOOLEAN] = {RETURN[s.terminal]}; NoteCanProduceEmpty: PROC[s: Symbol, flag: BOOLEAN] = {s.canProduceEmpty _ flag}; GetCanProduceEmpty: PROC[s: Symbol] RETURNS[BOOLEAN] = {RETURN[s.canProduceEmpty]}; NoteCanBeFirstList: PROC[s: Symbol, ref: REF ANY] = {s.canBeFirstList _ ref}; GetCanBeFirstList: PROC[s: Symbol] RETURNS[REF ANY] = {RETURN[s.canBeFirstList]}; -- then the derivitive procedures ProductionCell: TYPE = REF ProductionCellBody; ProductionCellBody: TYPE = RECORD[production: Production, next: ProductionCell]; PrepareTheGrammar: PUBLIC PROC[grammar: Grammar] = BEGIN ConstructProductionLists[grammar]; CheckBasicRules[grammar]; ConstructCanProduceEmpty[grammar]; ConstructCanBeFirst[grammar]; END; TestIfTerminal: PUBLIC PROC[symbol: Symbol] RETURNS[BOOLEAN] = {RETURN[symbol.terminal]}; GetNTerminalSymbols: PUBLIC PROC[grammar: Grammar] RETURNS[CARDINAL] = {RETURN[GetNTerminals[grammar]]}; GenTerminalSymbols: PUBLIC PROC[grammar: Grammar, for: PROC[Symbol]] = BEGIN FOR cell: SymbolCell _ NARROW[grammar.terminalList], cell.next WHILE cell # NIL DO for[cell.symbol]; ENDLOOP; END; GenProductionsForSymbol: PUBLIC PROC[s: Symbol, for: PROC[Production]] = BEGIN FOR cell: ProductionCell _ NARROW[GetProductionList[s]], cell.next WHILE cell # NIL DO for[cell.production]; ENDLOOP; END; TestCanProduceEmpty: PUBLIC PROC[s: Symbol] RETURNS[BOOLEAN] = {RETURN[GetCanProduceEmpty[s]]}; GenCanBeFirst: PUBLIC PROC[s: Symbol, for: PROC[Symbol]] = BEGIN FOR cell: SymbolCell _ NARROW[GetCanBeFirstList[s]], cell.next WHILE cell # NIL DO for[cell.symbol]; ENDLOOP; END; -- support code for the derivitive procedures -- This code includes several calculations that ultimately support the computation of First1(A). These are particularly inefficient routines, if they show up significantly in a Spy run, they should be reworked. CheckBasicRules: PROC[grammar: Grammar] = BEGIN -- the grammar is augmented: the left side symbol of start production appears no where else BEGIN first: Production _ grammar.startProduction; start: Symbol _ first.leftSymbol; FOR production: Production _ grammar.productions, production.next WHILE production # NIL DO IF production # grammar.startProduction AND production.leftSymbol = start THEN SymbolError[production.leftSymbol, "is the unique start symbol, hence should not occur on the left side of some production which is not the start production"]; FOR I: CARDINAL IN [0..production.rightSide.nRightSymbols) DO IF production.rightSide.symbols[I] = start THEN SymbolError[production.rightSide.symbols[I], "is the unique start symbol, hence should not occur on the right side of any production"]; ENDLOOP; ENDLOOP; END; -- no terminal appears on the left BEGIN FOR production: Production _ grammar.productions, production.next WHILE production # NIL DO IF production.leftSymbol.terminal THEN SymbolError[production.leftSymbol, "should be a non terminal"]; ENDLOOP; END; -- all symbols lead to non terminal sequences. BEGIN changes: BOOLEAN _ TRUE; FOR symbol: Symbol _ grammar.symbols, symbol.next WHILE symbol # NIL DO symbol.producesTerminals _ symbol.terminal; ENDLOOP; WHILE changes DO changes _ FALSE; FOR production: Production _ grammar.productions, production.next WHILE production # NIL DO producesTerminals: BOOLEAN _ TRUE; IF Equal[production.leftSymbol.text, "product"] THEN {a: INT _ 5; a _ 10}; IF production.leftSymbol.producesTerminals THEN LOOP; FOR I: CARDINAL IN [0..production.rightSide.nRightSymbols) DO symbol: Symbol _ production.rightSide.symbols[I]; IF NOT symbol.producesTerminals THEN {producesTerminals _ FALSE; EXIT}; ENDLOOP; IF producesTerminals THEN BEGIN production.leftSymbol.producesTerminals _ TRUE; changes _ TRUE; END; ENDLOOP; ENDLOOP; FOR symbol: Symbol _ grammar.symbols, symbol.next WHILE symbol # NIL DO IF NOT symbol.producesTerminals THEN SymbolError[symbol, "is a non terminal, but does not generate any terminal strings"]; ENDLOOP; END; -- all symbols are reachable BEGIN changes: BOOLEAN _ TRUE; FOR symbol: Symbol _ grammar.symbols, symbol.next WHILE symbol # NIL DO symbol.reachable _ FALSE; ENDLOOP; grammar.startProduction.leftSymbol.reachable _ TRUE; WHILE changes DO changes _ FALSE; FOR production: Production _ grammar.productions, production.next WHILE production # NIL DO IF NOT production.leftSymbol.reachable THEN LOOP; FOR I: CARDINAL IN [0..production.rightSide.nRightSymbols) DO IF NOT production.rightSide.symbols[I].reachable THEN BEGIN production.rightSide.symbols[I].reachable _ TRUE; changes _ TRUE; END; ENDLOOP; ENDLOOP; ENDLOOP; FOR symbol: Symbol _ grammar.symbols, symbol.next WHILE symbol # NIL DO IF NOT symbol.reachable THEN SymbolError[symbol, "is not reachable from the start symbol"]; ENDLOOP; END; END; ConstructProductionLists: PROC[grammar: Grammar] = BEGIN SetListsEmpty: PROC[s: Symbol] = {NoteProductionList[s, NIL]}; NoteOneProduction: PROC[production: Production] = BEGIN symbol: Symbol _ GetLeftSideSymbol[production]; newCell: ProductionCell _ NEW[ProductionCellBody _ [production, NARROW[GetProductionList[symbol]]]]; NoteProductionList[symbol, newCell]; END; GenAllSymbols[grammar, SetListsEmpty]; GenProductions[grammar, NoteOneProduction]; END; ConstructCanProduceEmpty: PROC[grammar: Grammar] = BEGIN someFlagsSet: BOOLEAN _ TRUE; ClearTheFlags: PROC[s: Symbol] = {NoteCanProduceEmpty[s, FALSE]}; ExploreOneProduction: PROC[production: Production] = BEGIN left: Symbol; FOR I: CARDINAL IN[0..GetLength[production]) DO currentSymbol: Symbol _ GetRightSideSymbol[production, I]; IF NOT GetCanProduceEmpty[currentSymbol] THEN RETURN; ENDLOOP; left _ GetLeftSideSymbol[production]; IF GetCanProduceEmpty[left] THEN RETURN; someFlagsSet _ TRUE; NoteCanProduceEmpty[left, TRUE]; END; GenAllSymbols[grammar, ClearTheFlags]; WHILE someFlagsSet DO someFlagsSet _ FALSE; GenProductions[grammar, ExploreOneProduction]; ENDLOOP; END; ConstructCanBeFirst: PROC[grammar: Grammar] = BEGIN somethingAdded: BOOLEAN _ TRUE; -- initial ClearTheSets: PROC[s: Symbol] = {NoteCanBeFirstList[s, NIL]}; AddToCanBeFirst: PROC[to: Symbol, s: Symbol] = BEGIN found: BOOLEAN _ FALSE; -- initial value FOR cell: SymbolCell _ NARROW[GetCanBeFirstList[to]], cell.next WHILE cell # NIL DO IF cell.symbol = s THEN {found _ TRUE; EXIT}; ENDLOOP; IF NOT found THEN BEGIN newCell: SymbolCell _ NEW[SymbolCellBody _ [s, NARROW[GetCanBeFirstList[to]]]]; NoteCanBeFirstList[to, newCell]; somethingAdded _ TRUE; END; END; ExploreOneProduction: PROC[production: Production] = BEGIN left: Symbol _ GetLeftSideSymbol[production]; FOR I: CARDINAL IN[0..GetLength[production]) DO currentSymbol: Symbol _ GetRightSideSymbol[production, I]; AddToCanBeFirst[left, currentSymbol]; FOR cell: SymbolCell _ NARROW[GetCanBeFirstList[currentSymbol]], cell.next WHILE cell # NIL DO AddToCanBeFirst[left, cell.symbol]; ENDLOOP; IF NOT GetCanProduceEmpty[currentSymbol] THEN EXIT; ENDLOOP; END; GenAllSymbols[grammar, ClearTheSets]; WHILE somethingAdded DO somethingAdded _ FALSE; GenProductions[grammar, ExploreOneProduction]; ENDLOOP; END; -- symbol hashing SymbolHashTable: TYPE = REF SymbolHashTableBody; SymbolHashTableBody: TYPE = RECORD[ slots: SEQUENCE nSlots: CARDINAL OF SymbolHashCell]; SymbolHashCell: TYPE = REF SymbolHashCellBody; SymbolHashCellBody: TYPE = RECORD[ symbol: Symbol, next: SymbolHashCell]; BuildSymbolHashTable: PROCEDURE RETURNS[SymbolHashTable] = BEGIN table: SymbolHashTable _ NEW[SymbolHashTableBody[1024]]; FOR I: CARDINAL IN [0..table.nSlots) DO table.slots[I] _ NIL ENDLOOP; RETURN[table]; END; EnterTerminalSymbol: PROCEDURE[table: SymbolHashTable, name: ROPE, variety: TerminalVariety, spellingOrClass: ROPE] RETURNS[Symbol] = BEGIN symbol: Symbol; new: BOOLEAN; [symbol, new] _ FetchSymbol[table, name]; IF NOT new THEN SymbolError[symbol, "is declared more than once"]; symbol.spellingOrClass _ spellingOrClass; symbol.terminal _ TRUE; symbol.terminalVariety _ variety; RETURN[symbol]; END; EnterNonTerminalSymbol: PROCEDURE[table: SymbolHashTable, name: ROPE] RETURNS[Symbol] = BEGIN symbol: Symbol; new: BOOLEAN; [symbol, new] _ FetchSymbol[table, name]; IF NOT new THEN SymbolError[symbol, "is declared more than once"]; symbol.terminal _ FALSE; RETURN[symbol]; END; LookUpSymbol: PROCEDURE[table: SymbolHashTable, name: ROPE] RETURNS[Symbol] = BEGIN symbol: Symbol; new: BOOLEAN; [symbol, new] _ FetchSymbol[table, name]; IF new THEN SymbolError[symbol, "is not declared"]; RETURN[symbol]; END; FetchSymbol: PROCEDURE[table: SymbolHashTable, name: ROPE] RETURNS[Symbol, --new--BOOLEAN] = BEGIN hashCode: CARDINAL _ 0; symbol: Symbol; cell: SymbolHashCell; FOR I: INT IN [0..Length[name]) DO hashCode _ hashCode + LOOPHOLE[Fetch[name, I], CARDINAL]; ENDLOOP; hashCode _ hashCode MOD table.nSlots; FOR cell: SymbolHashCell _ table.slots[hashCode], cell.next WHILE cell # NIL DO IF Equal[cell.symbol.text, name] THEN RETURN[cell.symbol, FALSE]; ENDLOOP; symbol _ NEW[SymbolBody _ [ name, generic, NIL, FALSE, FALSE, FALSE, NIL, NIL, NIL, FALSE, NIL]]; cell _ NEW[SymbolHashCellBody _ [symbol, table.slots[hashCode]]]; table.slots[hashCode] _ cell; RETURN[symbol, TRUE]; END; SymbolError: PROC[symbol: Symbol, msgText: Rope.ROPE, fatal: BOOLEAN _ FALSE] = BEGIN stream: STREAM; position: INT; [stream, position] _ GetErrorInfo[]; IO.PutF[stream, "\NError in Grammer definition"]; IF position >= 0 THEN IO.PutF[stream, " at position %g", IO.int[position]]; IO.PutF[stream, ", the symbol \"%g\" %g\N\N", IO.rope[symbol.text], IO.rope[msgText]]; IF fatal THEN KillParserGen[]; END; END.. -- remark: September 10, 1984 3:48:44 pm PDT: code to produce canbefirst list had a very bad bug: when finding that S could be a first symbol for S', it did not attempt to include other symbols known to be on the can be first list for S.