[ruby/yarp] Heredocs can create Interpolated(X)StringNodes or

(X)StringNodes
(https://github.com/ruby/yarp/pull/1427)

Prior to this commit, heredocs were automatically InterpolatedNodes
regardless of whether there was actually interpolation. With this
commit, heredocs are only interpolate if there is actually
interpolation

https://github.com/ruby/yarp/commit/e9f436128b
This commit is contained in:
Jemma Issroff 2023-09-12 09:39:18 -04:00 коммит произвёл git
Родитель b55785579c
Коммит 39336c1ab8
58 изменённых файлов: 686 добавлений и 1007 удалений

Просмотреть файл

@ -8,8 +8,12 @@ module YARP
File.read(filepath).split(/(?=\n)\n(?=<)/).each_with_index do |heredoc, index|
define_method "test_heredoc_#{index}" do
parts = YARP.parse(heredoc).value.statements.body.first.parts
actual = parts.map { |part| part.is_a?(StringNode) ? part.unescaped : "1" }.join
node = YARP.parse(heredoc).value.statements.body.first
if node.is_a? StringNode
actual = node.unescaped
else
actual = node.parts.map { |part| part.is_a?(StringNode) ? part.unescaped : "1" }.join
end
assert_equal(eval(heredoc), actual, "Expected heredocs to match.")
end

Просмотреть файл

@ -460,7 +460,7 @@ module YARP
def test_InterpolatedStringNode
assert_location(InterpolatedStringNode, "\"foo \#@bar baz\"")
assert_location(InterpolatedStringNode, "<<~A\nhello world\nA", 0...4)
assert_location(InterpolatedStringNode, "<<~A\nhello \#{1} world\nA", 0...4)
end
def test_InterpolatedSymbolNode

Просмотреть файл

@ -186,7 +186,11 @@ module YARP
# We only want to compare parent/child location overlap in the case that
# we are not looking at a heredoc. That's because heredoc locations are
# special in that they only use the declaration of the heredoc.
compare = !(current.is_a?(InterpolatedStringNode) || current.is_a?(InterpolatedXStringNode)) || !current.opening&.start_with?("<<")
compare = !(current.is_a?(StringNode) ||
current.is_a?(XStringNode) ||
current.is_a?(InterpolatedStringNode) ||
current.is_a?(InterpolatedXStringNode)) ||
!current.opening&.start_with?("<<")
current.child_nodes.each do |child|
# child_nodes can return nil values, so we need to skip those.

Просмотреть файл

@ -3,41 +3,29 @@
└── statements:
@ StatementsNode (location: (0...278))
└── body: (length: 13)
├── @ InterpolatedStringNode (location: (0...6))
├── @ StringNode (location: (0...6))
│ ├── opening_loc: (0...6) = "<<-EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (7...11))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (7...11) = " a\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: " a\n"
│ └── closing_loc: (11...15) = "EOF\n"
│ ├── content_loc: (7...11) = " a\n"
│ ├── closing_loc: (11...15) = "EOF\n"
│ └── unescaped: " a\n"
├── @ CallNode (location: (16...36))
│ ├── receiver:
│ │ @ InterpolatedStringNode (location: (16...24))
│ │ @ StringNode (location: (16...24))
│ │ ├── opening_loc: (16...24) = "<<-FIRST"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (37...41))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (37...41) = " a\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: " a\n"
│ │ └── closing_loc: (41...47) = "FIRST\n"
│ │ ├── content_loc: (37...41) = " a\n"
│ │ ├── closing_loc: (41...47) = "FIRST\n"
│ │ └── unescaped: " a\n"
│ ├── call_operator_loc: ∅
│ ├── message_loc: (25...26) = "+"
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (27...36))
│ │ └── arguments: (length: 1)
│ │ └── @ InterpolatedStringNode (location: (27...36))
│ │ └── @ StringNode (location: (27...36))
│ │ ├── opening_loc: (27...36) = "<<-SECOND"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (47...51))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (47...51) = " b\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: " b\n"
│ │ └── closing_loc: (51...58) = "SECOND\n"
│ │ ├── content_loc: (47...51) = " b\n"
│ │ ├── closing_loc: (51...58) = "SECOND\n"
│ │ └── unescaped: " b\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags:
@ -72,24 +60,16 @@
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "\n"
│ └── closing_loc: (77...81) = "EOF\n"
├── @ InterpolatedStringNode (location: (82...88))
├── @ StringNode (location: (82...88))
│ ├── opening_loc: (82...88) = "<<-EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (98...102))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (98...102) = " a\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: " a\n"
│ └── closing_loc: (102...106) = "EOF\n"
├── @ InterpolatedStringNode (location: (107...113))
│ ├── content_loc: (98...102) = " a\n"
│ ├── closing_loc: (102...106) = "EOF\n"
│ └── unescaped: " a\n"
├── @ StringNode (location: (107...113))
│ ├── opening_loc: (107...113) = "<<-EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (114...122))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (114...122) = " a\n b\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: " a\n b\n"
│ └── closing_loc: (122...128) = " EOF\n"
│ ├── content_loc: (114...122) = " a\n b\n"
│ ├── closing_loc: (122...128) = " EOF\n"
│ └── unescaped: " a\n b\n"
├── @ InterpolatedStringNode (location: (129...137))
│ ├── opening_loc: (129...137) = "<<-\"EOF\""
│ ├── parts: (length: 3)
@ -155,39 +135,28 @@
│ ├── content_loc: (175...178) = "abc"
│ ├── closing_loc: (178...179) = "#"
│ └── unescaped: "abc"
├── @ InterpolatedStringNode (location: (181...187))
├── @ StringNode (location: (181...187))
│ ├── opening_loc: (181...187) = "<<-EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (188...196))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (188...196) = " a\n b\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: " a\n b\n"
│ └── closing_loc: (196...200) = "EOF\n"
├── @ InterpolatedStringNode (location: (201...206))
│ ├── content_loc: (188...196) = " a\n b\n"
│ ├── closing_loc: (196...200) = "EOF\n"
│ └── unescaped: " a\n b\n"
├── @ StringNode (location: (201...206))
│ ├── opening_loc: (201...206) = "<<-''"
│ ├── parts: (length: 0)
│ └── closing_loc: (207...208) = "\n"
├── @ InterpolatedStringNode (location: (209...217))
│ ├── content_loc: (206...207) = "\n"
│ ├── closing_loc: (207...208) = "\n"
│ └── unescaped: ""
├── @ StringNode (location: (209...217))
│ ├── opening_loc: (209...217) = "<<-'EOF'"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (218...227))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (218...227) = " a \#{1}\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: " a \#{1}\n"
│ └── closing_loc: (227...231) = "EOF\n"
│ ├── content_loc: (218...227) = " a \#{1}\n"
│ ├── closing_loc: (227...231) = "EOF\n"
│ └── unescaped: " a \#{1}\n"
├── @ CallNode (location: (232...243))
│ ├── receiver:
│ │ @ InterpolatedStringNode (location: (232...236))
│ │ @ StringNode (location: (232...236))
│ │ ├── opening_loc: (232...236) = "<<-A"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (244...248))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (244...248) = " a\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: " a\n"
│ │ └── closing_loc: (248...250) = "A\n"
│ │ ├── content_loc: (244...248) = " a\n"
│ │ ├── closing_loc: (248...250) = "A\n"
│ │ └── unescaped: " a\n"
│ ├── call_operator_loc: ∅
│ ├── message_loc: (237...238) = "+"
│ ├── opening_loc: ∅
@ -222,15 +191,11 @@
│ └── name: "+"
└── @ CallNode (location: (267...278))
├── receiver:
│ @ InterpolatedStringNode (location: (267...271))
│ @ StringNode (location: (267...271))
│ ├── opening_loc: (267...271) = "<<-A"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (279...283))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (279...283) = " a\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: " a\n"
│ └── closing_loc: (283...285) = "A\n"
│ ├── content_loc: (279...283) = " a\n"
│ ├── closing_loc: (283...285) = "A\n"
│ └── unescaped: " a\n"
├── call_operator_loc: ∅
├── message_loc: (272...273) = "+"
├── opening_loc: ∅

Просмотреть файл

@ -37,15 +37,11 @@
│ │ └── unescaped: "ab"
│ ├── opening_loc: (28...31) = "%I{"
│ └── closing_loc: (36...37) = "}"
├── @ InterpolatedStringNode (location: (41...45))
├── @ StringNode (location: (41...45))
│ ├── opening_loc: (41...45) = "<<-E"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (47...70))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (47...70) = " 1 \\\r\n 2\r\n 3\r\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: " 1 2\r\n 3\r\n"
│ └── closing_loc: (70...73) = "E\r\n"
│ ├── content_loc: (47...70) = " 1 \\\r\n 2\r\n 3\r\n"
│ ├── closing_loc: (70...73) = "E\r\n"
│ └── unescaped: " 1 2\r\n 3\r\n"
├── @ LocalVariableWriteNode (location: (75...84))
│ ├── name: :x
│ ├── depth: 0
@ -72,15 +68,11 @@
│ │ └── arguments: (length: 1)
│ │ └── @ CallNode (location: (96...107))
│ │ ├── receiver:
│ │ │ @ InterpolatedStringNode (location: (96...102))
│ │ │ @ StringNode (location: (96...102))
│ │ │ ├── opening_loc: (96...102) = "<<~EOF"
│ │ │ ├── parts: (length: 1)
│ │ │ │ └── @ StringNode (location: (110...121))
│ │ │ │ ├── opening_loc: ∅
│ │ │ │ ├── content_loc: (110...121) = "\r\n baz\r\n"
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ └── unescaped: "\nbaz\r\n"
│ │ │ └── closing_loc: (121...128) = " EOF\r\n"
│ │ │ ├── content_loc: (110...121) = "\r\n baz\r\n"
│ │ │ ├── closing_loc: (121...128) = " EOF\r\n"
│ │ │ └── unescaped: "\nbaz\r\n"
│ │ ├── call_operator_loc: (102...103) = "."
│ │ ├── message_loc: (103...107) = "chop"
│ │ ├── opening_loc: ∅

Просмотреть файл

@ -5,10 +5,11 @@
└── body: (length: 2)
├── @ CallNode (location: (0...25))
│ ├── receiver:
│ │ @ InterpolatedStringNode (location: (0...9))
│ │ @ StringNode (location: (0...9))
│ │ ├── opening_loc: (0...9) = "<<-TARGET"
│ │ ├── parts: (length: 0)
│ │ └── closing_loc: (27...34) = "TARGET\n"
│ │ ├── content_loc: (9...27) = ".gsub /^\\s{/, ''\\\n"
│ │ ├── closing_loc: (27...34) = "TARGET\n"
│ │ └── unescaped: ""
│ ├── call_operator_loc: (9...10) = "."
│ ├── message_loc: (10...14) = "gsub"
│ ├── opening_loc: ∅
@ -32,10 +33,11 @@
│ └── name: "gsub"
└── @ CallNode (location: (37...62))
├── receiver:
│ @ InterpolatedStringNode (location: (37...46))
│ @ StringNode (location: (37...46))
│ ├── opening_loc: (37...46) = "<<-TARGET"
│ ├── parts: (length: 0)
│ └── closing_loc: (65...73) = "TARGET\r\n"
│ ├── content_loc: (46...65) = ".gsub /^\\s{/, ''\\\r\n"
│ ├── closing_loc: (65...73) = "TARGET\r\n"
│ └── unescaped: ""
├── call_operator_loc: (46...47) = "."
├── message_loc: (47...51) = "gsub"
├── opening_loc: ∅

Просмотреть файл

@ -3,7 +3,8 @@
└── statements:
@ StatementsNode (location: (0...6))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...6))
└── @ StringNode (location: (0...6))
├── opening_loc: (0...6) = "<<-END"
├── parts: (length: 0)
└── closing_loc: (7...10) = "END"
├── content_loc: (6...7) = "\n"
├── closing_loc: (7...10) = "END"
└── unescaped: ""

Просмотреть файл

