aboutsummaryrefslogtreecommitdiff
path: root/src/parse/Grammar.cpp
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2021-12-08 22:08:43 +0800
committercrupest <crupest@outlook.com>2021-12-08 22:08:43 +0800
commitc850d817cc0d8c2c80728f373f89dce91650023a (patch)
treefea43233ddc54fdf976d5eced8de9e6a80ef9634 /src/parse/Grammar.cpp
parent6230b3e2873f2114eead9f3f21ff817cd83058d2 (diff)
downloadcru-c850d817cc0d8c2c80728f373f89dce91650023a.tar.gz
cru-c850d817cc0d8c2c80728f373f89dce91650023a.tar.bz2
cru-c850d817cc0d8c2c80728f373f89dce91650023a.zip
...
Diffstat (limited to 'src/parse/Grammar.cpp')
-rw-r--r--src/parse/Grammar.cpp79
1 files changed, 78 insertions, 1 deletions
diff --git a/src/parse/Grammar.cpp b/src/parse/Grammar.cpp
index 34eb2dd4..9d486c38 100644
--- a/src/parse/Grammar.cpp
+++ b/src/parse/Grammar.cpp
@@ -4,6 +4,7 @@
#include <algorithm>
#include <iterator>
#include <unordered_map>
+#include <unordered_set>
namespace cru::parse {
Grammar::Grammar() {}
@@ -200,5 +201,81 @@ void Grammar::EliminateLeftRecursions() {
}
}
-void Grammar::LeftFactor() {}
+void Grammar::LeftFactor() {
+ auto left_production_map = GenerateLeftProductionMap();
+
+ for (auto nonterminal : nonterminals_) {
+ auto productions = left_production_map[nonterminal];
+ if (productions.size() <= 1) continue;
+
+ while (true) {
+ std::unordered_set<Production*> productions_with_longest_common_prefix;
+ std::vector<Symbol*> longest_common_prefix;
+
+ for (int i = 0; i < productions.size(); i++) {
+ for (int j = i + 1; j < productions.size(); j++) {
+ // Get common prefix of the two productions.
+ std::vector<Symbol*> common_prefix;
+ for (int k = 0; k < std::min(productions[i]->GetRight().size(),
+ productions[j]->GetRight().size());
+ k++) {
+ if (productions[i]->GetRight()[k] ==
+ productions[j]->GetRight()[k]) {
+ common_prefix.push_back(productions[i]->GetRight()[k]);
+ } else {
+ break;
+ }
+ }
+
+ if (common_prefix.size() ==
+ productions_with_longest_common_prefix.size()) {
+ if (std::equal(common_prefix.cend(), common_prefix.cend(),
+ longest_common_prefix.cbegin())) {
+ productions_with_longest_common_prefix.insert(productions[i]);
+ productions_with_longest_common_prefix.insert(productions[j]);
+ }
+ } else if (common_prefix.size() >
+ productions_with_longest_common_prefix.size()) {
+ longest_common_prefix = common_prefix;
+ productions_with_longest_common_prefix.clear();
+ productions_with_longest_common_prefix.insert(productions[i]);
+ productions_with_longest_common_prefix.insert(productions[j]);
+ }
+ }
+ }
+
+ if (longest_common_prefix.empty()) break;
+
+ for (auto p : productions_with_longest_common_prefix) {
+ RemoveProduction(p);
+ }
+
+ // Also update local list.
+ productions.erase(
+ std::remove_if(
+ productions.begin(), productions.end(),
+ [&productions_with_longest_common_prefix](Production* p) {
+ return productions_with_longest_common_prefix.count(p);
+ }),
+ productions.end());
+
+ auto helper_nonterminal =
+ CreateNonterminal(nonterminal->GetName() + u" (Left Factor Helper)");
+
+ for (auto p : productions_with_longest_common_prefix) {
+ auto right = p->GetRight();
+ right.erase(right.cbegin(),
+ right.cbegin() + longest_common_prefix.size());
+ CreateProduction(p->GetName() + u" (Left Factor Helper)",
+ helper_nonterminal, std::move(right));
+ }
+
+ auto new_right = longest_common_prefix;
+ new_right.push_back(helper_nonterminal);
+
+ CreateProduction(nonterminal->GetName() + u" (Left Factor)", nonterminal,
+ new_right);
+ }
+ }
+}
} // namespace cru::parse