def type_to_ql_typeref(t: s_obj.Object, *, _name=None, ctx: context.ContextLevel) -> qlast.TypeName: if not isinstance(t, s_abc.Collection): result = qlast.TypeName(name=_name, maintype=qlast.ObjectRef( module=t.get_name(ctx.env.schema).module, name=t.get_name(ctx.env.schema).name)) elif isinstance(t, s_abc.Tuple) and t.named: result = qlast.TypeName(name=_name, maintype=qlast.ObjectRef(name=t.schema_name), subtypes=[ type_to_ql_typeref(st, _name=sn, ctx=ctx) for sn, st in t.element_types.items() ]) else: result = qlast.TypeName(name=_name, maintype=qlast.ObjectRef(name=t.schema_name), subtypes=[ type_to_ql_typeref(st, ctx=ctx) for st in t.get_subtypes() ]) return result
def type_to_ql_typeref(t: s_obj.Object) -> qlast.TypeName: if not isinstance(t, s_types.Collection): result = qlast.TypeName( maintype=qlast.ObjectRef(module=t.name.module, name=t.name.name)) else: result = qlast.TypeName( maintype=qlast.ObjectRef(name=t.schema_name), subtypes=[type_to_ql_typeref(st) for st in t.get_subtypes()]) return result
def typeref_to_ast(t: so.Object) -> ql_ast.TypeName: if not isinstance(t, s_types.Collection): if isinstance(t, so.ObjectRef): name = t.classname else: name = t.name result = ql_ast.TypeName( maintype=ql_ast.ObjectRef(module=name.module, name=name.name)) else: result = ql_ast.TypeName( maintype=ql_ast.ObjectRef(name=t.schema_name), subtypes=[typeref_to_ast(st) for st in t.get_subtypes()]) return result
def _apply_field_ast(self, schema, context, node, op): if op.property in ('id', 'name'): pass elif op.property == 'bases': if not isinstance(op.new_value, so.ObjectList): bases = so.ObjectList.create(schema, op.new_value) else: bases = op.new_value base_names = bases.names(schema, allow_unresolved=True) node.bases = [ qlast.TypeName( maintype=qlast.ObjectRef( name=b.name, module=b.module ) ) for b in base_names ] elif op.property == 'mro': pass elif op.property == 'is_abstract': node.is_abstract = op.new_value elif op.property == 'is_final': node.is_final = op.new_value else: super()._apply_field_ast(schema, context, node, op)
def visit_Set(self, node): if node.expr is not None: result = self.visit(node.expr) else: links = [] while node.rptr and (not node.show_as_anchor or self.context.inline_anchors): rptr = node.rptr ptrcls = rptr.ptrcls pname = ptrcls.shortname if isinstance(rptr.target.scls, s_objtypes.ObjectType): target = rptr.target.scls.shortname target = qlast.TypeName(maintype=qlast.ObjectRef( name=target.name, module=target.module)) else: target = None link = qlast.Ptr( ptr=qlast.ObjectRef(name=pname.name, ), direction=rptr.direction, target=target, ) if isinstance(ptrcls.source, s_links.Link): link.type = 'property' links.append(link) node = node.rptr.source result = qlast.Path() if node.show_as_anchor and not self.context.inline_anchors: if issubclass(node.show_as_anchor, qlast.Expr): step = node.show_as_anchor() else: step = qlast.ObjectRef(name=node.show_as_anchor) else: step = qlast.ObjectRef(name=node.scls.shortname.name, module=node.scls.shortname.module) result.steps.append(step) result.steps.extend(reversed(links)) if node.shape: result = qlast.Shape(expr=result, elements=[]) for el in node.shape: rptr = el.rptr ptrcls = rptr.ptrcls pn = ptrcls.shortname pn = qlast.ShapeElement(expr=qlast.Path(steps=[ qlast.Ptr(ptr=qlast.ObjectRef(name=pn.name), direction=rptr.direction) ])) result.elements.append(pn) return result
def visit_TypeCast(self, node): if node.type.subtypes: typ = qlast.TypeName( maintype=qlast.ObjectRef(name=node.type.maintype), subtypes=[ qlast.ObjectRef(module=stn.module, name=stn.name) for stn in node.type.subtypes ]) else: mtn = node.type.maintype mt = qlast.ObjectRef(module=mtn.module, name=mtn.name) typ = qlast.TypeName(maintype=mt) result = qlast.TypeCast(expr=self.visit(node.expr), type=typ) return result
def typeref_to_ast(schema, t: so.Object) -> ql_ast.TypeName: if not isinstance(t, s_abc.Collection): if t.is_type() and t.is_any(): ref = ql_ast.AnyType() elif t.is_type() and t.is_anytuple(): ref = ql_ast.AnyTuple() else: ref = ql_ast.ObjectRef(module=t.get_name(schema).module, name=t.get_name(schema).name) result = ql_ast.TypeName(maintype=ref) else: result = ql_ast.TypeName( maintype=ql_ast.ObjectRef(name=t.schema_name), subtypes=[typeref_to_ast(schema, st) for st in t.get_subtypes()]) return result
def reduce_NodeName(self, *kids): maintype = kids[0].val # maintype cannot be a collection if maintype.module is None and maintype.name in {'array', 'tuple'}: raise EdgeQLSyntaxError(f"Unexpected {maintype.name!r}", context=kids[0].context) self.val = qlast.TypeName(maintype=maintype)
def reduce_COLON_TypedShape(self, *kids): # typed shape needs to be transformed here into a different # expression shape = kids[1].val self.val = [qlast.TypeName( maintype=shape.expr.steps[0], context=context.get_context(shape.expr.steps[0]) )] self.val += shape.elements
def reduce_ARRAY_LANGBRACKET_NonArrayTypeName_OptDimensions_RANGBRACKET( self, *kids): subtype = kids[2].val dimensions = kids[3].val self.val = qlast.TypeName( maintype=qlast.ObjectRef(name='array'), subtypes=[subtype], dimensions=dimensions, )
def _apply_field_ast(self, context, node, op): if op.property == 'type': tp = op.new_value if isinstance(tp, s_types.Collection): maintype = tp.schema_name stt = tp.get_subtypes() for st in stt: eltype = qlast.ObjectRef(module=st.module, name=st.name) tnn = qlast.TypeName( maintype=maintype, subtypes=[eltype]) else: tnn = qlast.TypeName(maintype=tp) node.type = tnn else: super()._apply_field_ast(context, node, op)
def reduce_ATTRIBUTE_ParenRawString_DeclarationSpecsBlob(self, *kids): eql = kids[1].parse_as_attribute_decl() attributes = [] for spec in kids[2].val: if isinstance(spec, esast.Attribute): attributes.append(spec) else: raise SchemaSyntaxError('illegal definition', context=spec.context) self.val = esast.AttributeDeclaration( name=eql.name.name, extends=[qlast.TypeName(maintype=base) for base in eql.bases], type=eql.type, attributes=attributes)
def _apply_field_ast(self, context, node, op): if op.property == 'name': pass elif op.property == 'bases': node.bases = [ qlast.TypeName(maintype=qlast.ObjectRef( name=b.classname.name, module=b.classname.module)) for b in op.new_value ] elif op.property == 'mro': pass elif op.property == 'is_abstract': node.is_abstract = op.new_value elif op.property == 'is_final': node.is_final = op.new_value else: super()._apply_field_ast(context, node, op)
def reduce_Expr_LBRACKET_IS_NodeName_RBRACKET(self, *kids): # The path filter rule is here to resolve ambiguity with # indexes and slices, so Expr needs to be enforced as a path. # # NOTE: We specifically disallow "Foo.(bar[IS Baz])"" because # it is incorrect logical grouping. The example where the # incorrect grouping is more obvious is: "Foo.<(bar[IS Baz])" path = kids[0].val if (isinstance(path, qlast.Path) and isinstance(path.steps[-1], qlast.Ptr)): # filtering a longer path path.steps[-1].target = kids[3].val self.val = path else: # any other expression is a path with a filter self.val = qlast.TypeFilter( expr=path, type=qlast.TypeName(maintype=kids[3].val))
def _classbases_from_ast(cls, astnode, context, schema): classname = cls._classname_from_ast(astnode, context, schema) modaliases = context.modaliases bases = so.ObjectList( utils.ast_to_typeref( qlast.TypeName(maintype=b), modaliases=modaliases, schema=schema) for b in getattr(astnode, 'bases', None) or [] ) mcls = cls.get_schema_metaclass() if not bases and classname not in mcls.get_root_classes(): default_base = mcls.get_default_base_name() if default_base is not None and classname != default_base: bases = so.ObjectList([ so.ObjectRef(classname=default_base) ]) return bases
def _cmd_tree_from_ast(cls, schema, astnode, context): cmd = super()._cmd_tree_from_ast(schema, astnode, context) if isinstance(astnode, cls.referenced_astnode): objcls = cls.get_schema_metaclass() try: base = utils.ast_to_typeref( qlast.TypeName(maintype=astnode.name), modaliases=context.modaliases, schema=schema) except errors.InvalidReferenceError: # Certain concrete items, like pointers create # abstract parents implicitly. nname = sn.shortname_from_fullname(cmd.classname) base = so.ObjectRef( name=sn.Name(module=nname.module, name=nname.name)) cmd.add( sd.AlterObjectProperty(property='bases', new_value=so.ObjectList.create( schema, [base]))) referrer_ctx = cls.get_referrer_context(context) referrer_class = referrer_ctx.op.get_schema_metaclass() referrer_name = referrer_ctx.op.classname refdict = referrer_class.get_refdict_for_class(objcls) cmd.add( sd.AlterObjectProperty( property=refdict.backref_attr, new_value=so.ObjectRef(name=referrer_name))) if getattr(astnode, 'is_abstract', None): cmd.add( sd.AlterObjectProperty(property='is_abstract', new_value=True)) return cmd
def visit_ObjectField(self, node): fname = node.name # handle boolean ops if fname == 'and': return self._visit_list_of_inputs(node.value.value, ast.ops.AND) elif fname == 'or': return self._visit_list_of_inputs(node.value.value, ast.ops.OR) elif fname == 'not': return qlast.UnaryOp(op=ast.ops.NOT, operand=self.visit(node.value)) # handle various scalar ops op = gt.GQL_TO_OPS_MAP.get(fname) if op: value = self.visit(node.value) return qlast.BinOp(left=self._context.filter, op=op, right=value) # we're at the beginning of a scalar op _, target = self._get_parent_and_current_type() name = self.get_path_prefix() name.append(qlast.Ptr(ptr=qlast.ObjectRef(name=fname))) name = qlast.Path(steps=name) # potentially need to cast the 'name' side into a <str>, so as # to be compatible with the 'value' typename = target.get_field_type(fname).short_name if (typename != 'str' and gt.EDB_TO_GQL_SCALARS_MAP[typename] in {GraphQLString, GraphQLID}): name = qlast.TypeCast( expr=name, type=qlast.TypeName(maintype=qlast.ObjectRef(name='str')), ) self._context.filter = name return self.visit(node.value)
def _classname_from_ast(cls, astnode, context, schema): name = super()._classname_from_ast(astnode, context, schema) parent_ctx = cls.get_referrer_context(context) if parent_ctx is not None: referrer_name = parent_ctx.op.classname try: base_ref = utils.ast_to_typeref( qlast.TypeName(maintype=astnode.name), modaliases=context.modaliases, schema=schema) except s_err.ItemNotFoundError: base_name = sn.Name(name) else: base_name = base_ref.classname pcls = cls.get_schema_metaclass() pnn = pcls.get_specialized_name(base_name, referrer_name) name = sn.Name(name=pnn, module=referrer_name.module) return name
def _classname_from_ast(cls, schema, astnode, context): name = super()._classname_from_ast(schema, astnode, context) parent_ctx = cls.get_referrer_context(context) if parent_ctx is not None: referrer_name = parent_ctx.op.classname try: base_ref = utils.ast_to_typeref( qlast.TypeName(maintype=astnode.name), modaliases=context.modaliases, schema=schema) except errors.InvalidReferenceError: base_name = sn.Name(name) else: base_name = base_ref.get_name(schema) quals = cls._classname_quals_from_ast(schema, astnode, base_name, referrer_name, context) pnn = sn.get_specialized_name(base_name, referrer_name, *quals) name = sn.Name(name=pnn, module=referrer_name.module) return name
def visit_Argument(self, node, *, get_path_prefix): op = ast.ops.EQ name_parts = node.name _, target = self._get_parent_and_current_type() name = get_path_prefix() name.append(qlast.Ptr(ptr=qlast.ObjectRef(name=name_parts))) name = qlast.Path(steps=name) value = self.visit(node.value) # potentially need to cast the 'name' side into a <str>, so as # to be compatible with the 'value' typename = target.get_field_type(name_parts).short_name if (typename != 'str' and gt.EDB_TO_GQL_SCALARS_MAP[typename] in {GraphQLString, GraphQLID}): name = qlast.TypeCast( expr=name, type=qlast.TypeName(maintype=qlast.ObjectRef(name='str')), ) return qlast.BinOp(left=name, op=op, right=value)
def _normalize_view_ptr_expr( shape_el: qlast.ShapeElement, view_scls: s_nodes.Node, *, path_id: irast.PathId, is_insert: bool = False, is_update: bool = False, view_rptr: typing.Optional[context.ViewRPtr] = None, ctx: context.CompilerContext) -> s_pointers.Pointer: steps = shape_el.expr.steps is_linkprop = False is_mutation = is_insert or is_update # Pointers may be qualified by the explicit source # class, which is equivalent to Expr[IS Type]. is_polymorphic = len(steps) == 2 scls = view_scls.peel_view() ptrsource = scls qlexpr = None if is_polymorphic: source = qlast.TypeFilter(expr=qlast.Path(steps=[qlast.Source()]), type=qlast.TypeName(maintype=steps[0])) lexpr = steps[1] ptrsource = schemactx.get_schema_type(steps[0], ctx=ctx) elif len(steps) == 1: lexpr = steps[0] is_linkprop = lexpr.type == 'property' if is_linkprop: if view_rptr is None: raise errors.EdgeQLError( 'invalid reference to link property ' 'in top level shape', context=lexpr.context) if view_rptr.ptrcls is None: derive_ptrcls(view_rptr, target_scls=view_scls, ctx=ctx) ptrsource = scls = view_rptr.ptrcls source = qlast.Source() else: raise RuntimeError( f'unexpected path length in view shape: {len(steps)}') ptrname = (lexpr.ptr.module, lexpr.ptr.name) ptrcls_is_derived = False compexpr = shape_el.compexpr if compexpr is None and is_insert and shape_el.elements: # Nested insert short form: # INSERT Foo { bar: Spam { name := 'name' }} # Expand to: # INSERT Foo { bar := (INSERT Spam { name := 'name' }) } if lexpr.target is not None: ptr_target = schemactx.get_schema_type(lexpr.target, ctx=ctx) else: ptr_target = None base_ptrcls = ptrcls = setgen.resolve_ptr( ptrsource, ptrname, s_pointers.PointerDirection.Outbound, target=ptr_target, ctx=ctx) compexpr = qlast.InsertQuery(subject=qlast.Path(steps=[ qlast.ObjectRef(name=ptrcls.target.name.name, module=ptrcls.target.name.module) ]), shape=shape_el.elements) if compexpr is None: if lexpr.target is not None: ptr_target = schemactx.get_schema_type(lexpr.target, ctx=ctx) else: ptr_target = None base_ptrcls = ptrcls = setgen.resolve_ptr( ptrsource, ptrname, s_pointers.PointerDirection.Outbound, target=ptr_target, ctx=ctx) base_ptr_is_computable = ptrcls in ctx.source_map if ptr_target is not None and ptr_target != base_ptrcls.target: # This happens when a union type target is narrowed by an # [IS Type] construct. Since the derived pointer will have # the correct target, we don't need to do anything, but # remove the [IS] qualifier to prevent recursion. lexpr.target = None else: ptr_target = ptrcls.target if ptrcls in ctx.pending_cardinality: # We do not know the parent's pointer cardinality yet. ptr_cardinality = None else: ptr_cardinality = ptrcls.cardinality if shape_el.elements: sub_view_rptr = context.ViewRPtr( ptrsource if is_linkprop else view_scls, ptrcls=ptrcls, is_insert=is_insert, is_update=is_update) sub_path_id = path_id.extend(ptrcls, target=ptrcls.target) ctx.path_scope.attach_path(sub_path_id) if is_update: for subel in shape_el.elements or []: is_prop = (isinstance(subel.expr.steps[0], qlast.Ptr) and subel.expr.steps[0].type == 'property') if not is_prop: raise errors.EdgeQLError( 'only references to link properties are allowed ' 'in nested UPDATE shapes', context=subel.context) ptr_target = _process_view(scls=ptr_target, path_id=sub_path_id, view_rptr=sub_view_rptr, elements=shape_el.elements, is_update=True, ctx=ctx) else: ptr_target = _process_view(scls=ptr_target, path_id=sub_path_id, view_rptr=sub_view_rptr, elements=shape_el.elements, ctx=ctx) ptrcls = sub_view_rptr.derived_ptrcls if ptrcls is None: ptrcls_is_derived = False ptrcls = sub_view_rptr.ptrcls else: ptrcls_is_derived = True if (shape_el.where or shape_el.orderby or shape_el.offset or shape_el.limit or base_ptr_is_computable or is_polymorphic): if qlexpr is None: qlexpr = qlast.Path(steps=[source, lexpr]) qlexpr = astutils.ensure_qlstmt(qlexpr) qlexpr.where = shape_el.where qlexpr.orderby = shape_el.orderby qlexpr.offset = shape_el.offset qlexpr.limit = shape_el.limit else: try: base_ptrcls = ptrcls = setgen.resolve_ptr( ptrsource, ptrname, s_pointers.PointerDirection.Outbound, ctx=ctx) ptr_name = ptrcls.shortname except errors.EdgeQLReferenceError: if is_mutation: raise base_ptrcls = ptrcls = None ptr_module = (ptrname[0] or ctx.derived_target_module or scls.name.module) ptr_name = sn.SchemaName(module=ptr_module, name=ptrname[1]) qlexpr = astutils.ensure_qlstmt(compexpr) with ctx.newscope(fenced=True) as shape_expr_ctx: # Put current pointer class in context, so # that references to link properties in sub-SELECT # can be resolved. This is necessary for proper # evaluation of link properties on computable links, # most importantly, in INSERT/UPDATE context. shape_expr_ctx.view_rptr = context.ViewRPtr( ptrsource if is_linkprop else view_scls, ptrcls=ptrcls, ptrcls_name=ptr_name, ptrcls_is_linkprop=is_linkprop, is_insert=is_insert, is_update=is_update) shape_expr_ctx.path_scope.unnest_fence = True if is_mutation: shape_expr_ctx.expr_exposed = True irexpr = dispatch.compile(qlexpr, ctx=shape_expr_ctx) irexpr.context = compexpr.context if base_ptrcls is None: base_ptrcls = ptrcls = shape_expr_ctx.view_rptr.ptrcls derived_ptrcls = shape_expr_ctx.view_rptr.derived_ptrcls if derived_ptrcls is not None: ptrcls_is_derived = True ptrcls = derived_ptrcls ptr_cardinality = None ptr_target = irutils.infer_type(irexpr, ctx.schema) if ptr_target is None: msg = 'cannot determine expression result type' raise errors.EdgeQLError(msg, context=shape_el.context) if is_mutation and not ptr_target.assignment_castable_to( base_ptrcls.target, schema=ctx.schema): # Validate that the insert/update expression is # of the correct class. lname = f'({ptrsource.name}).{ptrcls.shortname.name}' expected = [repr(str(base_ptrcls.target.name))] raise edgedb_error.InvalidPointerTargetError( f'invalid target for link {str(lname)!r}: ' f'{str(ptr_target.name)!r} (expecting ' f'{" or ".join(expected)})') if qlexpr is not None or ptr_target is not ptrcls.target: if not ptrcls_is_derived: if is_linkprop: rptrcls = view_rptr.derived_ptrcls if rptrcls is None: rptrcls = derive_ptrcls(view_rptr, target_scls=view_scls, ctx=ctx) src_scls = rptrcls else: src_scls = view_scls ptrcls = schemactx.derive_view(ptrcls, src_scls, ptr_target, is_insert=is_insert, is_update=is_update, derived_name_quals=[view_scls.name], ctx=ctx) if qlexpr is not None: ctx.source_map[ptrcls] = (qlexpr, ctx) ptrcls.computable = True if not is_mutation: if ptr_cardinality is None: if compexpr is not None: ctx.pending_cardinality.add(ptrcls) elif ptrcls is not base_ptrcls: ctx.pointer_derivation_map[base_ptrcls].append(ptrcls) ptrcls.cardinality = None else: ptrcls.cardinality = ptr_cardinality if ptrcls.is_protected_pointer() and qlexpr is not None: msg = f'cannot assign to {ptrcls.shortname.name}' raise errors.EdgeQLError(msg, context=shape_el.context) return ptrcls
def reduce_TUPLE_LANGBRACKET_NamedTupleTypeList_RANGBRACKET(self, *kids): self.val = qlast.TypeName( maintype=qlast.ObjectRef(name='tuple'), subtypes=kids[2].val, )
def reduce_TUPLE_LANGBRACKET_RANGBRACKET(self, *kids): self.val = qlast.TypeName( maintype=qlast.ObjectRef(name='tuple'), subtypes=[], )
def reduce_NodeName_LANGBRACKET_SubtypeList_RANGBRACKET(self, *kids): self.val = qlast.TypeName( maintype=kids[0].val, subtypes=kids[2].val, )
def reduce_ANYTUPLE(self, *kids): self.val = qlast.TypeName(maintype=qlast.AnyTuple())
def reduce_ANYTYPE(self, *kids): self.val = qlast.TypeName(maintype=qlast.AnyType())
def reduce_NodeName(self, *kids): self.val = qlast.TypeName(maintype=kids[0].val)
def reduce_ATTRIBUTE_ParenRawString_NL(self, *kids): eql = kids[1].parse_as_attribute_decl() self.val = esast.AttributeDeclaration( name=eql.name.name, extends=[qlast.TypeName(maintype=base) for base in eql.bases], type=eql.type)