MCPcopy Create free account
hub / github.com/cortex-js/compute-engine / boxCompiledRule

Function boxCompiledRule

src/compute-engine/fungrim/loader.ts:341–426  ·  view source on GitHub ↗
(
  ce: IComputeEngine,
  rule: CompiledFungrimRule,
  onGuardUndecided: FungrimGuardUndecidedHandler | undefined
)

Source from the content-addressed store, hash-verified

339 if (k !== '_x' && k !== 'x' && v.has('_x')) return false;
340 return true;
341}
342
343function boxCompiledRule(
344 ce: IComputeEngine,
345 rule: CompiledFungrimRule,
346 onGuardUndecided: FungrimGuardUndecidedHandler | undefined
347): { rule: BoxedRuleParts } | { error: string } {
348 // Box in a child scope where the wildcards carry their guard-implied
349 // types: strict validation of typed slots (Fibonacci(_n), Totient(_n), …)
350 // passes, and inferred wildcard types never leak into the user's scope.
351 const wildcards = collectWildcards(rule.match);
352 collectWildcards(rule.replace, wildcards);
353 for (const g of rule.guards) collectWildcards(guardJson(g), wildcards);
354 const types = wildcardTypes(rule);
355
356 ce.pushScope();
357 try {
358 for (const wc of wildcards) {
359 try {
360 ce.declare(wc, types[wc] ?? 'complex');
361 } catch {
362 /* tolerate */
363 }
364 }
365
366 // The artifact stores match/replace in canonical-form MathJSON; box
367 // CANONICALLY (raw boxing leaves ['Rational',1,2] as a structural
368 // function expression and loses literal matches; a bare-string side
369 // ('_x') passed unboxed to ce.rules would be parsed as LaTeX).
370 const match = ce.expr(rule.match as ExpressionInput);
371 if (!match.isValid)
372 return { error: `invalid match: ${match.toString()}`.slice(0, 160) };
373 const replace = ce.expr(rule.replace as ExpressionInput);
374 if (!replace.isValid)
375 return { error: `invalid replace: ${replace.toString()}`.slice(0, 160) };
376
377 const closures = buildGuardClosures(ce, rule.guards);
378
379 let condition: ((sub: BoxedSubstitution) => boolean) | undefined;
380 if (closures.length > 0) {
381 const id = rule.id;
382 condition = (sub: BoxedSubstitution): boolean => {
383 for (const f of closures) {
384 const r = f(sub);
385 if (r === true) continue;
386 // Fail-closed: an undecided predicate blocks firing, but is
387 // surfaced through the debug hook (§2.8).
388 if (r === undefined) onGuardUndecided?.(id, sub);
389 return false;
390 }
391 return true;
392 };
393 }
394
395 // Solve-root templates (`target: 'solve'`) always carry the no-capture
396 // filter (AND-combined with any guard closures) and run with variations
397 // so the `__b` offset can be empty (`g(x) = 0`). The guard closures are
398 // typically empty for these (domain guards dropped; see

Callers 1

loadIdentitiesFunction · 0.85

Calls 13

guardJsonFunction · 0.85
solveNoCaptureFilterFunction · 0.85
collectWildcardsFunction · 0.70
wildcardTypesFunction · 0.70
buildGuardClosuresFunction · 0.70
pushScopeMethod · 0.65
declareMethod · 0.65
exprMethod · 0.65
sliceMethod · 0.65
toStringMethod · 0.65
replaceMethod · 0.65
popScopeMethod · 0.65

Tested by

no test coverage detected