@ -16,15 +16,11 @@
│ │ ├── statements:
│ │ │ @ StatementsNode (location: (15...21))
│ │ │ └── body: (length: 1)
│ │ │ └── @ InterpolatedStringNode (location: (15...21))
│ │ │ └── @ StringNode (location: (15...21))
│ │ │ ├── opening_loc: (15...21) = "<<RUBY"
│ │ │ ├── parts: (length: 1)
│ │ │ │ └── @ StringNode (location: (22...30))
│ │ │ │ ├── opening_loc: ∅
│ │ │ │ ├── content_loc: (22...30) = " hello\n"
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ └── unescaped: " hello\n"
│ │ │ └── closing_loc: (30...35) = "RUBY\n"
│ │ │ ├── content_loc: (22...30) = " hello\n"
│ │ │ ├── closing_loc: (30...35) = "RUBY\n"
│ │ │ └── unescaped: " hello\n"
│ │ └── closing_loc: (35...36) = "}"
│ └── @ StringNode (location: (36...42))
│ ├── opening_loc: ∅

Просмотреть файл

@ -3,16 +3,13 @@
└── statements:
@ StatementsNode (location: (0...23))
└── body: (length: 2)
├── @ InterpolatedStringNode (location: (0...7))
├── @ StringNode (location: (0...7))
│ ├── opening_loc: (0...7) = "<<-HERE"
│ ├── parts: (length: 0)
│ └── closing_loc: (9...14) = "HERE\n"
└── @ InterpolatedStringNode (location: (15...23))
│ ├── content_loc: (7...9) = "\\\n"
│ ├── closing_loc: (9...14) = "HERE\n"
│ └── unescaped: ""
└── @ StringNode (location: (15...23))
├── opening_loc: (15...23) = "<<~THERE"
├── parts: (length: 1)
│ └── @ StringNode (location: (25...100))
│ ├── opening_loc: ∅
│ ├── content_loc: (25...100) = " way over\n <<HERE\n not here\n HERE\n\n <<~BUT\\\n but\n BUT\n there\n"
│ ├── closing_loc: ∅
│ └── unescaped: "way over\n<<HERE\n not here\nHERE\n\n<<~BUT but\nBUT\n there\n"
└── closing_loc: (100...106) = "THERE\n"
├── content_loc: (25...100) = " way over\n <<HERE\n not here\n HERE\n\n <<~BUT\\\n but\n BUT\n there\n"
├── closing_loc: (100...106) = "THERE\n"
└── unescaped: "way over\n<<HERE\n not here\nHERE\n\n<<~BUT but\nBUT\n there\n"

Просмотреть файл

@ -3,12 +3,8 @@
└── statements:
@ StatementsNode (location: (0...6))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...6))
└── @ StringNode (location: (0...6))
├── opening_loc: (0...6) = "<<-EOE"
├── parts: (length: 1)
│ └── @ StringNode (location: (7...23))
│ ├── opening_loc: ∅
│ ├── content_loc: (7...23) = " some\n heredocs\n"
│ ├── closing_loc: ∅
│ └── unescaped: " some\n heredocs\n"
└── closing_loc: (23...26) = "EOE"
├── content_loc: (7...23) = " some\n heredocs\n"
├── closing_loc: (23...26) = "EOE"
└── unescaped: " some\n heredocs\n"

Просмотреть файл

@ -15,15 +15,11 @@
│ ├── receiver:
│ │ @ CallNode (location: (2...26))
│ │ ├── receiver:
│ │ │ @ InterpolatedStringNode (location: (2...8))
│ │ │ @ StringNode (location: (2...8))
│ │ │ ├── opening_loc: (2...8) = "<<-END"
│ │ │ ├── parts: (length: 1)
│ │ │ │ └── @ StringNode (location: (12...16))
│ │ │ │ ├── opening_loc: ∅
│ │ │ │ ├── content_loc: (12...16) = " a\n"
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ └── unescaped: " a\n"
│ │ │ └── closing_loc: (16...22) = " END\n"
│ │ │ ├── content_loc: (12...16) = " a\n"
│ │ │ ├── closing_loc: (16...22) = " END\n"
│ │ │ └── unescaped: " a\n"
│ │ ├── call_operator_loc: ∅
│ │ ├── message_loc: (8...9) = "+"
│ │ ├── opening_loc: ∅

Просмотреть файл

@ -8,13 +8,9 @@
├── depth: 0
├── name_loc: (0...3) = "str"
├── value:
│ @ InterpolatedStringNode (location: (6...12))
│ @ StringNode (location: (6...12))
│ ├── opening_loc: (6...12) = "<<-XXX"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (14...30))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (14...30) = "before\\\r\nafter\r\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "beforeafter\r\n"
│ └── closing_loc: (30...35) = "XXX\r\n"
│ ├── content_loc: (14...30) = "before\\\r\nafter\r\n"
│ ├── closing_loc: (30...35) = "XXX\r\n"
│ └── unescaped: "beforeafter\r\n"
└── operator_loc: (4...5) = "="

Просмотреть файл

@ -8,12 +8,8 @@
│ ├── content_loc: (1...39) = " why would someone do this? \\\n blah\n"
│ ├── closing_loc: (39...40) = "\""
│ └── unescaped: " why would someone do this? blah\n"
└── @ InterpolatedStringNode (location: (42...49))
└── @ StringNode (location: (42...49))
├── opening_loc: (42...49) = "<<-DESC"
├── parts: (length: 1)
│ └── @ StringNode (location: (50...88))
│ ├── opening_loc: ∅
│ ├── content_loc: (50...88) = " why would someone do this? \\\n blah\n"
│ ├── closing_loc: ∅
│ └── unescaped: " why would someone do this? blah\n"
└── closing_loc: (88...93) = "DESC\n"
├── content_loc: (50...88) = " why would someone do this? \\\n blah\n"
├── closing_loc: (88...93) = "DESC\n"
└── unescaped: " why would someone do this? blah\n"

Просмотреть файл

@ -8,13 +8,9 @@
├── depth: 0
├── name_loc: (0...1) = "s"
├── value:
│ @ InterpolatedStringNode (location: (4...9))
│ @ StringNode (location: (4...9))
│ ├── opening_loc: (4...9) = "<<eos"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (10...17))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (10...17) = "a\\xE9b\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "a\xE9b\n"
│ └── closing_loc: (17...21) = "eos\n"
│ ├── content_loc: (10...17) = "a\\xE9b\n"
│ ├── closing_loc: (17...21) = "eos\n"
│ └── unescaped: "a\xE9b\n"
└── operator_loc: (2...3) = "="

Просмотреть файл

@ -8,13 +8,9 @@
├── depth: 0
├── name_loc: (0...1) = "s"
├── value:
│ @ InterpolatedStringNode (location: (4...10))
│ @ StringNode (location: (4...10))
│ ├── opening_loc: (4...10) = "<<-EOS"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (11...23))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (11...23) = "a\\247b\ncöd\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "a\xA7b\ncöd\n"
│ └── closing_loc: (23...27) = "EOS\n"
│ ├── content_loc: (11...23) = "a\\247b\ncöd\n"
│ ├── closing_loc: (23...27) = "EOS\n"
│ └── unescaped: "a\xA7b\ncöd\n"
└── operator_loc: (2...3) = "="

Просмотреть файл

@ -14,14 +14,10 @@
│ └── closing_loc: (16...17) = "]"
└── @ ArrayNode (location: (19...47))
├── elements: (length: 1)
│ └── @ InterpolatedStringNode (location: (20...27))
│ └── @ StringNode (location: (20...27))
│ ├── opening_loc: (20...27) = "<<-FILE"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (29...41))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (29...41) = " some text\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: " some text\n"
│ └── closing_loc: (41...46) = "FILE\n"
│ ├── content_loc: (29...41) = " some text\n"
│ ├── closing_loc: (41...46) = "FILE\n"
│ └── unescaped: " some text\n"
├── opening_loc: (19...20) = "["
└── closing_loc: (46...47) = "]"

Просмотреть файл

@ -8,15 +8,11 @@
│ ├── depth: 0
│ ├── name_loc: (0...1) = "c"
│ ├── value:
│ │ @ InterpolatedStringNode (location: (4...11))
│ │ @ StringNode (location: (4...11))
│ │ ├── opening_loc: (4...11) = "<<'CCC'"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (12...30))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (12...30) = "line2\nline3\nline4\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "line2\nline3\nline4\n"
│ │ └── closing_loc: (30...34) = "CCC\n"
│ │ ├── content_loc: (12...30) = "line2\nline3\nline4\n"
│ │ ├── closing_loc: (30...34) = "CCC\n"
│ │ └── unescaped: "line2\nline3\nline4\n"
│ └── operator_loc: (2...3) = "="
└── @ LocalVariableWriteNode (location: (35...41))
├── name: :d

Просмотреть файл

@ -13,15 +13,11 @@
│ │ │ │ ├── statements:
│ │ │ │ │ @ StatementsNode (location: (8...11))
│ │ │ │ │ └── body: (length: 1)
│ │ │ │ │ └── @ InterpolatedStringNode (location: (8...11))
│ │ │ │ │ └── @ StringNode (location: (8...11))
│ │ │ │ │ ├── opening_loc: (8...11) = "<<B"
│ │ │ │ │ ├── parts: (length: 1)
│ │ │ │ │ │ └── @ StringNode (location: (13...15))
│ │ │ │ │ │ ├── opening_loc: ∅
│ │ │ │ │ │ ├── content_loc: (13...15) = "b\n"
│ │ │ │ │ │ ├── closing_loc: ∅
│ │ │ │ │ │ └── unescaped: "b\n"
│ │ │ │ │ └── closing_loc: (15...17) = "B\n"
│ │ │ │ │ ├── content_loc: (13...15) = "b\n"
│ │ │ │ │ ├── closing_loc: (15...17) = "B\n"
│ │ │ │ │ └── unescaped: "b\n"
│ │ │ │ └── closing_loc: (11...12) = "}"
│ │ │ ├── @ StringNode (location: (12...13))
│ │ │ │ ├── opening_loc: ∅

Просмотреть файл

@ -8,13 +8,9 @@
├── depth: 0
├── name_loc: (0...1) = "a"
├── value:
│ @ InterpolatedStringNode (location: (4...12))
│ @ StringNode (location: (4...12))
│ ├── opening_loc: (4...12) = "<<~\"EOF\""
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (13...25))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (13...25) = " x\n y\n z\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "x\ny\nz\n"
│ └── closing_loc: (25...31) = " EOF\n"
│ ├── content_loc: (13...25) = " x\n y\n z\n"
│ ├── closing_loc: (25...31) = " EOF\n"
│ └── unescaped: "x\ny\nz\n"
└── operator_loc: (2...3) = "="

Просмотреть файл

@ -8,13 +8,9 @@
├── depth: 0
├── name_loc: (0...1) = "a"
├── value:
│ @ InterpolatedStringNode (location: (4...10))
│ @ StringNode (location: (4...10))
│ ├── opening_loc: (4...10) = "<<~EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (11...20))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (11...20) = " x\n\n z\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "x\n\nz\n"
│ └── closing_loc: (20...24) = "EOF\n"
│ ├── content_loc: (11...20) = " x\n\n z\n"
│ ├── closing_loc: (20...24) = "EOF\n"
│ └── unescaped: "x\n\nz\n"
└── operator_loc: (2...3) = "="

Просмотреть файл

@ -3,7 +3,8 @@
└── statements:
@ StatementsNode (location: (0...4))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...4))
└── @ StringNode (location: (0...4))
├── opening_loc: (0...4) = "<<~A"
├── parts: (length: 0)
└── closing_loc: (5...7) = "A\n"
├── content_loc: (4...5) = "\n"
├── closing_loc: (5...7) = "A\n"
└── unescaped: ""

Просмотреть файл

@ -3,12 +3,8 @@
└── statements:
@ StatementsNode (location: (0...4))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...4))
└── @ StringNode (location: (0...4))
├── opening_loc: (0...4) = "<<~A"
├── parts: (length: 1)
│ └── @ StringNode (location: (5...7))
│ ├── opening_loc: ∅
│ ├── content_loc: (5...7) = "a\n"
│ ├── closing_loc: ∅
│ └── unescaped: "a\n"
└── closing_loc: (7...9) = "A\n"
├── content_loc: (5...7) = "a\n"
├── closing_loc: (7...9) = "A\n"
└── unescaped: "a\n"

Просмотреть файл

