-- File [Ivy]<Nelson>Lupine>LupineUserInterfaceImpl.mesa.
-- Last edited by BZM on  2-May-82 23:18:33.

-- This interface uses a standard command interpreter called the Commander,
-- which is also used by the Lister.  While its teletype style of interaction
-- is poor compared to the tools paradigm, it has the great advantage of
-- being called from the command line, therefore making Lupine easily
-- useable from command files.  Keep this in mind before discarding the
-- Commander for something else.  All anticipated clients intend to use
-- Lupine via command files.


DIRECTORY
  CWF USING [SetWriteProcedure, SWF1, WF0, WF1, WF2, WFCR],
  CommanderDefs USING [
    AddCommand, CommandBlockHandle, InitCommander, WaitCommands],
  Exec USING [AddCommand, w],
  LongString USING [EquivalentStrings],
  LupineManager USING [
  	ErrorHandlerProc, Language, ParamPassingMethod,
	String, TranslateRemoteInterface],
  Runtime USING [CallDebugger, GetBcdTime],
  System USING [GreenwichMeanTime],
  TTY USING [PutChar, ResetUserAbort, UserAbort];


LupineUserInterfaceImpl: PROGRAM
  IMPORTS
    CommanderDefs, CWF, Exec, LongString, LupineManager, Runtime, TTY
  = BEGIN


-- Initialization and command procedures.

  herald: STRING = [100];
  PutLogChar: PROC [char: CHARACTER] = {TTY.PutChar[Exec.w, char]};

  StartLupine: PROCEDURE =
    BEGIN
    buildTime: System.GreenwichMeanTime ← Runtime.GetBcdTime[];
    CWF.SWF1[herald,
      "*NLupine 1 of %LT: Cedar's RPC Stub Translator."L, @buildTime ];
    CommanderDefs.InitCommander[herald];
    AddLupineCommands;
    [] ← CWF.SetWriteProcedure[PutLogChar];
    Exec.AddCommand["Lupine.~"L, RunLupine];
    END;

  RunLupine: PROCEDURE = {SetCommandDefaults; CommanderDefs.WaitCommands[]};


  CheckAbort: PROCEDURE =
    -- IF ControlDEL then cause process ABORT thru Commander to Executive.
    -- The Exec will catch it and UNWIND us, and then the Commander too. 
    BEGIN
    IF TTY.UserAbort[] THEN {TTY.ResetUserAbort;  ERROR ABORTED};
    END;


-- Command definitions for the Commander.

  AddLupineCommands: PROCEDURE =
    -- The Commander does not make copies of these strings and
    -- expects them to last forever (e.g., live in a global frame). 
    BEGIN OPEN CommanderDefs;
    cmd: CommandBlockHandle;

    cmd ← AddCommand["TranslateInterface", LOOPHOLE[TranslateInterface], 1];
    cmd.params[0] ←
      [type: string, prompt: "Filename of compiled remote interface"];

    cmd ← AddCommand[ "TargetLanguage", LOOPHOLE[SetTargetLanguage], 1];
    cmd.params[0] ←
      [type: string,
	prompt: "Target language for RPC modules (Cedar, Mesa) [Cedar]"];

    cmd ← AddCommand[
		"DefaultParameterPassing", LOOPHOLE[SetParameterPassing], 1];
    cmd.params[0] ←
      [type: string,
	prompt: "Default passing method (VAR, VALUE, RESULT, HANDLE) [VALUE]"];
    
    cmd ← AddCommand["InterMdsCalls", LOOPHOLE[SetInterMdsCalls], 1];
    cmd.params[0] ←
      [type: boolean,
       prompt: "Translate interfaces to handle interMDS calls only [FALSE]"];
    
    cmd←AddCommand["FreeServerArguments", LOOPHOLE[SetFreeServerArgs], 1];
    cmd.params[0] ←
      [type: boolean,
       prompt: "Automatically deallocate all server heap arguments [TRUE]"];
    
    cmd ← AddCommand["InlineDispatcherStubs", LOOPHOLE[SetInlineStubs], 1];
    cmd.params[0] ←
      [type: boolean,
       prompt: "Use INLINE stubs in the RpcServerImpl's dispatcher [TRUE]"];
    
    cmd←AddCommand["DynamicHeapNEWs", LOOPHOLE[SetDynamicHeapNEWs], 2];
    cmd.params[0] ←
      [type: numeric,
       prompt: "Maximum number of dynamic heap NEWs per call [50]"];
    cmd.params[1] ←
      [type: numeric,
       prompt: "Maximum number of dynamic MDS NEWs per call [50]"];

    cmd ← AddCommand["LineLength", LOOPHOLE[SetLineLength], 1];
    cmd.params[0] ←
      [type: numeric, prompt: "Desired line length of output files [82]"];

    cmd ← AddCommand["Spy", LOOPHOLE[CallSpy], 0];

    cmd ← AddCommand["CallDebugger", LOOPHOLE[SetCallDebugger], 1];
    cmd.params[0] ←
      [type: boolean,
       prompt: "For wizards only: Call debugger on user errors [FALSE]"];

    END;  -- AddLupineCommands.


