Skip to content

Instantly share code, notes, and snippets.

@eskil
Created May 13, 2016 21:03
Show Gist options
  • Save eskil/c327683c5a971087cb773442ec399de5 to your computer and use it in GitHub Desktop.
Save eskil/c327683c5a971087cb773442ec399de5 to your computer and use it in GitHub Desktop.
//#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