@@ -70,10 +70,11 @@ class Token:
7070
7171
7272class Scanner :
73- __slots__ = ("current" , "input" , "tokens" )
73+ __slots__ = ("current" , "idents" , " input" , "tokens" )
7474
7575 def __init__ (self , input : str ) -> None :
7676 self .input = input
77+ self .idents : set [str ] = set ()
7778 self .tokens = self .lex (input )
7879 self .current = next (self .tokens )
7980
@@ -163,13 +164,13 @@ def reject(self, expected: Sequence[TokenType]) -> NoReturn:
163164IDENT_PREFIX = "$"
164165
165166
166- def expression (s : Scanner ) -> ast .Expression :
167+ def expression (s : Scanner ) -> tuple [ ast .Expression , frozenset [ str ]] :
167168 if s .accept (TokenType .EOF ):
168169 ret : ast .expr = ast .Constant (False )
169170 else :
170171 ret = expr (s )
171172 s .accept (TokenType .EOF , reject = True )
172- return ast .fix_missing_locations (ast .Expression (ret ))
173+ return ast .fix_missing_locations (ast .Expression (ret )), frozenset ( s . idents )
173174
174175
175176def expr (s : Scanner ) -> ast .expr :
@@ -197,6 +198,7 @@ def not_expr(s: Scanner) -> ast.expr:
197198 return ret
198199 ident = s .accept (TokenType .IDENT )
199200 if ident :
201+ s .idents .add (ident .value )
200202 name = ast .Name (IDENT_PREFIX + ident .value , ast .Load ())
201203 if s .accept (TokenType .LPAREN ):
202204 ret = ast .Call (func = name , args = [], keywords = all_kwargs (s ))
@@ -314,12 +316,16 @@ class Expression:
314316 The expression can be evaluated against different matchers.
315317 """
316318
317- __slots__ = ("_code" , "input" )
319+ __slots__ = ("_code" , "_idents" , " input" )
318320
319- def __init__ (self , input : str , code : types .CodeType ) -> None :
321+ def __init__ (
322+ self , input : str , code : types .CodeType , idents : frozenset [str ]
323+ ) -> None :
320324 #: The original input line, as a string.
321325 self .input : Final = input
322326 self ._code : Final = code
327+ #: All identifiers which appear in the expression.
328+ self ._idents : Final = idents
323329
324330 @classmethod
325331 def compile (cls , input : str ) -> Expression :
@@ -329,13 +335,17 @@ def compile(cls, input: str) -> Expression:
329335
330336 :raises SyntaxError: If the expression is malformed.
331337 """
332- astexpr = expression (Scanner (input ))
338+ astexpr , idents = expression (Scanner (input ))
333339 code = compile (
334340 astexpr ,
335341 filename = "<pytest match expression>" ,
336342 mode = "eval" ,
337343 )
338- return Expression (input , code )
344+ return Expression (input , code , idents )
345+
346+ def idents (self ) -> frozenset [str ]:
347+ """Return the set of all identifiers which appear in the expression."""
348+ return self ._idents
339349
340350 def evaluate (self , matcher : ExpressionMatcher ) -> bool :
341351 """Evaluate the match expression.
0 commit comments