DIRECTORY FS USING [StreamOpen], GPM, IO, Rope; GPMImpl: CEDAR MONITOR IMPORTS FS, IO, Rope EXPORTS GPM = BEGIN OPEN GPM; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Open: PUBLIC PROC [stream: STREAM] RETURNS [self: Handle] = { self _ 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 }; 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 _ 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: STREAM, 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] = 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 self; IF c=0 THEN { a _ IO.GetChar[stream ! IO.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] = { 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] = { RETURN [Rope.Concat[" at ", IO.PutR[IO.int[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; 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 }; gpmVAL => { -- 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]] }; ToLength: PROC [e: Entry] RETURNS [i: Index] = INLINE { RETURN [LOOPHOLE[e]] }; ToChar: PROC [e: Entry] RETURNS [char: CHARACTER] = INLINE { RETURN [LOOPHOLE[e]] }; ToBuiltIn: PROC [e: Entry] RETURNS [name: BuiltIn] = INLINE { RETURN [LOOPHOLE[-e]] }; 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]; 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 _ NEW[Stack[21]]; WriteStack: PROC [self: Handle] = { h: STREAM _ FS.StreamOpen[fileName: "GPMStack.Txt", accessOptions: $create]; { 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: STREAM] = { OPEN IO; c: CHAR _ LOOPHOLE [MIN[self.st[i],128]]; Put[h, char[c]] }; WriteH: PROC [self: Handle, h: STREAM] = { 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: STREAM] = { 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: STREAM] = { 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: STREAM] = { 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: STREAM] = { 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: STREAM, 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[gpmVAL]; 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. GPMImpl.Mesa Strachey's General Purpose Macrogenerator as described in Computer Journal, Oct. 1965. pp. 225-241 Bill Paxton, November 1981 Maxwell, January 3, 1983 11:02 am Paul Rovner, July 20, 1983 1:55 pm McGregor, September 10, 1982 10:20 am Russ Atkinson, September 23, 1983 10:54 am Internal procs x is index of name finds name in e chain and sets w to start of def undefined return the item on the stack starting at st[x] if item is not complete, stop at st[s-1] and follow by "...(Incomplete)" first remove any defs in arg list from e chain and adjust defs in results so chain will be correct later WITH x:e SELECT FROM index => RETURN [x.index]; ENDCASE => ERROR }; WITH x:e SELECT FROM length => RETURN [x.length]; ENDCASE => ERROR }; WITH x:e SELECT FROM char => RETURN [x.char]; ENDCASE => ERROR }; WITH x:e SELECT FROM builtin => RETURN [x.name]; ENDCASE => ERROR }; RETURN [[index[i]]] }; RETURN [[length[i]]] }; RETURN [[char[c]]] }; [char[marker]]; RETURN [[builtin[name]]] }; debugging aids initialization Κ·– "Mesa" style˜JšΟc ™ J˜š)™)Jš8™8Jš™Jšœ!™!Jšœ"™"Jš%™%J™*—J™šΟk ˜ Jšžœžœ˜Jšžœ˜Jšžœ˜J˜J˜—šœ ž ˜Jšžœžœžœ˜Jš žœžœžœžœžœ˜J˜Jšžœžœžœ˜Jšžœžœžœžœ˜J˜—š Οnœžœžœ žœžœ˜=Jšœžœ ˜J˜J˜J˜J˜J˜J˜—šŸœžœžœ˜%Jšžœžœžœžœ˜0Jšœžœ˜J˜Jšœžœ˜J˜Jšœ žœ˜J˜—Jšœ žœ ˜#J˜š Ÿœžœžœžœžœ ˜1Jšžœžœžœžœ˜-Jš žœžœžœžœžœ˜2Jšžœžœ˜J˜—šŸ œžœžœžœ ˜*Jšžœžœžœžœ˜Jšžœžœžœ ˜Jšžœžœžœžœ˜%J˜—Jšœ žœžœ ˜"J˜š Ÿœžœžœžœžœžœ˜/Jšžœžœžœžœ˜-Jš žœžœžœžœžœ˜2Jšžœžœžœ ˜J˜—š Ÿœžœžœžœžœ˜(Jšžœžœžœžœ˜Jšžœžœžœ ˜Jšžœžœžœžœ˜%J˜—š Ÿœžœžœžœžœžœ˜BJšžœžœ˜$J˜—šŸ œžœžœ žœ˜=Jš žœžœ)žœžœžœžœ˜WJšœžœ˜J˜J˜—Jš œžœžœžœžœ˜;J˜Jš™J˜Jšœ ž œ˜˜šŸœžœžœžœ˜/šžœžœ˜ šžœžœžœ˜7Jšœžœ˜—šžœ"žœ˜*Jš œžœžœžœžœ˜.J˜Jš žœžœžœžœžœ˜?J˜—J˜J˜!—Jšžœ˜#J˜—šŸœžœžœžœ˜1šžœžœ˜ Jšœžœžœ"žœ˜HJ˜—Jšžœ!˜%J˜—šŸœžœžœ˜2Jš™Jš0™0Jšœ˜'Jšœ˜šžœ#˜&Jšœžœžœ˜J˜šžœ žœ ž˜Jšžœžœ žœžœ˜4Jšžœ˜—Jšžœžœžœ˜(Jšœ ˜%Jšžœžœžœ˜,Jšžœ˜—Jš ™ J˜ZJ˜—š Ÿœžœžœžœžœ˜GJš.™.š,™,Jš™—Jš œžœžœžœž œžœ˜LJ˜ J˜ Jšœ žœ ˜Jšœ žœ žœžœ ˜4J˜ Jšžœ žœ.˜@J˜—šŸœžœžœžœ˜3Jšžœžœžœžœ˜LJ˜—šŸœžœ4˜SJ˜ J˜ šžœžœ&˜@Jšœžœ˜ Jšžœ žœžœ#˜;J˜JJ˜WJ˜+J˜—Jš.™.Jš9™9J˜ J˜ Jšœ#˜Bšžœ%žœ˜LJ˜"J˜J˜ Jšžœ˜—J˜Jšžœžœ$žœ˜`J˜J˜"šžœ ž˜Jšžœžœ˜+Jšžœ9˜=—J˜$J˜Jšœ˜!Jšœ˜+J˜"šžœ ž˜J˜J˜J˜Jšžœ˜ J˜———Jšœžœ˜.J˜š Ÿœžœžœžœž œžœ˜LJšžœ ˜Jšœžœžœ˜Jšžœžœžœ-˜=šžœžœ˜!JšœEžœ˜N—šžœžœ˜#J˜šžœ+ž˜1J˜—J˜Jšžœ˜ —J˜ šžœž˜ Jšœ žœ˜'Jšœ+žœ˜2šœ˜-J˜J˜J˜J˜J˜J˜J˜Jšžœ˜—š œ žœžœžœžœ˜?J˜$J˜J˜J˜Jšžœ˜—š œ žœžœžœžœ ˜,J˜"J˜J˜J˜ J˜J˜J˜J˜J˜ J˜ J˜šžœžœ ˜.J˜2—J˜šžœžœ˜šžœž˜šžœ˜ Jšžœžœ5˜BJ˜J˜J˜ —šœ ˜ J˜šžœž˜J˜J˜J˜ Jšžœ˜ ——šžœ˜#J˜ J˜J˜šžœ#ž˜)˜8J˜*——Jš žœžœžœžœžœ˜B—Jšžœžœ˜—J˜ Jšžœ˜—J˜Jšžœ˜—š œ žœžœžœžœ ˜-Jšœ˜ J˜šžœžœžœ ž˜J˜Q—šžœžœžœ ž˜J˜Jšžœ žœžœ˜2Jšžœ˜—Jšžœ žœžœ˜šžœžœžœž˜$Jšœ!žœ˜)—Jšžœ˜ —Jšœžœ˜ ˜ Jšžœžœ?˜LJšžœ"˜&—Jšžœ˜—šžœ˜"Jšžœžœž˜Jšžœžœžœžœ˜(J˜ šžœž˜ Jšœžœ˜ J˜VJšœ žœ˜Jšžœžœ˜—J˜Jšžœžœžœ˜Jšžœ˜ —Jšžœ˜ J˜—Jš Ÿœžœ žœžœžœžœ˜NJš™Jš™Jš™J˜Jš Ÿœžœ žœžœžœžœ˜OJš™Jš™Jš™J˜JšŸœžœ žœž œžœžœžœ˜TJš™Jš™Jš™J˜Jš Ÿ œžœ žœžœžœžœ˜VJš™Jš™Jš™J˜š œžœ žœ žœžœžœžœ˜SJš™J˜—š œžœ žœ žœžœžœžœ˜TJš™J˜—šœžœž œžœ žœžœžœžœ˜VJš™J˜—˜Jš™J˜—Jšœž œ˜J˜š œ žœžœ žœžœ ˜LJš™J˜—Jš Ÿ œžœ žœžœžœžœ ˜IJ˜šŸ œžœžœ˜-J˜&Jš žœžœžœ žœžœ˜5J˜—Jšœžœ žœ ˜#J˜Jš™J˜šŸ œžœ˜#Jšœžœžœ>˜Lšœžœžœžœ ˜J˜J˜J˜J˜J˜Jšžœ ˜—J˜—š Ÿ œžœžœžœžœ˜@Jšœžœžœžœ˜)J˜J˜—š Ÿœžœžœžœžœ˜3Jšžœ žœ˜(šžœ˜J˜8šžœ(œžœ ž˜JJ˜Jšžœ˜ J˜———š Ÿœžœžœžœžœ˜3Jšžœ žœ˜.šžœ˜J˜6šžœ˜Jšžœ žœž˜)J˜Jšžœ˜ J˜———š Ÿ œžœžœžœžœ˜8Jšžœ žœ˜/šžœ˜J˜J˜:šžœžœ žœž˜;J˜ J˜Jšžœ˜ J˜———š Ÿ œžœžœžœžœ˜8Jšžœ žœ˜/šžœ˜J˜J˜8šžœžœ žœž˜;J˜ J˜Jšžœ˜ J˜———š Ÿ œžœžœžœžœ˜8Jšžœ žœ˜/šžœ˜J˜J˜*šžœžœ žœž˜;J˜ J˜Jšžœ˜ J˜———š Ÿ œžœžœžœžœ˜@Jšœžœ˜Jšœ žœ˜ J˜šžœ žœ ž˜J˜Jšžœ˜—J˜J˜J˜—Jš™J˜J˜J˜J˜J˜J˜Jšœžœ˜J˜J˜J˜J˜J˜Jšœ˜J˜J˜J˜J˜J˜J˜J˜J˜Jšœžœ˜J˜J˜J˜J˜Jšžœ˜J˜—…—+‚??