| 81 | method_name = method.__name__ |
| 82 | |
| 83 | def memoize_left_rec_wrapper(self: "Parser") -> T | None: |
| 84 | mark = self._mark() |
| 85 | key = mark, method_name, () |
| 86 | # Fast path: cache hit, and not verbose. |
| 87 | if key in self._cache and not self._verbose: |
| 88 | tree, endmark = self._cache[key] |
| 89 | self._reset(endmark) |
| 90 | return tree |
| 91 | # Slow path: no cache hit, or verbose. |
| 92 | verbose = self._verbose |
| 93 | fill = " " * self._level |
| 94 | if key not in self._cache: |
| 95 | if verbose: |
| 96 | print(f"{fill}{method_name} ... (looking at {self.showpeek()})") |
| 97 | self._level += 1 |
| 98 | |
| 99 | # For left-recursive rules we manipulate the cache and |
| 100 | # loop until the rule shows no progress, then pick the |
| 101 | # previous result. For an explanation why this works, see |
| 102 | # https://github.com/PhilippeSigaud/Pegged/wiki/Left-Recursion |
| 103 | # (But we use the memoization cache instead of a static |
| 104 | # variable; perhaps this is similar to a paper by Warth et al. |
| 105 | # (http://web.cs.ucla.edu/~todd/research/pub.php?id=pepm08). |
| 106 | |
| 107 | # Prime the cache with a failure. |
| 108 | self._cache[key] = None, mark |
| 109 | lastresult, lastmark = None, mark |
| 110 | depth = 0 |
| 111 | if verbose: |
| 112 | print(f"{fill}Recursive {method_name} at {mark} depth {depth}") |
| 113 | |
| 114 | while True: |
| 115 | self._reset(mark) |
| 116 | self.in_recursive_rule += 1 |
| 117 | try: |
| 118 | result = method(self) |
| 119 | finally: |
| 120 | self.in_recursive_rule -= 1 |
| 121 | endmark = self._mark() |
| 122 | depth += 1 |
| 123 | if verbose: |
| 124 | print( |
| 125 | f"{fill}Recursive {method_name} at {mark} depth {depth}: {result!s:.200} to {endmark}" |
| 126 | ) |
| 127 | if not result: |
| 128 | if verbose: |
| 129 | print(f"{fill}Fail with {lastresult!s:.200} to {lastmark}") |
| 130 | break |
| 131 | if endmark <= lastmark: |
| 132 | if verbose: |
| 133 | print(f"{fill}Bailing with {lastresult!s:.200} to {lastmark}") |
| 134 | break |
| 135 | self._cache[key] = lastresult, lastmark = result, endmark |
| 136 | |
| 137 | self._reset(lastmark) |
| 138 | tree = lastresult |
| 139 | |
| 140 | self._level -= 1 |