Conversation
Integrate compileToSymPy into the LSP checker so SymPy-incompatible expressions surface as warnings in the Diagnostics tab with accurate source positions. Add "JSON with SymPy expressions" export menu entry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
PR SummaryMedium Risk Overview Integrates SymPy compilation into Extends the editor export menu with “JSON with SymPy expressions”, generating an export that embeds per-expression SymPy output (or conversion errors) alongside the original SDCPN model. Written by Cursor Bugbot for commit 2c76726. This will update automatically on new commits. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
|
|
||
| // Run SymPy compilation checks on all code expressions | ||
| checkSymPyCompilation(sdcpn, itemDiagnostics); | ||
|
|
There was a problem hiding this comment.
SymPy warnings incorrectly invalidate the SDCPN check result
High Severity
checkSymPyCompilation appends SymPy failure diagnostics (intentionally marked as warnings, category 0) into itemDiagnostics, but isValid is computed as itemDiagnostics.length === 0. This means any code that can't be converted to SymPy — such as transition kernels returning array literals like [{ x: 1 }], which aren't handled by compile-to-sympy.ts — will cause the entire SDCPN to be reported as invalid, even though the TypeScript is perfectly valid. The comment on makeSymPyDiagnostic explicitly states these are "informational" and "the TypeScript code may still be valid," contradicting the effect on isValid.
Additional Locations (1)
| } | ||
| } | ||
|
|
||
| const exportExpr = exportAssignment!.expression; |
There was a problem hiding this comment.
Null dereference when fallback export search succeeds
Medium Severity
When exportAssignment is undefined (first find failed), the fallback search at line 130 can match an export = statement (since it doesn't filter by !isExportEquals). If exportDefault is found, the code falls through the if block and reaches exportAssignment!.expression on line 142, but exportAssignment is still undefined, causing a runtime crash. The fallback block never reassigns exportAssignment, so the non-null assertion is incorrect on that path.
| if (!exportAssignment) { | ||
| // Try export default as ExpressionStatement pattern | ||
| const exportDefault = sourceFile.statements.find((stmt) => { | ||
| if (ts.isExportAssignment(stmt)) { | ||
| return true; | ||
| } | ||
| // Handle "export default X(...)" which parses as ExportAssignment | ||
| return false; | ||
| }); | ||
| if (!exportDefault) { | ||
| return errNoPos("No default export found"); | ||
| } | ||
| } | ||
|
|
||
| const exportExpr = exportAssignment!.expression; |
There was a problem hiding this comment.
Critical null reference bug. If exportAssignment is null (line 128), the code enters the fallback logic (lines 129-136) to find exportDefault. However, line 142 unconditionally accesses exportAssignment!.expression which will be null, causing a runtime error.
Fix: The fallback logic appears incomplete. After finding exportDefault, it should be assigned to a variable and used instead of exportAssignment. The code should be:
let exportAssignment = sourceFile.statements.find(
(stmt): stmt is ts.ExportAssignment =>
ts.isExportAssignment(stmt) && !stmt.isExportEquals,
);
if (!exportAssignment) {
return errNoPos("No default export found");
}
const exportExpr = exportAssignment.expression;The fallback logic (lines 129-136) appears to be dead code that doesn't actually change the outcome and should be removed.
| if (!exportAssignment) { | |
| // Try export default as ExpressionStatement pattern | |
| const exportDefault = sourceFile.statements.find((stmt) => { | |
| if (ts.isExportAssignment(stmt)) { | |
| return true; | |
| } | |
| // Handle "export default X(...)" which parses as ExportAssignment | |
| return false; | |
| }); | |
| if (!exportDefault) { | |
| return errNoPos("No default export found"); | |
| } | |
| } | |
| const exportExpr = exportAssignment!.expression; | |
| const exportAssignment = sourceFile.statements.find( | |
| (stmt): stmt is ts.ExportAssignment => | |
| ts.isExportAssignment(stmt) && !stmt.isExportEquals, | |
| ); | |
| if (!exportAssignment) { | |
| return errNoPos("No default export found"); | |
| } | |
| const exportExpr = exportAssignment.expression; |
Spotted by Graphite
Is this helpful? React 👍 or 👎 to let us know.
🤖 Augment PR SummarySummary: This PR introduces SymPy compilation support for Petrinaut code snippets and surfaces conversion issues as diagnostics, plus adds an export option that includes SymPy-rendered expressions. Changes:
Technical Notes: Diagnostics are generated as synthetic 🤖 Was this summary useful? React with 👍 or 👎 |
| ); | ||
| } | ||
|
|
||
| const kernelCtx = buildContextForTransition( |
There was a problem hiding this comment.
checkSymPyCompilation() compiles transitionKernelCode for every transition, but the TS checker explicitly skips kernel validation when there are no coloured output places; this will likely produce noisy SymPy warnings (and change isValid) for kernels that are effectively unused.
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| checkSymPyCompilation(sdcpn, itemDiagnostics); | ||
|
|
||
| return { | ||
| isValid: itemDiagnostics.length === 0, |
There was a problem hiding this comment.
isValid: itemDiagnostics.length === 0 now treats SymPy compilation failures (which are emitted as warnings) as making the SDCPN invalid, which seems to contradict the “informational” intent in makeSymPyDiagnostic() and will also break callers/tests that expect warnings not to invalidate the model.
Severity: high
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| } | ||
| } | ||
|
|
||
| const exportExpr = exportAssignment!.expression; |
There was a problem hiding this comment.
If exportAssignment is not found but an ExportAssignment with isExportEquals is present, the current flow falls through and then dereferences exportAssignment!, which will throw at runtime instead of returning a clean { ok: false } result.
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| ); | ||
| if (!result.ok) return result; | ||
| lines.push(result.sympyCode); | ||
| } else if (ts.isExpressionStatement(stmt)) { |
There was a problem hiding this comment.
compileBlock() currently skips all ExpressionStatements under the assumption they are comments, but comments aren’t represented as expression statements in the TS AST; this can silently ignore real statements like console.log(...) and incorrectly report a successful SymPy compilation.
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| sourceFile, | ||
| ); | ||
| } | ||
| const key = prop.name.getText(sourceFile); |
There was a problem hiding this comment.
For object literals, const key = prop.name.getText(sourceFile) will include quotes for string-literal keys (e.g. "Place A"), and then the output wraps it in quotes again ('"Place A"'), producing incorrect SymPy/Python dict keys.
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| }); | ||
| } | ||
|
|
||
| const exportData = { |
There was a problem hiding this comment.
exportData spreads ...petriNetDefinition after setting title/sympy_expressions; since imported JSON can carry extra fields (e.g. exporting then re-importing), the spread can overwrite these new fields and drop the freshly-compiled SymPy output.
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
Boolean(expr) maps to sp.Ne(expr, 0) matching JS truthiness semantics. Number(expr) passes through as identity in symbolic math context. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Maps TypeScript array literals to Python lists, enabling transition kernels that return arrays of token objects to compile to SymPy. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Compiles tokens.map(callback) to [body for _iter in collection],
handling both destructured ({ x, y }) and simple (token) parameters.
Enables dynamics expressions that iterate over token arrays.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ompiler Only const is allowed — both let and var are now rejected. Standalone expression statements (not assigned or returned) produce a diagnostic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>



🌟 What is the purpose of this PR?
Adds the ability to compile Petrinaut's TypeScript expressions (rate laws, guard conditions, dynamics) into SymPy Python code — enabling interoperability with external scientific computing tools (TOPOS, Coherence Research). This is a demo-quality implementation proving the concept and establishing the TS→SymPy mapping.
🔗 Related links
🔍 What does this change?
compile-to-sympy.ts— AST-walking compiler that parses TypeScript expressions and emits SymPy equivalents. Supports arithmetic, comparisons, logical operators, ternary→Piecewise,Math.*functions/constants,Distribution.*, token/parameter access, object literals,constbindings, and type assertion unwrapping. Rejects unsupported syntax with positioned error diagnostics (start/length).compile-to-sympy.test.ts— 51 tests covering all supported syntax, real-world expressions (SIR model, orbital dynamics, transition kernels), and error cases with position assertions.checker.ts— Integrates SymPy compilation into the LSP checker. RunscompileToSymPyon all differential equations, transition lambdas, and transition kernels. Failures surface as warnings (code99000) with accurate source positions in the Diagnostics tab.export-sympy.ts— New export function that converts all model expressions to SymPy and produces a JSON file containing both the original SDCPN and asympy_expressionsarray.editor-view.tsx— Adds "JSON with SymPy expressions" to the Export submenu.Pre-Merge Checklist 🚀
🚢 Has this modified a publishable library?
This PR:
📜 Does this require a change to the docs?
The changes in this PR:
🕸️ Does this require a change to the Turbo Graph?
The changes in this PR:
let, arbitrary function calls) are rejected with warnings rather than errors, since the TS code itself is still valid.🐾 Next steps
🛡 What tests cover this?
compile-to-sympy.test.tscovering basic expressions, parameter/token access, all operators, Math functions, distributions, block bodies, real-world expressions, and error positioning.❓ How to test this?
sympy_expressionswith converted code