@hviana/sema 0.2.6 → 0.2.8
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.
- package/dist/example/demo.d.ts +1 -0
- package/dist/example/demo.js +39 -0
- package/dist/example/train_base.d.ts +87 -0
- package/dist/example/train_base.js +2252 -0
- package/dist/src/alphabet.d.ts +7 -0
- package/dist/src/alphabet.js +33 -0
- package/dist/src/alu/src/alu.d.ts +185 -0
- package/dist/src/alu/src/alu.js +440 -0
- package/dist/src/alu/src/expr.d.ts +61 -0
- package/dist/src/alu/src/expr.js +318 -0
- package/dist/src/alu/src/index.d.ts +11 -0
- package/dist/src/alu/src/index.js +19 -0
- package/dist/src/alu/src/kernel-arith.d.ts +16 -0
- package/dist/src/alu/src/kernel-arith.js +264 -0
- package/dist/src/alu/src/kernel-bits.d.ts +19 -0
- package/dist/src/alu/src/kernel-bits.js +152 -0
- package/dist/src/alu/src/kernel-logic.d.ts +4 -0
- package/dist/src/alu/src/kernel-logic.js +60 -0
- package/dist/src/alu/src/kernel-nd.d.ts +3 -0
- package/dist/src/alu/src/kernel-nd.js +208 -0
- package/dist/src/alu/src/kernel-numeric.d.ts +54 -0
- package/dist/src/alu/src/kernel-numeric.js +366 -0
- package/dist/src/alu/src/operation.d.ts +168 -0
- package/dist/src/alu/src/operation.js +189 -0
- package/dist/src/alu/src/parser.d.ts +221 -0
- package/dist/src/alu/src/parser.js +577 -0
- package/dist/src/alu/src/resonance.d.ts +55 -0
- package/dist/src/alu/src/resonance.js +126 -0
- package/dist/src/alu/src/text.d.ts +31 -0
- package/dist/src/alu/src/text.js +73 -0
- package/dist/src/alu/src/value.d.ts +109 -0
- package/dist/src/alu/src/value.js +300 -0
- package/dist/src/alu/test/alu.test.d.ts +1 -0
- package/dist/src/alu/test/alu.test.js +764 -0
- package/dist/src/bytes.d.ts +14 -0
- package/dist/src/bytes.js +59 -0
- package/dist/src/canon.d.ts +26 -0
- package/dist/src/canon.js +57 -0
- package/dist/src/config.d.ts +111 -0
- package/dist/src/config.js +91 -0
- package/dist/src/derive/src/deduction.d.ts +125 -0
- package/dist/src/derive/src/deduction.js +155 -0
- package/dist/src/derive/src/index.d.ts +7 -0
- package/dist/src/derive/src/index.js +11 -0
- package/dist/src/derive/src/priority-queue.d.ts +20 -0
- package/dist/src/derive/src/priority-queue.js +73 -0
- package/dist/src/derive/src/rewrite.d.ts +56 -0
- package/dist/src/derive/src/rewrite.js +100 -0
- package/dist/src/derive/src/trie.d.ts +90 -0
- package/dist/src/derive/src/trie.js +217 -0
- package/dist/src/derive/test/derive.test.d.ts +1 -0
- package/dist/src/derive/test/derive.test.js +122 -0
- package/dist/src/extension.d.ts +37 -0
- package/dist/src/extension.js +7 -0
- package/dist/src/geometry.d.ts +202 -0
- package/dist/src/geometry.js +575 -0
- package/dist/src/index.d.ts +16 -0
- package/dist/src/index.js +18 -0
- package/dist/src/ingest-cache.d.ts +41 -0
- package/dist/src/ingest-cache.js +161 -0
- package/dist/src/mind/articulation.d.ts +6 -0
- package/dist/src/mind/articulation.js +99 -0
- package/dist/src/mind/attention.d.ts +405 -0
- package/dist/src/mind/attention.js +1829 -0
- package/dist/src/mind/bridge.d.ts +30 -0
- package/dist/src/mind/bridge.js +569 -0
- package/dist/src/mind/canonical.d.ts +29 -0
- package/dist/src/mind/canonical.js +88 -0
- package/dist/src/mind/graph-search.d.ts +285 -0
- package/dist/src/mind/graph-search.js +956 -0
- package/dist/src/mind/index.d.ts +7 -0
- package/dist/src/mind/index.js +5 -0
- package/dist/src/mind/junction.d.ts +124 -0
- package/dist/src/mind/junction.js +317 -0
- package/dist/src/mind/learning.d.ts +47 -0
- package/dist/src/mind/learning.js +248 -0
- package/dist/src/mind/match.d.ts +126 -0
- package/dist/src/mind/match.js +441 -0
- package/dist/src/mind/mechanisms/alu.d.ts +4 -0
- package/dist/src/mind/mechanisms/alu.js +36 -0
- package/dist/src/mind/mechanisms/cast.d.ts +89 -0
- package/dist/src/mind/mechanisms/cast.js +716 -0
- package/dist/src/mind/mechanisms/confluence.d.ts +24 -0
- package/dist/src/mind/mechanisms/confluence.js +225 -0
- package/dist/src/mind/mechanisms/cover.d.ts +6 -0
- package/dist/src/mind/mechanisms/cover.js +203 -0
- package/dist/src/mind/mechanisms/extraction.d.ts +67 -0
- package/dist/src/mind/mechanisms/extraction.js +387 -0
- package/dist/src/mind/mechanisms/recall.d.ts +13 -0
- package/dist/src/mind/mechanisms/recall.js +325 -0
- package/dist/src/mind/mind.d.ts +276 -0
- package/dist/src/mind/mind.js +572 -0
- package/dist/src/mind/pipeline-mechanism.d.ts +142 -0
- package/dist/src/mind/pipeline-mechanism.js +213 -0
- package/dist/src/mind/pipeline.d.ts +20 -0
- package/dist/src/mind/pipeline.js +217 -0
- package/dist/src/mind/primitives.d.ts +70 -0
- package/dist/src/mind/primitives.js +287 -0
- package/dist/src/mind/rationale.d.ts +139 -0
- package/dist/src/mind/rationale.js +163 -0
- package/dist/src/mind/reasoning.d.ts +34 -0
- package/dist/src/mind/reasoning.js +240 -0
- package/dist/src/mind/recognition.d.ts +20 -0
- package/dist/src/mind/recognition.js +390 -0
- package/dist/src/mind/resonance.d.ts +23 -0
- package/dist/src/mind/resonance.js +0 -0
- package/dist/src/mind/trace.d.ts +15 -0
- package/dist/src/mind/trace.js +73 -0
- package/dist/src/mind/traverse.d.ts +113 -0
- package/dist/src/mind/traverse.js +568 -0
- package/dist/src/mind/types.d.ts +289 -0
- package/dist/src/mind/types.js +130 -0
- package/dist/src/rabitq-ivf/src/database.d.ts +113 -0
- package/dist/src/rabitq-ivf/src/database.js +201 -0
- package/dist/src/rabitq-ivf/src/index.d.ts +7 -0
- package/dist/src/rabitq-ivf/src/index.js +4 -0
- package/dist/src/rabitq-ivf/src/ivf.d.ts +200 -0
- package/dist/src/rabitq-ivf/src/ivf.js +1165 -0
- package/dist/src/rabitq-ivf/src/prng.d.ts +19 -0
- package/dist/src/rabitq-ivf/src/prng.js +36 -0
- package/dist/src/rabitq-ivf/src/rabitq.d.ts +95 -0
- package/dist/src/rabitq-ivf/src/rabitq.js +283 -0
- package/dist/src/sema.d.ts +31 -0
- package/dist/src/sema.js +63 -0
- package/dist/src/store-sqlite.d.ts +184 -0
- package/dist/src/store-sqlite.js +942 -0
- package/dist/src/store.d.ts +663 -0
- package/dist/src/store.js +1618 -0
- package/dist/src/vec.d.ts +31 -0
- package/dist/src/vec.js +109 -0
- package/package.json +1 -1
- package/src/mind/attention.ts +178 -63
- package/test/51-structural-resonance-ladder.test.mjs +6 -3
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { type Value } from "./value.js";
|
|
2
|
+
/** Arity: a fixed operand count, or "variadic" for a fold (and/or of many,
|
|
3
|
+
* dot, min/max, polynomial evaluation). */
|
|
4
|
+
export type Arity = number | "variadic";
|
|
5
|
+
/** Synchronous resonance an op callback may consult, pre-resolved by the host.
|
|
6
|
+
* This is the only path by which a computation reads MEANING from a symbol's
|
|
7
|
+
* opaque bytes; numbers never touch it. */
|
|
8
|
+
export interface ResonanceSync {
|
|
9
|
+
/** The resonant opposite of a symbol's bytes — its antonym / inverse in the
|
|
10
|
+
* resonance space, of whatever modality — or null if none is known. Found
|
|
11
|
+
* by the host's halo resonance and handed in pre-resolved (see
|
|
12
|
+
* resonance.ts). */
|
|
13
|
+
opposite(bytes: Uint8Array): Uint8Array | null;
|
|
14
|
+
/** The canonical operation a symbol's bytes MEAN, found by resonance over the
|
|
15
|
+
* operation concepts — the GENERIC, modality-agnostic recognition path, the
|
|
16
|
+
* same one the host uses to recognise an operator a query does not literally
|
|
17
|
+
* spell. Pre-resolved by the host; null when nothing resonates. This is how
|
|
18
|
+
* the FUNCTION ARGUMENT of a higher-order nd op (the "+" of a reduce, the
|
|
19
|
+
* predicate of a filter) is resolved when it is not a literal surface form —
|
|
20
|
+
* see {@link OpContext.resolveOp}. */
|
|
21
|
+
recogniseOp(bytes: Uint8Array): string | null;
|
|
22
|
+
}
|
|
23
|
+
/** Runtime knobs the numerical kernel reads (the limit layer's tolerance and
|
|
24
|
+
* iteration ceiling). Carried on the context so a derived op never closes
|
|
25
|
+
* over global state. */
|
|
26
|
+
export interface OpRuntime {
|
|
27
|
+
/** Convergence tolerance ε: a refinement stops when successive results agree
|
|
28
|
+
* within this. */
|
|
29
|
+
tol: number;
|
|
30
|
+
/** Hard iteration ceiling, so a non-converging step still terminates. */
|
|
31
|
+
maxIter: number;
|
|
32
|
+
}
|
|
33
|
+
/** Evaluate a symbolic EXPRESSION at a binding of its free variable, returning a
|
|
34
|
+
* plain number, or null when the expression cannot be evaluated (unparseable,
|
|
35
|
+
* or no evaluator wired in). This is the bridge that lets a NUMERICAL op act
|
|
36
|
+
* on a function: an integral, a derivative, a limit (or any refinement) needs
|
|
37
|
+
* to sample its integrand/argument at many points, and the integrand is itself
|
|
38
|
+
* a sub-expression with a free variable — evaluating it is a RECURSIVE
|
|
39
|
+
* application of the same ALU. Injected by the host (the {@link
|
|
40
|
+
* "./alu.js".Alu} facade); absent in a bare registry, where expression-ops
|
|
41
|
+
* simply decline. */
|
|
42
|
+
export type EvalExpr = (bytes: Uint8Array, variable: string, at: number) => number | null;
|
|
43
|
+
/** The context a callback runs in: the means to call sibling ops by name, the
|
|
44
|
+
* runtime knobs, pre-resolved resonance, and (when wired) an expression
|
|
45
|
+
* evaluator. This is what makes derivations compose without import cycles
|
|
46
|
+
* between kernel files. */
|
|
47
|
+
export interface OpContext {
|
|
48
|
+
/** Apply another registered op by canonical name. */
|
|
49
|
+
apply(name: string, args: Value[]): Value;
|
|
50
|
+
/** Whether an op is registered (lets a derivation pick a fast path when an
|
|
51
|
+
* optional sibling exists). */
|
|
52
|
+
has(name: string): boolean;
|
|
53
|
+
/** Resolve an operation-denoting VALUE to a canonical op name — the
|
|
54
|
+
* intelligent callback resolution the higher-order nd ops (map/reduce/filter/
|
|
55
|
+
* find) use for their FUNCTION ARGUMENT. An operation is named the SAME way
|
|
56
|
+
* any operation is recognised anywhere in the kernel, reusing the existing
|
|
57
|
+
* machinery rather than a bespoke table:
|
|
58
|
+
*
|
|
59
|
+
* 1. a literal SURFACE FORM — the op-value's bytes are a registered spelling
|
|
60
|
+
* ("+", "plus", "add", "*", "max", …) → the registry's own form index;
|
|
61
|
+
* 2. else its MEANING — {@link ResonanceSync.recogniseOp} over the same
|
|
62
|
+
* operation concepts, so a synonym or a multimodal gesture the bytes do
|
|
63
|
+
* not literally spell still resolves (pre-resolved by the host);
|
|
64
|
+
* 3. else a bare canonical NAME already (a symbol whose bytes equal an op
|
|
65
|
+
* name), or an int/bit treated as its decimal name — last resort.
|
|
66
|
+
*
|
|
67
|
+
* `arity` (when given) disambiguates a surface a unary and a binary op share
|
|
68
|
+
* (e.g. "-" is both negate and subtract): the claimant of that arity wins.
|
|
69
|
+
* Returns null when nothing resolves, so a higher-order op declines. */
|
|
70
|
+
resolveOp(op: Value, arity?: number): string | null;
|
|
71
|
+
/** The numerical runtime knobs. */
|
|
72
|
+
rt: OpRuntime;
|
|
73
|
+
/** Pre-resolved resonance for the polymorphic inverse and op recognition. */
|
|
74
|
+
resonance: ResonanceSync;
|
|
75
|
+
/** Evaluate a symbolic expression at a variable binding (see {@link
|
|
76
|
+
* EvalExpr}); undefined in a bare registry with no host evaluator. */
|
|
77
|
+
evalExpr?: EvalExpr;
|
|
78
|
+
}
|
|
79
|
+
/** The callback that executes an operation on resolved operands. */
|
|
80
|
+
export type OpFn = (args: Value[], ctx: OpContext) => Value;
|
|
81
|
+
/** How tightly an INFIX operation binds, and to which side — declared at
|
|
82
|
+
* registration, next to the op it describes, so the expression grammar is
|
|
83
|
+
* read off the registry rather than hardcoded in any parser. Higher
|
|
84
|
+
* `precedence` binds tighter. */
|
|
85
|
+
export interface InfixSyntax {
|
|
86
|
+
precedence: number;
|
|
87
|
+
rightAssoc?: boolean;
|
|
88
|
+
}
|
|
89
|
+
/** Optional syntactic/structure traits of an operation, declared where the op
|
|
90
|
+
* is registered. Every parser-facing distinction the ALU makes is one of
|
|
91
|
+
* these traits — there is no side table of names anywhere. */
|
|
92
|
+
export interface OpTraits {
|
|
93
|
+
/** Consumes an n-dimensional value WHOLE (exempt from broadcast). */
|
|
94
|
+
structural?: boolean;
|
|
95
|
+
/** Usable as an infix operator, with the given binding. */
|
|
96
|
+
infix?: InfixSyntax;
|
|
97
|
+
/** The FIRST operand is an EXPRESSION (a function's bytes) rather than a
|
|
98
|
+
* value; the remaining `arity − 1` operands are its points/bounds. The
|
|
99
|
+
* numerical layer's diff/integrate/solve/limit/optimize declare this. */
|
|
100
|
+
expression?: boolean;
|
|
101
|
+
}
|
|
102
|
+
/** An operation — primitive or derived, indistinguishable by type. */
|
|
103
|
+
export interface Operation extends OpTraits {
|
|
104
|
+
/** Canonical id, e.g. "add", "nand", "converge", "sin". */
|
|
105
|
+
name: string;
|
|
106
|
+
/** Operand count (fixed) or "variadic". */
|
|
107
|
+
arity: Arity;
|
|
108
|
+
/** True only for the kernel's irreducible roots. */
|
|
109
|
+
primitive: boolean;
|
|
110
|
+
/** Surface forms this op answers to — the spellings resonance maps to it,
|
|
111
|
+
* e.g. add ← ["+", "plus", "sum", "add"]. May be empty for an internal op
|
|
112
|
+
* that has no user-facing surface. */
|
|
113
|
+
forms: string[];
|
|
114
|
+
/** The callback. */
|
|
115
|
+
fn: OpFn;
|
|
116
|
+
}
|
|
117
|
+
/** A resonance that knows nothing — the default when no host is wired in, so
|
|
118
|
+
* the pure kernel and its tests run with zero coupling. Op recognition then
|
|
119
|
+
* falls back to literal surface forms / canonical names only. */
|
|
120
|
+
export declare const NO_RESONANCE: ResonanceSync;
|
|
121
|
+
/** The collection of operations, indexed by canonical name and by surface form.
|
|
122
|
+
* Building a kernel is a sequence of {@link prim}/{@link derive} calls; the
|
|
123
|
+
* registry then resolves names at apply time, so registration order need only
|
|
124
|
+
* respect the dependency DAG loosely (a derived op may reference an op
|
|
125
|
+
* registered later, since resolution is lazy). */
|
|
126
|
+
export declare class OperationRegistry {
|
|
127
|
+
private readonly ops;
|
|
128
|
+
/** Surface form → the canonical names that claim it. A form may be shared
|
|
129
|
+
* (e.g. "-" is both subtract and negate); the caller disambiguates by arity
|
|
130
|
+
* and position, so the index keeps every claimant. */
|
|
131
|
+
private readonly formIndex;
|
|
132
|
+
/** Register an operation record directly. */
|
|
133
|
+
register(op: Operation): void;
|
|
134
|
+
/** Register a PRIMITIVE op (hand-written callback), with optional {@link
|
|
135
|
+
* OpTraits} — structural (consumes an nd whole, exempt from broadcast),
|
|
136
|
+
* infix binding, expression-operand — declared here, next to the op. */
|
|
137
|
+
prim(name: string, arity: Arity, forms: string[], fn: OpFn, traits?: OpTraits): void;
|
|
138
|
+
/** Register a DERIVED op (callback composes other ops via ctx). `traits`
|
|
139
|
+
* as in {@link prim}. */
|
|
140
|
+
derive(name: string, arity: Arity, forms: string[], fn: OpFn, traits?: OpTraits): void;
|
|
141
|
+
/** The op with this canonical name, or undefined. */
|
|
142
|
+
get(name: string): Operation | undefined;
|
|
143
|
+
has(name: string): boolean;
|
|
144
|
+
/** The canonical names a surface form maps to (empty if none). Several ops
|
|
145
|
+
* can share one surface (unary vs binary "-"), so this returns all. */
|
|
146
|
+
lookupForm(form: string): readonly string[];
|
|
147
|
+
/** Every (surface form → canonical name) pair — the host enumerates these to
|
|
148
|
+
* seed its operator recogniser. */
|
|
149
|
+
formEntries(): Iterable<{
|
|
150
|
+
form: string;
|
|
151
|
+
name: string;
|
|
152
|
+
}>;
|
|
153
|
+
/** Every registered canonical name. */
|
|
154
|
+
names(): Iterable<string>;
|
|
155
|
+
/** Build a context bound to a resonance and runtime — the object derived
|
|
156
|
+
* callbacks call back into. `apply` validates arity-vs-presence and surfaces
|
|
157
|
+
* a clear error rather than letting an undefined op silently produce NaN.
|
|
158
|
+
* An optional expression evaluator lets the numerical layer act on functions
|
|
159
|
+
* (see {@link EvalExpr}). */
|
|
160
|
+
context(resonance: ResonanceSync, rt: OpRuntime, evalExpr?: EvalExpr): OpContext;
|
|
161
|
+
/** Resolve an operation-denoting value to a canonical op name — the shared
|
|
162
|
+
* machinery behind {@link OpContext.resolveOp}. Tries, in order: a literal
|
|
163
|
+
* surface form (the registry's own index, arity-disambiguated), the meaning
|
|
164
|
+
* via `resonance.recogniseOp`, then a bare canonical name / decimal reading.
|
|
165
|
+
* Returns null when nothing resolves. Static-shaped (takes the resonance
|
|
166
|
+
* explicitly) so both the context closure and callers can use it. */
|
|
167
|
+
resolveOp(op: Value, resonance: ResonanceSync, arity?: number): string | null;
|
|
168
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// operation.ts — the Operation record and the registry it lives in.
|
|
2
|
+
//
|
|
3
|
+
// An Operation is the irreducible unit of the ALU. A PRIMITIVE op has a
|
|
4
|
+
// hand-written callback (the kernel's irreducible roots: nand, the real
|
|
5
|
+
// arithmetic primitives, converge); a DERIVED op's callback only calls OTHER ops
|
|
6
|
+
// by canonical name through the {@link OpContext}. The two are the SAME type —
|
|
7
|
+
// a caller cannot tell a primitive from a derivation — which is the whole point:
|
|
8
|
+
// "subtract = add ∘ negate" is registered exactly like a primitive and competes
|
|
9
|
+
// in the search exactly like one. Because a derived callback's body is a
|
|
10
|
+
// sequence of `ctx.apply("…", …)` calls, the derivation DAG is literal,
|
|
11
|
+
// inspectable source: you can read off, from nand, how every gate is built, and
|
|
12
|
+
// from add/multiply/converge how every number-theoretic and numerical op is.
|
|
13
|
+
//
|
|
14
|
+
// The module is pure. The one window onto meaning a callback may need — the
|
|
15
|
+
// resonant opposite of a symbol, for the polymorphic inverse — arrives through
|
|
16
|
+
// {@link ResonanceSync}, which the host pre-resolves (SEMA's async resonance is
|
|
17
|
+
// hoisted out of the synchronous search, exactly as concept hops and connectors
|
|
18
|
+
// are). ALU never queries resonance itself.
|
|
19
|
+
import { isNd, nd } from "./value.js";
|
|
20
|
+
/** A resonance that knows nothing — the default when no host is wired in, so
|
|
21
|
+
* the pure kernel and its tests run with zero coupling. Op recognition then
|
|
22
|
+
* falls back to literal surface forms / canonical names only. */
|
|
23
|
+
export const NO_RESONANCE = {
|
|
24
|
+
opposite: () => null,
|
|
25
|
+
recogniseOp: () => null,
|
|
26
|
+
};
|
|
27
|
+
/** The collection of operations, indexed by canonical name and by surface form.
|
|
28
|
+
* Building a kernel is a sequence of {@link prim}/{@link derive} calls; the
|
|
29
|
+
* registry then resolves names at apply time, so registration order need only
|
|
30
|
+
* respect the dependency DAG loosely (a derived op may reference an op
|
|
31
|
+
* registered later, since resolution is lazy). */
|
|
32
|
+
export class OperationRegistry {
|
|
33
|
+
ops = new Map();
|
|
34
|
+
/** Surface form → the canonical names that claim it. A form may be shared
|
|
35
|
+
* (e.g. "-" is both subtract and negate); the caller disambiguates by arity
|
|
36
|
+
* and position, so the index keeps every claimant. */
|
|
37
|
+
formIndex = new Map();
|
|
38
|
+
/** Register an operation record directly. */
|
|
39
|
+
register(op) {
|
|
40
|
+
this.ops.set(op.name, op);
|
|
41
|
+
for (const f of op.forms) {
|
|
42
|
+
const a = this.formIndex.get(f);
|
|
43
|
+
if (a) {
|
|
44
|
+
if (!a.includes(op.name))
|
|
45
|
+
a.push(op.name);
|
|
46
|
+
}
|
|
47
|
+
else
|
|
48
|
+
this.formIndex.set(f, [op.name]);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Register a PRIMITIVE op (hand-written callback), with optional {@link
|
|
52
|
+
* OpTraits} — structural (consumes an nd whole, exempt from broadcast),
|
|
53
|
+
* infix binding, expression-operand — declared here, next to the op. */
|
|
54
|
+
prim(name, arity, forms, fn, traits = {}) {
|
|
55
|
+
this.register({ name, arity, primitive: true, forms, fn, ...traits });
|
|
56
|
+
}
|
|
57
|
+
/** Register a DERIVED op (callback composes other ops via ctx). `traits`
|
|
58
|
+
* as in {@link prim}. */
|
|
59
|
+
derive(name, arity, forms, fn, traits = {}) {
|
|
60
|
+
this.register({ name, arity, primitive: false, forms, fn, ...traits });
|
|
61
|
+
}
|
|
62
|
+
/** The op with this canonical name, or undefined. */
|
|
63
|
+
get(name) {
|
|
64
|
+
return this.ops.get(name);
|
|
65
|
+
}
|
|
66
|
+
has(name) {
|
|
67
|
+
return this.ops.has(name);
|
|
68
|
+
}
|
|
69
|
+
/** The canonical names a surface form maps to (empty if none). Several ops
|
|
70
|
+
* can share one surface (unary vs binary "-"), so this returns all. */
|
|
71
|
+
lookupForm(form) {
|
|
72
|
+
return this.formIndex.get(form) ?? [];
|
|
73
|
+
}
|
|
74
|
+
/** Every (surface form → canonical name) pair — the host enumerates these to
|
|
75
|
+
* seed its operator recogniser. */
|
|
76
|
+
*formEntries() {
|
|
77
|
+
for (const [form, names] of this.formIndex) {
|
|
78
|
+
for (const name of names)
|
|
79
|
+
yield { form, name };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** Every registered canonical name. */
|
|
83
|
+
names() {
|
|
84
|
+
return this.ops.keys();
|
|
85
|
+
}
|
|
86
|
+
/** Build a context bound to a resonance and runtime — the object derived
|
|
87
|
+
* callbacks call back into. `apply` validates arity-vs-presence and surfaces
|
|
88
|
+
* a clear error rather than letting an undefined op silently produce NaN.
|
|
89
|
+
* An optional expression evaluator lets the numerical layer act on functions
|
|
90
|
+
* (see {@link EvalExpr}). */
|
|
91
|
+
context(resonance, rt, evalExpr) {
|
|
92
|
+
const self = this;
|
|
93
|
+
const ctx = {
|
|
94
|
+
rt,
|
|
95
|
+
resonance,
|
|
96
|
+
evalExpr,
|
|
97
|
+
has: (name) => self.ops.has(name),
|
|
98
|
+
resolveOp: (op, arity) => self.resolveOp(op, resonance, arity),
|
|
99
|
+
apply(name, args) {
|
|
100
|
+
const op = self.ops.get(name);
|
|
101
|
+
if (!op)
|
|
102
|
+
throw new Error(`ALU: unknown operation "${name}"`);
|
|
103
|
+
// ── ELEMENT-WISE BROADCAST ────────────────────────────────────────
|
|
104
|
+
// A SCALAR op (non-structural) applied to an n-dimensional argument
|
|
105
|
+
// lifts over it: the op runs on each element and the results re-pack
|
|
106
|
+
// into an nd of the same shape. This is the ONE place "every operation
|
|
107
|
+
// supports nd" is implemented — add, sin, nand, the polymorphic inverse
|
|
108
|
+
// all broadcast for free, and because each element re-enters `apply`,
|
|
109
|
+
// NESTING recurses (an nd of nd lifts twice) with no extra code.
|
|
110
|
+
//
|
|
111
|
+
// • several nd args ZIP position-wise (their top-level lengths must
|
|
112
|
+
// agree); a scalar arg is held constant against the list — so
|
|
113
|
+
// add([1,2,3],[4,5,6]) = [5,7,9] and add([1,2,3], 10) = [11,12,13].
|
|
114
|
+
// • a structural op is exempt: it wants the whole list (a reduce
|
|
115
|
+
// cannot be lifted across the very elements it folds).
|
|
116
|
+
if (!op.structural && args.some(isNd)) {
|
|
117
|
+
let len = -1;
|
|
118
|
+
for (const a of args) {
|
|
119
|
+
if (!isNd(a))
|
|
120
|
+
continue;
|
|
121
|
+
if (len === -1)
|
|
122
|
+
len = a.items.length;
|
|
123
|
+
else if (a.items.length !== len) {
|
|
124
|
+
throw new Error(`ALU: cannot broadcast "${name}" over lists of unequal length ` +
|
|
125
|
+
`(${len} vs ${a.items.length})`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const out = [];
|
|
129
|
+
for (let i = 0; i < len; i++) {
|
|
130
|
+
out.push(ctx.apply(name, args.map((a) => isNd(a) ? a.items[i] : a)));
|
|
131
|
+
}
|
|
132
|
+
return nd(out);
|
|
133
|
+
}
|
|
134
|
+
if (op.arity !== "variadic" && args.length !== op.arity) {
|
|
135
|
+
throw new Error(`ALU: "${name}" expects ${op.arity} operand(s), got ${args.length}`);
|
|
136
|
+
}
|
|
137
|
+
return op.fn(args, ctx);
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
return ctx;
|
|
141
|
+
}
|
|
142
|
+
/** Resolve an operation-denoting value to a canonical op name — the shared
|
|
143
|
+
* machinery behind {@link OpContext.resolveOp}. Tries, in order: a literal
|
|
144
|
+
* surface form (the registry's own index, arity-disambiguated), the meaning
|
|
145
|
+
* via `resonance.recogniseOp`, then a bare canonical name / decimal reading.
|
|
146
|
+
* Returns null when nothing resolves. Static-shaped (takes the resonance
|
|
147
|
+
* explicitly) so both the context closure and callers can use it. */
|
|
148
|
+
resolveOp(op, resonance, arity) {
|
|
149
|
+
// The op-value's surface text, if it has a byte reading.
|
|
150
|
+
let text = null;
|
|
151
|
+
if (op.domain === "symbol") {
|
|
152
|
+
let s = "";
|
|
153
|
+
for (let i = 0; i < op.bytes.length; i++) {
|
|
154
|
+
s += String.fromCharCode(op.bytes[i]);
|
|
155
|
+
}
|
|
156
|
+
text = s.trim();
|
|
157
|
+
}
|
|
158
|
+
else if (op.domain === "int")
|
|
159
|
+
text = op.n.toString();
|
|
160
|
+
else if (op.domain === "bit")
|
|
161
|
+
text = String(op.b);
|
|
162
|
+
if (text === null || text.length === 0)
|
|
163
|
+
return null;
|
|
164
|
+
// (1) a literal SURFACE FORM, arity-disambiguated when asked.
|
|
165
|
+
const claimants = this.formIndex.get(text);
|
|
166
|
+
if (claimants && claimants.length > 0) {
|
|
167
|
+
if (arity !== undefined) {
|
|
168
|
+
for (const n of claimants) {
|
|
169
|
+
const o = this.ops.get(n);
|
|
170
|
+
const a = o && (o.arity === "variadic" ? 2 : o.arity);
|
|
171
|
+
if (a === arity)
|
|
172
|
+
return n;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return claimants[0];
|
|
176
|
+
}
|
|
177
|
+
// (2) the MEANING, via pre-resolved resonance (synonym / multimodal gesture
|
|
178
|
+
// the bytes do not literally spell) — only a symbol carries meaning.
|
|
179
|
+
if (op.domain === "symbol") {
|
|
180
|
+
const byMeaning = resonance.recogniseOp(op.bytes);
|
|
181
|
+
if (byMeaning && this.ops.has(byMeaning))
|
|
182
|
+
return byMeaning;
|
|
183
|
+
}
|
|
184
|
+
// (3) a bare canonical NAME already registered.
|
|
185
|
+
if (this.ops.has(text))
|
|
186
|
+
return text;
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import type { Alu } from "./alu.js";
|
|
2
|
+
import { type ConceptAnchor } from "./resonance.js";
|
|
3
|
+
/** A half-open byte range. */
|
|
4
|
+
export interface Span {
|
|
5
|
+
i: number;
|
|
6
|
+
j: number;
|
|
7
|
+
}
|
|
8
|
+
/** A computation the parser recognised and evaluated: the query span [i, j) it
|
|
9
|
+
* is authoritative for, and the canonical result bytes. */
|
|
10
|
+
export interface ComputedSpan extends Span {
|
|
11
|
+
bytes: Uint8Array;
|
|
12
|
+
}
|
|
13
|
+
/** The port through which the parser reaches its host. This interface is the
|
|
14
|
+
* ENTIRE coupling surface between the ALU and any host: the ALU imports
|
|
15
|
+
* nothing from a mind or store, and every member is a GENERIC capability the
|
|
16
|
+
* host already has — nothing here mentions operations, vocabularies, or any
|
|
17
|
+
* ALU notion, so the host holds no ALU-specific logic either.
|
|
18
|
+
*
|
|
19
|
+
* MEANING (resonance):
|
|
20
|
+
* • meaningOf — which of the given labelled forms does a span MEAN? The ALU
|
|
21
|
+
* supplies the anchors (its own concept vocabulary); the host only answers
|
|
22
|
+
* nearness in its meaning space, above its own threshold, or null. This
|
|
23
|
+
* is what recognises an operation the bytes do not literally spell — an
|
|
24
|
+
* "∫" drawn, a "plus" spoken — in any modality.
|
|
25
|
+
* • continuation — the grounded form the host's corpus continues a form to
|
|
26
|
+
* (the same grounding its recall uses), or null. The ALU is the one that
|
|
27
|
+
* READS this as the resonant opposite: it asks for the continuation of an
|
|
28
|
+
* operand under an inverse, and a corpus that learnt "opposite of large →
|
|
29
|
+
* small" grounds exactly that. The host stays neutral — it only ever
|
|
30
|
+
* answers "where does this form lead?".
|
|
31
|
+
*
|
|
32
|
+
* GEOMETRY (perception):
|
|
33
|
+
* • segment — coherent runs by the host's own geometric perception. The
|
|
34
|
+
* parser uses this to ask "is this gap pure separator?" (one segment or
|
|
35
|
+
* none) — never a character class of its own.
|
|
36
|
+
* • reach — how far (in bytes) an operator may look for its operand: the
|
|
37
|
+
* host's own grouping capacity, so adjacency is judged by the same
|
|
38
|
+
* geometry that groups the host's perception. */
|
|
39
|
+
export interface AluHost {
|
|
40
|
+
meaningOf(bytes: Uint8Array, anchors: ReadonlyArray<ConceptAnchor>): Promise<string | null>;
|
|
41
|
+
continuation(bytes: Uint8Array): Promise<Uint8Array | null>;
|
|
42
|
+
segment(bytes: Uint8Array): Span[];
|
|
43
|
+
reach: number;
|
|
44
|
+
}
|
|
45
|
+
/** A host that knows nothing beyond structure: no resonance, whitespace-run
|
|
46
|
+
* segmentation, unbounded reach. This is what "the ALU runs fully decoupled"
|
|
47
|
+
* means — the parser still reads literal notation, and only the meaning-based
|
|
48
|
+
* paths stay silent. */
|
|
49
|
+
export declare const STRUCTURAL_HOST: AluHost;
|
|
50
|
+
export declare class QueryParser {
|
|
51
|
+
private readonly alu;
|
|
52
|
+
private readonly host;
|
|
53
|
+
/** The host's generic capabilities, specialised once into the {@link
|
|
54
|
+
* AluResonance} the prefetch bridges consume — the ONE place the ALU gives
|
|
55
|
+
* the host's neutral answers their computational reading: op recognition is
|
|
56
|
+
* meaningOf over the ALU's own concept anchors, and the polymorphic INVERSE
|
|
57
|
+
* of a symbol is the corpus's grounded continuation of it (a learnt
|
|
58
|
+
* opposition relation leads from a form to its opposite; the host never
|
|
59
|
+
* needs to know that is what it grounded). */
|
|
60
|
+
private readonly resonance;
|
|
61
|
+
/** Session memo of a term's meaning-based op reading, keyed by its bytes.
|
|
62
|
+
* `meaningOf` is a full river fold of the span — measured at ~a third of
|
|
63
|
+
* a plain-English respond's latency, paid per WORD per query — and it is
|
|
64
|
+
* a pure function of the bytes (perception is pure; the concept anchors
|
|
65
|
+
* are fixed for the Alu's lifetime), so the reading never changes.
|
|
66
|
+
* Bounded: cleared wholesale when full (words recur; a rare clear only
|
|
67
|
+
* re-pays folds, never changes a reading). */
|
|
68
|
+
private readonly meaningMemo;
|
|
69
|
+
private static readonly MEANING_MEMO_MAX;
|
|
70
|
+
constructor(alu: Alu, host?: AluHost);
|
|
71
|
+
/** Recognise and evaluate every computation `query` invokes. All async
|
|
72
|
+
* resonance is resolved in here, so the caller receives finished spans it
|
|
73
|
+
* can fold synchronously into its search. Results are deduplicated; a span
|
|
74
|
+
* that fails to compute is simply absent (the "rule does not fire"
|
|
75
|
+
* contract).
|
|
76
|
+
*
|
|
77
|
+
* The pipeline is one composition ladder, iterated to a FIXPOINT:
|
|
78
|
+
*
|
|
79
|
+
* 1. infix arithmetic RUNS are found and evaluated through the
|
|
80
|
+
* registry-derived grammar;
|
|
81
|
+
* 2. every computed span — a run, or a fired operation — is COLLAPSED
|
|
82
|
+
* into a single virtual operand, so the next round consumes it as one
|
|
83
|
+
* finished value: "sum 1*3 4" is add(3, 4), and "sqrt sum 9 16" is
|
|
84
|
+
* sqrt(25) — nesting by iteration, exactly as the grammar nests
|
|
85
|
+
* expressions, with no recursion machinery of its own;
|
|
86
|
+
* 3. rounds repeat while operations still fire (each round consumes at
|
|
87
|
+
* least one term, so the ladder is bounded by the term count).
|
|
88
|
+
*
|
|
89
|
+
* One AUTHORITY law then reconciles the readings: a span strictly
|
|
90
|
+
* contained in a larger computed span is that computation's MATERIAL (the
|
|
91
|
+
* "2-4" inside a solve's "x^2-4", the inner sum under a sqrt), not a rival
|
|
92
|
+
* result — the same rule by which the host's search lets a computed span
|
|
93
|
+
* override colliding learned facts. */
|
|
94
|
+
parse(query: Uint8Array): Promise<ComputedSpan[]>;
|
|
95
|
+
/** Apply an ALU operation to operand byte spans, with the host's resonance
|
|
96
|
+
* wired in — the entry point for computation over n-dimensional values.
|
|
97
|
+
*
|
|
98
|
+
* Unlike the in-query arithmetic rule (which hands the facade scalar operand
|
|
99
|
+
* bytes), this recognises each operand's STRUCTURE into a Value first and
|
|
100
|
+
* runs the kernel on the finished Values, with the full resonance snapshot
|
|
101
|
+
* pre-resolved — because an nd computation needs meaning in two places the
|
|
102
|
+
* bare facade cannot reach on its own:
|
|
103
|
+
*
|
|
104
|
+
* • the polymorphic INVERSE inside a broadcast — `inverse [large, 3, tall]`
|
|
105
|
+
* lifts element-wise (operation.ts), and each symbol element's opposite
|
|
106
|
+
* is a resonant lookup, grounded in the host's corpus exactly as a scalar
|
|
107
|
+
* inverse is;
|
|
108
|
+
* • the FUNCTION ARGUMENT of a higher-order op — `reduce(xs, ‹+›)`,
|
|
109
|
+
* `map(xs, ‹negate›)`: the operator value is resolved by
|
|
110
|
+
* {@link "./operation.js".OpContext.resolveOp}, which falls through to
|
|
111
|
+
* resonance when the bytes are not a literal surface form.
|
|
112
|
+
*
|
|
113
|
+
* Both are async, so every SYMBOL span reachable in the operands (recursing
|
|
114
|
+
* through nd nesting, see {@link "./value.js".symbolSpans}) is resolved ONCE
|
|
115
|
+
* up front into a synchronous snapshot, and the synchronous kernel computes
|
|
116
|
+
* against it. Returns null when the op is unknown or the computation
|
|
117
|
+
* declines — the "this rule does not fire" contract.
|
|
118
|
+
*
|
|
119
|
+
* `asSymbol(idx)` keeps an operand opaque (not read as structure or a
|
|
120
|
+
* number) — the numerical-layer convention where operand 0 is an
|
|
121
|
+
* expression's bytes; it defaults to "recognise everything", which is what
|
|
122
|
+
* an nd computation wants. */
|
|
123
|
+
compute(name: string, operandBytes: Uint8Array[], asSymbol?: (idx: number) => boolean): Promise<Uint8Array | null>;
|
|
124
|
+
/** Lex the query ONCE into a single ascending token stream: the facade's
|
|
125
|
+
* scanner claims numeric OPERANDS and symbolic OPERATORS, and every maximal
|
|
126
|
+
* unclaimed run between spacing bytes is a TERM — a word, glyph, or opaque
|
|
127
|
+
* fragment that may name an operation. Terms are deliberately bounded by
|
|
128
|
+
* the spacing floor, not the host's geometric segmentation: perception may
|
|
129
|
+
* cut mid-word (its segments are grouping capacity, not word boundaries),
|
|
130
|
+
* while an operation NAME is a notation-level token. Geometry still
|
|
131
|
+
* governs what happens BETWEEN tokens (gap bridging, operand reach).
|
|
132
|
+
*
|
|
133
|
+
* Tokens are disjoint, ascending, and cover every non-spacing byte — so
|
|
134
|
+
* the gap between consecutive tokens is pure spacing BY CONSTRUCTION, a
|
|
135
|
+
* structural fact the run recogniser leans on. */
|
|
136
|
+
private lex;
|
|
137
|
+
/** The maximal infix-arithmetic runs in the query: an alternation of numeric
|
|
138
|
+
* operands and SYMBOLIC operators (e.g. "2+3*4"), returned as [start, end)
|
|
139
|
+
* ranges that begin and end on an operand.
|
|
140
|
+
*
|
|
141
|
+
* Consecutive tokens may be SEPARATED — "3 + 3" is the same run as "3+3".
|
|
142
|
+
* The gap between two tokens is a bridgeable separator exactly when the
|
|
143
|
+
* host's geometric segmenter reads it as at most one coherent run — the
|
|
144
|
+
* same judgement the perception tree makes about spacing — so no character
|
|
145
|
+
* is privileged as "the" separator. */
|
|
146
|
+
private arithmeticRuns;
|
|
147
|
+
/** The maximal BRACKETED arithmetic runs: like {@link arithmeticRuns} but
|
|
148
|
+
* grouping brackets participate — an all-'(' term opens depth where an
|
|
149
|
+
* operand is expected, a term beginning with ')' closes it where an
|
|
150
|
+
* operator could follow (only its bracket prefix joins the run; anything
|
|
151
|
+
* after — a trailing "?" — ends it). A run is recorded only when it is
|
|
152
|
+
* BALANCED, actually crossed a bracket, and contains an operator — the
|
|
153
|
+
* bracket-free case is {@link arithmeticRuns}' own. Evaluation is the
|
|
154
|
+
* same registry-derived grammar, whose lexer already reads the brackets. */
|
|
155
|
+
private bracketedRuns;
|
|
156
|
+
/** Evaluate an infix-arithmetic run to its canonical result bytes, through
|
|
157
|
+
* the kernel's recursive expression evaluator, or null if it does not
|
|
158
|
+
* evaluate. A whole result stays an exact int; otherwise the canonical
|
|
159
|
+
* rounded real — deterministic, so the search's chart memoises identical
|
|
160
|
+
* results identically. */
|
|
161
|
+
private evalRun;
|
|
162
|
+
/** Recognise and apply the operations the query's TERMS name — the generic,
|
|
163
|
+
* multimodal path. A term may name an operation literally (its bytes are a
|
|
164
|
+
* registered surface form — no resonance cost) or by RESONANCE (its gist
|
|
165
|
+
* lands on an operation's concept, any modality).
|
|
166
|
+
*
|
|
167
|
+
* Recognition PROPOSES, application DISPOSES: a surface form may be shared
|
|
168
|
+
* by several operations ("zero" is both the constant and solve's
|
|
169
|
+
* root-finding), so every literal claimant is kept, in registration order,
|
|
170
|
+
* and the first whose application actually fires wins — disambiguation by
|
|
171
|
+
* what the query supplies, not by a precedence table. Recognition runs
|
|
172
|
+
* CONCURRENTLY — each term's reading is independent — and is memoised in
|
|
173
|
+
* `readings` across fixpoint rounds (a surviving term is the same token
|
|
174
|
+
* object), so each term resonates at most once per parse. Application is
|
|
175
|
+
* then ordered and deterministic: an EXPRESSION op takes the function that
|
|
176
|
+
* follows it ({@link applyToExpression}), any other op takes its arity's
|
|
177
|
+
* worth of operands from the stream ({@link applyToStream}). Both read
|
|
178
|
+
* the same composed stream — an expression's TEXT comes from the raw query
|
|
179
|
+
* bytes by position, so composition never disturbs it, while a composed
|
|
180
|
+
* operand serves as a finished point/bound or argument. */
|
|
181
|
+
private operations;
|
|
182
|
+
/** Apply a NUMERICAL-LAYER op — one whose first operand is an EXPRESSION (a
|
|
183
|
+
* function), declared by the op's own `expression` trait. The registry's
|
|
184
|
+
* arity says how many trailing numeric operands are its points/bounds
|
|
185
|
+
* (arity − 1); the bytes between the operator and those points — with
|
|
186
|
+
* non-math filler stripped by {@link cleanExprText} — are the expression,
|
|
187
|
+
* evaluated by a recursive application of the kernel. */
|
|
188
|
+
private applyToExpression;
|
|
189
|
+
/** Apply any other op to the OPERAND STREAM after the operator: the numeric
|
|
190
|
+
* operands and symbol terms that follow it, merged nearest-first, each
|
|
191
|
+
* within the host's reach of the token before it — so "sqrt 144",
|
|
192
|
+
* "gcd 12 18", and "opposite large" are one rule, and the NEAREST token is
|
|
193
|
+
* the operand ("opposite large 5" inverts "large", not the 5).
|
|
194
|
+
*
|
|
195
|
+
* Two structural refinements make this read like notation rather than a
|
|
196
|
+
* special case:
|
|
197
|
+
*
|
|
198
|
+
* • WORD-INFIX — an under-supplied operator borrows the numeric operand
|
|
199
|
+
* immediately BEFORE it (within reach), so "7 minus 2" applies
|
|
200
|
+
* subtract(7, 2) exactly as "7 - 2" would: a synonym is notation too.
|
|
201
|
+
* • GROUNDED-ONLY SYMBOLS — when every operand is a symbol, a result that
|
|
202
|
+
* merely echoes an operand means resonance grounded nothing (an inverse
|
|
203
|
+
* with no learnt opposition), and the rule stays silent rather than
|
|
204
|
+
* invent meaning. Numeric identities ("max 3 7" → "7") are real
|
|
205
|
+
* results and pass.
|
|
206
|
+
*
|
|
207
|
+
* A NULLARY op never fires from a bare term: a computation must consume
|
|
208
|
+
* something, or any prose word that happens to name a constant would be
|
|
209
|
+
* rewritten. Symbol operands get their resonance (the opposite each may
|
|
210
|
+
* need) pre-resolved in one prefetch. */
|
|
211
|
+
private applyToStream;
|
|
212
|
+
/** Strip non-math filler tokens from a raw expression string — keep numbers,
|
|
213
|
+
* operators, parens, names the GRAMMAR resolves (unary functions,
|
|
214
|
+
* registered constants), and single-RUNE identifiers. The rune rule is the
|
|
215
|
+
* mirror of {@link "./alu.js".Alu.conceptAnchors}' compound rule: a compound
|
|
216
|
+
* name carries distributional meaning (it is either a resolvable operation
|
|
217
|
+
* or filler — "of", "at", "the"), while a bare atom carries none and can
|
|
218
|
+
* only be the expression's free variable. Dropping the filler leaves the
|
|
219
|
+
* evaluator clean notation. */
|
|
220
|
+
private cleanExprText;
|
|
221
|
+
}
|