-- OpDefsGenerator.mesa
-- last edited by Johnsson, November 2, 1978 10:22 AM
-- last edited by Satterthwaite, May 10, 1983 12:54 pm

DIRECTORY
Exec: TYPE USING [w],
Format: TYPE USING [NumberFormat, Number, StringProc],
Heap: TYPE USING [systemZone],
LongString: TYPE USING [StringToNumber, StringToOctal],
Segments: TYPE USING [FHandle, NewFile, Write],
Streams: TYPE USING [
Handle, Write,
CreateStream, Destroy, Ended, GetChar, GetIndex, NewStream, PutChar, PutWord, SetIndex],
Strings: TYPE USING [String, AppendChar, AppendString],
Time: TYPE USING [AppendCurrent],
TTY: TYPE USING [Handle, GetChar, GetID, PutChar, PutLine, PutString];


OpDefsGenerator: PROGRAM
IMPORTS Exec, Format, Heap, LongString, Segments, Streams, Strings, Time, TTY = {

CompStrDesc: TYPE = RECORD[offset, length: CARDINAL];

nChars: CARDINAL;
in, bOut: Streams.Handle;
tty: TTY.Handle;

nOpCodes: CARDINAL = 256;
OpCode: TYPE = [0..nOpCodes);
StringArray: TYPE = ARRAY OpCode OF Strings.String;
NumberArray: TYPE = ARRAY OpCode OF CARDINAL;
CharArray: TYPE = ARRAY OpCode OF CHAR;

stringData: LONG POINTER TO StringArray ← (Heap.systemZone).NEW[StringArray ← ALL[NIL]];
push: LONG POINTER TO NumberArray ← (Heap.systemZone).NEW[NumberArray];
pop: LONG POINTER TO NumberArray ← (Heap.systemZone).NEW[NumberArray];
len: LONG POINTER TO NumberArray ← (Heap.systemZone).NEW[NumberArray];
mark: LONG POINTER TO CharArray ← (Heap.systemZone).NEW[CharArray];

Name: PROC[s: Strings.String] = {
c: CHAR;
nc: CARDINAL ← 0;
CollectingChars: BOOL ← FALSE;
s.length ← 0;

DO
IF Streams.Ended[in] THEN RETURN;
c ← Streams.GetChar[in];
SELECT c FROM
' , '\t, '\n => IF CollectingChars THEN EXIT;
IN ['0..'9] => {
IF ~CollectingChars THEN {
 Streams.SetIndex[in, Streams.GetIndex[in]-1];
 EXIT};
Strings.AppendChar[s,c]};
IN ['A..'Z], IN ['a..'z] => {
CollectingChars ← TRUE;
Strings.AppendChar[s,c]};
ENDCASE => SIGNAL SyntaxError;
ENDLOOP;
nChars ← nChars + s.length};

Atom: PROC[s: Strings.String, del: CHAR] = {
c: CHAR;
nc: CARDINAL ← 0;
CollectingChars: BOOL ← FALSE;

DO
IF Streams.Ended[in] THEN SIGNAL SyntaxError;
c ← Streams.GetChar[in];
SELECT c FROM
' , '\n => IF CollectingChars THEN EXIT;
IN ['0..'9], IN ['A..'Z], IN ['a..'z], '+ => {
s[nc] ← c; nc ← nc+1; CollectingChars ← TRUE};
ENDCASE => EXIT;
ENDLOOP;
s.length ← nc;
IF c # del THEN SIGNAL SyntaxError};

SyntaxError: SIGNAL = CODE;


CollectOpData: PROC[stream: Streams.Handle] = {
name: STRING ← [20];
s: STRING ← [8];
code: CARDINAL;
CRcount: CARDINAL ← 0;
push^ ← pop^ ← len^ ← ALL[0];
mark^ ← ALL['F];

FOR i: OpCode IN OpCode DO
IF stringData[i] # NIL THEN (Heap.systemZone).FREE[@stringData[i]];
ENDLOOP;
nChars ← 0;
UNTIL CRcount = 3 DO
IF Streams.GetChar[in] = '\n THEN CRcount ← CRcount+1;
ENDLOOP;
code ← 177777b;
THROUGH OpCode DO
Name[name]; IF Streams.Ended[in] THEN EXIT;
Atom[s, '(];
IF s.length = 1 AND s[0] = '+ THEN code ← code + 1
ELSE code ← LongString.StringToNumber[s, 8];
Atom[s, ')]; -- decimal, ignore it
IF name.length # 0 THEN {
stringData[code] ← (Heap.systemZone).NEW[StringBody[name.length]];
Strings.AppendString[stringData[code],name]};
Atom[s, ',];
push[code] ← LongString.StringToOctal[s];
Atom[s, ',];
pop[code] ← LongString.StringToOctal[s];
Atom[s, ',];
len[code] ← LongString.StringToOctal[s];
Atom[s, ';];
mark[code] ← s[0];
ENDLOOP;

FOR i: OpCode IN OpCode DO
IF i MOD 4 = 0 THEN {
f: Format.NumberFormat = [8,TRUE,TRUE,3];
OutString[stream, "--"L];
OutNameR[stream, stringData[i],12];
OutNameR[stream, stringData[i+1],13];
OutNameR[stream, stringData[i+2],13];
OutNameR[stream, stringData[i+3],13];
OutString[stream, " "L];
OutNumF[stream, i,f]; OutChar[stream, '-];
OutNumF[stream, i+3,f]; OutString[stream, "\n "L]};
OutString[stream, " Q["L];
OutNum[stream, push[i]]; OutChar[stream, ',];
OutNum[stream, pop[i]]; OutChar[stream, ',];
OutNum[stream, len[i]]; OutChar[stream, ',];
OutChar[stream, mark[i]]; OutChar[stream, ']];
IF i MOD 4 = 3 THEN {
IF i = OpCode.LAST THEN OutString[stream, "];"L]
ELSE OutChar[stream, ',];
OutChar[stream, '\n]}
ELSE OutChar[stream, ',];
ENDLOOP};

OctalDecimalError: SIGNAL [CARDINAL] = CODE;
OpNameTooLong: ERROR [CARDINAL] = CODE;

OutStrings: PROC = {
charpos: CARDINAL ← 0;
--Streams.Reset[bOut];
Streams.PutWord[bOut, CARDINAL.SIZE + nOpCodes*(CompStrDesc.SIZE)];
FOR i: OpCode IN OpCode DO
j: CARDINAL = IF stringData[i] # NIL THEN stringData[i].length ELSE 0;
Streams.PutWord[bOut, charpos];
Streams.PutWord[bOut, j];
charpos ← charpos + j;
ENDLOOP;
Streams.PutWord[bOut, nChars];
Streams.PutWord[bOut, nChars];
FOR i: OpCode IN OpCode DO
IF stringData[i] # NIL THEN {
FOR j: CARDINAL IN [0..stringData[i].length) DO
Streams.PutChar[bOut, stringData[i][j]] ENDLOOP};
ENDLOOP;
Streams.Destroy[bOut]};


OutOpParams: PROC = {
stream: Streams.Handle ← Streams.NewStream[apoutfile, Streams.Write];
time: STRING ← [20];
GetTime[time];
OutString[stream, " -- generated by OpDefsGenerator "L];
OutString[stream, time];
OutString[stream, "

Q: TYPE = PRIVATE RECORD [
push: [0..3], pop: [0..7], length: [0..3], mark: BOOL];
T: BOOL = TRUE; F: BOOL = FALSE;

OpParms: PRIVATE ARRAY [0..256) OF Q = [\n"L];
CollectOpData[stream];
Streams.Destroy[stream]};


GetTime: PROC[time: STRING] = {
Time.AppendCurrent[time];
time.length ← time.length - 3};

OutMopcodes: PROC = {
stream: Streams.Handle ← Streams.NewStream[amoutfile, Streams.Write];
i: OpCode;
j, l: CARDINAL;
time: STRING ← [20];
GetTime[time];
OutString[stream, " -- generated by OpDefsGenerator "L];
OutString[stream, time];
OutChar[stream, '\n];
OutString[stream, modulename];
OutString[stream, ": DEFINITIONS = {

op: TYPE = [0..400B);

"L];
FOR i IN OpCode DO
IF stringData[i] # NIL AND (l ← stringData[i].length) # 0 THEN {
IF l > 10 THEN ERROR OpNameTooLong[i];
FOR j IN (l..10) DO OutChar[stream, ' ] ENDLOOP;
OutString[stream, prefixString];
OutString[stream, stringData[i]];
OutString[stream, ": op = "L];
OutNumF[stream, i, [8,FALSE,FALSE,3]]; OutChar[stream, 'B];
OutChar[stream, ';]}
ELSE { -- null item, check for rest of line empty
FOR j ← i, j+1 DO
 IF stringData[j] # NIL AND stringData[j].length # 0 THEN EXIT;
IF j MOD 4 = 3 THEN GOTO empty;
ENDLOOP;
FOR j IN [0..22) DO OutChar[stream, ' ]; ENDLOOP;
EXITS empty => {
 blank: BOOL ← j-i=3; -- TRUE iff whole line empty
 i ← j;
 IF blank THEN LOOP}}; -- no CR
IF (i MOD 4) # 3 THEN OutChar[stream, ' ] ELSE OutChar[stream, '\n];
ENDLOOP;
OutString[stream, "}.\n"L];
Streams.Destroy[stream]};


OutNameR: PROC[stream: Streams.Handle, s: Strings.String, n: CARDINAL] = {
l: CARDINAL ← IF s = NIL THEN 0 ELSE s.length;
THROUGH (l..n] DO OutChar[stream, ' ]; ENDLOOP;
OutString[stream, s]};

OutNameL: PROC[stream: Streams.Handle, s: Strings.String, n: CARDINAL] = {
l: CARDINAL ← IF s = NIL THEN 0 ELSE s.length;
OutString[stream, s];
THROUGH (l..n] DO OutChar[stream, ' ]; ENDLOOP};

OutNum: PROC[stream: Streams.Handle, n: CARDINAL] = INLINE {
OutNumF[stream, n, [10,FALSE,FALSE,1]]};

OutNumF: PROC [stream: Streams.Handle, n: CARDINAL, f: Format.NumberFormat] = {
PutNumber: Format.StringProc = {OutString[stream, s]};
Format.Number[n, f, PutNumber]};


OutString: PROC[stream: Streams.Handle, s: Strings.String] = {
IF s # NIL THEN
FOR i: CARDINAL IN [0..s.length) DO Streams.PutChar[stream, s[i]] ENDLOOP};

OutChar: PROC[stream: Streams.Handle, c: CHAR] = INLINE {Streams.PutChar[stream, c]};

OutListing: PROC[filename: STRING] = {
stream: Streams.Handle ← Streams.NewStream[filename, Streams.Write];
time: STRING ← [18];
GetTime[time];
OutString[stream, filename]; OutString[stream, "; "L];
OutString[stream, time]; OutChar[stream, '\n];
OutString[stream, "Format: name octal(decimal)push,pop,count,mark\n\n"L];
FOR i: OpCode IN OpCode DO
OutNameL[stream, stringData[i],8];
OutNumF[stream, i,[8,FALSE,FALSE,3]];
OutChar[stream, '(];
OutNumF[stream, i,[10,FALSE,FALSE,3]];
OutChar[stream, ')];
OutNum[stream, push[i]]; OutChar[stream, ',];
OutNum[stream, pop[i]]; OutChar[stream, ',];
OutNum[stream, len[i]]; OutChar[stream, ',];
OutChar[stream, mark[i]]; OutChar[stream, ';];
IF i MOD 4 = 3 THEN OutChar[stream, '\n] ELSE OutString[stream, " "L];
ENDLOOP;
Streams.Destroy[stream]};

DefaultNames: TYPE = {infile, apoutfile, amoutfile, boutfile, listfile, modulename, prefix};
DefaultStrings: TYPE = ARRAY DefaultNames OF Strings.String;

MopDefaults: DefaultStrings ← [
"OpCodes.txt",
"OpParams",
"Mopcodes.mesa",
"OpNames.binary",
"Mopcodes.list",
"Mopcodes",
"z"];

FopDefaults: DefaultStrings ← [
"FOpCodes.txt",
"FOpParams",
"FOpCodes.mesa",
"FOpNames.binary",
"FOpCodes.list",
"FOpCodes",
"q"];

infile: STRING ← [40];
apoutfile: STRING ← [40];
amoutfile: STRING ← [40];
boutfile: STRING ← [40];
listfile: STRING ← [40];
modulename: STRING ← [40];
prefixString: STRING ← [10];

outFH: Segments.FHandle;

SetDefaults: PROC[p: LONG POINTER TO DefaultStrings] = {
infile.length𡤀 Strings.AppendString[infile, p[$infile]];
apoutfile.length𡤀 Strings.AppendString[apoutfile, p[$apoutfile]];
amoutfile.length𡤀 Strings.AppendString[amoutfile, p[$amoutfile]];
boutfile.length𡤀 Strings.AppendString[boutfile, p[$boutfile]];
listfile.length𡤀 Strings.AppendString[listfile, p[$listfile]];
modulename.length𡤀 Strings.AppendString[modulename, p[$modulename]];
prefixString.length𡤀 Strings.AppendString[prefixString, p[$prefix]]};

GetResponse: PROC[prompt, response: STRING] = {
tty.PutString[prompt];
tty.GetID[response];
tty.PutChar['\n]};

tty ← Exec.w;

tty.PutString["\nMesa OpData Generator\n"];
DO
DO
tty.PutString["\nMopdata, Fopdata, or Quit: "];
SELECT tty.GetChar FROM
'm,'M => {tty.PutLine["Mopdata"]; SetDefaults[@MopDefaults]; EXIT};
'f,'F => {tty.PutLine["Fopdata"]; SetDefaults[@FopDefaults]; EXIT};
'q,'Q => {tty.PutLine["Quit"]; GOTO done};
ENDCASE;
ENDLOOP;
tty.PutLine["\nUse escape key to get defaults"];
GetResponse["Input file: ", infile];
IF infile.length = 0 THEN EXIT;
GetResponse[" OpParams file: ", apoutfile];
GetResponse[" Mopcodes file: ", amoutfile];
GetResponse[" Module name (capitalize correctly): ", modulename];
GetResponse[" Prefix with: ", prefixString];
GetResponse[" binary file for OpName strings: ", boutfile];
GetResponse[" listing file: ", listfile];
in ← Streams.NewStream[infile];
bOut ← Streams.CreateStream[
outFH ← Segments.NewFile[boutfile, Segments.Write],
Streams.Write];
OutOpParams[]; OutStrings[]; OutMopcodes[]; OutListing[listfile];
Streams.Destroy[in];
REPEAT done => NULL;
ENDLOOP;

}.