B MACRO PASȚMACRO1 PASRdMMACRO2 PASWCMACRO3 PASNMACRO4 PASA{MACRO5 PAS)32MACRO55 COM\@MACRO55 DOC>F {.PO15} program macro; {$Iglobdefs.pas} {$Istdutil.pas} {$Istdio.pas} {$Imacro1.pas} {$Imacro2.pas} {$Imacro3.pas} {$Imacro4.pas} {$Imacro5.pas} { Macro processor } { version 5.5, 26-May-85 } { by Jon Dart, 1866 Diamond St., San Diego, CA 92109 } const Version : string[20] = '5.5 (26-May-85)'; MBUFSIZE = 5500; { macro buffer size } ABUFSIZE = 50; { macro argument stack size } EBUFSIZE = 500; { macro evaluation stack size } PBUFSIZE = 250; { size of push-back buffer } MAXMACNEST= 15; { maximum level of nested macros } MAXINLINE = 132; { max. input line width } MAXOUTLINE = 160; { max. output line width } LBUFSIZE = 180; { line buffer size, must be > MAXOUTLINE } MAXREG = 9; { number of register variables - 1 } HASHSIZE = 97; { size of hash table } MAXSOLEVEL= 1; { max. level of so file nesting } MAXDILEVEL= 1; { max. level of di file nesting } MEMWARN = 1000; { warn user if < this amt. free memory } SQDFLT = LBRACE; { default start quote character } EQDFLT = RBRACE; { default end quote character } WCDFLT = CARET; { default word processor command character } HHDFLT = US; { default hard hyphen character } MCDFLT = PERCENT; { default macro character, can start a macro name } ASDFLT = DOLLAR; { default macro argument symbol } SPDFLT = BACKSLASH; { default macro separator symbol } GHDFLT = AMPER; { default ghost hyphen } MACTYPE = 0; { code for user-defined macro } DETYPE = 1; { codes for built-ins } RGTYPE = 2; EXTYPE = 3; FITYPE = 4; TRTYPE = 5; DITYPE = 6; EDTYPE = 7; APTYPE = 8; SOTYPE = 9; COTYPE = 10; QUTYPE = 11; SPTYPE = 12; MRTYPE = 13; MATYPE = 14; IFTYPE = 15; TRSTYPE = 16; type bitype = 0..16; mbuftype = array[1..mbufsize] of character; pbuftype = array[1..pbufsize] of character; lbuftype = array[1..lbufsize] of character; abuftype = array[1..abufsize] of character; ebuftype = array[1..ebufsize] of character; ripoint = ^rirecord; regarray = array[0..maxreg] of integer; rirecord = record { hash table entry } srcptr :integer; repptr :integer; srctype :bitype; capok :boolean; next :ripoint; end; csrecord = record { contains all info needed to evaluate a macro } ptr :ripoint; plev,nargs :integer; argptr :integer; end; cstype = array[1..maxmacnest] of csrecord; hasharray = array[1..hashsize] of ripoint; outrecord = record { info on current output file } destfile : filedesc; destname : textline; linebuf : lbuftype; lbfctr : integer; oldlbfctr : integer; sepflag : boolean; softhyflag : boolean; atsol : boolean; { true after 1st word read from an input line } end; inrecord = record { info on current input file } sourcefile : filedesc; sourcename : textline; linenum : integer; { line number in source file } pb : pbuftype; { push-back buffer } pbi : integer; { no. of chars pushed back } end; trtabletype = array[0..127] of character; soarraytype = array[0..maxsolevel] of inrecord; diarraytype = array[0..maxdilevel] of outrecord; var infile, outfile, sofile, difile : filedesc; ininfo : inrecord; outinfo : outrecord; infilename : textline; outfilename : textline; token : textline; msg,prompt : textline; mb : mbuftype; { macro buffer, holds all name & replacement texts } argstk : abuftype; { argument stack, holds pointers to arguments (in eb) during macro evaluation } callstk : cstype; { call stack, holds pointers to argstk for each level of macro nesting } eb : ebuftype; { evaluation buffer, holds macro arguments during evaluation } cp : integer; { depth of macro nesting } ap : integer; { index to next free slot in argstk } ep : integer; { index to next free slot in eb } mi : integer; { index to next free slot in mb } charcnt : integer; { count of chars. added to line buffer since last NEWLINE read } line : textline; { input line number } hashtable : hasharray; trtable : trtabletype; { transliteration table } registers : regarray; { storage for register variables } c : character; startquote : character; { quote characters, } endquote : character; { text within these copied literally } wcmdchar : character; { word processor command character, lines begining with this not filled } macrochar : character; { macro character, can start a macro name } hardhy : character; { hard hyphen character } argsym : character; { signals an argument in a macro definition } sepsym : character; { used to separate macros imbedded in words } ghosthy : character; { ghost hyphen } i : integer; rmarg : integer; { output right margin } lmarg : integer; { output left margin } indent : integer; { indent for next output line, includes left margin } solevel,dilevel: integer; { level of nesting for so, di commands } hashcode : integer; nlpar : integer; { nesting level of parentheses } soarray : soarraytype; diarray : diarraytype; repinfo,p : ripoint; fill : boolean; { true if filling enabled } trace : boolean; { true for detailed stats on macro expansion } macroson : boolean; { true if macro processing enabled } capsok : boolean; { true to allow caps in 1st letter of macro name } bjunk : boolean; gotfile : boolean; procedure cindent; forward; procedure olinit; forward; procedure soinit; forward; procedure break; forward; procedure prterr(var s:textline); { write error msg } begin with ininfo do begin putc(bel); write('Error in '); putstr(sourcename,TRMOUT); write(', line ',linenum,': '); putstr(s,TRMOUT); writeln; end; end; procedure exit; { called after fatal error, causes premature termination of program } var i :integer; begin for i:=dilevel downto 0 do pclose(diarray[i].destfile); for i:=solevel downto 0 do pclose(soarray[i].sourcefile); halt; end; procedure putback(c:character); { push c onto pushback stack } begin if c<>ENDFILE then with ininfo do begin if pbi >= pbufsize then begin setstring(msg,'pushback stack overflow'); prterr(msg); writeln; exit; end else begin pbi := pbi + 1; pb[pbi] := c; end end end; procedure pushachar(c:character); { pushes character onto evaluation stack } begin if ep>ebufsize then begin setstring(msg,'argument text too long'); prterr(msg); halt; end; eb[ep] := c; ep := ep + 1; end; procedure bumpastk(ep:integer; var argstk:abuftype; var ap:integer); { pushes ep onto argstk, bumps ap, checks for overflow } begin if ap>abufsize then begin setstring(msg,'too many arguments or macros nested too deep'); prterr(msg); halt; end; argstk[ap] := ep; ap := ap + 1; end; {$a-} function ngetc(var c:character):character; { get char, either from file or from pushed-back input } begin { ngetc } with ininfo do begin if pbi > 0 then { something pushed back } begin c := pb[pbi]; pbi := pbi - 1; ngetc := c; end else begin ngetc := getcf(c,sourcefile); if c = NEWLINE then with ininfo do linenum := linenum + 1 else if c = ENDFILE then begin pclose(sourcefile); if solevel > 0 then begin soarray[solevel] := ininfo; solevel := solevel - 1; ininfo := soarray[solevel]; ngetc := ngetc(c); end; end; end; end; end { ngetc }; {$a+} procedure showc(c:character); { like putc, but shows non-printing chars. in visible form } begin if c=NEWLINE then putc(c) else if c<32 then begin putc(CARET); putc(c+CAPA-1); end else putc(c); end; procedure gobblespaces(var c:character); { read from input until non-blank char. read } begin while ngetc(c) = SPACE do {nothing}; end; function hash(var s:textline):integer; { computes hash code } var j,cc,hc :integer; c :character; begin {hc := toupper(s[1]);} hc := s[1]; if chartbl[hc]='L' then hc := hc - CASEDIFF; { essential - makes "Stuff" & "stuff" hash the same } cc := 0; j := 2; c := s[j]; while (cc<5) and (c<>EOS) do begin if c<>ghosthy then begin cc := cc + 1; hc := hc + 3*c; end; j := j + 1; c := s[j]; end; hc := ((hc+cc) mod hashsize) + 1; hash := hc; end { hash }; function lookup(var s:textline; var p: ripoint):boolean; { takes string s and returns pointer to hash table record if s is defined as a macro. If s is not a defined macro, returns "false" and sets p = NIL. } var found,quit :boolean; i,j,sp,rp :integer; hashcode :integer; umb,usi :character; begin { lookup } hashcode := hash(s); { writeln('hashcode = ',hashcode); } p := hashtable[hashcode]; found := false; quit := false; while (not found) and (p <> NIL) and (not quit) do begin { search for match } sp := p^.srcptr; i := 1; { write('Comparing '); putstr(s,TRMOUT); putc(SPACE); j := sp; while mb[j]<>EOS do begin putc(mb[j]); j:=j+1 end; writeln; } {umb := toupper(mb[sp]); usi := toupper(s[i]);} umb := mb[sp]; if chartbl[umb]='L' then umb := umb - CASEDIFF; usi := s[i]; if chartbl[usi]='L' then usi := usi - CASEDIFF; if umb > usi then quit := true else if umb = usi then begin if p^.capok then begin i := i + 1; sp := sp + 1; end; while (s[i]=mb[sp]) and (s[i]<>EOS) do begin i:=i+1; sp:=sp+1; if s[i]=ghosthy then i:=i+1; end; end; found := s[i] = mb[sp]; if not found then p:=p^.next; end; lookup := found; if found and p^.capok then {fix 1st letter of replacement string} begin with p^ do begin rp := repptr; while (not isletter(mb[rp])) and (mb[rp]<>EOS) do rp:=rp+1; if islower(s[1]) then mb[rp] := tolower(mb[rp]) else mb[rp] := toupper(mb[rp]); end end; end { lookup }; procedure suppressnl; var c :character; begin if charcnt=0 then begin c := ngetc(c); if c<>NEWLINE then putback(c); end; end; function getarg(var arg:textline; var csp: csrecord; n:integer): boolean; { copy nth argument into character array arg } { returns 'false' if no nth argument } var c :character; i,j :integer; overflow:boolean; begin if (n<1) or (csp.nargs EOS; end; end; { getarg } {$a-} function expr(s:textline; var i: integer; var synerr:boolean):integer; { evaluate register expression in string s, return value, set synerr = true if syntax error } var v :integer; t :character; function term(var s:textline; var i:integer):integer; var v :integer; t :character; function factor(var s:textline; var i:integer):integer; { evaluate factor of arithmetic expression } begin { factor } if skipsp(s,i) = LPAREN then begin i := i + 1; if synerr then while not (s[i] in [RPAREN,EOS]) do i:=i+1 else factor := expr(s,i,synerr); if skipsp(s,i) = RPAREN then i := i + 1 else begin synerr := true; setstring(msg,'missing paren in expression'); prterr(msg) end; end else if s[i] = NUMSIGN then begin if i 0 then begin break; pclose(outinfo.destfile); dilevel := dilevel - 1; outinfo := diarray[dilevel]; end; suppressnl; end; procedure addmc(c:character; var mi:integer); { add 1 character to macro buffer } begin if mi > mbufsize then { fatal error } begin setstring(msg,'Macro buffer overflow'); prterr(msg); exit; end else begin mb[mi] := c; mi := mi + 1; end; end; {addmc} procedure install(var name:textline; mtype:bitype; var p:ripoint); { this procedure installs a macro in the hash table and copies its name into the macro buffer. It sets the macro type to mtype, and returns a pointer p to the hash table entry. The replacement text is initialized to a null string. } var newri :ripoint; hashcode :integer; i :integer; j :integer; procedure link(var newri:ripoint; var name: textline); { put newri into the hash table } { hash table items are ordered alphabetically by first letter } var p,q :ripoint; fc :character; quit :boolean; hashcode :integer; begin { link } hashcode := hash(name); p := hashtable[hashcode]; q := NIL; fc := toupper(name[1]); quit := false; while (p<>NIL) and (not quit) do { find place for installation } begin if toupper(mb[p^.srcptr]) >= fc then quit := true else begin q := p; p := p^.next end; end; if q = NIL then { install new item at head of hash list } begin newri^.next := p; hashtable[hashcode] := newri; end else { install new item in middle or at end of list } begin q^.next := newri; newri^.next := p; end; end; {link} begin { install } if trace then begin write('defining '); putstr(name,TRMOUT); writeln end; if not lookup(name,p) then begin { not previously defined, add to hash table } new(newri); link(newri,name); p := newri; end else { macro previously defined, re-define it } newri := p; with p^ do { initialize new entry } begin srcptr := mi; srctype := mtype; capok := capsok; j := 0; repeat { install name } j := j + 1; addmc(name[j],mi); until name[j]=EOS; repptr := mi; addmc(EOS,mi); end; end; { install } procedure define(var csp:csrecord); { process new macro definition } var name :textline; c :character; i,j :integer; p :ripoint; begin { define } if csp.nargs < 2 then begin setstring(msg,'%define: 2 arguments expected'); prterr(msg); end else begin i := argstk[csp.argptr]; { point to 1st argument, i.e. name } while eb[i]=SPACE do i:=i+1; { skip leading spaces } j := 1; while not (eb[i] in [SPACE,EOS]) do { collect name } begin if eb[i]<>ghosthy then bjunk := addstr(eb[i],name,j,MAXSTR); i := i + 1; end; bjunk := addstr(EOS,name,j,MAXSTR); install(name,MACTYPE,p); if memavail < MEMWARN then writeln('Warning: ',memavail,' bytes free memory'); mi := mi - 1; { back up over EOS put in macro buffer by install } i := argstk[csp.argptr+1]; { point to 2nd arg } repeat { move 2nd arg (definition) to macro buffer } c := eb[i]; addmc(c,mi); i := i + 1; until c = EOS; end; suppressnl; end { define }; procedure pusharg(k1:integer); {push text from argument buffer back onto input, in reverse order} var k,k2 :integer; begin k2:=k1; while eb[k2]<>EOS do k2:=k2+1; for k:=k2-1 downto k1 do putback(eb[k]); end; procedure replace(var csp:csrecord); { push the replacement string for a user-defined macro onto the stack } var i,j,k,lm :integer; argno :integer; p :ripoint; c :character; begin { replace } p := csp.ptr; { get pointer to hash table record } if trace then begin write('expanding macro: '); i := p^.srcptr; while mb[i] <> EOS do begin putc(mb[i]); i := i + 1; end; writeln; end; with p^ do begin i := repptr; j := i; while mb[j]<>EOS do j := j + 1; { find index to last char. } for k:=j-1 downto i do { push char.s in reverse order } begin if k=i then begin if (mb[k] <> ARGSYM) then putback(mb[k]) end else if (mb[k-1] = ARGSYM) then { expand $n } begin argno := mb[k] - DIGIT0; if (argno>0) and (argno<=csp.nargs) then pusharg(argstk[csp.argptr + argno - 1]); end else if mb[k]<>ARGSYM then putback(mb[k]); end; if trace then begin write('expansion = '); for k:= i to j - 1 do showc(mb[k]); writeln; end; end; end; { replace } procedure doif(var csp:csrecord); { process %ifelse built-in} var i,j,k :integer; c :character; begin {doif} if csp.nargs <3 then begin setstring(msg,'%ifelse: >=3 arguments expected'); prterr(msg); end else begin i := argstk[csp.argptr]; { point to 1st argument } j := argstk[csp.argptr+1]; { point to 2nd argument } while (eb[i]<>EOS) and (eb[i]=eb[j]) do begin i:=i+1; j:=j+1; end; if eb[i]=eb[j] then {push 3rd argument} begin k:=argstk[csp.argptr+2]; if eb[k]=EOS then suppressnl else pusharg(k); end else if csp.nargs>3 then {push 4th argument, if there is one} begin k:=argstk[csp.argptr+3]; if eb[k]=EOS then suppressnl else pusharg(k); end else {no 4th argument} suppressnl; end; end; {doif} procedure translit(var csp:csrecord); { process %translit built-in } var i,j :integer; istart,iend, jstart,jend :character; procedure getrange(i:integer;var rstart,rend:character); { takes string starting at eb[i], returns 1st and last characters specified by string } begin rstart:=eb[i]; rend:=eb[i]; if (eb[i]<>EOS) and (eb[i+1]=HYPHEN) then { range of chars specified } begin rstart := eb[i]; rend := eb[i+2]; end; end; begin { translit } if csp.nargs <>2 then begin setstring(msg,'%translit: 2 arguments expected'); prterr(msg); end else begin getrange(argstk[csp.argptr],istart,iend); getrange(argstk[csp.argptr+1],jstart,jend); j := jstart; for i:=istart to iend do begin trtable[i] := j; j := j + 1; if j>jend then j:=jstart; end; end; suppressnl; end; { translit } procedure evaluate(csp:csrecord); { Assuming a macro has been found, and its arguments collected, this procedure executes it if it is a built-in function, or else pushes its definition back onto the input } { csp is a record holding all necessary information about the macro } begin { evaluate } case csp.ptr^.srctype of MACTYPE: replace(csp); DETYPE: define(csp); DITYPE: divert(csp,IOWRITE); APTYPE: divert(csp,IOAPPEND); EDTYPE: enddivert; SOTYPE: chgsource(csp); TRTYPE: setonoff(csp,trace); EXTYPE: pushexpr(csp); RGTYPE: setregister(csp); FITYPE: setonoff(csp,fill); COTYPE: setonoff(csp,capsok); QUTYPE: quotes(csp,startquote,endquote); SPTYPE: spchars(csp,macrochar,argsym,sepsym,wcmdchar,hardhy,ghosthy); MRTYPE: margins(csp,lmarg,rmarg); MATYPE: setonoff(csp,macroson); IFTYPE: doif(csp); TRSTYPE: translit(csp); end; {case} end; { evaluate } procedure skip1; { write 1 blank line to the output} begin with outinfo do putcf(NEWLINE,destfile); end; procedure put1line; { put out 1 line } var i :integer; begin { put1line } with outinfo do begin for i:=1 to indent do putcf(SPACE,destfile); i := 1; while linebuf[i] <> EOS do begin putcf(trtable[linebuf[i]],destfile); i := i + 1; end; end; end; { put1line } procedure fixeol; { delete trailing blanks, make sure line buffer ends with NEWLINE + EOS } var i :integer; blank :boolean; c :character; begin { fixeol } with outinfo do begin i := lbfctr - 1; blank := true; while blank and (i>0) do begin c := linebuf[i]; blank := (c=SPACE) or (c=EOS) or (c=NEWLINE); if blank then i:=i-1; end; linebuf[i+1] := NEWLINE; linebuf[i+2] := EOS; end; end; { fixeol } procedure break; { flush output buffer, forcing a new line to start } var i,j :integer; begin {break} if outinfo.linebuf[1] <> EOS then begin fixeol; put1line; cindent; olinit; end; end; {break} function gettok(var token:textline):character; { gets token from input file, returns its first character } { Tokens are defined as a single non-alphabetic character, or a string containing one or more alphabetic characters, starting with a letter or a MACROCHAR, up to and not including the next blank, end of a line, end of file, or non-alphabetic character. Strings of one or more blanks are returned as separate tokens. Similarly, a NEWLINE or ENDFILE is never returned as part of a string; it is always returned separately as the first (and only) character in the token. Tokens always end with an EOS char. } { modified Sept. 1984 to recognize "ghost hyphens" -- to allow use with Oasis' HYPHEN program } var i,j,ccnt :integer; endtok :boolean; foundnz :boolean; c :character; begin { gettok } with ininfo, outinfo do if (not atsol) and (linebuf[1] = EOS) then { skip leading spaces } begin gobblespaces(c); putback(c); end; i := 1; while (ngetc(c)=SPACE) and (i1) then { just return spaces, put non-blank char. back for next time } putback(c) else if (i HYPHEN then begin { prev. char. not hyphen } foundnz := false; j := lbfctr-2; while (not foundnz) and (j>0) do begin foundnz := (linebuf[j]>=SPACE) or (linebuf[j]=BKS); if (not foundnz) then j := j - 1; end; if foundnz then if linebuf[j]<>BKS then { ok to remove soft hyphen } begin lbfctr := lbfctr - 1; softhyflag := true; end; end; end else if not (linebuf[lbfctr-1] in [HARDHY,SLASH]) then begin if not (token[1] in [HYPHEN,HARDHY,SLASH]) then begin { add space between words } linebuf[lbfctr] := SPACE; lbfctr := lbfctr + 1; end; end end end; atsol := false; i := 1; while token[i] <> EOS do begin linebuf[lbfctr] := token[i]; lbfctr := lbfctr + 1; i := i + 1; end; overflow := (lbfctr - 1 + indent) > rmarg; charcnt := charcnt + lbfctr - oldlbfctr; end { with } end { addtoln }; begin { puttok } if cp > 0 then { we're inside a macro } for i:=1 to slength(token) do pushachar(token[i]) else with ininfo, outinfo do begin if atsol then { at start of an input line } begin if (token[1] = wcmdchar) then break else if (token[1] = NEWLINE) then begin break; skip1; end else if token[1] = SPACE then begin break; i := 1; c := skipsp(token,i); indent := indent + i - 1; if i>1 then scopy(token,i,token,1); { remove leading blanks from token } end; end; if token[1] <> NEWLINE then begin addtoln(token,overflow); if overflow then { fold line } begin backup(lastword); { remove last word from line } break; i := 1; c := skipsp(lastword,i); lbfctr := 1; while lastword[i]<>EOS do {put last word on prev. line at start of line buffer } begin linebuf[lbfctr]:=lastword[i]; i := i + 1; lbfctr := lbfctr + 1; end; end; end else { newline } if (not fill) or (linebuf[1]=WCMDCHAR) then break; atsol := token[1] = NEWLINE; if atsol then charcnt:=0; end { with } end { puttok }; procedure cindent; { compute indent value } { called at start of program and after each line is output } begin indent := lmarg; end; procedure olinit; { initialize output line variables } { called at start of program and after each line is output } begin with outinfo do begin lbfctr := 1; oldlbfctr := 1; linebuf[1] := EOS; end; end; procedure soinit; { initializes for reading from a new source file } begin with ininfo, outinfo do begin atsol := true; pbi := 0; linenum := 1; end; end; procedure initvars; {initialize global variables} var detxt : textline; { names of built-in functions } trtxt : textline; fitxt : textline; ditxt : textline; edtxt : textline; sotxt : textline; intxt : textline; aptxt : textline; rgtxt : textline; extxt : textline; cotxt : textline; qutxt : textline; sptxt : textline; mrtxt : textline; matxt : textline; iftxt : textline; trstxt : textline; p : ripoint; begin {initvars} mi := 1; cp := 0; ep := 1; ap := 1; solevel := 0; dilevel := 0; charcnt := 0; with soarray[0] do begin sourcefile := infile; scopy(infilename,1,sourcename,1); end; with diarray[0] do begin atsol := true; destfile := outfile; scopy(outfilename,1,destname,1); end; ininfo := soarray[0]; outinfo := diarray[0]; soinit; lmarg := 0; rmarg := 80; fill := false; capsok := false; trace := false; macroson := true; cindent; olinit; for i:=1 to hashsize do hashtable[i] := NIL; for i:=0 to 127 do trtable[i] := i; startquote := SQDFLT; endquote := EQDFLT; wcmdchar := WCDFLT; macrochar := MCDFLT; hardhy := HHDFLT; argsym := ASDFLT; sepsym := SPDFLT; ghosthy := GHDFLT; {define names of built-ins:} setstring(detxt,'%define'); setstring(trtxt,'%trace'); setstring(fitxt,'%fill'); setstring(sotxt,'%source'); setstring(intxt,'%include'); setstring(ditxt,'%divert'); setstring(edtxt,'%enddivert'); setstring(rgtxt,'%register'); setstring(aptxt,'%append'); setstring(extxt,'%expr'); setstring(cotxt,'%capsok'); setstring(qutxt,'%quotes'); setstring(sptxt,'%spchars'); setstring(mrtxt,'%margins'); setstring(matxt,'%macros'); setstring(iftxt,'%ifelse'); setstring(trstxt,'%translit'); { put built-ins in hash table: } install(detxt,DETYPE,p); install(ditxt,DITYPE,p); install(edtxt,EDTYPE,p); install(sotxt,SOTYPE,p); install(intxt,SOTYPE,p); install(trtxt,TRTYPE,p); install(rgtxt,RGTYPE,p); install(extxt,EXTYPE,p); install(aptxt,APTYPE,p); install(fitxt,FITYPE,p); install(cotxt,COTYPE,p); install(qutxt,QUTYPE,p); install(sptxt,SPTYPE,p); install(mrtxt,MRTYPE,p); install(matxt,MATYPE,p); install(iftxt,IFTYPE,p); install(trstxt,TRSTYPE,p); end; {initvars} procedure initmacro(p:ripoint); { begins processing a macro or built-in } begin cp := cp + 1; if cp > MAXMACNEST then begin setstring(msg,'Macros nested too deep'); prterr(msg); halt; end; with callstk[cp] do begin ptr := p; plev := 0; nargs := 0; argptr := ap; bumpastk(ep,argstk,ap); c := ngetc(c); putback(c); if c = LPAREN then nargs := 1 else { no arguments, push back balanced parens } begin putback(RPAREN); putback(LPAREN); end; end; end; { initmacro } begin { main program } clrscr; {Turbo intrinsic} lowvideo; {Turbo intrinsic} writeln('MACRO - Text-oriented macro processor'); writeln('by Jon Dart ... Version ',Version); writeln; writeln; ioinit(4); { initialize i/o variables } repeat setstring(prompt,'Input file name? '); gotfile := getfile(infile,prompt,infilename,IOREAD); if gotfile then begin writeln; setstring(prompt,'Output file name? '); repeat gotfile := getfile(outfile,prompt,outfilename,IOWRITE) until gotfile; writeln; initvars; { read the file and do the stuff: } while gettok(token) <> ENDFILE do begin c := token[1]; if {(isletter(c)} (chartbl[c]='U') or (chartbl[c]='L') or (c=macrochar) or (c=sepsym) then begin if lookup(token,repinfo) then { macro or built-in } begin if macroson or (repinfo^.srctype <> MACTYPE) then initmacro(repinfo) else puttok(token) end else puttok(token) end else if c = STARTQUOTE then begin nlpar := 1; repeat c := gettok(token); if c = ENDQUOTE then nlpar := nlpar - 1 else if c = STARTQUOTE then nlpar := nlpar + 1 else if c = ENDFILE then begin setstring(msg,'missing right quote char.'); prterr(msg); nlpar := 0; end; if nlpar>0 then puttok(token) until nlpar = 0; end else if cp = 0 then { not in a macro } puttok(token) else if c = LPAREN then begin with callstk[cp] do begin if plev > 0 then puttok(token); plev := plev + 1; end end else if c = RPAREN then begin with callstk[cp] do begin plev := plev - 1; if (plev > 0) then puttok(token) else begin pushachar(EOS); evaluate(callstk[cp]); ap := argptr; ep := argstk[ap]; cp := cp - 1; end end end else if (c = COMMA) and (callstk[cp].plev > 0) then begin pushachar(EOS); with callstk[cp] do nargs := nargs + 1; bumpastk(ep,argstk,ap); end else puttok(token); end; if cp > 0 then begin setstring(msg,'")" expected'); prterr(msg); end; if fill then break; pclose(infile); pclose(outfile); end until not gotfile; end.  ͫCopyright (C) 1985 BORLAND IncBHeath/Zenith H89ctedPqtqY ELE MKp1q1~7#~= oͦkԄ!!"~#(}:$= +*!5!*!!:(2!5:(>2!!!:O::O:!*! !45(! +/ 0y0( d!k5!{5__o&  :(͠|(  *"x2y( >28!"9!! og2"">~22 9/4*9 Co&ͦͣ} [ (!e{ͦA8Q0G: x@!\w# (͂ ?(*( .( w^. ^!h6# (?( *( ͂( w#>?> w#ͦ 8 !ɿ .,;:=?*[]<>{}a{ |ʹ}ͽƐ'@'7||}>2ͯ*Bڨ  "og"2>2! ""*B"[Ru*"^#V#^#V#N#FO/o&9O/o&9!9(> (G!9 w#Eͺw}8' RB0 >' RqRR!+ Ͱ R!+ Ͱ r!+ Ͱ r!+ Ͱ r!# Ͱ r!+ Ͱ T]KB!z> S>))0 = | |̀̀DMgo>jB0 7?= H͒<z5a)a<z {0Gɯgo||~}||/g}/o#}o&K[xAJSJDM!b"!6J"DM'd } ) W _}8(8J`9{T]=o`9y ) >' ́ ͬ͗ }>' xˆ }} ˸T}ٕ(0D=z ,= ( ͒ 0%{ , 7 ?(8ͬ x ͆ - r 8˸x ͏  ,-xG}r }مM 9r .>#n0͒ { = - nx ͇ ,-(-˸G,-r }ٕ?M 9.> 8ͬ ?= u+-(>͆ 0ͬ ͆ 8 ?x ͇ , 78ƀ8ƀ8ox٨!دoGOW_gɷɷ|لg{ً_zيWyىOxوG|ٔg{ٛ_zٚWyٙOx٘Gxٸyٹzٺ{ٻ|ټx٨ xx( ?}ٽ }ցr <(r 7{ = |٤g{٣_z٢Wy١Ox٠G{ ͬ ́ }x>' ͬ}ƀ/ƀo -́ }0͎-́ ͎,}l˸ 8 4 ͗ x( - 8́ - 8,́ }l8;*!͗ ! >4ͬ͗ ͗ ͬ--- ́ ,,,-xGg?+2n*8t z~,->' x' ͘}. ͆́ , ! >4,͢- o&0%,͗ }gr }؉}颋.:}8c~I$I~L*͢ٷx˸ }0G,<},-(-́ !>I0 ͗͘ o8 ͆ >' m.`1pF,t6|!wS<.z}[|%FXc~ur1}͆ٯx(<˸ 8 !~J 0.O!>s 8 =  n s͗ ͆ .n 0 ͎-́ OT0 j oD,:j !I}袋.}8c~I$I~L! >ͬ͗ I× nn ͗ = ͆ nf^VNF!DLT\I!!53!r1!͒!> x #-= o˸x͆(- }(x>8(z ,z `iÃ!>' |r |̀>)=|(DMbo˸88x(0 8> ̀x(>-{(ay( z(>. ( {>E>+|(|Dg>-|/ 0:p# ~# +>0w#,-  60#}˸}րogM| .(z = ~> x0w#xG%͇ %͇ ZJDM%͇ = _~65+~hìx-Sx9?+{Η@}|z z gZJDM0{ ,7}o˸? #yO!@9i&?  #?w#?/w#?w#!9! E9!!9~(+Fͺ!"9!(#>2*"| >"2:( Ͷ *w*6 !\$![ (ͦ( #:~CONTRMKBDLSTCAUXUSR>2i:*ˮ~0:*:(@q##pZ* :(  ~* < >26"!"""~>2""v>2>"!"ˮ(!~8>~O6~*"w(6(2(-()(6 (8 0 :(* y(~#+ (( 66 #6 #"*: y~o p .##~ͺ(.6w4._~ =*##55= *[R8*~#"= ͣ}== ͯ}͵}*#w+#~+>*~('k!0(ˮ]k!8ˮ!]~-#8~>27kˮw>O$6̃s #r$ͣ6̏ k ( (ˮ qk(ˮ ( k ˮ*O:~ ##~._q4((=ʦ==ʩ=ʬò*:4^q*##~6ͺ>2}*|(̓|( ̓6-#[RM8( G> A~#*'C! !TRUEFALSE!9N#Y~#( G~#> >    "~(kѻ(( !0 (ˮ!!>2Sz:0:*6##ww#w$w#w:  ##N#F*B>2w#w#[s#r> "~ͮ*-w#ww##> ͮÁ""~>2:ZR0 *4#4>2:ZR> *4 #4(> >22*f(/˦:G(##~++ :O x yC!ͺ Q*:G(##~._.͚f<\=<͚*##w ͮ +4 #4x >>2:G("ͮ"*nˮ*0 SZѷR8@* N#F#s#r, 0})jS\*##w+ N#FB ͮr+s>2!T]>)j)0 0= ]R!#]*^#V#N#F#^#V>2Ͱ:0:*6 #-Nw#Fwq#p#6#w#w#w"~Á>">!DM!":*B:!>(>2>">!"2"~ʰ*w#wx(9* :O *-4 #4!*4 #4 *-N#Fq#pV+^Bq#pSZѷR&* s#r$ s#rL <?*L!\  <( !\$>2>2L:>!(* \$\<(!3: [1ð\!(7"~> 2"S"Ns#FrB(Z#\: \<(?*"}K\! !*}#"}! x \* *>) 2""{_!"*nf}(HR0nf" ^VMDnfutqp*s#r*s#r"* uKB!0>' ~#fo{_"*R0RnfR0KqputsrNF( ^VNF^V*SutKqp R*R(~w~wnf ut"6#K*K*!""*NFy(* "*B0Cnf* [R*"*RS[s#r^#V""6#>O"w2x2*"!F"" &y*"*>2"*"!F"""!\*: Nr!~6go(\R*s#r_2x( s x(T]DMx(R0 U(͝O/o&9q# (!>F0#( ~ ( #]( ~ ( (#}(  i&T-a%â}ͦo*!~6o&|:2 2}:__zѯ2*|KB " z ^C User break+=  I/O Run-time error {ʹ, PC=*ͯNot enough memory Program aborted :ʎ'1!d!qXB>l!P!K!K:͛!ͪ }2A!*A&n&!L}2B*B&}27!*7&n&!U}28*8&}2-*-&-!Eʫ!*-&! R}2.ô!*-&}2.*.&}2#*#&!}2$*$&}2*&V!E!*&! }2"*&}2*&}2!*&n&!L!*&n&!U}o}2*&}2!*&n&!D}2*&}2!*&n&!X͒}2*&""**E"*""*"*""**E"*""*"*"!"!"**+n&!͒EK#*!"*!"#*"*"""}2*^#V*Eʈ#!}2ú#**^#V+*&s*^#V!*s#r!}2*&""*#!"!"!}2*&}o**+n&!͒}oES$**+n&*!!RU#}o}2*&}oEP$*!"#**+!s*&}o}2*&P!R"!"H*H!R!n&!R"͸E$**H+!R*Hn&s*H!"HÍ$**H+!s"MP!!"**+n&!͒EN%!:**+n&e.=P!*!"%!:P!!:""}2*"**+n&́*&͑!͑O}oEʸ%*!"~%**+n&!E%!"%*"*""**^#V+n&́! ͑! ͑OE1&*^#V!*s#r%**^#V+n&}2*&"t"v"x"z*z*x+n&!͒Eʰ&*v*t+*z*x+n&s*x!"x*t!"t`&*v*t+!s"g"i!"]*i*]+n&*g*]+n&*i*]+n&!͒}oE!'*]!"]&*i*]+n&*g*]+n&}2k*k&"W"Y*Y*W%!-Ew'!"K}'!"K*Y*W^#V+n&́!+͑!-͑OEʺ'*W^#V!*Ws#r!"M*Y*W^#V+n&R"E(! *M*Y*W^#V+n&!0R"M*W^#V!*Ws#r'*K*M"[*[!:"B"D"F*F!Eʊ(*D*B+!-s*F}/o|/g#*D*B!.("H(*F! ͥEʴ(*F! *D*B.("B*D*B+*F! E!0s*D*B!+!s*B!"H*H:""8*8!!EA)MToo many files requested.!!$!+:9!s!$!+:9!s!$!+:9!s!!!*8fz)".!$*.+:"0!,!*00*,s#r*09!s*.#é)!*8!!fz:*".!$*.+:9!s*.# *!!/fzg*".!*.!Xs*.#E*!0!9fzʔ*".!*.!Ds*.#r*!:!@fz*".!*.!Xs*.#ß*!A!Zfz*".!*.!Us*.#*![!`fz+".!*.!Xs*.#*!a!zfzH+".!*.!Ls*.#&+!{!fzu+".!*.!Xs*.#S+Ì-"""!}2*"**:p!Eʆ-*!|g}o}2*&!E,*p*7*]s#r*5!s#r*3!!s#rS-*&!ED,*o*3!s#rS-*&!ES-*p*]"!*!}oE=-**!Ro**0^#V!;**!Ro!"!}2*!͸*&}o}oE*-*0^#V*+n&!}2*&}oE'-*!",*3*s#rS-*o*3!s#r!Eʆ-!}2*9*s*2!s*&"'")*)$P!!}2+!}2!"*!͸*&}o}oE@.!$*+:9n&!}2*&E1.*'!$*+:!y+E..*}2+=.*!"û-*+&"*$P!+!|!+:p!Eʀ.!|͓}2***&! Eʨ.͛ ö.͛**&" "!ͩ!D *&*s*n&!E.!*s/*n&! E/! *s*n&}2!*!&"*ͷ.*s*n&́.*n&}2*&}2"!}2 !$*&+)))))"*2n&Eʙ/!}2 0*3^#V!E>0*IE/!}2 *2!s.0**0^#V!+!!;́!͑!͑O}oE.0MDisk read error!*3!s#r*0^#V*3^#V+n&*s*3^#V!!R|g}o!E0*5*5^#V!s#r*5^#V*7^#VͥE0*2!s*3*3^#V!s#r* &}2"*&!E)1*!/}21!$*&+)))))"!*&O/E1*&!|g}o*s*n&!Eʙ1!*s*2!s1*n&! *n&! }oE1!*&O/}2! *s1!*s*n&}2*&}2}2!$*&+)))))"*0^#V*3^#V+*&s*3*3^#V!s#r*3^#V!E2**0^#V!+!!ͺ!͒E2MDisk write error!*3!s#r}2}2*&!E3*&́.|3*&!EG3*&! E33!ͺ D3!ͺ*&" |3*&! Em3! *&1! }2*&*&1}2*&́!͑!͑!͑O}oEʔ4!$*&+)))))"*9n&!|g}ó!͑!͑OE}4*9n&!|g}o!E&4!*&2*3^#V!E}4**0^#V!+*3^#V!R!!ͺ *Ͱ *9!s"}2"!"*&!Ej5**+!ͷ.s*&!E5*!E5*!R"!́.! ́.!́.g5*&! ͥ*&!͒}o*&! }oEg5*!"*&́.Ñ5**+!*&0s*!"*&! *&!}o**ͥ}oEʬ4*&!E5*!R"**+!s*&!͒}2*&}2"!"**+n&!͒Ea6**+n&*&2*!"!6""""!}2*!6*!!R͕4}2*#"**+n&! E6**+!s*!+n&́!͑! ͑O}2*&}oEw7**v+}2*&!͒}2*&EA7*&*s!}2t7͛ ͛ͺ Can't open:  *!6͛ ~7!}2*&*&}oE}6*&5.5 (26-May-85)gÆggÂ\"!I"!́.͛ͺ Error in  *!6͛ͺ, line *S^#V!&ͺ:  *!6͛ *!vzl8"!*))))))))n&}3*+98*¨!vzʪ8"!*))))))))n&}3*+w8 }2ߣ*ߣ&!͒E\9!I"ף*ףO^#V!ͥE9!Mpushback stack overfloww$!͹7͛ .8\9*ףO*ףO^#V!s#r*ףU*ףO^#V+*ߣ&s}2֣*!Eʡ9!Margument text too longw$!͹7 !*+*֣&s*!""ȣ"ʣ"̣*ȣ^#V!2E&:!M,too many arguments or macros nested too deepw$!͹7 *ʣ*ȣ^#V+*̣s*ȣ^#V!*ȣs#r !"!I"*O^#V!E:*U*O^#V+n&*s*O*O^#V!Rs#r*n&}2ä;**n&0}2*n&! E;!I"*S*S^#V!s#rä;*n&!Eʤ;*n&}3*¨!Eʤ;!*¨))))))))!IQ*¨!R"¨!*¨))))))))IQ*P:}2*& "}2*&! E;*&́.<*&! E*t&! R}2t*u&*t&EI>!}2 ?*u&*t&E ?*^#Vn&Eʈ>*~!"~*z!"z**~+n&!%*z+n&**~+n&!͒}oE ?*~!"~*z!"z**~+n&*̨&E?*~!"~È>**~+n&!%*z+n&}2*&}oEP?*^#V^#V*s#rk=*&}2*&*^#Vn&}oE+@*^#V"*^#V"x!%*x+n&"}o!%*x+n&!͒}oE?*x!"xÑ?*!+n&-!E @!%*x+!%*x+n&!s+@!%*x+!%*x+n&!s*&*}!Eg@!kP:}2k*k&! ͒Eg@*k&ͮ8"d"f"h*d!*f^#V*d}oEʻ@!}2j*h!+!sÊA!*f^#V*d!R+n&"Y!"W!*Y+n&}2[*[&*h!W!RU#}o}2V*V&EIA*W!R"W!*h!W!RU#}2UA*Y!"Y*[&!*V&}oE@*h!+n&!͒}2j*j&öDþC!Ԣ"ܢ"ޢ*ޢ*ܢ%!(EʢB*ܢ^#V!*ܢs#r*n&E)B*ޢ*ܢ^#V+n&́!)͑!͑O}oE&B*ܢ^#V!*ܢs#rA;B*ޢ*ܢ*͐A"*ޢ*ܢ%!)EjB*ܢ^#V!*ܢs#rßB!*s!Mmissing paren in expressionw$!͹7ñC*ޢ*ܢ^#V+n&!#EʣC*ܢ^#V*ޢ#EpC*ޢ*ܢ^#V!+n&R"E)C!ը*ޢ*ܢ^#V!+n&!0R)^#V"YC!*s!Mdigit expected after #w$!͹7*ܢ^#V!*ܢs#ràC!*s!Mdigit expected after #w$!͹7ñC*ޢ*ܢJ'"*Ԣ"!""**͖A"**%}2*&́!*͑!/͑!%͑OEʣD*^#V!*s#r*&*RND***͖A"ÑD/RqD***͖A"ÑD%R‘D***͖AE"**%}2C*"*"c!""R!*s!*͓A"!*%}2*&́!+͑!-͑OE|E*^#V!*s#r*&!+ESE*!*͓A"jE*!*͓AR"!*%}2D*"T*Tc""Ң!}2ɢ*Ң^#V!EE!M%register: 2 arguments expectedw$!͹7F!k*Ң!h@}2!"!k!%}2j*j&!#EF!k*!+n&}2j*j&R"EF*j&!0R"â!k*Ң!h@}2!"!k!!ɢ͐A"*ɢ&}oEF!ը*â)*s#r*&EF͛ͺ register *â!&ͺ = *!& F!}2ɢ*ɢ&EG!M bad syntaxw$!͹71@"h!*h!h@EʤG!" !! !͐A"*&}oEʡG!" *!* .(" !#!vzʡG" !* +n&ͮ8* +~GG!M%expr: argument expectedw$!͹7"""!*!h@}2!!+n&*s!*!h@}2!!+n&*s1@"""""""!?*!h@}2!?!+n&*s!?*!h@}2!?!+n&*s!?*!h@}2!?!+n&*s!?*!h@}2!?!+n&*s!?*!h@}2!?!+n&*s!?*!h@}2!?!+n&*s1@"9";"=!"ݠ!ߠ*=!h@EkI!ߠ!ݠJ'*;s#r!"ݠ!ߠ*=!h@EʗI!ߠ!ݠJ'*9s#r1@"٠"۠!}*۠!h@}oEI!M%trace: on or off expectedw$!͹7J!!}#fz.J"Ϡ!}*Ϡ+!}*Ϡ+n&!s*Ϡ#I!}!+n&!O!}!+n&!N}oEmJ!*٠sJ!}!+n&!O!}!+n&!F}o!}!+n&!F}oEJ!*٠sJ!Mon or off expected after trw$!͹71@"{1@!!*{!h@}oEVK!M'%source or %include: file name expectedw$!͹7ÛL*¨!EʦK!M+%source or %include command nested too deepw$!͹7ÛL!"!!!%}2!!*!!!L&!!!v+}2 * &!E#L!Mcan't open file w$!!!#}2!͹7ÛL!*¨))))))))!IQ*¨!"¨ͳ7!I"s*s!!R* &*ss!*¨))))))))!IQ""!*!h@}oEL!M&%divert or %append: file name expectedw$!͹7zN*!EJM!M*%divert or %append command nested too deepw$!͹7zN!"!!%}2!*!!L&!*v+}2*&!EM!Mcan't open file w$!!#}2!͹7zN!*))))))))!;*!"!;"!!*!L&*&*s* !*!R)))))))) n&sͰ7!*))))))))!;1@*!ENͶ7*;&}3*!R"!*))))))));1@"}2*^#V!|EO!MMacro buffer overfloww$!͹7.8HO!%*^#V+*&s*^#V!*s#raP""*8<"!i*+)^#V"!"*!+n&!}2!}2*!͒*&}o}oEO!%*^#V+n&!*&ͥEO!}2O*"*^#V"ÖO*!E8P*^#V*s#r!i*+)*^#Vs#r`P**^#Vs#r*^#V*s#r"}2"*&EʣP͛ͺ defining  *!6͛ **1=}oEP!!!*LO**s#rP*^#V"*^#V"**s#r**&s**&s!"*!"**+n&!N**+n&!EQ**s#r!!N"}*}^#V!EQ!M%define: 2 arguments expectedw$!͹7WS!*}^#V+n&" !* +n&! ER* !" Q!"!* +n&́! ͑!͑O}oEʂR!* +n&*̨&͒EsR!* +n&!#!!RU#}2* !" R!!#!!RU#}2!#!!IO=!ER͛ͺ Warning: =!&ͺ bytes free memory *!R"!*}^#V!+n&" !* +n&}2"*"&!N* !" *"&!ES1@"*"!*+n&!͒EʒS*!"iS*!R*vzS"!*+n&ͮ8*+æS" * ^#V"*&ENT͛ͺexpanding macro:  *^#V"!%*+n&!͒EET!%*+n&́.*!" T͛ *"*^#V"*"!%*+n&!͒EʑT*!"hT*!R*vzʵU"**ET!%*+n&*Ψ&͒ET!%*+n&ͮ8ìU!%*!R+n&*Ψ&EU!%*+n&!0R"*!** ^#V͸}oE|U!* ^#V*!R+n&[SìU!%*+n&*Ψ&͒EʬU!%*+n&ͮ8*+åT*&EV͛ͺ expansion =  **!Rfz V"!%*+n&ͳ;*#U͛ "*^#V!EjV!M%ifelse: >=3 arguments expectedw$!͹7W!*^#V+n&"!*^#V!+n&"!*+n&!͒!*+n&!*+n&}oEV*!"*!"àV!*+n&!*+n&EeW!*^#V!+n&"!*+n&!E[W1@bW*[SW*^#V!EW!*^#V!+n&"!*+n&!EʻW1@W*[SW1@eX"Ξ"О"Ҟ!*Ҟ+n&*Оs!*Ҟ+n&*Ξs!*Ҟ+n&!͒!*Ҟ!+n&!-}oEdX!*Ҟ+n&*Оs!*Ҟ!+n&*Ξs"*^#V!͒EʹX!M%translit: 2 arguments expectedw$!͹7`Y!*^#V+n&!מ!֞W!*^#V!+n&!՞!ԞW*՞&"؞*מ&*֞&fz`Y"ڞ!*ڞ*؞s*؞!"؞*؞*Ԟ&EWY*՞&"؞*ڞ#Y1@*n&RŽY!S[R¢Y!tQ[RºY!!͜L[RY!!͜L[RY~N[ RY!J[RZ!!͛I[R"Z!G[R6Z!͏E[RNZ!!͛I[ RfZ!!͛I[ R‚Z!!Ө!ҨG[ R®Z!!Ш!Ψ!ͨ!Ѩ!Ϩ!̨-H[ RZ!!ƨ!Ȩ/I[RZ!!͛I[RZ!V[R[!W!;"! *n&2!;"!*ĨfzN["! *n&2*#.[!"*S*+n&!͒Eʦ[!*S*+n&n&*n&2*!"T[!;"*^#V!R"!}2*&*!}oEK\*S*+n&}2*&! *&!}o*&! }o}2*&EH\*!R"[*S*!+! s*S*!+!s!!+n&!͒Eʨ\ͧ[[ͭ7Ͱ7"!I"!;"* n&}o*S!+n&!}oE\!x<*x&ͮ8!"!xP:! *!R}oEJ]**+*x&s*!"]*!Ef]*x&ͮ8a_*!REa_!;"* *x&*ͨ&s* n&}oE]**+*x&s*!"!*x&n&!U!*x&n&!L}o*x&*Ш&}o*x&*ͨ&}oE^!}2z*z&}o*!R}oE^!xP:}2x!*x&n&!U!*x&n&!L}o*x&*̨&}oE^**+*x&s*!"^!}2z*x&ͮ8-^a_*x&!-*x&*Ϩ&}oEa_!}2z!xP:́!-͑*Ϩ&͑O*!R}oEX_**+*x&s*!"_*x&ͮ8**+!s*!+n&}2*&2e"!"!I"!;"*^#V!*S!+n&! ͒}oEa* ^#V" * n&Eʌ`* !R*^#V!Rfz[`" **+*S* +n&s*!"* #`*S* !R+!-s** s#ra*S* +n&! ͒E.a* !R" !}2* !*&}o}oEa*S* +n&! }2*&}oEa* !R" `*&E+a* " 5a!}2*&}oEba*^#V!R!ͤ"" * *^#V!Rfzʻa" **+*S* +n&s*!"* #~a** s#r**+!s""!I"!;"* !s* *^#Vs#r* n&Ecd*^#V!Ecd*S!+n&*Ѩ&EhbͶ7cd*S*^#V!R+n&!-Eʷc*S*^#V!R+n&!-͒Eʴc!}2*^#V!R"*&}o*!}oE]c*S*+n&! ͥ*S*+n&!}o}2*&}oEZc*!R"b*&Eʴc*S*+n&!͒Eʴc**^#V!Rs#r* !scd*S*^#V!R+n&́*Ϩ&͑!/͑O}oEcd*!+n&́!-͑*Ϩ&͑!/͑O}oEcd*S*^#V+! s**^#V!s#r* !s!"**+n&!͒Ed*S*^#V+**+n&s**^#V!s#r*!"wd*^#V!R*Ĩ*Ȩ*s*}*^#V* ^#VR"}"v*!Ee!*v#fz|e"k*v*k+n&]9*k#Ye~g!I"n!;"p*p n&EIf*v!+n&*Ѩ&EʼeͶ7If*v!+n&! EeͶ7[If*v!+n&! EIfͶ7!"k*v!k%}2m*Ĩ*k!R"Ĩ*k!EIf*v*k*v!L&*v!+n&! ͒Eg*v!ja*j&Eg!͌_Ͷ7!"k!!k%}2m*p!s#r!*k+n&!͒Eg*pS*p^#V+!*k+n&s*k!"k*p*p^#V!s#rçfIg*&}o*pS!+n&*Ѩ&}oEIgͶ7*p *v!+n&! s*p n&E~g!"}*ƨ"Ĩ!;"*!s#r* !s#r*S!+!s!I"ޝ!;"* !s*ޝO!s#r*ޝS!s#r!"!"!"!"!"¨!"!"}!!))))))))"֝*&*֝s!!*֝!L&!!))))))))"֝*֝ !s*&*֝s!!*֝!L&!!))))))))IQ!!))))))));ͳ7!"ƨ!P"Ȩ!}2!}2!}2!}2ͭ7Ͱ7!!afzWi"ʨ!i*ʨ+)!s#r*ʨ#1i!!fzʄi"ʨ!*ʨ*ʨs*ʨ#bi!{}2Ө!}}2Ҩ!^}2Ѩ!%}2Ш!}2Ϩ!$}2Ψ!\}2ͨ!&}2̨!M%definew$!2M%tracew$!M%fillw$!M%sourcew$!M%includew$!M%divertw$!k͛ͺ%MACRO - Text-oriented macro processor ͛ͺby Jon Dart ... Version !7:!ͪ ͛ ͛ !)!MInput file name? w$!!!!b6}2*&Eʝq͛ !MOutput file name? w$!!!!b6}2*&Em͛ g!Eͩ\!͒EPq!E!+n&}2Ԩ!*Ԩ&n&!U!*Ԩ&n&!L}o*Ԩ&*Ш&}o*Ԩ&*ͨ&}oEn!E!1=Eʽn*&*n&!͒}oEʳn*kún!E͉_n!E͉_Mq*Ԩ&*Ө&Eʢo!"!Eͩ\}2Ԩ*Ԩ&*Ҩ&Eo*!R"xo*Ԩ&*Ө&E5o*!"xo*Ԩ&!Exo!Mmissing right quote char.w$!͹7!"*!Eʏo!E͉_*!EnMq*!Eʼo!E͉_Mq*Ԩ&!(Ep!{*+)))"*^#V!Eo!E͉_**^#V!s#rMq*Ԩ&!)Ep!{*+)))"**^#V!Rs#r*^#V!Eʃp!E͉_p!]9!{*+)))dY*^#V"!*+n&"*!R"Mq*Ԩ&!,!{*+)))^#V!}oEFq!]9!{*+)))"**^#V!s#r*!!9Mq!E͉_m*!E~q!M ")" expectedw$!͹7*&EʋqͶ7*&}3*&}3*&}oEem s#r*!!9p!E͉_m*!E!M ")" expectedw$!͹7*MACRO version 5.5, 26-May-85 Specifications: This macro processor resembles somewhat the one in Kernighan and Plauger's book Software Tools in Pascal. However, some additions and modifications have been made to allow it to be used with text files as well as programs. Many details of operation and implementation are also different. The macro processor scans its input file looking for macros. A macro is defined by a command of the form: %define(name,replacement) "Name" must contain only alphanumeric characters, and it must start with either a letter or a special character defined as MACROCHAR in the source (currently set to "%"). "Name" must not contain any embedded blanks. Whenever "name" is encountered in the input file, separated from other text by blanks or by non-alphabetic characters, it will be replaced by the string "replacement". Macros are allowed to have up to ten arguments, which may be included in the replacement text by specifying them as $1, $2, etc. (a la Kernighan and Plauger). If a carriage return immediately follows the end of a built-in that does not produce any output (such as %define) then the carriage return will not be written to the output file. Thus, lines containing only %define or similar statements produce no output. Text within special "quote" characters is copied literally to the output, without any macro expansions (the quote characters are stripped off). The default quote characters are "{" and "}". If the statement "%capsok(on)" appears in the source before the definition of a macro, then the macro processor will recognize the macro regardless of whether the first letter of its name is lower- or upper-case. If the macro name is found in the input file with an initial capital letter, then the replacement text will also be made to have an initial capital letter. If the macro appears with a small initial letter, the replacement text will also have a small initial letter. Without the "%capsok" option, macro names in the source must exactly match the name in the definition (including case of all letters) in order for replacement to occur. Note that the %capsok option is set at definition time, not at the time the macro is being evaluated. This option can be selectively disabled for some some macros by setting "%capsok(off)". Other built-ins: %append(filename) Following this command, all output from MACRO is tacked onto the end of the named file, instead of going to the normal output file. The file must already exist on the disk. %divert(filename) Following this command, all output from MACRO is written to the file name given. If the file does not already exist on the disk, it is created. %enddivert Marks the end of a diversion begun with %divert or %append, resumes writing to the standard output file. %expr(expr) Causes the current value of "expr" to be written to the output file as a decimal number. "Expr" is an expression consisting of register variables and/or integers (see %register for details). %fill(on) or %fill(off) Allows or disables filling of text (default is off). If filling is on, as many words are packed into output lines as will fit within the right and left margins (default 0 and 79). MACRO is fairly sophisticated in its handling of hyphens, slashes etc. "Hard hyphen" and "ghost hyphen" characters are recognized and treated appropriately (these can be changed with the %spchars built-in). Lines beginning with a space or a word-processor command character (user-settable) will be output without filling. %ifelse(a,b,c,d) If a=b, expands to c, otherwise expands to d. The carriage return following this statement is suppressed if it produces no output. %include(filename) %source(filename) Starts reading from "filename" instead of the original input file. Reading resumes from the input file after the end of file on "filename". "%source" is a synonym for "%include". %macros(on) or %macros(off) Turns on and off expansion of macros. Default is on. If %macros(off) is specified, built-ins are still evaluated, but macros defined with %define statements are treated as text and output without replacement. %margins(left,right) sets right and left margins %quotes(char1,char2) sets quote characters to char1 (left) and char2 (right). %register(#n,expr) Sets one of ten register variables (#0 to #9) to the value of "expr", which may be any expression containing register variables (prefixed by #), integer constants and the operations +, -, \ (integer division), *, and % (modulus). %spchars(macrochar,argsym,sepsym,wcmdchar,hardhy,ghosthy) sets MACRO to recognize the following special characters: macrochar non-alphanumeric character, can start a macro name (default "%") argsym identifies a macro argument in the replacement portion of a "%define" macro. Default "$". sepsym separator, can be used to set off a macro embedded within a word. It will not be copied to the output. Default "\". wcmdchar word-processor command character. Lines beginning with this character are not filled. Default "^". hardhy hard hyphen character, cannot be removed during filling as a regular hyphen can. Default is Control-Underline. ghosthy "ghost hyphen", may be embedded in words, is ignored by MACRO (but will be copied to the output, unless the word of which it is a part is a macro). Default "&". %trace(on) or %trace(off) If on, shows the expansion of each macro on the screen, plus other stats. Default is off. %translit(char1,char2) Causes char1 to be replaced by char2 everywhere in the output. Ranges of characters can be specified for both char1 and char2: for example, "%translit(a-z,A-Z)" maps all lower-case characters to upper-case. Trans- literation is always done after macro expansion. Consequently, using %translit to redefine any of the special characters, such as argsym or sepsym (see under %spchars for list) does not affect the operation of MACRO-- the same characters are still recognized as special, and will be transliterated only if they are inside quotes or are otherwise part of a string that would normally appear in the output file. If you want to specify the quote characters for either char1 or char2, I recommend the following way: %quotes(",') (set new quote characters) %translit("{',[) (translit { to [) %quotes({,}) (reset old quote characters) Known bugs/limitations: 1. A command of the form %define(x,x) or even %define(x,something x) causes an infinite loop. To avoid this, if the replacement string contains the name string as a substring, enclose the replacement string in 2 layers of quotes (the first gets stripped off during definition, the second gets stripped off at evaluation time). E.g.: %define(very,{{very, very}}) 2. Because macros are evaluated immediately as they are encountered, be careful about using built-ins inside of an %ifelse macro. E.g. the statement: %ifelse(draft,true,%include(outline.txt)) may cause an "argument too long" error, because MACRO will evaluate the %include macro and try to store the whole contents of "outline.txt" in its argument buffer (before even checking to see if draft=true!). This problem can be avoided by putting quote characters around the %include macro. Similarly, a statement like: %ifelse(draft,true,%trace(on)) will always turn tracing on (because %trace is evaluated before %ifelse), unless the %trace macro is enclosed in quotes. 3. MACRO does not currently support nested %source, %divert or %append commands, but this may be added in a future version. The hooks are in the program, it is just not implemented (or tested). 4. Tab characters (^I) in the input are copied directly to the output. This will result in improperly spaced output if filling is enabled and the input file contains tabs. It is recommended that input files not contain tabs if filling is to be used. 5. The input file should be in ASCII format (i.e. lines should end with a carriage return or line feed or both). MACRO doesn't (yet) know what to do with WordStar-style "soft" carriage returns. 6. The text of all macros (name and replacement) is currently limited to 5500 characters. The total length of all arguments in a single macro call must not exceed 500 characters. Macros can be nested 15 deep. Input lines may be up to 132 characters long; output lines may be up to 160 characters. All of these limitations are set by constants defined in file "MACRO1.PAS".