https://github.com/python/cpython/commit/ac86e12d747dacd7008cd55691b2944aaae3e6b1
commit: ac86e12d747dacd7008cd55691b2944aaae3e6b1
branch: main
author: Pablo Galindo Salgado <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-09-14T11:41:14-04:00
summary:

gh-153568: Stop re-walking memo lists when growing left-recursive rules 
(#153574)

The left-recursion driver now keeps a direct reference to its memo
entry and updates it in place on every iteration.

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-07-11-14-59-17.gh-issue-153568.leftrec.rst
M Parser/parser.c
M Parser/pegen.c
M Parser/pegen.h
M Tools/peg_generator/pegen/c_generator.py

diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-11-14-59-17.gh-issue-153568.leftrec.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-11-14-59-17.gh-issue-153568.leftrec.rst
new file mode 100644
index 000000000000000..8302aae4cb8ca2a
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-11-14-59-17.gh-issue-153568.leftrec.rst
@@ -0,0 +1,2 @@
+Speed up parsing of left-recursive rules by updating their memoization
+entries in place.
diff --git a/Parser/parser.c b/Parser/parser.c
index 800db5b490fea98..71cee7c00d4103f 100644
--- a/Parser/parser.c
+++ b/Parser/parser.c
@@ -4204,12 +4204,14 @@ dotted_name_rule(Parser *p)
     }
     int _mark = p->mark;
     int _resmark = p->mark;
