Skip to content

Instantly share code, notes, and snippets.

@leegao
Created November 10, 2016 23:31
Show Gist options
  • Save leegao/b94aca75929ace9d1750a6c1c27fcc28 to your computer and use it in GitHub Desktop.
Save leegao/b94aca75929ace9d1750a6c1c27fcc28 to your computer and use it in GitHub Desktop.
Ahead-of-time RPN compiler.
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