Created
November 10, 2016 23:31
-
-
Save leegao/b94aca75929ace9d1750a6c1c27fcc28 to your computer and use it in GitHub Desktop.
Ahead-of-time RPN compiler.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ctypes, mmap, struct | |
DEBUG = True | |
try: | |
VirtualAlloc = ctypes.windll.kernel32.VirtualAlloc | |
raise Exception("Windows not supported.") | |
except(AttributeError): | |
libc = ctypes.CDLL("libc.so.6") | |
libc.valloc.restype = ctypes.c_void_p | |
def valloc(size): | |
addr = libc.valloc(size) | |
if not addr or libc.mprotect(addr, size, 0x07): | |
raise RuntimeError("Cannot allocate RWX memory") | |
return addr | |
def pack_number(term): | |
number = float(term) | |
return struct.pack('f', number) | |
def compile(rpn_code): | |
size = mmap.PAGESIZE | |
page = valloc(size) | |
# cdecl header, compatible with python ctype | |
buffer = push("rbp") + \ | |
mov('rbp', 'rsp') | |
sp = 0 | |
for term in rpn_code.split(): | |
try: | |
buffer += \ | |
push(pack_number(term)) | |
sp += 1 | |
except(ValueError): | |
header = fld("[rsp]") + \ | |
pop("rax") + \ | |
fld("[rsp]") + \ | |
"%s" + \ | |
fst("[rsp]") | |
dictionary = { | |
"+" : header % fadd, | |
"-" : header % fsub, | |
"*" : header % fmul, | |
"/" : header % fdiv, | |
"//": header % (fdiv + frndint), | |
"%" : header % fprem, | |
"^" : header % FPOW, | |
} | |
sp -= 1 | |
buffer += dictionary[term] | |
buffer += pop("rax") * sp # clear the stack into the return slot | |
buffer += mov("rsp", "rbp") + \ | |
pop('rbp') + \ | |
ret | |
if not ctypes.memmove(page, buffer, min(len(buffer), size)): | |
raise RuntimeError("Input cannot not fit into memory.") | |
if DEBUG: print(''.join(map(lambda x: '%02x' % ord(x), buffer))) | |
return ctypes.CFUNCTYPE(ctypes.c_int)(page) | |
# fake mnemonics | |
push = lambda r: "\x55" if r == 'rbp' else ("\x68" + str(r)) | |
mov = lambda r1, r2: "\x48\x89" + ("\xe5" if (r1, r2) == ("rbp", "rsp") else "\xec" if (r1, r2) == ("rsp", "rbp") else None) | |
pop = lambda r: "\x58" if r == "rax" else "\x5d" if r == "rbp" else None | |
fld = lambda r: "\xD9\x44\x24\x00" if r == "[rsp]" else None | |
fst = lambda r: "\xD9\x54\x24\x00" if r == "[rsp]" else None | |
ret = "\xc3" | |
fadd = "\xd8\xc1" | |
fsub = "\xd8\xe1" | |
fmul = "\xd8\xc9" | |
fdiv = "\xd8\xf1" | |
fprem = "\xd9\xf8" | |
frndint = "\xD9\xFC" | |
FPOW = "\xD9\xF1\xDB\x14\x24\xDB\x04\x24\xDE\xE9\xD9\xF0\xD9\xE8\xDE\xC1\xDB\x04\x24\xD9\xC9\xD9\xFD" | |
def rpn(code): | |
f = compile(code) | |
return struct.unpack('f', struct.pack('i', f()))[0] | |
if __name__ == '__main__': | |
import sys | |
DEBUG = True if len(sys.argv) > 2 and sys.argv[2] == '--debug' else False | |
print(rpn(sys.argv[1] if len(sys.argv) > 1 else "2 0.5 ^")) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment