Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jefftrull/5514049 to your computer and use it in GitHub Desktop.
Save jefftrull/5514049 to your computer and use it in GitHub Desktop.
// An attempt to generically parse expressions like this:
// <token> <int> ;
// - <some-expr> ;
// - <some-expr> ;
// - <some-expr> ; <- exactly as many times as given by <int> above
// END <token>
// where <token> is a string, and <some-expr> is a rule with attribute A
// the synthesized attribute of the result should be a fusion sequence of A
// Furthermore the resulting grammar should check the number of <some-expr> and ensure it
// matches the supplied <int>, with (ideally) a suitable error message
// first experiment: use a grammar with a rule parameter. Jeroen Habraken says:
// jaafar, it should be able to accept the rule as an inherited attribute which you then pass to lazy()
// however, it must be a complete type, thus rule<I, T(), S> (S optional), i.e., T must be there at that point
#include <string>
#include <vector>
#include <iterator>
#include <iostream>
#include <algorithm>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/repository/include/qi_kwd.hpp>
#include <boost/spirit/repository/include/qi_keywords.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
// typedef for stream iterator we will use
typedef boost::spirit::istream_iterator Iter;
using namespace boost::spirit::qi;
using boost::spirit::qi::rule;
using boost::spirit::qi::locals;
using boost::spirit::qi::grammar;
template<typename Attr>
struct rpt_parser : grammar<Iter,
locals<int>,
std::vector<Attr>(std::string,
rule<Iter, Attr(), space_type>),
space_type>
{
rpt_parser(std::string const& kwd,
rule<Iter, Attr(), space_type> baserule)
: rpt_parser::base_type(rpt_stmt)
{
using namespace boost::spirit::qi;
using boost::spirit::repository::dkwd;
using boost::phoenix::push_back; // to store results in containers
using boost::phoenix::val; // to defer evaluation of parsers
rpt_stmt = dkwd(kwd)[omit[int_[_a = _1]]] > ';' // preamble
> repeat(_a)['-' > lazy(val(baserule))[push_back(_val, _1) > ';']] // repeated statements
> dkwd("END") > dkwd(kwd) ; // termination
}
rule<Iter,
locals<int>,
std::vector<Attr>(std::string, rule<Iter, Attr(), space_type>),
space_type> rpt_stmt;
};
struct test_result
{
std::vector<std::string> strings;
std::vector<int> ints;
};
BOOST_FUSION_ADAPT_STRUCT(
test_result,
(std::vector<std::string>, strings)
(std::vector<int>, ints)
)
struct test_parser : grammar<Iter, test_result(), space_type>
{
test_parser() : test_parser::base_type(start)
{
using namespace boost::spirit::qi;
start = rpt_parser<std::string>("WORDS", string) >> rpt_parser<int>("INTS", int_) ;
}
rule<Iter, test_result(), space_type> start;
};
int main()
{
std::stringstream testdata("WORDS 5;\n- ALPHA ;\n- BETA ;\n- GAMMA ;\n- DELTA ;\n- EPSILON ;\nEND WORDS\nINTS 3;\n- -1 ;\n- 17 ;\n- 3223 ;\nEND INTS\n");
testdata.unsetf(std::ios::skipws);
Iter beg(testdata), end;
test_parser tp;
test_result result;
if (!phrase_parse(beg, end, tp, space, result))
{
std::cerr << "failed to parse input" << std::endl;
return 1;
} else if (beg != end) {
std::cerr << "parse succeeded but not all input consumed" << std::endl;
std::cerr << "remaining: ";
std::copy(beg, end, std::ostream_iterator<char>(std::cout, ""));
return 1;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment