-- File [Ivy]<Nelson>Lupine>LupineDeclareUtilityImpl.mesa.
-- Last edited by BZM on March 18, 1982  10:56 AM.

-- This module cooperates with LupineDeclare*Impl to export LupineDeclare.


DIRECTORY
  CWF USING [SWF2, WF1, WFC],
  LongString USING [EqualStrings, EquivalentStrings],
  LupineDeclare USING [
	DirectoryItem, DirectoryList, DispatcherType,
	EmptyDirectoryList, InterfaceUse, ModuleNameList ],
  LupineDeclarePrivate USING [GetString, StringConstantType],
  LupineManagerPrivate USING [
	AllocString, Error, Indent, IsNull, Language,
	MaxIdentifierLength, ModuleName,
	Nest, Options, String, StringNIL,
	WFL, WFL1, WFLL, WFS, WFS1, WFSL ],
  LupineSymbolTable USING [
	DirectoryProcedure, EnumerateDirectory, EnumerateTransfers,
	IsAnonymous, PutTypeName, SymbolHandle, SymbolID,
	SymbolName, SymbolType, SymbolUniqueID,
	TransferProcedure, TypeHandle ];


LupineDeclareUtilityImpl: PROGRAM
  IMPORTS
	CWF, LongString, LupineManagerPrivate,
	Private: LupineDeclarePrivate, ST: LupineSymbolTable 
  EXPORTS LupineDeclare, LupineDeclarePrivate
  = BEGIN OPEN LupineManagerPrivate, LupineDeclare;



-- General symbol and type routines.

  TransferName: PUBLIC PROCEDURE [transfer: ST.SymbolHandle, nameString: String]
      RETURNS[name: String] =
    BEGIN
    IF ST.IsAnonymous[transfer]
      THEN ERROR
      ELSE RETURN[ST.SymbolName[transfer, nameString]];
    END;

  WriteTransferName: PUBLIC PROCEDURE [transfer: ST.SymbolHandle] =
    BEGIN
    name: AllocString = [MaxIdentifierLength];
    WFS1[TransferName[transfer, name]];
    END;

  ParameterName: PUBLIC PROCEDURE [parameter: ST.SymbolHandle,
	parameterNumber: INTEGER, nameString: String]
      RETURNS [name: String] =
    BEGIN
    IF ST.IsAnonymous[parameter]
     THEN BEGIN
      paramID: LONG CARDINAL --ST.SymbolID-- ← ST.SymbolUniqueID[parameter];
      CWF.SWF2[nameString, "anonP%Did%LU"L, @parameterNumber, @paramID];
      RETURN[nameString];
      END
     ELSE RETURN[ST.SymbolName[parameter, nameString]];
    END;

  WriteParameterName: PUBLIC PROCEDURE [
	parameter: ST.SymbolHandle, parameterNumber: INTEGER] =
    BEGIN
    name: AllocString = [MaxIdentifierLength];
    WFS1[ParameterName[parameter, parameterNumber, name]];
    END;
    
  WriteSymbolName: PUBLIC PROCEDURE [symbol: ST.SymbolHandle] =
    BEGIN
    name: AllocString = [MaxIdentifierLength];
    IF ST.IsAnonymous[symbol]
      THEN Error[AnonymousIdentifier]
      ELSE WFS1[ST.SymbolName[symbol, name]];
    END;
    
  WriteSymbolType: PUBLIC PROCEDURE [symbol: ST.SymbolHandle] =
    BEGIN
    WriteTypeName[ST.SymbolType[symbol]];
    END;
    
  WriteTypeName: PUBLIC PROCEDURE [
      type: ST.TypeHandle, includeReadonly: BOOLEAN←TRUE ] =
    BEGIN
    ST.PutTypeName[
	putProc: CWF.WFC,
	type: type,
	includeReadonly: includeReadonly,
	rootInterfaceOpenName: ModuleName[openedInterface] ];
    END;