@ -8,13 +8,9 @@
├── depth: 0
├── name_loc: (0...1) = "a"
├── value:
│ @ InterpolatedStringNode (location: (4...12))
│ @ StringNode (location: (4...12))
│ ├── opening_loc: (4...12) = "<<~\"EOF\""
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (13...43))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (13...43) = " blah blah\n\t blah blah\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "blah blah\n blah blah\n"
│ └── closing_loc: (43...49) = " EOF\n"
│ ├── content_loc: (13...43) = " blah blah\n\t blah blah\n"
│ ├── closing_loc: (43...49) = " EOF\n"
│ └── unescaped: "blah blah\n blah blah\n"
└── operator_loc: (2...3) = "="

Просмотреть файл

@ -8,13 +8,9 @@
├── depth: 0
├── name_loc: (0...1) = "a"
├── value:
│ @ InterpolatedStringNode (location: (4...12))
│ @ StringNode (location: (4...12))
│ ├── opening_loc: (4...12) = "<<~\"EOF\""
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (13...37))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (13...37) = " blah blah\n \tblah blah\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "blah blah\n\tblah blah\n"
│ └── closing_loc: (37...43) = " EOF\n"
│ ├── content_loc: (13...37) = " blah blah\n \tblah blah\n"
│ ├── closing_loc: (37...43) = " EOF\n"
│ └── unescaped: "blah blah\n\tblah blah\n"
└── operator_loc: (2...3) = "="

Просмотреть файл

@ -8,13 +8,9 @@
├── depth: 0
├── name_loc: (0...1) = "a"
├── value:
│ @ InterpolatedStringNode (location: (4...10))
│ @ StringNode (location: (4...10))
│ ├── opening_loc: (4...10) = "<<~EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (11...21))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (11...21) = " x\n \n z\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "x\n\nz\n"
│ └── closing_loc: (21...25) = "EOF\n"
│ ├── content_loc: (11...21) = " x\n \n z\n"
│ ├── closing_loc: (21...25) = "EOF\n"
│ └── unescaped: "x\n\nz\n"
└── operator_loc: (2...3) = "="

Просмотреть файл

@ -5,15 +5,11 @@
└── body: (length: 1)
└── @ CallNode (location: (0...22))
├── receiver:
│ @ InterpolatedStringNode (location: (0...5))
│ @ StringNode (location: (0...5))
│ ├── opening_loc: (0...5) = "<<END"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (7...12))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (7...12) = "blah\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "blah\n"
│ └── closing_loc: (12...16) = "END\n"
│ ├── content_loc: (7...12) = "blah\n"
│ ├── closing_loc: (12...16) = "END\n"
│ └── unescaped: "blah\n"
├── call_operator_loc: (16...17) = "."
├── message_loc: (17...22) = "strip"
├── opening_loc: ∅

Просмотреть файл

@ -3,12 +3,8 @@
└── statements:
@ StatementsNode (location: (0...9))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...9))
└── @ StringNode (location: (0...9))
├── opening_loc: (0...9) = "<<OOTPÜT"
├── parts: (length: 1)
│ └── @ StringNode (location: (10...12))
│ ├── opening_loc: ∅
│ ├── content_loc: (10...12) = ".\n"
│ ├── closing_loc: ∅
│ └── unescaped: ".\n"
└── closing_loc: (12...20) = "OOTPÜT\n"
├── content_loc: (10...12) = ".\n"
├── closing_loc: (12...20) = "OOTPÜT\n"
└── unescaped: ".\n"

Просмотреть файл

@ -3,12 +3,8 @@
└── statements:
@ StatementsNode (location: (0...5))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...5))
└── @ StringNode (location: (0...5))
├── opening_loc: (0...5) = "<<EOS"
├── parts: (length: 1)
│ └── @ StringNode (location: (6...21))
│ ├── opening_loc: ∅
│ ├── content_loc: (6...21) = "foo\\rbar\nbaz\\r\n"
│ ├── closing_loc: ∅
│ └── unescaped: "foo\rbar\nbaz\r\n"
└── closing_loc: (21...25) = "EOS\n"
├── content_loc: (6...21) = "foo\\rbar\nbaz\\r\n"
├── closing_loc: (21...25) = "EOS\n"
└── unescaped: "foo\rbar\nbaz\r\n"

Просмотреть файл

@ -3,12 +3,8 @@
└── statements:
@ StatementsNode (location: (0...5))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...5))
└── @ StringNode (location: (0...5))
├── opening_loc: (0...5) = "<<EOS"
├── parts: (length: 1)
│ └── @ StringNode (location: (7...24))
│ ├── opening_loc: ∅
│ ├── content_loc: (7...24) = "foo\\rbar\r\nbaz\\r\r\n"
│ ├── closing_loc: ∅
│ └── unescaped: "foo\rbar\r\nbaz\r\r\n"
└── closing_loc: (24...29) = "EOS\r\n"
├── content_loc: (7...24) = "foo\\rbar\r\nbaz\\r\r\n"
├── closing_loc: (24...29) = "EOS\r\n"
└── unescaped: "foo\rbar\r\nbaz\r\r\n"

Просмотреть файл

@ -3,12 +3,8 @@
└── statements:
@ StatementsNode (location: (0...7))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...7))
└── @ StringNode (location: (0...7))
├── opening_loc: (0...7) = "<<'eot'"
├── parts: (length: 1)
│ └── @ StringNode (location: (9...15))
│ ├── opening_loc: ∅
│ ├── content_loc: (9...15) = "body\r\n"
│ ├── closing_loc: ∅
│ └── unescaped: "body\r\n"
└── closing_loc: (15...19) = "eot\n"
├── content_loc: (9...15) = "body\r\n"
├── closing_loc: (15...19) = "eot\n"
└── unescaped: "body\r\n"

Просмотреть файл

@ -3,12 +3,8 @@
└── statements:
@ StatementsNode (location: (0...5))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...5))
└── @ StringNode (location: (0...5))
├── opening_loc: (0...5) = "<<EOS"
├── parts: (length: 1)
│ └── @ StringNode (location: (6...19))
│ ├── opening_loc: ∅
│ ├── content_loc: (6...19) = "foo\rbar\r\nbaz\n"
│ ├── closing_loc: ∅
│ └── unescaped: "foo\rbar\r\nbaz\n"
└── closing_loc: (19...23) = "EOS\n"
├── content_loc: (6...19) = "foo\rbar\r\nbaz\n"
├── closing_loc: (19...23) = "EOS\n"
└── unescaped: "foo\rbar\r\nbaz\n"

Просмотреть файл

@ -3,12 +3,8 @@
└── statements:
@ StatementsNode (location: (0...5))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...5))
└── @ StringNode (location: (0...5))
├── opening_loc: (0...5) = "<<EOS"
├── parts: (length: 1)
│ └── @ StringNode (location: (7...22))
│ ├── opening_loc: ∅
│ ├── content_loc: (7...22) = "foo\rbar\r\r\nbaz\r\n"
│ ├── closing_loc: ∅
│ └── unescaped: "foo\rbar\r\r\nbaz\r\n"
└── closing_loc: (22...27) = "EOS\r\n"
├── content_loc: (7...22) = "foo\rbar\r\r\nbaz\r\n"
├── closing_loc: (22...27) = "EOS\r\n"
└── unescaped: "foo\rbar\r\r\nbaz\r\n"

Просмотреть файл

@ -3,12 +3,8 @@
└── statements:
@ StatementsNode (location: (0...10))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...10))
└── @ StringNode (location: (0...10))
├── opening_loc: (0...10) = "<<-HEREDOC"
├── parts: (length: 1)
│ └── @ StringNode (location: (11...15))
│ ├── opening_loc: ∅
│ ├── content_loc: (11...15) = "\#${\n"
│ ├── closing_loc: ∅
│ └── unescaped: "\#${\n"
└── closing_loc: (15...23) = "HEREDOC\n"
├── content_loc: (11...15) = "\#${\n"
├── closing_loc: (15...23) = "HEREDOC\n"
└── unescaped: "\#${\n"

Просмотреть файл

@ -3,12 +3,8 @@
└── statements:
@ StatementsNode (location: (0...5))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...5))
└── @ StringNode (location: (0...5))
├── opening_loc: (0...5) = "<<EOS"
├── parts: (length: 1)
│ └── @ StringNode (location: (6...14))
│ ├── opening_loc: ∅
│ ├── content_loc: (6...14) = "\r\n\r\r\n\\r\n"
│ ├── closing_loc: ∅
│ └── unescaped: "\r\n\r\r\n\r\n"
└── closing_loc: (14...18) = "EOS\n"
├── content_loc: (6...14) = "\r\n\r\r\n\\r\n"
├── closing_loc: (14...18) = "EOS\n"
└── unescaped: "\r\n\r\r\n\r\n"

Просмотреть файл

@ -3,12 +3,8 @@
└── statements:
@ StatementsNode (location: (0...5))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...5))
└── @ StringNode (location: (0...5))
├── opening_loc: (0...5) = "<<EOS"
├── parts: (length: 1)
│ └── @ StringNode (location: (7...18))
│ ├── opening_loc: ∅
│ ├── content_loc: (7...18) = "\r\r\n\r\r\r\n\\r\r\n"
│ ├── closing_loc: ∅
│ └── unescaped: "\r\r\n\r\r\r\n\r\r\n"
└── closing_loc: (18...23) = "EOS\r\n"
├── content_loc: (7...18) = "\r\r\n\r\r\r\n\\r\r\n"
├── closing_loc: (18...23) = "EOS\r\n"
└── unescaped: "\r\r\n\r\r\r\n\r\r\n"

Просмотреть файл

@ -10,15 +10,11 @@
│ ├── value:
│ │ @ CallNode (location: (15...31))
│ │ ├── receiver:
│ │ │ @ InterpolatedStringNode (location: (15...25))
│ │ │ @ StringNode (location: (15...25))
│ │ │ ├── opening_loc: (15...25) = "<<-HEREDOC"
│ │ │ ├── parts: (length: 1)
│ │ │ │ └── @ StringNode (location: (32...57))
│ │ │ │ ├── opening_loc: ∅
│ │ │ │ ├── content_loc: (32...57) = " very long string\n"
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ └── unescaped: " very long string\n"
│ │ │ └── closing_loc: (57...71) = " HEREDOC\n"
│ │ │ ├── content_loc: (32...57) = " very long string\n"
│ │ │ ├── closing_loc: (57...71) = " HEREDOC\n"
│ │ │ └── unescaped: " very long string\n"
│ │ ├── call_operator_loc: (25...26) = "."
│ │ ├── message_loc: (26...31) = "strip"
│ │ ├── opening_loc: ∅

Просмотреть файл

@ -3,15 +3,11 @@
└── statements:
@ StatementsNode (location: (0...48))
└── body: (length: 2)
├── @ InterpolatedStringNode (location: (0...8))
├── @ StringNode (location: (0...8))
│ ├── opening_loc: (0...8) = "<<-EOFOO"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (9...28))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (9...28) = "\\n\\n\\n\\n\\n\\n\\n\\n\\n\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "\n\n\n\n\n\n\n\n\n\n"
│ └── closing_loc: (28...34) = "EOFOO\n"
│ ├── content_loc: (9...28) = "\\n\\n\\n\\n\\n\\n\\n\\n\\n\n"
│ ├── closing_loc: (28...34) = "EOFOO\n"
│ └── unescaped: "\n\n\n\n\n\n\n\n\n\n"
└── @ ClassNode (location: (35...48))
├── locals: []
├── class_keyword_loc: (35...40) = "class"

Просмотреть файл

@ -8,15 +8,11 @@
│ ├── depth: 0
│ ├── name_loc: (6...12) = "string"
│ ├── value:
│ │ @ InterpolatedStringNode (location: (15...22))
│ │ @ StringNode (location: (15...22))
│ │ ├── opening_loc: (15...22) = "<<-\"^D\""
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (23...48))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (23...48) = " very long string\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: " very long string\n"
│ │ └── closing_loc: (48...57) = " ^D\n"
│ │ ├── content_loc: (23...48) = " very long string\n"
│ │ ├── closing_loc: (48...57) = " ^D\n"
│ │ └── unescaped: " very long string\n"
│ └── operator_loc: (13...14) = "="
└── @ CallNode (location: (63...74))
├── receiver: ∅

