From 2963b08f7902f7fbde6efdf32096512364fe6e60 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 20 Aug 2014 22:53:52 -0700 Subject: [PATCH] Eliminate duplicates when constructing choice rules --- spec/compiler/rules/choice_spec.cc | 51 ++++++++++++++++++++++++++++++ src/compiler/rules/choice.cc | 23 ++++++++++++-- 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 spec/compiler/rules/choice_spec.cc diff --git a/spec/compiler/rules/choice_spec.cc b/spec/compiler/rules/choice_spec.cc new file mode 100644 index 00000000..36d1ddb0 --- /dev/null +++ b/spec/compiler/rules/choice_spec.cc @@ -0,0 +1,51 @@ +#include "compiler/compiler_spec_helper.h" +#include "compiler/rules/choice.h" + +using namespace rules; + +START_TEST + +describe("Choice", []() { + describe("constructing choices", [&]() { + it("eliminates duplicate members", [&]() { + auto rule = Choice::Build({ + seq({ sym("one"), sym("two") }), + sym("three"), + seq({ sym("one"), sym("two") }) + }); + + AssertThat(rule, EqualsPointer(choice({ + seq({ sym("one"), sym("two") }), + sym("three"), + }))); + }); + + it("eliminates duplicates within nested choices", [&]() { + auto rule = Choice::Build({ + seq({ sym("one"), sym("two") }), + Choice::Build({ + sym("three"), + seq({ sym("one"), sym("two") }) + }) + }); + + AssertThat(rule, EqualsPointer(choice({ + seq({ sym("one"), sym("two") }), + sym("three"), + }))); + }); + + it("doesn't construct a choice if there's only one unique member", [&]() { + auto rule = Choice::Build({ + sym("one"), + Choice::Build({ + sym("one"), + }) + }); + + AssertThat(rule, EqualsPointer(sym("one"))); + }); + }); +}); + +END_TEST diff --git a/src/compiler/rules/choice.cc b/src/compiler/rules/choice.cc index 548dcb48..bc6ff0ff 100644 --- a/src/compiler/rules/choice.cc +++ b/src/compiler/rules/choice.cc @@ -14,8 +14,27 @@ using std::dynamic_pointer_cast; Choice::Choice(const vector &elements) : elements(elements) {} -rule_ptr Choice::Build(const vector &elements) { - return make_shared(elements); +void add_choice_element(vector *vec, const rule_ptr new_rule) { + auto choice = dynamic_pointer_cast(new_rule); + if (choice.get()) { + for (auto &child : choice->elements) + add_choice_element(vec, child); + } else { + for (auto &el : *vec) + if (el->operator==(*new_rule)) + return; + vec->push_back(new_rule); + } +} + +rule_ptr Choice::Build(const vector &inputs) { + vector elements; + for (auto &el : inputs) + add_choice_element(&elements, el); + if (elements.size() == 1) + return elements.front(); + else + return make_shared(elements); } bool Choice::operator==(const Rule &rule) const {