@hviana/sema 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/dist/example/demo.d.ts +1 -0
  2. package/dist/example/demo.js +39 -0
  3. package/dist/example/train_base.d.ts +87 -0
  4. package/dist/example/train_base.js +2216 -0
  5. package/dist/src/alphabet.d.ts +7 -0
  6. package/dist/src/alphabet.js +33 -0
  7. package/dist/src/alu/src/alu.d.ts +185 -0
  8. package/dist/src/alu/src/alu.js +440 -0
  9. package/dist/src/alu/src/expr.d.ts +61 -0
  10. package/dist/src/alu/src/expr.js +318 -0
  11. package/dist/src/alu/src/index.d.ts +11 -0
  12. package/dist/src/alu/src/index.js +19 -0
  13. package/dist/src/alu/src/kernel-arith.d.ts +16 -0
  14. package/dist/src/alu/src/kernel-arith.js +264 -0
  15. package/dist/src/alu/src/kernel-bits.d.ts +19 -0
  16. package/dist/src/alu/src/kernel-bits.js +152 -0
  17. package/dist/src/alu/src/kernel-logic.d.ts +4 -0
  18. package/dist/src/alu/src/kernel-logic.js +60 -0
  19. package/dist/src/alu/src/kernel-nd.d.ts +3 -0
  20. package/dist/src/alu/src/kernel-nd.js +208 -0
  21. package/dist/src/alu/src/kernel-numeric.d.ts +54 -0
  22. package/dist/src/alu/src/kernel-numeric.js +366 -0
  23. package/dist/src/alu/src/operation.d.ts +168 -0
  24. package/dist/src/alu/src/operation.js +189 -0
  25. package/dist/src/alu/src/parser.d.ts +212 -0
  26. package/dist/src/alu/src/parser.js +469 -0
  27. package/dist/src/alu/src/resonance.d.ts +55 -0
  28. package/dist/src/alu/src/resonance.js +126 -0
  29. package/dist/src/alu/src/text.d.ts +31 -0
  30. package/dist/src/alu/src/text.js +73 -0
  31. package/dist/src/alu/src/value.d.ts +109 -0
  32. package/dist/src/alu/src/value.js +300 -0
  33. package/dist/src/alu/test/alu.test.d.ts +1 -0
  34. package/dist/src/alu/test/alu.test.js +764 -0
  35. package/dist/src/bytes.d.ts +14 -0
  36. package/dist/src/bytes.js +59 -0
  37. package/dist/src/config.d.ts +114 -0
  38. package/dist/src/config.js +96 -0
  39. package/dist/src/derive/src/deduction.d.ts +125 -0
  40. package/dist/src/derive/src/deduction.js +155 -0
  41. package/dist/src/derive/src/index.d.ts +7 -0
  42. package/dist/src/derive/src/index.js +11 -0
  43. package/dist/src/derive/src/priority-queue.d.ts +20 -0
  44. package/dist/src/derive/src/priority-queue.js +73 -0
  45. package/dist/src/derive/src/rewrite.d.ts +56 -0
  46. package/dist/src/derive/src/rewrite.js +100 -0
  47. package/dist/src/derive/src/trie.d.ts +90 -0
  48. package/dist/src/derive/src/trie.js +217 -0
  49. package/dist/src/derive/test/derive.test.d.ts +1 -0
  50. package/dist/src/derive/test/derive.test.js +122 -0
  51. package/dist/src/extension.d.ts +37 -0
  52. package/dist/src/extension.js +7 -0
  53. package/dist/src/geometry.d.ts +137 -0
  54. package/dist/src/geometry.js +430 -0
  55. package/dist/src/index.d.ts +15 -0
  56. package/dist/src/index.js +21 -0
  57. package/dist/src/ingest-cache.d.ts +41 -0
  58. package/dist/src/ingest-cache.js +161 -0
  59. package/dist/src/mind/articulation.d.ts +6 -0
  60. package/dist/src/mind/articulation.js +99 -0
  61. package/dist/src/mind/attention.d.ts +72 -0
  62. package/dist/src/mind/attention.js +894 -0
  63. package/dist/src/mind/canonical.d.ts +29 -0
  64. package/dist/src/mind/canonical.js +88 -0
  65. package/dist/src/mind/graph-search.d.ts +270 -0
  66. package/dist/src/mind/graph-search.js +847 -0
  67. package/dist/src/mind/index.d.ts +5 -0
  68. package/dist/src/mind/index.js +5 -0
  69. package/dist/src/mind/junction.d.ts +95 -0
  70. package/dist/src/mind/junction.js +262 -0
  71. package/dist/src/mind/learning.d.ts +47 -0
  72. package/dist/src/mind/learning.js +201 -0
  73. package/dist/src/mind/match.d.ts +111 -0
  74. package/dist/src/mind/match.js +422 -0
  75. package/dist/src/mind/mechanisms/alu.d.ts +4 -0
  76. package/dist/src/mind/mechanisms/alu.js +29 -0
  77. package/dist/src/mind/mechanisms/cast.d.ts +35 -0
  78. package/dist/src/mind/mechanisms/cast.js +447 -0
  79. package/dist/src/mind/mechanisms/confluence.d.ts +24 -0
  80. package/dist/src/mind/mechanisms/confluence.js +213 -0
  81. package/dist/src/mind/mechanisms/cover.d.ts +6 -0
  82. package/dist/src/mind/mechanisms/cover.js +179 -0
  83. package/dist/src/mind/mechanisms/extraction.d.ts +67 -0
  84. package/dist/src/mind/mechanisms/extraction.js +342 -0
  85. package/dist/src/mind/mechanisms/recall.d.ts +13 -0
  86. package/dist/src/mind/mechanisms/recall.js +151 -0
  87. package/dist/src/mind/mind.d.ts +147 -0
  88. package/dist/src/mind/mind.js +300 -0
  89. package/dist/src/mind/pipeline-mechanism.d.ts +142 -0
  90. package/dist/src/mind/pipeline-mechanism.js +213 -0
  91. package/dist/src/mind/pipeline.d.ts +20 -0
  92. package/dist/src/mind/pipeline.js +185 -0
  93. package/dist/src/mind/primitives.d.ts +43 -0
  94. package/dist/src/mind/primitives.js +162 -0
  95. package/dist/src/mind/rationale.d.ts +134 -0
  96. package/dist/src/mind/rationale.js +162 -0
  97. package/dist/src/mind/reasoning.d.ts +15 -0
  98. package/dist/src/mind/reasoning.js +162 -0
  99. package/dist/src/mind/recognition.d.ts +20 -0
  100. package/dist/src/mind/recognition.js +223 -0
  101. package/dist/src/mind/resonance.d.ts +23 -0
  102. package/dist/src/mind/resonance.js +0 -0
  103. package/dist/src/mind/trace.d.ts +15 -0
  104. package/dist/src/mind/trace.js +73 -0
  105. package/dist/src/mind/traverse.d.ts +100 -0
  106. package/dist/src/mind/traverse.js +447 -0
  107. package/dist/src/mind/types.d.ts +174 -0
  108. package/dist/src/mind/types.js +84 -0
  109. package/dist/src/rabitq-hnsw/src/database.d.ts +200 -0
  110. package/dist/src/rabitq-hnsw/src/database.js +388 -0
  111. package/dist/src/rabitq-hnsw/src/heap.d.ts +22 -0
  112. package/dist/src/rabitq-hnsw/src/heap.js +89 -0
  113. package/dist/src/rabitq-hnsw/src/hnsw.d.ts +125 -0
  114. package/dist/src/rabitq-hnsw/src/hnsw.js +474 -0
  115. package/dist/src/rabitq-hnsw/src/index.d.ts +10 -0
  116. package/dist/src/rabitq-hnsw/src/index.js +6 -0
  117. package/dist/src/rabitq-hnsw/src/prng.d.ts +19 -0
  118. package/dist/src/rabitq-hnsw/src/prng.js +36 -0
  119. package/dist/src/rabitq-hnsw/src/rabitq.d.ts +95 -0
  120. package/dist/src/rabitq-hnsw/src/rabitq.js +283 -0
  121. package/dist/src/rabitq-hnsw/src/store.d.ts +162 -0
  122. package/dist/src/rabitq-hnsw/src/store.js +825 -0
  123. package/dist/src/rabitq-hnsw/test/hnsw.test.d.ts +1 -0
  124. package/dist/src/rabitq-hnsw/test/hnsw.test.js +948 -0
  125. package/dist/src/store-sqlite.d.ts +149 -0
  126. package/dist/src/store-sqlite.js +702 -0
  127. package/dist/src/store.d.ts +638 -0
  128. package/dist/src/store.js +1618 -0
  129. package/dist/src/vec.d.ts +31 -0
  130. package/dist/src/vec.js +109 -0
  131. package/package.json +1 -1
