fix: reject names the expression parser cannot read (#2062) - #2064
Merged
Conversation
Name validation and the expression grammar disagreed on what an identifier is. Validation used Character.isJavaIdentifierStart/Part, which accept any Unicode letter; Parser.jjt defines LETTER as ["a"-"z", "_", "A"-"Z"]. A name such as PROTEINA_A with an accent was therefore accepted and saved, and then failed to parse during math generation. Add isValidExpressionIdentifier / indexOfFirstIllegalIdentifierChar as the single definition of the rule, and switch fixToken and fixTokenStrict to it. Those two manglers exist to turn externally supplied names into legal identifiers, but inherited the same Unicode-wide notion of a letter and so passed non-ASCII letters through untouched. Behaviour is unchanged for any name that is already ASCII, which is every name the manglers previously handled correctly. Refs #2062 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D71LBYmQNf5J94wPqr81Jx
…ls loadable Model.validateNamingConflicts already required a symbol name to be a legal identifier and already produced a good message with a suggested replacement. It never fired for a non-ASCII name only because the mangler it compared against treated any Unicode letter as a letter. It now asks TokenMangler directly, so the guard covers every model symbol, not just species contexts. SpeciesContext.vetoableChange gets the same rule, and its message now states what a name may contain rather than only naming the offending character. The catch is models already saved with such a name. They are unrunnable today, but they do open, and opening them is the user's only way to rename the species. Enforcing the rule on the read path would have taken that away, so reads from VCML and from the database go through SpeciesContext.fromPersistedContent and Model.setRestoringFromPersistedContent, which relax the lexicon check for the duration of the read only - naming conflicts are still checked. The illegal name is then reported by SpeciesContext.gatherIssues as an error the user can act on, instead of surfacing at math-generation time as a parse error that names an expression rather than the species. Fixes #2062 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D71LBYmQNf5J94wPqr81Jx
The two definitions drifted apart once and nothing caught it, so the central test asserts agreement by actually parsing: every name the rule accepts must parse as a single identifier equal to itself, and every name it rejects must not. Tightening or loosening either side alone now fails here. Also covers the round trip that matters in practice - a VCML holding a name the parser cannot read still loads, keeps the name as saved, and is reported as an error issue - and records that '.' is the one character the grammar accepts in an IDENTIFIER (as a name-scope qualifier) that a name still may not contain. Refs #2062 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D71LBYmQNf5J94wPqr81Jx
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 #2062.
What was wrong
Name validation and the expression grammar disagreed on what an identifier is:
Character.isJavaIdentifierStart/Part, which accept any Unicode letterParser.jjtdefines#LETTERas["a"-"z", "_", "A"-"Z"]— ASCII onlySo a species named
PROTEÍNA_Awas accepted, saved, and then failed math generation withParse Error while parsing expression 'PROTEÍNA_A'→ "Application has no generated Math".The error names an expression, so nothing pointed at the species. Three Contact-Us crash
reports from one Spanish-language model.
The root cause is narrower than the issue says
Model.validateNamingConflictsalready required a legal identifier and already produced agood message with a suggested replacement:
The guard was correct in shape. It never fired because
fixTokenStrictusedCharacter.isLetterOrDigit, so it consideredÍa letter and returned the name unchanged. Themanglers that exist precisely to turn external names into legal identifiers were passing
non-ASCII letters straight through.
I've updated the issue with this correction.
The fix
TokenManglergains one definition of the rule —isValidExpressionIdentifier/indexOfFirstIllegalIdentifierChar— andfixToken/fixTokenStrictnow use it.Behaviour is unchanged for any name that is already ASCII, i.e. every name these manglers
previously handled correctly.
Model.validateNamingConflictsasks that predicate directly, so the existing guard nowworks for every model symbol, not just species contexts. This is what actually fixes the
reported bug.
SpeciesContext.vetoableChangegets the same rule, and its message now states what aname may contain instead of only naming the offending character.
Keeping already-saved models openable
Models saved with such a name are unrunnable today, but they do open — and opening them is
the user's only way to rename the species. Enforcing the rule on the read path would have taken
that away, turning "opens but won't run" into "won't open", which is worse than the bug.
Reads from VCML and from the database therefore go through
SpeciesContext.fromPersistedContent/Model.setRestoringFromPersistedContent, which relaxthe lexicon check for the duration of the read only — naming conflicts are still checked. The
illegal name is then reported by
SpeciesContext.gatherIssuesas an error the user can act on.I found this the right way: the round-trip test failed, which is also how I found the
Model-level guard I'd missed on the first pass.Tests
New
SpeciesContextNameTest(10 tests,Fast). The central one asserts the rule and the parseragree by actually parsing — every accepted name must parse as a single identifier equal to
itself, every rejected name must not — so tightening or loosening either side alone fails here.
That is the check whose absence let the two drift apart.
It also covers the real round trip (a VCML holding an unreadable name still loads, keeps the
name, and is reported as an error issue) and records one thing worth knowing:
.is the singlecharacter the grammar accepts inside an
IDENTIFIER— as a name-scope qualifier,(<ID> ".")* <ID>— that a name still may not contain.Verification
SpeciesContextNameTest— 10/10vcell-coreFast — 606 tests, 1 error, and that one is the documented Poetry/VCellDataTestenvironmental failure, present before this change
vcell-utilFast 42/42 ·vcell-mathFast 6545/6545 ·vcell-clientFast 31/31mvn compile test-compileacross all modules — cleanvcell-serverFast was still running locally when I stopped it; every class it had reported wasgreen. Worth watching the SBML/SEDML regression suites in the merge queue, since re-arming the
mangler could change how an imported model with non-ASCII names is renamed on import — that is
the intended behaviour, but it is the one place the blast radius extends past the reported bug.
🤖 Generated with Claude Code
https://claude.ai/code/session_01D71LBYmQNf5J94wPqr81Jx