コード例 #1
0
ファイル: eta_expand.py プロジェクト: stjordanis/edgedb
def eta_expand_ordered(
    expr: qlast.Expr,
    stype: s_types.Type,
    *,
    ctx: context.ContextLevel,
) -> qlast.Expr:
    """Do an order-preserving η-expansion

    Unlike in the lambda calculus, edgeql is a set-based language
    with a notion of ordering, which we need to preserve.
    We do this by using enumerate and ORDER BY on it:
        EXPAND_ORDERED(t, e) =
            WITH enum := enumerate(e)
            SELECT EXPAND(t, enum.1) ORDER BY enum.0
    """
    enumerated = qlast.FunctionCall(func=('__std__', 'enumerate'), args=[expr])

    enumerated_alias, enumerated_path = _get_alias('enum', ctx=ctx)

    element_path = astutils.extend_path(enumerated_path, '1')
    result_expr = eta_expand(element_path, stype, ctx=ctx)

    return qlast.SelectQuery(
        result=result_expr,
        orderby=[
            qlast.SortExpr(path=astutils.extend_path(enumerated_path, '0'))
        ],
        aliases=[qlast.AliasedExpr(alias=enumerated_alias, expr=enumerated)],
    )
コード例 #2
0
ファイル: eta_expand.py プロジェクト: stjordanis/edgedb
def eta_expand_ir(
    ir: irast.Set,
    *,
    toplevel: bool = False,
    ctx: context.ContextLevel,
) -> irast.Set:
    """η-expansion of an IR set.

    Our core implementation of η-expansion operates on an AST,
    so this mostly just checks that we really want to expand
    and then sets up an anchor for the AST based implementation
    to run on.
    """
    if (ctx.env.options.schema_object_context or ctx.env.options.func_params
            or ctx.env.options.schema_view_mode):
        return ir

    if not needs_eta_expansion(ir, ctx=ctx):
        return ir

    with ctx.new() as subctx:
        subctx.anchors = subctx.anchors.copy()
        source_ref = subctx.create_anchor(ir)

        alias, path = _get_alias('eta', ctx=subctx)
        qry = qlast.SelectQuery(
            result=eta_expand_ordered(path,
                                      setgen.get_set_type(ir, ctx=subctx),
                                      ctx=subctx),
            aliases=[qlast.AliasedExpr(alias=alias, expr=source_ref)],
        )
        if toplevel:
            subctx.toplevel_stmt = None
        return dispatch.compile(qry, ctx=subctx)
コード例 #3
0
def get_param_anchors_for_callable(
    params: s_func.ParameterLikeList,
    schema: s_schema.Schema,
    *,
    inlined_defaults: bool,
) -> Tuple[Dict[str, irast.Parameter], List[qlast.AliasedExpr], ]:
    anchors = {}
    aliases = []

    if inlined_defaults:
        anchors['__defaults_mask__'] = irast.Parameter(
            name='__defaults_mask__',
            typeref=irtyputils.type_to_typeref(  # note: no cache
                schema,
                cast(s_scalars.ScalarType, schema.get('std::bytes')),
            ),
        )

    pg_params = s_func.PgParams.from_params(schema, 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

        if not inlined_defaults:
            continue

        aliases.append(
            qlast.AliasedExpr(
                alias=p_shortname,
                expr=qlast.
                IfElse(condition=qlast.BinOp(left=qlast.FunctionCall(
                    func=('std', 'bytes_get_bit'),
                    args=[
                        qlast.Path(
                            steps=[qlast.ObjectRef(name='__defaults_mask__')]),
                        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)))))

    return anchors, aliases
コード例 #4
0
def get_param_anchors_for_callable(params, schema):
    anchors = {}
    aliases = []

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

    pg_params = s_func.PgParams.from_params(schema, 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

        aliases.append(
            qlast.AliasedExpr(
                alias=p_shortname,
                expr=qlast.
                IfElse(condition=qlast.BinOp(left=qlast.FunctionCall(
                    func=('std', 'bytes_get_bit'),
                    args=[
                        qlast.Path(
                            steps=[qlast.ObjectRef(name='__defaults_mask__')]),
                        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)))))

    return anchors, aliases
コード例 #5
0
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
コード例 #6
0
ファイル: expressions.py プロジェクト: doytsujin/edgedb
 def reduce_Identifier_ASSIGN_Expr(self, *kids):
     self.val = qlast.AliasedExpr(alias=kids[0].val, expr=kids[2].val)
コード例 #7
0
ファイル: desugar_group.py プロジェクト: stjordanis/edgedb
def desugar_group(
    node: qlast.GroupQuery,
    aliases: AliasGenerator,
) -> qlast.InternalGroupQuery:
    assert not isinstance(node, qlast.InternalGroupQuery)
    alias_map: Dict[str, Tuple[str, qlast.Expr]] = {}

    def rewrite_atom(el: qlast.GroupingAtom) -> qlast.GroupingAtom:
        if isinstance(el, qlast.ObjectRef):
            return el
        elif isinstance(el, qlast.Path):
            assert isinstance(el.steps[0], qlast.Ptr)
            ptrname = el.steps[0].ptr.name
            if ptrname not in alias_map:
                alias = aliases.get(ptrname)
                alias_map[ptrname] = (alias, el)
            alias = alias_map[ptrname][0]
            return qlast.ObjectRef(name=alias)
        else:
            return qlast.GroupingIdentList(
                context=el.context,
                elements=tuple(rewrite_atom(at) for at in el.elements),
            )

    def rewrite(el: qlast.GroupingElement) -> qlast.GroupingElement:
        if isinstance(el, qlast.GroupingSimple):
            return qlast.GroupingSimple(
                context=el.context, element=rewrite_atom(el.element))
        elif isinstance(el, qlast.GroupingSets):
            return qlast.GroupingSets(
                context=el.context, sets=[rewrite(s) for s in el.sets])
        elif isinstance(el, qlast.GroupingOperation):
            return qlast.GroupingOperation(
                context=el.context,
                oper=el.oper,
                elements=[rewrite_atom(a) for a in el.elements])
        raise AssertionError

    for using_clause in (node.using or ()):
        alias_map[using_clause.alias] = (using_clause.alias, using_clause.expr)

    using = node.using[:] if node.using else []
    by = [rewrite(by_el) for by_el in node.by]
    for alias, path in alias_map.values():
        using.append(qlast.AliasedExpr(alias=alias, expr=path))

    actual_keys = collect_grouping_atoms(by)

    g_alias = aliases.get('g')
    grouping_alias = aliases.get('grouping')
    output_dict = {
        'key': make_free_object({
            name: name_path(alias)
            for name, (alias, _) in alias_map.items()
            if alias in actual_keys
        }),
        'grouping': qlast.FunctionCall(
            func='array_unpack',
            args=[name_path(grouping_alias)],
        ),
        'elements': name_path(g_alias),
    }
    output_shape = make_free_object(output_dict)

    return qlast.InternalGroupQuery(
        context=node.context,
        aliases=node.aliases,
        subject_alias=node.subject_alias,
        subject=node.subject,
        # rewritten parts!
        using=using,
        by=by,
        group_alias=g_alias,
        grouping_alias=grouping_alias,
        result=output_shape,
        from_desugaring=True,
    )