Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,4 @@ type_check/lineprecision.txt

# PyCharm Stuff
.idea/*
demo/issue_41.py
7 changes: 2 additions & 5 deletions src/celpy/celparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,11 +266,8 @@ def member_dot_arg(self, tree: lark.Tree) -> None:
else:
exprlist = ""
right = cast(lark.Token, tree.children[1]).value
if self.stack:
left = self.stack.pop()
self.stack.append(f"{left}.{right}({exprlist})")
else:
self.stack.append(f".{right}({exprlist})")
left = self.stack.pop()
self.stack.append(f"{left}.{right}({exprlist})")

def member_index(self, tree: lark.Tree) -> None:
right = self.stack.pop()
Expand Down
124 changes: 62 additions & 62 deletions src/celpy/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2030,71 +2030,71 @@ def member_dot_arg(self, tree: lark.Tree) -> Result:
Tuple[lark.Tree, lark.Token], tree.children[:2]
)

if method_name_token.value == "map":
if method_name_token.value in {"map", "filter", "all", "exists", "exists_one", "reduce", "min"}:
member_list = cast(celpy.celtypes.ListType, self.visit(member_tree))
sub_expr = self.build_macro_eval(tree)
mapping = cast(Iterable[celpy.celtypes.Value], map(sub_expr, member_list))
result = celpy.celtypes.ListType(mapping)
return result

elif method_name_token.value == "filter":
member_list = cast(celpy.celtypes.ListType, self.visit(member_tree))
sub_expr = self.build_macro_eval(tree)
result = celpy.celtypes.ListType(filter(sub_expr, member_list))
return result

elif method_name_token.value == "all":
member_list = cast(celpy.celtypes.ListType, self.visit(member_tree))
and_oper = cast(
CELBoolFunction,
eval_error("no such overload", TypeError)(celpy.celtypes.logical_and),
)
sub_expr = self.build_ss_macro_eval(tree)
reduction = reduce(
and_oper, map(sub_expr, member_list), celpy.celtypes.BoolType(True)
)
return reduction

elif method_name_token.value == "exists":
member_list = cast(celpy.celtypes.ListType, self.visit(member_tree))
or_oper = cast(
CELBoolFunction,
eval_error("no such overload", TypeError)(celpy.celtypes.logical_or),
)
sub_expr = self.build_ss_macro_eval(tree)
reduction = reduce(
or_oper, map(sub_expr, member_list), celpy.celtypes.BoolType(False)
)
return reduction
if isinstance(member_list, CELEvalError):
return member_list

if method_name_token.value == "map":
sub_expr = self.build_macro_eval(tree)
mapping = cast(Iterable[celpy.celtypes.Value], map(sub_expr, member_list))
result = celpy.celtypes.ListType(mapping)
return result

elif method_name_token.value == "filter":
sub_expr = self.build_macro_eval(tree)
result = celpy.celtypes.ListType(filter(sub_expr, member_list))
return result

elif method_name_token.value == "all":
sub_expr = self.build_ss_macro_eval(tree)
and_oper = cast(
CELBoolFunction,
eval_error("no such overload", TypeError)(
celpy.celtypes.logical_and)
)
reduction = reduce(and_oper, map(sub_expr, member_list), celpy.celtypes.BoolType(True))
return reduction

elif method_name_token.value == "exists":
sub_expr = self.build_ss_macro_eval(tree)
or_oper = cast(
CELBoolFunction,
eval_error("no such overload", TypeError)(
celpy.celtypes.logical_or)
)
reduction = reduce(or_oper, map(sub_expr, member_list), celpy.celtypes.BoolType(False))
return reduction

elif method_name_token.value == "exists_one":
# Is there exactly 1?
sub_expr = self.build_macro_eval(tree)
count = sum(1 for value in member_list if bool(sub_expr(value)))
return celpy.celtypes.BoolType(count == 1)

elif method_name_token.value == "reduce":
# Apply a function to reduce the list to a single value.
# The `tree` is a `member_dot_arg` construct with (member, method_name, args)
# The args have two variables and two expressions.
reduce_expr, init_expr_tree = self.build_reduce_macro_eval(tree)
initial_value = self.visit(init_expr_tree)
reduction = reduce(reduce_expr, member_list, initial_value)
return reduction

elif method_name_token.value == "min":
# Special case of "reduce()"
# with <member>.min() -> <member>.reduce(r, i, int_max, r < i ? r : i)
try:
# Note. The Result type includes None, which will raise an exception.
reduction = min(member_list) # type: ignore [type-var]
except ValueError as ex:
err = "Attempt to reduce an empty sequence or a sequence with a None value"
reduction = CELEvalError(err, ex.__class__, ex.args, tree=tree)
return reduction

elif method_name_token.value == "exists_one":
# Is there exactly 1?
member_list = cast(celpy.celtypes.ListType, self.visit(member_tree))
sub_expr = self.build_macro_eval(tree)
count = sum(1 for value in member_list if bool(sub_expr(value)))
return celpy.celtypes.BoolType(count == 1)

elif method_name_token.value == "reduce":
# Apply a function to reduce the list to a single value.
# The `tree` is a `member_dot_arg` construct with (member, method_name, args)
# The args have two variables and two expressions.
member_list = cast(celpy.celtypes.ListType, self.visit(member_tree))
reduce_expr, init_expr_tree = self.build_reduce_macro_eval(tree)
initial_value = self.visit(init_expr_tree)
reduction = reduce(reduce_expr, member_list, initial_value)
return reduction

elif method_name_token.value == "min":
# Special case of "reduce()"
# with <member>.min() -> <member>.reduce(r, i, int_max, r < i ? r : i)
member_list = cast(celpy.celtypes.ListType, self.visit(member_tree))
try:
# Note. The Result type includes None, which will raise an exception.
reduction = min(member_list) # type: ignore [type-var]
except ValueError as ex:
err = "Attempt to reduce an empty sequence or a sequence with a None value"
reduction = CELEvalError(err, ex.__class__, ex.args, tree=tree)
return reduction
else:
raise RuntimeError("Internal Design Error") # pragma: no cover

else:
# Not a macro: a method evaluation.
Expand Down
9 changes: 9 additions & 0 deletions tests/test_evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1631,6 +1631,15 @@ def test_member_dot_arg_all(monkeypatch):
)


def test_member_dot_arg_all_issue_41(monkeypatch):
"""The filter macro CelEvalError().all(x, x) == CelEvalError()"""
tree = macro_member_tree("all")
the_error = CELEvalError()
eval_0 = Evaluator(tree, activation=Mock(resolve_variable=Mock(return_value=the_error)))
assert eval_0.member_dot_arg(tree.children[0]) is the_error



def test_member_dot_arg_exists(monkeypatch):
"""The filter macro [true, false].exists(x, x) == [true]"""
visit = Mock(
Expand Down
1 change: 1 addition & 0 deletions tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,3 +448,4 @@ def test_dump_issue_35():
def test_tree_dump(parser):
ast = parser.parse("-(3*4+5-1/2%3==1)?name[index]:f(1,2)||false&&true")
assert tree_dump(ast) == '- (3 * 4 + 5 - 1 / 2 % 3 == 1) ? name[index] : f(1, 2) || false && true'

16 changes: 8 additions & 8 deletions type_check/lineprecision.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
Name Lines Precise Imprecise Any Empty Unanalyzed
-------------------------------------------------------------------
celpy 306 76 0 4 226 0
celpy.__main__ 508 204 8 42 254 0
celpy.adapter 141 34 3 9 89 6
celpy.c7nlib 1663 351 14 149 1149 0
celpy.celparser 389 199 3 23 164 0
celpy.celtypes 1504 417 14 224 812 37
celpy.evaluation 2578 866 38 176 1481 17
celpy 293 76 0 4 213 0
celpy.__main__ 465 172 7 42 244 0
celpy.adapter 137 35 3 9 85 5
celpy.c7nlib 1584 340 15 154 1075 0
celpy.celparser 401 207 2 22 170 0
celpy.celtypes 1503 438 14 221 791 39
celpy.evaluation 2472 835 33 177 1411 16
xlate 0 0 0 0 0 0
xlate.c7n_to_cel 1755 385 103 146 1115 6
xlate.c7n_to_cel 1730 377 102 145 1101 5
Loading