773 lines
10 KiB
Plaintext
773 lines
10 KiB
Plaintext
// 888 .d888 d8b .d8888b.
|
|
// 888 d88P" Y8P d88P Y88b
|
|
// 888 888 888
|
|
// 88888b. 888888 888 .d88P
|
|
// 888 "88b 888 888 .od888P"
|
|
// 888 888 888 888 d88P"
|
|
// 888 d88P 888 888 888"
|
|
// 88888P" 888 888 888888888
|
|
//
|
|
// The new and improved brainfuck 'interpreter'
|
|
//
|
|
// Steps of process:
|
|
// > Compile code into bytecode
|
|
// > Run said bytecode :)
|
|
// > Profit ?
|
|
//
|
|
|
|
.constant
|
|
BF_MEM_MASK 0x0fff
|
|
BF_MEM_SIZE 0x1000
|
|
BYTECODE_ADD 0
|
|
BYTECODE_CLR 6
|
|
BYTECODE_IN 5
|
|
BYTECODE_JNZ 3
|
|
BYTECODE_JZ 2
|
|
BYTECODE_MEM 1
|
|
BYTECODE_OUT 4
|
|
INITIAL_CAPACITY 126
|
|
_OBJREF 0xdeadc001
|
|
.end-constant
|
|
|
|
.main
|
|
LDC_W _OBJREF
|
|
INVOKEVIRTUAL _main
|
|
IFEQ success
|
|
error:
|
|
ERR
|
|
success:
|
|
HALT
|
|
.end-main
|
|
|
|
.method _main()
|
|
.var
|
|
txt
|
|
input
|
|
.end-var
|
|
|
|
// compile brainfuck at stdin to bytecode
|
|
LDC_W _OBJREF
|
|
INVOKEVIRTUAL bytecode_compile
|
|
ISTORE txt
|
|
|
|
// get input
|
|
LDC_W _OBJREF
|
|
INVOKEVIRTUAL get_input
|
|
ISTORE input
|
|
|
|
LDC_W _OBJREF
|
|
ILOAD txt
|
|
ILOAD input
|
|
INVOKEVIRTUAL bytecode_execute
|
|
POP
|
|
|
|
BIPUSH 0
|
|
IRETURN
|
|
.end-method
|
|
|
|
.method bytecode_compile()
|
|
.var
|
|
txt
|
|
char
|
|
tmp
|
|
quotes_stack
|
|
.end-var
|
|
|
|
LDC_W _OBJREF
|
|
LDC_W INITIAL_CAPACITY
|
|
INVOKEVIRTUAL list_new
|
|
ISTORE txt
|
|
LDC_W _OBJREF
|
|
LDC_W INITIAL_CAPACITY
|
|
INVOKEVIRTUAL list_new
|
|
ISTORE quotes_stack
|
|
BIPUSH 0
|
|
ISTORE tmp
|
|
|
|
pl:
|
|
IN
|
|
ISTORE char
|
|
l:
|
|
ILOAD char
|
|
BIPUSH 58
|
|
IF_ICMPEQ ret
|
|
ILOAD char
|
|
IFEQ ret
|
|
|
|
ILOAD char
|
|
BIPUSH 43
|
|
ISUB
|
|
IFLT pl
|
|
ILOAD char
|
|
BIPUSH 43
|
|
IF_ICMPEQ plus_compile
|
|
ILOAD char
|
|
BIPUSH 45
|
|
IF_ICMPEQ hyphen_compile
|
|
ILOAD char
|
|
BIPUSH 60
|
|
IF_ICMPEQ lt_compile
|
|
ILOAD char
|
|
BIPUSH 62
|
|
IF_ICMPEQ gt_compile
|
|
ILOAD char
|
|
BIPUSH 91
|
|
IF_ICMPEQ bq_open_compile
|
|
ILOAD char
|
|
BIPUSH 93
|
|
IF_ICMPEQ bq_close_compile
|
|
ILOAD char
|
|
BIPUSH 46
|
|
IF_ICMPEQ period_compile
|
|
ILOAD char
|
|
BIPUSH 44
|
|
IF_ICMPEQ comma_compile
|
|
GOTO pl
|
|
|
|
next_add_char:
|
|
IN
|
|
ISTORE char
|
|
ILOAD char
|
|
BIPUSH 43
|
|
IF_ICMPEQ plus_compile
|
|
ILOAD char
|
|
BIPUSH 45
|
|
IF_ICMPEQ hyphen_compile
|
|
ILOAD char
|
|
BIPUSH 60
|
|
IF_ICMPEQ add_compile
|
|
ILOAD char
|
|
BIPUSH 62
|
|
IF_ICMPEQ add_compile
|
|
ILOAD char
|
|
BIPUSH 91
|
|
IF_ICMPEQ add_compile
|
|
ILOAD char
|
|
BIPUSH 93
|
|
IF_ICMPEQ add_compile
|
|
ILOAD char
|
|
BIPUSH 46
|
|
IF_ICMPEQ add_compile
|
|
ILOAD char
|
|
BIPUSH 44
|
|
IF_ICMPEQ add_compile
|
|
GOTO next_add_char
|
|
|
|
add_compile:
|
|
ILOAD tmp
|
|
IFEQ l
|
|
|
|
// write ADD x to txt
|
|
LDC_W _OBJREF
|
|
ILOAD txt
|
|
LDC_W BYTECODE_ADD
|
|
INVOKEVIRTUAL list_append
|
|
ISTORE txt
|
|
LDC_W _OBJREF
|
|
ILOAD txt
|
|
ILOAD tmp
|
|
INVOKEVIRTUAL list_append
|
|
ISTORE txt
|
|
BIPUSH 0
|
|
ISTORE tmp
|
|
GOTO l
|
|
|
|
plus_compile:
|
|
IINC tmp 1
|
|
GOTO next_add_char
|
|
|
|
hyphen_compile:
|
|
IINC tmp -1
|
|
GOTO next_add_char
|
|
|
|
next_mem_char:
|
|
IN
|
|
ISTORE char
|
|
ILOAD char
|
|
BIPUSH 60
|
|
IF_ICMPEQ lt_compile
|
|
ILOAD char
|
|
BIPUSH 62
|
|
IF_ICMPEQ gt_compile
|
|
ILOAD char
|
|
BIPUSH 43
|
|
IF_ICMPEQ mem_compile
|
|
ILOAD char
|
|
BIPUSH 45
|
|
IF_ICMPEQ mem_compile
|
|
ILOAD char
|
|
BIPUSH 91
|
|
IF_ICMPEQ mem_compile
|
|
ILOAD char
|
|
BIPUSH 93
|
|
IF_ICMPEQ mem_compile
|
|
ILOAD char
|
|
BIPUSH 46
|
|
IF_ICMPEQ mem_compile
|
|
ILOAD char
|
|
BIPUSH 44
|
|
IF_ICMPEQ mem_compile
|
|
GOTO next_mem_char
|
|
|
|
mem_compile:
|
|
ILOAD tmp
|
|
IFEQ l
|
|
|
|
// write MEM x to txt
|
|
LDC_W _OBJREF
|
|
ILOAD txt
|
|
LDC_W BYTECODE_MEM
|
|
INVOKEVIRTUAL list_append
|
|
ISTORE txt
|
|
LDC_W _OBJREF
|
|
ILOAD txt
|
|
ILOAD tmp
|
|
INVOKEVIRTUAL list_append
|
|
ISTORE txt
|
|
BIPUSH 0
|
|
ISTORE tmp
|
|
GOTO l
|
|
|
|
lt_compile:
|
|
IINC tmp -1
|
|
GOTO next_mem_char
|
|
|
|
gt_compile:
|
|
IINC tmp 1
|
|
GOTO next_mem_char
|
|
|
|
bq_open_compile:
|
|
// quotes_stack.append(txt.ptr)
|
|
LDC_W _OBJREF
|
|
ILOAD quotes_stack
|
|
BIPUSH 1
|
|
ILOAD txt
|
|
IALOAD
|
|
INVOKEVIRTUAL list_append
|
|
ISTORE quotes_stack
|
|
|
|
// txt.append(JZ 0), 0 is later filled in
|
|
LDC_W _OBJREF
|
|
ILOAD txt
|
|
LDC_W BYTECODE_JZ
|
|
INVOKEVIRTUAL list_append
|
|
ISTORE txt
|
|
LDC_W _OBJREF
|
|
ILOAD txt
|
|
BIPUSH 0
|
|
INVOKEVIRTUAL list_append
|
|
ISTORE txt
|
|
GOTO pl
|
|
|
|
bq_close_compile:
|
|
LDC_W _OBJREF
|
|
ILOAD quotes_stack
|
|
INVOKEVIRTUAL list_pop
|
|
ISTORE tmp
|
|
|
|
// check if we have the following situation: [+] or [-]
|
|
// lab_a: JZ lab_d
|
|
// lab_b: ADD -1
|
|
// lab_c: JNZ lab_b
|
|
//
|
|
// In this case, txt.rsize - jz.pos == 4 and txt[jz.pos + 2] == ADD
|
|
BIPUSH 1
|
|
ILOAD txt
|
|
IALOAD
|
|
ILOAD tmp
|
|
ISUB
|
|
BIPUSH 4
|
|
IF_ICMPEQ possible_clear
|
|
|
|
jnz_write:
|
|
// JNZ after [
|
|
LDC_W _OBJREF
|
|
ILOAD txt
|
|
LDC_W BYTECODE_JNZ
|
|
INVOKEVIRTUAL list_append
|
|
ISTORE txt
|
|
LDC_W _OBJREF
|
|
ILOAD txt
|
|
ILOAD tmp
|
|
BIPUSH 2
|
|
IADD
|
|
INVOKEVIRTUAL list_append
|
|
ISTORE txt
|
|
|
|
// the JZ before needs to jump after the JNZ x instruction
|
|
BIPUSH 1
|
|
ILOAD txt
|
|
IALOAD
|
|
ILOAD tmp
|
|
BIPUSH 1
|
|
IADD
|
|
ILOAD txt
|
|
IASTORE
|
|
|
|
BIPUSH 0
|
|
ISTORE tmp
|
|
GOTO pl
|
|
|
|
possible_clear:
|
|
// if txt[jz.pos + 2] == ADD (0)
|
|
ILOAD tmp
|
|
BIPUSH 2
|
|
IADD
|
|
ILOAD txt
|
|
IALOAD
|
|
IFEQ emit_clear
|
|
GOTO jnz_write
|
|
|
|
emit_clear:
|
|
// pop the add and jz
|
|
LDC_W _OBJREF
|
|
ILOAD txt
|
|
INVOKEVIRTUAL list_pop
|
|
LDC_W _OBJREF
|
|
ILOAD txt
|
|
INVOKEVIRTUAL list_pop
|
|
LDC_W _OBJREF
|
|
ILOAD txt
|
|
INVOKEVIRTUAL list_pop
|
|
LDC_W _OBJREF
|
|
ILOAD txt
|
|
INVOKEVIRTUAL list_pop
|
|
|
|
// add clear opcode
|
|
LDC_W _OBJREF
|
|
ILOAD txt
|
|
LDC_W BYTECODE_CLR
|
|
INVOKEVIRTUAL list_append
|
|
ISTORE txt
|
|
|
|
// reset tmp
|
|
BIPUSH 0
|
|
ISTORE tmp
|
|
|
|
// jump back
|
|
GOTO pl
|
|
|
|
period_compile:
|
|
LDC_W _OBJREF
|
|
ILOAD txt
|
|
LDC_W BYTECODE_OUT
|
|
INVOKEVIRTUAL list_append
|
|
ISTORE txt
|
|
GOTO pl
|
|
|
|
comma_compile:
|
|
LDC_W _OBJREF
|
|
ILOAD txt
|
|
LDC_W BYTECODE_IN
|
|
INVOKEVIRTUAL list_append
|
|
ISTORE txt
|
|
GOTO pl
|
|
|
|
ret:
|
|
ILOAD txt
|
|
IRETURN
|
|
.end-method
|
|
|
|
.method bytecode_execute(txt, input)
|
|
.var
|
|
mem
|
|
mem_val
|
|
mem_ptr
|
|
pc
|
|
last_pc
|
|
op
|
|
i_ptr
|
|
i_end
|
|
.end-var
|
|
// initialize brainfuck memory
|
|
LDC_W BF_MEM_SIZE
|
|
NEWARRAY
|
|
ISTORE mem
|
|
BIPUSH 0
|
|
ISTORE mem_ptr
|
|
BIPUSH 0
|
|
ISTORE mem_val
|
|
|
|
// set program counter
|
|
BIPUSH 2
|
|
ISTORE pc
|
|
BIPUSH 1
|
|
ILOAD txt
|
|
IALOAD
|
|
BIPUSH -1
|
|
IADD
|
|
ISTORE last_pc
|
|
|
|
// set input counter and end of input
|
|
BIPUSH 2
|
|
ISTORE i_ptr
|
|
BIPUSH 1
|
|
ILOAD input
|
|
IALOAD
|
|
ISTORE i_end
|
|
|
|
exec_loop:
|
|
// if last_pc < pc
|
|
ILOAD last_pc
|
|
ILOAD pc
|
|
ISUB
|
|
IFLT exec_done
|
|
|
|
// read op
|
|
ILOAD pc
|
|
ILOAD txt
|
|
IALOAD
|
|
ISTORE op
|
|
|
|
// move pc past op
|
|
IINC pc 1
|
|
|
|
// do a small binary tree lookup, 1 < op => JZ/JNZ/OUT/IN else ADD/MEM
|
|
BIPUSH 1
|
|
ILOAD op
|
|
ISUB
|
|
IFLT exec_rest
|
|
|
|
// if (op == 0) goto exec_add
|
|
ILOAD op
|
|
IFEQ exec_add
|
|
|
|
exec_mem:
|
|
// mem[mem_ptr] = mem_val
|
|
ILOAD mem_val
|
|
ILOAD mem_ptr
|
|
ILOAD mem
|
|
IASTORE
|
|
|
|
// mem_ptr += txt[pc]
|
|
ILOAD mem_ptr
|
|
ILOAD pc
|
|
ILOAD txt
|
|
IALOAD
|
|
IADD
|
|
ISTORE mem_ptr
|
|
|
|
// apply memory mask (wrap around)
|
|
ILOAD mem_ptr
|
|
LDC_W BF_MEM_MASK
|
|
IAND
|
|
ISTORE mem_ptr
|
|
|
|
// retrieve mem_val
|
|
ILOAD mem_ptr
|
|
ILOAD mem
|
|
IALOAD
|
|
ISTORE mem_val
|
|
|
|
// pc is increased by 1 for MEM's argument
|
|
IINC pc 1
|
|
GOTO exec_loop
|
|
|
|
exec_add:
|
|
// mem_val += txt[pc]
|
|
ILOAD mem_val
|
|
ILOAD pc
|
|
ILOAD txt
|
|
IALOAD
|
|
IADD
|
|
ISTORE mem_val
|
|
|
|
// pc is increased by 1, for ADD's argument
|
|
IINC pc 1
|
|
GOTO exec_loop
|
|
|
|
exec_rest:
|
|
ILOAD op
|
|
BIPUSH 4
|
|
ISUB
|
|
IFLT exec_jumps
|
|
ILOAD op
|
|
BIPUSH 4
|
|
IF_ICMPEQ exec_out
|
|
ILOAD op
|
|
BIPUSH 5
|
|
IF_ICMPEQ exec_in
|
|
ILOAD op
|
|
BIPUSH 6
|
|
IF_ICMPEQ exec_clr
|
|
|
|
// Opcodes shouldnt get here
|
|
BIPUSH 73
|
|
OUT
|
|
BIPUSH 110
|
|
OUT
|
|
BIPUSH 99
|
|
OUT
|
|
BIPUSH 111
|
|
OUT
|
|
BIPUSH 114
|
|
OUT
|
|
BIPUSH 114
|
|
OUT
|
|
BIPUSH 101
|
|
OUT
|
|
BIPUSH 99
|
|
OUT
|
|
BIPUSH 116
|
|
OUT
|
|
BIPUSH 32
|
|
OUT
|
|
BIPUSH 111
|
|
OUT
|
|
BIPUSH 112
|
|
OUT
|
|
BIPUSH 10
|
|
OUT
|
|
ERR
|
|
|
|
exec_in:
|
|
ILOAD i_ptr
|
|
ILOAD i_end
|
|
IF_ICMPEQ exec_in_eof
|
|
ILOAD i_ptr
|
|
ILOAD input
|
|
IALOAD
|
|
ISTORE mem_val
|
|
IINC i_ptr 1
|
|
GOTO exec_loop
|
|
|
|
exec_in_eof:
|
|
BIPUSH 0
|
|
ISTORE mem_val
|
|
GOTO exec_loop
|
|
|
|
exec_out:
|
|
ILOAD mem_val
|
|
OUT
|
|
GOTO exec_loop
|
|
|
|
exec_jumps:
|
|
ILOAD op
|
|
BIPUSH 3
|
|
IF_ICMPEQ exec_jnz
|
|
|
|
exec_jz:
|
|
ILOAD mem_val
|
|
IFEQ exec_do_jump
|
|
|
|
exec_dont_jump:
|
|
IINC pc 1
|
|
GOTO exec_loop
|
|
|
|
exec_jnz:
|
|
ILOAD mem_val
|
|
IFEQ exec_dont_jump
|
|
|
|
exec_do_jump:
|
|
// pc = txt[pc]
|
|
ILOAD pc
|
|
ILOAD txt
|
|
IALOAD
|
|
ISTORE pc
|
|
GOTO exec_loop
|
|
|
|
exec_clr:
|
|
BIPUSH 0
|
|
ISTORE mem_val
|
|
GOTO exec_loop
|
|
|
|
exec_done:
|
|
BIPUSH 0
|
|
IRETURN
|
|
.end-method
|
|
|
|
.method get_input()
|
|
.var
|
|
input
|
|
char
|
|
.end-var
|
|
|
|
LDC_W _OBJREF
|
|
LDC_W INITIAL_CAPACITY
|
|
INVOKEVIRTUAL list_new
|
|
ISTORE input
|
|
|
|
loop:
|
|
IN
|
|
ISTORE char
|
|
ILOAD char
|
|
IFEQ done
|
|
|
|
LDC_W _OBJREF
|
|
ILOAD input
|
|
ILOAD char
|
|
INVOKEVIRTUAL list_append
|
|
ISTORE input
|
|
GOTO loop
|
|
|
|
done:
|
|
ILOAD input
|
|
IRETURN
|
|
.end-method
|
|
|
|
.method list_append(list_ref, value)
|
|
.var
|
|
list
|
|
i
|
|
size
|
|
capacity
|
|
.end-var
|
|
|
|
BIPUSH 0
|
|
ILOAD list_ref
|
|
IALOAD
|
|
DUP
|
|
ISTORE capacity
|
|
|
|
BIPUSH 1
|
|
ILOAD list_ref
|
|
IALOAD
|
|
DUP
|
|
ISTORE size
|
|
|
|
IF_ICMPEQ realloc
|
|
ILOAD list_ref
|
|
ISTORE list
|
|
done:
|
|
// arr[size] = value
|
|
ILOAD value
|
|
ILOAD size
|
|
ILOAD list
|
|
IASTORE
|
|
|
|
// size++
|
|
IINC size 1
|
|
|
|
// arr.size = size
|
|
ILOAD size
|
|
BIPUSH 1
|
|
ILOAD list
|
|
IASTORE
|
|
|
|
ILOAD list
|
|
IRETURN
|
|
|
|
realloc:
|
|
BIPUSH 1
|
|
ISTORE i
|
|
ILOAD size
|
|
DUP
|
|
IADD
|
|
NEWARRAY
|
|
ISTORE list
|
|
|
|
realloc_loop:
|
|
// list[i] = list_ref[i];
|
|
ILOAD i
|
|
ILOAD list_ref
|
|
IALOAD
|
|
ILOAD i
|
|
ILOAD list
|
|
IASTORE
|
|
|
|
// i++
|
|
IINC i 1
|
|
|
|
// if (i < size) goto realloc_loop
|
|
ILOAD i
|
|
ILOAD size
|
|
ISUB
|
|
IFLT realloc_loop
|
|
|
|
realloc_done:
|
|
// list[LIST_CAPACITY] = cap * 2
|
|
ILOAD size
|
|
DUP
|
|
IADD
|
|
BIPUSH 0
|
|
ILOAD list
|
|
IASTORE
|
|
GOTO done
|
|
.end-method
|
|
|
|
.method list_new(capacity)
|
|
.var
|
|
list
|
|
capacity_real
|
|
.end-var
|
|
ILOAD capacity
|
|
BIPUSH 2
|
|
IADD
|
|
ISTORE capacity_real
|
|
ILOAD capacity_real
|
|
NEWARRAY
|
|
ISTORE list
|
|
ILOAD capacity_real
|
|
BIPUSH 0
|
|
ILOAD list
|
|
IASTORE
|
|
BIPUSH 2
|
|
BIPUSH 1
|
|
ILOAD list
|
|
IASTORE
|
|
|
|
ILOAD list
|
|
IRETURN
|
|
.end-method
|
|
|
|
.method list_pop(list_ref)
|
|
.var
|
|
size
|
|
.end-var
|
|
|
|
BIPUSH 1
|
|
ILOAD list_ref
|
|
IALOAD
|
|
ISTORE size
|
|
IINC size -1
|
|
ILOAD size
|
|
IFLT error
|
|
|
|
ILOAD size
|
|
BIPUSH 1
|
|
ILOAD list_ref
|
|
IASTORE
|
|
ILOAD size
|
|
ILOAD list_ref
|
|
IALOAD
|
|
IRETURN
|
|
|
|
error:
|
|
BIPUSH 91
|
|
OUT
|
|
BIPUSH 33
|
|
OUT
|
|
BIPUSH 93
|
|
OUT
|
|
BIPUSH 32
|
|
OUT
|
|
BIPUSH 66
|
|
OUT
|
|
BIPUSH 111
|
|
OUT
|
|
BIPUSH 117
|
|
OUT
|
|
BIPUSH 110
|
|
OUT
|
|
BIPUSH 100
|
|
OUT
|
|
BIPUSH 115
|
|
OUT
|
|
BIPUSH 32
|
|
OUT
|
|
BIPUSH 101
|
|
OUT
|
|
BIPUSH 114
|
|
OUT
|
|
BIPUSH 114
|
|
OUT
|
|
BIPUSH 111
|
|
OUT
|
|
BIPUSH 114
|
|
OUT
|
|
BIPUSH 10
|
|
OUT
|
|
ERR
|
|
.end-method
|
|
|