Using non-ascii symbols

Bengt Richter bokr at oz.net
Fri Jan 27 09:11:24 CET 2006


On Thu, 26 Jan 2006 17:47:51 +0100, Claudio Grondi <claudio.grondi at freenet.de> wrote:

>Rocco Moretti wrote:
>> Terry Hancock wrote:
>> 
>>> One thing that I also think would be good is to open up the
>>> operator set for Python. Right now you can overload the
>>> existing operators, but you can't easily define new ones.
>>> And even if you do, you are very limited in what you can
>>> use, and understandability suffers.
>> 
>> 
>> One of the issues that would need to be dealt with in allowing new 
>> operators to be defined is how to work out precedence rules for the new 
>> operators. Right now you can redefine the meaning of addition and 
>> multiplication, but you can't change the order of operations. (Witness 
>> %, and that it must have the same precedence in both multiplication and 
>> string replacement.)
>> 
>> If you allow (semi)arbitrary characters to be used as operators, some 
>> scheme must be chosen for assigning a place in the precedence hierarchy.
>
>Speaking maybe only for myself:
>I don't like implicit rules, so I don't like also any  precedence 
>hierarchy being in action, so for safety reasons I always write even 
>8+6*2 (==20) as 8+(6*2) to be sure all will go the way I expect it.
>
Maybe you would like the unambiguousness of
    (+ 8 (* 6 2))
or
    6 2 * 8 +
?

Hm, ... ISTM you could have a concept of all objects as potential operator
objects as now, but instead of selecting methods of the objects according
to special symbols like + - * etc, allow method selection by rules applied
to a sequence of objects for selecting methods. E.g., say
    a, X, b, Y, c
is a sequence of objects (happening to be contained in a tuple expression here).
Now let's define seqeval such that
    seqeval((a, X, b, Y, c))
looks at the objects to see if they have certain methods, and then calls some of
those methods with some of the other objects as arguments, and applies rules of
precedence and association to do something useful, producing a final result.

I'm just thinking out loud here, but what I'm getting at is being able to write
    8+6*2
as
    seqeval((8, PLUS, 6, TIMES, 2))
with the appropriate definitions of seqeval and PLUS and TIMES. This is with a view
to having seqeval as a builtin that does standard processing, and then having
a language change to make white-space-separated expressions like
    8 PLUS 6 TIMES 2
be syntactic sugar for an implicit
    seqeval((8, PLUS, 6, TIMES, 2))
where PLUS and TIMES may be arbitrary user-defined objects suitable for seqeval.
I'm thinking out loud, so I anticipate syntactic ambiguities in expressions and the need to
use parens etc., but this would in effect let us define arbitrarily named operators.
Precedence might be established by looking for PLUS.__precedence__. But as usual,
parens would control precedence dominantly. E.g.,
   (8 PLUS 6) TIMES 2
would be sugar for
   seqeval((seqeval(8, PLUS, 6), TIMES, 2)

IOW, we have an object sequence expression analogous to a tuple expression without commas.
I guess generator expressions might be somewhat of a problem to disambiguate sometimes, we'll see
how bad that gets ;-)

One way to detect operator objects would be to test callable(obj), which would allow
for functions and types and bound methods etc. Now there needs to be a way of
handling UNARY_PLUS vs PLUS functionality (obviously the name bindings are just mnemonic
and aren't seen by seqeval unless they're part of the operator object). ...

A sketch:

 >>> def seqeval(objseq):
 ...     """evaluate an object sequence. rules tbd."""
 ...     args=[]
 ...     ops=[]
 ...     for obj in objseq:
 ...         if callable(obj):
 ...             if ops[-1:] and obj.__precedence__<= ops[-1].__precedence__:
 ...                 args[-2:] = [ops.pop()(*args[-2:])]
 ...             ops.append(obj)
 ...             continue
 ...         elif isinstance(obj, tuple):
 ...             obj = seqeval(obj)
 ...         while len(args)==0 and ops: # unary
 ...             obj = ops.pop()(obj)
 ...         args.append(obj)
 ...     while ops:
 ...         args[-2:] = [ops.pop()(*args[-2:])]
 ...     return args[-1]
 ...
 >>> def PLUS(x, y=None):
 ...     print 'PLUS(%s, %s)'%(x,y)
 ...     if y is None: return x
 ...     else: return x+y
 ...
 >>> PLUS.__precedence__ = 1
 >>>
 >>> def MINUS(x, y=None):
 ...     print 'MINUS(%s, %s)'%(x,y)
 ...     if y is None: return -x
 ...     else: return x-y
 ...
 >>> MINUS.__precedence__ = 1
 >>>
 >>> def TIMES(x, y):
 ...     print 'TIMES(%s, %s)'%(x,y)
 ...     return x*y
 ...
 >>> TIMES.__precedence__ = 2
 >>>
 >>> seqeval((8, PLUS, 6, TIMES, 2))
 TIMES(6, 2)
 PLUS(8, 12)
 20
 >>> seqeval(((8, PLUS, 6), TIMES, 2))
 PLUS(8, 6)
 TIMES(14, 2)
 28
 >>> seqeval(((8, PLUS, 6), TIMES, (MINUS, 2)))
 PLUS(8, 6)
 MINUS(2, None)
 TIMES(14, -2)
 -28
 >>> seqeval((MINUS, (8, PLUS, 6), TIMES, (MINUS, 2)))
 PLUS(8, 6)
 MINUS(14, None)
 MINUS(2, None)
 TIMES(-14, -2)
 28
 >>> list(seqeval((i, TIMES, j, PLUS, k)) for i in (2,3) for j in (10,100) for k in (5,7))
 TIMES(2, 10)
 PLUS(20, 5)
 TIMES(2, 10)
 PLUS(20, 7)
 TIMES(2, 100)
 PLUS(200, 5)
 TIMES(2, 100)
 PLUS(200, 7)
 TIMES(3, 10)
 PLUS(30, 5)
 TIMES(3, 10)
 PLUS(30, 7)
 TIMES(3, 100)
 PLUS(300, 5)
 TIMES(3, 100)
 PLUS(300, 7)
 [25, 27, 205, 207, 35, 37, 305, 307] 

Regards,
Bengt Richter



More information about the Python-list mailing list