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

Function canonicalForm

src/compute-engine/boxed-expression/canonical.ts:14–85  ·  view source on GitHub ↗
(
  expr: Expression,
  forms: CanonicalOptions,
  scope?: Scope
)

Source from the content-addressed store, hash-verified

12import { canonicalMultiply, canonicalDivide } from './arithmetic-mul-div.js';
13import { canonicalPower } from './arithmetic-power.js';
14import { canonicalOrder } from './order.js';
15import { declaredBinders, binderShadowAt } from './binding-sites.js';
16import { asBigint } from './numerics.js';
17import { isOperatorDef, isImaginaryUnit } from './utils.js';
18import { isFunction, isNumber, isSymbol } from './type-guards.js';
19
20export function canonicalForm(
21 expr: Expression,
22 forms: CanonicalOptions,
23 scope?: Scope
24): Expression {
25 // No canonical form?
26 if (forms === false) return expr;
27
28 // Full canonical form? Without a scope, `_inScope()` would just call its
29 // callback: go straight to the getter. This is on the recursive path of
30 // canonicalizing an already-boxed tree (each operand's `.canonical` comes
31 // back through here), where every frame per level costs depth.
32 if (forms === true)
33 return scope === undefined
34 ? expr.canonical
35 : expr.engine._inScope(scope, () => expr.canonical);
36
37 if (typeof forms === 'string') forms = [forms];
38
39 // A partial form is not fully canonical, so it follows the STRUCTURAL symbol
40 // contract: symbols resolve against the scope chain (a `holdUntil: 'never'`
41 // constant still substitutes its value, an existing declaration still binds),
42 // but a name that resolves to nothing is left unbound instead of being
43 // declared into the caller's scope. The suppression wraps the whole pipeline
44 // because the forms reach `.canonical` on a symbol through many helpers
45 // (`isImaginaryUnit`, `flatten`, `canonicalInvisibleOperator`, …), not just
46 // `symbolForm`. See `docs/SCOPING-MODEL.md` A1.
47 //
48 // The supplied scope is honored on this path too (B1): it steers the lookups
49 // the forms perform, exactly as it does on the `forms === true` path above.
50 // Without it, `scope` was silently ignored for every partial form.
51 const formList = forms;
52 return expr.engine._inScope(scope, () =>
53 expr.engine._resolveOnly(() => applyForms(expr, formList))
54 );
55}
56
57/**
58 * `expr`'s symbol name, or `''` for anything that is not a symbol — a sentinel
59 * no binder can bind, so a shadow-set membership test on it is always false.
60 */
61function symbolNameOf(expr: Expression): string {
62 return isSymbol(expr) ? expr.symbol : '';
63}
64
65function applyForms(
66 expr: Expression,
67 forms: readonly CanonicalForm[]
68): Expression {
69 // Like for full canonicalization, request the canonical form of symbols.
70 // Automatically, this involves the substitution of the symbol with its
71 // value, if it is a constant-flagged symbol, with a 'holdUntil' attribute of

Callers 3

core.tsFile · 0.90
boxFunctionFunction · 0.90
boxFunction · 0.90

Calls 12

canonicalOrderFunction · 0.90
isFunctionFunction · 0.90
symbolFormFunction · 0.85
invisibleOperatorFormFunction · 0.85
numberFormFunction · 0.85
multiplyFormFunction · 0.85
addFormFunction · 0.85
powerFormFunction · 0.85
divideFormFunction · 0.85
flattenFormFunction · 0.85
_inScopeMethod · 0.65
functionMethod · 0.65

Tested by

no test coverage detected