@@ -4294,6 +4294,37 @@ def dedent(self, line):
42944294StateKeeper = Callable [[str | None ], None ]
42954295ConverterArgs = dict [str , Any ]
42964296
4297+ class ParamState (enum .IntEnum ):
4298+ """Parameter parsing state.
4299+
4300+ [ [ a, b, ] c, ] d, e, f=3, [ g, h, [ i ] ] <- line
4301+ 01 2 3 4 5 6 <- state transitions
4302+ """
4303+ # Before we've seen anything.
4304+ # Legal transitions: to LEFT_SQUARE_BEFORE or REQUIRED
4305+ START = 0
4306+
4307+ # Left square backets before required params.
4308+ LEFT_SQUARE_BEFORE = 1
4309+
4310+ # In a group, before required params.
4311+ GROUP_BEFORE = 2
4312+
4313+ # Required params, positional-or-keyword or positional-only (we
4314+ # don't know yet). Renumber left groups!
4315+ REQUIRED = 3
4316+
4317+ # Positional-or-keyword or positional-only params that now must have
4318+ # default values.
4319+ OPTIONAL = 4
4320+
4321+ # In a group, after required params.
4322+ GROUP_AFTER = 5
4323+
4324+ # Right square brackets after required params.
4325+ RIGHT_SQUARE_AFTER = 6
4326+
4327+
42974328class DSLParser :
42984329 function : Function | None
42994330 state : StateKeeper
@@ -4331,7 +4362,7 @@ def reset(self) -> None:
43314362 self .keyword_only = False
43324363 self .positional_only = False
43334364 self .group = 0
4334- self .parameter_state = self . ps_start
4365+ self .parameter_state : ParamState = ParamState . START
43354366 self .seen_positional_with_default = False
43364367 self .indent = IndentStack ()
43374368 self .kind = CALLABLE
@@ -4726,22 +4757,8 @@ def state_modulename_name(self, line: str | None) -> None:
47264757 #
47274758 # These rules are enforced with a single state variable:
47284759 # "parameter_state". (Previously the code was a miasma of ifs and
4729- # separate boolean state variables.) The states are:
4730- #
4731- # [ [ a, b, ] c, ] d, e, f=3, [ g, h, [ i ] ] <- line
4732- # 01 2 3 4 5 6 <- state transitions
4733- #
4734- # 0: ps_start. before we've seen anything. legal transitions are to 1 or 3.
4735- # 1: ps_left_square_before. left square brackets before required parameters.
4736- # 2: ps_group_before. in a group, before required parameters.
4737- # 3: ps_required. required parameters, positional-or-keyword or positional-only
4738- # (we don't know yet). (renumber left groups!)
4739- # 4: ps_optional. positional-or-keyword or positional-only parameters that
4740- # now must have default values.
4741- # 5: ps_group_after. in a group, after required parameters.
4742- # 6: ps_right_square_after. right square brackets after required parameters.
4743- ps_start , ps_left_square_before , ps_group_before , ps_required , \
4744- ps_optional , ps_group_after , ps_right_square_after = range (7 )
4760+ # separate boolean state variables.) The states are defined in the
4761+ # ParamState class.
47454762
47464763 def state_parameters_start (self , line : str | None ) -> None :
47474764 if not self .valid_line (line ):
@@ -4759,8 +4776,8 @@ def to_required(self):
47594776 """
47604777 Transition to the "required" parameter state.
47614778 """
4762- if self .parameter_state != self . ps_required :
4763- self .parameter_state = self . ps_required
4779+ if self .parameter_state is not ParamState . REQUIRED :
4780+ self .parameter_state = ParamState . REQUIRED
47644781 for p in self .function .parameters .values ():
47654782 p .group = - p .group
47664783
@@ -4793,17 +4810,18 @@ def state_parameter(self, line):
47934810 self .parse_special_symbol (line )
47944811 return
47954812
4796- if self .parameter_state in (self .ps_start , self .ps_required ):
4797- self .to_required ()
4798- elif self .parameter_state == self .ps_left_square_before :
4799- self .parameter_state = self .ps_group_before
4800- elif self .parameter_state == self .ps_group_before :
4801- if not self .group :
4813+ match self .parameter_state :
4814+ case ParamState .START | ParamState .REQUIRED :
48024815 self .to_required ()
4803- elif self .parameter_state in (self .ps_group_after , self .ps_optional ):
4804- pass
4805- else :
4806- fail ("Function " + self .function .name + " has an unsupported group configuration. (Unexpected state " + str (self .parameter_state ) + ".a)" )
4816+ case ParamState .LEFT_SQUARE_BEFORE :
4817+ self .parameter_state = ParamState .GROUP_BEFORE
4818+ case ParamState .GROUP_BEFORE :
4819+ if not self .group :
4820+ self .to_required ()
4821+ case ParamState .GROUP_AFTER | ParamState .OPTIONAL :
4822+ pass
4823+ case st :
4824+ fail (f"Function { self .function .name } has an unsupported group configuration. (Unexpected state { st } .a)" )
48074825
48084826 # handle "as" for parameters too
48094827 c_name = None
@@ -4863,8 +4881,9 @@ def state_parameter(self, line):
48634881 name , legacy , kwargs = self .parse_converter (parameter .annotation )
48644882
48654883 if not default :
4866- if self .parameter_state == self .ps_optional :
4867- fail ("Can't have a parameter without a default (" + repr (parameter_name ) + ")\n after a parameter with a default!" )
4884+ if self .parameter_state is ParamState .OPTIONAL :
4885+ fail (f"Can't have a parameter without a default ({ parameter_name !r} )\n "
4886+ "after a parameter with a default!" )
48684887 if is_vararg :
48694888 value = NULL
48704889 kwargs .setdefault ('c_default' , "NULL" )
@@ -4876,8 +4895,8 @@ def state_parameter(self, line):
48764895 if is_vararg :
48774896 fail ("Vararg can't take a default value!" )
48784897
4879- if self .parameter_state == self . ps_required :
4880- self .parameter_state = self . ps_optional
4898+ if self .parameter_state is ParamState . REQUIRED :
4899+ self .parameter_state = ParamState . OPTIONAL
48814900 default = default .strip ()
48824901 bad = False
48834902 ast_input = f"x = { default } "
@@ -5001,22 +5020,22 @@ def bad_node(self, node):
50015020
50025021 if isinstance (converter , self_converter ):
50035022 if len (self .function .parameters ) == 1 :
5004- if ( self .parameter_state != self . ps_required ) :
5023+ if self .parameter_state is not ParamState . REQUIRED :
50055024 fail ("A 'self' parameter cannot be marked optional." )
50065025 if value is not unspecified :
50075026 fail ("A 'self' parameter cannot have a default value." )
50085027 if self .group :
50095028 fail ("A 'self' parameter cannot be in an optional group." )
50105029 kind = inspect .Parameter .POSITIONAL_ONLY
5011- self .parameter_state = self . ps_start
5030+ self .parameter_state = ParamState . START
50125031 self .function .parameters .clear ()
50135032 else :
50145033 fail ("A 'self' parameter, if specified, must be the very first thing in the parameter block." )
50155034
50165035 if isinstance (converter , defining_class_converter ):
50175036 _lp = len (self .function .parameters )
50185037 if _lp == 1 :
5019- if ( self .parameter_state != self . ps_required ) :
5038+ if self .parameter_state is not ParamState . REQUIRED :
50205039 fail ("A 'defining_class' parameter cannot be marked optional." )
50215040 if value is not unspecified :
50225041 fail ("A 'defining_class' parameter cannot have a default value." )
@@ -5065,12 +5084,13 @@ def parse_special_symbol(self, symbol):
50655084 fail ("Function " + self .function .name + " uses '*' more than once." )
50665085 self .keyword_only = True
50675086 elif symbol == '[' :
5068- if self .parameter_state in (self .ps_start , self .ps_left_square_before ):
5069- self .parameter_state = self .ps_left_square_before
5070- elif self .parameter_state in (self .ps_required , self .ps_group_after ):
5071- self .parameter_state = self .ps_group_after
5072- else :
5073- fail ("Function " + self .function .name + " has an unsupported group configuration. (Unexpected state " + str (self .parameter_state ) + ".b)" )
5087+ match self .parameter_state :
5088+ case ParamState .START | ParamState .LEFT_SQUARE_BEFORE :
5089+ self .parameter_state = ParamState .LEFT_SQUARE_BEFORE
5090+ case ParamState .REQUIRED | ParamState .GROUP_AFTER :
5091+ self .parameter_state = ParamState .GROUP_AFTER
5092+ case st :
5093+ fail (f"Function { self .function .name } has an unsupported group configuration. (Unexpected state { st } .b)" )
50745094 self .group += 1
50755095 self .function .docstring_only = True
50765096 elif symbol == ']' :
@@ -5079,20 +5099,27 @@ def parse_special_symbol(self, symbol):
50795099 if not any (p .group == self .group for p in self .function .parameters .values ()):
50805100 fail ("Function " + self .function .name + " has an empty group.\n All groups must contain at least one parameter." )
50815101 self .group -= 1
5082- if self .parameter_state in (self .ps_left_square_before , self .ps_group_before ):
5083- self .parameter_state = self .ps_group_before
5084- elif self .parameter_state in (self .ps_group_after , self .ps_right_square_after ):
5085- self .parameter_state = self .ps_right_square_after
5086- else :
5087- fail ("Function " + self .function .name + " has an unsupported group configuration. (Unexpected state " + str (self .parameter_state ) + ".c)" )
5102+ match self .parameter_state :
5103+ case ParamState .LEFT_SQUARE_BEFORE | ParamState .GROUP_BEFORE :
5104+ self .parameter_state = ParamState .GROUP_BEFORE
5105+ case ParamState .GROUP_AFTER | ParamState .RIGHT_SQUARE_AFTER :
5106+ self .parameter_state = ParamState .RIGHT_SQUARE_AFTER
5107+ case st :
5108+ fail (f"Function { self .function .name } has an unsupported group configuration. (Unexpected state { st } .c)" )
50885109 elif symbol == '/' :
50895110 if self .positional_only :
50905111 fail ("Function " + self .function .name + " uses '/' more than once." )
50915112 self .positional_only = True
5092- # ps_required and ps_optional are allowed here, that allows positional-only without option groups
5113+ # REQUIRED and OPTIONAL are allowed here, that allows positional-only without option groups
50935114 # to work (and have default values!)
5094- if (self .parameter_state not in (self .ps_required , self .ps_optional , self .ps_right_square_after , self .ps_group_before )) or self .group :
5095- fail ("Function " + self .function .name + " has an unsupported group configuration. (Unexpected state " + str (self .parameter_state ) + ".d)" )
5115+ allowed = {
5116+ ParamState .REQUIRED ,
5117+ ParamState .OPTIONAL ,
5118+ ParamState .RIGHT_SQUARE_AFTER ,
5119+ ParamState .GROUP_BEFORE ,
5120+ }
5121+ if (self .parameter_state not in allowed ) or self .group :
5122+ fail (f"Function { self .function .name } has an unsupported group configuration. (Unexpected state { self .parameter_state } .d)" )
50965123 if self .keyword_only :
50975124 fail ("Function " + self .function .name + " mixes keyword-only and positional-only parameters, which is unsupported." )
50985125 # fixup preceding parameters
0 commit comments