-- file StringCompactor.mesa -- last edited by Satterthwaite, January 24, 1980 2:09 PM DIRECTORY AltoDefs: FROM "altodefs" USING [CharsPerWord], CharIO: FROM "chario" USING [CR, NUL, NumberFormat, PutChar, PutNumber, PutString], PGScondefs: FROM "PGScondefs" USING [FixupBcdHeader], StreamDefs: FROM "streamdefs" USING [ StreamHandle, StreamIndex, Read, Write, Append, GetIndex, ModifyIndex, NewByteStream, SetIndex], StringDefs: FROM "stringdefs" USING [WordsForString], SystemDefs: FROM "systemdefs" USING [AllocateHeapNode, FreeHeapNode], TableCommand: FROM "tablecommand" USING [CreateBCDStream]; StringCompactor: PROGRAM IMPORTS CharIO, PGScondefs, StreamDefs, StringDefs, SystemDefs, TableCommand EXPORTS TableCommand = BEGIN OPEN StreamDefs; CharsPerWord: CARDINAL = AltoDefs.CharsPerWord; CompStrDesc: TYPE = RECORD [offset, length: CARDINAL]; nArrays: CARDINAL; nStrings: CARDINAL; nChars: CARDINAL; nWords: CARDINAL; in: StreamHandle; SLptr: TYPE = POINTER TO SL; SL: TYPE = RECORD [ link: SLptr, startIndex: StreamIndex, length: CARDINAL]; ALptr: TYPE = POINTER TO AL; AL: TYPE = RECORD [ link: ALptr, name: NL, ARRAYindex: StreamIndex, needsIndexDef: BOOLEAN, headSL, tailSL: SLptr, nstrings: CARDINAL]; NL: TYPE = RECORD [startIndex: StreamIndex, length: CARDINAL]; BackUp: PROC [s: StreamHandle] = {SetIndex[s, ModifyIndex[GetIndex[s], -1]]}; NextString: PROC [s: SLptr] RETURNS [BOOLEAN] = BEGIN c: CHARACTER; nc: CARDINAL ← 0; quoteFound, collectingChars: BOOLEAN ← FALSE; DO IF in.endof[in] THEN SIGNAL SyntaxError; c ← in.get[in]; IF c = '; AND ~collectingChars THEN RETURN [FALSE]; IF c = '" THEN IF quoteFound THEN IF collectingChars THEN {quoteFound ← FALSE; nc ← nc+1} ELSE ERROR ELSE IF collectingChars THEN quoteFound ← TRUE ELSE {s.startIndex ← GetIndex[in]; collectingChars ← TRUE} ELSE IF quoteFound THEN {s.length ← nc; BackUp[in]; EXIT} ELSE IF collectingChars THEN nc ← nc+1; ENDLOOP; nStrings ← nStrings+1; nChars ← nChars + nc; nWords ← nWords + StringDefs.WordsForString[nc]; RETURN [TRUE] END; lastCR: StreamIndex; AllDone: SIGNAL = CODE; SyntaxError: SIGNAL = CODE; NextItem: PROC [a: ALptr] = BEGIN c: CHARACTER; nc: CARDINAL; state: {start, aRray, arRay, arrAy, arraY, sTring, stRing, strIng, striNg, strinG, Of, oF, end}; array: BOOLEAN; state ← start; nc ← 0; DO IF in.endof[in] THEN SIGNAL AllDone; c ← in.get[in]; nc ← nc+1; SELECT c FROM 'A => state ← SELECT state FROM start => aRray, arrAy => arraY, stRing => strIng, ENDCASE => start; 'R => state ← SELECT state FROM aRray => arRay, arRay => arrAy, stRing => strIng, ENDCASE => start; 'Y => IF state = arraY THEN {array ← TRUE; a.ARRAYindex ← GetIndex[in]; state ← end} ELSE state ← start; 'S => IF state = start THEN {a.name.length ← nc-1; state ← sTring} ELSE state ← start; 'T => state ← IF state = sTring THEN stRing ELSE start; 'I => state ← IF state = strIng THEN striNg ELSE start; 'N => state ← IF state = striNg THEN strinG ELSE start; 'G => IF state = strinG THEN {array ← FALSE; state ← end} ELSE state ← start; CharIO.CR => BEGIN IF state = end THEN EXIT; lastCR ← GetIndex[in]; nc ← 0; state ← start; END; IN [0C..' ] => IF state = end THEN EXIT ELSE state ← start; ENDCASE => state ← start; ENDLOOP; a.name.startIndex ← lastCR; a.needsIndexDef ← array; IF array THEN BEGIN state ← Of; DO IF in.endof[in] THEN SIGNAL SyntaxError; c ← in.get[in]; nc ← nc+1; SELECT c FROM IN [0C..' ] => SELECT state FROM start => state ← Of; Of => NULL; end => EXIT; ENDCASE => state ← start; 'O => state ← IF state = Of THEN oF ELSE start; 'F => state ← IF state = oF THEN end ELSE start; ENDCASE => {a.needsIndexDef ← FALSE; state ← start}; ENDLOOP; a.name.length ← nc; END; CollectStrings[a]; IF array THEN nArrays ← nArrays + 1; END; headAL, tailAL: ALptr; CollectStrings: PROC [a: ALptr] = BEGIN s: SLptr; oldnStrings: CARDINAL = nStrings; a.headSL ← a.tailSL ← NIL; WHILE NextString[s ← AllocateSL[]] DO AppendSL[a, s] ENDLOOP; SystemDefs.FreeHeapNode[s]; a.nstrings ← nStrings - oldnStrings; END; CollectArrays: PROC = BEGIN a: ALptr; headAL ← tailAL ← NIL; nArrays ← nStrings ← nChars ← nWords ← 0; lastCR ← StreamIndex[0,0]; DO NextItem[a ← AllocateAL[] ! AllDone => {SystemDefs.FreeHeapNode[a]; EXIT}]; AppendAL[a]; ENDLOOP; END; AllocateSL: PROC RETURNS [s: SLptr] = {s ← SystemDefs.AllocateHeapNode[SIZE[SL]]; s.link ← NIL; RETURN}; AppendSL: PROC [a: ALptr, s: SLptr] = BEGIN IF a.tailSL = NIL THEN a.headSL ← s ELSE a.tailSL.link ← s; a.tailSL ← s; END; AllocateAL: PROC RETURNS [a: ALptr] = {a ← SystemDefs.AllocateHeapNode[SIZE[AL]]; a.link ← NIL; RETURN}; AppendAL: PROC [a: ALptr] = BEGIN IF tailAL = NIL THEN headAL ← a ELSE tailAL.link ← a; tailAL ← a; END; OutStrings: PROC [out: StreamHandle, compact: BOOLEAN] = BEGIN a: ALptr; s, nexts: SLptr; charpos: CARDINAL; i: CARDINAL; c: CHARACTER; buffer: PACKED ARRAY [0..CharsPerWord) OF CHARACTER; byte: [0 .. CharsPerWord]; FlushBuffer: PROC = {UNTIL byte = 0 DO PutChar[CharIO.NUL] ENDLOOP}; PutChar: PROC [c: CHARACTER] = BEGIN buffer[byte] ← c; IF (byte ← byte+1) = CharsPerWord THEN {out.put[out, buffer]; byte ← 0}; END; IF compact THEN {out.put[out, nStrings*SIZE[CompStrDesc]+1]; charpos ← 0} ELSE {out.put[out, nStrings]; charpos ← (nStrings+1)*CharsPerWord}; FOR a ← headAL, a.link UNTIL a = NIL DO FOR s ← a.headSL, s.link UNTIL s = NIL DO IF compact THEN BEGIN out.put[out, charpos]; out.put[out, s.length]; charpos ← charpos + s.length; END ELSE BEGIN out.put[out, charpos/CharsPerWord]; charpos ← charpos + StringDefs.WordsForString[s.length]*CharsPerWord; END; ENDLOOP; ENDLOOP; IF compact THEN {out.put[out, nChars]; out.put[out, nChars]}; byte ← 0; FOR a ← headAL, a.link UNTIL a = NIL DO s ← a.headSL; FOR s ← a.headSL, nexts UNTIL s = NIL DO IF ~compact THEN {out.put[out, s.length]; out.put[out, s.length]}; SetIndex[in, s.startIndex]; FOR i IN [0 .. s.length) DO c ← in.get[in]; PutChar[IF c # '" THEN c ELSE in.get[in]]; ENDLOOP; IF ~compact THEN FlushBuffer[]; nexts ← s.link; SystemDefs.FreeHeapNode[s]; ENDLOOP; ENDLOOP; FlushBuffer[]; END; OutRecordDecl: PROC [ out: StreamHandle, formatId: STRING, compact: BOOLEAN] = BEGIN OPEN CharIO; a, nexta: ALptr; c: CHARACTER; nonBlank: BOOLEAN; i: CARDINAL; FOR i IN [0..formatId.length) DO IF formatId[i] = '. THEN EXIT; PutChar[out, formatId[i]]; ENDLOOP; PutString[out, ": DEFINITIONS = BEGIN CSRptr: TYPE = BASE POINTER TO CompStrRecord;"L]; PutString[out, IF compact THEN " CompStrDesc: TYPE = RECORD [offset, length: CARDINAL]; CompStrRecord: TYPE = RECORD [ stringOffset: CSRptr RELATIVE POINTER TO StringBody, "L ELSE " StringOffset: TYPE = CSRptr RELATIVE POINTER TO StringBody; StringRecord: TYPE = RECORD [ nStrings: CARDINAL, "L]; a ← headAL; DO SetIndex[in, a.name.startIndex]; PutString[out, " "L]; nonBlank ← FALSE; FOR i IN [0..a.name.length) DO IF a.needsIndexDef AND GetIndex[in] = a.ARRAYindex THEN BEGIN PutString[out, " [0.."L]; PutNumber[out, a.nstrings, NumberFormat[10,FALSE,FALSE,0]]; PutChar[out, ')]; END; c ← in.get[in]; IF nonBlank OR c # ' THEN {PutChar[out, c]; nonBlank ← TRUE}; ENDLOOP; PutString[out, IF compact THEN "CompStrDesc"L ELSE "StringOffset"L]; nexta ← a.link; SystemDefs.FreeHeapNode[a]; IF (a ← nexta) = NIL THEN EXIT; PutChar[out, ',]; PutChar[out, CharIO.CR]; ENDLOOP; PutString[out, "]; END. "L]; END; CompileStrings: PUBLIC PROC [ inputFile: STRING, -- the source file interfaceId: STRING, -- exported interface or "SELF" formatId: STRING, -- ASCII record declaration moduleId: STRING, -- output file compact, altoCode: BOOLEAN] RETURNS [CARDINAL, CARDINAL] = BEGIN sStream, rStream: StreamHandle; in ← NewByteStream[inputFile, Read]; CollectArrays[]; sStream ← TableCommand.CreateBCDStream[ in: in, modId: moduleId, count: 1 + nStrings*(IF compact THEN SIZE[CompStrDesc] ELSE 1) + (IF compact THEN StringDefs.WordsForString[nChars] ELSE nWords), interfaceId: interfaceId, altoCode: altoCode]; OutStrings[sStream, compact]; PGScondefs.FixupBcdHeader[]; sStream.destroy[sStream]; rStream ← NewByteStream[formatId, Write+Append]; OutRecordDecl[rStream, formatId, compact]; rStream.destroy[rStream]; in.destroy[in]; RETURN [nStrings, nChars]; END; END.