-- File: CommandActions.mesa  edit by:
  -- Mark Apr 16, 1980 7:41 PM
  -- Bruce October 11, 1980  2:24 PM
  -- Johnsson July 16, 1980  8:23 AM

DIRECTORY
  Actions USING [DumpFrame],
  Ascii USING [BS, ControlA, ControlH, ControlQ, ControlV, ControlW, CR, DEL, ESC, SP],
  ComData USING [typeCARDINAL, typeINT],
  CommandList USING [Command, Error],
  Commands USING [Command, CommandRec, Prompt, TermProc],
  CommandTable USING [CSRptr],
  CompilerUtil USING [error, LockTableSegment, UnlockTableSegment],
  DContext USING [SetOctal, SetProcess],
  DebugFormat USING [LongSubStringDescriptor],
  DebugOps USING [
    fileSW, Foo, FooProc, Interpret, InvalidCharacter, InvalidNumber,
    Kill, LongREAD, ParseError, Proceed, Quit,
    ShortREAD, ShortWRITE, StringExpToOctal, SyntaxError],
  DI USING [Error, GetValue, MakeLongType, Number, NumberType, SetDefaultRadix, Words],
  DOutput USING [Char, EOL, Line, Octal, LongSubString, Text],
  Dump USING [Char],
  Event USING [Notify, Vetoed],
  Gf USING [Name],
  Init USING [CheckSymTabLength, CoreSwap],
  MachineDefs USING [FHandle, GFHandle, StartDP],
  SDDefs USING [sAlternateBreak, sBreak, SD],
  State USING [Get, GetGS, GetString, GSHandle, SetParse, SetString, strings, WritePrompt],
  Storage USING [ExpandString, String],
  String USING [
    AppendChar, AppendOctal, InvalidNumber, StringToNumber, StringToLongNumber],
  Table USING [Overflow],
  TajoUtility USING [CheckVeto],
  TextSW USING [BlinkingCaret],
  UserInput USING [ResetUserAbort, userAbort];

