(C) Eskil Heyn Olsen 1995-2008. This file contains the major part of a tool to load, parse and do lookups in dive tables. An example dive table entry for surface interval rule ; E [ 0:10 0:55 1:58 3:25 6:35 ] 12:00 An example dive table entry for residual nitrogen time ; 40' [ 257 241 213 187 161 138 116 101 87 73 61 49 37 25 17 7 ] An example dive table entry for group lookup given depth and bottom time ; 140' { C 5 7 10 G 15(@10' 2) I 20(@10' 6) 25(@20' 2 14) 30(@20' 5 21) N 40(@30' 2 16 26) 50(@30' 6 24 44) Z 60(@30' 16 23 56) Z 70(@40' 4 19 32 68) Z 80(@40' 10 23 41 79) x 90(@50' 2 14 18 42 88) 120(@50' 12 14 36 56 120) 180(@60' 10 26 32 54 94 168) 240(@70' 8 28 34 50 78 124 187) 360(@80' 9 32 42 64 84 122 142 187) 480(@80' 31 44 59 100 114 122 142 187) 720(@90' 16 56 88 97 100 114 122 142 187) } this example is for a dive to 140'. It shows that the diver enters group C after 5 minutes, goes to D at 7, E at 10, then G at 15 and now has a 2 minute deco obligation at 10'. Then ie. at 120 minutes, the deco profile starts at with 12 minutes at 50', 14 minutes at 40', 36 minutes at 30' and so on. This file is not the complete implementation. //#define BOOST_SPIRIT_DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tables.h" // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . void test_lexical_cast () { using namespace std; using namespace boost; Group none; Group a ('A'); Group o ('O'); Group z ('Z'); Group x ('x'); BOOST_CHECK_EQUAL (lexical_cast (none), " "); BOOST_CHECK_EQUAL (lexical_cast (a), "A"); BOOST_CHECK_EQUAL (lexical_cast (o), "O"); BOOST_CHECK_EQUAL (lexical_cast (z), "Z"); BOOST_CHECK_EQUAL (lexical_cast (x), "x"); } boost::unit_test::test_suite* init_group_test_suite () { boost::unit_test::test_suite *suite = BOOST_TEST_SUITE ("class Group"); suite->add (BOOST_TEST_CASE (&test_lexical_cast)); return suite; } // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . struct adjust_dive_tables { struct adjust_rnts { struct adjust_rnt { adjust_rnt (const Depth &d, RNTMap &m) : depth (d), rntmap (m), delta (-1) { } void operator() (RNT &rnt) { delta ++; Depth d = depth + rnt.add_depth; if (d == depth) return; rnt.residual_nitrogen_time = rntmap[d][delta].residual_nitrogen_time; } const Depth &depth; RNTMap &rntmap; int delta; }; adjust_rnts (RNTMap &m) : rntmap (m) { } void operator() (RNTMap::value_type &vt) { const Depth &depth = vt.first; RNTs rnts = vt.second; for_each (rnts.begin (), rnts.end (), adjust_rnt (depth, rntmap)); } RNTMap &rntmap; }; void operator() (TablesCollection::value_type &vt) { // Adjust RNTs (all the ones that should be moved up 10') for_each (vt.second.rntmap.begin (), vt.second.rntmap.end (), adjust_rnts (vt.second.rntmap)); } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . struct check_dive_tables { struct check_surface_intervals { check_surface_intervals () : group ('A'), delta (2) { } ~check_surface_intervals () { assert (group == 'x'); } void operator() (const IntervalMap::value_type &vt) { using namespace boost; using namespace std; assert (group != 'x'); const Group &iv_group = vt.first; Intervals intervals = vt.second; // Make sure the groups are in order assert (group == iv_group); // Make sure there's enough entries (and no more) assert (intervals.size () == delta); // Make sure the intervals are sensible sort (intervals.begin (), intervals.end ()); assert (intervals == vt.second); ++delta; ++group; } Group group; unsigned int delta; }; struct check_rntmap { // check that a RNT list is ordered, except entries marked as no_group_change struct check_rnts { check_rnts () : residual_nitrogen_time (inf_time ()), seen_residual_nitrogen_time (false) { } void operator() (const RNT &rnt) { if (rnt.no_group_change) { assert (!seen_residual_nitrogen_time); return; } if (rnt.add_depth == Depth ()) { seen_residual_nitrogen_time = true; assert (residual_nitrogen_time >= rnt.residual_nitrogen_time); residual_nitrogen_time = rnt.residual_nitrogen_time; } } duration_t residual_nitrogen_time; bool seen_residual_nitrogen_time; }; check_rntmap () : depth () { } ~check_rntmap () { std::cout << "check there's only exceptional exposure tables after " << depth << "\n"; } void operator() (const RNTMap::value_type &vt) { assert (depth < vt.first); depth = vt.first; const RNTs &rnts = vt.second; assert (rnts.size () == 16); for_each (rnts.begin (), rnts.end (), check_rnts ()); } Depth depth; }; struct check_tables { struct check_table { check_table () { } void operator() (const TableRow &row) { assert (bottom_time < row.bottom_time ()); // TOD: check deco stops are at every 10' // TOD: check deco stop time is ascending (holds for most dive tables, not necessarily for all) bottom_time = row.bottom_time (); } Group rep_group; duration_t bottom_time; }; void operator() (const Tables::value_type &vt) { const Table &table = vt.second; for_each (table.begin (), table.end (), check_table ()); } }; void operator() (TablesCollection::value_type &vt) { for_each (vt.second.surface_intervals.begin (), vt.second.surface_intervals.end (), check_surface_intervals ()); for_each (vt.second.rntmap.begin (), vt.second.rntmap.end (), check_rntmap ()); for_each (vt.second.tables.begin (), vt.second.tables.end (), check_tables ()); } }; // ------------------------------------------------------------------------------------------ struct assign_unit_actor { assign_unit_actor (Depth::unit_t &t) : ref (t) { } template void operator() (It begin, It end) const { if (*begin == '\'' || *begin == 'f') ref = Depth::IMPERIAL; else ref = Depth::METRIC; } Depth::unit_t &ref; }; assign_unit_actor assign_unit_a (Depth::unit_t &t) { return assign_unit_actor (t); } struct compose_depth_actor { compose_depth_actor (Depth &d, Depth::depth_t &dn, Depth::unit_t &du) : ref (d), d_ref (dn), u_ref (du) { } template void operator() (It begin, It end) const { ref = Depth (d_ref, u_ref); } Depth &ref; Depth::depth_t &d_ref; Depth::unit_t &u_ref; }; compose_depth_actor compose_depth_a (Depth &d, Depth::depth_t &dn, Depth::unit_t &du) { return compose_depth_actor (d, dn, du); } struct assign_time_actor { assign_time_actor (duration_t &r) : ref (r) { } void operator() (int v) const { ref = minutes (v); } template void operator() (It begin, It end) const { ref = inf_time (); } duration_t &ref; }; assign_time_actor assign_time_a (duration_t &d) { return assign_time_actor (d); } struct compose_time_actor { compose_time_actor (duration_t &r, int &_h, int &_m) : ref (r), h (_h), m (_m) { } template void operator() (It begin, It end) const { ref = (hours (h) + minutes (m)); } duration_t &ref; int &h, &m; }; compose_time_actor compose_time_a (duration_t &d, int &hours, int &mins) { return compose_time_actor (d, hours, mins); } struct compose_interval_actor { compose_interval_actor (Interval &r, const duration_t &_a, const duration_t &_b) : ref (r), a (_a), b (_b) { } template void operator() (It begin, It end) const { ref = Interval (a, b); } Interval &ref; const duration_t &a, &b; }; compose_interval_actor compose_interval_a (Interval &r, const duration_t &a, const duration_t &b) { return compose_interval_actor (r, a, b); } struct compose_rnt_a { compose_rnt_a (RNT &r, Depth &d) : ref (r), depth (d) { } void operator() (int m) const { ref.add_depth = Depth (); ref.residual_nitrogen_time = minutes (m); ref.no_group_change = false; } template void operator() (It begin, It end) const { if (*begin == '-') { ref.add_depth = Depth (); ref.residual_nitrogen_time = minutes (0); ref.no_group_change = true; } else { ref.add_depth = depth; ref.residual_nitrogen_time = minutes (0); ref.no_group_change = false; } } RNT &ref; Depth &depth; }; struct compose_dive_tables_a { compose_dive_tables_a (DiveTables &d, std::string &n, IntervalMap &i, RNTMap &r, Tables &t) : ref (d), name (n), intervalmap (i), rntmap (r), tables (t) { } template void operator() (It, It) const { ref.full_name = name; ref.surface_intervals = intervalmap; ref.rntmap = rntmap; ref.tables = tables; } DiveTables &ref; std::string &name; IntervalMap &intervalmap; RNTMap &rntmap; Tables &tables; }; struct assign_deco_actor { assign_deco_actor (Depth &dr, DecoStop &dsr) : depth (dr), decostop (dsr) { } void operator() (int v) const { decostop = DecoStop (depth, minutes (v)); } Depth &depth; DecoStop &decostop; }; assign_deco_actor assign_deco_a (Depth &d, DecoStop &ds) { return assign_deco_actor (d, ds); } struct assign_row_actor { assign_row_actor (TableRow &r, duration_t &bt, Group &rg, DecoStops &ds) : row (r), repetitive_group (rg), bottom_time (bt), stops (ds) { } template void operator() (It begin, It end) const { row = TableRow (bottom_time, repetitive_group, stops); } TableRow &row; Group &repetitive_group; duration_t &bottom_time; DecoStops &stops; }; assign_row_actor assign_row_a (TableRow &r, duration_t &bt, Group &rg, DecoStops &ds) { return assign_row_actor (r, bt, rg, ds); } struct assign_group_actor { assign_group_actor (Group &r) : ref (r) { } template void operator() (It begin, It end) const { ref = Group (*begin); } void operator() (const char &d) const { ref = Group (d); } Group &ref; }; assign_group_actor assign_group_a (Group &d) { return assign_group_actor (d); } struct decrement_depth_actor { decrement_depth_actor (Depth &d) : depth (d) { } template void operator() (T) const { depth -= Depth (10); } Depth &depth; }; decrement_depth_actor decrement_depth_a (Depth &d) { return decrement_depth_actor (d); } struct dive_table_grammar : boost::spirit::grammar { dive_table_grammar (TablesCollection &t) : tablescollection (t) { } template struct definition { definition (const dive_table_grammar &dt) : si1 (minutes (0)), tablescollection (dt.tablescollection) { using namespace boost::spirit; m_depth_rule = int_p[assign_a (raw_depth)] >> str_p ("'")[assign_unit_a (raw_unit)] | real_p[assign_a (raw_depth)] >> str_p ("m")[assign_unit_a (raw_unit)]; //| real_p[assign_a (raw_depth)] >> str_p ("fsw")[assign_unit_a (raw_unit)]; m_group_rule = (range_p ('A', 'Z') | ch_p ('x')); m_decos_rule = (ch_p ('(') >> '@' >> m_depth_rule[compose_depth_a (deco_depth, raw_depth, raw_unit)] >> int_p[assign_deco_a (deco_depth, deco)][push_back_a (decos, deco)][decrement_depth_a (deco_depth)] >> *(int_p[assign_deco_a (deco_depth, deco)][push_back_a (decos, deco)][decrement_depth_a (deco_depth)]) >> ')'); m_time_and_deco_stops_rule = (int_p[assign_time_a (bottom_time)] >> m_decos_rule) | int_p[assign_time_a (bottom_time)] | str_p ("*")[assign_time_a (bottom_time)]; m_row_rule = m_group_rule[assign_group_a (group)] >> *(m_time_and_deco_stops_rule) [assign_row_a (row, bottom_time, group, decos)] [push_back_a (table, row)] [clear_a (decos)] [increment_a (group)]; m_deco_table_rule = m_depth_rule[compose_depth_a (table_depth, raw_depth, raw_unit)] >> ('{' >> *(m_row_rule) >> '}')[insert_at_a (tables, table_depth, table)][clear_a (table)]; m_time_rule = ( int_p[assign_a (hours)] >> ':' >> int_p[assign_a (mins)] )[compose_time_a (si2, hours, mins)]; m_si_rule = m_group_rule[assign_group_a (group) ] >> '[' >> *(m_time_rule[compose_interval_a (si, si1, si2)][push_back_a (intervals, si)]) >> ']' >> m_time_rule [compose_interval_a (si, si1, si2)] [push_back_a (intervals, si)] [insert_at_a (intervalmap, group, intervals)] [clear_a (intervals)]; m_rnt_rule = m_depth_rule[compose_depth_a (rnt_depth, raw_depth, raw_unit)] >> '[' >> *(m_depth_rule[compose_depth_a (tmp_depth, raw_depth, raw_unit)][compose_rnt_a (rnt, tmp_depth)] | int_p[compose_rnt_a (rnt, tmp_depth)] | str_p("-")[compose_rnt_a (rnt, tmp_depth)] )[push_back_a (rnts, rnt)] >> ch_p (']') [insert_at_a (rntmap, rnt_depth, rnts)][clear_a (rnts)]; m_rule = *comment_p ("//") >> (str_p ("table") >> confix_p ("\"", (*anychar_p)[assign_a (table_name)], "\"") >> *(comment_p ("//") | m_si_rule) >> *(comment_p ("//") | m_rnt_rule) >> *(comment_p ("//") | m_deco_table_rule) >> *comment_p ("//") ) [compose_dive_tables_a (divetables, table_name, intervalmap, rntmap, tables)] [insert_at_a (tablescollection, table_name, divetables)] [clear_a (intervalmap)] [clear_a (rntmap)] [clear_a (tables)]; BOOST_SPIRIT_DEBUG_NODE (m_rule); BOOST_SPIRIT_DEBUG_NODE (m_time_rule); BOOST_SPIRIT_DEBUG_NODE (m_si_rule); BOOST_SPIRIT_DEBUG_NODE (m_rnt_rule); BOOST_SPIRIT_DEBUG_NODE (m_depth_rule); BOOST_SPIRIT_DEBUG_NODE (m_deco_table_rule); BOOST_SPIRIT_DEBUG_NODE (m_decos_rule); BOOST_SPIRIT_DEBUG_NODE (m_row_rule); BOOST_SPIRIT_DEBUG_NODE (m_group_rule); BOOST_SPIRIT_DEBUG_NODE (m_time_and_deco_stops_rule); } const boost::spirit::rule& start () const { return m_rule; } boost::spirit::rule m_rule; boost::spirit::rule m_depth_rule, m_deco_table_rule, m_decos_rule, m_si_rule, m_time_rule, m_rnt_rule, m_row_rule, m_group_rule, m_time_and_deco_stops_rule; Depth::depth_t raw_depth; Depth::unit_t raw_unit; int hours, mins; duration_t si1, si2; Interval si; Intervals intervals; IntervalMap intervalmap; Depth rnt_depth; Depth tmp_depth; RNT rnt; RNTs rnts; RNTMap rntmap; Depth deco_depth; DecoStops decos; DecoStop deco; duration_t bottom_time; Group group; TableRow row; Depth table_depth; Table table; std::string table_name; Tables tables; DiveTables divetables; TablesCollection &tablescollection; }; TablesCollection &tablescollection; }; template void parse_dive_table (TablesCollection &tablescollection, It begin, It end) { using namespace std; using namespace boost::spirit; dive_table_grammar grammar (tablescollection); BOOST_SPIRIT_DEBUG_NODE (grammar); parse_info result = parse (begin, end, grammar, space_p); if (result.full) { for_each (tablescollection.begin (), tablescollection.end (), adjust_dive_tables ()); for_each (tablescollection.begin (), tablescollection.end (), check_dive_tables ()); } else { typedef typename It::difference_type diff_t; string snip (std::min (distance (result.stop, end), (diff_t)40), ' '); copy (result.stop, result.stop + snip.capacity (), snip.begin ()); cout << "Parse failed around \n" << snip << "'" << endl; throw std::logic_error (std::string (result.stop, end)); } } void parse_table_file (TablesCollection &tablescollection, const std::string &filename) { boost::spirit::file_iterator<> begin (filename); parse_dive_table (tablescollection, begin, begin.make_end ()); }