#!/usr/bin/env python
# coding: utf-8
import os
from const import *
import serializer
try:
from rpython.rlib.jit import JitDriver
from rpython.rlib.jit import elidable, unroll_safe
from rpython.rlib.jit import assert_green
from rpython.rlib.jit import set_param
except ImportError:
"""Python compatibility."""
class JitDriver(object):
def __init__(self, **kw): pass
def jit_merge_point(self, **kw): pass
def can_enter_jit(self, **kw): pass
def elidable(f): return f
def dont_look_inside(f): return f
def unroll_safe(f): return f
def hint(v, **kw): return v
def assert_green(x): pass
def set_param(driver, name, value): pass
def get_location(pc, stackok, is_queue, program):
"""Add debug information.
PYPYLOG=jit-log-opt,jit-backend,jit-summary:
"""
op = program.get_op(pc)
val = program.get_value(pc)
return "#%d(s%d)_%s_%d" % (pc, stackok, serializer.OPCODE_NAMES[op], val)
driver = JitDriver(greens=['pc', 'stackok', 'is_queue', 'program'], reds=['stacksize', 'storage', 'selected'], get_printable_location=get_location)
set_param(driver, 'trace_limit', 100000)
DEBUG = False
class Stack(object):
def __init__(self):
self.size = 0x1000
self.list = [0] * self.size
self.pos = 0
@unroll_safe
def push(self, value):
pos = self.pos
#assert pos >= 0
self.list[pos] = value
new_pos = pos + 1
self.pos = new_pos
if new_pos >= self.size:
self.list += [0] * self.size
self.size = len(self.list)
@unroll_safe
def pop(self):
pos = self.pos
new_pos = pos - 1
#assert new_pos >= 0
v = self.list[new_pos]
self.pos = new_pos
return v
@unroll_safe
def dup(self):
pos = self.pos
last_pos = pos - 1
#assert last_pos >= 0
v = self.list[last_pos]
self.push(v)
@unroll_safe
def swap(self):
pos = self.pos
self.list[pos - 2], self.list[pos - 1] = self.list[pos - 1], self.list[pos - 2]
@unroll_safe
def __len__(self):
return self.pos
@unroll_safe
def add(self):
r1 = self.pop()
r2 = self.pop()
r = r2 + r1
self.push(r)
@unroll_safe
def sub(self):
r1 = self.pop()
r2 = self.pop()
r = r2 - r1
self.push(r)
@unroll_safe
def mul(self):
r1 = self.pop()
r2 = self.pop()
r = r2 * r1
self.push(r)
@unroll_safe
def div(self):
r1 = self.pop()
r2 = self.pop()
r = r2 / r1
self.push(r)
@unroll_safe
def mod(self):
r1 = self.pop()
r2 = self.pop()
r = r2 % r1
self.push(r)
@unroll_safe
def cmp(self):
r1 = self.pop()
r2 = self.pop()
r = 1 if r2 >= r1 else 0
self.push(r)
class Queue(Stack):
def __init__(self):
self.list = []
self.pos = 0
@unroll_safe
def push(self, value):
self.list.append(value)
self.pos += 1
@unroll_safe
def pop(self):
v = self.list.pop(0)
self.pos -= 1
return v
@unroll_safe
def swap(self):
l = self.list
l[0], l[1] = l[1], l[0]
def init_storage():
"""Initialize stacks and a queue for program."""
storage = []
for i in range(0, 28):
if i == VAL_QUEUE:
storage.append(Queue())
else:
storage.append(Stack())
return storage
def get_utf8():
"""Get a utf-8 character from standard input.
The length of utf-8 character is detectable in first byte.
If decode fails, it means it is a broken character.
Non-utf-8 character input is undefined in aheui.
Let's put -1 in this implementaion.
"""
buf = os.read(0, 1)
if buf:
v = ord(buf[0])
if v >= 0x80:
if (v & 0xf0) == 0xf0:
length = 4
elif (v & 0xe0) == 0xe0:
length = 3
elif (v & 0xc0) == 0xc0:
length = 2
else:
length = 0
if length > 0:
buf += os.read(0, length - 1)
if len(buf) == length:
try:
v = ord((buf).decode('utf-8')[0])
except:
v = -1
else:
v = -1
else:
v = -1
return v
def get_number():
"""Get a number from standard input."""
numchars = []
while True:
numchar = os.read(0, 1)
if numchar not in ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']:
break
else:
numchars.append(numchar)
assert len(numchars) > 0
num = int(''.join(numchars))
return num
class Program(object):
_immutable_fields_ = ['opcodes[*]', 'values[*]', 'size']
def __init__(self, lines):
self.opcodes = [l[0] for l in lines]
self.values = [l[1] for l in lines]
self.size = len(lines)
@elidable
def get_op(self, pc):
return self.opcodes[pc]
@elidable
def get_value(self, pc):
return self.values[pc]
@elidable
def get_req_size(self, pc):
return OP_REQSIZE[self.get_op(pc)]
def mainloop(program, debug):
assert_green(program)
pc = 0
stacksize = 0
is_queue = False
storage = init_storage()
selected = storage[0]
while pc < program.size:
#debug.storage(storage, selected)
#raw_input()
#debug.show(pc)
stackok = program.get_req_size(pc) <= stacksize
driver.jit_merge_point(pc=pc, stackok=stackok, is_queue=is_queue, program=program, stacksize=stacksize, storage=storage, selected=selected)
op = program.get_op(pc)
#assert_green(op)
stacksize += - OP_STACKDEL[op] + OP_STACKADD[op]
if op == OP_ADD:
selected.add()
elif op == OP_SUB:
selected.sub()
elif op == OP_MUL:
selected.mul()
elif op == OP_DIV:
selected.div()
elif op == OP_MOD:
selected.mod()
elif op == OP_POP:
selected.pop()
elif op == OP_PUSH:
value = program.get_value(pc)
selected.push(value)
elif op == OP_DUP:
selected.dup()
elif op == OP_SWAP:
selected.swap()
elif op == OP_SEL:
value = program.get_value(pc)
selected = storage[value]
stacksize = len(selected)
is_queue = value == VAL_QUEUE
elif op == OP_MOV:
r = selected.pop()
value = program.get_value(pc)
storage[value].push(r)
elif op == OP_CMP:
selected.cmp()
elif op == OP_BRZ:
r = selected.pop()
if r == 0:
value = program.get_value(pc)
pc = value
stackok = program.get_req_size(pc) <= stacksize
driver.can_enter_jit(pc=pc, stackok=stackok, is_queue=is_queue, program=program, stacksize=stacksize, storage=storage, selected=selected)
continue
elif op == OP_BRPOP1:
if not stackok:
value = program.get_value(pc)
pc = value
stackok = program.get_req_size(pc) <= stacksize
driver.can_enter_jit(pc=pc, stackok=stackok, is_queue=is_queue, program=program, stacksize=stacksize, storage=storage, selected=selected)
continue
elif op == OP_BRPOP2:
if not stackok:
value = program.get_value(pc)
pc = value
stackok = program.get_req_size(pc) <= stacksize
driver.can_enter_jit(pc=pc, stackok=stackok, is_queue=is_queue, program=program, stacksize=stacksize, storage=storage, selected=selected)
continue
elif op == OP_POPNUM:
r = selected.pop()
os.write(1, str(r))
elif op == OP_POPCHAR:
r = selected.pop()
os.write(1, unichr(r).encode('utf-8'))
elif op == OP_PUSHNUM:
num = get_number()
selected.push(num)
elif op == OP_PUSHCHAR:
c = get_utf8()
selected.push(c)
elif op == OP_JMP:
value = program.get_value(pc)
pc = value
stackok = program.get_req_size(pc) <= stacksize
driver.can_enter_jit(pc=pc, stackok=stackok, is_queue=is_queue, program=program, stacksize=stacksize, storage=storage, selected=selected)
continue
elif op == OP_HALT:
break
elif op == OP_NONE:
pass
else:
os.write(2, 'Missing operator: %d' % op)
assert False
pc += 1
if len(selected) > 0:
return selected.pop()
else:
return 0
def entry_point(argv):
try:
filename = argv[1]
except IndexError:
print 'aheui: error: no input files'
return 1
assembler = serializer.Serializer()
fp = os.open(filename, os.O_RDONLY, 0777)
if filename.endswith('.aheuic'):
assembler.read(fp)
os.close(fp)
else:
program_contents = ''
while True:
read = os.read(fp, 4096)
if len(read) == 0:
break
program_contents += read
os.close(fp)
assembler.compile(program_contents)
assembler.optimize()
binname = filename
if binname.endswith('.aheui'):
binname += 'c'
else:
binname += '.aheuic'
try:
bfp = os.open(binname, os.O_WRONLY|os.O_CREAT, 0644)
assembler.write(bfp)
os.write(bfp, '\n\n')
assembler.dump(bfp)
os.close(bfp)
except:
pass
program = Program(assembler.lines)
exitcode = mainloop(program, assembler.debug)
return exitcode
def jitpolicy(driver):
from rpython.jit.codewriter.policy import JitPolicy
return JitPolicy()
def target(*args):
return entry_point, None
if __name__ == '__main__':
"""Python compatibility."""
import sys
sys.exit(entry_point(sys.argv))