Last active
June 14, 2017 15:26
-
-
Save dmsnell/84538226462e7266e664daf8eb2fcb86 to your computer and use it in GitHub Desktop.
An interpreter for BF in PEGjs
This file contains 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
// Brain-Fried Language | |
// Load document from this GIST for a secret message | |
// https://gist.githubusercontent.com/dmsnell/b5e9465e97d352556249500c80ae6cbd/raw/bf547f71291e44c890ce9fe80c3c3b5217aae715/rot13.bf | |
{ | |
const stdin = 'Jbeq Pnzc'.split('').map( c => c.charCodeAt( 0 ) ).concat( 0 ); | |
const incDP = m => Object.assign( {}, m, { dp: m.dp + 1, maxRegister: Math.max( m.maxRegister, m.dp + 1 ) } ) | |
const decDP = m => Object.assign( {}, m, { dp: Math.max( 0, m.dp - 1 ) } ) | |
const incB = m => { | |
const next = m.data.slice(); next[ m.dp ]++ | |
return Object.assign( {}, m, { data: next } ) | |
} | |
const decB = m => { | |
const next = m.data.slice(); next[ m.dp ]-- | |
return Object.assign( {}, m, { data: next } ) | |
} | |
const out = m => Object.assign( {}, m, { output: m.output + String.fromCharCode( m.data[ m.dp ] ) } ) | |
const in2 = m => { | |
const next = m.data.slice(); next[ m.dp ] = stdin.shift() | |
return Object.assign( {}, m, { data: next } ); | |
} | |
const jz = m => { | |
if ( m.data[ m.dp ] ) { return m } | |
// jump to matching ']' | |
const jump = m.program.slice( m.ip + 1 ).reduce( | |
( accum, opcode, address ) => { | |
const keepGoing = accum.keepGoing; | |
const depth = accum.depth; | |
if ( ! keepGoing ) { return accum } | |
if ( '[' === opcode ) { return { keepGoing: true, depth: depth + 1 } } | |
if ( ']' === opcode && 0 === depth ) { return { keepGoing: false, depth, index: address } } | |
if ( ']' === opcode ) { return { keepGoing: true, depth: depth - 1 } } | |
return accum | |
} | |
, { keepGoing: true, depth: 0, index: 0 } | |
).index | |
return Object.assign( {}, m, { ip: m.ip + jump } ) | |
} | |
const jnz = m => { | |
if ( ! m.data[ m.dp ] ) { return m } | |
// jump to matching '[' | |
const jump = m.program.slice( 0, m.ip ).reverse().reduce( | |
( accum, opcode, address ) => { | |
const keepGoing = accum.keepGoing; | |
const depth = accum.depth; | |
if ( ! keepGoing ) { return accum } | |
if ( ']' === opcode ) { return { keepGoing: true, depth: depth + 1 } } | |
if ( '[' === opcode && 0 === depth ) { return { keepGoing: false, depth, index: address } } | |
if ( '[' === opcode ) { return { keepGoing: true, depth: depth - 1 } } | |
return accum | |
} | |
, { keepGoing: true, depth: 0, index: 0 } | |
).index | |
return Object.assign( {}, m, { ip: m.ip - jump - 1 } ) | |
} | |
const run = ( op, machine ) => { | |
switch ( op ) { | |
case ">": return incDP( machine ) | |
case "<": return decDP( machine ) | |
case "+": return incB( machine ) | |
case "-": return decB( machine ) | |
case ".": return out( machine ) | |
case ",": return in2( machine ) | |
case "[": return jz( machine ) | |
case "]": return jnz( machine ) | |
default: return machine | |
} | |
} | |
} | |
Program | |
= ops:OpCode* | |
{ | |
const size = 10 | |
let machine = { | |
program: ops, | |
cycles: 0, | |
maxRegister: 0, | |
ip: 0, | |
dp: 0, | |
data: new Array(size), | |
output: '' | |
}; | |
for ( let i = 0; i < size; i++ ) { machine.data[ i ] = 0 } | |
const safety = 100000 | |
for ( var i = 0; i < safety && machine.ip < machine.program.length; i++ ) { | |
machine = run( machine.program[ machine.ip ], machine ) | |
machine.ip++ | |
machine.cycles++ | |
} | |
return Object.assign( machine, { program: machine.program.join('') } ); | |
} | |
OpCode | |
= o:[><+-.,\[\]] _ { return o } | |
_ = (![><+-.,\[\]] .)* |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment