+ # Patterns for _expect_expr()
+ _inner_expr_prefix_pat = re.compile(r"\{")
+ _inner_expr_pat = re.compile(r"[^}]+")
+ _inner_expr_suffix_pat = re.compile(r"\}")
+ _const_float_pat = re.compile(
+ r"[-+]?(?:(?:\d*\.\d+)|(?:\d+\.?))(?:[Ee][+-]?\d+)?\b"
+ )
+
+ # Parses an expression outside a `{`/`}` context.
+ #
+ # This function accepts:
+ #
+ # • A Python expression within `{` and `}`.
+ #
+ # • A Python name.
+ #
+ # • If `accept_const_int` is `True`: a constant integer, which may
+ # be negative if `allow_neg_int` is `True`.
+ #
+ # • If `accept_float` is `True`: a constant floating point number.
+ #
+ # Returns the stripped expression string and AST expression.
+ def _expect_expr(
+ self,
+ accept_const_int: bool = False,
+ allow_neg_int: bool = False,
+ accept_const_float: bool = False,
+ ):
+ begin_text_loc = self._text_loc
+
+ # Constant floating point number?
+ m = None
+
+ if accept_const_float:
+ m = self._try_parse_pat(self._const_float_pat)
+
+ if m is not None:
+ return self._ast_expr_from_str(m.group(0), begin_text_loc)
+
+ # Constant integer?
+ m = None
+
+ if accept_const_int:
+ m = self._try_parse_pat(self._const_int_pat)
+
+ if m is not None:
+ # Negative and allowed?
+ if m.group("neg") == "-" and not allow_neg_int:
+ _raise_error(
+ "Expecting a positive constant integer", begin_text_loc
+ )
+
+ expr_str = self._norm_const_int(m.group(0))
+ return self._ast_expr_from_str(expr_str, begin_text_loc)
+
+ # Name?
+ m = self._try_parse_pat(_py_name_pat)
+
+ if m is not None:
+ return self._ast_expr_from_str(m.group(0), begin_text_loc)
+
+ # Expect `{`
+ msg_accepted_parts = ["a name", "or `{`"]
+
+ if accept_const_float:
+ msg_accepted_parts.insert(0, "a constant floating point number")
+
+ if accept_const_int:
+ msg_pos = "" if allow_neg_int else "positive "
+ msg_accepted_parts.insert(0, "a {}constant integer".format(msg_pos))
+
+ if len(msg_accepted_parts) == 2:
+ msg_accepted = " ".join(msg_accepted_parts)
+ else:
+ msg_accepted = ", ".join(msg_accepted_parts)
+
+ self._expect_pat(
+ self._inner_expr_prefix_pat,
+ "Expecting {}".format(msg_accepted),
+ )
+
+ # Expect an expression
+ self._skip_ws()
+ expr_text_loc = self._text_loc
+ m = self._expect_pat(self._inner_expr_pat, "Expecting an expression")
+ expr_str = m.group(0)
+
+ # Expect `}`
+ self._skip_ws()
+ self._expect_pat(self._inner_expr_suffix_pat, "Expecting `}`")
+
+ return self._ast_expr_from_str(expr_str, expr_text_loc)
+