Files
2018-cpp-spirit-parsing/spirit2_grammar.cpp
2018-09-13 11:11:32 +02:00

204 lines
5.8 KiB
C++

// Example how to use Boost Spirit to parse simple arithmetic expressions such
// as "1 + 2 * 3".
//
// test1() parses and accepts "1"
// test2() parses "1" and returns it in an integer variable.
// test3() parses "1+2*3" but only accepts it without calculating.
// test4() parses "1 + 2 * 3"
//
// Evaluation of the expression is added in spirit3_arithmetic.cpp
#include <iostream>
#include <iomanip>
#include <stdexcept>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
/******************************************************************************/
// Helper to run a parser, check for errors, and capture the results.
template <typename Parser, typename ... Args>
void ParseOrDie(const std::string& input, const Parser& p, Args&& ... args)
{
std::string::const_iterator begin = input.begin(), end = input.end();
bool ok = qi::parse(begin, end, p, std::forward<Args>(args) ...);
if (!ok || begin != end) {
std::cout << "Unparseable: "
<< std::quoted(std::string(begin, end)) << std::endl;
throw std::runtime_error("Parse error");
}
}
/******************************************************************************/
// First grammar example: parse a single integer
class ArithmeticGrammar1 : public qi::grammar<
// the string iterator to parse: can also be const char* or templated.
std::string::const_iterator>
{
public:
// string iterator to parse
using Iterator = std::string::const_iterator;
ArithmeticGrammar1()
// call base constructor and specify start symbol
: ArithmeticGrammar1::base_type(start)
{
// construct the grammar: just set "start" for now.
start = qi::int_;
}
// List of rule objects in the grammar. Templates just like qi::grammar.
qi::rule<Iterator> start;
};
void test1()
{
std::string input = "12345";
ArithmeticGrammar1 g;
ParseOrDie(input, g);
}
/******************************************************************************/
// Modify grammar to actually return an integer
class ArithmeticGrammar2 : public qi::grammar<
// the string iterator to parse: can also be const char* or templated.
std::string::const_iterator,
// return value of the grammar, written in function syntax!
int()>
{
public:
using Iterator = std::string::const_iterator;
ArithmeticGrammar2() : ArithmeticGrammar2::base_type(start)
{
start %= qi::int_;
}
// List of rule objects in the grammar. Each rule can have a return type.
qi::rule<Iterator, int()> start;
};
void test2()
{
std::string input = "12345";
int out_int;
// note that the grammar object does not contain any return values.
ParseOrDie(input, ArithmeticGrammar2(), out_int);
std::cout << "test2() parse result: "
<< out_int << std::endl;
}
/******************************************************************************/
// Let's make the grammar more interesting.
class ArithmeticGrammar3 : public qi::grammar<std::string::const_iterator, int()>
{
public:
using Iterator = std::string::const_iterator;
ArithmeticGrammar3() : ArithmeticGrammar3::base_type(start)
{
start = product >> *('+' >> product);
product = factor >> *('*' >> factor);
factor = qi::int_ | group;
group = '(' >> start >> ')';
}
// List of rule objects in the grammar. Now there are four rules and each
// returns an integer value.
qi::rule<Iterator, int()> start, group, product, factor;
};
void test3()
{
std::string input = "1+2*3";
int out_int;
ParseOrDie(input, ArithmeticGrammar3(), out_int);
std::cout << "test3() parse result: "
<< out_int << std::endl;
}
/******************************************************************************/
// Introduce error checking when running the arithmetic grammar and add a skip
// parser to jump over spaces.
// Helper to run a parser, check for errors, and capture the results.
template <typename Parser, typename Skipper, typename ... Args>
void PhraseParseOrDie(
const std::string& input, const Parser& p, const Skipper& s,
Args&& ... args)
{
std::string::const_iterator begin = input.begin(), end = input.end();
boost::spirit::qi::phrase_parse(
begin, end, p, s, std::forward<Args>(args) ...);
if (begin != end) {
std::cout << "Unparseable: "
<< std::quoted(std::string(begin, end)) << std::endl;
throw std::runtime_error("Parse error");
}
}
class ArithmeticGrammar4 : public qi::grammar<
// the string iterator to parse: can also be const char* or templated.
std::string::const_iterator,
// return value of the grammar, written in function syntax!
int(),
// the _type_ of the skip parser
qi::space_type>
{
public:
using Iterator = std::string::const_iterator;
ArithmeticGrammar4() : ArithmeticGrammar4::base_type(start)
{
start = product >> *('+' >> product);
product = factor >> *('*' >> factor);
factor = qi::int_ | group;
group = '(' >> start >> ')';
}
// as before, mirrors the template arguments of qi::grammar.
qi::rule<Iterator, int(), qi::space_type> start, group, product, factor;
};
void test4(std::string input)
{
int out_int;
PhraseParseOrDie(
// input string
input,
// grammar
ArithmeticGrammar4(),
// skip parser
qi::space,
// output variable
out_int);
std::cout << "test4() parse result: "
<< out_int << std::endl;
}
/******************************************************************************/
int main(int argc, char* argv[])
{
test1();
test2();
test3();
test4(argc >= 2 ? argv[1] : "1 + 2 * 3");
return 0;
}
/******************************************************************************/