Skip to content

Commit 1f42963

Browse files
shugokddnewton
authored andcommitted
Accept a nested target as the first for loop index
A `for` loop index may be a multiple-assignment target whose first element is itself a parenthesized (nested) target, e.g. for (a, b), c in 1..10 end CRuby's parse.y accepts this, and a plain multiple assignment with the same target (`(a, b), c = ...`) is accepted, but Prism rejected it with "unexpected write target". When parse_parentheses finishes the first `(a, b)`, it validates the multi-target it produced. In a `for` index it only allowed the target to be followed by `in`, so a following `,` (which continues the index target list) fell through to the "not a statement level" error. Allow a comma there as well.
1 parent d07335c commit 1f42963

3 files changed

Lines changed: 142 additions & 19 deletions

File tree

snapshots/for.txt

Lines changed: 131 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
@ ProgramNode (location: (1,0)-(19,22))
1+
@ ProgramNode (location: (1,0)-(27,3))
22
├── flags: ∅
3-
├── locals: [:i, :j, :k]
3+
├── locals: [:i, :j, :k, :a, :b, :c, :d]
44
└── statements:
5-
@ StatementsNode (location: (1,0)-(19,22))
5+
@ StatementsNode (location: (1,0)-(27,3))
66
├── flags: ∅
7-
└── body: (length: 6)
7+
└── body: (length: 8)
88
├── @ ForNode (location: (1,0)-(3,3))
99
│ ├── flags: newline
1010
│ ├── index:
@@ -186,34 +186,148 @@
186186
│ ├── in_keyword_loc: (15,6)-(15,8) = "in"
187187
│ ├── do_keyword_loc: (15,15)-(15,17) = "do"
188188
│ └── end_keyword_loc: (17,0)-(17,3) = "end"
189-
└── @ ForNode (location: (19,0)-(19,22))
189+
├── @ ForNode (location: (19,0)-(19,22))
190+
│ ├── flags: newline
191+
│ ├── index:
192+
│ │ @ LocalVariableTargetNode (location: (19,4)-(19,5))
193+
│ │ ├── flags: ∅
194+
│ │ ├── name: :i
195+
│ │ └── depth: 0
196+
│ ├── collection:
197+
│ │ @ RangeNode (location: (19,9)-(19,14))
198+
│ │ ├── flags: static_literal
199+
│ │ ├── left:
200+
│ │ │ @ IntegerNode (location: (19,9)-(19,10))
201+
│ │ │ ├── flags: static_literal, decimal
202+
│ │ │ └── value: 1
203+
│ │ ├── right:
204+
│ │ │ @ IntegerNode (location: (19,12)-(19,14))
205+
│ │ │ ├── flags: static_literal, decimal
206+
│ │ │ └── value: 10
207+
│ │ └── operator_loc: (19,10)-(19,12) = ".."
208+
│ ├── statements:
209+
│ │ @ StatementsNode (location: (19,16)-(19,17))
210+
│ │ ├── flags: ∅
211+
│ │ └── body: (length: 1)
212+
│ │ └── @ LocalVariableReadNode (location: (19,16)-(19,17))
213+
│ │ ├── flags: newline
214+
│ │ ├── name: :i
215+
│ │ └── depth: 0
216+
│ ├── for_keyword_loc: (19,0)-(19,3) = "for"
217+
│ ├── in_keyword_loc: (19,6)-(19,8) = "in"
218+
│ ├── do_keyword_loc: ∅
219+
│ └── end_keyword_loc: (19,19)-(19,22) = "end"
220+
├── @ ForNode (location: (21,0)-(23,3))
221+
│ ├── flags: newline
222+
│ ├── index:
223+
│ │ @ MultiTargetNode (location: (21,4)-(21,13))
224+
│ │ ├── flags: ∅
225+
│ │ ├── lefts: (length: 2)
226+
│ │ │ ├── @ MultiTargetNode (location: (21,4)-(21,10))
227+
│ │ │ │ ├── flags: ∅
228+
│ │ │ │ ├── lefts: (length: 2)
229+
│ │ │ │ │ ├── @ LocalVariableTargetNode (location: (21,5)-(21,6))
230+
│ │ │ │ │ │ ├── flags: ∅
231+
│ │ │ │ │ │ ├── name: :a
232+
│ │ │ │ │ │ └── depth: 0
233+
│ │ │ │ │ └── @ LocalVariableTargetNode (location: (21,8)-(21,9))
234+
│ │ │ │ │ ├── flags: ∅
235+
│ │ │ │ │ ├── name: :b
236+
│ │ │ │ │ └── depth: 0
237+
│ │ │ │ ├── rest: ∅
238+
│ │ │ │ ├── rights: (length: 0)
239+
│ │ │ │ ├── lparen_loc: (21,4)-(21,5) = "("
240+
│ │ │ │ └── rparen_loc: (21,9)-(21,10) = ")"
241+
│ │ │ └── @ LocalVariableTargetNode (location: (21,12)-(21,13))
242+
│ │ │ ├── flags: ∅
243+
│ │ │ ├── name: :c
244+
│ │ │ └── depth: 0
245+
│ │ ├── rest: ∅
246+
│ │ ├── rights: (length: 0)
247+
│ │ ├── lparen_loc: ∅
248+
│ │ └── rparen_loc: ∅
249+
│ ├── collection:
250+
│ │ @ RangeNode (location: (21,17)-(21,22))
251+
│ │ ├── flags: static_literal
252+
│ │ ├── left:
253+
│ │ │ @ IntegerNode (location: (21,17)-(21,18))
254+
│ │ │ ├── flags: static_literal, decimal
255+
│ │ │ └── value: 1
256+
│ │ ├── right:
257+
│ │ │ @ IntegerNode (location: (21,20)-(21,22))
258+
│ │ │ ├── flags: static_literal, decimal
259+
│ │ │ └── value: 10
260+
│ │ └── operator_loc: (21,18)-(21,20) = ".."
261+
│ ├── statements:
262+
│ │ @ StatementsNode (location: (22,0)-(22,1))
263+
│ │ ├── flags: ∅
264+
│ │ └── body: (length: 1)
265+
│ │ └── @ LocalVariableReadNode (location: (22,0)-(22,1))
266+
│ │ ├── flags: newline
267+
│ │ ├── name: :i
268+
│ │ └── depth: 0
269+
│ ├── for_keyword_loc: (21,0)-(21,3) = "for"
270+
│ ├── in_keyword_loc: (21,14)-(21,16) = "in"
271+
│ ├── do_keyword_loc: ∅
272+
│ └── end_keyword_loc: (23,0)-(23,3) = "end"
273+
└── @ ForNode (location: (25,0)-(27,3))
190274
├── flags: newline
191275
├── index:
192-
│ @ LocalVariableTargetNode (location: (19,4)-(19,5))
276+
│ @ MultiTargetNode (location: (25,4)-(25,17))
193277
│ ├── flags: ∅
194-
│ ├── name: :i
195-
│ └── depth: 0
278+
│ ├── lefts: (length: 1)
279+
│ │ └── @ MultiTargetNode (location: (25,4)-(25,10))
280+
│ │ ├── flags: ∅
281+
│ │ ├── lefts: (length: 2)
282+
│ │ │ ├── @ LocalVariableTargetNode (location: (25,5)-(25,6))
283+
│ │ │ │ ├── flags: ∅
284+
│ │ │ │ ├── name: :a
285+
│ │ │ │ └── depth: 0
286+
│ │ │ └── @ LocalVariableTargetNode (location: (25,8)-(25,9))
287+
│ │ │ ├── flags: ∅
288+
│ │ │ ├── name: :b
289+
│ │ │ └── depth: 0
290+
│ │ ├── rest: ∅
291+
│ │ ├── rights: (length: 0)
292+
│ │ ├── lparen_loc: (25,4)-(25,5) = "("
293+
│ │ └── rparen_loc: (25,9)-(25,10) = ")"
294+
│ ├── rest:
295+
│ │ @ SplatNode (location: (25,12)-(25,14))
296+
│ │ ├── flags: ∅
297+
│ │ ├── operator_loc: (25,12)-(25,13) = "*"
298+
│ │ └── expression:
299+
│ │ @ LocalVariableTargetNode (location: (25,13)-(25,14))
300+
│ │ ├── flags: ∅
301+
│ │ ├── name: :c
302+
│ │ └── depth: 0
303+
│ ├── rights: (length: 1)
304+
│ │ └── @ LocalVariableTargetNode (location: (25,16)-(25,17))
305+
│ │ ├── flags: ∅
306+
│ │ ├── name: :d
307+
│ │ └── depth: 0
308+
│ ├── lparen_loc: ∅
309+
│ └── rparen_loc: ∅
196310
├── collection:
197-
│ @ RangeNode (location: (19,9)-(19,14))
311+
│ @ RangeNode (location: (25,21)-(25,26))
198312
│ ├── flags: static_literal
199313
│ ├── left:
200-
│ │ @ IntegerNode (location: (19,9)-(19,10))
314+
│ │ @ IntegerNode (location: (25,21)-(25,22))
201315
│ │ ├── flags: static_literal, decimal
202316
│ │ └── value: 1
203317
│ ├── right:
204-
│ │ @ IntegerNode (location: (19,12)-(19,14))
318+
│ │ @ IntegerNode (location: (25,24)-(25,26))
205319
│ │ ├── flags: static_literal, decimal
206320
│ │ └── value: 10
207-
│ └── operator_loc: (19,10)-(19,12) = ".."
321+
│ └── operator_loc: (25,22)-(25,24) = ".."
208322
├── statements:
209-
│ @ StatementsNode (location: (19,16)-(19,17))
323+
│ @ StatementsNode (location: (26,0)-(26,1))
210324
│ ├── flags: ∅
211325
│ └── body: (length: 1)
212-
│ └── @ LocalVariableReadNode (location: (19,16)-(19,17))
326+
│ └── @ LocalVariableReadNode (location: (26,0)-(26,1))
213327
│ ├── flags: newline
214328
│ ├── name: :i
215329
│ └── depth: 0
216-
├── for_keyword_loc: (19,0)-(19,3) = "for"
217-
├── in_keyword_loc: (19,6)-(19,8) = "in"
330+
├── for_keyword_loc: (25,0)-(25,3) = "for"
331+
├── in_keyword_loc: (25,18)-(25,20) = "in"
218332
├── do_keyword_loc: ∅
219-
└── end_keyword_loc: (19,19)-(19,22) = "end"
333+
└── end_keyword_loc: (27,0)-(27,3) = "end"

