def visit_BinOp(self, node): self.generic_visit(node) if isinstance(node.op, BitAnd): return BoolOp(And(), (node.left, node.right)) elif isinstance(node.op, BitOr): return BoolOp(Or(), (node.left, node.right)) else: raise TypeError("unsupported operation '%s'" % node.op.__class__.__name__)
def insert_with_block_check(self, node): """Modifies a with statement node in-place to add an initial check for whether or not the block should be executed. If the block is not executed it will raise a XonshBlockError containing the required information. """ nwith = self._nwith # the nesting level of the current with-statement lineno = get_lineno(node) col = get_col(node, 0) # Add or discover target names targets = set() i = 0 # index of unassigned items def make_next_target(): nonlocal i targ = '__xonsh_with_target_{}_{}__'.format(nwith, i) n = Name(id=targ, ctx=Store(), lineno=lineno, col_offset=col) targets.add(targ) i += 1 return n for item in node.items: if item.optional_vars is None: if has_elts(item.context_expr): targs = [make_next_target() for _ in item.context_expr.elts] optvars = Tuple(elts=targs, ctx=Store(), lineno=lineno, col_offset=col) else: optvars = make_next_target() item.optional_vars = optvars else: targets.update(gather_names(item.optional_vars)) # Ok, now that targets have been found / created, make the actual check # to see if we are in a non-executing block. This is equivalent to # writing the following condition: # # if getattr(targ0, '__xonsh_block__', False) or \ # getattr(targ1, '__xonsh_block__', False) or ...: # raise XonshBlockError(lines, globals(), locals()) tests = [_getblockattr(t, lineno, col) for t in sorted(targets)] if len(tests) == 1: test = tests[0] else: test = BoolOp(op=Or(), values=tests, lineno=lineno, col_offset=col) ldx, udx = self._find_with_block_line_idx(node) lines = [Str(s=s, lineno=lineno, col_offset=col) for s in self.lines[ldx:udx]] check = If(test=test, body=[ Raise(exc=xonsh_call('XonshBlockError', args=[List(elts=lines, ctx=Load(), lineno=lineno, col_offset=col), xonsh_call('globals', args=[], lineno=lineno, col=col), xonsh_call('locals', args=[], lineno=lineno, col=col)], lineno=lineno, col=col), cause=None, lineno=lineno, col_offset=col)], orelse=[], lineno=lineno, col_offset=col) node.body.insert(0, check)
def compBreaker(self, node): assert isinstance(node, Compare) if len(node.comparators) == 1: return node else: comp1 = Compare(node.left, node.ops[0:1], node.comparators[0:1]) comp2 = Compare(node.comparators[0], node.ops[1:], node.comparators[1:]) newNode = BoolOp(And(), [comp1, self.compBreaker(comp2)]) return newNode
def _get_all_names( self, node: ast.BoolOp, ) -> List[str]: # That's an ugly hack to make sure that we do not visit # one chained `BoolOp` elements twice. Sorry! node._wps_visited = True # type: ignore # noqa: Z441 names = [] for operand in node.values: if isinstance(operand, ast.BoolOp): names.extend(self._get_all_names(operand)) else: names.append(astor.to_source(operand)) return names
def visit_BoolOp(self, bool_op: ast.BoolOp) -> Optional[IType]: """ Verifies if the types of the operands are valid to the boolean operations If the operations are valid, changes de Python operator by the Boa operator in the syntax tree :param bool_op: the python ast boolean operation node :return: the type of the result of the operation if the operation is valid. Otherwise, returns None :rtype: IType or None """ lineno: int = bool_op.lineno col_offset: int = bool_op.col_offset try: return_type: IType = None bool_operation: IOperation = None operator: Operator = self.get_operator(bool_op.op) if not isinstance(operator, Operator): # the operator is invalid or it was not implemented yet self._log_error( CompilerError.UnresolvedReference(lineno, col_offset, type(operator).__name__) ) l_operand = self.visit(bool_op.values[0]) for index, operand in enumerate(bool_op.values[1:]): r_operand = self.visit(operand) operation: IOperation = self.get_bin_op(operator, r_operand, l_operand) if operation is None: self._log_error( CompilerError.NotSupportedOperation(lineno, col_offset, operator) ) elif bool_operation is None: return_type = operation.result bool_operation = operation lineno = operand.lineno col_offset = operand.col_offset l_operand = r_operand bool_op.op = bool_operation return return_type except CompilerError.MismatchedTypes as raised_error: raised_error.line = lineno raised_error.col = col_offset # raises the exception with the line/col info self._log_error(raised_error)
def _union(self, node): values = [] generated = [] for union in node: for what, sub in union.items(): if ':' in what: if what in generated: # only generate any imported function once continue generated.append(what) name = what yield self._type(what, name, sub) else: # this is a build_in type (and my have been refined) # therefore generate one function per type name = self._unique(what) yield self._function(name, self._type(what, what, sub)) values += [ UnaryOp( op=Not(), operand=Call( func=Name(id=self._python_name(name), ctx=Load()), args=[Name(id='value', ctx=Load())], keywords=[], ), ), ] yield [ If( test=BoolOp( op=And(), values=values, ), body=[ Return(value=Constant(value=False, kind=None), ), ], orelse=[], ), ]
def transform(tree): # Ignore the "lambda e: ...", and descend into the ..., in: # - let[] or letrec[] in tail position. # - letseq[] is a nested sequence of lets, so covers that too. # - do[] in tail position. # - May be generated also by a "with multilambda" block # that has already expanded. if islet(tree): view = ExpandedLetView(tree) assert view.body, "BUG: what's this, a decorator inside a lambda?" thelambda = view.body # lambda e: ... thelambda.body = transform(thelambda.body) elif isdo(tree): thebody = ExpandedDoView(tree).body # list of do-items lastitem = thebody[-1] # lambda e: ... thelambda = lastitem thelambda.body = transform(thelambda.body) elif type(tree) is Call: # Apply TCO to tail calls. # - If already an explicit jump() or loop(), leave it alone. # - If a call to an ec, leave it alone. # - Because an ec call may appear anywhere, a tail-position # analysis will not find all of them. # - This function analyzes only tail positions within # a return-value expression. # - Hence, transform_return() calls us on the content of # all ec nodes directly. ec(...) is like return; the # argument is the retexpr. if not (isx(tree.func, _isjump) or isec(tree, known_ecs)): tree.args = [tree.func] + tree.args tree.func = hq[jump] tree = transform_call(tree) elif type(tree) is IfExp: # Only either body or orelse runs, so both of them are in tail position. # test is not in tail position. tree.body = transform(tree.body) tree.orelse = transform(tree.orelse) elif type(tree) is BoolOp: # and, or # and/or is a combined test-and-return. Any number of these may be nested. # Because it is in general impossible to know beforehand how many # items will be actually evaluated, we define only the last item # (in the whole expression) to be in tail position. if type(tree.values[-1]) in (Call, IfExp, BoolOp): # must match above handlers # other items: not in tail position, compute normally if len(tree.values) > 2: op_of_others = BoolOp(op=tree.op, values=tree.values[:-1], lineno=tree.lineno, col_offset=tree.col_offset) else: op_of_others = tree.values[0] if type(tree.op) is Or: # or(data1, ..., datan, tail) --> it if any(others) else tail tree = aif( Tuple(elts=[ op_of_others, transform_data( Name(id="it", lineno=tree.lineno, col_offset=tree.col_offset)), transform(tree.values[-1]) ], lineno=tree.lineno, col_offset=tree.col_offset)) # tail-call item elif type(tree.op) is And: # and(data1, ..., datan, tail) --> tail if all(others) else False fal = q[False] fal = copy_location(fal, tree) tree = IfExp(test=op_of_others, body=transform(tree.values[-1]), orelse=transform_data(fal)) else: # cannot happen assert False, "unknown BoolOp type {}".format( tree.op) # pragma: no cover else: # optimization: BoolOp, no call or compound in tail position --> treat as single data item tree = transform_data(tree) else: tree = transform_data(tree) return tree
def handle_f(self, f): # Do we need to use .attname here? # What about transforms/lookups? f.contains_aggregate = False if "__" in f.name: # We need to chain a bunch of attr lookups, returning None # if any of them give us a None, we should be returning a None. # # . while x is not None and parts: # . x = getattr(x, parts.pop(0), None) # return x return [ Assign(targets=[Name(id="source", **self.file_store)], value=Name(id="self", **self.file), **self.file), Assign( targets=[Name(id="parts", **self.file_store)], value=Call( func=Attribute(value=Constant(value=f.name, **self.file), attr="split", **self.file), args=[Constant(value="__", **self.file)], keywords=[], kwonlyargs=[], **self.file, ), **self.file, ), While( test=BoolOp( op=And(), values=[ Compare( left=Name(id="source", **self.file), ops=[IsNot()], comparators=[ Constant(value=None, **self.file) ], **self.file, ), Name(id="parts", **self.file), ], **self.file, ), body=[ Assign( targets=[Name(id="source", **self.file_store)], value=Call( func=Name(id="getattr", **self.file), args=[ Name(id="source", **self.file), Call( func=Attribute(value=Name(id="parts", **self.file), attr="pop", **self.file), args=[Constant(value=0, **self.file)], keywords=[], kwonlyargs=[], **self.file, ), Constant(value=None, **self.file), ], keywords=[], kwonlyargs=[], **self.file, ), **self.file, ), ], orelse=[], **self.file, ), Return(value=Name(id="source", **self.file), **self.file), ] return Attribute(value=Name(id="self", **self.file), attr=f.name, **self.file)