Skip to content

Instantly share code, notes, and snippets.

@pineapplemachine
Last active April 8, 2016 16:32
Show Gist options
  • Select an option

  • Save pineapplemachine/29ef80147d3b682a1717 to your computer and use it in GitHub Desktop.

Select an option

Save pineapplemachine/29ef80147d3b682a1717 to your computer and use it in GitHub Desktop.
Brainfuck interpreter in D
/+
Author: Sophie Kirschner (sophiek@pineapplemachine.com)
License: Public domain
+/
import std.stdio;
/+
// Example usage
void main(){
// Create a new interpreter targeting stdin and stdout, with a memory tape size of 16
auto test = new interpreter!char(stdin, stdout, 16);
// Output "Hello World!"
test.evaluate("++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.");
}
+/
class interpreter(Mem = char){
/// Associate constants with instruction characters
static enum instructions{
INCREMENT = '+',
DECREMENT = '-',
LEFT = '<',
RIGHT = '>',
OPEN = '[',
CLOSE = ']',
INPUT = ',',
OUTPUT = '.'
}
/// Array representing memory tape
Mem[] memory;
/// Pointer to current position on tape
Mem* memptr;
/// Read input from here
FILE* input;
/// Write output here
FILE* output;
this(IO)(IO input, IO output, int memsize, Mem fillvalue){
this.setinput(input);
this.setoutput(output);
this.initmemory(memsize);
this.fillmemory(fillvalue);
}
this(IO)(IO input, IO output, int memsize){
this(input, output, memsize, 0);
}
this(IO)(IO input, IO output, Mem[] memory){
this.setinput(input);
this.setoutput(output);
this.memory = memory;
this.memptr = memory.ptr;
}
/// Initialize memory tape to size
void initmemory(int size){
this.memory = new Mem[size];
this.memptr = this.memory.ptr;
}
/// Fill memory uniformly with value
void fillmemory(Mem value){
for(int i; i < this.memory.length; ++i){
this.memory[i] = value;
}
}
/// Fill memory with 0 and reset the memory pointer
void resetmemory(){
this.fillmemory(0);
this.memptr = this.memory.ptr;
}
/// Set input stream
void setinput(FILE* input){
this.input = input;
}
/// ditto
void setinput(File input){
this.input = input.getFP();
}
/// Set output stream
void setoutput(FILE* output){
this.output = output;
}
/// ditto
void setoutput(File output){
this.output = output.getFP();
}
/// Evaluate a single instruction and return the new position (Typically current + 1)
char* step(bool log = false)(in char* pos, in char* begin, in char* end){
// Very useful for debugging
static if(log){
for(char* i = cast(char*)begin; i < end; ++i){
write(*i);
}
writefln(" MEM %d", cast(int)*this.memptr);
for(char* i = cast(char*)begin; i < end; ++i){
write(i == pos ? '^' : ' ');
}
writeln();
}
switch(*pos){
case interpreter.instructions.INCREMENT:
++(*this.memptr);
break;
case interpreter.instructions.DECREMENT:
--(*this.memptr);
break;
case interpreter.instructions.RIGHT:
++this.memptr; this.wrapmemptr();
break;
case interpreter.instructions.LEFT:
--this.memptr; this.wrapmemptr();
break;
case interpreter.instructions.OPEN:
if(*this.memptr == 0){
return this.matchbrace(pos, end, true); // jump forward to close bracket
}
break;
case interpreter.instructions.CLOSE:
if(*this.memptr != 0){
return this.matchbrace(pos, begin, false); // jump back to open bracket
}
break;
case interpreter.instructions.INPUT:
fread(this.memptr, Mem.sizeof, 1, this.input);
break;
case interpreter.instructions.OUTPUT:
fwrite(this.memptr, Mem.sizeof, 1, this.output);
fflush(this.output);
break;
default:
break; // Do nothing (unrecognized characters considered comments)
}
return cast(char*)pos + 1;
}
/// Evaluate instructions from beginning until end
void evaluate(bool log = false)(in char* begin, in char* end){
char* pos = cast(char*)begin;
while(pos < end){
pos = this.step!(log)(pos, begin, end);
}
}
/// Evaluate instructions from a string
void evaluate(bool log = false)(in string str){
this.evaluate!(log)(str.ptr, str.ptr + str.length);
}
/// ditto
void evaluate(bool log = false)(in char[] str){
this.evaluate!(log)(str.ptr, str.ptr + str.length);
}
/// ditto
void evaluate(bool log = false)(in char* str, in int length){
this.evaluate!(log)(str, str + length);
}
/// Keep memory pointer within bounds by wrapping beginning and end
void wrapmemptr(){
Mem* upper = this.memory.ptr + this.memory.length - 1;
if(this.memptr < this.memory.ptr){
this.memptr = upper;
}else if(this.memptr > upper){
this.memptr = this.memory.ptr;
}
}
/// Find matching braces
char* matchbrace(in char* origin, in char* boundary, in bool forward){
char* pos = cast(char*)origin;
if(forward){
++pos;
for(int nest = 1; nest > 0 && pos < boundary; ++pos){
nest += (*pos == interpreter.instructions.OPEN) - (*pos == interpreter.instructions.CLOSE);
}
return pos;
}else{
--pos;
for(int nest = -1; nest < 0 && pos >= boundary; --pos){
nest += (*pos == interpreter.instructions.OPEN) - (*pos == interpreter.instructions.CLOSE);
}
return pos + 2;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment