Echo Writes Code

combinators.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#ifndef CRUCIBLE_PARSING_COMBINATORS_HPP
#define CRUCIBLE_PARSING_COMBINATORS_HPP

#include "crucible/core/error_chain.inl"
#include "crucible/parsing/result.inl"

#include <type_traits>
#include <utility>

namespace crucible::parsing::combinators {
  template<typename LHS_PARSER, typename RHS_PARSER>
  class BothMatch final {
    static_assert(std::is_same_v<typename LHS_PARSER::StateType, typename RHS_PARSER::StateType>);

  public:
    using StateType = typename LHS_PARSER::StateType;

    BothMatch(StateType const &initial_state, StateType const &lhs_next_state, StateType const &rhs_next_state);

    [[nodiscard]]
    auto format() const -> std::string;

  private:
    StateType my_initial_state;

    StateType my_lhs_next_state;

    StateType my_rhs_next_state;
  };

  template<typename LHS_PARSER, typename RHS_PARSER>
  class NeitherMatch final {
  public:
    using LHSError = typename LHS_PARSER::ErrorType;

    using RHSError = typename RHS_PARSER::ErrorType;

    NeitherMatch(LHSError const &lhs_error, RHSError const &rhs_error);

    [[nodiscard]]
    auto format() const -> std::string;

  private:
    LHSError my_lhs_error;

    RHSError my_rhs_error;
  };

  template<typename LHS_PARSER, typename RHS_PARSER>
  struct OneOfError final : core::error_chain::ErrorChain<OneOfError<LHS_PARSER, RHS_PARSER>, BothMatch<LHS_PARSER, RHS_PARSER>, NeitherMatch<LHS_PARSER, RHS_PARSER>> {
    // As far as I know, this is the syntax that is _required_ to communicate "please make my
    // constructor be the same as the constructor of my parent class."
    // I tried `using ErrorChain::ErrorChain`, which works, but _only_ if `OneOfError` is _not_ a
    // template type.
    // As soon as this cursed syntax is fixed, PLEASE change this line.
    using core::error_chain::ErrorChain<OneOfError<LHS_PARSER, RHS_PARSER>, BothMatch<LHS_PARSER, RHS_PARSER>, NeitherMatch<LHS_PARSER, RHS_PARSER>>::ErrorChain;

    static constexpr char const message[] = "Expected exactly one match";
  };

  template<typename LHS_PARSER, typename RHS_PARSER>
  class OneOf final {
    static_assert(std::is_same_v<typename LHS_PARSER::AcceptType, typename RHS_PARSER::AcceptType>);

  public:
    using StateType = typename LHS_PARSER::StateType;

    using ErrorType = OneOfError<LHS_PARSER, RHS_PARSER>;

    using ValueType = typename LHS_PARSER::ValueType;

    using RejectType = result::Reject<StateType, ErrorType>;

    using AcceptType = result::Accept<StateType, ValueType>;

    using ResultType = core::result::Result<RejectType, AcceptType>;

    OneOf(LHS_PARSER const &lhs_parser, RHS_PARSER const &rhs_parser);

    [[nodiscard]]
    auto operator()(StateType const &state) const -> ResultType;

  private:
    LHS_PARSER my_lhs_parser;

    RHS_PARSER my_rhs_parser;
  };

  template<typename LHS_PARSER, typename RHS_PARSER>
  class AndThen final {
    static_assert(std::is_same_v<typename LHS_PARSER::RejectType, typename RHS_PARSER::RejectType>);

  public:
    using StateType = typename LHS_PARSER::StateType;

    using ErrorType = typename LHS_PARSER::ErrorType;

    using ValueType = std::pair<typename LHS_PARSER::ValueType, typename RHS_PARSER::ValueType>;

    using RejectType = result::Reject<StateType, ErrorType>;

    using AcceptType = result::Accept<StateType, ValueType>;

    using ResultType = core::result::Result<RejectType, AcceptType>;

    AndThen(LHS_PARSER const &lhs_parser, RHS_PARSER const &rhs_parser);

    [[nodiscard]]
    auto operator()(StateType const &state) const -> ResultType;

  private:
    LHS_PARSER my_lhs_parser;

    RHS_PARSER my_rhs_parser;
  };

  namespace operators {
    template<typename LHS_PARSER, typename RHS_PARSER>
    [[nodiscard]]
    auto operator^(LHS_PARSER const &lhs_parser, RHS_PARSER const &rhs_parser) -> OneOf<LHS_PARSER, RHS_PARSER>;

    template<typename LHS_PARSER, typename RHS_PARSER>
    [[nodiscard]]
    auto operator&(LHS_PARSER const &lhs_parser, RHS_PARSER const &rhs_parser) -> AndThen<LHS_PARSER, RHS_PARSER>;
  }
}

#endif // CRUCIBLE_PARSING_COMBINATORS_HPP