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
5 changes: 3 additions & 2 deletions mrbgems/mruby-regexp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ simulation) with backtracking fallback.

- `\n`, `\t`, `\r`, `\f`, `\v`, `\a`, `\e` control characters
- `\NNN` octal, one to three digits
- `\xHH` hex, one or two digits
- `\xHH` hex, one or two digits; `\x` with no digit raises `RegexpError`
- `\uXXXX` Unicode codepoint, exactly four hex digits
- `\u{...}` Unicode codepoints, one to six hex digits each, several of
them separated by spaces: `/\u{61 62}/` is `ab`
Expand Down Expand Up @@ -175,7 +175,8 @@ pattern analysis.
- **No Unicode properties**: `\p{Alpha}`, `\p{L}`, etc. are not
supported.
- **No `\x{...}` hex escape**: the hex escape is `\xHH`, so it reaches
`0xff` at most. Write `\u{...}` for a codepoint above that.
`0xff` at most, and `\x{...}` raises `RegexpError` as CRuby does, since
the brace is not a hex digit. Write `\u{...}` for a codepoint above that.
- **No encodings**: a pattern is a byte string read the way the build reads a
String, and there is no encoding to consult about a byte that starts no
whole character. Such a byte is that byte, inside a character class as much
Expand Down
6 changes: 5 additions & 1 deletion mrbgems/mruby-regexp/src/re_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,10 @@ parse_escape(re_compiler *c)
return val & 0xff;
}
/* Hex escape `\xHH` (1-2 hex digits, value 0-255). The `\x{HHHH}` form
for codepoints above 0xff is not implemented. */
for codepoints above 0xff is not implemented, and it is not read as
`\x` either: a `\x` that no hex digit follows used to come out as
`\x00`, so `\x{41}` compiled to a NUL and a quantifier. CRuby rejects
it, as it rejects a bare `\x` and `\xZ`. */
case 'x': {
int val = 0;
int n = 0;
Expand All @@ -536,6 +539,7 @@ parse_escape(re_compiler *c)
next_char(c);
n++;
}
if (n == 0) compile_error(c, "invalid hex escape");
return val & 0xff;
}
default: return ch; /* literal: \., \\, \/, \(, etc. */
Expand Down
29 changes: 29 additions & 0 deletions mrbgems/mruby-regexp/test/regexp_syntax.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,35 @@
assert_equal 0, (/\x7/ =~ "\a")
end

assert("Regexp - a hex escape needs at least one digit") do
# `\x` followed by no hex digit used to read as `\x00`, so `\x{41}` was a
# NUL and a quantifier, and matched 41 NUL bytes. A regexp literal never
# gets this far (the parser refuses it), so the pattern has to be a string.
assert_raise_with_message(RegexpError, "invalid hex escape: /\\x{41}/") do
Regexp.new("\\x{41}")
end
assert_raise_with_message(RegexpError, "invalid hex escape: /\\x/") do
Regexp.new("\\x")
end
assert_raise(RegexpError) { Regexp.new("\\xZ") }
assert_raise(RegexpError) { Regexp.new("a\\x") }
assert_raise(RegexpError) { Regexp.new("\\x{}") }

# inside a character class the escape reads the same way
assert_raise_with_message(RegexpError, "invalid hex escape: /[\\x]/") do
Regexp.new("[\\x]")
end
assert_raise(RegexpError) { Regexp.new("[\\xZ]") }
assert_raise(RegexpError) { Regexp.new("[\\x{41}]") }
assert_raise(RegexpError) { Regexp.new("[a-\\x]") }
assert_raise(RegexpError) { Regexp.new("[\\x-z]") }

# one digit is enough, and a second non-digit ends the escape
assert_equal 0, (Regexp.new("\\x4") =~ "\x04")
assert_equal 0, (Regexp.new("\\x4Z") =~ "\x04Z")
assert_equal 0, (Regexp.new("[\\x4]") =~ "\x04")
end

assert("Regexp - \\h and \\H hex-digit shorthands") do
assert_equal 0, (/\h/ =~ "f")
assert_nil (/\h/ =~ "g")
Expand Down
Loading