Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wiseplat
GitHub Repository: wiseplat/python-code
Path: blob/master/ invest-robot-contest_TinkoffBotTwitch-main/venv/lib/python3.8/site-packages/numpy/f2py/symbolic.py
7796 views
1
"""Fortran/C symbolic expressions
2
3
References:
4
- J3/21-007: Draft Fortran 202x. https://j3-fortran.org/doc/year/21/21-007.pdf
5
"""
6
7
# To analyze Fortran expressions to solve dimensions specifications,
8
# for instances, we implement a minimal symbolic engine for parsing
9
# expressions into a tree of expression instances. As a first
10
# instance, we care only about arithmetic expressions involving
11
# integers and operations like addition (+), subtraction (-),
12
# multiplication (*), division (Fortran / is Python //, Fortran // is
13
# concatenate), and exponentiation (**). In addition, .pyf files may
14
# contain C expressions that support here is implemented as well.
15
#
16
# TODO: support logical constants (Op.BOOLEAN)
17
# TODO: support logical operators (.AND., ...)
18
# TODO: support defined operators (.MYOP., ...)
19
#
20
__all__ = ['Expr']
21
22
23
import re
24
import warnings
25
from enum import Enum
26
from math import gcd
27
28
29
class Language(Enum):
30
"""
31
Used as Expr.tostring language argument.
32
"""
33
Python = 0
34
Fortran = 1
35
C = 2
36
37
38
class Op(Enum):
39
"""
40
Used as Expr op attribute.
41
"""
42
INTEGER = 10
43
REAL = 12
44
COMPLEX = 15
45
STRING = 20
46
ARRAY = 30
47
SYMBOL = 40
48
TERNARY = 100
49
APPLY = 200
50
INDEXING = 210
51
CONCAT = 220
52
RELATIONAL = 300
53
TERMS = 1000
54
FACTORS = 2000
55
REF = 3000
56
DEREF = 3001
57
58
59
class RelOp(Enum):
60
"""
61
Used in Op.RELATIONAL expression to specify the function part.
62
"""
63
EQ = 1
64
NE = 2
65
LT = 3
66
LE = 4
67
GT = 5
68
GE = 6
69
70
@classmethod
71
def fromstring(cls, s, language=Language.C):
72
if language is Language.Fortran:
73
return {'.eq.': RelOp.EQ, '.ne.': RelOp.NE,
74
'.lt.': RelOp.LT, '.le.': RelOp.LE,
75
'.gt.': RelOp.GT, '.ge.': RelOp.GE}[s.lower()]
76
return {'==': RelOp.EQ, '!=': RelOp.NE, '<': RelOp.LT,
77
'<=': RelOp.LE, '>': RelOp.GT, '>=': RelOp.GE}[s]
78
79
def tostring(self, language=Language.C):
80
if language is Language.Fortran:
81
return {RelOp.EQ: '.eq.', RelOp.NE: '.ne.',
82
RelOp.LT: '.lt.', RelOp.LE: '.le.',
83
RelOp.GT: '.gt.', RelOp.GE: '.ge.'}[self]
84
return {RelOp.EQ: '==', RelOp.NE: '!=',
85
RelOp.LT: '<', RelOp.LE: '<=',
86
RelOp.GT: '>', RelOp.GE: '>='}[self]
87
88
89
class ArithOp(Enum):
90
"""
91
Used in Op.APPLY expression to specify the function part.
92
"""
93
POS = 1
94
NEG = 2
95
ADD = 3
96
SUB = 4
97
MUL = 5
98
DIV = 6
99
POW = 7
100
101
102
class OpError(Exception):
103
pass
104
105
106
class Precedence(Enum):
107
"""
108
Used as Expr.tostring precedence argument.
109
"""
110
ATOM = 0
111
POWER = 1
112
UNARY = 2
113
PRODUCT = 3
114
SUM = 4
115
LT = 6
116
EQ = 7
117
LAND = 11
118
LOR = 12
119
TERNARY = 13
120
ASSIGN = 14
121
TUPLE = 15
122
NONE = 100
123
124
125
integer_types = (int,)
126
number_types = (int, float)
127
128
129
def _pairs_add(d, k, v):
130
# Internal utility method for updating terms and factors data.
131
c = d.get(k)
132
if c is None:
133
d[k] = v
134
else:
135
c = c + v
136
if c:
137
d[k] = c
138
else:
139
del d[k]
140
141
142
class ExprWarning(UserWarning):
143
pass
144
145
146
def ewarn(message):
147
warnings.warn(message, ExprWarning, stacklevel=2)
148
149
150
class Expr:
151
"""Represents a Fortran expression as a op-data pair.
152
153
Expr instances are hashable and sortable.
154
"""
155
156
@staticmethod
157
def parse(s, language=Language.C):
158
"""Parse a Fortran expression to a Expr.
159
"""
160
return fromstring(s, language=language)
161
162
def __init__(self, op, data):
163
assert isinstance(op, Op)
164
165
# sanity checks
166
if op is Op.INTEGER:
167
# data is a 2-tuple of numeric object and a kind value
168
# (default is 4)
169
assert isinstance(data, tuple) and len(data) == 2
170
assert isinstance(data[0], int)
171
assert isinstance(data[1], (int, str)), data
172
elif op is Op.REAL:
173
# data is a 2-tuple of numeric object and a kind value
174
# (default is 4)
175
assert isinstance(data, tuple) and len(data) == 2
176
assert isinstance(data[0], float)
177
assert isinstance(data[1], (int, str)), data
178
elif op is Op.COMPLEX:
179
# data is a 2-tuple of constant expressions
180
assert isinstance(data, tuple) and len(data) == 2
181
elif op is Op.STRING:
182
# data is a 2-tuple of quoted string and a kind value
183
# (default is 1)
184
assert isinstance(data, tuple) and len(data) == 2
185
assert (isinstance(data[0], str)
186
and data[0][::len(data[0])-1] in ('""', "''", '@@'))
187
assert isinstance(data[1], (int, str)), data
188
elif op is Op.SYMBOL:
189
# data is any hashable object
190
assert hash(data) is not None
191
elif op in (Op.ARRAY, Op.CONCAT):
192
# data is a tuple of expressions
193
assert isinstance(data, tuple)
194
assert all(isinstance(item, Expr) for item in data), data
195
elif op in (Op.TERMS, Op.FACTORS):
196
# data is {<term|base>:<coeff|exponent>} where dict values
197
# are nonzero Python integers
198
assert isinstance(data, dict)
199
elif op is Op.APPLY:
200
# data is (<function>, <operands>, <kwoperands>) where
201
# operands are Expr instances
202
assert isinstance(data, tuple) and len(data) == 3
203
# function is any hashable object
204
assert hash(data[0]) is not None
205
assert isinstance(data[1], tuple)
206
assert isinstance(data[2], dict)
207
elif op is Op.INDEXING:
208
# data is (<object>, <indices>)
209
assert isinstance(data, tuple) and len(data) == 2
210
# function is any hashable object
211
assert hash(data[0]) is not None
212
elif op is Op.TERNARY:
213
# data is (<cond>, <expr1>, <expr2>)
214
assert isinstance(data, tuple) and len(data) == 3
215
elif op in (Op.REF, Op.DEREF):
216
# data is Expr instance
217
assert isinstance(data, Expr)
218
elif op is Op.RELATIONAL:
219
# data is (<relop>, <left>, <right>)
220
assert isinstance(data, tuple) and len(data) == 3
221
else:
222
raise NotImplementedError(
223
f'unknown op or missing sanity check: {op}')
224
225
self.op = op
226
self.data = data
227
228
def __eq__(self, other):
229
return (isinstance(other, Expr)
230
and self.op is other.op
231
and self.data == other.data)
232
233
def __hash__(self):
234
if self.op in (Op.TERMS, Op.FACTORS):
235
data = tuple(sorted(self.data.items()))
236
elif self.op is Op.APPLY:
237
data = self.data[:2] + tuple(sorted(self.data[2].items()))
238
else:
239
data = self.data
240
return hash((self.op, data))
241
242
def __lt__(self, other):
243
if isinstance(other, Expr):
244
if self.op is not other.op:
245
return self.op.value < other.op.value
246
if self.op in (Op.TERMS, Op.FACTORS):
247
return (tuple(sorted(self.data.items()))
248
< tuple(sorted(other.data.items())))
249
if self.op is Op.APPLY:
250
if self.data[:2] != other.data[:2]:
251
return self.data[:2] < other.data[:2]
252
return tuple(sorted(self.data[2].items())) < tuple(
253
sorted(other.data[2].items()))
254
return self.data < other.data
255
return NotImplemented
256
257
def __le__(self, other): return self == other or self < other
258
259
def __gt__(self, other): return not (self <= other)
260
261
def __ge__(self, other): return not (self < other)
262
263
def __repr__(self):
264
return f'{type(self).__name__}({self.op}, {self.data!r})'
265
266
def __str__(self):
267
return self.tostring()
268
269
def tostring(self, parent_precedence=Precedence.NONE,
270
language=Language.Fortran):
271
"""Return a string representation of Expr.
272
"""
273
if self.op in (Op.INTEGER, Op.REAL):
274
precedence = (Precedence.SUM if self.data[0] < 0
275
else Precedence.ATOM)
276
r = str(self.data[0]) + (f'_{self.data[1]}'
277
if self.data[1] != 4 else '')
278
elif self.op is Op.COMPLEX:
279
r = ', '.join(item.tostring(Precedence.TUPLE, language=language)
280
for item in self.data)
281
r = '(' + r + ')'
282
precedence = Precedence.ATOM
283
elif self.op is Op.SYMBOL:
284
precedence = Precedence.ATOM
285
r = str(self.data)
286
elif self.op is Op.STRING:
287
r = self.data[0]
288
if self.data[1] != 1:
289
r = self.data[1] + '_' + r
290
precedence = Precedence.ATOM
291
elif self.op is Op.ARRAY:
292
r = ', '.join(item.tostring(Precedence.TUPLE, language=language)
293
for item in self.data)
294
r = '[' + r + ']'
295
precedence = Precedence.ATOM
296
elif self.op is Op.TERMS:
297
terms = []
298
for term, coeff in sorted(self.data.items()):
299
if coeff < 0:
300
op = ' - '
301
coeff = -coeff
302
else:
303
op = ' + '
304
if coeff == 1:
305
term = term.tostring(Precedence.SUM, language=language)
306
else:
307
if term == as_number(1):
308
term = str(coeff)
309
else:
310
term = f'{coeff} * ' + term.tostring(
311
Precedence.PRODUCT, language=language)
312
if terms:
313
terms.append(op)
314
elif op == ' - ':
315
terms.append('-')
316
terms.append(term)
317
r = ''.join(terms) or '0'
318
precedence = Precedence.SUM if terms else Precedence.ATOM
319
elif self.op is Op.FACTORS:
320
factors = []
321
tail = []
322
for base, exp in sorted(self.data.items()):
323
op = ' * '
324
if exp == 1:
325
factor = base.tostring(Precedence.PRODUCT,
326
language=language)
327
elif language is Language.C:
328
if exp in range(2, 10):
329
factor = base.tostring(Precedence.PRODUCT,
330
language=language)
331
factor = ' * '.join([factor] * exp)
332
elif exp in range(-10, 0):
333
factor = base.tostring(Precedence.PRODUCT,
334
language=language)
335
tail += [factor] * -exp
336
continue
337
else:
338
factor = base.tostring(Precedence.TUPLE,
339
language=language)
340
factor = f'pow({factor}, {exp})'
341
else:
342
factor = base.tostring(Precedence.POWER,
343
language=language) + f' ** {exp}'
344
if factors:
345
factors.append(op)
346
factors.append(factor)
347
if tail:
348
if not factors:
349
factors += ['1']
350
factors += ['/', '(', ' * '.join(tail), ')']
351
r = ''.join(factors) or '1'
352
precedence = Precedence.PRODUCT if factors else Precedence.ATOM
353
elif self.op is Op.APPLY:
354
name, args, kwargs = self.data
355
if name is ArithOp.DIV and language is Language.C:
356
numer, denom = [arg.tostring(Precedence.PRODUCT,
357
language=language)
358
for arg in args]
359
r = f'{numer} / {denom}'
360
precedence = Precedence.PRODUCT
361
else:
362
args = [arg.tostring(Precedence.TUPLE, language=language)
363
for arg in args]
364
args += [k + '=' + v.tostring(Precedence.NONE)
365
for k, v in kwargs.items()]
366
r = f'{name}({", ".join(args)})'
367
precedence = Precedence.ATOM
368
elif self.op is Op.INDEXING:
369
name = self.data[0]
370
args = [arg.tostring(Precedence.TUPLE, language=language)
371
for arg in self.data[1:]]
372
r = f'{name}[{", ".join(args)}]'
373
precedence = Precedence.ATOM
374
elif self.op is Op.CONCAT:
375
args = [arg.tostring(Precedence.PRODUCT, language=language)
376
for arg in self.data]
377
r = " // ".join(args)
378
precedence = Precedence.PRODUCT
379
elif self.op is Op.TERNARY:
380
cond, expr1, expr2 = [a.tostring(Precedence.TUPLE,
381
language=language)
382
for a in self.data]
383
if language is Language.C:
384
r = f'({cond}?{expr1}:{expr2})'
385
elif language is Language.Python:
386
r = f'({expr1} if {cond} else {expr2})'
387
elif language is Language.Fortran:
388
r = f'merge({expr1}, {expr2}, {cond})'
389
else:
390
raise NotImplementedError(
391
f'tostring for {self.op} and {language}')
392
precedence = Precedence.ATOM
393
elif self.op is Op.REF:
394
r = '&' + self.data.tostring(Precedence.UNARY, language=language)
395
precedence = Precedence.UNARY
396
elif self.op is Op.DEREF:
397
r = '*' + self.data.tostring(Precedence.UNARY, language=language)
398
precedence = Precedence.UNARY
399
elif self.op is Op.RELATIONAL:
400
rop, left, right = self.data
401
precedence = (Precedence.EQ if rop in (RelOp.EQ, RelOp.NE)
402
else Precedence.LT)
403
left = left.tostring(precedence, language=language)
404
right = right.tostring(precedence, language=language)
405
rop = rop.tostring(language=language)
406
r = f'{left} {rop} {right}'
407
else:
408
raise NotImplementedError(f'tostring for op {self.op}')
409
if parent_precedence.value < precedence.value:
410
# If parent precedence is higher than operand precedence,
411
# operand will be enclosed in parenthesis.
412
return '(' + r + ')'
413
return r
414
415
def __pos__(self):
416
return self
417
418
def __neg__(self):
419
return self * -1
420
421
def __add__(self, other):
422
other = as_expr(other)
423
if isinstance(other, Expr):
424
if self.op is other.op:
425
if self.op in (Op.INTEGER, Op.REAL):
426
return as_number(
427
self.data[0] + other.data[0],
428
max(self.data[1], other.data[1]))
429
if self.op is Op.COMPLEX:
430
r1, i1 = self.data
431
r2, i2 = other.data
432
return as_complex(r1 + r2, i1 + i2)
433
if self.op is Op.TERMS:
434
r = Expr(self.op, dict(self.data))
435
for k, v in other.data.items():
436
_pairs_add(r.data, k, v)
437
return normalize(r)
438
if self.op is Op.COMPLEX and other.op in (Op.INTEGER, Op.REAL):
439
return self + as_complex(other)
440
elif self.op in (Op.INTEGER, Op.REAL) and other.op is Op.COMPLEX:
441
return as_complex(self) + other
442
elif self.op is Op.REAL and other.op is Op.INTEGER:
443
return self + as_real(other, kind=self.data[1])
444
elif self.op is Op.INTEGER and other.op is Op.REAL:
445
return as_real(self, kind=other.data[1]) + other
446
return as_terms(self) + as_terms(other)
447
return NotImplemented
448
449
def __radd__(self, other):
450
if isinstance(other, number_types):
451
return as_number(other) + self
452
return NotImplemented
453
454
def __sub__(self, other):
455
return self + (-other)
456
457
def __rsub__(self, other):
458
if isinstance(other, number_types):
459
return as_number(other) - self
460
return NotImplemented
461
462
def __mul__(self, other):
463
other = as_expr(other)
464
if isinstance(other, Expr):
465
if self.op is other.op:
466
if self.op in (Op.INTEGER, Op.REAL):
467
return as_number(self.data[0] * other.data[0],
468
max(self.data[1], other.data[1]))
469
elif self.op is Op.COMPLEX:
470
r1, i1 = self.data
471
r2, i2 = other.data
472
return as_complex(r1 * r2 - i1 * i2, r1 * i2 + r2 * i1)
473
474
if self.op is Op.FACTORS:
475
r = Expr(self.op, dict(self.data))
476
for k, v in other.data.items():
477
_pairs_add(r.data, k, v)
478
return normalize(r)
479
elif self.op is Op.TERMS:
480
r = Expr(self.op, {})
481
for t1, c1 in self.data.items():
482
for t2, c2 in other.data.items():
483
_pairs_add(r.data, t1 * t2, c1 * c2)
484
return normalize(r)
485
486
if self.op is Op.COMPLEX and other.op in (Op.INTEGER, Op.REAL):
487
return self * as_complex(other)
488
elif other.op is Op.COMPLEX and self.op in (Op.INTEGER, Op.REAL):
489
return as_complex(self) * other
490
elif self.op is Op.REAL and other.op is Op.INTEGER:
491
return self * as_real(other, kind=self.data[1])
492
elif self.op is Op.INTEGER and other.op is Op.REAL:
493
return as_real(self, kind=other.data[1]) * other
494
495
if self.op is Op.TERMS:
496
return self * as_terms(other)
497
elif other.op is Op.TERMS:
498
return as_terms(self) * other
499
500
return as_factors(self) * as_factors(other)
501
return NotImplemented
502
503
def __rmul__(self, other):
504
if isinstance(other, number_types):
505
return as_number(other) * self
506
return NotImplemented
507
508
def __pow__(self, other):
509
other = as_expr(other)
510
if isinstance(other, Expr):
511
if other.op is Op.INTEGER:
512
exponent = other.data[0]
513
# TODO: other kind not used
514
if exponent == 0:
515
return as_number(1)
516
if exponent == 1:
517
return self
518
if exponent > 0:
519
if self.op is Op.FACTORS:
520
r = Expr(self.op, {})
521
for k, v in self.data.items():
522
r.data[k] = v * exponent
523
return normalize(r)
524
return self * (self ** (exponent - 1))
525
elif exponent != -1:
526
return (self ** (-exponent)) ** -1
527
return Expr(Op.FACTORS, {self: exponent})
528
return as_apply(ArithOp.POW, self, other)
529
return NotImplemented
530
531
def __truediv__(self, other):
532
other = as_expr(other)
533
if isinstance(other, Expr):
534
# Fortran / is different from Python /:
535
# - `/` is a truncate operation for integer operands
536
return normalize(as_apply(ArithOp.DIV, self, other))
537
return NotImplemented
538
539
def __rtruediv__(self, other):
540
other = as_expr(other)
541
if isinstance(other, Expr):
542
return other / self
543
return NotImplemented
544
545
def __floordiv__(self, other):
546
other = as_expr(other)
547
if isinstance(other, Expr):
548
# Fortran // is different from Python //:
549
# - `//` is a concatenate operation for string operands
550
return normalize(Expr(Op.CONCAT, (self, other)))
551
return NotImplemented
552
553
def __rfloordiv__(self, other):
554
other = as_expr(other)
555
if isinstance(other, Expr):
556
return other // self
557
return NotImplemented
558
559
def __call__(self, *args, **kwargs):
560
# In Fortran, parenthesis () are use for both function call as
561
# well as indexing operations.
562
#
563
# TODO: implement a method for deciding when __call__ should
564
# return an INDEXING expression.
565
return as_apply(self, *map(as_expr, args),
566
**dict((k, as_expr(v)) for k, v in kwargs.items()))
567
568
def __getitem__(self, index):
569
# Provided to support C indexing operations that .pyf files
570
# may contain.
571
index = as_expr(index)
572
if not isinstance(index, tuple):
573
index = index,
574
if len(index) > 1:
575
ewarn(f'C-index should be a single expression but got `{index}`')
576
return Expr(Op.INDEXING, (self,) + index)
577
578
def substitute(self, symbols_map):
579
"""Recursively substitute symbols with values in symbols map.
580
581
Symbols map is a dictionary of symbol-expression pairs.
582
"""
583
if self.op is Op.SYMBOL:
584
value = symbols_map.get(self)
585
if value is None:
586
return self
587
m = re.match(r'\A(@__f2py_PARENTHESIS_(\w+)_\d+@)\Z', self.data)
588
if m:
589
# complement to fromstring method
590
items, paren = m.groups()
591
if paren in ['ROUNDDIV', 'SQUARE']:
592
return as_array(value)
593
assert paren == 'ROUND', (paren, value)
594
return value
595
if self.op in (Op.INTEGER, Op.REAL, Op.STRING):
596
return self
597
if self.op in (Op.ARRAY, Op.COMPLEX):
598
return Expr(self.op, tuple(item.substitute(symbols_map)
599
for item in self.data))
600
if self.op is Op.CONCAT:
601
return normalize(Expr(self.op, tuple(item.substitute(symbols_map)
602
for item in self.data)))
603
if self.op is Op.TERMS:
604
r = None
605
for term, coeff in self.data.items():
606
if r is None:
607
r = term.substitute(symbols_map) * coeff
608
else:
609
r += term.substitute(symbols_map) * coeff
610
if r is None:
611
ewarn('substitute: empty TERMS expression interpreted as'
612
' int-literal 0')
613
return as_number(0)
614
return r
615
if self.op is Op.FACTORS:
616
r = None
617
for base, exponent in self.data.items():
618
if r is None:
619
r = base.substitute(symbols_map) ** exponent
620
else:
621
r *= base.substitute(symbols_map) ** exponent
622
if r is None:
623
ewarn('substitute: empty FACTORS expression interpreted'
624
' as int-literal 1')
625
return as_number(1)
626
return r
627
if self.op is Op.APPLY:
628
target, args, kwargs = self.data
629
if isinstance(target, Expr):
630
target = target.substitute(symbols_map)
631
args = tuple(a.substitute(symbols_map) for a in args)
632
kwargs = dict((k, v.substitute(symbols_map))
633
for k, v in kwargs.items())
634
return normalize(Expr(self.op, (target, args, kwargs)))
635
if self.op is Op.INDEXING:
636
func = self.data[0]
637
if isinstance(func, Expr):
638
func = func.substitute(symbols_map)
639
args = tuple(a.substitute(symbols_map) for a in self.data[1:])
640
return normalize(Expr(self.op, (func,) + args))
641
if self.op is Op.TERNARY:
642
operands = tuple(a.substitute(symbols_map) for a in self.data)
643
return normalize(Expr(self.op, operands))
644
if self.op in (Op.REF, Op.DEREF):
645
return normalize(Expr(self.op, self.data.substitute(symbols_map)))
646
if self.op is Op.RELATIONAL:
647
rop, left, right = self.data
648
left = left.substitute(symbols_map)
649
right = right.substitute(symbols_map)
650
return normalize(Expr(self.op, (rop, left, right)))
651
raise NotImplementedError(f'substitute method for {self.op}: {self!r}')
652
653
def traverse(self, visit, *args, **kwargs):
654
"""Traverse expression tree with visit function.
655
656
The visit function is applied to an expression with given args
657
and kwargs.
658
659
Traverse call returns an expression returned by visit when not
660
None, otherwise return a new normalized expression with
661
traverse-visit sub-expressions.
662
"""
663
result = visit(self, *args, **kwargs)
664
if result is not None:
665
return result
666
667
if self.op in (Op.INTEGER, Op.REAL, Op.STRING, Op.SYMBOL):
668
return self
669
elif self.op in (Op.COMPLEX, Op.ARRAY, Op.CONCAT, Op.TERNARY):
670
return normalize(Expr(self.op, tuple(
671
item.traverse(visit, *args, **kwargs)
672
for item in self.data)))
673
elif self.op in (Op.TERMS, Op.FACTORS):
674
data = {}
675
for k, v in self.data.items():
676
k = k.traverse(visit, *args, **kwargs)
677
v = (v.traverse(visit, *args, **kwargs)
678
if isinstance(v, Expr) else v)
679
if k in data:
680
v = data[k] + v
681
data[k] = v
682
return normalize(Expr(self.op, data))
683
elif self.op is Op.APPLY:
684
obj = self.data[0]
685
func = (obj.traverse(visit, *args, **kwargs)
686
if isinstance(obj, Expr) else obj)
687
operands = tuple(operand.traverse(visit, *args, **kwargs)
688
for operand in self.data[1])
689
kwoperands = dict((k, v.traverse(visit, *args, **kwargs))
690
for k, v in self.data[2].items())
691
return normalize(Expr(self.op, (func, operands, kwoperands)))
692
elif self.op is Op.INDEXING:
693
obj = self.data[0]
694
obj = (obj.traverse(visit, *args, **kwargs)
695
if isinstance(obj, Expr) else obj)
696
indices = tuple(index.traverse(visit, *args, **kwargs)
697
for index in self.data[1:])
698
return normalize(Expr(self.op, (obj,) + indices))
699
elif self.op in (Op.REF, Op.DEREF):
700
return normalize(Expr(self.op,
701
self.data.traverse(visit, *args, **kwargs)))
702
elif self.op is Op.RELATIONAL:
703
rop, left, right = self.data
704
left = left.traverse(visit, *args, **kwargs)
705
right = right.traverse(visit, *args, **kwargs)
706
return normalize(Expr(self.op, (rop, left, right)))
707
raise NotImplementedError(f'traverse method for {self.op}')
708
709
def contains(self, other):
710
"""Check if self contains other.
711
"""
712
found = []
713
714
def visit(expr, found=found):
715
if found:
716
return expr
717
elif expr == other:
718
found.append(1)
719
return expr
720
721
self.traverse(visit)
722
723
return len(found) != 0
724
725
def symbols(self):
726
"""Return a set of symbols contained in self.
727
"""
728
found = set()
729
730
def visit(expr, found=found):
731
if expr.op is Op.SYMBOL:
732
found.add(expr)
733
734
self.traverse(visit)
735
736
return found
737
738
def polynomial_atoms(self):
739
"""Return a set of expressions used as atoms in polynomial self.
740
"""
741
found = set()
742
743
def visit(expr, found=found):
744
if expr.op is Op.FACTORS:
745
for b in expr.data:
746
b.traverse(visit)
747
return expr
748
if expr.op in (Op.TERMS, Op.COMPLEX):
749
return
750
if expr.op is Op.APPLY and isinstance(expr.data[0], ArithOp):
751
if expr.data[0] is ArithOp.POW:
752
expr.data[1][0].traverse(visit)
753
return expr
754
return
755
if expr.op in (Op.INTEGER, Op.REAL):
756
return expr
757
758
found.add(expr)
759
760
if expr.op in (Op.INDEXING, Op.APPLY):
761
return expr
762
763
self.traverse(visit)
764
765
return found
766
767
def linear_solve(self, symbol):
768
"""Return a, b such that a * symbol + b == self.
769
770
If self is not linear with respect to symbol, raise RuntimeError.
771
"""
772
b = self.substitute({symbol: as_number(0)})
773
ax = self - b
774
a = ax.substitute({symbol: as_number(1)})
775
776
zero, _ = as_numer_denom(a * symbol - ax)
777
778
if zero != as_number(0):
779
raise RuntimeError(f'not a {symbol}-linear equation:'
780
f' {a} * {symbol} + {b} == {self}')
781
return a, b
782
783
784
def normalize(obj):
785
"""Normalize Expr and apply basic evaluation methods.
786
"""
787
if not isinstance(obj, Expr):
788
return obj
789
790
if obj.op is Op.TERMS:
791
d = {}
792
for t, c in obj.data.items():
793
if c == 0:
794
continue
795
if t.op is Op.COMPLEX and c != 1:
796
t = t * c
797
c = 1
798
if t.op is Op.TERMS:
799
for t1, c1 in t.data.items():
800
_pairs_add(d, t1, c1 * c)
801
else:
802
_pairs_add(d, t, c)
803
if len(d) == 0:
804
# TODO: deterimine correct kind
805
return as_number(0)
806
elif len(d) == 1:
807
(t, c), = d.items()
808
if c == 1:
809
return t
810
return Expr(Op.TERMS, d)
811
812
if obj.op is Op.FACTORS:
813
coeff = 1
814
d = {}
815
for b, e in obj.data.items():
816
if e == 0:
817
continue
818
if b.op is Op.TERMS and isinstance(e, integer_types) and e > 1:
819
# expand integer powers of sums
820
b = b * (b ** (e - 1))
821
e = 1
822
823
if b.op in (Op.INTEGER, Op.REAL):
824
if e == 1:
825
coeff *= b.data[0]
826
elif e > 0:
827
coeff *= b.data[0] ** e
828
else:
829
_pairs_add(d, b, e)
830
elif b.op is Op.FACTORS:
831
if e > 0 and isinstance(e, integer_types):
832
for b1, e1 in b.data.items():
833
_pairs_add(d, b1, e1 * e)
834
else:
835
_pairs_add(d, b, e)
836
else:
837
_pairs_add(d, b, e)
838
if len(d) == 0 or coeff == 0:
839
# TODO: deterimine correct kind
840
assert isinstance(coeff, number_types)
841
return as_number(coeff)
842
elif len(d) == 1:
843
(b, e), = d.items()
844
if e == 1:
845
t = b
846
else:
847
t = Expr(Op.FACTORS, d)
848
if coeff == 1:
849
return t
850
return Expr(Op.TERMS, {t: coeff})
851
elif coeff == 1:
852
return Expr(Op.FACTORS, d)
853
else:
854
return Expr(Op.TERMS, {Expr(Op.FACTORS, d): coeff})
855
856
if obj.op is Op.APPLY and obj.data[0] is ArithOp.DIV:
857
dividend, divisor = obj.data[1]
858
t1, c1 = as_term_coeff(dividend)
859
t2, c2 = as_term_coeff(divisor)
860
if isinstance(c1, integer_types) and isinstance(c2, integer_types):
861
g = gcd(c1, c2)
862
c1, c2 = c1//g, c2//g
863
else:
864
c1, c2 = c1/c2, 1
865
866
if t1.op is Op.APPLY and t1.data[0] is ArithOp.DIV:
867
numer = t1.data[1][0] * c1
868
denom = t1.data[1][1] * t2 * c2
869
return as_apply(ArithOp.DIV, numer, denom)
870
871
if t2.op is Op.APPLY and t2.data[0] is ArithOp.DIV:
872
numer = t2.data[1][1] * t1 * c1
873
denom = t2.data[1][0] * c2
874
return as_apply(ArithOp.DIV, numer, denom)
875
876
d = dict(as_factors(t1).data)
877
for b, e in as_factors(t2).data.items():
878
_pairs_add(d, b, -e)
879
numer, denom = {}, {}
880
for b, e in d.items():
881
if e > 0:
882
numer[b] = e
883
else:
884
denom[b] = -e
885
numer = normalize(Expr(Op.FACTORS, numer)) * c1
886
denom = normalize(Expr(Op.FACTORS, denom)) * c2
887
888
if denom.op in (Op.INTEGER, Op.REAL) and denom.data[0] == 1:
889
# TODO: denom kind not used
890
return numer
891
return as_apply(ArithOp.DIV, numer, denom)
892
893
if obj.op is Op.CONCAT:
894
lst = [obj.data[0]]
895
for s in obj.data[1:]:
896
last = lst[-1]
897
if (
898
last.op is Op.STRING
899
and s.op is Op.STRING
900
and last.data[0][0] in '"\''
901
and s.data[0][0] == last.data[0][-1]
902
):
903
new_last = as_string(last.data[0][:-1] + s.data[0][1:],
904
max(last.data[1], s.data[1]))
905
lst[-1] = new_last
906
else:
907
lst.append(s)
908
if len(lst) == 1:
909
return lst[0]
910
return Expr(Op.CONCAT, tuple(lst))
911
912
if obj.op is Op.TERNARY:
913
cond, expr1, expr2 = map(normalize, obj.data)
914
if cond.op is Op.INTEGER:
915
return expr1 if cond.data[0] else expr2
916
return Expr(Op.TERNARY, (cond, expr1, expr2))
917
918
return obj
919
920
921
def as_expr(obj):
922
"""Convert non-Expr objects to Expr objects.
923
"""
924
if isinstance(obj, complex):
925
return as_complex(obj.real, obj.imag)
926
if isinstance(obj, number_types):
927
return as_number(obj)
928
if isinstance(obj, str):
929
# STRING expression holds string with boundary quotes, hence
930
# applying repr:
931
return as_string(repr(obj))
932
if isinstance(obj, tuple):
933
return tuple(map(as_expr, obj))
934
return obj
935
936
937
def as_symbol(obj):
938
"""Return object as SYMBOL expression (variable or unparsed expression).
939
"""
940
return Expr(Op.SYMBOL, obj)
941
942
943
def as_number(obj, kind=4):
944
"""Return object as INTEGER or REAL constant.
945
"""
946
if isinstance(obj, int):
947
return Expr(Op.INTEGER, (obj, kind))
948
if isinstance(obj, float):
949
return Expr(Op.REAL, (obj, kind))
950
if isinstance(obj, Expr):
951
if obj.op in (Op.INTEGER, Op.REAL):
952
return obj
953
raise OpError(f'cannot convert {obj} to INTEGER or REAL constant')
954
955
956
def as_integer(obj, kind=4):
957
"""Return object as INTEGER constant.
958
"""
959
if isinstance(obj, int):
960
return Expr(Op.INTEGER, (obj, kind))
961
if isinstance(obj, Expr):
962
if obj.op is Op.INTEGER:
963
return obj
964
raise OpError(f'cannot convert {obj} to INTEGER constant')
965
966
967
def as_real(obj, kind=4):
968
"""Return object as REAL constant.
969
"""
970
if isinstance(obj, int):
971
return Expr(Op.REAL, (float(obj), kind))
972
if isinstance(obj, float):
973
return Expr(Op.REAL, (obj, kind))
974
if isinstance(obj, Expr):
975
if obj.op is Op.REAL:
976
return obj
977
elif obj.op is Op.INTEGER:
978
return Expr(Op.REAL, (float(obj.data[0]), kind))
979
raise OpError(f'cannot convert {obj} to REAL constant')
980
981
982
def as_string(obj, kind=1):
983
"""Return object as STRING expression (string literal constant).
984
"""
985
return Expr(Op.STRING, (obj, kind))
986
987
988
def as_array(obj):
989
"""Return object as ARRAY expression (array constant).
990
"""
991
if isinstance(obj, Expr):
992
obj = obj,
993
return Expr(Op.ARRAY, obj)
994
995
996
def as_complex(real, imag=0):
997
"""Return object as COMPLEX expression (complex literal constant).
998
"""
999
return Expr(Op.COMPLEX, (as_expr(real), as_expr(imag)))
1000
1001
1002
def as_apply(func, *args, **kwargs):
1003
"""Return object as APPLY expression (function call, constructor, etc.)
1004
"""
1005
return Expr(Op.APPLY,
1006
(func, tuple(map(as_expr, args)),
1007
dict((k, as_expr(v)) for k, v in kwargs.items())))
1008
1009
1010
def as_ternary(cond, expr1, expr2):
1011
"""Return object as TERNARY expression (cond?expr1:expr2).
1012
"""
1013
return Expr(Op.TERNARY, (cond, expr1, expr2))
1014
1015
1016
def as_ref(expr):
1017
"""Return object as referencing expression.
1018
"""
1019
return Expr(Op.REF, expr)
1020
1021
1022
def as_deref(expr):
1023
"""Return object as dereferencing expression.
1024
"""
1025
return Expr(Op.DEREF, expr)
1026
1027
1028
def as_eq(left, right):
1029
return Expr(Op.RELATIONAL, (RelOp.EQ, left, right))
1030
1031
1032
def as_ne(left, right):
1033
return Expr(Op.RELATIONAL, (RelOp.NE, left, right))
1034
1035
1036
def as_lt(left, right):
1037
return Expr(Op.RELATIONAL, (RelOp.LT, left, right))
1038
1039
1040
def as_le(left, right):
1041
return Expr(Op.RELATIONAL, (RelOp.LE, left, right))
1042
1043
1044
def as_gt(left, right):
1045
return Expr(Op.RELATIONAL, (RelOp.GT, left, right))
1046
1047
1048
def as_ge(left, right):
1049
return Expr(Op.RELATIONAL, (RelOp.GE, left, right))
1050
1051
1052
def as_terms(obj):
1053
"""Return expression as TERMS expression.
1054
"""
1055
if isinstance(obj, Expr):
1056
obj = normalize(obj)
1057
if obj.op is Op.TERMS:
1058
return obj
1059
if obj.op is Op.INTEGER:
1060
return Expr(Op.TERMS, {as_integer(1, obj.data[1]): obj.data[0]})
1061
if obj.op is Op.REAL:
1062
return Expr(Op.TERMS, {as_real(1, obj.data[1]): obj.data[0]})
1063
return Expr(Op.TERMS, {obj: 1})
1064
raise OpError(f'cannot convert {type(obj)} to terms Expr')
1065
1066
1067
def as_factors(obj):
1068
"""Return expression as FACTORS expression.
1069
"""
1070
if isinstance(obj, Expr):
1071
obj = normalize(obj)
1072
if obj.op is Op.FACTORS:
1073
return obj
1074
if obj.op is Op.TERMS:
1075
if len(obj.data) == 1:
1076
(term, coeff), = obj.data.items()
1077
if coeff == 1:
1078
return Expr(Op.FACTORS, {term: 1})
1079
return Expr(Op.FACTORS, {term: 1, Expr.number(coeff): 1})
1080
if ((obj.op is Op.APPLY
1081
and obj.data[0] is ArithOp.DIV
1082
and not obj.data[2])):
1083
return Expr(Op.FACTORS, {obj.data[1][0]: 1, obj.data[1][1]: -1})
1084
return Expr(Op.FACTORS, {obj: 1})
1085
raise OpError(f'cannot convert {type(obj)} to terms Expr')
1086
1087
1088
def as_term_coeff(obj):
1089
"""Return expression as term-coefficient pair.
1090
"""
1091
if isinstance(obj, Expr):
1092
obj = normalize(obj)
1093
if obj.op is Op.INTEGER:
1094
return as_integer(1, obj.data[1]), obj.data[0]
1095
if obj.op is Op.REAL:
1096
return as_real(1, obj.data[1]), obj.data[0]
1097
if obj.op is Op.TERMS:
1098
if len(obj.data) == 1:
1099
(term, coeff), = obj.data.items()
1100
return term, coeff
1101
# TODO: find common divisor of coefficients
1102
if obj.op is Op.APPLY and obj.data[0] is ArithOp.DIV:
1103
t, c = as_term_coeff(obj.data[1][0])
1104
return as_apply(ArithOp.DIV, t, obj.data[1][1]), c
1105
return obj, 1
1106
raise OpError(f'cannot convert {type(obj)} to term and coeff')
1107
1108
1109
def as_numer_denom(obj):
1110
"""Return expression as numer-denom pair.
1111
"""
1112
if isinstance(obj, Expr):
1113
obj = normalize(obj)
1114
if obj.op in (Op.INTEGER, Op.REAL, Op.COMPLEX, Op.SYMBOL,
1115
Op.INDEXING, Op.TERNARY):
1116
return obj, as_number(1)
1117
elif obj.op is Op.APPLY:
1118
if obj.data[0] is ArithOp.DIV and not obj.data[2]:
1119
numers, denoms = map(as_numer_denom, obj.data[1])
1120
return numers[0] * denoms[1], numers[1] * denoms[0]
1121
return obj, as_number(1)
1122
elif obj.op is Op.TERMS:
1123
numers, denoms = [], []
1124
for term, coeff in obj.data.items():
1125
n, d = as_numer_denom(term)
1126
n = n * coeff
1127
numers.append(n)
1128
denoms.append(d)
1129
numer, denom = as_number(0), as_number(1)
1130
for i in range(len(numers)):
1131
n = numers[i]
1132
for j in range(len(numers)):
1133
if i != j:
1134
n *= denoms[j]
1135
numer += n
1136
denom *= denoms[i]
1137
if denom.op in (Op.INTEGER, Op.REAL) and denom.data[0] < 0:
1138
numer, denom = -numer, -denom
1139
return numer, denom
1140
elif obj.op is Op.FACTORS:
1141
numer, denom = as_number(1), as_number(1)
1142
for b, e in obj.data.items():
1143
bnumer, bdenom = as_numer_denom(b)
1144
if e > 0:
1145
numer *= bnumer ** e
1146
denom *= bdenom ** e
1147
elif e < 0:
1148
numer *= bdenom ** (-e)
1149
denom *= bnumer ** (-e)
1150
return numer, denom
1151
raise OpError(f'cannot convert {type(obj)} to numer and denom')
1152
1153
1154
def _counter():
1155
# Used internally to generate unique dummy symbols
1156
counter = 0
1157
while True:
1158
counter += 1
1159
yield counter
1160
1161
1162
COUNTER = _counter()
1163
1164
1165
def eliminate_quotes(s):
1166
"""Replace quoted substrings of input string.
1167
1168
Return a new string and a mapping of replacements.
1169
"""
1170
d = {}
1171
1172
def repl(m):
1173
kind, value = m.groups()[:2]
1174
if kind:
1175
# remove trailing underscore
1176
kind = kind[:-1]
1177
p = {"'": "SINGLE", '"': "DOUBLE"}[value[0]]
1178
k = f'{kind}@__f2py_QUOTES_{p}_{COUNTER.__next__()}@'
1179
d[k] = value
1180
return k
1181
1182
new_s = re.sub(r'({kind}_|)({single_quoted}|{double_quoted})'.format(
1183
kind=r'\w[\w\d_]*',
1184
single_quoted=r"('([^'\\]|(\\.))*')",
1185
double_quoted=r'("([^"\\]|(\\.))*")'),
1186
repl, s)
1187
1188
assert '"' not in new_s
1189
assert "'" not in new_s
1190
1191
return new_s, d
1192
1193
1194
def insert_quotes(s, d):
1195
"""Inverse of eliminate_quotes.
1196
"""
1197
for k, v in d.items():
1198
kind = k[:k.find('@')]
1199
if kind:
1200
kind += '_'
1201
s = s.replace(k, kind + v)
1202
return s
1203
1204
1205
def replace_parenthesis(s):
1206
"""Replace substrings of input that are enclosed in parenthesis.
1207
1208
Return a new string and a mapping of replacements.
1209
"""
1210
# Find a parenthesis pair that appears first.
1211
1212
# Fortran deliminator are `(`, `)`, `[`, `]`, `(/', '/)`, `/`.
1213
# We don't handle `/` deliminator because it is not a part of an
1214
# expression.
1215
left, right = None, None
1216
mn_i = len(s)
1217
for left_, right_ in (('(/', '/)'),
1218
'()',
1219
'{}', # to support C literal structs
1220
'[]'):
1221
i = s.find(left_)
1222
if i == -1:
1223
continue
1224
if i < mn_i:
1225
mn_i = i
1226
left, right = left_, right_
1227
1228
if left is None:
1229
return s, {}
1230
1231
i = mn_i
1232
j = s.find(right, i)
1233
1234
while s.count(left, i + 1, j) != s.count(right, i + 1, j):
1235
j = s.find(right, j + 1)
1236
if j == -1:
1237
raise ValueError(f'Mismatch of {left+right} parenthesis in {s!r}')
1238
1239
p = {'(': 'ROUND', '[': 'SQUARE', '{': 'CURLY', '(/': 'ROUNDDIV'}[left]
1240
1241
k = f'@__f2py_PARENTHESIS_{p}_{COUNTER.__next__()}@'
1242
v = s[i+len(left):j]
1243
r, d = replace_parenthesis(s[j+len(right):])
1244
d[k] = v
1245
return s[:i] + k + r, d
1246
1247
1248
def _get_parenthesis_kind(s):
1249
assert s.startswith('@__f2py_PARENTHESIS_'), s
1250
return s.split('_')[4]
1251
1252
1253
def unreplace_parenthesis(s, d):
1254
"""Inverse of replace_parenthesis.
1255
"""
1256
for k, v in d.items():
1257
p = _get_parenthesis_kind(k)
1258
left = dict(ROUND='(', SQUARE='[', CURLY='{', ROUNDDIV='(/')[p]
1259
right = dict(ROUND=')', SQUARE=']', CURLY='}', ROUNDDIV='/)')[p]
1260
s = s.replace(k, left + v + right)
1261
return s
1262
1263
1264
def fromstring(s, language=Language.C):
1265
"""Create an expression from a string.
1266
1267
This is a "lazy" parser, that is, only arithmetic operations are
1268
resolved, non-arithmetic operations are treated as symbols.
1269
"""
1270
r = _FromStringWorker(language=language).parse(s)
1271
if isinstance(r, Expr):
1272
return r
1273
raise ValueError(f'failed to parse `{s}` to Expr instance: got `{r}`')
1274
1275
1276
class _Pair:
1277
# Internal class to represent a pair of expressions
1278
1279
def __init__(self, left, right):
1280
self.left = left
1281
self.right = right
1282
1283
def substitute(self, symbols_map):
1284
left, right = self.left, self.right
1285
if isinstance(left, Expr):
1286
left = left.substitute(symbols_map)
1287
if isinstance(right, Expr):
1288
right = right.substitute(symbols_map)
1289
return _Pair(left, right)
1290
1291
def __repr__(self):
1292
return f'{type(self).__name__}({self.left}, {self.right})'
1293
1294
1295
class _FromStringWorker:
1296
1297
def __init__(self, language=Language.C):
1298
self.original = None
1299
self.quotes_map = None
1300
self.language = language
1301
1302
def finalize_string(self, s):
1303
return insert_quotes(s, self.quotes_map)
1304
1305
def parse(self, inp):
1306
self.original = inp
1307
unquoted, self.quotes_map = eliminate_quotes(inp)
1308
return self.process(unquoted)
1309
1310
def process(self, s, context='expr'):
1311
"""Parse string within the given context.
1312
1313
The context may define the result in case of ambiguous
1314
expressions. For instance, consider expressions `f(x, y)` and
1315
`(x, y) + (a, b)` where `f` is a function and pair `(x, y)`
1316
denotes complex number. Specifying context as "args" or
1317
"expr", the subexpression `(x, y)` will be parse to an
1318
argument list or to a complex number, respectively.
1319
"""
1320
if isinstance(s, (list, tuple)):
1321
return type(s)(self.process(s_, context) for s_ in s)
1322
1323
assert isinstance(s, str), (type(s), s)
1324
1325
# replace subexpressions in parenthesis with f2py @-names
1326
r, raw_symbols_map = replace_parenthesis(s)
1327
r = r.strip()
1328
1329
def restore(r):
1330
# restores subexpressions marked with f2py @-names
1331
if isinstance(r, (list, tuple)):
1332
return type(r)(map(restore, r))
1333
return unreplace_parenthesis(r, raw_symbols_map)
1334
1335
# comma-separated tuple
1336
if ',' in r:
1337
operands = restore(r.split(','))
1338
if context == 'args':
1339
return tuple(self.process(operands))
1340
if context == 'expr':
1341
if len(operands) == 2:
1342
# complex number literal
1343
return as_complex(*self.process(operands))
1344
raise NotImplementedError(
1345
f'parsing comma-separated list (context={context}): {r}')
1346
1347
# ternary operation
1348
m = re.match(r'\A([^?]+)[?]([^:]+)[:](.+)\Z', r)
1349
if m:
1350
assert context == 'expr', context
1351
oper, expr1, expr2 = restore(m.groups())
1352
oper = self.process(oper)
1353
expr1 = self.process(expr1)
1354
expr2 = self.process(expr2)
1355
return as_ternary(oper, expr1, expr2)
1356
1357
# relational expression
1358
if self.language is Language.Fortran:
1359
m = re.match(
1360
r'\A(.+)\s*[.](eq|ne|lt|le|gt|ge)[.]\s*(.+)\Z', r, re.I)
1361
else:
1362
m = re.match(
1363
r'\A(.+)\s*([=][=]|[!][=]|[<][=]|[<]|[>][=]|[>])\s*(.+)\Z', r)
1364
if m:
1365
left, rop, right = m.groups()
1366
if self.language is Language.Fortran:
1367
rop = '.' + rop + '.'
1368
left, right = self.process(restore((left, right)))
1369
rop = RelOp.fromstring(rop, language=self.language)
1370
return Expr(Op.RELATIONAL, (rop, left, right))
1371
1372
# keyword argument
1373
m = re.match(r'\A(\w[\w\d_]*)\s*[=](.*)\Z', r)
1374
if m:
1375
keyname, value = m.groups()
1376
value = restore(value)
1377
return _Pair(keyname, self.process(value))
1378
1379
# addition/subtraction operations
1380
operands = re.split(r'((?<!\d[edED])[+-])', r)
1381
if len(operands) > 1:
1382
result = self.process(restore(operands[0] or '0'))
1383
for op, operand in zip(operands[1::2], operands[2::2]):
1384
operand = self.process(restore(operand))
1385
op = op.strip()
1386
if op == '+':
1387
result += operand
1388
else:
1389
assert op == '-'
1390
result -= operand
1391
return result
1392
1393
# string concatenate operation
1394
if self.language is Language.Fortran and '//' in r:
1395
operands = restore(r.split('//'))
1396
return Expr(Op.CONCAT,
1397
tuple(self.process(operands)))
1398
1399
# multiplication/division operations
1400
operands = re.split(r'(?<=[@\w\d_])\s*([*]|/)',
1401
(r if self.language is Language.C
1402
else r.replace('**', '@__f2py_DOUBLE_STAR@')))
1403
if len(operands) > 1:
1404
operands = restore(operands)
1405
if self.language is not Language.C:
1406
operands = [operand.replace('@__f2py_DOUBLE_STAR@', '**')
1407
for operand in operands]
1408
# Expression is an arithmetic product
1409
result = self.process(operands[0])
1410
for op, operand in zip(operands[1::2], operands[2::2]):
1411
operand = self.process(operand)
1412
op = op.strip()
1413
if op == '*':
1414
result *= operand
1415
else:
1416
assert op == '/'
1417
result /= operand
1418
return result
1419
1420
# referencing/dereferencing
1421
if r.startswith('*') or r.startswith('&'):
1422
op = {'*': Op.DEREF, '&': Op.REF}[r[0]]
1423
operand = self.process(restore(r[1:]))
1424
return Expr(op, operand)
1425
1426
# exponentiation operations
1427
if self.language is not Language.C and '**' in r:
1428
operands = list(reversed(restore(r.split('**'))))
1429
result = self.process(operands[0])
1430
for operand in operands[1:]:
1431
operand = self.process(operand)
1432
result = operand ** result
1433
return result
1434
1435
# int-literal-constant
1436
m = re.match(r'\A({digit_string})({kind}|)\Z'.format(
1437
digit_string=r'\d+',
1438
kind=r'_(\d+|\w[\w\d_]*)'), r)
1439
if m:
1440
value, _, kind = m.groups()
1441
if kind and kind.isdigit():
1442
kind = int(kind)
1443
return as_integer(int(value), kind or 4)
1444
1445
# real-literal-constant
1446
m = re.match(r'\A({significant}({exponent}|)|\d+{exponent})({kind}|)\Z'
1447
.format(
1448
significant=r'[.]\d+|\d+[.]\d*',
1449
exponent=r'[edED][+-]?\d+',
1450
kind=r'_(\d+|\w[\w\d_]*)'), r)
1451
if m:
1452
value, _, _, kind = m.groups()
1453
if kind and kind.isdigit():
1454
kind = int(kind)
1455
value = value.lower()
1456
if 'd' in value:
1457
return as_real(float(value.replace('d', 'e')), kind or 8)
1458
return as_real(float(value), kind or 4)
1459
1460
# string-literal-constant with kind parameter specification
1461
if r in self.quotes_map:
1462
kind = r[:r.find('@')]
1463
return as_string(self.quotes_map[r], kind or 1)
1464
1465
# array constructor or literal complex constant or
1466
# parenthesized expression
1467
if r in raw_symbols_map:
1468
paren = _get_parenthesis_kind(r)
1469
items = self.process(restore(raw_symbols_map[r]),
1470
'expr' if paren == 'ROUND' else 'args')
1471
if paren == 'ROUND':
1472
if isinstance(items, Expr):
1473
return items
1474
if paren in ['ROUNDDIV', 'SQUARE']:
1475
# Expression is a array constructor
1476
if isinstance(items, Expr):
1477
items = (items,)
1478
return as_array(items)
1479
1480
# function call/indexing
1481
m = re.match(r'\A(.+)\s*(@__f2py_PARENTHESIS_(ROUND|SQUARE)_\d+@)\Z',
1482
r)
1483
if m:
1484
target, args, paren = m.groups()
1485
target = self.process(restore(target))
1486
args = self.process(restore(args)[1:-1], 'args')
1487
if not isinstance(args, tuple):
1488
args = args,
1489
if paren == 'ROUND':
1490
kwargs = dict((a.left, a.right) for a in args
1491
if isinstance(a, _Pair))
1492
args = tuple(a for a in args if not isinstance(a, _Pair))
1493
# Warning: this could also be Fortran indexing operation..
1494
return as_apply(target, *args, **kwargs)
1495
else:
1496
# Expression is a C/Python indexing operation
1497
# (e.g. used in .pyf files)
1498
assert paren == 'SQUARE'
1499
return target[args]
1500
1501
# Fortran standard conforming identifier
1502
m = re.match(r'\A\w[\w\d_]*\Z', r)
1503
if m:
1504
return as_symbol(r)
1505
1506
# fall-back to symbol
1507
r = self.finalize_string(restore(r))
1508
ewarn(
1509
f'fromstring: treating {r!r} as symbol (original={self.original})')
1510
return as_symbol(r)
1511
1512