Skip to content

Commit

Permalink
Check the multiple definition.
Browse files Browse the repository at this point in the history
  • Loading branch information
oo13 committed Jul 8, 2024
1 parent ed0f6ed commit 61b2f2f
Show file tree
Hide file tree
Showing 11 changed files with 313 additions and 73 deletions.
42 changes: 24 additions & 18 deletions include/tphrase/Generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ namespace tphrase {
\param [in] syntax The phrase syntax.
\note Only the phrase syntax that contains the nonterminal "main" can add.
\note The recursive reference to a nonterminal is not allowed.
\note All the errors in syntax is added.
\note get_error_message() will return an empty string if no errors are detected.
\note An empty generator is created if some errors are detected.
*/
Expand All @@ -64,6 +65,7 @@ namespace tphrase {
\param [in] start_condition The name of the nonterminal where is the start condition.
\note Only the phrase syntax that contains the start condition can add.
\note The recursive reference to a nonterminal is not allowed.
\note All the errors in syntax is added.
\note get_error_message() will return an empty string if no errors are detected.
\note An empty generator is created if some errors are detected.
*/
Expand Down Expand Up @@ -116,9 +118,10 @@ namespace tphrase {

/** Add a phrase syntax.
\param [in] syntax The phrase syntax to be copied and added.
\return true if no errors are detected.
\return true if no errors are detected and added.
\note Only the phrase syntax that contains the nonterminal "main" can be added.
\note The recursive reference to a nonterminal is not allowed.
\note All the errors in syntax is added.
\note No phrase syntax is added if some errors are detected.
*/
bool add(const Syntax &syntax);
Expand All @@ -134,9 +137,10 @@ namespace tphrase {
/** Add a phrase syntax.
\param [in] syntax The phrase syntax to be copied and added.
\param [in] start_condition The name of the nonterminal where is the start condition.
\return true if no errors are detected.
\return true if no errors are detected and added.
\note Only the phrase syntax that contains the start condition can be added.
\note The recursive reference to a nonterminal is not allowed.
\note All the errors in syntax is added.
\note No phrase syntax is added if some errors are detected.
*/
bool add(const Syntax &syntax, const std::string &start_condition);
Expand Down Expand Up @@ -274,46 +278,46 @@ namespace tphrase {

/** Add the assignments from a source Syntax.
\param [in] a The source syntax.
\return true if no errors are added.
\note The production rules for the existing nonterminals is overwritten if the source Syntax has the assignment for the same nonterminal.
\note The function causes no errors, but adds the error messages in the source syntax into this.
\return true if no errors are added from the source syntax.
\note The error message in the source syntax is added to this.
\note If the source syntax has the nonterminal that this already contains, then: (1) the nonterminal in the source syntax overwrites it, (2) an error message is added to this, (3) true is returned unless other error is added.
*/
bool add(const Syntax &a);
/** Add the assignments from a source Syntax.
\param [inout] a The source syntax. (moved)
\return true if no errors are added.
\note The production rules for the existing nonterminals is overwritten if the source Syntax has the assignment for the same nonterminal.
\note The function causes no errors, but adds the error messages in the source syntax into this.
\return true if no errors are added from the source syntax.
\note The error message in the source syntax is added to this.
\note If the source syntax has the nonterminal that this already contains, then: (1) the nonterminal in the source syntax overwrites it, (2) an error message is added to this, (3) true is returned unless other error is added.
*/
bool add(Syntax &&a);
/** Add the assignments from a phrase syntax.
\tparam T The type of an input iterator. The dereference of a value of T can be convertible to a value of char.
\tparam S The type of the end for T.
\param [inout] begin The iterator to point the beginning of the source text of a phrase syntax. (Universal reference; accessed by the reference or moved)
\param [inout] end The end iterator. (Universal reference; accessed by the reference or moved)
\return true if no errors are detected.
\return true if no parse errors are detected.
\note The instance doesn't use begin, end, and something referred by begin after returning from the function.
\note The production rules for the existing nonterminals is overwritten if the source Syntax has the assignment for the same nonterminal.
\note Some parse errors may be detected if src has errors.
\note No phrase syntax is added if some errors are detected.
\note No phrase syntax is added if some parse errors are detected.
\note If the source syntax has the nonterminal that this already contains, then: (1) the nonterminal in the source syntax overwrites it, (2) an error message is added to this, (3) true is returned unless some parse errors are detected.
\attention std::istream_iterator skips any white spaces by default, so you should configure the target input stream not to skip the spaces (stream.unsetf(std::ios_base::skipws)) if you want to use an istream_iterator.
*/
template<typename T, typename S> REQUIRES_CharInputIteratorConcept(T, S)
bool add(T &&begin, S &&end);
/** Add the assignments from a phrase syntax.
\param [in] src The source text of a phrase syntax.
\return true if no errors are detected.
\note The production rules for the existing nonterminals is overwritten if the source Syntax has the assignment for the same nonterminal.
\return true if no parse errors are detected.
\note Some parse errors may be detected if src has errors.
\note No phrase syntax is added if some errors are detected.
\note No phrase syntax is added if some parse errors are detected.
\note If the source syntax has the nonterminal that this already contains, then: (1) the nonterminal in the source syntax overwrites it, (2) an error message is added to this, (3) true is returned unless some parse errors are detected.
*/
bool add(const std::string &src);
/** Add the assignments from a phrase syntax.
\param [in] src The source text of a phrase syntax.
\return true if no errors are detected.
\note The production rules for the existing nonterminals is overwritten if the source Syntax has the assignment for the same nonterminal.
\return true if no parse errors are detected.
\note Some parse errors may be detected if src has errors.
\note No phrase syntax is added if some errors are detected.
\note No phrase syntax is added if some parse errors are detected.
\note If the source syntax has the nonterminal that this already contains, then: (1) the nonterminal in the source syntax overwrites it, (2) an error message is added to this, (3) true is returned unless some parse errors are detected.
*/
bool add(const char *src);

Expand All @@ -337,9 +341,11 @@ namespace tphrase {
Syntax(InputIteratorBase &it);
/** Add the assignments from a phrase syntax.
\param [inout] it InputIterator of the source text of a phrase syntax.
\return true if no errors are detected.
\return true if no parse errors are detected.
\note The instance doesn't use 'it' and something referred by 'it' after returning from the constructor.
\note Some parse errors may be detected if src has errors.
\note No phrase syntax is added if some parse errors are detected.
\note If the source syntax has the nonterminal that this already contains, then: (1) the nonterminal in the source syntax overwrites it, (2) an error message is added to this, (3) true is returned unless some parse errors are detected.
*/
bool add(InputIteratorBase &it);

Expand Down
2 changes: 1 addition & 1 deletion manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ It can use to create a common library with some assignments, but the number of t
## Overview
The phrase syntax is expressed by the 8bit plain text that UTF-8 can pass through. It may be problematic for gsub function, so the C++ coders may replace it by a gsub function that supports UTF-8, such as a function using [SRELL](https://www.akenotsuki.com/misc/srell/en/). (The unit of the column number in the error message is byte. TPhrase user cannot change it.)

The phrase syntax consists of assignments. The order of the assignments doesn't affect the generated text. The recursive reference is not allowed.
The phrase syntax consists of assignments. The order of the assignments doesn't affect the generated text. The recursive reference is not allowed. The multiple definition for a nonterminal occurs an error.

It needs a definition of the nonterminal where is the start condition to generate the phrase. It's "main" by default, and C++ coders can change it.

Expand Down
50 changes: 24 additions & 26 deletions src/DataSyntax.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,6 @@

#include "DataSyntax.h"

namespace {
/** Insert an item, but assign the item if it already exists.
\param [in] m The target map.
\param [inout] k The key. (moved)
\param [inout] r The value. (moved)
\note This function is equivalent of std::unordered_map::insert_to_assign() in C++17 or later.
*/
void
insert_or_assign(std::unordered_map<std::string, tphrase::DataProductionRule> &m,
std::string &&k,
tphrase::DataProductionRule &&r)
{
auto it = m.find(k);
if (it == m.end()) {
m.emplace(std::move(k), std::move(r));
} else {
it->second = std::move(r);
}
}
}

namespace tphrase {
DataSyntax::DataSyntax()
: assignments{},
Expand Down Expand Up @@ -129,17 +108,36 @@ namespace tphrase {
return it->second;
}

void DataSyntax::add(std::string &&nonterminal, DataProductionRule &&rule)
bool DataSyntax::add(std::string &&nonterminal,
DataProductionRule &&rule,
std::string &err_msg)
{
insert_or_assign(assignments, std::move(nonterminal), std::move(rule));
is_bound = false;
auto it = assignments.find(nonterminal);
if (it == assignments.end()) {
assignments.emplace(std::move(nonterminal), std::move(rule));
return true;
} else {
// This is a parse error and it needs no newline.
err_msg += "The nonterminal \"";
err_msg += nonterminal;
err_msg += "\" is already defined.";
return false;
}
}

void DataSyntax::add(DataSyntax &&syntax)
void DataSyntax::add(DataSyntax &&syntax, std::string &err_msg)
{
for (auto &it : syntax.assignments) {
std::string s{it.first};
insert_or_assign(assignments, std::move(s), std::move(it.second));
auto found = assignments.find(it.first);
if (found == assignments.end()) {
assignments.emplace(it.first, std::move(it.second));
} else {
found->second = std::move(it.second);
err_msg += "The nonterminal \"";
err_msg += it.first;
err_msg += "\" is already defined.\n";
}
}
is_bound = false;
}
Expand Down
9 changes: 7 additions & 2 deletions src/DataSyntax.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,19 @@ namespace tphrase {
/** Add a pair of a nonterminal and a production rule.
\param [inout] nonterminal The nonterminal. (moved)
\param [inout] rule The production rule to be assigned to the nonterminal. (moved)
\param [inout] err_msg The error message is added if an error is detected.
\return true if no errors are detected.
\note It has a side effect to make the instance the unbound state (although the object that was bound on this remains bound on it).
\note If this already contains nonterminal, then: (1) nonterminal and rule do NOT add to this, (2) an error message is added to err_msg, (3) false is returned.
*/
void add(std::string &&nonterminal, DataProductionRule &&rule);
bool add(std::string &&nonterminal, DataProductionRule &&rule, std::string &err_msg);
/** Add a set of the assignments.
\param [inout] syntax The syntax with the assignments to be added. (moved)
\param [inout] err_msg The error messages are added if some errors are detected.
\note It has a side effect to make the instance the unbound state (although the object that was bound on this remains bound on it).
\note If syntax has the nonterminal that this already contains, then: (1) the nonterminal in syntax overwrites it, (2) an error message is added to err_msg.
*/
void add(DataSyntax &&syntax);
void add(DataSyntax &&syntax, std::string &err_msg);

/** Try to bind the expansions on the nonterminals in this.
\param [in] start_condition The nonterminal where is the start condition.
Expand Down
15 changes: 13 additions & 2 deletions src/Generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ namespace tphrase {

/** The default constructor. */
Impl() = default;
/** The constructor to copy error messages.
\param [in] err The error messages.
*/
Impl(const std::string &err);
/** The constructor to move error messages.
\param [inout] err The error messages. (moved)
*/
Expand All @@ -61,6 +65,11 @@ namespace tphrase {
Impl &operator=(const Impl &a) = default;
};

Generator::Impl::Impl(const std::string &err)
: err_msg{err}, data{}
{
}

Generator::Impl::Impl(std::string &&err)
: err_msg{std::move(err)}, data{}
{
Expand All @@ -83,7 +92,7 @@ namespace tphrase {

Generator::Generator(const Syntax &syntax,
const std::string &start_condition)
: pimpl{new Impl}
: pimpl{new Impl{syntax.get_error_message()}}
{
pimpl->data.add(syntax.get_syntax_data(),
start_condition,
Expand Down Expand Up @@ -153,12 +162,14 @@ namespace tphrase {
bool Generator::add(const Syntax &syntax,
const std::string &start_condition)
{
const std::size_t prev_len{pimpl->err_msg.size()};
pimpl->err_msg += syntax.get_error_message();
if (!pimpl->data.add(syntax.get_syntax_data(),
start_condition,
pimpl->err_msg)) {
return false;
}
return true;
return prev_len == pimpl->err_msg.size();
}

bool Generator::add(Syntax &&syntax, const std::string &start_condition)
Expand Down
6 changes: 3 additions & 3 deletions src/Syntax.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ namespace tphrase {
if (!good) {
pimpl->err_msg += a.pimpl->err_msg;
}
pimpl->data.add(DataSyntax(a.pimpl->data));
pimpl->data.add(DataSyntax(a.pimpl->data), pimpl->err_msg);
return good;
}

Expand All @@ -139,7 +139,7 @@ namespace tphrase {
if (!good) {
pimpl->err_msg += a.pimpl->err_msg;
}
pimpl->data.add(std::move(a.pimpl->data));
pimpl->data.add(std::move(a.pimpl->data), pimpl->err_msg);
return good;
}

Expand Down Expand Up @@ -181,7 +181,7 @@ namespace tphrase {
DataSyntax data{parse(it, pimpl->err_msg)};
const bool good = prev_len == pimpl->err_msg.size();
if (good) {
pimpl->data.add(std::move(data));
pimpl->data.add(std::move(data), pimpl->err_msg);
}
return good;
}
Expand Down
6 changes: 5 additions & 1 deletion src/parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,11 @@ namespace {
if (op_type == ':') {
rule.equalize_chance(true);
}
syntax.add(std::move(nonterminal), std::move(rule));
std::string err_msg;
syntax.add(std::move(nonterminal), std::move(rule), err_msg);
if (!err_msg.empty()) {
throw_parse_error(it, err_msg);
}
} else {
throw_parse_error(it, "The end of the text or \"\\n\" is expected.");
}
Expand Down
Loading

0 comments on commit 61b2f2f

Please sign in to comment.