Brainfuck compiler

by mcsf

JavaScript

//    h    e    l    l    o   !
// [104, 101, 108, 108, 111, 33]

SOURCE = `
    ++++++++++
[ > ++++++++++ < - ]
> ++++ .
  ---  .
< ++ [ > ++++ < - ]
> -    ..
  +++  .
<   ++++++++
[ > ---------- < - ]
> ++   .
`

DATA_SIZE = 3

function compile(source) {
	let pc = 0
	const program = [], stack = [];
  
	[].slice.call(source).forEach((char) => {
  	switch (char) {
    	case '+':
      case '-':
      case '>':
      case '<':
      case '.':
      case ',':
      	program.push([char])
        break

			case '[':
      	program.push([char])
        stack.push(pc)
        break
        
			case ']': {
      	if (! stack.length) throw new Error('Unexpected "]": empty stack.')
        const jmpPc = stack.pop()
        program.push([char, jmpPc])
        program[jmpPc][1] = pc
        break
			}
      
			case '\n':
      case ' ':
      	pc--
      	break

    	default:
      	throw new Error('Illegal character.')
    }
    pc++
  })
  
  if (stack.length) throw new Error('Unexpected end of program: stack not empty.')

	program.push(['END'])
  
  return program
}

function execute(program) {
	let pc = 0, ptr = 0
	const data = new Array(DATA_SIZE), output = []
	for (let i = 0; i < data.length; i++) data[i] = 0
  
  let time = 2000

	while (program[pc][0] !== 'END' && ptr < DATA_SIZE) {
  	if (time-- < 0) throw new Error('Aborted.')
    
  	const [operator, operand] = program[pc]
  	switch (operator) {
    	case '+': data[ptr]++; break
      case '-': data[ptr]--; break
      case '>': ptr++; break;
			case '<': ptr--; break
			case '.': output.push(data[ptr]); break
      case ',': data[ptr] = prompt(); break
      case '[': if (! data[ptr]) pc = operand; break
      case ']': if (data[ptr]) pc = operand; break
      case 'END': return
    }
    pc++
  }
  
  return [data, output]
}

{
  const program = compile(SOURCE)
  console.log('program', program)
  const [data, output] = execute(program)
  console.log('execute', data, output)
  const string = output.map(c =>...