Skip to content

Commit 5f9ce20

Browse files
committed
Use the same memo-blocking array for all left-recursive rules.
1 parent 43a0dea commit 5f9ce20

File tree

1 file changed

+41
-91
lines changed

1 file changed

+41
-91
lines changed

pegged/grammar.d

Lines changed: 41 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -113,35 +113,9 @@ string grammar(Memoization withMemo = Memoization.yes)(string definition)
113113
import pegged.introspection;
114114
import std.algorithm : canFind;
115115
GrammarInfo grammarInfo = grammarInfo(defAsParseTree.children[0]);
116-
string[][string] stoppers; // Keys are the rules that stop left-recursion and the
117-
// values are arrays of strings containing the corresponding
118-
// rules for which memoization needs to be blocked.
119-
120-
// Prints comment showing detected left-recursive cycles.
121-
string printLeftRecursiveCycles()
122-
{
123-
string result;
124-
foreach (cycle; grammarInfo.leftRecursiveCycles)
125-
{
126-
result ~= cycle[0];
127-
foreach (rule; cycle[1..$])
128-
result ~= " <- " ~ rule;
129-
result ~= "\n";
130-
}
131-
return result.length > 0 ? "/** Left-recursive cycles:\n" ~ result ~ "*/\n\n" : "";
132-
}
133-
134-
// Prints comment showing rules that stop left-recursive cycles and rules for which memoization is blocked during recursion.
135-
string printLeftRecursionStoppers()
136-
{
137-
import std.array: join;
138-
string result;
139-
foreach (stopper, rules; stoppers)
140-
result ~= stopper ~ ": " ~ rules.join(", ") ~ "\n";
141-
return result.length > 0 ?
142-
"/** Rules that stop left-recursive cycles, followed by rules for which\n"
143-
~ " * memoization is blocked during recursion:\n" ~ result ~ "*/\n\n" : "";
144-
}
116+
string[] stoppers; // Keys are the rules that stop left-recursion and the
117+
// values are arrays of strings containing the corresponding
118+
// rules for which memoization needs to be blocked.
145119

146120
/*
147121
I once considered that if two left-recursive cycles intersect, unbounded left-recursion
@@ -159,48 +133,34 @@ string grammar(Memoization withMemo = Memoization.yes)(string definition)
159133
if (!canFind(allLeftRecursiveRules, rule))
160134
allLeftRecursiveRules ~= rule;
161135
foreach (cycle; grammarInfo.leftRecursiveCycles)
162-
stoppers[cycle[0]] = allLeftRecursiveRules;
163-
// Analysis completed.
136+
stoppers ~= cycle[0];
164137

165-
/// Returns code to prevent memoization of incomplete matches during left-recursion through this rule.
166-
string blockMemoForLeftRecursion(string stopper)
138+
// Prints comment showing detected left-recursive cycles.
139+
string printLeftRecursiveCycles()
167140
{
168141
string result;
169-
foreach (rule; stoppers[stopper])
170-
result ~= " blockMemo_" ~ rule ~ "_atPos ~= p.end;\n";
171-
return result;
142+
foreach (cycle; grammarInfo.leftRecursiveCycles)
143+
{
144+
result ~= cycle[0];
145+
foreach (rule; cycle[1..$])
146+
result ~= " <- " ~ rule;
147+
result ~= "\n";
148+
}
149+
return result.length > 0 ? "/** Left-recursive cycles:\n" ~ result ~ "*/\n\n" : "";
172150
}
173151

174-
/// Returns code that enables memoization when left-recursion has completed.
175-
string unblockMemoForLeftRecursion(string stopper)
152+
// Prints comment showing rules that stop left-recursive cycles and rules for which memoization is blocked during recursion.
153+
string printLeftRecursionStoppers()
176154
{
155+
import std.array: join;
177156
string result;
178-
foreach (rule; stoppers[stopper])
179-
// TODO investigate if p.end is always the last element.
180-
result ~= " assert(blockMemo_" ~ rule ~ "_atPos.canFind(p.end));\n"
181-
~ " blockMemo_" ~ rule ~ "_atPos = remove(blockMemo_" ~ rule ~ "_atPos, countUntil(blockMemo_" ~ rule ~ "_atPos, p.end));\n";
182-
return result;
183-
}
184-
185-
/// If $(D_PARAM name) is part of a left-recursive cycle and not a stopping rule, code is
186-
// inserted to test for blocking and if blocked return with "$(D_PARAM code)(p)".
187-
string maybeBlockedMemo(string name, string code)
188-
{
189-
assert(name !in stoppers);
190-
foreach (cycle; stoppers)
191-
foreach (rule; cycle)
192-
if (rule == name)
193-
return
194-
" if (blockMemo_" ~ name ~ "_atPos.canFind(p.end))\n"
195-
~ " return " ~ code ~ "(p);\n";
196-
return "";
197-
}
198-
199-
/// Returns a Boolean expression whether $(D_PARAM rule) is not blocked.
200-
string shouldMemoLeftRecursion(string rule)
201-
{
202-
return "!blockMemo_" ~ rule ~ "_atPos.canFind(p.end)";
157+
foreach (stopper; stoppers)
158+
result ~= stopper ~ ": " ~ allLeftRecursiveRules.join(", ") ~ "\n";
159+
return result.length > 0 ?
160+
"/** Rules that stop left-recursive cycles, followed by rules for which\n"
161+
~ " * memoization is blocked during recursion:\n" ~ result ~ "*/\n\n" : "";
203162
}
163+
// Analysis completed.
204164

