How to add reader macro in Python?

Background

In Verilog, I can specify binary numbers using expressions, including underscores. For example:

12'b1100_0111_0001 == 12'b110001110001  // The underscores are ignored

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())
+4
2
class BinaryUnderscorifier(object):

    def __getattr__(self, name):
        return int(name.replace('_', ''), 2)

b = BinaryUnderscorifier()

print b._1100_0111_0001 == 0b110001110001       # prints "True"

, b._1100_0111_0001 0b110001110001, , , , , , .

+5

, , .

.

Infix. :

class Infix(object):
    """
    Instances of this class can be treated like an infix operator
    by enclosing them with <<.>>.
    """
    def __init__(self, function):
        self.function = function
    def __rlshift__(self, other):
        return Infix(lambda x, self=self, other=other: self.function(other, x))
    def __rshift__(self, other):
        return self.function(other)
    def __call__(self, value1, value2):
        return self.function(value1, value2)

_ Infix, a bin int , bin, , , lambda, .

In [247]: _ = Infix(lambda x,y: bin(int(bin(int(x))[2:]+str(y),2)))

In [248]: 0b1101 <<_>> 1001
Out[248]: '0b11011001'

, IPython , _ . , IPython _ , , ghost _ Infix , redefine _.

, - , bin .

, -. , "is-lexically-adj-to", , Lisp Haskell.

+1
source

Source: https://habr.com/ru/post/1528200/


All Articles