Last active
April 8, 2016 16:32
-
-
Save pineapplemachine/29ef80147d3b682a1717 to your computer and use it in GitHub Desktop.
Brainfuck interpreter in D
This file contains hidden or 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
| /+ | |
| 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