Skip to content

Instantly share code, notes, and snippets.

@syuchan1005
Created March 21, 2018 02:49
Show Gist options
  • Save syuchan1005/c996ead6e1bd251a999bc90c406ab03e to your computer and use it in GitHub Desktop.
Save syuchan1005/c996ead6e1bd251a999bc90c406ab03e to your computer and use it in GitHub Desktop.
演算子指定可能なBrainfuckインタプリタ
class Brainfuck {
constructor() {
this.functions = {
incrementPointer: () => {
this.ptr += 1;
if (this.debug) this.write('>', this.ptr);
}, // '>'
decrementPointer: () => {
this.ptr -= 1;
if (this.ptr < 0) this.ptr = 0;
if (this.debug) this.write('<', this.ptr);
}, // '<'
incrementBytePointer: () => {
this.data[this.ptr] = this.data[this.ptr] || 0;
this.data[this.ptr] += 1;
if (this.debug) this.write('+', this.data[this.ptr], this.ptr);
}, // '+'
decrementBytePointer: () => {
this.data[this.ptr] = this.data[this.ptr] || 0;
this.data[this.ptr] -= 1;
if (this.debug) this.write('-', this.data[this.ptr], this.ptr);
}, // '-'
outputBytePointer: () => {
const c = String.fromCharCode(this.data[this.ptr]);
this.output.push(c);
if (this.debug) this.write('.', c, this.data[this.ptr]);
}, // '.'
inputBytePointer: () => {
const c = this.stdin.shift();
if (typeof c === 'string') this.data[this.ptr] = c.charCodeAt(0);
if (this.debug) this.write(',', c, this.data[this.ptr]);
}, // ','
jumpForward: '[',
jumpBackward: ']',
};
}
parse(source, stdin, ops, debug, debugWrite) {
if (!source) return '';
this.source = source;
this.stdin = stdin.split('').filter(v => v.length !== 0);
const op = Object.assign({
incrementPointer: '>',
decrementPointer: '<',
incrementBytePointer: '+',
decrementBytePointer: '-',
outputBytePointer: '.',
inputBytePointer: ',',
jumpForward: '[',
jumpBackward: ']',
}, ops);
this.operators = {};
Object.keys(op).forEach((key) => { this.operators[op[key]] = key; });
this.jumpForward = op.jumpForward;
this.jumpBackward = op.jumpBackward;
this.debug = Boolean(debug);
this.write = debugWrite || console.log; /* eslint-disable-line no-console */
this.output = [];
this.data = [];
this.ptr = 0;
const nodes = this.parseProgram();
nodes.forEach((node) => { node(); });
return this.output.join('');
}
parseProgram() {
const nodes = [];
while (this.source.length > 0) {
Object.keys(this.operators).some((operator) => {
if (this.source.startsWith(operator)) {
const func = this.functions[this.operators[operator]];
this.source = this.source.substr(operator.length);
if (typeof func === 'string') {
if (func === '[') {
nodes.push(this.parseLoop());
} else {
throw new Error('Missing opening function');
}
} else {
nodes.push(func);
}
return true;
}
return false;
});
}
return nodes;
}
parseLoop() {
const nodes = [];
while (!this.source.startsWith(this.jumpBackward)) {
Object.keys(this.operators).some((operator) => {
if (this.source.startsWith(operator)) {
const func = this.functions[this.operators[operator]];
this.source = this.source.substr(operator.length);
if (typeof func === 'string' && func === '[') {
nodes.push(this.parseLoop());
} else {
nodes.push(func);
}
return true;
}
return false;
});
}
this.source = this.source.substr(this.jumpBackward.length);
return () => {
let loopCounter = 0;
while (this.data[this.ptr] > 0) {
loopCounter += 1;
if (loopCounter > 10000) throw new Error('Infinite loop detected');
nodes.forEach((node) => { node(); });
}
};
}
}
export default Brainfuck;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment