Skip to content

Commit

Permalink
[ruby/prism] Handle a non-interpolated dsym spanning a heredoc
Browse files Browse the repository at this point in the history
  • Loading branch information
kddnewton authored and matzbot committed Dec 11, 2023
1 parent 261e8f2 commit c65de63
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 44 deletions.
28 changes: 28 additions & 0 deletions prism/prism.c
Expand Up @@ -12543,6 +12543,34 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s
content = parser->current;
unescaped = parser->current_string;
parser_lex(parser);

// If we have two string contents in a row, then the content of this
// symbol is split because of heredoc contents. This looks like:
//
// <<A; :'a
// A
// b'
//
// In this case, the best way we have to represent this is as an
// interpolated string node, so that's what we'll do here.
if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
pm_node_list_t parts = { 0 };
pm_token_t bounds = not_provided(parser);

pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &unescaped);
pm_node_list_append(&parts, part);

part = (pm_node_t *) pm_string_node_create_unescaped(parser, &bounds, &parser->current, &bounds, &parser->current_string);
pm_node_list_append(&parts, part);

if (next_state != PM_LEX_STATE_NONE) {
lex_state_set(parser, next_state);
}

parser_lex(parser);
expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC);
return (pm_node_t *) pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous);
}
} else {
content = (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = parser->previous.end, .end = parser->previous.end };
pm_string_shared_init(&unescaped, content.start, content.end);
Expand Down
8 changes: 8 additions & 0 deletions test/prism/fixtures/spanning_heredoc.txt
Expand Up @@ -53,3 +53,11 @@ p]
<<A; /\
A
(?<a>)/ =~ ''

<<A; :'a
A
b'

