Following this code, I changed the struct to a class and added other copy constructors and overloaded the = operator. I also added functionality for the parser to spot newlines, tabs etc. However, the code does not run due some errors. I have a clue on how to fix it. Below is a snippet of the error message and the code.
***/main.cpp:130:24: required from ‘Parser::BNF<Iterator>::BNF() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >]’
****/main.cpp:156:52: required from here
/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:41:16: error: no type named ‘value_type’ in ‘struct boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false>’
41 | struct is_container_of_ranges
| ^~~~~~~~~~~~~~~~~~~~~~
......
***/main.cpp:130:24: required from ‘Parser::BNF<Iterator>::BNF() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >]’
***/main.cpp:156:52: required from here
/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:72:17: error: no matching function for call to ‘boost::spirit::traits::assign_to_attribute_from_iterators<boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false>, __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, void>::call(const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false>&, boost::spirit::traits::detail::is_container_of_ranges<boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false> >)’
72 | call(first, last, attr, detail::is_container_of_ranges<Attribute>());
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
namespace Ast {
enum class TermType {
literal, rule_name
};
class Term {
private:
std::string data;
TermType term_type;
public:
Term() = default;
void set_data(std::string const &data) {
this->data = data;
}
std::string const &get_data() const {
return data;
}
void set_term_type(TermType term_type) {
this->term_type = term_type;
}
TermType const &get_term_type() const {
return term_type;
}
bool operator==(const Term &term) {
if (!data.compare(term.data) && term_type == term.term_type)
return true;
return false;
}
Term &operator=(const Term &term) {
if (this != &term) {
data = term.data;
term_type = term.term_type;
}
return *this;
}
Term(const Term &term) {
data = term.data;
term_type = term.term_type;
}
};
class List : public std::vector<Term> {
// attributes not required for parsing
// constructor, copy constructor, = operator overloading
};
using Expression = std::vector<List>;
class Rule {
private:
Term name; // lhs
Expression rhs;
public:
void set_rule_name(const Term &rule_name) {
this->name = rule_name;
}
Term const &get_rule_name() const {
return name;
}
void set_expression(const Expression &expression) {
rhs = expression;
}
Expression const &get_expression() const {
return rhs;
}
// constructor, copy constructor, = operator overloading
};
using Syntax = std::list<Rule>;
}
BOOST_FUSION_ADAPT_ADT(Ast::Term,
(obj.get_data(), obj.set_data(val))
(obj.get_term_type(), obj.set_term_type(val)))
BOOST_FUSION_ADAPT_ADT(Ast::Rule,
(obj.get_rule_name(), obj.set_rule_name(val))
(obj.get_expression(), obj.set_expression(val)))
namespace Parser {
template<typename Iterator>
struct BNF : qi::grammar<Iterator, Ast::Syntax()> {
BNF()
: BNF::base_type(start) {
using namespace qi;
_blank = blank;
unesc_char.add
("\\a", '\a')
("\\b", '\b')
("\\f", '\f')
("\\n", '\n')
("\\r", '\r')
("\\t", '\t')
("\\v", '\v')
("\\\\", '\\')
("\\'", '\'')
("\\\"", '\"');
_skipper = blank | (eol >> !skip(_blank.alias())[_rule]);
start = skip(_skipper.alias())[_rule % +eol];
_rule = _rule_name >> "::=" >> _expression;
_expression = _list % '|';
_list = +_term;
_term = _literal >> attr(Ast::TermType::literal)
| _rule_name;
_literal = unesc_char | '"' >> *(_character - '"') >> '"'
| "'" >> *(_character - "'") >> "'";
_character = alnum | char_("\"'| !#$%&()*+,./:;>=<?@]\\^_`{}~[-");
_rule_name = '<' >> qi::raw[(alpha >> *(alnum | char_('-')))] >> '>'
>> attr(Ast::TermType::rule_name);
// clang-format on
BOOST_SPIRIT_DEBUG_NODES(
(_rule)(_expression)(_list)(_literal)(_character)(_rule_name))
}
private:
using Skipper = qi::rule<Iterator>;
Skipper _skipper, _blank;
qi::rule<Iterator, Ast::Syntax()> start;
qi::rule<Iterator, Ast::Rule(), Skipper> _rule;
qi::rule<Iterator, Ast::Expression(), Skipper> _expression;
qi::rule<Iterator, Ast::List(), Skipper> _list;
// lexemes
qi::rule<Iterator, Ast::Term()> _term;
qi::rule<Iterator, std::string()> _literal;
qi::rule<Iterator, Ast::Term()> _rule_name;
qi::rule<Iterator, char()> _character;
qi::symbols<char const, char const> unesc_char;
};
}
int main() {
Parser::BNF<std::string::const_iterator> const parser;
std::string const input = R"(<code> ::= <letter><digit> | <letter><digit><code>
<letter> ::= "a" | "b" | "c" | "d" | "e"
| "f" | "g" | "h" | "i"
<digit> ::= "0" | "1 \n 3" | "2" | "3 \t yy" |
"4"
)";
auto it = input.begin(), itEnd = input.end();
Ast::Syntax syntax;
if (parse(it, itEnd, parser, syntax)) {
for (auto &rule : syntax) {
std::cout << rule.get_rule_name().get_data() << " ::= ";
std::string sep;
for (auto &list : rule.get_expression()) {
std::cout << sep;
for (auto &term: list) { std::cout << term.get_data(); }
sep = " | ";
};
std::cout << "\n";
}
} else {
std::cout << "Failed\n";
}
if (it != itEnd)
std::cout << "Remaining: " << std::quoted(std::string(it, itEnd)) << "\n";
}