Skip to content

Commit 5617203

Browse files
committed
Fix up newline-delimited array literals
1 parent 43ffaad commit 5617203

5 files changed

Lines changed: 108 additions & 3 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
@ ProgramNode (location: (1,0)-(12,0))
2+
├── flags: ∅
3+
├── locals: []
4+
└── statements:
5+
@ StatementsNode (location: (1,0)-(12,0))
6+
├── flags: ∅
7+
└── body: (length: 4)
8+
├── @ ArrayNode (location: (1,0)-(3,0))
9+
│ ├── flags: newline
10+
│ ├── elements: (length: 2)
11+
│ │ ├── @ StringNode (location: (2,0)-(2,3))
12+
│ │ │ ├── flags: ∅
13+
│ │ │ ├── opening_loc: ∅
14+
│ │ │ ├── content_loc: (2,0)-(2,3) = "foo"
15+
│ │ │ ├── closing_loc: ∅
16+
│ │ │ └── unescaped: "foo"
17+
│ │ └── @ StringNode (location: (2,4)-(2,7))
18+
│ │ ├── flags: ∅
19+
│ │ ├── opening_loc: ∅
20+
│ │ ├── content_loc: (2,4)-(2,7) = "bar"
21+
│ │ ├── closing_loc: ∅
22+
│ │ └── unescaped: "bar"
23+
│ ├── opening_loc: (1,0)-(2,0) = "%w\n"
24+
│ └── closing_loc: (2,7)-(3,0) = "\n"
25+
├── @ ArrayNode (location: (4,0)-(6,0))
26+
│ ├── flags: newline, static_literal
27+
│ ├── elements: (length: 2)
28+
│ │ ├── @ SymbolNode (location: (5,0)-(5,3))
29+
│ │ │ ├── flags: static_literal, forced_us_ascii_encoding
30+
│ │ │ ├── opening_loc: ∅
31+
│ │ │ ├── value_loc: (5,0)-(5,3) = "baz"
32+
│ │ │ ├── closing_loc: ∅
33+
│ │ │ └── unescaped: "baz"
34+
│ │ └── @ SymbolNode (location: (5,4)-(5,7))
35+
│ │ ├── flags: static_literal, forced_us_ascii_encoding
36+
│ │ ├── opening_loc: ∅
37+
│ │ ├── value_loc: (5,4)-(5,7) = "qux"
38+
│ │ ├── closing_loc: ∅
39+
│ │ └── unescaped: "qux"
40+
│ ├── opening_loc: (4,0)-(5,0) = "%i\n"
41+
│ └── closing_loc: (5,7)-(6,0) = "\n"
42+
├── @ ArrayNode (location: (7,0)-(9,0))
43+
│ ├── flags: newline
44+
│ ├── elements: (length: 2)
45+
│ │ ├── @ StringNode (location: (8,0)-(8,1))
46+
│ │ │ ├── flags: ∅
47+
│ │ │ ├── opening_loc: ∅
48+
│ │ │ ├── content_loc: (8,0)-(8,1) = "a"
49+
│ │ │ ├── closing_loc: ∅
50+
│ │ │ └── unescaped: "a"
51+
│ │ └── @ StringNode (location: (8,2)-(8,3))
52+
│ │ ├── flags: ∅
53+
│ │ ├── opening_loc: ∅
54+
│ │ ├── content_loc: (8,2)-(8,3) = "b"
55+
│ │ ├── closing_loc: ∅
56+
│ │ └── unescaped: "b"
57+
│ ├── opening_loc: (7,0)-(8,0) = "%W\n"
58+
│ └── closing_loc: (8,3)-(9,0) = "\n"
59+
└── @ ArrayNode (location: (10,0)-(12,0))
60+
├── flags: newline, static_literal
61+
├── elements: (length: 2)
62+
│ ├── @ SymbolNode (location: (11,0)-(11,1))
63+
│ │ ├── flags: static_literal, forced_us_ascii_encoding
64+
│ │ ├── opening_loc: ∅
65+
│ │ ├── value_loc: (11,0)-(11,1) = "c"
66+
│ │ ├── closing_loc: ∅
67+
│ │ └── unescaped: "c"
68+
│ └── @ SymbolNode (location: (11,2)-(11,3))
69+
│ ├── flags: static_literal, forced_us_ascii_encoding
70+
│ ├── opening_loc: ∅
71+
│ ├── value_loc: (11,2)-(11,3) = "d"
72+
│ ├── closing_loc: ∅
73+
│ └── unescaped: "d"
74+
├── opening_loc: (10,0)-(11,0) = "%I\n"
75+
└── closing_loc: (11,3)-(12,0) = "\n"

