mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-15 01:54:43 +00:00
Formatted src/services/xeger
This commit is contained in:
@@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
std::string GenerateStringFromRegex(std::string_view pattern, std::mt19937& rng,
|
std::string GenerateStringFromRegex(std::string_view pattern, std::mt19937& rng,
|
||||||
const int unbounded_repeat_cap) {
|
const int unbounded_repeat_cap) {
|
||||||
xeger_internal::Parser parser(pattern);
|
xeger_internal::Parser parser(pattern);
|
||||||
const xeger_internal::NodePtr ast = parser.Parse();
|
const xeger_internal::NodePtr ast = parser.Parse();
|
||||||
xeger_internal::XegerGenerator generator(rng, unbounded_repeat_cap);
|
xeger_internal::XegerGenerator generator(rng, unbounded_repeat_cap);
|
||||||
return generator.Run(*ast);
|
return generator.Run(*ast);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,108 +3,108 @@
|
|||||||
* @brief Visitor that walks a regex AST and emits a random matching string.
|
* @brief Visitor that walks a regex AST and emits a random matching string.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "services/xeger/xeger_internal.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "services/xeger/xeger_internal.h"
|
||||||
|
|
||||||
namespace xeger_internal {
|
namespace xeger_internal {
|
||||||
|
|
||||||
XegerGenerator::XegerGenerator(std::mt19937& rng, const int unbounded_cap)
|
XegerGenerator::XegerGenerator(std::mt19937& rng, const int unbounded_cap)
|
||||||
: rng_(rng), unbounded_cap_(unbounded_cap) {}
|
: rng_(rng), unbounded_cap_(unbounded_cap) {}
|
||||||
|
|
||||||
std::string XegerGenerator::Run(const Node& root) {
|
std::string XegerGenerator::Run(const Node& root) {
|
||||||
out_.clear();
|
out_.clear();
|
||||||
root.Accept(*this);
|
root.Accept(*this);
|
||||||
return out_;
|
return out_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void XegerGenerator::Visit(const Literal& node) { out_ += node.ch; }
|
void XegerGenerator::Visit(const Literal& node) { out_ += node.ch; }
|
||||||
|
|
||||||
void XegerGenerator::Visit(const AnyChar&) {
|
void XegerGenerator::Visit(const AnyChar&) {
|
||||||
static constexpr int kPrintableLow = ' '; // empty space - character 32
|
static constexpr int kPrintableLow = ' '; // empty space - character 32
|
||||||
static constexpr int kPrintableHigh = '~'; // tilde - character 126
|
static constexpr int kPrintableHigh = '~'; // tilde - character 126
|
||||||
|
|
||||||
std::uniform_int_distribution<int> dist(kPrintableLow, kPrintableHigh);
|
std::uniform_int_distribution<int> dist(kPrintableLow, kPrintableHigh);
|
||||||
|
|
||||||
// assert that the rand number is in the char range
|
// assert that the rand number is in the char range
|
||||||
bool inCharRange = dist.min() >= std::numeric_limits<char>::min() &&
|
bool inCharRange = dist.min() >= std::numeric_limits<char>::min() &&
|
||||||
dist.max() <= std::numeric_limits<char>::max();
|
dist.max() <= std::numeric_limits<char>::max();
|
||||||
assert(inCharRange && "Random number is out of char range");
|
assert(inCharRange && "Random number is out of char range");
|
||||||
|
|
||||||
out_ += static_cast<char>(dist(rng_));
|
out_ += static_cast<char>(dist(rng_));
|
||||||
}
|
}
|
||||||
|
|
||||||
void XegerGenerator::Visit(const CharClass& node) {
|
void XegerGenerator::Visit(const CharClass& node) {
|
||||||
out_ += PickFromClass(node);
|
out_ += PickFromClass(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void XegerGenerator::Visit(const Concat& node) {
|
void XegerGenerator::Visit(const Concat& node) {
|
||||||
for (const NodePtr& part : node.parts) {
|
for (const NodePtr& part : node.parts) {
|
||||||
part->Accept(*this);
|
part->Accept(*this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void XegerGenerator::Visit(const Alternation& node) {
|
void XegerGenerator::Visit(const Alternation& node) {
|
||||||
std::uniform_int_distribution<size_t> dist(0, node.options.size() - 1);
|
std::uniform_int_distribution<size_t> dist(0, node.options.size() - 1);
|
||||||
node.options[dist(rng_)]->Accept(*this);
|
node.options[dist(rng_)]->Accept(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void XegerGenerator::Visit(const Repeat& node) {
|
void XegerGenerator::Visit(const Repeat& node) {
|
||||||
const int lo = node.min;
|
const int lo = node.min;
|
||||||
const int hi = (node.max < 0) ? node.min + unbounded_cap_ : node.max;
|
const int hi = (node.max < 0) ? node.min + unbounded_cap_ : node.max;
|
||||||
std::uniform_int_distribution<int> dist(lo, hi);
|
std::uniform_int_distribution<int> dist(lo, hi);
|
||||||
const int count = dist(rng_);
|
const int count = dist(rng_);
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
node.child->Accept(*this);
|
node.child->Accept(*this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XegerGenerator::InRanges(const CharClass& node, const char c) {
|
bool XegerGenerator::InRanges(const CharClass& node, const char c) {
|
||||||
for (const auto& [lo, hi] : node.ranges) {
|
for (const auto& [lo, hi] : node.ranges) {
|
||||||
if (c >= lo && c <= hi) {
|
if (c >= lo && c <= hi) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char XegerGenerator::PickFromClass(const CharClass& node) {
|
char XegerGenerator::PickFromClass(const CharClass& node) {
|
||||||
if (!node.negated) {
|
if (!node.negated) {
|
||||||
int total = 0;
|
int total = 0;
|
||||||
for (const auto& [lo, hi] : node.ranges) {
|
for (const auto& [lo, hi] : node.ranges) {
|
||||||
total += hi - lo + 1;
|
total += hi - lo + 1;
|
||||||
}
|
|
||||||
std::uniform_int_distribution<int> dist(0, total - 1);
|
|
||||||
int k = dist(rng_);
|
|
||||||
for (const auto& [lo, hi] : node.ranges) {
|
|
||||||
const int width = hi - lo + 1;
|
|
||||||
if (k < width) {
|
|
||||||
return static_cast<char>(lo + k);
|
|
||||||
}
|
}
|
||||||
k -= width;
|
std::uniform_int_distribution<int> dist(0, total - 1);
|
||||||
}
|
int k = dist(rng_);
|
||||||
return node.ranges.back().second; // unreachable
|
for (const auto& [lo, hi] : node.ranges) {
|
||||||
}
|
const int width = hi - lo + 1;
|
||||||
|
if (k < width) {
|
||||||
|
return static_cast<char>(lo + k);
|
||||||
|
}
|
||||||
|
k -= width;
|
||||||
|
}
|
||||||
|
return node.ranges.back().second; // unreachable
|
||||||
|
}
|
||||||
|
|
||||||
// Negated class: draw random printable characters until one falls
|
// Negated class: draw random printable characters until one falls
|
||||||
// outside the forbidden set.
|
// outside the forbidden set.
|
||||||
static constexpr int kPrintableLow = 32;
|
static constexpr int kPrintableLow = 32;
|
||||||
static constexpr int kPrintableHigh = 126;
|
static constexpr int kPrintableHigh = 126;
|
||||||
static constexpr int kMaxAttempts = 1000;
|
static constexpr int kMaxAttempts = 1000;
|
||||||
std::uniform_int_distribution<int> dist(kPrintableLow, kPrintableHigh);
|
std::uniform_int_distribution<int> dist(kPrintableLow, kPrintableHigh);
|
||||||
for (int attempt = 0; attempt < kMaxAttempts; ++attempt) {
|
for (int attempt = 0; attempt < kMaxAttempts; ++attempt) {
|
||||||
const char c = static_cast<char>(dist(rng_));
|
const char c = static_cast<char>(dist(rng_));
|
||||||
if (!InRanges(node, c)) {
|
if (!InRanges(node, c)) {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"GenerateStringFromRegex: negated class rules out every printable "
|
"GenerateStringFromRegex: negated class rules out every printable "
|
||||||
"character");
|
"character");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace xeger_internal
|
} // namespace xeger_internal
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
* @brief Recursive-descent parser that turns a regex pattern into an AST.
|
* @brief Recursive-descent parser that turns a regex pattern into an AST.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "services/xeger/xeger_internal.h"
|
|
||||||
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -14,31 +12,33 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "services/xeger/xeger_internal.h"
|
||||||
|
|
||||||
namespace xeger_internal {
|
namespace xeger_internal {
|
||||||
|
|
||||||
Parser::Parser(std::string_view pattern) : src_(StripAnchors(pattern)) {}
|
Parser::Parser(std::string_view pattern) : src_(StripAnchors(pattern)) {}
|
||||||
|
|
||||||
NodePtr Parser::Parse() {
|
NodePtr Parser::Parse() {
|
||||||
NodePtr node = ParseAlternation();
|
NodePtr node = ParseAlternation();
|
||||||
if (!AtEnd()) {
|
if (!AtEnd()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
std::format("GenerateStringFromRegex: unexpected '{}' at position {}",
|
std::format("GenerateStringFromRegex: unexpected '{}' at position {}",
|
||||||
Peek(), pos_));
|
Peek(), pos_));
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Parser::StripAnchors(std::string_view pattern) {
|
std::string Parser::StripAnchors(std::string_view pattern) {
|
||||||
if (!pattern.empty() && pattern.front() == kAnchorStart) {
|
if (!pattern.empty() && pattern.front() == kAnchorStart) {
|
||||||
pattern.remove_prefix(1);
|
pattern.remove_prefix(1);
|
||||||
}
|
}
|
||||||
const bool has_trailing_dollar =
|
const bool has_trailing_dollar =
|
||||||
!pattern.empty() && pattern.back() == kAnchorEnd &&
|
!pattern.empty() && pattern.back() == kAnchorEnd &&
|
||||||
(pattern.size() < 2 || pattern[pattern.size() - 2] != kEscape);
|
(pattern.size() < 2 || pattern[pattern.size() - 2] != kEscape);
|
||||||
if (has_trailing_dollar) {
|
if (has_trailing_dollar) {
|
||||||
pattern.remove_suffix(1);
|
pattern.remove_suffix(1);
|
||||||
}
|
}
|
||||||
return std::string(pattern);
|
return std::string(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Parser::AtEnd() const { return pos_ >= src_.size(); }
|
bool Parser::AtEnd() const { return pos_ >= src_.size(); }
|
||||||
@@ -46,262 +46,263 @@ char Parser::Peek() const { return src_[pos_]; }
|
|||||||
char Parser::Advance() { return src_[pos_++]; }
|
char Parser::Advance() { return src_[pos_++]; }
|
||||||
|
|
||||||
bool Parser::Match(const char c) {
|
bool Parser::Match(const char c) {
|
||||||
if (!AtEnd() && Peek() == c) {
|
if (!AtEnd() && Peek() == c) {
|
||||||
++pos_;
|
++pos_;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::Expect(const char c) {
|
void Parser::Expect(const char c) {
|
||||||
if (!Match(c)) {
|
if (!Match(c)) {
|
||||||
throw std::runtime_error(std::format(
|
throw std::runtime_error(std::format(
|
||||||
"GenerateStringFromRegex: expected '{}' at position {}", c, pos_));
|
"GenerateStringFromRegex: expected '{}' at position {}", c, pos_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NodePtr Parser::ParseAlternation() {
|
NodePtr Parser::ParseAlternation() {
|
||||||
std::vector<NodePtr> options;
|
std::vector<NodePtr> options;
|
||||||
options.push_back(ParseConcat());
|
options.push_back(ParseConcat());
|
||||||
while (Match(kAlternation)) {
|
while (Match(kAlternation)) {
|
||||||
options.push_back(ParseConcat());
|
options.push_back(ParseConcat());
|
||||||
}
|
}
|
||||||
if (options.size() == 1) {
|
if (options.size() == 1) {
|
||||||
return std::move(options.front());
|
return std::move(options.front());
|
||||||
}
|
}
|
||||||
auto alternation = std::make_unique<Alternation>();
|
auto alternation = std::make_unique<Alternation>();
|
||||||
alternation->options = std::move(options);
|
alternation->options = std::move(options);
|
||||||
return alternation;
|
return alternation;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodePtr Parser::ParseConcat() {
|
NodePtr Parser::ParseConcat() {
|
||||||
auto concat = std::make_unique<Concat>();
|
auto concat = std::make_unique<Concat>();
|
||||||
while (!AtEnd() && Peek() != kAlternation && Peek() != kGroupClose) {
|
while (!AtEnd() && Peek() != kAlternation && Peek() != kGroupClose) {
|
||||||
concat->parts.push_back(ParseRepeat());
|
concat->parts.push_back(ParseRepeat());
|
||||||
}
|
}
|
||||||
if (concat->parts.size() == 1) {
|
if (concat->parts.size() == 1) {
|
||||||
return std::move(concat->parts.front());
|
return std::move(concat->parts.front());
|
||||||
}
|
}
|
||||||
return concat;
|
return concat;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodePtr Parser::ParseRepeat() {
|
NodePtr Parser::ParseRepeat() {
|
||||||
NodePtr atom = ParseAtom();
|
NodePtr atom = ParseAtom();
|
||||||
if (AtEnd()) {
|
if (AtEnd()) {
|
||||||
return atom;
|
|
||||||
}
|
|
||||||
|
|
||||||
int lo = 0;
|
|
||||||
int hi = -1;
|
|
||||||
switch (Peek()) {
|
|
||||||
case kStar:
|
|
||||||
Advance();
|
|
||||||
lo = 0;
|
|
||||||
hi = -1;
|
|
||||||
break;
|
|
||||||
case kPlus:
|
|
||||||
Advance();
|
|
||||||
lo = 1;
|
|
||||||
hi = -1;
|
|
||||||
break;
|
|
||||||
case kOptional:
|
|
||||||
Advance();
|
|
||||||
lo = 0;
|
|
||||||
hi = 1;
|
|
||||||
break;
|
|
||||||
case kBraceOpen:
|
|
||||||
return ParseBrace(std::move(atom));
|
|
||||||
default:
|
|
||||||
return atom;
|
return atom;
|
||||||
}
|
}
|
||||||
auto repeat = std::make_unique<Repeat>();
|
|
||||||
repeat->child = std::move(atom);
|
int lo = 0;
|
||||||
repeat->min = lo;
|
int hi = -1;
|
||||||
repeat->max = hi;
|
switch (Peek()) {
|
||||||
return repeat;
|
case kStar:
|
||||||
|
Advance();
|
||||||
|
lo = 0;
|
||||||
|
hi = -1;
|
||||||
|
break;
|
||||||
|
case kPlus:
|
||||||
|
Advance();
|
||||||
|
lo = 1;
|
||||||
|
hi = -1;
|
||||||
|
break;
|
||||||
|
case kOptional:
|
||||||
|
Advance();
|
||||||
|
lo = 0;
|
||||||
|
hi = 1;
|
||||||
|
break;
|
||||||
|
case kBraceOpen:
|
||||||
|
return ParseBrace(std::move(atom));
|
||||||
|
default:
|
||||||
|
return atom;
|
||||||
|
}
|
||||||
|
auto repeat = std::make_unique<Repeat>();
|
||||||
|
repeat->child = std::move(atom);
|
||||||
|
repeat->min = lo;
|
||||||
|
repeat->max = hi;
|
||||||
|
return repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodePtr Parser::ParseBrace(NodePtr atom) {
|
NodePtr Parser::ParseBrace(NodePtr atom) {
|
||||||
Expect(kBraceOpen);
|
Expect(kBraceOpen);
|
||||||
std::string lower_digits;
|
std::string lower_digits;
|
||||||
std::string upper_digits;
|
std::string upper_digits;
|
||||||
bool has_comma = false;
|
bool has_comma = false;
|
||||||
while (!AtEnd() && std::isdigit(static_cast<unsigned char>(Peek())) != 0) {
|
while (!AtEnd() && std::isdigit(static_cast<unsigned char>(Peek())) != 0) {
|
||||||
lower_digits += Advance();
|
lower_digits += Advance();
|
||||||
}
|
}
|
||||||
if (Match(kBraceComma)) {
|
if (Match(kBraceComma)) {
|
||||||
has_comma = true;
|
has_comma = true;
|
||||||
while (!AtEnd() &&
|
while (!AtEnd() &&
|
||||||
std::isdigit(static_cast<unsigned char>(Peek())) != 0) {
|
std::isdigit(static_cast<unsigned char>(Peek())) != 0) {
|
||||||
upper_digits += Advance();
|
upper_digits += Advance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expect(kBraceClose);
|
Expect(kBraceClose);
|
||||||
|
|
||||||
auto repeat = std::make_unique<Repeat>();
|
auto repeat = std::make_unique<Repeat>();
|
||||||
repeat->child = std::move(atom);
|
repeat->child = std::move(atom);
|
||||||
repeat->min = lower_digits.empty() ? 0 : std::stoi(lower_digits);
|
repeat->min = lower_digits.empty() ? 0 : std::stoi(lower_digits);
|
||||||
if (!has_comma) {
|
if (!has_comma) {
|
||||||
repeat->max = repeat->min;
|
repeat->max = repeat->min;
|
||||||
} else if (upper_digits.empty()) {
|
} else if (upper_digits.empty()) {
|
||||||
repeat->max = -1;
|
repeat->max = -1;
|
||||||
} else {
|
} else {
|
||||||
repeat->max = std::stoi(upper_digits);
|
repeat->max = std::stoi(upper_digits);
|
||||||
}
|
}
|
||||||
if (repeat->max != -1 && repeat->max < repeat->min) {
|
if (repeat->max != -1 && repeat->max < repeat->min) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"GenerateStringFromRegex: repetition max is smaller than min");
|
"GenerateStringFromRegex: repetition max is smaller than min");
|
||||||
}
|
}
|
||||||
return repeat;
|
return repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodePtr Parser::ParseAtom() {
|
NodePtr Parser::ParseAtom() {
|
||||||
if (AtEnd()) {
|
if (AtEnd()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"GenerateStringFromRegex: pattern ended unexpectedly");
|
"GenerateStringFromRegex: pattern ended unexpectedly");
|
||||||
}
|
}
|
||||||
const char c = Peek();
|
const char c = Peek();
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case kGroupOpen: {
|
case kGroupOpen: {
|
||||||
Advance();
|
Advance();
|
||||||
// Non-capturing group "(?:...)" -- capture semantics don't apply
|
// Non-capturing group "(?:...)" -- capture semantics don't apply
|
||||||
// here (nothing back-references a group), so it's treated exactly
|
// here (nothing back-references a group), so it's treated exactly
|
||||||
// like a plain group once the "?:" marker is consumed.
|
// like a plain group once the "?:" marker is consumed.
|
||||||
if (!AtEnd() && Peek() == kNonCapturingMarker) {
|
if (!AtEnd() && Peek() == kNonCapturingMarker) {
|
||||||
if (pos_ + 1 < src_.size() && src_[pos_ + 1] == kNonCapturingColon) {
|
if (pos_ + 1 < src_.size() &&
|
||||||
pos_ += 2;
|
src_[pos_ + 1] == kNonCapturingColon) {
|
||||||
} else {
|
pos_ += 2;
|
||||||
throw std::runtime_error(std::format(
|
} else {
|
||||||
"GenerateStringFromRegex: unsupported group syntax at "
|
throw std::runtime_error(std::format(
|
||||||
"position {} (only plain '(...)' and non-capturing "
|
"GenerateStringFromRegex: unsupported group syntax at "
|
||||||
"'(?:...)' groups are supported)",
|
"position {} (only plain '(...)' and non-capturing "
|
||||||
pos_));
|
"'(?:...)' groups are supported)",
|
||||||
}
|
pos_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NodePtr inner = ParseAlternation();
|
||||||
|
Expect(kGroupClose);
|
||||||
|
return inner;
|
||||||
}
|
}
|
||||||
NodePtr inner = ParseAlternation();
|
case kClassOpen:
|
||||||
Expect(kGroupClose);
|
return ParseCharClass();
|
||||||
return inner;
|
case kAnyChar:
|
||||||
}
|
Advance();
|
||||||
case kClassOpen:
|
return std::make_unique<AnyChar>();
|
||||||
return ParseCharClass();
|
case kEscape:
|
||||||
case kAnyChar:
|
Advance();
|
||||||
Advance();
|
return ParseEscape();
|
||||||
return std::make_unique<AnyChar>();
|
case kStar:
|
||||||
case kEscape:
|
case kPlus:
|
||||||
Advance();
|
case kOptional:
|
||||||
return ParseEscape();
|
case kBraceOpen:
|
||||||
case kStar:
|
throw std::runtime_error(std::format(
|
||||||
case kPlus:
|
"GenerateStringFromRegex: quantifier with no preceding item at "
|
||||||
case kOptional:
|
"position {}",
|
||||||
case kBraceOpen:
|
pos_));
|
||||||
throw std::runtime_error(std::format(
|
default:
|
||||||
"GenerateStringFromRegex: quantifier with no preceding item at "
|
Advance();
|
||||||
"position {}",
|
return std::make_unique<Literal>(c);
|
||||||
pos_));
|
}
|
||||||
default:
|
|
||||||
Advance();
|
|
||||||
return std::make_unique<Literal>(c);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NodePtr Parser::MakeClass(std::vector<std::pair<char, char>> ranges,
|
NodePtr Parser::MakeClass(std::vector<std::pair<char, char>> ranges,
|
||||||
const bool negated) {
|
const bool negated) {
|
||||||
auto char_class = std::make_unique<CharClass>();
|
auto char_class = std::make_unique<CharClass>();
|
||||||
char_class->ranges = std::move(ranges);
|
char_class->ranges = std::move(ranges);
|
||||||
char_class->negated = negated;
|
char_class->negated = negated;
|
||||||
return char_class;
|
return char_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodePtr Parser::ParseEscape() {
|
NodePtr Parser::ParseEscape() {
|
||||||
if (AtEnd()) {
|
if (AtEnd()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"GenerateStringFromRegex: a backslash at the very end has nothing "
|
"GenerateStringFromRegex: a backslash at the very end has nothing "
|
||||||
"to escape");
|
"to escape");
|
||||||
}
|
}
|
||||||
const char c = Advance();
|
const char c = Advance();
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'd':
|
case 'd':
|
||||||
return MakeClass({{'0', '9'}}, false);
|
return MakeClass({{'0', '9'}}, false);
|
||||||
case 'D':
|
case 'D':
|
||||||
return MakeClass({{'0', '9'}}, true);
|
return MakeClass({{'0', '9'}}, true);
|
||||||
case 'w':
|
case 'w':
|
||||||
return MakeClass({{'a', 'z'}, {'A', 'Z'}, {'0', '9'}, {'_', '_'}},
|
return MakeClass({{'a', 'z'}, {'A', 'Z'}, {'0', '9'}, {'_', '_'}},
|
||||||
false);
|
false);
|
||||||
case 'W':
|
case 'W':
|
||||||
return MakeClass({{'a', 'z'}, {'A', 'Z'}, {'0', '9'}, {'_', '_'}},
|
return MakeClass({{'a', 'z'}, {'A', 'Z'}, {'0', '9'}, {'_', '_'}},
|
||||||
true);
|
true);
|
||||||
case 's':
|
case 's':
|
||||||
return MakeClass({{' ', ' '}, {'\t', '\t'}, {'\n', '\n'}, {'\r', '\r'}},
|
return MakeClass(
|
||||||
false);
|
{{' ', ' '}, {'\t', '\t'}, {'\n', '\n'}, {'\r', '\r'}}, false);
|
||||||
case 'S':
|
case 'S':
|
||||||
return MakeClass({{' ', ' '}, {'\t', '\t'}, {'\n', '\n'}, {'\r', '\r'}},
|
return MakeClass(
|
||||||
true);
|
{{' ', ' '}, {'\t', '\t'}, {'\n', '\n'}, {'\r', '\r'}}, true);
|
||||||
case 'n':
|
case 'n':
|
||||||
return std::make_unique<Literal>('\n');
|
return std::make_unique<Literal>('\n');
|
||||||
case 't':
|
case 't':
|
||||||
return std::make_unique<Literal>('\t');
|
return std::make_unique<Literal>('\t');
|
||||||
case 'r':
|
case 'r':
|
||||||
return std::make_unique<Literal>('\r');
|
return std::make_unique<Literal>('\r');
|
||||||
default:
|
default:
|
||||||
// \. \* \( \\ etc. -- the literal character itself.
|
// \. \* \( \\ etc. -- the literal character itself.
|
||||||
return std::make_unique<Literal>(c);
|
return std::make_unique<Literal>(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NodePtr Parser::ParseCharClass() {
|
NodePtr Parser::ParseCharClass() {
|
||||||
Expect(kClassOpen);
|
Expect(kClassOpen);
|
||||||
auto char_class = std::make_unique<CharClass>();
|
auto char_class = std::make_unique<CharClass>();
|
||||||
if (Match(kClassNegate)) {
|
if (Match(kClassNegate)) {
|
||||||
char_class->negated = true;
|
char_class->negated = true;
|
||||||
}
|
}
|
||||||
if (AtEnd()) {
|
if (AtEnd()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"GenerateStringFromRegex: character class '[' is never closed");
|
"GenerateStringFromRegex: character class '[' is never closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!AtEnd() && Peek() != kClassClose) {
|
while (!AtEnd() && Peek() != kClassClose) {
|
||||||
char lo = ClassChar();
|
char lo = ClassChar();
|
||||||
const bool is_range = Peek() == kClassRange && pos_ + 1 < src_.size() &&
|
const bool is_range = Peek() == kClassRange && pos_ + 1 < src_.size() &&
|
||||||
src_[pos_ + 1] != kClassClose;
|
src_[pos_ + 1] != kClassClose;
|
||||||
if (is_range) {
|
if (is_range) {
|
||||||
Advance();
|
Advance();
|
||||||
char hi = ClassChar();
|
char hi = ClassChar();
|
||||||
if (hi < lo) {
|
if (hi < lo) {
|
||||||
std::swap(lo, hi);
|
std::swap(lo, hi);
|
||||||
|
}
|
||||||
|
char_class->ranges.emplace_back(lo, hi);
|
||||||
|
} else {
|
||||||
|
char_class->ranges.emplace_back(lo, lo);
|
||||||
}
|
}
|
||||||
char_class->ranges.emplace_back(lo, hi);
|
}
|
||||||
} else {
|
Expect(kClassClose);
|
||||||
char_class->ranges.emplace_back(lo, lo);
|
if (char_class->ranges.empty()) {
|
||||||
}
|
throw std::runtime_error(
|
||||||
}
|
"GenerateStringFromRegex: empty character class []");
|
||||||
Expect(kClassClose);
|
}
|
||||||
if (char_class->ranges.empty()) {
|
return char_class;
|
||||||
throw std::runtime_error(
|
|
||||||
"GenerateStringFromRegex: empty character class []");
|
|
||||||
}
|
|
||||||
return char_class;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char Parser::ClassChar() {
|
char Parser::ClassChar() {
|
||||||
const char c = Advance();
|
const char c = Advance();
|
||||||
if (c != kEscape) {
|
if (c != kEscape) {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
if (AtEnd()) {
|
if (AtEnd()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"GenerateStringFromRegex: backslash at end of character class");
|
"GenerateStringFromRegex: backslash at end of character class");
|
||||||
}
|
}
|
||||||
const char escaped = Advance();
|
const char escaped = Advance();
|
||||||
switch (escaped) {
|
switch (escaped) {
|
||||||
case 'n':
|
case 'n':
|
||||||
return '\n';
|
return '\n';
|
||||||
case 't':
|
case 't':
|
||||||
return '\t';
|
return '\t';
|
||||||
case 'r':
|
case 'r':
|
||||||
return '\r';
|
return '\r';
|
||||||
default:
|
default:
|
||||||
return escaped;
|
return escaped;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace xeger_internal
|
} // namespace xeger_internal
|
||||||
|
|||||||
Reference in New Issue
Block a user