-- Directory and interface manipulation routines.

  Directory: PUBLIC PROCEDURE [
	includeInterfaceDirectory: BOOLEAN←TRUE,
	extraDirectories: DirectoryList←EmptyDirectoryList,
	options: Options,
	nest: Nest←0 ] =
    BEGIN
    printedOne: BOOLEAN←FALSE;
    maxExtraDirLength: INTEGER = 6*16;
    duplicate: PACKED ARRAY [0..maxExtraDirLength) OF BOOLEAN ← ALL[FALSE];
    PrintDirItem: PROCEDURE [dirItem: DirectoryItem, file: String←StringNIL] =
      BEGIN
      hasFileName: BOOLEAN =
      	~IsNull[file] AND ~LongString.EquivalentStrings[dirItem.module, file];
      IF ~dirItem.forLanguage[options.targetLanguage] OR IsNull[dirItem.module]
	  THEN RETURN;
      IF printedOne
	  THEN WFS[",*N"L, Indent[nest+1]]
	  ELSE BEGIN
	    WFSL["*N*N"L, Indent[nest], "DIRECTORY*N"L, Indent[nest+1]];
	    printedOne ← TRUE;
	    END;
      WFS[dirItem.module];
      IF hasFileName THEN WFS[": FROM """L, file, """"L];
      SELECT TRUE FROM
	dirItem.usingAll # StringNIL =>
	  WFS[" USING ["L, dirItem.usingAll, "]"L];
	dirItem.usingSome # StringNIL =>
	  WFS[" --USING SOME OF ["L, dirItem.usingSome, "]--"L];
	ENDCASE => NULL;
      END;  -- PrintDirItem.
    IF LENGTH[extraDirectories] > maxExtraDirLength THEN ERROR;
    IF includeInterfaceDirectory
      THEN BEGIN
      PrintInterfaceDirItem: ST.DirectoryProcedure =
	BEGIN
	FOR dir: CARDINAL IN [0..LENGTH[extraDirectories]) DO
	  IF LongString.EqualStrings[extraDirectories[dir].module, moduleName]
	    THEN {duplicate[dir] ← TRUE; EXIT};
	    ENDLOOP;
	PrintDirItem[dirItem: [module: moduleName], file: fileName];
	END;  -- PrintInterfaceDirItem.
      [] ← ST.EnumerateDirectory[PrintInterfaceDirItem];
      END;
    FOR dir: CARDINAL IN [0..LENGTH[extraDirectories]) DO
      -- Must do this list second so that USING conflicts are avoided.
      IF ~duplicate[dir] THEN PrintDirItem[dirItem: extraDirectories[dir]];
      ENDLOOP;
    IF printedOne THEN WFS1[";*N"L];
    END;

  ModuleHead: PUBLIC PROCEDURE [
  	moduleNames: ModuleNameList,
	moduleType: String,
	interfaceList: DirectoryList←EmptyDirectoryList,
	options: Options,
	nest: Nest←0 ] = 
    BEGIN
    UseKeywords: ARRAY InterfaceUse OF RECORD [begin, end: String] = [
	imports: ["IMPORTS "L, "*N"L], exports: ["EXPORTS "L, "*N"L],
	shares:  ["SHARES  "L, "*N"L], beginOpen: ["= BEGIN OPEN "L, ";*N"L] ];
    hasThisUse: ARRAY InterfaceUse OF BOOLEAN ← ALL[FALSE];
    IF LENGTH[moduleNames]=0 OR IsNull[moduleType] THEN ERROR;
    FOR item: CARDINAL IN [0..LENGTH[interfaceList]) DO
      FOR use: InterfaceUse IN InterfaceUse DO
	IF interfaceList[item].usedIn[use] THEN hasThisUse[use] ← TRUE;
	ENDLOOP;
      ENDLOOP;
    WFS["*N*N"L, Indent[nest]];
    FOR name: CARDINAL IN [0..LENGTH[moduleNames]) DO
      IF name > 0 THEN WFS1[", "L];
      WFS[moduleNames[name]];
      ENDLOOP;
    WFS[": "L, moduleType, "*N"L];
    FOR use: InterfaceUse IN InterfaceUse DO
      IF hasThisUse[use] THEN
	InterfaceList [ nest: nest+1,
	  use: use,
	  targetSystem: options.targetLanguage,
	  interfaceList: interfaceList,
	  beginText: UseKeywords[use].begin,
	  endText: UseKeywords[use].end ]; 
	ENDLOOP;
    IF ~hasThisUse[beginOpen] THEN WFL1[nest+1, "= BEGIN"L];
    END;

  InterfaceList: PUBLIC PROCEDURE [
  	use: InterfaceUse,
  	targetSystem: Language,
	interfaceList: DirectoryList,
	beginText, endText: String←StringNIL,
	nest: Nest ] = 
    BEGIN
    printedOne: BOOLEAN←FALSE;
    WFS[Indent[nest], beginText];
    FOR item: CARDINAL IN [0..LENGTH[interfaceList]) DO
      interface: DirectoryItem = interfaceList[item];
      IF   ~interface.usedIn[use]
	OR ~interface.forLanguage[targetSystem]
	OR IsNull[interface.module]
	  THEN LOOP;
      IF printedOne THEN WFS1[", "L] ELSE printedOne ← TRUE;
      IF use=beginOpen OR (use=imports AND ~interface.usedIn[beginOpen])
	THEN IF ~IsNull[interface.openName] THEN WFS[interface.openName, ": "L];
      WFS[interface.module];
      ENDLOOP;
    WFS1[endText];
    END;


-- Other code generation routines.

  LastLupineTransferIndex: INTEGER = 3B;

  ProcedureIndex: PUBLIC PROCEDURE [nest: Nest←1] =
    BEGIN
    TransferEnumeration[ nest: nest,
	enumerationName: Private.GetString[procedureIndex],
	procs: TRUE ];
    END;

  SignalIndex: PUBLIC PROCEDURE [nest: Nest←1] =
    BEGIN
    TransferEnumeration[ nest: nest,
	enumerationName: Private.GetString[signalIndex],
	exceptions: TRUE ];
    END;

  TransferEnumeration: PROCEDURE [
	enumerationName: String,
	procs, exceptions: BOOLEAN ← FALSE,
	nest: Nest ] =
    BEGIN
    index: INTEGER ← LastLupineTransferIndex;
    WriteIndex: PROC =
      {CWF.WF1[" (%D)"L, @index];  index ← index + 1};
    NameList: ST.TransferProcedure =
      BEGIN
      WFS1[IF transferIndex=1 THEN ",*B"L ELSE ", "L];
      WriteTransferName[transfer];  WriteIndex;
      END;
    WFSL["*N"L, Indent[nest], enumerationName,
      ": PRIVATE TYPE = MACHINE DEPENDENT {*B"L,
      "LupineUnusedIndex (0), LupineLastIndex"L ];
    WriteIndex;
    [] ← ST.EnumerateTransfers[
      proc: NameList, procs: procs, errors: exceptions, signals: exceptions ];
    WFS1["};*N"L];
    END;


  DispatcherHead: PUBLIC PROCEDURE [type: DispatcherType, nest: Nest] =
    BEGIN
    WFS1["*N"L];
    WFLL[nest, DispatcherName[type], ": --"L,
      Private.GetString[dispatcherDeclaration], "-- RpcPrivate.Dispatcher ="L];
    WFL1[nest+1, "BEGIN"L];
    END;

  DispatcherSelect: PUBLIC PROCEDURE [type: DispatcherType, nest: Nest] =
    BEGIN
    WFL[nest+1, "SELECT LOOPHOLE[pkt.data[0], RpcControl."L,
      (SELECT type FROM
	server => Private.GetString[procedureIndex],
	client => Private.GetString[signalIndex],
	ENDCASE => ERROR),
      "] FROM"L];
    END;

  DispatcherTail: PUBLIC PROCEDURE [type: DispatcherType, nest: Nest] =
    BEGIN
    WFL1[nest+2, "ENDCASE => RETURN[Lupine.DispatchingError[]];*N"L];
    WFL[nest+1, "END;  -- "L, DispatcherName[type]];
    END;


-- Routines private to LupineDeclare*Impl via LupineDeclarePrivate.

  DispatcherName: PUBLIC PROCEDURE [type: DispatcherType] RETURNS [String] =
    BEGIN
    RETURN[ SELECT type FROM
	none => StringNIL,
	client => GetString[clientDispatcher],
	server => GetString[serverDispatcher],
	callback => GetString[callbackDispatcher],
	ENDCASE => ERROR ];
    END;


  StringConstants: PACKED ARRAY Private.StringConstantType OF STRING = [
    clientDispatcher: "ClientDispatcher",
    serverDispatcher: "ServerDispatcher",
    callbackDispatcher: "CallbackDispatcher",
    procedureIndex: "ProcedureIndex",
    signalIndex: "SignalIndex",
    dispatcherDeclaration: "PROCEDURE [pkt: RPCPkt, callLength: DataLength, lastPkt: BOOLEAN, localConversation: Conversation] RETURNS [returnLength: DataLength]",
    dispatcherArgs: "pkt: pkt, callLength: callLength, lastPkt: lastPkt, localConversation: localConversation",
    dispatcherResults: "returnLength: returnLength",
    dispatcherPkt: "pkt",
    dispatcherConversation: "localConversation",
    dispatcherReturnLength: "returnLength" ];

  GetString: PUBLIC PROCEDURE [type: Private.StringConstantType]
      RETURNS [--string:-- String] =
    BEGIN RETURN[StringConstants[type]] END;


END.  -- LupineDeclareUtilityImpl.