Просмотреть файл

@ -18,15 +18,11 @@
│ │ │ ├── statements:
│ │ │ │ @ StatementsNode (location: (8...11))
│ │ │ │ └── body: (length: 1)
│ │ │ │ └── @ InterpolatedStringNode (location: (8...11))
│ │ │ │ └── @ StringNode (location: (8...11))
│ │ │ │ ├── opening_loc: (8...11) = "<<A"
│ │ │ │ ├── parts: (length: 1)
│ │ │ │ │ └── @ StringNode (location: (15...17))
│ │ │ │ │ ├── opening_loc: ∅
│ │ │ │ │ ├── content_loc: (15...17) = "2\n"
│ │ │ │ │ ├── closing_loc: ∅
│ │ │ │ │ └── unescaped: "2\n"
│ │ │ │ └── closing_loc: (17...19) = "A\n"
│ │ │ │ ├── content_loc: (15...17) = "2\n"
│ │ │ │ ├── closing_loc: (17...19) = "A\n"
│ │ │ │ └── unescaped: "2\n"
│ │ │ └── closing_loc: (11...12) = "}"
│ │ └── closing_loc: ∅
│ ├── @ StringNode (location: (13...14))

Просмотреть файл

@ -3,12 +3,8 @@
└── statements:
@ StatementsNode (location: (0...8))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...8))
└── @ StringNode (location: (0...8))
├── opening_loc: (0...8) = "<<-'EOS'"
├── parts: (length: 1)
│ └── @ StringNode (location: (9...44))
│ ├── opening_loc: ∅
│ ├── content_loc: (9...44) = " cd L:\\Work\\MG3710IQPro\\Develop\n"
│ ├── closing_loc: ∅
│ └── unescaped: " cd L:\\Work\\MG3710IQPro\\Develop\n"
└── closing_loc: (44...48) = "EOS\n"
├── content_loc: (9...44) = " cd L:\\Work\\MG3710IQPro\\Develop\n"
├── closing_loc: (44...48) = "EOS\n"
└── unescaped: " cd L:\\Work\\MG3710IQPro\\Develop\n"

Просмотреть файл

@ -13,15 +13,11 @@
│ │ └── arguments: (length: 1)
│ │ └── @ CallNode (location: (167...192))
│ │ ├── receiver:
│ │ │ @ InterpolatedStringNode (location: (167...171))
│ │ │ @ StringNode (location: (167...171))
│ │ │ ├── opening_loc: (167...171) = "<<-A"
│ │ │ ├── parts: (length: 1)
│ │ │ │ └── @ StringNode (location: (181...183))
│ │ │ │ ├── opening_loc: ∅
│ │ │ │ ├── content_loc: (181...183) = "a\n"
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ └── unescaped: "a\n"
│ │ │ └── closing_loc: (183...185) = "A\n"
│ │ │ ├── content_loc: (181...183) = "a\n"
│ │ │ ├── closing_loc: (183...185) = "A\n"
│ │ │ └── unescaped: "a\n"
│ │ ├── call_operator_loc: (171...172) = "."
│ │ ├── message_loc: (172...176) = "gsub"
│ │ ├── opening_loc: (176...177) = "("
@ -64,15 +60,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (279...295))
│ │ └── arguments: (length: 2)
│ │ ├── @ InterpolatedStringNode (location: (279...283))
│ │ ├── @ StringNode (location: (279...283))
│ │ │ ├── opening_loc: (279...283) = "<<-A"
│ │ │ ├── parts: (length: 1)
│ │ │ │ └── @ StringNode (location: (289...291))
│ │ │ │ ├── opening_loc: ∅
│ │ │ │ ├── content_loc: (289...291) = "c\n"
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ └── unescaped: "c\n"
│ │ │ └── closing_loc: (291...293) = "A\n"
│ │ │ ├── content_loc: (289...291) = "c\n"
│ │ │ ├── closing_loc: (291...293) = "A\n"
│ │ │ └── unescaped: "c\n"
│ │ └── @ InterpolatedStringNode (location: (285...295))
│ │ ├── opening_loc: (285...286) = "\""
│ │ ├── parts: (length: 2)
@ -99,15 +91,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (325...343))
│ │ └── arguments: (length: 2)
│ │ ├── @ InterpolatedStringNode (location: (325...329))
│ │ ├── @ StringNode (location: (325...329))
│ │ │ ├── opening_loc: (325...329) = "<<-A"
│ │ │ ├── parts: (length: 1)
│ │ │ │ └── @ StringNode (location: (337...339))
│ │ │ │ ├── opening_loc: ∅
│ │ │ │ ├── content_loc: (337...339) = "e\n"
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ └── unescaped: "e\n"
│ │ │ └── closing_loc: (339...341) = "A\n"
│ │ │ ├── content_loc: (337...339) = "e\n"
│ │ │ ├── closing_loc: (339...341) = "A\n"
│ │ │ └── unescaped: "e\n"
│ │ └── @ InterpolatedStringNode (location: (331...343))
│ │ ├── opening_loc: (331...334) = "%q["
│ │ ├── parts: (length: 2)
@ -134,15 +122,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (430...448))
│ │ └── arguments: (length: 2)
│ │ ├── @ InterpolatedStringNode (location: (430...434))
│ │ ├── @ StringNode (location: (430...434))
│ │ │ ├── opening_loc: (430...434) = "<<-A"
│ │ │ ├── parts: (length: 1)
│ │ │ │ └── @ StringNode (location: (442...444))
│ │ │ │ ├── opening_loc: ∅
│ │ │ │ ├── content_loc: (442...444) = "g\n"
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ └── unescaped: "g\n"
│ │ │ └── closing_loc: (444...446) = "A\n"
│ │ │ ├── content_loc: (442...444) = "g\n"
│ │ │ ├── closing_loc: (444...446) = "A\n"
│ │ │ └── unescaped: "g\n"
│ │ └── @ InterpolatedStringNode (location: (436...448))
│ │ ├── opening_loc: (436...439) = "%Q["
│ │ ├── parts: (length: 2)
@ -169,15 +153,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (523...541))
│ │ └── arguments: (length: 2)
│ │ ├── @ InterpolatedStringNode (location: (523...527))
│ │ ├── @ StringNode (location: (523...527))
│ │ │ ├── opening_loc: (523...527) = "<<-A"
│ │ │ ├── parts: (length: 1)
│ │ │ │ └── @ StringNode (location: (535...537))
│ │ │ │ ├── opening_loc: ∅
│ │ │ │ ├── content_loc: (535...537) = "i\n"
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ └── unescaped: "i\n"
│ │ │ └── closing_loc: (537...539) = "A\n"
│ │ │ ├── content_loc: (535...537) = "i\n"
│ │ │ ├── closing_loc: (537...539) = "A\n"
│ │ │ └── unescaped: "i\n"
│ │ └── @ ArrayNode (location: (529...541))
│ │ ├── elements: (length: 2)
│ │ │ ├── @ StringNode (location: (532...535))
@ -204,15 +184,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (691...709))
│ │ └── arguments: (length: 2)
│ │ ├── @ InterpolatedStringNode (location: (691...695))
│ │ ├── @ StringNode (location: (691...695))
│ │ │ ├── opening_loc: (691...695) = "<<-A"
│ │ │ ├── parts: (length: 1)
│ │ │ │ └── @ StringNode (location: (703...705))
│ │ │ │ ├── opening_loc: ∅
│ │ │ │ ├── content_loc: (703...705) = "k\n"
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ └── unescaped: "k\n"
│ │ │ └── closing_loc: (705...707) = "A\n"
│ │ │ ├── content_loc: (703...705) = "k\n"
│ │ │ ├── closing_loc: (705...707) = "A\n"
│ │ │ └── unescaped: "k\n"
│ │ └── @ ArrayNode (location: (697...709))
│ │ ├── elements: (length: 1)
│ │ │ └── @ InterpolatedStringNode (location: (700...708))
@ -243,15 +219,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (784...802))
│ │ └── arguments: (length: 2)
│ │ ├── @ InterpolatedStringNode (location: (784...788))
│ │ ├── @ StringNode (location: (784...788))
│ │ │ ├── opening_loc: (784...788) = "<<-A"
│ │ │ ├── parts: (length: 1)
│ │ │ │ └── @ StringNode (location: (796...798))
│ │ │ │ ├── opening_loc: ∅
│ │ │ │ ├── content_loc: (796...798) = "m\n"
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ └── unescaped: "m\n"
│ │ │ └── closing_loc: (798...800) = "A\n"
│ │ │ ├── content_loc: (796...798) = "m\n"
│ │ │ ├── closing_loc: (798...800) = "A\n"
│ │ │ └── unescaped: "m\n"
│ │ └── @ ArrayNode (location: (790...802))
│ │ ├── elements: (length: 2)
│ │ │ ├── @ SymbolNode (location: (793...796))
@ -278,15 +250,11 @@
├── arguments:
│ @ ArgumentsNode (location: (946...964))
│ └── arguments: (length: 2)
│ ├── @ InterpolatedStringNode (location: (946...950))
│ ├── @ StringNode (location: (946...950))
│ │ ├── opening_loc: (946...950) = "<<-A"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (958...960))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (958...960) = "o\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "o\n"
│ │ └── closing_loc: (960...962) = "A\n"
│ │ ├── content_loc: (958...960) = "o\n"
│ │ ├── closing_loc: (960...962) = "A\n"
│ │ └── unescaped: "o\n"
│ └── @ ArrayNode (location: (952...964))
│ ├── elements: (length: 1)
│ │ └── @ InterpolatedSymbolNode (location: (955...963))

Просмотреть файл

