diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea7..8b35928 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -11,11 +11,7 @@ assignees: '' A clear and concise description of what the bug is. **To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error +Steps to reproduce the behavior. **Expected behavior** A clear and concise description of what you expected to happen. @@ -23,16 +19,9 @@ A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] +**Environment (please complete the following information):** + - OS + - Compiler **Additional context** Add any other context about the problem here. diff --git a/CMakeLists.txt b/CMakeLists.txt index f311fe4..2a9c0a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.15...3.19) project( "matchit" - VERSION 1.0.0 + VERSION 1.0.1 LANGUAGES CXX DESCRIPTION "match(it): A lightweight single-header pattern-matching library for C++17 with macro-free APIs." diff --git a/From-Pattern-Matching-Proposal-to-matchit.md b/From-Pattern-Matching-Proposal-to-matchit.md index 09bce21..2a91ef7 100644 --- a/From-Pattern-Matching-Proposal-to-matchit.md +++ b/From-Pattern-Matching-Proposal-to-matchit.md @@ -182,14 +182,14 @@ int eval(const Expr &ex) Id i; Id e, l, r; return match(ex)( - pattern | as(i) = expr(i), + pattern | as(i) = i, pattern | asNegDs(some(e)) = [&]{ return -eval(*e); }, pattern | asAddDs(some(l), some(r)) = [&]{ return eval(*l) + eval(*r); }, // Optimize multiplication by 0. - pattern | asMulDs(some(as(0)), _) = expr(0), - pattern | asMulDs(_, some(as(0))) = expr(0), + pattern | asMulDs(some(as(0)), _) = 0, + pattern | asMulDs(_, some(as(0))) = 0, pattern | asMulDs(some(l), some(r)) = [&]{ return eval(*l) * eval(*r); }, - pattern | _ = expr(-1) + pattern | _ = -1 ); } @@ -219,11 +219,11 @@ Op parseOp(Parser& parser) { enum class Op { Add, Sub, Mul, Div }; Op parseOp(Parser& parser) { Id token; - return match(parser.consumeToken()) ( - pattern | '+' = expr(Op::Add), - pattern | '-' = expr(Op::Sub), - pattern | '*' = expr(Op::Mul), - pattern | '/' = expr(Op::Div), + return match(parser.consumeToken())( + pattern | '+' = Op::Add, + pattern | '-' = Op::Sub, + pattern | '*' = Op::Mul, + pattern | '/' = Op::Div, pattern | token = [&]{ std::cerr << "Unexpected: " << *token; std::terminate(); @@ -754,7 +754,7 @@ char* String::data() { Id l; Id> r; return match(*this) ( - pattern | asEnum(l) = expr(l), + pattern | asEnum(l) = l, pattern | asEnum(r) = [&]{ return (*r).ptr; } ); } @@ -850,8 +850,8 @@ In `match(it)`: int fib(int n) { Id x; return match (n) ( - pattern | (_ < 0) = expr(0), - pattern | or_(1,2) = expr(n), //1|2 + pattern | (_ < 0) = 0, + pattern | or_(1,2) = n, //1|2 pattern | x = [&] { return fib(*x - 1) + fib(*x - 2); } ); } @@ -1028,7 +1028,7 @@ void Node::balance() { = [&] { return Node{Red, std::make_shared(Black, *a, *x, *b), *y, std::make_shared(Black, *c, *z, *d)}; }, - pattern | self = expr(self) // do nothing + pattern | self = self // do nothing ); } ``` diff --git a/From-Rust-to-matchit.md b/From-Rust-to-matchit.md index 9bba895..9bd5a57 100644 --- a/From-Rust-to-matchit.md +++ b/From-Rust-to-matchit.md @@ -30,10 +30,10 @@ for (auto i = -2; i <= 5; ++i) { std::cout << match(i)( - pattern | -1 = expr("It's minus one"), - pattern | 1 = expr("It's a one"), - pattern | or_(2,4) = expr("It's either a two or a four"), - pattern | _ = expr("Matched none of the arms") + pattern | -1 = "It's minus one", + pattern | 1 = "It's a one", + pattern | or_(2,4) = "It's either a two or a four", + pattern | _ = "Matched none of the arms" ) << std::endl; } @@ -364,17 +364,17 @@ In C++ with `match(it)`: ```C++ constexpr auto c = 'f'; constexpr auto valid_variable = match(c)( - pattern | ('a' <= _ && _ <= 'z') = expr(true), - pattern | ('A' <= _ && _ <= 'Z') = expr(true), - // pattern | ('α' <= _ && _ <= 'ω') = expr(true), - pattern | _ = expr(false) + pattern | ('a' <= _ && _ <= 'z') = true, + pattern | ('A' <= _ && _ <= 'Z') = true, + // pattern | ('α' <= _ && _ <= 'ω') = true, + pattern | _ = false ); constexpr auto ph = 10; -std::cout << match(ph)( - pattern | (0 <= _ && _ <= 6 ) = expr("acid"), - pattern | (7 ) = expr("neutral"), - pattern | (8 <= _ && _ <= 14) = expr("base"), +std::cout << match(ph)( + pattern | (0 <= _ && _ <= 6 ) = "acid", + pattern | (7 ) = "neutral", + pattern | (8 <= _ && _ <= 14) = "base", pattern | _ = [] { assert(false && "unreachable"); } ) << std::endl; @@ -390,11 +390,11 @@ constexpr uint8_t MESOSPHERE_MAX = 85; constexpr auto altitude = 70; -std::cout << match(altitude)( - pattern | (TROPOSPHERE_MIN <= _ && _ <= TROPOSPHERE_MAX ) = expr("troposphere"), - pattern | (STRATOSPHERE_MIN <= _ && _ <= STRATOSPHERE_MAX) = expr("stratosphere"), - pattern | (MESOSPHERE_MIN <= _ && _ <= MESOSPHERE_MAX ) = expr("mesosphere"), - pattern | _ = expr("outer space, maybe") +std::cout << match(altitude)( + pattern | (TROPOSPHERE_MIN <= _ && _ <= TROPOSPHERE_MAX ) = "troposphere", + pattern | (STRATOSPHERE_MIN <= _ && _ <= STRATOSPHERE_MAX) = "stratosphere", + pattern | (MESOSPHERE_MIN <= _ && _ <= MESOSPHERE_MAX ) = "mesosphere", + pattern | _ = "outer space, maybe" ) << std::endl; namespace binary @@ -415,11 +415,11 @@ match(n_items * bytes_per_item) ); // using qualified paths: -std::cout << match(static_cast(0xfacade))( - pattern | (0U <= _ && _ <= numeric_limits::max()) = expr("fits in a u8", - pattern | (0U <= _ && _ <= numeric_limits::max()) = expr("fits in a u16", - pattern | (0U <= _ && _ <= numeric_limits::max()) = expr("fits in a u32", - pattern | _ = expr("too big") +std::cout << match(static_cast(0xfacade))( + pattern | (0U <= _ && _ <= numeric_limits::max()) = "fits in a u8", + pattern | (0U <= _ && _ <= numeric_limits::max()) = "fits in a u16", + pattern | (0U <= _ && _ <= numeric_limits::max()) = "fits in a u32", + pattern | _ = "too big" ) << std::endl; ``` @@ -427,9 +427,9 @@ Tips: feel free to use variables in `match(it)`. You can write codes like ```C++ // using variables: -std::cout << match(0xfacade)( - pattern | (min(a, b) <= _ && _ <= max(a, b)) = expr("fits in the range"), - pattern | _ = expr("out of the range") +std::cout << match(0xfacade)( + pattern | (min(a, b) <= _ && _ <= max(a, b)) = "fits in the range", + pattern | _ = "out of the range" ) << std::endl; ``` @@ -455,12 +455,14 @@ int32_t const *int_reference = &value; int32_t const zero = 0; auto const a = match(*int_reference)( - pattern | zero = expr("zero"), - pattern | _ = expr("some")); + pattern | zero = "zero", + pattern | _ = "some" +); auto const b = match(int_reference)( - pattern | &zero = expr("zero"), - pattern | _ = expr("some")); + pattern | &zero = "zero", + pattern | _ = "some" +); assert(a == b); ``` @@ -589,9 +591,9 @@ In C++ with `match(it)`: // Fixed size constexpr auto arr = std::array{1, 2, 3}; Id a, b, c; -match(arr)( - pattern | ds(1, _, _) = expr("starts with one"), - pattern | ds(a, b, c) = expr("starts with something else") +match(arr)( + pattern | ds(1, _, _) = "starts with one", + pattern | ds(a, b, c) = "starts with something else" ); ``` @@ -678,14 +680,15 @@ int32_t main() Id color; match(favorite_color)( pattern | some(color) = [&] { return "Using your favorite color, " + *color + ", as the background"; }, - pattern | _ | when(expr(is_tuesday)) = expr("Tuesday is green day!"), + pattern | _ | when(is_tuesday) = "Tuesday is green day!", pattern | _ = [&] { Id age_; return match(age)( - pattern | as(age_) | when(age_ > 30) = expr("Using purple as the background color"), - pattern | as(age_) = expr("Using orange as the background color"), - pattern | _ = expr("Using blue as the background color")); + pattern | as(age_) | when(age_ > 30) = "Using purple as the background color", + pattern | as(age_) = "Using orange as the background color", + pattern | _ = "Using blue as the background color" + ); }); return 0; @@ -740,7 +743,6 @@ int32_t main() { return std::optional(); } - }; using namespace matchit; @@ -752,7 +754,8 @@ int32_t main() std::cout << *top << std::endl; return true; }, - pattern | _ = expr(false))) + pattern | _ = false) + ) { }; } @@ -1085,9 +1088,9 @@ int32_t main() { auto const y = false; std::cout << - match(x)( - pattern | or_(4, 5, 6) | when(expr(y)) = expr("yes"), - pattern | _ = expr("no") + match(x)( + pattern | or_(4, 5, 6) | when(y) = "yes", + pattern | _ = "no" ) << std::endl; } ``` diff --git a/LICENSE b/LICENSE index c9ab656..1cbca2f 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [2021] [Bowen Fu] + Copyright [2021-2022] [Bowen Fu] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 29cc124..93a486b 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ [![codecov](https://codecov.io/gh/BowenFu/matchit.cpp/branch/main/graph/badge.svg?token=G5B0RE6THD)](https://codecov.io/gh/BowenFu/matchit.cpp) [badge.godbolt]: https://img.shields.io/badge/try-godbolt-blue -[godbolt]: https://godbolt.org/z/8YMr8Kz8j +[godbolt]: https://godbolt.org/z/Psf3qrxW4 ## Features @@ -72,6 +72,8 @@ message(STATUS "Matchit header are present at ${matchit_SOURCE_DIR}") And add `${matchit_SOURCE_DIR}/include` to your include path. +Replace `main` with latest release tag to avoid API compatibility breaking. + ### Option 3. Manage with cmake find_package Clone the repo via @@ -89,6 +91,45 @@ make install Then use find_package in your CMakeLists.txt. +### Option 4. Manage with vcpkg + +(Thanks to @[daljit97](https://github.com/daljit97) for adding the support.) + +``` +vcpkg install matchit +``` + +### Option 5. Manage with conan + +Now the library has been submitted to [Conan Center Index](https://github.com/conan-io/conan-center-index). + +You can now install the library via conan. + +(Thanks to @[sanblch](https://github.com/sanblch) for adding the support.) + + +## Tips for Debugging + +To make your debugging easier, try to write your lambda function body in separate lines so that you can set break points in it. + +```c++ +pattern | xyz = [&] +{ + // Separate lines for function body <- set break points here +} +``` + +is much more debugging-friendly compared to + +```c++ +pattern | xyz = [&] { /* some codes here */ }, // <- Set break points here, you will debug into the library. +``` + +Do not debug into this library unless you really decide to root cause / fix some bugs in this library, just like you won't debug into STL variant or ranges. + +Please try to create a minimal sample to reproduce the issues you've met. You can root cause the issue more quickly in that way. + +You can also create an issue in this repo and attach the minimal sample codes and I'll try to response as soon as possible (sometimes please expect one or two days delay). ## Syntax Design @@ -124,7 +165,7 @@ constexpr int32_t factorial(int32_t n) using namespace matchit; assert(n >= 0); return match(n)( - pattern | 0 = expr(1), + pattern | 0 = 1, pattern | _ = [n] { return n * factorial(n - 1); } ); } @@ -145,7 +186,7 @@ This is a function call and will return some value returned by handlers. The ret When handlers return values, the patterns must be exhaustive. A runtime error will happen if all patterns do not get matched. It is not an error if handlers' return types are all void. -`expr` in the above sample is a helper function that can be used to generate a nullary function that returns a value. `expr(1)` is equivalent to `[]{return 1;}`. It can be useful for short functions. +The handler can also be a value or an Id variable. `1` is equivalent to `[]{return 1;}`. The wildcard `_` will match any values. It is a common practice to always use it as the last pattern, playing the same role in our library as `default case` does for `switch` statements, to avoid case escaping. @@ -179,8 +220,8 @@ constexpr bool contains(Map const& map, Key const& key) { using namespace matchit; return match(map.find(key))( - pattern | map.end() = expr(false), - pattern | _ = expr(true) + pattern | map.end() = false, + pattern | _ = true ); } ``` @@ -193,8 +234,9 @@ We can use **Predicate Pattern** to put some restrictions on the value to be mat constexpr double relu(double value) { return match(value)( - pattern | (_ >= 0) = expr(value), - pattern | _ = expr(0)); + pattern | (_ >= 0) = value, + pattern | _ = 0 + ); } static_assert(relu(5) == 5); @@ -212,8 +254,8 @@ constexpr bool isValid(int32_t n) { using namespace matchit; return match(n)( - pattern | or_(1, 3, 5) = expr(true), - pattern | _ = expr(false) + pattern | or_(1, 3, 5) = true, + pattern | _ = false ); } @@ -239,8 +281,8 @@ constexpr bool isLarge(double value) { using namespace matchit; return match(value)( - pattern | app(_ * _, _ > 1000) = expr(true), - pattern | _ = expr(false) + pattern | app(_ * _, _ > 1000) = true, + pattern | _ = false ); } @@ -266,7 +308,8 @@ bool checkAndlogLarge(double value) pattern | app(_ * _, s.at(_ > 1000)) = [&] { std::cout << value << "^2 = " << *s << " > 1000!" << std::endl; return true; }, - pattern | _ = expr(false)); + pattern | _ = false + ); } ``` @@ -289,8 +332,8 @@ constexpr bool symmetric(std::array const& arr) using namespace matchit; Id i, j; return match(arr)( - pattern | ds(i, j, _, j, i) = expr(true), - pattern | _ = expr(false) + pattern | ds(i, j, _, j, i) = true, + pattern | _ = false ); } @@ -342,8 +385,8 @@ constexpr auto dsByMember(DummyStruct const&v) constexpr auto dsA = dsVia(&DummyStruct::size, &DummyStruct::name); Id name; return match(v)( - pattern | dsA(2, name) = expr(name), - pattern | _ = expr("not matched") + pattern | dsA(2, name) = name, + pattern | _ = "not matched" ); }; @@ -351,7 +394,7 @@ static_assert(dsByMember(DummyStruct{1, "123"}) == std::string_view{"not matched static_assert(dsByMember(DummyStruct{2, "123"}) == std::string_view{"123"}); ``` -Let's continue the journey. +Let's continue the journey. Sometimes you have multiple identifiers and you want exert a restriction on the relationship of them. Is that possible? Sure! Here comes the **Match Guard**. Its syntax is @@ -370,8 +413,9 @@ constexpr bool sumIs(std::array const& arr, int32_t s) using namespace matchit; Id i, j; return match(arr)( - pattern | ds(i, j) | when(i + j == s) = expr(true), - pattern | _ = expr(false)); + pattern | ds(i, j) | when(i + j == s) = true, + pattern | _ = false + ); } static_assert(sumIs(std::array{5, 6}, 11)); @@ -394,10 +438,10 @@ constexpr int32_t detectTuplePattern(Tuple const& tuple) using namespace matchit; return match(tuple) ( - pattern | ds(2, ooo, 2) = expr(4), - pattern | ds(2, ooo ) = expr(3), - pattern | ds(ooo, 2 ) = expr(2), - pattern | ds(ooo ) = expr(1) + pattern | ds(2, ooo, 2) = 4, + pattern | ds(2, ooo ) = 3, + pattern | ds(ooo, 2 ) = 2, + pattern | ds(ooo ) = 1 ); } @@ -416,8 +460,8 @@ constexpr bool recursiveSymmetric(Range const &range) Id> subrange; return match(range)( pattern | ds(i, subrange.at(ooo), i) = [&] { return recursiveSymmetric(*subrange); }, - pattern | ds(_, ooo, _) = expr(false), - pattern | _ = expr(true) + pattern | ds(_, ooo, _) = false, + pattern | _ = true ); ``` @@ -444,7 +488,8 @@ constexpr auto square(std::optional const& t) Id id; return match(t)( pattern | some(id) = id * id, - pattern | none = expr(0)); + pattern | none = 0 + ); } constexpr auto x = std::make_optional(5); static_assert(square(x) == 25); @@ -459,7 +504,7 @@ Some and none patterns are not atomic patterns in `match(it)`, they are composed template constexpr auto cast = [](auto && input) { return static_cast(input); -}; +}; constexpr auto deref = [](auto &&x) { return *x; }; @@ -483,8 +528,8 @@ constexpr auto getClassName(T const& v) { using namespace matchit; return match(v)( - pattern | as(_) = expr("chars"), - pattern | as(_) = expr("int32_t") + pattern | as(_) = "chars", + pattern | as(_) = "int32_t" ); } @@ -505,8 +550,8 @@ struct Square : Shape {}; auto getClassName(Shape const &s) { return match(s)( - pattern | as(_) = expr("Circle"), - pattern | as(_) = expr("Square") + pattern | as(_) = "Circle", + pattern | as(_) = "Square" ); } ``` @@ -569,9 +614,10 @@ int32_t staticCastAs(Num const& input) { using namespace matchit; return match(input)( - pattern | as(_) = expr(1), - pattern | kind = expr(2), - pattern | _ = expr(3)); + pattern | as(_) = 1, + pattern | kind = 2, + pattern | _ = 3 + ); } int32_t main() @@ -587,6 +633,44 @@ There is additional **Customziation Point**. Users can specialize `PatternTraits` if they want to add a brand new pattern. +### Hello Black Hole! + +One thing to note is that `Id` is not a plain type. Any copies of it are just references to it. +So do not try to return it from where it is defined. + +A bad case would be + +```c++ +auto badId() +{ + Id x; + return x; +} +``` + +Returning a composed pattern including a local `Id` is also incorrect. + +```c++ +auto badPattern() +{ + Id x; + return composeSomePattern(x); +} +``` + +Good practice is to define the `Id` close to its usage in pattern matching. +```c++ +auto goodPattern() +{ + Id x; + auto somePattern = composeSomePattern(x); + return match(...) + ( + pattern | somePattern = ... + ); +} +``` + ## Real world use case [`mathiu`](https://github.com/BowenFu/mathiu.cpp) is a simple computer algebra system built upon `match(it)`. @@ -601,9 +685,18 @@ auto const d = diff(e, x); std::cout << toString(d) << std::endl; ``` +## Projects using this library + +[opennask](https://github.com/HobbyOSs/opennask) : An 80x86 assembler like MASM/NASM for the tiny OS. + + +If you are aware of other projects using this library, please let me know by submitting an issue or an PR. + ## Contact -If you have questions regarding the library, I would like to invite you to [open an issue at GitHub](https://github.com/bowenfu/matchit.cpp/issues/new/choose). +If you have any questions or ideas regarding the library, please [open an issue](https://github.com/bowenfu/matchit.cpp/issues/new/choose). + +Discussions / issues / PRs are all welcome. ## Related Work @@ -619,3 +712,24 @@ If you have questions regarding the library, I would like to invite you to [open ## Other Work If you are interested in `match(it)`, you may also be interested in [hspp](https://github.com/BowenFu/hspp) which brings Haskell style programming to C++. + +## Support this library + +Please star the repo, share the repo, or sponsor one dollar to let me know this library matters. + +## Contributor(s) + +Thanks to all for contributing code and sending in bugs. + +In particular, thanks to the following contributors: + +Hugo Etchegoyen (@[hugoetchegoyen](https://github.com/hugoetchegoyen)) + + +## Sponsor(s) + +Thanks to @[e-dant](https://github.com/e-dant) for sponsoring this project. + +## Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=bowenfu/matchit.cpp&type=Date)](https://star-history.com/#bowenfu/matchit.cpp&Date) diff --git a/REFERENCE.md b/REFERENCE.md index 740d6d5..d30024d 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -21,9 +21,8 @@ The operator `=` between the pattern and handler can be any binary operators. It ### Handler Handlers should always be nullary functions. This is different from `mpark/patterns` and `jbandela/simple_match`. -`expr` can be called to return simple functions returning a single value. -`expr(value)` syntax is inspired by `Boost/Lambda` library. -`pattern | xxx = expr(zzz)` or `pattern | xxx = [&]{zzzzzz}` syntaxes can be aligned and it is easy to find out the pattern parts and handler parts. +Expressions can be used as handlers as well, which are equivalent to nullary functions that return the same values. +Id instances used as handlers are equivalent to nullary functions that return the bound values. ## Pattern Primitives @@ -34,8 +33,8 @@ You can even use a function call as Expression Pattern, the result of the functi ```C++ match(map.find(key))( - pattern | map.end() = expr(false), - pattern | _ = expr(true) + pattern | map.end() = false, + pattern | _ = true ) ``` @@ -53,8 +52,9 @@ Predicate Pattern corresponds to `(? expr)`. ```C++ match(value)( - pattern | meet([](auto &&v { return v >= 0; })) = expr(value), - pattern | _ = expr(0)) + pattern | meet([](auto &&v { return v >= 0; })) = value, + pattern | _ = 0 +) ``` Predicate Pattern syntax is `meet(predicate function)`. @@ -62,8 +62,9 @@ Predicate Pattern syntax is `meet(predicate function)`. ```C++ match(value)( - pattern | (_ >= 0) = expr(value), - pattern | _ = expr(0)) + pattern | (_ >= 0) = value, + pattern | _ = 0 +) ``` The short syntax is inspired by `jbandela/simple_match`. @@ -80,14 +81,14 @@ This means that handlers in `match(it)` are always nullary, but can be unary or ```C++ Id s; match(value)( - pattern | app(_ * _, s) = expr(s), - pattern | _ = expr(0)); + pattern | app(_ * _, s) = s, + pattern | _ = 0 +); ``` You have to define / declare the identifiers first then bind them inside patterns and access them in handlers. -`expr(s)` is a short for `[&]{ return *s; }`, i.e., a function returning the value bound to the identifier. -Identifier Pattern supports binding non-constructable (via reference), non-copyable (via reference or moving) types. +Identifier Pattern supports binding non-constructable (via reference), non-copyable (via reference or moving) types. This can be similar to "Ref pattern" or "Mut Ref pattern" in Rust. Identifier Pattern requires `operator==` for the binding types. @@ -105,13 +106,14 @@ A simple sample can be ```C++ bool flag = true; return match(v)( - pattern | 0 | when(expr(flag)) = expr(true), - pattern | _ = expr(false)); + pattern | 0 | when(flag) = true, + pattern | _ = false +); ``` ### Ooo Pattern -Ooo Pattern can match arbitrary number of items. +Ooo Pattern can match arbitrary number of items. Similar patterns exist in most related works. The current one is mostly influenced by `..` pattern in Rust. (Also inspired by Racket's `...`). It can only be used inside Destructure Patterns and at most one Ooo pattern can appear inside one Destructure Pattern. @@ -120,10 +122,10 @@ Refer to [Pattern Cominators / Destructure Pattern](#destructure-pattern). ```C++ match(tuple) ( - pattern | ds(2, ooo, 2) = expr(4), - pattern | ds(2, ooo ) = expr(3), - pattern | ds(ooo, 2 ) = expr(2), - pattern | ds(ooo ) = expr(1) + pattern | ds(2, ooo, 2) = 4, + pattern | ds(2, ooo ) = 3, + pattern | ds(ooo, 2 ) = 2, + pattern | ds(ooo ) = 1 ) ``` @@ -136,8 +138,8 @@ The Racket syntax is `(or pat ...)`, and the corresponding C++ syntax is `or_(pa ```C++ match(n)( - pattern | or_(1, 3, 5) = expr(true), - pattern | _ = expr(false)) + pattern | or_(1, 3, 5) = true, + pattern | _ = false) ``` Note subpatterns of `or_` pattern can be any patterns, not just expression patterns. @@ -145,8 +147,9 @@ Say Predicate Patterns ```C++ match(n)( - pattern | or_(_ < 3, 5) = expr(true), - pattern | _ = expr(false)) + pattern | or_(_ < 3, 5) = true, + pattern | _ = false +) ``` In Rust and some other related work, there exists a similar `anyof` pattern. But only literal patterns can be used as subpatterns. @@ -158,18 +161,20 @@ The Racket syntax is `(and pat ...)`, and the corresponding C++ syntax is `and_( ```C++ match(value)( - pattern | and_(_ >= min, _ <= max)) = expr(value), - pattern | (_ > max) = expr(max), - pattern | _ = expr(min)) + pattern | and_(_ >= min, _ <= max)) = value, + pattern | (_ > max) = max, + pattern | _ = min +) ``` Note this can also be written as ```C++ match(value)( - pattern | (min <= _ && _ <= max) = expr(value), - pattern | (_ > max) = expr(max), - pattern | _ = expr(min)) + pattern | (min <= _ && _ <= max) = value, + pattern | (_ > max) = max, + pattern | _ = min +) ``` But `&&` can only be used between Predicate patterns, while `and_` can be used for all kinds of patterns (except Ooo Pattern). @@ -189,8 +194,9 @@ That is to say, Predicate Pattern can be expressed with App Pattern, `meet(unary ```C++ match(value)( - pattern | app(_ * _, _ > 1000) = expr(true), - pattern | _ = expr(false)) + pattern | app(_ * _, _ > 1000) = true, + pattern | _ = false +) ``` ### Destructure Pattern @@ -205,7 +211,8 @@ match(expr)( pattern | ds('-', i, j) = i - j, pattern | ds('*', i, j) = i * j, pattern | ds('/', i, j) = i / j, - pattern | _ = expr(-1)) + pattern | _ = -1 +) ``` Note the outermost `ds` inside pattern can be saved. That is to say, when pattern receives multiple parameters, they are treated as subpatterns of a ds pattern. @@ -218,10 +225,11 @@ match(expr)( pattern | ds('-', i, j) = i - j, pattern | ds('*', i, j) = i * j, pattern | ds('/', i, j) = i / j, - pattern | _ = expr(-1)) + pattern | _ = -1 +) ``` -We support Destructure Pattern for `std::tuple`, `std::pair`, `std::array`, and containers / ranges that can be called with `std::begin` and `std::end`. +We support Destructure Pattern for `std::tuple`, `std::pair`, `std::array`, and containers / ranges that can be called with `std::begin` and `std::end`. Mismatch of element numbers is a compile error for fixed-size containers. Mismatch of element numbers is just a mismatch for dynamic containers, neither a compile error, nor a runtime error. @@ -240,8 +248,8 @@ constexpr auto dsViaMember(DummyStruct const&v) const auto dsA = dsVia(&DummyStruct::size, &DummyStruct::name); Id name; return match(v)( - pattern | dsA(2, name) = expr(name), - pattern | _ = expr("not matched") + pattern | dsA(2, name) = name, + pattern | _ = "not matched" ); }; @@ -256,8 +264,9 @@ At Pattern is similar to the `@` pattern in Rust. It can have one subpattern. Th ```C++ Id s; match(value)( - pattern | app(_ * _, s.at(_ > 1000)) = expr(s), - pattern | _ = expr(0)); + pattern | app(_ * _, s.at(_ > 1000)) = s, + pattern | _ = 0 +); ``` ### At Pattern for Ooo Pattern @@ -269,8 +278,8 @@ Id i; Id> subrange; return match(range)( pattern | ds(i, subrange.at(ooo), i) = [&] { return recursiveSymmetric(*subrange); }, - pattern | ds(i, subrange.at(ooo), _) = expr(false), - pattern | _ = expr(true) + pattern | ds(i, subrange.at(ooo), _) = false, + pattern | _ = true ); ``` @@ -284,7 +293,8 @@ Their usage can be ```C++ match(t)( pattern | some(id) = id * id, - pattern | none = expr(0)); + pattern | none = 0 +); ``` ### As Pattern @@ -294,8 +304,8 @@ It can be used to handle sum type, including class hierarchies, std::variant, an ```C++ match(v)( - pattern | as(_) = expr("chars"), - pattern | as(_) = expr("int32_t") + pattern | as(_) = "chars", + pattern | as(_) = "int32_t" ); ``` diff --git a/develop/header.txt b/develop/header.txt index caff298..b2a1dce 100644 --- a/develop/header.txt +++ b/develop/header.txt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Bowen Fu + * Copyright (c) 2021-2022 Bowen Fu * Distributed Under The Apache-2.0 License */ diff --git a/develop/matchit/expression.h b/develop/matchit/expression.h index 7b19c83..2e30084 100644 --- a/develop/matchit/expression.h +++ b/develop/matchit/expression.h @@ -36,6 +36,19 @@ namespace matchit { return v; }); } + template + constexpr auto toNullary(T &&v) + { + if constexpr (std::is_invocable_v>) + { + return v; + } + else + { + return expr(v); + } + } + // for constant template class EvalTraits diff --git a/develop/matchit/patterns.h b/develop/matchit/patterns.h index 4a4029c..7e3fc53 100644 --- a/develop/matchit/patterns.h +++ b/develop/matchit/patterns.h @@ -9,6 +9,10 @@ #include #include +#if !defined(NO_SCALAR_REFERENCES_USED_IN_PATTERNS) +#define NO_SCALAR_REFERENCES_USED_IN_PATTERNS 0 +#endif // !defined(NO_SCALAR_REFERENCES_USED_IN_PATTERNS) + namespace matchit { namespace impl @@ -118,8 +122,8 @@ namespace matchit template class Unique; - template - using UniqueT = typename Unique::type; + template + using UniqueT = typename Unique>::type; template <> class Unique> @@ -132,14 +136,14 @@ namespace matchit class Unique> { public: - using type = PrependUniqueT>>; + using type = PrependUniqueT>; }; static_assert( - std::is_same_v, UniqueT>>); + std::is_same_v, UniqueT>); static_assert( std::is_same_v, int32_t>, - UniqueT, int32_t>>>); + UniqueT, int32_t>>); using std::get; @@ -263,10 +267,13 @@ namespace matchit using type = std::variant; }; + template + using UniqVariant = typename Variant>::type; + template class Context { - using ElementT = typename Variant>>::type; + using ElementT = UniqVariant; using ContainerT = std::array; ContainerT mMemHolder; size_t mSize = 0; @@ -340,11 +347,12 @@ namespace matchit }; template - constexpr auto when(Pred const &pred) + constexpr auto when(Pred &&pred) { - return When{pred}; + auto p = toNullary(pred); + return When{p}; } - + template class PatternHelper { @@ -352,9 +360,10 @@ namespace matchit constexpr explicit PatternHelper(Pattern const &pattern) : mPattern{pattern} {} template - constexpr auto operator=(Func const &func) + constexpr auto operator=(Func &&func) { - return PatternPair{mPattern, func}; + auto f = toNullary(func); + return PatternPair{mPattern, f}; } template constexpr auto operator|(When const &w) @@ -569,8 +578,11 @@ namespace matchit // support constexpr. template using AppResultCurTuple = - std::conditional_t> || - std::is_scalar_v>, + std::conditional_t> +#if NO_SCALAR_REFERENCES_USED_IN_PATTERNS + || std::is_scalar_v> +#endif // NO_SCALAR_REFERENCES_USED_IN_PATTERNS + , std::tuple<>, std::tuple>>>; @@ -738,22 +750,29 @@ namespace matchit template using ValueVariant = - std::conditional_t, - std::variant, - std::variant>; + std::conditional_t, + UniqVariant*>, + std::conditional_t, + UniqVariant, std::remove_reference_t *>, + std::conditional_t>, + UniqVariant*, std::remove_reference_t const *>, + UniqVariant, std::remove_reference_t*, std::remove_reference_t const *> + > + >>; template struct StorePointer &>() = &std::declval())>> - : std::conjunction, - std::negation>> + : std::is_reference // need to double check this condition. to loosen it. { }; + static_assert(!StorePointer::value); + // static_assert(StorePointer, std::tuple>::value); static_assert(StorePointer::value); - static_assert(StorePointer::value); static_assert(StorePointer::value); + static_assert(StorePointer::value); static_assert(StorePointer const, std::tuple const &>::value); @@ -792,89 +811,174 @@ namespace matchit }; template - class Id + class IdBlockBase { - private: - class Block - { - public: - ValueVariant mVariant; - int32_t mDepth; + int32_t mDepth; + protected: + ValueVariant mVariant; - constexpr auto &variant() { return mVariant; } - constexpr auto hasValue() const - { - return std::visit(overload([](Type const &) - { return true; }, - [](Type const *) - { return true; }, - [](std::monostate const &) - { return false; }), - mVariant); - } - constexpr decltype(auto) value() const - { - return std::visit( - overload([](Type const &v) -> Type const & { return v; }, - [](Type const *p) -> Type const & { return *p; }, - [](std::monostate const &) -> Type const & { - throw std::logic_error("invalid state!"); - }), - mVariant); - } + public: + constexpr IdBlockBase() + : mDepth{} + , mVariant{} + {} - constexpr decltype(auto) mutableValue() - { - return std::visit( - overload([](Type &v) -> Type & { return v; }, - [](Type const *) -> Type & { - throw std::logic_error( - "Cannot get mutableValue for pointer type!"); - }, - [](std::monostate &) -> Type & { - throw std::logic_error("Invalid state!"); - }), - mVariant); - } - constexpr void reset(int32_t depth) - { - if (mDepth - depth >= 0) - { - mVariant = {}; - mDepth = depth; - } - } - constexpr void confirm(int32_t depth) - { - if (mDepth > depth || mDepth == 0) - { - assert(depth == mDepth - 1 || depth == mDepth || mDepth == 0); - mDepth = depth; - } - } - }; - class IdUtil + constexpr auto &variant() { return mVariant; } + constexpr void reset(int32_t depth) { - public: - template - constexpr static auto bindValue(ValueVariant &v, Value &&value, - std::false_type /* StorePointer */) + if (mDepth - depth >= 0) { - // for constexpr - v = ValueVariant{std::forward(value)}; + mVariant = {}; + mDepth = depth; } - template - constexpr static auto bindValue(ValueVariant &v, Value &&value, - std::true_type /* StorePointer */) + } + constexpr void confirm(int32_t depth) + { + if (mDepth > depth || mDepth == 0) { - v = ValueVariant{&value}; + assert(depth == mDepth - 1 || depth == mDepth || mDepth == 0); + mDepth = depth; } - }; + } + }; - using BlockVT = std::variant; - BlockVT mBlock = Block{}; + constexpr IdBlockBase dummy; + + template + class IdBlock : public IdBlockBase + { + public: + constexpr auto hasValue() const + { + return std::visit(overload([](Type const &) + { return true; }, + [](Type const *) + { return true; }, + // [](Type *) + // { return true; }, + [](std::monostate const &) + { return false; }), + IdBlockBase::mVariant); + } + constexpr decltype(auto) get() const + { + return std::visit( + overload([](Type const &v) -> Type const & { return v; }, + [](Type const *p) -> Type const & { return *p; }, + [](Type *p) -> Type const & { return *p; }, + [](std::monostate const &) -> Type const & { + throw std::logic_error("invalid state!"); + }), + IdBlockBase::mVariant); + } + }; + + template + class IdBlock : public IdBlock + {}; + + template + class IdBlock : public IdBlockBase + { + public: + constexpr auto hasValue() const + { + return std::visit(overload([](Type *) + { return true; }, + [](std::monostate const &) + { return false; }), + IdBlockBase::mVariant); + } + + constexpr decltype(auto) get() + { + return std::visit( + overload( + [](Type * v) -> Type & + { + if (v == nullptr) + { + throw std::logic_error( + "Trying to dereference a nullptr!"); + } + return *v; + }, + [](std::monostate &) -> Type & + { + throw std::logic_error("Invalid state!"); + }), + IdBlockBase::mVariant); + } + }; + + template + class IdBlock : public IdBlockBase + { + public: + constexpr auto hasValue() const + { + return std::visit(overload([](Type const &) + { return true; }, + [](Type *) + { return true; }, + [](std::monostate const &) + { return false; }), + IdBlockBase::mVariant); + } + + constexpr decltype(auto) get() + { + return std::visit( + overload( + [](Type &v) -> Type & + { + return v; + }, + [](Type * v) -> Type & + { + if (v == nullptr) + { + throw std::logic_error( + "Trying to dereference a nullptr!"); + } + return *v; + }, + [](std::monostate &) -> Type & + { + throw std::logic_error("Invalid state!"); + }), + IdBlockBase::mVariant); + } + }; + + template + class IdUtil + { + public: + template + constexpr static auto bindValue(ValueVariant &v, Value &&value, + std::false_type /* StorePointer */) + { + // for constexpr + v = ValueVariant{std::forward(value)}; + } + template + constexpr static auto bindValue(ValueVariant &v, Value &&value, + std::true_type /* StorePointer */) + { + v = ValueVariant{&value}; + } + }; + + template + class Id + { + private: + using BlockT = IdBlock; + using BlockVT = std::variant; + BlockVT mBlock = BlockT{}; - constexpr Type const &internalValue() const { return block().value(); } + constexpr decltype(auto) internalValue() const { return block().get(); } public: constexpr Id() = default; @@ -891,10 +995,10 @@ namespace matchit // non-const to inform users not to mark Id as const. constexpr auto at(Ooo const &) { return OooBinder{*this}; } - constexpr Block &block() const + constexpr BlockT &block() const { - return std::visit(overload([](Block &v) -> Block & { return v; }, - [](Block *p) -> Block & { return *p; }), + return std::visit(overload([](BlockT &v) -> BlockT & { return v; }, + [](BlockT *p) -> BlockT & { return *p; }), // constexpr does not allow mutable, we use const_cast // instead. Never declare Id as const. const_cast(mBlock)); @@ -906,9 +1010,9 @@ namespace matchit { if (hasValue()) { - return IdTraits::equal(internalValue(), v); + return IdTraits>::equal(internalValue(), v); } - IdUtil::bindValue(block().variant(), std::forward(v), + IdUtil::bindValue(block().variant(), std::forward(v), StorePointer{}); return true; } @@ -916,10 +1020,9 @@ namespace matchit constexpr void confirm(int32_t depth) const { return block().confirm(depth); } constexpr bool hasValue() const { return block().hasValue(); } // non-const to inform users not to mark Id as const. - constexpr Type const &value() { return block().value(); } + constexpr decltype(auto) get() { return block().get(); } // non-const to inform users not to mark Id as const. - constexpr Type const &operator*() { return value(); } - constexpr Type &&move() { return std::move(block().mutableValue()); } + constexpr decltype(auto) operator*() { return get(); } }; template @@ -1537,16 +1640,16 @@ namespace matchit std::tuple<>>); constexpr auto x = [](auto &&t) { return t; }; - static_assert(std::is_same_v>:: - template AppResultTuple, - std::tuple<>>); + // static_assert(std::is_same_v>:: + // template AppResultTuple, + // std::tuple<>>); static_assert( std::is_same_v>:: template AppResultTuple>, std::tuple>>); - static_assert(std::is_same_v>>:: - template AppResultTuple, - std::tuple<>>); + // static_assert(std::is_same_v>>:: + // template AppResultTuple, + // std::tuple<>>); static_assert(PatternTraits>>::nbIdV == 0); static_assert(PatternTraits>>>::nbIdV == 1); diff --git a/develop/matchit/utility.h b/develop/matchit/utility.h index ce98ad6..a6a2670 100644 --- a/develop/matchit/utility.h +++ b/develop/matchit/utility.h @@ -44,36 +44,69 @@ namespace matchit template class AsPointer { + static_assert(!std::is_reference_v); public: template >::type * = nullptr> - constexpr auto operator()(Variant const &v) const + typename std::enable_if>>::type * = nullptr> + constexpr auto operator()(Variant&& v) const { return get_if(std::addressof(v)); } // template to disable implicit cast to std::any - template ::value>::type * = nullptr> - constexpr auto operator()(A const &a) const + template , std::any>::value>::type * = nullptr> + constexpr auto operator()(A&& a) const { return std::any_cast(std::addressof(a)); } + // cast to base class template && std::is_base_of_v>::type * = nullptr> - constexpr auto operator()(D const &d) const + constexpr auto operator()(D const& d) const -> decltype(static_cast(std::addressof(d))) { return static_cast(std::addressof(d)); } + // No way to handle rvalue to save copy in this class. Need to define some in another way to handle this. + // cast to base class + template && std::is_base_of_v>::type * = nullptr> + constexpr auto operator()(D& d) const + -> decltype(static_cast(std::addressof(d))) + { + return static_cast(std::addressof(d)); + } + + // cast to derived class template && std::is_base_of_v>::type * = nullptr> - constexpr auto operator()(B const &b) const + constexpr auto operator()(B const& b) const -> decltype(dynamic_cast(std::addressof(b))) { return dynamic_cast(std::addressof(b)); } + + // cast to derived class + template && std::is_base_of_v>::type * = nullptr> + constexpr auto operator()(B& b) const + -> decltype(dynamic_cast(std::addressof(b))) + { + return dynamic_cast(std::addressof(b)); + } + + constexpr auto operator()(T const& b) const + { + return std::addressof(b); + } + + constexpr auto operator()(T& b) const + { + return std::addressof(b); + } }; + static_assert(std::is_invocable_v, int>); + static_assert(std::is_invocable_v>, std::tuple>); + template constexpr AsPointer asPointer; diff --git a/include/matchit.h b/include/matchit.h index 29bcebb..f3df93b 100644 --- a/include/matchit.h +++ b/include/matchit.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Bowen Fu + * Copyright (c) 2021-2022 Bowen Fu * Distributed Under The Apache-2.0 License */ @@ -111,6 +111,19 @@ namespace matchit { return v; }); } + template + constexpr auto toNullary(T &&v) + { + if constexpr (std::is_invocable_v>) + { + return v; + } + else + { + return expr(v); + } + } + // for constant template class EvalTraits @@ -316,6 +329,10 @@ namespace matchit #include #include +#if !defined(NO_SCALAR_REFERENCES_USED_IN_PATTERNS) +#define NO_SCALAR_REFERENCES_USED_IN_PATTERNS 0 +#endif // !defined(NO_SCALAR_REFERENCES_USED_IN_PATTERNS) + namespace matchit { namespace impl @@ -425,8 +442,8 @@ namespace matchit template class Unique; - template - using UniqueT = typename Unique::type; + template + using UniqueT = typename Unique>::type; template <> class Unique> @@ -439,14 +456,14 @@ namespace matchit class Unique> { public: - using type = PrependUniqueT>>; + using type = PrependUniqueT>; }; static_assert( - std::is_same_v, UniqueT>>); + std::is_same_v, UniqueT>); static_assert( std::is_same_v, int32_t>, - UniqueT, int32_t>>>); + UniqueT, int32_t>>); using std::get; @@ -570,10 +587,13 @@ namespace matchit using type = std::variant; }; + template + using UniqVariant = typename Variant>::type; + template class Context { - using ElementT = typename Variant>>::type; + using ElementT = UniqVariant; using ContainerT = std::array; ContainerT mMemHolder; size_t mSize = 0; @@ -647,11 +667,12 @@ namespace matchit }; template - constexpr auto when(Pred const &pred) + constexpr auto when(Pred &&pred) { - return When{pred}; + auto p = toNullary(pred); + return When{p}; } - + template class PatternHelper { @@ -659,9 +680,10 @@ namespace matchit constexpr explicit PatternHelper(Pattern const &pattern) : mPattern{pattern} {} template - constexpr auto operator=(Func const &func) + constexpr auto operator=(Func &&func) { - return PatternPair{mPattern, func}; + auto f = toNullary(func); + return PatternPair{mPattern, f}; } template constexpr auto operator|(When const &w) @@ -876,8 +898,11 @@ namespace matchit // support constexpr. template using AppResultCurTuple = - std::conditional_t> || - std::is_scalar_v>, + std::conditional_t> +#if NO_SCALAR_REFERENCES_USED_IN_PATTERNS + || std::is_scalar_v> +#endif // NO_SCALAR_REFERENCES_USED_IN_PATTERNS + , std::tuple<>, std::tuple>>>; @@ -1045,22 +1070,29 @@ namespace matchit template using ValueVariant = - std::conditional_t, - std::variant, - std::variant>; + std::conditional_t, + UniqVariant*>, + std::conditional_t, + UniqVariant, std::remove_reference_t *>, + std::conditional_t>, + UniqVariant*, std::remove_reference_t const *>, + UniqVariant, std::remove_reference_t*, std::remove_reference_t const *> + > + >>; template struct StorePointer &>() = &std::declval())>> - : std::conjunction, - std::negation>> + : std::is_reference // need to double check this condition. to loosen it. { }; + static_assert(!StorePointer::value); + // static_assert(StorePointer, std::tuple>::value); static_assert(StorePointer::value); - static_assert(StorePointer::value); static_assert(StorePointer::value); + static_assert(StorePointer::value); static_assert(StorePointer const, std::tuple const &>::value); @@ -1099,89 +1131,174 @@ namespace matchit }; template - class Id + class IdBlockBase { - private: - class Block - { - public: - ValueVariant mVariant; - int32_t mDepth; + int32_t mDepth; + protected: + ValueVariant mVariant; - constexpr auto &variant() { return mVariant; } - constexpr auto hasValue() const - { - return std::visit(overload([](Type const &) - { return true; }, - [](Type const *) - { return true; }, - [](std::monostate const &) - { return false; }), - mVariant); - } - constexpr decltype(auto) value() const - { - return std::visit( - overload([](Type const &v) -> Type const & { return v; }, - [](Type const *p) -> Type const & { return *p; }, - [](std::monostate const &) -> Type const & { - throw std::logic_error("invalid state!"); - }), - mVariant); - } + public: + constexpr IdBlockBase() + : mDepth{} + , mVariant{} + {} - constexpr decltype(auto) mutableValue() - { - return std::visit( - overload([](Type &v) -> Type & { return v; }, - [](Type const *) -> Type & { - throw std::logic_error( - "Cannot get mutableValue for pointer type!"); - }, - [](std::monostate &) -> Type & { - throw std::logic_error("Invalid state!"); - }), - mVariant); - } - constexpr void reset(int32_t depth) - { - if (mDepth - depth >= 0) - { - mVariant = {}; - mDepth = depth; - } - } - constexpr void confirm(int32_t depth) - { - if (mDepth > depth || mDepth == 0) - { - assert(depth == mDepth - 1 || depth == mDepth || mDepth == 0); - mDepth = depth; - } - } - }; - class IdUtil + constexpr auto &variant() { return mVariant; } + constexpr void reset(int32_t depth) { - public: - template - constexpr static auto bindValue(ValueVariant &v, Value &&value, - std::false_type /* StorePointer */) + if (mDepth - depth >= 0) { - // for constexpr - v = ValueVariant{std::forward(value)}; + mVariant = {}; + mDepth = depth; } - template - constexpr static auto bindValue(ValueVariant &v, Value &&value, - std::true_type /* StorePointer */) + } + constexpr void confirm(int32_t depth) + { + if (mDepth > depth || mDepth == 0) { - v = ValueVariant{&value}; + assert(depth == mDepth - 1 || depth == mDepth || mDepth == 0); + mDepth = depth; } - }; + } + }; + + constexpr IdBlockBase dummy; + + template + class IdBlock : public IdBlockBase + { + public: + constexpr auto hasValue() const + { + return std::visit(overload([](Type const &) + { return true; }, + [](Type const *) + { return true; }, + // [](Type *) + // { return true; }, + [](std::monostate const &) + { return false; }), + IdBlockBase::mVariant); + } + constexpr decltype(auto) get() const + { + return std::visit( + overload([](Type const &v) -> Type const & { return v; }, + [](Type const *p) -> Type const & { return *p; }, + [](Type *p) -> Type const & { return *p; }, + [](std::monostate const &) -> Type const & { + throw std::logic_error("invalid state!"); + }), + IdBlockBase::mVariant); + } + }; + + template + class IdBlock : public IdBlock + {}; + + template + class IdBlock : public IdBlockBase + { + public: + constexpr auto hasValue() const + { + return std::visit(overload([](Type *) + { return true; }, + [](std::monostate const &) + { return false; }), + IdBlockBase::mVariant); + } - using BlockVT = std::variant; - BlockVT mBlock = Block{}; + constexpr decltype(auto) get() + { + return std::visit( + overload( + [](Type * v) -> Type & + { + if (v == nullptr) + { + throw std::logic_error( + "Trying to dereference a nullptr!"); + } + return *v; + }, + [](std::monostate &) -> Type & + { + throw std::logic_error("Invalid state!"); + }), + IdBlockBase::mVariant); + } + }; - constexpr Type const &internalValue() const { return block().value(); } + template + class IdBlock : public IdBlockBase + { + public: + constexpr auto hasValue() const + { + return std::visit(overload([](Type const &) + { return true; }, + [](Type *) + { return true; }, + [](std::monostate const &) + { return false; }), + IdBlockBase::mVariant); + } + + constexpr decltype(auto) get() + { + return std::visit( + overload( + [](Type &v) -> Type & + { + return v; + }, + [](Type * v) -> Type & + { + if (v == nullptr) + { + throw std::logic_error( + "Trying to dereference a nullptr!"); + } + return *v; + }, + [](std::monostate &) -> Type & + { + throw std::logic_error("Invalid state!"); + }), + IdBlockBase::mVariant); + } + }; + + template + class IdUtil + { + public: + template + constexpr static auto bindValue(ValueVariant &v, Value &&value, + std::false_type /* StorePointer */) + { + // for constexpr + v = ValueVariant{std::forward(value)}; + } + template + constexpr static auto bindValue(ValueVariant &v, Value &&value, + std::true_type /* StorePointer */) + { + v = ValueVariant{&value}; + } + }; + + template + class Id + { + private: + using BlockT = IdBlock; + using BlockVT = std::variant; + BlockVT mBlock = BlockT{}; + + constexpr decltype(auto) internalValue() const { return block().get(); } public: constexpr Id() = default; @@ -1198,10 +1315,10 @@ namespace matchit // non-const to inform users not to mark Id as const. constexpr auto at(Ooo const &) { return OooBinder{*this}; } - constexpr Block &block() const + constexpr BlockT &block() const { - return std::visit(overload([](Block &v) -> Block & { return v; }, - [](Block *p) -> Block & { return *p; }), + return std::visit(overload([](BlockT &v) -> BlockT & { return v; }, + [](BlockT *p) -> BlockT & { return *p; }), // constexpr does not allow mutable, we use const_cast // instead. Never declare Id as const. const_cast(mBlock)); @@ -1213,9 +1330,9 @@ namespace matchit { if (hasValue()) { - return IdTraits::equal(internalValue(), v); + return IdTraits>::equal(internalValue(), v); } - IdUtil::bindValue(block().variant(), std::forward(v), + IdUtil::bindValue(block().variant(), std::forward(v), StorePointer{}); return true; } @@ -1223,10 +1340,9 @@ namespace matchit constexpr void confirm(int32_t depth) const { return block().confirm(depth); } constexpr bool hasValue() const { return block().hasValue(); } // non-const to inform users not to mark Id as const. - constexpr Type const &value() { return block().value(); } + constexpr decltype(auto) get() { return block().get(); } // non-const to inform users not to mark Id as const. - constexpr Type const &operator*() { return value(); } - constexpr Type &&move() { return std::move(block().mutableValue()); } + constexpr decltype(auto) operator*() { return get(); } }; template @@ -1844,16 +1960,16 @@ namespace matchit std::tuple<>>); constexpr auto x = [](auto &&t) { return t; }; - static_assert(std::is_same_v>:: - template AppResultTuple, - std::tuple<>>); + // static_assert(std::is_same_v>:: + // template AppResultTuple, + // std::tuple<>>); static_assert( std::is_same_v>:: template AppResultTuple>, std::tuple>>); - static_assert(std::is_same_v>>:: - template AppResultTuple, - std::tuple<>>); + // static_assert(std::is_same_v>>:: + // template AppResultTuple, + // std::tuple<>>); static_assert(PatternTraits>>::nbIdV == 0); static_assert(PatternTraits>>>::nbIdV == 1); @@ -1977,36 +2093,69 @@ namespace matchit template class AsPointer { + static_assert(!std::is_reference_v); public: template >::type * = nullptr> - constexpr auto operator()(Variant const &v) const + typename std::enable_if>>::type * = nullptr> + constexpr auto operator()(Variant&& v) const { return get_if(std::addressof(v)); } // template to disable implicit cast to std::any - template ::value>::type * = nullptr> - constexpr auto operator()(A const &a) const + template , std::any>::value>::type * = nullptr> + constexpr auto operator()(A&& a) const { return std::any_cast(std::addressof(a)); } + // cast to base class template && std::is_base_of_v>::type * = nullptr> - constexpr auto operator()(D const &d) const + constexpr auto operator()(D const& d) const -> decltype(static_cast(std::addressof(d))) { return static_cast(std::addressof(d)); } + // No way to handle rvalue to save copy in this class. Need to define some in another way to handle this. + // cast to base class + template && std::is_base_of_v>::type * = nullptr> + constexpr auto operator()(D& d) const + -> decltype(static_cast(std::addressof(d))) + { + return static_cast(std::addressof(d)); + } + + // cast to derived class template && std::is_base_of_v>::type * = nullptr> - constexpr auto operator()(B const &b) const + constexpr auto operator()(B const& b) const -> decltype(dynamic_cast(std::addressof(b))) { return dynamic_cast(std::addressof(b)); } + + // cast to derived class + template && std::is_base_of_v>::type * = nullptr> + constexpr auto operator()(B& b) const + -> decltype(dynamic_cast(std::addressof(b))) + { + return dynamic_cast(std::addressof(b)); + } + + constexpr auto operator()(T const& b) const + { + return std::addressof(b); + } + + constexpr auto operator()(T& b) const + { + return std::addressof(b); + } }; + static_assert(std::is_invocable_v, int>); + static_assert(std::is_invocable_v>, std::tuple>); + template constexpr AsPointer asPointer; diff --git a/sample/CMakeLists.txt b/sample/CMakeLists.txt index 3200e13..94587d0 100644 --- a/sample/CMakeLists.txt +++ b/sample/CMakeLists.txt @@ -50,6 +50,7 @@ Closed-Class-Hierarchy Matcher-within visit graph +mutation ) foreach(sample ${MATCHIT_SAMPLES}) diff --git a/sample/Conditional-if-let-Expressions.cpp b/sample/Conditional-if-let-Expressions.cpp index 7635983..433b948 100644 --- a/sample/Conditional-if-let-Expressions.cpp +++ b/sample/Conditional-if-let-Expressions.cpp @@ -23,17 +23,17 @@ int32_t main() return "Using your favorite color, " + *color + ", as the background"; }, - pattern | _ | when(expr(is_tuesday)) = expr("Tuesday is green day!"), + pattern | _ | when(is_tuesday) = "Tuesday is green day!", pattern | _ = [&] { Id age_; return match(age)(pattern | as(age_) | when(age_ > 30) = - expr("Using purple as the background color"), + "Using purple as the background color", pattern | as(age_) = - expr("Using orange as the background color"), + "Using orange as the background color", pattern | _ = - expr("Using blue as the background color")); + "Using blue as the background color"); }); return 0; diff --git a/sample/Destructuring-Nested-Structs-and-Enums.cpp b/sample/Destructuring-Nested-Structs-and-Enums.cpp index f1543f6..92f5c71 100644 --- a/sample/Destructuring-Nested-Structs-and-Enums.cpp +++ b/sample/Destructuring-Nested-Structs-and-Enums.cpp @@ -26,7 +26,6 @@ int32_t main() using namespace matchit; Id r, g, b; Id h, s, v; - Id text; match(msg)( pattern | as(as(ds(r, g, b))) = [&] diff --git a/sample/Evaluating-Expression-Trees.cpp b/sample/Evaluating-Expression-Trees.cpp index 457ad9f..d9d7cde 100644 --- a/sample/Evaluating-Expression-Trees.cpp +++ b/sample/Evaluating-Expression-Trees.cpp @@ -51,14 +51,14 @@ int eval(const Expr &ex) return match(ex)( // clang-format off // FIXME: Expr{5} won't match the following line. - pattern | as(i) = expr(i), + pattern | as(i) = i, pattern | asNegDs(some(e)) = [&]{ return -eval(*e); }, pattern | asAddDs(some(l), some(r)) = [&]{ return eval(*l) + eval(*r); }, // Optimize multiplication by 0. - pattern | asMulDs(some(as(0)), _) = expr(0), - pattern | asMulDs(_, some(as(0))) = expr(0), + pattern | asMulDs(some(as(0)), _) = 0, + pattern | asMulDs(_, some(as(0))) = 0, pattern | asMulDs(some(l), some(r)) = [&]{ return eval(*l) * eval(*r); }, - pattern | _ = expr(-1) + pattern | _ = -1 // clang-format on ); } diff --git a/sample/Extra-Conditionals-with-Match-Guards.cpp b/sample/Extra-Conditionals-with-Match-Guards.cpp index f8e03be..59376c1 100644 --- a/sample/Extra-Conditionals-with-Match-Guards.cpp +++ b/sample/Extra-Conditionals-with-Match-Guards.cpp @@ -56,8 +56,8 @@ void sample3() std::cout << match(x)( // clang-format off - pattern | or_(4, 5, 6) | when(expr(y)) = expr("yes"), - pattern | _ = expr("no") + pattern | or_(4, 5, 6) | when(y) = "yes", + pattern | _ = "no" // clang-format on ) << std::endl; diff --git a/sample/Literal-pattern.cpp b/sample/Literal-pattern.cpp index b8f1049..1023a22 100644 --- a/sample/Literal-pattern.cpp +++ b/sample/Literal-pattern.cpp @@ -8,10 +8,10 @@ int32_t main() using namespace matchit; std::cout << match(i)( // clang-format off - pattern | -1 = expr("It's minus one"), - pattern | 1 = expr("It's a one"), - pattern | or_(2, 4) = expr("It's either a two or a four"), - pattern | _ = expr("Matched none of the arms") + pattern | -1 = "It's minus one", + pattern | 1 = "It's a one", + pattern | or_(2, 4) = "It's either a two or a four", + pattern | _ = "Matched none of the arms" // clang-format off ) << std::endl; diff --git a/sample/Predicate-based-Discriminator.cpp b/sample/Predicate-based-Discriminator.cpp index 488bbbb..d6e6b75 100644 --- a/sample/Predicate-based-Discriminator.cpp +++ b/sample/Predicate-based-Discriminator.cpp @@ -63,9 +63,10 @@ char *String::data() Id l; Id> r; return match(*this)( - pattern | asEnum(l) = expr(l), + pattern | asEnum(l) = l, pattern | asEnum(r) = [&] - { return (*r).ptr; }); + { return (*r).ptr; } + ); } int32_t main() diff --git a/sample/Range-pattern.cpp b/sample/Range-pattern.cpp index 88c00ce..4e5cf99 100644 --- a/sample/Range-pattern.cpp +++ b/sample/Range-pattern.cpp @@ -14,9 +14,9 @@ void sample() constexpr auto c = 'f'; constexpr auto valid_variable = match(c)( // clang-format off - pattern | ('a' <= _ && _ <= 'z') = expr(true), - pattern | ('A' <= _ && _ <= 'Z') = expr(true), - pattern | _ = expr(false) + pattern | ('a' <= _ && _ <= 'z') = true, + pattern | ('A' <= _ && _ <= 'Z') = true, + pattern | _ = false // clang-format on ); static_cast(valid_variable); @@ -24,9 +24,9 @@ void sample() constexpr auto ph = 10; std::cout << match(ph)( // clang-format off - pattern | (0 <= _ && _ <= 6 ) = expr("acid"), - pattern | (7 ) = expr("neutral"), - pattern | (8 <= _ && _ <= 14) = expr("base"), + pattern | (0 <= _ && _ <= 6 ) = "acid", + pattern | (7 ) = "neutral", + pattern | (8 <= _ && _ <= 14) = "base", pattern | (_ ) = [] { assert(false && "unreachable"); return ""; }) // clang-format on << std::endl; @@ -45,10 +45,10 @@ void sample() std::cout << match(altitude)( // clang-format off - pattern | (TROPOSPHERE_MIN <= _ && _ <= TROPOSPHERE_MAX ) = expr("troposphere"), - pattern | (STRATOSPHERE_MIN <= _ && _ <= STRATOSPHERE_MAX) = expr("stratosphere"), - pattern | (MESOSPHERE_MIN <= _ && _ <= MESOSPHERE_MAX ) = expr("mesosphere"), - pattern | (_ ) = expr("outer space, maybe")) + pattern | (TROPOSPHERE_MIN <= _ && _ <= TROPOSPHERE_MAX ) = "troposphere", + pattern | (STRATOSPHERE_MIN <= _ && _ <= STRATOSPHERE_MAX) = "stratosphere", + pattern | (MESOSPHERE_MIN <= _ && _ <= MESOSPHERE_MAX ) = "mesosphere", + pattern | (_ ) = "outer space, maybe") // clang-format on << std::endl; @@ -66,10 +66,10 @@ void sample() std::cout << match(static_cast(0xfacade))( // clang-format off - pattern | (0U <= _ && _ <= std::numeric_limits::max()) = expr("fits in a u8"), - pattern | (0U <= _ && _ <= std::numeric_limits::max()) = expr("fits in a u16"), - pattern | (0U <= _ && _ <= std::numeric_limits::max()) = expr("fits in a u32"), - pattern | _ = expr("too big")) + pattern | (0U <= _ && _ <= std::numeric_limits::max()) = "fits in a u8", + pattern | (0U <= _ && _ <= std::numeric_limits::max()) = "fits in a u16", + pattern | (0U <= _ && _ <= std::numeric_limits::max()) = "fits in a u32", + pattern | _ = "too big") // clang-format on << std::endl; } diff --git a/sample/Red-black-Tree-Rebalancing.cpp b/sample/Red-black-Tree-Rebalancing.cpp index c376c9b..62fcbdd 100644 --- a/sample/Red-black-Tree-Rebalancing.cpp +++ b/sample/Red-black-Tree-Rebalancing.cpp @@ -78,7 +78,7 @@ void Node::balance() return Node{Red, std::make_shared(Black, *a, *x, *b), *y, std::make_shared(Black, *c, *z, *d)}; }, - pattern | self = expr(self) // do nothing + pattern | self = self // do nothing ); } @@ -125,7 +125,7 @@ void Node::balance() = [&] { return Node{Red, std::make_shared(Black, *a, *x, *b), *y, std::make_shared(Black, *c, *z, *d)}; }, - pattern | self = expr(self) // do nothing + pattern | self = self // do nothing ); } diff --git a/sample/Reference-pattern.cpp b/sample/Reference-pattern.cpp index 38b655d..954cc9a 100644 --- a/sample/Reference-pattern.cpp +++ b/sample/Reference-pattern.cpp @@ -9,11 +9,15 @@ void sample() int32_t const zero = 0; - auto const a = match(*int_reference)(pattern | zero = expr("zero"), - pattern | _ = expr("some")); + auto const a = match(*int_reference)( + pattern | zero = "zero", + pattern | _ = "some" + ); - auto const b = match(int_reference)(pattern | &zero = expr("zero"), - pattern | _ = expr("some")); + auto const b = match(int_reference)( + pattern | &zero = "zero", + pattern | _ = "some" + ); static_cast(a); static_cast(b); diff --git a/sample/Slice-pattern.cpp b/sample/Slice-pattern.cpp index db1ff3f..d28053c 100644 --- a/sample/Slice-pattern.cpp +++ b/sample/Slice-pattern.cpp @@ -8,8 +8,10 @@ void sample1() // Fixed size constexpr auto arr = std::array{1, 2, 3}; Id a, b, c; - match(arr)(pattern | ds(1, _, _) = expr("starts with one"), - pattern | ds(a, b, c) = expr("starts with something else")); + match(arr)( + pattern | ds(1, _, _) = "starts with one", + pattern | ds(a, b, c) = "starts with something else" + ); } void sample2() diff --git a/sample/Terminate.cpp b/sample/Terminate.cpp index b7d7cbc..b90234e 100644 --- a/sample/Terminate.cpp +++ b/sample/Terminate.cpp @@ -14,8 +14,8 @@ Op parseOp(char t) using namespace matchit; Id token; return match(t)( - pattern | '+' = expr(Op::Add), pattern | '-' = expr(Op::Sub), - pattern | '*' = expr(Op::Mul), pattern | '/' = expr(Op::Div), + pattern | '+' = Op::Add, pattern | '-' = Op::Sub, + pattern | '*' = Op::Mul, pattern | '/' = Op::Div, pattern | token = [&] { std::cerr << "Unexpected: " << *token; diff --git a/sample/While-conditional-let-Loops.cpp b/sample/While-conditional-let-Loops.cpp index f1ba61a..979834e 100644 --- a/sample/While-conditional-let-Loops.cpp +++ b/sample/While-conditional-let-Loops.cpp @@ -39,7 +39,7 @@ int32_t main() std::cout << *top << std::endl; return true; }, - pattern | _ = expr(false))) + pattern | _ = false)) { }; } \ No newline at end of file diff --git a/sample/checkAndlogLarge.cpp b/sample/checkAndlogLarge.cpp index 9e7fc12..cc9160d 100644 --- a/sample/checkAndlogLarge.cpp +++ b/sample/checkAndlogLarge.cpp @@ -15,7 +15,8 @@ constexpr bool checkAndlogLarge(double value) std::cout << value << "^2 = " << *s << " > 1000!" << std::endl; return true; }, - pattern | _ = expr(false)); + pattern | _ = false + ); } // comment out std::cout then uncomment this. diff --git a/sample/clip.cpp b/sample/clip.cpp index 9d97d15..dc20473 100644 --- a/sample/clip.cpp +++ b/sample/clip.cpp @@ -6,9 +6,9 @@ constexpr double clip(double value, double min, double max) using namespace matchit; return match(value)( // clang-format off - pattern | and_(_ >= min, _ <= max) = expr(value), - pattern | (_ > max) = expr(max), - pattern | _ = expr(min) + pattern | and_(_ >= min, _ <= max) = value, + pattern | (_ > max) = max, + pattern | _ = min // clang-format on ); } diff --git a/sample/contains.cpp b/sample/contains.cpp index 98f4b0d..e104b14 100644 --- a/sample/contains.cpp +++ b/sample/contains.cpp @@ -8,8 +8,8 @@ constexpr bool contains(Map const &map, Key const &key) using namespace matchit; return match(map.find(key))( // clang-format off - pattern | map.end() = expr(false), - pattern | _ = expr(true) + pattern | map.end() = false, + pattern | _ = true // clang-format on ); } diff --git a/sample/customAsPointer.cpp b/sample/customAsPointer.cpp index 7733a4d..ce7ea5b 100644 --- a/sample/customAsPointer.cpp +++ b/sample/customAsPointer.cpp @@ -51,9 +51,9 @@ constexpr int32_t staticCastAs(Num const &input) using namespace matchit; return match(input)( // clang-format off - pattern | as(_) = expr(1), - pattern | kind = expr(2), - pattern | _ = expr(3) + pattern | as(_) = 1, + pattern | kind = 2, + pattern | _ = 3 // clang-format on ); } diff --git a/sample/customDs.cpp b/sample/customDs.cpp index 330fe65..2865477 100644 --- a/sample/customDs.cpp +++ b/sample/customDs.cpp @@ -38,8 +38,8 @@ constexpr auto getSecond(DummyStruct const &d) Id i; return match(d)( // clang-format off - pattern | ds(2, i) = expr(i), - pattern | _ = expr("not matched") + pattern | ds(2, i) = i, + pattern | _ = "not matched" // clang-format on ); } @@ -59,8 +59,8 @@ constexpr auto dsByMember(DummyStruct const &v) Id i; return match(v)( // clang-format off - pattern | dsA(2, i) = expr(i), - pattern | _ = expr("not matched") + pattern | dsA(2, i) = i, + pattern | _ = "not matched" // clang-format on ); } diff --git a/sample/detectTuplePattern.cpp b/sample/detectTuplePattern.cpp index d46c08c..0dfec1a 100644 --- a/sample/detectTuplePattern.cpp +++ b/sample/detectTuplePattern.cpp @@ -8,10 +8,10 @@ constexpr int32_t detectTuplePattern(Tuple const &tuple) using namespace matchit; return match(tuple)( // clang-format off - pattern | ds(2, ooo, 2) = expr(4), - pattern | ds(2, ooo ) = expr(3), - pattern | ds(ooo, 2 ) = expr(2), - pattern | ds(ooo ) = expr(1) + pattern | ds(2, ooo, 2) = 4, + pattern | ds(2, ooo ) = 3, + pattern | ds(ooo, 2 ) = 2, + pattern | ds(ooo ) = 1 // clang-format on ); } diff --git a/sample/getClassName.cpp b/sample/getClassName.cpp index 623e86e..c48b6e6 100644 --- a/sample/getClassName.cpp +++ b/sample/getClassName.cpp @@ -15,8 +15,10 @@ struct Square : Shape constexpr auto getClassName(Shape const &s) { using namespace matchit; - return match(s)(pattern | as(_) = expr("Circle"), - pattern | as(_) = expr("Square")); + return match(s)( + pattern | as(_) = "Circle", + pattern | as(_) = "Square" + ); } int32_t main() diff --git a/sample/isLarge.cpp b/sample/isLarge.cpp index 740a569..688f3f9 100644 --- a/sample/isLarge.cpp +++ b/sample/isLarge.cpp @@ -1,3 +1,5 @@ +#define NO_SCALAR_REFERENCES_USED_IN_PATTERNS 1 + #include "matchit.h" #include @@ -6,8 +8,8 @@ constexpr bool isLarge(double value) using namespace matchit; return match(value)( // clang-format off - pattern | app(_ * _, _ > 1000) = expr(true), - pattern | _ = expr(false) + pattern | app(_ * _, _ > 1000) = true, + pattern | _ = false // clang-format on ); } diff --git a/sample/isValid.cpp b/sample/isValid.cpp index b345ce9..28b6646 100644 --- a/sample/isValid.cpp +++ b/sample/isValid.cpp @@ -6,8 +6,8 @@ constexpr bool isValid(int32_t n) using namespace matchit; return match(n)( // clang-format off - pattern | or_(1, 3, 5) = expr(true), - pattern | _ = expr(false) + pattern | or_(1, 3, 5) = true, + pattern | _ = false // clang-format on ); } diff --git a/sample/mutation.cpp b/sample/mutation.cpp new file mode 100644 index 0000000..6c33efc --- /dev/null +++ b/sample/mutation.cpp @@ -0,0 +1,63 @@ +#include "matchit.h" +#include +#include + +class Circle { +public: + void setLineWidth(float w) + { + std::cout << "Calling Circle::setLineWidth with w = " << w << std::endl; + }; + // other stuff +}; + +class Square { +public: + void setLineWidth(float) + { + // set line width + }; + // other stuff +}; + +class Image { + // other stuff +}; + +auto operator==(Circle const&, Circle const&) +{ + return true; +} + +auto operator==(Square const&, Square const&) +{ + return true; +} + +using Visual = std::variant; + +using namespace matchit; + +void setLineWidth(Visual &visual, float width) { + Id sq; + Id cir; + match(visual) + ( + pattern | as(_) = []{}, + pattern | as(sq) = [&] + { + (*sq).setLineWidth(width); + }, + pattern | as(cir) = [&] + { + (*cir).setLineWidth(width); + } + ); +} + +int main() +{ + Visual v = Circle{}; + setLineWidth(v, 1); + return 0; +} diff --git a/sample/optionalLift.cpp b/sample/optionalLift.cpp index 8a273ea..7a9597c 100644 --- a/sample/optionalLift.cpp +++ b/sample/optionalLift.cpp @@ -14,7 +14,7 @@ auto optionalLift(Func func) return match(v)( // clang-format off pattern | some(x) = [&] { return std::make_optional(func(*x)); }, - pattern | none = expr(RetType{}) + pattern | none = RetType{} // clang-format on ); }; diff --git a/sample/quotientRemainder.cpp b/sample/quotientRemainder.cpp index 8663a37..d06575b 100644 --- a/sample/quotientRemainder.cpp +++ b/sample/quotientRemainder.cpp @@ -1,3 +1,5 @@ +#define NO_SCALAR_REFERENCES_USED_IN_PATTERNS 1 + #include "matchit.h" #include @@ -23,6 +25,7 @@ constexpr std::array quoRem(int32_t dividend, int32_t divisor) ); } +// todo: make Id variants for Id Id Id Id constexpr auto qrResult1 = quoRem(12, 6); static_assert(qrResult1[0] == 2); static_assert(qrResult1[1] == 0); diff --git a/sample/relu.cpp b/sample/relu.cpp index 3afdbce..6aaa189 100644 --- a/sample/relu.cpp +++ b/sample/relu.cpp @@ -6,8 +6,8 @@ constexpr double relu(double value) using namespace matchit; return match(value)( // clang-format off - pattern | (_ >= 0) = expr(value), - pattern | _ = expr(0) + pattern | (_ >= 0) = value, + pattern | _ = 0 // clang-format on ); } diff --git a/sample/someNone.cpp b/sample/someNone.cpp index 845909f..95b20bc 100644 --- a/sample/someNone.cpp +++ b/sample/someNone.cpp @@ -1,3 +1,5 @@ +#define NO_SCALAR_REFERENCES_USED_IN_PATTERNS 1 + #include "matchit.h" #include #include @@ -10,7 +12,7 @@ constexpr auto square(std::optional const &t) return match(t)( // clang-format off pattern | some(id) = id * id, - pattern | none = expr(0) + pattern | none = 0 // clang-format on ); } diff --git a/sample/sumIs.cpp b/sample/sumIs.cpp index 4d93b48..f90fbb0 100644 --- a/sample/sumIs.cpp +++ b/sample/sumIs.cpp @@ -8,8 +8,8 @@ constexpr bool sumIs(std::array const &arr, int32_t s) Id i, j; return match(arr)( // clang-format off - pattern | ds(i, j) | when(i + j == s) = expr(true), - pattern | _ = expr(false) + pattern | ds(i, j) | when(i + j == s) = true, + pattern | _ = false // clang-format on ); } diff --git a/sample/symmetric.cpp b/sample/symmetric.cpp index 70378b0..08bceb2 100644 --- a/sample/symmetric.cpp +++ b/sample/symmetric.cpp @@ -10,8 +10,8 @@ constexpr bool recursiveSymmetric(Range const &range) return match(range)( // clang-format off pattern | ds(i, subrange.at(ooo), i) = [&] { return recursiveSymmetric(*subrange); }, - pattern | ds(_, ooo, _) = expr(false), - pattern | _ = expr(true) + pattern | ds(_, ooo, _) = false, + pattern | _ = true // clang-format on ); } @@ -22,8 +22,8 @@ constexpr bool symmetricArray(std::array const &arr) Id i, j; return match(arr)( // clang-format off - pattern | ds(i, j, _, j, i) = expr(true), - pattern | _ = expr(false) + pattern | ds(i, j, _, j, i) = true, + pattern | _ = false // clang-format on ); } diff --git a/sample/variantAny.cpp b/sample/variantAny.cpp index cf0c0e6..f23a426 100644 --- a/sample/variantAny.cpp +++ b/sample/variantAny.cpp @@ -1,3 +1,5 @@ +#define NO_SCALAR_REFERENCES_USED_IN_PATTERNS 1 + #include "matchit.h" #include @@ -7,8 +9,8 @@ constexpr auto getClassName(T const &v) using namespace matchit; return match(v)( // clang-format off - pattern | as(_) = expr("chars"), - pattern | as(_) = expr("int32_t") + pattern | as(_) = "chars", + pattern | as(_) = "int32_t" // clang-format on ); } diff --git a/test/matchit/CMakeLists.txt b/test/matchit/CMakeLists.txt index 4751f42..809aae7 100644 --- a/test/matchit/CMakeLists.txt +++ b/test/matchit/CMakeLists.txt @@ -1,5 +1,5 @@ -add_executable(unittests app.cpp constexpr.cpp expr.cpp legacy.cpp noRet.cpp id.cpp ds.cpp) +add_executable(unittests app.cpp constexpr.cpp expr.cpp legacy.cpp noRet.cpp id.cpp ds.cpp optexpr.cpp) target_compile_options(unittests PRIVATE ${BASE_COMPILE_FLAGS}) target_link_libraries(unittests PRIVATE matchit gtest_main) set_target_properties(unittests PROPERTIES CXX_EXTENSIONS OFF) -gtest_discover_tests(unittests) \ No newline at end of file +gtest_discover_tests(unittests) diff --git a/test/matchit/app.cpp b/test/matchit/app.cpp index 6a9bfe5..95612f6 100644 --- a/test/matchit/app.cpp +++ b/test/matchit/app.cpp @@ -31,3 +31,14 @@ TEST(App, someAs) auto const x = std::unique_ptr{new Derived}; EXPECT_TRUE(matched(x, some(as(_)))); } + +TEST(App, scalarMut) +{ + auto const x = std::make_unique(10); + Id ii; + match(x) + ( + pattern | some(as(as(ii))) = [&] { *ii = 20 ; } + ); + EXPECT_EQ(*x, 20); +} diff --git a/test/matchit/constexpr.cpp b/test/matchit/constexpr.cpp index 0f318cc..e0d42f1 100644 --- a/test/matchit/constexpr.cpp +++ b/test/matchit/constexpr.cpp @@ -6,8 +6,8 @@ constexpr int32_t fib(int32_t n) assert(n >= 1); return match(n)( // clang-format off - pattern | 1 = expr(1), - pattern | 2 = expr(1), + pattern | 1 = 1, + pattern | 2 = 1, pattern | _ = [n] { return fib(n - 1) + fib(n - 2); } // clang-format on ); @@ -24,9 +24,9 @@ constexpr auto eval(Value &&input) { return match(input)( // clang-format off - pattern | ds('/', 1, 1) = expr(1), - pattern | ds('/', 0, _) = expr(0), - pattern | _ = expr(-1)); + pattern | ds('/', 1, 1) = 1, + pattern | ds('/', 0, _) = 0, + pattern | _ = -1); // clang-format on } diff --git a/test/matchit/ds.cpp b/test/matchit/ds.cpp index 51fc75c..e52e4c6 100644 --- a/test/matchit/ds.cpp +++ b/test/matchit/ds.cpp @@ -97,7 +97,8 @@ TEST(Ds, vecOooBinder1) expectRange(*subrange, expected); return true; }, - pattern | _ = expr(false)); + pattern | _ = false + ); EXPECT_TRUE(matched); } @@ -168,7 +169,7 @@ TEST(Ds, arrayOooBinder1) expectRange(*subrange, expected); return true; }, - pattern | _ = expr(false)); + pattern | _ = false); EXPECT_TRUE(matched); } @@ -240,8 +241,8 @@ constexpr bool recursiveSymmetric(Range const &range) return match(range)( // clang-format off pattern | ds(i, subrange.at(ooo), i) = [&] { return recursiveSymmetric(*subrange); }, - pattern | ds(i, subrange.at(ooo), _) = expr(false), - pattern | _ = expr(true) + pattern | ds(i, subrange.at(ooo), _) = false, + pattern | _ = true // clang-format on ); } diff --git a/test/matchit/id.cpp b/test/matchit/id.cpp index e9b67d6..287f691 100644 --- a/test/matchit/id.cpp +++ b/test/matchit/id.cpp @@ -27,7 +27,7 @@ TEST(Id, resetAfterFailure) match(10)(pattern | x = [&] { EXPECT_EQ(*x, 10); }); auto const matched = - match(10)(pattern | not_(x) = expr(true), pattern | _ = expr(false)); + match(10)(pattern | not_(x) = true, pattern | _ = false); EXPECT_FALSE(matched); } @@ -36,16 +36,16 @@ TEST(Id, resetAfterFailure2) Id x; match(10)(pattern | x = [&] { EXPECT_EQ(*x, 10); }); - auto const matched = match(10)(pattern | and_(x, not_(x)) = expr(true), - pattern | _ = expr(false)); + auto const matched = match(10)(pattern | and_(x, not_(x)) = true, + pattern | _ = false); EXPECT_FALSE(matched); } TEST(Id, resetAfterFailure3) { Id x; - auto result = match(10)(pattern | and_(x, app(_ / 2, x)) = expr(true), - pattern | _ = expr(false)); + auto result = match(10)(pattern | and_(x, app(_ / 2, x)) = true, + pattern | _ = false); EXPECT_FALSE(result); result = match(10)( pattern | and_(x, app(_ / 2, not_(x))) = @@ -54,7 +54,7 @@ TEST(Id, resetAfterFailure3) EXPECT_EQ(*x, 10); return true; }, - pattern | _ = expr(false)); + pattern | _ = false); EXPECT_TRUE(result); } @@ -68,7 +68,7 @@ TEST(Id, resetAfterFailure33) EXPECT_EQ(*x, 5); return true; }, - pattern | _ = expr(false)); + pattern | _ = false); EXPECT_TRUE(result); result = match(10)( @@ -78,7 +78,7 @@ TEST(Id, resetAfterFailure33) EXPECT_EQ(*x, 5); return true; }, - pattern | _ = expr(false)); + pattern | _ = false); EXPECT_TRUE(result); result = match(10)( @@ -88,7 +88,7 @@ TEST(Id, resetAfterFailure33) EXPECT_EQ(*x, 5); return true; }, - pattern | _ = expr(false)); + pattern | _ = false); EXPECT_TRUE(result); } @@ -106,7 +106,7 @@ TEST(Id, resetAfterFailure4) EXPECT_EQ(*x, 5); return true; }, - pattern | _ = expr(false)); + pattern | _ = false); EXPECT_TRUE(matched); } @@ -120,7 +120,7 @@ TEST(Id, resetAfterFailure5) EXPECT_EQ(*x, 10); return true; }, - pattern | _ = expr(false)); + pattern | _ = false); EXPECT_TRUE(result); result = match(10)( @@ -130,7 +130,7 @@ TEST(Id, resetAfterFailure5) EXPECT_EQ(*x, 10); return true; }, - pattern | _ = expr(false)); + pattern | _ = false); EXPECT_FALSE(result); } @@ -163,7 +163,7 @@ TEST(Id, matchMultipleTimes3) TEST(Id, AppToId) { Id ii; - auto const result = match(11)(pattern | app(_ * _, ii) = expr(ii)); + auto const result = match(11)(pattern | app(_ * _, ii) = ii); EXPECT_EQ(result, 121); } @@ -183,27 +183,27 @@ TEST(Id, AppToId3) Id> ii; auto const result = match(std::make_shared(11))( pattern | ii = [&] - { return ii.move(); }); + { return *ii; }); EXPECT_EQ(*result, 11); } TEST(Id, AppToId4) { - Id> ii; + Id&&> ii; auto const result = match(11)( pattern | app([](auto &&x) { return std::make_shared(x); }, ii) = [&] - { return ii.move(); }); + { return *ii; }); EXPECT_EQ(*result, 11); } TEST(Id, AppToId5) { - Id> ii; + Id&&> ii; auto const result = match(std::make_unique(11))( pattern | ii = [&] - { return ii.move(); }); + { return std::move(*ii); }); EXPECT_EQ(*result, 11); } @@ -227,33 +227,30 @@ TEST(Id, AppToId5Plus2) TEST(Id, AppToId5PlusPro) { - Id> jj; + Id&&> jj; auto const result = match(std::make_unique(11))( pattern | and_(_, jj) = [&] - { return jj.move(); }); + { return std::move(*jj); }); EXPECT_EQ(*result, 11); } -TEST(Id, AppToId5PlusProNegative) +TEST(Id, AppToId5PlusPro2) { - auto const invalidMove = [] - { - Id> ii, jj; - match(std::make_unique(11))( - pattern | and_(ii, jj) = [&] - { return jj.move(); }); - }; - EXPECT_THROW(invalidMove(), std::logic_error); + Id&&> ii, jj; + auto const result = match(std::make_unique(11))( + pattern | and_(ii, jj) = [&] + { return std::move(*jj); }); + EXPECT_EQ(*result, 11); } TEST(Id, AppToId6) { - Id> ii; + Id&&> ii; auto const result = match(11)( pattern | app([](auto &&x) { return std::make_unique(x); }, ii) = [&] - { return ii.move(); }); + { return std::move(*ii); }); EXPECT_EQ(*result, 11); } @@ -262,36 +259,36 @@ TEST(Id, AppToId7) Id> ii; auto const result = match(std::make_optional(11))(pattern | ii = [&] - { return ii.move(); }); + { return *ii; }); EXPECT_EQ(*result, 11); } TEST(Id, AppToId8) { - Id> ii; + Id&&> ii; auto const result = match(11)(pattern | app([](auto &&x) { return std::make_optional(x); }, ii) = [&] - { return ii.move(); }); + { return *ii; }); EXPECT_EQ(*result, 11); } TEST(Id, IdAtInt) { Id ii; - auto const result = match(11)(pattern | app(_ * _, ii.at(121)) = expr(ii)); + auto const result = match(11)(pattern | app(_ * _, ii.at(121)) = ii); EXPECT_EQ(result, 121); } TEST(Id, IdAtUnique) { - Id> ii; + Id&&> ii; auto const result = match(11)( pattern | app([](auto &&x) { return std::make_unique(x * x); }, ii.at(some(_))) = [&] - { return ii.move(); }); + { return std::move(*ii); }); EXPECT_EQ(*result, 121); } @@ -301,11 +298,12 @@ TEST(Id, invalidValue) EXPECT_THROW(*x, std::logic_error); } -TEST(Id, invalidMove) +TEST(Id, move) { - Id x; - EXPECT_THROW(x.move(), std::logic_error); + Id x; + EXPECT_THROW(*x, std::logic_error); std::string str = "12345"; x.matchValue(str); - EXPECT_THROW(x.move(), std::logic_error); + auto y = std::move(*x); + EXPECT_TRUE((*x).empty()); } diff --git a/test/matchit/legacy.cpp b/test/matchit/legacy.cpp index 6d59c8e..5c03b72 100644 --- a/test/matchit/legacy.cpp +++ b/test/matchit/legacy.cpp @@ -13,7 +13,7 @@ using namespace matchit; static_assert(impl::StorePointer const, std::unique_ptr const &>::value); static_assert(!impl::StorePointer, - std::unique_ptr &&>::value); + std::unique_ptr>::value); std::tuple<> xxx(); @@ -27,12 +27,19 @@ TEST(Match, test1) { Id ii; return match(input)( - pattern | 1 = func1, pattern | 2 = func2, pattern | or_(56, 59) = func2, - pattern | (_ < 0) = expr(-1), pattern | (_ < 10) = expr(-10), - pattern | and_(_<17, _> 15) = expr(16), - pattern | app(_ * _, _ > 1000) = expr(1000), - pattern | app(_ * _, ii) = expr(ii), pattern | ii = -ii, - pattern | _ = expr(111)); + // clang-format off + pattern | 1 = func1, + pattern | 2 = func2, + pattern | or_(56, 59) = func2, + pattern | (_ < 0) = -1, + pattern | (_ < 10) = -10, + pattern | and_(_<17, _> 15) = 16, + pattern | app(_ * _, _ > 1000) = 1000, + pattern | app(_ * _, ii) = ii, + pattern | ii = -ii, + pattern | _ = 111 + // clang-format on + ); }; EXPECT_EQ(matchFunc(1), 1); EXPECT_EQ(matchFunc(2), 12); @@ -52,9 +59,9 @@ TEST(Match, test2) Id i; Id j; return match(input)( - pattern | ds('/', 1, 1) = expr(1), pattern | ds('/', 0, _) = expr(0), + pattern | ds('/', 1, 1) = 1, pattern | ds('/', 0, _) = 0, pattern | ds('*', i, j) = i * j, pattern | ds('+', i, j) = i + j, - pattern | _ = expr(-1)); + pattern | _ = -1); }; EXPECT_EQ(matchFunc(std::make_tuple('/', 1, 1)), 1); EXPECT_EQ(matchFunc(std::make_tuple('+', 2, 1)), 3); @@ -81,7 +88,7 @@ TEST(Match, test3) Id i; // compose patterns for destructuring struct A. auto const dsA = dsVia(&A::a, &A::b); - return match(input)(pattern | dsA(i, 1) = expr(i), pattern | _ = expr(-1)); + return match(input)(pattern | dsA(i, 1) = i, pattern | _ = -1); }; EXPECT_EQ(matchFunc(A{3, 1}), 3); EXPECT_EQ(matchFunc(A{2, 2}), -1); @@ -127,9 +134,9 @@ TEST(Match, test4) { auto const matchFunc = [](Num const &input) { - return match(input)(pattern | as(_) = expr(1), - pattern | kind = expr(2), - pattern | _ = expr(3)); + return match(input)(pattern | as(_) = 1, + pattern | kind = 2, + pattern | _ = 3); }; matchit::impl::AsPointer()(std::variant{}); EXPECT_EQ(matchFunc(One{}), 1); @@ -141,8 +148,8 @@ TEST(Match, test5) auto const matchFunc = [](std::pair ij) { return match(ij.first % 3, ij.second % 5)( - pattern | ds(0, 0) = expr(1), pattern | ds(0, _ > 2) = expr(2), - pattern | ds(_, _ > 2) = expr(3), pattern | _ = expr(4)); + pattern | ds(0, 0) = 1, pattern | ds(0, _ > 2) = 2, + pattern | ds(_, _ > 2) = 3, pattern | _ = 4); }; EXPECT_EQ(matchFunc(std::make_pair(3, 5)), 1); EXPECT_EQ(matchFunc(std::make_pair(3, 4)), 2); @@ -154,7 +161,7 @@ int32_t fib(int32_t n) { EXPECT_TRUE(n > 0); return match(n)( - pattern | 1 = expr(1), pattern | 2 = expr(1), + pattern | 1 = 1, pattern | 2 = 1, pattern | _ = [n] { return fib(n - 1) + fib(n - 2); }); } @@ -177,7 +184,7 @@ TEST(Match, test7) auto const at = [](auto &&id, auto &&pattern) { return and_(pattern, id); }; return match(ij.first % 3, ij.second % 5)( - pattern | ds(0, _ > 2) = expr(2), pattern | ds(1, _ > 2) = expr(3), + pattern | ds(0, _ > 2) = 2, pattern | ds(1, _ > 2) = 3, pattern | at(id, ds(_, 2)) = [&id] { @@ -185,7 +192,7 @@ TEST(Match, test7) static_cast(id); return 4; }, - pattern | _ = expr(5)); + pattern | _ = 5); }; EXPECT_EQ(matchFunc(std::make_pair(4, 2)), 4); } @@ -195,8 +202,8 @@ TEST(Match, test8) auto const equal = [](std::pair> ijk) { Id x; - return match(ijk)(pattern | ds(x, ds(_, x)) = expr(true), - pattern | _ = expr(false)); + return match(ijk)(pattern | ds(x, ds(_, x)) = true, + pattern | _ = false); }; EXPECT_TRUE(equal(std::make_pair(2, std::make_pair(1, 2)))); EXPECT_FALSE(equal(std::make_pair(2, std::make_pair(1, 3)))); @@ -208,8 +215,8 @@ TEST(Match, test9) auto const optional = [](auto const &i) { Id x; - return match(i)(pattern | some(x) = expr(true), - pattern | none = expr(false)); + return match(i)(pattern | some(x) = true, + pattern | none = false); }; EXPECT_EQ(optional(std::make_unique(2)), true); EXPECT_EQ(optional(std::make_shared(2)), true); @@ -245,9 +252,9 @@ TEST(Match, test10) auto const dynCast = [](auto const &i) { - return match(i)(pattern | some(as(_)) = expr("Circle"), - pattern | some(as(_)) = expr("Square"), - pattern | none = expr("None")); + return match(i)(pattern | some(as(_)) = "Circle", + pattern | some(as(_)) = "Square", + pattern | none = "None"); }; EXPECT_EQ(dynCast(std::unique_ptr(new Square{})), "Square"); @@ -259,8 +266,8 @@ TEST(Match, test10_) { auto const dToBCast = [](auto const &i) { - return match(i)(pattern | some(as(_)) = expr("Shape"), - pattern | none = expr("None")); + return match(i)(pattern | some(as(_)) = "Shape", + pattern | none = "None"); }; EXPECT_EQ(dToBCast(std::make_unique()), "Shape"); @@ -270,8 +277,8 @@ TEST(Match, test11) { auto const getIf = [](auto const &i) { - return match(i)(pattern | as(_) = expr("Square"), - pattern | as(_) = expr("Circle")); + return match(i)(pattern | as(_) = "Square", + pattern | as(_) = "Circle"); }; std::variant sc = Square{}; @@ -313,7 +320,7 @@ TEST(Match, test13) auto const dsAgg = [](auto const &v) { Id i; - return match(v)(pattern | ds(1, i) = expr(i), pattern | ds(_, i) = expr(i)); + return match(v)(pattern | ds(1, i) = i, pattern | ds(_, i) = i); }; EXPECT_EQ(dsAgg(A{1, 2}), 2); @@ -326,8 +333,8 @@ TEST(Match, test14) { auto const anyCast = [](auto const &i) { - return match(i)(pattern | as(_) = expr("Square"), - pattern | as(_) = expr("Circle")); + return match(i)(pattern | as(_) = "Square", + pattern | as(_) = "Circle"); }; std::any sc; @@ -350,8 +357,8 @@ TEST(Match, test15) auto const optional = [](auto const &i) { Id c; - return match(i)(pattern | none = expr(1), pattern | some(none) = expr(2), - pattern | some(some(c)) = expr(c)); + return match(i)(pattern | none = 1, pattern | some(none) = 2, + pattern | some(some(c)) = c); }; char const **x = nullptr; char const *y_ = nullptr; @@ -368,8 +375,8 @@ TEST(Match, test16) { auto const notX = [](auto const &i) { - return match(i)(pattern | not_(or_(1, 2)) = expr(3), pattern | 2 = expr(2), - pattern | _ = expr(1)); + return match(i)(pattern | not_(or_(1, 2)) = 3, pattern | 2 = 2, + pattern | _ = 1); }; EXPECT_EQ(notX(1), 1); EXPECT_EQ(notX(2), 2); @@ -382,8 +389,8 @@ TEST(Match, test17) auto const whenX = [](auto const &x) { Id i, j; - return match(x)(pattern | ds(i, j) | when(i + j == 10) = expr(3), - pattern | ds(_ < 5, _) = expr(5), pattern | _ = expr(1)); + return match(x)(pattern | ds(i, j) | when(i + j == 10) = 3, + pattern | ds(_ < 5, _) = 5, pattern | _ = 1); }; EXPECT_EQ(whenX(std::make_pair(1, 9)), 3); EXPECT_EQ(whenX(std::make_pair(1, 7)), 5); @@ -395,8 +402,8 @@ TEST(Match, test18) auto const idNotOwn = [](auto const &x) { Id i; - return match(x)(pattern | i | when(i == 5) = expr(1), - pattern | _ = expr(2)); + return match(x)(pattern | i | when(i == 5) = 1, + pattern | _ = 2); }; EXPECT_EQ(idNotOwn(1), 2); EXPECT_EQ(idNotOwn(5), 1); @@ -409,25 +416,25 @@ TEST(Match, test19) Id j; return match(input)( // `... / 2 3` - pattern | ds(ooo, '/', 2, 3) = expr(1), + pattern | ds(ooo, '/', 2, 3) = 1, // `... 3` - pattern | ds(ooo, 3) = expr(3), + pattern | ds(ooo, 3) = 3, // `/ ...` - pattern | ds('/', ooo) = expr(4), + pattern | ds('/', ooo) = 4, - pattern | ooo = expr(222), + pattern | ooo = 222, // `3 3 3 3 ..` all 3 - pattern | ooo = expr(333), + pattern | ooo = 333, // `... int32_t 3` - pattern | ds(ooo, j, 3) = expr(7), + pattern | ds(ooo, j, 3) = 7, // `... int32_t 3` - pattern | ds(ooo, or_(j), 3) = expr(8), + pattern | ds(ooo, or_(j), 3) = 8, // `...` - pattern | ooo = expr(12), + pattern | ooo = 12, - pattern | _ = expr(-1)); + pattern | _ = -1); }; EXPECT_EQ(matchFunc(std::make_tuple('/', 2, 3)), 1); EXPECT_EQ(matchFunc(std::make_tuple(3, 3, 3, 3, 3)), 3); diff --git a/test/matchit/noRet.cpp b/test/matchit/noRet.cpp index a2edcf7..7bfed24 100644 --- a/test/matchit/noRet.cpp +++ b/test/matchit/noRet.cpp @@ -19,5 +19,5 @@ TEST(MatchStatement, test) TEST(MatchExpreesion, Nomatch) { - EXPECT_THROW(match(4)(pattern | 1 = expr(true)), std::logic_error); + EXPECT_THROW(match(4)(pattern | 1 = true), std::logic_error); } \ No newline at end of file diff --git a/test/matchit/optexpr.cpp b/test/matchit/optexpr.cpp new file mode 100644 index 0000000..3679458 --- /dev/null +++ b/test/matchit/optexpr.cpp @@ -0,0 +1,55 @@ +#include +#include + +#include "matchit.h" +using namespace matchit; + +template +constexpr auto eval1(std::tuple const& exp) +{ + using namespace matchit; + Id i; + Id j; + return match(exp) // no expr() + ( + pattern | ds('+', i, j) | when((i + j) > 0) = i + j, + pattern | ds('-', i, j) | when(true) = i - j, + pattern | ds('*', i, j) | when(i) = i, + pattern | ds('/', i, j) = 12345, + pattern | _ = [] { return -1; } + ); +} + +template +constexpr auto eval2(std::tuple const& exp) +{ + using namespace matchit; + Id i; + Id j; + return match(exp) // unnecessary expr() + ( + pattern | ds('+', i, j) | when((i + j) > 0) = i + j, + pattern | ds('-', i, j) | when(expr(true)) = i - j, + pattern | ds('*', i, j) | when(expr(i)) = expr(i), + pattern | ds('/', i, j) = expr(12345), + pattern | _ = [] { return -1; } + ); +} + +TEST(OptExpr, no_expr) +{ + EXPECT_EQ(eval1(std::tuple{'+', 20, 3}), 23); + EXPECT_EQ(eval1(std::tuple{'-', 20, 3}), 17); + EXPECT_EQ(eval1(std::tuple{'*', 20, 3}), 20); + EXPECT_EQ(eval1(std::tuple{'/', 20, 3}), 12345); + EXPECT_EQ(eval1(std::tuple{' ', 20, 3}), -1); +} + +TEST(OptExpr, unnecessary_expr) +{ + EXPECT_EQ(eval2(std::tuple{'+', 20, 3}), 23); + EXPECT_EQ(eval2(std::tuple{'-', 20, 3}), 17); + EXPECT_EQ(eval2(std::tuple{'*', 20, 3}), 20); + EXPECT_EQ(eval2(std::tuple{'/', 20, 3}), 12345); + EXPECT_EQ(eval2(std::tuple{' ', 20, 3}), -1); +}