20
I made a Brainfuck interpreter within G'MIC (Shell-Like language for image processing)
(programming.dev)
Three things before I'll get to the relevant details.
- Brainfuck is a esoteric languages which uses 8 characters. I'll leave details here - https://en.wikipedia.org/wiki/Brainfuck
- G'MIC is a language largely inspired by bash languages and one other shell scripting language, and partly inspired by C++ for JIT compilation. It's two languages in one as in one outside of JIT and one inside of JIT. It's main purpose is image processing, and it can do 3D things too, basically image-related things. It's turing-complete, so making files has been done with it. Even making a executable compiled program is possible in practice (but, I would point to doing C++ and compile there instead).
- I am a G'MIC filters developer.
Anyways, I taken some time to code up a Brainfuck interpreter within G'MIC. It wasn't that hard to do once I understood what Brainfuck is as a language. I did one earlier than this, but I had to have users define inputs beforehand. Recently, I created rep_cin command to relieve users of doing that, and that is the closest to input()
within Python or std::cin
via C++.
Anyways, here's the code to my Brainfuck interpreter:
#@cli run_brainfuck_it: brainfuck_file,'_enforce_numbers_input={ 0=false | 1=true },_size_of_array>0
#@cli : Interprets Brainfuck code file within G'MIC brainfuck_interpreter.
#@cli : Default values: ,'_enforce_numbers_input=0','_size_of_array=512'
run_brainfuck_it:
skip ${2=0},${3=512}
it $1
_brainfuck_interpreter $2,$3
um run_brainfuck_it,run_brainfuck,_brainfuck_interpreter,_brainfuck_interpreter_byte_input
#@cli run_brainfuck: brainfuck_code,'_enforce_numbers_input={ 0=false | 1=true },_size_of_array>0
#@cli : Interprets Brainfuck code within G'MIC brainfuck_interpreter.
#@cli : Default values: ,'_enforce_numbers_input=0','_size_of_array=512'
run_brainfuck:
skip ${2=0},${3=512}
('$1')
_brainfuck_interpreter $2,$3
um run_brainfuck_it,run_brainfuck,_brainfuck_interpreter,_brainfuck_interpreter_byte_input
_brainfuck_interpreter:
# 1. Convert image into dynamic image
resize 1,{whd#-1},1,1,-1 ({h}) append y # Convert string images into dynamic image
name[-1] brainfuck_code # Name image into brainfuck_code
# 2. Remove unused characters
eval "
const brainfuck_code=$brainfuck_code;
for(p=h#brainfuck_code-2,p>-1,--p,
char=i[#brainfuck_code,p];
if(!(inrange(char,_'+',_'.',1,1)||(find('<>[]',char,0,1)!=-1)),
da_remove(#brainfuck_code,p);
);
);
if(!da_size(#brainfuck_code),
run('error inval_code');
);
da_freeze(#brainfuck_code);
"
# 3. Evaluate brackets
eval[brainfuck_code] >"
begin(level=0;);
i==_'['?++level:
i==_']'?--level;
if(level<0,run('error inv_bracks'););
end(if(level,run('error inv_bracks');););"
1x2 # Create 2 images of 1x1x1x1. One image is for storing print out characters, and the other is to allow inputs.
_arg_level=1
# 4. Create JIT code for executing brainfuck code.
repeat h#$brainfuck_code {
idx:=i[#0,$>]
if $idx==_',' code_str.=run('$0_byte_input[-2]\ $1');ind_list[ind]=i#-2; continue fi
if $idx==_'.' code_str.=da_push(#-1,ind_list[ind]); continue fi
if $idx==_'+' code_str.=ind_list[ind]++;ind_list[ind]%=256; continue fi
if $idx==_'-' code_str.=ind_list[ind]--;ind_list[ind]%=256; continue fi
if $idx==_'<' code_str.=if(!inrange(--ind,0,$2,1,0),run("'error out_of_bound'");); continue fi
if $idx==_'>' code_str.=if(!inrange(++ind,0,$2,1,0),run("'error out_of_bound'");); continue fi
if $idx==_'[' code_str.=repeat(inf,if(!ind_list[ind],break();); continue fi
if $idx==_']' code_str.=); fi
}
# 5. Execute created JIT code. v + and v - is used to change verbosity level, not part of JIT execution. e[] is used to print into console.
v +
eval >begin(ind=0;ind_list=vector$2(););$code_str;end(da_freeze(#-1););
v -
# 6. Print out executed code result
v + e[$^] "Brainfuck Output: "{t} v -
remove
_brainfuck_interpreter_byte_input:
repeat inf {
wait # For some reason, I had to add this to make this code work!
if $> rep_cin "Brainfuck Interpreter - Wrong Input! Insert Integer for Argument#"$_arg_level": "
else rep_cin "Brainfuck Interpreter - Enter Argument#"$_arg_level" (Integers Only): "
fi
if $1 input:=(${}%208)+_'0'
else input=${}
fi
if isint($input) break fi
}
if $1
v + e[$^] "Brainfuck Interpreter Inserted Argument#"$_arg_level": "{$input-_'0'} v -
else
input%=256
v + e[$^] "Brainfuck Interpreter Inserted Argument#"$_arg_level": "$input" :: "{`$input`} v -
fi
_arg_level+=1
f[-1] $input
And the CLI test:
C:\Users\User\Documents\G'MIC\Brainfuck Interpreter>gmic "brainfuck_interpreter.gmic" run_brainfuck \">,>,<<++++++[>-------->--------<<-]>[>[>+>+<<-]>[<+>-]<<-]>[-]>+>>++++++++++<[->-[>>>]++++++++++<<+[<<<]>>>>]<-<++++++++++>>>[-<<<->>>]<<<<++++++[>++++++++>[++++++++>]<[<]>-]>>[.<<]<[<<]>>.\",1
[gmic]./ Start G'MIC interpreter (v.3.3.3).
[gmic]./ Input custom command file 'brainfuck_interpreter.gmic' (4 new, total: 4806).
[gmic]./ Brainfuck Interpreter Inserted Argument#1: 31
[gmic]./ Brainfuck Interpreter Inserted Argument#2: 3
[gmic]./ Brainfuck Output: 93
[gmic]./ End G'MIC interpreter.
Brain fucked.
At least mine.