-- PLEvalImpl.Mesa Last Modified by JHM

-- used to be Eval.Mesa

DIRECTORY
  Disp: TYPE USING [Print],
  IO: TYPE USING [char, CR, Put, rope, ROPE],
  PL: TYPE USING [Dist, EndDisplay, Environment, ERecord, GetSpecialNodes,
  	Insert, Interrupt, LSTNode, Node, NodeRecord, NodeType, OS, PBug, rASS, rCAT,
  	rCATL, rCLOSURE, rCOMB, rDELETE, rEQUAL, RErr, rFAIL, rFCN, rGOBBLE,
  	rGTR, rHOLE, rID, rITER, rLST, rMAPPLY, rMINUS, rOPT, rPALT, rPAPPLY,
  	rPATTERN, rPFUNC, rPFUNC1, rPLUS, rPROG, rSEQ, rSEQOF, rSEQOFC,
  	rSTR, rTILDE, rUNARY, rUNDEFINED, rVAL, rWILD, rZARY, SErr,
  	Symbol, SymbolRecord, Z],
  Process: TYPE USING [Yield],
  PString: TYPE USING [ConvertStream, CopyStream, Empty, EmptyS, Item,
  	MakeInteger, MakeNUM, NewStream, Stream, Sub, SubString,
  	SubStringStream],
  Rope: TYPE USING [Concat, Equal, Length, ROPE],
  Route: TYPE USING [KeyRoutine];

