Skip to content

Commit 7c415b1

Browse files
committed
Fix up regex delimited by \r\n
1 parent 5617203 commit 7c415b1

2 files changed

Lines changed: 28 additions & 1 deletion

File tree

src/prism.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2249,7 +2249,15 @@ pm_regular_expression_flags_create(pm_parser_t *parser, const pm_token_t *closin
22492249
if (closing->type == PM_TOKEN_REGEXP_END) {
22502250
pm_buffer_t unknown_flags = { 0 };
22512251

2252-
for (const uint8_t *flag = closing->start + 1; flag < closing->end; flag++) {
2252+
// The closing delimiter is normally a single byte, so the options
2253+
// follow it. A `\r\n` newline delimiter is two bytes, however, so we
2254+
// skip past it to avoid misreading the trailing `\n` as an option.
2255+
const uint8_t *flag = closing->start + 1;
2256+
if ((closing->end - closing->start) >= 2 && closing->start[0] == '\r' && closing->start[1] == '\n') {
2257+
flag++;
2258+
}
2259+
2260+
for (; flag < closing->end; flag++) {
22532261
switch (*flag) {
22542262
case 'i': flags |= PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE; break;
22552263
case 'm': flags |= PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE; break;

test/prism/regexp_test.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,27 @@ def test_last_encoding_option_wins
237237
assert_equal Regexp::NOENCODING, option
238238
end
239239

240+
# A `%r` regexp can use a newline (LF or CRLF) as its delimiter. The closing
241+
# delimiter terminates the regexp and must not be misread as an option -- in
242+
# particular the trailing `\n` of a `\r\n` delimiter.
243+
def test_newline_delimiter
244+
assert_newline_regexp("%r\nfoo\n", "foo", 0)
245+
assert_newline_regexp("%r\r\nfoo\r\n", "foo", 0)
246+
assert_newline_regexp("%r\r\nfoo\r\ni", "foo", Regexp::IGNORECASE)
247+
assert_newline_regexp("%r\r\nfoo\r\nmix", "foo", Regexp::IGNORECASE | Regexp::MULTILINE | Regexp::EXTENDED)
248+
assert_newline_regexp("%r\r\n\r\n", "", 0)
249+
end
250+
240251
private
241252

253+
def assert_newline_regexp(source, content, options)
254+
node = Prism.parse_statement(source)
255+
256+
assert_kind_of RegularExpressionNode, node
257+
assert_equal content, node.content
258+
assert_equal options, node.options
259+
end
260+
242261
def assert_valid_regexp(source)
243262
assert Prism.parse_success?("/#{source}/ =~ \"\"")
244263
end

0 commit comments

Comments
 (0)