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^.kindprimitive 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 ilayers[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 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.