def compile_BinOp( expr: qlast.Base, *, ctx: context.ContextLevel) -> irast.Set: try_folding = True if isinstance(expr.op, ast.ops.TypeCheckOperator): op_node = compile_type_check_op(expr, ctx=ctx) elif isinstance(expr.op, qlast.SetOperator): op_node = compile_set_op(expr, ctx=ctx) try_folding = False elif isinstance(expr.op, qlast.EquivalenceOperator): op_node = compile_equivalence_op(expr, ctx=ctx) elif isinstance(expr.op, ast.ops.MembershipOperator): op_node = compile_membership_op(expr, ctx=ctx) try_folding = False else: left = dispatch.compile(expr.left, ctx=ctx) right = dispatch.compile(expr.right, ctx=ctx) op_node = irast.BinOp(left=left, right=right, op=expr.op) if try_folding: folded = try_fold_binop(op_node, ctx=ctx) if folded is not None: return folded return setgen.ensure_set(op_node, ctx=ctx)
def extend_irbinop(binop, *exprs, op=ast.ops.AND): exprs = list(exprs) binop = binop or exprs.pop(0) for expr in exprs: if expr is not binop: binop = irast.BinOp(left=binop, right=expr, op=op) return binop
def compile_membership_op(expr: qlast.BinOp, *, ctx: context.ContextLevel) -> irast.Base: left = dispatch.compile(expr.left, ctx=ctx) with ctx.newscope(fenced=True) as scopectx: # [NOT] IN is an aggregate, so we need to put a scope fence. right = setgen.scoped_set(dispatch.compile(expr.right, ctx=scopectx), ctx=scopectx) op_node = irast.BinOp(left=left, right=right, op=expr.op) return setgen.generated_set(op_node, ctx=ctx)
def try_fold_binop(binop: irast.BinOp, *, ctx: context.ContextLevel) -> typing.Optional[irast.Set]: """Try folding a binary operator expression.""" schema = ctx.schema real_t = schema.get('std::anyreal') result_type = irutils.infer_type(binop, schema) folded = None left = binop.left right = binop.right op = binop.op if (isinstance(left.expr, irast.Constant) and isinstance(right.expr, irast.Constant)): # Left and right nodes are constants. if isinstance(op, ast.ops.ComparisonOperator): folded = try_fold_comparison_binop(op, left, right, ctx=ctx) elif result_type.issubclass(real_t): folded = try_fold_arithmetic_binop(op, left, right, ctx=ctx) elif op in {ast.ops.ADD, ast.ops.MUL}: # Let's check if we have (CONST + (OTHER_CONST + X)) # tree, which can be optimized to ((CONST + OTHER_CONST) + X) my_const = left other_binop = right if isinstance(right.expr, irast.Constant): my_const, other_binop = other_binop, my_const if (isinstance(my_const.expr, irast.Constant) and isinstance(other_binop.expr, irast.BinOp) and other_binop.expr.op == op): other_const = other_binop.expr.left other_binop_node = other_binop.expr.right if isinstance(other_binop_node.expr, irast.Constant): other_binop_node, other_const = \ other_const, other_binop_node if isinstance(other_const.expr, irast.Constant): new_const = try_fold_arithmetic_binop(op, other_const, my_const, ctx=ctx) if new_const is not None: folded_binop = irast.BinOp(left=new_const, right=other_binop_node, op=op) folded = setgen.ensure_set(folded_binop, ctx=ctx) return folded
def compile_type_check_op(expr: qlast.BinOp, *, ctx: context.ContextLevel) -> irast.BinOp: # <Expr> IS <Type> left = dispatch.compile(expr.left, ctx=ctx) with ctx.new() as subctx: subctx.path_as_type = True right = dispatch.compile(expr.right, ctx=subctx) ltype = irutils.infer_type(left, ctx.schema) left = setgen.ptr_step_set(left, source=ltype, ptr_name=('std', '__type__'), direction=s_pointers.PointerDirection.Outbound, source_context=expr.context, ctx=ctx) pathctx.register_set_in_scope(left, ctx=ctx) right = typegen.process_type_ref_expr(right) return irast.BinOp(left=left, right=right, op=expr.op)