4141
4242__all__ = [
4343 "Expression" ,
44- "ParseError " ,
44+ "ExpressionMatcher " ,
4545]
4646
4747
48+ FILE_NAME : Final = "<pytest match expression>"
49+
50+
4851class TokenType (enum .Enum ):
4952 LPAREN = "left parenthesis"
5053 RPAREN = "right parenthesis"
@@ -66,25 +69,11 @@ class Token:
6669 pos : int
6770
6871
69- class ParseError (Exception ):
70- """The :class:`Expression` contains invalid syntax.
71-
72- :param column: The column in the line where the error occurred (1-based).
73- :param message: A description of the error.
74- """
75-
76- def __init__ (self , column : int , message : str ) -> None :
77- self .column = column
78- self .message = message
79-
80- def __str__ (self ) -> str :
81- return f"at column { self .column } : { self .message } "
82-
83-
8472class Scanner :
85- __slots__ = ("current" , "tokens" )
73+ __slots__ = ("current" , "input" , " tokens" )
8674
8775 def __init__ (self , input : str ) -> None :
76+ self .input = input
8877 self .tokens = self .lex (input )
8978 self .current = next (self .tokens )
9079
@@ -108,15 +97,15 @@ def lex(self, input: str) -> Iterator[Token]:
10897 elif (quote_char := input [pos ]) in ("'" , '"' ):
10998 end_quote_pos = input .find (quote_char , pos + 1 )
11099 if end_quote_pos == - 1 :
111- raise ParseError (
112- pos + 1 ,
100+ raise SyntaxError (
113101 f'closing quote "{ quote_char } " is missing' ,
102+ (FILE_NAME , 1 , pos + 1 , input ),
114103 )
115104 value = input [pos : end_quote_pos + 1 ]
116105 if (backslash_pos := input .find ("\\ " )) != - 1 :
117- raise ParseError (
118- backslash_pos + 1 ,
106+ raise SyntaxError (
119107 r'escaping with "\" not supported in marker expression' ,
108+ (FILE_NAME , 1 , backslash_pos + 1 , input ),
120109 )
121110 yield Token (TokenType .STRING , value , pos )
122111 pos += len (value )
@@ -134,9 +123,9 @@ def lex(self, input: str) -> Iterator[Token]:
134123 yield Token (TokenType .IDENT , value , pos )
135124 pos += len (value )
136125 else :
137- raise ParseError (
138- pos + 1 ,
126+ raise SyntaxError (
139127 f'unexpected character "{ input [pos ]} "' ,
128+ (FILE_NAME , 1 , pos + 1 , input ),
140129 )
141130 yield Token (TokenType .EOF , "" , pos )
142131
@@ -159,12 +148,12 @@ def accept(self, type: TokenType, *, reject: bool = False) -> Token | None:
159148 return None
160149
161150 def reject (self , expected : Sequence [TokenType ]) -> NoReturn :
162- raise ParseError (
163- self .current .pos + 1 ,
151+ raise SyntaxError (
164152 "expected {}; got {}" .format (
165153 " OR " .join (type .value for type in expected ),
166154 self .current .type .value ,
167155 ),
156+ (FILE_NAME , 1 , self .current .pos + 1 , self .input ),
168157 )
169158
170159
@@ -225,14 +214,14 @@ def not_expr(s: Scanner) -> ast.expr:
225214def single_kwarg (s : Scanner ) -> ast .keyword :
226215 keyword_name = s .accept (TokenType .IDENT , reject = True )
227216 if not keyword_name .value .isidentifier ():
228- raise ParseError (
229- keyword_name .pos + 1 ,
217+ raise SyntaxError (
230218 f"not a valid python identifier { keyword_name .value } " ,
219+ (FILE_NAME , 1 , keyword_name .pos + 1 , s .input ),
231220 )
232221 if keyword .iskeyword (keyword_name .value ):
233- raise ParseError (
234- keyword_name .pos + 1 ,
222+ raise SyntaxError (
235223 f"unexpected reserved python keyword `{ keyword_name .value } `" ,
224+ (FILE_NAME , 1 , keyword_name .pos + 1 , s .input ),
236225 )
237226 s .accept (TokenType .EQUAL , reject = True )
238227
@@ -247,9 +236,9 @@ def single_kwarg(s: Scanner) -> ast.keyword:
247236 elif value_token .value in BUILTIN_MATCHERS :
248237 value = BUILTIN_MATCHERS [value_token .value ]
249238 else :
250- raise ParseError (
251- value_token .pos + 1 ,
239+ raise SyntaxError (
252240 f'unexpected character/s "{ value_token .value } "' ,
241+ (FILE_NAME , 1 , value_token .pos + 1 , s .input ),
253242 )
254243
255244 ret = ast .keyword (keyword_name .value , ast .Constant (value ))
@@ -338,7 +327,7 @@ def compile(cls, input: str) -> Expression:
338327
339328 :param input: The input expression - one line.
340329
341- :raises ParseError : If the expression is malformed.
330+ :raises SyntaxError : If the expression is malformed.
342331 """
343332 astexpr = expression (Scanner (input ))
344333 code = compile (
0 commit comments