205165
string generateForgetMemo()
206166
{
@@ -227,25 +187,6 @@ string grammar(Memoization withMemo = Memoization.yes)(string definition)
227187
{
228188
string result;
229189

230-
// Variables holding the block-state.
231-
string generateBlockers()
232-
{
233-
string result;
234-
string[] visited = [];
235-
foreach (cycle; grammarInfo.leftRecursiveCycles)
236-
foreach (rule; cycle)
237-
if (!visited.canFind(rule))
238-
{
239-
visited ~= rule;
240-
result ~= "
241-
static size_t[] blockMemo_" ~ rule ~ "_atPos;";
242-
}
243-
if (result.length > 0)
244-
return "
245-
import std.algorithm: canFind, countUntil, remove;" ~ result;
246-
return result;
247-
}
248-
249190
switch (p.name)
250191
{
251192
case "Pegged":
@@ -273,7 +214,10 @@ string grammar(Memoization withMemo = Memoization.yes)(string definition)
273214
result ~= "
274215
import std.typecons:Tuple, tuple;
275216
static TParseTree[Tuple!(string, size_t)] memo;";
276-
result ~= generateBlockers();
217+
if (grammarInfo.leftRecursiveCycles.length > 0)
218+
result ~= "
219+
import std.algorithm: canFind, countUntil, remove;
220+
static size_t[] blockMemoAtPos;";
277221
}
278222

279223
result ~= "
@@ -517,7 +461,7 @@ string grammar(Memoization withMemo = Memoization.yes)(string definition)
517461
~ " {\n"
518462
~ " if(__ctfe)\n"
519463
~ " {\n"
520-
~ (shortName in stoppers ?
464+
~ (stoppers.canFind(shortName) ?
521465
" assert(false, \"" ~ shortName ~ " is left-recursive, which is not supported "
522466
~ "at compile-time. Consider using asModule().\");\n"
523467
:
@@ -526,7 +470,7 @@ string grammar(Memoization withMemo = Memoization.yes)(string definition)
526470
~ " }\n"
527471
~ " else\n"
528472
~ " {\n"
529-
~ (shortName in stoppers ?
473+
~ (stoppers.canFind(shortName) ?
530474
// This rule needs to prevent infinite left-recursion.
531475
" static TParseTree[size_t /*position*/] seed;\n"
532476
~ " if (auto s = p.end in seed)\n"
@@ -571,7 +515,7 @@ string grammar(Memoization withMemo = Memoization.yes)(string definition)
571515
~ " {\n"
572516
~ " if(__ctfe)\n"
573517
~ " {\n"
574-
~ (shortName in stoppers ?
518+
~ (stoppers.canFind(shortName) ?
575519
" assert(false, \"" ~ shortName ~ " is left-recursive, which is not supported "
576520
~ "at compile-time. Consider using asModule().\");\n"
577521
:
@@ -580,17 +524,17 @@ string grammar(Memoization withMemo = Memoization.yes)(string definition)
580524
~ " }\n"
581525
~ " else\n"
582526
~ " {\n"
583-
~ (shortName in stoppers ?
527+
~ (stoppers.canFind(shortName) ?
584528
// This rule needs to prevent infinite left-recursion.
585529
" static TParseTree[size_t /*position*/] seed;\n"
586530
~ " if (auto s = p.end in seed)\n"
587531
~ " return *s;\n"
588-
~ " if (" ~ shouldMemoLeftRecursion(shortName) ~ ")\n"
532+
~ " if (!blockMemoAtPos.canFind(p.end))\n"
589533
~ " if (auto m = tuple(" ~ innerName ~ ", p.end) in memo)\n"
590534
~ " return *m;\n"
591535
~ " auto current = fail(p);\n"
592536
~ " seed[p.end] = current;\n"
593-
~ blockMemoForLeftRecursion(shortName)
537+
~ " blockMemoAtPos ~= p.end;\n"
594538
~ " while (true)\n"
595539
~ " {\n"
596540
~ " auto result = " ~ code ~ "(p);\n"
@@ -612,14 +556,20 @@ string grammar(Memoization withMemo = Memoization.yes)(string definition)
612556
// care of by memo. Note that p.end remains constant for the course of recursion,
613557
// and the length of seed only grows when nested recursion occurs.
614558
~ " seed.remove(p.end);\n"
615-
~ unblockMemoForLeftRecursion(shortName)
559+
// TODO investigate if p.end is always the last element of blockMemoAtPos.
560+
~ " assert(blockMemoAtPos.canFind(p.end));\n"
561+
~ " blockMemoAtPos = blockMemoAtPos.remove(countUntil(blockMemoAtPos, p.end));\n"
616562
~ " memo[tuple(" ~ innerName ~ ", p.end)] = current;\n"
617563
~ " return current;\n"
618564
~ " }\n"
619565
~ " }\n"
620566
:
621567
// Possibly left-recursive rule, but infinite recursion is already prevented by another rule in the same cycle.
622-
maybeBlockedMemo(shortName, code)
568+
(allLeftRecursiveRules.canFind(shortName) ?
569+
" if (blockMemoAtPos.canFind(p.end))\n"
570+
~ " return " ~ code ~ "(p);\n"
571+
: ""
572+
)
623573
~ " if (auto m = tuple(" ~ innerName ~ ", p.end) in memo)\n"
624574
~ " return *m;\n"
625575
~ " else\n"

0 commit comments

Comments
 (0)