Beispiel #1
0
def get_params_symtable(
    params: FuncParameterList,
    schema: s_schema.Schema,
    *,
    inlined_defaults: bool,
) -> Dict[str, qlast.Expr]:

    anchors: Dict[str, qlast.Expr] = {}

    defaults_mask = qlast.TypeCast(
        expr=qlast.Parameter(name='__defaults_mask__', optional=False),
        type=qlast.TypeName(
            maintype=qlast.ObjectRef(
                module='std',
                name='bytes',
            ),
        ),
    )

    for pi, p in enumerate(params.get_in_canonical_order(schema)):
        p_shortname = p.get_parameter_name(schema)
        p_is_optional = p.get_typemod(schema) is not ft.TypeModifier.SINGLETON
        anchors[p_shortname] = qlast.TypeCast(
            expr=qlast.Parameter(
                name=p_shortname,
                optional=p_is_optional,
            ),
            type=utils.typeref_to_ast(schema, p.get_type(schema)),
        )

        p_default = p.get_default(schema)
        if p_default is None:
            continue

        if not inlined_defaults:
            continue

        anchors[p_shortname] = qlast.IfElse(
            condition=qlast.BinOp(
                left=qlast.FunctionCall(
                    func=('std', 'bytes_get_bit'),
                    args=[
                        defaults_mask,
                        qlast.IntegerConstant(value=str(pi)),
                    ]),
                op='=',
                right=qlast.IntegerConstant(value='0'),
            ),
            if_expr=anchors[p_shortname],
            else_expr=qlast._Optional(expr=p_default.qlast),
        )

    return anchors
Beispiel #2
0
    def visit_OperatorCall(self, node):
        args = node.args

        if node.operator_kind is ft.OperatorKind.INFIX:
            result = qlast.BinOp(
                left=self.visit(args[0].expr),
                right=self.visit(args[1].expr),
                op=node.func_shortname.name,
            )
        elif node.operator_kind is ft.OperatorKind.PREFIX:
            result = qlast.UnaryOp(
                operand=self.visit(args[0].expr),
                op=node.func_shortname.name,
            )
        elif node.func_shortname == 'std::IF':
            result = qlast.IfElse()
            result.condition = self.visit(args[0].expr)
            result.if_expr = self.visit(args[1].expr)
            result.else_expr = self.visit(args[2].expr)
        else:
            raise RuntimeError(
                f'unexpected operator kind: {node.operator_kind}')

        return result
def compile_func_to_ir(func, schema, *,
                       anchors=None,
                       security_context=None,
                       modaliases=None,
                       implicit_id_in_shapes=False,
                       implicit_tid_in_shapes=False):
    """Compile an EdgeQL function into EdgeDB IR."""

    if debug.flags.edgeql_compile:
        debug.header('EdgeQL Function')
        debug.print(func.get_code(schema))

    trees = ql_parser.parse_block(func.get_code(schema) + ';')
    if len(trees) != 1:
        raise errors.InvalidFunctionDefinitionError(
            'functions can only contain one statement')

    tree = trees[0]
    if modaliases:
        ql_parser.append_module_aliases(tree, modaliases)

    if anchors is None:
        anchors = {}

    anchors['__defaults_mask__'] = irast.Parameter(
        name='__defaults_mask__',
        typeref=irtyputils.type_to_typeref(schema, schema.get('std::bytes')))

    func_params = func.get_params(schema)
    pg_params = s_func.PgParams.from_params(schema, func_params)
    for pi, p in enumerate(pg_params.params):
        p_shortname = p.get_shortname(schema)
        anchors[p_shortname] = irast.Parameter(
            name=p_shortname,
            typeref=irtyputils.type_to_typeref(schema, p.get_type(schema)))

        if p.get_default(schema) is None:
            continue

        tree.aliases.append(
            qlast.AliasedExpr(
                alias=p_shortname,
                expr=qlast.IfElse(
                    condition=qlast.BinOp(
                        left=qlast.FunctionCall(
                            func=('std', 'bytes_get_bit'),
                            args=[
                                qlast.FuncArg(
                                    arg=qlast.Path(steps=[
                                        qlast.ObjectRef(
                                            name='__defaults_mask__')
                                    ])),
                                qlast.FuncArg(
                                    arg=qlast.IntegerConstant(value=str(pi)))
                            ]),
                        right=qlast.IntegerConstant(value='0'),
                        op='='),
                    if_expr=qlast.Path(
                        steps=[qlast.ObjectRef(name=p_shortname)]),
                    else_expr=qlast._Optional(expr=p.get_ql_default(schema)))))

    ir = compile_ast_to_ir(
        tree, schema, anchors=anchors, func=func,
        security_context=security_context, modaliases=modaliases,
        implicit_id_in_shapes=implicit_id_in_shapes,
        implicit_tid_in_shapes=implicit_tid_in_shapes)

    return ir
Beispiel #4
0
 def reduce_Expr_IF_Expr_ELSE_Expr(self, *kids):
     self.val = qlast.IfElse(if_expr=kids[0].val,
                             condition=kids[2].val,
                             else_expr=kids[4].val)
Beispiel #5
0
    def _get_general_offset_limit(self, after, before, first, last):
        # convert any static values to corresponding qlast
        if after is not None:
            if isinstance(after, qlast.Base):
                after = qlast.TypeCast(type=qlast.TypeName(
                    maintype=qlast.ObjectRef(name='int64')),
                                       expr=after)
            else:
                after = qlast.BaseConstant.from_python(after)
        if before is not None:
            if isinstance(before, qlast.Base):
                before = qlast.TypeCast(type=qlast.TypeName(
                    maintype=qlast.ObjectRef(name='int64')),
                                        expr=before)
            else:
                before = qlast.BaseConstant.from_python(before)
        if first is not None and not isinstance(first, qlast.Base):
            first = qlast.BaseConstant.from_python(first)
        if last is not None and not isinstance(last, qlast.Base):
            last = qlast.BaseConstant.from_python(last)

        offset = limit = None
        # convert before, after, first and last into offset and limit
        if after is not None:
            # The +1 is to make 'after' into an appropriate index.
            #
            # 0--a--1--b--2--c--3-- ... we call element at
            # index 0 (or "element 0" for short), the element
            # immediately after the mark 0. So after "element
            # 0" really means after "index 1".
            offset = qlast.BinOp(left=after,
                                 op='+',
                                 right=qlast.IntegerConstant(value='1'))

        if before is not None:
            # limit = before - (after or 0)
            if after:
                limit = qlast.BinOp(left=before, op='-', right=after)
            else:
                limit = before

        if first is not None:
            if limit is None:
                limit = first
            else:
                limit = qlast.IfElse(if_expr=first,
                                     condition=qlast.BinOp(left=first,
                                                           op='<',
                                                           right=limit),
                                     else_expr=limit)

        if last is not None:
            if limit is not None:
                if offset:
                    offset = qlast.BinOp(left=offset,
                                         op='+',
                                         right=qlast.BinOp(left=limit,
                                                           op='-',
                                                           right=last))
                else:
                    offset = qlast.BinOp(left=limit, op='-', right=last)

                limit = qlast.IfElse(if_expr=last,
                                     condition=qlast.BinOp(left=last,
                                                           op='<',
                                                           right=limit),
                                     else_expr=limit)

            else:
                # FIXME: there wasn't any limit, so we can define last
                # in terms of offset alone without negative OFFSET
                # implementation
                raise g_errors.GraphQLTranslationError(
                    f'last translates to a negative OFFSET in '
                    f'EdgeQL which is currently unsupported')

        return offset, limit