-- JaMLocalImpl.mesa
-- Last changed by Doug Wyatt,  7-Oct-81 17:14:10

DIRECTORY
  JaMBasic USING [Object, Tuple],
  JaMInternal USING [Frame, Locals, LocalsRecord, Node, Stack, TupleSequence],
  JaMOps USING [Assert, Equal, Error, Install, InstallReason, KeyName, limitchk,
    Load, MakeName, MarkLoop, nullOb, Pop, PopCardinal, PopInteger, Push,
    PushBoolean, PushCardinal, rangechk, RegisterExplicit, RegisterInternal,
    Store, Top, UnmarkLoop],
  JaMStorage USING [Zone],
  Inline USING [LowHalf];

JaMLocalImpl: PROGRAM
IMPORTS JaMOps, JaMStorage, Inline
EXPORTS JaMOps = {
OPEN JaMOps, JaMInternal, JaMBasic;

-- Constants

initLocalsSize: CARDINAL = 20;
maxlenLimit: CARDINAL = LAST[CARDINAL];

-- Globals

zone: UNCOUNTED ZONE = JaMStorage.Zone[];

undeflv: name Object;
lfacmd: command Object;

-- Procedures for local/global variables 

IsLocal: PROC[key: Object] RETURNS[BOOLEAN] = INLINE {
  RETURN[WITH key:key SELECT FROM name => key.id.local, ENDCASE => FALSE] };

Fetch: PROC[frame: Frame] = {
  -- does load local or dictionary load
  key: Object ← Pop[frame.opstk];
  val: Object ← IF IsLocal[key] THEN LoadLocal[frame,key] ELSE Load[frame,key];
  Push[frame.opstk,val];
  };

Assign: PROC[frame: Frame] = {
  -- does store local or dictionary store
  val: Object ← Pop[frame.opstk];
  key: Object ← Pop[frame.opstk];
  IF IsLocal[key] THEN StoreLocal[frame,key,val] ELSE Store[frame,key,val];
  };

-- Procedures for local variables

LoadLocal: PUBLIC PROC[frame: Frame, key: Object] RETURNS[Object] = {
  -- generates undeflv if not known
  known: BOOLEAN; ob: Object;
  [known, ob] ← TryToLoadLocal[frame, key];
  IF known THEN RETURN[ob] ELSE ERROR Error[undeflv];
  };

TryToLoadLocal: PUBLIC PROC[frame: Frame, key: Object] RETURNS[BOOLEAN,Object] = {
  locals: Locals ← frame.locals;
  name: Object ← KeyName[key];
  FOR i: CARDINAL DECREASING IN[0..locals.curlen) DO
    tuple: Tuple ← locals.array[i];
    IF Equal[tuple.key,name] THEN RETURN[TRUE,tuple.value];
    ENDLOOP;
  RETURN[FALSE,nullOb];
  };

DefineLocal: PUBLIC PROC[frame: Frame, key,value: Object] = {
  locals: Locals ← frame.locals;
  tuple: Tuple ← [KeyName[key],value];
  i: CARDINAL ← locals.curlen;
  IF i>=locals.maxlen THEN GrowLocals[locals,locals.maxlen/2];
  Assert[i<locals.maxlen]; locals.array[i] ← tuple;
  locals.curlen ← i + 1;
  };

StoreLocal: PUBLIC PROC[frame: Frame, key,value: Object] = {
  locals: Locals ← frame.locals;
  name: Object ← KeyName[key];
  FOR i: CARDINAL DECREASING IN[0..locals.curlen) DO
    tuple: Tuple ← locals.array[i];
    IF Equal[tuple.key,name] THEN { locals.array[i].value ← value; RETURN };
    ENDLOOP;
  DefineLocal[frame,name,value];
  };

NewLocals: PUBLIC PROC RETURNS[Locals] = {
  n: CARDINAL = initLocalsSize;
  array: LONG POINTER TO TupleSequence ← zone.NEW[TupleSequence[n]];
  locals: Locals ← zone.NEW[LocalsRecord ← [curlen: 0, maxlen: n, array: array]];
  RETURN[locals];
  };

FreeLocals: PUBLIC PROC[locals: Locals] = {
  zone.FREE[@locals.array];
  zone.FREE[@locals];
  };

GrowLocals: PROC[locals: Locals, grow: CARDINAL] = {
  old: LONG POINTER TO TupleSequence ← locals.array;
  new: LONG POINTER TO TupleSequence ← NIL;
  oldmax: CARDINAL ← locals.maxlen;
  newmax: CARDINAL;
  IF grow = 0 THEN grow ← oldmax/2;
  newmax ← oldmax + MIN[grow,maxlenLimit-oldmax];
  IF NOT newmax>oldmax THEN ERROR Error[limitchk];
  new ← zone.NEW[TupleSequence[newmax]];
  FOR i: CARDINAL IN[0..oldmax) DO new[i] ← old[i] ENDLOOP;
  locals.array ← new; locals.maxlen ← newmax;
  zone.FREE[@old];
  };

-- Intrinsics

LvLoad: PROC[frame: Frame] = {
  key: Object ← Pop[frame.opstk];
  val: Object ← LoadLocal[frame,key];
  Push[frame.opstk,val];
  };

LvKnown: PROC[frame: Frame] = {
  key: Object ← Pop[frame.opstk];
  known: BOOLEAN;
  [known,] ← TryToLoadLocal[frame,key];
  PushBoolean[frame.opstk, known];
  };

LvDefine: PROC[frame: Frame] = {
  val: Object ← Pop[frame.opstk];
  key: Object ← Pop[frame.opstk];
  DefineLocal[frame,key,val];
  };

LvStore: PROC[frame: Frame] = {
  val: Object ← Pop[frame.opstk];
  key: Object ← Pop[frame.opstk];
  StoreLocal[frame,key,val];
  };

LvMaxLength: PROC[frame: Frame] = {
  -- pushes current max available for locals
  locals: Locals ← frame.locals;
  PushCardinal[frame.opstk, locals.maxlen];
  };

LvLength: PROC[frame: Frame] = {
  -- pushes current number of locals
  locals: Locals ← frame.locals;
  PushCardinal[frame.opstk, locals.curlen];
  };

LvGrow: PROC[frame: Frame] = {
  -- increases max available for locals by amount from opstk 
  inc: LONG INTEGER ← PopInteger[frame.opstk];
  IF inc <= 0 THEN RETURN;
  IF inc>LAST[CARDINAL] THEN ERROR Error[rangechk];
  GrowLocals[frame.locals, Inline.LowHalf[inc]];
  };

LvForAll: PUBLIC PROC[frame: Frame] = {
  -- Expects opstk: (object)
  -- For each pair in locals put (name)(value) onto opstk and execute object
  -- Returns opstk: ()
  locals: Locals ← frame.locals;
  ob: Object ← Pop[frame.opstk];
  -- save them on exec stack
  MarkLoop[frame];
  Push[frame.execstk, ob];
  -- prime state
  PushCardinal[frame.execstk, locals.curlen];
  -- start it
  LvFAProc[frame];
  };

LvFAProc: PROC[frame: Frame] = {
  lv: CARDINAL ← PopCardinal[frame.execstk];
  ob: Object ← Top[frame.execstk];
  locals: Locals ← frame.locals;
  IF (lv ← MIN[lv,locals.maxlen])>0 THEN {
    i: CARDINAL ← lv - 1;
    tuple: Tuple ← locals.array[i];
    -- set up opstk
    Push[frame.opstk,tuple.key];
    Push[frame.opstk,tuple.value];
    -- set up execstk
    PushCardinal[frame.execstk, i];
    Push[frame.execstk, lfacmd];
    Push[frame.execstk, ob];
    -- and let it happen
    }
  ELSE {
    [] ← Pop[frame.execstk]; -- remove object
    UnmarkLoop[frame]; -- remove mark
    };
  };

-- Initialization

InstallLocal: PROC[why: InstallReason, frame: Frame] = { SELECT why FROM
  register => {
    undeflv ← MakeName[".undeflv"L];
    lfacmd ← RegisterInternal["@lvforall"L, LvFAProc];
    -- Local variable commands
    RegisterExplicit[frame, ".lvload"L, LvLoad];
    RegisterExplicit[frame, ".lvknown"L, LvKnown];
    RegisterExplicit[frame, ".lv"L, LvDefine];
    RegisterExplicit[frame, ".lvstore"L, LvStore];
    RegisterExplicit[frame, ".lvforall"L, LvForAll];
    RegisterExplicit[frame, ".lvlength"L, LvLength];
    RegisterExplicit[frame, ".lvmaxlength"L, LvMaxLength];
    RegisterExplicit[frame, ".lvgrow"L, LvGrow];
    RegisterExplicit[frame, ".fetch"L, Fetch];
    RegisterExplicit[frame, ".assign"L, Assign];
    };
  ENDCASE;
  };

Install[InstallLocal];

}.