@ -3,24 +3,16 @@
└── statements:
@ StatementsNode (location: (0...372))
└── body: (length: 18)
├── @ InterpolatedStringNode (location: (0...6))
├── @ StringNode (location: (0...6))
│ ├── opening_loc: (0...6) = "<<~EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (7...11))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (7...11) = " a\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "a\n"
│ └── closing_loc: (11...15) = "EOF\n"
├── @ InterpolatedStringNode (location: (16...22))
│ ├── content_loc: (7...11) = " a\n"
│ ├── closing_loc: (11...15) = "EOF\n"
│ └── unescaped: "a\n"
├── @ StringNode (location: (16...22))
│ ├── opening_loc: (16...22) = "<<~EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (23...34))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (23...34) = "\ta\n b\n\t\tc\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "\ta\nb\n\t\tc\n"
│ └── closing_loc: (34...38) = "EOF\n"
│ ├── content_loc: (23...34) = "\ta\n b\n\t\tc\n"
│ ├── closing_loc: (34...38) = "EOF\n"
│ └── unescaped: "\ta\nb\n\t\tc\n"
├── @ InterpolatedStringNode (location: (39...45))
│ ├── opening_loc: (39...45) = "<<~EOF"
│ ├── parts: (length: 2)
@ -104,96 +96,56 @@
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "\n"
│ └── closing_loc: (121...125) = "EOF\n"
├── @ InterpolatedStringNode (location: (126...132))
├── @ StringNode (location: (126...132))
│ ├── opening_loc: (126...132) = "<<~EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (133...141))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (133...141) = " a\n b\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "a\nb\n"
│ └── closing_loc: (141...145) = "EOF\n"
├── @ InterpolatedStringNode (location: (146...152))
│ ├── content_loc: (133...141) = " a\n b\n"
│ ├── closing_loc: (141...145) = "EOF\n"
│ └── unescaped: "a\nb\n"
├── @ StringNode (location: (146...152))
│ ├── opening_loc: (146...152) = "<<~EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (153...162))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (153...162) = " a\n b\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "a\n b\n"
│ └── closing_loc: (162...166) = "EOF\n"
├── @ InterpolatedStringNode (location: (167...173))
│ ├── content_loc: (153...162) = " a\n b\n"
│ ├── closing_loc: (162...166) = "EOF\n"
│ └── unescaped: "a\n b\n"
├── @ StringNode (location: (167...173))
│ ├── opening_loc: (167...173) = "<<~EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (174...183))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (174...183) = "\t\t\ta\n\t\tb\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "\ta\nb\n"
│ └── closing_loc: (183...187) = "EOF\n"
├── @ InterpolatedStringNode (location: (188...196))
│ ├── content_loc: (174...183) = "\t\t\ta\n\t\tb\n"
│ ├── closing_loc: (183...187) = "EOF\n"
│ └── unescaped: "\ta\nb\n"
├── @ StringNode (location: (188...196))
│ ├── opening_loc: (188...196) = "<<~'EOF'"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (197...206))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (197...206) = " a \#{1}\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "a \#{1}\n"
│ └── closing_loc: (206...210) = "EOF\n"
├── @ InterpolatedStringNode (location: (211...217))
│ ├── content_loc: (197...206) = " a \#{1}\n"
│ ├── closing_loc: (206...210) = "EOF\n"
│ └── unescaped: "a \#{1}\n"
├── @ StringNode (location: (211...217))
│ ├── opening_loc: (211...217) = "<<~EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (218...225))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (218...225) = "\ta\n\t b\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "a\n b\n"
│ └── closing_loc: (225...229) = "EOF\n"
├── @ InterpolatedStringNode (location: (230...236))
│ ├── content_loc: (218...225) = "\ta\n\t b\n"
│ ├── closing_loc: (225...229) = "EOF\n"
│ └── unescaped: "a\n b\n"
├── @ StringNode (location: (230...236))
│ ├── opening_loc: (230...236) = "<<~EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (237...244))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (237...244) = "\t a\n\tb\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: " a\nb\n"
│ └── closing_loc: (244...248) = "EOF\n"
├── @ InterpolatedStringNode (location: (249...255))
│ ├── content_loc: (237...244) = "\t a\n\tb\n"
│ ├── closing_loc: (244...248) = "EOF\n"
│ └── unescaped: " a\nb\n"
├── @ StringNode (location: (249...255))
│ ├── opening_loc: (249...255) = "<<~EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (256...271))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (256...271) = " \ta\n b\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "a\nb\n"
│ └── closing_loc: (271...275) = "EOF\n"
├── @ InterpolatedStringNode (location: (276...282))
│ ├── content_loc: (256...271) = " \ta\n b\n"
│ ├── closing_loc: (271...275) = "EOF\n"
│ └── unescaped: "a\nb\n"
├── @ StringNode (location: (276...282))
│ ├── opening_loc: (276...282) = "<<~EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (283...292))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (283...292) = " a\n\n b\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "a\n\nb\n"
│ └── closing_loc: (292...296) = "EOF\n"
├── @ InterpolatedStringNode (location: (297...303))
│ ├── content_loc: (283...292) = " a\n\n b\n"
│ ├── closing_loc: (292...296) = "EOF\n"
│ └── unescaped: "a\n\nb\n"
├── @ StringNode (location: (297...303))
│ ├── opening_loc: (297...303) = "<<~EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (304...313))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (304...313) = " a\n\n b\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "a\n\nb\n"
│ └── closing_loc: (313...317) = "EOF\n"
├── @ InterpolatedStringNode (location: (318...324))
│ ├── content_loc: (304...313) = " a\n\n b\n"
│ ├── closing_loc: (313...317) = "EOF\n"
│ └── unescaped: "a\n\nb\n"
├── @ StringNode (location: (318...324))
│ ├── opening_loc: (318...324) = "<<~EOF"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (325...336))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (325...336) = " a\n\n\n\n b\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "a\n\n\n\nb\n"
│ └── closing_loc: (336...340) = "EOF\n"
│ ├── content_loc: (325...336) = " a\n\n\n\n b\n"
│ ├── closing_loc: (336...340) = "EOF\n"
│ └── unescaped: "a\n\n\n\nb\n"
├── @ InterpolatedStringNode (location: (341...347))
│ ├── opening_loc: (341...347) = "<<~EOF"
│ ├── parts: (length: 3)

Просмотреть файл

@ -23,12 +23,8 @@
│ ├── content_loc: (23...29) = "\\c\#{1}"
│ ├── closing_loc: (29...30) = "\""
│ └── unescaped: "\u0003{1}"
└── @ InterpolatedStringNode (location: (32...39))
└── @ StringNode (location: (32...39))
├── opening_loc: (32...39) = "<<~HERE"
├── parts: (length: 1)
│ └── @ StringNode (location: (40...50))
│ ├── opening_loc: ∅
│ ├── content_loc: (40...50) = " \\c\#{1}\n"
│ ├── closing_loc: ∅
│ └── unescaped: "\u0003{1}\n"
└── closing_loc: (50...55) = "HERE\n"
├── content_loc: (40...50) = " \\c\#{1}\n"
├── closing_loc: (50...55) = "HERE\n"
└── unescaped: "\u0003{1}\n"

Просмотреть файл

@ -113,15 +113,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (86...92))
│ │ └── arguments: (length: 1)
│ │ └── @ InterpolatedStringNode (location: (86...92))
│ │ └── @ StringNode (location: (86...92))
│ │ ├── opening_loc: (86...92) = "<<-DOC"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (101...105))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (101...105) = " b\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: " b\n"
│ │ └── closing_loc: (105...109) = "DOC\n"
│ │ ├── content_loc: (101...105) = " b\n"
│ │ ├── closing_loc: (105...109) = "DOC\n"
│ │ └── unescaped: " b\n"
│ ├── closing_loc: (92...93) = ")"
│ ├── block:
│ │ @ BlockNode (location: (94...116))
@ -160,15 +156,11 @@
├── arguments:
│ @ ArgumentsNode (location: (122...128))
│ └── arguments: (length: 1)
│ └── @ InterpolatedStringNode (location: (122...128))
│ └── @ StringNode (location: (122...128))
│ ├── opening_loc: (122...128) = "<<-DOC"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (133...137))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (133...137) = " b\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: " b\n"
│ └── closing_loc: (137...141) = "DOC\n"
│ ├── content_loc: (133...137) = " b\n"
│ ├── closing_loc: (137...141) = "DOC\n"
│ └── unescaped: " b\n"
├── closing_loc: (128...129) = ")"
├── block:
│ @ BlockNode (location: (130...148))

Просмотреть файл

@ -3,40 +3,36 @@
└── statements:
@ StatementsNode (location: (0...608))
└── body: (length: 33)
├── @ InterpolatedStringNode (location: (0...5))
├── @ StringNode (location: (0...5))
│ ├── opening_loc: (0...5) = "<<DOC"
│ ├── parts: (length: 0)
│ └── closing_loc: (6...10) = "DOC\n"
├── @ InterpolatedStringNode (location: (11...18))
│ ├── content_loc: (5...6) = "\n"
│ ├── closing_loc: (6...10) = "DOC\n"
│ └── unescaped: ""
├── @ StringNode (location: (11...18))
│ ├── opening_loc: (11...18) = "<<'DOC'"
│ ├── parts: (length: 0)
│ └── closing_loc: (19...23) = "DOC\n"
├── @ InterpolatedStringNode (location: (24...30))
│ ├── content_loc: (18...19) = "\n"
│ ├── closing_loc: (19...23) = "DOC\n"
│ └── unescaped: ""
├── @ StringNode (location: (24...30))
│ ├── opening_loc: (24...30) = "<<~DOC"
│ ├── parts: (length: 0)
│ └── closing_loc: (31...35) = "DOC\n"
├── @ InterpolatedStringNode (location: (36...44))
│ ├── content_loc: (30...31) = "\n"
│ ├── closing_loc: (31...35) = "DOC\n"
│ └── unescaped: ""
├── @ StringNode (location: (36...44))
│ ├── opening_loc: (36...44) = "<<~'DOC'"
│ ├── parts: (length: 0)
│ └── closing_loc: (45...49) = "DOC\n"
├── @ InterpolatedStringNode (location: (50...55))
│ ├── content_loc: (44...45) = "\n"
│ ├── closing_loc: (45...49) = "DOC\n"
│ └── unescaped: ""
├── @ StringNode (location: (50...55))
│ ├── opening_loc: (50...55) = "<<DOC"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (56...60))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (56...60) = " a\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: " a\n"
│ └── closing_loc: (60...64) = "DOC\n"
├── @ InterpolatedStringNode (location: (65...72))
│ ├── content_loc: (56...60) = " a\n"
│ ├── closing_loc: (60...64) = "DOC\n"
│ └── unescaped: " a\n"
├── @ StringNode (location: (65...72))
│ ├── opening_loc: (65...72) = "<<'DOC'"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (73...77))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (73...77) = " a\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: " a\n"
│ └── closing_loc: (77...81) = "DOC\n"
│ ├── content_loc: (73...77) = " a\n"
│ ├── closing_loc: (77...81) = "DOC\n"
│ └── unescaped: " a\n"
├── @ InterpolatedStringNode (location: (82...87))
│ ├── opening_loc: (82...87) = "<<DOC"
│ ├── parts: (length: 3)
@ -91,42 +87,26 @@
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "\nb\n"
│ └── closing_loc: (146...150) = "DOC\n"
├── @ InterpolatedStringNode (location: (151...157))
├── @ StringNode (location: (151...157))
│ ├── opening_loc: (151...157) = "<<~DOC"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (158...168))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (158...168) = " a\n b\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "a\n b\n"
│ └── closing_loc: (168...172) = "DOC\n"
├── @ InterpolatedStringNode (location: (173...180))
│ ├── content_loc: (158...168) = " a\n b\n"
│ ├── closing_loc: (168...172) = "DOC\n"
│ └── unescaped: "a\n b\n"
├── @ StringNode (location: (173...180))
│ ├── opening_loc: (173...180) = "<<'DOC'"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (181...186))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (181...186) = "a\n\nb\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "a\n\nb\n"
│ └── closing_loc: (186...190) = "DOC\n"
├── @ InterpolatedStringNode (location: (191...198))
│ ├── content_loc: (181...186) = "a\n\nb\n"
│ ├── closing_loc: (186...190) = "DOC\n"
│ └── unescaped: "a\n\nb\n"
├── @ StringNode (location: (191...198))
│ ├── opening_loc: (191...198) = "<<'DOC'"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (199...206))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (199...206) = " a\n\n b\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: " a\n\n b\n"
│ └── closing_loc: (206...210) = "DOC\n"
├── @ InterpolatedStringNode (location: (211...218))
│ ├── content_loc: (199...206) = " a\n\n b\n"
│ ├── closing_loc: (206...210) = "DOC\n"
│ └── unescaped: " a\n\n b\n"
├── @ StringNode (location: (211...218))
│ ├── opening_loc: (211...218) = "<<'DOC'"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (219...225))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (219...225) = " a\\nb\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: " a\\nb\n"
│ └── closing_loc: (225...229) = "DOC\n"
│ ├── content_loc: (219...225) = " a\\nb\n"
│ ├── closing_loc: (225...229) = "DOC\n"
│ └── unescaped: " a\\nb\n"
├── @ InterpolatedStringNode (location: (230...235))
│ ├── opening_loc: (230...235) = "<<DOC"
│ ├── parts: (length: 4)
@ -276,15 +256,11 @@
│ ├── statements:
│ │ @ StatementsNode (location: (435...443))
│ │ └── body: (length: 1)
│ │ └── @ InterpolatedStringNode (location: (435...443))
│ │ └── @ StringNode (location: (435...443))
│ │ ├── opening_loc: (435...443) = "<<-'DOC'"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (444...455))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (444...455) = " a\n\n b\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: " a\n\n b\n"
│ │ └── closing_loc: (455...461) = " DOC\n"
│ │ ├── content_loc: (444...455) = " a\n\n b\n"
│ │ ├── closing_loc: (455...461) = " DOC\n"
│ │ └── unescaped: " a\n\n b\n"
│ ├── consequent: ∅
│ └── end_keyword_loc: (461...464) = "end"
├── @ InterpolatedStringNode (location: (466...472))

Просмотреть файл

@ -185,10 +185,11 @@
│ │ ├── arguments:
│ │ │ @ ArgumentsNode (location: (108...114))
│ │ │ └── arguments: (length: 1)
│ │ │ └── @ InterpolatedStringNode (location: (108...114))
│ │ │ └── @ StringNode (location: (108...114))
│ │ │ ├── opening_loc: (108...114) = "<<-FOO"
│ │ │ ├── parts: (length: 0)
│ │ │ └── closing_loc: (119...123) = "FOO\n"
│ │ │ ├── content_loc: (114...119) = ") do\n"
│ │ │ ├── closing_loc: (119...123) = "FOO\n"
│ │ │ └── unescaped: ""
│ │ ├── closing_loc: (114...115) = ")"
│ │ ├── block:
│ │ │ @ BlockNode (location: (116...130))

