/** DECIMAL first-class member — Slice 1 shared contract (TS↔Python parity). * * KERN's Decimal surface lowers to decimal.js on the TS leg and Python's stdlib * `decimal` module on the Python leg. A de-risking probe (decimal.js 10.6.0 vs * CPython `decimal`, 150 cases) proved that ARITHMETIC VALUE parity is byte-exact * once both engines are configured to the SAME context (28 significant digits, * ROUND_HALF_EVEN): `Decimal("0.1") + Decimal("0.2")` renders as exactly `0.3` on * BOTH legs, division/precision/rounding all agree. * * The ONE fundamental divergence is SIGNIFICANCE / SCALE. Python's `decimal` * preserves the scale of a literal and rides it through arithmetic: * str(Decimal("1.10")) == "1.10" (trailing zero kept) * str(Decimal("0.00")) == "0.00" * str(Decimal("1E+2")) == "1E+2" * str(Decimal("-0")) == "-0" * decimal.js NORMALIZES significance away — it has no concept of scale: * new Decimal("1.10").toString() === "1.1" * new Decimal("0.00").toString() === "0" * new Decimal("1E+2").toString() === "100" * new Decimal("-0").toString() === "0" * * Because KERN's invariant is byte-exact cross-target parity, KERN does NOT * promise that `str(Decimal("1.10")) == "1.10"`. The v1 contract binds Decimal to * NUMERIC semantics with a KERN-owned canonical stringifier, and FAILS CLOSED — at * compile time, symmetrically on BOTH legs — for any Decimal string literal that * carries scale/significance the two engines render differently. That is the set * this module detects: a portable Decimal literal is one whose VALUE and canonical * rendering are identical on both engines, so no scale promise is ever made. * * Both the TS emitter (`codegen-expression.ts`) and the Python emitter * (`codegen-body-python.ts`) import {@link assertPortableDecimalLiteral} and * {@link decimalScaleFailMessage} from THIS one module, so the refusal is * byte-identical (single-sourced) across targets — exactly the regex fail-close * pattern (`scanRegexAstral` / `regexAstralFailMessage`). */ export { assertPortableDecimalLiteral, DECIMAL_DIV_ZERO_FAILCLOSE, DECIMAL_MOD_ZERO_FAILCLOSE, DECIMAL_POW_ZERO_NEGATIVE_EXP_FAILCLOSE, DECIMAL_SCALE_FAILCLOSE, DECIMAL_TS_PACKAGE, decimalScaleFailMessage, isPortableDecimalLiteral, } from '../decimal/contract.js'; export { DECIMAL_POW_NON_INTEGER_EXP_FAILCLOSE, decimalPowFailMessage, decimalZeroDivisorFailMessage, } from '../decimal/probe-gates.js'; /** Render the TS-leg preamble that a `Decimal.*` lowering requires: the * `decimal.js` import PLUS a one-time global context configuration that mirrors * CPython's DEFAULT decimal context, so the two engines agree byte-for-byte on * ARITHMETIC results (the de-risking probe's qualified-pass envelope): * - precision 28 significant digits (Python default `Context().prec`) * - ROUND_HALF_EVEN (banker's rounding) (Python default rounding) * - toExpNeg/-toExpPos kept at decimal.js defaults (-7 / 21); KERN's canonical * Decimal literal set is fail-closed to forms whose value rendering agrees * regardless of the sci-notation threshold, so the exponent knobs do not * affect any IN-CORE literal. * * This is the KERN-OWNED canonical-context rule applied to the TS leg. The * Python leg gets the same numeric envelope for free from its stdlib default * context (prec 28, ROUND_HALF_EVEN), so no Python preamble is needed beyond the * `import decimal as __k_decimal` the `requires.py: 'decimal'` path injects. */ export declare function decimalImportLineTS(): string; /** Diagnostic for a `Decimal(...)` construction whose argument is NOT a string * literal. v1 only supports construction from a STRING literal (the string form * avoids float-precision loss: `Decimal(0.1)` would already have lost precision * before the call). A non-literal arg (an ident, a number literal, an expression) * is refused symmetrically on both legs. */ export declare const DECIMAL_NON_STRING_LITERAL_FAILCLOSE = "Decimal construction requires a string literal argument"; export declare function decimalNonStringLiteralFailMessage(): string; /** Diagnostic for the BARE `Decimal(...)` construction form. Slice 1 registers * the canonical `Decimal.of(...)` / `Decimal.add(...)` namespace dispatch; the * bare `Decimal(...)` call would otherwise verbatim-emit an undefined global on * both legs (no `import` is injected for it), so it is fail-closed SYMMETRICALLY * with a redirect to `Decimal.of`. (The `+` operator and bare construction are * deferred to a typed-IR slice — see the Slice-1 report.) */ export declare const DECIMAL_BARE_CONSTRUCTION_FAILCLOSE = "Bare Decimal(...) construction is not portable in Slice 1"; export declare function decimalBareConstructionFailMessage(): string; /** DECIMAL Slice 2 (item 3) — fail-close prefix for the `+`/`-`/`*` operator on * syntactically-proven Decimal operands. Today `Decimal.of("1.5") + Decimal.of("2.5")` * would emit `a + b` on both legs — and on TS, decimal.js's `+` invokes `.valueOf()` * and DEGRADES TO FLOAT (silent precision loss + a TS↔Python divergence). We block * it at compile time with a byte-identical diagnostic on both targets. This is the * syntactic SEED of slice-3's `provenType:'decimal'` typed-IR work; it is * deliberately CONSERVATIVE — see {@link isSyntacticDecimalProducer}. */ export declare const DECIMAL_OPERATOR_FAILCLOSE = "Decimal does not support the arithmetic operator"; /** Build the byte-identical compile-error for a blocked Decimal operator. Selected * only by the offending operator, so the refusal is observably symmetric across * TS and Python (both legs throw this exact text). */ export declare function decimalOperatorFailMessage(op: string): string; /** TS-leg helper functions for the divergent Decimal ops, single-sourced here and * rendered into the file-level decimal preamble (alongside the `decimal.js` * import) by `kernStdlibPreamble`. Each takes two `Decimal` values and returns a * `Decimal`, with the SYMMETRIC zero/pow guards throwing the byte-identical KERN * diagnostics above. `Decimal.div/mod/pow` lower to calls into these. * * `__k_decimal_pow_int` mirrors the Python helper exactly: it special-cases * `0**0 → 1` (so both legs agree even though Python's `**` raises for that input) * and preflights `0**neg` to the zero-divide error. The compile-time fail-close * already guarantees the exponent is an integer literal and the base non-negative, * so the helper does no further integer validation — it only encodes the two * value-level guards that keep a non-finite Decimal off the comparison surface. */ export declare function decimalOpsHelpersTS(): string; /** Python-leg twin of {@link decimalOpsHelpersTS}: the SAME three guarded helpers, * defined in Python and registered into the prelude (via the `decimal-ops` * requirement). `__k_decimal_pow_int` special-cases `0**0 → 1` BEFORE the native * `**` — load-bearing, because under the DEFAULT decimal context (which the * generated Python runs under — prec/rounding are set on `getcontext()` but the * `InvalidOperation` trap stays ENABLED at its default), `Decimal("0") ** * Decimal("0")` RAISES `InvalidOperation`, whereas decimal.js returns `1` * (empirically verified, CPython 3.12 + decimal.js 10.6.0). The special-case makes * both legs agree on `1`. `0**neg` is preflighted to the same byte-identical KERN * zero-error string the TS leg throws — neither engine's native error is ever * observed because the guard runs first. The `from decimal import Decimal as * _KernDecimal` import rides along so `_KernDecimal(1)` resolves. */ export declare const KERN_DECIMAL_OPS_HELPER_PY: string; export declare function decimalOfLiteralValue(node: unknown): string | null; export declare function assertPortableDecimalPow(base: unknown, exp: unknown): void; export declare function assertNonZeroDecimalDivisor(op: string, divisor: unknown): void; /** Diagnostic prefix when a `Decimal.` argument is a provably-non-Decimal * literal. Both legs throw this identical text (single-sourced), so the refusal is * byte-identical across TS and Python. */ export declare const DECIMAL_NON_DECIMAL_OPERAND_FAILCLOSE = "Decimal operation requires Decimal operands"; /** Diagnostic prefix when a `Decimal.` argument is a UNARY-prefixed expression * (`-Decimal.of("0")`, `~d`, `!d`). Both legs throw this identical text, so the * refusal is byte-identical across TS and Python. A separate constant from the * non-Decimal-literal family because it points users at the portable fix * (`Decimal.neg(x)`), which a raw host-number literal does not have. */ export declare const DECIMAL_UNARY_OPERAND_FAILCLOSE = "Decimal operation requires Decimal operands"; /** Build the byte-identical compile-error for a non-Decimal operand passed to a * Decimal binary/unary op. Selected only by the offending method + operand kind, * so the refusal is observably symmetric across TS and Python. */ export declare function decimalNonDecimalOperandFailMessage(method: string, operandKind: string): string; /** Build the byte-identical compile-error for a UNARY-prefixed operand (`-x`, `~x`, * `!x`) passed to a Decimal binary/unary op. A unary operator on a real Decimal * silently DEGRADES on the TS leg only: JS `-new Decimal("0")` invokes decimal.js's * `.valueOf()`, coercing the Decimal to a primitive (`-0`) BEFORE the helper sees it, * so the guarded helper's `b.isZero()` throws a bare host `TypeError` instead of the * KERN diagnostic — while Python's `-Decimal("0")` keeps a real Decimal and raises the * INTENDED `KERN Decimal division by zero`. That asymmetry (same root as the natural * `+`/`-`/`*` operator fail-close: decimal.js operators degrade via `.valueOf()`, here * via UNARY minus/plus) is closed by refusing every unary-wrapped operand up front. * Both legs throw this identical text (single-sourced), so the refusal is symmetric. */ export declare function decimalUnaryOperandFailMessage(method: string, op: string): string; /** Throw the symmetric fail-close when any argument of a `Decimal.` call * (other than the `Decimal.of` constructor, whose arg is a validated STRING literal) * is unsafe across targets. Two refusals, checked in order per operand: * 1. UNARY-prefixed (`-Decimal.of("0")`, `~d`, `!d`, `-0.1`) — see {@link topLevelUnaryOp}. * A unary on a Decimal degrades on the TS leg via decimal.js's `.valueOf()` * (the `Decimal.div(Decimal.of("1"), -Decimal.of("0"))` repro: TS throws a bare * `TypeError`, Python raises the KERN diagnostic). ALWAYS refused, but the * remediation text is ROUTED by the unary's `.argument`: a unary that wraps a * provably-non-Decimal LITERAL is a SIGNED HOST NUMBER (`-0.1`, `-5`), whose real * fix is `Decimal.of("0.1")` — so it gets the {@link decimalNonDecimalOperandFailMessage} * ("use Decimal.of(...)"), NOT the misleading `Decimal.neg(x)` advice (`Decimal.neg(0.1)` * is itself invalid). A unary that wraps a potential Decimal PRODUCER * (`-Decimal.of("0")` → `unary(call)`, `-d` → `unary(ident)`, …) keeps the * `Decimal.neg(x)`-pointing {@link decimalUnaryOperandFailMessage}, whose * `.valueOf()`-degradation rationale actually applies. * 2. A provably-non-Decimal LITERAL (`0.1`, `"x"`, `true`, …) — see * {@link nonDecimalOperandKind} — refused with {@link decimalNonDecimalOperandFailMessage}. * Called from BOTH legs' dispatch site with the SAME `{method, args}`, so the refusal * is byte-identical. A no-op for `of` and for operands that are neither a unary nor a * provably-non-Decimal literal (idents/calls/members flow through — they MAY be a * Decimal; KERN has no typed IR to prove otherwise, the conservative/sound default). * * WRAPPER-BYPASS CLOSURE: each operand is first run through * {@link unwrapTransparentDecimalOperand}, which peels every `typeAssert`/`nonNull` * transparent wrapper (recursively, in any nesting/combination), BEFORE both checks. * Without this, a wrapped operand slips past BOTH: `(-Decimal.of("0") as Decimal)` * (`typeAssert(unary(call))`) is not a top-level `unary` and not a literal kind, so it * used to emit `(-new Decimal("0") as Decimal)` on the TS leg (the unary `.valueOf()` * TypeError) while Python kept a Decimal — ASYMMETRIC; and `(0.1 as any)` * (`typeAssert(numLit)`) hid a non-Decimal literal, re-opening the silent-boolean * divergence through a cast. Both checks operate on the UNWRAPPED node, so the refusal * stays symmetric across legs (single-sourced) for every wrapper shape. */ export declare function assertDecimalOperands(method: string, args: ReadonlyArray): void; /** True iff `node` is, BY SYNTAX ALONE, unambiguously a Decimal-producing call: * a call whose callee is a member access `Decimal.` on the literal `Decimal` * namespace identifier AND whose method is one of the KNOWN Decimal-producing * members (`Decimal.of(...)`, `Decimal.add(...)`, `Decimal.sub(...)`, …). * * CONSERVATIVE BY DESIGN (the critical soundness property): it fires ONLY on this * exact shape. It does NOT track types through variables, params, returns, or * member chains — a `let d = Decimal.of("1.5"); d + x` is NOT caught here (that * needs the typed IR of slice 3; a `// SLICE 3:` note marks the generalization). * It therefore can NEVER false-fire on ordinary numeric `+`/`-`/`*` (a `numLit`, * `ident`, plain `call`, member-read, etc. all return false). * * Finding-2 narrowing: the method MUST be a known producer. `Decimal.nope(...)` * is NOT a producer, so it returns false here and the binary-emit site lowers * its operands normally — surfacing the unknown-member / non-canonical-literal * diagnostic the operand actually has, rather than the generic operator error. */ export declare function isSyntacticDecimalProducer(node: unknown): boolean; /** DECIMAL Slice 2 (item 3) — throw the symmetric operator fail-close when a binary * `+`/`-`/`*` has an operand that is a syntactically-proven Decimal producer. A * no-op for every other operator and for operands that are not the proven shape, so * ordinary numeric arithmetic is completely unaffected. Called from BOTH legs' * binary-emit site with the SAME `{op, left, right}` shape, so the refusal is * byte-identical. * * SLICE 3: generalize the operand test from `isSyntacticDecimalProducer` (syntax * only) to a typed-IR `provenType === 'decimal'` check so Decimal values that flow * through a binding/param/return are also caught — and `+`/`-`/`*` can then be * lowered (dispatched to `.plus()`/`.minus()`/`.times()` on TS) rather than refused. */ export declare function assertNoDecimalOperator(node: { op: string; left: unknown; right: unknown; }): void;