-- file Sequencer.mesa
-- last modified by Satterthwaite, June 8, 1983 4:27 pm
-- last modified by Russ Atkinson, 10-Dec-80 10:53:50

DIRECTORY
  CBinary: TYPE USING [DebugTab, ErrorTab, MesaTab],
  Alloc: TYPE USING [
    Handle, Selector, TableInfo, Chunkify, Create, Destroy, Failure, Reset],
  CharIO: TYPE USING [PutChar, PutOctal, PutString],
  CompilerOps: TYPE USING [LetterSwitches, StreamId, Transaction],
  CompilerUtil: TYPE USING [
    TableId,
    InstallParseTables, PrefillSymbols, P1Unit, P2Unit, P3Unit, P3Postlude, P4Unit, P5module,
    EndObjectFile, PrintBodies, PrintSymbols, PrintTree, StartObjectFile, TableOut],
  ComData: TYPE USING [
    bcdSeg, codeSeg, compilerVersion, globalFrameSize, interface, linkCount, matched,
    nErrors, nWarnings, objectBytes, objectFile, objectStamp, objectVersion,
    ownSymbols, pattern, source, sourceTokens, symSeg, switches, table, textIndex, zone],
  Copier: TYPE USING [FileInit, FileReset],
  File: TYPE USING [nullCapability],
  FileParmOps: TYPE USING [AcquireOutput, ReleaseOutput],
  FileStream: TYPE USING [Create, SetLength],
  LiteralOps: TYPE USING [Initialize, Finalize],
  Log: TYPE USING [Error],
  OSMiscOps: TYPE USING [
    BcdCreateTime, GenerateUniqueId, GetTableBase, --ImageId,--
    MergeStamps, SignalArgs, StampToTime, TimeToStamp],
  Stream: TYPE USING [Handle, Delete, SendNow],
  Strings: TYPE USING [String, AppendString],
  SymLiteralOps: TYPE USING [Finalize, Initialize],
  SymbolPack: TYPE,
  SymbolOps: TYPE USING [Finalize, Initialize],
  SymbolSegment: TYPE USING [Tables, treeType],
  SymbolTable: TYPE USING [Forget, anySpan],
  Time: TYPE USING [Append, Unpack],
  TimeStamp: TYPE USING [Stamp],
  Tree: TYPE USING [Link],
  TreeOps: TYPE USING [Finalize, Initialize, PopTree, Reset];

