Skip to content

Instantly share code, notes, and snippets.

@frodo821
Last active April 6, 2023 12:18
Show Gist options
  • Save frodo821/a244195c935db39dd38ed92dc33367db to your computer and use it in GitHub Desktop.
Save frodo821/a244195c935db39dd38ed92dc33367db to your computer and use it in GitHub Desktop.
Brainfuck interpreter implementation in JavaScript
const insts = Object.freeze({
'>': '1',
'<': 'f',
'+': '2',
'-': 'd',
'.': '3',
',': 'e',
'[': '$',
']': '_',
});
const rinsts = Object.freeze(
Object.fromEntries(
Object.entries(insts).map(([a, b]) => [b, a]),
),
);
/**
* convert standard Brainfuck code to javascript identifier-safe brainfuck code
* @param {string} code a Brainfuck code
* @returns javascript identifier-safe brainfuck code
*/
function toInstructions(code) {
return '$' + code.replace(/\s/g, '').split('').map(i => (insts[i] || i)).join('');
}
/**
* creates and returns a Brainfuck executor
* @param {{enableWrite?: boolean, async?: boolean}} [opts] enable writing to console instead of returning a string
* @returns {{[key: string]: string | Promise<string> | undefined}} a brainfuck executor
*/
function Brainfuck({enableWrite, async}) {
const bf = {
ptr: 0,
buffer: [0],
labels: [],
strbuf: '',
readbuf: '',
pc: 0,
code: '',
['<']() {
if (this.ptr == 0) {
this.buffer.unshift(0);
return;
}
this.ptr--;
},
['>']() {
this.ptr++;
if (this.buffer.length <= this.ptr) {
this.buffer.push(0);
}
},
['+']() {
this.buffer[this.ptr]++;
},
['-']() {
this.buffer[this.ptr]--;
},
['.']() {
this.strbuf = `${this.strbuf}${String.fromCharCode(this.buffer[this.ptr])}`;
if (enableWrite && this.strbuf.endsWith('\n')) {
console.log(this.strbuf);
this.strbuf = '';
}
},
[',']() {
if (this.readbuf.length === 0) {
this.readbuf = (prompt('reading a char') || '').concat('\n');
}
this.readbuf.charCodeAt(0);
this.readbuf = this.readbuf.substring(1);
},
['[']() {
this.labels.unshift(this.pc);
if (this.buffer[this.ptr] === 0) {
const jmpEnd = insts["]"];
const jmpBegin = insts["["];
const begin = this.pc;
while (true) {
const instr = this.code[this.pc];
switch (instr) {
case undefined:
return;
case jmpBegin:
this.labels.unshift(this.pc);
break;
case jmpEnd:
const at = this.labels[0];
this.labels.shift();
if (at === begin) {
this.pc++;
return;
}
default:
break;
}
this.pc++;
}
}
},
[']']() {
const jumpTo = this.labels[0];
if (jumpTo === undefined) {
return;
}
if (this.buffer[this.ptr] !== 0) {
this.pc = jumpTo;
} else {
this.labels.shift();
}
},
next() {
let insts;
if (this.code.length <= this.pc) {
return false;
}
while((insts = rinsts[this.code[this.pc++]]) === undefined) {
if (this.code.length <= this.pc) {
return false;
}
}
this[insts]();
return true;
},
reinitialize(code) {
this.ptr = 0;
this.buffer = [0];
this.labels = [];
this.strbuf = '';
this.readbuf = '';
this.pc = 0;
this.code = code;
},
}
let prox = new Proxy({}, {
has() { return true; },
get(_, prop) {
prop = prop.toString();
if (!async) {
bf.reinitialize(prop.substring(1));
while(bf.next());
if (enableWrite) {
if (bf.strbuf) {
console.log(bf.strbuf);
}
} else {
return bf.strbuf;
}
return;
}
return new Promise((res) => {
const executor = Object.assign({}, bf);
executor.reinitialize(prop.substring(1));
function internal() {
if(executor.next()) {
setTimeout(internal, 0);
} else {
if (enableWrite) {
if (executor.strbuf) {
console.log(executor.strbuf);
}
res(undefined);
} else {
res(executor.strbuf);
}
}
}
internal();
});
},
});
return prox;
}
bf = Brainfuck({async:true});
// get this name by
// toInstructions('+++++++++[>++++++++>+++++++++++>+++>+<<<<-]>.>++.+++++++..+++.>+++++.<<+++++++++++++++.>.+++.------.--------.>+.>+.');
hw = bf.$222222222$122222222122222222222122212ffffd_13122322222223322231222223ff2222222222222223132223dddddd3dddddddd3123123;
hw.then(k => console.log(k));
/* brainfuck library, each cell is not limited (values can take both positive and negative) */const insts=Object.freeze({">":"1","<":"f","+":"2","-":"d",".":"3",",":"e","[":"$","]":"_"}),rinsts=Object.freeze(Object.fromEntries(Object.entries(insts).map(([t,s])=>[s,t])));function toInstructions(t){return"$"+t.replace(/\s/g,"").split("").map(t=>insts[t]||t).join("")}function Brainfuck({enableWrite:t,async:s}){let i={ptr:0,buffer:[0],labels:[],strbuf:"",readbuf:"",pc:0,code:"","<"(){if(0==this.ptr){this.buffer.unshift(0);return}this.ptr--},">"(){this.ptr++,this.buffer.length<=this.ptr&&this.buffer.push(0)},"+"(){this.buffer[this.ptr]++},"-"(){this.buffer[this.ptr]--},"."(){this.strbuf=`${this.strbuf}${String.fromCharCode(this.buffer[this.ptr])}`,t&&this.strbuf.endsWith("\n")&&(console.log(this.strbuf),this.strbuf="")},","(){0===this.readbuf.length&&(this.readbuf=(prompt("reading a char")||"").concat("\n")),this.readbuf.charCodeAt(0),this.readbuf=this.readbuf.substring(1)},"["(){if(this.labels.unshift(this.pc),0===this.buffer[this.ptr]){let t=insts["]"],s=insts["["],i=this.pc;for(;;){let r=this.code[this.pc];switch(r){case void 0:return;case s:this.labels.unshift(this.pc);break;case t:let e=this.labels[0];if(this.labels.shift(),e===i){this.pc++;return}}this.pc++}}},"]"(){let t=this.labels[0];void 0!==t&&(0!==this.buffer[this.ptr]?this.pc=t:this.labels.shift())},next(){let t;if(this.code.length<=this.pc)return!1;for(;void 0===(t=rinsts[this.code[this.pc++]]);)if(this.code.length<=this.pc)return!1;return this[t](),!0},reinitialize(t){this.ptr=0,this.buffer=[0],this.labels=[],this.strbuf="",this.readbuf="",this.pc=0,this.code=t}};return new Proxy({},{has:()=>!0,get(r,e){if(e=e.toString(),!s){for(i.reinitialize(e.substring(1));i.next(););return t?void(i.strbuf&&console.log(i.strbuf)):i.strbuf}return new Promise(s=>{let r=Object.assign({},i);r.reinitialize(e.substring(1)),!function i(){r.next()?setTimeout(i,0):t?(r.strbuf&&console.log(r.strbuf),s(void 0)):s(r.strbuf)}()})}})}
/* brainfuck library, each cell is limited to 8 bit unsigned integer */const insts = Object.freeze({ ">": "1", "<": "f", "+": "2", "-": "d", ".": "3", ",": "e", "[": "$", "]": "_" }), rinsts = Object.freeze(Object.fromEntries(Object.entries(insts).map(([t, s]) => [s, t]))); function toInstructions(t) { return "$" + t.replace(/\s/g, "").split("").map(t => insts[t] || t).join("") } function Brainfuck({ enableWrite: t, async: s }) { let i = { ptr: 0, buffer: [0], labels: [], strbuf: "", readbuf: "", pc: 0, code: "", "<"() { if (0 == this.ptr) { this.buffer.unshift(0); return } this.ptr-- }, ">"() { this.ptr++, this.buffer.length <= this.ptr && this.buffer.push(0) }, "+"() { this.buffer[this.ptr] = (this.buffer[this.ptr]+1) % 256 }, "-"() { this.buffer[this.ptr]=(this.buffer[this.ptr]+255)%256 }, "."() { this.strbuf = `${this.strbuf}${String.fromCharCode(this.buffer[this.ptr])}`, t && this.strbuf.endsWith("\n") && (console.log(this.strbuf), this.strbuf = "") }, ","() { 0 === this.readbuf.length && (this.readbuf = (prompt("reading a char") || "").concat("\n")), this.readbuf.charCodeAt(0), this.readbuf = this.readbuf.substring(1) }, "["() { if (this.labels.unshift(this.pc), 0 === this.buffer[this.ptr]) { let t = insts["]"], s = insts["["], i = this.pc; for (; ;) { let r = this.code[this.pc]; switch (r) { case void 0: return; case s: this.labels.unshift(this.pc); break; case t: let e = this.labels[0]; if (this.labels.shift(), e === i) { this.pc++; return } }this.pc++ } } }, "]"() { let t = this.labels[0]; void 0 !== t && (0 !== this.buffer[this.ptr] ? this.pc = t : this.labels.shift()) }, next() { let t; if (this.code.length <= this.pc) return !1; for (; void 0 === (t = rinsts[this.code[this.pc++]]);)if (this.code.length <= this.pc) return !1; return this[t](), !0 }, reinitialize(t) { this.ptr = 0, this.buffer = [0], this.labels = [], this.strbuf = "", this.readbuf = "", this.pc = 0, this.code = t } }; return new Proxy({}, { has: () => !0, get(r, e) { if (e = e.toString(), !s) { for (i.reinitialize(e.substring(1)); i.next();); return t ? void (i.strbuf && console.log(i.strbuf)) : i.strbuf } return new Promise(s => { let r = Object.assign({}, i); r.reinitialize(e.substring(1)), !function i() { r.next() ? setTimeout(i, 0) : t ? (r.strbuf && console.log(r.strbuf), s(void 0)) : s(r.strbuf) }() }) } }) }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment