packages feed

language-Modula2-0.1: examples/Dipl/M2/MACHINE.MOD

IMPLEMENTATION MODULE Machine;
FROM SYSTEM IMPORT ADDRESS, WORD, BYTE, ADR, TSIZE, Registers;
FROM Lib IMPORT IncAddr, Move, WordMove, WordFill, Compare, Intr;
FROM Memory IMPORT ALLOCATE, MarkNeeded, AddMarker, DropMarker;
FROM IO IMPORT WrStr, WrChar, WrCharRep, WrInt, WrHex, WrLn, RdKey;
CONST
   MaxStack=8000;
   MaxLayers=8000;
   indention=3;
   FillValue=700H;
   TextScreen=0B000H;
TYPE
(*
   envList=POINTER TO environment;
   closure=POINTER TO clRec;
   flag=(defined, primitive, packed, string, length);
   clRec=RECORD
            CASE kind:flag OF
               defined:
                  root:link
               ELSE
                  handler:handlerType
            END;
            env,valEnv:envList
         END;
   environment=RECORD
                  name:link;
                  newDef:closure;
                  next:envList
               END;
*)
   stackItem=   RECORD
                  name:wdPtr;
                  oldDef:closure
               END;
VAR
   stack:ARRAY[1..MaxStack] OF stackItem;
   layers:ARRAY[1..MaxLayers] OF [0..MaxStack];
   sp:[0..MaxStack];
   topLayer,breakLayer:[0..MaxLayers];
   runScreen:ARRAY [0..25*80-1] OF WORD;
   runCurPos:CARDINAL;

PROCEDURE StartLayer;
BEGIN
   INC(topLayer);
   layers[topLayer]:=sp
END StartLayer;

PROCEDURE SaveDefinition(name:link);
BEGIN
   INC(sp);
   stack[sp].name:=wdPtr(name);
   stack[sp].oldDef:=name^
END SaveDefinition;

PROCEDURE ChangeDefinition(name:link; value:closure);
BEGIN
   SaveDefinition(name);
   name^:=value
END ChangeDefinition;

PROCEDURE NewLayer(d:closure);
VAR
   swap:closure;
   i:CARDINAL;
   layer:envList;
BEGIN
   IF (d^.kind<packed) & (d^.env=NIL) & (d^.valEnv=NIL) THEN
      RETURN
   END;
   IF d^.kind>primitive THEN
      INC(sp);
      stack[sp].name:=wdPtr(ADR(parent));
      stack[sp].oldDef:=parent;
      parent:=d;
      INC(sp);
      stack[sp].name:=wdPtr(wPackage);
      stack[sp].oldDef:=wPackage^;
      wPackage^:=d
   ELSE
      IF d^.valEnv=NIL THEN
         layer:=d^.env
      ELSE
         i:=sp;
         layer:=d^.valEnv;
         WHILE layer#NIL DO
            INC(sp);
            WITH stack[sp] DO
               name:=layer^.name;
               oldDef:=layer^.newDef;
               Reduce(oldDef);
               oldDef:=result
            END;
            layer:=layer^.next
         END;
         layer:=d^.env;
         WHILE i<sp DO
            INC(i);
             WITH stack[i] DO
               swap:=oldDef;
               oldDef:=link(name)^;
               link(name)^:=swap
            END
         END
      END;
      INC(sp);
      stack[sp].name:=wdPtr(ADR(parent));
      stack[sp].oldDef:=parent;
      parent:=d;
      WHILE layer#NIL DO
         INC(sp);
         WITH stack[sp] DO
            name:=layer^.name;
            oldDef:=link(name)^;
            link(name)^:=layer^.newDef
         END;
         layer:=layer^.next
      END
   END
END NewLayer;
      
PROCEDURE PopLayer;
BEGIN
(*   limit:=layers[topLayer]; *)
   WHILE sp>layers[topLayer] DO
      link(stack[sp].name)^:=stack[sp].oldDef;
      DEC(sp)
   END;
   DEC(topLayer)
END PopLayer;

PROCEDURE Keep(name:link);
VAR
   s:CARDINAL;
