Global Utilities

Self-reproducing programs in Joy

The following are solutions to the questions about self-reproducing Joy programs in Chapter 18:

sesame_f == [[duco] duco] ; sesame_g_h == [[unit dup car]] [ unit dup car ] ; sesame_i == [[pop] dip duco] duco ; sesame_j == [[swap] dip duco] duco ; sesame_k_l == [duco] duco dup ; sesame_m == [[false [not] infra dureco] [not] infra dureco ] ; sesame_n == [[[duco duco] duco duco] [duco duco] duco duco ] ;

Self-compilation into Pascal

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.