mirror of
https://github.com/davidhalter/parso.git
synced 2025-12-08 05:34:51 +08:00
Add some first f-string issues.
This commit is contained in:
@@ -220,7 +220,7 @@ class PythonFStringGrammar(Grammar):
|
||||
)
|
||||
return p.parse(tokens=tokens)
|
||||
|
||||
def parse_leaf(leaf, error_recovery=True):
|
||||
def parse_leaf(self, leaf, error_recovery=True):
|
||||
code = leaf._get_payload()
|
||||
return self.parse(code, error_recovery=True, start_pos=leaf.start_pos)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class _NormalizerMeta(type):
|
||||
|
||||
class Normalizer(use_metaclass(_NormalizerMeta)):
|
||||
def __init__(self, grammar, config):
|
||||
self._grammar = grammar
|
||||
self.grammar = grammar
|
||||
self._config = config
|
||||
self.issues = []
|
||||
|
||||
|
||||
@@ -254,7 +254,7 @@ class ErrorFinder(Normalizer):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ErrorFinder, self).__init__(*args, **kwargs)
|
||||
self._error_dict = {}
|
||||
self.version = self._grammar.version_info
|
||||
self.version = self.grammar.version_info
|
||||
|
||||
def initialize(self, node):
|
||||
def create_context(node):
|
||||
@@ -836,6 +836,54 @@ class _TryStmtRule(SyntaxRule):
|
||||
self.add_issue(default_except, message=self.message)
|
||||
|
||||
|
||||
@ErrorFinder.register_rule(type='string')
|
||||
class _FStringRule(SyntaxRule):
|
||||
_fstring_grammar = None
|
||||
message_empty = "f-string: empty expression not allowed" # f'{}'
|
||||
"f-string: single '}' is not allowed" # f'}'
|
||||
"f-string: expressions nested too deeply" # f'{1:{5:{3}}}'
|
||||
message_backslash = "f-string expression part cannot include a backslash" # f'{"\"}' or f'{"\\"}'
|
||||
message_comment = "f-string expression part cannot include '#'" # f'{#}'
|
||||
"f-string: unterminated string" # f'{"}'
|
||||
"f-string: mismatched '(', '{', or '['"
|
||||
"f-string: invalid conversion character: expected 's', 'r', or 'a'" # f'{1!b}'
|
||||
"f-string: unexpected end of string" # Doesn't really happen?!
|
||||
"f-string: expecting '}'" # f'{'
|
||||
|
||||
@classmethod
|
||||
def _load_grammar(cls):
|
||||
import parso
|
||||
|
||||
if cls._fstring_grammar is None:
|
||||
cls._fstring_grammar = parso.load_grammar(language='python-f-string')
|
||||
return cls._fstring_grammar
|
||||
|
||||
def is_issue(self, fstring):
|
||||
if 'f' not in fstring.string_prefix.lower():
|
||||
return
|
||||
|
||||
parsed = self._load_grammar().parse_leaf(fstring)
|
||||
for child in parsed.children:
|
||||
type = child.type
|
||||
if type == 'expression':
|
||||
self._check_expression(child.children[1])
|
||||
|
||||
def _check_expression(self, python_expr):
|
||||
value = python_expr.value
|
||||
if '\\' in value:
|
||||
self.add_issue(python_expr, message=self.message_backslash)
|
||||
return
|
||||
if '#' in value:
|
||||
self.add_issue(python_expr, message=self.message_comment)
|
||||
return
|
||||
# This is now nested parsing. We parsed the fstring and now
|
||||
# we're parsing Python again.
|
||||
module = self._normalizer.grammar.parse(value)
|
||||
parsed_expr = module.children[0]
|
||||
if parsed_expr.type == 'endmarker':
|
||||
self.add_issue(python_expr, message=self.message_empty)
|
||||
|
||||
|
||||
class _CheckAssignmentRule(SyntaxRule):
|
||||
def _check_assignment(self, node, is_deletion=False):
|
||||
error = None
|
||||
|
||||
@@ -186,5 +186,5 @@ class Parser(parser.BaseParser):
|
||||
|
||||
def convert_leaf(self, pgen_grammar, type, value, prefix, start_pos):
|
||||
# TODO this is so ugly.
|
||||
leaf_type = TokenNamespace.token_map[type]
|
||||
leaf_type = TokenNamespace.token_map[type].lower()
|
||||
return TypedLeaf(leaf_type, value, start_pos, prefix)
|
||||
|
||||
@@ -203,7 +203,10 @@ class Leaf(NodeOrLeaf):
|
||||
|
||||
@utf8_repr
|
||||
def __repr__(self):
|
||||
return "<%s: %s>" % (type(self).__name__, self.value)
|
||||
value = self.value
|
||||
if not value:
|
||||
value = self.type
|
||||
return "<%s: %s>" % (type(self).__name__, value)
|
||||
|
||||
|
||||
class TypedLeaf(Leaf):
|
||||
|
||||
@@ -138,6 +138,12 @@ FAILING_EXAMPLES = [
|
||||
'def x():\n 1\n 2',
|
||||
'if 1:\nfoo',
|
||||
'if 1: blubb\nif 1:\npass\nTrue and False',
|
||||
|
||||
# f-strings
|
||||
'f"{}"',
|
||||
'f"{\\}"',
|
||||
#'f"{\'\\\'}"',
|
||||
'f"{#}"',
|
||||
]
|
||||
|
||||
GLOBAL_NONLOCAL_ERROR = [
|
||||
|
||||
Reference in New Issue
Block a user