Background
In Verilog, I can specify binary numbers using expressions, including underscores. For example:
12'b1100_0111_0001 == 12'b110001110001
I find this nice function and want to add it in Python to write something like:
x = 0b1100_0111_0001
y = 0x3fff_cc00
In Lisp, I could do this using something like set-dispatch-macro-character to be able to edit the source code before compiling it with a reader macro.
Question
What is a good way to add a Lisp reader macro in Python?
I would also be pleased with suggestions for troubleshooting issues with my current approach or with a completely different approach.
What i tried
I tried adding a custom search object to sys.meta_path. This almost works, except for two problems:
- finder, pyc-
- , , , "verilog_test_code.py"
verilog_test_code.py
def run_test():
return 0b1100_0111_0001+0x3fff_cc00
toplevel.py
import remove_underscores
import verilog_test_code
print verilog_test_code.run_test()
remove_underscores.py
import tokenize,sys,imp
def remove_underscores(src):
"""Remove underscores from hexadecimals."""
result = []
g = tokenize.generate_tokens(src.readline)
for toknum, tokval, _, _, _ in g:
if toknum == tokenize.NAME and tokval[0]=='_' and result and result[-1][0]==tokenize.NUMBER:
result[-1][1] = str(result[-1][1]) + tokval[1:].replace('_','')
else:
result.append([toknum, tokval])
return tokenize.untokenize(result)
class my_finder_loader(object):
def is_package(self, fullname):
return False
def get_code(self, fullname):
return compile(self.get_source(fullname),fullname,'exec')
def get_source(self, fullname):
(src, pathname, description) = imp.find_module(fullname)
srctext = remove_underscores(src)
src.close()
return srctext
def load_module(self, fullname):
if fullname in sys.modules:
return sys.modules[fullname]
code = self.get_code(fullname)
mod = sys.modules.setdefault(fullname, imp.new_module(fullname))
mod.__file__ = "<%s>" % self.__class__.__name__
mod.__loader__ = self
mod.__package__ = ''
exec(code, mod.__dict__)
return mod
def find_module(self, fullname, path=None):
if not fullname.startswith('verilog_'):
return None
return self
sys.meta_path.append(my_finder_loader())