Skip Navigation

I made a Brainfuck interpreter within G'MIC (Shell-Like language for image processing)

Three things before I'll get to the relevant details.

  1. Brainfuck is a esoteric languages which uses 8 characters. I'll leave details here - https://en.wikipedia.org/wiki/Brainfuck
  2. 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).
  3. 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.
2