Skip to content

Commit 47c17b4

Browse files
authored
Merge pull request #3387 from Earlopain/parser-translator-tokens-line-cont-single-quotes
Fix parser translator tokens for backslashes in single-quoted strings and word arrays
2 parents 1d22424 + b6554ad commit 47c17b4

File tree

4 files changed

+123
-95
lines changed

4 files changed

+123
-95
lines changed

lib/prism/translation/parser/lexer.rb

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -410,12 +410,20 @@ def to_a
410410

411411
lines.each.with_index do |line, index|
412412
chomped_line = line.chomp
413-
414-
# When the line ends with an odd number of backslashes, it must be a line continuation.
415-
if chomped_line[/\\{1,}\z/]&.length&.odd?
416-
chomped_line.delete_suffix!("\\")
417-
current_line << chomped_line
418-
adjustment += 2
413+
backslash_count = chomped_line[/\\{1,}\z/]&.length || 0
414+
is_interpolation = interpolation?(quote_stack.last)
415+
is_percent_array = percent_array?(quote_stack.last)
416+
417+
if backslash_count.odd? && (is_interpolation || is_percent_array)
418+
if is_percent_array
419+
# Remove the last backslash, keep potential newlines
420+
current_line << line.sub(/(\\)(\r?\n)\z/, '\2')
421+
adjustment += 1
422+
else
423+
chomped_line.delete_suffix!("\\")
424+
current_line << chomped_line
425+
adjustment += 2
426+
end
419427
# If the string ends with a line continuation emit the remainder
420428
emit = index == lines.count - 1
421429
else
@@ -615,7 +623,13 @@ def unescape_string(string, quote)
615623
# TODO: Implement regexp escaping
616624
return string if quote == "/" || quote.start_with?("%r")
617625

618-
if quote == "'" || quote.start_with?("%q") || quote.start_with?("%w") || quote.start_with?("%i")
626+
if interpolation?(quote)
627+
# In interpolation, escape sequences can be written literally. For example, "\\n" becomes "\n",
628+
# and "\\\\n" becomes "\\n". Unknown escapes sequences, like "\\o" simply become "o".
629+
string.gsub(/\\./) do |match|
630+
ESCAPES[match[1]] || match[1]
631+
end
632+
else
619633
if quote == "'"
620634
delimiter = "'"
621635
else
@@ -624,14 +638,18 @@ def unescape_string(string, quote)
624638

625639
delimiters = Regexp.escape("#{delimiter}#{DELIMITER_SYMETRY[delimiter]}")
626640
string.gsub(/\\([\\#{delimiters}])/, '\1')
627-
else
628-
# When double-quoted, escape sequences can be written literally. For example, "\\n" becomes "\n",
629-
# and "\\\\n" becomes "\\n". Unknown escapes sequences, like "\\o" simply become "o".
630-
string.gsub(/\\./) do |match|
631-
ESCAPES[match[1]] || match[1]
632-
end
633641
end
634642
end
643+
644+
# Determine if characters preceeded by a backslash should be escaped or not
645+
def interpolation?(quote)
646+
quote != "'" && !quote.start_with?("%q", "%w", "%i")
647+
end
648+
649+
# Determine if the string is part of a %-style array.
650+
def percent_array?(quote)
651+
quote.start_with?("%w", "%W", "%i", "%I")
652+
end
635653
end
636654
end
637655
end

test/prism/fixtures/strings.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ b\nar
8383

8484
'\\ foo \\ bar'
8585

86+
'foo\
87+
bar\\
88+
baz
89+
'
90+
8691
"#$foo"
8792

8893
"#@foo"

test/prism/ruby/parser_test.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ class ParserTest < TestCase
8282
# output expected by the parser gem, so we'll skip them for now.
8383
skip_tokens = [
8484
"dash_heredocs.txt",
85-
"dos_endings.txt",
8685
"embdoc_no_newline_at_end.txt",
8786
"heredocs_with_ignored_newlines.txt",
8887
"methods.txt",

test/prism/snapshots/strings.txt

Lines changed: 87 additions & 81 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)