Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions mrbgems/mruby-regexp/src/re_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -455,15 +455,14 @@ compile_charclass(re_compiler *c)
if (peek(c) == '-' && c->p + 1 < c->src_end && c->p[1] != ']') {
next_char(c); /* skip '-' */
uint32_t hi = read_class_atom(c);
if (cp < 128 && hi < 128) {
class_set_range(cc, (uint8_t)cp, (uint8_t)hi);
}
else {
/* Range that touches non-ASCII: store as codepoint range.
Mixed ASCII/non-ASCII ranges are rare; stash the whole span
in the codepoint list (the bitmap covers ASCII only, so a
non-ASCII upper bound forces the codepoint path). */
if (cp <= hi) class_add_range(c, cc, cp, hi);
/* A range that straddles the ASCII boundary is split in two: the
bitmap takes the half below 128 and the codepoint list the rest.
Neither half can hold the other, and class_match() picks the side
to read from the codepoint alone, so a span left whole in the
codepoint list is unreachable below 128. */
if (cp <= hi) {
if (cp < 128) class_set_range(cc, (uint8_t)cp, (uint8_t)(hi < 128 ? hi : 127));
if (hi >= 128) class_add_range(c, cc, cp < 128 ? 128 : cp, hi);
}
}
else {
Expand Down
30 changes: 30 additions & 0 deletions mrbgems/mruby-regexp/test/regexp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,36 @@
assert_equal "abc", md[0]
end

assert("Regexp - character class range across the ASCII boundary") do
# A range from an ASCII bound to a non-ASCII one used to be stored whole in
# the codepoint list, which the matcher never reads below 128, so the ASCII
# half of the range matched nothing.
assert_equal "a", "a".match(/[a-Ā]/)[0]
assert_equal "z", "z".match(/[a-Ā]/)[0]
assert_equal "{", "{".match(/[a-Ā]/)[0] # 0x7b, inside a-Ā
assert_nil "A".match(/[a-Ā]/) # 0x41, below the range
assert_nil "`".match(/[a-Ā]/) # 0x60, just below 'a'
assert_equal "abĀz", "!abĀz!".match(/[a-Ā]+/)[0]
# The non-ASCII half still answers on its own.
assert_equal "Ā", "Ā".match(/[a-Ā]/)[0]
assert_equal "À", "À".match(/[a-Ā]/)[0]
assert_nil "ā".match(/[a-Ā]/) # one past the upper bound
# Negation reads the same class, so it rejected the ASCII half it had to
# accept and accepted the half it had to reject.
assert_nil "a".match(/[^a-Ā]/)
assert_nil "Ā".match(/[^a-Ā]/)
assert_equal "A", "A".match(/[^a-Ā]/)[0]
assert_equal "ā", "ā".match(/[^a-Ā]/)[0]
# The /i fold walks the bitmap, so it reaches the ASCII half once that half
# is stored there. Non-ASCII case folding is still not applied.
assert_equal "A", "A".match(/[a-Ā]/i)[0]
assert_nil "A".match(/[^a-Ā]/i)
# Ranges that stay on one side of the boundary are unaffected.
assert_equal "b", "b".match(/[a-c]/)[0]
assert_equal "ą", "ą".match(/[Ā-Đ]/)[0]
assert_nil "a".match(/[Ā-Đ]/)
end

assert("Regexp - POSIX bracket classes") do
# ASCII semantics, like this gem's \w/\d shorthands.
assert_equal "abc", "123abc456".match(/[[:alpha:]]+/)[0]
Expand Down
Loading