Here is the RegexDispatcher class that submits its subclass methods with a regular expression.
Each dispatch method is annotated with a regular expression, for example.
def plus(self, regex: r"\+", **kwargs): ...
In this case, the annotation is called "regex", and its value is a regular expression for matching, "\ +", which is the + sign. These annotated methods are placed in subclasses, not in the base class.
When the submit method (...) is called on a line, the class finds a method with a regular expression annotation that matches the line and calls it. Here is the class:
import inspect import re class RegexMethod: def __init__(self, method, annotation): self.method = method self.name = self.method.__name__ self.order = inspect.getsourcelines(self.method)[1]
To use this class, subclass it to create a class with annotated methods. As an example, here is a simple RPNCalculator that inherits from RegexDispatcher. The methods to be sent are (of course) those that contain the regex annotation. The parent dispatch () method is called in the call .
from RegexDispatcher import * import math class RPNCalculator(RegexDispatcher): def __init__(self): RegexDispatcher.__init__(self) self.stack = [] def __str__(self): return str(self.stack) # Make RPNCalculator objects callable def __call__(self, expression): # Calculate the value of expression for t in expression.split(): self.dispatch(t, token=t) return self.top() # return the top of the stack # Stack management def top(self): return self.stack[-1] if len(self.stack) > 0 else [] def push(self, x): return self.stack.append(float(x)) def pop(self, n=1): return self.stack.pop() if n == 1 else [self.stack.pop() for n in range(n)] # Handle numbers def number(self, regex: r"[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?", **kwargs): self.stack.append(float(kwargs['token'])) # Binary operators def plus(self, regex: r"\+", **kwargs): a, b = self.pop(2) self.push(b + a) def minus(self, regex: r"\-", **kwargs): a, b = self.pop(2) self.push(b - a) def multiply(self, regex: r"\*", **kwargs): a, b = self.pop(2) self.push(b * a) def divide(self, regex: r"\/", **kwargs): a, b = self.pop(2) self.push(b / a) def pow(self, regex: r"exp", **kwargs): a, b = self.pop(2) self.push(a ** b) def logN(self, regex: r"logN", **kwargs): a, b = self.pop(2) self.push(math.log(a,b)) # Unary operators def neg(self, regex: r"neg", **kwargs): self.push(-self.pop()) def sqrt(self, regex: r"sqrt", **kwargs): self.push(math.sqrt(self.pop())) def log2(self, regex: r"log2", **kwargs): self.push(math.log2(self.pop())) def log10(self, regex: r"log10", **kwargs): self.push(math.log10(self.pop())) def pi(self, regex: r"pi", **kwargs): self.push(math.pi) def e(self, regex: r"e", **kwargs): self.push(math.e) def deg(self, regex: r"deg", **kwargs): self.push(math.degrees(self.pop())) def rad(self, regex: r"rad", **kwargs): self.push(math.radians(self.pop())) # Whole stack operators def cls(self, regex: r"c", **kwargs): self.stack=[] def sum(self, regex: r"sum", **kwargs): self.stack=[math.fsum(self.stack)] if __name__ == '__main__': calc = RPNCalculator() print(calc('2 2 exp 3 + neg')) print(calc('c 1 2 3 4 5 sum 2 * 2 / pi')) print(calc('pi 2 * deg')) print(calc('2 2 logN'))
I like this solution because there are no separate lookup tables. A regular expression for matching is built into the method that will be called as the annotation. This is as it should be for me. It would be nice if Python allowed for more flexible annotations, because I would prefer to add a comment to the regular expression method only to the method itself, rather than pasting it into the method parameter list. However, this is currently not possible.
For fun, take a look at the Tungsten language, in which functions are polymorphic on arbitrary patterns, and not just on argument types. A function that is polymorphic in a regular expression is a very powerful idea, but we cannot get it in Python. The RegexDispatcher class is the best I could do.