Selection: accept , and whitespace as list separators (#116) - #118
Merged
Conversation
`resi 2,3,6` silently selected only residue 2. CommandParser splits argv on commas, so the expression reached the parser as the string "resi 2 3 6", and the grammar only accepted `+` between list entries — everything past the first value was dropped with no error and exit 0, which is how a figure can be rendered from a truncated selection and still look plausible. Value lists (chain, resn, resi, name, element, imgt) now treat `+`, `,` and plain whitespace interchangeably. The list ends at an operator or the next primary keyword, so `chain A,B and name CA` still parses as it reads, and `-` keeps its range meaning (`resi 10-12,20` = 10, 11, 12, 20). Also fixed along the way: - `resn` and `element` accepted only a single value, not even `+` lists. - The `imgt` numeric set stopped at the first non-`+` token. - `imgt` was missing from isPrimaryKeyword, so it did not terminate a list or a `:loadalign` selection tail. - Slash components now split on a gap too, so `/1brs/A/10,11/` is 10+11 rather than residue 1011. Verified against 59 existing selection expressions: byte-identical results before and after. Fixes #116
Review follow-up to the previous commit, no behavior change except where noted. - chain / resn / name / element were the same parse-a-list-then-compare-a- string-field code four times over, with the list rule copy-pasted into each. They now share parseValueList(field, kw, case), so the next change to the separator rule can't reach three of the four and miss the fourth. Their labels also stop claiming `chain A` for `chain A,B`. - Hoisted isOperatorWord() next to isPrimaryKeyword and gave both the same case-insensitive matcher, instead of atListValue keeping a second copy of the operator vocabulary (and a lowercase string copy per token to use it). - resi and imgt stated one list rule twice, in opposite polarity; both now call atListNumber(). - Tokenizer::scan() is private — it leaves `spaced` unset, so next()/peek() are the only correct entry points. - Dropped the comma→space pre-pass in ExprContext: pca()/centroid() hand the paren body over verbatim and the grammar reads commas itself now. Its comment claimed the selection grammar never uses commas, which this branch makes false. Behavior change: `imgt cdr1,cdr2` was still truncating to cdr1 — the region spelling most users type, and one the docs in the previous commit already promised was a list. Region names now list like the numeric form, and an unknown region voids the whole term rather than quietly narrowing to the regions that did parse.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #116.
The bug
resi 2,3,6silently selected only residue 2 — no warning, no error, exit 0.The comma never reaches the selection grammar:
CommandParser::parsesplitsargv on commas as well as whitespace, so
:color red resi 2,3,6arrives at theparser as the string
"resi 2 3 6". The grammar only accepted+between listentries, so it read
resi 2and dropped the rest without a word.That is the dangerous shape the issue calls out — a figure coloured from a
truncated selection looks entirely plausible and survives visual review.
The fix
Value lists now treat
+,,and plain whitespace as the same separator:Two prongs, because the comma survives on some paths and not others:
,to the+token, which covers commands that pass theraw argument tail through (
:select,pca(...)/centroid(...)).after
CommandParser. A list ends at an operator or the next primarykeyword, so
chain A,B and name CAstill parses as it reads, and-keepsits range meaning (
resi 10-12,20= 10, 11, 12, 20).Also fixed, same shape
resnandelementaccepted a single value only — not even+lists.imgt's numeric set stopped at the first non-+token; its region namestruncated on every separator (
imgt cdr1,cdr2was cdr1). An unknown regionnow voids the whole term rather than quietly narrowing to the ones that
parsed.
imgtwas missing fromisPrimaryKeyword, so it neither terminated a listnor bounded a
:loadalignfile-glob tail./1brs/A/10,11/is 10+11, not residue1011.
docs/SCRIPTING.mdpromisedpca(resi 1,2,3)==pca(resi 1 2 3). Both weretruncating; both now work, and the comma→space pre-pass in
ExprContextthatexisted to fake it is gone.
Verification
within/same/
byres, object qualifiers, IMGT, nucleic selectors) produce byte-identicalresults before and after.
+/,/space equivalence across all six keywords andboth slash positions, and pin the boundaries: a list stops at an operator and
at a primary keyword, and adjacency never overrides an explicit operator.
Deliberately not in this PR
Selection::parsestill ignores unconsumed trailingtokens, so other malformed expressions stay quiet. Making that loud needs an
error channel on
Selection::parseand its ~16 call sites — filed separately.:align chain=A,B. Same root cause one level up, but unreachable from thegrammar:
chain=AandBare separate argv tokens before any selectionparsing happens. Filed separately.
🤖 Generated with Claude Code