#!/usr/bin/env python3

import sys
import argparse

from mit.globals import *


# Process command-line arguments
parser = argparse.ArgumentParser(
    prog='brainfuck',
    description='''\
Brainfuck compiler targeting the Mit VM.

Brainfuck source is read from standard input.

https://github.com/rrthomas/mit
https://en.wikipedia.org/wiki/Brainfuck#Commands''',
    formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument(
    '--version',
    action='version',
    version='''\
%(prog)s 0.2
Copyright (c) 2019 Alistair Turnbull <apt1002@mupsych.org>
Copyright (c) 2020 Reuben Thomas <rrt@sc3d.org>
brainfuck comes with ABSOLUTELY NO WARRANTY.
You may redistribute copies under the terms of the MIT/X11 License.'''
)
parser.add_argument(
    'object_file',
    metavar='OBJECT-FILE',
    help='object file to output',
)
args = parser.parse_args()


def inc(): push(1); ass(ADD)
def dec(): push(1); ass(NEG); ass(ADD)
def dup(): push(0); ass(DUP)
def over(): push(1); ass(DUP)
def libc(fn): push(fn); trap(LIBC)

try:
    pushrel_long(0) # Data pointer.
    stack = [] # [(start of loop, word to patch)]
    for c in sys.stdin.read():
        if c == '>': inc()
        elif c == '<': dec()
        elif c == '+': dup(); ass(LOAD1); inc(); over(); ass(STORE1)
        elif c == '-': dup(); ass(LOAD1); dec(); over(); ass(STORE1)
        elif c == '.':
            dup() # buffer
            push(1) # length
            libc(LibC.STDOUT)
            libc(LibC.WRITE)
            ass(POP)
        elif c == ',':
            dup() # buffer
            push(1) # length
            libc(LibC.STDIN)
            libc(LibC.READ)
            ass(POP)
        elif c == '[':
            loop = label()
            dup()
            ass(LOAD1)
            pushrel_long(0)
            patch = assembler.pc - word_bytes
            ass(JUMPZ)
            stack.append((loop, patch))
        elif c == ']':
            loop, patch = stack.pop()
            label()
            jumprel(loop)
            M_word[patch] = label() - patch
    ass(RET)
    M_word[M_word.start + word_bytes] = label() - M_word.start # Patch data pointer.
except IndexError as e:
    print("Out of memory", file=sys.stderr)
    sys.exit(2)

save(args.object_file)