+    Memo *_memo = _PyPegen_insert_memo_direct(p, _mark, dotted_name_type);
+    if (_memo == NULL) {
+        p->level--;
+        return NULL;
+    }
     while (1) {
-        int tmpvar_0 = _PyPegen_update_memo(p, _mark, dotted_name_type, _res);
-        if (tmpvar_0) {
-            p->level--;
-            return _res;
-        }
+        _memo->node = _res;
+        _memo->mark = p->mark;
         p->mark = _mark;
         void *_raw = dotted_name_raw(p);
         if (p->error_indicator) {
@@ -9646,12 +9648,14 @@ attr_rule(Parser *p)
     }
     int _mark = p->mark;
     int _resmark = p->mark;
+    Memo *_memo = _PyPegen_insert_memo_direct(p, _mark, attr_type);
+    if (_memo == NULL) {
+        p->level--;
+        return NULL;
+    }
     while (1) {
-        int tmpvar_1 = _PyPegen_update_memo(p, _mark, attr_type, _res);
-        if (tmpvar_1) {
-            p->level--;
-            return _res;
-        }
+        _memo->node = _res;
+        _memo->mark = p->mark;
         p->mark = _mark;
         void *_raw = attr_raw(p);
         if (p->error_indicator) {
@@ -13517,12 +13521,14 @@ bitwise_or_rule(Parser *p)
     }
     int _mark = p->mark;
     int _resmark = p->mark;
+    Memo *_memo = _PyPegen_insert_memo_direct(p, _mark, bitwise_or_type);
+    if (_memo == NULL) {
+        p->level--;
+        return NULL;
+    }
     while (1) {
-        int tmpvar_2 = _PyPegen_update_memo(p, _mark, bitwise_or_type, _res);
-        if (tmpvar_2) {
-            p->level--;
-            return _res;
-        }
+        _memo->node = _res;
+        _memo->mark = p->mark;
         p->mark = _mark;
         void *_raw = bitwise_or_raw(p);
         if (p->error_indicator) {
@@ -13658,12 +13664,14 @@ bitwise_xor_rule(Parser *p)
     }
     int _mark = p->mark;
     int _resmark = p->mark;
+    Memo *_memo = _PyPegen_insert_memo_direct(p, _mark, bitwise_xor_type);
+    if (_memo == NULL) {
+        p->level--;
+        return NULL;
+    }
     while (1) {
-        int tmpvar_3 = _PyPegen_update_memo(p, _mark, bitwise_xor_type, _res);
-        if (tmpvar_3) {
-            p->level--;
-            return _res;
-        }
+        _memo->node = _res;
+        _memo->mark = p->mark;
         p->mark = _mark;
         void *_raw = bitwise_xor_raw(p);
         if (p->error_indicator) {
@@ -13780,12 +13788,14 @@ bitwise_and_rule(Parser *p)
     }
     int _mark = p->mark;
     int _resmark = p->mark;
+    Memo *_memo = _PyPegen_insert_memo_direct(p, _mark, bitwise_and_type);
+    if (_memo == NULL) {
+        p->level--;
+        return NULL;
+    }
     while (1) {
-        int tmpvar_4 = _PyPegen_update_memo(p, _mark, bitwise_and_type, _res);
-        if (tmpvar_4) {
-            p->level--;
-            return _res;
-        }
+        _memo->node = _res;
+        _memo->mark = p->mark;
         p->mark = _mark;
         void *_raw = bitwise_and_raw(p);
         if (p->error_indicator) {
@@ -13921,12 +13931,14 @@ shift_expr_rule(Parser *p)
     }
     int _mark = p->mark;
     int _resmark = p->mark;
+    Memo *_memo = _PyPegen_insert_memo_direct(p, _mark, shift_expr_type);
+    if (_memo == NULL) {
+        p->level--;
+        return NULL;
+    }
     while (1) {
-        int tmpvar_5 = _PyPegen_update_memo(p, _mark, shift_expr_type, _res);
-        if (tmpvar_5) {
-            p->level--;
-            return _res;
-        }
+        _memo->node = _res;
+        _memo->mark = p->mark;
         p->mark = _mark;
         void *_raw = shift_expr_raw(p);
         if (p->error_indicator) {
@@ -14082,12 +14094,14 @@ sum_rule(Parser *p)
     }
     int _mark = p->mark;
     int _resmark = p->mark;
+    Memo *_memo = _PyPegen_insert_memo_direct(p, _mark, sum_type);
+    if (_memo == NULL) {
+        p->level--;
+        return NULL;
+    }
     while (1) {
-        int tmpvar_6 = _PyPegen_update_memo(p, _mark, sum_type, _res);
-        if (tmpvar_6) {
-            p->level--;
-            return _res;
-        }
+        _memo->node = _res;
+        _memo->mark = p->mark;
         p->mark = _mark;
         void *_raw = sum_raw(p);
         if (p->error_indicator) {
@@ -14268,12 +14282,14 @@ term_rule(Parser *p)
     }
     int _mark = p->mark;
     int _resmark = p->mark;
+    Memo *_memo = _PyPegen_insert_memo_direct(p, _mark, term_type);
+    if (_memo == NULL) {
+        p->level--;
+        return NULL;
+    }
     while (1) {
-        int tmpvar_7 = _PyPegen_update_memo(p, _mark, term_type, _res);
-        if (tmpvar_7) {
-            p->level--;
-            return _res;
-        }
+        _memo->node = _res;
+        _memo->mark = p->mark;
         p->mark = _mark;
         void *_raw = term_raw(p);
         if (p->error_indicator) {
@@ -14904,12 +14920,14 @@ primary_rule(Parser *p)
     }
     int _mark = p->mark;
     int _resmark = p->mark;
+    Memo *_memo = _PyPegen_insert_memo_direct(p, _mark, primary_type);
+    if (_memo == NULL) {
+        p->level--;
+        return NULL;
+    }
     while (1) {
-        int tmpvar_8 = _PyPegen_update_memo(p, _mark, primary_type, _res);
-        if (tmpvar_8) {
-            p->level--;
-            return _res;
-        }
+        _memo->node = _res;
+        _memo->mark = p->mark;
         p->mark = _mark;
         void *_raw = primary_raw(p);
         if (p->error_indicator) {
@@ -20067,12 +20085,14 @@ t_primary_rule(Parser *p)
     }
     int _mark = p->mark;
     int _resmark = p->mark;
+    Memo *_memo = _PyPegen_insert_memo_direct(p, _mark, t_primary_type);
+    if (_memo == NULL) {
+        p->level--;
+        return NULL;
+    }
     while (1) {
-        int tmpvar_9 = _PyPegen_update_memo(p, _mark, t_primary_type, _res);
-        if (tmpvar_9) {
-            p->level--;
-            return _res;
-        }
+        _memo->node = _res;
+        _memo->mark = p->mark;
         p->mark = _mark;
         void *_raw = t_primary_raw(p);
         if (p->error_indicator) {
diff --git a/Parser/pegen.c b/Parser/pegen.c
index e709031ae781598..003c505de0c4a2d 100644
--- a/Parser/pegen.c
+++ b/Parser/pegen.c
@@ -102,6 +102,17 @@ _PyPegen_insert_memo(Parser *p, int mark, int type, void 
*node)
     return 0;
 }
 
+// Like _PyPegen_insert_memo(), but returns the inserted Memo so callers
+// can update it in place without re-walking the token's memo list.
+Memo *
+_PyPegen_insert_memo_direct(Parser *p, int mark, int type)
+{
+    if (_PyPegen_insert_memo(p, mark, type, NULL) < 0) {
+        return NULL;
+    }
+    return p->tokens[mark]->memo;
+}
+
 // Like _PyPegen_insert_memo(), but updates an existing node if found.
 int
 _PyPegen_update_memo(Parser *p, int mark, int type, void *node)
diff --git a/Parser/pegen.h b/Parser/pegen.h
index 5ebf6aded852787..308fb0d62974097 100644
--- a/Parser/pegen.h
+++ b/Parser/pegen.h
@@ -148,6 +148,7 @@ PyObject *_PyPegen_get_memo_statistics(void);
 
 int _PyPegen_insert_memo(Parser *p, int mark, int type, void *node);
 int _PyPegen_update_memo(Parser *p, int mark, int type, void *node);
+Memo *_PyPegen_insert_memo_direct(Parser *p, int mark, int type);
 int _PyPegen_is_memoized(Parser *p, int type, void *pres);
 
 int _PyPegen_lookahead(int, void *(func)(Parser *), Parser *);
diff --git a/Tools/peg_generator/pegen/c_generator.py 
b/Tools/peg_generator/pegen/c_generator.py
index d9236dfb22835bd..812dd4a19d82f7b 100644
--- a/Tools/peg_generator/pegen/c_generator.py
+++ b/Tools/peg_generator/pegen/c_generator.py
@@ -572,11 +572,15 @@ def _set_up_rule_memoization(self, node: Rule, 
result_type: str) -> None:
             self.print("}")
             self.print("int _mark = p->mark;")
             self.print("int _resmark = p->mark;")
+            self.print(f"Memo *_memo = _PyPegen_insert_memo_direct(p, _mark, 
{node.name}_type);")
+            self.print("if (_memo == NULL) {")
+            with self.indent():
+                self.add_return("NULL")
+            self.print("}")
             self.print("while (1) {")
             with self.indent():
-                self.call_with_errorcheck_return(
-                    f"_PyPegen_update_memo(p, _mark, {node.name}_type, _res)", 
"_res"
-                )
+                self.print("_memo->node = _res;")
+                self.print("_memo->mark = p->mark;")
                 self.print("p->mark = _mark;")
                 self.print(f"void *_raw = {node.name}_raw(p);")
                 self.print("if (p->error_indicator) {")

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to