-- JaMExecImpl.mesa
-- Original version by John Warnock, January, 1978.
-- Last changed by Bill Paxton, January 21, 1981  3:25 PM
-- Last changed by Doug Wyatt, 19-Feb-82 10:55:12

DIRECTORY
  JaMBasic USING [Object],
  JaMInternal USING [Frame, Locals, Node, Stack],
  JaMOps USING [AAtom, ArrayFromStack, Assert, Bug, ClearStack, DoCommand,
    Empty, GetLocalsTop, GetMark, GetStream, Install, InstallReason,
    KillStream, MakeName, Pop, PopBoolean, PopInteger, Push, PushInteger,
    RegisterExplicit, RegisterInternal, Restore, SetLocalsTop, StackForAll,
    StreamHandle, StreamToken, StringToken, Top, TryToLoad, TryToLoadLocal],
  JaMVM USING [Check],
  Process USING [Yield];

JaMExecImpl: PROGRAM
IMPORTS JaMOps, JaMVM, Process
EXPORTS JaMOps = {
OPEN JaMOps, JaMInternal, JaMBasic;

-- Globals

rangechk,typechk,undefname,limitchk: PUBLIC name Object; -- public error names
syntaxerr,stkovrflw,execovrflw: name Object; -- local error names
step,interrupt,anon: name Object;
asstep,arfree,sstep,rfree,reptcmd,forcmd,loopcmd: command Object;

-- Errors

Error: PUBLIC ERROR[error: Object, restore: BOOLEAN ← TRUE] = CODE;
StackOverflow: PUBLIC ERROR[stack: Stack] = CODE;
Bug: PUBLIC ERROR = CODE;
StopExecution: ERROR = CODE;

HandleStackOverflow: PROC[frame: Frame, stack: Stack] RETURNS[Object] = {
  oparray: array Object ← ArrayFromStack[frame.opstk];
  execarray: array Object ← ArrayFromStack[frame.execstk];
  ClearStack[frame.opstk]; ClearStack[frame.execstk];
  Push[frame.opstk,oparray]; Push[frame.opstk,execarray];
  SELECT stack FROM
    frame.opstk => RETURN[stkovrflw];
    frame.execstk => RETURN[execovrflw];
    ENDCASE => ERROR Bug;
  };

UnwindExecStk: PROC[frame: Frame, exit: BOOLEAN ← FALSE] = {
  Proc: PROC[ob: Object] RETURNS[BOOLEAN] = {
    WITH ob:ob SELECT FROM
      exec,loop => IF exit THEN RETURN[TRUE];
      stream => KillStream[ob];
      scope => SetLocalsTop[frame,ob.top];
      ENDCASE;
    RETURN[FALSE] };
  [] ← StackForAll[stack: frame.execstk, proc: Proc, unwind: TRUE];
  };

-- The main execution procedure
Execute: PUBLIC PROC[frame: Frame, ob: Object] = {
  outermost: BOOLEAN ← Empty[frame.execstk];
  IF outermost THEN InnerExecute[frame,ob !
    StackOverflow => { ob ← HandleStackOverflow[frame,stack]; RETRY };
    StopExecution => { UnwindExecStk[frame]; CONTINUE };
    UNWIND => UnwindExecStk[frame]]
  ELSE InnerExecute[frame,ob];
  };

InnerExecute: PROC[frame: Frame, object: Object] = {
  Push[frame.execstk,[X,exec[]]]; -- mark base of stack for this call on Execute
  Push[frame.execstk,object]; -- push the object to be executed
  DO
    ob: Object ← Pop[frame.execstk]; -- pop next object to be executed
    IF ob.tag=L THEN Push[frame.opstk,ob] -- to execute a literal, push it on the opstk
    ELSE {
      ENABLE Error => {
        Push[frame.opstk,ob]; -- push the offending object
        Push[frame.execstk,error]; -- push the error
        CONTINUE }; -- and press on
      PushToken: PROC[frame: Frame, tok: Object] = INLINE {
        stack: Stack ← (IF tok.tag=L THEN frame.opstk ELSE frame.execstk);
	Push[stack,tok] };
      WITH ob:ob SELECT FROM
        null => NULL; -- what did you expect?
        integer,real,boolean,dict,mark,user => Push[frame.opstk,ob]; -- always literal
        stream => {
	  -- To execute a stream Object: get characters from the stream until a token
	  -- is recognized or end of stream is encountered. If a token was found,
	  -- push the stream object back onto the stack, then push the token.
	  found,error: BOOLEAN; tok: Object;
	  s: StreamHandle ← GetStream[ob];
	  [found,error,tok] ← StreamToken[frame,s];
	  IF found THEN { Push[frame.execstk,ob]; PushToken[frame,tok] }
	  ELSE KillStream[ob];
	  IF error THEN Push[frame.execstk,syntaxerr];
	  };
	string => {
	  -- To execute a string Object: take characters from the front of the string
	  -- until a token is recognized or the string is emptied. If a token was found,
	  -- push the remainder of the string back onto the stack, then push the token.
	  found,error: BOOLEAN; tok: Object; rem: string Object;
	  [found,error,tok,rem] ← StringToken[frame,ob];
	  IF found THEN {
	    IF rem.length>0 THEN Push[frame.execstk,rem];
	    PushToken[frame,tok] };
	  IF error THEN Push[frame.execstk,syntaxerr];
	  };
        array => {
	  -- To execute an array Object: take the first element of the array
	  -- as a token if the array is not empty. If a token was obtained,
	  -- push the remaining subarray back onto the stack, then push the token.
	  found: BOOLEAN; tok: Object; rem: array Object;
	  [found,tok,rem] ← AAtom[ob];
	  IF found THEN {
	    IF rem.length>0 THEN Push[frame.execstk,rem];
	    PushToken[frame,tok] };
	  };
        name => { found: BOOLEAN; value: Object;
	  -- To execute a name, look it up in the appropriate context
	  IF ob.id.local THEN [found,value] ← TryToLoadLocal[frame,ob]
	  ELSE [found,value] ← TryToLoad[frame,ob];
	  IF found THEN {
	    MarkScope[frame,ob,value]; -- mark scope for array or string
	    PushToken[frame,value] }
	  ELSE { Push[frame.opstk,ob]; Push[frame.execstk,undefname] };
	  };
        command => {
	  -- to execute a command, call the corresponding procedure
	  -- restore the opstk if an error occurs (and only Pops have been done)
	  mark: Node ← GetMark[frame.opstk];
	  DoCommand[frame,ob ! Error => IF restore THEN Restore[frame.opstk,mark]];
	  };
	exec => EXIT; -- back to where we started
	loop => ERROR Bug; -- should have been removed by loop control
	scope => SetLocalsTop[frame,ob.top];
        ENDCASE => ERROR Bug;
      };
    IF frame.abort THEN { Push[frame.execstk,interrupt]; frame.abort ← FALSE };
    Process.Yield[];
    ENDLOOP;
  };

GetAbort: PUBLIC PROC[frame: Frame] RETURNS[BOOLEAN] = { RETURN[frame.abort] };
SetAbort: PUBLIC PROC[frame: Frame, flag: BOOLEAN] = { frame.abort ← flag };

-- "Stop" clears the execution stack and returns to the caller
-- of the outermost instance of Execute.

Stop: PUBLIC PROC[frame: Frame] = {
  JaMVM.Check[];
  ERROR StopExecution;
  };

SingStep: PROC[frame: Frame, ob: Object] = {
  Push[frame.opstk, ob];
  Push[frame.execstk, asstep];
  Push[frame.execstk, step];
  Push[frame.execstk, arfree];
  };

MarkLoop: PUBLIC PROC[frame: Frame] = {
  Push[frame.execstk,[X,loop[]]] };

UnmarkLoop: PUBLIC PROC[frame: Frame] = {
  ob: Object ← Pop[frame.execstk]; Assert[ob.type=loop] };

MarkScope: PROC[frame: Frame, name: name Object, ob: Object] = INLINE {
  IF ob.tag=X THEN SELECT ob.type FROM
    array,string => {
      top: CARDINAL ← GetLocalsTop[frame];
      Push[frame.execstk,[X,scope[top,name.id]]] };
    ENDCASE;
  };

ScopeName: PROC[frame: Frame] = {
  ob: Object ← Pop[frame.opstk];
  WITH ob:ob SELECT FROM
    scope => Push[frame.opstk,[L,name[ob.id]]];
    ENDCASE => ERROR Error[typechk];
  };

-- "Exec" Moves the top of the operand stack to the top of the execution stack.

Exec: PROC[frame: Frame] = {
  ob: Object ← Pop[frame.opstk];
  MarkScope[frame,anon,ob];
  Push[frame.execstk, ob];
  };

--"If" is the implementation of the testing instruction.
--Two operands are required: an Object and a Boolean. If the
--Boolean is TRUE then the object is executed otherwise the 
--object is popped.

If: PROC[frame: Frame] = {
  ob: Object ← Pop[frame.opstk];
  bool: BOOLEAN ← PopBoolean[frame.opstk];
  IF bool THEN Push[frame.execstk,ob];
  };

--"IfElse" is the implementation of the two way conditional execution
--instruction. Three operands are required: Object1,Object2, and a  Boolean.
-- If the Boolean is TRUE then Object2 is executed otherwise Object1 is 
--executed.

IfElse: PROC[frame: Frame] = {
  obF: Object ← Pop[frame.opstk];
  obT: Object ← Pop[frame.opstk];
  bool: BOOLEAN ← PopBoolean[frame.opstk];
  Push[frame.execstk,(IF bool THEN obT ELSE obF)];
  };

--"Rept" is the "loop for count" instruction. Two operands are required:
-- an integer and an Object. The Object is executed for the number
-- of times indicated by the integer. (0 for Negative).

Rept: PROC[frame: Frame] = {
  ob: Object ← Pop[frame.opstk];
  i: LONG INTEGER ← PopInteger[frame.opstk];
  MarkLoop[frame];
  Push[frame.execstk, ob];
  PushInteger[frame.execstk, i];
  Push[frame.execstk, reptcmd];
  };

CRept: PROC[frame: Frame] = {
  i: LONG INTEGER ← PopInteger[frame.execstk];
  ob: Object ← Top[frame.execstk];
  IF i > 0 THEN {
    PushInteger[frame.execstk,i-1];
    Push[frame.execstk, reptcmd];
    Push[frame.execstk, ob] }
  ELSE {
    [] ← Pop[frame.execstk];
    UnmarkLoop[frame];
    };
  };

--"For" is based on the Algol "for i ← i step j until k do" instruction.
-- Four operands are required: three IntegerTypes and an Object.
-- The Object is executed for each iteration, with the current loop index on
--  the operand stack. 

For: PROC[frame: Frame] = {
  ob: Object ← Pop[frame.opstk];
  k: LONG INTEGER ← PopInteger[frame.opstk];
  j: LONG INTEGER ← PopInteger[frame.opstk];
  i: LONG INTEGER ← PopInteger[frame.opstk];
  MarkLoop[frame];
  Push[frame.execstk, ob];
  PushInteger[frame.execstk, k];
  PushInteger[frame.execstk, j];
  PushInteger[frame.execstk, i];
  Push[frame.execstk, forcmd];
  };

CFor: PROC[frame: Frame] = {
  i: LONG INTEGER ← PopInteger[frame.execstk];
  j: LONG INTEGER ← PopInteger[frame.execstk];
  k: LONG INTEGER ← PopInteger[frame.execstk];
  ob: Object ← Top[frame.execstk];
  IF (IF j>0 THEN i>k ELSE i<k) THEN {
    [] ← Pop[frame.execstk];
    UnmarkLoop[frame] }
  ELSE {
    PushInteger[frame.execstk, k];
    PushInteger[frame.execstk, j];
    PushInteger[frame.execstk, i+j];
    Push[frame.execstk, forcmd];
    PushInteger[frame.opstk, i];
    Push[frame.execstk, ob] };
  };

--"Loop" is the "loop forever" instruction. One operand is required:
-- The Object is executed until an ".exit" command is executed.

Loop: PROC[frame: Frame] = {
  ob: Object ← Pop[frame.opstk];
  MarkLoop[frame];
  Push[frame.execstk, ob];
  Push[frame.execstk, loopcmd];
  };

CLoop: PROC[frame: Frame] = {
  ob: Object ← Top[frame.execstk];
  Push[frame.execstk, loopcmd];
  Push[frame.execstk, ob];
  };

-- "Exit" pops the execution stack until the innermost loop is 
-- terminated. (until a mark is encountered).

Exit: PROC[frame: Frame] = {
  UnwindExecStk[frame,TRUE];
  IF Top[frame.execstk].type#exec THEN UnmarkLoop[frame];
  };

--"SingleStep" Sets stepflag in the execution module.
-- Each token is put on the operand stack
--  and ".step" is executed followed by the token.

SingleStep: PROC[frame: Frame] = { frame.astepflag ← frame.stepflag ← TRUE };
FreeRun: PROC[frame: Frame] = { frame.astepflag ← frame.stepflag ← FALSE };
ASingleStep: PROC[frame: Frame] = { frame.stepflag ← frame.astepflag };
AFreeRun: PROC[frame: Frame] = { frame.stepflag ← FALSE };

-- Initialization

InstallExec: PROC[why: InstallReason, frame: Frame] = { SELECT why FROM
  register => {
    -- Errors
    rangechk ← MakeName[".rangechk"L];
    typechk ← MakeName[".typechk"L];
    undefname ← MakeName[".undefname"L];
    limitchk ← MakeName[".limitchk"L];
    syntaxerr ← MakeName[".syntaxerr"L];
    stkovrflw ← MakeName[".stkovrflw"L];
    execovrflw ← MakeName[".execovrflw"L];
    -- Known names
    step ← MakeName[".step"L];
    interrupt ← MakeName[".interrupt"L];
    anon ← MakeName["(anonymous)"L]; -- name for anonymous arrays and strings
    -- Internal commands
    asstep ← RegisterInternal["@asinglestep"L, ASingleStep];
    arfree ← RegisterInternal["@arunfree"L, AFreeRun];
    sstep ← RegisterInternal["@singlestep"L, SingleStep];
    rfree ← RegisterInternal["@runfree"L, FreeRun];
    reptcmd ← RegisterInternal["@rept"L, CRept];
    forcmd ← RegisterInternal["@for"L, CFor];
    loopcmd ← RegisterInternal["@loop"L, CLoop];
    -- Execution commands
    RegisterExplicit[frame, ".scopename"L, ScopeName];
    RegisterExplicit[frame, ".if"L, If];
    RegisterExplicit[frame, ".ifelse"L, IfElse];
    RegisterExplicit[frame, ".rept"L, Rept];
    RegisterExplicit[frame, ".for"L, For];
    RegisterExplicit[frame, ".loop"L, Loop];
    RegisterExplicit[frame, ".exit"L, Exit];
    RegisterExplicit[frame, ".stop"L, Stop];
    RegisterExplicit[frame, ".interrupt"L, Stop];
    RegisterExplicit[frame, ".exec"L, Exec];
    RegisterExplicit[frame, ".singlestep"L, SingleStep];
    RegisterExplicit[frame, ".runfree"L, FreeRun];
    };
  ENDCASE;
  };

Install[InstallExec];

}.

Paxton  January 7, 1981  2:28 PM
	use ArraySpread in exec loop instead of ArrayAtom

Paxton  January 7, 1981  3:42 PM
	change for name strings

Paxton  January 9, 1981  4:19 PM
	add local variables

Wyatt  16-Apr-81 12:57:31
	Pilot conversion

Wyatt  24-Aug-81 12:33:02
	add Process.Yield at bottom of Execute loop

Wyatt  16-Sep-81 12:51:59
	Rewrite

Wyatt  19-Feb-82 10:54:50
	add restore parameter to Error