-- NPGSScan.pgs -- Copyright 1985, 1986, 1988, 1990 by Xerox Corporation. All rights reserved. -- DO NOT CONVERT TO TIOGA FORMAT! (NPGS requires the mesa-style comments) -- Russ Atkinson (RRA) March 17, 1988 10:14:22 am PST -- Doug Wyatt, May 15, 1986 5:36:01 pm PDT -- JKF May 24, 1990 10:02:00 am PDT -- NPGS [defs: NPGSParseTable, grammar: NPGS] NPGSScan.pgs DIRECTORY IO USING [STREAM], NPGS1 USING [ ActionStack, Index, LinkStack, Token, Value, ValueStack, nullValue, InputLoc], NPGSParseTable USING [ endMarker, HashIndex, HashTableRef, IndexTableRef, InitHashTable, InitIndexTable, InitScanTable, InitVocabulary, ProdDataRef, ScanTableRef, TSymbol, VocabularyRef, tokenID, tokenNUM, tokenTAB3, tokenTAB4], NPGSConDefs USING [ aliases, alternateLim, CuspPreSetPriorityTokenArray, -- new for CUSP eofMark, ExpandAliases, ExpandProdInfo, ExpandRhsChar, ExpandSymTab, flags, getindex, inchar, MakeAliases, MakeProdInfo, MakeRhsChar, MakeSymInfo, MakeSymTab, MakeTokenInfo, maxProd, maxRhsSymbols, maxRule, nextAlias, numProd, numRules, outbufLim, outchar, outeol, outnum, outstring, prodInfo, pssLim, resetoutstream, rhsChar, rhsLim, seterrstream, setindex, setoutstream, symInfo, symTab, symTabSize, tokenInfo, tokenSize, totalTokens, warningsLogged], NPGSTypes USING [Options], Rope USING [FromRefText, Length, ROPE]; NPGSScan: CEDAR PROGRAM IMPORTS NPGS1, NPGSConDefs, NPGSParseTable, Rope EXPORTS NPGS1, NPGSConDefs = { OPEN NPGS1, NPGSParseTable, NPGSConDefs; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; -- TreeBuild: interpreter section -- interpreter state check: NAT = 3; warning: NAT = 0; specErrorCases: NAT = 5; token, numRhsChars: CARDINAL 0; lineWidth: INTEGER; insertFlag: CARDINAL; HashChainArray: TYPE = ARRAY [1..symTabSize/4] OF CARDINAL; hashChain: REF HashChainArray; -- local data base (supplied by parser) v: NPGS1.ValueStack; l: NPGS1.LinkStack; q: NPGS1.ActionStack; prodData: ProdDataRef; -- initialization/termination AssignDescriptors: PUBLIC PROC [ qd: NPGS1.ActionStack, vd: NPGS1.ValueStack, ld: NPGS1.LinkStack, pp: ProdDataRef] = { q qd; v vd; l ld; prodData pp; }; OutToken: PUBLIC PROC [symbol: CARDINAL] RETURNS [CARDINAL] = { r: ROPE = "* * *"; IF symbol = 0 THEN {outstring[r]; RETURN[Rope.Length[r]]}; FOR i: CARDINAL IN [0..symInfo[symbol].length) DO outchar[symTab[symbol*tokenSize+i],1]; ENDLOOP; RETURN [symInfo[symbol].length]; }; -- the interpretation rules prix, chix: CARDINAL 0; --indexes into prodInfo and rhsChar rhsFlag: BOOL; lastSymbol, lhsDef: CARDINAL 0; ProcessQueue: PUBLIC PROC [qI, top: CARDINAL, cusp: BOOL] = { i, j, k: CARDINAL; -- local procedures PrintTableHead: PROC [c: CHAR] = { IF flags[echo] THEN {outeol[2]; outstring["||TABLE"]; outchar[c,1]; outeol[1]}; }; SetRuleChain: PROC [rule: CARDINAL, chain: BOOL] = { FixLastProd[]; IF prix=prodInfo.length THEN --production table overflow prodInfo ExpandProdInfo[prodInfo, prodInfo.length/8]; prodInfo[prix].chain chain; numRules MAX[numRules, rule]; IF rule>maxRule THEN Error[check+10,-5,InputLoc[]] ELSE prodInfo[prix].rule rule; }; FixLastProd: PROC = { eofMark: CARDINAL NPGSConDefs.eofMark; prodInfo[prix-1].lhs lhsDef; IF prodInfo[prix-1].count=0 THEN tokenInfo[lhsDef-eofMark].empty TRUE; IF rhsFlag THEN { -- too many rhsChars rhsFlag FALSE; Error[check+5, (prix-1+specErrorCases), InputLoc[]]; }; }; ProdHeader: PROC [new: BOOL] = { prodInfo[prix].index chix; IF lhsDef>eofMark THEN { IF tokenInfo[lhsDef-eofMark].count=alternateLim THEN { Error[check+1, lhsDef,InputLoc[]]; --too many alternatives tokenInfo[lhsDef-eofMark].count 1; }; tokenInfo[lhsDef-eofMark].count tokenInfo[lhsDef-eofMark].count + 1; }; IF flags[echo] THEN { lineWidth outbufLim-tokenSize-14; outeol[IF new THEN 2 ELSE 1]; outnum[prix,3]; outstring[IF prodInfo[prix].chain THEN " C " ELSE " "]; outnum[prodInfo[prix].rule,3]; outchar[' ,2]; outchar[' ,tokenSize-(IF new THEN OutToken[lhsDef] ELSE 0)]; outstring[IF new THEN " ::= " ELSE " | "]; }; prix prix+1; }; LhsSymbol: PROC [symbol: CARDINAL] = { lhsDef symbol; IF lhsDef<=eofMark THEN Error[check+4,lhsDef,InputLoc[]] -- undefined or terminal symbol before ::= ELSE IF tokenInfo[lhsDef-eofMark].index = 0 THEN tokenInfo[lhsDef-eofMark].index prix ELSE Error[check+2,lhsDef,InputLoc[]]; --multiple definitions ProdHeader[TRUE]; }; -- end of local procedures FOR qj: CARDINAL IN [0..qI) DO top top-q[qj].tag.pLength+1; SELECT prodData[q[qj].transition].rule FROM 0 => -- TYPE: NPGSParseTable EXPORTS: SELF -- GOAL: grammar -- TERMINALS: -- symbol num '? '| "::=" 'C "||TABLE1" "||TABLE2" "||TABLE3" -- "||TABLE4" "||INPUT" "||CHAIN" "||LISTS" "||PRINTLR" -- "||PRINTLALR" "||FIRST" "||IDS" "GOAL" -- ALIASES: symbol tokenID num tokenNUM '? tokenQUERY -- "||TABLE3" tokenTAB3 "||TABLE4" tokenTAB4 '? initialSymbol -- PRODUCTIONS: -- grammar ::= '? head ruleset { FixLastProd[]; numProd prix-1; numRhsChars chix-1; i 1; IF flags[echo] THEN outeol[1]; IF numProd > pssLim OR totalTokens > pssLim THEN Error[check+6,0,InputLoc[]]; IF cusp THEN CuspPreSetPriorityTokenArray[hashChain]; -- new for CUSP hashChain NIL; WHILE symInfo[i].used AND i <= totalTokens DO IF i > eofMark AND tokenInfo[i-eofMark].index = 0 THEN EXIT; i i+1; ENDLOOP; IF i <= totalTokens THEN { defflag: BOOL FALSE; lineWidth 0; Error[warning+3,0,InputLoc[]]; seterrstream[]; FOR i IN [i..totalTokens] DO j 0; IF ~symInfo[i].used THEN j j+1; IF i > eofMark AND tokenInfo[i-eofMark].index = 0 THEN j j+2; IF j # 0 THEN { IF (lineWidth lineWidth+(k(11+j)/2)) > outbufLim THEN { outeol[1]; lineWidth k}; outnum[i,5]; IF j#2 THEN outchar['U,1] ELSE defflag TRUE; IF j>1 THEN outchar['D,1]}; ENDLOOP; outeol[1]; resetoutstream[]; IF defflag THEN -- nonterminal used but not defined Error[check+3,-2,InputLoc[]]; }; FOR i IN [1..numProd] DO IF prodInfo[i].chain THEN IF prodInfo[i].count # 1 OR rhsChar[prodInfo[i].index] <= eofMark THEN { Error[warning+2, -(i+specErrorCases), InputLoc[]]; prodInfo[i].chain FALSE; }; ENDLOOP; FinishInput[]; }; 1 => -- head ::= directives terminals nonterminals "||TABLE4" -- head ::= directives terminals nonterminals aliases "||TABLE4" { totalTokens lastSymbol; PrintTableHead['4]; tokenInfo MakeTokenInfo[totalTokens-eofMark+1]; numRules 0; prodInfo MakeProdInfo[maxProd+1]; rhsChar MakeRhsChar[maxRhsSymbols+1]; FOR i IN [0..totalTokens-eofMark] DO tokenInfo[i] [count:0, empty:FALSE, index:0]; ENDLOOP; prodInfo[0] [count:2, rule:0, chain:FALSE, lhs:0, index:0]; FOR i IN [1..maxProd] DO prodInfo[i] [count:0, rule:0, chain:FALSE, lhs:0, index:0]; ENDLOOP; IF flags[echo] THEN { outeol[1]; outchar[' ,20]; outstring["GOAL ::= "]; [] OutToken[eofMark+1]; outchar[' ,1]; [] OutToken[eofMark]; }; rhsChar[0] eofMark+1; rhsChar[1] eofMark; symInfo[eofMark+1].used symInfo[eofMark].used TRUE; prix 1; chix 2; lhsDef 0; rhsFlag FALSE; }; 2 => -- directives ::= {flags ALL[FALSE]; l[top] InputLoc[]}; 3 => -- directive ::= "||INPUT" {flags[echo] TRUE; setoutstream[".echo"]}; 4 => -- directive ::= "||CHAIN" flags[chain] TRUE; 5 => -- directive ::= "||LISTS" flags[lists] TRUE; 6 => -- directive ::= "||PRINTLR" flags[printLR] TRUE; 7 => -- directive ::= "||PRINTLALR" flags[printLALR] TRUE; 8 => -- directive ::= "||FIRST" flags[first] TRUE; 9 => -- directive ::= "||IDS" flags[ids] TRUE; 10 => -- terminals ::= "||TABLE1" { IF flags[echo] THEN { outeol[1]; FOR opt: NPGSTypes.Options IN NPGSTypes.Options DO IF flags[opt] THEN outstring[ SELECT opt FROM echo => "||INPUT ", chain => "||CHAIN ", lists => "||LISTS ", printLR => "||PRINTLR ", printLALR => "||PRINTLALR ", first => "||FIRST ", ENDCASE => "||IDS "]; ENDLOOP; }; PrintTableHead['1]; }; 11 => -- terminals ::= terminals discard symbol -- nonterminals ::= nonterminals discard symbol { lastSymbol v[top+2].s; IF flags[echo] THEN { outnum[lastSymbol, 3]; outchar[' , 2]; [] OutToken[lastSymbol]; outeol[1]}; }; 12 => -- nonterminals ::= "||TABLE2" { PrintTableHead['2]; eofMark lastSymbol; nextAlias 0; -- assert TABLE3 empty in case it is omitted aliases NIL; }; 13 => -- aliases ::= "||TABLE3" { PrintTableHead['3]; aliases MakeAliases[64]; }; 14 => -- aliases ::= aliases symbol symbol {temp: UNSPECIFIED NPGSConDefs.eofMark; IF v[top+1].s>temp THEN Error[check+7,v[top+1].s,InputLoc[]]; IF v[top+2].s<=temp THEN Error[check+8,v[top+2].s,InputLoc[]]; IF nextAlias=aliases.length THEN aliases ExpandAliases[aliases, aliases.length/8]; aliases[nextAlias] [v[top+1].s,v[top+2].s]; nextAlias nextAlias+1; IF flags[echo] THEN { outchar[' ,tokenSize+1-OutToken[v[top+1].s]]; outchar[' ,1]; j CARDINAL[v[top+2].s]*tokenSize; FOR i IN [j..j+tokenSize) WHILE symTab[i]#0C DO outchar[symTab[i],1] ENDLOOP; outeol[1]}; }; 15 => -- discard ::= l[top] InputLoc[]; -- keep the parser error recovery happy 16 => -- rulegroup ::= symbol "::=" {SetRuleChain[prix, FALSE]; LhsSymbol[v[top].s]}; 17 => -- rulegroup ::= prefix symbol "::=" LhsSymbol[v[top+1].s]; 18 => -- rulegroup ::= rulegroup symbol "::=" {SetRuleChain[prix, FALSE]; LhsSymbol[v[top+1].s]}; 19 => -- rulegroup ::= rulegroup prefix symbol "::=" LhsSymbol[v[top+2].s]; 20 => -- rulegroup ::= rulegroup '| {SetRuleChain[prix, FALSE]; ProdHeader[FALSE]}; 21 => -- rulegroup ::= rulegroup prefix '| ProdHeader[FALSE]; 22 => -- rulegroup ::= rulegroup symbol {temp: CARDINAL 0; i v[top+1].s; symInfo[i].used TRUE; IF i=eofMark OR i=eofMark+1 THEN Error[check+9,0,InputLoc[]]; --goal symbols IF flags[echo] THEN { IF lineWidth 0 THEN outchar[' , 1]}; IF chix=rhsChar.length THEN rhsChar ExpandRhsChar[rhsChar, rhsChar.length/8]; rhsChar[chix]i; chix chix +1; IF prodInfo[prix-1].count = rhsLim THEN { prodInfo[prix-1].count 1; rhsFlag TRUE}; temp prodInfo[prix-1].count; prodInfo[prix-1].count temp+1; }; 23 => -- prefix ::= num SetRuleChain[v[top].s, FALSE]; 24 => -- prefix ::= num num -- prefix ::= '? num SetRuleChain[v[top+1].s, FALSE]; 25 => -- prefix ::= discard 'C SetRuleChain[prix, TRUE]; 26 => -- prefix ::= discard 'C num SetRuleChain[v[top+2].s, TRUE]; 27 => -- prefix ::= '? SetRuleChain[prix, FALSE]; 28 => -- directives ::= directives directive -- discard ::= num -- discard ::= '? -- ruleset ::=C rulegroup -- ruleset ::= goalrule rulegroup -- goalrule ::= "GOAL" "::=" symbol symbol NULL; ENDCASE => ERROR; ENDLOOP; }; -- the following procedure is called from the ScanReset if no errors FinishInput: PROC = { emptyFlag: BOOL TRUE; j, k: CARDINAL; -- compute nonterminals deriving empty WHILE emptyFlag DO emptyFlag FALSE; FOR i: CARDINAL IN [1..totalTokens-eofMark] DO -- each nonterminal IF tokenInfo[i].empty THEN LOOP; --which does not derive empty j tokenInfo[i].index; FOR prix: CARDINAL IN [j..j+tokenInfo[i].count) DO -- each production of the nonterminal k prodInfo[prix].index; FOR chix: CARDINAL IN [k..k+prodInfo[prix].count) DO -- each rhs character IF rhsChar[chix]<=eofMark OR ~tokenInfo[rhsChar[chix]-eofMark].empty THEN EXIT; REPEAT FINISHED => {tokenInfo[i].empty emptyFlag TRUE; EXIT}; ENDLOOP ENDLOOP ENDLOOP ENDLOOP; }; -- the following procedure outputs the data structure contents in the tables: -- PRODUCTIONINFO TOKENINFO SYMINFO -- num count rule chain lhs index count empty index link used length symbol -- 4 1 3 5 1 4 5 2 3 1 5 2 4 1 3 1 ... CheckOut: PUBLIC PROC = { IF flags[ids] THEN { seterrstream[]; outeol[1]; outstring[" PRODUCTIONINFO TOKENINFO SYMINFO"]; FOR i: CARDINAL IN [0..MAX[numProd,totalTokens]] DO outeol[1]; outnum[i,4]; outchar[' ,1]; IF i>numProd THEN outchar[' ,20] ELSE { outnum[prodInfo[i].count,3]; outnum[prodInfo[i].rule,5]; outchar[IF prodInfo[i].chain THEN 'C ELSE ' , 1]; outnum[prodInfo[i].lhs,4]; outnum[prodInfo[i].index,5]; outchar[' ,2]}; IF i IN (0..totalTokens] THEN { IF i<=eofMark THEN outchar[' ,11] ELSE { outnum[tokenInfo[i-eofMark].count,3]; outchar[IF tokenInfo[i-eofMark].empty THEN 'E ELSE ' ,1]; outnum[tokenInfo[i-eofMark].index,5]; outchar[' ,2]}; outnum[symInfo[i].link,4]; outchar[IF symInfo[i].used THEN 'U ELSE ' ,1]; outnum[symInfo[i].length,3]; outchar[' ,1]; [] OutToken[i]} ENDLOOP; outeol[1]; outstring["RHSCHAR"]; lineWidth outbufLim; FOR i: CARDINAL IN [0..numRhsChars] DO lineWidth lineWidth+tokenSize+1; IF lineWidth > outbufLim THEN {outeol[1]; outnum[i,4]; lineWidth 4}; outchar[' ,1]; [] OutToken[rhsChar[i]]; ENDLOOP; outeol[1]; resetoutstream[]; }; }; -- error recovery TokenValue: PUBLIC PROC [s: TSymbol] RETURNS [NPGS1.Value] = { RETURN [NPGS1.nullValue]; }; -- Scanner: text input and error routines -- table installation hashTab: HashTableRef; scanTab: ScanTableRef; vocab: VocabularyRef; vocabIndex: IndexTableRef; InstallScanTable: PUBLIC PROC = { hashTab NPGSParseTable.InitHashTable[]; scanTab NPGSParseTable.InitScanTable[]; vocab NPGSParseTable.InitVocabulary[]; vocabIndex NPGSParseTable.InitIndexTable[]; }; -- scanner state stream: STREAM NIL; -- the input stream (a dummy for now) Logger: PROC [PROC [log: STREAM]] NIL; NUL: CHAR = '\000; buffer: REF TEXT NIL; -- token assembly area nTokens: CARDINAL 0; -- token count nErrors: CARDINAL; -- lexical errors char: CHAR; -- current (most recently scanned) character tI: CARDINAL 0; -- its (stream) index eof: BOOL; NextChar: PROC = { -- also expanded inline within Atom tI tI + 1; [char, eof] inchar[]}; NextToken: PUBLIC PROC RETURNS[t: Token] = { --OPEN t; LocateToken: PROC[string: REF TEXT] RETURNS[CARDINAL] = { -- returns token corresponding to string i, j: CARDINAL; j hashChain[(string.length*256+(string[0].ORD)) MOD (symTabSize/4) + 1]; WHILE j # 0 DO IF symInfo[j].length = string.length THEN { i j*tokenSize; FOR k: CARDINAL IN [0..string.length) DO IF symTab[i+k]#string[k] THEN EXIT REPEAT FINISHED => RETURN [j] ENDLOOP; }; j symInfo[j].link; ENDLOOP; RETURN [0]; }; TokenToSymTab: PROC[string: REF TEXT, token: CARDINAL] = { i, j: CARDINAL; i token*tokenSize; FOR j IN [0..string.length) DO symTab[i+j] string[j] ENDLOOP; symInfo[token].length string.length; symInfo[token].used FALSE; j (string.length*256+(string[0].ORD)) MOD (symTabSize/4) + 1; symInfo[token].link hashChain[j]; hashChain[j] token; }; TokenOrId: PROC[sub: CARDINAL] = { j, s1, s2: CARDINAL; h: HashIndex; WHILE char NOT IN [NUL..' ] DO IF subtokenSize THEN { buffer.length sub tokenSize; Error[1,-1,t.index]}; --overlength IF sub = 1 THEN { t.class scanTab[buffer[0]]; IF t.class # 0 THEN RETURN}; j buffer[0] - 0C; h ((j*128-j) + CARDINAL[buffer[sub-1]-0C]) MOD HashIndex.LAST + 1; WHILE (j hashTab[h].symbol) # 0 DO IF vocabIndex[j]-(s2vocabIndex[j-1]) = sub THEN FOR s1 IN [0 .. sub) DO IF buffer[s1] # vocab.text[s2] THEN EXIT; s2 s2+1; REPEAT FINISHED => { IF j = tokenTAB3 THEN insertFlag 2 ELSE IF j = tokenTAB4 THEN insertFlag 3; IF j<=tokenNUM THEN EXIT; t.class j; RETURN}; ENDLOOP; IF (h hashTab[h].link) = 0 THEN EXIT; ENDLOOP; buffer.length sub; t.class tokenID; t.value.s LocateToken[buffer]; SELECT insertFlag FROM 1 => -- reading terminals and nonterminals IF t.value # NPGS1.nullValue THEN Error[check+2,t.value.s,t.index] -- multiply defined symbol ELSE { IF token=symTab.length THEN { symTab ExpandSymTab[symTab, symTab.length/16]; symTab ExpandSymTab[symTab, symInfo.length/16]; }; TokenToSymTab[buffer, token]; t.value.s token; --[scalar[token]]; token token+1; }; 2 => -- processing aliases IF t.value=NPGS1.nullValue THEN { s1 token*tokenSize; IF token=symTab.length THEN symTab ExpandSymTab[symTab, symTab.length/16]; FOR j IN [0..buffer.length) DO symTab[s1+j] buffer[j] ENDLOOP; t.value.s token; --[scalar[token]]; token token+1; }; 3 => -- processing productions IF t.value = NPGS1.nullValue THEN Error[check+3,-3,t.index]; --symbol not defined ENDCASE; }; DO WHILE char IN [NUL..' ] DO SELECT char FROM '\032 --^Z-- => UNTIL char = '\n DO IF eof THEN GO TO EndFile; NextChar[]; ENDLOOP; ENDCASE; IF eof THEN GO TO EndFile; NextChar[]; ENDLOOP; t.index tI; t.value.s NPGS1.nullValue; SELECT char FROM IN ['0..'9] => { val: CARDINAL 0; valid: BOOL TRUE; maxVal: CARDINAL = CARDINAL.LAST; WHILE char IN ['0..'9] DO IF valid THEN { d: [0..9] = char.ORD-'0.ORD; valid val { NextChar[]; IF char # '- THEN {buffer[0] '-; TokenOrId[1]; GO TO GotNext}; char NUL; DO pChar: CHAR = char; IF eof THEN GO TO EndFile; NextChar[]; SELECT char FROM '- => IF pChar = '- THEN EXIT; '\n => EXIT; ENDCASE; ENDLOOP; NextChar[]; }; ENDCASE => {TokenOrId[0]; GO TO GotNext}; REPEAT GotNext => NULL; EndFile => {t.class endMarker; t.index tI; t.value.s NPGS1.nullValue}; ENDLOOP; nTokens nTokens + 1; }; -- initialization/finalization ScanInit: PUBLIC PROC [source: STREAM, logger: PROC [PROC [log: STREAM]]] = { stream source; Logger logger; buffer NEW[TEXT[tokenSize]]; tI 0; [char, eof] inchar[]; nTokens nErrors 0; buffer.length tokenSize; -- initialise symbol table token 1; insertFlag 1; symTab MakeSymTab[(symTabSize+1)*tokenSize]; symInfo MakeSymInfo[symTabSize+1]; FOR i: CARDINAL IN [1..symTabSize] DO symInfo[i] [link:0, length:0, used:FALSE]; ENDLOOP; hashChain NEW[HashChainArray ALL[0]]; }; ScanReset: PUBLIC PROC = { stream NIL; Logger NIL; CheckOut[]; buffer NIL; }; ScanStats: PUBLIC PROC RETURNS[CARDINAL, CARDINAL] = { RETURN [nTokens, nErrors]; }; -- error handling ResetScanIndex: PUBLIC PROC[index: CARDINAL] RETURNS[success: BOOLTRUE] = { IF index = tI THEN RETURN; setindex[index]; [char, eof] inchar[]; tI index; }; PrintTextLine: PROC[origin: CARDINAL] RETURNS[start: CARDINAL] = { lineIndex: CARDINAL; char: CHAR; n: [1..100]; start lineIndex origin; FOR n IN [1..100] UNTIL lineIndex = 0 DO lineIndex lineIndex - 1; setindex[lineIndex]; IF inchar[].c = '\n THEN EXIT; start lineIndex; ENDLOOP; setindex[start]; FOR n IN [1..100] UNTIL ([c: char] inchar[]).end DO SELECT char FROM '\n, '\032 --^Z-- => EXIT; ENDCASE => outchar[char,1]; ENDLOOP; outeol[1]; }; ErrorContext: PUBLIC PROC [to: STREAM, message: ROPE, tokenIndex: CARDINAL] = { saveIndex: CARDINAL = getindex[]; origin: CARDINAL = tokenIndex; char: CHAR; setindex[PrintTextLine[origin]]; UNTIL getindex[] = origin OR ([c: char] inchar[]).end DO outchar[IF char = '\n THEN '\n ELSE ' ,1]; ENDLOOP; outstring["^ ["]; outnum[tokenIndex,1]; outchar['],1]; outeol[1]; outstring[message]; setindex[saveIndex]; }; Error: PROC [code: CARDINAL, control: INTEGER, index: CARDINAL] = { Inner: PROC [log: STREAM -- a dummy for now --] ~ { ErrorContext[log, SELECT code FROM 1 => "WARNING - Overlength symbol (increase TOKENSIZE?) truncated to - ", 2 => "WARNING - Not a chain production - ", 3 => "WARNING - Unused(U) or undefined(D)symbols (refer to TABLE1 and 2)", 4 => "ERROR - Nonterminal with too many rules (increase ALTERNATELIM?) - ", 5 => "ERROR - Multiple definitions of symbol - ", 6 => "ERROR - Symbol not defined - ", 7 => "ERROR - Terminal precedes ::= - ", 8 => "ERROR - Too many rhs symbols in production (increase RHSLIM?) - ", 9 => "ERROR - Internal field will overflow - increase PSSLIM", 10 => "ERROR - Aliased symbol not a terminal symbol - ", 11 => "ERROR - Aliases must not be terminal symbols - ", 12 => "ERROR - Goal symbols used in rhs", 13 => "ERROR - Number greater than ", ENDCASE => NIL, index]; SELECT -control FROM <0 => [] OutToken[control]; 0 => NULL; 1 => outstring[Rope.FromRefText[buffer]]; 2 => outstring["see previous message"]; 3 => {outstring[Rope.FromRefText[buffer]]; outstring[" not in TABLE1 or 2"]}; 4 => NULL; -- not used 5 => outstring["MAXRULE"]; ENDCASE => outnum[-control-specErrorCases, 5]; outeol[2]; }; Logger[Inner]; IF code>check THEN nErrors nErrors+1 ELSE warningsLogged TRUE; }; }.  2NewlineDelimiter Jͷ[\