<<A; :"a
A
b"
132 changes: 88 additions & 44 deletions test/prism/snapshots/spanning_heredoc.txt
@@ -1,8 +1,8 @@
@ ProgramNode (location: (4,0)-(55,13))
@ ProgramNode (location: (4,0)-(63,2))
├── locals: [:a]
└── statements:
@ StatementsNode (location: (4,0)-(55,13))
└── body: (length: 10)
@ StatementsNode (location: (4,0)-(63,2))
└── body: (length: 14)
├── @ CallNode (location: (4,0)-(7,7))
│ ├── flags: ∅
│ ├── receiver: ∅
Expand Down Expand Up @@ -321,45 +321,89 @@
│ ├── content_loc: (54,0)-(54,0) = ""
│ ├── closing_loc: (54,0)-(55,0) = "A\n"
│ └── unescaped: ""
└── @ MatchWriteNode (location: (53,5)-(55,13))
├── call:
│ @ CallNode (location: (53,5)-(55,13))
│ ├── flags: ∅
│ ├── receiver:
│ │ @ InterpolatedRegularExpressionNode (location: (53,5)-(55,7))
│ │ ├── flags: ∅
│ │ ├── opening_loc: (53,5)-(53,6) = "/"
│ │ ├── parts: (length: 2)
│ │ │ ├── @ StringNode (location: (53,6)-(53,7))
│ │ │ │ ├── flags: ∅
│ │ │ │ ├── opening_loc: ∅
│ │ │ │ ├── content_loc: (53,6)-(53,7) = "\\"
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ └── unescaped: ""
│ │ │ └── @ StringNode (location: (55,0)-(55,6))
│ │ │ ├── flags: ∅
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (55,0)-(55,6) = "(?<a>)"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "(?<a>)"
│ │ └── closing_loc: (55,6)-(55,7) = "/"
│ ├── call_operator_loc: ∅
│ ├── name: :=~
│ ├── message_loc: (55,8)-(55,10) = "=~"
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (55,11)-(55,13))
├── @ MatchWriteNode (location: (53,5)-(55,13))
│ ├── call:
│ │ @ CallNode (location: (53,5)-(55,13))
│ │ ├── flags: ∅
│ │ ├── receiver:
│ │ │ @ InterpolatedRegularExpressionNode (location: (53,5)-(55,7))
│ │ │ ├── flags: ∅
│ │ │ ├── opening_loc: (53,5)-(53,6) = "/"
│ │ │ ├── parts: (length: 2)
│ │ │ │ ├── @ StringNode (location: (53,6)-(53,7))
│ │ │ │ │ ├── flags: ∅
│ │ │ │ │ ├── opening_loc: ∅
│ │ │ │ │ ├── content_loc: (53,6)-(53,7) = "\\"
│ │ │ │ │ ├── closing_loc: ∅
│ │ │ │ │ └── unescaped: ""
│ │ │ │ └── @ StringNode (location: (55,0)-(55,6))
│ │ │ │ ├── flags: ∅
│ │ │ │ ├── opening_loc: ∅
│ │ │ │ ├── content_loc: (55,0)-(55,6) = "(?<a>)"
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ └── unescaped: "(?<a>)"
│ │ │ └── closing_loc: (55,6)-(55,7) = "/"
│ │ ├── call_operator_loc: ∅
│ │ ├── name: :=~
│ │ ├── message_loc: (55,8)-(55,10) = "=~"
│ │ ├── opening_loc: ∅
│ │ ├── arguments:
│ │ │ @ ArgumentsNode (location: (55,11)-(55,13))
│ │ │ ├── flags: ∅
│ │ │ └── arguments: (length: 1)
│ │ │ └── @ StringNode (location: (55,11)-(55,13))
│ │ │ ├── flags: ∅
│ │ │ ├── opening_loc: (55,11)-(55,12) = "'"
│ │ │ ├── content_loc: (55,12)-(55,12) = ""
│ │ │ ├── closing_loc: (55,12)-(55,13) = "'"
│ │ │ └── unescaped: ""
│ │ ├── closing_loc: ∅
│ │ └── block: ∅
│ └── targets: (length: 1)
│ └── @ LocalVariableTargetNode (location: (53,5)-(55,7))
│ ├── name: :a
│ └── depth: 0
├── @ StringNode (location: (57,0)-(57,3))
│ ├── flags: ∅
│ ├── opening_loc: (57,0)-(57,3) = "<<A"
│ ├── content_loc: (58,0)-(58,0) = ""
│ ├── closing_loc: (58,0)-(59,0) = "A\n"
│ └── unescaped: ""
├── @ InterpolatedSymbolNode (location: (57,5)-(59,2))
│ ├── opening_loc: (57,5)-(57,7) = ":'"
│ ├── parts: (length: 2)
│ │ ├── @ StringNode (location: (57,7)-(58,0))
│ │ │ ├── flags: ∅
│ │ │ ├── opening_loc: ∅
│ │ │ ├── content_loc: (57,7)-(58,0) = "a\n"
│ │ │ ├── closing_loc: ∅
│ │ │ └── unescaped: "a\n"
│ │ └── @ StringNode (location: (59,0)-(59,1))
│ │ ├── flags: ∅
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (59,0)-(59,1) = "b"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "b"
│ └── closing_loc: (59,1)-(59,2) = "'"
├── @ StringNode (location: (61,0)-(61,3))
│ ├── flags: ∅
│ ├── opening_loc: (61,0)-(61,3) = "<<A"
│ ├── content_loc: (62,0)-(62,0) = ""
│ ├── closing_loc: (62,0)-(63,0) = "A\n"
│ └── unescaped: ""
└── @ InterpolatedSymbolNode (location: (61,5)-(63,2))
├── opening_loc: (61,5)-(61,7) = ":\""
├── parts: (length: 2)
│ ├── @ StringNode (location: (61,7)-(62,0))
│ │ ├── flags: ∅
│ │ └── arguments: (length: 1)
│ │ └── @ StringNode (location: (55,11)-(55,13))
│ │ ├── flags: ∅
│ │ ├── opening_loc: (55,11)-(55,12) = "'"
│ │ ├── content_loc: (55,12)-(55,12) = ""
│ │ ├── closing_loc: (55,12)-(55,13) = "'"
│ │ └── unescaped: ""
│ ├── closing_loc: ∅
│ └── block: ∅
└── targets: (length: 1)
└── @ LocalVariableTargetNode (location: (53,5)-(55,7))
├── name: :a
└── depth: 0
│ │ ├── opening_loc: ∅
│ │ ├── content_loc: (61,7)-(62,0) = "a\n"
│ │ ├── closing_loc: ∅
│ │ └── unescaped: "a\n"
│ └── @ StringNode (location: (63,0)-(63,1))
│ ├── flags: ∅
│ ├── opening_loc: ∅
│ ├── content_loc: (63,0)-(63,1) = "b"
│ ├── closing_loc: ∅
│ └── unescaped: "b"
└── closing_loc: (63,1)-(63,2) = "\""

0 comments on commit c65de63

Please sign in to comment.