diff --git a/conftest.py b/conftest.py index 57b8588..f69ad9e 100644 --- a/conftest.py +++ b/conftest.py @@ -14,7 +14,7 @@ from parso.utils import parse_version_string collect_ignore = ["setup.py"] VERSIONS_2 = '2.6', '2.7' -VERSIONS_3 = '3.3', '3.4', '3.5', '3.6', '3.7' +VERSIONS_3 = '3.3', '3.4', '3.5', '3.6', '3.7', '3.8' @pytest.fixture(scope='session') @@ -155,3 +155,9 @@ def works_ge_py3(each_version): def works_ge_py35(each_version): version_info = parse_version_string(each_version) return Checker(each_version, version_info >= (3, 5)) + + +@pytest.fixture +def works_ge_py38(each_version): + version_info = parse_version_string(each_version) + return Checker(each_version, version_info >= (3, 8)) diff --git a/parso/python/grammar38.txt b/parso/python/grammar38.txt index 6aceb50..b5798e1 100644 --- a/parso/python/grammar38.txt +++ b/parso/python/grammar38.txt @@ -69,8 +69,8 @@ assert_stmt: 'assert' test [',' test] compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt async_stmt: 'async' (funcdef | with_stmt | for_stmt) -if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] -while_stmt: 'while' test ':' suite ['else' ':' suite] +if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite] +while_stmt: 'while' namedexpr_test ':' suite ['else' ':' suite] for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] try_stmt: ('try' ':' suite ((except_clause ':' suite)+ @@ -83,6 +83,7 @@ with_item: test ['as' expr] except_clause: 'except' [test ['as' NAME]] suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT +namedexpr_test: test [':=' test] test: or_test ['if' or_test 'else' test] | lambdef test_nocond: or_test | lambdef_nocond lambdef: 'lambda' [varargslist] ':' test @@ -108,7 +109,7 @@ atom: ('(' [yield_expr|testlist_comp] ')' | '[' [testlist_comp] ']' | '{' [dictorsetmaker] '}' | NAME | NUMBER | strings | '...' | 'None' | 'True' | 'False') -testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] ) +testlist_comp: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] ) trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME subscriptlist: subscript (',' subscript)* [','] subscript: test | [test] ':' [test] [sliceop] @@ -134,6 +135,7 @@ arglist: argument (',' argument)* [','] # multiple (test comp_for) arguments are blocked; keyword unpackings # that precede iterable unpackings are blocked; etc. argument: ( test [comp_for] | + test ':=' test | test '=' test | '**' test | '*' test ) diff --git a/parso/python/tokenize.py b/parso/python/tokenize.py index f2bde66..5b70d94 100644 --- a/parso/python/tokenize.py +++ b/parso/python/tokenize.py @@ -190,9 +190,13 @@ def _create_token_collection(version_info): Bracket = '[][(){}]' - special_args = [r'\r\n?', r'\n', r'[:;.,@]'] + special_args = [r'\r\n?', r'\n', r'[;.,@]'] if version_info >= (3, 0): special_args.insert(0, r'\.\.\.') + if version_info >= (3, 8): + special_args.insert(0, ":=?") + else: + special_args.insert(0, ":") Special = group(*special_args) Funny = group(Operator, Bracket, Special) diff --git a/test/test_parser.py b/test/test_parser.py index aa17b65..a2091b4 100644 --- a/test/test_parser.py +++ b/test/test_parser.py @@ -189,3 +189,7 @@ def test_no_error_nodes(each_version): check(child) check(parse("if foo:\n bar", version=each_version)) + + +def test_named_expression(works_ge_py38): + works_ge_py38.parse("(a := 1, a + 1)")