diff --git a/3party/opening_hours/opening_hours_tests/osm_time_range_tests.cpp b/3party/opening_hours/opening_hours_tests/osm_time_range_tests.cpp index d971ca4225..f96fec8e3d 100644 --- a/3party/opening_hours/opening_hours_tests/osm_time_range_tests.cpp +++ b/3party/opening_hours/opening_hours_tests/osm_time_range_tests.cpp @@ -44,6 +44,16 @@ #include +namespace +{ +template +std::string ToString(T const & t) +{ + std::stringstream sstr; + sstr << t; + return sstr.str(); +} + template bool test(Char const * in, Parser const & p, bool full_match = true) { @@ -71,6 +81,8 @@ std::basic_string ParseAndUnparse(Char const * input) return sstr.str(); } +} // namespace + BOOST_AUTO_TEST_CASE(OpeningHours_Locale) { @@ -107,6 +119,7 @@ BOOST_AUTO_TEST_CASE(OpeningHours_TestTime) { BOOST_CHECK(!Time{}.HasValue()); + BOOST_CHECK_EQUAL(ToString(Time{}), "hh:mm"); } { Time time{10_min}; @@ -116,6 +129,8 @@ BOOST_AUTO_TEST_CASE(OpeningHours_TestTime) BOOST_CHECK(time.IsMinutes()); BOOST_CHECK(!time.IsEvent()); BOOST_CHECK(!time.IsEventOffset()); + + BOOST_CHECK_EQUAL(ToString(time), "10"); } { Time time{100_min}; @@ -128,6 +143,8 @@ BOOST_AUTO_TEST_CASE(OpeningHours_TestTime) BOOST_CHECK_EQUAL(time.GetHoursCount(), 1); BOOST_CHECK_EQUAL(time.GetMinutesCount(), 40); + + BOOST_CHECK_EQUAL(ToString(time), "01:40"); } { Time time{}; @@ -142,6 +159,8 @@ BOOST_AUTO_TEST_CASE(OpeningHours_TestTime) BOOST_CHECK_EQUAL(time.GetHoursCount(), 22); BOOST_CHECK_EQUAL(time.GetMinutesCount(), 15); + + BOOST_CHECK_EQUAL(ToString(time), "22:15"); } { Time time{}; @@ -152,6 +171,8 @@ BOOST_AUTO_TEST_CASE(OpeningHours_TestTime) BOOST_CHECK(!time.IsMinutes()); BOOST_CHECK(time.IsEvent()); BOOST_CHECK(!time.IsEventOffset()); + + BOOST_CHECK_EQUAL(ToString(time), "sunrise"); } { Time time{}; @@ -168,6 +189,7 @@ BOOST_AUTO_TEST_CASE(OpeningHours_TestTime) { Time time{10_min}; BOOST_CHECK_EQUAL((-time).GetMinutesCount(), -10); + BOOST_CHECK_EQUAL(ToString(-time), "10"); } { Time t1{2_h}; @@ -185,14 +207,25 @@ BOOST_AUTO_TEST_CASE(OpeningHours_TestTimespan) { Timespan span; + BOOST_CHECK(span.IsEmpty()); + BOOST_CHECK(!span.HasStart()); + BOOST_CHECK(!span.HasEnd()); + BOOST_CHECK_EQUAL(ToString(span), "hh:mm-hh:mm"); + span.SetStart(10_h); + BOOST_CHECK(span.HasStart()); BOOST_CHECK(span.IsOpen()); + BOOST_CHECK_EQUAL(ToString(span), "10:00"); + span.SetEnd(12_h); + BOOST_CHECK(span.HasEnd()); BOOST_CHECK(!span.IsOpen()); + BOOST_CHECK_EQUAL(ToString(span), "10:00-12:00"); BOOST_CHECK(!span.HasPeriod()); span.SetPeriod(10_min); BOOST_CHECK(span.HasPeriod()); + BOOST_CHECK_EQUAL(ToString(span), "10:00-12:00/10"); } } @@ -205,21 +238,25 @@ BOOST_AUTO_TEST_CASE(OpeningHours_TestNthEntry) BOOST_CHECK(entry.IsEmpty()); BOOST_CHECK(!entry.HasStart()); BOOST_CHECK(!entry.HasEnd()); + BOOST_CHECK_EQUAL(ToString(entry), ""); - entry.SetStart(NthEntry::ENth::Fifth); + entry.SetStart(NthEntry::ENth::Third); BOOST_CHECK(!entry.IsEmpty()); BOOST_CHECK(entry.HasStart()); BOOST_CHECK(!entry.HasEnd()); + BOOST_CHECK_EQUAL(ToString(entry), "3"); - entry.SetEnd(NthEntry::ENth::Third); + entry.SetEnd(NthEntry::ENth::Fifth); BOOST_CHECK(!entry.IsEmpty()); BOOST_CHECK(entry.HasStart()); BOOST_CHECK(entry.HasEnd()); + BOOST_CHECK_EQUAL(ToString(entry), "3-5"); entry.SetStart(NthEntry::ENth::None); BOOST_CHECK(!entry.IsEmpty()); BOOST_CHECK(!entry.HasStart()); BOOST_CHECK(entry.HasEnd()); + BOOST_CHECK_EQUAL(ToString(entry), "-5"); } } @@ -269,7 +306,52 @@ BOOST_AUTO_TEST_CASE(OpeningHours_TestWeekdayRange) } -BOOST_AUTO_TEST_CASE(OpeningHoursTimerange_DayOffset) +BOOST_AUTO_TEST_CASE(OpeningHours_Holidays) +{ + using namespace osmoh; + + { + Holiday h; + BOOST_CHECK(!h.IsPlural()); + BOOST_CHECK_EQUAL(h.GetOffset(), 0); + BOOST_CHECK_EQUAL(ToString(h), "SH"); + + h.SetOffset(11); + + BOOST_CHECK_EQUAL(h.GetOffset(), 11); + BOOST_CHECK_EQUAL(ToString(h), "SH +11 days"); + + h.SetOffset(-1); + BOOST_CHECK_EQUAL(ToString(h), "SH -1 day"); + + h.SetPlural(true); + BOOST_CHECK(h.IsPlural()); + BOOST_CHECK_EQUAL(ToString(h), "PH"); + } +} + +BOOST_AUTO_TEST_CASE(OpeningHours_Weekdays) +{ + using namespace osmoh; + + { + Weekdays w; + BOOST_CHECK(w.IsEmpty()); + BOOST_CHECK(!w.HasWeekday()); + BOOST_CHECK(!w.HasHolidays()); + + BOOST_CHECK_EQUAL(ToString(w), ""); + + WeekdayRange r; + r.SetStart(EWeekday::Su); + w.AddHoliday(Holiday{}); + w.AddWeekdayRange(r); + + BOOST_CHECK_EQUAL(ToString(w), "SH,Su"); + } +} + +BOOST_AUTO_TEST_CASE(OpeningHours_DayOffset) { using namespace osmoh; @@ -278,21 +360,25 @@ BOOST_AUTO_TEST_CASE(OpeningHoursTimerange_DayOffset) BOOST_CHECK(offset.IsEmpty()); BOOST_CHECK(!offset.HasWDayOffset()); BOOST_CHECK(!offset.HasOffset()); + BOOST_CHECK_EQUAL(ToString(offset), ""); offset.SetWDayOffset(EWeekday::Mo); BOOST_CHECK(!offset.IsEmpty()); BOOST_CHECK(offset.HasWDayOffset()); + BOOST_CHECK_EQUAL(ToString(offset), "+Mo"); offset.SetOffset(11); BOOST_CHECK(offset.HasOffset()); + BOOST_CHECK_EQUAL(ToString(offset), "+Mo +11 days"); BOOST_CHECK(offset.IsWDayOffsetPositive()); offset.SetWDayOffsetPositive(false); BOOST_CHECK(!offset.IsWDayOffsetPositive()); + BOOST_CHECK_EQUAL(ToString(offset), "-Mo +11 days"); } } -BOOST_AUTO_TEST_CASE(OpeningHoursTimerange_TestMonthDay) +BOOST_AUTO_TEST_CASE(OpeningHours_TestMonthDay) { using namespace osmoh; @@ -304,32 +390,37 @@ BOOST_AUTO_TEST_CASE(OpeningHoursTimerange_TestMonthDay) BOOST_CHECK(!md.HasDayNum()); BOOST_CHECK(!md.HasOffset()); BOOST_CHECK(!md.IsVariable()); + BOOST_CHECK_EQUAL(ToString(md), ""); } { MonthDay md; + md.SetMonth(MonthDay::EMonth::Jul); + BOOST_CHECK(!md.IsEmpty()); + BOOST_CHECK(md.HasMonth()); + BOOST_CHECK_EQUAL(ToString(md), "Jul"); + md.SetYear(1990); BOOST_CHECK(!md.IsEmpty()); BOOST_CHECK(md.HasYear()); - - md.SetMonth(MonthDay::EMonth::Jul); - BOOST_CHECK(!md.IsEmpty()); BOOST_CHECK(md.HasYear()); - BOOST_CHECK(md.HasMonth()); + BOOST_CHECK_EQUAL(ToString(md), "1990 Jul"); md.SetDayNum(17); BOOST_CHECK(!md.IsEmpty()); BOOST_CHECK(md.HasYear()); BOOST_CHECK(md.HasMonth()); BOOST_CHECK(md.HasDayNum()); + BOOST_CHECK_EQUAL(ToString(md), "1990 Jul 17"); DateOffset offset; offset.SetWDayOffset(EWeekday::Mo); md.SetOffset(offset); BOOST_CHECK(md.HasOffset()); + BOOST_CHECK_EQUAL(ToString(md), "1990 Jul 17 +Mo"); } } -BOOST_AUTO_TEST_CASE(OpeningHoursTimerange_TestMonthdayRange) +BOOST_AUTO_TEST_CASE(OpeningHours_TestMonthdayRange) { using namespace osmoh; @@ -340,12 +431,14 @@ BOOST_AUTO_TEST_CASE(OpeningHoursTimerange_TestMonthdayRange) BOOST_CHECK(!range.HasEnd()); BOOST_CHECK(!range.HasPeriod()); BOOST_CHECK(!range.HasPlus()); + BOOST_CHECK_EQUAL(ToString(range), ""); } { MonthdayRange range; MonthDay md; md.SetYear(1990); + md.SetMonth(MonthDay::EMonth::Sep); range.SetStart(md); BOOST_CHECK(!range.IsEmpty()); @@ -353,6 +446,7 @@ BOOST_AUTO_TEST_CASE(OpeningHoursTimerange_TestMonthdayRange) BOOST_CHECK(!range.HasEnd()); BOOST_CHECK(!range.HasPeriod()); BOOST_CHECK(!range.HasPlus()); + BOOST_CHECK_EQUAL(ToString(range), "1990 Sep"); } { MonthdayRange range; @@ -386,6 +480,81 @@ BOOST_AUTO_TEST_CASE(OpeningHoursTimerange_TestMonthdayRange) } } +BOOST_AUTO_TEST_CASE(OpeningHours_YearRange) +{ + using namespace osmoh; + + { + YearRange range; + BOOST_CHECK(range.IsEmpty()); + BOOST_CHECK(!range.HasStart()); + BOOST_CHECK(!range.HasEnd()); + BOOST_CHECK(!range.HasPlus()); + BOOST_CHECK_EQUAL(ToString(range), ""); + + range.SetStart(1812); + BOOST_CHECK(range.HasStart()); + BOOST_CHECK(range.IsOpen()); + BOOST_CHECK_EQUAL(ToString(range), "1812"); + + range.SetEnd(1815); + BOOST_CHECK(range.HasEnd()); + BOOST_CHECK(!range.IsOpen()); + BOOST_CHECK_EQUAL(ToString(range), "1812-1815"); + + BOOST_CHECK(!range.HasPeriod()); + range.SetPeriod(10); + BOOST_CHECK(range.HasPeriod()); + BOOST_CHECK_EQUAL(ToString(range), "1812-1815/10"); + } + { + YearRange range; + range.SetStart(1812); + range.SetPlus(true); + BOOST_CHECK(range.HasStart()); + BOOST_CHECK(range.IsOpen()); + BOOST_CHECK(range.HasPlus()); + BOOST_CHECK_EQUAL(ToString(range), "1812+"); + } +} + +BOOST_AUTO_TEST_CASE(OpeningHours_WeekRange) +{ + using namespace osmoh; + + { + WeekRange range; + BOOST_CHECK(range.IsEmpty()); + BOOST_CHECK(!range.HasStart()); + BOOST_CHECK(!range.HasEnd()); + BOOST_CHECK_EQUAL(ToString(range), ""); + + range.SetStart(18); + BOOST_CHECK(range.HasStart()); + BOOST_CHECK(range.IsOpen()); + BOOST_CHECK_EQUAL(ToString(range), "18"); + + range.SetEnd(42); + BOOST_CHECK(range.HasEnd()); + BOOST_CHECK(!range.IsOpen()); + BOOST_CHECK_EQUAL(ToString(range), "18-42"); + + BOOST_CHECK(!range.HasPeriod()); + range.SetPeriod(10); + BOOST_CHECK(range.HasPeriod()); + BOOST_CHECK_EQUAL(ToString(range), "18-42/10"); + } +} + +BOOST_AUTO_TEST_CASE(OpeningHours_RuleSequence) +{ + using namespace osmoh; + + { + RuleSequence s; + } +} + BOOST_AUTO_TEST_CASE(OpeningHoursTimerange_TestParseUnparse) { { @@ -446,6 +615,26 @@ BOOST_AUTO_TEST_CASE(OpeningHoursTimerange_TestParseUnparse) BOOST_AUTO_TEST_CASE(OpeningHoursWeekdays_TestParseUnparse) { + { + auto const rule = "We[4] -2 days"; + auto const parsedUnparsed = ParseAndUnparse(rule); + BOOST_CHECK_EQUAL(parsedUnparsed, rule); + } + { + auto const rule = "Sa[4,5]"; + auto const parsedUnparsed = ParseAndUnparse(rule); + BOOST_CHECK_EQUAL(parsedUnparsed, rule); + } + { + auto const rule = "Mo[1,3]"; + auto const parsedUnparsed = ParseAndUnparse(rule); + BOOST_CHECK_EQUAL(parsedUnparsed, rule); + } + { + auto const rule = "Tu[4,5] +1 day"; + auto const parsedUnparsed = ParseAndUnparse(rule); + BOOST_CHECK_EQUAL(parsedUnparsed, rule); + } { auto const rule = "SH -2 days"; auto const parsedUnparsed = ParseAndUnparse(rule); @@ -600,6 +789,58 @@ BOOST_AUTO_TEST_CASE(OpeningHoursWeekRanges_TestParseUnparse) } } +BOOST_AUTO_TEST_CASE(OpeningHoursRuleSequence_TestParseUnparse) +{ + { + auto const rule = "24/7"; + auto const parsedUnparsed = ParseAndUnparse(rule); + BOOST_CHECK_EQUAL(parsedUnparsed, rule); + } + { + auto const rule = "2016-2025"; + auto const parsedUnparsed = ParseAndUnparse(rule); + BOOST_CHECK_EQUAL(parsedUnparsed, rule); + } + { + auto const rule = "Feb 03 -Mo -2 days-Jan 11 +3 days"; + auto const parsedUnparsed = ParseAndUnparse(rule); + BOOST_CHECK_EQUAL(parsedUnparsed, rule); + } + { + auto const rule = "week 19-31"; + auto const parsedUnparsed = ParseAndUnparse(rule); + BOOST_CHECK_EQUAL(parsedUnparsed, rule); + } + { + auto const rule = "06:00-02:00/21:03,18:15"; + auto const parsedUnparsed = ParseAndUnparse(rule); + BOOST_CHECK_EQUAL(parsedUnparsed, rule); + } + { + auto const rule = "06:13-15:00; 16:30+"; + auto const parsedUnparsed = ParseAndUnparse(rule); + BOOST_CHECK_EQUAL(parsedUnparsed, rule); + } + { + auto const rule = "We-Sa; Mo[1,3] closed"; + auto const parsedUnparsed = ParseAndUnparse(rule); + BOOST_CHECK_EQUAL(parsedUnparsed, rule); + } + { + auto const rule = ( "We-Sa; Mo[1,3] closed; Su[-1,-2] closed; " + "Fr[2] open; Fr[-2], Fr open; Su[-2] -2 days" ); + auto const parsedUnparsed = ParseAndUnparse(rule); + BOOST_CHECK_EQUAL(parsedUnparsed, rule); + } +} + // { + // auto const rule = ( "PH,Tu-Su 10:00-18:00; Sa[1] 10:00-18:00 open " + // "\"Eintritt ins gesamte Haus frei\"; " + // "Jan 1,Dec 24,Dec 25,easter -2 days: closed" ); + // auto const parsedUnparsed = ParseAndUnparse(rule); + // BOOST_CHECK_EQUAL(parsedUnparsed, rule); + // } + // BOOST_AUTO_TEST_CASE(OpeningHours_TimeHit) // { // { @@ -609,13 +850,6 @@ BOOST_AUTO_TEST_CASE(OpeningHoursWeekRanges_TestParseUnparse) // BOOST_CHECK(oh.UpdateState("12-12-2013 16:00").IsClosed()); // BOOST_CHECK(oh.UpdateState("12-12-2013 20:00").IsOpen()); // } -// { -// OSMTimeRange oh = OSMTimeRange::FromString("We-Sa; Mo[1,3] closed; Su[-1,-2] closed; Fr[2] open; Fr[-2], Fr open; Su[-2] -2 days"); -// BOOST_CHECK(oh.IsValid()); -// BOOST_CHECK(oh.UpdateState("20-03-2015 18:00").IsOpen()); -// BOOST_CHECK(oh.UpdateState("17-03-2015 18:00").IsClosed()); -// } - // { // OSMTimeRange oh = OSMTimeRange::FromString("We-Fr; Mo[1,3] closed; Su[-1,-2] closed"); // BOOST_CHECK(oh.IsValid()); diff --git a/3party/opening_hours/osm_parsers.hpp b/3party/opening_hours/osm_parsers.hpp index d3b6db127d..c4dbc3cda6 100644 --- a/3party/opening_hours/osm_parsers.hpp +++ b/3party/opening_hours/osm_parsers.hpp @@ -299,9 +299,10 @@ class weekday_selector : public qi::grammar> - ushort_[_val = _1 * _a] >> - charset::no_case[(lit(L"days") | lit(L"day"))]; + ( (lit('+')[_a = 1] | lit('-') [_a = -1]) >> + ushort_ [_val = _1 * _a] >> + charset::no_case[(lit(L"days") | lit(L"day"))] ) + ; holiday = (charset::no_case[lit(L"SH")] [bind(&osmoh::Holiday::SetPlural, _val, false)] >> -day_offset [bind(&osmoh::Holiday::SetOffset, _val, _1)]) @@ -310,18 +311,13 @@ class weekday_selector : public qi::grammar> L'[' >> nth_entry[bind(&osmoh::WeekdayRange::AddNth, _val, _1)] % L',' - >> L']' >> day_offset[bind(&osmoh::WeekdayRange::SetOffset, _val, _1)]) - - | (charset::no_case[wdays][bind(&osmoh::WeekdayRange::SetStart, _val, _1)] - >> L'[' >> nth_entry[bind(&osmoh::WeekdayRange::AddNth, _val, _1)] % L',' - >> L']') - - | charset::no_case[(wdays >> dash >> wdays)][bind(&osmoh::WeekdayRange::SetStart, _val, _1), - bind(&osmoh::WeekdayRange::SetEnd, _val, _2)] - - | charset::no_case[wdays][bind(&osmoh::WeekdayRange::SetStart, _val, _1)] + weekday_range = + ( charset::no_case[wdays] [bind(&osmoh::WeekdayRange::SetStart, _val, _1)] >> + '[' >> (nth_entry [bind(&osmoh::WeekdayRange::AddNth, _val, _1)]) % ',') >> ']' >> + -(day_offset [bind(&osmoh::WeekdayRange::SetOffset, _val, _1)]) + | charset::no_case[(wdays >> dash >> wdays)] [bind(&osmoh::WeekdayRange::SetStart, _val, _1), + bind(&osmoh::WeekdayRange::SetEnd, _val, _2)] + | charset::no_case[wdays] [bind(&osmoh::WeekdayRange::SetStart, _val, _1)] ; weekday_sequence %= (weekday_range % L',') >> !qi::no_skip[charset::alpha] >> -lit(L':'); @@ -456,7 +452,7 @@ class time_selector : public qi::grammar -// class selectors : public qi::grammar +// class selectors : public qi::grammar // { // protected: // weekday_selector weekday_selector; @@ -466,9 +462,8 @@ class time_selector : public qi::grammar week_selector; // qi::rule comment; -// qi::rule small_range_selectors; -// qi::rule wide_range_selectors; -// qi::rule main; +// qi::rule main; + // public: // selectors() : selectors::base_type(main) // { @@ -478,107 +473,156 @@ class time_selector : public qi::grammar> +(char_ - '"') >> '"']; -// wide_range_selectors = -year_selector >> -month_selector >> -week_selector >> -lit(':') | (comment >> ':'); -// small_range_selectors = -weekday_selector[at_c<0>(_val) = _1] >> -( lit("24/7") | time_selector[at_c<1>(_val) = _1]); +// // comment %= lexeme['"' >> +(char_ - '"') >> '"']; +// // wide_range_selectors = -year_selector >> -month_selector >> -week_selector >> -lit(':') | (comment >> ':'); +// // small_range_selectors = -weekday_selector[at_c<0>(_val) = _1] >> -( lit("24/7") | time_selector[at_c<1>(_val) = _1]); + +// // main = +// // ( +// // lit(L"24/7") +// // | lit(L"24時間営業") +// // | lit(L"7/24") +// // | lit(L"24時間") +// // | charset::no_case[lit(L"daily 24/7")] +// // | charset::no_case[lit(L"24 hours")] +// // | charset::no_case[lit(L"24 horas")] +// // | charset::no_case[lit(L"круглосуточно")] +// // | charset::no_case[lit(L"24 часа")] +// // | charset::no_case[lit(L"24 hrs")] +// // | charset::no_case[lit(L"nonstop")] +// // | charset::no_case[lit(L"24hrs")] +// // | charset::no_case[lit(L"open 24 hours")] +// // | charset::no_case[lit(L"24 stunden")] +// // )[at_c<0>(at_c<2>(_val)) = State::eOpen] +// // | (-wide_range_selectors >> small_range_selectors[_val = _1, at_c<0>(at_c<2>(_val)) = State::eOpen]) +// // ; -// main = -// ( -// lit(L"24/7") -// | lit(L"24時間営業") -// | lit(L"7/24") -// | lit(L"24時間") -// | charset::no_case[lit(L"daily 24/7")] -// | charset::no_case[lit(L"24 hours")] -// | charset::no_case[lit(L"24 horas")] -// | charset::no_case[lit(L"круглосуточно")] -// | charset::no_case[lit(L"24 часа")] -// | charset::no_case[lit(L"24 hrs")] -// | charset::no_case[lit(L"nonstop")] -// | charset::no_case[lit(L"24hrs")] -// | charset::no_case[lit(L"open 24 hours")] -// | charset::no_case[lit(L"24 stunden")] -// )[at_c<0>(at_c<2>(_val)) = State::eOpen] -// | (-wide_range_selectors >> small_range_selectors[_val = _1, at_c<0>(at_c<2>(_val)) = State::eOpen]) -// ; // BOOST_SPIRIT_DEBUG_NODE(main); // BOOST_SPIRIT_DEBUG_NODE(small_range_selectors); // BOOST_SPIRIT_DEBUG_NODE(wide_range_selectors); // } // }; -// template -// class time_domain : public qi::grammar*>> -// { -// protected: -// selectors selector_sequence; +template +class time_domain : public qi::grammar +{ +protected: + weekday_selector weekday_selector; + time_selector time_selector; + year_selector year_selector; + month_selector month_selector; + week_selector week_selector; -// qi::rule comment; -// qi::rule separator; -// qi::rule base_separator; -// qi::rule rule_sequence; -// qi::rule rule_modifier; -// qi::rule*>> main; + qi::rule comment; + qi::rule separator; -// public: -// time_domain() : time_domain::base_type(main) -// { -// using qi::lit; -// using qi::lexeme; -// using qi::_1; -// using qi::_a; -// using qi::_val; -// using charset::char_; -// using boost::phoenix::at_c; -// using qi::lazy; -// using qi::eps; -// using osmoh::State; + qi::rule small_range_selectors; + qi::rule wide_range_selectors; + qi::rule rule_modifier; -// comment %= lexeme['"' >> +(char_ - '"') >> '"'] | lexeme['(' >> +(char_ - ')') >> ')']; -// base_separator = lit(';') | lit("||"); -// separator = lit(';') | lit("||") | lit(','); + qi::rule rule_sequence; + qi::rule main; -// rule_modifier = -// (charset::no_case[lit("open")][at_c<0>(_val) = State::eOpen] >> -comment[at_c<1>(_val) = _1]) -// | ((charset::no_case[lit("closed") | lit("off")])[at_c<0>(_val) = State::eClosed] >> -comment[at_c<1>(_val) = _1]) -// | (charset::no_case[lit("unknown")][at_c<0>(_val) = State::eUnknown] >> -comment[at_c<1>(_val) = _1]) -// | comment[at_c<0>(_val) = State::eUnknown, at_c<1>(_val) = _1] -// ; +public: + time_domain() : time_domain::base_type(main) + { + using qi::lit; + using qi::lexeme; + using qi::_1; + using qi::_a; + using qi::_r1; + using qi::_val; + using charset::char_; + using qi::eps; + using qi::lazy; + using phx::back; + using phx::push_back; + using phx::construct; -// rule_sequence = selector_sequence[_val = _1] -// >> -rule_modifier[at_c<2>(_val) = _1, at_c<3>(_val) = 1]; + using Modifier = osmoh::RuleSequence::Modifier; -// main %= -(lit("opening_hours") >> lit('=')) -// >> rule_sequence[_a = phx::val(&base_separator), -// phx::if_(at_c<3>(_1) || phx::size(at_c<1>(_1)))[_a = phx::val(&separator)]] % lazy(*_a); + comment %= '"' >> +(char_ - '"') >> '"' + // | lexeme['(' >> +(char_ - ')') >> ')'] + ; -// BOOST_SPIRIT_DEBUG_NODE(main); -// BOOST_SPIRIT_DEBUG_NODE(rule_sequence); -// BOOST_SPIRIT_DEBUG_NODE(rule_modifier); -// } -// }; + separator %= charset::string(";") + | charset::string("||") + | charset::string(",") + ; -// template -// inline bool parse_timerange(Iterator first, Iterator last, osmoh::TTimeRules & context) -// { -// using qi::phrase_parse; -// using charset::space; + wide_range_selectors = + ( -(year_selector [bind(&osmoh::RuleSequence::SetYears, _r1, _1)]) >> + -(month_selector [bind(&osmoh::RuleSequence::SetMonths, _r1, _1)]) >> + -(week_selector [bind(&osmoh::RuleSequence::SetWeeks, _r1, _1)]) >> + -(lit(':') [bind(&osmoh::RuleSequence::SetSeparatorForReadability, _r1, true)])) + | (comment >> ':') [bind(&osmoh::RuleSequence::SetComment, _r1, _1)] + ; -// time_domain time_domain; + small_range_selectors = + ( -(weekday_selector [bind(&osmoh::RuleSequence::SetWeekdays, _r1, _1)]) >> + -(time_selector [bind(&osmoh::RuleSequence::SetTimes, _r1, _1)])) + ; -// bool r = phrase_parse( -// first, /* start iterator */ -// last, /* end iterator */ -// time_domain, /* the parser */ -// space, /* the skip-parser */ -// context /* result storage */ -// ); + rule_modifier = + (charset::no_case[lit("open")] + [bind(&osmoh::RuleSequence::SetModifier, _r1, Modifier::Open)] >> + -(comment [bind(&osmoh::RuleSequence::SetModifierComment, _r1, _1)])) -// if (first != last) // fail if we did not get a full match -// return false; -// return r; -// } + | ((charset::no_case[lit("closed") | lit("off")]) + [bind(&osmoh::RuleSequence::SetModifier, _r1, Modifier::Closed)] >> + -(comment [bind(&osmoh::RuleSequence::SetModifierComment, _r1, _1)])) + + | (charset::no_case[lit("unknown")] + [bind(&osmoh::RuleSequence::SetModifier, _r1, Modifier::Unknown)] >> + -(comment [bind(&osmoh::RuleSequence::SetModifierComment, _r1, _1)])) + + | comment [bind(&osmoh::RuleSequence::SetModifier, _r1, Modifier::Unknown), + bind(&osmoh::RuleSequence::SetModifierComment, _r1, _1)] + + // | eps [bind(&osmoh::RuleSequence::SetModifier, _val, Modifier::Open)] + ; + + rule_sequence = + lit("24/7") [bind(&osmoh::RuleSequence::Set24Per7, _val, true)] + | ( -wide_range_selectors(_val) >> + -small_range_selectors(_val) >> + -rule_modifier(_val) ) + ; + + main %= + ( -(lit("opening_hours") >> lit('=')) >> + (rule_sequence % (separator + [phx::bind(&osmoh::RuleSequence::SetAnySeparator, &back(_val), _1)]))) + ; + // + + // main = + // ( + // lit(L"24/7") + // | lit(L"24時間営業") + // | lit(L"7/24") + // | lit(L"24時間") + // | charset::no_case[lit(L"daily 24/7")] + // | charset::no_case[lit(L"24 hours")] + // | charset::no_case[lit(L"24 horas")] + // | charset::no_case[lit(L"круглосуточно")] + // | charset::no_case[lit(L"24 часа")] + // | charset::no_case[lit(L"24 hrs")] + // | charset::no_case[lit(L"nonstop")] + // | charset::no_case[lit(L"24hrs")] + // | charset::no_case[lit(L"open 24 hours")] + // | charset::no_case[lit(L"24 stunden")] + // )[at_c<0>(at_c<2>(_val)) = State::eOpen] + // | (-wide_range_selectors >> small_range_selectors[_val = _1, at_c<0>(at_c<2>(_val)) = State::eOpen]) + // ; + + BOOST_SPIRIT_DEBUG_NODE(main); + BOOST_SPIRIT_DEBUG_NODE(rule_sequence); + BOOST_SPIRIT_DEBUG_NODE(rule_modifier); + BOOST_SPIRIT_DEBUG_NODE(small_range_selectors); + BOOST_SPIRIT_DEBUG_NODE(wide_range_selectors); + } +}; } // namespace parsing } // namespace osmoh diff --git a/3party/opening_hours/osm_time_range.cpp b/3party/opening_hours/osm_time_range.cpp index ca6ec3d474..31c9504294 100644 --- a/3party/opening_hours/osm_time_range.cpp +++ b/3party/opening_hours/osm_time_range.cpp @@ -244,7 +244,7 @@ std::ostream & operator<<(std::ostream & ost, Time const & time) } ost << time.GetEvent(); } - if (time.IsMinutes()) + else if (time.IsMinutes()) PrintPaddedNumber(ost, std::abs(minutes), 2); else { @@ -257,9 +257,24 @@ std::ostream & operator<<(std::ostream & ost, Time const & time) } +bool Timespan::IsEmpty() const +{ + return !HasStart() && !HasEnd(); +} + bool Timespan::IsOpen() const { - return !m_end.HasValue(); + return GetStart().HasValue() && !GetEnd().HasValue(); +} + +bool Timespan::HasStart() const +{ + return GetStart().HasValue(); +} + +bool Timespan::HasEnd() const +{ + return GetEnd().HasValue(); } bool Timespan::HasPlus() const @@ -503,7 +518,9 @@ std::ostream & operator<<(std::ostream & ost, WeekdayRange const & range) { if (range.HasNth()) { + ost << '['; PrintVector(ost, range.GetNths()); + ost << ']'; } PrintOffset(ost, range.GetOffset(), true); } @@ -556,6 +573,11 @@ std::ostream & operator<<(std::ostream & ost, THolidays const & holidays) } +bool Weekdays::IsEmpty() const +{ + return GetWeekdayRanges().empty() && GetHolidays().empty(); +} + bool Weekdays::HasWeekday() const { return !GetWeekdayRanges().empty(); @@ -912,6 +934,11 @@ bool YearRange::IsEmpty() const return !HasStart() && !HasEnd(); } +bool YearRange::IsOpen() const +{ + return HasStart() && !HasEnd(); +} + bool YearRange::HasStart() const { return GetStart() != 0; @@ -969,6 +996,9 @@ void YearRange::SetPeriod(uint32_t const period) std::ostream & operator<<(std::ostream & ost, YearRange const range) { + if (range.IsEmpty()) + return ost; + ost << range.GetStart(); if (range.HasEnd()) { @@ -993,6 +1023,11 @@ bool WeekRange::IsEmpty() const return !HasStart() && !HasEnd(); } +bool WeekRange::IsOpen() const +{ + return HasStart() && !HasEnd(); +} + bool WeekRange::HasStart() const { return GetStart() != 0; @@ -1040,6 +1075,9 @@ void WeekRange::SetPeriod(uint32_t const period) std::ostream & operator<<(std::ostream & ost, WeekRange const range) { + if (range.IsEmpty()) + return ost; + PrintPaddedNumber(ost, range.GetStart(), 2); if (range.HasEnd()) { @@ -1058,6 +1096,229 @@ std::ostream & operator<<(std::ostream & ost, TWeekRanges const ranges) return ost; } + +bool RuleSequence::IsEmpty() const +{ + return (!HasYears() && !HasMonth() && + !HasWeeks() && !HasWeekdays() && + !HasTimes()); +} + +bool RuleSequence::Is24Per7() const +{ + return m_24_per_7; +} + +bool RuleSequence::HasYears() const +{ + return !GetYears().empty(); +} + +bool RuleSequence::HasMonth() const +{ + return !GetMonths().empty(); +} + +bool RuleSequence::HasWeeks() const +{ + return !GetWeeks().empty(); +} + +bool RuleSequence::HasWeekdays() const +{ + return !GetWeekdays().IsEmpty(); +} + +bool RuleSequence::HasTimes() const +{ + return !GetTimes().empty(); +} + +bool RuleSequence::HasComment() const +{ + return !GetComment().empty(); +} + +bool RuleSequence::HasModifierComment() const +{ + return !GetModifierComment().empty(); +} + +bool RuleSequence::HasSeparatorForReadability() const +{ + return m_separator_for_readablility; +} + +TYearRanges const & RuleSequence::GetYears() const +{ + return m_years; +} + +TMonthdayRanges const & RuleSequence::GetMonths() const +{ + return m_months; +} + +TWeekRanges const & RuleSequence::GetWeeks() const +{ + return m_weeks; +} + +Weekdays const & RuleSequence::GetWeekdays() const +{ + return m_weekdays; +} + +TTimespans const & RuleSequence::GetTimes() const +{ + return m_times; +} + +std::string const & RuleSequence::GetComment() const +{ + return m_comment; +} + +std::string const & RuleSequence::GetModifierComment() const +{ + return m_modifier_comment; +} + +std::string const & RuleSequence::GetAnySeparator() const +{ + return m_any_separator; +} + +RuleSequence::Modifier RuleSequence::GetModifier() const +{ + return m_modifier; +} + +void RuleSequence::Set24Per7(bool const on) +{ + // std::cout << "Set24Per7: " << on << '\n'; + m_24_per_7 = on; + // dump(); +} + +void RuleSequence::SetYears(TYearRanges const & years) +{ + // std::cout << "SetYears: " << years << '\n'; + m_years = years; + // dump(); +} + +void RuleSequence::SetMonths(TMonthdayRanges const & months) +{ + // std::cout << "SetMonths: " << months << '\n'; + m_months = months; + // dump(); +} + +void RuleSequence::SetWeeks(TWeekRanges const & weeks) +{ + // std::cout << "SetWeeks: " << weeks << '\n'; + m_weeks = weeks; + // dump(); +} + +void RuleSequence::SetWeekdays(Weekdays const & weekdays) +{ + // std::cout << "SetWeekdays: " << weekdays << '\n'; + m_weekdays = weekdays; + // dump(); +} + +void RuleSequence::SetTimes(TTimespans const & times) +{ + // std::cout << "SetTimes: " << times << '\n'; + m_times = times; + // dump(); +} + +void RuleSequence::SetComment(std::string const & comment) +{ + // std::cout << "SetComment: " << comment << '\n'; + m_comment = comment; + // dump(); +} + +void RuleSequence::SetModifierComment(std::string & comment) +{ + // std::cout << "SetModifierComment: " << comment << '\n'; + m_modifier_comment = comment; + // dump(); +} + +void RuleSequence::SetAnySeparator(std::string const & separator) +{ + // std::cout << "SetAnySeparator: " << separator << '\n'; + m_any_separator = separator; + // dump(); +} + +void RuleSequence::SetSeparatorForReadability(bool const on) +{ + // std::cout << "SetSeparatorForReadability: " << on << '\n'; + m_separator_for_readablility = on; + // dump(); +} + +void RuleSequence::SetModifier(Modifier const modifier) +{ + // std::cout << "SetModifier " ;//<< modifier << '\n'; + m_modifier = modifier; + // dump(); +} + +// uint32_t RuleSequence::id{}; +// void RuleSequence::dump() const +// { +// std::cout << "My id: " << my_id << '\n' +// << "Years " << GetYears().size() << '\n' +// << "Months " << GetMonths().size() << '\n' +// << "Weeks " << GetWeeks().size() << '\n' +// << "Holidays " << GetWeekdays().GetHolidays().size() << '\n' +// << "Weekdays " << GetWeekdays().GetWeekdayRanges().size() << '\n' +// << "Times " << GetTimes().size() << std::endl; +// } + +std::ostream & operator<<(std::ostream & ost, RuleSequence const & s) +{ + if (s.Is24Per7()) + ost << "24/7"; + else + { + if (s.HasComment()) + ost << s.GetComment() << ':'; + else + { + if (s.HasYears()) + ost << s.GetYears(); + if (s.HasMonth()) + ost << s.GetMonths(); + if (s.HasWeeks()) + ost << s.GetWeeks(); + + if (s.HasSeparatorForReadability()) + ost << ':'; + + if (s.HasWeekdays()) + ost << s.GetWeekdays(); + if (s.HasTimes()) + ost << s.GetTimes(); + } + } + + return ost; +} + +std::ostream & operator<<(std::ostream & ost, TRuleSequences const & s) +{ + PrintVector(ost, s); + return ost; +} + // std::ostream & operator << (std::ostream & s, State const & w) // { // static char const * st[] = {"unknown", "closed", "open"}; diff --git a/3party/opening_hours/osm_time_range.hpp b/3party/opening_hours/osm_time_range.hpp index 843c2da647..ad747ca7a6 100644 --- a/3party/opening_hours/osm_time_range.hpp +++ b/3party/opening_hours/osm_time_range.hpp @@ -120,7 +120,10 @@ class Timespan Timespan(Time const & start, Time const & end, bool plus = false); Timespan(Time const & start, Time const & end, Time const & period); + bool IsEmpty() const; bool IsOpen() const; + bool HasStart() const; + bool HasEnd() const; bool HasPlus() const; bool HasPeriod() const; @@ -271,6 +274,7 @@ std::ostream & operator<<(std::ostream & ost, THolidays const & holidys); class Weekdays // Correspond to weekday_selector in osm opening hours { public: + bool IsEmpty() const; bool HasWeekday() const; bool HasHolidays() const; @@ -423,6 +427,7 @@ class YearRange public: bool IsEmpty() const; + bool IsOpen() const; bool HasStart() const; bool HasEnd() const; bool HasPlus() const; @@ -456,6 +461,7 @@ class WeekRange public: bool IsEmpty() const; + bool IsOpen() const; bool HasStart() const; bool HasEnd() const; bool HasPeriod() const; @@ -478,36 +484,98 @@ using TWeekRanges = std::vector; std::ostream & operator<<(std::ostream & ost, WeekRange const range); std::ostream & operator<<(std::ostream & ost, TWeekRanges const ranges); + +class RuleSequence +{ + // static uint32_t id; + // uint32_t my_id; + public: + enum class Modifier { + Unknown, + Closed, + Open + }; + + public: + // RuleSequence() + // { + // ++id; + // my_id = id; + // std::cout << "RuleSequence(" << my_id << ")" << std::endl; + // } + + // ~RuleSequence() + // { + // std::cout << "~RuleSequence(" << my_id << ")" << std::endl; + // } + + bool IsEmpty() const; + bool Is24Per7() const; + + bool HasYears() const; + bool HasMonth() const; + bool HasWeeks() const; + bool HasWeekdays() const; + bool HasTimes() const; + bool HasComment() const; + bool HasModifierComment() const; + bool HasSeparatorForReadability() const; + + TYearRanges const & GetYears() const; + TMonthdayRanges const & GetMonths() const; + TWeekRanges const & GetWeeks() const; + Weekdays const & GetWeekdays() const; + TTimespans const & GetTimes() const; + + std::string const & GetComment() const; + std::string const & GetModifierComment() const; + std::string const & GetAnySeparator() const; + + Modifier GetModifier() const; + + void Set24Per7(bool const on); + void SetYears(TYearRanges const & years); + void SetMonths(TMonthdayRanges const & months); + void SetWeeks(TWeekRanges const & weeks); + + void SetWeekdays(Weekdays const & weekdays); + void SetTimes(TTimespans const & times); + + void SetComment(std::string const & comment); + void SetModifierComment(std::string & comment); + void SetAnySeparator(std::string const & separator); + void SetSeparatorForReadability(bool const on); + + void SetModifier(Modifier const modifier); + + private: + void dump() const; + + private: + bool m_24_per_7{false}; + + TYearRanges m_years; + TMonthdayRanges m_months; + TWeekRanges m_weeks; + + Weekdays m_weekdays; + TTimespans m_times; + + std::string m_comment; + std::string m_any_separator{";"}; + bool m_separator_for_readablility{false}; + + Modifier m_modifier{Modifier::Unknown}; + std::string m_modifier_comment; +}; + +using TRuleSequences = std::vector; + +std::ostream & operator<<(std::ostream & ost, RuleSequence const & sequence); +std::ostream & operator<<(std::ostream & ost, TRuleSequences const & sequences); } // namespace osmoh -// class State -// { -// public: -// enum EState { -// eUnknown = 0, -// eClosed = 1, -// eOpen = 2 -// }; - -// uint8_t state; -// std::string comment; - -// State() : state(eUnknown) {} -// }; - -// class TimeRule -// { -// public: -// TWeekdays weekdays; -// TTimeSpans timespan; // TODO(mgsergio) rename to timespans -// State state; -// uint8_t int_flags = 0; - -// friend std::ostream & operator <<(std::ostream & s, TimeRule const & r); -// }; -// } // namespace osmoh - // class OSMTimeRange // { // public: diff --git a/3party/opening_hours/parse.cpp b/3party/opening_hours/parse.cpp index 06503608e8..789044a892 100644 --- a/3party/opening_hours/parse.cpp +++ b/3party/opening_hours/parse.cpp @@ -31,6 +31,11 @@ template struct context_parser using type = osmoh::parsing::week_selector; }; +template struct context_parser +{ + using type = osmoh::parsing::time_domain; +}; + template using context_parser_t = typename context_parser::type; @@ -89,4 +94,9 @@ bool Parse(std::string const & str, TWeekRanges & w) { return ParseImp(str, w); } + +bool Parse(std::string const & str, TRuleSequences & r) +{ + return ParseImp(str, r); +} } // namespace osmoh diff --git a/3party/opening_hours/parse.hpp b/3party/opening_hours/parse.hpp index f1574cad77..2be3e65d0c 100644 --- a/3party/opening_hours/parse.hpp +++ b/3party/opening_hours/parse.hpp @@ -10,4 +10,5 @@ bool Parse(std::string const &, Weekdays &); bool Parse(std::string const &, TMonthdayRanges &); bool Parse(std::string const &, TYearRanges &); bool Parse(std::string const &, TWeekRanges &); +bool Parse(std::string const &, TRuleSequences &); } // namespace osmoh