1515#
1616# TODO: support logical constants (Op.BOOLEAN)
1717# TODO: support logical operators (.AND., ...)
18- # TODO: support relational operators (<, >, ..., .LT., ...)
1918# TODO: support defined operators (.MYOP., ...)
2019#
2120__all__ = ['Expr' ]
@@ -50,12 +49,43 @@ class Op(Enum):
5049 APPLY = 200
5150 INDEXING = 210
5251 CONCAT = 220
52+ RELATIONAL = 300
5353 TERMS = 1000
5454 FACTORS = 2000
5555 REF = 3000
5656 DEREF = 3001
5757
5858
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+
5989class ArithOp (Enum ):
6090 """
6191 Used in Op.APPLY expression to specify the function part.
@@ -77,12 +107,19 @@ class Precedence(Enum):
77107 """
78108 Used as Expr.tostring precedence argument.
79109 """
80- NONE = 0
81- TUPLE = 1
82- SUM = 2
110+ ATOM = 0
111+ POWER = 1
112+ UNARY = 2
83113 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
86123
87124
88125integer_types = (int ,)
@@ -178,6 +215,9 @@ def __init__(self, op, data):
178215 elif op in (Op .REF , Op .DEREF ):
179216 # data is Expr instance
180217 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
181221 else :
182222 raise NotImplementedError (
183223 f'unknown op or missing sanity check: { op } ' )
@@ -341,19 +381,32 @@ def tostring(self, parent_precedence=Precedence.NONE,
341381 language = language )
342382 for a in self .data ]
343383 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
350393 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
352396 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 } '
354407 else :
355408 raise NotImplementedError (f'tostring for op { self .op } ' )
356- if parent_precedence .value > precedence .value :
409+ if parent_precedence .value < precedence .value :
357410 # If parent precedence is higher than operand precedence,
358411 # operand will be enclosed in parenthesis.
359412 return '(' + r + ')'
@@ -590,7 +643,11 @@ def substitute(self, symbols_map):
590643 return normalize (Expr (self .op , operands ))
591644 if self .op in (Op .REF , Op .DEREF ):
592645 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 )))
594651 raise NotImplementedError (f'substitute method for { self .op } : { self !r} ' )
595652
596653 def traverse (self , visit , * args , ** kwargs ):
@@ -642,6 +699,11 @@ def traverse(self, visit, *args, **kwargs):
642699 elif self .op in (Op .REF , Op .DEREF ):
643700 return normalize (Expr (self .op ,
644701 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 )))
645707 raise NotImplementedError (f'traverse method for { self .op } ' )
646708
647709 def contains (self , other ):
@@ -963,6 +1025,30 @@ def as_deref(expr):
9631025 return Expr (Op .DEREF , expr )
9641026
9651027
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+
9661052def as_terms (obj ):
9671053 """Return expression as TERMS expression.
9681054 """
@@ -1257,23 +1343,32 @@ def restore(r):
12571343 return as_complex (* self .process (operands ))
12581344 raise NotImplementedError (
12591345 f'parsing comma-separated list (context={ context } ): { r } ' )
1260- return tuple (self .process (restore (r .split (',' )), context ))
12611346
12621347 # ternary operation
12631348 m = re .match (r'\A([^?]+)[?]([^:]+)[:](.+)\Z' , r )
12641349 if m :
12651350 assert context == 'expr' , context
12661351 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 )
12731353 expr1 = self .process (expr1 )
12741354 expr2 = self .process (expr2 )
12751355 return as_ternary (oper , expr1 , expr2 )
12761356
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+
12771372 # keyword argument
12781373 m = re .match (r'\A(\w[\w\d_]*)\s*[=](.*)\Z' , r )
12791374 if m :
0 commit comments