1414# http://tomerfiliba.com/blog/Infix-Operators/
1515
1616
17+ class PythonText :
18+ """
19+ Class for holding text representation of Python, with possible additional annotations.
20+ str() method returns only the text for interoperability.
21+ """
22+ def __init__ (self , s : str , * , is_in_parens : bool = False ):
23+ assert isinstance (s , str )
24+ assert isinstance (is_in_parens , bool )
25+ self .s = s
26+ self .is_in_parens = is_in_parens
27+
28+ def __str__ (self ):
29+ return self .s
30+
31+ def __repr__ (self ):
32+ return self .s .__repr__ ()
33+
34+
1735# list of window/aggregation functions that must be windowed/aggregated
1836# (note some other functions work in more than one mode)
1937# noinspection SpellCheckingInspection
@@ -130,7 +148,12 @@ def evaluate(self, data_frame):
130148
131149 # emitters
132150
133- def to_python (self , * , want_inline_parens = False ):
151+ def to_python (self , * , want_inline_parens : bool = False ) -> PythonText :
152+ """
153+ Convert parsed expression into a string
154+
155+ :param want_inline_parens: bool if True put parens around complex expressions that don't already have a grouper.
156+ """
134157 raise NotImplementedError (
135158 "base class method called"
136159 ) # https://docs.python.org/3/library/exceptions.html
@@ -144,10 +167,10 @@ def to_source(self, *, want_inline_parens=False, dialect="Python"):
144167 # printing
145168
146169 def __repr__ (self ):
147- return self .to_python (want_inline_parens = False )
170+ return str ( self .to_python (want_inline_parens = False ) )
148171
149172 def __str__ (self ):
150- return self .to_python (want_inline_parens = False )
173+ return str ( self .to_python (want_inline_parens = False ) )
151174
152175
153176def _is_none_value (x ):
@@ -696,8 +719,8 @@ def replace_view(self, view):
696719 def evaluate (self , data_frame ):
697720 return self .value
698721
699- def to_python (self , * , want_inline_parens = False ):
700- return self .value .__repr__ ()
722+ def to_python (self , * , want_inline_parens : bool = False ) -> PythonText :
723+ return PythonText ( self .value .__repr__ (), is_in_parens = False )
701724
702725 # don't collect -5 as a complex expression
703726 def __neg__ (self ):
@@ -752,14 +775,14 @@ def evaluate(self, data_frame):
752775 res [i ] = vi
753776 return res
754777
755- def to_python (self , * , want_inline_parens = False ):
778+ def to_python (self , * , want_inline_parens : bool = False ) -> PythonText :
756779 def li_to_python (value ):
757780 try :
758- return value .to_python (want_inline_parens = want_inline_parens )
781+ return str ( value .to_python (want_inline_parens = False ) )
759782 except AttributeError :
760783 return str (value ) # TODO: check if this should be repr?
761784
762- return "[" + ", " .join ([li_to_python (ai ) for ai in self .value ]) + "]"
785+ return PythonText ( "[" + ", " .join ([li_to_python (ai ) for ai in self .value ]) + "]" , is_in_parens = False )
763786
764787 def get_column_names (self , columns_seen ):
765788 for ti in self .value :
@@ -814,8 +837,8 @@ def get_views(self):
814837 def replace_view (self , view ):
815838 return ColumnReference (view = view , column_name = self .column_name )
816839
817- def to_python (self , want_inline_parens = False ):
818- return self .column_name
840+ def to_python (self , * , want_inline_parens : bool = False ) -> PythonText :
841+ return PythonText ( self .column_name , is_in_parens = False )
819842
820843 def get_column_names (self , columns_seen ):
821844 columns_seen .add (self .column_name )
@@ -964,32 +987,36 @@ def evaluate(self, data_frame):
964987 pass
965988 raise KeyError (f"function { self .op } not found" )
966989
967- def to_python (self , * , want_inline_parens = False ):
968- subs = [ai .to_python (want_inline_parens = True ) for ai in self .args ]
969- if len (subs ) <= 0 :
970- return self .op + "()"
971- if len (subs ) == 1 :
990+ def to_python (self , * , want_inline_parens : bool = False ) -> PythonText :
991+ n_args = len (self .args )
992+ if n_args <= 0 :
993+ return PythonText (self .op + "()" , is_in_parens = False )
994+ if n_args == 1 :
995+ sub_0 = self .args [0 ].to_python (want_inline_parens = False )
972996 if self .inline :
973- return self .op + self .args [0 ].to_python (want_inline_parens = True )
997+ if sub_0 .is_in_parens :
998+ return PythonText (self .op + str (sub_0 ), is_in_parens = False )
999+ return PythonText (self .op + '(' + str (sub_0 ) + ')' , is_in_parens = False )
9741000 if self .method :
975- if isinstance (self .args [0 ], ColumnReference ):
976- return subs [0 ] + "." + self .op + "()"
977- else :
978- return "(" + subs [0 ] + ")." + self .op + "()"
1001+ if sub_0 .is_in_parens or isinstance (self .args [0 ], ColumnReference ):
1002+ return PythonText (str (sub_0 ) + "." + self .op + "()" , is_in_parens = False )
1003+ return PythonText ("(" + str (sub_0 ) + ")." + self .op + "()" , is_in_parens = False )
9791004 if self .inline :
980- result = ""
981- if want_inline_parens :
982- result = result + "("
983- result = result + (" " + self .op + " " ).join (subs )
1005+ subs = [str (ai .to_python (want_inline_parens = True )) for ai in self .args ]
1006+ result = (" " + self .op + " " ).join (subs )
9841007 if want_inline_parens :
985- result = result + ")"
986- return result
1008+ return PythonText ('(' + result + ')' , is_in_parens = True )
1009+ return PythonText (result , is_in_parens = False )
1010+ subs = [ai .to_python (want_inline_parens = False ) for ai in self .args ]
1011+ subs_0 = subs [0 ]
1012+ subs = [str (si ) for si in subs ]
9871013 if self .method :
988- if isinstance (self .args [0 ], ColumnReference ):
989- return subs [0 ] + "." + self .op + "(" + ", " .join (subs [1 :]) + ")"
1014+ if subs_0 . is_in_parens or isinstance (self .args [0 ], ColumnReference ):
1015+ return PythonText ( subs [0 ] + "." + self .op + "(" + ", " .join (subs [1 :]) + ")" , is_in_parens = False )
9901016 else :
991- return "(" + subs [0 ] + ")." + self .op + "(" + ", " .join (subs [1 :]) + ")"
992- return self .op + "(" + ", " .join (subs ) + ")"
1017+ return PythonText ("(" + subs [0 ] + ")." + self .op + "(" + ", " .join (subs [1 :]) + ")" , is_in_parens = False )
1018+ # treat as fn call
1019+ return PythonText (self .op + '(' + ", " .join (subs ) + ')' , is_in_parens = False )
9931020
9941021
9951022# define with def so function has usable __name__
0 commit comments