Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Grammar/python.gram
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,12 @@ invalid_named_expression(memo):
| a=expression ':=' expression {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
a, "cannot use assignment expressions with %s", _PyPegen_get_expr_name(a)) }
| a=expression '&''&' b=expression {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. This is actually a different feature. You can even open a separate issue and PR for this (if there is no already open). Anyway, this is a nice improvement.

I suggest to add also cases for <> ("not equal" in Pascal and Python 2), "=<", "=>" and "=!" (typo in "<=", ">=" and "!="), "===" ("is" in JavaScript and PHP).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually there is already a PR for improving SyntaxError message of && and ||: #150906

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for review, great suggestion, I will add those~

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then let's focus on keywords in this PR.

RAISE_SYNTAX_ERROR_KNOWN_RANGE(
a, b, "invalid syntax '&&'. Use 'and' instead.") }
| a=expression '|''|' b=expression {
RAISE_SYNTAX_ERROR_KNOWN_RANGE(
a, b, "invalid syntax '||'. Use 'or' instead.") }
| a=NAME '=' b=bitwise_or !('='|':=') {
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "invalid syntax. Maybe you meant '==' or ':=' instead of '='?") }
| !(list|tuple|genexp|'True'|'None'|'False') a=bitwise_or b='=' bitwise_or !('='|':=') {
Expand Down
27 changes: 26 additions & 1 deletion Lib/traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -1485,8 +1485,12 @@ def _find_keyword_typos(self):
# Limit the number of possible matches to try
max_matches = 3
matches = []

hint = _get_cross_language_keyword_hint(wrong_name)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
hint = _get_cross_language_keyword_hint(wrong_name)
hint = _CROSS_LANGUAGE_KEYWORD_HINTS.get(wrong_name)

If it is only used once, and is marked as a "private" method, I would suggest calling the body directly

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about this. I still think a get method looks nicer..

if hint:
matches.append(hint)
if _suggestions is not None:
suggestion = _suggestions._generate_suggestions(keyword.kwlist, wrong_name)
suggestion = _suggestions._generate_suggestions(keyword.kwlist + keyword.softkwlist, wrong_name)
if suggestion:
matches.append(suggestion)
matches.extend(difflib.get_close_matches(wrong_name, keyword.kwlist, n=max_matches, cutoff=0.5))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
matches.extend(difflib.get_close_matches(wrong_name, keyword.kwlist, n=max_matches, cutoff=0.5))
matches.extend(difflib.get_close_matches(wrong_name, keyword.kwlist+keyword.softkwlist, n=max_matches, cutoff=0.5))

Maybe we should add softkwlist here too?

Expand Down Expand Up @@ -1787,6 +1791,20 @@ def print(self, *, file=None, chain=True, **kwargs):
})


# Cross-language keyword suggestions.
_CROSS_LANGUAGE_KEYWORD_HINTS = frozendict({
# C/C++ equivalents
'switch': 'match',
'delete': 'del',
# function define equivalents
'function': 'def',
'func': 'def',
Comment thread
serhiy-storchaka marked this conversation as resolved.
# null equivalents
'NULL': 'None',
'null': 'None',
'nil': 'None',
})

def _substitution_cost(ch_a, ch_b):
if ch_a == ch_b:
return 0
Expand Down Expand Up @@ -1866,6 +1884,13 @@ def _get_cross_language_hint(obj, wrong_name):
return None


def _get_cross_language_keyword_hint(wrong_name):
"""Check if wrong_name is a common keyword from another language
"""
hint = _CROSS_LANGUAGE_KEYWORD_HINTS.get(wrong_name)
return hint

This comment was marked as resolved.



def _get_safe___dir__(obj):
# Use obj.__dir__() to avoid a TypeError when calling dir(obj).
# See gh-131001 and gh-139933.
Expand Down
68 changes: 68 additions & 0 deletions Parser/parser.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading