def call_Select(self, node: ast.Call, args: List[ast.AST]): r''' Transformation #1: seq.Select(x: f(x)).Select(y: g(y)) => Select(Select(seq, x: f(x)), y: g(y)) is turned into seq.Select(x: g(f(x))) => Select(seq, x: g(f(x))) Transformation #2: seq.SelectMany(x: f(x)).Select(y: g(y)) => Select(SelectMany(seq, x: f(x)), y: g(y)) is turned into seq.SelectMany(x: f(x).Select(y: g(y))) => SelectMany(seq, x: Select(f(x), y: g(y))) Transformation #3: seq.Where(x: f(x)).Select(y: g(y)) => Select(Where(seq, x: f(x), y: g(y)) is not altered. ''' source = args[0] transform = args[1] assert isinstance(transform, ast.Lambda) parent_select = self.visit(source) if is_call_of(parent_select, 'Select'): return self.visit_Select_of_Select(parent_select, transform) elif is_call_of(parent_select, 'SelectMany'): return self.visit_Select_of_SelectMany(parent_select, transform) else: selection = self.visit(transform) return make_Select(parent_select, selection)
def call_SelectMany(self, node: ast.Call, args: List[ast.AST]): r''' Transformation #1: seq.SelectMany(x: f(x)).SelectMany(y: f(y)) => SelectMany(SelectMany(seq, x: f(x)), y: f(y)) is turned into: seq.SelectMany(x: f(x).SelectMany(y: f(y))) => SelectMany(seq, x: SelectMany(f(x), y: f(y))) Transformation #2: seq.Select(x: f(x)).SelectMany(y: g(y)) => SelectMany(Select(seq, x: f(x)), y:g(y)) is turned into seq.SelectMany(x: g(f(x))) => SelectMany(seq, x: g(f(x))) Transformation #3: seq.Where(x: f(x)).SelectMany(y: g(y)) ''' selection = args[1] assert isinstance(selection, ast.Lambda) parent_select = self.visit(args[0]) if is_call_of(parent_select, 'SelectMany'): return self.visit_SelectMany_of_SelectMany(parent_select, selection) elif is_call_of(parent_select, 'Select'): return self.visit_SelectMany_of_Select(parent_select, selection) else: return function_call( 'SelectMany', [parent_select, self.visit(selection)])
def _is_method_call_on_first(node: ast.Call): """ Determine if this is a call like First(seq).method(args). """ if not isinstance(node.func, ast.Attribute): return False if not is_call_of(node.func.value, "First"): return False return True
def call_Where(self, node: ast.Call, args: List[ast.AST]) -> ast.AST: r''' Transformation #1: seq.Where(x: f(x)).Where(x: g(x)) => Where(Where(seq, x: f(x)), y: g(y)) is turned into seq.Where(x: f(x) and g(y)) => Where(seq, x: f(x) and g(y)) Transformation #2: seq.Select(x: f(x)).Where(y: g(y)) => Where(Select(seq, x: f(x)), y: g(y)) Is turned into: seq.Where(x: g(f(x))).Select(x: f(x)) => Select(Where(seq, x: g(f(x)), f(x)) Transformation #3: seq.SelectMany(x: f(x)).Where(y: g(y)) => Where(SelectMany(seq, x: f(x)), y: g(y)) Is turned into: seq.SelectMany(x: f(x).Where(y: g(y))) => SelectMany(seq, x: Where(f(x), g(y))) ''' source = args[0] filter = args[1] assert isinstance(filter, ast.Lambda) parent_where = self.visit(source) if is_call_of(parent_where, 'Where'): return self.visit_Where_of_Where(parent_where, filter) elif is_call_of(parent_where, 'Select'): return self.visit_Where_of_Select(parent_where, filter) elif is_call_of(parent_where, 'SelectMany'): return self.visit_Where_of_SelectMany(parent_where, filter) else: f = self.visit(filter) if lambda_is_true(f): return parent_where else: return function_call('Where', [parent_where, f])
def visit_Attribute(self, node): """ If this is a reference against a dict, then we can de-ref it if there is a key. Otherwise, we need to make sure to make a new version of the Attribute so it does not get reused' """ if is_call_of(node.value, "First"): return self.visit_Attribute_Of_First(node.value.args[0], node.attr) # type: ignore visited_value = self.visit(node.value) if isinstance(visited_value, ast.Dict): return self.visit_Subscript_Dict_with_value( visited_value, node.attr) return ast.Attribute(value=visited_value, attr=node.attr, ctx=ast.Load())
def visit_Subscript(self, node): r''' Simple Reduction (t1, t2, t3...)[1] => t2 Move [] past a First() seq.First()[0] => seq.Select(j: j[0]).First() ''' v = self.visit(node.value) s = self.visit(node.slice) if type(v) is ast.Tuple: return self.visit_Subscript_Tuple(v, s) if type(v) is ast.List: return self.visit_Subscript_List(v, s) if is_call_of(v, 'First'): return self.visit_Subscript_Of_First(v.args[0], s) # Nothing interesting, so do the normal thing several levels down. return ast.Subscript(v, s, ast.Load())
def test_is_call_not_a_call(): start = _parse_ast("dude1") assert not is_call_of(start, "dude1")
def test_is_call_to_expected_method(): start = _parse_ast("a.dude(10)") assert not is_call_of(start, "dude")
def test_is_call_to_unexpected_function(): start = _parse_ast("dude(10)") assert not is_call_of(start, "dude1")
def test_is_call_to_expected_function(): start = _parse_ast('dude(10)') assert is_call_of(start, 'dude')
def test_is_call_not_a_call(): start = _parse_ast('dude1') assert not is_call_of(start, 'dude1')
def test_is_call_to_expected_method(): start = _parse_ast('a.dude(10)') assert not is_call_of(start, 'dude')