Skip to content

Instantly share code, notes, and snippets.

@Garmelon
Last active June 9, 2023 09:55
Show Gist options
  • Save Garmelon/cb9275c021ad88325d86a192b94cc20b to your computer and use it in GitHub Desktop.
Save Garmelon/cb9275c021ad88325d86a192b94cc20b to your computer and use it in GitHub Desktop.
Brainfuck interpreter written in PostScript
% 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
( ) 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