Skip to content

Commit bcb0c25

Browse files
committed
improve treatment of outer parenthesis
1 parent 89d06c4 commit bcb0c25

File tree

1 file changed

+57
-30
lines changed

1 file changed

+57
-30
lines changed

data_algebra/expr_rep.py

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,24 @@
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

153176
def _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

Comments
 (0)