(* %%% ==================================================================== %%% @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 = "M.Piff@sheffield.ac.uk (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 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.