/
spirit4_struct.cpp
151 lines (127 loc) · 4.5 KB
/
spirit4_struct.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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;
}
/******************************************************************************/