@@ -0,0 +1,31 @@
1
+ /** An ASCII decimal digit. */
2
+ export declare const isDigitByte: (c: number) => boolean;
3
+ /** The decimal point of a numeral. */
4
+ export declare const DOT = 46;
5
+ /** The explicit list container's delimiters — the canonical nd spelling the
6
+ * codec emits, so a computed list feeds straight back in as an operand. */
7
+ export declare const OPEN = 91;
8
+ export declare const CLOSE = 93;
9
+ /** ASCII whitespace (space, tab, newline, carriage return) — the one spacing
10
+ * convention bytes of every textual modality share. */
11
+ export declare const isSpaceByte: (c: number) => boolean;
12
+ /** A byte that DELIMITS list elements inside an explicit container: whitespace,
13
+ * a comma, or a semicolon. Deliberately excludes anything that can sit inside
14
+ * a numeral or a symbol token (letters, digits, sign/decimal `+ - .`) and the
15
+ * brackets themselves, so an element's own bytes are never split. */
16
+ export declare const isSepByte: (c: number) => boolean;
17
+ /** The index of the first non-whitespace byte (or the length, if all space). */
18
+ export declare function trimStart(b: Uint8Array): number;
19
+ /** The index one past the last non-whitespace byte (or 0, if all space). */
20
+ export declare function trimEnd(b: Uint8Array): number;
21
+ /** The index of the `]` matching the `[` at `open`, bracket-depth aware, or -1
22
+ * when it is unbalanced — used to test whether a span is one explicit list
23
+ * CONTAINER (its opening bracket closing exactly at its end). */
24
+ export declare function matchBracket(b: Uint8Array, open: number): number;
25
+ /** The maximal non-whitespace runs of `bytes` within [from, to) — the spacing
26
+ * floor's reading of "the tokens", shared by the structural host's segmenter
27
+ * and the parser's term lexing so neither re-implements the walk. */
28
+ export declare function nonSpaceRuns(bytes: Uint8Array, from?: number, to?: number): Array<{
29
+ i: number;
30
+ j: number;
31
+ }>;
@@ -0,0 +1,73 @@
1
+ // text.ts — the byte-class floor the ALU's lexing rests on.
2
+ //
3
+ // SEMA is byte-native and multimodal; the ALU keeps its knowledge of raw text
4
+ // as small as possible. What remains is the IRREDUCIBLE floor — the decimal
5
+ // numeral grammar (digits ground a quantity exactly as the byte alphabet
6
+ // grounds a symbol), whitespace as the universal spacing byte, and the list
7
+ // grammar's delimiters — collected HERE, once, so no other module hardcodes a
8
+ // character class of its own. Everything above this floor (which spans name
9
+ // operations, where terms begin and end, what an expression means) is resolved
10
+ // from the operation registry, the host's geometric segmentation, or resonance.
11
+ /** An ASCII decimal digit. */
12
+ export const isDigitByte = (c) => c >= 0x30 && c <= 0x39;
13
+ /** The decimal point of a numeral. */
14
+ export const DOT = 0x2e; // "."
15
+ /** The explicit list container's delimiters — the canonical nd spelling the
16
+ * codec emits, so a computed list feeds straight back in as an operand. */
17
+ export const OPEN = 0x5b; // "["
18
+ export const CLOSE = 0x5d; // "]"
19
+ /** ASCII whitespace (space, tab, newline, carriage return) — the one spacing
20
+ * convention bytes of every textual modality share. */
21
+ export const isSpaceByte = (c) => c === 0x20 || c === 0x09 || c === 0x0a || c === 0x0d;
22
+ /** A byte that DELIMITS list elements inside an explicit container: whitespace,
23
+ * a comma, or a semicolon. Deliberately excludes anything that can sit inside
24
+ * a numeral or a symbol token (letters, digits, sign/decimal `+ - .`) and the
25
+ * brackets themselves, so an element's own bytes are never split. */
26
+ export const isSepByte = (c) => isSpaceByte(c) || c === 0x2c /* , */ || c === 0x3b /* ; */;
27
+ /** The index of the first non-whitespace byte (or the length, if all space). */
28
+ export function trimStart(b) {
29
+ let i = 0;
30
+ while (i < b.length && isSpaceByte(b[i]))
31
+ i++;
32
+ return i;
33
+ }
34
+ /** The index one past the last non-whitespace byte (or 0, if all space). */
35
+ export function trimEnd(b) {
36
+ let j = b.length;
37
+ const lo = trimStart(b);
38
+ while (j > lo && isSpaceByte(b[j - 1]))
39
+ j--;
40
+ return j;
41
+ }
42
+ /** The index of the `]` matching the `[` at `open`, bracket-depth aware, or -1
43
+ * when it is unbalanced — used to test whether a span is one explicit list
44
+ * CONTAINER (its opening bracket closing exactly at its end). */
45
+ export function matchBracket(b, open) {
46
+ let depth = 0;
47
+ for (let i = open; i < b.length; i++) {
48
+ if (b[i] === OPEN)
49
+ depth++;
50
+ else if (b[i] === CLOSE && --depth === 0)
51
+ return i;
52
+ }
53
+ return -1;
54
+ }
55
+ /** The maximal non-whitespace runs of `bytes` within [from, to) — the spacing
56
+ * floor's reading of "the tokens", shared by the structural host's segmenter
57
+ * and the parser's term lexing so neither re-implements the walk. */
58
+ export function nonSpaceRuns(bytes, from = 0, to = bytes.length) {
59
+ const out = [];
60
+ let i = from;
61
+ while (i < to) {
62
+ if (isSpaceByte(bytes[i])) {
63
+ i++;
64
+ continue;
65
+ }
66
+ let j = i;
67
+ while (j < to && !isSpaceByte(bytes[j]))
68
+ j++;
69
+ out.push({ i, j });
70
+ i = j;
71
+ }
72
+ return out;
73
+ }
@@ -0,0 +1,109 @@
1
+ /** Which stratum a {@link Value} belongs to. Four scalar domains + the
2
+ * recursive container `nd`. */
3
+ export type Domain = "bit" | "int" | "real" | "symbol" | "nd";
4
+ /** A quantity the ALU computes on — a tagged union over the scalar domains plus
5
+ * the recursive n-dimensional container. An `nd`'s `items` are themselves
6
+ * Values of any domain, so nesting (a matrix = an nd of nd) and heterogeneity
7
+ * (a list mixing numbers, symbols, sub-lists) are the same case. */
8
+ export type Value = {
9
+ domain: "bit";
10
+ b: 0 | 1;
11
+ } | {
12
+ domain: "int";
13
+ n: bigint;
14
+ } | {
15
+ domain: "real";
16
+ x: number;
17
+ } | {
18
+ domain: "symbol";
19
+ bytes: Uint8Array;
20
+ } | {
21
+ domain: "nd";
22
+ items: Value[];
23
+ };
24
+ export declare const bit: (b: 0 | 1) => Value;
25
+ export declare const int: (n: bigint) => Value;
26
+ export declare const real: (x: number) => Value;
27
+ /** A symbolic value: an opaque byte span of any modality (see module note). */
28
+ export declare const symbol: (bytes: Uint8Array) => Value;
29
+ /** An n-dimensional value: an ordered list of element values (each any domain,
30
+ * including a nested nd). See the module note on nd. */
31
+ export declare const nd: (items: Value[]) => Value;
32
+ /** The domain tag of a value. */
33
+ export declare const tagOf: (v: Value) => Domain;
34
+ /** Whether a value is a numeric SCALAR (bit, int, or real) — an nd is not, even
35
+ * if every element is numeric (it is a container; reduce it to a scalar). */
36
+ export declare function isNumeric(v: Value): boolean;
37
+ /** Whether a value is the n-dimensional container. */
38
+ export declare function isNd(v: Value): v is {
39
+ domain: "nd";
40
+ items: Value[];
41
+ };
42
+ /** Collect, into `out`, every SYMBOL byte span reachable inside a value — itself
43
+ * when it is a symbol, or each element recursively when it is an nd. These are
44
+ * the spans whose MEANING (a resonant opposite, the operation a higher-order op
45
+ * argument names) the host pre-resolves before a computation; numbers carry no
46
+ * meaning to resolve, so they are skipped. */
47
+ export declare function symbolSpans(v: Value, out: Uint8Array[]): void;
48
+ /** A value as a JS number — bit→0|1, int→Number, real as-is. Throws on a
49
+ * symbol, because arithmetic on an opaque form is a programming error the
50
+ * caller (or the graph adapter's try/catch) should surface, never silently
51
+ * coerce to NaN. */
52
+ export declare function asReal(v: Value): number;
53
+ /** A value as an exact bigint — real is truncated toward zero (a real that is
54
+ * not integral loses its fraction; callers wanting exactness keep it int). */
55
+ export declare function asInt(v: Value): bigint;
56
+ /** A value as a logic bit — nonzero numbers read as 1, zero as 0. Throws on a
57
+ * symbol (an opaque form is not a truth value). */
58
+ export declare function asBit(v: Value): 0 | 1;
59
+ /** Promote a value into a target numeric domain (bit ⊂ int ⊂ real). Used when
60
+ * a mixed-domain expression must agree on one domain before a primitive runs —
61
+ * the rule is the natural lattice: any real operand pulls the whole expression
62
+ * to real, otherwise any int pulls it to int, otherwise bit. */
63
+ export declare function coerce(v: Value, domain: "bit" | "int" | "real"): Value;
64
+ /** The common numeric domain of a list of operands, by the bit ⊂ int ⊂ real
65
+ * lattice: real if any operand is real, else int if any is int, else bit.
66
+ * Throws if any operand is a symbol. This is how a polymorphic arithmetic op
67
+ * decides whether to take the exact bigint path or the native-double path. */
68
+ export declare function joinDomain(args: Value[]): "bit" | "int" | "real";
69
+ /** The host's bridge between byte spans and values. ALU emits and consumes
70
+ * bytes only through this, so the surrounding system owns how a number is
71
+ * spelled. A default ({@link decimalCodec}) is provided. */
72
+ export interface ValueCodec {
73
+ encode(v: Value): Uint8Array;
74
+ decode(bytes: Uint8Array): Value | null;
75
+ }
76
+ /** Parse a byte span into a SCALAR value: a clean integer numeral → exact int, a
77
+ * fractional/exponent numeral → real, anything else → a symbol carrying its
78
+ * bytes verbatim for the polymorphic inverse.
79
+ *
80
+ * This is the IRREDUCIBLE FLOOR — and the ONLY structure read at the value
81
+ * model's base: a number-form's digits ground a quantity, the exact analogue of
82
+ * the byte alphabet grounding a symbol. It does NOT read n-dimensional
83
+ * STRUCTURE (no bracket/comma grammar); recognising a list — a run of element
84
+ * values joined by a consistent separator — is layered ONE level up, in the
85
+ * facade's {@link "./alu.js".Alu.recogniseValue} (over the operand scanner),
86
+ * which calls this for each element. So the codec's `decode` (which delegates
87
+ * here) is scalar-only: a bracket literal decodes to an opaque symbol.
88
+ *
89
+ * Numerals are ASCII by construction; for any other byte content the bytes are
90
+ * preserved opaquely, so a symbol of any modality round-trips untouched. */
91
+ export declare function parseValue(bytes: Uint8Array): Value;
92
+ /** Canonically format a real as a decimal string: round to `precision` places,
93
+ * then trim trailing zeros (and a bare trailing point), normalising -0 to 0.
94
+ * Determinism here is load-bearing — the search keys an output span by its
95
+ * bytes, so two derivations of the same number MUST spell it identically. */
96
+ export declare function formatReal(x: number, precision: number): string;
97
+ /** The default codec: integers as exact decimals, reals canonically formatted,
98
+ * bits as "0"/"1", symbols as their own bytes (verbatim, any modality), and an
99
+ * nd as a bracket literal `[e0,e1,…]` of its element encodings (recursive, so a
100
+ * nested list nests its brackets and a symbol element keeps its raw bytes).
101
+ * `precision` controls real rounding (see {@link formatReal}).
102
+ *
103
+ * The bracket layout is the CANONICAL OUTPUT spelling of an `nd` — one
104
+ * deterministic form, the analogue of decimal for a number, so the search's
105
+ * chart memoises identical results identically. It is an ENCODE-only grammar:
106
+ * {@link parseValue} (hence `decode`) reads only scalars, never structure — a
107
+ * bracket literal decodes back to an opaque symbol. Reading list STRUCTURE from
108
+ * bytes is layered one level up, in {@link "./alu.js".Alu.recogniseValue}. */
109
+ export declare function decimalCodec(precision: number): ValueCodec;
@@ -0,0 +1,300 @@
1
+ // value.ts — the ALU value model.
2
+ //
3
+ // One tagged union carries every kind of quantity the ALU computes on, so a
4
+ // single Operation registry can dispatch on the tag rather than maintaining
5
+ // disjoint kernels. The four domains are exactly the strata the kernel needs:
6
+ //
7
+ // • bit — a logic value 0|1. The completeness layer (nand and the gates
8
+ // derived from it) lives here.
9
+ // • int — an EXACT integer (JS bigint). The "everything derives from nand"
10
+ // bootstrap runs here: add is a ripple of full_adders, multiply is
11
+ // shift-add, and so on. bigint keeps the proof exact past 2^53.
12
+ // • real — an IEEE double. The limit layer (converge, exp, sin, …) is
13
+ // intrinsically continuous, so it runs on the host's native reals
14
+ // rather than a fixed-point reimplementation atop bits.
15
+ // • symbol — a raw BYTE SPAN that is not a number. SEMA is byte-native and
16
+ // multimodal: a symbol's bytes may name a written form, a region of
17
+ // an image, a fragment of audio — any learned form, in any
18
+ // modality. ALU never interprets those bytes; it treats a symbol
19
+ // as an opaque token. This is the carrier for the POLYMORPHIC
20
+ // inverse: the inverse of a number is its negation, but the inverse
21
+ // of a symbol is its RESONANT OPPOSITE — found in the resonance
22
+ // space, not by arithmetic (e.g. the bytes of "large" resolve to
23
+ // "small", but the mechanism is modality-agnostic — see
24
+ // resonance.ts). Resonance is the only thing that can read meaning
25
+ // from a symbol, and ALU reaches it only through an injected
26
+ // capability, never directly.
27
+ //
28
+ // • nd — an N-DIMENSIONAL value: an ordered list of elements, each of which
29
+ // is ITSELF a Value of ANY domain — a scalar (bit/int/real/symbol)
30
+ // OR another nd. So nd is recursive (a tensor is an nd of nds), it
31
+ // is RAGGED (rows need not be equal length — it is a list, not a
32
+ // rectangular array), and it is HETEROGENEOUS (a row may mix a
33
+ // number, a symbol, and a sub-list). This one recursive case is the
34
+ // whole generalisation: there is no separate "vector" vs "matrix"
35
+ // type, only nd nesting, and the rank is read off the nesting depth.
36
+ // Every scalar operation lifts over it automatically (broadcast, see
37
+ // operation.ts), and the structural operations (the nd kernel) build
38
+ // the list-processing layer — map/reduce/filter/find/… — on a tiny
39
+ // core of nd/length/at, exactly as logic builds on nand.
40
+ //
41
+ // The module is pure: it knows nothing of SEMA, the store, or the search. The
42
+ // only host coupling is the injected ValueCodec, whose default lives here.
43
+ // ── constructors ──────────────────────────────────────────────────────────
44
+ export const bit = (b) => ({ domain: "bit", b });
45
+ export const int = (n) => ({ domain: "int", n });
46
+ export const real = (x) => ({ domain: "real", x });
47
+ /** A symbolic value: an opaque byte span of any modality (see module note). */
48
+ export const symbol = (bytes) => ({
49
+ domain: "symbol",
50
+ bytes,
51
+ });
52
+ /** An n-dimensional value: an ordered list of element values (each any domain,
53
+ * including a nested nd). See the module note on nd. */
54
+ export const nd = (items) => ({ domain: "nd", items });
55
+ /** The domain tag of a value. */
56
+ export const tagOf = (v) => v.domain;
57
+ /** Whether a value is a numeric SCALAR (bit, int, or real) — an nd is not, even
58
+ * if every element is numeric (it is a container; reduce it to a scalar). */
59
+ export function isNumeric(v) {
60
+ return v.domain === "bit" || v.domain === "int" || v.domain === "real";
61
+ }
62
+ /** Whether a value is the n-dimensional container. */
63
+ export function isNd(v) {
64
+ return v.domain === "nd";
65
+ }
66
+ /** Collect, into `out`, every SYMBOL byte span reachable inside a value — itself
67
+ * when it is a symbol, or each element recursively when it is an nd. These are
68
+ * the spans whose MEANING (a resonant opposite, the operation a higher-order op
69
+ * argument names) the host pre-resolves before a computation; numbers carry no
70
+ * meaning to resolve, so they are skipped. */
71
+ export function symbolSpans(v, out) {
72
+ if (v.domain === "symbol")
73
+ out.push(v.bytes);
74
+ else if (isNd(v)) {
75
+ for (const e of v.items)
76
+ symbolSpans(e, out);
77
+ }
78
+ }
79
+ // ── coercions (the functor's object map between domains) ────────────────────
80
+ /** A value as a JS number — bit→0|1, int→Number, real as-is. Throws on a
81
+ * symbol, because arithmetic on an opaque form is a programming error the
82
+ * caller (or the graph adapter's try/catch) should surface, never silently
83
+ * coerce to NaN. */
84
+ export function asReal(v) {
85
+ switch (v.domain) {
86
+ case "bit":
87
+ return v.b;
88
+ case "int":
89
+ return Number(v.n);
90
+ case "real":
91
+ return v.x;
92
+ case "symbol":
93
+ throw new TypeError("asReal: a symbol value has no numeric reading");
94
+ case "nd":
95
+ // An nd has no scalar reading: a scalar primitive never sees one, because
96
+ // broadcast (operation.ts) lifts it element-wise first. Reaching here
97
+ // means a structural op was mis-called on a list as if it were a scalar.
98
+ throw new TypeError("asReal: an nd value has no scalar numeric reading");
99
+ }
100
+ }
101
+ /** A value as an exact bigint — real is truncated toward zero (a real that is
102
+ * not integral loses its fraction; callers wanting exactness keep it int). */
103
+ export function asInt(v) {
104
+ switch (v.domain) {
105
+ case "bit":
106
+ return BigInt(v.b);
107
+ case "int":
108
+ return v.n;
109
+ case "real":
110
+ return BigInt(Math.trunc(v.x));
111
+ case "symbol":
112
+ throw new TypeError("asInt: a symbol value has no integer reading");
113
+ case "nd":
114
+ throw new TypeError("asInt: an nd value has no scalar integer reading");
115
+ }
116
+ }
117
+ /** A value as a logic bit — nonzero numbers read as 1, zero as 0. Throws on a
118
+ * symbol (an opaque form is not a truth value). */
119
+ export function asBit(v) {
120
+ switch (v.domain) {
121
+ case "bit":
122
+ return v.b;
123
+ case "int":
124
+ return v.n === 0n ? 0 : 1;
125
+ case "real":
126
+ return v.x === 0 ? 0 : 1;
127
+ case "symbol":
128
+ throw new TypeError("asBit: a symbol value is not a truth value");
129
+ case "nd":
130
+ throw new TypeError("asBit: an nd value is not a scalar truth value");
131
+ }
132
+ }
133
+ /** Promote a value into a target numeric domain (bit ⊂ int ⊂ real). Used when
134
+ * a mixed-domain expression must agree on one domain before a primitive runs —
135
+ * the rule is the natural lattice: any real operand pulls the whole expression
136
+ * to real, otherwise any int pulls it to int, otherwise bit. */
137
+ export function coerce(v, domain) {
138
+ if (v.domain === domain)
139
+ return v;
140
+ switch (domain) {
141
+ case "bit":
142
+ return bit(asBit(v));
143
+ case "int":
144
+ return int(asInt(v));
145
+ case "real":
146
+ return real(asReal(v));
147
+ }
148
+ }
149
+ /** The common numeric domain of a list of operands, by the bit ⊂ int ⊂ real
150
+ * lattice: real if any operand is real, else int if any is int, else bit.
151
+ * Throws if any operand is a symbol. This is how a polymorphic arithmetic op
152
+ * decides whether to take the exact bigint path or the native-double path. */
153
+ export function joinDomain(args) {
154
+ let d = "bit";
155
+ for (const a of args) {
156
+ if (a.domain === "symbol") {
157
+ throw new TypeError("joinDomain: cannot join a symbol value numerically");
158
+ }
159
+ if (a.domain === "nd") {
160
+ throw new TypeError("joinDomain: cannot join an nd value numerically");
161
+ }
162
+ if (a.domain === "real")
163
+ return "real";
164
+ if (a.domain === "int" && d === "bit")
165
+ d = "int";
166
+ }
167
+ return d;
168
+ }
169
+ const ASCII = {
170
+ decode: (b) => {
171
+ let s = "";
172
+ for (let i = 0; i < b.length; i++)
173
+ s += String.fromCharCode(b[i]);
174
+ return s;
175
+ },
176
+ encode: (s) => {
177
+ const out = new Uint8Array(s.length);
178
+ for (let i = 0; i < s.length; i++)
179
+ out[i] = s.charCodeAt(i) & 0xff;
180
+ return out;
181
+ },
182
+ };
183
+ /** A decimal numeral, sign, optional fraction, optional exponent — the syntax
184
+ * {@link parseScalar} accepts as a number. Anything else is a symbol. */
185
+ const NUMERAL = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/;
186
+ /** Concatenate byte arrays — used to build an nd literal's encoding without
187
+ * routing a (possibly non-ASCII, multimodal) symbol's bytes through a string. */
188
+ function concatBytes(arrs) {
189
+ let len = 0;
190
+ for (const a of arrs)
191
+ len += a.length;
192
+ const out = new Uint8Array(len);
193
+ let o = 0;
194
+ for (const a of arrs) {
195
+ out.set(a, o);
196
+ o += a.length;
197
+ }
198
+ return out;
199
+ }
200
+ /** Parse a SCALAR span: a clean integer numeral → exact int, a fractional/
201
+ * exponent numeral → real, anything else → a symbol carrying `bytes` verbatim.
202
+ * Strict, so it never hijacks a form that merely begins with a digit. */
203
+ function parseScalar(bytes, s) {
204
+ if (s.length > 0 && NUMERAL.test(s)) {
205
+ if (/[.eE]/.test(s)) {
206
+ const x = Number(s);
207
+ if (Number.isFinite(x))
208
+ return real(x);
209
+ }
210
+ else {
211
+ // A clean integer: strip a leading '+' that BigInt rejects.
212
+ try {
213
+ return int(BigInt(s[0] === "+" ? s.slice(1) : s));
214
+ }
215
+ catch {
216
+ /* fall through to symbol */
217
+ }
218
+ }
219
+ }
220
+ return symbol(bytes);
221
+ }
222
+ /** Parse a byte span into a SCALAR value: a clean integer numeral → exact int, a
223
+ * fractional/exponent numeral → real, anything else → a symbol carrying its
224
+ * bytes verbatim for the polymorphic inverse.
225
+ *
226
+ * This is the IRREDUCIBLE FLOOR — and the ONLY structure read at the value
227
+ * model's base: a number-form's digits ground a quantity, the exact analogue of
228
+ * the byte alphabet grounding a symbol. It does NOT read n-dimensional
229
+ * STRUCTURE (no bracket/comma grammar); recognising a list — a run of element
230
+ * values joined by a consistent separator — is layered ONE level up, in the
231
+ * facade's {@link "./alu.js".Alu.recogniseValue} (over the operand scanner),
232
+ * which calls this for each element. So the codec's `decode` (which delegates
233
+ * here) is scalar-only: a bracket literal decodes to an opaque symbol.
234
+ *
235
+ * Numerals are ASCII by construction; for any other byte content the bytes are
236
+ * preserved opaquely, so a symbol of any modality round-trips untouched. */
237
+ export function parseValue(bytes) {
238
+ return parseScalar(bytes, ASCII.decode(bytes));
239
+ }
240
+ /** Canonically format a real as a decimal string: round to `precision` places,
241
+ * then trim trailing zeros (and a bare trailing point), normalising -0 to 0.
242
+ * Determinism here is load-bearing — the search keys an output span by its
243
+ * bytes, so two derivations of the same number MUST spell it identically. */
244
+ export function formatReal(x, precision) {
245
+ if (!Number.isFinite(x))
246
+ return String(x); // "Infinity" / "NaN" / "-Infinity"
247
+ let s = x.toFixed(precision);
248
+ if (s.indexOf(".") >= 0)
249
+ s = s.replace(/\.?0+$/, "");
250
+ if (s === "-0" || s === "")
251
+ s = "0";
252
+ return s;
253
+ }
254
+ /** The default codec: integers as exact decimals, reals canonically formatted,
255
+ * bits as "0"/"1", symbols as their own bytes (verbatim, any modality), and an
256
+ * nd as a bracket literal `[e0,e1,…]` of its element encodings (recursive, so a
257
+ * nested list nests its brackets and a symbol element keeps its raw bytes).
258
+ * `precision` controls real rounding (see {@link formatReal}).
259
+ *
260
+ * The bracket layout is the CANONICAL OUTPUT spelling of an `nd` — one
261
+ * deterministic form, the analogue of decimal for a number, so the search's
262
+ * chart memoises identical results identically. It is an ENCODE-only grammar:
263
+ * {@link parseValue} (hence `decode`) reads only scalars, never structure — a
264
+ * bracket literal decodes back to an opaque symbol. Reading list STRUCTURE from
265
+ * bytes is layered one level up, in {@link "./alu.js".Alu.recogniseValue}. */
266
+ export function decimalCodec(precision) {
267
+ const enc = (v) => {
268
+ switch (v.domain) {
269
+ case "bit":
270
+ return ASCII.encode(String(v.b));
271
+ case "int":
272
+ return ASCII.encode(v.n.toString());
273
+ case "real":
274
+ return ASCII.encode(formatReal(v.x, precision));
275
+ case "symbol":
276
+ return v.bytes;
277
+ case "nd": {
278
+ // [e0,e1,…] at the byte level, so a symbol element's arbitrary
279
+ // (possibly non-ASCII / multimodal) bytes pass through untouched.
280
+ const OPEN = ASCII.encode("[");
281
+ const CLOSE = ASCII.encode("]");
282
+ const COMMA = ASCII.encode(",");
283
+ const parts = [OPEN];
284
+ for (let i = 0; i < v.items.length; i++) {
285
+ if (i > 0)
286
+ parts.push(COMMA);
287
+ parts.push(enc(v.items[i]));
288
+ }
289
+ parts.push(CLOSE);
290
+ return concatBytes(parts);
291
+ }
292
+ }
293
+ };
294
+ return {
295
+ encode: enc,
296
+ decode(bytes) {
297
+ return parseValue(bytes);
298
+ },
299
+ };
300
+ }
@@ -0,0 +1 @@
1
+ export {};