summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--prism/prism.c187
-rw-r--r--test/prism/snapshots/dos_endings.txt20
-rw-r--r--test/prism/snapshots/heredocs_nested.txt14
-rw-r--r--test/prism/snapshots/heredocs_with_ignored_newlines.txt56
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_squiggly.txt26
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt4
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_squiggly_blank_lines.txt26
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_squiggly_interp.txt26
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_squiggly_tabs.txt20
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_squiggly_tabs_extra.txt20
-rw-r--r--test/prism/snapshots/seattlerb/heredoc_squiggly_visually_blank_lines.txt26
-rw-r--r--test/prism/snapshots/tilde_heredocs.txt262
-rw-r--r--test/prism/snapshots/unparser/corpus/semantic/dstr.txt42
-rw-r--r--test/prism/snapshots/whitequark/dedenting_heredoc.txt204
-rw-r--r--test/prism/snapshots/whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt20
-rw-r--r--test/prism/snapshots/whitequark/dedenting_non_interpolating_heredoc_line_continuation.txt20
-rw-r--r--test/prism/snapshots/whitequark/parser_bug_640.txt2
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_11989.txt2
-rw-r--r--test/prism/snapshots/whitequark/slash_newline_in_heredocs.txt20
-rw-r--r--test/prism/unescape_test.rb54
20 files changed, 735 insertions, 316 deletions
diff --git a/prism/prism.c b/prism/prism.c
index f9ce0aef70..a432d1a454 100644
--- a/prism/prism.c
+++ b/prism/prism.c
@@ -8582,12 +8582,17 @@ parser_lex(pm_parser_t *parser) {
}
}
- if (
- lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE &&
- (lex_mode->as.heredoc.common_whitespace > whitespace) &&
- peek_at(parser, start) != '\n'
- ) {
- lex_mode->as.heredoc.common_whitespace = whitespace;
+ if (lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE) {
+ if ((lex_mode->as.heredoc.common_whitespace > whitespace) && peek_at(parser, start) != '\n') {
+ lex_mode->as.heredoc.common_whitespace = whitespace;
+ }
+
+ parser->current.end = breakpoint + 1;
+
+ if (!was_escaped_newline) {
+ pm_token_buffer_flush(parser, &token_buffer);
+ LEX(PM_TOKEN_STRING_CONTENT);
+ }
}
// Otherwise we hit a newline and it wasn't followed by
@@ -8613,45 +8618,46 @@ parser_lex(pm_parser_t *parser) {
}
uint8_t peeked = peek(parser);
- switch (peeked) {
- case '\r':
- parser->current.end++;
- if (peek(parser) != '\n') {
- if (quote == PM_HEREDOC_QUOTE_SINGLE) {
+ if (quote == PM_HEREDOC_QUOTE_SINGLE) {
+ switch (peeked) {
+ case '\r':
+ parser->current.end++;
+ if (peek(parser) != '\n') {
pm_token_buffer_push(&token_buffer, '\\');
+ pm_token_buffer_push(&token_buffer, '\r');
+ break;
}
- pm_token_buffer_push(&token_buffer, '\r');
- break;
- }
- /* fallthrough */
- case '\n':
- // If this is a dedenting heredoc then we need
- // to leave the escaped newline in place so that
- // it can be removed later when we dedent the
- // heredoc.
- if (quote == PM_HEREDOC_QUOTE_SINGLE || lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE) {
+ /* fallthrough */
+ case '\n':
pm_token_buffer_push(&token_buffer, '\\');
pm_token_buffer_push(&token_buffer, '\n');
- }
-
- token_buffer.cursor = parser->current.end + 1;
- breakpoint = parser->current.end;
-
- if (quote != PM_HEREDOC_QUOTE_SINGLE) {
- was_escaped_newline = true;
- }
-
- continue;
- default:
- if (quote == PM_HEREDOC_QUOTE_SINGLE) {
+ token_buffer.cursor = parser->current.end + 1;
+ breakpoint = parser->current.end;
+ continue;
+ default:
+ parser->current.end++;
pm_token_buffer_push(&token_buffer, '\\');
pm_token_buffer_push(&token_buffer, peeked);
+ break;
+ }
+ } else {
+ switch (peeked) {
+ case '\r':
parser->current.end++;
- } else {
+ if (peek(parser) != '\n') {
+ pm_token_buffer_push(&token_buffer, '\r');
+ break;
+ }
+ /* fallthrough */
+ case '\n':
+ was_escaped_newline = true;
+ token_buffer.cursor = parser->current.end + 1;
+ breakpoint = parser->current.end;
+ continue;
+ default:
escape_read(parser, &token_buffer.buffer, PM_ESCAPE_FLAG_NONE);
- }
-
- break;
+ break;
+ }
}
token_buffer.cursor = parser->current.end;
@@ -11147,9 +11153,8 @@ parse_method_definition_name(pm_parser_t *parser) {
}
}
-static pm_string_t *
-parse_heredoc_dedent_single_node(pm_parser_t *parser, pm_string_t *string, bool dedent_node, size_t common_whitespace, pm_heredoc_quote_t quote)
-{
+static void
+parse_heredoc_dedent_string(pm_string_t *string, size_t common_whitespace) {
// 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.
pm_string_ensure_owned(string);
@@ -11158,80 +11163,37 @@ parse_heredoc_dedent_single_node(pm_parser_t *parser, pm_string_t *string, bool
// 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 = pm_string_length(string);
- uint8_t *source_start = (uint8_t *) string->source;
-
- const uint8_t *source_cursor = source_start;
+ const uint8_t *source_cursor = (uint8_t *) string->source;
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) {
- size_t 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) && pm_char_is_inline_whitespace(*source_cursor) && trimmed_whitespace < common_whitespace) {
- if (*source_cursor == '\t') {
- trimmed_whitespace = (trimmed_whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_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;
+ size_t 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) && pm_char_is_inline_whitespace(*source_cursor) && trimmed_whitespace < common_whitespace) {
+ if (*source_cursor == '\t') {
+ trimmed_whitespace = (trimmed_whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE;
+ if (trimmed_whitespace > common_whitespace) break;
+ } else {
+ trimmed_whitespace++;
}
- // 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;
+ source_cursor++;
+ dest_length--;
}
- // We only want to write this node into the list if it has any content.
+ memmove((uint8_t *) string->source, source_cursor, (size_t) (source_end - source_cursor));
string->length = dest_length;
-
- if (dest_length != 0) {
- pm_unescape_manipulate_string(parser, string, (quote == PM_HEREDOC_QUOTE_SINGLE) ? PM_UNESCAPE_MINIMAL : PM_UNESCAPE_ALL);
- }
- return string;
}
// Take a heredoc node that is indented by a ~ and trim the leading whitespace.
static void
-parse_heredoc_dedent(pm_parser_t *parser, pm_node_t *heredoc_node, pm_heredoc_quote_t quote, size_t common_whitespace)
-{
- pm_node_list_t *nodes;
-
- if (quote == PM_HEREDOC_QUOTE_BACKTICK) {
- nodes = &((pm_interpolated_x_string_node_t *) heredoc_node)->parts;
- } else {
- nodes = &((pm_interpolated_string_node_t *) heredoc_node)->parts;
- }
-
+parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_whitespace) {
// The next node should be dedented if it's the first node in the list or if
// if follows a string node.
bool dedent_next = true;
@@ -11254,7 +11216,10 @@ parse_heredoc_dedent(pm_parser_t *parser, pm_node_t *heredoc_node, pm_heredoc_qu
}
pm_string_node_t *string_node = ((pm_string_node_t *) node);
- parse_heredoc_dedent_single_node(parser, &string_node->unescaped, dedent_next, common_whitespace, quote);
+ if (dedent_next) {
+ parse_heredoc_dedent_string(&string_node->unescaped, common_whitespace);
+ }
+
if (string_node->unescaped.length == 0) {
pm_node_destroy(parser, node);
} else {
@@ -12574,12 +12539,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power) {
cast->base.type = PM_X_STRING_NODE;
}
- node = (pm_node_t *) cast;
-
- if (indent == PM_HEREDOC_INDENT_TILDE && (lex_mode->as.heredoc.common_whitespace != (size_t) -1) && (lex_mode->as.heredoc.common_whitespace != 0)) {
- parse_heredoc_dedent_single_node(parser, &cast->unescaped, true, lex_mode->as.heredoc.common_whitespace, quote);
+ size_t common_whitespace = lex_mode->as.heredoc.common_whitespace;
+ if (indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
+ parse_heredoc_dedent_string(&cast->unescaped, common_whitespace);
}
+ node = (pm_node_t *) cast;
lex_state_set(parser, PM_LEX_STATE_END);
expect1(parser, PM_TOKEN_HEREDOC_END, PM_ERR_HEREDOC_TERM);
} else {
@@ -12629,8 +12594,16 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power) {
// If this is a heredoc that is indented with a ~, then we need
// to dedent each line by the common leading whitespace.
- if (indent == PM_HEREDOC_INDENT_TILDE && (lex_mode->as.heredoc.common_whitespace != (size_t) -1) && (lex_mode->as.heredoc.common_whitespace != 0)) {
- parse_heredoc_dedent(parser, node, quote, lex_mode->as.heredoc.common_whitespace);
+ size_t common_whitespace = lex_mode->as.heredoc.common_whitespace;
+ if (indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
+ pm_node_list_t *nodes;
+ if (quote == PM_HEREDOC_QUOTE_BACKTICK) {
+ nodes = &((pm_interpolated_x_string_node_t *) node)->parts;
+ } else {
+ nodes = &((pm_interpolated_string_node_t *) node)->parts;
+ }
+
+ parse_heredoc_dedent(parser, nodes, common_whitespace);
}
}
}
diff --git a/test/prism/snapshots/dos_endings.txt b/test/prism/snapshots/dos_endings.txt
index d01c5a7ac9..7e553ed932 100644
--- a/test/prism/snapshots/dos_endings.txt
+++ b/test/prism/snapshots/dos_endings.txt
@@ -72,12 +72,22 @@
│ │ └── arguments: (length: 1)
│ │ └── @ CallNode (location: (17,8)-(17,19))
│ │ ├── receiver:
- │ │ │ @ StringNode (location: (17,8)-(17,14))
- │ │ │ ├── flags: ∅
+ │ │ │ @ InterpolatedStringNode (location: (17,8)-(17,14))
│ │ │ ├── opening_loc: (17,8)-(17,14) = "<<~EOF"
- │ │ │ ├── content_loc: (18,0)-(19,0) = "\r\n baz\r\n"
- │ │ │ ├── closing_loc: (20,0)-(20,0) = " EOF\r\n"
- │ │ │ └── unescaped: "\nbaz\r\n"
+ │ │ │ ├── parts: (length: 2)
+ │ │ │ │ ├── @ StringNode (location: (18,0)-(18,0))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── content_loc: (18,0)-(18,0) = "\r\n"
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── unescaped: "\n"
+ │ │ │ │ └── @ StringNode (location: (19,0)-(19,0))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (19,0)-(19,0) = " baz\r\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "baz\r\n"
+ │ │ │ └── closing_loc: (20,0)-(20,0) = " EOF\r\n"
│ │ ├── call_operator_loc: (17,14)-(17,15) = "."
│ │ ├── message_loc: (17,15)-(17,19) = "chop"
│ │ ├── opening_loc: ∅
diff --git a/test/prism/snapshots/heredocs_nested.txt b/test/prism/snapshots/heredocs_nested.txt
index 8680dd2346..0e76652072 100644
--- a/test/prism/snapshots/heredocs_nested.txt
+++ b/test/prism/snapshots/heredocs_nested.txt
@@ -5,7 +5,7 @@
└── body: (length: 1)
└── @ InterpolatedStringNode (location: (1,0)-(1,7))
├── opening_loc: (1,0)-(1,7) = "<<~RUBY"
- ├── parts: (length: 3)
+ ├── parts: (length: 4)
│ ├── @ StringNode (location: (2,0)-(2,0))
│ │ ├── flags: ∅
│ │ ├── opening_loc: ∅
@@ -24,10 +24,16 @@
│ │ │ ├── closing_loc: (6,0)-(6,0) = "RUBY\n"
│ │ │ └── unescaped: " hello\n"
│ │ └── closing_loc: (7,0)-(7,1) = "}"
- │ └── @ StringNode (location: (7,1)-(8,0))
+ │ ├── @ StringNode (location: (7,1)-(7,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (7,1)-(7,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── @ StringNode (location: (8,0)-(8,0))
│ ├── flags: ∅
│ ├── opening_loc: ∅
- │ ├── content_loc: (7,1)-(8,0) = "\npost\n"
+ │ ├── content_loc: (8,0)-(8,0) = "post\n"
│ ├── closing_loc: ∅
- │ └── unescaped: "\npost\n"
+ │ └── unescaped: "post\n"
└── closing_loc: (9,0)-(9,0) = "RUBY\n"
diff --git a/test/prism/snapshots/heredocs_with_ignored_newlines.txt b/test/prism/snapshots/heredocs_with_ignored_newlines.txt
index 280a01f879..361c8687aa 100644
--- a/test/prism/snapshots/heredocs_with_ignored_newlines.txt
+++ b/test/prism/snapshots/heredocs_with_ignored_newlines.txt
@@ -9,9 +9,55 @@
│ ├── content_loc: (2,0)-(1,0) = ""
│ ├── closing_loc: (2,0)-(2,0) = "HERE\n"
│ └── unescaped: ""
- └── @ StringNode (location: (4,0)-(4,8))
- ├── flags: ∅
+ └── @ InterpolatedStringNode (location: (4,0)-(4,8))
├── opening_loc: (4,0)-(4,8) = "<<~THERE"
- ├── content_loc: (5,0)-(13,0) = " way over\n <<HERE\n not here\n HERE\n\n <<~BUT\\\n but\n BUT\n there\n"
- ├── closing_loc: (14,0)-(14,0) = "THERE\n"
- └── unescaped: "way over\n<<HERE\n not here\nHERE\n\n<<~BUT but\nBUT\n there\n"
+ ├── parts: (length: 8)
+ │ ├── @ StringNode (location: (5,0)-(5,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (5,0)-(5,0) = " way over\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "way over\n"
+ │ ├── @ StringNode (location: (6,0)-(6,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (6,0)-(6,0) = " <<HERE\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "<<HERE\n"
+ │ ├── @ StringNode (location: (7,0)-(7,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (7,0)-(7,0) = " not here\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " not here\n"
+ │ ├── @ StringNode (location: (8,0)-(8,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (8,0)-(8,0) = " HERE\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "HERE\n"
+ │ ├── @ StringNode (location: (9,0)-(9,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (9,0)-(9,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ ├── @ StringNode (location: (10,0)-(11,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (10,0)-(11,0) = " <<~BUT\\\n but\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "<<~BUT but\n"
+ │ ├── @ StringNode (location: (12,0)-(12,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (12,0)-(12,0) = " BUT\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "BUT\n"
+ │ └── @ StringNode (location: (13,0)-(13,0))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (13,0)-(13,0) = " there\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: " there\n"
+ └── closing_loc: (14,0)-(14,0) = "THERE\n"
diff --git a/test/prism/snapshots/seattlerb/heredoc_squiggly.txt b/test/prism/snapshots/seattlerb/heredoc_squiggly.txt
index bc5273e9ac..207da6b038 100644
--- a/test/prism/snapshots/seattlerb/heredoc_squiggly.txt
+++ b/test/prism/snapshots/seattlerb/heredoc_squiggly.txt
@@ -8,10 +8,26 @@
├── depth: 0
├── name_loc: (1,0)-(1,1) = "a"
├── value:
- │ @ StringNode (location: (1,4)-(1,12))
- │ ├── flags: ∅
+ │ @ InterpolatedStringNode (location: (1,4)-(1,12))
│ ├── opening_loc: (1,4)-(1,12) = "<<~\"EOF\""
- │ ├── content_loc: (2,0)-(4,0) = " x\n y\n z\n"
- │ ├── closing_loc: (5,0)-(5,0) = " EOF\n"
- │ └── unescaped: "x\ny\nz\n"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (2,0)-(2,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (2,0)-(2,0) = " x\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "x\n"
+ │ │ ├── @ StringNode (location: (3,0)-(3,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (3,0)-(3,0) = " y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "y\n"
+ │ │ └── @ StringNode (location: (4,0)-(4,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (4,0)-(4,0) = " z\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "z\n"
+ │ └── closing_loc: (5,0)-(5,0) = " EOF\n"
└── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt b/test/prism/snapshots/seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt
index eeaa6e73d6..43703cbe04 100644
--- a/test/prism/snapshots/seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt
+++ b/test/prism/snapshots/seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt
@@ -21,10 +21,10 @@
│ │ │ @ InterpolatedStringNode (location: (1,8)-(1,14))
│ │ │ ├── opening_loc: (1,8)-(1,14) = "<<~EOF"
│ │ │ ├── parts: (length: 3)
- │ │ │ │ ├── @ StringNode (location: (2,0)-(3,4))
+ │ │ │ │ ├── @ StringNode (location: (2,0)-(2,0))
│ │ │ │ │ ├── flags: ∅
│ │ │ │ │ ├── opening_loc: ∅
- │ │ │ │ │ ├── content_loc: (2,0)-(3,4) = "\n "
+ │ │ │ │ │ ├── content_loc: (2,0)-(2,0) = "\n"
│ │ │ │ │ ├── closing_loc: ∅
│ │ │ │ │ └── unescaped: "\n"
│ │ │ │ ├── @ EmbeddedStatementsNode (location: (3,4)-(3,10))
diff --git a/test/prism/snapshots/seattlerb/heredoc_squiggly_blank_lines.txt b/test/prism/snapshots/seattlerb/heredoc_squiggly_blank_lines.txt
index 8adeee53bf..a9abc3f40a 100644
--- a/test/prism/snapshots/seattlerb/heredoc_squiggly_blank_lines.txt
+++ b/test/prism/snapshots/seattlerb/heredoc_squiggly_blank_lines.txt
@@ -8,10 +8,26 @@
├── depth: 0
├── name_loc: (1,0)-(1,1) = "a"
├── value:
- │ @ StringNode (location: (1,4)-(1,10))
- │ ├── flags: ∅
+ │ @ InterpolatedStringNode (location: (1,4)-(1,10))
│ ├── opening_loc: (1,4)-(1,10) = "<<~EOF"
- │ ├── content_loc: (2,0)-(4,0) = " x\n\n z\n"
- │ ├── closing_loc: (5,0)-(5,0) = "EOF\n"
- │ └── unescaped: "x\n\nz\n"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (2,0)-(2,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (2,0)-(2,0) = " x\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "x\n"
+ │ │ ├── @ StringNode (location: (3,0)-(3,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (3,0)-(3,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── @ StringNode (location: (4,0)-(4,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (4,0)-(4,0) = " z\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "z\n"
+ │ └── closing_loc: (5,0)-(5,0) = "EOF\n"
└── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/heredoc_squiggly_interp.txt b/test/prism/snapshots/seattlerb/heredoc_squiggly_interp.txt
index 620b29591c..122a6ad05f 100644
--- a/test/prism/snapshots/seattlerb/heredoc_squiggly_interp.txt
+++ b/test/prism/snapshots/seattlerb/heredoc_squiggly_interp.txt
@@ -10,13 +10,19 @@
├── value:
│ @ InterpolatedStringNode (location: (1,4)-(1,10))
│ ├── opening_loc: (1,4)-(1,10) = "<<~EOF"
- │ ├── parts: (length: 3)
- │ │ ├── @ StringNode (location: (2,0)-(3,3))
+ │ ├── parts: (length: 5)
+ │ │ ├── @ StringNode (location: (2,0)-(2,0))
│ │ │ ├── flags: ∅
│ │ │ ├── opening_loc: ∅
- │ │ │ ├── content_loc: (2,0)-(3,3) = " w\n x"
+ │ │ │ ├── content_loc: (2,0)-(2,0) = " w\n"
│ │ │ ├── closing_loc: ∅
- │ │ │ └── unescaped: " w\nx"
+ │ │ │ └── unescaped: " w\n"
+ │ │ ├── @ StringNode (location: (3,0)-(3,3))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (3,0)-(3,3) = " x"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "x"
│ │ ├── @ EmbeddedStatementsNode (location: (3,3)-(3,8))
│ │ │ ├── opening_loc: (3,3)-(3,5) = "\#{"
│ │ │ ├── statements:
@@ -25,11 +31,17 @@
│ │ │ │ └── @ IntegerNode (location: (3,5)-(3,7))
│ │ │ │ └── flags: decimal
│ │ │ └── closing_loc: (3,7)-(3,8) = "}"
- │ │ └── @ StringNode (location: (3,8)-(4,0))
+ │ │ ├── @ StringNode (location: (3,8)-(3,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (3,8)-(3,0) = " y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " y\n"
+ │ │ └── @ StringNode (location: (4,0)-(4,0))
│ │ ├── flags: ∅
│ │ ├── opening_loc: ∅
- │ │ ├── content_loc: (3,8)-(4,0) = " y\n z\n"
+ │ │ ├── content_loc: (4,0)-(4,0) = " z\n"
│ │ ├── closing_loc: ∅
- │ │ └── unescaped: " y\n z\n"
+ │ │ └── unescaped: " z\n"
│ └── closing_loc: (5,0)-(5,0) = " EOF\n"
└── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/heredoc_squiggly_tabs.txt b/test/prism/snapshots/seattlerb/heredoc_squiggly_tabs.txt
index f876ffa456..dc7ec36a9e 100644
--- a/test/prism/snapshots/seattlerb/heredoc_squiggly_tabs.txt
+++ b/test/prism/snapshots/seattlerb/heredoc_squiggly_tabs.txt
@@ -8,10 +8,20 @@
├── depth: 0
├── name_loc: (1,0)-(1,1) = "a"
├── value:
- │ @ StringNode (location: (1,4)-(1,12))
- │ ├── flags: ∅
+ │ @ InterpolatedStringNode (location: (1,4)-(1,12))
│ ├── opening_loc: (1,4)-(1,12) = "<<~\"EOF\""
- │ ├── content_loc: (2,0)-(3,0) = " blah blah\n\t blah blah\n"
- │ ├── closing_loc: (4,0)-(4,0) = " EOF\n"
- │ └── unescaped: "blah blah\n blah blah\n"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (2,0)-(2,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (2,0)-(2,0) = " blah blah\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "blah blah\n"
+ │ │ └── @ StringNode (location: (3,0)-(3,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (3,0)-(3,0) = "\t blah blah\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " blah blah\n"
+ │ └── closing_loc: (4,0)-(4,0) = " EOF\n"
└── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/heredoc_squiggly_tabs_extra.txt b/test/prism/snapshots/seattlerb/heredoc_squiggly_tabs_extra.txt
index 32a8b64ce4..9d0962371d 100644
--- a/test/prism/snapshots/seattlerb/heredoc_squiggly_tabs_extra.txt
+++ b/test/prism/snapshots/seattlerb/heredoc_squiggly_tabs_extra.txt
@@ -8,10 +8,20 @@
├── depth: 0
├── name_loc: (1,0)-(1,1) = "a"
├── value:
- │ @ StringNode (location: (1,4)-(1,12))
- │ ├── flags: ∅
+ │ @ InterpolatedStringNode (location: (1,4)-(1,12))
│ ├── opening_loc: (1,4)-(1,12) = "<<~\"EOF\""
- │ ├── content_loc: (2,0)-(3,0) = " blah blah\n \tblah blah\n"
- │ ├── closing_loc: (4,0)-(4,0) = " EOF\n"
- │ └── unescaped: "blah blah\n\tblah blah\n"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (2,0)-(2,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (2,0)-(2,0) = " blah blah\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "blah blah\n"
+ │ │ └── @ StringNode (location: (3,0)-(3,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (3,0)-(3,0) = " \tblah blah\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\tblah blah\n"
+ │ └── closing_loc: (4,0)-(4,0) = " EOF\n"
└── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/seattlerb/heredoc_squiggly_visually_blank_lines.txt b/test/prism/snapshots/seattlerb/heredoc_squiggly_visually_blank_lines.txt
index edf33da732..1d93f974eb 100644
--- a/test/prism/snapshots/seattlerb/heredoc_squiggly_visually_blank_lines.txt
+++ b/test/prism/snapshots/seattlerb/heredoc_squiggly_visually_blank_lines.txt
@@ -8,10 +8,26 @@
├── depth: 0
├── name_loc: (1,0)-(1,1) = "a"
├── value:
- │ @ StringNode (location: (1,4)-(1,10))
- │ ├── flags: ∅
+ │ @ InterpolatedStringNode (location: (1,4)-(1,10))
│ ├── opening_loc: (1,4)-(1,10) = "<<~EOF"
- │ ├── content_loc: (2,0)-(4,0) = " x\n \n z\n"
- │ ├── closing_loc: (5,0)-(5,0) = "EOF\n"
- │ └── unescaped: "x\n\nz\n"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (2,0)-(2,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (2,0)-(2,0) = " x\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "x\n"
+ │ │ ├── @ StringNode (location: (3,0)-(3,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (3,0)-(3,0) = " \n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── @ StringNode (location: (4,0)-(4,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (4,0)-(4,0) = " z\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "z\n"
+ │ └── closing_loc: (5,0)-(5,0) = "EOF\n"
└── operator_loc: (1,2)-(1,3) = "="
diff --git a/test/prism/snapshots/tilde_heredocs.txt b/test/prism/snapshots/tilde_heredocs.txt
index b0b060255c..3d3335776d 100644
--- a/test/prism/snapshots/tilde_heredocs.txt
+++ b/test/prism/snapshots/tilde_heredocs.txt
@@ -9,12 +9,28 @@
│ ├── content_loc: (2,0)-(2,0) = " a\n"
│ ├── closing_loc: (3,0)-(3,0) = "EOF\n"
│ └── unescaped: "a\n"
- ├── @ StringNode (location: (5,0)-(5,6))
- │ ├── flags: ∅
+ ├── @ InterpolatedStringNode (location: (5,0)-(5,6))
│ ├── opening_loc: (5,0)-(5,6) = "<<~EOF"
- │ ├── content_loc: (6,0)-(8,0) = "\ta\n b\n\t\tc\n"
- │ ├── closing_loc: (9,0)-(9,0) = "EOF\n"
- │ └── unescaped: "\ta\nb\n\t\tc\n"
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (6,0)-(6,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (6,0)-(6,0) = "\ta\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\ta\n"
+ │ │ ├── @ StringNode (location: (7,0)-(7,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (7,0)-(7,0) = " b\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "b\n"
+ │ │ └── @ StringNode (location: (8,0)-(8,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (8,0)-(8,0) = "\t\tc\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\t\tc\n"
+ │ └── closing_loc: (9,0)-(9,0) = "EOF\n"
├── @ InterpolatedStringNode (location: (11,0)-(11,6))
│ ├── opening_loc: (11,0)-(11,6) = "<<~EOF"
│ ├── parts: (length: 2)
@@ -60,10 +76,10 @@
├── @ InterpolatedStringNode (location: (19,0)-(19,6))
│ ├── opening_loc: (19,0)-(19,6) = "<<~EOF"
│ ├── parts: (length: 3)
- │ │ ├── @ StringNode (location: (20,0)-(21,1))
+ │ │ ├── @ StringNode (location: (20,0)-(20,0))
│ │ │ ├── flags: ∅
│ │ │ ├── opening_loc: ∅
- │ │ │ ├── content_loc: (20,0)-(21,1) = " a\n "
+ │ │ │ ├── content_loc: (20,0)-(20,0) = " a\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: " a\n"
│ │ ├── @ EmbeddedStatementsNode (location: (21,1)-(21,5))
@@ -84,10 +100,10 @@
├── @ InterpolatedStringNode (location: (24,0)-(24,6))
│ ├── opening_loc: (24,0)-(24,6) = "<<~EOF"
│ ├── parts: (length: 3)
- │ │ ├── @ StringNode (location: (25,0)-(26,2))
+ │ │ ├── @ StringNode (location: (25,0)-(25,0))
│ │ │ ├── flags: ∅
│ │ │ ├── opening_loc: ∅
- │ │ │ ├── content_loc: (25,0)-(26,2) = " a\n "
+ │ │ │ ├── content_loc: (25,0)-(25,0) = " a\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "a\n"
│ │ ├── @ EmbeddedStatementsNode (location: (26,2)-(26,6))
@@ -105,73 +121,193 @@
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "\n"
│ └── closing_loc: (27,0)-(27,0) = "EOF\n"
- ├── @ StringNode (location: (29,0)-(29,6))
- │ ├── flags: ∅
+ ├── @ InterpolatedStringNode (location: (29,0)-(29,6))
│ ├── opening_loc: (29,0)-(29,6) = "<<~EOF"
- │ ├── content_loc: (30,0)-(31,0) = " a\n b\n"
- │ ├── closing_loc: (32,0)-(32,0) = "EOF\n"
- │ └── unescaped: "a\nb\n"
- ├── @ StringNode (location: (34,0)-(34,6))
- │ ├── flags: ∅
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (30,0)-(30,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (30,0)-(30,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ └── @ StringNode (location: (31,0)-(31,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (31,0)-(31,0) = " b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n"
+ │ └── closing_loc: (32,0)-(32,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (34,0)-(34,6))
│ ├── opening_loc: (34,0)-(34,6) = "<<~EOF"
- │ ├── content_loc: (35,0)-(36,0) = " a\n b\n"
- │ ├── closing_loc: (37,0)-(37,0) = "EOF\n"
- │ └── unescaped: "a\n b\n"
- ├── @ StringNode (location: (39,0)-(39,6))
- │ ├── flags: ∅
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (35,0)-(35,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (35,0)-(35,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ └── @ StringNode (location: (36,0)-(36,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (36,0)-(36,0) = " b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " b\n"
+ │ └── closing_loc: (37,0)-(37,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (39,0)-(39,6))
│ ├── opening_loc: (39,0)-(39,6) = "<<~EOF"
- │ ├── content_loc: (40,0)-(41,0) = "\t\t\ta\n\t\tb\n"
- │ ├── closing_loc: (42,0)-(42,0) = "EOF\n"
- │ └── unescaped: "\ta\nb\n"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (40,0)-(40,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (40,0)-(40,0) = "\t\t\ta\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\ta\n"
+ │ │ └── @ StringNode (location: (41,0)-(41,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (41,0)-(41,0) = "\t\tb\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n"
+ │ └── closing_loc: (42,0)-(42,0) = "EOF\n"
├── @ StringNode (location: (44,0)-(44,8))
│ ├── flags: ∅
│ ├── opening_loc: (44,0)-(44,8) = "<<~'EOF'"
│ ├── content_loc: (45,0)-(45,0) = " a \#{1}\n"
│ ├── closing_loc: (46,0)-(46,0) = "EOF\n"
│ └── unescaped: "a \#{1}\n"
- ├── @ StringNode (location: (48,0)-(48,6))
- │ ├── flags: ∅
+ ├── @ InterpolatedStringNode (location: (48,0)-(48,6))
│ ├── opening_loc: (48,0)-(48,6) = "<<~EOF"
- │ ├── content_loc: (49,0)-(50,0) = "\ta\n\t b\n"
- │ ├── closing_loc: (51,0)-(51,0) = "EOF\n"
- │ └── unescaped: "a\n b\n"
- ├── @ StringNode (location: (53,0)-(53,6))
- │ ├── flags: ∅
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (49,0)-(49,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (49,0)-(49,0) = "\ta\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ └── @ StringNode (location: (50,0)-(50,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (50,0)-(50,0) = "\t b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " b\n"
+ │ └── closing_loc: (51,0)-(51,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (53,0)-(53,6))
│ ├── opening_loc: (53,0)-(53,6) = "<<~EOF"
- │ ├── content_loc: (54,0)-(55,0) = "\t a\n\tb\n"
- │ ├── closing_loc: (56,0)-(56,0) = "EOF\n"
- │ └── unescaped: " a\nb\n"
- ├── @ StringNode (location: (58,0)-(58,6))
- │ ├── flags: ∅
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (54,0)-(54,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (54,0)-(54,0) = "\t a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " a\n"
+ │ │ └── @ StringNode (location: (55,0)-(55,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (55,0)-(55,0) = "\tb\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n"
+ │ └── closing_loc: (56,0)-(56,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (58,0)-(58,6))
│ ├── opening_loc: (58,0)-(58,6) = "<<~EOF"
- │ ├── content_loc: (59,0)-(60,0) = " \ta\n b\n"
- │ ├── closing_loc: (61,0)-(61,0) = "EOF\n"
- │ └── unescaped: "a\nb\n"
- ├── @ StringNode (location: (63,0)-(63,6))
- │ ├── flags: ∅
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (59,0)-(59,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (59,0)-(59,0) = " \ta\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ └── @ StringNode (location: (60,0)-(60,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (60,0)-(60,0) = " b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n"
+ │ └── closing_loc: (61,0)-(61,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (63,0)-(63,6))
│ ├── opening_loc: (63,0)-(63,6) = "<<~EOF"
- │ ├── content_loc: (64,0)-(66,0) = " a\n\n b\n"
- │ ├── closing_loc: (67,0)-(67,0) = "EOF\n"
- │ └── unescaped: "a\n\nb\n"
- ├── @ StringNode (location: (69,0)-(69,6))
- │ ├── flags: ∅
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (64,0)-(64,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (64,0)-(64,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ ├── @ StringNode (location: (65,0)-(65,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (65,0)-(65,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── @ StringNode (location: (66,0)-(66,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (66,0)-(66,0) = " b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n"
+ │ └── closing_loc: (67,0)-(67,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (69,0)-(69,6))
│ ├── opening_loc: (69,0)-(69,6) = "<<~EOF"
- │ ├── content_loc: (70,0)-(72,0) = " a\n\n b\n"
- │ ├── closing_loc: (73,0)-(73,0) = "EOF\n"
- │ └── unescaped: "a\n\nb\n"
- ├── @ StringNode (location: (75,0)-(75,6))
- │ ├── flags: ∅
+ │ ├── parts: (length: 3)
+ │ │ ├── @ StringNode (location: (70,0)-(70,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (70,0)-(70,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ ├── @ StringNode (location: (71,0)-(71,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (71,0)-(71,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── @ StringNode (location: (72,0)-(72,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (72,0)-(72,0) = " b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n"
+ │ └── closing_loc: (73,0)-(73,0) = "EOF\n"
+ ├── @ InterpolatedStringNode (location: (75,0)-(75,6))
│ ├── opening_loc: (75,0)-(75,6) = "<<~EOF"
- │ ├── content_loc: (76,0)-(80,0) = " a\n\n\n\n b\n"
- │ ├── closing_loc: (81,0)-(81,0) = "EOF\n"
- │ └── unescaped: "a\n\n\n\nb\n"
+ │ ├── parts: (length: 5)
+ │ │ ├── @ StringNode (location: (76,0)-(76,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (76,0)-(76,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ ├── @ StringNode (location: (77,0)-(77,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (77,0)-(77,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ ├── @ StringNode (location: (78,0)-(78,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (78,0)-(78,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ ├── @ StringNode (location: (79,0)-(79,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (79,0)-(79,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── @ StringNode (location: (80,0)-(80,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (80,0)-(80,0) = " b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "b\n"
+ │ └── closing_loc: (81,0)-(81,0) = "EOF\n"
├── @ InterpolatedStringNode (location: (83,0)-(83,6))
│ ├── opening_loc: (83,0)-(83,6) = "<<~EOF"
│ ├── parts: (length: 3)
- │ │ ├── @ StringNode (location: (84,0)-(85,2))
+ │ │ ├── @ StringNode (location: (84,0)-(84,0))
│ │ │ ├── flags: ∅
│ │ │ ├── opening_loc: ∅
- │ │ │ ├── content_loc: (84,0)-(85,2) = "\n "
+ │ │ │ ├── content_loc: (84,0)-(84,0) = "\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "\n"
│ │ ├── @ EmbeddedStatementsNode (location: (85,2)-(85,6))
@@ -191,7 +327,7 @@
│ └── closing_loc: (86,0)-(86,0) = " EOF\n"
└── @ InterpolatedStringNode (location: (88,0)-(88,6))
├── opening_loc: (88,0)-(88,6) = "<<~EOT"
- ├── parts: (length: 2)
+ ├── parts: (length: 3)
│ ├── @ EmbeddedStatementsNode (location: (89,2)-(89,6))
│ │ ├── opening_loc: (89,2)-(89,4) = "\#{"
│ │ ├── statements:
@@ -200,10 +336,16 @@
│ │ │ └── @ IntegerNode (location: (89,4)-(89,5))
│ │ │ └── flags: decimal
│ │ └── closing_loc: (89,5)-(89,6) = "}"
- │ └── @ StringNode (location: (89,6)-(90,0))
+ │ ├── @ StringNode (location: (89,6)-(89,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (89,6)-(89,0) = "\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── @ StringNode (location: (90,0)-(90,0))
│ ├── flags: ∅
│ ├── opening_loc: ∅
- │ ├── content_loc: (89,6)-(90,0) = "\n\tb\n"
+ │ ├── content_loc: (90,0)-(90,0) = "\tb\n"
│ ├── closing_loc: ∅
- │ └── unescaped: "\n\tb\n"
+ │ └── unescaped: "\tb\n"
└── closing_loc: (91,0)-(91,0) = "EOT\n"
diff --git a/test/prism/snapshots/unparser/corpus/semantic/dstr.txt b/test/prism/snapshots/unparser/corpus/semantic/dstr.txt
index 126886f3e0..f52663c5a1 100644
--- a/test/prism/snapshots/unparser/corpus/semantic/dstr.txt
+++ b/test/prism/snapshots/unparser/corpus/semantic/dstr.txt
@@ -62,10 +62,10 @@
├── @ InterpolatedStringNode (location: (26,0)-(26,6))
│ ├── opening_loc: (26,0)-(26,6) = "<<~DOC"
│ ├── parts: (length: 3)
- │ │ ├── @ StringNode (location: (27,0)-(28,2))
+ │ │ ├── @ StringNode (location: (27,0)-(27,0))
│ │ │ ├── flags: ∅
│ │ │ ├── opening_loc: ∅
- │ │ │ ├── content_loc: (27,0)-(28,2) = " a\n "
+ │ │ │ ├── content_loc: (27,0)-(27,0) = " a\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "a\n"
│ │ ├── @ EmbeddedStatementsNode (location: (28,2)-(28,5))
@@ -81,30 +81,46 @@
│ └── closing_loc: (29,0)-(29,0) = "DOC\n"
├── @ InterpolatedStringNode (location: (31,0)-(31,6))
│ ├── opening_loc: (31,0)-(31,6) = "<<~DOC"
- │ ├── parts: (length: 3)
- │ │ ├── @ StringNode (location: (32,0)-(33,2))
+ │ ├── parts: (length: 4)
+ │ │ ├── @ StringNode (location: (32,0)-(32,0))
│ │ │ ├── flags: ∅
│ │ │ ├── opening_loc: ∅
- │ │ │ ├── content_loc: (32,0)-(33,2) = " a\n "
+ │ │ │ ├── content_loc: (32,0)-(32,0) = " a\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "a\n"
│ │ ├── @ EmbeddedStatementsNode (location: (33,2)-(33,5))
│ │ │ ├── opening_loc: (33,2)-(33,4) = "\#{"
│ │ │ ├── statements: ∅
│ │ │ └── closing_loc: (33,4)-(33,5) = "}"
- │ │ └── @ StringNode (location: (33,5)-(34,0))
+ │ │ ├── @ StringNode (location: (33,5)-(33,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (33,5)-(33,0) = "\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\n"
+ │ │ └── @ StringNode (location: (34,0)-(34,0))
│ │ ├── flags: ∅
│ │ ├── opening_loc: ∅
- │ │ ├── content_loc: (33,5)-(34,0) = "\n b\n"
+ │ │ ├── content_loc: (34,0)-(34,0) = " b\n"
│ │ ├── closing_loc: ∅
- │ │ └── unescaped: "\nb\n"
+ │ │ └── unescaped: "b\n"
│ └── closing_loc: (35,0)-(35,0) = "DOC\n"
- ├── @ StringNode (location: (37,0)-(37,6))
- │ ├── flags: ∅
+ ├── @ InterpolatedStringNode (location: (37,0)-(37,6))
│ ├── opening_loc: (37,0)-(37,6) = "<<~DOC"
- │ ├── content_loc: (38,0)-(39,0) = " a\n b\n"
- │ ├── closing_loc: (40,0)-(40,0) = "DOC\n"
- │ └── unescaped: "a\n b\n"
+ │ ├── parts: (length: 2)
+ │ │ ├── @ StringNode (location: (38,0)-(38,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (38,0)-(38,0) = " a\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "a\n"
+ │ │ └── @ StringNode (location: (39,0)-(39,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (39,0)-(39,0) = " b\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: " b\n"
+ │ └── closing_loc: (40,0)-(40,0) = "DOC\n"
├── @ StringNode (location: (42,0)-(42,7))
│ ├── flags: ∅
│ ├── opening_loc: (42,0)-(42,7) = "<<'DOC'"
diff --git a/test/prism/snapshots/whitequark/dedenting_heredoc.txt b/test/prism/snapshots/whitequark/dedenting_heredoc.txt
index 891eba567f..6155f22fd2 100644
--- a/test/prism/snapshots/whitequark/dedenting_heredoc.txt
+++ b/test/prism/snapshots/whitequark/dedenting_heredoc.txt
@@ -14,10 +14,10 @@
│ │ └── @ InterpolatedStringNode (location: (1,2)-(1,8))
│ │ ├── opening_loc: (1,2)-(1,8) = "<<~\"E\""
│ │ ├── parts: (length: 3)
- │ │ │ ├── @ StringNode (location: (2,0)-(3,2))
+ │ │ │ ├── @ StringNode (location: (2,0)-(2,0))
│ │ │ │ ├── flags: ∅
│ │ │ │ ├── opening_loc: ∅
- │ │ │ │ ├── content_loc: (2,0)-(3,2) = " x\n "
+ │ │ │ │ ├── content_loc: (2,0)-(2,0) = " x\n"
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ └── unescaped: " x\n"
│ │ │ ├── @ EmbeddedStatementsNode (location: (3,2)-(3,10))
@@ -54,10 +54,10 @@
│ │ └── @ InterpolatedStringNode (location: (6,2)-(6,8))
│ │ ├── opening_loc: (6,2)-(6,8) = "<<~\"E\""
│ │ ├── parts: (length: 3)
- │ │ │ ├── @ StringNode (location: (7,0)-(8,2))
+ │ │ │ ├── @ StringNode (location: (7,0)-(7,0))
│ │ │ │ ├── flags: ∅
│ │ │ │ ├── opening_loc: ∅
- │ │ │ │ ├── content_loc: (7,0)-(8,2) = " x\n "
+ │ │ │ │ ├── content_loc: (7,0)-(7,0) = " x\n"
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ └── unescaped: " x\n"
│ │ │ ├── @ EmbeddedStatementsNode (location: (8,2)-(8,8))
@@ -95,12 +95,22 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (11,2)-(11,6))
│ │ └── arguments: (length: 1)
- │ │ └── @ StringNode (location: (11,2)-(11,6))
- │ │ ├── flags: ∅
+ │ │ └── @ InterpolatedStringNode (location: (11,2)-(11,6))
│ │ ├── opening_loc: (11,2)-(11,6) = "<<~E"
- │ │ ├── content_loc: (12,0)-(13,0) = "\tx\n y\n"
- │ │ ├── closing_loc: (14,0)-(14,0) = "E\n"
- │ │ └── unescaped: "x\ny\n"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (12,0)-(12,0))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (12,0)-(12,0) = "\tx\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "x\n"
+ │ │ │ └── @ StringNode (location: (13,0)-(13,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (13,0)-(13,0) = " y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "y\n"
+ │ │ └── closing_loc: (14,0)-(14,0) = "E\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags: ∅
@@ -113,12 +123,22 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (16,2)-(16,6))
│ │ └── arguments: (length: 1)
- │ │ └── @ StringNode (location: (16,2)-(16,6))
- │ │ ├── flags: ∅
+ │ │ └── @ InterpolatedStringNode (location: (16,2)-(16,6))
│ │ ├── opening_loc: (16,2)-(16,6) = "<<~E"
- │ │ ├── content_loc: (17,0)-(18,0) = "\tx\n y\n"
- │ │ ├── closing_loc: (19,0)-(19,0) = "E\n"
- │ │ └── unescaped: "\tx\ny\n"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (17,0)-(17,0))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (17,0)-(17,0) = "\tx\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "\tx\n"
+ │ │ │ └── @ StringNode (location: (18,0)-(18,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (18,0)-(18,0) = " y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "y\n"
+ │ │ └── closing_loc: (19,0)-(19,0) = "E\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags: ∅
@@ -131,12 +151,22 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (21,2)-(21,6))
│ │ └── arguments: (length: 1)
- │ │ └── @ StringNode (location: (21,2)-(21,6))
- │ │ ├── flags: ∅
+ │ │ └── @ InterpolatedStringNode (location: (21,2)-(21,6))
│ │ ├── opening_loc: (21,2)-(21,6) = "<<~E"
- │ │ ├── content_loc: (22,0)-(23,0) = " \tx\n y\n"
- │ │ ├── closing_loc: (24,0)-(24,0) = "E\n"
- │ │ └── unescaped: "x\ny\n"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (22,0)-(22,0))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (22,0)-(22,0) = " \tx\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "x\n"
+ │ │ │ └── @ StringNode (location: (23,0)-(23,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (23,0)-(23,0) = " y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "y\n"
+ │ │ └── closing_loc: (24,0)-(24,0) = "E\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags: ∅
@@ -149,12 +179,22 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (26,2)-(26,6))
│ │ └── arguments: (length: 1)
- │ │ └── @ StringNode (location: (26,2)-(26,6))
- │ │ ├── flags: ∅
+ │ │ └── @ InterpolatedStringNode (location: (26,2)-(26,6))
│ │ ├── opening_loc: (26,2)-(26,6) = "<<~E"
- │ │ ├── content_loc: (27,0)-(28,0) = " \tx\n\ty\n"
- │ │ ├── closing_loc: (29,0)-(29,0) = "E\n"
- │ │ └── unescaped: "\tx\ny\n"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (27,0)-(27,0))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (27,0)-(27,0) = " \tx\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "\tx\n"
+ │ │ │ └── @ StringNode (location: (28,0)-(28,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (28,0)-(28,0) = "\ty\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "y\n"
+ │ │ └── closing_loc: (29,0)-(29,0) = "E\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags: ∅
@@ -167,12 +207,22 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (31,2)-(31,6))
│ │ └── arguments: (length: 1)
- │ │ └── @ StringNode (location: (31,2)-(31,6))
- │ │ ├── flags: ∅
+ │ │ └── @ InterpolatedStringNode (location: (31,2)-(31,6))
│ │ ├── opening_loc: (31,2)-(31,6) = "<<~E"
- │ │ ├── content_loc: (32,0)-(33,0) = " x\n \\\ty\n"
- │ │ ├── closing_loc: (34,0)-(34,0) = "E\n"
- │ │ └── unescaped: " x\n\ty\n"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (32,0)-(32,0))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (32,0)-(32,0) = " x\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " x\n"
+ │ │ │ └── @ StringNode (location: (33,0)-(33,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (33,0)-(33,0) = " \\\ty\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "\ty\n"
+ │ │ └── closing_loc: (34,0)-(34,0) = "E\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags: ∅
@@ -185,12 +235,22 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (36,2)-(36,6))
│ │ └── arguments: (length: 1)
- │ │ └── @ StringNode (location: (36,2)-(36,6))
- │ │ ├── flags: ∅
+ │ │ └── @ InterpolatedStringNode (location: (36,2)-(36,6))
│ │ ├── opening_loc: (36,2)-(36,6) = "<<~E"
- │ │ ├── content_loc: (37,0)-(38,0) = " x\n \\ y\n"
- │ │ ├── closing_loc: (39,0)-(39,0) = "E\n"
- │ │ └── unescaped: " x\n y\n"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (37,0)-(37,0))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (37,0)-(37,0) = " x\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " x\n"
+ │ │ │ └── @ StringNode (location: (38,0)-(38,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (38,0)-(38,0) = " \\ y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " y\n"
+ │ │ └── closing_loc: (39,0)-(39,0) = "E\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags: ∅
@@ -221,12 +281,28 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (44,2)-(44,6))
│ │ └── arguments: (length: 1)
- │ │ └── @ StringNode (location: (44,2)-(44,6))
- │ │ ├── flags: ∅
+ │ │ └── @ InterpolatedStringNode (location: (44,2)-(44,6))
│ │ ├── opening_loc: (44,2)-(44,6) = "<<~E"
- │ │ ├── content_loc: (45,0)-(47,0) = " x\n\ny\n"
- │ │ ├── closing_loc: (48,0)-(48,0) = "E\n"
- │ │ └── unescaped: " x\n\ny\n"
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ StringNode (location: (45,0)-(45,0))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (45,0)-(45,0) = " x\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " x\n"
+ │ │ │ ├── @ StringNode (location: (46,0)-(46,0))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (46,0)-(46,0) = "\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "\n"
+ │ │ │ └── @ StringNode (location: (47,0)-(47,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (47,0)-(47,0) = "y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "y\n"
+ │ │ └── closing_loc: (48,0)-(48,0) = "E\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags: ∅
@@ -239,12 +315,28 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (50,2)-(50,6))
│ │ └── arguments: (length: 1)
- │ │ └── @ StringNode (location: (50,2)-(50,6))
- │ │ ├── flags: ∅
+ │ │ └── @ InterpolatedStringNode (location: (50,2)-(50,6))
│ │ ├── opening_loc: (50,2)-(50,6) = "<<~E"
- │ │ ├── content_loc: (51,0)-(53,0) = " x\n \n y\n"
- │ │ ├── closing_loc: (54,0)-(54,0) = "E\n"
- │ │ └── unescaped: "x\n \ny\n"
+ │ │ ├── parts: (length: 3)
+ │ │ │ ├── @ StringNode (location: (51,0)-(51,0))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (51,0)-(51,0) = " x\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "x\n"
+ │ │ │ ├── @ StringNode (location: (52,0)-(52,0))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (52,0)-(52,0) = " \n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: " \n"
+ │ │ │ └── @ StringNode (location: (53,0)-(53,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (53,0)-(53,0) = " y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: "y\n"
+ │ │ └── closing_loc: (54,0)-(54,0) = "E\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags: ∅
@@ -257,12 +349,22 @@
│ ├── arguments:
│ │ @ ArgumentsNode (location: (56,2)-(56,6))
│ │ └── arguments: (length: 1)
- │ │ └── @ StringNode (location: (56,2)-(56,6))
- │ │ ├── flags: ∅
+ │ │ └── @ InterpolatedStringNode (location: (56,2)-(56,6))
│ │ ├── opening_loc: (56,2)-(56,6) = "<<~E"
- │ │ ├── content_loc: (57,0)-(58,0) = " x\n y\n"
- │ │ ├── closing_loc: (59,0)-(59,0) = "E\n"
- │ │ └── unescaped: "x\n y\n"
+ │ │ ├── parts: (length: 2)
+ │ │ │ ├── @ StringNode (location: (57,0)-(57,0))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── content_loc: (57,0)-(57,0) = " x\n"
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── unescaped: "x\n"
+ │ │ │ └── @ StringNode (location: (58,0)-(58,0))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── content_loc: (58,0)-(58,0) = " y\n"
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── unescaped: " y\n"
+ │ │ └── closing_loc: (59,0)-(59,0) = "E\n"
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags: ∅
@@ -332,10 +434,10 @@
│ └── @ InterpolatedXStringNode (location: (72,2)-(72,8))
│ ├── opening_loc: (72,2)-(72,8) = "<<~`E`"
│ ├── parts: (length: 3)
- │ │ ├── @ StringNode (location: (73,0)-(74,2))
+ │ │ ├── @ StringNode (location: (73,0)-(73,0))
│ │ │ ├── flags: ∅
│ │ │ ├── opening_loc: ∅
- │ │ │ ├── content_loc: (73,0)-(74,2) = " x\n "
+ │ │ │ ├── content_loc: (73,0)-(73,0) = " x\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: " x\n"
│ │ ├── @ EmbeddedStatementsNode (location: (74,2)-(74,8))
diff --git a/test/prism/snapshots/whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt b/test/prism/snapshots/whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt
index ac083f3edc..aa9b58063c 100644
--- a/test/prism/snapshots/whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt
+++ b/test/prism/snapshots/whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt
@@ -3,9 +3,19 @@
└── statements:
@ StatementsNode (location: (1,0)-(1,8))
└── body: (length: 1)
- └── @ StringNode (location: (1,0)-(1,8))
- ├── flags: ∅
+ └── @ InterpolatedStringNode (location: (1,0)-(1,8))
├── opening_loc: (1,0)-(1,8) = "<<~'FOO'"
- ├── content_loc: (2,0)-(3,0) = " baz\\\\\n qux\n"
- ├── closing_loc: (4,0)-(4,0) = "FOO\n"
- └── unescaped: "baz\\\nqux\n"
+ ├── parts: (length: 2)
+ │ ├── @ StringNode (location: (2,0)-(2,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (2,0)-(2,0) = " baz\\\\\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "baz\\\\\n"
+ │ └── @ StringNode (location: (3,0)-(3,0))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (3,0)-(3,0) = " qux\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "qux\n"
+ └── closing_loc: (4,0)-(4,0) = "FOO\n"
diff --git a/test/prism/snapshots/whitequark/dedenting_non_interpolating_heredoc_line_continuation.txt b/test/prism/snapshots/whitequark/dedenting_non_interpolating_heredoc_line_continuation.txt
index 6d123a3387..5f2502dac8 100644
--- a/test/prism/snapshots/whitequark/dedenting_non_interpolating_heredoc_line_continuation.txt
+++ b/test/prism/snapshots/whitequark/dedenting_non_interpolating_heredoc_line_continuation.txt
@@ -3,9 +3,19 @@
└── statements:
@ StatementsNode (location: (1,0)-(1,8))
└── body: (length: 1)
- └── @ StringNode (location: (1,0)-(1,8))
- ├── flags: ∅
+ └── @ InterpolatedStringNode (location: (1,0)-(1,8))
├── opening_loc: (1,0)-(1,8) = "<<~'FOO'"
- ├── content_loc: (2,0)-(3,0) = " baz\\\n qux\n"
- ├── closing_loc: (4,0)-(4,0) = "FOO\n"
- └── unescaped: "baz\\\nqux\n"
+ ├── parts: (length: 2)
+ │ ├── @ StringNode (location: (2,0)-(2,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (2,0)-(2,0) = " baz\\\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "baz\\\n"
+ │ └── @ StringNode (location: (3,0)-(3,0))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (3,0)-(3,0) = " qux\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "qux\n"
+ └── closing_loc: (4,0)-(4,0) = "FOO\n"
diff --git a/test/prism/snapshots/whitequark/parser_bug_640.txt b/test/prism/snapshots/whitequark/parser_bug_640.txt
index 3ddfb52d98..1363dabe3b 100644
--- a/test/prism/snapshots/whitequark/parser_bug_640.txt
+++ b/test/prism/snapshots/whitequark/parser_bug_640.txt
@@ -8,4 +8,4 @@
├── opening_loc: (1,0)-(1,6) = "<<~FOO"
├── content_loc: (2,0)-(3,0) = " baz\\\n qux\n"
├── closing_loc: (4,0)-(4,0) = "FOO\n"
- └── unescaped: "bazqux\n"
+ └── unescaped: "baz qux\n"
diff --git a/test/prism/snapshots/whitequark/ruby_bug_11989.txt b/test/prism/snapshots/whitequark/ruby_bug_11989.txt
index 27ec4058af..2d56025693 100644
--- a/test/prism/snapshots/whitequark/ruby_bug_11989.txt
+++ b/test/prism/snapshots/whitequark/ruby_bug_11989.txt
@@ -16,7 +16,7 @@
│ ├── opening_loc: (1,2)-(1,8) = "<<~\"E\""
│ ├── content_loc: (2,0)-(2,0) = " x\\n y\n"
│ ├── closing_loc: (3,0)-(3,0) = "E\n"
- │ └── unescaped: "x\n y\n"
+ │ └── unescaped: "x\n y\n"
├── closing_loc: ∅
├── block: ∅
├── flags: ∅
diff --git a/test/prism/snapshots/whitequark/slash_newline_in_heredocs.txt b/test/prism/snapshots/whitequark/slash_newline_in_heredocs.txt
index 2a63c8cb0b..f7a249946a 100644
--- a/test/prism/snapshots/whitequark/slash_newline_in_heredocs.txt
+++ b/test/prism/snapshots/whitequark/slash_newline_in_heredocs.txt
@@ -9,9 +9,19 @@
│ ├── content_loc: (2,0)-(4,0) = " 1 \\\n 2\n 3\n"
│ ├── closing_loc: (5,0)-(5,0) = "E\n"
│ └── unescaped: " 1 2\n 3\n"
- └── @ StringNode (location: (8,0)-(8,4))
- ├── flags: ∅
+ └── @ InterpolatedStringNode (location: (8,0)-(8,4))
├── opening_loc: (8,0)-(8,4) = "<<~E"
- ├── content_loc: (9,0)-(11,0) = " 1 \\\n 2\n 3\n"
- ├── closing_loc: (12,0)-(12,0) = "E\n"
- └── unescaped: "1 2\n3\n"
+ ├── parts: (length: 2)
+ │ ├── @ StringNode (location: (9,0)-(10,0))
+ │ │ ├── flags: ∅
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (9,0)-(10,0) = " 1 \\\n 2\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "1 2\n"
+ │ └── @ StringNode (location: (11,0)-(11,0))
+ │ ├── flags: ∅
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (11,0)-(11,0) = " 3\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "3\n"
+ └── closing_loc: (12,0)-(12,0) = "E\n"
diff --git a/test/prism/unescape_test.rb b/test/prism/unescape_test.rb
index de4d1bf1b9..2e7e9fcd3e 100644
--- a/test/prism/unescape_test.rb
+++ b/test/prism/unescape_test.rb
@@ -67,6 +67,20 @@ module Prism
def prism_result(escape) = prism(escape, &:unescaped)
end
+ class Heredoc < Base
+ def ruby_result(escape) = ruby(escape, &:itself)
+ def prism_result(escape)
+ prism(escape) do |node|
+ case node.type
+ when :interpolated_string_node, :interpolated_x_string_node
+ node.parts.flat_map(&:unescaped).join
+ else
+ node.unescaped
+ end
+ end
+ end
+ end
+
class RegExp < Base
def ruby_result(escape) = ruby(escape, &:source)
def prism_result(escape) = prism(escape, &:unescaped)
@@ -94,30 +108,30 @@ module Prism
escapes = [*ascii, *ascii8, *newlines, *octal, *hex2, *hex4, *hex6, *ctrls]
contexts = [
- [Context::String.new("?", ""), escapes],
- [Context::String.new("'", "'"), escapes],
- [Context::String.new("\"", "\""), escapes],
- [Context::String.new("%q[", "]"), escapes],
- [Context::String.new("%Q[", "]"), escapes],
- [Context::String.new("%[", "]"), escapes],
- [Context::String.new("`", "`"), escapes],
- [Context::String.new("%x[", "]"), escapes],
- [Context::String.new("<<H\n", "\nH"), escapes],
- [Context::String.new("<<'H'\n", "\nH"), escapes],
- [Context::String.new("<<\"H\"\n", "\nH"), escapes],
- [Context::String.new("<<`H`\n", "\nH"), escapes],
+ [Context::String.new("?", ""), escapes],
+ [Context::String.new("'", "'"), escapes],
+ [Context::String.new("\"", "\""), escapes],
+ [Context::String.new("%q[", "]"), escapes],
+ [Context::String.new("%Q[", "]"), escapes],
+ [Context::String.new("%[", "]"), escapes],
+ [Context::String.new("`", "`"), escapes],
+ [Context::String.new("%x[", "]"), escapes],
+ [Context::String.new("<<H\n", "\nH"), escapes],
+ [Context::String.new("<<'H'\n", "\nH"), escapes],
+ [Context::String.new("<<\"H\"\n", "\nH"), escapes],
+ [Context::String.new("<<`H`\n", "\nH"), escapes],
[Context::String.new("<<-H\n", "\nH"), escapes],
[Context::String.new("<<-'H'\n", "\nH"), escapes],
[Context::String.new("<<-\"H\"\n", "\nH"), escapes],
[Context::String.new("<<-`H`\n", "\nH"), escapes],
- # [Context::String.new("<<~H\n", "\nH"), escapes],
- # [Context::String.new("<<~'H'\n", "\nH"), escapes],
- # [Context::String.new("<<~\"H\"\n", "\nH"), escapes],
- # [Context::String.new("<<~`H`\n", "\nH"), escapes],
- [Context::List.new("%w[", "]"), escapes],
- [Context::List.new("%W[", "]"), escapes],
- [Context::List.new("%i[", "]"), escapes],
- [Context::List.new("%I[", "]"), escapes],
+ [Context::Heredoc.new("<<~H\n", "\nH"), escapes],
+ [Context::Heredoc.new("<<~'H'\n", "\nH"), escapes],
+ [Context::Heredoc.new("<<~\"H\"\n", "\nH"), escapes],
+ [Context::Heredoc.new("<<~`H`\n", "\nH"), escapes],
+ [Context::List.new("%w[", "]"), escapes],
+ [Context::List.new("%W[", "]"), escapes],
+ [Context::List.new("%i[", "]"), escapes],
+ [Context::List.new("%I[", "]"), escapes],
# [Context::Symbol.new("%s[", "]"), escapes],
# [Context::Symbol.new(":'", "'"), escapes],
# [Context::Symbol.new(":\"", "\""), escapes],