Created
May 13, 2016 21:03
-
-
Save eskil/c327683c5a971087cb773442ec399de5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//#define BOOST_SPIRIT_DEBUG | |
#include <boost/spirit.hpp> | |
#include <boost/spirit/actor.hpp> | |
#include <string> | |
#include <map> | |
#include <stdexcept> | |
#include <algorithm> | |
using namespace std; | |
using namespace boost::spirit; | |
struct route_t { | |
typedef enum { DEFAULT, NETWORK, NET, HOST } RouteType; | |
RouteType type; | |
std::string dest; | |
std::string nm; | |
std::string gw; | |
std::string metric; | |
std::string device; | |
std::string nic_number; | |
}; | |
typedef vector<route_t> route_list_t; | |
static uint_parser<unsigned, 10, 1, 3> uint3_p; | |
static uint_parser<unsigned, 10, 1, 2> uint2_p; | |
struct routes_grammar : public grammar<routes_grammar> { | |
routes_grammar (route_list_t &r) : routes (r) | |
{ } | |
template <typename ScannerT> | |
struct definition { | |
definition(const routes_grammar &self) | |
: routes (self.routes), default_type (route_t::DEFAULT), | |
network_type (route_t::NETWORK), net_type (route_t::NET), | |
host_type (route_t::HOST) | |
{ | |
r_dig = limit_d(0, 255)[uint3_p]; | |
r_dnm = limit_d(0, 32)[uint2_p]; | |
r_ip = (r_dig >> '.' >> r_dig >> '.' >> r_dig >> '.' >> r_dig)[assign_a (dest)]; | |
r_nm = r_dnm[assign_a (nm)]; | |
r_gw = r_ip[assign_a (route.gw)]; | |
r_dest_net = r_ip >> '/' >> r_nm[assign_a (route.dest, dest)][assign_a (route.nm, nm)]; | |
r_dest_host = r_ip[assign_a (route.dest, dest)][assign_a (route.nm, 32)]; | |
r_table = str_p ("table") >> "nic_" >> (+digit_p)[assign_a (route.nic_number)]; | |
r_metric = !(str_p ("metric") >> digit_p[assign_a (route.metric)]); | |
r_device = str_p ("dev") >> (+alpha_p >> digit_p >> !(ch_p ('.') >> +digit_p))[assign_a (route.device)]; | |
r_default = (str_p ("default") >> "via" >> r_gw >> r_device >> r_table >> r_metric) | |
[assign_a (route.type, default_type)] | |
[assign_a (route.dest, "0.0.0.0")] | |
; | |
r_network = (r_dest_net >> r_device >> r_table >> "scope" >> "link") | |
[assign_a (route.type, network_type)] | |
; | |
r_net = (r_dest_net >> "via" >> r_gw >> r_device >> r_table >> r_metric) | |
[assign_a (route.type, net_type)] | |
; | |
r_host = (r_dest_host >> "via" >> r_gw >> r_device >> r_table >> r_metric) | |
[assign_a (route.type, host_type)] | |
; | |
r_main = *(r_default | r_network | r_net | r_host)[push_back_a (routes, route)][assign_a (route, blank_route)]; | |
BOOST_SPIRIT_DEBUG_NODE (r_main); | |
BOOST_SPIRIT_DEBUG_NODE (r_host); | |
BOOST_SPIRIT_DEBUG_NODE (r_net); | |
BOOST_SPIRIT_DEBUG_NODE (r_network); | |
BOOST_SPIRIT_DEBUG_NODE (r_default); | |
BOOST_SPIRIT_DEBUG_NODE (r_device); | |
BOOST_SPIRIT_DEBUG_NODE (r_metric); | |
BOOST_SPIRIT_DEBUG_NODE (r_table); | |
BOOST_SPIRIT_DEBUG_NODE (r_dest_net); | |
BOOST_SPIRIT_DEBUG_NODE (r_dest_host); | |
BOOST_SPIRIT_DEBUG_NODE (r_gw); | |
BOOST_SPIRIT_DEBUG_NODE (r_nm); | |
BOOST_SPIRIT_DEBUG_NODE (r_ip); | |
} | |
// Since rules are referred to by reference, they must live | |
// for the duration of anyone using them, hence declared in | |
// structure scope rather than insice definition. | |
rule<ScannerT> r_ip, r_table, r_metric, r_dig, r_gw; | |
rule<ScannerT> r_scope, r_device, r_nm, r_dnm, r_dest_net, r_dest_host; | |
rule<ScannerT> r_default, r_network, r_net, r_host; | |
rule<ScannerT> r_main; | |
route_t::RouteType default_type, network_type, net_type, host_type; | |
route_list_t &routes; | |
std::string metric, nm; | |
std::string dest, gw; | |
int nic; | |
string device; | |
route_t route; | |
route_t blank_route; | |
const rule<ScannerT>& start() const { return r_main; } | |
}; | |
route_list_t &routes; | |
}; | |
std::ostream& operator<< (std::ostream &os, const route_t &r) { | |
os << "Route for NIC " << r.nic_number << " of type " << r.type << " "; | |
switch (r.type) { | |
case route_t::DEFAULT: | |
os << "default via " << r.gw; | |
if (!r.metric.empty ()) os << " metric " << r.metric; | |
break; | |
case route_t::NETWORK: | |
os << "default network route"; | |
break; | |
case route_t::NET: | |
os << "network route to " << r.dest << "/" << r.nm << " via " << r.gw; | |
if (!r.metric.empty ()) os << " metric " << r.metric; | |
break; | |
case route_t::HOST: | |
os << "host route to " << r.dest << " via " << r.gw; | |
if (!r.metric.empty ()) os << " metric " << r.metric; | |
break; | |
default: | |
throw std::logic_error ("unknown route type"); | |
} | |
return os; | |
} | |
std::ostream& operator<< (std::ostream &os, const route_list_t &l) { | |
os << "there are " << l.size () << " routes\n"; | |
copy (l.begin (), l.end (), ostream_iterator<route_t>(os, "\n")); | |
return os; | |
} | |
template<typename Iterator> | |
route_list_t parse_route (Iterator begin, Iterator end) { | |
route_list_t routes; | |
routes_grammar parser (routes); | |
BOOST_SPIRIT_DEBUG_NODE (parser); | |
parse_info<Iterator> result = parse (begin, end, parser, space_p); | |
if (result.full) { | |
cout << "Parse hit " << result.hit | |
<< " full " << result.full | |
<< " length " << result.length | |
<< endl; | |
} else { | |
typedef typename Iterator::difference_type diff_t; | |
string snip (std::min (distance (result.stop, end), (diff_t)20), ' '); | |
copy (result.stop, result.stop + snip.capacity (), snip.begin ()); | |
cout << "Parse failed around '" << snip << "'" << endl; | |
exit (1); | |
} | |
return routes; | |
} | |
int main (int argc, char *argv[]) { | |
route_list_t routes; | |
routes_grammar parser (routes); | |
BOOST_SPIRIT_DEBUG_NODE (parser); | |
char *sample = "10.11.39.45 via 10.64.0.2 dev int0 table nic_0"; | |
parse_info<string::iterator> result = parse (sample, sample + strlen (sample), parser, space_p); | |
if (result.full) { | |
cout << "Parse hit " << result.hit | |
<< " full " << result.full | |
<< " length " << result.length | |
<< endl; | |
} else { | |
cout << "Parse failed around\n"; | |
} | |
string input; | |
while (!cin.eof ()) { | |
getline (cin, input); | |
parse_info<string::iterator> result = parse (input.begin (), input.end (), parser, space_p); | |
if (result.full) { | |
cout << "Parse hit " << result.hit | |
<< " full " << result.full | |
<< " length " << result.length | |
<< endl; | |
} else { | |
typedef string::iterator::difference_type diff_t; | |
string snip (std::min (distance (result.stop, input.end ()), (diff_t)20), ' '); | |
copy (result.stop, result.stop + snip.capacity (), snip.begin ()); | |
cout << "Parse failed around '" << snip << "'" << endl; | |
} | |
} | |
if (!routes.empty()) { | |
cout << routes; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment