15
15
#
16
16
# TODO: support logical constants (Op.BOOLEAN)
17
17
# TODO: support logical operators (.AND., ...)
18
- # TODO: support relational operators (<, >, ..., .LT., ...)
19
18
# TODO: support defined operators (.MYOP., ...)
20
19
#
21
20
__all__ = ['Expr' ]
@@ -50,12 +49,43 @@ class Op(Enum):
50
49
APPLY = 200
51
50
INDEXING = 210
52
51
CONCAT = 220
52
+ RELATIONAL = 300
53
53
TERMS = 1000
54
54
FACTORS = 2000
55
55
REF = 3000
56
56
DEREF = 3001
57
57
58
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
+
59
89
class ArithOp (Enum ):
60
90
"""
61
91
Used in Op.APPLY expression to specify the function part.
@@ -77,12 +107,19 @@ class Precedence(Enum):
77
107
"""
78
108
Used as Expr.tostring precedence argument.
79
109
"""
80
- NONE = 0
81
- TUPLE = 1
82
- SUM = 2
110
+ ATOM = 0
111
+ POWER = 1
112
+ UNARY = 2
83
113
PRODUCT = 3
84
- POWER = 4
85
- ATOM = 5
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
86
123
87
124
88
125
integer_types = (int ,)
@@ -178,6 +215,9 @@ def __init__(self, op, data):
178
215
elif op in (Op .REF , Op .DEREF ):
179
216
# data is Expr instance
180
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
181
221
else :
182
222
raise NotImplementedError (
183
223
f'unknown op or missing sanity check: { op } ' )
@@ -341,19 +381,32 @@ def tostring(self, parent_precedence=Precedence.NONE,
341
381
language = language )
342
382
for a in self .data ]
343
383
if language is Language .C :
344
- return f'({ cond } ? { expr1 } : { expr2 } )'
345
- if language is Language .Python :
346
- return f'({ expr1 } if { cond } else { expr2 } )'
347
- if language is Language .Fortran :
348
- return f'merge({ expr1 } , { expr2 } , { cond } )'
349
- raise NotImplementedError (f'tostring for { self .op } and { language } ' )
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
350
393
elif self .op is Op .REF :
351
- return '&' + self .data .tostring (language = language )
394
+ r = '&' + self .data .tostring (Precedence .UNARY , language = language )
395
+ precedence = Precedence .UNARY
352
396
elif self .op is Op .DEREF :
353
- return '*' + self .data .tostring (language = language )
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 } '
354
407
else :
355
408
raise NotImplementedError (f'tostring for op { self .op } ' )
356
- if parent_precedence .value > precedence .value :
409
+ if parent_precedence .value < precedence .value :
357
410
# If parent precedence is higher than operand precedence,
358
411
# operand will be enclosed in parenthesis.
359
412
return '(' + r + ')'
@@ -590,7 +643,11 @@ def substitute(self, symbols_map):
590
643
return normalize (Expr (self .op , operands ))
591
644
if self .op in (Op .REF , Op .DEREF ):
592
645
return normalize (Expr (self .op , self .data .substitute (symbols_map )))
593
-
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 )))
594
651
raise NotImplementedError (f'substitute method for { self .op } : { self !r} ' )
595
652
596
653
def traverse (self , visit , * args , ** kwargs ):
@@ -642,6 +699,11 @@ def traverse(self, visit, *args, **kwargs):
642
699
elif self .op in (Op .REF , Op .DEREF ):
643
700
return normalize (Expr (self .op ,
644
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 )))
645
707
raise NotImplementedError (f'traverse method for { self .op } ' )
646
708
647
709
def contains (self , other ):
@@ -963,6 +1025,30 @@ def as_deref(expr):
963
1025
return Expr (Op .DEREF , expr )
964
1026
965
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
+
966
1052
def as_terms (obj ):
967
1053
"""Return expression as TERMS expression.
968
1054
"""
@@ -1257,23 +1343,32 @@ def restore(r):
1257
1343
return as_complex (* self .process (operands ))
1258
1344
raise NotImplementedError (
1259
1345
f'parsing comma-separated list (context={ context } ): { r } ' )
1260
- return tuple (self .process (restore (r .split (',' )), context ))
1261
1346
1262
1347
# ternary operation
1263
1348
m = re .match (r'\A([^?]+)[?]([^:]+)[:](.+)\Z' , r )
1264
1349
if m :
1265
1350
assert context == 'expr' , context
1266
1351
oper , expr1 , expr2 = restore (m .groups ())
1267
- if 0 :
1268
- # TODO: enable this when support for boolean
1269
- # expressions is fully implemented
1270
- oper = self .process (oper )
1271
- else :
1272
- oper = as_symbol (self .finalize_string (oper ))
1352
+ oper = self .process (oper )
1273
1353
expr1 = self .process (expr1 )
1274
1354
expr2 = self .process (expr2 )
1275
1355
return as_ternary (oper , expr1 , expr2 )
1276
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
+
1277
1372
# keyword argument
1278
1373
m = re .match (r'\A(\w[\w\d_]*)\s*[=](.*)\Z' , r )
1279
1374
if m :
0 commit comments