src/prism.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11373,6 +11373,8 @@ parser_lex(pm_parser_t *parser) {
1137311373
// First we'll set the beginning of the token.
1137411374
parser->current.start = parser->current.end;
1137511375

11376+
pm_lex_mode_t *lex_mode = parser->lex_modes.current;
11377+
1137611378
// If there's any whitespace at the start of the list, then we're
1137711379
// going to trim it off the beginning and create a new token.
1137811380
size_t whitespace;
@@ -11382,6 +11384,12 @@ parser_lex(pm_parser_t *parser) {
1138211384
if (peek_offset(parser, (ptrdiff_t)whitespace) == '\n') {
1138311385
whitespace += 1;
1138411386
}
11387+
} else if (lex_mode->as.list.terminator == '\n') {
11388+
// When the list delimiter is a newline (e.g. `%w` followed by a
11389+
// newline), the newline is the terminator rather than a word
11390+
// separator. We only trim inline whitespace here so that the
11391+
// terminating newline is left for the terminator handling below.
11392+
whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
1138511393
} else {
1138611394
whitespace = pm_strspn_whitespace_newlines(parser->current.end, parser->end - parser->current.end, &parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
1138711395
}
@@ -11403,7 +11411,6 @@ parser_lex(pm_parser_t *parser) {
1140311411

1140411412
// Here we'll get a list of the places where strpbrk should break,
1140511413
// and then find the first one.
11406-
pm_lex_mode_t *lex_mode = parser->lex_modes.current;
1140711414
const uint8_t *breakpoints = lex_mode->as.list.breakpoints;
1140811415
const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
1140911416

@@ -11413,8 +11420,10 @@ parser_lex(pm_parser_t *parser) {
1141311420

1141411421
while (breakpoint != NULL) {
1141511422
// If we hit whitespace, then we must have received content by
11416-
// now, so we can return an element of the list.
11417-
if (pm_char_is_whitespace(*breakpoint)) {
11423+
// now, so we can return an element of the list. A whitespace
11424+
// character that is also the terminator (e.g. a newline
11425+
// delimiter) is handled by the terminator check below, not here.
11426+
if (pm_char_is_whitespace(*breakpoint) && *breakpoint != lex_mode->as.list.terminator) {
1141811427
parser->current.end = breakpoint;
1141911428
pm_token_buffer_flush(parser, &token_buffer);
1142011429
LEX(PM_TOKEN_STRING_CONTENT);
@@ -11443,6 +11452,14 @@ parser_lex(pm_parser_t *parser) {
1144311452
// Otherwise, switch back to the default state and return
1144411453
// the end of the list.
1144511454
parser->current.end = breakpoint + 1;
11455+
11456+
// If the terminator is a newline (i.e. the list delimiter
11457+
// was a newline), then we need to record it so that line
11458+
// numbers after the list remain accurate.
11459+
if (*breakpoint == '\n') {
11460+
pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
11461+
}
11462+
1144611463
lex_mode_pop(parser);
1144711464
lex_state_set(parser, PM_LEX_STATE_END);
1144811465
LEX(PM_TOKEN_STRING_END);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
%w
2+
foo bar
3+
4+
%i
5+
baz qux
6+
7+
%W
8+
a b
9+
10+
%I
11+
c d

test/prism/ruby/ruby_parser_test.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class RubyParserTest < TestCase
4646
"multi_write.txt",
4747
"not.txt",
4848
"patterns.txt",
49+
"percent_array_newline_delimiter.txt",
4950
"regex.txt",
5051
"seattlerb/and_multi.txt",
5152
"seattlerb/heredoc__backslash_dos_format.txt",

test/prism/snippets_test.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class SnippetsTest < TestCase
77
except = [
88
"encoding_binary.txt",
99
"newline_terminated.txt",
10+
"percent_array_newline_delimiter.txt",
1011
"seattlerb/begin_rescue_else_ensure_no_bodies.txt",
1112
"seattlerb/case_in.txt",
1213
"seattlerb/parse_line_defn_no_parens.txt",

0 commit comments

Comments
 (0)