1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
use parser;
use parser::{OpCode,Loop,Program};

struct Context {
    isn: uint,
}

impl Context {
    fn new() -> Context {
        Context {
            isn: 0
        }
    }
}

fn effective_len(program: &Program) -> uint {
    let mut len = 0;
    for op in program.iter() {
        match *op {
            Loop(ref l) => len += effective_len(l) + 1,
            _       => len += 1,
        }
    }
    len
}

pub fn compile<W: Writer>(program: &[OpCode], outfile: &mut W) {
    let mut ctx = Context::new();

    outfile.write(PRELUDE.as_bytes());
    let _ = inner(program, outfile, &mut ctx);
    outfile.write(format!("    isn{}:\n", ctx.isn).as_bytes());
    outfile.write(EPILOGUE.as_bytes());
}

#[allow(unused_must_use)]
fn inner<W: Writer>(program: &[OpCode], outfile: &mut W, ctx: &mut Context) {
    macro_rules! write(
        ($op:expr) => (
            outfile.write($op.as_bytes());
            )
        )

    macro_rules! write_s(
        ($op:expr) => (
            {
            write!($op.to_string());
            ()
            }

            )
        )


    let len = program.len();
    let mut pc = 0;
    while pc < len {
        write!(format!("    isn{}:\n", ctx.isn));
        ctx.isn += 1;
        match program[pc] {
            parser::Rshift  => write_s!("    add     esi, dword 1\n"),
            parser::Lshift  => write_s!("    sub     esi, dword 1\n"),
            parser::Inc     => write_s!("    add     [esi], dword 1\n"),
            parser::Dec     => write_s!("    sub     [esi], dword 1\n"),
            parser::Putc    => write_s!("    call    dot\n"),
            parser::Getc    => panic!("Getc not implemented"),
            parser::Loop(ref l) => {
                let jmp = format!("    jmp     isn{}\n", ctx.isn - 1);
                write_s!("    cmp      [esi], byte 0\n");
                write!(format!("    je      isn{}\n", ctx.isn + effective_len(l)));
                inner(l.as_slice(), outfile, ctx);
                write!(jmp);
            }
        }
        pc += 1;
    }
}

// {{{ boilerplate
static PRELUDE: &'static str = "
global start

section .bss
tape:    resb     30000

section .text

dot:
    push    dword 1
    push    esi
    push    dword 1
    mov     eax, 4
    sub     esp, 4
    int     0x80
    add     esp, 16
    ret

start:
    ; Begin, setup rcx as our index
    mov     esi, tape
";

static EPILOGUE: &'static str = "
    ; End, exit zero because everything probably went super well
    push    dword 0
    mov     eax, 1
    push    dword 0
    int     0x80
";
// }}}