src/prism.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19097,9 +19097,10 @@ parse_parentheses(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
1909719097

1909819098
if (context_p(parser, PM_CONTEXT_MULTI_TARGET)) {
1909919099
/* All set, this is explicitly allowed by the parent context. */
19100-
} else if (context_p(parser, PM_CONTEXT_FOR_INDEX) && match1(parser, PM_TOKEN_KEYWORD_IN)) {
19100+
} else if (context_p(parser, PM_CONTEXT_FOR_INDEX) && match2(parser, PM_TOKEN_KEYWORD_IN, PM_TOKEN_COMMA)) {
1910119101
/* All set, we're inside a for loop and we're parsing multiple
19102-
* targets. */
19102+
* targets. A comma continues the index target list, as in
19103+
* `for (a, b), c in ...`. */
1910319104
} else if (flags & PM_PARSE_ACCEPTS_STATEMENT) {
1910419105
/* The rescue-modifier value parser promotes this target on a
1910519106
* following `=` or comma. Reject any other binary operator that

test/prism/fixtures/for.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,11 @@ i
1717
end
1818

1919
for i in 1..10; i; end
20+
21+
for (a, b), c in 1..10
22+
i
23+
end
24+
25+
for (a, b), *c, d in 1..10
26+
i
27+
end

0 commit comments

Comments
 (0)