Skip to content

Commit

Permalink
grammar context
Browse files Browse the repository at this point in the history
  • Loading branch information
TheDeathlyCow committed Nov 21, 2023
1 parent 1f4ae7f commit 575174a
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 21 deletions.
28 changes: 25 additions & 3 deletions src/calyx/grammar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,30 @@ Grammar::generate(const String_t& start, ErrorHolder& errors) noexcept
return Result(*exp);
}

std::optional<Result>
Grammar::generate(
const std::map<String_t, std::vector<String_t>>& context,
ErrorHolder& errors
) noexcept
{
return generate(getOptions().fromString("start"), context, errors);
}

std::optional<Result>
Grammar::generate(
const String_t& start,
const std::map<String_t, std::vector<String_t>>& context,
ErrorHolder& errors
) noexcept
{
std::optional<Expansion> exp = _registry.evaluate(start, context, errors);
if (!exp || errors.hasError())
{
return {};
}
return Result(*exp);
}

void
Grammar::filters(const std::map<String_t, filters::Filter_t>& filters) noexcept
{
Expand All @@ -137,6 +161,4 @@ void Grammar::filter(String_t filterName, filters::Filter_t filter) noexcept
Options& Grammar::getOptions() const noexcept
{
return _registry.getOptions();
}


}
31 changes: 30 additions & 1 deletion src/calyx/grammar.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

