mirror of
https://github.com/bingmann/2018-cpp-spirit-parsing.git
synced 2025-12-16 20:27:06 +03:00
Seven instructive examples how to use Boost Spirit
This commit is contained in:
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
*.o
|
||||
/regex
|
||||
/spirit1_simple
|
||||
/spirit2_grammar
|
||||
/spirit3_arithmetic
|
||||
/spirit4_struct
|
||||
/spirit5_ast
|
||||
/spirit6_ast
|
||||
/spirit7_html
|
||||
46
Makefile
Normal file
46
Makefile
Normal file
@@ -0,0 +1,46 @@
|
||||
# really simple Makefile
|
||||
|
||||
CXX=g++
|
||||
CXXFLAGS=-W -Wall -pedantic -std=c++14
|
||||
|
||||
PROGRAMS= \
|
||||
regex \
|
||||
spirit1_simple \
|
||||
spirit2_grammar \
|
||||
spirit3_arithmetic \
|
||||
spirit4_struct \
|
||||
spirit5_ast \
|
||||
spirit6_ast \
|
||||
spirit7_html
|
||||
|
||||
all: $(PROGRAMS)
|
||||
|
||||
clean:
|
||||
rm -f *.o $(PROGRAMS)
|
||||
|
||||
%.o: %.cpp
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
regex: regex.o
|
||||
$(CXX) $(CXXFLAGS) -o $@ $^ -lboost_regex
|
||||
|
||||
spirit1_simple: spirit1_simple.o
|
||||
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||
|
||||
spirit2_grammar: spirit2_grammar.o
|
||||
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||
|
||||
spirit3_arithmetic: spirit3_arithmetic.o
|
||||
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||
|
||||
spirit4_struct: spirit4_struct.o
|
||||
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||
|
||||
spirit5_ast: spirit5_ast.o
|
||||
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||
|
||||
spirit6_ast: spirit6_ast.o
|
||||
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||
|
||||
spirit7_html: spirit7_html.o
|
||||
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||
7
example.html
Normal file
7
example.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<h1>Example for C++ HTML Parser</h1>
|
||||
|
||||
Welcome to the example text for the HTML <b>snippet</b> parser,
|
||||
which can also interpret *Markdown* style.
|
||||
|
||||
Furthermore, the markup parser can read additional tags like <% func(a,5) %>
|
||||
which could then filled at evaluation with the result of C++ functions.
|
||||
128
regex.cpp
Normal file
128
regex.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
// Example how to use C++11 <regex> and Boost.Regex
|
||||
|
||||
#include <iostream>
|
||||
|
||||
/******************************************************************************/
|
||||
// use std::regex to find a date in a string
|
||||
|
||||
#include <regex>
|
||||
|
||||
void std_regex() {
|
||||
std::string str = "C++ Meetup on 2018-09-12 about String Parsing";
|
||||
|
||||
// simple regex match: "on ####-##-##"
|
||||
std::regex re1("on ([0-9]{4}-[0-9]{2}-[0-9]{2})");
|
||||
|
||||
if (std::regex_search(str, re1)) {
|
||||
std::cout << "std::regex_search() with re1: matched!" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << "std::regex_search() with re1: no match!" << std::endl;
|
||||
}
|
||||
|
||||
if (std::regex_match(str, re1)) {
|
||||
std::cout << "std::regex_match() with re1: matched!" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << "std::regex_match() with re1: no match!" << std::endl;
|
||||
}
|
||||
|
||||
// regex match and std::string captures
|
||||
std::smatch match;
|
||||
|
||||
if (std::regex_search(str, match, re1)) {
|
||||
std::cout << "std::regex_search() with re1: matched!" << std::endl
|
||||
<< " match.size() = " << match.size() << std::endl
|
||||
<< " match[0] = " << match[0] << std::endl
|
||||
<< " match[1] = " << match[1] << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << "std::regex_search() with re1: no match!" << std::endl;
|
||||
}
|
||||
|
||||
// Also: std::cmatch for const char* captures
|
||||
const char* cstr = "Hello on 2018-09-13";
|
||||
std::cmatch cmatch;
|
||||
|
||||
if (std::regex_search(cstr, cmatch, re1)) {
|
||||
std::cout << "std::regex_search() with re1: matched!" << std::endl
|
||||
<< " match.size() = " << cmatch.size() << std::endl
|
||||
<< " match[0] = " << cmatch[0] << std::endl
|
||||
<< " match[1] = " << cmatch[1] << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << "std::regex_search() with re1: no match!" << std::endl;
|
||||
}
|
||||
|
||||
// use regex_replace and construct a new string
|
||||
std::string result = std::regex_replace(str, re1, "TODAY");
|
||||
std::cout << "std::regex_replace() result = " << result << std::endl;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// alternative: use Boost.Regex
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
void boost_regex() {
|
||||
std::string str = "C++ Meetup on 2018-09-12 about String Parsing";
|
||||
|
||||
// simple regex match
|
||||
boost::regex re1("on ([0-9]{4}-[0-9]{2}-[0-9]{2})");
|
||||
|
||||
if (boost::regex_search(str, re1)) {
|
||||
std::cout << "boost::regex_search() with re1: matched!" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << "boost::regex_search() with re1: no match!" << std::endl;
|
||||
}
|
||||
|
||||
if (boost::regex_match(str, re1)) {
|
||||
std::cout << "boost::regex_match() with re1: matched!" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << "boost::regex_match() with re1: no match!" << std::endl;
|
||||
}
|
||||
|
||||
// regex match and std::string captures
|
||||
boost::smatch match;
|
||||
|
||||
if (boost::regex_search(str, match, re1)) {
|
||||
std::cout << "boost::regex_search() with re1: matched!" << std::endl
|
||||
<< " match.size() = " << match.size() << std::endl
|
||||
<< " match[0] = " << match[0] << std::endl
|
||||
<< " match[1] = " << match[1] << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << "boost::regex_search() with re1: no match!" << std::endl;
|
||||
}
|
||||
|
||||
// also: boost::cmatch for const char* captures, and regex_replace.
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// Note: I usually make a "compatibility" include which defines
|
||||
|
||||
#if (__cplusplus >= 201103L)
|
||||
|
||||
using std::regex_search;
|
||||
// ... and other symbols
|
||||
|
||||
#else
|
||||
|
||||
// or import from Boost
|
||||
using boost::regex_search;
|
||||
|
||||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int main()
|
||||
{
|
||||
std_regex();
|
||||
boost_regex();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
166
spirit1_simple.cpp
Normal file
166
spirit1_simple.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
// Example how to use Boost Spirit to parse plain integers and lists of integers
|
||||
//
|
||||
// test1() parses "5",
|
||||
// test2() parses "76131 Karlsruhe",
|
||||
// test3() parses "[12345,42,5,]"
|
||||
// test4() parses "[12345,42,5]"
|
||||
// test5() parses "[12345, 42, 5 ]"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
namespace qi = boost::spirit::qi;
|
||||
|
||||
/******************************************************************************/
|
||||
// First Example: parse a single integer
|
||||
|
||||
void test1()
|
||||
{
|
||||
std::string input = "12345";
|
||||
int out_int;
|
||||
|
||||
qi::parse(
|
||||
// input string (iterators)
|
||||
input.begin(), input.end(),
|
||||
// parser grammar
|
||||
qi::int_,
|
||||
// output fields
|
||||
out_int);
|
||||
|
||||
std::cout << "test1() parse result: "
|
||||
<< out_int << std::endl;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// Parse an integer followed by a space and a string
|
||||
|
||||
void test2()
|
||||
{
|
||||
std::string input = "76131 Karlsruhe";
|
||||
int out_int;
|
||||
std::string out_string;
|
||||
|
||||
qi::parse(
|
||||
// input string (iterators)
|
||||
input.begin(), input.end(),
|
||||
// parser grammar
|
||||
qi::int_ >> ' ' >> *qi::char_,
|
||||
// output fields
|
||||
out_int, out_string);
|
||||
|
||||
std::cout << "test2() parse result: "
|
||||
<< out_int << " " << out_string << std::endl;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// Parse a bracketed list of integers
|
||||
|
||||
void test3()
|
||||
{
|
||||
std::string input = "[12345,42,5,]";
|
||||
std::vector<int> out_int_list;
|
||||
|
||||
qi::parse(
|
||||
// input string (iterators)
|
||||
input.begin(), input.end(),
|
||||
// parser grammar
|
||||
'[' >> *(qi::int_ >> ',') >> ']',
|
||||
// output list
|
||||
out_int_list);
|
||||
|
||||
std::cout << "test3() parse result: size "
|
||||
<< out_int_list.size() << std::endl;
|
||||
for (const size_t &i : out_int_list)
|
||||
std::cout << i << std::endl;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// Parse a bracketed list of integers without last comma
|
||||
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
void test4(std::string input)
|
||||
{
|
||||
std::vector<int> out_int_list;
|
||||
|
||||
ParseOrDie(
|
||||
// input string
|
||||
input,
|
||||
// parser grammar with '%' operator
|
||||
'[' >> (qi::int_ % ',') >> ']',
|
||||
// output list
|
||||
out_int_list);
|
||||
|
||||
std::cout << "test4() parse result: size "
|
||||
<< out_int_list.size() << std::endl;
|
||||
for (const size_t &i : out_int_list)
|
||||
std::cout << i << std::endl;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// Parse a bracketed list of integers with spaces between symbols
|
||||
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
void test5(std::string input)
|
||||
{
|
||||
std::vector<int> out_int_list;
|
||||
|
||||
PhraseParseOrDie(
|
||||
// input string
|
||||
input,
|
||||
// parser grammar
|
||||
'[' >> (qi::int_ % ',') >> ']',
|
||||
// skip parser
|
||||
qi::space,
|
||||
// output list
|
||||
out_int_list);
|
||||
|
||||
std::cout << "test5() parse result: size "
|
||||
<< out_int_list.size() << std::endl;
|
||||
for (const size_t &i : out_int_list)
|
||||
std::cout << i << std::endl;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test4(argc >= 2 ? argv[1] : "[12345,42,5]");
|
||||
test5(argc >= 3 ? argv[2] : "[12345, 42, 5]");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
203
spirit2_grammar.cpp
Normal file
203
spirit2_grammar.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
// 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;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
88
spirit3_arithmetic.cpp
Normal file
88
spirit3_arithmetic.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
// Example how to use Boost Spirit to parse and _evaluate_ a simple arithmetic
|
||||
// grammar. Evaluation is added by amending rules with semantic actions.
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include <boost/spirit/include/phoenix_core.hpp>
|
||||
#include <boost/spirit/include/phoenix_operator.hpp>
|
||||
|
||||
namespace qi = boost::spirit::qi;
|
||||
|
||||
/******************************************************************************/
|
||||
// Arithmetic parser with semantic actions which calculate the arithmetic
|
||||
// expression's result
|
||||
|
||||
// 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 ArithmeticGrammar1 : public qi::grammar<
|
||||
std::string::const_iterator,
|
||||
// define grammar to return an integer ... which we will calculate from the
|
||||
// expression
|
||||
int(), qi::space_type>
|
||||
{
|
||||
public:
|
||||
using Iterator = std::string::const_iterator;
|
||||
|
||||
ArithmeticGrammar1() : ArithmeticGrammar1::base_type(start)
|
||||
{
|
||||
start =
|
||||
// first component: product, and transfer the result of product
|
||||
// (qi::_1) to the result of this rule (start, qi::_val).
|
||||
product [qi::_val = qi::_1]
|
||||
// zero or more components: add result of product (qi::_1) to the
|
||||
// result of this rule (qi::_val).
|
||||
>> *('+' >> product [qi::_val += qi::_1]);
|
||||
|
||||
// product is defined in same way as start, but with multiplication
|
||||
product = factor [qi::_val = qi::_1]
|
||||
>> *('*' >> factor [qi::_val *= qi::_1]);
|
||||
|
||||
// factor is either option, both return an int, and with "%=" is
|
||||
// equivalent to [qi::_val = qi::_1] in both cases.
|
||||
factor %= qi::int_ | group;
|
||||
|
||||
// group's result is identical to start. again "%=" is a shortcut
|
||||
group %= '(' >> start >> ')';
|
||||
}
|
||||
|
||||
// each rule also returns an integer
|
||||
qi::rule<Iterator, int(), qi::space_type> start, group, product, factor;
|
||||
};
|
||||
|
||||
void test1(std::string input)
|
||||
{
|
||||
int out_int;
|
||||
|
||||
PhraseParseOrDie(input, ArithmeticGrammar1(), qi::space, out_int);
|
||||
|
||||
std::cout << "test1() parse result: "
|
||||
<< out_int << std::endl;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
test1(argc >= 2 ? argv[1] : "1 + 2 * 3");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
151
spirit4_struct.cpp
Normal file
151
spirit4_struct.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
// Example how to use Boost Spirit to parse CSV data directly into a C++ struct
|
||||
//
|
||||
// This example is designed to read the file "stock_list.txt"
|
||||
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include <boost/spirit/include/phoenix.hpp>
|
||||
#include <boost/fusion/include/adapt_struct.hpp>
|
||||
|
||||
namespace qi = boost::spirit::qi;
|
||||
namespace phx = boost::phoenix;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// Our simple stock struct: two strings and a double.
|
||||
|
||||
struct Stock
|
||||
{
|
||||
std::string symbol;
|
||||
std::string name;
|
||||
double price;
|
||||
|
||||
// constructors
|
||||
Stock() { }
|
||||
Stock(std::string symbol, std::string name, double price)
|
||||
: symbol(symbol), name(name), price(price) { }
|
||||
|
||||
// and how to format it to cout
|
||||
friend std::ostream& operator << (std::ostream& os, const Stock& s)
|
||||
{
|
||||
return os << "[Stock"
|
||||
<< " symbol=" << std::quoted(s.symbol)
|
||||
<< " name=" << std::quoted(s.name)
|
||||
<< " price=" << s.price
|
||||
<< "]";
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
// First Grammar: use Boost Phoenix in semantic action to construct a Stock
|
||||
// object with parsed parameters
|
||||
|
||||
class StockGrammar1 : public qi::grammar<
|
||||
// new grammar, this time the result type is a "Stock" object!
|
||||
std::string::const_iterator, Stock()>
|
||||
{
|
||||
public:
|
||||
using Iterator = std::string::const_iterator;
|
||||
|
||||
StockGrammar1() : StockGrammar1::base_type(start)
|
||||
{
|
||||
// define name rule: returns all characters up to ';' as a string.
|
||||
name %= *(~qi::char_(';'));
|
||||
|
||||
// parse a CSV line, and construct Stock object using the three symbols
|
||||
// stored as qi::_1, .. qi::_3. Optionally allow a trailing ';'.
|
||||
start = (name >> ';' >> name >> ';' >> qi::double_ >> -(qi::lit(';')))
|
||||
[qi::_val = phx::construct<Stock>(qi::_1, qi::_2, qi::_3) ];
|
||||
}
|
||||
|
||||
// a helper rule which parser a name
|
||||
qi::rule<Iterator, std::string()> name;
|
||||
// rule which actually parses a CSV line containing the information
|
||||
qi::rule<Iterator, Stock()> start;
|
||||
};
|
||||
|
||||
void test1_stream(std::istream& input)
|
||||
{
|
||||
// function to read each line of input and parse it.
|
||||
std::string line;
|
||||
StockGrammar1 g;
|
||||
while (std::getline(input, line)) {
|
||||
Stock stock;
|
||||
ParseOrDie(line, g, stock);
|
||||
std::cout << stock << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// First Grammar: use Boost Fusion to instrument the Stock class and enable
|
||||
// automatic semantic actions
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
Stock,
|
||||
(std::string, symbol)
|
||||
(std::string, name)
|
||||
(double, price)
|
||||
)
|
||||
|
||||
class StockGrammar2
|
||||
: public qi::grammar<std::string::const_iterator, Stock()>
|
||||
{
|
||||
public:
|
||||
using Iterator = std::string::const_iterator;
|
||||
|
||||
StockGrammar2() : StockGrammar2::base_type(start)
|
||||
{
|
||||
name %= *(~qi::char_(';'));
|
||||
// parse CSV line, and let Boost Fusion automatically map results into
|
||||
// the Stock struct (this does not use the constructor).
|
||||
start %= name >> ';' >> name >> ';' >> qi::double_ >> -(qi::lit(';'));
|
||||
}
|
||||
|
||||
qi::rule<Iterator, std::string()> name;
|
||||
qi::rule<Iterator, Stock()> start;
|
||||
};
|
||||
|
||||
void test2_stream(std::istream& input)
|
||||
{
|
||||
std::string line;
|
||||
while (std::getline(input, line)) {
|
||||
Stock stock;
|
||||
ParseOrDie(line, StockGrammar2(), stock);
|
||||
std::cout << stock << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc >= 2) {
|
||||
std::ifstream in(argv[1]);
|
||||
test1_stream(in);
|
||||
}
|
||||
else {
|
||||
std::cout << "Reading stdin" << std::endl;
|
||||
test2_stream(std::cin);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
126
spirit5_ast.cpp
Normal file
126
spirit5_ast.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
// Example how to use Boost Spirit to construct an abstract syntax tree (AST)
|
||||
// for a simple arithmetic grammar and to evaluate expressions.
|
||||
//
|
||||
// The grammar accepts expressions like "1 + 2 * 3", constructs an AST and
|
||||
// evaluates it correctly.
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include <boost/spirit/include/phoenix.hpp>
|
||||
|
||||
namespace qi = boost::spirit::qi;
|
||||
namespace phx = boost::phoenix;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Utility 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 ASTNode
|
||||
{
|
||||
public:
|
||||
virtual double evaluate() = 0;
|
||||
virtual ~ASTNode() { }
|
||||
};
|
||||
|
||||
using ASTNodePtr = ASTNode*;
|
||||
|
||||
template <char Operator>
|
||||
class OperatorNode : public ASTNode
|
||||
{
|
||||
public:
|
||||
OperatorNode(const ASTNodePtr& left, const ASTNodePtr& right)
|
||||
: left(left), right(right) { }
|
||||
|
||||
double evaluate() {
|
||||
if (Operator == '+')
|
||||
return left->evaluate() + right->evaluate();
|
||||
else if (Operator == '*')
|
||||
return left->evaluate() * right->evaluate();
|
||||
}
|
||||
|
||||
~OperatorNode() {
|
||||
delete left;
|
||||
delete right;
|
||||
}
|
||||
|
||||
private:
|
||||
ASTNodePtr left, right;
|
||||
};
|
||||
|
||||
class ConstantNode : public ASTNode
|
||||
{
|
||||
public:
|
||||
ConstantNode(double value)
|
||||
: value(value) { }
|
||||
|
||||
double evaluate() {
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
double value;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
class ArithmeticGrammar1
|
||||
: public qi::grammar<std::string::const_iterator, ASTNodePtr(), qi::space_type>
|
||||
{
|
||||
public:
|
||||
using Iterator = std::string::const_iterator;
|
||||
|
||||
ArithmeticGrammar1() : ArithmeticGrammar1::base_type(start)
|
||||
{
|
||||
start = (product >> '+' >> start)
|
||||
[qi::_val = phx::new_<OperatorNode<'+'> >(qi::_1, qi::_2) ] |
|
||||
product [qi::_val = qi::_1];
|
||||
product = (factor >> '*' >> product)
|
||||
[qi::_val = phx::new_<OperatorNode<'*'> >(qi::_1, qi::_2) ] |
|
||||
factor [qi::_val = qi::_1];
|
||||
factor = group [qi::_val = qi::_1] |
|
||||
qi::int_ [qi::_val = phx::new_<ConstantNode>(qi::_1) ];
|
||||
group %= '(' >> start >> ')';
|
||||
}
|
||||
|
||||
qi::rule<Iterator, ASTNodePtr(), qi::space_type> start, group, product, factor;
|
||||
};
|
||||
|
||||
void test1(std::string input)
|
||||
{
|
||||
ASTNode* out_node;
|
||||
PhraseParseOrDie(input, ArithmeticGrammar1(), qi::space, out_node);
|
||||
|
||||
std::cout << "evaluate() = " << out_node->evaluate() << std::endl;
|
||||
delete out_node;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
test1(argc >= 2 ? argv[1] : "1 + 2 * 3");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
182
spirit6_ast.cpp
Normal file
182
spirit6_ast.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
// Example how to use Boost Spirit to construct an abstract syntax tree (AST)
|
||||
// for a simple arithmetic grammar and to evaluate expressions _with_ variables!
|
||||
//
|
||||
// The grammar accepts expressions like "y = 1 + 2 * x", constructs an AST and
|
||||
// evaluates it correctly. Non-assignment expression are also evaluated.
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include <boost/spirit/include/phoenix.hpp>
|
||||
|
||||
namespace qi = boost::spirit::qi;
|
||||
namespace phx = boost::phoenix;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Utility 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");
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// the variable value map
|
||||
std::map<std::string, double> variable_map;
|
||||
|
||||
class ASTNode
|
||||
{
|
||||
public:
|
||||
virtual double evaluate() = 0;
|
||||
virtual ~ASTNode() { }
|
||||
};
|
||||
|
||||
using ASTNodePtr = ASTNode*;
|
||||
|
||||
template <char Operator>
|
||||
class OperatorNode : public ASTNode
|
||||
{
|
||||
public:
|
||||
OperatorNode(const ASTNodePtr& left, const ASTNodePtr& right)
|
||||
: left(left), right(right) { }
|
||||
|
||||
double evaluate() {
|
||||
if (Operator == '+')
|
||||
return left->evaluate() + right->evaluate();
|
||||
else if (Operator == '*')
|
||||
return left->evaluate() * right->evaluate();
|
||||
}
|
||||
|
||||
~OperatorNode() {
|
||||
delete left;
|
||||
delete right;
|
||||
}
|
||||
|
||||
private:
|
||||
ASTNodePtr left, right;
|
||||
};
|
||||
|
||||
class ConstantNode : public ASTNode
|
||||
{
|
||||
public:
|
||||
ConstantNode(double value)
|
||||
: value(value) { }
|
||||
|
||||
double evaluate() {
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
double value;
|
||||
};
|
||||
|
||||
class VariableNode : public ASTNode
|
||||
{
|
||||
public:
|
||||
VariableNode(std::string identifier)
|
||||
: identifier(identifier) { }
|
||||
|
||||
double evaluate() {
|
||||
return variable_map[identifier];
|
||||
}
|
||||
|
||||
private:
|
||||
std::string identifier;
|
||||
};
|
||||
|
||||
class AssignmentNode : public ASTNode
|
||||
{
|
||||
public:
|
||||
AssignmentNode(std::string identifier, const ASTNodePtr& value)
|
||||
: identifier(identifier), value(value) { }
|
||||
|
||||
double evaluate() {
|
||||
double v = value->evaluate();
|
||||
variable_map[identifier] = v;
|
||||
return v;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string identifier;
|
||||
ASTNodePtr value;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
class ArithmeticGrammar1
|
||||
: public qi::grammar<std::string::const_iterator, ASTNodePtr(), qi::space_type>
|
||||
{
|
||||
public:
|
||||
using Iterator = std::string::const_iterator;
|
||||
|
||||
ArithmeticGrammar1() : ArithmeticGrammar1::base_type(start)
|
||||
{
|
||||
varname %= qi::alpha >> *qi::alnum;
|
||||
|
||||
start = (varname >> '=' >> term)
|
||||
[qi::_val = phx::new_<AssignmentNode>(qi::_1, qi::_2) ] |
|
||||
term [qi::_val = qi::_1];
|
||||
|
||||
term = (product >> '+' >> term)
|
||||
[qi::_val = phx::new_<OperatorNode<'+'> >(qi::_1, qi::_2) ] |
|
||||
product [qi::_val = qi::_1];
|
||||
product = (factor >> '*' >> product)
|
||||
[qi::_val = phx::new_<OperatorNode<'*'> >(qi::_1, qi::_2) ] |
|
||||
factor [qi::_val = qi::_1];
|
||||
factor = group [qi::_val = qi::_1] |
|
||||
varname [qi::_val = phx::new_<VariableNode>(qi::_1) ] |
|
||||
qi::int_ [qi::_val = phx::new_<ConstantNode>(qi::_1) ];
|
||||
group %= '(' >> term >> ')';
|
||||
}
|
||||
|
||||
qi::rule<Iterator, std::string(), qi::space_type> varname;
|
||||
qi::rule<Iterator, ASTNodePtr(), qi::space_type> start, term, group, product, factor;
|
||||
};
|
||||
|
||||
void test1(std::string input)
|
||||
{
|
||||
try {
|
||||
ASTNode* out_node;
|
||||
PhraseParseOrDie(input, ArithmeticGrammar1(), qi::space, out_node);
|
||||
|
||||
std::cout << "evaluate() = " << out_node->evaluate() << std::endl;
|
||||
delete out_node;
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
std::cout << "EXCEPTION: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int main()
|
||||
{
|
||||
// important variables
|
||||
variable_map["x"] = 42;
|
||||
|
||||
std::cout << "Reading stdin" << std::endl;
|
||||
|
||||
std::string line;
|
||||
while (std::getline(std::cin, line)) {
|
||||
test1(line);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
1032
spirit7_html.cpp
Normal file
1032
spirit7_html.cpp
Normal file
File diff suppressed because it is too large
Load Diff
80
stock_list.txt
Normal file
80
stock_list.txt
Normal file
@@ -0,0 +1,80 @@
|
||||
AAPL;Apple Inc.;221.68
|
||||
ABBV;AbbVie Inc.;92.89
|
||||
ABT;Abbott Laboratories;66.79
|
||||
ACN;Accenture plc;170.15
|
||||
ADBE;Adobe Systems Incorporated;267.58
|
||||
AMGN;Amgen Inc.;198.30
|
||||
AMZN;Amazon.com, Inc.;1976.44
|
||||
BA;The Boeing Company;353.36
|
||||
BABA;Alibaba Group Holding Limited;160.67
|
||||
BAC;Bank of America Corporation;30.50
|
||||
BBL;BHP Billiton plc;39.57
|
||||
BHP;BHP Billiton Limited;44.26
|
||||
BP;BP p.l.c.;43.24
|
||||
BRK-A;Berkshire Hathaway Inc.;322874.00
|
||||
BRK-B;Berkshire Hathaway Inc.;214.83
|
||||
BTI;British American Tobacco p.l.c.;49.01
|
||||
BUD;Anheuser-Busch InBev SA/NV;88.23
|
||||
C;Citigroup Inc.;70.81
|
||||
CHL;China Mobile Limited;48.40
|
||||
CMCSA;Comcast Corporation;35.94
|
||||
COST;Costco Wholesale Corporation;243.16
|
||||
CRM;salesforce.com, inc.;154.03
|
||||
CSCO;Cisco Systems, Inc.;46.61
|
||||
CVX;Chevron Corporation;116.79
|
||||
DIS;The Walt Disney Company;109.86
|
||||
DWDP;DowDuPont Inc.;69.65
|
||||
FB;Facebook, Inc.;162.30
|
||||
GE;General Electric Company;12.53
|
||||
GOOG;Alphabet Inc.;1159.74
|
||||
GOOGL;Alphabet Inc.;1168.81
|
||||
HD;The Home Depot, Inc.;212.08
|
||||
HON;Honeywell International Inc.;164.21
|
||||
HSBC;HSBC Holdings plc;42.85
|
||||
IBM;International Business Machines Corporation;147.02
|
||||
INTC;Intel Corporation;44.49
|
||||
IVV;iShares Core S&P 500 ETF;290.81
|
||||
JNJ;Johnson & Johnson;139.22
|
||||
JPM;JPMorgan Chase & Co.;113.63
|
||||
KO;The Coca-Cola Company;46.22
|
||||
LLY;Eli Lilly and Company;106.17
|
||||
MA;Mastercard Incorporated;213.10
|
||||
MCD;McDonald's Corporation;165.01
|
||||
MDT;Medtronic plc;95.92
|
||||
MMM;3M Company;214.29
|
||||
MO;Altria Group, Inc.;63.83
|
||||
MRK;Merck & Co., Inc.;69.80
|
||||
MSFT;Microsoft Corporation;110.72
|
||||
NFLX;Netflix, Inc.;362.39
|
||||
NKE;NIKE, Inc.;82.22
|
||||
NVDA;NVIDIA Corporation;265.14
|
||||
NVS;Novartis AG;84.61
|
||||
ORCL;Oracle Corporation;49.19
|
||||
PEP;PepsiCo, Inc.;113.61
|
||||
PFE;Pfizer Inc.;42.51
|
||||
PG;The Procter & Gamble Company;82.97
|
||||
PM;Philip Morris International Inc.;80.95
|
||||
PTR;PetroChina Company Limited;72.73
|
||||
PYPL;PayPal Holdings, Inc.;90.98
|
||||
QCOM;QUALCOMM Incorporated;70.50
|
||||
RDS-A;Royal Dutch Shell plc;64.50
|
||||
RDS-B;Royal Dutch Shell plc;66.51
|
||||
RY;Royal Bank of Canada;79.46
|
||||
SAP;SAP SE;120.68
|
||||
SNP;China Petroleum & Chemical Corporation;97.03
|
||||
T;AT&T Inc.;33.35
|
||||
TD;The Toronto-Dominion Bank;60.52
|
||||
TM;Toyota Motor Corporation;119.99
|
||||
TOT;TOTAL S.A.;62.47
|
||||
TSM;Taiwan Semiconductor Manufacturing Company Limited;44.09
|
||||
UL;Unilever PLC;56.29
|
||||
UN;Unilever N.V.;56.79
|
||||
UNH;UnitedHealth Group Incorporated;263.02
|
||||
UNP;Union Pacific Corporation;157.25
|
||||
UPS;United Parcel Service, Inc.;123.58
|
||||
UTX;United Technologies Corporation;133.96
|
||||
V;Visa Inc.;145.84
|
||||
VZ;Verizon Communications Inc.;55.01
|
||||
WFC;Wells Fargo & Company;56.04
|
||||
WMT;Walmart Inc.;96.26
|
||||
XOM;Exxon Mobil Corporation;83.33
|
||||
Reference in New Issue
Block a user