Repository navigation
Description
v0.4.0 split noDo into noDo (COND) + noDoBlock (CMDARG) and fixed the
CMDARG half. The COND half is still wrong, and it diverges in one clean
family: a do...end block inside a loop condition is refused where MRI
accepts it.
Measured against MRI 4.0.5 (+PRISM, and cross-checked with
ruby --parser=parse.y -c) and against parser@v0.4.0 called directly through
parser.Parse:
| shape | MRI | parser v0.4.0 |
|---|---|---|
while (y do end); end |
accepted | REFUSED |
while [y do end]; end |
accepted | REFUSED |
while "#{y do end}"; end |
accepted | REFUSED |
while {a: y do end}[:a]; end |
accepted | REFUSED |
while (y do end) && true; end |
accepted | REFUSED |
until (y do end); end |
accepted | REFUSED |
for i in (y do end); end |
accepted | REFUSED |
for i in [y do end]; end |
accepted | REFUSED |
if (y do end); end |
accepted | accepted |
unless (y do end); end |
accepted | accepted |
if already works, and that is the diagnosis
MRI pushes COND only around loop conditions — COND_PUSH(1) appears at
while/until/for in parse.y, and not at if/unless, which is why
if (y do end); end needs no fix and already passes. So the defect is not
"COND is wrong"; it is that noDo is never cleared when the parser descends
into a nested sub-expression inside a loop condition — parentheses, an array
literal, a hash-literal value, a string interpolation, or an operand of &&.
MRI clears it with COND_PUSH(0) on entering each of those.
v0.4.0 already does the analogous clearing for noDoBlock in
parseStatements, parseCallArgs and parseHashLiteral. The COND half needs
the same treatment in the grouping constructs, which is a different set of
call sites.
Two controls that must keep holding — they are what makes this safe
while begin; y do end; end; endmust stay a SyntaxError. MRI refuses
it, and v0.4.0 correctly refuses it. This is precisely whynoDomust not
be cleared inparseStatements— the asymmetry v0.4.0 established
deliberately. A blanket clear would break it, and this shape is the witness
that catches that.- The CMDARG half must stay fixed.
foo :a, (y do end)and
x = (y do end)are accepted by both today; they must remain so.
Both belong in the test table alongside the eight rows above, so the next change
to this area cannot fix one half by breaking the other.
How it was measured
parser.Parse called directly on each shape from a throwaway module pinned to
v0.4.0, compared row by row against ruby -c. Both references agree on every
row, including the one they both refuse, so the table is not an artefact of
PRISM vs parse.y.
Follow-up to #32, found while verifying that release's payoff in the downstream
consumer.
Activity
parser: the `arg`/`command` boundary as a two-direction table, and #33'…
parser: refuse a paren-less command call in an `arg` position (array …