Просмотреть файл

@ -11,10 +11,11 @@
├── arguments:
│ @ ArgumentsNode (location: (2...10))
│ └── arguments: (length: 1)
│ └── @ InterpolatedStringNode (location: (2...10))
│ └── @ StringNode (location: (2...10))
│ ├── opening_loc: (2...10) = "<<-TABLE"
│ ├── parts: (length: 0)
│ └── closing_loc: (14...20) = "TABLE\n"
│ ├── content_loc: (10...14) = " do\n"
│ ├── closing_loc: (14...20) = "TABLE\n"
│ └── unescaped: ""
├── closing_loc: ∅
├── block:
│ @ BlockNode (location: (11...23))

Просмотреть файл

@ -90,15 +90,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (58...62))
│ │ └── arguments: (length: 1)
│ │ └── @ InterpolatedStringNode (location: (58...62))
│ │ └── @ StringNode (location: (58...62))
│ │ ├── opening_loc: (58...62) = "<<~E"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (63...76))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (63...76) = "\tx\n y\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "x\ny\n"
│ │ └── closing_loc: (76...78) = "E\n"
│ │ ├── content_loc: (63...76) = "\tx\n y\n"
│ │ ├── closing_loc: (76...78) = "E\n"
│ │ └── unescaped: "x\ny\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags:
@ -111,15 +107,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (81...85))
│ │ └── arguments: (length: 1)
│ │ └── @ InterpolatedStringNode (location: (81...85))
│ │ └── @ StringNode (location: (81...85))
│ │ ├── opening_loc: (81...85) = "<<~E"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (86...95))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (86...95) = "\tx\n y\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "\tx\ny\n"
│ │ └── closing_loc: (95...97) = "E\n"
│ │ ├── content_loc: (86...95) = "\tx\n y\n"
│ │ ├── closing_loc: (95...97) = "E\n"
│ │ └── unescaped: "\tx\ny\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags:
@ -132,15 +124,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (100...104))
│ │ └── arguments: (length: 1)
│ │ └── @ InterpolatedStringNode (location: (100...104))
│ │ └── @ StringNode (location: (100...104))
│ │ ├── opening_loc: (100...104) = "<<~E"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (105...122))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (105...122) = " \tx\n y\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "x\ny\n"
│ │ └── closing_loc: (122...124) = "E\n"
│ │ ├── content_loc: (105...122) = " \tx\n y\n"
│ │ ├── closing_loc: (122...124) = "E\n"
│ │ └── unescaped: "x\ny\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags:
@ -153,15 +141,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (127...131))
│ │ └── arguments: (length: 1)
│ │ └── @ InterpolatedStringNode (location: (127...131))
│ │ └── @ StringNode (location: (127...131))
│ │ ├── opening_loc: (127...131) = "<<~E"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (132...146))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (132...146) = " \tx\n\ty\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "\tx\ny\n"
│ │ └── closing_loc: (146...148) = "E\n"
│ │ ├── content_loc: (132...146) = " \tx\n\ty\n"
│ │ ├── closing_loc: (146...148) = "E\n"
│ │ └── unescaped: "\tx\ny\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags:
@ -174,15 +158,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (151...155))
│ │ └── arguments: (length: 1)
│ │ └── @ InterpolatedStringNode (location: (151...155))
│ │ └── @ StringNode (location: (151...155))
│ │ ├── opening_loc: (151...155) = "<<~E"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (156...168))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (156...168) = " x\n \\\ty\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: " x\n\ty\n"
│ │ └── closing_loc: (168...170) = "E\n"
│ │ ├── content_loc: (156...168) = " x\n \\\ty\n"
│ │ ├── closing_loc: (168...170) = "E\n"
│ │ └── unescaped: " x\n\ty\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags:
@ -195,15 +175,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (173...177))
│ │ └── arguments: (length: 1)
│ │ └── @ InterpolatedStringNode (location: (173...177))
│ │ └── @ StringNode (location: (173...177))
│ │ ├── opening_loc: (173...177) = "<<~E"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (178...191))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (178...191) = " x\n \\ y\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: " x\n y\n"
│ │ └── closing_loc: (191...193) = "E\n"
│ │ ├── content_loc: (178...191) = " x\n \\ y\n"
│ │ ├── closing_loc: (191...193) = "E\n"
│ │ └── unescaped: " x\n y\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags:
@ -216,10 +192,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (196...200))
│ │ └── arguments: (length: 1)
│ │ └── @ InterpolatedStringNode (location: (196...200))
│ │ └── @ StringNode (location: (196...200))
│ │ ├── opening_loc: (196...200) = "<<~E"
│ │ ├── parts: (length: 0)
│ │ └── closing_loc: (201...205) = " E\n"
│ │ ├── content_loc: (200...201) = "\n"
│ │ ├── closing_loc: (201...205) = " E\n"
│ │ └── unescaped: ""
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags:
@ -232,15 +209,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (208...212))
│ │ └── arguments: (length: 1)
│ │ └── @ InterpolatedStringNode (location: (208...212))
│ │ └── @ StringNode (location: (208...212))
│ │ ├── opening_loc: (208...212) = "<<~E"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (213...220))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (213...220) = " x\n\ny\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: " x\n\ny\n"
│ │ └── closing_loc: (220...222) = "E\n"
│ │ ├── content_loc: (213...220) = " x\n\ny\n"
│ │ ├── closing_loc: (220...222) = "E\n"
│ │ └── unescaped: " x\n\ny\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags:
@ -253,15 +226,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (225...229))
│ │ └── arguments: (length: 1)
│ │ └── @ InterpolatedStringNode (location: (225...229))
│ │ └── @ StringNode (location: (225...229))
│ │ ├── opening_loc: (225...229) = "<<~E"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (230...243))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (230...243) = " x\n \n y\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "x\n \ny\n"
│ │ └── closing_loc: (243...245) = "E\n"
│ │ ├── content_loc: (230...243) = " x\n \n y\n"
│ │ ├── closing_loc: (243...245) = "E\n"
│ │ └── unescaped: "x\n \ny\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags:
@ -274,15 +243,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (248...252))
│ │ └── arguments: (length: 1)
│ │ └── @ InterpolatedStringNode (location: (248...252))
│ │ └── @ StringNode (location: (248...252))
│ │ ├── opening_loc: (248...252) = "<<~E"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (253...263))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (253...263) = " x\n y\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "x\n y\n"
│ │ └── closing_loc: (263...265) = "E\n"
│ │ ├── content_loc: (253...263) = " x\n y\n"
│ │ ├── closing_loc: (263...265) = "E\n"
│ │ └── unescaped: "x\n y\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags:
@ -295,15 +260,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (268...272))
│ │ └── arguments: (length: 1)
│ │ └── @ InterpolatedStringNode (location: (268...272))
│ │ └── @ StringNode (location: (268...272))
│ │ ├── opening_loc: (268...272) = "<<~E"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (273...277))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (273...277) = " x\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "x\n"
│ │ └── closing_loc: (277...279) = "E\n"
│ │ ├── content_loc: (273...277) = " x\n"
│ │ ├── closing_loc: (277...279) = "E\n"
│ │ └── unescaped: "x\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags:
@ -316,15 +277,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (282...286))
│ │ └── arguments: (length: 1)
│ │ └── @ InterpolatedStringNode (location: (282...286))
│ │ └── @ StringNode (location: (282...286))
│ │ ├── opening_loc: (282...286) = "<<~E"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (287...292))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (287...292) = " ð\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "ð\n"
│ │ └── closing_loc: (292...294) = "E\n"
│ │ ├── content_loc: (287...292) = " ð\n"
│ │ ├── closing_loc: (292...294) = "E\n"
│ │ └── unescaped: "ð\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags:
@ -337,10 +294,11 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (297...301))
│ │ └── arguments: (length: 1)
│ │ └── @ InterpolatedStringNode (location: (297...301))
│ │ └── @ StringNode (location: (297...301))
│ │ ├── opening_loc: (297...301) = "<<~E"
│ │ ├── parts: (length: 0)
│ │ └── closing_loc: (302...304) = "E\n"
│ │ ├── content_loc: (301...302) = "\n"
│ │ ├── closing_loc: (302...304) = "E\n"
│ │ └── unescaped: ""
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags:

Просмотреть файл

@ -3,12 +3,8 @@
└── statements:
@ StatementsNode (location: (0...8))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...8))
└── @ StringNode (location: (0...8))
├── opening_loc: (0...8) = "<<~'FOO'"
├── parts: (length: 1)
│ └── @ StringNode (location: (9...23))
│ ├── opening_loc: ∅
│ ├── content_loc: (9...23) = " baz\\\\\n qux\n"
│ ├── closing_loc: ∅
│ └── unescaped: "baz\\\nqux\n"
└── closing_loc: (23...27) = "FOO\n"
├── content_loc: (9...23) = " baz\\\\\n qux\n"
├── closing_loc: (23...27) = "FOO\n"
└── unescaped: "baz\\\nqux\n"

Просмотреть файл

@ -3,12 +3,8 @@
└── statements:
@ StatementsNode (location: (0...8))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...8))
└── @ StringNode (location: (0...8))
├── opening_loc: (0...8) = "<<~'FOO'"
├── parts: (length: 1)
│ └── @ StringNode (location: (9...22))
│ ├── opening_loc: ∅
│ ├── content_loc: (9...22) = " baz\\\n qux\n"
│ ├── closing_loc: ∅
│ └── unescaped: "baz\\\nqux\n"
└── closing_loc: (22...26) = "FOO\n"
├── content_loc: (9...22) = " baz\\\n qux\n"
├── closing_loc: (22...26) = "FOO\n"
└── unescaped: "baz\\\nqux\n"

Просмотреть файл

@ -3,30 +3,18 @@
└── statements:
@ StatementsNode (location: (0...52))
└── body: (length: 3)
├── @ InterpolatedStringNode (location: (0...8))
├── @ StringNode (location: (0...8))
│ ├── opening_loc: (0...8) = "<<'HERE'"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (9...17))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (9...17) = "foo\nbar\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "foo\nbar\n"
│ └── closing_loc: (17...22) = "HERE\n"
├── @ InterpolatedStringNode (location: (23...29))
│ ├── content_loc: (9...17) = "foo\nbar\n"
│ ├── closing_loc: (17...22) = "HERE\n"
│ └── unescaped: "foo\nbar\n"
├── @ StringNode (location: (23...29))
│ ├── opening_loc: (23...29) = "<<HERE"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (30...38))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (30...38) = "foo\nbar\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "foo\nbar\n"
│ └── closing_loc: (38...43) = "HERE\n"
└── @ InterpolatedXStringNode (location: (44...52))
│ ├── content_loc: (30...38) = "foo\nbar\n"
│ ├── closing_loc: (38...43) = "HERE\n"
│ └── unescaped: "foo\nbar\n"
└── @ XStringNode (location: (44...52))
├── opening_loc: (44...52) = "<<`HERE`"
├── parts: (length: 1)
│ └── @ StringNode (location: (53...61))
│ ├── opening_loc: ∅
│ ├── content_loc: (53...61) = "foo\nbar\n"
│ ├── closing_loc: ∅
│ └── unescaped: "foo\nbar\n"
└── closing_loc: (61...66) = "HERE\n"
├── content_loc: (53...61) = "foo\nbar\n"
├── closing_loc: (61...66) = "HERE\n"
└── unescaped: "foo\nbar\n"

Просмотреть файл

@ -199,57 +199,33 @@
│ ├── content_loc: (346...350) = "\#@@1"
│ ├── closing_loc: (350...351) = "`"
│ └── unescaped: "\#@@1"
├── @ InterpolatedStringNode (location: (354...363))
├── @ StringNode (location: (354...363))
│ ├── opening_loc: (354...363) = "<<-\"HERE\""
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (364...368))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (364...368) = "\#@1\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "\#@1\n"
│ └── closing_loc: (368...373) = "HERE\n"
├── @ InterpolatedStringNode (location: (374...383))
│ ├── content_loc: (364...368) = "\#@1\n"
│ ├── closing_loc: (368...373) = "HERE\n"
│ └── unescaped: "\#@1\n"
├── @ StringNode (location: (374...383))
│ ├── opening_loc: (374...383) = "<<-\"HERE\""
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (384...389))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (384...389) = "\#@@1\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "\#@@1\n"
│ └── closing_loc: (389...394) = "HERE\n"
├── @ InterpolatedStringNode (location: (395...404))
│ ├── content_loc: (384...389) = "\#@@1\n"
│ ├── closing_loc: (389...394) = "HERE\n"
│ └── unescaped: "\#@@1\n"
├── @ StringNode (location: (395...404))
│ ├── opening_loc: (395...404) = "<<-'HERE'"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (405...409))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (405...409) = "\#@1\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "\#@1\n"
│ └── closing_loc: (409...414) = "HERE\n"
├── @ InterpolatedStringNode (location: (415...424))
│ ├── content_loc: (405...409) = "\#@1\n"
│ ├── closing_loc: (409...414) = "HERE\n"
│ └── unescaped: "\#@1\n"
├── @ StringNode (location: (415...424))
│ ├── opening_loc: (415...424) = "<<-'HERE'"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (425...430))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (425...430) = "\#@@1\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "\#@@1\n"
│ └── closing_loc: (430...435) = "HERE\n"
├── @ InterpolatedXStringNode (location: (436...445))
│ ├── content_loc: (425...430) = "\#@@1\n"
│ ├── closing_loc: (430...435) = "HERE\n"
│ └── unescaped: "\#@@1\n"
├── @ XStringNode (location: (436...445))
│ ├── opening_loc: (436...445) = "<<-`HERE`"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (446...450))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (446...450) = "\#@1\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "\#@1\n"
│ └── closing_loc: (450...455) = "HERE\n"
└── @ InterpolatedXStringNode (location: (456...465))
│ ├── content_loc: (446...450) = "\#@1\n"
│ ├── closing_loc: (450...455) = "HERE\n"
│ └── unescaped: "\#@1\n"
└── @ XStringNode (location: (456...465))
├── opening_loc: (456...465) = "<<-`HERE`"
├── parts: (length: 1)
│ └── @ StringNode (location: (466...471))
│ ├── opening_loc: ∅
│ ├── content_loc: (466...471) = "\#@@1\n"
│ ├── closing_loc: ∅
│ └── unescaped: "\#@@1\n"
└── closing_loc: (471...476) = "HERE\n"
├── content_loc: (466...471) = "\#@@1\n"
├── closing_loc: (471...476) = "HERE\n"
└── unescaped: "\#@@1\n"

Просмотреть файл

@ -3,12 +3,8 @@
└── statements:
@ StatementsNode (location: (0...6))
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (0...6))
└── @ StringNode (location: (0...6))
├── opening_loc: (0...6) = "<<~FOO"
├── parts: (length: 1)
│ └── @ StringNode (location: (7...20))
│ ├── opening_loc: ∅
│ ├── content_loc: (7...20) = " baz\\\n qux\n"
│ ├── closing_loc: ∅
│ └── unescaped: "bazqux\n"
└── closing_loc: (20...24) = "FOO\n"
├── content_loc: (7...20) = " baz\\\n qux\n"
├── closing_loc: (20...24) = "FOO\n"
└── unescaped: "bazqux\n"

Просмотреть файл

@ -96,33 +96,21 @@
│ ├── value_loc: (134...138) = "a\\\nb"
│ ├── closing_loc: (138...139) = "'"
│ └── unescaped: "ab"
├── @ InterpolatedStringNode (location: (141...150))
├── @ StringNode (location: (141...150))
│ ├── opening_loc: (141...150) = "<<-\"HERE\""
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (151...156))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (151...156) = "a\\\nb\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "ab\n"
│ └── closing_loc: (156...161) = "HERE\n"
├── @ InterpolatedStringNode (location: (162...171))
│ ├── content_loc: (151...156) = "a\\\nb\n"
│ ├── closing_loc: (156...161) = "HERE\n"
│ └── unescaped: "ab\n"
├── @ StringNode (location: (162...171))
│ ├── opening_loc: (162...171) = "<<-'HERE'"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (172...177))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (172...177) = "a\\\nb\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "a\\\nb\n"
│ └── closing_loc: (177...182) = "HERE\n"
├── @ InterpolatedXStringNode (location: (183...192))
│ ├── content_loc: (172...177) = "a\\\nb\n"
│ ├── closing_loc: (177...182) = "HERE\n"
│ └── unescaped: "a\\\nb\n"
├── @ XStringNode (location: (183...192))
│ ├── opening_loc: (183...192) = "<<-`HERE`"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (193...198))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (193...198) = "a\\\nb\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "ab\n"
│ └── closing_loc: (198...203) = "HERE\n"
│ ├── content_loc: (193...198) = "a\\\nb\n"
│ ├── closing_loc: (198...203) = "HERE\n"
│ └── unescaped: "ab\n"
└── @ XStringNode (location: (204...210))
├── opening_loc: (204...205) = "`"
├── content_loc: (205...209) = "a\\\nb"

Просмотреть файл

@ -11,15 +11,11 @@
├── arguments:
│ @ ArgumentsNode (location: (2...8))
│ └── arguments: (length: 1)
│ └── @ InterpolatedStringNode (location: (2...8))
│ └── @ StringNode (location: (2...8))
│ ├── opening_loc: (2...8) = "<<~\"E\""
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (9...19))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (9...19) = " x\\n y\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "x\n y\n"
│ └── closing_loc: (19...21) = "E\n"
│ ├── content_loc: (9...19) = " x\\n y\n"
│ ├── closing_loc: (19...21) = "E\n"
│ └── unescaped: "x\n y\n"
├── closing_loc: ∅
├── block: ∅
├── flags:

Просмотреть файл

@ -13,15 +13,11 @@
│ └── arguments: (length: 1)
│ └── @ StringConcatNode (location: (2...12))
│ ├── left:
│ │ @ InterpolatedStringNode (location: (2...6))
│ │ @ StringNode (location: (2...6))
│ │ ├── opening_loc: (2...6) = "<<~E"
│ │ ├── parts: (length: 1)
│ │ │ └── @ StringNode (location: (13...17))
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (13...17) = " x\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "x\n"
│ │ └── closing_loc: (17...19) = "E\n"
│ │ ├── content_loc: (13...17) = " x\n"
│ │ ├── closing_loc: (17...19) = "E\n"
│ │ └── unescaped: "x\n"
│ └── right:
│ @ StringNode (location: (7...12))
│ ├── opening_loc: (7...8) = "\""

Просмотреть файл

@ -3,21 +3,13 @@
└── statements:
@ StatementsNode (location: (0...33))
└── body: (length: 2)
├── @ InterpolatedStringNode (location: (0...4))
├── @ StringNode (location: (0...4))
│ ├── opening_loc: (0...4) = "<<-E"
│ ├── parts: (length: 1)
│ │ └── @ StringNode (location: (5...25))
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (5...25) = " 1 \\\n 2\n 3\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: " 1 2\n 3\n"
│ └── closing_loc: (25...27) = "E\n"
└── @ InterpolatedStringNode (location: (29...33))
│ ├── content_loc: (5...25) = " 1 \\\n 2\n 3\n"
│ ├── closing_loc: (25...27) = "E\n"
│ └── unescaped: " 1 2\n 3\n"
└── @ StringNode (location: (29...33))
├── opening_loc: (29...33) = "<<~E"
├── parts: (length: 1)
│ └── @ StringNode (location: (34...54))
│ ├── opening_loc: ∅
│ ├── content_loc: (34...54) = " 1 \\\n 2\n 3\n"
│ ├── closing_loc: ∅
│ └── unescaped: "1 2\n3\n"
└── closing_loc: (54...56) = "E\n"
├── content_loc: (34...54) = " 1 \\\n 2\n 3\n"
├── closing_loc: (54...56) = "E\n"
└── unescaped: "1 2\n3\n"

Просмотреть файл