Sequencer: MONITOR
    IMPORTS
      Alloc, CBinary, CharIO, CompilerUtil, Copier, FileStream, FileParmOps, Log, LiteralOps,
      OSMiscOps, Stream, SymLiteralOps, SymbolOps, SymbolTable, Strings, Time, TreeOps, 
      ownSymbols: SymbolPack, dataPtr: ComData
    EXPORTS CompilerOps, CompilerUtil = { 

-- scratch region and scratch zone management

  zone: UNCOUNTED ZONE;
  table: Alloc.Handle ← NIL;

  AcquireZone: PUBLIC PROC RETURNS [UNCOUNTED ZONE] = {
    RETURN [zone]};
    

-- stream management

  StreamId: TYPE = CompilerOps.StreamId;
  TransactionPtr: TYPE = POINTER TO CompilerOps.Transaction;

  getStream: PROC [StreamId] RETURNS [Stream.Handle];

  streamInfo: ARRAY StreamId[$source .. $log] OF RECORD [
    access: {read, write},
    stream: Stream.Handle,
    status: RECORD [count: NAT, open: BOOL]];

  AcquireStream: PUBLIC PROC [id: StreamId] RETURNS [stream: Stream.Handle] = {
    IF streamInfo[id].stream = NIL THEN {
      streamInfo[id].stream ← getStream[id];
      streamInfo[id].status ← [count:0, open:TRUE]};
    stream ← streamInfo[id].stream;
    IF streamInfo[id].status = [count: 0, open: FALSE] THEN
      streamInfo[id].status.open ← TRUE;
    streamInfo[id].status.count ← streamInfo[id].status.count + 1};

  ReleaseStream: PUBLIC PROC [id: StreamId] = {
    streamInfo[id].status.count ← streamInfo[id].status.count - 1;
    IF streamInfo[id].status.count = 0 THEN {
      IF streamInfo[id].access = $write THEN streamInfo[id].stream.SendNow[];
      streamInfo[id].status.open ← FALSE}};


-- table segment management

  tableBase: ARRAY CompilerUtil.TableId [$error..$debug] OF LONG POINTER;
  
  AcquireTable: PUBLIC PROC [id: CompilerUtil.TableId] RETURNS [LONG POINTER] = {
    RETURN [tableBase[id]]};

  ReleaseTable: PUBLIC PROC [id: CompilerUtil.TableId] = {};


-- compiler inquiries

  DefaultSwitches: PUBLIC PROC RETURNS [CompilerOps.LetterSwitches] = {
    RETURN [[
      TRUE , -- A  Address fault for NIL checks
      TRUE , -- B  Bounds checking
      TRUE , -- C  compile for Cedar (special FORK)
      FALSE, -- D  call Debugger on compiler error (FALSE => just log error)
      TRUE , -- E  fixed (big Eval stack)
      TRUE , -- F  Floating point microcode
      TRUE , -- G  TRUE => loG goes to compiler.log, FALSE => use foo.errlog
      FALSE, -- H  unused
      FALSE, -- I  unused
      FALSE, -- J  cross-Jumping optimization
      FALSE, -- K  unused
      TRUE , -- L  allocate space for code Links
      TRUE , -- M  reference counting Microcode
      TRUE , -- N  Nil pointer checking
      FALSE, -- O  unused
      FALSE, -- P  Pause after compilation with errors
      FALSE, -- Q  unused
      FALSE, -- R  unused
      TRUE , -- S  Sort (by static frequency) global vars & entry indexes
      FALSE, -- T  unused
      FALSE, -- U  uninitialized variable checking
      FALSE, -- V  unused
      TRUE , -- W  log Warning messages
      FALSE, -- X  unused
      FALSE, -- Y  complain about KFCB
      FALSE  -- Z  unused
      ]]};

  CompilerVersion: PUBLIC PROC RETURNS [TimeStamp.Stamp] = {
    RETURN [dataPtr.compilerVersion]};

  AppendHerald: PUBLIC PROC [s: Strings.String] = {
    t: STRING = [20];
    Time.Append[t, Time.Unpack[[OSMiscOps.BcdCreateTime[]]]];
    Strings.AppendString[s, "Cedar 4.x Compiler of "L];
    Strings.AppendString[s, t]};

  
-- compiler sequencing

  pass: CHARACTER ['1..'5];

  ExtendedTables: TYPE = Alloc.Selector[SymbolSegment.Tables.FIRST .. SymbolSegment.Tables.LAST+1];
  
  Initialize: PROC = {
    weights: ARRAY ExtendedTables OF Alloc.TableInfo ← [
      [30], [20], [4], [4], [4], [2], [4], [1], [2], [1], [2], [2], [20]];	-- empirical
    IF table = NIL THEN {
      table ← Alloc.Create[weights: DESCRIPTOR[weights]]; 
      table.Chunkify[SymbolSegment.treeType];
      table.Chunkify[SymbolSegment.Tables.LAST+1]}	-- codeType
    ELSE table.Reset[];
    SymbolOps.Initialize[table, zone];  LiteralOps.Initialize[table, zone];
    TreeOps.Initialize[table, zone]};

  Finalize: PROC [parms: TransactionPtr, ownedObject: BOOL] = {
    parms.objectVersion ← dataPtr.objectVersion;
    parms.interface ← dataPtr.interface;
    parms.matched ← dataPtr.matched AND (dataPtr.nErrors = 0);
    parms.sourceTokens ← dataPtr.sourceTokens;
    parms.nErrors ← dataPtr.nErrors;  parms.nWarnings ← dataPtr.nWarnings;
    parms.objectBytes ← dataPtr.objectBytes;
    parms.objectFrameSize ← dataPtr.globalFrameSize;  
    parms.linkCount ← dataPtr.linkCount;
    parms.bcdPages ← [base: dataPtr.bcdSeg.base, pages: dataPtr.bcdSeg.pages];
    parms.codePages ← [base: dataPtr.codeSeg.base, pages: dataPtr.codeSeg.pages];
    parms.symbolPages ← [base: dataPtr.symSeg.base, pages: dataPtr.symSeg.pages];
    CompilerUtil.EndObjectFile[dataPtr.nErrors=0];
    IF streamInfo[$object].stream # NIL THEN {
      Stream.Delete[streamInfo[$object].stream]; streamInfo[$object].stream ← NIL};
    IF ownedObject AND parms.objectFile # File.nullCapability THEN
      FileParmOps.ReleaseOutput[parms.objectFile];
    TreeOps.Finalize[];  LiteralOps.Finalize[];  SymbolOps.Finalize[];
    table.Reset[]};

  Debug: PROC [tree, symbols: PROC [Alloc.Handle]] = {
    tree[table]; symbols[table]};


  Punt: PUBLIC ERROR = CODE;


  started: BOOL ← FALSE;
  
  Start: PUBLIC ENTRY PROC [scratchZone: UNCOUNTED ZONE] = {
    IF started THEN Stop[];
    zone ← scratchZone;  table ← NIL;
    started ← TRUE};
    
  Stop: PUBLIC ENTRY PROC = {
    IF started THEN {
      zone ← NIL;
      IF table # NIL THEN {Alloc.Destroy[table]; table ← NIL};
      started ← FALSE}};
      
  DoTransaction: PUBLIC ENTRY PROC [parms: TransactionPtr] = {
    root: Tree.Link;
    msg, signal: UNSPECIFIED;
    debug: BOOL;
    nParseErrors: CARDINAL;
    parsed, aborted: BOOL;
    ownedObject: BOOL ← FALSE;

    PrintTreeRoot: PROC [table: Alloc.Handle] = {
      CompilerUtil.PrintTree[table, root]};
    PrintSymbols: PROC [table: Alloc.Handle] = {
      CompilerUtil.PrintSymbols[table, dataPtr.interface]};
    
    IF ~started THEN RETURN WITH ERROR Punt[];
    getStream ← parms.getStream;
    dataPtr.source ← parms.source;
    dataPtr.objectFile ← parms.objectName;
    dataPtr.switches ← parms.switches;
    dataPtr.matched ← parms.op = replace;
    dataPtr.pattern ← parms.pattern;
    debug ← dataPtr.switches['d];  dataPtr.switches['d] ← FALSE;

    dataPtr.objectVersion ← OSMiscOps.GenerateUniqueId[];
    dataPtr.objectStamp ← OSMiscOps.TimeToStamp[dataPtr.source.version];
    -- encode switches, compiler version (see DIRECTORY processing also)
      dataPtr.objectStamp ← OSMiscOps.MergeStamps[
        dataPtr.objectStamp, OSMiscOps.TimeToStamp[[0, 0, LOOPHOLE[dataPtr.switches]]]];
      dataPtr.objectStamp ← OSMiscOps.MergeStamps[
        dataPtr.objectStamp, OSMiscOps.TimeToStamp[dataPtr.compilerVersion]];

    dataPtr.nErrors ← dataPtr.nWarnings ← 0;  aborted ← FALSE;
    streamInfo[$source] ←
      [access: $read, stream: parms.sourceStream, status: [count: 0, open: FALSE]];
    streamInfo[$object] ← streamInfo[$log] ←
      [access: $write, stream: NIL, status: [count: 0, open: FALSE]];
    Initialize[];
    dataPtr.table ← table;  dataPtr.zone ← zone;

      BEGIN
      ENABLE { 
	Alloc.Failure => {IF ~debug THEN GO TO storageFull};
	UNWIND => {Finalize[parms, ownedObject]};
	ANY => {
	  IF ~debug THEN {[msg, signal] ← OSMiscOps.SignalArgs[]; GO TO uncaughtSignal}}};

      CompilerUtil.PrefillSymbols[];
      dataPtr.textIndex ← 0;

    -- first pass
      IF parms.startPass # NIL AND ~parms.startPass[1] THEN GO TO cancel;
      pass ← '1;
      parsed ← CompilerUtil.P1Unit[];
      nParseErrors ← dataPtr.nErrors;
      IF ~parsed THEN GO TO failed;
      root ← TreeOps.PopTree[];  TreeOps.Reset[];
      IF parms.debugPass <= 1 THEN Debug[PrintTreeRoot, PrintSymbols];

    -- second pass
      IF parms.startPass # NIL AND ~parms.startPass[2] THEN GO TO cancel;
      pass ← '2;
      root ← CompilerUtil.P2Unit[root];
      IF parms.debugPass <= 2 THEN Debug[PrintTreeRoot, PrintSymbols];

    -- third and fourth passes
      IF parms.startPass # NIL AND ~parms.startPass[3] THEN GO TO cancel;
      SymLiteralOps.Initialize[table];
      Copier.FileInit[
	  [dataPtr.objectVersion, [parms.objectName, 0, parms.objectName.length]],
	  table, zone, parms.fileParms];

	BEGIN
	ENABLE Alloc.Failure => {GO TO noSpace};

	pass ← '3;
	root ← CompilerUtil.P3Unit[root];
	CompilerUtil.P3Postlude[dataPtr.nErrors <= nParseErrors];

	IF parms.debugPass <= 3 THEN Debug[PrintTreeRoot, PrintSymbols];
	IF dataPtr.nErrors > nParseErrors THEN GO TO DeleteFiles;

	dataPtr.objectVersion ← OSMiscOps.StampToTime[dataPtr.objectStamp];
	parms.fileParms.Forget[
	  [dataPtr.objectVersion, [parms.objectName, 0, parms.objectName.length]]];
	IF parms.objectFile = File.nullCapability THEN {
	  parms.objectFile ← FileParmOps.AcquireOutput[parms.objectName];
	  ownedObject ← TRUE}
	ELSE SymbolTable.Forget[[parms.objectFile, SymbolTable.anySpan]];
	IF parms.objectFile # File.nullCapability THEN {
	  streamInfo[$object].stream ← FileStream.Create[parms.objectFile];
	  FileStream.SetLength[streamInfo[$object].stream, 0]};
	CompilerUtil.StartObjectFile[AcquireStream[$object], zone];

	IF parms.startPass # NIL AND ~parms.startPass[4] THEN GO TO subCancel;
	pass ← '4;
	CompilerUtil.P4Unit[root];
	IF parms.debugPass <= 4 THEN Debug[CompilerUtil.PrintBodies, PrintSymbols];
	GO TO DeleteFiles;

	EXITS
	  DeleteFiles => Copier.FileReset[];
	  subCancel => {Copier.FileReset[]; GO TO cancel};
	  noSpace => {Copier.FileReset[]; SymLiteralOps.Finalize[]; GO TO storageFull};
	END;

      IF dataPtr.nErrors # 0 THEN GO TO failed;

    -- fifth pass
      IF ~dataPtr.interface THEN {
	ENABLE UNWIND => {CompilerUtil.EndObjectFile[FALSE]};
	IF parms.startPass # NIL AND ~parms.startPass[5] THEN GO TO cancel;
	pass ← '5;
	CompilerUtil.P5module[]};
      SymLiteralOps.Finalize[];

      IF parms.startPass # NIL AND ~parms.startPass[6] THEN GO TO cancel;
      CompilerUtil.TableOut[table];
