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)], )
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)
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
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
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
def reduce_Identifier_ASSIGN_Expr(self, *kids): self.val = qlast.AliasedExpr(alias=kids[0].val, expr=kids[2].val)
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, )