@ -10037,6 +10037,53 @@ parse_method_definition_name(yp_parser_t *parser) {
}
}
static int
parse_heredoc_common_whitespace_for_single_node(yp_parser_t *parser, yp_node_t *node, int common_whitespace)
{
const yp_location_t *content_loc = &((yp_string_node_t *) node)->content_loc;
int cur_whitespace;
const uint8_t *cur_char = content_loc->start;
while (cur_char && cur_char < content_loc->end) {
// Any empty newlines aren't included in the minimum whitespace
// calculation.
size_t eol_length;
while ((eol_length = match_eol_at(parser, cur_char))) {
cur_char += eol_length;
}
if (cur_char == content_loc->end) break;
cur_whitespace = 0;
while (yp_char_is_inline_whitespace(*cur_char) && cur_char < content_loc->end) {
if (cur_char[0] == '\t') {
cur_whitespace = (cur_whitespace / YP_TAB_WHITESPACE_SIZE + 1) * YP_TAB_WHITESPACE_SIZE;
} else {
cur_whitespace++;
}
cur_char++;
}
// If we hit a newline, then we have encountered a line that
// contains only whitespace, and it shouldn't be considered in
// the calculation of common leading whitespace.
eol_length = match_eol_at(parser, cur_char);
if (eol_length) {
cur_char += eol_length;
continue;
}
if (cur_whitespace < common_whitespace || common_whitespace == -1) {
common_whitespace = cur_whitespace;
}
cur_char = next_newline(cur_char + 1, parser->end - (cur_char + 1));
if (cur_char) cur_char++;
}
return common_whitespace;
}
// Calculate the common leading whitespace for each line in a heredoc.
static int
parse_heredoc_common_whitespace(yp_parser_t *parser, yp_node_list_t *nodes) {
@ -10044,69 +10091,102 @@ parse_heredoc_common_whitespace(yp_parser_t *parser, yp_node_list_t *nodes) {
for (size_t index = 0; index < nodes->size; index++) {
yp_node_t *node = nodes->nodes[index];
if (!YP_NODE_TYPE_P(node, YP_STRING_NODE)) continue;
const yp_location_t *content_loc = &((yp_string_node_t *) node)->content_loc;
// If the previous node wasn't a string node, we don't want to trim
// whitespace. This could happen after an interpolated expression or
// variable.
if (index == 0 || YP_NODE_TYPE_P(nodes->nodes[index - 1], YP_STRING_NODE)) {
int cur_whitespace;
const uint8_t *cur_char = content_loc->start;
while (cur_char && cur_char < content_loc->end) {
// Any empty newlines aren't included in the minimum whitespace
// calculation.
size_t eol_length;
while ((eol_length = match_eol_at(parser, cur_char))) {
cur_char += eol_length;
}
if (cur_char == content_loc->end) break;
cur_whitespace = 0;
while (yp_char_is_inline_whitespace(*cur_char) && cur_char < content_loc->end) {
if (cur_char[0] == '\t') {
cur_whitespace = (cur_whitespace / YP_TAB_WHITESPACE_SIZE + 1) * YP_TAB_WHITESPACE_SIZE;
} else {
cur_whitespace++;
}
cur_char++;
}
// If we hit a newline, then we have encountered a line that
// contains only whitespace, and it shouldn't be considered in
// the calculation of common leading whitespace.
eol_length = match_eol_at(parser, cur_char);
if (eol_length) {
cur_char += eol_length;
continue;
}
if (cur_whitespace < common_whitespace || common_whitespace == -1) {
common_whitespace = cur_whitespace;
}
cur_char = next_newline(cur_char + 1, parser->end - (cur_char + 1));
if (cur_char) cur_char++;
}
common_whitespace = parse_heredoc_common_whitespace_for_single_node(parser, node, common_whitespace);
}
}
return common_whitespace;
}
static yp_string_t *
parse_heredoc_dedent_single_node(yp_parser_t *parser, yp_string_t *string, bool dedent_node, int common_whitespace, yp_heredoc_quote_t quote)
{
// Get a reference to the string struct that is being held by the string
// node. This is the value we're going to actually manipulate.
yp_string_ensure_owned(string);
// Now get the bounds of the existing string. We'll use this as a
// destination to move bytes into. We'll also use it for bounds checking
// since we don't require that these strings be null terminated.
size_t dest_length = yp_string_length(string);
uint8_t *source_start = (uint8_t *) string->source;
const uint8_t *source_cursor = source_start;
const uint8_t *source_end = source_cursor + dest_length;
// We're going to move bytes backward in the string when we get leading
// whitespace, so we'll maintain a pointer to the current position in the
// string that we're writing to.
uint8_t *dest_cursor = source_start;
while (source_cursor < source_end) {
// If we need to dedent the next element within the heredoc or the next
// line within the string node, then we'll do it here.
if (dedent_node) {
int trimmed_whitespace = 0;
// While we haven't reached the amount of common whitespace that we need
// to trim and we haven't reached the end of the string, we'll keep
// trimming whitespace. Trimming in this context means skipping over
// these bytes such that they aren't copied into the new string.
while ((source_cursor < source_end) && yp_char_is_inline_whitespace(*source_cursor) && trimmed_whitespace < common_whitespace) {
if (*source_cursor == '\t') {
trimmed_whitespace = (trimmed_whitespace / YP_TAB_WHITESPACE_SIZE + 1) * YP_TAB_WHITESPACE_SIZE;
if (trimmed_whitespace > common_whitespace) break;
} else {
trimmed_whitespace++;
}
source_cursor++;
dest_length--;
}
}
// At this point we have dedented all that we need to, so we need to find
// the next newline.
const uint8_t *breakpoint = next_newline(source_cursor, source_end - source_cursor);
if (breakpoint == NULL) {
// If there isn't another newline, then we can just move the rest of the
// string and break from the loop.
memmove(dest_cursor, source_cursor, (size_t) (source_end - source_cursor));
break;
}
// Otherwise, we need to move everything including the newline, and
// then set the dedent_node flag to true.
if (breakpoint < source_end) breakpoint++;
memmove(dest_cursor, source_cursor, (size_t) (breakpoint - source_cursor));
dest_cursor += (breakpoint - source_cursor);
source_cursor = breakpoint;
dedent_node = true;
}
// We only want to write this node into the list if it has any content.
string->length = dest_length;
if (dest_length != 0) {
yp_unescape_manipulate_string(parser, string, (quote == YP_HEREDOC_QUOTE_SINGLE) ? YP_UNESCAPE_MINIMAL : YP_UNESCAPE_ALL);
}
return string;
}
// Take a heredoc node that is indented by a ~ and trim the leading whitespace.
static void
parse_heredoc_dedent(yp_parser_t *parser, yp_node_t *node, yp_heredoc_quote_t quote) {
parse_heredoc_dedent(yp_parser_t *parser, yp_node_t *heredoc_node, yp_heredoc_quote_t quote)
{
yp_node_list_t *nodes;
if (quote == YP_HEREDOC_QUOTE_BACKTICK) {
nodes = &((yp_interpolated_x_string_node_t *) node)->parts;
nodes = &((yp_interpolated_x_string_node_t *) heredoc_node)->parts;
} else {
nodes = &((yp_interpolated_string_node_t *) node)->parts;
nodes = &((yp_interpolated_string_node_t *) heredoc_node)->parts;
}
// First, calculate how much common whitespace we need to trim. If there is
@ -10135,74 +10215,11 @@ parse_heredoc_dedent(yp_parser_t *parser, yp_node_t *node, yp_heredoc_quote_t qu
continue;
}
// Get a reference to the string struct that is being held by the string
// node. This is the value we're going to actual manipulate.
yp_string_t *string = &(((yp_string_node_t *) node)->unescaped);
yp_string_ensure_owned(string);
// Now get the bounds of the existing string. We'll use this as a
// destination to move bytes into. We'll also use it for bounds checking
// since we don't require that these strings be null terminated.
size_t dest_length = yp_string_length(string);
uint8_t *source_start = (uint8_t *) string->source;
const uint8_t *source_cursor = source_start;
const uint8_t *source_end = source_cursor + dest_length;
// We're going to move bytes backward in the string when we get leading
// whitespace, so we'll maintain a pointer to the current position in the
// string that we're writing to.
uint8_t *dest_cursor = source_start;
while (source_cursor < source_end) {
// If we need to dedent the next element within the heredoc or the next
// line within the string node, then we'll do it here.
if (dedent_next) {
int trimmed_whitespace = 0;
// While we haven't reached the amount of common whitespace that we need
// to trim and we haven't reached the end of the string, we'll keep
// trimming whitespace. Trimming in this context means skipping over
// these bytes such that they aren't copied into the new string.
while ((source_cursor < source_end) && yp_char_is_inline_whitespace(*source_cursor) && trimmed_whitespace < common_whitespace) {
if (*source_cursor == '\t') {
trimmed_whitespace = (trimmed_whitespace / YP_TAB_WHITESPACE_SIZE + 1) * YP_TAB_WHITESPACE_SIZE;
if (trimmed_whitespace > common_whitespace) break;
} else {
trimmed_whitespace++;
}
source_cursor++;
dest_length--;
}
}
// At this point we have dedented all that we need to, so we need to find
// the next newline.
const uint8_t *breakpoint = next_newline(source_cursor, source_end - source_cursor);
if (breakpoint == NULL) {
// If there isn't another newline, then we can just move the rest of the
// string and break from the loop.
memmove(dest_cursor, source_cursor, (size_t) (source_end - source_cursor));
break;
}
// Otherwise, we need to move everything including the newline, and
// then set the dedent_next flag to true.
if (breakpoint < source_end) breakpoint++;
memmove(dest_cursor, source_cursor, (size_t) (breakpoint - source_cursor));
dest_cursor += (breakpoint - source_cursor);
source_cursor = breakpoint;
dedent_next = true;
}
// We only want to write this node into the list if it has any content.
if (dest_length == 0) {
yp_string_node_t *string_node = ((yp_string_node_t *) node);
parse_heredoc_dedent_single_node(parser, &string_node->unescaped, dedent_next, common_whitespace, quote);
if (string_node->unescaped.length == 0) {
yp_node_destroy(parser, node);
} else {
string->length = dest_length;
yp_unescape_manipulate_string(parser, string, (quote == YP_HEREDOC_QUOTE_SINGLE) ? YP_UNESCAPE_MINIMAL : YP_UNESCAPE_ALL);
nodes->nodes[write_index++] = node;
}
@ -11246,42 +11263,94 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
yp_heredoc_indent_t indent = parser->lex_modes.current->as.heredoc.indent;
yp_node_t *node;
if (quote == YP_HEREDOC_QUOTE_BACKTICK) {
node = (yp_node_t *) yp_interpolated_xstring_node_create(parser, &parser->current, &parser->current);
} else {
node = (yp_node_t *) yp_interpolated_string_node_create(parser, &parser->current, NULL, &parser->current);
}
parser_lex(parser);
yp_node_t *part;
while (!match_any_type_p(parser, 2, YP_TOKEN_HEREDOC_END, YP_TOKEN_EOF)) {
if ((part = parse_string_part(parser)) == NULL) continue;
if (parser->current.type == YP_TOKEN_HEREDOC_END) {
if (quote == YP_HEREDOC_QUOTE_BACKTICK) {
yp_interpolated_xstring_node_append((yp_interpolated_x_string_node_t *) node, part);
node = (yp_node_t *) yp_xstring_node_create(
parser,
&parser->previous,
&((yp_token_t) { .type = YP_TOKEN_STRING_CONTENT, .start = parser->previous.end, .end = parser->current.start }),
&parser->current);
} else {
yp_interpolated_string_node_append((yp_interpolated_string_node_t *) node, part);
node = (yp_node_t *)yp_string_node_create(
parser,
&parser->previous,
&((yp_token_t) { .type = YP_TOKEN_STRING_CONTENT, .start = parser->previous.end, .end = parser->current.start }),
&parser->current);
}
node->location.end = parser->previous.end;
lex_state_set(parser, YP_LEX_STATE_END);
expect(parser, YP_TOKEN_HEREDOC_END, YP_ERR_HEREDOC_TERM);
return node;
}
yp_token_t opening_token = parser->previous;
yp_node_t *part = parse_string_part(parser);
if (part == NULL) {
// We couldn't parse anything, so return a missing node
return (yp_node_t *) yp_missing_node_create(parser, parser->previous.start, parser->previous.end);
}
if (YP_NODE_TYPE_P(part, YP_STRING_NODE) && match_any_type_p(parser, 2, YP_TOKEN_HEREDOC_END, YP_TOKEN_EOF)) {
// We only have a single string, so we can return it
yp_string_node_t *str_part = (yp_string_node_t *)part;
str_part->opening_loc = YP_LOCATION_TOKEN_VALUE(&opening_token);
str_part->closing_loc = YP_LOCATION_TOKEN_VALUE(&parser->current);
str_part->base.location = str_part->opening_loc;
if (quote == YP_HEREDOC_QUOTE_BACKTICK) {
part->type = YP_X_STRING_NODE;
}
lex_state_set(parser, YP_LEX_STATE_END);
expect(parser, YP_TOKEN_HEREDOC_END, YP_ERR_HEREDOC_TERM);
node = part;
if (indent == YP_HEREDOC_INDENT_TILDE) {
int common_whitespace = parse_heredoc_common_whitespace_for_single_node(parser, node, -1);
parse_heredoc_dedent_single_node(parser, &str_part->unescaped, true, common_whitespace, quote);
}
}
else {
// We have multiple parts, continue parsing them
yp_node_list_t parts = YP_EMPTY_NODE_LIST;
yp_node_list_append(&parts, part);
lex_state_set(parser, YP_LEX_STATE_END);
expect(parser, YP_TOKEN_HEREDOC_END, YP_ERR_HEREDOC_TERM);
while (!match_any_type_p(parser, 2, YP_TOKEN_HEREDOC_END, YP_TOKEN_EOF)) {
if ((part = parse_string_part(parser)) != NULL) {
yp_node_list_append(&parts, part);
}
}
if (quote == YP_HEREDOC_QUOTE_BACKTICK) {
assert(YP_NODE_TYPE_P(node, YP_INTERPOLATED_X_STRING_NODE));
yp_interpolated_xstring_node_closing_set(((yp_interpolated_x_string_node_t *) node), &parser->previous);
node->location = ((yp_interpolated_x_string_node_t *) node)->opening_loc;
} else {
assert(YP_NODE_TYPE_P(node, YP_INTERPOLATED_STRING_NODE));
yp_interpolated_string_node_closing_set((yp_interpolated_string_node_t *) node, &parser->previous);
node->location = ((yp_interpolated_string_node_t *) node)->opening_loc;
}
if (quote == YP_HEREDOC_QUOTE_BACKTICK) {
node = (yp_node_t *) yp_interpolated_xstring_node_create(parser, &opening_token, &opening_token);
((yp_interpolated_x_string_node_t *)node)->parts = parts;
} else {
node = (yp_node_t *) yp_interpolated_string_node_create(parser, &opening_token, NULL, &opening_token);
((yp_interpolated_string_node_t *)node)->parts = parts;
}
// If this is a heredoc that is indented with a ~, then we need to dedent
// each line by the common leading whitespace.
if (indent == YP_HEREDOC_INDENT_TILDE) {
parse_heredoc_dedent(parser, node, quote);
lex_state_set(parser, YP_LEX_STATE_END);
expect(parser, YP_TOKEN_HEREDOC_END, YP_ERR_HEREDOC_TERM);
if (quote == YP_HEREDOC_QUOTE_BACKTICK) {
assert(YP_NODE_TYPE_P(node, YP_INTERPOLATED_X_STRING_NODE));
yp_interpolated_xstring_node_closing_set(((yp_interpolated_x_string_node_t *) node), &parser->previous);
node->location = ((yp_interpolated_x_string_node_t *) node)->opening_loc;
} else {
assert(YP_NODE_TYPE_P(node, YP_INTERPOLATED_STRING_NODE));
yp_interpolated_string_node_closing_set((yp_interpolated_string_node_t *) node, &parser->previous);
node->location = ((yp_interpolated_string_node_t *) node)->opening_loc;
}
// If this is a heredoc that is indented with a ~, then we need to dedent
// each line by the common leading whitespace.
if (indent == YP_HEREDOC_INDENT_TILDE) {
parse_heredoc_dedent(parser, node, quote);
}
}
// If there's a string immediately following this heredoc, then it's a