-- file StringCompactor.mesa
-- last edited by Satterthwaite, July 2, 1982 3:55 pm
DIRECTORY
CharIO: TYPE USING [NumberFormat, PutChar, PutNumber, PutString],
Environment: TYPE USING [charsPerWord],
FileStream: TYPE USING [FileByteIndex, Create, EndOf, GetIndex, SetIndex],
LongStorage: TYPE USING [Node, Free],
OSMiscOps: TYPE USING [FindFile],
Stream: TYPE USING [Handle, Delete, GetChar, PutChar, PutWord],
Strings: TYPE USING [String, AppendString],
TableCommand: TYPE USING [CreateBCDStream, FinishBcdStream];
StringCompactor: PROGRAM
IMPORTS
CharIO, FileStream, OSMiscOps, Storage: LongStorage,
Stream, Strings, TableCommand
EXPORTS TableCommand = {
CharsPerWord: CARDINAL = Environment.charsPerWord;
StreamIndex: TYPE = FileStream.FileByteIndex;
SyntaxError: ERROR = CODE;
CompStrDesc: TYPE = RECORD [offset, length: CARDINAL];
nArrays: CARDINAL;
nStrings: CARDINAL;
nChars: CARDINAL;
nWords: CARDINAL;
in: Stream.Handle;
SLptr: TYPE = LONG POINTER TO SL;
SL: TYPE = RECORD [
link: SLptr,
startIndex: StreamIndex,
length: CARDINAL];
ALptr: TYPE = LONG POINTER TO AL;
AL: TYPE = RECORD [
link: ALptr,
name: NL,
ARRAYindex: StreamIndex,
needsIndexDef: BOOL,
headSL, tailSL: SLptr,
nstrings: CARDINAL];
NL: TYPE = RECORD [startIndex: StreamIndex, length: CARDINAL];
BackUp: PROC [s: Stream.Handle] = {
FileStream.SetIndex[s, FileStream.GetIndex[s] - 1]};
NextString: PROC [s: SLptr] RETURNS [found: BOOL ← TRUE] = {
nc: CARDINAL ← 0;
quoteFound, collectingChars: BOOL ← FALSE;
DO
c: CHAR;
IF FileStream.EndOf[in] THEN ERROR SyntaxError;
c ← in.GetChar[];
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 ← FileStream.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 + SIZE[StringBody[nc]]};
lastCR: StreamIndex;
AllDone: ERROR = CODE;
NextItem: PROC [a: ALptr] = {
nc: CARDINAL ← 0;
state: {
start, aRray, arRay, arrAy, arraY,
sTring, stRing, strIng, striNg, strinG, Of, oF, end} ← start;
array: BOOL;
DO
c: CHAR;
IF FileStream.EndOf[in] THEN ERROR AllDone;
c ← in.GetChar[]; 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 ← FileStream.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;
'\n => {
IF state = end THEN EXIT;
lastCR ← FileStream.GetIndex[in]; nc ← 0; state ← start};
IN [0c..' ] => IF state = end THEN EXIT ELSE state ← start;
ENDCASE => state ← start;
ENDLOOP;
a.name.startIndex ← lastCR; a.needsIndexDef ← array;
IF array THEN {
state ← Of;
DO
c: CHAR;
IF FileStream.EndOf[in] THEN ERROR SyntaxError;
c ← in.GetChar[]; 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};
CollectStrings[a];
IF array THEN nArrays ← nArrays + 1};
headAL, tailAL: ALptr;
CollectStrings: PROC [a: ALptr] = {
s: SLptr;
oldnStrings: CARDINAL = nStrings;
a.headSL ← a.tailSL ← NIL;
WHILE NextString[s ← AllocateSL[]] DO AppendSL[a, s] ENDLOOP;
Storage.Free[s];
a.nstrings ← nStrings - oldnStrings};
CollectArrays: PROC = {
a: ALptr;
headAL ← tailAL ← NIL;
nArrays ← nStrings ← nChars ← nWords ← 0;
lastCR ← 0;
DO
NextItem[a ← AllocateAL[] ! AllDone => {Storage.Free[a]; EXIT}];
AppendAL[a];
ENDLOOP};
AllocateSL: PROC RETURNS [s: SLptr] = {
s ← Storage.Node[SIZE[SL]]; s.link ← NIL; RETURN};
AppendSL: PROC [a: ALptr, s: SLptr] = {
IF a.tailSL = NIL THEN a.headSL ← s ELSE a.tailSL.link ← s;
a.tailSL ← s};
AllocateAL: PROC RETURNS [a: ALptr] = {
a ← Storage.Node[SIZE[AL]]; a.link ← NIL; RETURN};
AppendAL: PROC [a: ALptr] = {
IF tailAL = NIL THEN headAL ← a ELSE tailAL.link ← a;
tailAL ← a};
OutStrings: PROC [out: Stream.Handle, compact: BOOL] = {
charPos: CARDINAL;
buffer: PACKED ARRAY [0..CharsPerWord) OF CHAR;
byte: [0 .. CharsPerWord] ← 0;
FlushBuffer: PROC = {UNTIL byte = 0 DO PutChar['\000] ENDLOOP};
PutChar: PROC [c: CHAR] = {
buffer[byte] ← c;
IF (byte ← byte+1) = CharsPerWord THEN {
FOR i: [0..CharsPerWord) IN [0..CharsPerWord) DO
out.PutChar[buffer[i]] ENDLOOP;
byte ← 0}};
IF compact THEN {out.PutWord[nStrings*SIZE[CompStrDesc]+1]; charPos ← 0}
ELSE {out.PutWord[nStrings]; charPos ← (nStrings+1)*CharsPerWord};
FOR a: ALptr ← headAL, a.link UNTIL a = NIL DO
FOR s: SLptr ← a.headSL, s.link UNTIL s = NIL DO
IF compact THEN {
out.PutWord[charPos]; out.PutWord[s.length];
charPos ← charPos + s.length}
ELSE {
out.PutWord[charPos/CharsPerWord];
charPos ← charPos + SIZE[StringBody[s.length]]*CharsPerWord};
ENDLOOP;
ENDLOOP;
IF compact THEN {out.PutWord[nChars]; out.PutWord[nChars]};
FOR a: ALptr ← headAL, a.link UNTIL a = NIL DO
s: SLptr ← a.headSL;
UNTIL s = NIL
DO
nextS: SLptr = s.link;
IF ~compact THEN {out.PutWord[s.length]; out.PutWord[s.length]};
FileStream.SetIndex[in, s.startIndex];
FOR i: CARDINAL IN [0 .. s.length) DO
c: CHAR = in.GetChar[];
PutChar[IF c # '" THEN c ELSE in.GetChar[]];
ENDLOOP;
IF ~compact THEN FlushBuffer[];
Storage.Free[s]; s ← nextS;
ENDLOOP;
ENDLOOP;
FlushBuffer[]};
OutRecordDecl: PROC [
out: Stream.Handle, formatId: Strings.String, compact, altoCode: BOOL] = {
OPEN CharIO;
a: ALptr ← headAL;
FOR i: CARDINAL IN [0..formatId.length) DO
IF formatId[i] = '. THEN EXIT; PutChar[out, formatId[i]];
ENDLOOP;
PutString[out, ": DEFINITIONS = {
CSRptr: TYPE = "L];
IF ~altoCode THEN PutString[out, "LONG "L];
PutString[out, "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
nextA: ALptr = a.link;
nonBlank: BOOL ← FALSE;
FileStream.SetIndex[in, a.name.startIndex];
PutString[out, " "L];
FOR i: CARDINAL IN [0..a.name.length) DO
c: CHAR;
IF a.needsIndexDef AND FileStream.GetIndex[in] = a.ARRAYindex THEN {
PutString[out, " [0.."L];
PutNumber[out, a.nstrings, NumberFormat[10,FALSE,FALSE,0]];
PutChar[out, ')]};
c ← in.GetChar[];
IF nonBlank OR c # ' THEN {PutChar[out, c]; nonBlank ← TRUE};
ENDLOOP;
PutString[out, IF compact THEN "CompStrDesc"L ELSE "StringOffset"L];
Storage.Free[a];
IF (a ← nextA) = NIL THEN EXIT;
PutChar[out, ',]; PutChar[out, '\n];
ENDLOOP;
PutString[out, "];
}.
"L]};
CompileStrings: PUBLIC PROC [
inputFile: Strings.String, -- the source file
interfaceId: Strings.String, -- exported interface or "SELF"
formatId: Strings.String, -- ASCII record declaration
moduleId: Strings.String, -- output file
compact, altoCode: BOOL]
RETURNS [CARDINAL, CARDINAL] = {
t: STRING = [40];
sStream, rStream: Stream.Handle;
t.length ← 0; Strings.AppendString[t, inputFile];
in ← FileStream.Create[OSMiscOps.FindFile[t, read]];
CollectArrays[];
sStream ← TableCommand.CreateBCDStream[
in: in, modId: moduleId,
interfaceId: interfaceId,
altoCode: altoCode];
OutStrings[sStream, compact];
TableCommand.FinishBcdStream[]; Stream.Delete[sStream];
t.length ← 0; Strings.AppendString[t, formatId];
rStream ← FileStream.Create[OSMiscOps.FindFile[t, write]];
OutRecordDecl[rStream, formatId, compact, altoCode]; Stream.Delete[rStream];
Stream.Delete[in];
RETURN [nStrings, nChars]};
}.