From 27400587d56d9aa0b2894dd5e23a110bad68fda2 Mon Sep 17 00:00:00 2001 From: Zach Laine Date: Thu, 22 Feb 2024 00:56:54 -0600 Subject: [PATCH] Add Phil Endecott's example latlong parser from the Boost mailing list review as a test. --- test/CMakeLists.txt | 1 + test/parse_coords_new.cpp | 199 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+) create mode 100644 test/parse_coords_new.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index eb12a0f0..ca3de3b5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -67,3 +67,4 @@ add_test_executable(class_type) add_test_executable(case_fold_generated) add_test_executable(no_case) add_test_executable(merge_separate) +add_test_executable(parse_coords_new) diff --git a/test/parse_coords_new.cpp b/test/parse_coords_new.cpp new file mode 100644 index 00000000..2835a790 --- /dev/null +++ b/test/parse_coords_new.cpp @@ -0,0 +1,199 @@ +/** + * Copyright (C) 2024 Phil Endecott + * Copyright (C) 2024 T. Zachary Laine + * + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ + +#include + +#if !BOOST_PARSER_USE_STD_TUPLE + +#include + +#include +#include +#include +#include + +#include + + +namespace bp = boost::parser; + +using namespace boost::hana::literals; + +namespace g2d { + struct Vector + { + double x, y; + }; + + std::ostream & operator<<(std::ostream & os, Vector vec) + { + os << "vec{" << vec.x << ", " << vec.y << "}"; + return os; + } +}; + + +bp::rule uint_0_60 = "uint_0_60"; +bp::rule double_0_60 = "double_0_60"; +bp::rule degrees_decimal_minutes = "degrees_decimal_minutes"; +bp::rule degrees_minutes_seconds = "degrees_minutes_seconds"; +bp::rule degrees = "degrees"; +bp::rule latitude = "latitude"; +bp::rule longitude = "longitude"; +bp::rule signed_latitude = "signed_latitude"; +bp::rule signed_longitude = "signed_longitude"; +bp::rule latlon = "latlon"; + + +const auto degrees_symbol = bp::no_case[ bp::lit("degrees") | bp::lit("deg") | bp::lit('d') ]; +const auto minutes_symbol = bp::no_case[ bp::lit('\'') | bp::lit("minutes") | bp::lit("min") | bp::lit('m') ]; +const auto seconds_symbol = bp::no_case[ bp::lit('"') | bp::lit("seconds") | bp::lit("sec") | bp::lit('s') ]; + +const auto uint_0_60_def = bp::uint_ [( [](auto ctx) { _pass(ctx) = _attr(ctx) < 60U; _val(ctx) = _attr(ctx); } )]; +const auto double_0_60_def = bp::double_ [( [](auto ctx) { _pass(ctx) = _attr(ctx) < 60; _val(ctx) = _attr(ctx); } )]; + +const auto decimal_degrees = bp::double_ >> -degrees_symbol; + +const auto degrees_decimal_minutes_def = (bp::uint_ >> -degrees_symbol + >> (double_0_60 - '.') >> -minutes_symbol) [( [](auto ctx) { + auto d = _attr(ctx)[0_c]; + auto m = _attr(ctx)[1_c]; + _val(ctx) = d + m/60.0; +} )]; + +const auto degrees_minutes_seconds_def = (bp::uint_ >> -degrees_symbol + >> uint_0_60 >> -minutes_symbol + >> (double_0_60 - '.') >> -seconds_symbol) [( [](auto ctx) { + auto d = _attr(ctx)[0_c]; + auto m = _attr(ctx)[1_c]; + auto s = _attr(ctx)[2_c]; + _val(ctx) = d + m/60.0 + s/3600.0; +} )]; + +const auto degrees_def = degrees_minutes_seconds + | degrees_decimal_minutes + | decimal_degrees; + +const auto northsouth = bp::no_case[ bp::char_("ns") ]; +const auto eastwest = bp::no_case[ bp::char_("ew") ]; + +const auto latitude_def = (degrees >> northsouth) [( [](auto ctx) { + auto d = _attr(ctx)[0_c]; + auto ns = _attr(ctx)[1_c]; + _pass(ctx) = d <= 90; + _val(ctx) = ns=='S' || ns=='s' ? -d : d; +} )]; + +const auto longitude_def = (degrees >> eastwest) [( [](auto ctx) { + auto d = _attr(ctx)[0_c]; + auto ew = _attr(ctx)[1_c]; + _pass(ctx) = d <= 180; + _val(ctx) = ew=='W' || ew=='w' ? -d : d; +} )]; + +const auto signed_degrees = bp::double_ >> -degrees_symbol; + +const auto signed_latitude_def = signed_degrees [( [](auto ctx) { auto d = _attr(ctx); _pass(ctx) = -90 <= d && d <= 90; _val(ctx) = _attr(ctx); } )]; +const auto signed_longitude_def = signed_degrees [( [](auto ctx) { auto d = _attr(ctx); _pass(ctx) = -180 <= d && d <= 180; _val(ctx) = _attr(ctx); } )]; + +const auto latlon_def = ((latitude >> longitude) [( [](auto ctx) { _val(ctx) = g2d::Vector{_attr(ctx)[1_c], _attr(ctx)[0_c]}; } )] ) + | ((longitude >> latitude) [( [](auto ctx) { _val(ctx) = g2d::Vector{_attr(ctx)[0_c], _attr(ctx)[1_c]}; } )] ) + | ((signed_longitude >> signed_latitude) + [( [](auto ctx) { _val(ctx) = g2d::Vector{_attr(ctx)[0_c], _attr(ctx)[1_c]}; } )] ); + +BOOST_PARSER_DEFINE_RULES(uint_0_60); +BOOST_PARSER_DEFINE_RULES(double_0_60); +BOOST_PARSER_DEFINE_RULES(degrees_decimal_minutes); +BOOST_PARSER_DEFINE_RULES(degrees_minutes_seconds); +BOOST_PARSER_DEFINE_RULES(degrees); +BOOST_PARSER_DEFINE_RULES(latitude); +BOOST_PARSER_DEFINE_RULES(longitude); +BOOST_PARSER_DEFINE_RULES(signed_latitude); +BOOST_PARSER_DEFINE_RULES(signed_longitude); +BOOST_PARSER_DEFINE_RULES(latlon); + + +#if 0 +static std::optional try_parse_latlon(std::string_view s) +{ + auto input = latlon >> bp::eoi; + return parse(s,input, bp::ws|bp::lit(',')); +} + + +int main(int argc, const char* argv[]) +{ + assert(argc==2); + + auto opt_coords = try_parse_latlon(argv[1]); + + if (!opt_coords) { + std::cerr << "parse error\n"; + return 1; + } else { + std::cout << opt_coords->x << " " << opt_coords->y << "\n"; + return 0; + } +} +#endif + +TEST(parse_coords, all_examples) +{ + std::vector test_coords = { + "12.34 N, 56.78 E", + "56.78,-12.34", + "12d 30' n 45d 15' 7\" w", + "12 30 45 N, 45 15 7 W", + "12d 30m 15s S 50d 59m 59s W", + "50d 0.5m n 50d 59m 59s W"}; + + { + auto result = bp::parse(test_coords[0], latlon, bp::ws | bp::lit(',')); + EXPECT_TRUE(result); + EXPECT_LT(std::abs(result->x - 56.78), 0.001); + EXPECT_LT(std::abs(result->y - 12.34), 0.001); + } + + { + auto result = bp::parse(test_coords[1], latlon, bp::ws | bp::lit(',')); + EXPECT_TRUE(result); + EXPECT_LT(std::abs(result->x - 56.78), 0.001); + EXPECT_LT(std::abs(result->y - -12.34), 0.001); + } + + { + auto result = bp::parse(test_coords[2], latlon, bp::ws | bp::lit(',')); + EXPECT_TRUE(result); + EXPECT_LT(std::abs(result->x - -45.2519), 0.001); + EXPECT_LT(std::abs(result->y - 12.5), 0.001); + } + + { + auto result = bp::parse(test_coords[3], latlon, bp::ws | bp::lit(',')); + EXPECT_TRUE(result); + EXPECT_LT(std::abs(result->x - -45.2519), 0.001); + EXPECT_LT(std::abs(result->y - 12.5125), 0.001); + } + + { + auto result = bp::parse(test_coords[4], latlon, bp::ws | bp::lit(',')); + EXPECT_TRUE(result); + EXPECT_LT(std::abs(result->x - -50.9997), 0.001); + EXPECT_LT(std::abs(result->y - -12.5042), 0.001); + } + + { + auto result = bp::parse(test_coords[5], latlon, bp::ws | bp::lit(',')); + EXPECT_TRUE(result); + EXPECT_LT(std::abs(result->x - -50.9997), 0.001); + EXPECT_LT(std::abs(result->y - 50.0083), 0.001); + } +} + +#endif