Created
November 10, 2016 23:05
-
-
Save leegao/5f8f1ecea6a9f551a2e63cb904a9f157 to your computer and use it in GitHub Desktop.
Ahead-of-time compiled RPN for /r/DailyProgrammer
This file contains hidden or 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 = ''.join(( | |
"\x55", # push rbp | |
"\x48\x89\xe5", # mov rbp, rsp | |
)) | |
sp = 0 | |
for term in rpn_code.split(): | |
try: | |
number = pack_number(term) | |
buffer += "\x68" + str(number) # mov rax, number | |
sp += 1 | |
except(ValueError): | |
binary_prelude = ''.join(( | |
"\xD9\x44\x24\x00", # fld [rsp] | |
"\x59", # pop rsp | |
"\xD9\x44\x24\x00", # fld [rsp] | |
"%s", | |
"\xD9\x54\x24\x00", # fst [rsp] | |
)) | |
dictionary = { | |
"+" : binary_prelude % "\xd8\xc1", | |
"-" : binary_prelude % "\xd8\xe1", | |
"*" : binary_prelude % "\xd8\xc9", | |
"/" : binary_prelude % "\xd8\xf1", | |
"//": binary_prelude % "\xd8\xf1\xD9\xFC", | |
"%" : binary_prelude % "\xd9\xf8", | |
"^" : binary_prelude % "\xD9\xF1\xDB\x14\x24\xDB\x04\x24\xDE\xE9\xD9\xF0\xD9\xE8\xDE\xC1\xDB\x04\x24\xD9\xC9\xD9\xFD", | |
} | |
sp -= 1 | |
buffer += dictionary[term] | |
pass | |
for _ in range(sp): buffer += "\x58" # pop rax to clear the stack | |
buffer += ''.join(( | |
"\x48\x89\xec", # mov rsp, rbp | |
"\x5d", # pop rbp | |
"\xc3", # 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) | |
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