-- OpDefsGenerator.mesa
-- last edited by Johnsson, November 2, 1978  10:22 AM
-- last edited by Satterthwaite, September 16, 1982 9:14 am

DIRECTORY
  Ascii: TYPE USING [CR, SP, TAB],
  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 =

BEGIN

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

nChars: CARDINAL;
InStream, bOutStream: Streams.Handle;
tty: TTY.Handle;

numopcodes: CARDINAL = 256;
opcode: TYPE = [0..numopcodes);
StringArray: TYPE = ARRAY opcode OF Strings.String;
NumberArray: TYPE = ARRAY opcode OF CARDINAL;
CharArray: TYPE = ARRAY opcode OF CHARACTER;

StringData: LONG POINTER TO StringArray ← (Heap.systemZone).NEW[StringArray ← ALL[NIL]];
push: LONG POINTER TO ARRAY opcode OF CARDINAL ← (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: PROCEDURE [s: Strings.String] =
  BEGIN
  c: CHARACTER;
  nc: CARDINAL ← 0;
  CollectingChars: BOOLEAN ← FALSE;
  s.length ← 0;

  DO
  IF Streams.Ended[InStream] THEN RETURN;
  c ← Streams.GetChar[InStream];
  SELECT c FROM
    Ascii.SP, Ascii.TAB, Ascii.CR =>
      IF CollectingChars THEN EXIT;
    IN ['0..'9] =>
      BEGIN
      IF ~CollectingChars THEN
	BEGIN
	Streams.SetIndex[InStream, Streams.GetIndex[InStream]-1];
	EXIT
	END;
      Strings.AppendChar[s,c];
      END;
    IN ['A..'Z], IN ['a..'z] =>
      BEGIN
      CollectingChars ← TRUE;
      Strings.AppendChar[s,c];
      END;
    ENDCASE => SIGNAL SyntaxError;
  ENDLOOP;
  nChars ← nChars + s.length;
  END;

Atom: PROCEDURE [s: Strings.String, del: CHARACTER] =
  BEGIN
  c: CHARACTER;
  nc: CARDINAL ← 0;
  CollectingChars: BOOLEAN ← FALSE;

  DO
  IF Streams.Ended[InStream] THEN SIGNAL SyntaxError;
  c ← Streams.GetChar[InStream];
  SELECT c FROM
    Ascii.SP, Ascii.CR =>
      IF CollectingChars THEN EXIT;
    IN ['0..'9], IN ['A..'Z], IN ['a..'z], '+ =>
      BEGIN s[nc] ← c; nc ← nc+1; CollectingChars ← TRUE END;
    ENDCASE => EXIT;
  ENDLOOP;
  s.length ← nc;
  IF c # del THEN SIGNAL SyntaxError;
  END;

SyntaxError: SIGNAL = CODE;


CollectOpData: PROCEDURE [stream: Streams.Handle] = 
  BEGIN
  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[InStream] = Ascii.CR THEN CRcount ← CRcount+1;
    ENDLOOP;
  code ← 177777B;
  THROUGH opcode DO
    Name[name]; IF Streams.Ended[InStream] 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
      BEGIN
      StringData[code] ← (Heap.systemZone).NEW[StringBody[name.length]];
      Strings.AppendString[StringData[code],name];
      END;
    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
      BEGIN
      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, "
  "L];
      END;
    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
      BEGIN
      IF i = LAST[opcode] THEN OutString[stream, "];"L]
      ELSE OutChar[stream, ',];
      OutChar[stream, Ascii.CR];
      END
    ELSE OutChar[stream, ',];
    ENDLOOP;
  END;

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

OutStrings: PROCEDURE =
  BEGIN
  charpos: CARDINAL ← 0;
--Streams.Reset[bOutStream];
  Streams.PutWord[bOutStream, numopcodes*SIZE[CompStrDesc]+1];
  FOR i: opcode IN opcode DO
    j: CARDINAL = IF StringData[i] # NIL THEN StringData[i].length ELSE 0;
    Streams.PutWord[bOutStream, charpos];
    Streams.PutWord[bOutStream, j];
    charpos ← charpos + j;
    ENDLOOP;
  Streams.PutWord[bOutStream, nChars];
  Streams.PutWord[bOutStream, nChars];
  FOR i: opcode IN opcode DO
   IF StringData[i] # NIL THEN
    BEGIN
    FOR j: CARDINAL IN [0..StringData[i].length)
      DO Streams.PutChar[bOutStream, StringData[i][j]] ENDLOOP;
    END;
   ENDLOOP;
  Streams.Destroy[bOutStream];
  END;


OutOpParams: PROCEDURE =
  BEGIN
  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: BOOLEAN];
  T: BOOLEAN = TRUE; F: BOOLEAN = FALSE;

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


GetTime: PROCEDURE [time: STRING] =
  BEGIN
  Time.AppendCurrent[time];
  time.length ← time.length - 3;
  END;

OutMopcodes: PROCEDURE =
  BEGIN
  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, Ascii.CR];
  OutString[stream, modulename];
  OutString[stream, ": DEFINITIONS =

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

"L];
  FOR i IN opcode DO
    IF StringData[i] # NIL AND (l ← StringData[i].length) # 0 THEN
      BEGIN
      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, ';];
      END
    ELSE
      BEGIN -- 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 =>
	BEGIN
	blank: BOOLEAN ← j-i=3; -- TRUE iff whole line empty
	i ← j;
	IF blank THEN LOOP; -- no CR
	END;
      END;
    IF (i MOD 4) # 3 THEN OutChar[stream, ' ] ELSE OutChar[stream, Ascii.CR];
    ENDLOOP;
  OutString[stream, "END...
"L];
  Streams.Destroy[stream];
  END;


OutNameR: PROCEDURE [stream: Streams.Handle, s: Strings.String, n: CARDINAL] =
  BEGIN
  l: CARDINAL ← IF s = NIL THEN 0 ELSE s.length;
  THROUGH (l..n] DO OutChar[stream, Ascii.SP]; ENDLOOP;
  OutString[stream, s];
  END;

OutNameL: PROCEDURE [stream: Streams.Handle, s: Strings.String, n: CARDINAL] =
  BEGIN
  l: CARDINAL ← IF s = NIL THEN 0 ELSE s.length;
  OutString[stream, s];
  THROUGH (l..n] DO OutChar[stream, Ascii.SP]; ENDLOOP;
  END;

OutNum: PROCEDURE [stream: Streams.Handle, n: CARDINAL] = INLINE
  BEGIN
  OutNumF[stream, n, [10,FALSE,FALSE,1]];
  END;

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


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

OutChar: PROCEDURE [stream: Streams.Handle, c: CHARACTER] = INLINE
  BEGIN Streams.PutChar[stream, c] END;

OutListing: PROCEDURE [filename: STRING] =
  BEGIN
  stream: Streams.Handle ← Streams.NewStream[filename, Streams.Write];
  time: STRING ← [18];
  GetTime[time];
  OutString[stream, filename]; OutString[stream, ";  "L];
  OutString[stream, time]; OutChar[stream, Ascii.CR];
  OutString[stream, "Format: name octal(decimal)push,pop,count,mark

"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, Ascii.CR] ELSE OutString[stream, "  "L];
    ENDLOOP;
  Streams.Destroy[stream];
  END;

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: PROCEDURE [p: LONG POINTER TO DefaultStrings] =
  BEGIN
  infile.length←0;       Strings.AppendString[infile, p[$infile]];
  apoutfile.length←0;    Strings.AppendString[apoutfile, p[$apoutfile]];
  amoutfile.length←0;    Strings.AppendString[amoutfile, p[$amoutfile]];
  boutfile.length←0;     Strings.AppendString[boutfile, p[$boutfile]];
  listfile.length←0;     Strings.AppendString[listfile, p[$listfile]];
  modulename.length←0;   Strings.AppendString[modulename, p[$modulename]];
  prefixString.length←0; Strings.AppendString[prefixString, p[$prefix]];
  END;

GetResponse: PROCEDURE[prompt, response: STRING] =
  BEGIN
  TTY.PutString[tty, prompt];
  TTY.GetID[tty, response];
  TTY.PutChar[tty, Ascii.CR];
  END;

tty ← Exec.w;

TTY.PutString[tty, "
Mesa OpData Generator
"];
DO
DO
  TTY.PutString[tty, "
Mopdata, Fopdata, or Quit: "];
  SELECT TTY.GetChar[tty] FROM
    'm,'M => BEGIN TTY.PutLine[tty, "Mopdata"]; SetDefaults[@MopDefaults]; EXIT END;
    'f,'F => BEGIN TTY.PutLine[tty, "Fopdata"]; SetDefaults[@FopDefaults]; EXIT END;
    'q,'Q => BEGIN TTY.PutLine[tty, "Quit"]; GOTO done END;
    ENDCASE;
  ENDLOOP;
TTY.PutChar[tty, Ascii.CR];
TTY.PutLine[tty, "Use 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];
InStream ← Streams.NewStream[infile];
bOutStream ← Streams.CreateStream[
  outFH ← Segments.NewFile[boutfile, Segments.Write],
  Streams.Write];
OutOpParams[]; OutStrings[]; OutMopcodes[]; OutListing[listfile];
Streams.Destroy[InStream];
REPEAT done => NULL;
ENDLOOP;

END.