CommandActions: PROGRAM
  IMPORTS
    Actions, com: ComData, Commands, CompilerUtil, DContext, DebugOps, DI, DOutput,
    Dump, Event, Gf, Init, State, Storage, String, Table, TajoUtility, TextSW, UserInput
  EXPORTS Actions, Commands, DebugOps =
  BEGIN OPEN Commands;

  Command: PUBLIC TYPE = CommandList.Command;
  Error: PUBLIC TYPE = CommandList.Error;
  GFHandle: TYPE = MachineDefs.GFHandle;
  FHandle: TYPE = MachineDefs.FHandle;

  data: State.GSHandle ← State.GetGS[];
  
  WriteCommand: PUBLIC PROCEDURE [code: Command] =
    BEGIN
    base: CommandTable.CSRptr ← CompilerUtil.LockTableSegment[CompilerUtil.error];
    ss: DebugFormat.LongSubStringDescriptor;
    IF code = null THEN RETURN;
    ss ← [
      base: @base[base.stringOffset],
      offset: base.CommandStrings[code].offset,
      length: base.CommandStrings[code].length];
    DOutput.LongSubString[@ss];
    CompilerUtil.UnlockTableSegment[CompilerUtil.error];
    END;

  WriteError: PUBLIC PROCEDURE [error: Error, eof: BOOLEAN ← TRUE] =
    BEGIN
    base: CommandTable.CSRptr ← CompilerUtil.LockTableSegment[CompilerUtil.error];
    ss: DebugFormat.LongSubStringDescriptor ← [
      base: @base[base.stringOffset],
      offset: base.ErrorMessages[error].offset,
      length: base.ErrorMessages[error].length];
    IF eof THEN DOutput.EOL[];
    DOutput.LongSubString[@ss];
    CompilerUtil.UnlockTableSegment[CompilerUtil.error];
    END;

  GetConfirm: PROCEDURE [char: CHARACTER] =
    BEGIN
    IF UserInput.userAbort THEN 
      BEGIN
      UserInput.ResetUserAbort[];
      WriteError[aborted];
      Commands.Prompt[];
      END;
    SELECT char FROM
      'y, 'Y, Ascii.CR  =>  
	BEGIN
	DOutput.Char[Ascii.CR];
	Call0[];
	Commands.Prompt[];
	END;
      Ascii.DEL =>
	BEGIN
	WriteCommand[del];
	Commands.Prompt[];
	END;
      ENDCASE => BEGIN DOutput.Char['?]; State.SetParse[ThrowAway] END;
    RETURN
    END;

  Confirm: PUBLIC PROCEDURE [code: Command, call: PROCEDURE] =
    BEGIN
    WriteCommand[code];
    WriteCommand[confirm];
    data.call0 ← call;
    State.SetParse[GetConfirm];
    RETURN
    END;
    
  ThrowAway: PROCEDURE [c: CHARACTER] =
    BEGIN
    SELECT c FROM
      Ascii.DEL =>
	BEGIN
	WriteCommand[del];
	Commands.Prompt[];
	END;
      ENDCASE => DOutput.Char['?];
    END;
    
  GetComment: PUBLIC PROC [printDash: BOOLEAN] = {
    IF printDash THEN DOutput.Char['-];
    State.Get[].h.reentrantParse ← data.parse;
    GetLine[[sId: comment, colon: FALSE, resetPrompt: printDash, resetParse: printDash],
      Comment, comment !
	  UNWIND => NULL;
	  ANY => {data.resetParse ← data.resetPrompt ← TRUE; REJECT}]  };

  ModuleBreak: PUBLIC PROC [proc: PROCEDURE [STRING,STRING], prompt: Command]=
    BEGIN
    WriteCommand[prompt];
    data.call2 ← proc;
    GetText[[sId: module, resetPrompt: FALSE],GotBreakParam,IsTerm]
    END;

  invisible: BOOLEAN;

  Login: PUBLIC PROC =
    BEGIN
    WriteCommand[login];
    GetString[[sId: user, resetPrompt: FALSE],GotUser,user];
    END;

  GetLine: PUBLIC PROC [com: CommandRec, call: PROCEDURE [STRING],
      prompt: Command ← null] =
    BEGIN WriteCommand[prompt]; GetText[com,call,IsCR] END;

  GetString: PUBLIC PROC [com: CommandRec, call: PROCEDURE [STRING],
      prompt: Command ← null] =
    BEGIN
    WriteCommand[prompt];
    GetText[com,call,IF com.atom THEN IsAtom ELSE IsCR]
    END;

  GetText: PUBLIC PROCEDURE [
      com: CommandRec, call: PROCEDURE [STRING], term: TermProc] =
    BEGIN
    s: STRING ← State.GetString[(data.currentId ← com.sId)];
    IF s = NIL THEN {s ← Storage.String[10]; State.SetString[com.sId,s]};
    WriteCommand[com.prompt];
    IF com.colon THEN DOutput.Text[": "L];
    data.resetPrompt ← com.resetPrompt;
    data.resetParse ← com.resetParse;
    data.call1 ← call;
    data.term ← term;
    data.firstChar ← TRUE;
    data.currentString ← s;
    invisible ← FALSE;
    State.SetParse[CollectString];
    END;
    
  GotBreakParam: PROC [s: STRING] =
    BEGIN
    IF data.cr THEN {data.call2[s,NIL]; data.resetPrompt ← TRUE}
    ELSE {data.s1 ← s; GetString[[prompt: expression, sId: exp], GotBoth]};
    END;

  ready, owrite: BOOLEAN ← FALSE;

  GetTwoStrings: PUBLIC PROC [
      com1, com2: CommandRec, proc: PROCEDURE[STRING, STRING],
      prompt: Command ← null] =
    BEGIN
    WriteCommand[prompt]; 
    data.call2 ← proc;
    data.com2 ← com2;
    ready ← prompt = write;
    GetString[com1, GotOne];
    END;

  GotUser: PROC [s: STRING] =
    BEGIN
    Event.Notify[setDefaults];
    GetString[[sId: password, resetPrompt: FALSE], GotPassword, password];
    invisible ← TRUE;
    END;

  GotOne: PROC [s: STRING] = {
    owrite ← ready; data.s1 ← s; GetString[data.com2, GotBoth]};

  Write: PROC [val: STRING] = {
    lp: LONG POINTER ← LOOPHOLE[StringExpToLOctal[data.s1]];
    IF val = NIL OR val.maxlength < 10 THEN
	BEGIN
	Storage.ExpandString[@val,10];
	data.currentString ← val;
	State.strings[data.currentId] ← val;
	END;
    val.length ← 0;
    String.AppendOctal[val,IF data.inNub THEN lp↑ ELSE DebugOps.LongREAD[lp]];
    DOutput.Text[val]};

  GotPassword: PROC [password: STRING] =
    BEGIN
    data.resetPrompt ← TRUE;
    invisible ← FALSE;
    Event.Notify[setDefaults];
    END;

  GotBoth: PROC [s2: STRING] =
    BEGIN Call2[data.s1, s2]; data.resetPrompt ← TRUE; END;

  Append: PROCEDURE [s: STRING, c: CHARACTER] =
    BEGIN
    quoted: BOOLEAN ← s.length # 0 AND s[s.length-1] = Ascii.ControlV;
    IF ~quoted AND data.term[c] THEN {Call1[s]; State.WritePrompt[]}
    ELSE
      BEGIN
      IF quoted THEN s.length ← s.length - 1;
      IF s = NIL OR s.length = s.maxlength THEN
	BEGIN
	Storage.ExpandString[@s,10];
	data.currentString ← s;
	State.strings[data.currentId] ← s;
	END;
      String.AppendChar[s, c];
      IF c # Ascii.ControlV THEN DOutput.Char[IF invisible THEN '* ELSE c];
      END;
    RETURN
    END;

  ResetFirst: PROC RETURNS [first: BOOLEAN] = 
    {first ← data.firstChar; IF first THEN data.firstChar ← FALSE};

  CollectString: PROCEDURE [char: CHARACTER] =
    BEGIN OPEN Ascii;
    s: STRING ← data.currentString;
    SELECT char FROM
      ESC => 
	  SELECT TRUE FROM
	    ~ResetFirst[] => Append[s, char];
	    owrite => {
	      Write[s ! UNWIND => owrite ← ready ← FALSE]; owrite ← ready ← FALSE};
	    invisible => THROUGH [0..s.length) DO DOutput.Char['*] ENDLOOP
	    ENDCASE => DOutput.Text[s];
      DEL => 
	BEGIN
	WriteCommand[del];
	invisible ← FALSE;
	IF data.resetParse THEN Commands.Prompt[] ELSE OtherPrompt[];
	END;
      ControlA, ControlH, BS => 
	BEGIN
	IF s.length = 0 THEN RETURN;
	s.length ← s.length-1; 
	DOutput.Char[char];
	END;
      ControlW, ControlQ => 
	BEGIN
	state: {ti, v, li} ← ti;
	i: CARDINAL;
	FOR i DECREASING IN [0..s.length) DO
	  SELECT s[i] FROM
	    IN ['A..'Z], IN ['a..'z], IN ['0..'9] =>
	      IF state = ti THEN state ← v;
	    ENDCASE => IF state = v THEN state ← li;
	  IF state = li THEN GOTO Done;
	  DOutput.Char[BS];
	  REPEAT
	    Done => s.length ← i+1;
	    FINISHED => s.length ← 0;
	ENDLOOP;
	END;
      ENDCASE =>
	BEGIN
	IF ResetFirst[] THEN s.length ← 0;
	Append[s,char];
	END;
    RETURN
    END;

  Execute: PUBLIC PROC [
      code: Command, call: PROC, resetPrompt: BOOLEAN ← TRUE] =
    BEGIN
    TextSW.BlinkingCaret[DebugOps.fileSW, off];
    WriteCommand[code];
    data.resetPrompt ← resetPrompt;
    call[];
    State.WritePrompt[];
    END;

  Call0: PROC = 
    BEGIN
    TextSW.BlinkingCaret[DebugOps.fileSW, off];
    data.call0[ ! Table.Overflow => {Init.CheckSymTabLength[]; RETRY}]
    END;

  Call1: PROC [s1: STRING] = 
    BEGIN
    TextSW.BlinkingCaret[DebugOps.fileSW, off];
    data.call1[s1 ! Table.Overflow => {Init.CheckSymTabLength[]; RETRY}]
    END;

  Call2: PROC [s1: STRING, s2: STRING] = 
    BEGIN
    TextSW.BlinkingCaret[DebugOps.fileSW, off];
    data.call2[s1, s2 ! Table.Overflow => {Init.CheckSymTabLength[]; RETRY}]
    END;

  IsAtom: PUBLIC PROCEDURE [c: CHARACTER] RETURNS [BOOLEAN] =
    BEGIN RETURN[c = Ascii.SP OR c = Ascii.CR] END;
      
  IsCR: PUBLIC PROCEDURE [c: CHARACTER] RETURNS [BOOLEAN] =
    BEGIN RETURN[c = Ascii.CR] END;

  IsTerm: PROC [c: CHARACTER] RETURNS [BOOLEAN] =
    BEGIN RETURN [(data.cr ← c = Ascii.CR) OR c = Ascii.SP] END;

  -- Utilities
  
  Numeric: PUBLIC PROC [s: STRING] RETURNS [BOOLEAN] = {
    IF s = NIL OR s.length = 0 THEN RETURN[FALSE];
    FOR i: CARDINAL IN [0..s.length) DO
      SELECT s[i] FROM
	IN ['0..'9] => NULL;
	'- => IF i # 0 OR s.length = 1 THEN RETURN[FALSE];
	'b,'B,'d,'D => IF s.length = 1 OR i # s.length-1 THEN RETURN[FALSE];
	ENDCASE => RETURN[FALSE];
      ENDLOOP;
    RETURN[TRUE]};

  StringExpToNum: PUBLIC PROC [exp: STRING, radix: CARDINAL]
    RETURNS [u: UNSPECIFIED] =
    BEGIN OPEN DebugOps;
    Result: FooProc = BEGIN u ← ProcessNum[f, one].n.u END;
    IF Numeric[exp] THEN RETURN[String.StringToNumber[exp,radix !
      String.InvalidNumber => ERROR DebugOps.InvalidNumber[NIL]]];
    DI.SetDefaultRadix[radix];
    Interpret[exp, Result, IF radix = 8 THEN com.typeCARDINAL ELSE com.typeINT 
      ! ParseError, SyntaxError, InvalidCharacter =>
	ERROR InvalidNumber[NIL];
      UNWIND => DI.SetDefaultRadix[10]];
    DI.SetDefaultRadix[10];
    END;

  StringExpToLNum: PUBLIC PROC [exp: STRING, radix: CARDINAL]
    RETURNS [u: LONG UNSPECIFIED] =
    BEGIN OPEN DebugOps;
    Result: FooProc = BEGIN u ← ProcessNum[f, two].n.lu END;
    IF Numeric[exp] THEN RETURN[String.StringToLongNumber[exp,radix !
      String.InvalidNumber => ERROR DebugOps.InvalidNumber[NIL]]];
    DI.SetDefaultRadix[radix];
    Interpret[exp, Result,
      DI.MakeLongType[IF radix = 8 THEN com.typeCARDINAL ELSE com.typeINT]
	! ParseError, SyntaxError, InvalidCharacter => ERROR InvalidNumber[NIL];
	  UNWIND => DI.SetDefaultRadix[10]];
    DI.SetDefaultRadix[10];
    END;

  StringExpToOctal: PUBLIC PROC [s: STRING] RETURNS [CARDINAL] = 
    BEGIN RETURN[StringExpToNum[s,8]] END;

  StringExpToLOctal: PUBLIC PROC [s: STRING] RETURNS [LONG CARDINAL] =
    BEGIN RETURN[StringExpToLNum[s,8]] END;

  StringExpToDecimal: PUBLIC PROC [s: STRING] RETURNS [INTEGER] =
    BEGIN RETURN[StringExpToNum[s,10]] END;

  StringExpToLDecimal: PUBLIC PROC [s: STRING] RETURNS [LONG INTEGER] =
    BEGIN RETURN[StringExpToLNum[s,10]] END;

  ProcessNum: PROC [f: DebugOps.Foo, size: DI.NumberType] RETURNS [n: DI.Number]=
    BEGIN
    i: DI.NumberType;
    p: LONG POINTER TO DI.Words;
    IF f.bits # 0 OR f.addr.offset # 0 THEN GOTO invalid;
    n.type ← LOOPHOLE[f.words];
    SELECT n.type FROM
      size => NULL;
      one => IF size # two THEN GOTO invalid;
      two => {IF size = one THEN DI.Error[sizeMismatch]; GOTO invalid};
      ENDCASE => GOTO invalid;
    DI.GetValue[f];
    p ← f.addr.base;
    FOR i IN [nogood..n.type) DO
      n.w[i] ← p[i];
      ENDLOOP;
    IF n.type # size THEN n.w[one] ← 0;
    RETURN;
    EXITS
      invalid => SIGNAL DebugOps.InvalidNumber[f]
    END;

  module: STRING ← [40];

  StringToModule: PUBLIC PROC [in: STRING] RETURNS [STRING] = {
    IF Numeric[in] THEN {
      module.length ← 0;
      Gf.Name[module,LOOPHOLE[StringExpToOctal[in]]];
      RETURN[module]}
    ELSE RETURN[in]};

  -- actions

  CallInterpreter: PUBLIC PROC [resetPrompt: BOOLEAN ← FALSE] =
    BEGIN 
    State.Get[].h.reentrantParse ← data.parse;
    GetLine[[
      sId:di, colon:FALSE, resetPrompt:resetPrompt, resetParse:resetPrompt],
      IntString, di !
	UNWIND => NULL;
	ANY => {data.resetParse ← data.resetPrompt ← TRUE; REJECT}];
    END;

  IntString: PROC [s: STRING] =
    BEGIN
    DOutput.EOL[];
    DebugOps.Interpret[s !
      Table.Overflow => {Init.CheckSymTabLength[]; RETRY};
      DebugOps.InvalidCharacter =>
	BEGIN
	DOutput.Text[" !"L];
	Dump.Char[s[index]];
	DOutput.Line[" is an invalid character"L];
	CONTINUE
	END;
      DebugOps.SyntaxError =>
	BEGIN
	DOutput.Text[" ! Syntax error at ["L];
	DOutput.Octal[errorLoc];
	DOutput.Line["]."L];
	CONTINUE
	END;
      DebugOps.ParseError =>
	BEGIN
	DOutput.Text[" ! Parse error at ["L];
	DOutput.Octal[errorLoc];
	DOutput.Line["]."L];
	CONTINUE
	END];
    DOutput.EOL[];
    IF data.resetPrompt THEN RETURN ELSE OtherPrompt[];
    END;

  OtherPrompt: PROC =
    BEGIN
    data.parse ← State.Get[].h.reentrantParse;
    DOutput.Text["  >"L];
    TextSW.BlinkingCaret[DebugOps.fileSW, on];
    END;

  CallNub: PUBLIC PROC = {data.inNub ← TRUE; data.nubLevel ← data.nubLevel+1};

  Comment: PUBLIC PROC [STRING] = {IF data.resetPrompt THEN RETURN ELSE OtherPrompt[]};

  DisplayFrame: PUBLIC PROC [s: STRING] =
    BEGIN Actions.DumpFrame[DebugOps.StringExpToOctal[s]] END;

  DoKill: PUBLIC PROC = {IF OkToLeave[] THEN SIGNAL DebugOps.Kill};
  DoProceed: PUBLIC PROC = {SIGNAL DebugOps.Proceed};
  DoQuit: PUBLIC PROC = {IF OkToLeave[] THEN SIGNAL DebugOps.Quit};

  OkToLeave: PROC RETURNS [ok: BOOLEAN] = {
    ok ← TRUE;
    TajoUtility.CheckVeto[abortSession ! Event.Vetoed => {ok ← FALSE; CONTINUE}]  };

  SetOctalContext: PUBLIC PROC [s: STRING] =
    BEGIN DContext.SetOctal[LOOPHOLE[DebugOps.StringExpToOctal[s]]] END;

  SetProcessContext: PUBLIC PROC [s: STRING] =
    BEGIN DContext.SetProcess[LOOPHOLE[DebugOps.StringExpToOctal[s]]] END;

  StartUser: PUBLIC PROC [gf: STRING] = 
    BEGIN OPEN DebugOps;
    IF data.worryEntry THEN {
      DOutput.Line[" not permitted in worry mode!"L]; RETURN};
    ShortWRITE[@LOOPHOLE[
      data.ESV.parameter,MachineDefs.StartDP].frame,StringExpToOctal[gf]];
    Init.CoreSwap[start];
    END;

  SearchMode: PUBLIC PROC = BEGIN data.search ← ~data.search END;
  TreeMode: PUBLIC PROC = BEGIN data.tree ← ~data.tree END;

  WorryMode: PUBLIC PROC =
    BEGIN OPEN SDDefs;
    temp: UNSPECIFIED = DebugOps.ShortREAD[SD+sBreak];
    data.worryBreaks ← ~data.worryBreaks;
    DebugOps.ShortWRITE[SD+sBreak,DebugOps.ShortREAD[SD+sAlternateBreak]];
    DebugOps.ShortWRITE[SD+sAlternateBreak,temp];
    END;

  UserScreen: PUBLIC PROC = BEGIN Init.CoreSwap[showscreen] END;

  END.