namespace calyx
{

/**
* @brief Defines a grammar in calyx. Grammars are a set of mutually-recursive rules that are evaluated randomly in order to
* produce a text. Defines various constructors for configuring the generator, and methods for generating text.
Expand Down Expand Up @@ -214,6 +213,36 @@ namespace calyx
*/
[[nodiscard]] std::optional<Result> generate(const String_t& start, ErrorHolder& errors) noexcept;

/**
* @brief Randomly generates some text with dynamic context using this Grammar's rules, starting from the "start" rule.
*
* @param context The dynamic context is a set of rules that are used as a fallback if no other rule can be found for a name.
* Expansions are chosen with uniform random distribution.
* @param errors Error handler containing errors messages that may arise when generating text.
* @return Returns an optional that contains the randomly generated result. This optional contains a value if and only if
* errors does not have an error.
*/
[[nodiscard]] std::optional<Result> generate(
const std::map<String_t, std::vector<String_t>>& context,
ErrorHolder& errors
) noexcept;

/**
* @brief Randomly generates some text with dynamic context using this Grammar's rules, starting from a given rule.
*
* @param start The name of the rule to start generating from.
* @param context The dynamic context is a set of rules that are used as a fallback if no other rule can be found for a name.
* Expansions are chosen with uniform random distribution.
* @param errors Error handler containing errors messages that may arise when generating text.
* @return Returns an optional that contains the randomly generated result. This optional contains a value if and only if
* errors does not have an error.
*/
[[nodiscard]] std::optional<Result> generate(
const String_t& start,
const std::map<String_t, std::vector<String_t>>& context,
ErrorHolder& errors
) noexcept;

/**
* @brief Adds multiple filters to the grammar, as a map of filter names to filter functions.
*
Expand Down
2 changes: 1 addition & 1 deletion src/registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ Registry::evaluate(const String_t& startSymbol, ErrorHolder& errors)
}

std::optional<Expansion>
Registry::evaluate(const String_t& startSymbol, std::map<String_t, std::vector<String_t>> context, ErrorHolder& errors)
Registry::evaluate(const String_t& startSymbol, const std::map<String_t, std::vector<String_t>>& context, ErrorHolder& errors)
{
this->resetEvaluationContext();

Expand Down
28 changes: 23 additions & 5 deletions src/registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace calyx
{
/**
* @brief Central registry that tracks the rules of a grammar, as well as evaluation context like memoization, unique expansions,
* and evaluation mode.
* and filters.
*
* The registry stores all of the rules of a Context-Free Grammar (CFG), as well as some additional context. Consider the following example,
* which defines a grammar that can be used to generate strings that are palindromes over the alphabet {a, b}.
Expand Down Expand Up @@ -67,19 +67,37 @@ namespace calyx

Registry& operator=(Registry&& other) noexcept;

Options& getOptions() const;
Options& getOptions() const;

/**
* @brief Defines a rule with uniform expansions
*
* @param term Name of the rule
* @param production Uniform expansions
* @param errors Error holder, if an error occurs then the rule is not added to the registry
*/
void defineRule(String_t term, const std::vector<String_t>& production, ErrorHolder& errors);

/**
* @brief Defines a rule with weighted expansions
*
* @param term Name of the rule
* @param productions Map of expansions to weights
* @param errors Error holder, if an error occurs then the rule is not added to the registry
*/
void defineRule(String_t term, const std::map<String_t, double>& productions, ErrorHolder& errors);

void addFilter(String_t name, filters::Filter_t filter);

std::optional<const filters::Filter_t> getFilter(const String_t& name) const;

void defineRule(String_t term, const std::map<String_t, double>& productions, ErrorHolder& errors);

std::optional<Expansion> evaluate(const String_t& startSymbol, ErrorHolder& errors);

std::optional<Expansion> evaluate(const String_t& startSymbol, std::map<String_t, std::vector<String_t>> context, ErrorHolder& errors);
std::optional<Expansion> evaluate(
const String_t& startSymbol,
const std::map<String_t, std::vector<String_t>>& context,
ErrorHolder& errors
);

std::shared_ptr<Expansion> memoizeExpansion(const String_t& symbol, ErrorHolder& errors);

Expand Down
2 changes: 1 addition & 1 deletion test/production/weighted_branch_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ TEST_CASE("Branch with multi choice test")

TEST_CASE("Results rougly match weightings")
{
Registry registry = Registry(Options());
auto registry = Registry(Options(1234u));
Options& ops = registry.getOptions();
ErrorHolder errs;

Expand Down
12 changes: 6 additions & 6 deletions test/registry_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,21 @@ TEST_CASE("Evaluate recursive rules")
REQUIRE(exp->flatten(ops) == atom);
}

TEST_CASE("Evaluate rules with initialized context")
TEST_CASE("Evaluate rules with dynamic context")
{
Registry registry(std::make_shared<Options>(true));
const Options& ops = registry.getOptions();


String_t start = ops.fromString("start");
String_t prod = ops.fromString("{atom}");
String_t atom = ops.fromString("atom");
ErrorHolder errs;

registry.defineRule(start, std::vector { prod }, errs);
// define "start" -> "{atom}", without definition for atom
registry.defineRule(start, std::vector { ops.fromString("{atom}") }, errs);
REQUIRE_FALSE(errs.hasError());

// add definition for atom in dynamic context
std::map<String_t, std::vector<String_t>> context = {
{ atom, std::vector { atom } }
};
Expand All @@ -90,12 +91,11 @@ TEST_CASE("Evaluate rules with initialized context")
REQUIRE(exp->flatten(ops) == atom);
}

TEST_CASE("Evaluate only initialized context")
TEST_CASE("Evaluate rules only from dynamic context")
{
Registry registry(std::make_shared<Options>(true));
Options& ops = registry.getOptions();



String_t start = ops.fromString("start");
String_t prod = ops.fromString("{atom}");
String_t atom = ops.fromString("atom");
Expand Down
10 changes: 6 additions & 4 deletions test/result_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ using namespace calyx;

TEST_CASE("Wrap expression tree with result")
{
Result result = Result(Expansion(Exp::TEMPLATE, Expansion(Exp::ATOM, "A T O M")));
const Result result = Result(
Expansion(Exp::TEMPLATE, Expansion(Exp::ATOM, "A T O M"))
);

REQUIRE(Exp::TEMPLATE == result.getTree().getSymbol());
REQUIRE(Exp::ATOM == result.getTree().getTail()[0].getSymbol());
Expand All @@ -18,7 +20,7 @@ TEST_CASE("Wrap expression tree with result")

TEST_CASE("Flattens expression tree to string")
{
Expansion tripleAtomTree = Expansion(
const Expansion tripleAtomTree = Expansion(
Exp::TEMPLATE,
std::vector {
Expansion(Exp::ATOM, "O N E"),
Expand All @@ -27,9 +29,9 @@ TEST_CASE("Flattens expression tree to string")
}
);

Result result = Result(tripleAtomTree);
const Result result = Result(tripleAtomTree);

Options ops;
const Options ops;
std::string text = result.getText(ops);

REQUIRE("O N E | T W O" == text);
Expand Down

0 comments on commit 575174a

Please sign in to comment.