(*
%%% ====================================================================
%%% @TeX-Utility{
%%% filename = "pp.exe",
%%% version = "1.01",
%%% date = "16 Feb 1994",
%%% time = "11:01:23 BST",
%%% author = "Mike Piff",
%%% address = "Dr M. J. Piff
%%% University of Sheffield
%%% School of Mathematics and Statistics
%%% Hicks Building
%%% Hounsfield Road
%%% SHEFFIELD S3 7RH
%%% England",
%%% telephone = "+44 742 824431",
%%% email = "
[email protected] (Janet)",
%%% keywords = "Pascal,Modula2,formatting",
%%% supported = "yes",
%%% checksum = "",
%%% docstring = "
%%% PP is a program that takes a Modula-2 or Pascal
%%% program, and converts it into a \LaTeX/plain \TeX\ file
%%% ready to input into your document.
%%% The source code is included in this distribution,
%%% together with an MS-DOS executable.
%%%
%%% As Modula-2 is case-sensitive, whereas Pascal is not,
%%% it is necessary to inform the program which language is
%%% being used. Two customization files MOD.RES and PAS.RES
%%% are included. It is possible that customization files
%%% can be built for other languages too---I haven't tried,
%%% but please let me know if you produce any.
%%% The format of each file is
%%%
%%% No. of keywords
%%% optional * to indicate case-insensitive
%%% list of keywords
%%%
%%% The first thing the program does is prompt for the list of
%%% keywords. After that it asks for an input file and an output
%%% file, and that is it!
%%%
%%% ...almost. One problem with this kind of listing is that
%%% \TeX\ has trouble breaking a program at a meaningful place,
%%% eg, after declarations. Thus, we need to give it some clues.
%%% A BLANK LINE in the input indicates a good breakpoint, but
%%% DOES NOT result in blank lines in the output. However,
%%% n>1 blank lines result in n-1 blank output lines. Thus,
%%% space your program carefully!
%%%
%%% Another problem is the way that Modula-2 and Pascal delimit
%%% comments. Whereas Wirth used [ ] for sets in Pascal,
%%% contrary to all mathematical practices, and { } or ({}* *{})
%%% for comments, in Modula-2 he
%%% reserved { } for sets. Thus, the results will look better
%%% if you use ({}* *{}) for comments in Pascal rather than
%%% { }. Maybe this could be added to the customization file,
%%% but I haven't found the need yet.
%%%
%%% To make your comments come out reasonable, assume that you
%%% are in horizontal (text) mode inbetween ({}* and *{}). Thus,
%%% write
%%% (* and so $2^n\geq\log m$ *)
%%% rather than
%%% ({}* and so 2$\uparrow$n>= log m *{})
%%% If you want to include a bit of code inside a comment,
%%% precede it and follow it by **. Thus
%%% ({}* Or use
%%% **
%%% PROCEDURE xxx;FORWARD;
%%% **
%%% if yours is a single pass compiler *{})
%%%
%%% The output of PP is similar to that of WEB, say, but your
%%% program is free-standing and you do not need to learn to
%%% use WEB. Generally, horizontal spacing is respected in the
%%% output, but you should use spaces rather than tabs for this
%%% to work. I have used := rather than <- for assignments.
%%% ",
%%% }
%%% ====================================================================
*)
MODULE PP;
FROM InOut IMPORT OpenInput, CloseInput, EOL, Read, Write, WriteLn,
ReadCard, OpenOutput, CloseOutput, ReadString, WriteString, Done;
CONST
minnestinglevel=0; maxnestinglevel=100;
minlineindex=0; maxlineindex=4000;
minidentindex=0; maxidentindex=99;
zero='0'; seven='7'; nine='9';
ampersand='&'; lbrace='{'; rbrace='}';
caret='^'; bar='|'; tilde='~';
equalch='='; squote="'"; dquote='"';
less='<'; greater='>'; space=' ';
hatch='#'; period='.';
plusch='+'; minusch='-'; star='*';
colonch=':'; underscore='_';
lpar='('; rpar=')'; tab=11C; eof=32C;
TYPE
identlengths=CARDINAL[minidentindex..maxidentindex];
nonterminals=(Next,
integernum,realnum,realexpt,realp,
realdots,octno,charno,hexno,
dot,dots,becomes,colon,
identifier,lt,leq,gt,geq,neq,
plus,minus,equal,logand,
bset,eset,deref,lognot,logor,
leftpar,starcom,bcomment,ecomment,stars,
other,sstrg,dstrg,spaces,
(* the rest are transient states *)
Fail,reale,realepm,oct,hex,
sqstring,dqstring);
terminals=CHAR;
states=ARRAY nonterminals OF BOOLEAN;
indices=CARDINAL[minlineindex..maxlineindex];
stringarrays=ARRAY indices OF terminals;
lines=RECORD
chr:stringarrays;
start,posn:indices;
END;
modes=(texmode,progmode);
nestinglevels=CARDINAL[minnestinglevel..maxnestinglevel];
modesatlevels=ARRAY nestinglevels OF modes;
VAR
line:lines;
EOF:BOOLEAN;
commtlev:nestinglevels;
modeatlevel:modesatlevels;
PROCEDURE BeginProgram;
BEGIN
WriteString('\par\begingroup\parindent=0pt{}\parskip=0pt plus1pt\relax');
WriteLn;
WriteString('\obeylines\obeyspaces\def {\hskip0.3em\relax}%');WriteLn;
END BeginProgram;
PROCEDURE EndProgram;
BEGIN
WriteString('\endgroup%'); WriteLn;
END EndProgram;
MODULE TableHandler;
IMPORT identlengths, lines, minidentindex, maxidentindex,
WriteString, WriteLn, OpenInput, CloseInput, ReadCard, Read;
EXPORT reserved;
CONST
minresw=0; maxresw=100;
minrwlength=0; maxrwlength=32;
space=' '; star='*';
TYPE
resnos=CARDINAL[minresw..maxresw];
rwlengths=CARDINAL[minrwlength..maxrwlength];
reservedwords=ARRAY rwlengths OF CHAR;
tables=ARRAY resnos OF reservedwords;
identifierstrings=ARRAY identlengths OF CHAR;
VAR
table:tables;
lastresw:resnos;
upcase:BOOLEAN;
PROCEDURE SetUpTable(VAR table:tables);
VAR
i:CARDINAL;
k:resnos;
PROCEDURE ReadReserved(VAR r:reservedwords);
VAR
i:rwlengths;
ch:CHAR;
BEGIN
i:=minrwlength;
REPEAT Read(ch) UNTIL ch>space;
IF ch=star THEN
upcase:=TRUE;
REPEAT Read(ch) UNTIL ch>space;
END;
WHILE ch>space DO
r[i]:=ch;
INC(i);
Read(ch);
END;
r[i]:=0C;
END ReadReserved;
BEGIN
WriteString('PP Copyright (C) 1992 by Mike Piff, ');
WriteString('10/29/92 01:27pm');
WriteLn;
WriteString('Input filename for reserved words');WriteLn;
OpenInput('RES');
ReadCard(i); lastresw:=resnos(i+minresw-1);
upcase:=FALSE;
FOR k:=minresw TO lastresw DO
ReadReserved(table[k]);
END;
CloseInput;
END SetUpTable;
PROCEDURE reserved(VAR e:lines):BOOLEAN;
VAR
s:identifierstrings;
min,max,test:resnos;
i:CARDINAL;
TYPE
orders=(lessthan,equalto,greaterthan);
PROCEDURE order(VAR s1,s2:ARRAY OF CHAR):orders;
VAR
i:CARDINAL;
BEGIN
i:=0;
LOOP
IF (s1[i]<s2[i]) THEN RETURN lessthan; EXIT;
ELSIF (s1[i]>s2[i]) THEN RETURN greaterthan; EXIT;
ELSIF (s1[i]=0C) & (s2[i]=0C) THEN RETURN equalto; EXIT;
END;
INC(i);
END;
END order;
VAR
ch,cch:CHAR;
BEGIN
WITH e DO
FOR i:=start TO posn-1 DO
ch:=chr[i]; cch:=CAP(ch);
IF (cch<'A') OR (cch>'Z') THEN RETURN FALSE; END;
IF upcase THEN
s[i-start]:=cch;
ELSE
s[i-start]:=ch;
END;
END;
s[posn-start]:=0C;
END;
min:=minresw; max:=lastresw;
LOOP
IF (min>max) THEN RETURN FALSE; END;
test:=(min+max)DIV 2;
CASE order(s,table[test]) OF
lessthan:
IF test=minresw THEN
RETURN FALSE;
ELSE
max:=test-1;
END;|
equalto:
RETURN TRUE;|
greaterthan:
IF test=lastresw THEN
RETURN FALSE;
ELSE
min:=test+1;
END;
END;
END;
END reserved;
BEGIN
SetUpTable(table);
END TableHandler;
PROCEDURE ReadLine(VAR s:ARRAY OF CHAR);
CONST Ignorech=12C;
VAR i:CARDINAL; ch:CHAR;
BEGIN
s[0]:=0C; EOF:=FALSE; i:=minlineindex;
LOOP
IF i>(HIGH(s)-1) THEN EXIT END;
Read(ch);
IF ch<>Ignorech THEN
IF ch=EOL THEN EXIT END;
IF ch=eof THEN EOF:=TRUE;EXIT END;
IF ~Done THEN EOF:=TRUE;EXIT END;
IF ch=tab THEN ch:=space; END;
s[i]:=ch; INC(i);
END;
END;
s[i]:=0C;
END ReadLine;
PROCEDURE Clear(VAR s:states);
VAR
n:nonterminals;
BEGIN
FOR n:=MIN(nonterminals) TO MAX(nonterminals) DO
s[n]:=FALSE;
END;
END Clear;
PROCEDURE Initialise(VAR e:lines);
BEGIN
WITH e DO
posn:=minlineindex; start:=minlineindex;
END;
END Initialise;
PROCEDURE MakeTransition(VAR state:states;t:terminals;VAR made:BOOLEAN;
VAR nt:nonterminals);
VAR
newstate:states;
n:nonterminals;
PROCEDURE digit(t:terminals):BOOLEAN;
BEGIN
RETURN (t>=zero) & (t<=nine);
END digit;
PROCEDURE octal(t:terminals):BOOLEAN;
BEGIN
RETURN (t>=zero) & (t<=seven);
END octal;
PROCEDURE hexadecimal(t:terminals):BOOLEAN;
BEGIN
t:=CAP(t);
RETURN ((t>=zero) & (t<=nine)) OR ((t>='A') & (t<='F'));;
END hexadecimal;
PROCEDURE letter(t:terminals):BOOLEAN;
BEGIN
t:=CAP(t);RETURN(((t>='A') & (t<='Z')) OR (t=underscore));
END letter;
PROCEDURE setstate(t:nonterminals);
BEGIN
newstate[t]:=TRUE;made:=TRUE;nt:=t;
END setstate;
BEGIN
Clear(newstate); made:=FALSE;
IF modeatlevel[commtlev]=texmode THEN
FOR n:=MIN(nonterminals) TO MAX(nonterminals) DO
IF state[n] THEN
CASE n OF
Next:
IF t=star THEN
setstate(starcom);
ELSIF t=lbrace THEN
setstate(bset);
ELSE
setstate(other);
END;|
leftpar: IF t=star THEN setstate(bcomment); END;|
starcom:
IF t=rpar THEN setstate(ecomment);
ELSIF t=star THEN setstate(stars);
END;|
stars: IF t=star THEN setstate(stars); END;
ELSE
END;
END;
END
ELSE
FOR n:=MIN(nonterminals) TO MAX(nonterminals) DO
IF state[n] THEN
CASE n OF
Next:
IF digit(t) THEN
setstate(integernum); setstate(hex);
IF octal(t) THEN setstate(oct); END;
ELSIF letter(t) THEN setstate(identifier);
ELSE
CASE t OF
less: setstate(lt);|
plusch: setstate(plus);|
minusch: setstate(minus);|
equalch: setstate(equal);|
ampersand: setstate(logand);|
lbrace: setstate(bset);|
rbrace: setstate(eset);|
caret: setstate(deref);|
tilde: setstate(lognot);|
bar: setstate(logor);|
greater: setstate(gt);|
hatch: setstate(neq);|
lpar: setstate(leftpar);|
star: setstate(starcom);|
period: setstate(dot);|
squote: setstate(sqstring);|
dquote: setstate(dqstring);|
colonch: setstate(colon);
ELSE setstate(other);
END;
END;|
integernum:
IF digit(t) THEN setstate(integernum);
ELSIF t=period THEN setstate(realp);
END;|
realp:
IF t=period THEN setstate(realdots);
ELSIF digit(t) THEN setstate(realnum);
ELSIF t='E' THEN setstate(reale);
END;|
oct:
IF octal(t) THEN setstate(oct);
ELSIF CAP(t)='B' THEN setstate(octno);
ELSIF CAP(t)='C' THEN setstate(charno);
END;|
hex:
IF hexadecimal(t) THEN setstate(hex);
ELSIF CAP(t)='H' THEN setstate(hexno);
END;|
realnum:
IF digit(t) THEN setstate(realnum);
ELSIF t='E' THEN setstate(reale);
END;|
reale:
IF digit(t) THEN setstate(realexpt);
ELSIF (t=plusch) OR (t=minusch) THEN setstate(realepm);
END;|
realepm:
IF digit(t) THEN setstate(realexpt); END;|
realexpt:
IF digit(t) THEN setstate(realexpt); END;|
dot:
IF t=period THEN setstate(dots); END;|
identifier:
IF digit(t) OR letter(t) THEN setstate(identifier); END;|
lt:
IF t=equalch THEN setstate(leq);
ELSIF t=greater THEN setstate(neq);
END;|
gt:
IF t=equalch THEN setstate(geq); END;|
leftpar:
IF t=star THEN setstate(bcomment); END;|
starcom:
IF t=rpar THEN setstate(ecomment);
ELSIF t=star THEN setstate(stars);
END;|
stars: IF t=star THEN setstate(stars); END;|
sqstring:
IF t=squote THEN
setstate(sstrg);
ELSE
setstate(sqstring);
END;|
dqstring:
IF t=dquote THEN
setstate(dstrg);
ELSE
setstate(dqstring);
END;|
spaces:
IF t=space THEN setstate(spaces); END;|
colon:
IF t=equalch THEN setstate(becomes); END;
ELSE
END;
END;
END;
END;
state:=newstate;
END MakeTransition;
PROCEDURE ProcessNextLexeme(VAR line:lines);
VAR
s:states;
made:BOOLEAN;
PROCEDURE Write_Lexeme(VAR s:lines);
VAR i:indices;
ch:CHAR;
BEGIN
WITH s DO
FOR i:=start TO (posn-1) DO
ch:=chr[i];
IF ch=underscore THEN
Write('\');Write('_');
ELSE
Write(ch);
END;
END;
END;
END Write_Lexeme;
PROCEDURE WriteReal(VAR s:lines);
VAR i:indices;
BEGIN
WITH s DO
Write('$');
i:=start;
WHILE chr[i]<>'E' DO Write(chr[i]); INC(i); END;
WriteString('{\cdot}10^{');
FOR i:=i+1 TO (posn-1) DO Write(chr[i]); END;
WriteString('}$');
END;
END WriteReal;
PROCEDURE WritePrime;
BEGIN
WriteString("$'$");
END WritePrime;
VAR
nt:nonterminals;
BEGIN
WITH line DO
Clear(s); s[Next]:=TRUE; start:=posn; made:=TRUE;
WHILE made DO
MakeTransition(s,chr[posn],made,nt);
IF made THEN INC(posn); END;
END;
IF nt=realdots THEN DEC(posn,2); nt:=realnum; END;
IF modeatlevel[commtlev]=texmode THEN
IF nt=bcomment THEN
INC(commtlev);
modeatlevel[commtlev]:=texmode;
WriteString('\noindent($*$');
ELSIF nt=ecomment THEN
DEC(commtlev);
WriteString('$*$)');
IF modeatlevel[commtlev]=progmode THEN BeginProgram; END;
ELSIF nt=stars THEN
modeatlevel[commtlev]:=progmode;
BeginProgram;
ELSE
Write_Lexeme(line);
END;
ELSE
IF nt=stars THEN
modeatlevel[commtlev]:=texmode;
EndProgram;
ELSE
CASE nt OF
identifier:
IF reserved(line) THEN
WriteString('{\bf{}');
Write_Lexeme(line);
WriteString('}');
ELSE
WriteString('{\it{}');
Write_Lexeme(line);
WriteString('\/}');
END;|
becomes:
WriteString('$:=$');|
lt:
WriteString('$<$');|
leq:
WriteString('$\leq$');|
gt:
WriteString('$>$');|
geq:
WriteString('$\geq$');|
neq:
WriteString('$\neq$');|
plus:
WriteString('$+$');|
minus:
WriteString('$-$');|
equal:
WriteString('$=$');|
starcom:
WriteString('$\times$');|
bcomment:
EndProgram;
INC(commtlev);
modeatlevel[commtlev]:=texmode;
WriteString('($*$');|
ecomment:
EndProgram;
DEC(commtlev);
WriteString('$*$)');
IF modeatlevel[commtlev]=progmode THEN BeginProgram; END;|
logand:
WriteString('\&');|
bset:
WriteString('$\{$');|
eset:
WriteString('$\}$');|
deref:
WriteString('$\uparrow$');|
dots:
WriteString('$\,.\,.\,$');|
lognot:
WriteString('$\neg$');|
logor:
WriteString('$|$');|
sstrg:
WritePrime;
WriteString('\Verb');
Write_Lexeme(line);
WritePrime;|
dstrg:
WritePrime;WritePrime;
WriteString("\Verb");
Write_Lexeme(line);
WritePrime;WritePrime;|
realexpt:
WriteReal(line);
ELSE
Write_Lexeme(line);
END;
END;
END;
END;
END ProcessNextLexeme;
PROCEDURE WriteVerb;
BEGIN
WriteString("\begingroup\catcode`\@=11");WriteLn;
WriteString("\gdef\@Makeother#1{\catcode`#1=12\relax}%");WriteLn;
WriteString("\gdef\Verb{\begingroup \catcode``=13 \@Noligs");WriteLn;
WriteString("\tt \let\do\@Makeother \dospecials \@sVerb}%");WriteLn;
WriteString("\gdef\@sVerb#1{\def\@tempa ##1#1{\leavevmode\null##1\endgroup}\@tempa}%");WriteLn;
WriteString("\begingroup\catcode``=13");WriteLn;
WriteString("\gdef\@Noligs{\let`\@Lquote}\endgroup");WriteLn;
WriteString("\gdef\@Lquote{\leavevmode{\kern\z@}`}\endgroup");WriteLn;
END WriteVerb;
BEGIN
commtlev:=minnestinglevel;
modeatlevel[commtlev]:=progmode;
WriteString('Give input file:');
OpenInput('MOD');
WriteString('Give output file:');
OpenOutput('TEX');
WriteVerb;
WriteString('\def\Filbreak{\vskip0in plus1in ');
WriteString('\penalty-50 \vskip0in plus-1in\relax}');
WriteLn;
WriteString('\Filbreak\medbreak');WriteLn;
BeginProgram;
WITH line DO
REPEAT
ReadLine(chr);
Initialise(line);
IF (chr[posn]=0C) & ~EOF THEN (* blank line *)
WriteString('\Filbreak%');
WriteLn;
ReadLine(chr);
Initialise(line);
WHILE (chr[posn]=0C) & ~EOF DO
WriteString('\vskip\baselineskip%');
WriteLn;
ReadLine(chr);
Initialise(line);
END;
END;
IF ~EOF THEN
WHILE chr[posn]#0C DO
ProcessNextLexeme(line);
END;
WriteLn;
END;
UNTIL EOF;
END;
EndProgram;
WriteString('\Filbreak\medbreak');WriteLn;
CloseInput; CloseOutput;
END PP.