From 4033b516eea2fc07203648a87db038b3902a0a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 5 Jan 2026 14:11:41 +0100 Subject: [PATCH] add patch --- .envrc | 1 + .gitignore | 3 + 0001-add-to-parser.patch | 244 +++++++++++++++++++++++++++++++++++++++ flake.lock | 61 ++++++++++ flake.nix | 47 ++++++++ 5 files changed, 356 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 0001-add-to-parser.patch create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..01a019e --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake . --show-trace diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..24e5af8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +python +.direnv +result diff --git a/0001-add-to-parser.patch b/0001-add-to-parser.patch new file mode 100644 index 0000000..9e50d4c --- /dev/null +++ b/0001-add-to-parser.patch @@ -0,0 +1,244 @@ +From 074d19076df40ace10bc115f8154bfbb96526649 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= +Date: Mon, 5 Jan 2026 11:21:45 +0100 +Subject: [PATCH] add to parser + +--- + Grammar/Tokens | 1 + + Grammar/python.gram | 73 +++++++++++++++++++++++++++++------------ + Parser/action_helpers.c | 26 +++++++++++++++ + Parser/pegen.h | 1 + + 4 files changed, 80 insertions(+), 21 deletions(-) + +diff --git a/Grammar/Tokens b/Grammar/Tokens +index 0547e6ed08f..fd0ce412f6c 100644 +--- a/Grammar/Tokens ++++ b/Grammar/Tokens +@@ -59,6 +59,7 @@ RARROW '->' + ELLIPSIS '...' + COLONEQUAL ':=' + EXCLAMATION '!' ++PIPE '|>' + + OP + TYPE_IGNORE +diff --git a/Grammar/python.gram b/Grammar/python.gram +index 110136af81b..48cebe45d5b 100644 +--- a/Grammar/python.gram ++++ b/Grammar/python.gram +@@ -726,13 +726,13 @@ star_expressions[expr_ty]: + | star_expression + + star_expression[expr_ty] (memo): +- | '*' a=bitwise_or { _PyAST_Starred(a, Load, EXTRA) } ++ | '*' a=pipe { _PyAST_Starred(a, Load, EXTRA) } + | expression + + star_named_expressions[asdl_expr_seq*]: a[asdl_expr_seq*]=','.star_named_expression+ [','] { a } + + star_named_expression[expr_ty]: +- | '*' a=bitwise_or { _PyAST_Starred(a, Load, EXTRA) } ++ | '*' a=pipe { _PyAST_Starred(a, Load, EXTRA) } + | named_expression + + assignment_expression[expr_ty]: +@@ -767,13 +767,13 @@ inversion[expr_ty] (memo): + # -------------------- + + comparison[expr_ty]: +- | a=bitwise_or b=compare_op_bitwise_or_pair+ { ++ | a=pipe b=compare_op_bitwise_or_pair+ { + _PyAST_Compare( + a, + CHECK(asdl_int_seq*, _PyPegen_get_cmpops(p, b)), + CHECK(asdl_expr_seq*, _PyPegen_get_exprs(p, b)), + EXTRA) } +- | bitwise_or ++ | pipe + + compare_op_bitwise_or_pair[CmpopExprPair*]: + | eq_bitwise_or +@@ -787,21 +787,47 @@ compare_op_bitwise_or_pair[CmpopExprPair*]: + | isnot_bitwise_or + | is_bitwise_or + +-eq_bitwise_or[CmpopExprPair*]: '==' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, Eq, a) } ++eq_bitwise_or[CmpopExprPair*]: '==' a=pipe { _PyPegen_cmpop_expr_pair(p, Eq, a) } + noteq_bitwise_or[CmpopExprPair*]: + | (tok='!=' { _PyPegen_check_barry_as_flufl(p, tok) ? NULL : tok}) a=bitwise_or {_PyPegen_cmpop_expr_pair(p, NotEq, a) } +-lte_bitwise_or[CmpopExprPair*]: '<=' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, LtE, a) } +-lt_bitwise_or[CmpopExprPair*]: '<' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, Lt, a) } +-gte_bitwise_or[CmpopExprPair*]: '>=' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, GtE, a) } +-gt_bitwise_or[CmpopExprPair*]: '>' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, Gt, a) } +-notin_bitwise_or[CmpopExprPair*]: 'not' 'in' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, NotIn, a) } +-in_bitwise_or[CmpopExprPair*]: 'in' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, In, a) } +-isnot_bitwise_or[CmpopExprPair*]: 'is' 'not' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, IsNot, a) } +-is_bitwise_or[CmpopExprPair*]: 'is' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, Is, a) } ++lte_bitwise_or[CmpopExprPair*]: '<=' a=pipe { _PyPegen_cmpop_expr_pair(p, LtE, a) } ++lt_bitwise_or[CmpopExprPair*]: '<' a=pipe { _PyPegen_cmpop_expr_pair(p, Lt, a) } ++gte_bitwise_or[CmpopExprPair*]: '>=' a=pipe { _PyPegen_cmpop_expr_pair(p, GtE, a) } ++gt_bitwise_or[CmpopExprPair*]: '>' a=pipe { _PyPegen_cmpop_expr_pair(p, Gt, a) } ++notin_bitwise_or[CmpopExprPair*]: 'not' 'in' a=pipe { _PyPegen_cmpop_expr_pair(p, NotIn, a) } ++in_bitwise_or[CmpopExprPair*]: 'in' a=pipe { _PyPegen_cmpop_expr_pair(p, In, a) } ++isnot_bitwise_or[CmpopExprPair*]: 'is' 'not' a=pipe { _PyPegen_cmpop_expr_pair(p, IsNot, a) } ++is_bitwise_or[CmpopExprPair*]: 'is' a=pipe { _PyPegen_cmpop_expr_pair(p, Is, a) } + + # Bitwise operators + # ----------------- + ++pipe[expr_ty]: ++ | lhs=bitwise_or '|>' rhs=primary_nocall b=genexp { ++ _PyAST_Call(rhs, ++ _PyPegen_desugar_pipe( ++ CHECK(asdl_expr_seq*, (asdl_expr_seq*)_PyPegen_singleton_seq(p, b)), ++ lhs, ++ p->arena ++ ) ++ , NULL, ++ EXTRA ++ ) ++ } ++ | lhs=bitwise_or '|>' rhs=primary_nocall '(' arg=[arguments] ')' { ++ _PyAST_Call( ++ rhs, ++ _PyPegen_desugar_pipe( ++ (arg) ? ((expr_ty) arg)->v.Call.args : NULL, ++ lhs, ++ p->arena ++ ), ++ (arg) ? ((expr_ty) arg)->v.Call.keywords : NULL, ++ EXTRA ++ ) ++ } ++ | bitwise_or ++ + bitwise_or[expr_ty]: + | a=bitwise_or '|' b=bitwise_xor { _PyAST_BinOp(a, BitOr, b, EXTRA) } + | bitwise_xor +@@ -856,6 +882,11 @@ await_primary[expr_ty] (memo): + | 'await' a=primary { CHECK_VERSION(expr_ty, 5, "Await expressions are", _PyAST_Await(a, EXTRA)) } + | primary + ++primary_nocall[expr_ty]: ++ | a=primary_nocall '.' b=NAME { _PyAST_Attribute(a, b->v.Name.id, Load, EXTRA) } ++ | a=primary_nocall '[' b=slices ']' { _PyAST_Subscript(a, b, Load, EXTRA) } ++ | atom ++ + primary[expr_ty]: + | a=primary '.' b=NAME { _PyAST_Attribute(a, b->v.Name.id, Load, EXTRA) } + | a=primary b=genexp { _PyAST_Call(a, CHECK(asdl_expr_seq*, (asdl_expr_seq*)_PyPegen_singleton_seq(p, b)), NULL, EXTRA) } +@@ -1020,7 +1051,7 @@ dict[expr_ty]: + double_starred_kvpairs[asdl_seq*]: a=','.double_starred_kvpair+ [','] { a } + + double_starred_kvpair[KeyValuePair*]: +- | '**' a=bitwise_or { _PyPegen_key_value_pair(p, NULL, a) } ++ | '**' a=pipe { _PyPegen_key_value_pair(p, NULL, a) } + | kvpair + + kvpair[KeyValuePair*]: a=expression ':' b=expression { _PyPegen_key_value_pair(p, a, b) } +@@ -1098,7 +1129,7 @@ kwarg_or_double_starred[KeywordOrStarred*]: + # Generic targets + # --------------- + +-# NOTE: star_targets may contain *bitwise_or, targets may not. ++# NOTE: star_targets may contain *pipe, targets may not. + star_targets[expr_ty]: + | a=star_target !',' { a } + | a=star_target b=(',' c=star_target { c })* [','] { +@@ -1266,9 +1297,9 @@ 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=NAME '=' b=bitwise_or !('='|':=') { ++ | a=NAME '=' b=pipe !('='|':=') { + 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 !('='|':=') { ++ | !(list|tuple|genexp|'True'|'None'|'False') a=pipe b='=' pipe !('='|':=') { + RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "cannot assign to %s here. Maybe you meant '==' instead of '='?", + _PyPegen_get_expr_name(a)) } + +@@ -1334,7 +1365,7 @@ invalid_comprehension: + | ('[' | '{') a=star_named_expression b=',' for_if_clauses { + RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "did you forget parentheses around the comprehension target?") } + invalid_dict_comprehension: +- | '{' a='**' bitwise_or for_if_clauses '}' { ++ | '{' a='**' pipe for_if_clauses '}' { + RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "dict unpacking cannot be used in dict comprehension") } + invalid_parameters: + | a="/" ',' { +@@ -1397,7 +1428,7 @@ invalid_with_item: + RAISE_SYNTAX_ERROR_INVALID_TARGET(STAR_TARGETS, a) } + + invalid_for_if_clause: +- | 'async'? 'for' (bitwise_or (',' bitwise_or)* [',']) !'in' { ++ | 'async'? 'for' (pipe (',' pipe)* [',']) !'in' { + RAISE_SYNTAX_ERROR("'in' expected after for-loop variables") } + + invalid_for_target: +@@ -1531,12 +1562,12 @@ invalid_class_def_raw: + + invalid_double_starred_kvpairs: + | ','.double_starred_kvpair+ ',' invalid_kvpair +- | expression ':' a='*' bitwise_or { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "cannot use a starred expression in a dictionary value") } ++ | expression ':' a='*' pipe { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "cannot use a starred expression in a dictionary value") } + | expression a=':' &('}'|',') { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expression expected after dictionary key and ':'") } + invalid_kvpair: + | a=expression !(':') { + RAISE_ERROR_KNOWN_LOCATION(p, PyExc_SyntaxError, a->lineno, a->end_col_offset - 1, a->end_lineno, -1, "':' expected after dictionary key") } +- | expression ':' a='*' bitwise_or { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "cannot use a starred expression in a dictionary value") } ++ | expression ':' a='*' pipe { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "cannot use a starred expression in a dictionary value") } + | expression a=':' &('}'|',') {RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expression expected after dictionary key and ':'") } + 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..53887c4fb55 100644 +--- a/Parser/action_helpers.c ++++ b/Parser/action_helpers.c +@@ -1152,6 +1152,32 @@ _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) { ++ if (args == NULL) { ++ asdl_expr_seq *new_args = _Py_asdl_expr_seq_new(1, arena); ++ if (new_args == NULL) { ++ return NULL; ++ } ++ ++ asdl_seq_SET(new_args, 0, piped_lhs); ++ return new_args; ++ } ++ ++ Py_ssize_t args_len = asdl_seq_LEN(args); ++ asdl_expr_seq *new_args = _Py_asdl_expr_seq_new(args_len + 1, arena); ++ if (new_args == NULL) { ++ return NULL; ++ } ++ ++ asdl_seq_SET(new_args, 0, piped_lhs); ++ Py_ssize_t i = 0; ++ for (i = 0; i < args_len; i++) { ++ asdl_seq_SET(new_args, i + 1, asdl_seq_GET(args, i)); ++ } ++ ++ return new_args; ++} ++ + expr_ty _PyPegen_collect_call_seqs(Parser *p, asdl_expr_seq *a, asdl_seq *b, + 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 +--- 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); + 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); +-- +2.49.0 + diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..5fd6f52 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1767379071, + "narHash": "sha256-EgE0pxsrW9jp9YFMkHL9JMXxcqi/OoumPJYwf+Okucw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "fb7944c166a3b630f177938e478f0378e64ce108", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..dad22d4 --- /dev/null +++ b/flake.nix @@ -0,0 +1,47 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + outputs = + { + self, + nixpkgs, + flake-utils, + }: + flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = (import nixpkgs) { + inherit system; + }; + in + rec { + packages = { + python = pkgs.python315.overrideAttrs ( + finalAttrs: previousAttrs: { + patches = previousAttrs.patches ++ [ + ./0001-add-to-parser.patch + ]; + nativeBuildInputs = previousAttrs.nativeBuildInputs ++ [ + pkgs.python3 + ]; + postConfigure = '' + ${previousAttrs.postConfigure or ""} + make regen-pegen + make regen-token + ''; + } + ); + }; + devShells.default = + with pkgs; + mkShell { + buildInputs = [ ]; + packages = [ + packages.python + ]; + }; + } + ); +}