BEGIN
   s:=sp;
   WHILE (s>layers[topLayer]) AND (link(stack[s].name)#name) DO
      DEC(s)
   END;
   IF s>layers[topLayer] THEN
      stack[s]:=stack[sp];
      DEC(sp)
   END
END Keep;

PROCEDURE ShowDef(d:closure);
FORWARD;

PROCEDURE Reduce(def:closure);
BEGIN
   StartLayer;
   WHILE def^.kind#primitive DO
      IF debug OR (topLayer<=breakLayer) THEN
         ShowDef(def)
      END;
      NewLayer(def);
      def:=link(def^.root)^
   END;
   IF (def^.env#NIL) OR (def^.valEnv#NIL) THEN
      NewLayer(def)
   END;
   def^.handler;
   PopLayer
END Reduce;

PROCEDURE DummyHandler;
BEGIN
   result:=wPackage^
END DummyHandler;

PROCEDURE EmptyHandler;
END EmptyHandler;

PROCEDURE ParentHandler;
BEGIN
   result:=parent
END ParentHandler;

PROCEDURE Root(def:closure):link;
BEGIN
   RETURN link(def^.root)
END Root;

PROCEDURE EnvCopy(source:envList; VAR dest:envList);
VAR
   current:POINTER TO envList;
BEGIN
   dest:=NIL;
   current:=ADR(dest);
   WHILE source#NIL DO
      NEW(current^);
      current^^:=source^;
      current:=ADR(current^^.next);
      source:=source^.next
   END
END EnvCopy;

PROCEDURE Copy(source:closure; VAR dest:closure);
BEGIN
   NEW(dest);
   IF source^.kind>primitive THEN
      dest^:=source^
   ELSE
      dest^.root:=source^.root;
      dest^.kind:=source^.kind;
      dest^.valEnv:=NIL;
      EnvCopy(source^.env,dest^.env);
      EnvCopy(source^.valEnv,dest^.valEnv)
   END
END Copy;

PROCEDURE JoinEnv(VAR union:envList; add:envList);
VAR
   newEnv:envList;
   current:POINTER TO envList;
BEGIN
   current:=ADR(union);
   WHILE add#NIL DO
      WHILE (current^#NIL) AND (Seg(add^.name^) > Seg(current^^.name^)) DO
         current:=ADR(current^^.next)
      END;
      IF (current^#NIL) AND (add^.name=current^^.name) THEN
         current^^.newDef:=add^.newDef
      ELSE
         NEW(newEnv);
         newEnv^:=add^;
         newEnv^.next:=current^;
         current^:=newEnv
      END;
      add:=add^.next
   END
END JoinEnv;
   
PROCEDURE Join(VAR def:closure; description:closure);
BEGIN
   IF (def^.kind<=primitive) AND (description^.kind<=primitive) THEN
      JoinEnv(def^.env,description^.env);
      JoinEnv(def^.valEnv,description^.valEnv)
   ELSIF (def^.kind=defined) AND (def^.env=NIL) AND (def^.valEnv=NIL) THEN
      def^.kind:=description^.kind;
      EnvCopy(description^.env,def^.env);
      EnvCopy(description^.valEnv,def^.valEnv)
   END
END Join;

PROCEDURE Build;
VAR
   follow,valFollow:envList;
   current:POINTER TO envList;
BEGIN
   IF (parent^.kind<packed) & (parent^.valEnv#NIL) THEN
      NEW(result);
      result^:=wType^^;
      follow:=result^.env;
      valFollow:=parent^.valEnv;
      IF link(valFollow^.name)=wType THEN
         valFollow:=valFollow^.next
      END;
      current:=ADR(result^.env);
      WHILE valFollow#NIL DO
         WHILE (follow#NIL) & (Seg(follow^.name^) < Seg(valFollow^.name^)) DO
            current^:=NIL;
            NEW(current^);
            current^^.name:=follow^.name;
            current^^.newDef:=follow^.newDef;
            current:=ADR(current^^.next);
            follow:=follow^.next
         END;
         IF (follow#NIL) & (follow^.name = valFollow^.name) THEN
            follow:=follow^.next
         END;
         current^:=NIL;
         NEW(current^);
         current^^.name:=valFollow^.name;
         current^^.newDef:=link(valFollow^.name)^;
         current:=ADR(current^^.next);
         valFollow:=valFollow^.next
      END;
      WHILE follow#NIL DO
         current^:=NIL;
         NEW(current^);
         current^^.name:=follow^.name;
         current^^.newDef:=follow^.newDef;
         current:=ADR(current^^.next);
         follow:=follow^.next
      END;
      current^:=NIL
   ELSE
      result:=wType^
   END
END Build;

PROCEDURE BuildMixed;
VAR
   e:envList;
   dest:POINTER TO envList;
BEGIN
   result:=parent;
   IF (result^.kind<packed) & (result^.valEnv#NIL) THEN
      NEW(result);
      result^:=parent^;
      WITH result^ DO
         dest:=ADR(env);
         REPEAT
            NEW(e);
            e^.name:=valEnv^.name;
            e^.newDef:=link(e^.name)^;
            WHILE (dest^#NIL) & (Seg(e^.name^) > Seg(dest^^.name^)) DO
               dest:=ADR(dest^^.next)
            END;
            e^.next:=dest^;
            dest^:=e;
            valEnv:=valEnv^.next
         UNTIL valEnv=NIL
      END
   END
END BuildMixed;

PROCEDURE BuildReduced;
VAR
   follow:envList;
   i:CARDINAL;
BEGIN
   follow:=parent^.env;
   WHILE follow#NIL DO
      link(stack[sp].name)^:=stack[sp].oldDef;
      DEC(sp);
      follow:=follow^.next
   END;
   SaveDefinition(ADR(parent));
   Copy(parent,parent);
   follow:=parent^.env;
   WHILE follow#NIL DO
      Reduce(follow^.newDef);
      follow^.newDef:=result;
      follow:=follow^.next
   END;
   follow:=parent^.valEnv;
   WHILE follow#NIL DO
      Reduce(link(follow^.name)^);
      follow^.newDef:=result;
      follow:=follow^.next
   END;
   result:=parent;
   JoinEnv(result^.env,result^.valEnv);
   result^.valEnv:=NIL
END BuildReduced;

PROCEDURE MakeDef(VAR def:closure; defRoot:link);
BEGIN
   NEW(def);
   WITH def^ DO
      kind:=defined;
      env:=NIL;
      valEnv:=NIL;
      root:=wdPtr(defRoot)
   END
END MakeDef;

PROCEDURE LinkDef(l:link; defRoot:link);
BEGIN
   SaveDefinition(l);
   MakeDef(l^,defRoot)
END LinkDef;

PROCEDURE MakePrimitive(VAR def:closure; defHandler:handlerType);
BEGIN
   NEW(def);
   WITH def^ DO
      kind:=primitive;
      env:=NIL;
      valEnv:=NIL;
      handler:=defHandler
   END
END MakePrimitive;

PROCEDURE LinkPrimitive(l:link; defHandler:handlerType);
BEGIN
   SaveDefinition(l);
   MakePrimitive(l^,defHandler)
END LinkPrimitive;

PROCEDURE MakePacked(VAR d:closure; root:link; x:ARRAY OF BYTE);
BEGIN
   NEW(d);
   d^.root:=wdPtr(root);
   d^.kind:=packed;
   IF HIGH(x)>3 THEN
      ALLOCATE(d^.env,HIGH(x)+1);
      d^.kind:=length;
      Move(ADR(x),d^.env,HIGH(x)+1);
      d^.valEnv:=envList(HIGH(x)+1)
   ELSE
      d^.valEnv:=NIL;
      d^.env:=NIL;
      Move(ADR(x),ADR(d^.env),HIGH(x)+1)
   END
END MakePacked;

PROCEDURE LinkPacked(l:link; root:link; x:ARRAY OF BYTE);
BEGIN
   SaveDefinition(l);
   MakePacked(l^,root,x)
END LinkPacked;

PROCEDURE MakeString(VAR d:closure; root:link; a:ADDRESS);
BEGIN
   NEW(d);
   d^.kind:=string;
   d^.root:=wdPtr(root);
   d^.env:=envList(a);
   d^.valEnv:=NIL
END MakeString;

PROCEDURE LinkString(l:link; root:link; a:ADDRESS);
BEGIN
   SaveDefinition(l);
   MakeString(l^,defRoot)
END LinkString;

PROCEDURE AddEnv(VAR d:closure; prop:link; newValue:closure);
VAR
   newProp:envList;
   current:POINTER TO envList;
BEGIN
   IF (prop=NIL) OR (d^.kind>primitive) THEN
      RETURN
   END;
   current:=ADR(d^.env);
   WHILE (current^#NIL) AND (Seg(current^^.name^) < Seg(prop^)) DO
      current:=ADR(current^^.next)
   END;
   IF (current^#NIL) AND (link(current^^.name)=prop) THEN
      current^^.newDef:=newValue
   ELSE
      NEW(newProp);
      WITH newProp^ DO
         name:=wdPtr(prop);
         newDef:=newValue;
         next:=current^
      END;
      current^:=newProp
   END
END AddEnv;

PROCEDURE AddValuedEnv(VAR d:closure; prop:link; newValue:closure);
VAR
   newProp:envList;
   current:POINTER TO envList;
BEGIN
   IF (prop=NIL) OR (d^.kind>primitive) THEN
      RETURN
   END;
   current:=ADR(d^.valEnv);
   WHILE (current^#NIL) AND (Seg(current^^.name^) < Seg(prop^)) DO
      current:=ADR(current^^.next)
   END;
   IF (current^#NIL) AND (link(current^^.name)=prop) THEN
      current^^.newDef:=newValue
   ELSE
      NEW(newProp);
      WITH newProp^ DO
         name:=wdPtr(prop);
         newDef:=newValue;
         next:=current^
      END;
      current^:=newProp
   END
END AddValuedEnv;

PROCEDURE RemoveEnv(VAR d:closure; prop:link);
VAR
   current:POINTER TO envList;
BEGIN
   IF (prop=NIL) OR (d^.kind>primitive) THEN
      RETURN
   END;
   current:=ADR(d^.env);
   WHILE (current^#NIL) AND (link(current^^.name)#prop) DO
      current:=ADR(current^^.next)
   END;
   IF current^#NIL THEN
      current^:=current^^.next
   END;
   current:=ADR(d^.valEnv);
   WHILE (current^#NIL) AND (link(current^^.name)#prop) DO
      current:=ADR(current^^.next)
   END;
   IF current^#NIL THEN
      current^:=current^^.next
   END
END RemoveEnv;

PROCEDURE EnvEqual(e1,e2:envList):BOOLEAN;
BEGIN
   WHILE e1#NIL DO
      IF (e2#NIL) AND (e1^.name=e2^.name) AND (e1^.newDef=e2^.newDef) THEN
         e1:=e1^.next;
         e2:=e2^.next
      ELSE
         RETURN FALSE
      END
   END;
   RETURN e2=NIL
END EnvEqual;
   
TYPE
   CharPtr=POINTER TO CHAR;

PROCEDURE SameStrings(s1,s2:ADDRESS):BOOLEAN;
BEGIN
   WHILE (s1^=s2^) AND (CharPtr(s1)^#0C) DO
      IncAddr(s1,1);
      IncAddr(s2,1)
   END;
   RETURN s1^=s2^
END SameStrings;

PROCEDURE Equal(d1,d2:closure):BOOLEAN;
BEGIN
   IF (d1=d2) OR (d1^=d2^) THEN
      RETURN TRUE
   END;
   IF (d1^.root#d2^.root) OR (d1^.kind#d2^.kind) THEN
      RETURN FALSE
   END;
   CASE d1^.kind OF
      |packed:RETURN FALSE
      |string:RETURN SameStrings(d1^.env,d2^.env)
      |length:RETURN (d1^.valEnv=d2^.valEnv) AND
            (CARDINAL(d1^.valEnv)=Compare(d1^.env,d2^.env,CARDINAL(d1^.valEnv)))
   ELSE
      RETURN EnvEqual(d1^.env,d2^.env) AND EnvEqual(d1^.valEnv,d2^.valEnv)
   END
END Equal;
      
PROCEDURE ExtractPacked(package:closure; VAR x:ARRAY OF BYTE);
BEGIN
   IF HIGH(x)<4 THEN
      Move(ADR(package^.env),ADR(x),HIGH(x)+1)
   ELSE
      Move(package^.env,ADR(x),HIGH(x)+1)
   END
END ExtractPacked;

PROCEDURE Envelope(VAR def:closure; struct:closure;
                  cons,valCons,listEnd,prop,value,next:link);
   PROCEDURE EnvToList(env:envList;cons:link);
   BEGIN
      IF env#NIL THEN
         EnvToList(env^.next,cons);
         tempDef:=def;
         MakeDef(def,cons);
         AddEnv(def,next,tempDef);
         MakeDef(tempDef,link(env^.name));
         AddEnv(def,prop,tempDef);
         AddEnv(def,value,env^.newDef)
      END
   END EnvToList;
BEGIN
   MakeDef(def,listEnd);
   IF struct^.kind>primitive THEN
      tempDef:=def;
      MakeDef(def,cons);
      AddEnv(def,next,tempDef);
      MakeDef(tempDef,wPackage);
      AddEnv(def,prop,tempDef);
      NEW(tempDef);
      tempDef^:=struct^;
      tempDef^.root:=wdPtr(wPacked);
      AddEnv(def,value,tempDef)
   ELSE
      EnvToList(struct^.env,cons);
      EnvToList(struct^.valEnv,valCons)
   END;
   tempDef:=EmptyClosure
END Envelope;

   
PROCEDURE MarkEnvironment(VAR env:envList);
VAR
   fresh:BOOLEAN;
   ep:POINTER TO envList;
BEGIN
   fresh:=TRUE;
   ep:=ADR(env);
   WHILE (ep^#NIL) AND fresh DO
      MarkNeeded(ep^, TSIZE(environment), fresh);
      MarkClosure(ep^^.newDef);
      ep:=ADR(ep^^.next)
   END
END MarkEnvironment;

PROCEDURE MarkString(VAR str:ADDRESS);
VAR
   length:CARDINAL;
   fresh:BOOLEAN;
   follow:CharPtr;
BEGIN
   IF str#NIL THEN
      length:=1;
      follow:=CharPtr(str);
      WHILE follow^#0C DO
         INC(length);
         IncAddr(follow,1)
      END;
      MarkNeeded(str,length,fresh)
   END
END MarkString;

PROCEDURE MarkClosure(VAR def:closure);
VAR
   fresh:BOOLEAN;
BEGIN
   IF def=NIL THEN
      RETURN
   END;
   MarkNeeded(def,TSIZE(clRec),fresh);
   IF fresh THEN
      CASE def^.kind OF
         defined:
            MarkLink(link(def^.root));
            MarkEnvironment(def^.env);
            MarkEnvironment(def^.valEnv)
         |primitive:
            MarkEnvironment(def^.env);
            MarkEnvironment(def^.valEnv)
         |string:
            MarkLink(link(def^.root));
            MarkString(def^.env)
         |length:
            MarkLink(link(def^.root));
            MarkNeeded(def^.env,CARDINAL(def^.valEnv),fresh)
      END
   END
END MarkClosure;

PROCEDURE MarkLink(VAR l:link);
VAR
   fresh:BOOLEAN;
BEGIN
   MarkNeeded(l,TSIZE(closure),fresh);
   IF fresh THEN
      MarkClosure(l^)
   END
END MarkLink;

PROCEDURE MarkStack;
VAR
   i:CARDINAL;
BEGIN
   FOR i:=sp TO 1 BY -1 DO
      MarkLink(link(stack[i].name));
      MarkClosure(stack[i].oldDef)
   END
END MarkStack;

PROCEDURE MarkTemporaries;
BEGIN
   MarkClosure(tempDef);
   MarkClosure(result);
   MarkClosure(EmptyClosure);
   MarkClosure(HandlePackage);
   MarkClosure(HandleParent);
   MarkLink(wPackage);
   MarkClosure(parent)
END MarkTemporaries;

PROCEDURE CursorPos():CARDINAL;
VAR
   r:Registers;
BEGIN
   r.AH:=3;
   r.BH:=0;
   Intr(r,10H);
   RETURN r.DX
END CursorPos;

PROCEDURE PlaceCursor(pos:CARDINAL);
VAR
   r:Registers;
BEGIN
   r.AH:=2;
   r.BH:=0;
   r.DX:=pos;
   Intr(r,10H)
END PlaceCursor;

PROCEDURE SaveScreen(VAR scr:ARRAY OF WORD);
BEGIN
   WordMove([TextScreen:0], ADR(scr), 25*80)
END SaveScreen;

PROCEDURE RestoreScreen(VAR scr:ARRAY OF WORD);
BEGIN
   WordMove(ADR(scr), [TextScreen:0], 25*80)
END RestoreScreen;

PROCEDURE ClearScreen;
BEGIN
   WordFill([TextScreen:0], 50*80, FillValue);
   PlaceCursor(0)
END ClearScreen;

PROCEDURE WriteDef(d:closure; indent:CARDINAL);
VAR
   follow:CharPtr;
   
   PROCEDURE WriteEnv(e:envList; indent:CARDINAL; colon:BOOLEAN);
   BEGIN
      WHILE e#NIL DO
         WrLn;
         WrCharRep(' ',indent);
         WrStr(e^.name^.name^);
         IF colon THEN
            WrChar(':')
         END;
         WrChar('=');
         WriteDef(e^.newDef, indent);
         e:=e^.next
      END
   END WriteEnv;
BEGIN
   CASE d^.kind OF
      |defined:
         WrStr(d^.root^.name^);
         WrChar('(');
         IF d^.valEnv#NIL THEN
            WriteEnv(d^.valEnv, indent+indention, TRUE);
            WrLn
         END;
         WriteEnv(d^.env, indent+indention, FALSE);
         WrChar(')')
      |string:
         WrChar('[');
         WrStr(d^.root^.name^);
         WrChar(']');
         WrChar('"');
         follow:=CharPtr(d^.env);
         WHILE follow^#0C DO
            WrChar(follow^);
            IncAddr(follow,1)
         END;
         WrChar('"')
      |primitive:
         WrStr('PRIMITIVE[');
         WrHex(Seg(d^.env),0);
         WrChar(':');
         WrHex(Ofs(d^.env),0);
         WrChar(']')
      |packed:
         WrChar('[');
         WrStr(d^.root^.name^);
         WrChar(']');
         WrInt(INTEGER(d^.env),0);
         IF CHAR(d^.env) >= ' ' THEN
            WrStr(" - '");
            WrChar(CHAR(d^.env));
            WrChar("'")
         END
      |length:
         WrChar('[');
         WrStr(d^.root^.name^);
         WrChar(']');
         WrHex(Seg(d^.valEnv),5)
   END
END WriteDef;

PROCEDURE ShowDef(d:closure);
VAR
   ch:CHAR;
BEGIN
   debug:=TRUE;
   breakLayer:=0;
   runCurPos:=CursorPos();
   SaveScreen(runScreen);
   ClearScreen;
   WriteDef(d,0);
   LOOP
      ch:=RdKey();
      CASE ch OF
(*         |'w','W':
            Info(debugWin,winfo);
            SetWrap(NOT winfo.WrapOn);
            Clear;
            WriteDef(d,0)
*)
         |' ':
            EXIT
         |'p','P':
            debug:=FALSE;
            breakLayer:=topLayer;
            RestoreScreen(runScreen);
            EXIT
         |'/':
            RestoreScreen(runScreen);
            ch:=RdKey();
            ClearScreen;
            WriteDef(d,0)
         |'g','G':
            debug:=FALSE;
            EXIT
         |'q','Q':
            ch:=RdKey();
            IF ch='!' THEN
               HALT
            END
      END
   END;
   RestoreScreen(runScreen);
   PlaceCursor(runCurPos)
END ShowDef;

BEGIN
   sp:=0;
   topLayer:=1;
   breakLayer:=0;
   MakePrimitive(EmptyClosure,EmptyHandler);
   MakePrimitive(HandlePackage,DummyHandler);
   MakePrimitive(HandleParent,ParentHandler);
   tempDef:=EmptyClosure;
   result:=EmptyClosure;
   parent:=NIL;
   AddMarker(MarkStack);
   AddMarker(MarkTemporaries);
   debug:=FALSE
END Machine.