PLEvalImpl: CEDAR PROGRAM IMPORTS P: PL, S: PString, Disp, Route, Process, Rope, IO EXPORTS PL = {
OPEN P, Rope, IO;
--
N: ZONE = P.Z;

NodeType: TYPE = PL.NodeType;
Node: TYPE = PL.Node;
NodeRecord: TYPE = PL.NodeRecord;
ListNodeRecord: TYPE = LST NodeRecord;
LSTNode: TYPE = PL.LSTNode;
Environment: TYPE = PL.Environment;
ERecord: TYPE = PL.ERecord;
Symbol: TYPE = PL.Symbol;
Stream: TYPE = PString.Stream;
--
Fail: Node;
MTSt: rSTR;
Nail: LSTNode;

Left, Right: REF VAL P.SymbolRecord;

Apply: PROC[val, func: Node] RETURNS [ret: Node] = {
CK;
WITH func SELECT FROM
   f: rID => WITH f.name SELECT FROM
        x: rZARY => ret ← x.p[val];
	ENDCASE => P.RErr["Primitive out of context"];
   f: rCOMB => WITH f.proc SELECT FROM
   	x: rUNARY => ret ← x.p[val,f.parm];
	ENDCASE => P.RErr["Primitive out of context"];
   f: rCLOSURE =>
      WITH f.exp SELECT FROM
         g: rFCN =>
          ret ← Eval[g.fcn, ExtendEnv[f.env, g.parms, val]];
         g: rPATTERN =>
          WITH val SELECT FROM
             v: rSTR => ret ← Match[v.str, g.pattern, f.env];
             ENDCASE => P.RErr["Pattern applied to non-string"];
         ENDCASE => P.PBug["Bad closure"];
   f: rSTR => WITH val SELECT FROM 
      l: rLST => { -- subscript operation
            i: LONG INTEGER ← S.MakeInteger[f.str];
            neg: BOOLEAN ← FALSE;
            t: LSTNode ← l;
            IF i=0 THEN P.RErr["Zero subscript"];
            IF i<0 THEN { i ← -i; neg← TRUE };
            UNTIL i=1
      	         DO
	         IF t.listhead=NIL THEN P.RErr["Subscript too big"];
	         t ← t.listtail;
	         i ← i-1;
	         ENDLOOP;
            IF t.listhead=NIL THEN P.RErr["Subscript too big"];
            ret ← IF neg THEN t.listtail ELSE t.listhead;
            };
      ENDCASE => P.RErr["String applied to non-list"];
   f: rLST =>
      {
      m: PROC[x: Node] RETURNS [t: Node] =
	   {t ← Apply[val, x]};
      ret ← MapList[f, m];
      }
   ENDCASE => P.RErr["Bad application"];
ret.e ← TRUE;
};

Binary: PROC[vi, vj: Node, p: PROC[ROPE, ROPE] RETURNS [ROPE]] RETURNS[ans: Node] = {
WITH vi SELECT FROM
  i: rSTR => WITH vj SELECT FROM
           j: rSTR => ans ← SN[p[i.str, j.str]];
           j: rLST =>
            {C: PROC [j: Node] RETURNS [Node] =
              {WITH j SELECT FROM
                x: rSTR => RETURN [SN[p[i.str, x.str]]];
                ENDCASE =>
                  ERROR P.RErr["Binary operation to list of non-strings"]};
              ans ← MapList[j, C]};
           ENDCASE => ERROR P.RErr["second operand mal-formed"];
  i: rLST => WITH vj SELECT FROM
      j: rSTR => 
            {C: PROC [i: Node] RETURNS [t:Node] =
              {WITH i SELECT FROM
                x: rSTR => RETURN [SN[p[x.str, j.str]]];
                ENDCASE =>
                  ERROR P.RErr["Binary operation to list of non-strings"]};
              ans ← MapList[i, C]};
      j: rLST =>
      {ti: LSTNode ← i;
       tj: LSTNode ← j;
       tk: LSTNode ← NewNail[];
       ans ← tk;
       UNTIL ti.listhead=NIL AND tj.listhead=NIL
	   DO
	   IF ti.listhead=NIL OR tj.listhead=NIL THEN
                  P.RErr["List lengths differ"];
	   WITH ti.listhead SELECT FROM
             hi: rSTR =>
            WITH tj.listhead SELECT FROM
             hj: rSTR => {
                    tk↑ ← [TRUE,LST[SN[p[hi.str, hj.str]],NewNail[]]];
                    tk ← tk.listtail;
                    ti ← ti.listtail;
                    tj ← tj.listtail};
	    ENDCASE =>
                P.RErr["Binary operation to list of non-strings"];
	    ENDCASE =>
                P.RErr["Binary operation to list of non-strings"];
	   ENDLOOP}
    ENDCASE => P.RErr["second operand mal-formed"];
 ENDCASE => P.RErr["first operand mal-formed"];
};

BlessLST: PUBLIC PROC[x: Node] RETURNS[LSTNode] = {
      WITH x SELECT FROM
       xl: rLST => RETURN[xl];
       ENDCASE => ERROR P.PBug["Non-list"]};

BlessString: PUBLIC PROC[x: Node] RETURNS[ROPE] = {
      WITH x SELECT FROM
       s: rSTR => RETURN[s.str];
       ENDCASE => ERROR P.PBug["Non-string"]};

CK: PROC = {
Process.Yield[]; 
--IF TypeScript.UserAbort[Disp.H] THEN
--      {TypeScript.ResetUserAbort[Disp.H]; P.Interrupt;};
};

ConcL: PROC[vi, vj: Node] RETURNS [ans: LSTNode] =
      {
      WITH vi SELECT FROM li: rLST =>
      WITH vj SELECT FROM vj: rLST =>
      {t: LSTNode ← NewNail[];
       ti: LSTNode ← li;
       ans ← t;
       UNTIL ti.listhead=NIL
	     DO
	     t↑ ← [TRUE, LST[ti.listhead, NewNail[]]];
	     t ← t.listtail;
	     ti ← ti.listtail;
	     ENDLOOP;
       t↑ ← vj↑};
    ENDCASE => P.RErr["Right operand of ,,  is  a non-list"];
    ENDCASE => P.RErr["Left operand of ,,  is  a non-list"];
      };

EmptyString: PROC[n: Node] RETURNS[BOOLEAN] = {
      RETURN[WITH n SELECT FROM
		s: rSTR => S.Empty[s.str],
                ENDCASE => FALSE];
      };
      
ErrorProcess: PROC[mess, est: ROPE, env: Environment, x,lop,rop: Node] RETURNS [proceed: BOOLEAN] =
      { m: Node ← NIL;
      P.OS.Put[rope[mess], rope[": "], rope[est]];
      P.OS.Put[rope[", in"], char[CR]];
      Disp.Print[x];
      Left.v ← lop;
      Right.v ← rop;
      DO
	   m ← Route.KeyRoutine[SN[">"]];
	   WITH m SELECT FROM
             f: rFAIL => P.EndDisplay;
	     s: rSTR => IF EmptyString[m] THEN RETURN[FALSE]
	               ELSE IF EQ[s.str,"pr"] THEN RETURN[TRUE]
	               ELSE
		        { 
		         m← P.Dist[s.str !
		         P.SErr => LOOP];
		         m ← Eval[m, env];
		         Disp.Print[m];
		         };
             ENDCASE=>ERROR;
	   ENDLOOP;
      };

Eval: PUBLIC PROC[x: Node, env: Environment]
      RETURNS [ans: Node] = {
lop: Node ← Nail;
rop: Node ← Nail;
IF x.e THEN RETURN [x];
{ ENABLE {
	 P.RErr =>
              IF ErrorProcess["Run-time error", est, env,x, lop,rop] THEN RETRY;
	 P.PBug =>
              IF ErrorProcess["Poplar Bug!!", est, env,x, lop,rop] THEN RETRY;
	 P.Interrupt => IF ErrorProcess["***Interrupt***", "", env,x, lop,rop]
		 THEN RESUME
		 };
CK;
DO
    B: PROC[lop, rop: Node]  RETURNS [loop: BOOLEAN]=
     {loop ← FALSE;
      SELECT x.Type  FROM
      PLUS=>
          { P: PROC[i,j: ROPE] RETURNS[ROPE] =
		        {RETURN[S.MakeNUM[S.MakeInteger[i]+S.MakeInteger[j]]]};
		    ans ← Binary[lop, rop, P];
            };
     MINUS=>
          { M: PROC[i,j: ROPE] RETURNS[ROPE] =
		        {RETURN[S.MakeNUM[S.MakeInteger[i]-S.MakeInteger[j]]]};
		    ans ← Binary[lop, rop, M];
            };
      SEQ => {
	   from: LONG INTEGER = S.MakeInteger[BlessString[lop]];
	   to: LONG INTEGER ← S.MakeInteger[BlessString[rop]];
	   inc: LONG INTEGER = IF from>to THEN 1 ELSE -1;
	   tl: LSTNode ←
              N.NEW[ListNodeRecord ←
			[TRUE, LST[SN[S.MakeNUM[to]], Nail]]];
	   UNTIL from=to
		 DO
		 to ← to+inc;
		 tl ← N.NEW[ListNodeRecord ←
			[TRUE,LST[SN[S.MakeNUM[to]], tl]]];
		 ENDLOOP;
	   ans ← tl;
	   };
     PAPPLY => {
      WITH rop SELECT FROM r: rCLOSURE => 
      WITH r.exp SELECT FROM f: rFCN =>
	   {env ← ExtendEnv[r.env, f.parms, lop];
	   x ← f.fcn;
	   loop ← TRUE}; -- tail recursion
      ENDCASE => ans ← Apply[lop,rop];
      ENDCASE => ans ← Apply[lop,rop];
      };
    MAPPLY =>  
	   WITH lop SELECT FROM il: rLST =>
	   {lx: LSTNode ← NewNail[];
	    tl: LSTNode ← il;
             ans ← lx;
             FOR tl ← tl, tl.listtail UNTIL tl.listhead=NIL
		 DO
		 t: Node = Apply[tl.listhead, rop];
		 IF t.Type#FAIL THEN
		       {lx↑ ← [TRUE, LST[t, NewNail[]]];
		        lx← lx.listtail};
		 ENDLOOP};
             ENDCASE => P.RErr["Non-list before //"];
   GOBBLE => 
	   WITH lop  SELECT FROM lx: rLST =>
              { GMap: PROC[x: Node] =
		 {ans ← N.NEW[NodeRecord
			← [,LST[ans,
	                             N.NEW[ListNodeRecord
				← [,LST[x, Nail]]]]]];
		 ans ← Apply[ans, rop];
		 };
                IF lx.listhead=NIL THEN P.RErr["[] before ///"];
	       ans ← lx.listhead;
	       Map[lx.listtail, GMap];
		 };
             ENDCASE =>  P.RErr["Non-list before ///"];
   ITER => DO
	 ans ← lop;
	 lop ← Apply[lop, rop];
	 IF lop.Type=FAIL THEN EXIT;
	 ENDLOOP;
   CAT => ans ←  IF EmptyString[lop] THEN rop
		 ELSE IF EmptyString[rop] THEN lop
		 ELSE Binary[lop, rop, Rope.Concat];
   CATL => ans ← ConcL[lop, rop];
   EQUAL => { -- this will occur only in checking mode
		 IF ~VEqual[lop,rop] THEN P.RErr["Equality Check"];
		 ans ← lop;
		 };
   ENDCASE}; -- End of B
WITH x SELECT FROM
n: rID => ans ← IF n.name.t=VAL THEN Look[n.name,env] ELSE x;
n: rHOLE => ans ←x;
n: rFAIL => ans ←x;
n: rSTR => ans ←x;
n: rWILD => ans ←x;
n: rPATTERN => ans ← N.NEW[NodeRecord ←[,CLOSURE[x,env]]];
n: rFCN =>
      {
      WITH n.parms SELECT FROM ne: rEQUAL =>
	   {
	   ElimEq: PROC[n: Node] RETURNS [a: Node] = {
		a ← n;
		IF n = NIL THEN RETURN;
		WITH n SELECT  FROM
			x: rEQUAL => RETURN[ElimEq[x.left]];
			x: rFAIL => NULL;
			x: rID => NULL;
			x: rWILD => NULL;
			x: rHOLE => NULL;
			x: rUNDEFINED => NULL;
			x: rSTR => NULL;
			x: rCOMB => x.parm ← ElimEq[x.parm];
			x: rASS => x.rhs ← ElimEq[x.rhs];
			x: rLST => {IF x.listhead=NIL THEN RETURN;
				x.listhead ← ElimEq[x.listhead];
				x.listtail ← BlessLST[ElimEq[x.listtail]]};
			x: rSEQOF => x.pat ← ElimEq[x.pat];
			x: rSEQOFC => x.pat ← ElimEq[x.pat];
			x: rOPT => x.pat ← ElimEq[x.pat];
			x: rDELETE => x.pat ← ElimEq[x.pat];
		    x: rCAT=> {x.left ← ElimEq[x.left]; x.right ← ElimEq[x.right]};
			x: rCATL=> {x.left ← ElimEq[x.left]; x.right ← ElimEq[x.right]};
			x: rGTR=>  {x.left ← ElimEq[x.left]; x.right ← ElimEq[x.right]};
			x: rPALT=>  {x.left ← ElimEq[x.left]; x.right ← ElimEq[x.right]};
			x: rPAPPLY=> {x.left ← ElimEq[x.left]; x.right ← ElimEq[x.right]};
			x: rMAPPLY=> {x.left ← ElimEq[x.left]; x.right ← ElimEq[x.right]};
			x: rGOBBLE=> {x.left ← ElimEq[x.left]; x.right ← ElimEq[x.right]};
			x: rITER=> {x.left ← ElimEq[x.left]; x.right ← ElimEq[x.right]};
			x: rPROG=> {x.left ← ElimEq[x.left]; x.right ← ElimEq[x.right]};
			x: rSEQ=> {x.left ← ElimEq[x.left]; x.right ← ElimEq[x.right]};
			x: rPLUS=> {x.left ← ElimEq[x.left]; x.right ← ElimEq[x.right]};
			x: rMINUS=> {x.left ← ElimEq[x.left]; x.right ← ElimEq[x.right]};
			x: rEQUAL=> {x.left ← ElimEq[x.left]; x.right ← ElimEq[x.right]};
			x: rTILDE => x.not ← ElimEq[x.not];
			x: rPATTERN => x.pattern ← ElimEq[x.pattern];
			x: rFCN=> {x.parms ← ElimEq[x.parms]; x.fcn ← ElimEq[x.fcn]};
			ENDCASE => P.PBug["Unknown variant"];
		};
	   [] ← Eval[n.fcn, ExtendEnv[env, ne.left, ne.right]];
	   [] ← ElimEq[x];
	   };
         ENDCASE;
      ans ← N.NEW[NodeRecord ← [,CLOSURE[x,env]]];
      };
n: rCOMB => ans ← N.NEW[NodeRecord ← [,COMB[n.proc,Eval[n.parm,env]]]];
n: rCAT => IF B[Eval[n.left,env], Eval[n.right,env]] THEN LOOP;
n: rCATL => IF B[Eval[n.left,env], Eval[n.right,env]] THEN LOOP;
n: rEQUAL => IF B[Eval[n.left,env], Eval[n.right,env]] THEN LOOP;
n: rPAPPLY => IF B[Eval[n.left,env], Eval[n.right,env]] THEN LOOP;
n: rMAPPLY => IF B[Eval[n.left,env], Eval[n.right,env]] THEN LOOP;
n: rGOBBLE => IF B[Eval[n.left,env], Eval[n.right,env]] THEN LOOP;
n: rITER => IF B[Eval[n.left,env], Eval[n.right,env]] THEN LOOP;
n: rPLUS => IF B[Eval[n.left,env], Eval[n.right,env]] THEN LOOP;
n: rMINUS => IF B[Eval[n.left,env], Eval[n.right,env]] THEN LOOP;
n: rSEQ => IF B[Eval[n.left,env], Eval[n.right,env]] THEN LOOP;
n: rASS => {
      e: Environment ← env;
      ans ← Eval[n.rhs, env];
       DO
	   IF e=NIL THEN
		 WITH n.lhs SELECT FROM
			i: rVAL => { i.v ← ans; EXIT };
			ENDCASE => P.RErr["Assignment to built-in primitive"];
                        -- assume global
	   IF e.name=n.lhs THEN { e.val ← ans; EXIT };
	   e ← e.next;
	   ENDLOOP;
      };
n: rTILDE =>
      { 
      ans ← Eval[n.not,env];
      ans ← IF ans.Type=FAIL THEN MTSt ELSE Fail;
      };
n: rPROG => {
      [] ← Eval[n.left,env];
      x ← n.right;
      LOOP;
      };
n: rGTR => {
      ans ← Eval[n.left,env];
      IF ans.Type#FAIL THEN { x ← n.right; LOOP };
      };
n: rPALT => {
      ans ← Eval[n.left,env];
      IF ans.Type=FAIL THEN { x ← n.right; LOOP };
      };
n: rLST => IF n.listhead=NIL THEN ans ← x
      ELSE ans ← N.NEW[NodeRecord ← [,LST[Eval[n.listhead, env],
                                      BlessLST[Eval[n.listtail, env]]]]];
ENDCASE => P.PBug["Unknown type"];
ans.e ← TRUE;
RETURN;
ENDLOOP;
};
};

EQ: PUBLIC PROC[x, y: ROPE] RETURNS[BOOLEAN] =
	{RETURN[Equal[x,y]]};

VEqual: PUBLIC PROC[lx,rx: Node] RETURNS [res: BOOLEAN] =
      {
      DO
	   WITH lx SELECT FROM
             ll: rLST => WITH rx SELECT FROM
                    rl: rLST =>
		    IF ll.listhead=NIL THEN res ← rl.listhead=NIL
		    ELSE IF rl.listhead=NIL THEN res ← FALSE
		    ELSE IF ~VEqual[ll.listhead, rl.listhead] THEN
		       res ← FALSE
		    ELSE {
		          lx ← ll.listtail;
		          rx ← rl.listtail;
		          LOOP;
		          };
                    ENDCASE => res ← FALSE;
	    ll: rSTR =>
               WITH rx SELECT FROM
                 rs: rSTR =>
		 {a,b: Stream;
		 a ← S.NewStream[ll.str];
		 b ← S.NewStream[rs.str];
		 DO
		       IF S.EmptyS[a] THEN {res ← S.EmptyS[b]; EXIT};
		       IF S.EmptyS[b] THEN {res ← FALSE; EXIT};
		       {ac,bc: CHARACTER;
			[ac,a] ← S.Item[a];
			[bc, b] ← S.Item[b];
                        IF ac=bc THEN LOOP};
		       res ← FALSE; EXIT;
		       ENDLOOP;
		 };
	     ENDCASE => P.RErr["Illegal equality check"];
	   ENDCASE => P.RErr["Illegal equality check"];
	   EXIT;
	   ENDLOOP;
      };
      
ExtendEnv: PROC[e: Environment, bv,val: Node] RETURNS [nev: Environment] =
      {
      nev ← e;
      WITH bv SELECT FROM
         bvx: rID =>
	   nev ← N.NEW[ERecord ← [bvx.name, val, nev]];
         bvx: rLST => 
	    WITH val SELECT FROM
                vl1: rLST => {bl: LSTNode ← bvx;
                        vl: LSTNode ← vl1;
		    DO
                    IF bl.listhead=NIL AND vl.listhead=NIL
                          THEN EXIT;
                    IF bl.listhead=NIL THEN
                            P.RErr["Too many parameters for function"];
                    IF vl.listhead=NIL THEN
                            P.RErr["Too few parameters for function"];
		    nev ← ExtendEnv[nev, bl.listhead, vl.listhead]; 
		    vl ← vl.listtail;
		    bl ← bl.listtail;
		    ENDLOOP};
                ENDCASE => P.RErr["Non-list before /"];
      ENDCASE => P.RErr["Illegal BV"]};

LengthList: PUBLIC PROC[n: LSTNode] RETURNS[i:CARDINAL] = {
i ←  0;
WHILE n.listhead ~= NIL DO
      i ← i + 1;
      n ← n.listtail;
      ENDLOOP;
};

Look: PROC[n: Symbol, e: Environment] RETURNS[Node] = {
FOR e ← e, e.next UNTIL e=NIL DO
      IF e.name=n THEN RETURN[e.val]
      ENDLOOP;
WITH n SELECT FROM
	v: rVAL => IF v.v#NIL THEN RETURN[v.v];
	ENDCASE;
ERROR P.RErr["Undefined variable"]
};

Map: PUBLIC PROC[l: LSTNode, p: PROC[Node]] =
      {
      FOR l ← l, l.listtail UNTIL l.listhead=NIL
	   DO IF l.Type#LST THEN ERROR P.PBug["Malformed List"];
      p[l.listhead]; ENDLOOP;
      };

MapList: PUBLIC PROC[l: LSTNode, p: PROC[Node] RETURNS [Node]] RETURNS [ans: LSTNode] =
      {
      tans: LSTNode ← NewNail[];
      ans ← tans;
      UNTIL l.listhead=NIL
	   DO
	   tans↑ ← [,LST[p[l.listhead], NewNail[]]];
	   tans ← tans.listtail;
	   l ← l.listtail;
	   ENDLOOP;
      };

Match: PROC
       [subject: ROPE, pattern: Node, env: Environment]
       RETURNS [struc: Node] = {
s: Stream;
HolePassed: SIGNAL [holeString: rSTR] = CODE;

M2: PROC[xp: Node, discarding: BOOLEAN,
			env: Environment] RETURNS [ans: Node] =
      -- The state of s is an implict input and output of M2
{
BinaryMatch: PROC[left, right: Node]
		RETURNS [lans: Node, rans:Node] =
             {
	         holeNode: rSTR ← NIL;
		lans ← M2[left, discarding, env!
				  HolePassed => {
					      holeNode ← holeString;
					      RESUME
					      }];
		IF lans.Type # FAIL THEN
		     {IF holeNode=NIL THEN
			rans ← M2[right, discarding, env]
		     ELSE { -- unanchored match allowed
			  i: CARDINAL ← 0;
			  s1: ROPE ← S.ConvertStream[s];
			  s2: Stream ← S.CopyStream[s];
				DO
				rans ← M2[right, discarding, env];
				IF rans.Type#FAIL THEN
				      {IF discarding THEN EXIT;
				      holeNode↑ ←
					SN[S.SubString[s1,0,i]]↑;
				      lans←Eval[lans,env];
				      EXIT};
				IF S.EmptyS[s2] THEN EXIT;
				[, s2] ← S.Item[s2];
				s ← S.CopyStream[s2];
				i ← i+1;
				ENDLOOP;
			  };
			  };
		     };
{ ENABLE {
	 P.RErr =>
              IF ErrorProcess["Run-time error", est,
			env, xp,SN[S.ConvertStream[s]],Nail] THEN RETRY;
	 P.PBug =>
              IF ErrorProcess["Poplar Bug!!", est,
			env, xp,SN[S.ConvertStream[s]],Nail] THEN RETRY;
	 P.Interrupt => IF ErrorProcess["***Interrupt***", "",
			env, xp,SN[S.ConvertStream[s]],Nail]
		 THEN RESUME
		 };
CK;
ans ←  NIL;
   DO
   WITH xp SELECT FROM
	p: rPATTERN => xp ← p.pattern;
	p: rID => IF p.name.t=VAL THEN xp ← Look[p.name, env]
		ELSE EXIT;
	p: rCLOSURE =>
		 { env ← p.env; xp ← p.exp }
	ENDCASE => EXIT;
  ENDLOOP;
  WITH xp SELECT FROM
    p: rID => {WITH p.name SELECT FROM t: rPFUNC=>
		 {[ans, s] ← t.p[s];
		 ans.e ← TRUE};
		ENDCASE => P.RErr["Primitive out of context"]
		 };
    p: rCOMB => {WITH p.proc SELECT FROM t: rPFUNC1=>
		 {ans ← Eval[p.parm, env];
		 [ans, s] ← t.p[s, ans];
		 ans.e ← TRUE};
		ENDCASE => P.RErr["Primitive out of context"]
		 };
     p: rGTR=> {
		       t: Node ← M2[p.left, TRUE, env];
		       IF t.Type= FAIL THEN ans ← Fail
			   ELSE IF ~discarding THEN
			     {ans ← N.NEW[NodeRecord ← [,GTR[t, p.right]]];
			       IF t.e THEN ans ← Eval[ans,env]}
			   ELSE ans ← MTSt;
		       };
     p: rPAPPLY => {
		       t: Node ← M2[p.left, discarding, env];
		       IF t.Type= FAIL THEN ans ← Fail
			   ELSE IF ~discarding THEN
			     {ans ← N.NEW[NodeRecord ← [,PAPPLY[t, p.right]]];
			     IF t.e THEN ans ← Eval[ans,env];
			     }
			   ELSE ans ← MTSt;
		       };
     p: rMAPPLY => {
		       t: Node ← M2[p.left, discarding, env];
		       IF t.Type= FAIL THEN ans ← Fail
			   ELSE IF ~discarding THEN
			     {ans ← N.NEW[NodeRecord ← [,MAPPLY[t, p.right]]];
			     IF t.e THEN ans ← Eval[ans,env];
			     }
			  ELSE ans ← MTSt;
		       };
     p: rGOBBLE => {
		       t: Node ← M2[p.left, discarding, env];
		       IF t.Type= FAIL THEN ans ← Fail
			   ELSE IF ~discarding THEN
			     {ans ← N.NEW[NodeRecord ← [,GOBBLE[t, p.right]]];
			     IF t.e THEN ans ← Eval[ans,env];
			     }
			   ELSE ans ← MTSt;
		       };
     p: rITER => {
		       t: Node ← M2[p.left, discarding, env];
		       IF t.Type= FAIL THEN ans ← Fail
			   ELSE IF ~discarding THEN
			     {ans ← N.NEW[NodeRecord ← [,ITER[t, p.right]]];
			     IF t.e THEN ans ← Eval[ans,env];
			     }
			   ELSE ans ← MTSt;
		       };
      p: rTILDE => {
		       s1: Stream ← S.CopyStream[s];
		       ans ← M2[p.not, TRUE, env];
		       s ← s1;
		       ans ← IF ans.Type=FAIL THEN MTSt
				ELSE Fail;
		       };
      p: rDELETE => {
		       ans ← M2[p.pat, TRUE, env];
		       IF ans.Type#FAIL THEN ans ← MTSt;
		       };
      p: rSTR => {
		s1: ROPE ← S.ConvertStream[s];
		l1, i: LONG INTEGER ← 0;
		l1 ← Rope.Length[p.str];
		UNTIL i=l1 DO
		     IF S.EmptyS[s] THEN  { ans ← Fail; EXIT };
		     {c: CHARACTER;
			[c,s] ← S.Item[s];
			IF c#S.Sub[p.str, i]
			THEN  { ans ← Fail; EXIT }};
		     i ← i+1;
		     REPEAT
		     FINISHED =>
			  IF discarding THEN ans ← MTSt
			  ELSE {ans ← SN[S.SubString[s1,0,i]];
				ans.e ← TRUE};
		     ENDLOOP;
		       };
    p: rWILD => {
		       IF S.EmptyS[s] THEN ans ← Fail
		       ELSE {
			     IF discarding THEN ans ← MTSt
			     ELSE {ans ← SN[S.SubStringStream[s,0,1]];
				  ans.e ← TRUE;
				  };
			     [,s] ← S.Item[s];
			     };
		       };
    p: rHOLE => { h: rSTR = N.NEW[STR NodeRecord ← MTSt↑];
	            ans ← h;
				-- empty string, to be overwritten
		       ans.e ← FALSE;
		       SIGNAL HolePassed[h];
		       };
    p: rLST => {
	       IF p.listhead=NIL THEN ans ← xp
	       ELSE IF p.listtail.listhead=NIL
			THEN { -- not really binary
			     ans ← M2[p.listhead, discarding, env];
			     IF ans.Type#FAIL AND ~discarding
				THEN
				  {ans ← N.NEW[NodeRecord
					← [ans.e,
						LST[ans,Nail]]];
				  }}
	     ELSE {lans, rans: Node;
		  [lans, rans] ← BinaryMatch[p.listhead, p.listtail];
		   IF lans.Type=FAIL OR rans.Type=FAIL
				THEN ans ← Fail
		   ELSE IF discarding THEN ans ← MTSt
		   ELSE ans ← N.NEW[NodeRecord
				←[lans.e AND rans.e,
				 LST[lans,BlessLST[rans]]]]}};

   p: rCAT=> {lans, rans: Node;
		  [lans, rans] ← BinaryMatch[p.left, p.right];
		   IF lans.Type=FAIL OR rans.Type=FAIL
				THEN ans ← Fail
		   ELSE IF discarding THEN ans ← MTSt
		   ELSE { ans ← N.NEW[NodeRecord ← [,CAT[lans, rans]]];
			         IF lans.e AND rans.e THEN ans←Eval[ans,env]};
	       };
   p: rPLUS=> {lans, rans: Node;
		  [lans, rans] ← BinaryMatch[p.left, p.right];
		   IF lans.Type=FAIL OR rans.Type=FAIL
				THEN ans ← Fail
		   ELSE IF discarding THEN ans ← MTSt
		   ELSE { ans ← N.NEW[NodeRecord ← [,PLUS[lans, rans]]];
			         IF lans.e AND rans.e THEN ans←Eval[ans,env]};
	       };
   p: rMINUS => {lans, rans: Node;
		  [lans, rans] ← BinaryMatch[p.left, p.right];
		   IF lans.Type=FAIL OR rans.Type=FAIL
				THEN ans ← Fail
		   ELSE IF discarding THEN ans ← MTSt
		   ELSE { ans ← N.NEW[NodeRecord ← [,MINUS[lans, rans]]];
			         IF lans.e AND rans.e THEN ans←Eval[ans,env]};
	       };
   p: rCATL => {lans, rans: Node;
		  [lans, rans] ← BinaryMatch[p.left, p.right];
		   IF lans.Type=FAIL OR rans.Type=FAIL
				THEN ans ← Fail
		   ELSE IF discarding THEN ans ← MTSt
		   ELSE { ans ← N.NEW[NodeRecord ← [,CATL[lans, rans]]];
			         IF lans.e AND rans.e THEN ans←Eval[ans,env]};
	       };

   p: rSEQOF => { 
		ans ← Fail;
		DO
		s1: Stream = S.CopyStream[s];
		t: Node = M2[p.pat, discarding, env !
			HolePassed => { holeString.e ← TRUE;
					RESUME }];
				-- The user probably didn't mean to
				-- put "..." at the end of a seq, but
				-- what can we do?
		IF t.Type=FAIL THEN { s ← s1; EXIT };
		IF discarding THEN ans ← MTSt
		ELSE IF ans.Type=FAIL THEN ans ← t
		ELSE ans ← Binary[ans, t, Rope.Concat];
		ENDLOOP;
		       };

   p: rSEQOFC => { 
	tans: LSTNode ← NewNail[];
	tans1: LSTNode = tans;
	ans ← tans;
		DO
		s1: Stream = S.CopyStream[s];
		t: Node = M2[p.pat, discarding, env !
			HolePassed =>
			{ holeString.e ← TRUE; RESUME }];
		IF t.Type=FAIL THEN { s ← s1; EXIT };
		IF discarding THEN ans ← MTSt
		ELSE {tans↑ ← [TRUE,
				LST[t,NewNail[]]];
			tans ← tans.listtail};
		ENDLOOP;
	IF tans1.listhead=NIL THEN ans ← Fail;
	};

   p: rOPT => { --  (optional part) has single pattern as part
	s1: Stream ← S.CopyStream[s];
	ans ← M2[p.pat, discarding, env];
	IF ans.Type=FAIL THEN { ans ← MTSt; s ← s1 };
		       };
	   
  p: rPALT => { 
	s1: Stream ← S.CopyStream[s];
	ans ← M2[p.left, discarding, env];
	IF ans.Type=FAIL THEN
		{ s ← s1; ans ← M2[p.right, discarding, env] };
	};
  p: rFAIL => ans ← Fail;
  ENDCASE => P.RErr["Unknown pattern type"];
      }};

holeNode: rSTR ← NIL;
struc ← NIL;
s ← S.NewStream[subject];
struc ← M2[pattern, FALSE, env !
		HolePassed => { holeNode ← holeString; RESUME }];
IF holeNode#NIL THEN
      {
      holeNode↑ ← SN[S.ConvertStream[s]]↑;
      struc ← Eval[struc,env];
      }
ELSE IF ~S.EmptyS[s] THEN struc ← Fail;
struc.e ← TRUE;
};

NewNail: PUBLIC PROC RETURNS [LSTNode] = 
  {RETURN[N.NEW[ListNodeRecord ← [TRUE,LST[NIL,NIL]]]]};

SN: PUBLIC PROC [s: ROPE] RETURNS [rSTR] = 
  {RETURN[N.NEW[STR NodeRecord ← [TRUE,STR[s]]]]};

BlessVAL: PROC[x: P.Symbol] RETURNS [REF VAL P.SymbolRecord] =
   {WITH x SELECT FROM
       r: rVAL => RETURN[r];
       ENDCASE => ERROR;
    };
 
[Fail,MTSt,Nail] ← P.GetSpecialNodes[];

Left ← BlessVAL[P.Insert["left", [,,VAL[NIL]]]];
Right ← BlessVAL[P.Insert["right", [,,VAL[NIL]]]];

}.