From 1c9882a2cb6e9cbd4ba2ec0f47866c6117641c63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 5 Jan 2026 18:27:49 +0100 Subject: [PATCH] underscores as placeholders --- 0001-add-to-parser.patch | 68 ++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/0001-add-to-parser.patch b/0001-add-to-parser.patch index 91b1b77..d75672a 100644 --- a/0001-add-to-parser.patch +++ b/0001-add-to-parser.patch @@ -1,4 +1,4 @@ -From d947b1d3160d8a6990f9b7bed476184271fedd9a Mon Sep 17 00:00:00 2001 +From 79b30daff39dc53c4d822250946e1667cae1798e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 5 Jan 2026 16:14:52 +0100 Subject: [PATCH] add to parser @@ -6,9 +6,9 @@ Subject: [PATCH] add to parser --- Grammar/Tokens | 1 + Grammar/python.gram | 77 ++++++++++++++++++++++++++++++----------- - Parser/action_helpers.c | 26 ++++++++++++++ + Parser/action_helpers.c | 62 +++++++++++++++++++++++++++++++++ Parser/pegen.h | 1 + - 4 files changed, 84 insertions(+), 21 deletions(-) + 4 files changed, 120 insertions(+), 21 deletions(-) diff --git a/Grammar/Tokens b/Grammar/Tokens index 0547e6ed08f..fd0ce412f6c 100644 @@ -23,7 +23,7 @@ index 0547e6ed08f..fd0ce412f6c 100644 OP TYPE_IGNORE diff --git a/Grammar/python.gram b/Grammar/python.gram -index 110136af81b..7afaa1c140e 100644 +index 110136af81b..30f7950d1a9 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -726,13 +726,13 @@ star_expressions[expr_ty]: @@ -94,9 +94,9 @@ index 110136af81b..7afaa1c140e 100644 + | lhs=pipe '|>' rhs=primary_nocall b=genexp { + _PyAST_Call(rhs, + _PyPegen_desugar_pipe( ++ p, + CHECK(asdl_expr_seq*, (asdl_expr_seq*)_PyPegen_singleton_seq(p, b)), -+ lhs, -+ p->arena ++ lhs + ) + , NULL, + EXTRA @@ -106,9 +106,9 @@ index 110136af81b..7afaa1c140e 100644 + _PyAST_Call( + rhs, + _PyPegen_desugar_pipe( ++ p, + (arg) ? ((expr_ty) arg)->v.Call.args : NULL, -+ lhs, -+ p->arena ++ lhs + ), + (arg) ? ((expr_ty) arg)->v.Call.keywords : NULL, + EXTRA @@ -195,16 +195,20 @@ index 110136af81b..7afaa1c140e 100644 invalid_starred_expression_unpacking: | a='*' expression '=' b=expression { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "cannot assign to iterable argument unpacking") } diff --git a/Parser/action_helpers.c b/Parser/action_helpers.c -index 50856686335..44d617f48cc 100644 +index 50856686335..3241c007aa8 100644 --- a/Parser/action_helpers.c +++ b/Parser/action_helpers.c -@@ -1152,6 +1152,32 @@ _PyPegen_get_last_comprehension_item(comprehension_ty comprehension) { +@@ -1152,6 +1152,68 @@ _PyPegen_get_last_comprehension_item(comprehension_ty comprehension) { return PyPegen_last_item(comprehension->ifs, expr_ty); } -+asdl_expr_seq * _PyPegen_desugar_pipe(asdl_expr_seq* args, expr_ty piped_lhs, PyArena* arena) { ++asdl_expr_seq * _PyPegen_desugar_pipe(Parser * p, asdl_expr_seq* args, expr_ty piped_lhs) { ++ // loop index ++ Py_ssize_t i = 0; ++ ++ // if the list was somehow null, replace it with a list only containing the piped argument + if (args == NULL) { -+ asdl_expr_seq *new_args = _Py_asdl_expr_seq_new(1, arena); ++ asdl_expr_seq *new_args = _Py_asdl_expr_seq_new(1, p->arena); + if (new_args == NULL) { + return NULL; + } @@ -213,14 +217,46 @@ index 50856686335..44d617f48cc 100644 + return new_args; + } + ++ // calculate the initial length + Py_ssize_t orig_args_len = asdl_seq_LEN(args); -+ asdl_expr_seq *new_args = _Py_asdl_expr_seq_new(orig_args_len + 1, arena); ++ ++ // look for a `_` to replace ++ Py_ssize_t underscore_index = -1; ++ for (i = 0; i < orig_args_len; i++) { ++ expr_ty arg = asdl_seq_GET(args, i); ++ ++ // if we see an underscore, count it ++ if ( ++ arg->kind == Name_kind ++ && PyUnicode_CompareWithASCIIString(arg->v.Name.id, "_") == 0 ++ ) { ++ // maybe this is the 2nd underscore, raise a syntax error ++ if (underscore_index != -1) { ++ return RAISE_SYNTAX_ERROR_KNOWN_RANGE(arg, arg, "only one `_` is allowed when piping"); ++ } ++ ++ underscore_index = i; ++ } ++ ++ assert(current_elem->kind == Constant_kind); ++ } ++ ++ // overwrite the `_` element if found ++ if (underscore_index != -1) { ++ asdl_seq_SET(args, underscore_index, piped_lhs); ++ return args; ++ } ++ ++ // otherwise, allocate a new expr seq of one item longer than the original ++ asdl_expr_seq *new_args = _Py_asdl_expr_seq_new(orig_args_len + 1, p->arena); + if (new_args == NULL) { + return NULL; + } + ++ // stick the piped element at the end (so at orig_args_len) ++ // which is usually 1 past the end but now we allocated one more element + asdl_seq_SET(new_args, orig_args_len, piped_lhs); -+ Py_ssize_t i = 0; ++ // and copy the rest of the elements. + for (i = 0; i < orig_args_len; i++) { + asdl_seq_SET(new_args, i, asdl_seq_GET(args, i)); + } @@ -232,14 +268,14 @@ index 50856686335..44d617f48cc 100644 int lineno, int col_offset, int end_lineno, int end_col_offset, PyArena *arena) { diff --git a/Parser/pegen.h b/Parser/pegen.h -index be5333eb268..8e109999b54 100644 +index be5333eb268..d9557494e6c 100644 --- a/Parser/pegen.h +++ b/Parser/pegen.h @@ -344,6 +344,7 @@ stmt_ty _PyPegen_class_def_decorators(Parser *, asdl_expr_seq *, stmt_ty); KeywordOrStarred *_PyPegen_keyword_or_starred(Parser *, void *, int); asdl_expr_seq *_PyPegen_seq_extract_starred_exprs(Parser *, asdl_seq *); asdl_keyword_seq *_PyPegen_seq_delete_starred_exprs(Parser *, asdl_seq *); -+asdl_expr_seq * _PyPegen_desugar_pipe(asdl_expr_seq* args, expr_ty piped_lhs, PyArena* arena); ++asdl_expr_seq * _PyPegen_desugar_pipe(Parser*, asdl_expr_seq*, expr_ty); expr_ty _PyPegen_collect_call_seqs(Parser *, asdl_expr_seq *, asdl_seq *, int lineno, int col_offset, int end_lineno, int end_col_offset, PyArena *arena);