From 20c018fa27bce768d3d3724eb2a2561bee8e6bc3 Mon Sep 17 00:00:00 2001 From: Joseph Johansen Date: Thu, 3 Dec 2020 00:07:48 +0000 Subject: [PATCH] Add support for escape sequences in double quoted rbs strings --- lib/rbs/parser.rb | 20 +++++++++++++++++++- lib/rbs/parser.y | 20 +++++++++++++++++++- test/rbs/type_parsing_test.rb | 10 ++++++++++ test/rbs/types_test.rb | 2 ++ 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/lib/rbs/parser.rb b/lib/rbs/parser.rb index bae5a32c7..cc8f49da7 100644 --- a/lib/rbs/parser.rb +++ b/lib/rbs/parser.rb @@ -262,6 +262,10 @@ def empty_params_result /%a\(.*?\)/, /%a\<.*?\>/, /%a\|.*?\|/) + +escape_sequences = %w[a b e f n r s t v "].map { |l| "\\\\#{l}" } +DBL_QUOTE_STR_ESCAPE_SEQUENCES_RE = /(#{escape_sequences.join("|")})/ + def next_token if @type type = @type @@ -341,7 +345,21 @@ def next_token when input.scan(/[a-z_]\w*\b/) new_token(:tLIDENT) when input.scan(/"(\\"|[^"])*"/) - s = input.matched.yield_self {|s| s[1, s.length - 2] }.gsub(/\\"/, '"') + s = input.matched.yield_self {|s| s[1, s.length - 2] } + .gsub(DBL_QUOTE_STR_ESCAPE_SEQUENCES_RE) do |match| + case match + when '\\a' then "\a" + when '\\b' then "\b" + when '\\e' then "\e" + when '\\f' then "\f" + when '\\n' then "\n" + when '\\r' then "\r" + when '\\s' then "\s" + when '\\t' then "\t" + when '\\v' then "\v" + when '\\"' then '"' + end + end new_token(:tSTRING, s) when input.scan(/'(\\'|[^'])*'/) s = input.matched.yield_self {|s| s[1, s.length - 2] }.gsub(/\\'/, "'") diff --git a/lib/rbs/parser.y b/lib/rbs/parser.y index 9d14c9814..6f6378574 100644 --- a/lib/rbs/parser.y +++ b/lib/rbs/parser.y @@ -1294,6 +1294,10 @@ ANNOTATION_RE = Regexp.union(/%a\{.*?\}/, /%a\(.*?\)/, /%a\<.*?\>/, /%a\|.*?\|/) + +escape_sequences = %w[a b e f n r s t v "].map { |l| "\\\\#{l}" } +DBL_QUOTE_STR_ESCAPE_SEQUENCES_RE = /(#{escape_sequences.join("|")})/ + def next_token if @type type = @type @@ -1373,7 +1377,21 @@ def next_token when input.scan(/[a-z_]\w*\b/) new_token(:tLIDENT) when input.scan(/"(\\"|[^"])*"/) - s = input.matched.yield_self {|s| s[1, s.length - 2] }.gsub(/\\"/, '"') + s = input.matched.yield_self {|s| s[1, s.length - 2] } + .gsub(DBL_QUOTE_STR_ESCAPE_SEQUENCES_RE) do |match| + case match + when '\\a' then "\a" + when '\\b' then "\b" + when '\\e' then "\e" + when '\\f' then "\f" + when '\\n' then "\n" + when '\\r' then "\r" + when '\\s' then "\s" + when '\\t' then "\t" + when '\\v' then "\v" + when '\\"' then '"' + end + end new_token(:tSTRING, s) when input.scan(/'(\\'|[^'])*'/) s = input.matched.yield_self {|s| s[1, s.length - 2] }.gsub(/\\'/, "'") diff --git a/test/rbs/type_parsing_test.rb b/test/rbs/type_parsing_test.rb index 4bbd9a010..69237b693 100644 --- a/test/rbs/type_parsing_test.rb +++ b/test/rbs/type_parsing_test.rb @@ -536,6 +536,16 @@ def test_literal assert_instance_of Types::Literal, type assert_equal "super \" duper", type.literal end + + Parser.parse_type('"escape sequences \a\b\e\f\n\r\s\t\v\""').yield_self do |type| + assert_instance_of Types::Literal, type + assert_equal "escape sequences \a\b\e\f\n\r\s\t\v\"", type.literal + end + + Parser.parse_type(%q{'not escape sequences \a\b\e\f\n\r\s\t\v\"'}).yield_self do |type| + assert_instance_of Types::Literal, type + assert_equal 'not escape sequences \a\b\e\f\n\r\s\t\v\"', type.literal + end end def test_record diff --git a/test/rbs/types_test.rb b/test/rbs/types_test.rb index 7a35265de..f84d23f42 100644 --- a/test/rbs/types_test.rb +++ b/test/rbs/types_test.rb @@ -9,6 +9,8 @@ def test_to_s assert_equal "Array[Integer]", parse_type("Array[Integer]").to_s assert_equal "Array[Integer]?", parse_type("Array[Integer]?").to_s assert_equal '"foo"?', parse_type('"foo" ?').to_s + assert_equal '"foo\\\\n"', parse_type(%q{'foo\n'}).to_s + assert_equal '"foo\\n"', parse_type('"foo\n"').to_s assert_equal ":foo ?", parse_type(":foo ?").to_s assert_equal "[ Integer, bool? ]", parse_type("[Integer, bool?]").to_s assert_equal "[ ]", parse_type("[ ]").to_s