Seven instructive examples how to use Boost Spirit

This commit is contained in:
Timo Bingmann
2018-09-13 11:08:06 +02:00
commit 7715e8827d
12 changed files with 2218 additions and 0 deletions

9
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

80
stock_list.txt Normal file
View 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