-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathpattern.hh
128 lines (102 loc) · 3.66 KB
/
pattern.hh
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
// Copyright (c) 2022 Mikael Simonsson <https://mikaelsimonsson.com>.
// SPDX-License-Identifier: BSL-1.0
// # Pattern (`std::regex` wrapper)
// Uses ECMAScript grammar by default:
// https://en.cppreference.com/w/cpp/regex/ecmascript
#pragma once
#include "snn-core/strcore.hh"
#include "snn-core/regex/matches.hh"
#include "snn-core/regex/matches_view.hh"
#include "snn-core/string/append_iterator.hh"
#include <regex>
namespace snn::regex
{
// ## Constants
// ### Pattern options
inline constexpr auto ecmascript = std::regex::ECMAScript;
inline constexpr auto icase = std::regex::icase;
inline constexpr auto optimize = std::regex::optimize;
// ## Classes
// ### pattern
class pattern final
{
public:
// #### Types
using option_type = std::regex_constants::syntax_option_type;
// #### Constructors
explicit pattern(std::regex pat) noexcept
: pat_{std::move(pat)}
{
}
explicit pattern(const transient<cstrview> pat, const option_type options = ecmascript)
: pat_{pat.get().cbegin(), pat.get().size(), options}
{
}
// #### Match
template <typename Callback>
usize match_all(const cstrview subject, Callback cb) const
{
std::cregex_iterator cur{subject.begin(), subject.end(), pat_};
const std::cregex_iterator last;
usize count = 0;
while (cur != last)
{
cb(matches_view{*cur});
++cur;
++count;
}
return count;
}
[[nodiscard]] matches match_first(const cstrview subject) const
{
matches ma;
const bool b = std::regex_search(subject.begin(), subject.end(), ma.cmatch(), pat_);
snn_should(ma.cmatch().ready());
snn_should((b && ma.count() > 0) || (!b && ma.count() == 0));
ignore_if_unused(b);
return ma;
}
[[nodiscard]] matches match_full(const cstrview subject) const
{
matches ma;
const bool b = std::regex_match(subject.begin(), subject.end(), ma.cmatch(), pat_);
snn_should(ma.cmatch().ready());
snn_should((b && ma.count() > 0) || (!b && ma.count() == 0));
ignore_if_unused(b);
return ma;
}
// #### Replace
// Replace all matches with replacement format string.
// Replacement format string can not contain null bytes.
// Replacement format string syntax:
// $& Full match.
// $0 Full match.
// $1 Sub match 1.
// ...
// $9 Sub match 9.
// $$ Literal '$' character.
template <any_strcore Str = str>
[[nodiscard]] Str replace(const transient<cstrview> subject,
const str& replacement_format) const
{
const cstrview s = subject.get();
// std::regex_replace will pass the "regex replacement format string" as a
// null-terminated c-string. Even if passed a std::string it will call c_str() on it.
Str result{container::reserve, s.size()};
std::regex_replace(string::append_iterator{result}, s.begin(), s.end(), pat_,
replacement_format.null_terminated().get());
return result;
}
// #### Internal
[[nodiscard]] std::regex& internal() noexcept
{
return pat_;
}
[[nodiscard]] const std::regex& internal() const noexcept
{
return pat_;
}
private:
std::regex pat_;
};
}