# $Id: bfc.imc,v 1.4 2003/08/30 10:01:14 leo Exp $
# A Brainfuck compiler
# By Leon Brocard <acme@astray.com>
#
# See http://www.catseye.mb.ca/esoteric/bf/
# for more information on this silly language
.sub _main
  .local int pc
  .local int maxpc
  .local int label
  .local string labelstr
  .local string code
  .local string filename
  .local string file
  .local string line
  .local string program
  .local string char

  program = P5[0]
  # check argc
  I0 = P5
  if I0 < 2 goto usage
  # Get the filename
  filename = P5[1]
  if filename goto SOURCE
usage:
  print "usage: ../../parrot "
  print program
  print " file.bf\n"
  end

  # Read the file into S1
SOURCE:
  open P1, filename, "<"
  defined I0, P1
  if I0, SOURCE_LOOP
  print filename
  print " not found\n"
  branch usage
SOURCE_LOOP:
  read line, P1, 1024
  file = file . line
  if line goto SOURCE_LOOP
  close P1

  length maxpc, file

  # Initialise
  code =      "set I0, 0          # pc\n"
  code = code . "new P0, .PerlArray # memory\n"
  code = code . "set I1, 0          # pointer\n"

  pc    = 0    # pc
  label = 0    # label count

  # The main interpreter loop
INTERP:
  char = file[pc]
  code = code . "\nSTEP"
  labelstr = pc
  code = code . labelstr
  code = code . ": # "
  code = code . char
  code = code . "\n"

  if char != "+" goto NOTPLUS
  code = code . "set I2, P0[I1]\n"
  code = code . "inc I2\n"
  code = code . "set P0[I1], I2\n"
  goto NEXT

NOTPLUS:
  if char != "-" goto NOTMINUS
  code = code . "set I2, P0[I1]\n"
  code = code . "dec I2\n"
  code = code . "set P0[I1], I2\n"
  goto NEXT

NOTMINUS:
  if char != ">" goto NOTGT
  code = code . "inc I1\n"
  goto NEXT

NOTGT:
  if char != "<" goto NOTLT
  code = code . "dec I1\n"
  goto NEXT

NOTLT:
  if char != "[" goto NOTOPEN

  I2 = 0 # "depth"

  label = pc
OPEN_LOOP:
  inc label
  substr S2, file, label, 1
  if S2 != "[" goto OPEN_NOTOPEN
  inc I2
  goto OPEN_LOOP
OPEN_NOTOPEN:
  if S2 != "]" goto OPEN_LOOP
  if I2 == 0 goto OPEN_NEXT
  dec I2
  goto OPEN_LOOP
OPEN_NEXT:
  inc label
  labelstr = label
  code = code . "set I2, P0[I1]\n"
  code = code . "unless I2, STEP"
  code = code . labelstr
  code = code . "\n"

  goto NEXT

NOTOPEN:
  if char != "]" goto NOTCLOSE

  label = pc
  I2 = 0 # "height"

CLOSE_LOOP:
  dec label
  substr S2, file, label, 1
  if S2 != "]" goto CLOSE_NOTCLOSE
  inc I2
  goto CLOSE_LOOP
CLOSE_NOTCLOSE:
  if S2 != "[" goto CLOSE_LOOP
  if I2 == 0 goto CLOSE_NEXT
  dec I2
  goto CLOSE_LOOP

CLOSE_NEXT:
  labelstr = label
  code = code . "branch STEP"
  code = code . labelstr
  code = code . "\n"

  goto NEXT

NOTCLOSE:
  if char != "." goto NOTDOT
  code = code . "set I2, P0[I1]\n"
  code = code . "chr S31, I2\n"
  code = code . "print S31\n"
  goto NEXT

NOTDOT:
  if char != "," goto NEXT
  code = code . "getstdin P30\n"
  code = code . "readline S31, P30\n"
  code = code . "ord I2, S31\n"
  code = code . "set P0[I1], I2\n"
  goto NEXT

NEXT:
  inc pc

  if pc < maxpc goto INTERP
  code = code . "end\n"

#  print code
#  print "\n"

  # Now actually run it
  compreg P1, "PASM"
  compile P0, P1, code
  invoke
  end
.end