-- Individual command processors.

  targetLanguage: LupineManager.Language;
  defaultParamPassing: LupineManager.ParamPassingMethod;
  interMdsCalls: BOOLEAN;
  freeServerArgs: BOOLEAN;
  inlineDispatcherStubs: BOOLEAN;
  maxHeapAllocs, maxMdsAllocs: LONG INTEGER;
  lineLength: LONG INTEGER;
  callDebugger: BOOLEAN;

  SetCommandDefaults: PROCEDURE =
    INLINE BEGIN
    targetLanguage ← Cedar;
    defaultParamPassing ← Value;
    interMdsCalls ← FALSE;
    freeServerArgs ← TRUE;
    inlineDispatcherStubs ← TRUE;
    maxHeapAllocs ← maxMdsAllocs ← 50;
    lineLength ← 82;
    callDebugger ← FALSE;
    END;


  TranslateInterface: PROCEDURE [interfaceFile: STRING] = 
    BEGIN OPEN CWF;
    aborted, errors, warnings: BOOLEAN ← FALSE;
    HandleError: LupineManager.ErrorHandlerProc =
	-- PROCEDURE [type: ErrorType, code: ErrorCode,
	--     codeExplanation: String,
	--     outputFileName: String←StringNIL,
	--     outputFileCharPosition: LONG INTEGER,
 	--     problemCausingText: String←NIL ]
	--   RETURNS [abortTranslation: BOOLEAN←FALSE];
      BEGIN
      CheckAbort;
      SELECT type FROM
	abort => { aborted ← TRUE;  WF0["*NFatal error"L] };
	error => { errors ← TRUE;  WF0["*NError"L] };
	warning => { warnings ← TRUE;  WF0["*NWarning"L] };
	ENDCASE => NULL;
      IF outputFileName # NIL
	THEN WF2[" at %S[%LD]"L, outputFileName, @outputFileCharPosition];
      WF1[":*N  %S*N"L, codeExplanation];
      IF problemCausingText # NIL
	THEN WF1[
	  "  Problem-causing file, module, identifier, or type: %S*N"L,
	  problemCausingText ];
      IF callDebugger THEN Runtime.CallDebugger["Tracking a Lupine problem..."L];
      RETURN [abortTranslation: FALSE];
      END;  -- HandleError.
    CheckAbort;
    LupineManager.TranslateRemoteInterface [
	interfaceBcdFilename: interfaceFile,
  	errorHandler: HandleError,
	options: [
	    targetLanguage: targetLanguage,
	    defaultParamPassing:
	      (IF interMdsCalls THEN InterMds ELSE defaultParamPassing),
	    freeServerArguments: freeServerArgs,
	    inlineServerDispatcherStubs: inlineDispatcherStubs,
	    maxHeapAllocations: maxHeapAllocs,
	    maxMdsAllocations: maxMdsAllocs ],
	desiredLineLength: lineLength ];
    IF aborted OR errors OR warnings THEN WFCR;
    WF2["Translation of %S %S.*N*N"L, interfaceFile,
      (SELECT TRUE FROM
	aborted => "aborted"L,
	errors => "finished with errors"L,
	warnings => "finished with warnings"L,
	ENDCASE => "complete"L) ];
    END;  -- TranslateInterface.
    

  SetTargetLanguage: PROCEDURE [language: STRING] =
    BEGIN OPEN LongString;
    SELECT TRUE FROM
	EquivalentStrings[language, "Cedar"L] => targetLanguage ← Cedar;
	EquivalentStrings[language, "Mesa"L] => targetLanguage ← Mesa;
	ENDCASE => CWF.WF0["  Invalid language--no change made!*N"L];
    END;

  SetParameterPassing: PROCEDURE [kind: STRING] =
    BEGIN OPEN LongString;
    SELECT TRUE FROM
	EquivalentStrings[kind, "VAR"L] => defaultParamPassing ← Var;
	EquivalentStrings[kind, "VALUE"L] => defaultParamPassing ← Value;
	EquivalentStrings[kind, "RESULT"L] => defaultParamPassing ← Result;
	EquivalentStrings[kind, "HANDLE"L] => defaultParamPassing ← Handle;
	ENDCASE => CWF.WF0["  Invalid passing method--no change made!*N"L];
    END;
    
  SetInterMdsCalls: PROCEDURE [onOff: BOOLEAN] = {interMdsCalls ← onOff};

  SetFreeServerArgs: PROCEDURE [onOff: BOOLEAN] = 
	BEGIN freeServerArgs ← onOff END;

  SetInlineStubs: PROCEDURE [onOff: BOOLEAN] = 
	BEGIN inlineDispatcherStubs ← onOff END;

  SetDynamicHeapNEWs: PROCEDURE [heap, mds: CARDINAL] = 
	BEGIN maxHeapAllocs ← heap;  maxMdsAllocs ← mds END;

  SetLineLength: PROCEDURE [length: CARDINAL] = {lineLength ← length};

  SetCallDebugger: PROCEDURE [onOff: BOOLEAN] = {callDebugger ← onOff};

  CallSpy: PROCEDURE = {Runtime.CallDebugger["Do some spying..."L]};


  -- Module Initialization.
 
  StartLupine;

  END.