Skip to content

Replace hardcoded parser with PubChem structure parser#37

Merged
djleamen merged 1 commit into
mainfrom
djleamen/replace-hardcoded-molecule-lookup-tables
Jun 18, 2026
Merged

Replace hardcoded parser with PubChem structure parser#37
djleamen merged 1 commit into
mainfrom
djleamen/replace-hardcoded-molecule-lookup-tables

Conversation

@djleamen

Copy link
Copy Markdown
Owner

Why

The previous molecule parser relied on a large hardcoded lookup table and could silently return incorrect structures (for example, showing methane or benzene for unrelated valid inputs). This change replaces that behavior with structure parsing based on real compound records.

What changed

  • Replaced the hardcoded parser in moleculeParser.ts with a PubChem-driven flow:
    • resolve identifier -> PubChem CID
    • fetch PubChem compound record (3D first, then record fallback)
    • build atom and bond data directly from returned coordinates and bond order
  • Removed silent defaulting to methane; parser now surfaces explicit errors when structure data is unavailable.
  • Kept a small offline fallback set for common molecules so basic cases still render when lookup fails.
  • Fixed identifier lookup and parser caching to preserve SMILES case sensitivity, preventing C1CCCCC1 (cyclohexane) from collapsing into c1ccccc1 (benzene).
  • Updated README sections to reflect the new parser architecture, coverage, and current limitations.

Notes for review

  • This is a substantial simplification of bespoke parsing logic in favor of external structure data.
  • Current linting remains blocked by existing repo ESLint config migration (eslint.config.*), unrelated to this change.

Fixes: #35

- Replace hardcoded molecule parsing tables with PubChem CID and record-based parsing\n- Use real atom/bond coordinates from PubChem records with explicit parse errors\n- Keep a small offline fallback set for common molecules\n- Fix SMILES lookup and parser cache key handling to preserve case sensitivity\n- Update README to document the new parsing approach and limitations\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 18, 2026 23:03
@github-actions

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@djleamen
djleamen merged commit cdc4715 into main Jun 18, 2026
7 of 10 checks passed
@djleamen
djleamen deleted the djleamen/replace-hardcoded-molecule-lookup-tables branch June 18, 2026 23:04
@sonarqubecloud

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR replaces the previous hardcoded/pattern-based molecule parsing with a PubChem-backed structure loader that resolves identifiers to PubChem CIDs, fetches compound records (preferring 3D), and builds atoms/bonds directly from returned coordinate/bond data. It also updates identifier lookup to preserve SMILES case sensitivity and refreshes README documentation accordingly.

Changes:

  • Reworked parseMolecule() to resolve identifiers → PubChem CID → PubChem compound record (3D-first) → Molecule3D.
  • Added parsing cache and an explicit error path when no structure data is available, plus a small offline fallback set.
  • Updated local identifier lookup (SMILES case sensitivity) and README to reflect the new architecture/limitations.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.

File Description
src/utils/moleculeParser.ts Replaces hardcoded parsing with PubChem CID resolution + record fetching, adds caching and offline fallbacks.
src/utils/identifierLookup.ts Preserves SMILES case in local identifier lookup to avoid aromatic/aliphatic collapsing.
README.md Updates documentation to describe PubChem-backed parsing and current limitations.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +38 to +42
'iupacname:water': createWater(),
'iupacname:methane': createMethane(),
'iupacname:ammonia': createAmmonia(),
'iupacname:carbon dioxide': createCarbonDioxide(),
'iupacname:benzene': createBenzene(),
Comment on lines +44 to +48
'casnumber:7732-18-5': createWater(),
'casnumber:74-82-8': createMethane(),
'casnumber:7664-41-7': createAmmonia(),
'casnumber:124-38-9': createCarbonDioxide(),
'casnumber:71-43-2': createBenzene(),
'casnumber:7664-41-7': createAmmonia(),
'casnumber:124-38-9': createCarbonDioxide(),
'casnumber:71-43-2': createBenzene(),
'casnumber:64-17-5': createEthanol()
Comment on lines +328 to +333
function findOfflineFallback(candidates: IdentifierInput[]): Molecule3D | null {
for (const candidate of candidates) {
const key = `${candidate.type}:${candidate.value.trim().toLowerCase()}`;
const fallback = COMMON_OFFLINE_FALLBACKS[key];
if (fallback) {
return fallback;
Comment on lines +93 to +96
throw new Error(
`Unable to parse molecule "${cleanValue}". No structure data was found for the provided identifiers.`
+ (failureMessages.length > 0 ? ` Last error: ${failureMessages[0]}` : '')
);
Comment on lines +127 to +135
const unique = new Map<string, IdentifierInput>();
for (const entry of [primary, ...fromIdentifiers]) {
const key = getIdentifierKey(entry.type, entry.value);
if (!unique.has(key)) {
unique.set(key, entry);
}
} catch (error) {
console.warn('Failed to lookup SMILES from PubChem:', error);
}

// Try partial matches for common patterns
const partialMatch = tryPartialMatches(lowerName);
if (partialMatch) return partialMatch;

// Pattern matching for alkenes
const alkeneMatch = tryMatchAlkene(lowerName);
if (alkeneMatch) return alkeneMatch;

// Pattern matching for alkanes
const alkaneMatch = tryMatchAlkane(lowerName);
if (alkaneMatch) return alkaneMatch;
return [...unique.values()];
Comment on lines +31 to +37
const COMMON_OFFLINE_FALLBACKS: Record<string, Molecule3D> = {
'smiles:o': createWater(),
'smiles:c': createMethane(),
'smiles:n': createAmmonia(),
'smiles:o=c=o': createCarbonDioxide(),
'smiles:c1ccccc1': createBenzene(),
'smiles:cco': createEthanol(),
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.

Replace hardcoded molecule lookup tables with a real structure parser (e.g. openchemlib or RDKit-JS)

2 participants