Years ago, my former student and now colleague Arnold Pears
used HITECO 1 as a basis for writing
a small self-compiling compiler which uses Pascal
as the target language.
I am grateful for his permission to include his work,
with only minor reformatting.
The compiler written in itself is as follows.
Note again that the left half essentially specifies the grammar
and the make clauses in the right half specify the semantics.
program {a} = procedure {x}
switch
case {statement} make {writeln (module:m,';');}
case {variable} make {writeln ('Runtime(1);');}
case {quote} make {writeln ('Runtime(2);');}
case {code} make {writeln ('Runtime(3);');}
case {message} make {writeln ('Runtime(4);');}
case {procedure} make {writeln ('Runtime(5);');}
case {make} code
case {check} ( make {write('if (buffer <> ''');};
quote; make {writeln (''') ');};
make {write(' then writeln (''error ');};
message; make {writeln (''');')} )
case {repeat} ( make {writeln ('scan;');};
make {write('while (buffer = ''');};
quote; make {writeln (''') do');};
make {writeln ('begin');};
statement; make {writeln ('scan;');};
make {writeln ('end;');} )
case {(}
( statement;
repeat {;}
statement;
check {)}
{) expected in compound statement} )
case {program} ( make {write('program ');};
quote; make {writeln ('(input,output);');};
make {writeln ('var');};
make {write('module,buffer:');};
make {writeln (' packed array[1..80] of char;');};
make {writeln ('c,m: integer;');};
make {writeln ('%include "runsup.h"');};
make {scan;};
check {=}
{= expected in procedure statement};
make {scan;};
check {procedure}
{procedure expected after =};
make {write('procedure ');};
variable; make {writeln (';');};
make {writeln ('begin');};
make {writeln ('scan;');};
make {writeln ('if not eof then');};
make {writeln ('begin');};
statement; make {writeln ('end;');};
make {writeln ('end;');};
make {writeln ('begin');};
make {writeln ('repeat');};
procedure; make {writeln (';');};
make {writeln ('until eof;');};
make {writeln ('end.');} )
case {switch} (
repeat {case} ( make {write('if buffer = ''');};
quote; make {writeln (''' then ');};
make {writeln ('begin');};
statement; make {writeln ('end');};
make {writeln (' else ');} ) ;
check {default}
{default expected in switch statement};
make {write('writeln (''');};
message; make {writeln (''');');} )
default
{illegal statement}
.
A readable Pascal version is as follows:
program a (input,output);
var
buffer,module: packed array [1..80] of char;
m,c:integer;
%include 'runsup.h'
procedure x;
begin
scan;
if not eof then
begin
if buffer = 'statement' then writeln(module:m,';')
else if buffer = 'variable' then writeln('Runtime(1);')
else if buffer = 'quote' then writeln('Runtime(2);')
else if buffer = 'code' then writeln('Runtime(3);')
else if buffer = 'message' then writeln('Runtime(4);')
else if buffer = 'procedure' then writeln('Runtime(5);')
else if buffer = 'make' then Runtime(3)
else if buffer = 'check' then
begin
write ('if (buffer <> ''');
Runtime(2);
writeln(''')');
write('then writeln (''error ');
Runtime(4);
writeln(''');');
end
else if buffer = 'repeat' then
begin
writeln('scan;');
write('while (buffer = ''');
Runtime(2);
writeln(' '') do');
writeln('begin');
x;
writeln('scan;');
writeln('end;');
end
else if buffer = '( ' then
begin
x;
scan;
while (buffer = '; ') do
begin x; scan end;
if (buffer <> ') ') then
writeln ('error ) expected after (');
end
else if buffer = 'program' then
begin
write('program ');
Runtime(2);
writeln(' (input,output);');
writeln('var');
writeln('module,buffer: packed array [1..80] of char;');
writeln('m,c:integer;');
writeln ('%include "runsup.h"');
scan;
if (buffer <> '= ' )
then writeln('error = expected in procedure statement');
scan;
if (buffer <> 'procedure')
then writeln('procedure expected after =');
write ('procedure ');
Runtime(1);
writeln(';');
writeln('begin');
writeln ('scan;');
writeln ('if not eof then');
writeln ('begin');
x;
writeln('end;');
writeln('end;');
writeln ('begin');
writeln ('repeat');
Runtime(5);
writeln(';');
writeln ('until eof;');
writeln ('end.');
end
else if buffer = 'switch' then
begin
scan;
while (buffer = 'case') do
begin
write('if buffer = ''');
Runtime(2);
writeln(''' then ');
writeln('begin');
x;
writeln('end');
writeln('else');
scan
end;
if (buffer <> 'default') then
writeln('error default expected ');
write('writeln(''');
Runtime(4);
writeln(''');');
end
else writeln('illegal statement ',buffer);
end
end;
begin
repeat
x;
until eof;
end.
Like HITECO 1, this version relies on some runtime support also written in Pascal:
procedure scan;
var
ch,delim:char;
begin
buffer := ' ';
c := 1;
read(ch);
while (ch = ' ') or (ch = chr(13)) do read (ch);
if ch = '{' then
begin delim := '}'; read(ch); end
else
delim := ' ';
repeat
buffer[c] := ch; c := c + 1; read(ch)
until (ch = delim) or ((input^ = ';') and (delim = ' '));
if (input^ = ';') and (delim = ' ') then buffer[c] := ch;
if ch = '.' then buffer := ' '
end;
procedure Runtime (i:integer);
begin
case i of
1:
begin
scan;
write(buffer:c);
module := buffer;
m := c;
end;
2,4:
begin
scan;
write(buffer:c);
end;
3:
begin
scan;
writeln(buffer:c);
end;
5:
write(module:m);
end
end;
The Pascal version with its included runtime support has to be
compiled, linked and run.
As its input it needs the compiler written in itself,
and it produces as output another Pascal version
which is not so readable but functionally equivalent.
This can be compiled, linked and run in the same way
to produce another not so readable Pascal version, and so on.
Versions 2 and 3, and also all later consecutive versions are
fully identical.
Versions 1 and 2 are almost identical, except for a few extra blank
characters.
It requires considerable understanding of self-compilation
and very careful analysis of the source and the Pascal version
to see why this should be the case.
Then to fix it is easy even if perhaps a little pointless.
But it should provide happy sleuthing.