--    IF parms.startPass # NIL THEN [] ← parms.startPass[0];
      IF dataPtr.nErrors # 0 THEN GO TO failed;

      EXITS
	failed => aborted ← TRUE;
        cancel => {Log.Error[aborted];  aborted ← TRUE};
	uncaughtSignal => {
	  OPEN CharIO;
	  errorStream: Stream.Handle = AcquireStream[$log];
	  Log.Error[compilerError];  aborted ← TRUE;
	  PutString[errorStream, "in Pass "L];  PutChar[errorStream, pass];
	  PutString[errorStream, ", signal = "L];  PutOctal[errorStream, signal];
	  PutString[errorStream, ", message = "L];  PutOctal[errorStream, msg];
	  PutChar[errorStream, '\n];
	  ReleaseStream[$log];
	  Finalize[parms, ownedObject];  RETURN WITH ERROR Punt[]};
	storageFull => StorageProblem["overflow"L];
      END;

    Finalize[parms, ownedObject]};

  StorageProblem: PROC [message: STRING] = {
    OPEN CharIO;
    errorStream: Stream.Handle = AcquireStream[$log];
    dataPtr.nErrors ← dataPtr.nErrors+1;
    PutString[errorStream, "\nStorage "L];  PutString[errorStream, message];
    PutString[errorStream, " in Pass "L];  PutChar[errorStream, pass];
    PutChar[errorStream, '\n];
    ReleaseStream[$log]};


-- * * * * * *  M A I N   B O D Y   C O D E  * * * * * *

  START dataPtr; 	-- initialize STRING variables, etc.
  START ownSymbols;
  dataPtr.ownSymbols ← ownSymbols;
--dataPtr.compilerVersion ← OSMiscOps.ImageId[];
  dataPtr.compilerVersion ← [net: 0Ch, host: 0Ch, time: 0F0010004h]; -- Cedar release

  CompilerUtil.InstallParseTables[OSMiscOps.GetTableBase[CBinary.MesaTab]] ;
  tableBase[$error] ← OSMiscOps.GetTableBase[CBinary.ErrorTab];
  tableBase[$debug] ← OSMiscOps.GetTableBase[CBinary.DebugTab];

  }.