<> <> <> <> <> <<>> DIRECTORY Convert, FileIO, GPM, IO, SafeStorage, Rope; GPMImpl: CEDAR MONITOR IMPORTS Convert, FileIO, IO, Rope, SafeStorage EXPORTS GPM = BEGIN OPEN GPM; Open: PUBLIC PROC [stream: IO.Handle] RETURNS [self: Handle] = { self _ zone.NEW[Object]; self.st _ GetStack[]; self.output _ GetText[]; self.outputChar _ endOfInput; self.stream _ stream; InitStack[self] }; Close: PUBLIC PROC [self: Handle] = { IF self.stream # NIL THEN IO.Close[self.stream]; self.stream _ NIL; FreeText[self.output]; self.output _ NIL; FreeStack[self.st]; self.st _ NIL }; zone: ZONE _ SafeStorage.NewZone[]; stk1, stk2: REF Stack; -- minicache GetStack: ENTRY PROC RETURNS [stk: REF Stack] = { IF stk1 # NIL THEN { stk _ stk1; stk1 _ NIL } ELSE IF stk2 # NIL THEN { stk _ stk2; stk2 _ NIL } ELSE stk _ zone.NEW[Stack[10000]] }; FreeStack: ENTRY PROC [stk: REF Stack] = { IF stk = NIL THEN RETURN; IF stk1 = NIL THEN stk1 _ stk ELSE IF stk2 = NIL THEN stk2 _ stk }; txt1, txt2: REF TEXT; -- minicache GetText: ENTRY PROC RETURNS [txt: REF TEXT] = { IF txt1 # NIL THEN { txt _ txt1; txt1 _ NIL } ELSE IF txt2 # NIL THEN { txt _ txt2; txt2 _ NIL } ELSE txt _ NEW[TEXT[100]] }; FreeText: ENTRY PROC [txt: REF TEXT] = { IF txt = NIL THEN RETURN; IF txt1 = NIL THEN txt1 _ txt ELSE IF txt2 = NIL THEN txt2 _ txt }; GetIndex: PUBLIC PROC [self: Handle] RETURNS [i: LONG INTEGER] = { RETURN [IO.GetIndex[self.stream]] }; DumpToStream: PUBLIC PROC [output: IO.Handle, input: Handle] = { DO IO.PutChar[output,GetChar[input ! Error => IF ec = EndOfStream THEN EXIT]]; ENDLOOP; input.stream _ NIL; Close[input] }; Error: PUBLIC ERROR [ec: ErrorCode, errorMsg: Rope.ROPE] = CODE; <> endOfInput: CHARACTER = 0C; Load: PROC [self: Handle] = INLINE { OPEN self; IF h=0 THEN { IF output.length = 0 AND outputChar = endOfInput THEN { outputChar _ a; RETURN }; IF output.length = output.maxLength THEN { new: REF TEXT _ NEW[TEXT[output.maxLength*2]]; new.length _ output.length; FOR i:NAT IN [0..output.length) DO new[i] _ output[i]; ENDLOOP; output _ new }; output[output.length] _ a; output.length _ output.length+1 } ELSE { st[s] _ char[a]; s _ s+1 }}; NextCh: PROC [self: Handle] = INLINE { OPEN IO, self; IF c=0 THEN { a _ GetChar[stream ! EndOfStream => { a _ endOfInput; CONTINUE }]; inputloc _ inputloc+1 } ELSE { a _ ToChar[st[c]]; c _ c+1 }}; Find: PROC [self: Handle, x: Index] = { OPEN self; <> <> ptr: Index _ e; -- start of definitions w _ x; -- index of name length DO -- search for macro name definition match: BOOLEAN _ TRUE; len: Index _ ToLength[st[w]]; FOR r:Index IN [0..len) DO IF st[w+r]#st[ptr+r+1] THEN { match _ FALSE; EXIT }; ENDLOOP; IF match THEN { w _ ptr+1+len; RETURN }; ptr _ ToIndex[st[ptr]]; -- next entry IF ptr = maxIndex THEN EXIT; -- end of chain ENDLOOP; <> Error[MacroError,Rope.Concat["Undefined name: ",Rope.Concat[Item[self,w],AtLoc[self]]]] }; Item: PROC [self: Handle, x: Index] RETURNS [rope: Rope.ROPE] = { OPEN self; <> <> <> proc: SAFE PROC RETURNS [CHARACTER] = { k _ k+1; RETURN [ToChar[st[x+k]]] }; k: Index _ 0; stklen: Index _ ToLength[st[x]]; incomplete: BOOLEAN _ stklen=0; len: Index _ IF incomplete THEN s-x-1 ELSE stklen-1; rope _ Rope.FromProc[len, proc]; IF incomplete THEN rope _ Rope.Concat[rope,"...(Incomplete)"] }; AtLoc: PROC [self: Handle] RETURNS [rope: Rope.ROPE] = { RETURN [Rope.Concat[" at ",Convert.ValueToRope[[unsigned[IO.GetIndex[self.stream]-1]]]]] }; EndFn: PROC [self: Handle] = { -- remove call and arg list; copy back result if any y: Index; len: Index; IF self.f > self.p THEN { -- encountered terminator in bad place msg: Rope.ROPE; IF self.c=0 THEN ERROR; -- found terminator in input source msg _ Rope.Concat["Terminator in argument list for ",Item[self,self.f+2]]; msg _ Rope.Concat[msg,". Probably due to a semicolon missing from the definition of "]; msg _ Rope.Concat[msg,Item[self,self.p+2]]; Error[MacroError,msg] }; <> <> y _ self.s; self.st[self.s] _ index[self.e]; len _ ToLength[self.st[self.p-1]]; -- arg list stuff to be removed WHILE ToIndex[self.st[y]] >= self.p-1+len DO -- take care of defs in results next: Index _ ToIndex[self.st[y]]; self.st[y] _ index[next-len]; y _ next; ENDLOOP; self.w _ ToIndex[self.st[y]]; WHILE self.w > self.p-1 DO self.w _ ToIndex[self.st[self.w]]; ENDLOOP; -- skip over defs in args self.st[y] _ index[self.w]; self.e _ ToIndex[self.st[self.s]]; IF self.h # 0 THEN IF self.h > self.p THEN self.h _ self.h-len ELSE self.st[self.h] _ length[ToLength[self.st[self.h]]-len]; self.c _ ToIndex[self.st[self.p+1]]; self.s _ self.s-len; y _ self.p-1; -- dest for results self.w _ self.p-1+len; -- source of results self.p _ ToIndex[self.st[self.p]]; UNTIL y=self.s DO self.st[y] _ self.st[self.w]; y _ y+1; self.w _ self.w+1; ENDLOOP }; outputloc, inputloc: INT _ 0; -- for debugging GetChar: PUBLIC PROC [self: Handle] RETURNS [char: CHARACTER] = { OPEN self; DO -- main loop flag: BOOLEAN _ FALSE; IF st = NIL THEN Error[EndOfStream, "Stream already closed"]; IF outputChar # endOfInput THEN { char _ outputChar; outputChar _ endOfInput; outputloc _ outputloc+1; RETURN }; IF output.length > outputPtr THEN { char _ output[outputPtr]; IF (outputPtr _ outputPtr+1) = output.length THEN output.length _ outputPtr _ 0; outputloc _ outputloc+1; RETURN }; NextCh[self]; SELECT a FROM startQuote => { q _ q+1; flag _ TRUE }; singleQuote => { NextCh[self]; Load[self]; LOOP }; startCall => { -- Fn; start new function call st[s] _ index[h]; st[s+1] _ index[f]; st[s+2] _ index[0]; st[s+3] _ length[0]; h _ s+3; f _ s+1; s _ s+4; LOOP }; sepArg => IF h=0 THEN NULL ELSE { -- NextItem; collect next arg st[h] _ length[s-h-ToLength[st[h]]]; st[s] _ length[0]; h _ s; s _ s+1; LOOP }; endCall => IF h=0 THEN NULL ELSE { -- Apply; oldstf1: Index _ ToIndex[st[f-1]]; oldstf: Index _ ToIndex[st[f]]; st[h] _ length[s-h]; st[s] _ mark; st[f-1] _ length[s-f+2]; st[f] _ index[p]; st[f+1] _ index[c]; p _ f; h _ oldstf1; f _ oldstf; s _ s+1; IF h # 0 THEN -- increase count of extra stuff st[h] _ length[ToLength[st[h]]+ToLength[st[p-1]]]; Find[self,p+2]; IF IsBuiltIn[st[w]] THEN { SELECT ToBuiltIn[st[w]] FROM DEF => { -- add a new definition IF h # 0 THEN st[h] _ length[ToLength[st[h]]-ToLength[st[p-1]]+6]; st[p-1] _ length[6]; st[p+5] _ index[e]; e _ p+5 }; VAL => { -- get value of name Find[self,p+6]; UNTIL st[w+1]=mark DO a _ ToChar[st[w+1]]; w _ w+1; Load[self]; ENDLOOP }; UPDATE => { -- change value of name y: Index; Find[self,p+9]; y _ p+9+ToLength[st[p+9]]; IF ToLength[st[y]] > ToLength[st[w]] THEN Error[MacroError,Rope.Concat["UPDATE arg too long for ", Rope.Concat[Item[self,p+9],AtLoc[self]]]]; FOR r:NAT IN [1..ToLength[st[y]]] DO st[w+r] _ st[y+r]; ENDLOOP }; ENDCASE => ERROR; EndFn[self]; LOOP }; c _ w+1; LOOP }; numArg => IF p=0 THEN NULL ELSE { -- LoadArg; NextCh[self]; -- read arg number w _ p+2; IF a NOT IN ['0..'9] THEN Error[MacroError,Rope.Concat["Bad arg number in definition of ",Item[self,p+2]]]; FOR r:NAT IN [0..a-'0) DO w _ w+ToLength[st[w]]; IF st[w]=mark THEN EXIT; -- act like have null arg ENDLOOP; IF st[w]=mark THEN LOOP; FOR r:NAT IN [1..ToLength[st[w]]) DO a _ ToChar[st[w+r]]; Load[self]; ENDLOOP; LOOP }; marker => { EndFn[self]; LOOP }; endOfInput => IF h # 0 THEN Error[MacroError,"End of input encountered inside macro call"] ELSE Error[EndOfStream,"EndOfStream"]; ENDCASE; DO -- subloop for copying literals IF flag THEN flag _ FALSE ELSE { Load[self]; IF q = 1 THEN EXIT }; NextCh[self]; SELECT a FROM startQuote => { q _ q+1; LOOP }; endOfInput => Error[MacroError,"End of input encountered inside macro quoted string"]; endQuote => NULL; ENDCASE => LOOP; q _ q-1; IF q = 1 THEN EXIT; ENDLOOP; ENDLOOP }; ToIndex: PROC [e: Entry] RETURNS [i: Index] = INLINE { RETURN [LOOPHOLE[e]] }; <> < RETURN [x.index];>> < ERROR };>> ToLength: PROC [e: Entry] RETURNS [i: Index] = INLINE { RETURN [LOOPHOLE[e]] }; <> < RETURN [x.length];>> < ERROR };>> ToChar: PROC [e: Entry] RETURNS [char: CHARACTER] = INLINE { RETURN [LOOPHOLE[e]] }; <> < RETURN [x.char];>> < ERROR };>> ToBuiltIn: PROC [e: Entry] RETURNS [name: BuiltIn] = INLINE { RETURN [LOOPHOLE[-e]] }; <> < RETURN [x.name];>> < ERROR };>> index: PROC [i: Index] RETURNS [Entry] = INLINE { RETURN [[LOOPHOLE[i,INTEGER]]] }; <> length: PROC [i: Index] RETURNS [Entry] = INLINE { RETURN [[LOOPHOLE[i,INTEGER]]] }; <> char: PROC [c: CHARACTER] RETURNS [Entry] = INLINE { RETURN [[LOOPHOLE[c,INTEGER]]] }; <> mark: Entry _ char[marker]; <<[char[marker]];>> marker: CHARACTER = 377C; builtin: PROC [name: BuiltIn] RETURNS [Entry] = INLINE { RETURN [[-name]] }; <> IsBuiltIn: PROC [e: Entry] RETURNS [BOOLEAN] = INLINE { RETURN [e < 0] }; InitStack: PROC [self: Handle] = { OPEN self; s _ 21; e _ 12; q _ 1; a _ endOfInput; FOR i:NAT IN [0..20] DO st[i] _ initst[i]; ENDLOOP }; initst: REF Stack _ zone.NEW[Stack[21]]; <> WriteStack: PROC [self: Handle] = { h: IO.Handle _ FileIO.Open["GPMStack.Txt", overwrite]; { ENABLE ANY => IO.Close[h]; WriteH[self, h]; WriteC[self, h]; WritePChain[self, h]; WriteFChain[self, h]; WriteEChain[self, h]; IO.Close[h] }}; WriteChar: PROC [i: Index, self: Handle, h: IO.Handle] = { OPEN IO; c: CHAR _ LOOPHOLE [MIN[self.st[i],128]]; Put[h, char[c]] }; WriteH: PROC [self: Handle, h: IO.Handle] = { OPEN IO; IF self.h = 0 THEN PutRope[h, "h = 0\n"] ELSE { PutRope[h, "\n\nIncomplete string on top of stack\n\n"]; FOR i: Index _ self.h + self.st[self.h] + 1--?--, i+1 UNTIL i >= self.s DO WriteChar[i, self, h]; ENDLOOP }}; WriteC: PROC [self: Handle, h: IO.Handle] = { OPEN IO; IF self.c = 0 THEN PutRope[h, "\n\nc = 0\n\n"] ELSE { PutRope[h, "\n\nString being scanned from stack\n\n"]; FOR i: Index _ self.c, i+1 UNTIL i >= self.s OR self.st[i] = mark DO WriteChar[i, self, h]; ENDLOOP }}; WritePChain: PROC [self: Handle, h: IO.Handle] = { OPEN IO; IF self.p = 0 THEN PutRope[h, "\n\np = 0\n\n"] ELSE { last: Index _ self.s; PutRope[h, "\n\nCalls entered but not yet completed\n\n"]; FOR i: Index _ self.p, self.st[i] UNTIL i >= last OR i=0 DO last _ i; WriteItem[self, h, i+2]; ENDLOOP }}; WriteFChain: PROC [self: Handle, h: IO.Handle] = { OPEN IO; IF self.f = 0 THEN PutRope[h, "\n\nf = 0\n\n"] ELSE { last: Index _ self.s; PutRope[h, "\n\nCalls started but not yet entered\n\n"]; FOR i: Index _ self.f, self.st[i] UNTIL i >= last OR i=0 DO last _ i; WriteItem[self, h, i+2]; ENDLOOP }}; WriteEChain: PROC [self: Handle, h: IO.Handle] = { OPEN IO; IF self.e = 0 THEN PutRope[h, "\n\ne = 0\n\n"] ELSE { last: Index _ self.s; PutRope[h, "\n\nCurrent definitions\n\n"]; FOR i: Index _ self.e, self.st[i] UNTIL i >= last OR i=0 DO last _ i; WriteItem[self, h, i+1]; ENDLOOP }}; WriteItem: PROC [self: Handle, h: IO.Handle, i: Index] = { OPEN IO; len: INTEGER _ self.st[i]; end: Index _ MIN[i+len, self.s]; Put[h, char['\t]]; FOR j: Index IN [i+1..end) DO WriteChar[j, self, h]; ENDLOOP; Put[h, char['\n]] }; <> initst[0] _ index[maxIndex]; initst[1] _ length[4]; initst[2] _ char['D]; initst[3] _ char['E]; initst[4] _ char['F]; initst[5] _ builtin[DEF]; initst[6] _ index[0]; initst[7] _ length[4]; initst[8] _ char['V]; initst[9] _ char['A]; initst[10] _ char['L]; initst[11] _ builtin[VAL]; initst[12] _ index[6]; initst[13] _ length[7]; initst[14] _ char['U]; initst[15] _ char['P]; initst[16] _ char['D]; initst[17] _ char['A]; initst[18] _ char['T]; initst[19] _ char['E]; initst[20] _ builtin[UPDATE]; FreeStack[GetStack[]]; FreeText[GetText[]]; END.