├── .gitignore ├── LICENSE_1_0.txt ├── README.md ├── include └── simple_match │ ├── boost │ ├── any.hpp │ ├── lexical_cast.hpp │ ├── optional.hpp │ └── variant.hpp │ ├── implementation │ └── some_none.hpp │ ├── regex.hpp │ ├── simple_match.hpp │ └── utility.hpp └── test ├── cppcon_feedback.cpp └── test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | -------------------------------------------------------------------------------- /LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple, Extensible C++ Pattern Matching Library 2 | 3 | I have recently been looking at Haskell and Rust. One of the things I wanted in C++ from those languages is pattern matching. 4 | 5 | Here is an example from the Rustlang Book (http://static.rust-lang.org/doc/master/book/match.html) 6 | 7 | ```rust 8 | match x { 9 | 1 => println!("one"), 10 | 2 => println!("two"), 11 | 3 => println!("three"), 12 | 4 => println!("four"), 13 | 5 => println!("five"), 14 | _ => println!("something else"), 15 | } 16 | ``` 17 | There is currently a C++ Library Mach7 that does pattern matching (https://github.com/solodon4/Mach7), however it is big, complicated, and uses a lot of macros. I wanted to see if I could use C++14 to write a simple implementation without macros. 18 | 19 | This library is the result of that effort. If you are familiar with C++14 especially variadic templates, forwarding, and tuples, this library and implementation should be easy for you to understand and extend. 20 | 21 | ## Usage 22 | You will need a C++14 compiler. I have used the latest Visual C++ 2015 CTP, GCC 4.9.2, and Clang 3.5 to test this library. 23 | 24 | The library consists of 2 headers. `simple_match.hpp` which is the core of the library, and `some_none.hpp` which contains code that lets you match on raw pointers, and unique_ptr, and shared_ptr. 25 | 26 | Here is a simple excerpt. Assume you have included simple_match.hpp 27 | 28 | ```cpp 29 | using namespace simple_match; 30 | using namespace simple_match::placeholders; 31 | 32 | int x = 0; 33 | 34 | while (true) { 35 | std::cin >> x; 36 | match(x, 37 | 1, []() {std::cout << "The answer is one\n"; }, 38 | 2, []() {std::cout << "The answer is two\n"; }, 39 | _x < 10, [](auto&& a) {std::cout << "The answer " << a << " is less than 10\n"; }, 40 | 10 < _x < 20, [](auto&& a) {std::cout << "The answer " << a << " is between 10 and 20 exclusive\n"; }, 41 | _, []() {std::cout << "Did not match\n"; } 42 | ); 43 | } 44 | ``` 45 | 46 | ## Example Files 47 | There are 2 files under the test directory: `test.cpp` and `cppcon-matching.cpp`. `test.cpp` contains just some simple tests of matching. `cppcon-matching.cpp`contains the example from Mach7 that was presented at cppcon. 48 | 49 | ## Extending 50 | There are 2 points of customization provided in namespace `simple_matcher::customization`. They are 51 | ```cpp 52 | template 53 | struct matcher; 54 | ``` 55 | 56 | and 57 | 58 | ```cpp 59 | template 60 | struct tuple_adapter; 61 | ``` 62 | ## License 63 | Licensed under the Boost Software License. 64 | 65 | ## Tutorial 66 | 67 | We are going to assume you have the following at the top of your file 68 | 69 | ```cpp 70 | #include "simple_match/simple_match.hpp" 71 | 72 | 73 | using namespace simple_match; 74 | using namespace simple_match::placeholders; 75 | ``` 76 | 77 | Here is how to match exactly 78 | 79 | ```cpp 80 | int i = 0; 81 | match(i, 82 | 1, [](){std::cout << "The answer is one";} 83 | 2, [](){std::cout << "The answer is two";} 84 | otherwise, [](){std::cout << "Did not match"} 85 | ); 86 | ``` 87 | The match function will try matching from top to bottom and run the lamba corresponding to the first successful match. `otherwise` always matches, and therefore you should have it at the end. If you find `otherwise` too long, you can also use `_`. It is located in the namespace `simple_match::placeholders` 88 | 89 | Match also works for strings. 90 | ``` 91 | std::string s = ""; 92 | 93 | match(s, 94 | "Hello", [](){ std::cout << " You said hello\n";}, 95 | _, [](){std::cout << "I do not know what you said\n";} // _ is the same as otherwise 96 | ); 97 | 98 | ``` 99 | 100 | 101 | You can even return values from a match 102 | 103 | ```cpp 104 | char digit = '0'; 105 | 106 | int value = match(digit, 107 | '0', [](){return 0;}, 108 | '1', [](){return 1;}, 109 | '2', [](){return 2;}, 110 | '3', [](){return 3;}, 111 | '4', [](){return 4;}, 112 | '5', [](){return 5;}, 113 | // and so on 114 | ); 115 | ``` 116 | 117 | We can also do comparisons, and ranges. To do so use `_x` from the `simple_match::placeholders` namespace. 118 | 119 | ```cpp 120 | int i = 0; 121 | match(i, 122 | _x < 0, [](int x){std::cout << x << " is a negative number\n";}, 123 | 1 < _x < 10, [](int z){std::cout << z << " is between 1 and 10\n"}, 124 | _x, [](int x){std::cout << x << " is the value\n";} 125 | ); 126 | 127 | ``` 128 | There are a some items of interest in the above example. When `_x` is used, it passes its value to the lambda. If `_x` is used without any comparison, it will pass the value to the lambda. Also, because of the way it is overloaded, it is very easy to make ranges using the `<` or `<=` operator as seen in the match above. 129 | 130 | ### Tuples 131 | 132 | Now we can even have more fun! Let's represent a 2d point as a tuple. 133 | 134 | ```cpp 135 | std::tuple p(0,0); 136 | 137 | match(p, 138 | ds(0,0), [](){std::cout << "Point is at the origin";}, 139 | ds(0,_), [](){std::cout << "Point is on the horizontal axis";}, 140 | ds(_,0), [](){std::cout << "Point is on the vertical axis";}. 141 | ds(_x < 10,_), [](int x){std::cout << x << " is less than 10";}, 142 | ds(_x,_x), [](int x, int y){ std::cout << x << "," << y << " Is not on an axis";} 143 | ); 144 | ``` 145 | 146 | `ds` stands for de-structure and splits a tuple into its parts. Notice you can use the same expressions as you could without tuples. As before `_x` results in a value being passed to the lambda. `_` matches anything and ignores it, so no corresponding variable is passed to the lambda. 147 | 148 | We can actually use `ds` to deconstruct our own `struct`s and `class`es . 149 | First we have to specialize `simple_match::customization::tuple_adapter` for our type. 150 | 151 | ```cpp 152 | struct point { 153 | int x; 154 | int y; 155 | point(int x_,int y_):x(x_),y(y_){} 156 | }; 157 | 158 | // Adapting point to be used with ds 159 | namespace simple_match { 160 | namespace customization { 161 | template<> 162 | struct tuple_adapter{ 163 | 164 | enum { tuple_len = 2 }; 165 | 166 | template 167 | static decltype(auto) get(T&& t) { 168 | return std::get(std::tie(t.x,t.y)); 169 | } 170 | }; 171 | } 172 | } 173 | ``` 174 | 175 | Then we can use `ds` like we did with a tuple. 176 | 177 | ```cpp 178 | point p{0,0}; 179 | 180 | match(p, 181 | ds(0,0), [](){std::cout << "Point is at the origin";}, 182 | ds(0,_), [](){std::cout << "Point is on the horizontal axis";}, 183 | ds(_,0), [](){std::cout << "Point is on the vertical axis";}. 184 | ds(_x < 10,_), [](int x){std::cout << x << " is less than 10";}, 185 | ds(_x,_x), [](int x, int y){ std::cout << x << "," << y << " Is not on an axis";} 186 | ); 187 | ``` 188 | 189 | ### Pointers as option types 190 | Sometimes we have pointer that we want to get a value safely out of. To do this we can use `some` and `none` . To do this we have to include `simple_match/some_none.hpp` 191 | 192 | Let us use the same `point` as before 193 | 194 | ```cpp 195 | point* pp = new point(0,0); 196 | 197 | match(pp, 198 | some(), [](point& p){std::cout << p.x << " is the x-value";} 199 | none(), [](){std::cout << "Null pointer\n";} 200 | ); 201 | ``` 202 | 203 | Notice how `some()` converted the pointer to a reference and passed it to us. 204 | 205 | Now, that is now how we should allocate memory with a naked new. We would probably use a `std::unique_ptr`. `some` has built in support for `unique_ptr` and `shared_ptr`. So we can write it like this. 206 | 207 | ```cpp 208 | auto pp = std::make_unique(0,0); 209 | 210 | match(pp, 211 | some(), [](point& p){std::cout << p.x << " is the x-value";} 212 | none(), [](){std::cout << "Null pointer\n";} 213 | ); 214 | ``` 215 | Notice, how our match code did not change. 216 | 217 | We can do better because `some` composes. Since we specialized `tuple_adapter` we can use `ds` with `point`. 218 | 219 | ```cpp 220 | auto pp = std::make_unique(0,0); 221 | 222 | match(pp, 223 | some(ds(0,0)), [](){std::cout << "Point is at the origin";}, 224 | some(ds(0,_)), [](){std::cout << "Point is on the horizontal axis";}, 225 | some(ds(_,0)), [](){std::cout << "Point is on the vertical axis";}. 226 | some(ds(_x < 10,_)), [](int x){std::cout << x << " is less than 10";}, 227 | some(ds(_x,_x)), [](int x, int y){ std::cout << x << "," << y << " Is not on an axis";}, 228 | none(), [](){std::cout << "Null pointer";} 229 | ); 230 | ``` 231 | Notice how `some` and `ds` compose. If we wanted to to, we could have pointers in tuples, and tuples in pointers and it would just work. 232 | 233 | `some` can also use RTTI to do downcasting. 234 | 235 | Here is an example. We will now make `point` a base class and have point2d, and point3d as subclasses, and adapt them. 236 | 237 | ```cpp 238 | struct point{ 239 | virtual ~point(){} 240 | }; 241 | 242 | struct point2d:point{ 243 | int x; 244 | int y; 245 | point2d(int x_,int y_):x(x_),y(y_){} 246 | }; 247 | 248 | struct point3d:point{ 249 | int x; 250 | int y; 251 | int z; 252 | point3d(int x_,int y_, int z_):x(x_),y(y_),z(z_){} 253 | }; 254 | 255 | // Adapting point2d and point3d to be used with ds 256 | namespace simple_match { 257 | namespace customization { 258 | template<> 259 | struct tuple_adapter{ 260 | 261 | enum { tuple_len = 2 }; 262 | 263 | template 264 | static decltype(auto) get(T&& t) { 265 | return std::get(std::tie(t.x,t.y)); 266 | } 267 | }; 268 | template<> 269 | struct tuple_adapter{ 270 | 271 | enum { tuple_len = 3 }; 272 | 273 | template 274 | static decltype(auto) get(T&& t) { 275 | return std::get(std::tie(t.x,t.y,t.z)); 276 | } 277 | }; 278 | } 279 | } 280 | ``` 281 | 282 | Then we can use it like this 283 | 284 | ```cpp 285 | std::unique_ptr pp(new point2d(0,0)); 286 | 287 | match(pp, 288 | some(ds(_x,_x)), [](int x, int y){std::cout << x << "," << y;}, 289 | some(ds(_x,_x,_x)), [](int x, int y, int z){std::cout << x << "," << y << "," << z;}, 290 | some(), [](point& p){std::cout << "Unknown point type\n"}, 291 | none(), [](){std::cout << "Null pointer\n"} 292 | ); 293 | 294 | ``` 295 | 296 | Notice how we can safely downcast, and use `ds` to destructure the `point`. Everything composes nicely. 297 | 298 | # Implementation Details 299 | 300 | simple_match actually was easier to implement than I thought it would be. I used the apply sample implementation from http://isocpp.org/files/papers/N3915.pdf to call a function with a tuple as arguments. 301 | 302 | Here is the core of the implementation 303 | 304 | ```cpp 305 | template 306 | bool match_check(T&& t, U&& u) { 307 | using namespace customization; 308 | using m = matcher, std::decay_t>; 309 | return m::check(std::forward(t), std::forward(u)); 310 | } 311 | 312 | 313 | template 314 | auto match_get(T&& t, U&& u) { 315 | using namespace customization; 316 | using m = matcher, std::decay_t>; 317 | return m::get(std::forward(t), std::forward(u)); 318 | } 319 | 320 | template 321 | auto match(T&& t, A1&& a, F1&& f) { 322 | if (match_check(std::forward(t), std::forward(a))) { 323 | return detail::apply(f, match_get(std::forward(t), std::forward(a))); 324 | } 325 | else { 326 | throw std::logic_error("No match"); 327 | } 328 | } 329 | 330 | 331 | template 332 | auto match(T&& t, A1&& a, F1&& f, A2&& a2, F2&& f2, Args&&... args) { 333 | if (match_check(t, a)) { 334 | return detail::apply(f, match_get(std::forward(t), std::forward(a))); 335 | } 336 | else { 337 | return match(t, std::forward(a2), std::forward(f2), std::forward(args)...); 338 | } 339 | } 340 | ``` 341 | 342 | `match` is a variadic function that takes the value to be matched, and then parameters for the match criteria, and lambda to be executed if the criteria succeeds. It goes through calling `match_check` until it returns true. Then it calls `match_get` to get a tuple of the values that need to be forwarded to the lambda, and uses apply to call the lambda. 343 | 344 | The match types are implemented by specializing `simple_match::customization::matcher` 345 | ```cpp 346 | namespace customization { 347 | template 348 | struct matcher; 349 | } 350 | ``` 351 | 352 | For an example, here is how the matcher that matches to values is implemented. Note, that it does not pass any values to the lambda and so returns an empty tuple. 353 | 354 | ```cpp 355 | // Match same type 356 | template 357 | struct matcher { 358 | static bool check(const T& t, const T& v) { 359 | return t == v; 360 | } 361 | static auto get(const T&, const T&) { 362 | return std::tie(); 363 | } 364 | } 365 | ``` 366 | 367 | I hope you enjoy using this code, as much as I have enjoyed writing it. Please give me any feedback you may have. 368 | 369 | -- John R. Bandela, MD 370 | 371 | > Written with [StackEdit](https://stackedit.io/). 372 | -------------------------------------------------------------------------------- /include/simple_match/boost/any.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 John R. Bandela 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #pragma once 7 | 8 | #ifndef SIMPLE_MATCH_ANY_HPP_JRB_2015_09_11 9 | #define SIMPLE_MATCH_ANY_HPP_JRB_2015_09_11 10 | #include 11 | #include "../simple_match.hpp" 12 | namespace simple_match { 13 | 14 | // tagged_any 15 | 16 | template 17 | struct tagged_any :boost::any { 18 | template 19 | tagged_any(A&& ...a) :boost::any{std::forward(a)...} {} 20 | }; 21 | 22 | namespace customization { 23 | template<> 24 | struct pointer_getter { 25 | template 26 | static auto get_pointer(T&& t) { 27 | return boost::any_cast(&t); 28 | } 29 | template 30 | static auto is_null(T&& t) { 31 | return t.empty(); 32 | } 33 | }; 34 | template 35 | struct pointer_getter> { 36 | template 37 | static auto get_pointer(T&& t) { 38 | return boost::any_cast(&t); 39 | } 40 | }; 41 | template 42 | static auto is_null(T&& t) { 43 | return t.empty(); 44 | } 45 | 46 | 47 | } 48 | 49 | 50 | } 51 | 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /include/simple_match/boost/lexical_cast.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../simple_match.hpp" 3 | 4 | #include 5 | 6 | #include 7 | namespace simple_match { 8 | 9 | namespace detail { 10 | 11 | template 12 | struct lexical_cast_t { 13 | Type t_; 14 | Matcher m_; 15 | 16 | template 17 | bool check(T&& t) { 18 | return boost::conversion::try_lexical_convert(std::forward(t), t_); 19 | } 20 | 21 | template 22 | auto get(T&& ) { 23 | return match_get(t_, m_); 24 | } 25 | 26 | lexical_cast_t(Matcher m) :m_{ std::move(m) } {} 27 | }; 28 | template 29 | struct lexical_cast_t { 30 | Type t_; 31 | 32 | template 33 | bool check(T&& t) { 34 | return boost::conversion::try_lexical_convert(std::forward(t), t_); 35 | } 36 | 37 | template 38 | auto get(T&& ) { 39 | return std::tie(); 40 | } 41 | }; 42 | 43 | 44 | } 45 | 46 | namespace customization { 47 | template 48 | struct matcher> { 49 | template 50 | static bool check(T&& t, U&& u) { 51 | return u.check(std::forward(t)); 52 | } 53 | 54 | template 55 | static auto get(T&& t, U&& u ) { 56 | return u.get(std::forward(t)); 57 | } 58 | }; 59 | } 60 | 61 | template 62 | detail::lexical_cast_t lexical_cast() { 63 | return {}; 64 | } 65 | 66 | template 67 | detail::lexical_cast_t lexical_cast(Matcher&& m) { 68 | return{ std::forward(m) }; 69 | } 70 | }; 71 | -------------------------------------------------------------------------------- /include/simple_match/boost/optional.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 John R. Bandela 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #pragma once 7 | 8 | #ifndef SIMPLE_MATCH_OPTIONAL_HPP_JRB_2015_09_11 9 | #define SIMPLE_MATCH_OPTIONAL_HPP_JRB_2015_09_11 10 | #include 11 | #include "../simple_match.hpp" 12 | namespace simple_match { 13 | 14 | namespace customization { 15 | template 16 | struct pointer_getter> { 17 | template 18 | static auto get_pointer_no_cast(T&& t) { 19 | return t.get_ptr(); 20 | } 21 | template 22 | static auto is_null(T&& t) { 23 | return !t; 24 | } 25 | }; 26 | } 27 | } 28 | 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/simple_match/boost/variant.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 John R. Bandela 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #pragma once 7 | 8 | #ifndef SIMPLE_MATCH_VARIANT_HPP_JRB_2015_09_11 9 | #define SIMPLE_MATCH_VARIANT_HPP_JRB_2015_09_11 10 | #include 11 | #include "../simple_match.hpp" 12 | namespace simple_match { 13 | namespace detail { 14 | template 15 | struct extract_variant_types{}; 16 | 17 | template 18 | struct extract_single_type { 19 | using type = std::tuple; 20 | }; 21 | 22 | template 23 | struct extract_single_type> { 24 | using type = std::tuple; 25 | }; 26 | 27 | // NB: dependence on boost::variant implementation detail 28 | template<> 29 | struct extract_single_type { 30 | using type = std::tuple<>; 31 | }; 32 | 33 | 34 | 35 | template 36 | struct extract_variant_types_helper { 37 | }; 38 | 39 | template 40 | struct extract_variant_types_helper { 41 | using type = cat_tuple_t::type, typename extract_variant_types_helper::type>; 42 | }; 43 | 44 | template<> 45 | struct extract_variant_types_helper<> { 46 | using type = std::tuple<>; 47 | }; 48 | 49 | template 50 | struct some_exhaustiveness_variant_generator {}; 51 | 52 | template 53 | struct some_exhaustiveness_variant_generator> { 54 | using type = some_exhaustiveness; 55 | 56 | }; 57 | 58 | template 59 | struct extract_variant_types> { 60 | using type = typename extract_variant_types_helper::type; 61 | 62 | }; 63 | 64 | 65 | 66 | 67 | } 68 | 69 | namespace customization { 70 | template 71 | struct pointer_getter> { 72 | template 73 | static auto get_pointer(T&& t) { 74 | return boost::get(&t); 75 | } 76 | }; 77 | 78 | 79 | template 80 | struct exhaustiveness_checker> { 81 | using vtypes = typename detail::extract_variant_types>::type; 82 | using type = typename detail::some_exhaustiveness_variant_generator::type; 83 | }; 84 | 85 | } 86 | 87 | } 88 | 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /include/simple_match/implementation/some_none.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 John R. Bandela 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #pragma once 7 | #ifndef SIMPLE_MATCH_SOME_NONE_HPP_JRB_2015_03_21 8 | #define SIMPLE_MATCH_SOME_NONE_HPP_JRB_2015_03_21 9 | #include 10 | #include 11 | 12 | namespace simple_match { 13 | 14 | namespace customization { 15 | 16 | template 17 | struct pointer_getter {}; 18 | 19 | 20 | } 21 | 22 | namespace detail { 23 | 24 | template 25 | struct cv_helper_imp; 26 | 27 | template 28 | struct cv_helper_imp { 29 | using type = std::add_pointer_t; 30 | }; 31 | 32 | template 33 | struct cv_helper_imp { 34 | using type = std::add_pointer_t>; 35 | }; 36 | 37 | template 38 | struct cv_helper_imp { 39 | using type = std::add_pointer_t>; 40 | }; 41 | 42 | template 43 | struct cv_helper_imp { 44 | using type = std::add_pointer_t>; 45 | }; 46 | 47 | } 48 | namespace utils { 49 | template 50 | using cv_helper = typename detail::cv_helper_imp::type; 51 | 52 | } 53 | 54 | namespace detail{ 55 | 56 | 57 | template 58 | struct some_t{ 59 | Matcher m_; 60 | 61 | template 62 | bool check(T&& t) { 63 | auto ptr = customization::pointer_getter>::template get_pointer(std::forward(t)); 64 | if (!ptr) { 65 | return false; 66 | } 67 | return match_check(*ptr, m_); 68 | 69 | } 70 | 71 | template 72 | auto get(T&& t) { 73 | auto ptr = customization::pointer_getter>::template get_pointer(std::forward(t)); 74 | return match_get(*ptr, m_); 75 | } 76 | 77 | 78 | }; 79 | 80 | template 81 | struct some_t { 82 | Matcher m_; 83 | 84 | template 85 | bool check(T&& t) { 86 | // If you get an error here, this means that some() without a type is not supported 87 | // Examples of this are variants and boost::any 88 | auto ptr = customization::pointer_getter>::get_pointer_no_cast(std::forward(t)); 89 | if (!ptr) { 90 | return false; 91 | } 92 | return match_check(*ptr, m_); 93 | 94 | } 95 | 96 | template 97 | auto get(T&& t) { 98 | auto ptr = (customization::pointer_getter>::get_pointer_no_cast(std::forward(t))); 99 | return match_get(*ptr, m_); 100 | } 101 | 102 | 103 | }; 104 | 105 | template 106 | struct some_t { 107 | 108 | template 109 | bool check(T&& t) { 110 | auto ptr = customization::pointer_getter>::template get_pointer(std::forward(t)); 111 | if (!ptr) { 112 | return false; 113 | } 114 | return true; 115 | 116 | } 117 | 118 | template 119 | auto get(T&& t) { 120 | auto ptr = customization::pointer_getter>::template get_pointer(std::forward(t)); 121 | return std::tie(*ptr); 122 | } 123 | 124 | 125 | }; 126 | 127 | template<> 128 | struct some_t { 129 | 130 | template 131 | bool check(T&& t) { 132 | // If you get an error here, this means that some() without a type is not supported 133 | // Examples of this are variants and boost::any 134 | auto ptr = customization::pointer_getter>::get_pointer_no_cast(std::forward(t)); 135 | if (!ptr) { 136 | return false; 137 | } 138 | return true; 139 | 140 | } 141 | 142 | template 143 | auto get(T&& t) { 144 | auto ptr = customization::pointer_getter>::get_pointer_no_cast(std::forward(t)); 145 | return std::tie(*ptr); 146 | } 147 | 148 | 149 | }; 150 | 151 | 152 | struct none_t{ 153 | template 154 | bool check(T&& t) { 155 | // If you get an error here, this means that none() is not supported 156 | // Example is boost::variant which has a never empty guarantee 157 | return customization::pointer_getter>::is_null(std::forward(t)); 158 | } 159 | 160 | template 161 | auto get(T&&) { 162 | return std::tie(); 163 | } 164 | }; 165 | 166 | } 167 | namespace customization { 168 | 169 | template 170 | struct matcher> { 171 | template 172 | static bool check(T&& t, U&& u) { 173 | return u.check(std::forward(t)); 174 | } 175 | template 176 | static auto get(T&& t, U&& u) { 177 | return u.get(std::forward(t)); 178 | } 179 | 180 | }; 181 | 182 | template 183 | struct matcher { 184 | template 185 | static bool check(T&& t, U&& u) { 186 | return u.check(std::forward(t)); 187 | } 188 | template 189 | static auto get(T&& t, U&& u) { 190 | return u.get(std::forward(t)); 191 | } 192 | 193 | }; 194 | } 195 | namespace customization { 196 | template 197 | struct pointer_getter { 198 | template 199 | static auto get_pointer(Type* t) { 200 | return dynamic_cast>(t); 201 | } 202 | static auto get_pointer_no_cast(Type* t) { 203 | return t; 204 | } 205 | static auto is_null(Type* t) { 206 | return !t; 207 | } 208 | }; 209 | 210 | 211 | template 212 | struct pointer_getter> { 213 | 214 | template 215 | static auto get_pointer(T&& t) { 216 | return dynamic_cast>(t.get()); 217 | } 218 | template 219 | static auto get_pointer_no_cast(T&& t) { 220 | return t.get(); 221 | } 222 | template 223 | static auto is_null(T&& t) { 224 | return !t; 225 | } 226 | }; 227 | 228 | template 229 | struct pointer_getter> { 230 | template 231 | static auto get_pointer(T&& t) { 232 | return dynamic_cast>(t.get()); 233 | } 234 | template 235 | static auto get_pointer_no_cast(T&& t) { 236 | return t.get(); 237 | } 238 | template 239 | static auto is_null(T&& t) { 240 | return !t; 241 | } 242 | }; 243 | 244 | } 245 | 246 | 247 | inline detail::none_t none() { return detail::none_t{}; } 248 | 249 | inline detail::some_t some() { return detail::some_t{}; } 250 | 251 | // Make Matcher... a variadic template so it is a worse match than some(T()) 252 | template 253 | detail::some_t some(Matcher&&... m) { return detail::some_t { std::forward(m)... }; } 254 | 255 | template 256 | detail::some_t some(Matcher&& m) { 257 | return detail::some_t { std::forward(m) }; 258 | } 259 | template 260 | detail::some_t some() { 261 | return detail::some_t{ }; 262 | } 263 | 264 | 265 | // exhaustiveness 266 | 267 | namespace detail { 268 | 269 | template 270 | struct type_in_tuple { 271 | static const bool value = false; 272 | }; 273 | template 274 | struct type_in_tuple> { 275 | static const bool value = type_in_tuple>::value; 276 | }; 277 | template 278 | struct type_in_tuple> { 279 | static const bool value = true; 280 | }; 281 | template 282 | struct type_in_tuple> { 283 | static const bool value = false; 284 | }; 285 | 286 | template 287 | struct has_otherwise { 288 | static const bool value = type_in_tuple::value; 289 | }; 290 | 291 | template 292 | struct some_exhaustiveness_helper { 293 | static const bool value = true; 294 | }; 295 | 296 | template 297 | struct get_some_classes {}; 298 | 299 | template<> 300 | struct get_some_classes> { 301 | using type = std::tuple<>; 302 | }; 303 | 304 | template 305 | struct get_some_classes> { 306 | using type = typename get_some_classes>::type; 307 | 308 | }; 309 | template 310 | struct get_some_classes, Rest...>> { 311 | using type = cat_tuple_t, typename get_some_classes>::type>; 312 | 313 | }; 314 | 315 | template 316 | struct all_in {}; 317 | 318 | template 319 | struct not_in_match_asserter { 320 | static const bool value = b; 321 | static_assert(value, "This type is not in the match"); 322 | }; 323 | 324 | template 325 | struct all_in { 326 | static const bool myvalue = type_in_tuple::value; 327 | static const bool value = not_in_match_asserter::value&& all_in::value; 328 | 329 | }; 330 | template 331 | struct all_in { 332 | static const bool myvalue = type_in_tuple::value; 333 | // static_assert(type_in_tuple::value, "This classes is not in the match"); 334 | static const bool value = not_in_match_asserter::value; 335 | 336 | }; 337 | 338 | 339 | 340 | template 341 | struct some_exhaustiveness_helper { 342 | using some_classes = typename get_some_classes::type; 343 | using a = all_in; 344 | static const bool value = a::value; 345 | 346 | 347 | }; 348 | 349 | 350 | 351 | 352 | 353 | 354 | } 355 | 356 | template 357 | struct some_exhaustiveness { 358 | template 359 | struct type { 360 | static const bool v = detail::has_otherwise::value; 361 | using seh = detail::some_exhaustiveness_helper; 362 | static const bool value = seh::value; 363 | }; 364 | }; 365 | } 366 | 367 | 368 | 369 | #endif 370 | -------------------------------------------------------------------------------- /include/simple_match/regex.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "simple_match.hpp" 3 | #include 4 | #include 5 | #include 6 | 7 | namespace simple_match { 8 | 9 | namespace detail { 10 | 11 | // Reuse our apply_impl for array. We can do this because std::get is specialized for array as well as tuple 12 | template 13 | decltype(auto) apply_array(F&& f, const std::array& t) { 14 | using namespace std; 15 | using Indices = make_index_sequence; 16 | return apply_impl(std::forward(f), t, Indices{}); 17 | } 18 | 19 | template 20 | struct regex_match_t { 21 | S s_; 22 | 23 | template 24 | bool match_search(Iter1 b, Iter2 e, Results& results) { 25 | std::basic_regex r{ s_.data(),s_.size() }; 26 | return std::regex_match(b, e, results, r); 27 | } 28 | 29 | regex_match_t(S&& s) :s_{ std::forward(s) } {} 30 | }; 31 | template 32 | struct regex_match_t> { 33 | using r_t = std::basic_regex; 34 | 35 | const r_t* r_; 36 | 37 | template 38 | bool match_search(Iter1 b, Iter2 e, Results& results) { 39 | return std::regex_match(b, e, results, *r_); 40 | } 41 | 42 | regex_match_t(const r_t& r) :r_{ &r } {} 43 | }; 44 | 45 | template 46 | struct regex_search_t { 47 | S s_; 48 | 49 | template 50 | bool match_search(Iter1 b, Iter2 e, Results& results) { 51 | std::basic_regex r{ s_.data(),s_.size() }; 52 | return std::regex_search(b, e, results, r); 53 | } 54 | 55 | regex_search_t(S&& s) :s_{ std::forward(s) } {} 56 | }; 57 | template 58 | struct regex_search_t> { 59 | using r_t = std::basic_regex; 60 | 61 | const r_t* r_; 62 | 63 | template 64 | bool match_search(Iter1 b, Iter2 e, Results& results) { 65 | return std::regex_search(b, e, results, *r_); 66 | } 67 | 68 | regex_search_t(const r_t& r) :r_{ &r } {} 69 | }; 70 | 71 | 72 | 73 | template 74 | struct regex_t:private R { 75 | std::tuple t_; 76 | enum { len = sizeof...(Ms) }; 77 | std::array ar_; 78 | 79 | template 80 | regex_t(T&& s, M&&... m) :R{ std::move(s) }, t_{ std::forward(m)...,detail::tuple_ignorer{} } {} 81 | 82 | auto get_tuple() { 83 | return detail::apply_array([](auto&... a) {return std::tie(a...);}, ar_); 84 | } 85 | template 86 | bool check(T&& t) { 87 | using std::begin; 88 | std::match_results results; 89 | 90 | if (this->match_search(t.begin(),t.end(), results)) { 91 | // The first sub_match is the whole string; the next 92 | // sub_match is the first parenthesized expression. 93 | if (results.size() != len + 1) { 94 | return false; 95 | } 96 | else{ 97 | for (std::size_t i = 1; i <= ar_.size();++i) { 98 | auto pos = results.position(i); 99 | auto len = results.length(i); 100 | ar_[i - 1] = S( t.data() + pos,len ); 101 | } 102 | return match_check(get_tuple(), t_); 103 | } 104 | } 105 | else { 106 | return false; 107 | } 108 | } 109 | 110 | template 111 | auto get(T&&) { 112 | return match_get(get_tuple(), t_); 113 | } 114 | 115 | }; 116 | 117 | } 118 | 119 | namespace customization { 120 | template 121 | struct matcher> { 122 | template 123 | static bool check(T&& t, U&& u) { 124 | return u.check(std::forward(t)); 125 | } 126 | 127 | template 128 | static auto get(T&& t, U&& u) { 129 | return u.get(std::forward(t)); 130 | } 131 | }; 132 | } 133 | 134 | 135 | template 136 | detail::regex_t>, boost::basic_string_ref, M...> rex_match(boost::basic_string_ref s, M&&... m) { return{ std::move(s),std::forward(m)... }; } 137 | 138 | 139 | template 140 | detail::regex_t>, boost::basic_string_ref, M...> 141 | rex_match(const Char* s, M&&... m) { return{ s,std::forward(m)... }; } 142 | 143 | 144 | template 145 | detail::regex_t>, boost::basic_string_ref, M...> rex_match(std::basic_regex& r, M&&... m) { return{ r,std::forward(m)... }; } 146 | 147 | template 148 | detail::regex_t>, boost::basic_string_ref, M...> rex_search(boost::basic_string_ref s, M&&... m) { return{ std::move(s),std::forward(m)... }; } 149 | 150 | 151 | template 152 | detail::regex_t>, boost::basic_string_ref, M...> 153 | rex_search(const Char* s, M&&... m) { return{ s,std::forward(m)... }; } 154 | 155 | 156 | template 157 | detail::regex_t>, boost::basic_string_ref, M...> rex_search(std::basic_regex& r, M&&... m) { return{ r,std::forward(m)... }; } 158 | 159 | 160 | 161 | } 162 | -------------------------------------------------------------------------------- /include/simple_match/simple_match.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 John R. Bandela 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #pragma once 7 | #ifndef SIMPLE_MATCH_HPP_JRB_2015_03_21 8 | #define SIMPLE_MATCH_HPP_JRB_2015_03_21 9 | 10 | #include 11 | #include 12 | 13 | namespace simple_match { 14 | using std::size_t; 15 | namespace customization { 16 | template 17 | struct matcher; 18 | 19 | } 20 | 21 | // Apply adapted from http://isocpp.org/files/papers/N3915.pdf 22 | namespace detail { 23 | template 24 | decltype(auto) apply_impl(F&& f, Tuple&& t, std::integer_sequence) { 25 | using namespace std; 26 | return std::forward(f)(get(std::forward(t))...); 27 | } 28 | template 29 | decltype(auto) apply(F&& f, Tuple&& t) { 30 | using namespace std; 31 | using Indices = make_index_sequence>::value>; 32 | return apply_impl(std::forward(f), std::forward(t), Indices{}); 33 | } 34 | 35 | 36 | } 37 | 38 | // exhaustiveness 39 | namespace detail { 40 | template 41 | struct cat_tuple {}; 42 | 43 | template 44 | struct cat_tuple,std::tuple> { 45 | using type = std::tuple; 46 | 47 | }; 48 | 49 | template 50 | using cat_tuple_t = typename cat_tuple::type; 51 | 52 | template 53 | struct arg_types { 54 | 55 | }; 56 | 57 | template 58 | struct arg_types { 59 | using type = std::tuple>; 60 | }; 61 | 62 | 63 | template 64 | struct arg_types{ 65 | using type = cat_tuple_t, std::decay_t>, typename arg_types::type>; 66 | 67 | }; 68 | 69 | template<> 70 | struct arg_types<>{ 71 | using type = std::tuple<>; 72 | 73 | }; 74 | 75 | 76 | } 77 | struct empty_exhaustiveness { 78 | template 79 | struct type{ 80 | static const bool value = true; 81 | }; 82 | }; 83 | namespace customization { 84 | 85 | template 86 | struct exhaustiveness_checker { 87 | using type = empty_exhaustiveness; 88 | }; 89 | } 90 | 91 | // end exhaustiveness 92 | 93 | template 94 | bool match_check(T&& t, U&& u) { 95 | using namespace customization; 96 | using m = matcher, std::decay_t>; 97 | return m::check(std::forward(t), std::forward(u)); 98 | } 99 | 100 | 101 | template 102 | auto match_get(T&& t, U&& u) { 103 | using namespace customization; 104 | using m = matcher, std::decay_t>; 105 | return m::get(std::forward(t), std::forward(u)); 106 | } 107 | 108 | 109 | struct no_match :std::logic_error { 110 | no_match() :logic_error{ "simple_match did not match" } {} 111 | }; 112 | 113 | namespace detail { 114 | 115 | template 116 | auto match_helper(T&& t, A1&& a, F1&& f) { 117 | if (match_check(std::forward(t), std::forward(a))) { 118 | return detail::apply(f, match_get(std::forward(t), std::forward(a))); 119 | } 120 | else { 121 | throw no_match{}; 122 | } 123 | } 124 | 125 | 126 | template 127 | auto match_helper(T&& t, A1&& a, F1&& f, A2&& a2, F2&& f2, Args&&... args) { 128 | if (match_check(t, a)) { 129 | return detail::apply(f, match_get(std::forward(t), std::forward(a))); 130 | } 131 | else { 132 | return match_helper(t, std::forward(a2), std::forward(f2), std::forward(args)...); 133 | } 134 | } 135 | 136 | } 137 | 138 | template 139 | auto match(T&& t, Args&&... a) { 140 | using atypes = typename detail::arg_types::type; 141 | using ec = typename customization::exhaustiveness_checker>::type; 142 | using ctypes = typename ec::template type; 143 | static_assert(ctypes::value, "Not all types are tested for in match"); 144 | return detail::match_helper(std::forward(t), std::forward(a)...); 145 | } 146 | 147 | 148 | 149 | 150 | struct otherwise_t {}; 151 | 152 | namespace placeholders { 153 | const otherwise_t otherwise{}; 154 | const otherwise_t _{}; 155 | } 156 | 157 | namespace customization { 158 | 159 | // Match same type 160 | template 161 | struct matcher { 162 | static bool check(const T& t, const T& v) { 163 | return t == v; 164 | } 165 | static auto get(const T&, const T&) { 166 | return std::tie(); 167 | } 168 | 169 | }; 170 | // Match string literals 171 | template 172 | struct matcher { 173 | static bool check(const T& t, const char* str) { 174 | return t == str; 175 | } 176 | static auto get(const T&, const T&) { 177 | return std::tie(); 178 | } 179 | }; 180 | 181 | 182 | 183 | 184 | // Match otherwise 185 | template 186 | struct matcher { 187 | template 188 | static bool check(T&&, otherwise_t) { 189 | return true; 190 | } 191 | template 192 | static auto get(T&&, otherwise_t) { 193 | return std::tie(); 194 | } 195 | 196 | }; 197 | 198 | 199 | 200 | } 201 | template 202 | struct matcher_predicate { 203 | F f_; 204 | }; 205 | 206 | template 207 | matcher_predicate make_matcher_predicate(F&& f) { 208 | return matcher_predicate{std::forward(f)}; 209 | } 210 | 211 | 212 | namespace customization { 213 | 214 | 215 | template 216 | struct matcher> { 217 | template 218 | static bool check(T&& t, U&& u) { 219 | return u.f_(std::forward(t)); 220 | } 221 | template 222 | static auto get(T&& t, U&&) { 223 | return std::tie(std::forward(t)); 224 | } 225 | 226 | }; 227 | 228 | } 229 | 230 | namespace placeholders { 231 | const auto _u = make_matcher_predicate([](auto&&) {return true; }); 232 | const auto _v = make_matcher_predicate([](auto&&) {return true; }); 233 | const auto _w = make_matcher_predicate([](auto&&) {return true; }); 234 | const auto _x = make_matcher_predicate([](auto&&) {return true; }); 235 | const auto _y = make_matcher_predicate([](auto&&) {return true; }); 236 | const auto _z = make_matcher_predicate([](auto&&) {return true; }); 237 | 238 | // relational operators 239 | template 240 | auto operator==(const matcher_predicate& m, const T& t) { 241 | return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x == t; }); 242 | } 243 | 244 | template 245 | auto operator!=(const matcher_predicate& m, const T& t) { 246 | return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x != t; }); 247 | } 248 | 249 | template 250 | auto operator<=(const matcher_predicate& m, const T& t) { 251 | return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x <= t; }); 252 | } 253 | template 254 | auto operator>=(const matcher_predicate& m, const T& t) { 255 | return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x >= t; }); 256 | } 257 | template 258 | auto operator<(const matcher_predicate& m, const T& t) { 259 | return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x < t; }); 260 | } 261 | template 262 | auto operator>(const matcher_predicate& m, const T& t) { 263 | return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x > t; }); 264 | } 265 | template 266 | auto operator!(const matcher_predicate& m) { 267 | return make_matcher_predicate([m](const auto& x) {return !m.f_(x); }); 268 | } 269 | 270 | template 271 | auto operator&&(const matcher_predicate& m, const matcher_predicate& m2) { 272 | return make_matcher_predicate([m, m2](const auto& x) {return m.f_(x) && m2.f_(x); }); 273 | } 274 | 275 | template 276 | auto operator||(const matcher_predicate& m, const matcher_predicate& m2) { 277 | return make_matcher_predicate([m, m2](const auto& x) {return m.f_(x) || m2.f_(x); }); 278 | } 279 | 280 | template 281 | auto operator==(const T& t, const matcher_predicate& m) { 282 | return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t == x; }); 283 | } 284 | 285 | template 286 | auto operator!=(const T& t, const matcher_predicate& m) { 287 | return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t != x; }); 288 | } 289 | 290 | template 291 | auto operator<=(const T& t, const matcher_predicate& m) { 292 | return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t <= x; }); 293 | } 294 | template 295 | auto operator>=(const T& t, const matcher_predicate& m) { 296 | return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t >= x; }); 297 | } 298 | template 299 | auto operator<(const T& t, const matcher_predicate& m) { 300 | return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t < x; }); 301 | } 302 | template 303 | auto operator>(const T& t, const matcher_predicate& m) { 304 | return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t > x; }); 305 | } 306 | 307 | } 308 | 309 | namespace detail { 310 | // We use this class, so we can differentiate between matcher and matchter 311 | struct tuple_ignorer {}; 312 | } 313 | 314 | namespace customization { 315 | 316 | template 317 | const std::tuple& simple_match_get_tuple(const std::tuple& t) { 318 | return t; 319 | } 320 | 321 | template 322 | std::tuple& simple_match_get_tuple(std::tuple& t) { 323 | return t; 324 | } 325 | 326 | template 327 | struct tuple_adapter { 328 | 329 | 330 | template 331 | static decltype(auto) get(T&& t) { 332 | using namespace simple_match::customization; 333 | return std::get(simple_match_get_tuple(std::forward(t))); 334 | } 335 | }; 336 | 337 | template 338 | struct matcher> { 339 | using tu = tuple_adapter; 340 | enum { tuple_len = sizeof... (Args) - 1}; 341 | template 342 | struct helper { 343 | template 344 | static bool check(T&& t, A&& a) { 345 | return match_check(tu::template get(std::forward(t)), std::get(std::forward(a))) 346 | && helper::check(std::forward(t), std::forward(a)); 347 | 348 | } 349 | 350 | template 351 | static auto get(T&& t, A&& a) { 352 | return std::tuple_cat(match_get(tu::template get(std::forward(t)), std::get(std::forward(a))), 353 | helper::get(std::forward(t), std::forward(a))); 354 | 355 | } 356 | }; 357 | 358 | template 359 | struct helper { 360 | template 361 | static bool check(T&& t, A&& a) { 362 | return match_check(tu::template get(std::forward(t)), std::get(std::forward(a))); 363 | 364 | } 365 | template 366 | static auto get(T&& t, A&& a) { 367 | return match_get(tu::template get(std::forward(t)), std::get(std::forward(a))); 368 | 369 | } 370 | }; 371 | 372 | 373 | template 374 | static bool check(T&& t, A&& a) { 375 | return helper<0, tuple_len - 1>::check(std::forward(t), std::forward(a)); 376 | 377 | } 378 | template 379 | static auto get(T&& t, A&& a) { 380 | return helper<0, tuple_len - 1>::get(std::forward(t), std::forward(a)); 381 | 382 | } 383 | 384 | }; 385 | 386 | 387 | } 388 | 389 | // destructure a tuple or other adapted structure 390 | template 391 | auto ds(A&& ... a) { 392 | return std::make_tuple(std::forward(a)..., detail::tuple_ignorer{}); 393 | } 394 | 395 | 396 | } 397 | 398 | 399 | #include "implementation/some_none.hpp" 400 | 401 | 402 | 403 | #endif 404 | -------------------------------------------------------------------------------- /include/simple_match/utility.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // Copyright 2015 John R. Bandela 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #pragma once 8 | 9 | #ifndef SIMPLE_MATCH_UTILITY_HPP_JRB_2015_09_11 10 | #define SIMPLE_MATCH_UTILITY_HPP_JRB_2015_09_11 11 | #include "simple_match.hpp" 12 | namespace simple_match { 13 | // tagged_tuple 14 | 15 | template 16 | struct tagged_tuple :std::tuple { 17 | using base = std::tuple; 18 | template 19 | tagged_tuple(A&&... a) :base{ std::forward(a)... } {} 20 | }; 21 | 22 | // inheriting_tagged_tuple 23 | 24 | template 25 | struct inheriting_tagged_tuple :Base,tagged_tuple { 26 | using base = tagged_tuple; 27 | template 28 | inheriting_tagged_tuple(A&&... a) :base{ std::forward(a)... } {} 29 | }; 30 | 31 | 32 | } 33 | 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /test/cppcon_feedback.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../include/simple_match/simple_match.hpp" 5 | #include "../include/simple_match/boost/variant.hpp" 6 | #include "../include/simple_match/boost/lexical_cast.hpp" 7 | #include "../include/simple_match/regex.hpp" 8 | 9 | 10 | void test_regex() { 11 | using namespace simple_match; 12 | using namespace simple_match::placeholders; 13 | 14 | auto toll_free = make_matcher_predicate([](boost::string_ref s) { 15 | const static std::vector toll_free_nums{ "800","888","877","866","855" }; 16 | return std::find(toll_free_nums.begin(), toll_free_nums.end(), s) != toll_free_nums.end(); 17 | 18 | }); 19 | 20 | std::regex r_text{ "([a-z]+)\\.txt" }; 21 | auto m = [&](boost::string_ref s) { 22 | match(s, 23 | rex_search(r_text, _x), [](auto& x) {std::cout << x << "\n";}, 24 | rex_search("([0-9]{4})-([0-9]{2})-([0-9]{2})", lexical_cast(_x), lexical_cast(0 < _x <= 12), lexical_cast(0 < _x <= 31)), [](auto y, auto m, auto d) {std::cout << "Got date " << y << " " << m << " " << d << "\n";}, 25 | rex_match("([0-9]{3})-([0-9]+)-([0-9]+)", "979", _y, _z), [](auto& y, auto& z) {std::cout << "Got local phone " << y << "-" << z << "\n";}, 26 | rex_match("([0-9]{3})-([0-9]+)-([0-9]+)", toll_free, _y, _z), [](auto& x, auto& y, auto& z) {std::cout << "Got toll free " << x << "-" << y << "-" << z << "\n";}, 27 | rex_match("([0-9]{3})-([0-9]+)-([0-9]+)", _x, _y, _z), [](auto& x, auto& y, auto& z) {std::cout << "Got long distance " << x << "-" << y << "-" << z << "\n";}, 28 | _x, [](auto& x) {std::cout << x << " Did not match a regex\n";} 29 | 30 | ); 31 | }; 32 | 33 | m(" foo.txt"); 34 | m(" 2015-01-22"); 35 | m("2015-13-22"); 36 | m("2015-01-00"); 37 | m("979-123-4567"); 38 | m("877-123-4567"); 39 | m("561-123-4567"); 40 | 41 | 42 | } 43 | 44 | struct add; 45 | struct sub; 46 | struct neg; 47 | struct mul; 48 | 49 | using expression_t = boost::variant, boost::recursive_wrapper, boost::recursive_wrapper, boost::recursive_wrapper, int >; 50 | 51 | struct add :std::tuple { using tuple::tuple; }; 52 | struct sub :std::tuple { using tuple::tuple; }; 53 | struct mul :std::tuple { using tuple::tuple; }; 54 | struct neg :std::tuple { using tuple::tuple; }; 55 | 56 | int eval(const expression_t& e) { 57 | using namespace simple_match; 58 | using namespace simple_match::placeholders; 59 | 60 | return simple_match::match(e, 61 | some(ds(some(_x), some(ds(some(_y), some(_z))))), [](int x, int y, int z) {std::cout << "Fused add-mul\n";return x + y*z;}, 62 | some(ds(_x, _y)), [](auto&& x, auto&& y) {return eval(x) + eval(y);}, 63 | some(ds(_x, _y)), [](auto&& x, auto&& y) {return eval(x) - eval(y);}, 64 | some(ds(_x, _y)), [](auto&& x, auto&& y) {return eval(x) * eval(y);}, 65 | some(ds(_x)), [](auto&& x) {return -eval(x);}, 66 | some(), [](auto x) {return x;} 67 | ); 68 | 69 | 70 | 71 | 72 | } 73 | 74 | 75 | struct animal { virtual ~animal() {} }; 76 | struct mammal :animal {}; 77 | struct domesticated_mammal :mammal {}; 78 | struct wild_mammal :mammal {}; 79 | struct cat : domesticated_mammal {}; 80 | struct dog : domesticated_mammal {}; 81 | struct bear :wild_mammal {}; 82 | 83 | struct platypus :wild_mammal {}; 84 | 85 | struct bird :animal {}; 86 | struct eagle :bird {}; 87 | 88 | void do_animal_stuff(const animal* a) { 89 | 90 | using namespace simple_match; 91 | using namespace simple_match::placeholders; 92 | 93 | // Reproduce 94 | match(a, 95 | some(), [](auto&&) {std::cout << "Lay eggs even though a mammal\n";}, 96 | some(), [](auto&&) {std::cout << "Give live birth\n";}, 97 | some(), [](auto&&) {std::cout << "Lay eggs in nest\n";}, 98 | _, []() {std::cout << "Reproduce somehow\n";} 99 | ); 100 | 101 | // make sound 102 | match(a, 103 | some(), [](auto&) {std::cout << "Meow\n";}, 104 | some(), [](auto&) {std::cout << "Woof\n";}, 105 | some(), [](auto&) {std::cout << "Growl\n";}, 106 | some(), [](auto&) {std::cout << "Screech\n";}, 107 | _, []() {std::cout << "Unknown animal sound\n";} 108 | ); 109 | 110 | // Check if can be pet 111 | match(a, 112 | some(), [](auto&&) {std::cout << "This will make a good pet\n";}, 113 | some(), [](auto&&) {std::cout << "This will not make a good pet\n";}, 114 | _, []() {std::cout << "Don't know what kind of pet this will make\n";} 115 | ); 116 | 117 | } 118 | 119 | 120 | void test_animal() { 121 | 122 | std::vector> animals; 123 | animals.emplace_back(new cat); 124 | animals.emplace_back(new dog); 125 | animals.emplace_back(new platypus); 126 | animals.emplace_back(new bear); 127 | animals.emplace_back(new eagle); 128 | 129 | for (auto& a : animals) { 130 | std::cout << typeid(*a.get()).name() << "\n"; 131 | do_animal_stuff(a.get()); 132 | 133 | } 134 | 135 | } 136 | 137 | 138 | int main() { 139 | test_regex(); 140 | 141 | expression_t e = sub{ 10,add{2,{mul{2,3}}} }; 142 | eval(e); 143 | 144 | test_animal(); 145 | } -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "../include/simple_match/simple_match.hpp" 6 | #include 7 | 8 | #include "../include/simple_match/boost/any.hpp" 9 | #include "../include/simple_match/boost/variant.hpp" 10 | #include "../include/simple_match/boost/optional.hpp" 11 | #include "../include/simple_match/utility.hpp" 12 | #include "../include/simple_match/boost/lexical_cast.hpp" 13 | #include "../include/simple_match/regex.hpp" 14 | 15 | 16 | struct point { 17 | int x; 18 | int y; 19 | point(int x_, int y_) :x(x_), y(y_) {} 20 | }; 21 | 22 | 23 | auto simple_match_get_tuple(const point& p) { 24 | return std::tie(p.x, p.y); 25 | } 26 | 27 | struct holder { virtual ~holder() {} }; 28 | template 29 | struct holder_t:holder { 30 | T value_; 31 | 32 | holder_t(T v) :value_{ std::move(v) } {} 33 | }; 34 | 35 | template 36 | auto simple_match_get_tuple(const holder_t& h) { 37 | return std::tie(h.value_); 38 | } 39 | 40 | template 41 | std::unique_ptr make_holder(T&& t) { 42 | return std::make_unique>>(std::forward(t)); 43 | } 44 | 45 | 46 | void test_holder() { 47 | using namespace simple_match; 48 | using namespace simple_match::placeholders; 49 | 50 | 51 | auto m = [](auto&& v) { 52 | match(v, 53 | some>(ds(5)), []() {std::cout << "Got five\n";}, 54 | some>(ds(_x)), [](auto x) {std::cout << "Got int " << x << "\n";}, 55 | some(), [](auto& x) {std::cout << "Got some other type of holder\n";}, 56 | none(), []() {std::cout << "Got nullptr\n";} 57 | ); 58 | }; 59 | auto five = make_holder(5); 60 | auto ten = make_holder(10); 61 | auto pi = make_holder(3.14); 62 | std::unique_ptr nothing; 63 | m(five); 64 | m(ten); 65 | m(pi); 66 | m(nothing); 67 | } 68 | 69 | void test_any() { 70 | using namespace simple_match; 71 | using namespace simple_match::placeholders; 72 | 73 | 74 | auto m = [](auto&& v) { 75 | match(v, 76 | some(5), []() {std::cout << "Got five\n";}, 77 | some(), [](auto x) {std::cout << "Got int " << x << "\n";}, 78 | none(), []() {std::cout << "Got nullptr\n";}, 79 | _, []() {std::cout << "Got some other type of any\n";} 80 | ); 81 | }; 82 | auto five = boost::any{5}; 83 | auto ten = boost::any{10}; 84 | auto pi = boost::any{3.14}; 85 | boost::any nothing; 86 | m(five); 87 | m(ten); 88 | m(pi); 89 | m(nothing); 90 | } 91 | 92 | 93 | 94 | void test_some_none() { 95 | using namespace simple_match; 96 | using namespace simple_match::placeholders; 97 | 98 | std::unique_ptr nothing; 99 | auto five = std::make_unique(5); 100 | auto ten = std::make_unique(10); 101 | auto twelve = std::make_unique(12); 102 | 103 | auto m = [](auto&& v) { 104 | match(v, 105 | some(5), []() {std::cout << "five\n"; }, 106 | some(11 <= _x <= 20), [](int x) {std::cout << x << " is on the range [11,20] \n"; }, 107 | some(), [](int x) {std::cout << x << "\n"; }, 108 | none(), []() {std::cout << "Nothing\n"; } 109 | ); 110 | }; 111 | 112 | m(nothing.get()); 113 | m(five.get()); 114 | m(ten.get()); 115 | m(twelve.get()); 116 | 117 | m(nothing); 118 | m(five); 119 | m(ten); 120 | m(twelve); 121 | 122 | } 123 | 124 | void test_ds(){ 125 | 126 | using namespace simple_match; 127 | using namespace simple_match::placeholders; 128 | 129 | auto m = [](auto&& v) { 130 | match(v, 131 | some(ds(1,2)), []() {std::cout << "one,two\n"; }, 132 | some(ds(_x,_y)), [](int x, int y) {std::cout << x << " " << y << "\n"; }, 133 | none(), []() {std::cout << "Nothing\n"; } 134 | ); 135 | }; 136 | 137 | 138 | auto tup_12 = std::make_unique < std::tuple>(1, 2); 139 | auto point_12 = std::make_unique (point{ 1, 2 }); 140 | auto point_34 = std::make_unique (point{ 3, 4 }); 141 | 142 | m(tup_12); 143 | m(point_12); 144 | m(point_34); 145 | 146 | 147 | 148 | } 149 | 150 | 151 | void test_string() { 152 | 153 | std::string s = "Test"; 154 | 155 | using namespace simple_match; 156 | using namespace simple_match::placeholders; 157 | match(s, 158 | 159 | "One", []() {std::cout << "one\n"; }, 160 | "Test", []() {std::cout << "two \n"; }, 161 | otherwise, []() {std::cout << "did not match \n"; } 162 | ); 163 | 164 | } 165 | 166 | 167 | 168 | struct add; 169 | struct sub; 170 | struct neg; 171 | struct mul; 172 | 173 | using math_variant_t = boost::variant, boost::recursive_wrapper, boost::recursive_wrapper, boost::recursive_wrapper,int >; 174 | 175 | struct add { 176 | math_variant_t left; 177 | math_variant_t right; 178 | }; 179 | struct sub { 180 | math_variant_t left; 181 | math_variant_t right; 182 | }; 183 | struct neg { 184 | math_variant_t value; 185 | }; 186 | struct mul { 187 | math_variant_t left; 188 | math_variant_t right; 189 | }; 190 | 191 | 192 | struct eval_t {}; 193 | using eval_any = simple_match::tagged_any; 194 | 195 | struct add_tag {}; 196 | struct sub_tag {}; 197 | struct mul_tag {}; 198 | struct neg_tag {}; 199 | 200 | 201 | 202 | using add2 = simple_match::tagged_tuple; 203 | using sub2 = simple_match::tagged_tuple; 204 | using mul2 = simple_match::tagged_tuple; 205 | using neg2 = simple_match::tagged_tuple; 206 | 207 | struct add3; 208 | struct sub3; 209 | struct neg3; 210 | struct mul3; 211 | 212 | using math_variant2_t = boost::variant, boost::recursive_wrapper, boost::recursive_wrapper, boost::recursive_wrapper,int >; 213 | 214 | struct add3 :std::tuple { using tuple::tuple; }; 215 | struct sub3 :std::tuple { using tuple::tuple; }; 216 | struct mul3 :std::tuple { using tuple::tuple; }; 217 | struct neg3 :std::tuple { using tuple::tuple; }; 218 | 219 | 220 | int eval(const math_variant_t& m) { 221 | using namespace simple_match; 222 | using namespace simple_match::placeholders; 223 | 224 | return simple_match::match(m, 225 | some(), [](auto&& a) {return eval(a.left) + eval(a.right);}, 226 | some(), [](auto&& a) {return eval(a.left) - eval(a.right);}, 227 | some(), [](auto&& a) {return -eval(a.value);}, 228 | some(), [](auto&& a) {return eval(a.left) * eval(a.right);}, 229 | some(), [](auto a) {return a;} 230 | 231 | ); 232 | 233 | 234 | 235 | } 236 | namespace simple_match { 237 | namespace customization { 238 | 239 | template<> 240 | struct exhaustiveness_checker { 241 | using type = some_exhaustiveness; 242 | }; 243 | } 244 | } 245 | 246 | int eval(const eval_any& m) { 247 | using namespace simple_match; 248 | using namespace simple_match::placeholders; 249 | 250 | return simple_match::match(m, 251 | some(ds(_x,_y)), [](auto&& x, auto&& y) {return eval(x) + eval(y);}, 252 | some(ds(_x,_y)), [](auto&& x, auto&& y) {return eval(x) - eval(y);}, 253 | some(ds(_x)), [](auto&& x) {return -eval(x);}, 254 | some(ds(_x,_y)), [](auto&& x,auto&& y) {return eval(x) * eval(y);}, 255 | some(), [](auto x) {return x;} 256 | 257 | ); 258 | 259 | 260 | 261 | } 262 | 263 | 264 | int eval(const math_variant2_t& m) { 265 | using namespace simple_match; 266 | using namespace simple_match::placeholders; 267 | 268 | return simple_match::match(m, 269 | some(ds(some(_x), some(ds(some(_y), some(_z))))), [](int x, int y, int z) {std::cout << "Fused add-mul\n";return x+y*z;}, 270 | some(ds(_x,_y)), [](auto&& x, auto&& y) {return eval(x) + eval(y);}, 271 | some(ds(_x,_y)), [](auto&& x, auto&& y) {return eval(x) - eval(y);}, 272 | some(ds(_x,_y)), [](auto&& x, auto&& y) {return eval(x) * eval(y);}, 273 | some(ds(_x)), [](auto&& x) {return -eval(x);}, 274 | some(), [](auto x) {return x;} 275 | ); 276 | 277 | 278 | 279 | } 280 | 281 | 282 | struct eval_base_t { virtual ~eval_base_t() {} }; 283 | struct int_holder_tag {}; 284 | 285 | 286 | using add4 = simple_match::inheriting_tagged_tuple, std::unique_ptr>; 287 | using sub4 = simple_match::inheriting_tagged_tuple, std::unique_ptr>; 288 | using mul4 = simple_match::inheriting_tagged_tuple, std::unique_ptr>; 289 | using neg4 = simple_match::inheriting_tagged_tuple>; 290 | using int_holder = simple_match::inheriting_tagged_tuple; 291 | 292 | 293 | namespace simple_match { 294 | namespace customization { 295 | 296 | template<> 297 | struct exhaustiveness_checker> { 298 | using type = some_exhaustiveness; 299 | }; 300 | } 301 | } 302 | 303 | 304 | 305 | int eval(const std::unique_ptr& m) { 306 | using namespace simple_match; 307 | using namespace simple_match::placeholders; 308 | 309 | return simple_match::match(m, 310 | some(ds(_x, _y)), [](auto&& x, auto&& y) {return eval(x) + eval(y);}, 311 | some(ds(_x, _y)), [](auto&& x, auto&& y) {return eval(x) - eval(y);}, 312 | some(ds(_x, _y)), [](auto&& x, auto&& y) {return eval(x) * eval(y);}, 313 | some(ds(_x)), [](auto&& x) {return -eval(x);}, 314 | some(ds(_x)), [](auto x) {return x;} 315 | ); 316 | 317 | 318 | 319 | } 320 | 321 | void FizzBuzz() { 322 | 323 | using namespace simple_match; 324 | using namespace simple_match::placeholders; 325 | for (int i = 1; i <= 100; ++i) { 326 | match(std::make_tuple(i%3,i%5), 327 | ds(0, 0 ), []() {std::cout << "FizzBuzz\n";}, 328 | ds(0, _), []() {std::cout << "Fizz\n";}, 329 | ds(_, 0), []() {std::cout << "Buzz\n";}, 330 | _, [i]() {std::cout << i << "\n";} 331 | ); 332 | } 333 | } 334 | 335 | 336 | 337 | // Adapted from https://github.com/solodon4/Mach7/blob/master/code/test/unit/cppcon-matching.cpp 338 | 339 | 340 | 341 | struct VarExp; 342 | struct NotExp; 343 | struct AndExp; 344 | struct OrExp; 345 | 346 | using BoolExp = boost::variant, bool, boost::recursive_wrapper, boost::recursive_wrapper, boost::recursive_wrapper>; 347 | 348 | struct VarExp : std::tuple { using tuple::tuple; }; 349 | struct NotExp : std::tuple { using tuple::tuple; }; 350 | struct AndExp : std::tuple {using tuple::tuple; }; 351 | struct OrExp : std::tuple {using tuple::tuple; }; 352 | 353 | 354 | template 355 | BoolExp make_bool_exp(U&&... u) { 356 | return T{ std::forward(u)... }; 357 | } 358 | 359 | void print(const BoolExp& exp) 360 | { 361 | using namespace simple_match; 362 | using namespace simple_match::placeholders; 363 | 364 | match(exp, 365 | some(ds(_x)), [](auto& x) {std::cout << x;}, 366 | some(), [](auto& x) {std::cout << x;}, 367 | some(ds(_x)), [](auto& x) {std::cout << '!'; print(x);}, 368 | some(ds(_x, _y)), [](auto& x, auto& y) {std::cout << '('; print(x);std::cout << " & "; print(y); std::cout << ')';}, 369 | some(ds(_x, _y)), [](auto& x, auto& y) {std::cout << '('; print(x);std::cout << " | "; print(y); std::cout << ')';} 370 | ); 371 | 372 | 373 | } 374 | 375 | 376 | BoolExp copy(const BoolExp& exp) 377 | { 378 | return exp; 379 | } 380 | 381 | #include 382 | 383 | template 384 | std::ostream& operator<<(std::ostream& os, const std::map& m) 385 | { 386 | for (auto&& p:m) 387 | { 388 | os << p.first << '=' << p.second << std::endl; 389 | } 390 | 391 | return os; 392 | } 393 | typedef std::map Context; 394 | 395 | bool eval(const Context& ctx, const BoolExp& exp) 396 | { 397 | using namespace simple_match; 398 | using namespace simple_match::placeholders; 399 | 400 | return match(exp, 401 | some(ds(_x)), [&](auto& x) {auto iter = ctx.find(x); return iter == ctx.end() ? false : iter->second;}, 402 | some(), [](auto& x) {return x;}, 403 | some(ds(_x)), [&](auto& x){return !eval(ctx,x);}, 404 | some(ds(_x, _y)), [&](auto& x, auto& y){return eval(ctx,x) && eval(ctx,y);}, 405 | some(ds(_x, _y)), [&](auto& x, auto& y){return eval(ctx,x) || eval(ctx,y);} 406 | ); 407 | 408 | } 409 | 410 | BoolExp replace(const BoolExp& where, const std::string& what, const BoolExp& with) 411 | { 412 | using namespace simple_match; 413 | using namespace simple_match::placeholders; 414 | 415 | return match(where, 416 | some(ds(_x)), [&](auto& x){ 417 | if (what == x) { 418 | return copy(with); 419 | } 420 | else { 421 | return make_bool_exp(x); 422 | } 423 | }, 424 | some(), [&](auto& x) {return copy(where);}, 425 | some(ds(_x)), [&](auto& x) {return make_bool_exp(replace(x,what,with));}, 426 | some(ds(_x,_y)), [&](auto& x, auto& y) {return make_bool_exp(replace(x,what,with), replace(y,what,with));}, 427 | some(ds(_x,_y)), [&](auto& x, auto& y) {return make_bool_exp(replace(x,what,with), replace(y,what,with));} 428 | ); 429 | 430 | } 431 | 432 | BoolExp& inplace(BoolExp& where, const std::string& what, const BoolExp& with) 433 | { 434 | using namespace simple_match; 435 | using namespace simple_match::placeholders; 436 | 437 | match(where, 438 | some(ds(_x)), [&](auto& x) mutable { x == what ? where=with : where;}, 439 | some(), [&](auto&)mutable {}, 440 | some(ds(_x)), [&](auto& x)mutable{inplace(x, what, with);}, 441 | some(ds(_x, _y)), [&](auto& x, auto& y)mutable {inplace(x, what, with); inplace(y, what, with);}, 442 | some(ds(_x, _y)), [&](auto& x, auto& y)mutable {inplace(x, what, with); inplace(y, what, with);} 443 | ); 444 | return where; 445 | } 446 | 447 | bool equal(const BoolExp& x1, const BoolExp& x2) 448 | { 449 | using namespace simple_match; 450 | using namespace simple_match::placeholders; 451 | 452 | return match(std::tie(x1, x2), 453 | ds(some(ds(_x)), some(ds(_y))), [](auto& x, auto& y) {return x == y;}, 454 | ds(some(), some()), [](auto& x, auto& y) {return x == y;}, 455 | ds(some(ds(_x)), some(ds(_y))), [](auto& x, auto& y) {return equal(x,y);}, 456 | ds(some(ds(_w,_x)), some(ds(_y,_z))), [](auto& w, auto& x, auto& y, auto& z) {return equal(w,y) && equal(x,z);}, 457 | ds(some(ds(_w,_x)), some(ds(_y,_z))), [](auto& w, auto& x, auto& y, auto& z) {return equal(w,y) && equal(x,z);}, 458 | _, []() {return false;} 459 | 460 | 461 | 462 | ); 463 | 464 | 465 | } 466 | 467 | typedef std::map Assignments; 468 | 469 | // Other example: unify 470 | bool exp_match(const BoolExp& p, const BoolExp& x, Assignments& ctx) 471 | { 472 | using namespace simple_match; 473 | using namespace simple_match::placeholders; 474 | 475 | return match(std::tie(p, x), 476 | ds(some(ds(_x)), _), [&](auto& name)mutable { 477 | if (ctx.count(name)==0) { ctx[name] = x;return true; } 478 | else { return equal(ctx[name], x); } 479 | }, 480 | ds(some(), some()), [&](auto& x, auto& y)mutable {return x == y;}, 481 | ds(some(ds(_x)), some(ds(_y))), [&](auto& x, auto& y)mutable {return exp_match(x, y,ctx);}, 482 | ds(some(ds(_w, _x)), some(ds(_y, _z))), [&](auto& w, auto& x, auto& y, auto& z)mutable {return exp_match(w, y,ctx) && exp_match(x, z,ctx);}, 483 | ds(some(ds(_w, _x)), some(ds(_y, _z))), [&](auto& w, auto& x, auto& y, auto& z)mutable {return exp_match(w, y,ctx )&& exp_match(x, z,ctx);}, 484 | _, []() {return false;} 485 | 486 | 487 | 488 | ); 489 | 490 | } 491 | void TestBoolExp() { 492 | 493 | BoolExp exp1 = AndExp( 494 | OrExp(VarExp("X"), VarExp("Y")), 495 | NotExp(VarExp("Z"))); 496 | 497 | 498 | std::cout << "exp1 = "; print(exp1); std::cout << std::endl; 499 | 500 | auto exp2 = copy(exp1); 501 | 502 | std::cout << "exp2 = "; print(exp2); std::cout << std::endl; 503 | 504 | auto exp3 = replace(exp1, "Z", exp2); 505 | 506 | std::cout << "exp3 = "; print(exp3); std::cout << std::endl; 507 | 508 | auto& exp4 = inplace(exp1, "Z", exp2); 509 | 510 | std::cout << "exp4 = "; print(exp4); std::cout << std::endl; 511 | std::cout << "exp1 = "; print(exp1); std::cout << " updated! " << std::endl; 512 | 513 | std::cout << (equal(exp1, exp2) ? "exp1 == exp2" : "exp1 <> exp2") << std::endl; 514 | 515 | Context ctx; 516 | ctx["Y"] = true; 517 | std::cout << eval(ctx, exp1) << std::endl; 518 | std::cout << eval(ctx, exp2) << std::endl; 519 | std::cout << eval(ctx, exp3) << std::endl; 520 | 521 | std::cout << ctx << std::endl; 522 | 523 | Assignments ctx2; 524 | 525 | if (exp_match(exp2, exp3, ctx2)) 526 | { 527 | std::cout << "exp2 matches exp3 with assignments: " << std::endl; 528 | 529 | for (Assignments::const_iterator p = ctx2.begin(); p != ctx2.end(); ++p) 530 | { 531 | std::cout << p->first << '='; print(p->second); std::cout << std::endl; 532 | } 533 | } 534 | } 535 | 536 | struct Base { virtual ~Base() {} }; 537 | struct Paper:Base {}; 538 | struct Rock:Base {}; 539 | struct Scissors:Base {}; 540 | 541 | void paper_rock_scissors(const Base* b1, const Base* b2) { 542 | using namespace simple_match; 543 | using namespace simple_match::placeholders; 544 | 545 | match(std::tie(b1, b2), 546 | ds(some(), some()), [](auto&, auto&) {std::cout << "Tie with both Paper\n";}, 547 | ds(some(), some()), [](auto&, auto&) {std::cout << "Winner 1 - Paper covers Rock\n";}, 548 | ds(some(), some()), [](auto&, auto&) {std::cout << "Winner 2 - Scissors cuts Paper\n";}, 549 | ds(some(), some()), [](auto&, auto&) {std::cout << "Winner 2 - Paper covers Rock\n";}, 550 | ds(some(), some()), [](auto&, auto&) {std::cout << "Tie with both Rock\n";}, 551 | ds(some(), some()), [](auto&, auto&) {std::cout << "Winner 1 - Rock smashes Scissors\n";}, 552 | ds(some(), some()), [](auto&, auto&) {std::cout << "Winner 1 - Scissors cuts Paper\n";}, 553 | ds(some(), some()), [](auto&, auto&) {std::cout << "Winner 2 - Rock smashes Scissors\n";}, 554 | ds(some(), some()), [](auto&, auto&) {std::cout << "Tie both with Scissors\n";} 555 | 556 | 557 | 558 | ); 559 | } 560 | 561 | 562 | boost::optional safe_div(int num ,int denom) { 563 | using namespace simple_match; 564 | using namespace simple_match::placeholders; 565 | return match(std::tie(num, denom), 566 | ds(_, 0), []() {return boost::optional{}; }, 567 | ds(_x,_y), [](int x, int y) {return boost::optional{x/y}; } 568 | ); 569 | } 570 | 571 | void test_optional() { 572 | using namespace simple_match; 573 | using namespace simple_match::placeholders; 574 | auto m = [](auto&& v) { 575 | return match(v, 576 | some(), [](auto x) {std::cout << "the safe_div answer is " << x << "\n";}, 577 | none(), []() {std::cout << "Tried to divide by 0 in safe_div\n";} 578 | ); 579 | }; 580 | 581 | m(safe_div(4, 2)); 582 | m(safe_div(4, 0)); 583 | 584 | } 585 | 586 | void test_lexical_cast() { 587 | using namespace simple_match; 588 | using namespace simple_match::placeholders; 589 | 590 | auto m = [](const std::string& s) { 591 | match(s, 592 | lexical_cast(_x), [](auto x) {std::cout << "Got int " << x << "\n";}, 593 | lexical_cast(_x < 123), [](auto x) {std::cout << "Got double less than 123.3 " << x << "\n";}, 594 | lexical_cast(), []() {std::cout << "Matched a double " << "\n";}, 595 | _, []() {std::cout << "Did not match\n";} 596 | 597 | ); 598 | }; 599 | 600 | m("123"); 601 | m("123.3"); 602 | m("23.3"); 603 | m(""); 604 | m("xyz"); 605 | } 606 | 607 | void test_regex() { 608 | using namespace simple_match; 609 | using namespace simple_match::placeholders; 610 | 611 | auto toll_free = make_matcher_predicate([](boost::string_ref s) { 612 | const static std::vector toll_free_nums{ "800","888","877","866","855" }; 613 | return std::find(toll_free_nums.begin(), toll_free_nums.end(), s) != toll_free_nums.end(); 614 | 615 | }); 616 | 617 | auto m = [&](const std::string& s) { 618 | match(s, 619 | rex_match("([a-z]+)\\.txt", _x), [](auto& x) {std::cout << x << "\n";}, 620 | rex_match("([0-9]{4})-([0-9]{2})-([0-9]{2})", lexical_cast(_x), lexical_cast(0 < _x <= 12), lexical_cast(0 < _x <= 31)), [](auto y, auto m, auto d) {std::cout << "Got date " << y << " " << m << " " << d << "\n";}, 621 | rex_match("([0-9]{3})-([0-9]+)-([0-9]+)", "979", _y, _z), [](auto& y, auto& z) {std::cout << "Got local phone " << y << "-" << z << "\n";}, 622 | rex_match("([0-9]{3})-([0-9]+)-([0-9]+)", toll_free, _y, _z), [](auto& x, auto& y, auto& z) {std::cout << "Got toll free " << x << "-" << y << "-" << z << "\n";}, 623 | rex_match("([0-9]{3})-([0-9]+)-([0-9]+)", _x, _y, _z), [](auto& x, auto& y, auto& z) {std::cout << "Got long distance " << x << "-" << y << "-" << z << "\n";}, 624 | _x, [](auto& x) {std::cout << x << " Did not match a regex\n";} 625 | 626 | ); 627 | }; 628 | 629 | m("foo.txt"); 630 | m("2015-01-22"); 631 | m("2015-13-22"); 632 | m("2015-01-00"); 633 | m("979-123-4567"); 634 | m("877-123-4567"); 635 | m("561-123-4567"); 636 | 637 | 638 | } 639 | 640 | int main() { 641 | 642 | 643 | FizzBuzz(); 644 | 645 | 646 | math_variant_t var{ add{2,mul{3,neg{2}} } }; 647 | std::cout << eval(var) << "\n"; 648 | eval_any any_var{ add2{2,mul2{3,neg2{2}} } }; 649 | std::cout << eval(any_var) << "\n"; 650 | 651 | math_variant2_t var2{ add3{2,mul3{3,neg3{2}} } }; 652 | std::cout << eval(var2) << "\n"; 653 | math_variant2_t var_fused{ sub3{10,add3{2,mul3{3,2} }} }; 654 | std::cout << eval(var_fused) << "\n"; 655 | 656 | 657 | std::unique_ptr eval_base = std::make_unique( 658 | std::make_unique(2), 659 | std::make_unique( 660 | std::make_unique(3), 661 | std::make_unique(std::make_unique(2)))); 662 | 663 | 664 | 665 | std::cout << eval(eval_base) << "\n"; 666 | 667 | 668 | test_string(); 669 | test_some_none(); 670 | test_ds(); 671 | 672 | using namespace simple_match; 673 | using namespace simple_match::placeholders; 674 | 675 | int x = 0; 676 | 677 | int xs[] = { 1,2,4,15,20,21 }; 678 | 679 | for (auto x:xs) { 680 | match(x, 681 | 1, []() {std::cout << "The answer is one\n"; }, 682 | 2, []() {std::cout << "The answer is two\n"; }, 683 | _x < 10, [](auto&& a) {std::cout << "The answer " << a << " is less than 10\n"; }, 684 | 10 < _x < 20, [](auto&& a) {std::cout << "The answer " << a << " is between 10 and 20 exclusive\n"; }, 685 | _, []() {std::cout << "Did not match\n"; } 686 | 687 | ); 688 | 689 | 690 | } 691 | 692 | 693 | TestBoolExp(); 694 | 695 | 696 | std::unique_ptr p1{ new Rock }; 697 | std::unique_ptr p2{ new Scissors }; 698 | std::unique_ptr p3{ new Paper }; 699 | 700 | paper_rock_scissors(p1.get(), p2.get()); 701 | paper_rock_scissors(p3.get(), p1.get()); 702 | 703 | 704 | test_holder(); 705 | test_any(); 706 | test_optional(); 707 | test_lexical_cast(); 708 | test_regex(); 709 | } 710 | --------------------------------------------------------------------------------