Skip to content

StrictCommaScope: reject the trailing commas OpenSCAD 2021.01 rejected - #9

Merged
revarbat merged 1 commit into
mainfrom
strict-commas
Sep 8, 2026
Merged

revarbat merged 1 commit into
mainfrom
strict-commas

Conversation

@revarbat

@revarbat revarbat commented Sep 8, 2026

Copy link
Copy Markdown
Member

Parser half of BelfrySCAD#362, which wants a --strict-commas flag so a script can be checked against OpenSCAD 2021.01.

The scope is narrower than "no trailing commas"

Because 2021.01 was narrower. Measured against 2021.01 itself rather than assumed:

rejected by 2021.01 accepted by 2021.01
cube(1,); a = [2, 4,];
translate([0,0,0],) cube(1); a = [for (i=[0:2]) i,];
max(1, 2,) a = [each [1,2],];
str("a","b",) module m(a, b,) {}
let(x=1, y=2,) function f(a, b,) = a+b;
for (i=[0:2],)
intersection_for(i=[0:1],)

List literals, list comprehensions and parameter declarations keep their trailing comma. Rejecting those would fail files 2021.01 loads happily.

Two productions, not one

arguments, which every call form routes through — and assignments_expr, which let/for/intersection_for have of their own. let is not a call in this grammar.

The first draft guarded only arguments, and let(x=1,) sailed straight through. The test that lists every rejected form caught it, which is why it lists them one by one rather than asserting a rule.

Why a scope object

This is a parse-wide mode, the shape ParseNumberingScope already uses here. A bool would have to be threaded through getASTFromString/getASTFromFile/getASTFromLibraryFile/getProgramFromFile and four helpers to say one thing.

Thread-local, nests, and restores on scope exit including when the parse throws — both tested, since a mode that leaked would silently change how every later parse behaves.

The cache

getProgramFromFile's shared cache keys on it too. Without that, a strict parse of a file already parsed leniently would be served the lenient tree and quietly pass — and only for a file included twice, which is the hardest version of that bug to ever notice.

655 tests pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AzS79GLeG7LpYB6KjgDbi1

For BelfrySCAD#362, which wants a --strict-commas flag so a script can be
checked against 2021.01. This is the parser half.

The scope is narrower than "no trailing commas", because 2021.01 was
narrower. Measured against 2021.01 itself rather than assumed:

    cube(1,);                 ERROR      a = [2, 4,];            accept
    translate([0,0,0],) ...   ERROR      a = [for (i=[0:2]) i,]; accept
    max(1, 2,)                ERROR      a = [each [1,2],];      accept
    str("a","b",)             ERROR      module m(a, b,) {}      accept
    let(x=1, y=2,)            ERROR      function f(a, b,) = ..  accept
    for (i=[0:2],)            ERROR
    intersection_for(i=[0:1],) ERROR

So list literals, list comprehensions and parameter DECLARATIONS keep their
trailing comma -- rejecting those would fail files 2021.01 loads happily.

TWO productions carry it, not the one it looks like: `arguments`, which every
call form routes through, and `assignments_expr`, which let/for/
intersection_for have of their own. `let` is not a call in this grammar. The
first draft guarded only `arguments` and let(x=1,) sailed through; the test
listing every rejected form caught it, which is the reason the test lists
them one by one rather than asserting a rule.

A scope object rather than a parameter on the parse entry points: this is a
parse-wide mode, the shape ParseNumberingScope already uses here, and a bool
would have to be threaded through getASTFromString/getASTFromFile/
getASTFromLibraryFile/getProgramFromFile and four helpers to say one thing.
Thread-local, nests, and restores on scope exit including when the parse
throws -- both tested, since a mode that leaks would silently change how
every later parse behaves.

getProgramFromFile's shared cache keys on it too. Without that, a strict
parse of a file already parsed leniently would be served the lenient tree
and quietly pass -- only for a file included twice, which is the hardest
version of that bug to ever notice.

655 tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AzS79GLeG7LpYB6KjgDbi1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant