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:
... """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]
...... print 'PLUS(%s, %s)'%(x,y)
... if y is None: return x
... else: return x+y
...... print 'MINUS(%s, %s)'%(x,y)
... if y is None: return -x
... else: return x-y
...... print 'TIMES(%s, %s)'%(x,y)
... return x*y
...TIMES(6, 2)
PLUS(8, 12)
20PLUS(8, 6)
TIMES(14, 2)
28PLUS(8, 6)
MINUS(2, None)
TIMES(14, -2)
-28PLUS(8, 6)
MINUS(14, None)
MINUS(2, None)
TIMES(-14, -2)
28TIMES(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