Last active
June 9, 2023 09:55
-
-
Save Garmelon/cb9275c021ad88325d86a192b94cc20b to your computer and use it in GitHub Desktop.
Brainfuck interpreter written in PostScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
% Brainfuck interpreter written in PostScript. | |
% | |
% Written in about 2 to 3 hours around midnight. This includes the time spent | |
% learning PostScript, which is now the first stack-based programming language | |
% I've actually used. Because of this, the code is pretty ugly (even for | |
% PostScript), but hey, it manages to correctly interpret the "Hello world" | |
% example from the Wikipedia page on Brainfuck. | |
% | |
% For best results, run via `ghostscript brainfuck.ps`. Enter your brainfuck | |
% code at the `brainfuck> ` prompt and your program input (if necessary) at the | |
% `input> ` prompt. Enjoy your program output in the bottom left of your page. | |
% Prepare a font for printing | |
/Courier findfont 16 scalefont setfont | |
16 16 moveto | |
% Generic helper functions | |
/readline { | |
1 dict begin | |
print flush | |
/f (%lineedit) (r) file def | |
f bytesavailable -1 eq { | |
() | |
} { | |
f f bytesavailable string readstring | |
pop | |
} ifelse | |
end | |
} def | |
/append { | |
10 dict begin | |
/last exch def | |
/s1 exch def | |
/s2 s1 length 1 add string def | |
0 1 s1 length 1 sub { | |
/i exch def | |
s2 i s1 i get put | |
} for | |
s2 s1 length last put | |
s2 | |
end | |
} def | |
% User input for "," operator | |
/input () def | |
/input_idx 0 def | |
/input_get { | |
% input_idx >= length(input) | |
input_idx input length ge { | |
% We need to get new input | |
/input (input> ) readline 10 append def | |
/input_idx 0 def | |
} if | |
% Prepare return value | |
input input_idx get | |
% idx += 1 | |
/input_idx input_idx 1 add def | |
} def | |
% User output for "." operator | |
/output_show { | |
1 dict begin | |
/char exch def | |
/str 1 string def | |
str 0 char put | |
str show | |
} def | |
% Initialize important variables | |
/program (brainfuck> ) readline def | |
/program_idx 0 def | |
/tape 0 dict def | |
/tape_idx 0 def | |
/jumps 0 dict def | |
% Fill jump table | |
0 1 program length 1 sub { | |
/i exch def | |
/char program i get def | |
char 91 eq { % char == "[" | |
i | |
} { | |
char 93 eq { % char == "]" | |
/j exch def | |
jumps i j put | |
jumps j i put | |
} if | |
} ifelse | |
} for | |
% Some useful functions | |
/tape_set { | |
tape exch | |
tape_idx exch | |
put | |
} def | |
/tape_get { | |
tape tape_idx known { | |
tape tape_idx get | |
} { | |
0 | |
} ifelse | |
} def | |
/tape_inc { | |
tape_get | |
1 add | |
tape_set | |
} def | |
/tape_dec { | |
tape_get | |
1 sub | |
tape_set | |
} def | |
/tape_idx_right { | |
/tape_idx tape_idx 1 add def | |
} def | |
/tape_idx_left { | |
/tape_idx tape_idx 1 sub def | |
} def | |
/next_command { | |
/program_idx program_idx 1 add def | |
} def | |
/jump_if_zero_else_next_command { | |
tape_get | |
0 eq { | |
/program_idx jumps program_idx get def | |
} { | |
next_command | |
} ifelse | |
} def | |
/jump_if_nonzero_else_next_command { | |
tape_get | |
0 ne { | |
/program_idx jumps program_idx get def | |
} { | |
next_command | |
} ifelse | |
} def | |
/read_input { | |
input_get | |
tape_set | |
} def | |
/print_output { | |
tape_get | |
output_show | |
} def | |
/command_table 0 dict def | |
command_table 43 {tape_inc next_command} put % + | |
command_table 45 {tape_dec next_command} put % - | |
command_table 60 {tape_idx_left next_command} put % < | |
command_table 62 {tape_idx_right next_command} put % > | |
command_table 91 {jump_if_zero_else_next_command} put % [ | |
command_table 93 {jump_if_nonzero_else_next_command} put % ] | |
command_table 44 {read_input next_command} put % , | |
command_table 46 {print_output next_command} put % . | |
% Execute brainfuck | |
{ | |
program print | |
program_idx 10 string cvs print | |
(\n) print | |
% Check if program_idx is still in range | |
program_idx program length ge { | |
exit | |
} if | |
/command program program_idx get def | |
command_table command known { | |
(Command known\n) print | |
/foo command_table command get def | |
foo | |
} { | |
(Command unknown\n) print | |
next_command | |
} ifelse | |
} loop |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment