11from __future__ import annotations
22
3+ from dataclasses import dataclass , field
4+ from functools import cached_property
5+
6+ from black .strings import Match
7+
8+ from .hashed_data import T
39from .symbol_graph import SymbolGraph
410from .utils import is_iterable
511
1319 Optional ,
1420 Union ,
1521 Iterable ,
16- TypeVar ,
22+ Dict ,
23+ Generic ,
1724 Type ,
1825 Tuple ,
1926 List ,
3744 ForAll ,
3845 Exists ,
3946 Literal ,
47+ ResultQuantifier ,
4048)
4149from .result_quantification_constraint import ResultQuantificationConstraint
4250
4351from .predicate import (
4452 Predicate ,
4553 # type: ignore
4654 Symbol , # type: ignore
55+ HasType ,
4756)
4857
49- T = TypeVar ("T" ) # Define type variable "T"
5058
5159ConditionType = Union [SymbolicExpression , bool , Predicate ]
5260"""
5361The possible types for conditions.
5462"""
55- EntityType = Union [SetOf [T ], Entity [T ], T , Iterable [T ], Type [T ]]
63+ EntityType = Union [SetOf [T ], Entity [T ], T , Iterable [T ], Type [T ], Match [ T ] ]
5664"""
5765The possible types for entities.
5866"""
6169def an (
6270 entity_ : EntityType ,
6371 quantification : Optional [ResultQuantificationConstraint ] = None ,
64- ) -> Union [An [T ], T , SymbolicExpression [ T ] ]:
72+ ) -> Union [An [T ], T ]:
6573 """
6674 Select a single element satisfying the given entity description.
6775
@@ -70,7 +78,7 @@ def an(
7078 :return: A quantifier representing "an" element.
7179 :rtype: An[T]
7280 """
73- return An ( entity_ , _quantification_constraint_ = quantification )
81+ return _quantify_entity ( An , entity_ , _quantification_constraint_ = quantification )
7482
7583
7684a = an
@@ -89,7 +97,23 @@ def the(
8997 :return: A quantifier representing "an" element.
9098 :rtype: The[T]
9199 """
92- return The (entity_ )
100+ return _quantify_entity (The , entity_ )
101+
102+
103+ def _quantify_entity (
104+ quantifier : Type [ResultQuantifier ], entity_ : EntityType , ** quantifier_kwargs
105+ ) -> Union [ResultQuantifier [T ], T ]:
106+ """
107+ Apply the given quantifier to the given entity.
108+
109+ :param quantifier: The quantifier to apply.
110+ :param entity_: The entity to quantify.
111+ :param quantifier_kwargs: Keyword arguments to pass to the quantifier.
112+ :return: The quantified entity.
113+ """
114+ if isinstance (entity_ , Match ):
115+ entity_ = entity_ .expression
116+ return quantifier (entity_ , ** quantifier_kwargs )
93117
94118
95119def entity (
@@ -319,3 +343,111 @@ def inference(
319343 return lambda ** kwargs : Variable (
320344 _type_ = type_ , _name__ = type_ .__name__ , _kwargs_ = kwargs , _is_inferred_ = True
321345 )
346+
347+
348+ @dataclass
349+ class Match (Generic [T ]):
350+ """
351+ Construct a query that looks for the pattern provided by the type and the keyword arguments.
352+ """
353+
354+ type_ : Type [T ]
355+ """
356+ The type of the variable.
357+ """
358+ kwargs : Dict [str , Any ]
359+ """
360+ The keyword arguments to match against.
361+ """
362+ variable : CanBehaveLikeAVariable [T ] = field (init = False )
363+ """
364+ The created variable from the type and kwargs.
365+ """
366+ conditions : List [ConditionType ] = field (init = False , default_factory = list )
367+ """
368+ The conditions that define the match.
369+ """
370+
371+ def _resolve (self , variable : Optional [CanBehaveLikeAVariable ] = None ):
372+ """
373+ Resolve the match by creating the variable and conditions expressions.
374+
375+ :param variable: An optional pre-existing variable to use for the match; if not provided, a new variable will be created.
376+ :return:
377+ """
378+ self .variable = variable if variable else self ._create_variable ()
379+ for k , v in self .kwargs .items ():
380+ attr = getattr (self .variable , k )
381+ if isinstance (v , Match ):
382+ v ._resolve (attr )
383+ self .conditions .append (HasType (attr , v .type_ ))
384+ self .conditions .extend (v .conditions )
385+ else :
386+ self .conditions .append (attr == v )
387+
388+ def _create_variable (self ) -> Variable [T ]:
389+ """
390+ Create a variable with the given type.
391+ """
392+ return let (self .type_ , None )
393+
394+ @cached_property
395+ def expression (self ) -> Entity [T ]:
396+ """
397+ Return the entity expression corresponding to the match query.
398+ """
399+ self ._resolve ()
400+ return entity (self .variable , * self .conditions )
401+
402+
403+ @dataclass
404+ class MatchEntity (Match [T ]):
405+ """
406+ A match that can also take a domain and should be used as the outermost match in a nested match statement.
407+ This is because the inner match statements derive their domain from the outer match as they are basically attributes
408+ of the outer match variable.
409+ """
410+
411+ domain : DomainType
412+ """
413+ The domain to use for the variable created by the match.
414+ """
415+
416+ def _create_variable (self ) -> Variable [T ]:
417+ """
418+ Create a variable with the given type and domain.
419+ """
420+ return let (self .type_ , self .domain )
421+
422+
423+ def match (type_ : Type [T ]) -> Union [Type [T ], Callable [..., Match [T ]]]:
424+ """
425+ This returns a factory function that creates a Match instance that looks for the pattern provided by the type and the
426+ keyword arguments.
427+
428+ :param type_: The type of the variable (i.e., The class you want to instantiate).
429+ :return: The factory function for creating the match query.
430+ """
431+
432+ def match_factory (** kwargs ) -> Match [T ]:
433+ return Match (type_ , kwargs )
434+
435+ return match_factory
436+
437+
438+ def entity_matching (
439+ type_ : Type [T ], domain : DomainType
440+ ) -> Union [Type [T ], Callable [..., MatchEntity [T ]]]:
441+ """
442+ Same as :py:func:`krrood.entity_query_language.entity.match` but with a domain to use for the variable created
443+ by the match.
444+
445+ :param type_: The type of the variable (i.e., The class you want to instantiate).
446+ :param domain: The domain used for the variable created by the match.
447+ :return: The factory function for creating the match query.
448+ """
449+
450+ def match_factory (** kwargs ) -> MatchEntity [T ]:
451+ return MatchEntity (type_ , kwargs , domain )
452+
453+ return match_factory
0 commit comments